Skip to content

Commit 211af4e

Browse files
fix(update): rewrite ctx.renderNotFound() (#3126)
Fixes #3026
1 parent 5903684 commit 211af4e

File tree

4 files changed

+63
-43
lines changed

4 files changed

+63
-43
lines changed

deno.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@
8080
"tailwindcss": "npm:tailwindcss@^4.1.10",
8181
"postcss": "npm:postcss@8.5.6",
8282

83-
"ts-morph": "npm:ts-morph@^25.0.1",
83+
"ts-morph": "npm:ts-morph@^26.0.0",
8484

8585
"@std/front-matter": "jsr:@std/front-matter@^1.0.5",
8686
"github-slugger": "npm:github-slugger@^2.0.0",

deno.lock

Lines changed: 18 additions & 18 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

update/src/update.ts

Lines changed: 39 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ export interface ImportState {
6767
core: Set<string>;
6868
runtime: Set<string>;
6969
compat: Set<string>;
70+
httpError: number;
7071
}
7172

7273
const compat = new Set([
@@ -267,6 +268,7 @@ async function updateFile(sourceFile: tsmorph.SourceFile): Promise<boolean> {
267268
core: new Set(),
268269
runtime: new Set(),
269270
compat: new Set(),
271+
httpError: 0,
270272
};
271273

272274
const text = sourceFile.getFullText()
@@ -302,7 +304,7 @@ async function updateFile(sourceFile: tsmorph.SourceFile): Promise<boolean> {
302304
const body = property.getBody();
303305
if (body !== undefined) {
304306
const stmts = body.getDescendantStatements();
305-
rewriteCtxMethods(stmts);
307+
rewriteCtxMethods(newImports, stmts);
306308
}
307309

308310
maybePrependReqVar(property, newImports, true);
@@ -317,7 +319,7 @@ async function updateFile(sourceFile: tsmorph.SourceFile): Promise<boolean> {
317319
const body = init.getBody();
318320
if (body !== undefined) {
319321
const stmts = body.getDescendantStatements();
320-
rewriteCtxMethods(stmts);
322+
rewriteCtxMethods(newImports, stmts);
321323
}
322324

323325
maybePrependReqVar(init, newImports, true);
@@ -329,7 +331,7 @@ async function updateFile(sourceFile: tsmorph.SourceFile): Promise<boolean> {
329331
const body = node.getBody();
330332
if (body !== undefined) {
331333
const stmts = body.getDescendantStatements();
332-
rewriteCtxMethods(stmts);
334+
rewriteCtxMethods(newImports, stmts);
333335
}
334336

335337
maybePrependReqVar(node, newImports, false);
@@ -354,7 +356,7 @@ async function updateFile(sourceFile: tsmorph.SourceFile): Promise<boolean> {
354356
const body = first.getBody();
355357
if (body !== undefined) {
356358
const stmts = body.getDescendantStatements();
357-
rewriteCtxMethods(stmts);
359+
rewriteCtxMethods(newImports, stmts);
358360
}
359361

360362
maybePrependReqVar(first, newImports, false);
@@ -366,7 +368,7 @@ async function updateFile(sourceFile: tsmorph.SourceFile): Promise<boolean> {
366368
const body = caller.getBody();
367369
if (body !== undefined) {
368370
const stmts = body.getDescendantStatements();
369-
rewriteCtxMethods(stmts);
371+
rewriteCtxMethods(newImports, stmts);
370372
}
371373

372374
maybePrependReqVar(caller, newImports, false);
@@ -441,6 +443,12 @@ async function updateFile(sourceFile: tsmorph.SourceFile): Promise<boolean> {
441443
namedImports: Array.from(newImports.compat),
442444
});
443445
}
446+
if (newImports.httpError > 0) {
447+
sourceFile.addImportDeclaration({
448+
moduleSpecifier: "fresh",
449+
namedImports: ["HttpError"],
450+
});
451+
}
444452

445453
await sourceFile.save();
446454
await format(sourceFile.getFilePath());
@@ -563,25 +571,43 @@ function maybePrependReqVar(
563571
}
564572

565573
function rewriteCtxMethods(
574+
importState: ImportState,
566575
nodes: (tsmorph.Node<tsmorph.ts.Node>)[],
567576
) {
568577
for (let i = 0; i < nodes.length; i++) {
569578
const node = nodes[i];
570579

580+
if (node.wasForgotten()) continue;
581+
582+
if (
583+
node.isKind(SyntaxKind.ExpressionStatement) &&
584+
node.getText() === "ctx.renderNotFound();"
585+
) {
586+
importState.httpError++;
587+
node.replaceWithText("throw new HttpError(404);");
588+
continue;
589+
}
590+
571591
if (node.isKind(SyntaxKind.PropertyAccessExpression)) {
572592
rewriteCtxMemberName(node);
573593
} else if (node.isKind(SyntaxKind.ReturnStatement)) {
574-
const expr = node.getExpression();
575-
if (expr !== undefined) {
576-
rewriteCtxMethods([expr]);
594+
if (node.getText() === "return ctx.renderNotFound();") {
595+
importState.httpError++;
596+
node.replaceWithText(`throw new HttpError(404)`);
597+
continue;
598+
} else {
599+
const expr = node.getExpression();
600+
if (expr !== undefined) {
601+
rewriteCtxMethods(importState, [expr]);
602+
}
577603
}
578604
} else if (node.isKind(SyntaxKind.VariableStatement)) {
579605
const decls = node.getDeclarations();
580606
for (let i = 0; i < decls.length; i++) {
581607
const decl = decls[i];
582608
const init = decl.getInitializer();
583609
if (init !== undefined) {
584-
rewriteCtxMethods([init]);
610+
rewriteCtxMethods(importState, [init]);
585611
}
586612
}
587613
} else if (
@@ -590,16 +616,16 @@ function rewriteCtxMethods(
590616
node.isKind(SyntaxKind.CallExpression)
591617
) {
592618
const expr = node.getExpression();
593-
rewriteCtxMethods([expr]);
619+
rewriteCtxMethods(importState, [expr]);
594620
} else if (node.isKind(SyntaxKind.BinaryExpression)) {
595-
rewriteCtxMethods([node.getLeft()]);
596-
rewriteCtxMethods([node.getRight()]);
621+
rewriteCtxMethods(importState, [node.getLeft()]);
622+
rewriteCtxMethods(importState, [node.getRight()]);
597623
} else if (
598624
!node.isKind(SyntaxKind.ExpressionStatement) &&
599625
node.getKindName().endsWith("Statement")
600626
) {
601627
const inner = node.getDescendantStatements();
602-
rewriteCtxMethods(inner);
628+
rewriteCtxMethods(importState, inner);
603629
}
604630
}
605631
}
@@ -609,19 +635,12 @@ function rewriteCtxMemberName(
609635
) {
610636
const children = node.getChildren();
611637
if (children.length === 0) return;
612-
const last = children[children.length - 1];
613638

614639
if (
615640
node.getExpression().getText() === "ctx" &&
616641
node.getName() === "remoteAddr"
617642
) {
618643
node.getExpression().replaceWithText("ctx.info.remoteAddr");
619-
} else if (last.getText() === "renderNotFound") {
620-
last.replaceWithText("throw");
621-
const caller = node.getParentIfKind(SyntaxKind.CallExpression);
622-
if (caller !== undefined) {
623-
caller.addArgument("404");
624-
}
625644
} else if (children[0].isKind(SyntaxKind.PropertyAccessExpression)) {
626645
rewriteCtxMemberName(children[0]);
627646
}

update/src/update_test.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -542,24 +542,25 @@ Deno.test(
542542
const files = await readFiles(dir);
543543
expect(files["/routes/index.tsx"])
544544
.toEqual(`import { FreshContext } from "fresh";
545+
import { HttpError } from "fresh";
545546
546547
export default async function Index(ctx: FreshContext) {
547548
const req = ctx.req;
548549
549550
if (true) {
550-
return ctx.throw(404);
551+
throw new HttpError(404);
551552
}
552553
if ("foo" === "foo" as any) {
553-
ctx.throw(404);
554-
return ctx.throw(404);
554+
throw new HttpError(404);
555+
throw new HttpError(404);
555556
}
556557
return new Response(req.url);
557558
}`);
558559
},
559560
);
560561

561562
Deno.test.ignore(
562-
"update - 1.x ctx.renderNotFound() -> ctx.throw()",
563+
"update - 1.x ctx.renderNotFound() -> throw new HttpError(404)",
563564
async () => {
564565
await using _tmp = await withTmpDir();
565566
const dir = _tmp.dir;

0 commit comments

Comments
 (0)