Skip to content

Commit

Permalink
autotype working
Browse files Browse the repository at this point in the history
  • Loading branch information
pcattori committed Sep 20, 2024
1 parent f7d63b2 commit a9a8aae
Show file tree
Hide file tree
Showing 5 changed files with 27 additions and 54 deletions.
48 changes: 4 additions & 44 deletions packages/react-router-dev/typescript/autotype/language-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,6 @@ function autotypeRoute(ctx: Context, filepath: string, code: string) {
...annotateDefaultExportExpression(ctx, stmt, typesSource),
...annotateNamedExportFunctionDeclaration(ctx, stmt, typesSource),
...annotateNamedExportVariableStatement(ctx, stmt, typesSource),
...annotateExportDeclaration(ctx, stmt, typesSource),
]),
];
return new AutotypedRoute(code, splices);
Expand Down Expand Up @@ -295,62 +294,23 @@ function annotateFunction(

const annotateReturnType = Route.exports[name]?.annotateReturnType;

name = name === "default" ? "Default" : name;
return [
param && param.type === undefined
? {
index: param.getEnd(),
content: `: Parameters<import("${typesSource}").Route["${name}"]>[0]`,
content: `: import("${typesSource}").${name}["args"]`,
remapDiagnostics,
}
: null,
returnTypeIndex && annotateReturnType && fn.type === undefined
? {
index: returnTypeIndex,
content: `: ReturnType<import("${typesSource}").Route["${name}"]> `,
content: `: import("${typesSource}").${name}["return"]`,
remapDiagnostics,
}
: null,
].filter((x) => x !== null);
}

function annotateExportDeclaration(
ctx: Context,
stmt: ts.Statement,
typesSource: string
): Splice[] {
if (!ctx.ts.isExportDeclaration(stmt)) return [];

const source = stmt.moduleSpecifier;
if (source && !ctx.ts.isStringLiteral(source)) return [];

const exports = stmt.exportClause;
if (!exports) return [];
if (!ctx.ts.isNamedExports(exports)) return [];

return exports.elements
.map((specifier) => {
const name = specifier.name.text;
const routeExport = Route.exports[name];
if (!routeExport) return undefined;

const localName = specifier.propertyName?.text ?? name;
const unique = AST.generateUniqueIdentifier();
const satisfiesType = `import("${typesSource}").Route["${name}"]`;

const splice: Splice = {
index: stmt.getStart(),
content: source
? `import { ${localName} as ${unique} } from "${source.text}"\n` +
`${unique} satisfies ${satisfiesType}\n`
: `${localName} satisfies ${satisfiesType}\n`,
remapDiagnostics: {
start: specifier.name.getStart(),
length: specifier.name.getWidth(),
},
};
return splice;
})
.filter((x) => x !== undefined);
].filter((x) => x !== null) as Splice[];
}

export class AutotypedRoute {
Expand Down
6 changes: 3 additions & 3 deletions packages/react-router-dev/typescript/decorate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ export function decorateLanguageService(ctx: Context) {
replacementSpan: { start: lineStart, length: position - lineStart },
};
})
.filter((x) => x !== null);
.filter((x) => x !== null) as ts.CompletionEntry[];

if (!completions) {
return routeExportCompletions.length > 0
Expand Down Expand Up @@ -176,7 +176,7 @@ export function decorateLanguageService(ctx: Context) {
return diagnostic;
}
})
.filter((x) => x !== undefined);
.filter((x) => x !== undefined) as ts.Diagnostic[];

const hmrNamedFunctionsDiagnostics: ts.Diagnostic[] = sourceFile.statements
// eslint-disable-next-line array-callback-return
Expand Down Expand Up @@ -212,7 +212,7 @@ export function decorateLanguageService(ctx: Context) {
};
}
})
.filter((x) => x !== undefined);
.filter((x) => x !== undefined) as ts.Diagnostic[];

return [
...hmrNamedFunctionsDiagnostics,
Expand Down
2 changes: 2 additions & 0 deletions packages/react-router-dev/typescript/plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import * as Path from "pathe";

import * as Typegen from "./typegen";
import type { Context } from "./context";
import { decorateLanguageService } from "./decorate";

export default function init(modules: { typescript: typeof ts }) {
function create(info: ts.server.PluginCreateInfo) {
Expand All @@ -29,6 +30,7 @@ export default function init(modules: { typescript: typeof ts }) {
};
Typegen.watch(ctx);

decorateLanguageService(ctx);
return info.languageService;
}
return { create };
Expand Down
17 changes: 14 additions & 3 deletions packages/react-router-dev/typescript/routes.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,26 @@
import Path from "pathe";
import Pathe from "pathe/utils";

import type ts from "typescript/lib/tsserverlibrary";

import type { Context } from "./context";
import type { RouteManifestEntry } from "../config/routes";

function noext(path: string) {
return Path.join(Path.dirname(path), Pathe.filename(path));
}

export function get(
ctx: Context,
fileName: string
): RouteManifestEntry | undefined {
return ctx.routes[Path.relative(ctx.config.appDirectory, fileName)];
const routeId = noext(Path.relative(ctx.config.appDirectory, fileName));
ctx.logger?.info(
`Route.get filename:${fileName} routeId:${routeId} routes:${JSON.stringify(
ctx.routes
)}`
);
return ctx.routes[routeId];
}

type RouteExportInfo = {
Expand All @@ -25,7 +36,7 @@ export const exports: Record<string, RouteExportInfo> = {
link: `https://remix.run/docs/en/main/route/links`,
}),
},
serverLoader: {
loader: {
annotateReturnType: false,
documentation: createDocumentation({
name: "serverLoader",
Expand All @@ -47,7 +58,7 @@ export const exports: Record<string, RouteExportInfo> = {
link: `https://remix.run/docs/en/main/route/hydrate-fallback`,
}),
},
serverAction: {
action: {
annotateReturnType: false,
documentation: createDocumentation({
name: "serverAction",
Expand Down
8 changes: 4 additions & 4 deletions packages/react-router-dev/typescript/typegen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,10 @@ function getModule(ctx: Context, route: RouteManifestEntry): string {
export type LoaderData = T.LoaderData<Route>
export type ActionData = T.ActionData<Route>
export type ServerLoader = T.ServerLoader<Params>
export type ClientLoader = T.ClientLoader<Params, Route>
export type ServerAction = T.ServerAction<Params>
export type ClientAction = T.ClientAction<Params, Route>
export type serverLoader = T.ServerLoader<Params>
export type clientLoader = T.ClientLoader<Params, Route>
export type serverAction = T.ServerAction<Params>
export type clientAction = T.ClientAction<Params, Route>
export type HydrateFallback = T.HydrateFallback<Params>
export type Default = T.Default<Params, LoaderData, ActionData>
Expand Down

0 comments on commit a9a8aae

Please sign in to comment.