Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 44 additions & 24 deletions packages/host/app/tools/patch-code.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,32 +66,52 @@ export default class PatchCodeTool extends HostBaseTool<
lintIssues = lintResult.lintIssues ?? [];
}

finalFileIdentifier = await this.determineFinalFileUrl(
fileUrl,
fileInfo,
hasEmptySearchPortion,
);
// An applied patch always changed the pre-lint content, so ending up
// back at the original file means the autofix/format pass reverted
// the whole edit. Re-patching can never make progress from here —
// any further attempt round-trips to this same content — so say so
// alongside the lint issues instead of writing a no-op save that
// would keep the model trying.
let revertedByFormatter =
sourceContent !== '' && patchedCode === sourceContent;
if (revertedByFormatter) {
lintIssues = [
...lintIssues,
'The automatic formatter reverted the applied changes, so the file is unchanged. Reformatting-only patches cannot fix the remaining issues; change the content itself or leave it as is.',
];
} else {
finalFileIdentifier = await this.determineFinalFileUrl(
fileUrl,
fileInfo,
hasEmptySearchPortion,
);

let clientRequestId = this.toolService.trackAiAssistantCardRequest({
action: 'patch-code',
roomId,
fileUrl: finalFileIdentifier,
});
let clientRequestId = this.toolService.trackAiAssistantCardRequest({
action: 'patch-code',
roomId,
fileUrl: finalFileIdentifier,
});

let savedThroughOpenFile = await this.trySaveThroughOpenFile(
finalFileIdentifier,
patchedCode,
clientRequestId,
);
if (!savedThroughOpenFile) {
this.cardService
.saveSource(new URL(finalFileIdentifier), patchedCode, 'bot-patch', {
resetLoader: hasExecutableExtension(finalFileIdentifier),
clientRequestId,
})
.catch((error: unknown) => {
console.error('PatchCodeTool: failed to save source', error);
});
let savedThroughOpenFile = await this.trySaveThroughOpenFile(
finalFileIdentifier,
patchedCode,
clientRequestId,
);
if (!savedThroughOpenFile) {
this.cardService
.saveSource(
new URL(finalFileIdentifier),
patchedCode,
'bot-patch',
{
resetLoader: hasExecutableExtension(finalFileIdentifier),
clientRequestId,
},
)
.catch((error: unknown) => {
console.error('PatchCodeTool: failed to save source', error);
});
}
}
}

Expand Down
40 changes: 40 additions & 0 deletions packages/realm-server/tests/realm-endpoints/lint-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -621,6 +621,46 @@ export class MyCard extends CardDef {
);
});

test('does not flag the whitespace prettier introduces by wrapping long text', async function (assert) {
// Prettier wraps text nodes longer than its print width across
// indented lines; no-whitespace-for-layout flags exactly that wrap and
// has no autofix, so reporting it would hand back an issue no edit can
// clear — the formatter reverts every attempted whitespace fix.
let longText =
'WELCOME TO MY HOMEPAGE &nbsp;&nbsp;&nbsp; HOT SITE AWARD WINNER &nbsp;&nbsp;&nbsp; BEST VIEWED IN 800x600 &nbsp;&nbsp;&nbsp;';
let response = await request
.post('/_lint')
.set(
'Authorization',
`Bearer ${createJWT(testRealm, 'john', ['read', 'write'])}`,
)
.set('X-HTTP-Method-Override', 'QUERY')
.set('Accept', 'application/json')
.send(`import { CardDef } from '@cardstack/base/card-api';
export class MyCard extends CardDef {
}
<template>
<div class='marquee'>
<span>${longText}</span>
</div>
</template>
`);

assert.strictEqual(response.status, 200, 'HTTP 200 status');
let responseJson = JSON.parse(response.text);
assert.ok(
/<span>[^<]*\n[^<]*<\/span>/.test(responseJson.output),
'prettier wrapped the long text node across lines',
);
let whitespaceMessage = responseJson.messages.find(
(m: any) => m.ruleId === 'no-whitespace-for-layout',
);
assert.notOk(
whitespaceMessage,
'no-whitespace-for-layout is not reported for formatter-introduced wrapping',
);
});

test('handles nested template structures correctly', async function (assert) {
let response = await request
.post('/_lint')
Expand Down
19 changes: 18 additions & 1 deletion packages/runtime-common/tasks/lint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,24 @@ async function initTemplateLinter(): Promise<any> {
const hostRequire = Module.createRequire(resolve(HOST_PKG, 'package.json'));
const tlModule = hostRequire('ember-template-lint');
const TemplateLinter = (tlModule as any).default ?? tlModule;
return new TemplateLinter({ workingDir: HOST_PKG });
// Host's config, minus rules the rest of this pipeline contradicts.
// Prettier (the pass right before template-lint) wraps text nodes longer
// than its print width across indented lines; no-whitespace-for-layout
// flags exactly that wrap and has no autofix, so for any over-width text
// node the pipeline would report an error that no edit can clear — the
// formatter reverts every attempted whitespace fix. The wrapped whitespace
// collapses at render, so nothing is lost by not flagging it.
const hostTemplateLintConfig = hostRequire('./.template-lintrc.js');
return new TemplateLinter({
workingDir: HOST_PKG,
config: {
...hostTemplateLintConfig,
rules: {
...hostTemplateLintConfig.rules,
'no-whitespace-for-layout': false,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve whitespace linting for standalone HBS files

This disables the rule for every template-lint invocation, including .hbs submissions. In lintOne, .hbs is absent from ESLINT_EXTENSIONS, so it never passes through the Prettier step that motivates this exception, but it is included in TEMPLATE_LINT_EXTENSIONS; consequently genuine whitespace-for-layout violations in standalone templates are now silently accepted. Apply the override only for .gts/.gjs inputs that were formatted, or retain a separately configured linter for .hbs.

Useful? React with 👍 / 👎.

},
},
});
}

// ---------------------------------------------------------------------------
Expand Down
Loading