@supacloud/compiler 0.21.1 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -1
- package/dist/cli.js +663 -355
- package/dist/database-contracts.d.ts +48 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +639 -338
- package/dist/sql-safety.d.ts +17 -0
- package/dist/static-di.d.ts +4 -0
- package/dist/type-safety.d.ts +8 -0
- package/package.json +3 -1
package/dist/cli.js
CHANGED
|
@@ -355,7 +355,7 @@ class ModuleGenerator {
|
|
|
355
355
|
this.imports = imports;
|
|
356
356
|
this.pascal = pascalName(module.name);
|
|
357
357
|
if (module.providers.some((provider) => (provider.functionalInjects?.length ?? 0) > 0) || module.controllers.some((controller) => (controller.functionalInjects?.length ?? 0) > 0)) {
|
|
358
|
-
|
|
358
|
+
throw new Error("SC2012: Compiled DI requires constructor injection; property inject() is not supported.");
|
|
359
359
|
}
|
|
360
360
|
}
|
|
361
361
|
renderFactories() {
|
|
@@ -389,6 +389,7 @@ class ModuleGenerator {
|
|
|
389
389
|
lines.push(` jobs: ${this.renderJobs()},`);
|
|
390
390
|
if (this.module.aspects && this.module.aspects.length > 0) {
|
|
391
391
|
lines.push(` aspects: ${this.renderAspects(this.module.aspects)},`);
|
|
392
|
+
lines.push(` aspectPipeline: ${this.renderAspectPipeline(this.module.aspects)},`);
|
|
392
393
|
}
|
|
393
394
|
lines.push(`}`);
|
|
394
395
|
return lines.join(`
|
|
@@ -465,6 +466,7 @@ class ModuleGenerator {
|
|
|
465
466
|
}
|
|
466
467
|
if (route.aspects && route.aspects.length > 0) {
|
|
467
468
|
fields.push(`aspects: ${this.renderAspects(route.aspects)}`);
|
|
469
|
+
fields.push(`aspectPipeline: ${this.renderAspectPipeline(route.aspects)}`);
|
|
468
470
|
}
|
|
469
471
|
const invokerArgs = (route.handlerParams ?? []).map((hp) => {
|
|
470
472
|
if (hp.kind === "param") {
|
|
@@ -545,7 +547,7 @@ ${indent(item, 2)}`).join(",")}
|
|
|
545
547
|
`idempotency: ${JSON.stringify(command.idempotency)}`,
|
|
546
548
|
...command.rpc ? [`rpc: ${JSON.stringify(command.rpc)}`] : [],
|
|
547
549
|
...command.standalone ? ["standalone: true"] : [],
|
|
548
|
-
...command.aspects && command.aspects.length > 0 ? [`aspects: ${this.renderAspects(command.aspects)}`] : []
|
|
550
|
+
...command.aspects && command.aspects.length > 0 ? [`aspects: ${this.renderAspects(command.aspects)}`, `aspectPipeline: ${this.renderAspectPipeline(command.aspects)}`] : []
|
|
549
551
|
];
|
|
550
552
|
return `{ ${fields.join(", ")} }`;
|
|
551
553
|
}).join(", ")}]`;
|
|
@@ -578,6 +580,7 @@ ${indent(item, 2)}`).join(",")}
|
|
|
578
580
|
fields.push(`idempotency: ${JSON.stringify(job.idempotency)}`);
|
|
579
581
|
if (job.aspects && job.aspects.length > 0) {
|
|
580
582
|
fields.push(`aspects: ${this.renderAspects(job.aspects)}`);
|
|
583
|
+
fields.push(`aspectPipeline: ${this.renderAspectPipeline(job.aspects)}`);
|
|
581
584
|
}
|
|
582
585
|
return `{ ${fields.join(", ")}, }`;
|
|
583
586
|
}).join(", ")}]`;
|
|
@@ -585,6 +588,24 @@ ${indent(item, 2)}`).join(",")}
|
|
|
585
588
|
renderAspects(aspects) {
|
|
586
589
|
return `[${aspects.map((aspect) => this.imports.add(aspect.name, aspect.importPath, aspect.importModule)).join(", ")}]`;
|
|
587
590
|
}
|
|
591
|
+
renderAspectPipeline(aspects) {
|
|
592
|
+
const lines = [
|
|
593
|
+
`async (context, next, observe) => {`,
|
|
594
|
+
` const state = { active: true };`,
|
|
595
|
+
` const step${aspects.length} = compiledAspectNext(next, state);`
|
|
596
|
+
];
|
|
597
|
+
for (let index = aspects.length - 1;index >= 0; index--) {
|
|
598
|
+
const aspect = aspects[index];
|
|
599
|
+
if (!aspect)
|
|
600
|
+
continue;
|
|
601
|
+
const name = this.imports.add(aspect.name, aspect.importPath, aspect.importModule);
|
|
602
|
+
const stage = JSON.stringify(`aspect[${index}]:${aspect.name}`);
|
|
603
|
+
lines.push(` const step${index} = compiledAspectNext(() => observeCompiledAspect(observe, ${stage}, () => ${name}(context, step${index + 1})), state);`);
|
|
604
|
+
}
|
|
605
|
+
lines.push(` try { return await step0(); } finally { state.active = false; }`, `}`);
|
|
606
|
+
return lines.join(`
|
|
607
|
+
`);
|
|
608
|
+
}
|
|
588
609
|
renderServicesFactory() {
|
|
589
610
|
return [
|
|
590
611
|
`function create${this.pascal}Services(`,
|
|
@@ -699,7 +720,7 @@ ${indent(item, 2)}`).join(",")}
|
|
|
699
720
|
const args = provider.deps.map((dep, index) => this.typedDepExpr(dep, kind, this.depOptions(provider, dep), `ConstructorParameters<typeof ${useClass}>[${index}]`)).join(", ");
|
|
700
721
|
const local = this.localVar(isMulti ? provider.useClass ?? `${provider.token}Item` : provider.token, kind);
|
|
701
722
|
return {
|
|
702
|
-
constLine: `const ${local} = ${
|
|
723
|
+
constLine: `const ${local} = new ${useClass}(${args});`,
|
|
703
724
|
key,
|
|
704
725
|
expr: local
|
|
705
726
|
};
|
|
@@ -735,32 +756,11 @@ ${indent(item, 2)}`).join(",")}
|
|
|
735
756
|
const key = camelName(controller.className);
|
|
736
757
|
const local = this.localVar(controller.className, kind);
|
|
737
758
|
return {
|
|
738
|
-
constLine: `const ${local} = ${
|
|
759
|
+
constLine: `const ${local} = new ${className}(${args});`,
|
|
739
760
|
key,
|
|
740
761
|
expr: local
|
|
741
762
|
};
|
|
742
763
|
}
|
|
743
|
-
instantiate(className, args, kind, functionalInjects) {
|
|
744
|
-
if (!functionalInjects || functionalInjects.length === 0) {
|
|
745
|
-
return `new ${className}(${args})`;
|
|
746
|
-
}
|
|
747
|
-
const clauses = functionalInjects.map((entry) => {
|
|
748
|
-
const token = this.imports.add(entry.expression, entry.importPath, entry.importModule);
|
|
749
|
-
const value = this.depExpr(entry.token, kind, entry);
|
|
750
|
-
return `if (token === ${token}) return ${value} as T;`;
|
|
751
|
-
});
|
|
752
|
-
const missing = `if (options?.optional) return undefined; throw new Error("Static inject token not available: " + String(token));`;
|
|
753
|
-
const injector = [
|
|
754
|
-
`{`,
|
|
755
|
-
`get<T>(token: unknown, options?: { optional?: boolean; self?: boolean; skipSelf?: boolean; host?: boolean }): T | undefined {`,
|
|
756
|
-
...clauses,
|
|
757
|
-
missing,
|
|
758
|
-
`},`,
|
|
759
|
-
`}`
|
|
760
|
-
].join(`
|
|
761
|
-
`);
|
|
762
|
-
return `runInInjectionContext(${injector}, () => new ${className}(${args}))`;
|
|
763
|
-
}
|
|
764
764
|
localVar(token, kind) {
|
|
765
765
|
const locals = this.locals[kind];
|
|
766
766
|
const existing = locals.get(token);
|
|
@@ -1933,6 +1933,7 @@ var HEADER = "// GENERATED BY @supacloud/compiler — do not edit", INTERFACES =
|
|
|
1933
1933
|
title?: string;
|
|
1934
1934
|
data?: Record<string, unknown>;
|
|
1935
1935
|
aspects?: CompiledAspect[];
|
|
1936
|
+
aspectPipeline?: CompiledAspectPipeline;
|
|
1936
1937
|
invoker?: (
|
|
1937
1938
|
controller: unknown,
|
|
1938
1939
|
request: {
|
|
@@ -1956,6 +1957,7 @@ export interface CompiledCommand {
|
|
|
1956
1957
|
idempotency: "required" | "none";
|
|
1957
1958
|
standalone?: boolean;
|
|
1958
1959
|
aspects?: CompiledAspect[];
|
|
1960
|
+
aspectPipeline?: CompiledAspectPipeline;
|
|
1959
1961
|
}
|
|
1960
1962
|
|
|
1961
1963
|
export interface CompiledJob {
|
|
@@ -1970,6 +1972,7 @@ export interface CompiledJob {
|
|
|
1970
1972
|
maxAttempts?: number;
|
|
1971
1973
|
idempotency?: "required" | "none";
|
|
1972
1974
|
aspects?: CompiledAspect[];
|
|
1975
|
+
aspectPipeline?: CompiledAspectPipeline;
|
|
1973
1976
|
}
|
|
1974
1977
|
|
|
1975
1978
|
export interface CompiledAspectContext {
|
|
@@ -2017,7 +2020,24 @@ export interface CompiledModule {
|
|
|
2017
2020
|
commands: CompiledCommand[];
|
|
2018
2021
|
jobs: CompiledJob[];
|
|
2019
2022
|
aspects?: CompiledAspect[];
|
|
2020
|
-
|
|
2023
|
+
aspectPipeline?: CompiledAspectPipeline;
|
|
2024
|
+
}`, TYPE_GUARDS = `type CompiledAspectObserver = (stage: string, run: () => unknown | Promise<unknown>) => unknown | Promise<unknown>;
|
|
2025
|
+
type CompiledAspectPipeline = (context: CompiledAspectContext, next: () => unknown | Promise<unknown>, observe?: CompiledAspectObserver) => unknown | Promise<unknown>;
|
|
2026
|
+
|
|
2027
|
+
function compiledAspectNext(next: () => unknown | Promise<unknown>, state: { active: boolean }): () => Promise<unknown> {
|
|
2028
|
+
let called = false;
|
|
2029
|
+
return async () => {
|
|
2030
|
+
if (!state.active) throw new Error("Aspect continuation is closed");
|
|
2031
|
+
if (called) throw new Error("Aspect continuation called multiple times");
|
|
2032
|
+
called = true;
|
|
2033
|
+
return await next();
|
|
2034
|
+
};
|
|
2035
|
+
}
|
|
2036
|
+
function observeCompiledAspect(observe: CompiledAspectObserver | undefined, stage: string, run: () => unknown | Promise<unknown>): unknown | Promise<unknown> {
|
|
2037
|
+
return observe ? observe(stage, run) : run();
|
|
2038
|
+
}
|
|
2039
|
+
|
|
2040
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
2021
2041
|
return typeof value === "object" && value !== null;
|
|
2022
2042
|
}
|
|
2023
2043
|
|
|
@@ -2199,7 +2219,7 @@ var init_graphql_options = __esm(() => {
|
|
|
2199
2219
|
|
|
2200
2220
|
// src/graphql-inputs.ts
|
|
2201
2221
|
import { resolve as resolve3 } from "node:path";
|
|
2202
|
-
import * as
|
|
2222
|
+
import * as ts7 from "@typescript/typescript6";
|
|
2203
2223
|
function graphqlInputPaths(options) {
|
|
2204
2224
|
if (!options.graphql)
|
|
2205
2225
|
return [];
|
|
@@ -2210,7 +2230,7 @@ function graphqlInputPaths(options) {
|
|
|
2210
2230
|
}
|
|
2211
2231
|
const root = resolve3(options.rootDir);
|
|
2212
2232
|
const schema = resolve3(root, options.graphql.schema);
|
|
2213
|
-
const documents =
|
|
2233
|
+
const documents = ts7.sys.readDirectory(root, [".graphql", ".gql"], ["**/node_modules/**", "**/.git/**", resolve3(options.outDir)], options.graphql.documents ?? ["**/*.graphql", "**/*.gql"]).map((path) => resolve3(path)).filter((path) => path !== schema);
|
|
2214
2234
|
return [schema, ...[...new Set(documents)].sort()];
|
|
2215
2235
|
}
|
|
2216
2236
|
var init_graphql_inputs = __esm(() => {
|
|
@@ -2218,7 +2238,7 @@ var init_graphql_inputs = __esm(() => {
|
|
|
2218
2238
|
});
|
|
2219
2239
|
|
|
2220
2240
|
// src/graphql-runtime.ts
|
|
2221
|
-
import * as
|
|
2241
|
+
import * as ts8 from "@typescript/typescript6";
|
|
2222
2242
|
import { resolve as resolve4 } from "node:path";
|
|
2223
2243
|
function renderGraphqlValidators(source, operationNames) {
|
|
2224
2244
|
const fileName = resolve4("/__supacloud_graphql__/contracts.ts");
|
|
@@ -2230,18 +2250,18 @@ function renderGraphqlValidators(source, operationNames) {
|
|
|
2230
2250
|
noPropertyAccessFromIndexSignature: true,
|
|
2231
2251
|
noFallthroughCasesInSwitch: true,
|
|
2232
2252
|
skipLibCheck: false,
|
|
2233
|
-
target:
|
|
2253
|
+
target: ts8.ScriptTarget.ES2022,
|
|
2234
2254
|
lib: ["lib.es2022.d.ts"],
|
|
2235
2255
|
types: [],
|
|
2236
2256
|
noEmit: true
|
|
2237
2257
|
};
|
|
2238
|
-
const host =
|
|
2258
|
+
const host = ts8.createCompilerHost(options);
|
|
2239
2259
|
const getSourceFile = host.getSourceFile.bind(host);
|
|
2240
|
-
host.getSourceFile = (path, languageVersion, onError, shouldCreateNewSourceFile) => path === fileName ?
|
|
2241
|
-
const program =
|
|
2242
|
-
const diagnostics =
|
|
2260
|
+
host.getSourceFile = (path, languageVersion, onError, shouldCreateNewSourceFile) => path === fileName ? ts8.createSourceFile(path, source, languageVersion, true) : getSourceFile(path, languageVersion, onError, shouldCreateNewSourceFile);
|
|
2261
|
+
const program = ts8.createProgram([fileName], options, host);
|
|
2262
|
+
const diagnostics = ts8.getPreEmitDiagnostics(program);
|
|
2243
2263
|
if (diagnostics.length) {
|
|
2244
|
-
throw new Error(`Generated GraphQL types cannot be validated: ${diagnostics.map((item) =>
|
|
2264
|
+
throw new Error(`Generated GraphQL types cannot be validated: ${diagnostics.map((item) => ts8.flattenDiagnosticMessageText(item.messageText, `
|
|
2245
2265
|
`)).join("; ")}`);
|
|
2246
2266
|
}
|
|
2247
2267
|
const checker = program.getTypeChecker();
|
|
@@ -2271,25 +2291,25 @@ function renderGraphqlValidators(source, operationNames) {
|
|
|
2271
2291
|
return name;
|
|
2272
2292
|
}
|
|
2273
2293
|
function expression(type) {
|
|
2274
|
-
if (type.flags &
|
|
2294
|
+
if (type.flags & ts8.TypeFlags.Any)
|
|
2275
2295
|
return unsupported(type);
|
|
2276
|
-
if (type.flags &
|
|
2296
|
+
if (type.flags & ts8.TypeFlags.Unknown)
|
|
2277
2297
|
return "true";
|
|
2278
|
-
if (type.flags &
|
|
2298
|
+
if (type.flags & ts8.TypeFlags.Never)
|
|
2279
2299
|
return "false";
|
|
2280
|
-
if (type.flags &
|
|
2300
|
+
if (type.flags & ts8.TypeFlags.Null)
|
|
2281
2301
|
return "value === null";
|
|
2282
|
-
if (type.flags &
|
|
2302
|
+
if (type.flags & ts8.TypeFlags.Undefined)
|
|
2283
2303
|
return "value === undefined";
|
|
2284
2304
|
if (type.isStringLiteral() || type.isNumberLiteral())
|
|
2285
2305
|
return `value === ${JSON.stringify(type.value)}`;
|
|
2286
|
-
if (type.flags &
|
|
2306
|
+
if (type.flags & ts8.TypeFlags.BooleanLiteral)
|
|
2287
2307
|
return `value === ${checker.typeToString(type)}`;
|
|
2288
|
-
if (type.flags &
|
|
2308
|
+
if (type.flags & ts8.TypeFlags.String)
|
|
2289
2309
|
return 'typeof value === "string"';
|
|
2290
|
-
if (type.flags &
|
|
2310
|
+
if (type.flags & ts8.TypeFlags.Number)
|
|
2291
2311
|
return 'typeof value === "number" && Number.isFinite(value)';
|
|
2292
|
-
if (type.flags &
|
|
2312
|
+
if (type.flags & ts8.TypeFlags.Boolean)
|
|
2293
2313
|
return 'typeof value === "boolean"';
|
|
2294
2314
|
if (type.isUnion())
|
|
2295
2315
|
return type.types.map((part) => `${reference(part)}(value)`).join(" || ");
|
|
@@ -2298,16 +2318,16 @@ function renderGraphqlValidators(source, operationNames) {
|
|
|
2298
2318
|
if (checker.isTupleType(type))
|
|
2299
2319
|
return unsupported(type);
|
|
2300
2320
|
if (checker.isArrayType(type)) {
|
|
2301
|
-
const item = checker.getIndexTypeOfType(type,
|
|
2321
|
+
const item = checker.getIndexTypeOfType(type, ts8.IndexKind.Number);
|
|
2302
2322
|
if (!item)
|
|
2303
2323
|
return unsupported(type);
|
|
2304
2324
|
return `isGraphqlArray(value) && Array.from(value).every(${reference(item)})`;
|
|
2305
2325
|
}
|
|
2306
|
-
if (type.flags &
|
|
2326
|
+
if (type.flags & ts8.TypeFlags.Object) {
|
|
2307
2327
|
if (type.getCallSignatures().length || type.getConstructSignatures().length)
|
|
2308
2328
|
return unsupported(type);
|
|
2309
2329
|
const indexes = checker.getIndexInfosOfType(type);
|
|
2310
|
-
if (indexes.some((index) => !(index.keyType.flags &
|
|
2330
|
+
if (indexes.some((index) => !(index.keyType.flags & ts8.TypeFlags.String)))
|
|
2311
2331
|
return unsupported(type);
|
|
2312
2332
|
const properties = checker.getPropertiesOfType(type).map((property) => {
|
|
2313
2333
|
const declaration = property.valueDeclaration ?? property.declarations?.[0];
|
|
@@ -2316,7 +2336,7 @@ function renderGraphqlValidators(source, operationNames) {
|
|
|
2316
2336
|
const check = reference(checker.getTypeOfSymbolAtLocation(property, declaration));
|
|
2317
2337
|
const key = JSON.stringify(property.name);
|
|
2318
2338
|
const present = `Object.prototype.hasOwnProperty.call(value, ${key})`;
|
|
2319
|
-
return property.flags &
|
|
2339
|
+
return property.flags & ts8.SymbolFlags.Optional ? `(!${present} || ${check}(value[${key}]))` : `(${present} && ${check}(value[${key}]))`;
|
|
2320
2340
|
});
|
|
2321
2341
|
const indexedValues = indexes.map((index) => `Object.values(value).every(${reference(index.type)})`);
|
|
2322
2342
|
return ["isGraphqlRecord(value)", ...properties, ...indexedValues].join(" && ");
|
|
@@ -2574,10 +2594,155 @@ var init_graphql = __esm(() => {
|
|
|
2574
2594
|
init_graphql_runtime();
|
|
2575
2595
|
});
|
|
2576
2596
|
|
|
2577
|
-
// src/
|
|
2578
|
-
import { mkdir as mkdir5, readFile as readFile11 } from "node:fs/promises";
|
|
2597
|
+
// src/database-contracts.ts
|
|
2579
2598
|
import { createHash as createHash9 } from "node:crypto";
|
|
2580
|
-
import {
|
|
2599
|
+
import { mkdir as mkdir5, readFile as readFile11 } from "node:fs/promises";
|
|
2600
|
+
import { dirname as dirname10, relative as relative12, resolve as resolve15 } from "node:path";
|
|
2601
|
+
import * as ts13 from "@typescript/typescript6";
|
|
2602
|
+
function hash2(value) {
|
|
2603
|
+
return createHash9("sha256").update(value).digest("hex");
|
|
2604
|
+
}
|
|
2605
|
+
function importPath(out, path) {
|
|
2606
|
+
const value = relative12(out, path).replaceAll("\\", "/").replace(/\.(?:d\.)?[cm]?ts$/, "");
|
|
2607
|
+
return value.startsWith(".") ? value : `./${value}`;
|
|
2608
|
+
}
|
|
2609
|
+
function parseDatabaseContractsOptions(value, directory) {
|
|
2610
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
2611
|
+
throw new TypeError("Expected database contracts configuration");
|
|
2612
|
+
const allowed = ["rootDir", "outDir", "postgrestTypes", "drizzleSchema", "role", "graphql", "migrations"];
|
|
2613
|
+
if (Object.keys(value).some((name) => !allowed.includes(name)))
|
|
2614
|
+
throw new TypeError("Unknown database contracts option");
|
|
2615
|
+
const field = (name) => {
|
|
2616
|
+
const result = Reflect.get(value, name);
|
|
2617
|
+
if (typeof result !== "string" || !result.trim())
|
|
2618
|
+
throw new TypeError(`Missing database contracts ${name}`);
|
|
2619
|
+
return result;
|
|
2620
|
+
};
|
|
2621
|
+
const graphql = Reflect.get(value, "graphql");
|
|
2622
|
+
assertGraphqlOptions(graphql);
|
|
2623
|
+
const migrations = Reflect.get(value, "migrations");
|
|
2624
|
+
if (!Array.isArray(migrations) || !migrations.every((entry) => typeof entry === "string" && entry.endsWith(".sql"))) {
|
|
2625
|
+
throw new TypeError("migrations must be an ordered list of SQL files");
|
|
2626
|
+
}
|
|
2627
|
+
return {
|
|
2628
|
+
rootDir: resolve15(directory, field("rootDir")),
|
|
2629
|
+
outDir: resolve15(directory, field("outDir")),
|
|
2630
|
+
postgrestTypes: resolve15(directory, field("postgrestTypes")),
|
|
2631
|
+
drizzleSchema: resolve15(directory, field("drizzleSchema")),
|
|
2632
|
+
role: field("role"),
|
|
2633
|
+
graphql: { ...graphql, schema: resolve15(directory, graphql.schema) },
|
|
2634
|
+
migrations: migrations.map((file) => resolve15(directory, file))
|
|
2635
|
+
};
|
|
2636
|
+
}
|
|
2637
|
+
async function generateDatabaseContracts(options, check = false) {
|
|
2638
|
+
const rootDir = resolve15(options.rootDir), outDir = resolve15(options.outDir);
|
|
2639
|
+
const postgrestTypes = resolve15(rootDir, options.postgrestTypes), drizzleSchema = resolve15(rootDir, options.drizzleSchema);
|
|
2640
|
+
const snapshot = await readFile11(postgrestTypes, "utf8");
|
|
2641
|
+
const syntax = ts13.createSourceFile(postgrestTypes, snapshot, ts13.ScriptTarget.Latest, true);
|
|
2642
|
+
const database = syntax.statements.find((node) => (ts13.isTypeAliasDeclaration(node) || ts13.isInterfaceDeclaration(node)) && node.name.text === "Database" && node.modifiers?.some((modifier) => modifier.kind === ts13.SyntaxKind.ExportKeyword));
|
|
2643
|
+
if (!database)
|
|
2644
|
+
throw new Error("PostgREST snapshot must export Database from the official type generator");
|
|
2645
|
+
const program = ts13.createProgram([postgrestTypes], {
|
|
2646
|
+
strict: true,
|
|
2647
|
+
noEmit: true,
|
|
2648
|
+
skipLibCheck: true,
|
|
2649
|
+
types: [],
|
|
2650
|
+
target: ts13.ScriptTarget.ES2022,
|
|
2651
|
+
module: ts13.ModuleKind.ESNext,
|
|
2652
|
+
moduleResolution: ts13.ModuleResolutionKind.Bundler
|
|
2653
|
+
});
|
|
2654
|
+
if (ts13.getPreEmitDiagnostics(program).length)
|
|
2655
|
+
throw new Error("PostgREST snapshot has TypeScript errors");
|
|
2656
|
+
const artifacts = await renderGraphql({
|
|
2657
|
+
rootDir,
|
|
2658
|
+
outDir,
|
|
2659
|
+
graphql: options.graphql
|
|
2660
|
+
});
|
|
2661
|
+
if (artifacts.diagnostics.some((item) => item.severity === "error")) {
|
|
2662
|
+
throw new Error(artifacts.diagnostics.map((item) => item.message).join(`
|
|
2663
|
+
`));
|
|
2664
|
+
}
|
|
2665
|
+
const inputs = {};
|
|
2666
|
+
const addInput = async (path) => {
|
|
2667
|
+
const absolute = resolve15(rootDir, path);
|
|
2668
|
+
inputs[relative12(rootDir, absolute).replaceAll("\\", "/")] = hash2(await readFile11(absolute, "utf8"));
|
|
2669
|
+
};
|
|
2670
|
+
await addInput(postgrestTypes);
|
|
2671
|
+
await addInput(drizzleSchema);
|
|
2672
|
+
const drizzleProgram = ts13.createProgram([drizzleSchema], {
|
|
2673
|
+
noEmit: true,
|
|
2674
|
+
moduleResolution: ts13.ModuleResolutionKind.Bundler,
|
|
2675
|
+
module: ts13.ModuleKind.ESNext,
|
|
2676
|
+
target: ts13.ScriptTarget.ES2022,
|
|
2677
|
+
types: [],
|
|
2678
|
+
skipLibCheck: true
|
|
2679
|
+
});
|
|
2680
|
+
for (const source of drizzleProgram.getSourceFiles()) {
|
|
2681
|
+
if (!source.isDeclarationFile && !source.fileName.includes("/node_modules/"))
|
|
2682
|
+
await addInput(source.fileName);
|
|
2683
|
+
}
|
|
2684
|
+
await addInput(resolve15(rootDir, options.graphql.schema));
|
|
2685
|
+
if (new Set(options.migrations.map((path) => resolve15(rootDir, path))).size !== options.migrations.length) {
|
|
2686
|
+
throw new Error("Duplicate migration in database contracts configuration");
|
|
2687
|
+
}
|
|
2688
|
+
for (const path of options.migrations)
|
|
2689
|
+
await addInput(path);
|
|
2690
|
+
const files = {
|
|
2691
|
+
...artifacts.files,
|
|
2692
|
+
"database.ts": [
|
|
2693
|
+
"// GENERATED BY @supacloud/compiler database-contracts. Do not edit.",
|
|
2694
|
+
`export type { Database } from ${JSON.stringify(importPath(outDir, postgrestTypes))};`,
|
|
2695
|
+
'export type { QueryData, QueryResult, QueryError } from "@supabase/supabase-js";',
|
|
2696
|
+
`export type DrizzleSchema = typeof import(${JSON.stringify(importPath(outDir, drizzleSchema))});`,
|
|
2697
|
+
'export * from "./graphql";',
|
|
2698
|
+
""
|
|
2699
|
+
].join(`
|
|
2700
|
+
`)
|
|
2701
|
+
};
|
|
2702
|
+
const manifest = {
|
|
2703
|
+
version: 1,
|
|
2704
|
+
role: options.role,
|
|
2705
|
+
inputs: Object.fromEntries(Object.entries(inputs).sort(([a], [b]) => a.localeCompare(b))),
|
|
2706
|
+
migrationOrder: options.migrations.map((path) => relative12(rootDir, resolve15(rootDir, path)).replaceAll("\\", "/")),
|
|
2707
|
+
outputs: Object.fromEntries(Object.entries(files).sort(([a], [b]) => a.localeCompare(b)).map(([file, text]) => [file, hash2(text)]))
|
|
2708
|
+
};
|
|
2709
|
+
files["database.manifest.json"] = JSON.stringify(manifest, null, 2) + `
|
|
2710
|
+
`;
|
|
2711
|
+
const mismatches = [];
|
|
2712
|
+
for (const [file, content] of Object.entries(files)) {
|
|
2713
|
+
const path = resolve15(outDir, file);
|
|
2714
|
+
let current;
|
|
2715
|
+
try {
|
|
2716
|
+
current = await readFile11(path, "utf8");
|
|
2717
|
+
} catch (error) {
|
|
2718
|
+
if (!(error instanceof Error && ("code" in error) && error.code === "ENOENT"))
|
|
2719
|
+
throw error;
|
|
2720
|
+
}
|
|
2721
|
+
if (current !== content)
|
|
2722
|
+
mismatches.push(file);
|
|
2723
|
+
}
|
|
2724
|
+
if (!check) {
|
|
2725
|
+
await mkdir5(outDir, { recursive: true });
|
|
2726
|
+
for (const [file, content] of Object.entries(files))
|
|
2727
|
+
await writeFileIfChanged(resolve15(outDir, file), content);
|
|
2728
|
+
}
|
|
2729
|
+
return { upToDate: mismatches.length === 0, mismatches, written: check ? [] : mismatches, manifest };
|
|
2730
|
+
}
|
|
2731
|
+
async function runDatabaseContractsFile(path, check = false) {
|
|
2732
|
+
const absolute = resolve15(path);
|
|
2733
|
+
const value = JSON.parse(await readFile11(absolute, "utf8"));
|
|
2734
|
+
return generateDatabaseContracts(parseDatabaseContractsOptions(value, dirname10(absolute)), check);
|
|
2735
|
+
}
|
|
2736
|
+
var init_database_contracts = __esm(() => {
|
|
2737
|
+
init_graphql();
|
|
2738
|
+
init_generate();
|
|
2739
|
+
init_graphql_options();
|
|
2740
|
+
});
|
|
2741
|
+
|
|
2742
|
+
// src/graphql-schema.ts
|
|
2743
|
+
import { mkdir as mkdir6, readFile as readFile12 } from "node:fs/promises";
|
|
2744
|
+
import { createHash as createHash10 } from "node:crypto";
|
|
2745
|
+
import { dirname as dirname11, resolve as resolve16 } from "node:path";
|
|
2581
2746
|
async function pullGraphqlSchema(options) {
|
|
2582
2747
|
assertGraphqlOptions({ schema: options.output });
|
|
2583
2748
|
const endpoint = new URL(options.url);
|
|
@@ -2617,7 +2782,7 @@ async function pullGraphqlSchema(options) {
|
|
|
2617
2782
|
throw new Error("GraphQL schema export failed. Verify caller grants and enable introspection only in the intended development environment.");
|
|
2618
2783
|
}
|
|
2619
2784
|
const schema = lexicographicSortSchema(buildClientSchema(data));
|
|
2620
|
-
const path =
|
|
2785
|
+
const path = resolve16(options.output);
|
|
2621
2786
|
const content = path.endsWith(".json") ? JSON.stringify(introspectionFromSchema(schema), null, 2) + `
|
|
2622
2787
|
` : `# GENERATED BY supacloud-compiler graphql-schema. DO NOT EDIT.
|
|
2623
2788
|
# Database First: change database declarations, apply migrations, then re-export for the intended role.
|
|
@@ -2625,16 +2790,16 @@ async function pullGraphqlSchema(options) {
|
|
|
2625
2790
|
`;
|
|
2626
2791
|
let previous;
|
|
2627
2792
|
try {
|
|
2628
|
-
previous = await
|
|
2793
|
+
previous = await readFile12(path, "utf8");
|
|
2629
2794
|
} catch (error) {
|
|
2630
2795
|
if (!(error instanceof Error && ("code" in error) && error.code === "ENOENT"))
|
|
2631
2796
|
throw error;
|
|
2632
2797
|
}
|
|
2633
2798
|
const upToDate = previous === content;
|
|
2634
|
-
const schemaHash =
|
|
2799
|
+
const schemaHash = createHash10("sha256").update(content).digest("hex");
|
|
2635
2800
|
if (options.check)
|
|
2636
2801
|
return { path, schemaHash, upToDate, written: false };
|
|
2637
|
-
await
|
|
2802
|
+
await mkdir6(dirname11(path), { recursive: true });
|
|
2638
2803
|
await writeFileIfChanged(path, content);
|
|
2639
2804
|
return { path, schemaHash, upToDate: true, written: true };
|
|
2640
2805
|
}
|
|
@@ -2644,13 +2809,13 @@ var init_graphql_schema = __esm(() => {
|
|
|
2644
2809
|
});
|
|
2645
2810
|
|
|
2646
2811
|
// src/cli.ts
|
|
2647
|
-
import { resolve as
|
|
2648
|
-
import { readFile as
|
|
2812
|
+
import { resolve as resolve17 } from "node:path";
|
|
2813
|
+
import { readFile as readFile13 } from "node:fs/promises";
|
|
2649
2814
|
|
|
2650
2815
|
// src/analyze.ts
|
|
2651
2816
|
import { createHash as createHash3 } from "node:crypto";
|
|
2652
2817
|
import { relative as relative2, resolve as resolvePath, sep as sep2 } from "node:path";
|
|
2653
|
-
import * as
|
|
2818
|
+
import * as ts4 from "@typescript/typescript6";
|
|
2654
2819
|
|
|
2655
2820
|
// src/program.ts
|
|
2656
2821
|
import { createHash as createHash2 } from "node:crypto";
|
|
@@ -3300,6 +3465,7 @@ var COMPILER_DIAGNOSTIC_CODES = {
|
|
|
3300
3465
|
"missing-token-factory": { code: "SC2009", docsUrl: "https://supacloud.dev/errors/SC2009" },
|
|
3301
3466
|
"provider-type-mismatch": { code: "SC2010", docsUrl: "https://supacloud.dev/errors/SC2010" },
|
|
3302
3467
|
"unsupported-provider-helper": { code: "SC2011", docsUrl: "https://supacloud.dev/errors/SC2011" },
|
|
3468
|
+
"runtime-injection-disallowed": { code: "SC2012", docsUrl: "https://supacloud.dev/errors/SC2012" },
|
|
3303
3469
|
"command-missing-permission": { code: "SC4001", docsUrl: "https://supacloud.dev/errors/SC4001" },
|
|
3304
3470
|
"duplicate-command": { code: "SC4002", docsUrl: "https://supacloud.dev/errors/SC4002" },
|
|
3305
3471
|
"route-command-unresolved": { code: "SC4003", docsUrl: "https://supacloud.dev/errors/SC4003" },
|
|
@@ -3346,6 +3512,20 @@ var COMPILER_DIAGNOSTIC_CODES = {
|
|
|
3346
3512
|
function validateGraph(graph, options = false) {
|
|
3347
3513
|
const strict = typeof options === "boolean" ? options : options.strict ?? false;
|
|
3348
3514
|
const diagnostics = [];
|
|
3515
|
+
for (const module of graph.modules) {
|
|
3516
|
+
for (const owner of [...module.providers, ...module.controllers]) {
|
|
3517
|
+
if (owner.functionalInjects?.length)
|
|
3518
|
+
diagnostics.push({
|
|
3519
|
+
severity: "error",
|
|
3520
|
+
code: "runtime-injection-disallowed",
|
|
3521
|
+
errorCode: "SC2012",
|
|
3522
|
+
docsUrl: "https://supacloud.dev/errors/SC2012",
|
|
3523
|
+
file: owner.file,
|
|
3524
|
+
message: "Property inject() requires runtime token resolution. Compiled applications require constructor injection.",
|
|
3525
|
+
suggestion: "Move injected fields into typed constructor parameters with @Inject(TOKEN) where needed."
|
|
3526
|
+
});
|
|
3527
|
+
}
|
|
3528
|
+
}
|
|
3349
3529
|
let moduleBoundaries;
|
|
3350
3530
|
if (typeof options === "object") {
|
|
3351
3531
|
try {
|
|
@@ -4126,6 +4306,67 @@ function detectOrphanModules(graph) {
|
|
|
4126
4306
|
}
|
|
4127
4307
|
return diagnostics;
|
|
4128
4308
|
}
|
|
4309
|
+
// src/static-di.ts
|
|
4310
|
+
import * as ts3 from "@typescript/typescript6";
|
|
4311
|
+
var runtimeApis = new Set([
|
|
4312
|
+
"inject",
|
|
4313
|
+
"createEnvironmentInjector",
|
|
4314
|
+
"runInInjectionContext",
|
|
4315
|
+
"EnvironmentInjector",
|
|
4316
|
+
"bootstrapBun",
|
|
4317
|
+
"runInScope",
|
|
4318
|
+
"runInRequestContext",
|
|
4319
|
+
"runInJobContext",
|
|
4320
|
+
"runInTransactionContext"
|
|
4321
|
+
]);
|
|
4322
|
+
function scanRuntimeDi(source, file) {
|
|
4323
|
+
const diagnostics = [];
|
|
4324
|
+
const namespaces = new Set;
|
|
4325
|
+
const report = (node) => diagnostics.push({
|
|
4326
|
+
severity: "error",
|
|
4327
|
+
code: "runtime-injection-disallowed",
|
|
4328
|
+
errorCode: "SC2012",
|
|
4329
|
+
docsUrl: "https://supacloud.dev/errors/SC2012",
|
|
4330
|
+
file,
|
|
4331
|
+
line: source.getLineAndCharacterOfPosition(node.getStart()).line + 1,
|
|
4332
|
+
message: "Compiled applications cannot import runtime DI. Use explicit constructors and generated scope factories."
|
|
4333
|
+
});
|
|
4334
|
+
for (const statement of source.statements) {
|
|
4335
|
+
if (!(ts3.isImportDeclaration(statement) || ts3.isExportDeclaration(statement)) || !statement.moduleSpecifier || !ts3.isStringLiteral(statement.moduleSpecifier) || !/^@supacloud\/app(?:\/|$)/.test(statement.moduleSpecifier.text))
|
|
4336
|
+
continue;
|
|
4337
|
+
if (ts3.isImportDeclaration(statement)) {
|
|
4338
|
+
if (statement.importClause?.isTypeOnly)
|
|
4339
|
+
continue;
|
|
4340
|
+
const binding = statement.importClause?.namedBindings;
|
|
4341
|
+
if (binding && ts3.isNamespaceImport(binding))
|
|
4342
|
+
namespaces.add(binding.name.text);
|
|
4343
|
+
if (binding && ts3.isNamedImports(binding))
|
|
4344
|
+
for (const item of binding.elements) {
|
|
4345
|
+
if (!item.isTypeOnly && runtimeApis.has((item.propertyName ?? item.name).text))
|
|
4346
|
+
report(item);
|
|
4347
|
+
}
|
|
4348
|
+
} else if (!statement.isTypeOnly) {
|
|
4349
|
+
if (!statement.exportClause)
|
|
4350
|
+
report(statement);
|
|
4351
|
+
else if (ts3.isNamedExports(statement.exportClause))
|
|
4352
|
+
for (const item of statement.exportClause.elements) {
|
|
4353
|
+
if (!item.isTypeOnly && runtimeApis.has((item.propertyName ?? item.name).text))
|
|
4354
|
+
report(item);
|
|
4355
|
+
}
|
|
4356
|
+
}
|
|
4357
|
+
}
|
|
4358
|
+
const visit = (node) => {
|
|
4359
|
+
if (ts3.isPropertyAccessExpression(node) && ts3.isIdentifier(node.expression) && namespaces.has(node.expression.text) && runtimeApis.has(node.name.text))
|
|
4360
|
+
report(node);
|
|
4361
|
+
if (ts3.isElementAccessExpression(node) && ts3.isIdentifier(node.expression) && namespaces.has(node.expression.text) && (!ts3.isStringLiteral(node.argumentExpression) || runtimeApis.has(node.argumentExpression.text)))
|
|
4362
|
+
report(node);
|
|
4363
|
+
if (ts3.isVariableDeclaration(node) && node.initializer && ts3.isIdentifier(node.initializer) && namespaces.has(node.initializer.text))
|
|
4364
|
+
report(node);
|
|
4365
|
+
ts3.forEachChild(node, visit);
|
|
4366
|
+
};
|
|
4367
|
+
visit(source);
|
|
4368
|
+
return diagnostics;
|
|
4369
|
+
}
|
|
4129
4370
|
|
|
4130
4371
|
// src/analyze.ts
|
|
4131
4372
|
var DEFAULT_INCLUDE = ["**/*.module.ts", "**/*.ts"];
|
|
@@ -4167,26 +4408,26 @@ function lineOf(node) {
|
|
|
4167
4408
|
return sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile)).line + 1;
|
|
4168
4409
|
}
|
|
4169
4410
|
function variableName(decl) {
|
|
4170
|
-
return
|
|
4411
|
+
return ts4.isIdentifier(decl.name) ? decl.name.text : nodeText(decl.name);
|
|
4171
4412
|
}
|
|
4172
4413
|
function propertyName(name) {
|
|
4173
|
-
if (
|
|
4414
|
+
if (ts4.isIdentifier(name) || ts4.isPrivateIdentifier(name))
|
|
4174
4415
|
return name.text;
|
|
4175
|
-
if (
|
|
4416
|
+
if (ts4.isStringLiteral(name) || ts4.isNumericLiteral(name))
|
|
4176
4417
|
return name.text;
|
|
4177
4418
|
return nodeText(name);
|
|
4178
4419
|
}
|
|
4179
4420
|
function parameterName(param) {
|
|
4180
|
-
return
|
|
4421
|
+
return ts4.isIdentifier(param.name) ? param.name.text : nodeText(param.name);
|
|
4181
4422
|
}
|
|
4182
4423
|
function decoratorsOf(node) {
|
|
4183
|
-
return
|
|
4424
|
+
return ts4.canHaveDecorators(node) ? ts4.getDecorators(node) ?? [] : [];
|
|
4184
4425
|
}
|
|
4185
4426
|
function decoratorArguments(dec) {
|
|
4186
|
-
return
|
|
4427
|
+
return ts4.isCallExpression(dec.expression) ? dec.expression.arguments : [];
|
|
4187
4428
|
}
|
|
4188
4429
|
function hasMethod(cls, name) {
|
|
4189
|
-
return cls.members.some((member) => (
|
|
4430
|
+
return cls.members.some((member) => (ts4.isMethodDeclaration(member) || ts4.isGetAccessorDeclaration(member) || ts4.isSetAccessorDeclaration(member)) && member.name !== undefined && propertyName(member.name) === name);
|
|
4190
4431
|
}
|
|
4191
4432
|
function hasDestroyHook(cls) {
|
|
4192
4433
|
return hasMethod(cls, "onDestroy") || hasMethod(cls, "ngOnDestroy");
|
|
@@ -4196,7 +4437,7 @@ function descendantsOfKind(root, predicate) {
|
|
|
4196
4437
|
const visit = (node) => {
|
|
4197
4438
|
if (predicate(node))
|
|
4198
4439
|
result.push(node);
|
|
4199
|
-
|
|
4440
|
+
ts4.forEachChild(node, visit);
|
|
4200
4441
|
};
|
|
4201
4442
|
visit(root);
|
|
4202
4443
|
return result;
|
|
@@ -4205,7 +4446,7 @@ async function analyzeProject(rootDir, include, cache, changedPaths) {
|
|
|
4205
4446
|
const session = cache?.programSession ?? createIncrementalProgramSession(rootDir);
|
|
4206
4447
|
if (cache)
|
|
4207
4448
|
cache.programSession = session;
|
|
4208
|
-
const rootNames =
|
|
4449
|
+
const rootNames = ts4.sys.readDirectory(rootDir, [".ts", ".tsx"], ["node_modules", "dist"], include ?? DEFAULT_INCLUDE);
|
|
4209
4450
|
const update = session.update(rootNames, changedPaths);
|
|
4210
4451
|
const program = update.program;
|
|
4211
4452
|
const checker = program.getTypeChecker();
|
|
@@ -4229,13 +4470,16 @@ async function analyzeProject(rootDir, include, cache, changedPaths) {
|
|
|
4229
4470
|
nativeTraitFiles.set(trait.file, kinds);
|
|
4230
4471
|
}
|
|
4231
4472
|
for (const sf of sourceFiles) {
|
|
4473
|
+
if (!/\.(?:test|spec)\.[cm]?tsx?$/.test(sf.fileName)) {
|
|
4474
|
+
ctx.diagnostics.push(...scanRuntimeDi(sf, sourcePath(rootDir, sf.fileName)));
|
|
4475
|
+
}
|
|
4232
4476
|
indexFile(sf, ctx);
|
|
4233
4477
|
}
|
|
4234
4478
|
const candidates = [];
|
|
4235
4479
|
for (const sf of sourceFiles) {
|
|
4236
4480
|
const traits = nativeTraitFiles.get(sf.fileName);
|
|
4237
4481
|
if (!cache || traits?.has("module")) {
|
|
4238
|
-
for (const cls of sf.statements.filter(
|
|
4482
|
+
for (const cls of sf.statements.filter(ts4.isClassDeclaration)) {
|
|
4239
4483
|
const moduleDec = findDecorator(cls, "Module");
|
|
4240
4484
|
if (!moduleDec)
|
|
4241
4485
|
continue;
|
|
@@ -4252,14 +4496,14 @@ async function analyzeProject(rootDir, include, cache, changedPaths) {
|
|
|
4252
4496
|
}
|
|
4253
4497
|
}
|
|
4254
4498
|
if (!cache || traits?.has("defineModule") || traits?.has("defineFeatureSlice")) {
|
|
4255
|
-
for (const call of descendantsOfKind(sf,
|
|
4499
|
+
for (const call of descendantsOfKind(sf, ts4.isCallExpression)) {
|
|
4256
4500
|
if (!["defineModule", "defineFeatureSlice"].includes(nodeText(call.expression)))
|
|
4257
4501
|
continue;
|
|
4258
4502
|
const parent = call.parent;
|
|
4259
|
-
if (!parent || !
|
|
4503
|
+
if (!parent || !ts4.isVariableDeclaration(parent))
|
|
4260
4504
|
continue;
|
|
4261
4505
|
const arg = call.arguments[0];
|
|
4262
|
-
if (!arg || !
|
|
4506
|
+
if (!arg || !ts4.isObjectLiteralExpression(arg))
|
|
4263
4507
|
continue;
|
|
4264
4508
|
candidates.push({
|
|
4265
4509
|
node: parent,
|
|
@@ -4403,7 +4647,7 @@ async function analyzeProject(rootDir, include, cache, changedPaths) {
|
|
|
4403
4647
|
const controllerDec = findDecorator(classInfo.decl, "Controller");
|
|
4404
4648
|
if (controllerDec) {
|
|
4405
4649
|
const arg = decoratorArguments(controllerDec)[0];
|
|
4406
|
-
const isStandalone = arg &&
|
|
4650
|
+
const isStandalone = arg && ts4.isObjectLiteralExpression(arg) && booleanProp(arg, "standalone");
|
|
4407
4651
|
if (isStandalone) {
|
|
4408
4652
|
const ctrl = parseController(classInfo.decl, ctx);
|
|
4409
4653
|
if (ctrl)
|
|
@@ -4528,16 +4772,16 @@ function collectModuleSourceClosure(module, ctx) {
|
|
|
4528
4772
|
ownedFiles.add(relativeFile);
|
|
4529
4773
|
for (const statement of sourceFile.statements) {
|
|
4530
4774
|
let moduleName;
|
|
4531
|
-
if (
|
|
4775
|
+
if (ts4.isImportDeclaration(statement) && ts4.isStringLiteral(statement.moduleSpecifier)) {
|
|
4532
4776
|
moduleName = statement.moduleSpecifier.text;
|
|
4533
|
-
} else if (
|
|
4777
|
+
} else if (ts4.isExportDeclaration(statement) && statement.moduleSpecifier && ts4.isStringLiteral(statement.moduleSpecifier)) {
|
|
4534
4778
|
moduleName = statement.moduleSpecifier.text;
|
|
4535
|
-
} else if (
|
|
4779
|
+
} else if (ts4.isImportEqualsDeclaration(statement) && ts4.isExternalModuleReference(statement.moduleReference) && ts4.isStringLiteral(statement.moduleReference.expression)) {
|
|
4536
4780
|
moduleName = statement.moduleReference.expression.text;
|
|
4537
4781
|
}
|
|
4538
4782
|
if (!moduleName || moduleName.startsWith("node:"))
|
|
4539
4783
|
continue;
|
|
4540
|
-
const resolved =
|
|
4784
|
+
const resolved = ts4.resolveModuleName(moduleName, sourceFile.fileName, ctx.program.getCompilerOptions(), ts4.sys).resolvedModule?.resolvedFileName;
|
|
4541
4785
|
if (resolved && isProjectSourcePath(resolved, ctx.rootDir) && !enqueued.has(resolved)) {
|
|
4542
4786
|
enqueued.add(resolved);
|
|
4543
4787
|
queue.push(resolved);
|
|
@@ -4559,15 +4803,15 @@ function isProjectSourceFile(sourceFile, rootDir) {
|
|
|
4559
4803
|
return isProjectSourcePath(sourceFile.fileName, rootDir) && /\.(tsx?|mts|cts)$/.test(sourceFile.fileName);
|
|
4560
4804
|
}
|
|
4561
4805
|
function indexFile(sf, ctx) {
|
|
4562
|
-
for (const cls of sf.statements.filter(
|
|
4806
|
+
for (const cls of sf.statements.filter(ts4.isClassDeclaration)) {
|
|
4563
4807
|
const name = cls.name?.text;
|
|
4564
4808
|
if (name && !ctx.classesByName.has(name)) {
|
|
4565
4809
|
ctx.classesByName.set(name, { name, decl: cls, file: sf.fileName });
|
|
4566
4810
|
}
|
|
4567
4811
|
}
|
|
4568
|
-
for (const statement of sf.statements.filter(
|
|
4812
|
+
for (const statement of sf.statements.filter(ts4.isVariableStatement)) {
|
|
4569
4813
|
for (const decl of statement.declarationList.declarations) {
|
|
4570
|
-
if (
|
|
4814
|
+
if (ts4.isIdentifier(decl.name) && !ctx.variablesByName.has(decl.name.text)) {
|
|
4571
4815
|
ctx.variablesByName.set(decl.name.text, decl);
|
|
4572
4816
|
}
|
|
4573
4817
|
const info = parseTokenVariable(decl, sf.fileName);
|
|
@@ -4579,16 +4823,16 @@ function indexFile(sf, ctx) {
|
|
|
4579
4823
|
}
|
|
4580
4824
|
function parseTokenVariable(decl, file) {
|
|
4581
4825
|
const init = decl.initializer;
|
|
4582
|
-
if (!init || !
|
|
4826
|
+
if (!init || !ts4.isNewExpression(init))
|
|
4583
4827
|
return;
|
|
4584
4828
|
if (nodeText(init.expression) !== "InjectionToken")
|
|
4585
4829
|
return;
|
|
4586
4830
|
const [nameArg, optionsArg] = init.arguments ?? [];
|
|
4587
4831
|
const info = { name: variableName(decl), file, line: lineOf(decl) };
|
|
4588
|
-
if (nameArg &&
|
|
4832
|
+
if (nameArg && ts4.isStringLiteral(nameArg)) {
|
|
4589
4833
|
info.stringName = nameArg.text;
|
|
4590
4834
|
}
|
|
4591
|
-
if (optionsArg &&
|
|
4835
|
+
if (optionsArg && ts4.isObjectLiteralExpression(optionsArg)) {
|
|
4592
4836
|
const scope = stringLiteralProp(optionsArg, "scope");
|
|
4593
4837
|
if (scope && isScope(scope)) {
|
|
4594
4838
|
info.scope = scope;
|
|
@@ -4608,22 +4852,22 @@ function parseModule(candidate, nameByNode, ctx) {
|
|
|
4608
4852
|
const { options, className, file, line } = candidate;
|
|
4609
4853
|
const name = nameByNode.get(candidate.node) ?? className;
|
|
4610
4854
|
const featureSpec = parseFeatureSpec(getProp(options, "spec"), ctx);
|
|
4611
|
-
const tags = arrayProp(options, "tags").map((el) =>
|
|
4855
|
+
const tags = arrayProp(options, "tags").map((el) => ts4.isStringLiteral(el) ? el.text : nodeText(el).replace(/['"]/g, "")).filter(Boolean);
|
|
4612
4856
|
const aspects = parseAspectRefs(getProp(options, "aspects"), ctx, `module ${name}`);
|
|
4613
4857
|
const imports = arrayProp(options, "imports").map((el) => {
|
|
4614
4858
|
const unwrapped = unwrapForwardRef(el);
|
|
4615
|
-
const decl =
|
|
4859
|
+
const decl = ts4.isIdentifier(unwrapped) ? resolveDeclaration(unwrapped, ctx)[0] : undefined;
|
|
4616
4860
|
if (decl) {
|
|
4617
4861
|
const known = nameByNode.get(decl);
|
|
4618
4862
|
if (known)
|
|
4619
4863
|
return known;
|
|
4620
|
-
if (
|
|
4864
|
+
if (ts4.isClassDeclaration(decl)) {
|
|
4621
4865
|
const dec = findDecorator(decl, "Module");
|
|
4622
4866
|
const decOptions = dec && decoratorObjectArg(dec);
|
|
4623
4867
|
const decName = decOptions && stringLiteralProp(decOptions, "name");
|
|
4624
4868
|
return decName ?? decl.name?.text ?? nodeText(el);
|
|
4625
4869
|
}
|
|
4626
|
-
if (
|
|
4870
|
+
if (ts4.isVariableDeclaration(decl))
|
|
4627
4871
|
return variableName(decl);
|
|
4628
4872
|
}
|
|
4629
4873
|
return nodeText(el);
|
|
@@ -4637,7 +4881,7 @@ function parseModule(candidate, nameByNode, ctx) {
|
|
|
4637
4881
|
providers.push(...parsedProviders);
|
|
4638
4882
|
continue;
|
|
4639
4883
|
}
|
|
4640
|
-
if (
|
|
4884
|
+
if (ts4.isCallExpression(el)) {
|
|
4641
4885
|
const helper = nodeText(el.expression).split(".").pop() ?? nodeText(el.expression);
|
|
4642
4886
|
warn(ctx, "unsupported-provider-helper", `无法静态展开 provider helper '${helper}';请改用显式 Provider 或实现编译器支持的 helper`, sourcePath(ctx.rootDir, el.getSourceFile().fileName), lineOf(el));
|
|
4643
4887
|
continue;
|
|
@@ -4647,10 +4891,10 @@ function parseModule(candidate, nameByNode, ctx) {
|
|
|
4647
4891
|
providers.push(provider);
|
|
4648
4892
|
}
|
|
4649
4893
|
for (const el of arrayProp(options, "jobs")) {
|
|
4650
|
-
if (!
|
|
4894
|
+
if (!ts4.isIdentifier(el))
|
|
4651
4895
|
continue;
|
|
4652
4896
|
const decl = resolveDeclaration(el, ctx)[0];
|
|
4653
|
-
if (!decl || !
|
|
4897
|
+
if (!decl || !ts4.isClassDeclaration(decl))
|
|
4654
4898
|
continue;
|
|
4655
4899
|
const className = decl.name?.text ?? el.text;
|
|
4656
4900
|
const registeredProvider = providers.find((provider) => provider.token === className || provider.useClass === className);
|
|
@@ -4687,18 +4931,18 @@ function parseModule(candidate, nameByNode, ctx) {
|
|
|
4687
4931
|
const handlerClasses = [];
|
|
4688
4932
|
const seenHandlers = new Set;
|
|
4689
4933
|
const collectHandler = (expr) => {
|
|
4690
|
-
if (!
|
|
4934
|
+
if (!ts4.isIdentifier(expr))
|
|
4691
4935
|
return;
|
|
4692
4936
|
const decl = resolveDeclaration(expr, ctx)[0];
|
|
4693
|
-
if (decl &&
|
|
4937
|
+
if (decl && ts4.isClassDeclaration(decl) && !seenHandlers.has(decl.name?.text ?? "")) {
|
|
4694
4938
|
seenHandlers.add(decl.name?.text ?? "");
|
|
4695
4939
|
handlerClasses.push(decl);
|
|
4696
4940
|
}
|
|
4697
4941
|
};
|
|
4698
4942
|
for (const el of arrayProp(options, "providers")) {
|
|
4699
|
-
if (
|
|
4943
|
+
if (ts4.isIdentifier(el))
|
|
4700
4944
|
collectHandler(el);
|
|
4701
|
-
if (
|
|
4945
|
+
if (ts4.isObjectLiteralExpression(el)) {
|
|
4702
4946
|
const useClass = getProp(el, "useClass");
|
|
4703
4947
|
if (useClass)
|
|
4704
4948
|
collectHandler(useClass);
|
|
@@ -4798,17 +5042,17 @@ function parseFeatureSpec(input, ctx, seen = new Set) {
|
|
|
4798
5042
|
if (seen.has(input))
|
|
4799
5043
|
return;
|
|
4800
5044
|
seen.add(input);
|
|
4801
|
-
if (
|
|
4802
|
-
const local = input.getSourceFile().statements.flatMap((statement) =>
|
|
5045
|
+
if (ts4.isIdentifier(input)) {
|
|
5046
|
+
const local = input.getSourceFile().statements.flatMap((statement) => ts4.isVariableStatement(statement) ? [...statement.declarationList.declarations] : []);
|
|
4803
5047
|
const resolved = resolveDeclaration(input, ctx)[0];
|
|
4804
|
-
const decl = (resolved &&
|
|
4805
|
-
if (decl &&
|
|
5048
|
+
const decl = (resolved && ts4.isVariableDeclaration(resolved) ? resolved : undefined) ?? ctx.variablesByName.get(input.text) ?? local.find((candidate) => ts4.isIdentifier(candidate.name) && candidate.name.text === input.text) ?? descendantsOfKind(input.getSourceFile(), ts4.isVariableDeclaration).find((candidate) => ts4.isIdentifier(candidate.name) && candidate.name.text === input.text);
|
|
5049
|
+
if (decl && ts4.isVariableDeclaration(decl))
|
|
4806
5050
|
return parseFeatureSpec(decl.initializer, ctx, seen);
|
|
4807
5051
|
}
|
|
4808
|
-
if (
|
|
5052
|
+
if (ts4.isCallExpression(input) && nodeText(input.expression) === "defineFeatureSpec") {
|
|
4809
5053
|
return parseFeatureSpec(input.arguments[0], ctx, seen);
|
|
4810
5054
|
}
|
|
4811
|
-
if (
|
|
5055
|
+
if (ts4.isAsExpression(input) || ts4.isSatisfiesExpression(input) || ts4.isParenthesizedExpression(input)) {
|
|
4812
5056
|
return parseFeatureSpec(input.expression, ctx, seen);
|
|
4813
5057
|
}
|
|
4814
5058
|
const invalid = () => {
|
|
@@ -4821,28 +5065,28 @@ function parseFeatureSpec(input, ctx, seen = new Set) {
|
|
|
4821
5065
|
});
|
|
4822
5066
|
return;
|
|
4823
5067
|
};
|
|
4824
|
-
if (!
|
|
5068
|
+
if (!ts4.isObjectLiteralExpression(input))
|
|
4825
5069
|
return invalid();
|
|
4826
5070
|
const name = stringLiteralProp(input, "name");
|
|
4827
5071
|
const statesExpr = getProp(input, "states");
|
|
4828
5072
|
const transitionObject = getProp(input, "transitions");
|
|
4829
|
-
if (!name || !statesExpr || !
|
|
5073
|
+
if (!name || !statesExpr || !ts4.isArrayLiteralExpression(statesExpr) || statesExpr.elements.some((state) => !ts4.isStringLiteral(state)) || !transitionObject || !ts4.isObjectLiteralExpression(transitionObject) || input.properties.some((property) => !ts4.isPropertyAssignment(property))) {
|
|
4830
5074
|
return invalid();
|
|
4831
5075
|
}
|
|
4832
5076
|
const states = [];
|
|
4833
5077
|
for (const state of statesExpr.elements) {
|
|
4834
|
-
if (!
|
|
5078
|
+
if (!ts4.isStringLiteral(state))
|
|
4835
5079
|
return invalid();
|
|
4836
5080
|
states.push(state.text);
|
|
4837
5081
|
}
|
|
4838
5082
|
const transitions = [];
|
|
4839
5083
|
for (const property of transitionObject.properties) {
|
|
4840
|
-
if (!
|
|
5084
|
+
if (!ts4.isPropertyAssignment(property) || ts4.isComputedPropertyName(property.name) || !ts4.isObjectLiteralExpression(property.initializer))
|
|
4841
5085
|
return invalid();
|
|
4842
5086
|
const options = property.initializer;
|
|
4843
5087
|
const from = stringLiteralProp(options, "from");
|
|
4844
5088
|
const to = stringLiteralProp(options, "to");
|
|
4845
|
-
if (!from || !to || options.properties.some((prop) => !
|
|
5089
|
+
if (!from || !to || options.properties.some((prop) => !ts4.isPropertyAssignment(prop)) || ["permission", "command", "route", "audit"].some((key) => getProp(options, key) && !stringLiteralProp(options, key)) || ["transaction", "idempotency"].some((key) => getProp(options, key) && !commandModeProp(options, key))) {
|
|
4846
5090
|
return invalid();
|
|
4847
5091
|
}
|
|
4848
5092
|
const permission = stringLiteralProp(options, "permission");
|
|
@@ -4869,21 +5113,21 @@ function resolveStaticObjectLiteral(input, ctx, seen = new Set) {
|
|
|
4869
5113
|
if (!input || seen.has(input))
|
|
4870
5114
|
return;
|
|
4871
5115
|
seen.add(input);
|
|
4872
|
-
if (
|
|
5116
|
+
if (ts4.isAsExpression(input) || ts4.isSatisfiesExpression(input) || ts4.isParenthesizedExpression(input)) {
|
|
4873
5117
|
return resolveStaticObjectLiteral(input.expression, ctx, seen);
|
|
4874
5118
|
}
|
|
4875
|
-
if (
|
|
4876
|
-
const declaration = resolveDeclaration(input, ctx).find(
|
|
5119
|
+
if (ts4.isIdentifier(input)) {
|
|
5120
|
+
const declaration = resolveDeclaration(input, ctx).find(ts4.isVariableDeclaration);
|
|
4877
5121
|
return declaration?.initializer ? resolveStaticObjectLiteral(declaration.initializer, ctx, seen) : undefined;
|
|
4878
5122
|
}
|
|
4879
|
-
if (
|
|
5123
|
+
if (ts4.isCallExpression(input)) {
|
|
4880
5124
|
const expressionName = nodeText(input.expression);
|
|
4881
5125
|
if (expressionName === "defineRouteContract" || expressionName.endsWith(".defineRouteContract")) {
|
|
4882
5126
|
return resolveStaticObjectLiteral(input.arguments[0], ctx, seen);
|
|
4883
5127
|
}
|
|
4884
5128
|
return;
|
|
4885
5129
|
}
|
|
4886
|
-
return
|
|
5130
|
+
return ts4.isObjectLiteralExpression(input) ? input : undefined;
|
|
4887
5131
|
}
|
|
4888
5132
|
function commandModeProp(object, name) {
|
|
4889
5133
|
const value = stringLiteralProp(object, name);
|
|
@@ -4918,9 +5162,9 @@ function parseProvider(el, exportsSet, ctx) {
|
|
|
4918
5162
|
const file = sourcePath(ctx.rootDir, el.getSourceFile().fileName);
|
|
4919
5163
|
const line = lineOf(el);
|
|
4920
5164
|
const unwrappedEl = unwrapForwardRef(el);
|
|
4921
|
-
if (
|
|
5165
|
+
if (ts4.isIdentifier(unwrappedEl)) {
|
|
4922
5166
|
const decl = resolveDeclaration(unwrappedEl, ctx)[0];
|
|
4923
|
-
const cls = decl &&
|
|
5167
|
+
const cls = decl && ts4.isClassDeclaration(decl) ? decl : undefined;
|
|
4924
5168
|
const className = cls?.name?.text ?? unwrappedEl.text;
|
|
4925
5169
|
const { deps, optionalDeps, selfDeps, skipSelfDeps, hostDeps, functionalInjects, missing } = cls ? classDeps(cls, ctx) : { deps: [], optionalDeps: [], selfDeps: [], skipSelfDeps: [], hostDeps: [], functionalInjects: [], missing: false };
|
|
4926
5170
|
const injectable = cls ? parseInjectableOptions(cls, ctx) : undefined;
|
|
@@ -4947,7 +5191,7 @@ function parseProvider(el, exportsSet, ctx) {
|
|
|
4947
5191
|
...cls ? { importPath: modulePath(ctx.rootDir, cls.getSourceFile().fileName) } : {}
|
|
4948
5192
|
};
|
|
4949
5193
|
}
|
|
4950
|
-
if (!
|
|
5194
|
+
if (!ts4.isObjectLiteralExpression(el))
|
|
4951
5195
|
return;
|
|
4952
5196
|
const provideExpr = getProp(el, "provide");
|
|
4953
5197
|
if (!provideExpr)
|
|
@@ -4962,8 +5206,8 @@ function parseProvider(el, exportsSet, ctx) {
|
|
|
4962
5206
|
const useExistingExpr = getProp(el, "useExisting");
|
|
4963
5207
|
if (useClassExpr) {
|
|
4964
5208
|
const unwrappedClass = unwrapForwardRef(useClassExpr);
|
|
4965
|
-
const decl =
|
|
4966
|
-
const cls = decl &&
|
|
5209
|
+
const decl = ts4.isIdentifier(unwrappedClass) ? resolveDeclaration(unwrappedClass, ctx)[0] : undefined;
|
|
5210
|
+
const cls = decl && ts4.isClassDeclaration(decl) ? decl : undefined;
|
|
4967
5211
|
const useClass = cls?.name?.text ?? nodeText(unwrappedClass);
|
|
4968
5212
|
let deps = explicitDeps;
|
|
4969
5213
|
let optionalDeps = [];
|
|
@@ -5019,7 +5263,7 @@ function parseProvider(el, exportsSet, ctx) {
|
|
|
5019
5263
|
}
|
|
5020
5264
|
if (useValueExpr) {
|
|
5021
5265
|
validateProviderCompatibility(provideExpr, useValueExpr, "value", token, ctx, file, line);
|
|
5022
|
-
const importPath =
|
|
5266
|
+
const importPath = ts4.isIdentifier(useValueExpr) ? importPathOf(useValueExpr, ctx) : undefined;
|
|
5023
5267
|
return {
|
|
5024
5268
|
token,
|
|
5025
5269
|
tokenKind,
|
|
@@ -5035,12 +5279,12 @@ function parseProvider(el, exportsSet, ctx) {
|
|
|
5035
5279
|
};
|
|
5036
5280
|
}
|
|
5037
5281
|
if (useFactoryExpr) {
|
|
5038
|
-
const factoryName =
|
|
5282
|
+
const factoryName = ts4.isIdentifier(useFactoryExpr) ? (() => {
|
|
5039
5283
|
const decl = resolveDeclaration(useFactoryExpr, ctx)[0];
|
|
5040
|
-
return decl && (
|
|
5284
|
+
return decl && (ts4.isFunctionDeclaration(decl) || ts4.isVariableDeclaration(decl)) ? (ts4.isFunctionDeclaration(decl) ? decl.name?.text : variableName(decl)) ?? useFactoryExpr.text : useFactoryExpr.text;
|
|
5041
5285
|
})() : nodeText(useFactoryExpr);
|
|
5042
5286
|
validateProviderCompatibility(provideExpr, useFactoryExpr, "factory", token, ctx, file, line);
|
|
5043
|
-
const importPath =
|
|
5287
|
+
const importPath = ts4.isIdentifier(useFactoryExpr) ? importPathOf(useFactoryExpr, ctx) : undefined;
|
|
5044
5288
|
return {
|
|
5045
5289
|
token,
|
|
5046
5290
|
tokenKind,
|
|
@@ -5076,20 +5320,20 @@ function parseProvider(el, exportsSet, ctx) {
|
|
|
5076
5320
|
function expandProviderExpressions(expressions, ctx, seen = new Set) {
|
|
5077
5321
|
const result = [];
|
|
5078
5322
|
for (const expression of expressions) {
|
|
5079
|
-
if (
|
|
5323
|
+
if (ts4.isSpreadElement(expression)) {
|
|
5080
5324
|
result.push(...expandProviderExpressions([expression.expression], ctx, seen));
|
|
5081
5325
|
continue;
|
|
5082
5326
|
}
|
|
5083
|
-
if (
|
|
5327
|
+
if (ts4.isIdentifier(expression)) {
|
|
5084
5328
|
const declaration = resolveDeclaration(expression, ctx)[0];
|
|
5085
|
-
if (declaration &&
|
|
5329
|
+
if (declaration && ts4.isVariableDeclaration(declaration) && declaration.initializer) {
|
|
5086
5330
|
const key = `${declaration.getSourceFile().fileName}:${declaration.pos}`;
|
|
5087
5331
|
if (seen.has(key))
|
|
5088
5332
|
continue;
|
|
5089
5333
|
const initializer = declaration.initializer;
|
|
5090
|
-
if (
|
|
5334
|
+
if (ts4.isCallExpression(initializer) && isProviderHelper(initializer, "makeEnvironmentProviders")) {
|
|
5091
5335
|
const nested = initializer.arguments[0];
|
|
5092
|
-
if (nested &&
|
|
5336
|
+
if (nested && ts4.isArrayLiteralExpression(nested)) {
|
|
5093
5337
|
seen.add(key);
|
|
5094
5338
|
result.push(...expandProviderExpressions([...nested.elements], ctx, seen));
|
|
5095
5339
|
seen.delete(key);
|
|
@@ -5098,9 +5342,9 @@ function expandProviderExpressions(expressions, ctx, seen = new Set) {
|
|
|
5098
5342
|
}
|
|
5099
5343
|
}
|
|
5100
5344
|
}
|
|
5101
|
-
if (
|
|
5345
|
+
if (ts4.isCallExpression(expression) && isProviderHelper(expression, "makeEnvironmentProviders")) {
|
|
5102
5346
|
const nested = expression.arguments[0];
|
|
5103
|
-
if (nested &&
|
|
5347
|
+
if (nested && ts4.isArrayLiteralExpression(nested)) {
|
|
5104
5348
|
result.push(...expandProviderExpressions([...nested.elements], ctx, seen));
|
|
5105
5349
|
continue;
|
|
5106
5350
|
}
|
|
@@ -5113,7 +5357,7 @@ function isProviderHelper(expression, name) {
|
|
|
5113
5357
|
return nodeText(expression.expression).split(".").pop() === name;
|
|
5114
5358
|
}
|
|
5115
5359
|
function parseFunctionalProvider(expression, exportsSet, ctx) {
|
|
5116
|
-
if (!
|
|
5360
|
+
if (!ts4.isCallExpression(expression))
|
|
5117
5361
|
return;
|
|
5118
5362
|
const helper = nodeText(expression.expression).split(".").pop();
|
|
5119
5363
|
const args = expression.arguments;
|
|
@@ -5126,7 +5370,7 @@ function parseFunctionalProvider(expression, exportsSet, ctx) {
|
|
|
5126
5370
|
return [];
|
|
5127
5371
|
const { name: token, kind: tokenKind } = tokenNameOf(tokenExpr, ctx);
|
|
5128
5372
|
validateProviderCompatibility(tokenExpr, valueExpr, "value", token, ctx, file, line);
|
|
5129
|
-
const importPath =
|
|
5373
|
+
const importPath = ts4.isIdentifier(valueExpr) ? importPathOf(valueExpr, ctx) : undefined;
|
|
5130
5374
|
return [{
|
|
5131
5375
|
token,
|
|
5132
5376
|
tokenKind,
|
|
@@ -5145,7 +5389,7 @@ function parseFunctionalProvider(expression, exportsSet, ctx) {
|
|
|
5145
5389
|
if (!initializer)
|
|
5146
5390
|
return [];
|
|
5147
5391
|
const token = helper === "provideAppInitializer" ? "APP_INITIALIZER" : "ENVIRONMENT_INITIALIZER";
|
|
5148
|
-
const importPath =
|
|
5392
|
+
const importPath = ts4.isIdentifier(initializer) ? importPathOf(initializer, ctx) : undefined;
|
|
5149
5393
|
return [{
|
|
5150
5394
|
token,
|
|
5151
5395
|
tokenKind: "injection-token",
|
|
@@ -5164,7 +5408,7 @@ function parseFunctionalProvider(expression, exportsSet, ctx) {
|
|
|
5164
5408
|
const providers = [];
|
|
5165
5409
|
const routes = args[0];
|
|
5166
5410
|
if (routes) {
|
|
5167
|
-
const importPath =
|
|
5411
|
+
const importPath = ts4.isIdentifier(routes) ? importPathOf(routes, ctx) : undefined;
|
|
5168
5412
|
providers.push({
|
|
5169
5413
|
token: "ROUTE_CONFIG",
|
|
5170
5414
|
tokenKind: "injection-token",
|
|
@@ -5179,7 +5423,7 @@ function parseFunctionalProvider(expression, exportsSet, ctx) {
|
|
|
5179
5423
|
});
|
|
5180
5424
|
}
|
|
5181
5425
|
for (const feature of args.slice(1)) {
|
|
5182
|
-
if (!
|
|
5426
|
+
if (!ts4.isCallExpression(feature))
|
|
5183
5427
|
continue;
|
|
5184
5428
|
const featureName = nodeText(feature.expression).split(".").pop();
|
|
5185
5429
|
if (featureName === "withRouterConfig" && feature.arguments[0]) {
|
|
@@ -5196,8 +5440,8 @@ function parseFunctionalProvider(expression, exportsSet, ctx) {
|
|
|
5196
5440
|
});
|
|
5197
5441
|
} else if (featureName === "withTitleStrategy" && feature.arguments[0]) {
|
|
5198
5442
|
const strategy = feature.arguments[0];
|
|
5199
|
-
const isClass =
|
|
5200
|
-
const importPath =
|
|
5443
|
+
const isClass = ts4.isIdentifier(strategy) && Boolean(resolveDeclaration(strategy, ctx).find((declaration) => ts4.isClassDeclaration(declaration)));
|
|
5444
|
+
const importPath = ts4.isIdentifier(strategy) ? importPathOf(strategy, ctx) : undefined;
|
|
5201
5445
|
providers.push({
|
|
5202
5446
|
token: "TITLE_STRATEGY",
|
|
5203
5447
|
tokenKind: "injection-token",
|
|
@@ -5229,14 +5473,14 @@ function parseFunctionalProvider(expression, exportsSet, ctx) {
|
|
|
5229
5473
|
importModule: "@supacloud/app"
|
|
5230
5474
|
}];
|
|
5231
5475
|
for (const feature of args) {
|
|
5232
|
-
if (!
|
|
5476
|
+
if (!ts4.isCallExpression(feature))
|
|
5233
5477
|
continue;
|
|
5234
5478
|
const featureName = nodeText(feature.expression).split(".").pop();
|
|
5235
5479
|
if (featureName === "withInterceptors") {
|
|
5236
5480
|
for (const interceptorArg of feature.arguments) {
|
|
5237
|
-
const values =
|
|
5481
|
+
const values = ts4.isArrayLiteralExpression(interceptorArg) ? [...interceptorArg.elements] : [interceptorArg];
|
|
5238
5482
|
for (const value of values) {
|
|
5239
|
-
const importPath =
|
|
5483
|
+
const importPath = ts4.isIdentifier(value) ? importPathOf(value, ctx) : undefined;
|
|
5240
5484
|
providers.push({
|
|
5241
5485
|
token: "HTTP_INTERCEPTORS",
|
|
5242
5486
|
tokenKind: "injection-token",
|
|
@@ -5283,9 +5527,9 @@ function providerTokenValueType(expr, ctx) {
|
|
|
5283
5527
|
const typeArguments = typeArgumentsOf(type, ctx);
|
|
5284
5528
|
if (typeArguments.length > 0)
|
|
5285
5529
|
return typeArguments[0];
|
|
5286
|
-
if (
|
|
5530
|
+
if (ts4.isIdentifier(expr)) {
|
|
5287
5531
|
const declaration = resolveDeclaration(expr, ctx)[0];
|
|
5288
|
-
if (declaration &&
|
|
5532
|
+
if (declaration && ts4.isClassDeclaration(declaration)) {
|
|
5289
5533
|
return declaredClassType(declaration, ctx);
|
|
5290
5534
|
}
|
|
5291
5535
|
}
|
|
@@ -5293,9 +5537,9 @@ function providerTokenValueType(expr, ctx) {
|
|
|
5293
5537
|
}
|
|
5294
5538
|
function providerImplementationType(expr, kind, ctx) {
|
|
5295
5539
|
if (kind === "class" || kind === "existing") {
|
|
5296
|
-
if (
|
|
5540
|
+
if (ts4.isIdentifier(expr)) {
|
|
5297
5541
|
const declaration = resolveDeclaration(expr, ctx)[0];
|
|
5298
|
-
if (declaration &&
|
|
5542
|
+
if (declaration && ts4.isClassDeclaration(declaration)) {
|
|
5299
5543
|
return declaredClassType(declaration, ctx);
|
|
5300
5544
|
}
|
|
5301
5545
|
}
|
|
@@ -5305,7 +5549,7 @@ function providerImplementationType(expr, kind, ctx) {
|
|
|
5305
5549
|
}
|
|
5306
5550
|
if (kind === "factory") {
|
|
5307
5551
|
const type = ctx.checker.getTypeAtLocation(expr);
|
|
5308
|
-
const signature = ctx.checker.getSignaturesOfType(type,
|
|
5552
|
+
const signature = ctx.checker.getSignaturesOfType(type, ts4.SignatureKind.Call)[0];
|
|
5309
5553
|
return signature?.getReturnType();
|
|
5310
5554
|
}
|
|
5311
5555
|
return ctx.checker.getTypeAtLocation(expr);
|
|
@@ -5324,16 +5568,16 @@ function isTypeReference(type) {
|
|
|
5324
5568
|
return "target" in type;
|
|
5325
5569
|
}
|
|
5326
5570
|
function isUnknownOrAny(type) {
|
|
5327
|
-
return (type.flags & (
|
|
5571
|
+
return (type.flags & (ts4.TypeFlags.Any | ts4.TypeFlags.Unknown)) !== 0;
|
|
5328
5572
|
}
|
|
5329
5573
|
function parseController(input, ctx) {
|
|
5330
5574
|
let decl;
|
|
5331
|
-
if (
|
|
5575
|
+
if (ts4.isClassDeclaration(input)) {
|
|
5332
5576
|
decl = input;
|
|
5333
5577
|
} else {
|
|
5334
5578
|
const unwrapped = unwrapForwardRef(input);
|
|
5335
|
-
const resolved =
|
|
5336
|
-
if (resolved &&
|
|
5579
|
+
const resolved = ts4.isIdentifier(unwrapped) ? resolveDeclaration(unwrapped, ctx)[0] : undefined;
|
|
5580
|
+
if (resolved && ts4.isClassDeclaration(resolved)) {
|
|
5337
5581
|
decl = resolved;
|
|
5338
5582
|
}
|
|
5339
5583
|
}
|
|
@@ -5346,9 +5590,9 @@ function parseController(input, ctx) {
|
|
|
5346
5590
|
let standalone;
|
|
5347
5591
|
const pathArg = decoratorArguments(controllerDec)[0];
|
|
5348
5592
|
if (pathArg) {
|
|
5349
|
-
if (
|
|
5593
|
+
if (ts4.isStringLiteral(pathArg)) {
|
|
5350
5594
|
path = pathArg.text;
|
|
5351
|
-
} else if (
|
|
5595
|
+
} else if (ts4.isObjectLiteralExpression(pathArg)) {
|
|
5352
5596
|
const p = stringLiteralProp(pathArg, "path");
|
|
5353
5597
|
if (p)
|
|
5354
5598
|
path = p;
|
|
@@ -5371,7 +5615,7 @@ function parseController(input, ctx) {
|
|
|
5371
5615
|
}
|
|
5372
5616
|
}
|
|
5373
5617
|
}
|
|
5374
|
-
for (const method of decl.members.filter(
|
|
5618
|
+
for (const method of decl.members.filter(ts4.isMethodDeclaration)) {
|
|
5375
5619
|
for (const dec of decoratorsOf(method)) {
|
|
5376
5620
|
const name = decoratorName2(dec);
|
|
5377
5621
|
const httpMethod = name ? ROUTE_DECORATORS[name] : undefined;
|
|
@@ -5379,7 +5623,7 @@ function parseController(input, ctx) {
|
|
|
5379
5623
|
continue;
|
|
5380
5624
|
const args = decoratorArguments(dec);
|
|
5381
5625
|
const pathArg = args[0];
|
|
5382
|
-
const routePath = pathArg &&
|
|
5626
|
+
const routePath = pathArg && ts4.isStringLiteral(pathArg) ? pathArg.text : "/";
|
|
5383
5627
|
const route = {
|
|
5384
5628
|
method: httpMethod,
|
|
5385
5629
|
path: routePath,
|
|
@@ -5450,12 +5694,12 @@ function parseController(input, ctx) {
|
|
|
5450
5694
|
} else if (dName === "Headers") {
|
|
5451
5695
|
hasBindingDecorator = true;
|
|
5452
5696
|
const argument = dArgs[0];
|
|
5453
|
-
const bindingName = argument !== undefined &&
|
|
5697
|
+
const bindingName = argument !== undefined && ts4.isStringLiteral(argument) ? argument.text : undefined;
|
|
5454
5698
|
paramNode = { name: pName, kind: "headers", ...bindingName === undefined ? {} : { bindingName } };
|
|
5455
5699
|
} else if (dName === "Cookie") {
|
|
5456
5700
|
hasBindingDecorator = true;
|
|
5457
5701
|
const argument = dArgs[0];
|
|
5458
|
-
const bindingName = argument !== undefined &&
|
|
5702
|
+
const bindingName = argument !== undefined && ts4.isStringLiteral(argument) ? argument.text : undefined;
|
|
5459
5703
|
paramNode = { name: pName, kind: "cookie", ...bindingName === undefined ? {} : { bindingName } };
|
|
5460
5704
|
}
|
|
5461
5705
|
}
|
|
@@ -5517,20 +5761,20 @@ function parseController(input, ctx) {
|
|
|
5517
5761
|
}
|
|
5518
5762
|
} else if (dName === "Title") {
|
|
5519
5763
|
const tArg = mArgs[0];
|
|
5520
|
-
if (tArg &&
|
|
5764
|
+
if (tArg && ts4.isStringLiteral(tArg)) {
|
|
5521
5765
|
route.title = tArg.text;
|
|
5522
5766
|
}
|
|
5523
5767
|
} else if (dName === "Data") {
|
|
5524
5768
|
const dArg = mArgs[0];
|
|
5525
|
-
if (dArg &&
|
|
5769
|
+
if (dArg && ts4.isObjectLiteralExpression(dArg)) {
|
|
5526
5770
|
route.data = { ...route.data, ...parseObjectLiteralValues(dArg) };
|
|
5527
5771
|
}
|
|
5528
5772
|
} else if (dName === "Resolve") {
|
|
5529
5773
|
const rArg = mArgs[0];
|
|
5530
|
-
if (rArg &&
|
|
5774
|
+
if (rArg && ts4.isObjectLiteralExpression(rArg)) {
|
|
5531
5775
|
const resolvers = route.resolvers ?? {};
|
|
5532
5776
|
for (const prop of rArg.properties) {
|
|
5533
|
-
if (
|
|
5777
|
+
if (ts4.isPropertyAssignment(prop)) {
|
|
5534
5778
|
const rName = propertyName(prop.name);
|
|
5535
5779
|
const init = prop.initializer;
|
|
5536
5780
|
if (init)
|
|
@@ -5561,7 +5805,7 @@ function parseController(input, ctx) {
|
|
|
5561
5805
|
});
|
|
5562
5806
|
}
|
|
5563
5807
|
const contract = getProp(optionsObject, "contract");
|
|
5564
|
-
if (contract &&
|
|
5808
|
+
if (contract && ts4.isObjectLiteralExpression(contract)) {
|
|
5565
5809
|
route.contract = {};
|
|
5566
5810
|
for (const field of ["body", "response", "evidence"]) {
|
|
5567
5811
|
const value = stringLiteralProp(contract, field);
|
|
@@ -5579,15 +5823,15 @@ function parseController(input, ctx) {
|
|
|
5579
5823
|
}
|
|
5580
5824
|
for (const field of ["body", "params", "query", "headers", "cookie", "response"]) {
|
|
5581
5825
|
const schemaExpr = getProp(optionsObject, field);
|
|
5582
|
-
if (schemaExpr &&
|
|
5826
|
+
if (schemaExpr && ts4.isIdentifier(schemaExpr)) {
|
|
5583
5827
|
route[field] = nodeText(schemaExpr);
|
|
5584
5828
|
const importPath = importPathOf(schemaExpr, ctx);
|
|
5585
5829
|
if (importPath)
|
|
5586
5830
|
schemaImports[schemaExpr.text] = importPath;
|
|
5587
5831
|
const declaration = resolveDeclaration(schemaExpr, ctx)[0];
|
|
5588
5832
|
const typeName = ctx.checker.typeToString(ctx.checker.getTypeAtLocation(schemaExpr));
|
|
5589
|
-
const initializer = declaration &&
|
|
5590
|
-
const opaque = /\bT(?:Unknown|Any)\b/.test(typeName) || initializer &&
|
|
5833
|
+
const initializer = declaration && ts4.isVariableDeclaration(declaration) ? declaration.initializer : undefined;
|
|
5834
|
+
const opaque = /\bT(?:Unknown|Any)\b/.test(typeName) || initializer && ts4.isCallExpression(initializer) && ts4.isPropertyAccessExpression(initializer.expression) && ["Unknown", "Any"].includes(initializer.expression.name.text);
|
|
5591
5835
|
(route.schemaKinds ??= {})[field] = opaque ? "opaque" : "declared";
|
|
5592
5836
|
}
|
|
5593
5837
|
}
|
|
@@ -5596,7 +5840,7 @@ function parseController(input, ctx) {
|
|
|
5596
5840
|
const responses = {};
|
|
5597
5841
|
const selectors = new Map;
|
|
5598
5842
|
for (const property of responsesObject.properties) {
|
|
5599
|
-
if (!
|
|
5843
|
+
if (!ts4.isPropertyAssignment(property) || ts4.isComputedPropertyName(property.name))
|
|
5600
5844
|
continue;
|
|
5601
5845
|
const status = propertyName(property.name);
|
|
5602
5846
|
if (!isRouteResponseSelector(status)) {
|
|
@@ -5628,7 +5872,7 @@ function parseController(input, ctx) {
|
|
|
5628
5872
|
}
|
|
5629
5873
|
selectors.set(canonical, status);
|
|
5630
5874
|
const schemaExpr = property.initializer;
|
|
5631
|
-
if (!
|
|
5875
|
+
if (!ts4.isIdentifier(schemaExpr)) {
|
|
5632
5876
|
ctx.diagnostics.push({
|
|
5633
5877
|
severity: "error",
|
|
5634
5878
|
code: "invalid-route-response-map",
|
|
@@ -5643,8 +5887,8 @@ function parseController(input, ctx) {
|
|
|
5643
5887
|
schemaImports[schemaExpr.text] = importPath;
|
|
5644
5888
|
const declaration = resolveDeclaration(schemaExpr, ctx)[0];
|
|
5645
5889
|
const typeName = ctx.checker.typeToString(ctx.checker.getTypeAtLocation(schemaExpr));
|
|
5646
|
-
const initializer = declaration &&
|
|
5647
|
-
const opaque = /\bT(?:Unknown|Any)\b/.test(typeName) || initializer &&
|
|
5890
|
+
const initializer = declaration && ts4.isVariableDeclaration(declaration) ? declaration.initializer : undefined;
|
|
5891
|
+
const opaque = /\bT(?:Unknown|Any)\b/.test(typeName) || initializer && ts4.isCallExpression(initializer) && ts4.isPropertyAccessExpression(initializer.expression) && ["Unknown", "Any"].includes(initializer.expression.name.text);
|
|
5648
5892
|
const previousKind = route.schemaKinds?.response;
|
|
5649
5893
|
(route.schemaKinds ??= {}).response = opaque || previousKind === "opaque" ? "opaque" : "declared";
|
|
5650
5894
|
}
|
|
@@ -5652,18 +5896,18 @@ function parseController(input, ctx) {
|
|
|
5652
5896
|
route.responses = responses;
|
|
5653
5897
|
}
|
|
5654
5898
|
const commandExpr = getProp(optionsObject, "command");
|
|
5655
|
-
if (commandExpr &&
|
|
5899
|
+
if (commandExpr && ts4.isIdentifier(commandExpr)) {
|
|
5656
5900
|
const commandDecl = resolveDeclaration(commandExpr, ctx)[0];
|
|
5657
|
-
route.command = commandDecl &&
|
|
5901
|
+
route.command = commandDecl && ts4.isClassDeclaration(commandDecl) ? commandDecl.name?.text ?? commandExpr.text : commandExpr.text;
|
|
5658
5902
|
}
|
|
5659
5903
|
const guardsExpr = getProp(optionsObject, "guards");
|
|
5660
|
-
if (guardsExpr &&
|
|
5904
|
+
if (guardsExpr && ts4.isArrayLiteralExpression(guardsExpr)) {
|
|
5661
5905
|
for (const el of guardsExpr.elements) {
|
|
5662
5906
|
routeGuards.push(tokenText(el, ctx));
|
|
5663
5907
|
}
|
|
5664
5908
|
}
|
|
5665
5909
|
const canMatchExpr = getProp(optionsObject, "canMatch");
|
|
5666
|
-
if (canMatchExpr &&
|
|
5910
|
+
if (canMatchExpr && ts4.isArrayLiteralExpression(canMatchExpr)) {
|
|
5667
5911
|
const canMatchList = [];
|
|
5668
5912
|
for (const el of canMatchExpr.elements) {
|
|
5669
5913
|
canMatchList.push(tokenText(el, ctx));
|
|
@@ -5673,16 +5917,16 @@ function parseController(input, ctx) {
|
|
|
5673
5917
|
}
|
|
5674
5918
|
}
|
|
5675
5919
|
const canDeactivateExpr = getProp(optionsObject, "canDeactivate");
|
|
5676
|
-
if (canDeactivateExpr &&
|
|
5920
|
+
if (canDeactivateExpr && ts4.isArrayLiteralExpression(canDeactivateExpr)) {
|
|
5677
5921
|
for (const el of canDeactivateExpr.elements) {
|
|
5678
5922
|
routeCanDeactivate.push(tokenText(el, ctx));
|
|
5679
5923
|
}
|
|
5680
5924
|
}
|
|
5681
5925
|
const resolversExpr = getProp(optionsObject, "resolvers");
|
|
5682
|
-
if (resolversExpr &&
|
|
5926
|
+
if (resolversExpr && ts4.isObjectLiteralExpression(resolversExpr)) {
|
|
5683
5927
|
const resolvers = {};
|
|
5684
5928
|
for (const prop of resolversExpr.properties) {
|
|
5685
|
-
if (
|
|
5929
|
+
if (ts4.isPropertyAssignment(prop)) {
|
|
5686
5930
|
const rName = propertyName(prop.name);
|
|
5687
5931
|
const init = prop.initializer;
|
|
5688
5932
|
if (init)
|
|
@@ -5694,22 +5938,22 @@ function parseController(input, ctx) {
|
|
|
5694
5938
|
}
|
|
5695
5939
|
}
|
|
5696
5940
|
const redirectToExpr = getProp(optionsObject, "redirectTo");
|
|
5697
|
-
if (redirectToExpr &&
|
|
5941
|
+
if (redirectToExpr && ts4.isStringLiteral(redirectToExpr)) {
|
|
5698
5942
|
route.redirectTo = redirectToExpr.text;
|
|
5699
5943
|
}
|
|
5700
5944
|
const pathMatchExpr = getProp(optionsObject, "pathMatch");
|
|
5701
|
-
if (pathMatchExpr &&
|
|
5945
|
+
if (pathMatchExpr && ts4.isStringLiteral(pathMatchExpr)) {
|
|
5702
5946
|
const val = pathMatchExpr.text;
|
|
5703
5947
|
if (val === "full" || val === "prefix") {
|
|
5704
5948
|
route.pathMatch = val;
|
|
5705
5949
|
}
|
|
5706
5950
|
}
|
|
5707
5951
|
const titleExpr = getProp(optionsObject, "title");
|
|
5708
|
-
if (titleExpr &&
|
|
5952
|
+
if (titleExpr && ts4.isStringLiteral(titleExpr)) {
|
|
5709
5953
|
route.title = titleExpr.text;
|
|
5710
5954
|
}
|
|
5711
5955
|
const dataExpr = getProp(optionsObject, "data");
|
|
5712
|
-
if (dataExpr &&
|
|
5956
|
+
if (dataExpr && ts4.isObjectLiteralExpression(dataExpr)) {
|
|
5713
5957
|
route.data = { ...route.data, ...parseObjectLiteralValues(dataExpr) };
|
|
5714
5958
|
}
|
|
5715
5959
|
const aspects = parseAspectRefs(getProp(optionsObject, "aspects"), ctx, `route ${httpMethod} ${routePath}`);
|
|
@@ -5764,7 +6008,7 @@ function parseJobOptions(meta, owner, ctx) {
|
|
|
5764
6008
|
const expression = getProp(meta, field);
|
|
5765
6009
|
if (!expression)
|
|
5766
6010
|
continue;
|
|
5767
|
-
if (!
|
|
6011
|
+
if (!ts4.isIdentifier(expression)) {
|
|
5768
6012
|
jobOptionError(ctx, "invalid-job-schema", `${owner} 的 ${field} schema 必须是可静态解析的标识符引用,不能使用内联调用或动态表达式`, expression, "SC4019", `将 schema 提取为命名导出,例如 ${field}: ${field === "input" ? "JobInput" : "JobOutput"}。`);
|
|
5769
6013
|
continue;
|
|
5770
6014
|
}
|
|
@@ -5788,7 +6032,7 @@ function parseJobEnum(meta, field, allowed, owner, ctx, code, errorCode) {
|
|
|
5788
6032
|
const expression = getProp(meta, field);
|
|
5789
6033
|
if (!expression)
|
|
5790
6034
|
return;
|
|
5791
|
-
if (!
|
|
6035
|
+
if (!ts4.isStringLiteral(expression) || !allowed.includes(expression.text)) {
|
|
5792
6036
|
jobOptionError(ctx, code, `${owner} 的 ${field} 必须是 ${allowed.map((value) => JSON.stringify(value)).join(" 或 ")} 字符串字面量`, expression, errorCode);
|
|
5793
6037
|
return;
|
|
5794
6038
|
}
|
|
@@ -5798,7 +6042,7 @@ function parseJobInteger(meta, field, min, max, owner, ctx, code, errorCode) {
|
|
|
5798
6042
|
const expression = getProp(meta, field);
|
|
5799
6043
|
if (!expression)
|
|
5800
6044
|
return;
|
|
5801
|
-
const value =
|
|
6045
|
+
const value = ts4.isNumericLiteral(expression) ? Number(expression.text) : Number.NaN;
|
|
5802
6046
|
if (!Number.isSafeInteger(value) || value < min || value > max) {
|
|
5803
6047
|
jobOptionError(ctx, code, `${owner} 的 ${field} 必须是 ${min} 到 ${max} 之间的安全整数`, expression, errorCode);
|
|
5804
6048
|
return;
|
|
@@ -5819,8 +6063,8 @@ function jobOptionError(ctx, code, message, node, errorCode, suggestion) {
|
|
|
5819
6063
|
}
|
|
5820
6064
|
function jobSchemaKind(identifier, declaration, ctx) {
|
|
5821
6065
|
const typeName = ctx.checker.typeToString(ctx.checker.getTypeAtLocation(identifier));
|
|
5822
|
-
const initializer =
|
|
5823
|
-
const opaque = /\bT(?:Unknown|Any)\b/.test(typeName) || initializer &&
|
|
6066
|
+
const initializer = ts4.isVariableDeclaration(declaration) ? declaration.initializer : undefined;
|
|
6067
|
+
const opaque = /\bT(?:Unknown|Any)\b/.test(typeName) || initializer && ts4.isCallExpression(initializer) && ts4.isPropertyAccessExpression(initializer.expression) && ["Unknown", "Any"].includes(initializer.expression.name.text);
|
|
5824
6068
|
return opaque ? "opaque" : "declared";
|
|
5825
6069
|
}
|
|
5826
6070
|
function checkedRpc(meta, ctx) {
|
|
@@ -5842,7 +6086,7 @@ function checkedRpc(meta, ctx) {
|
|
|
5842
6086
|
}
|
|
5843
6087
|
function classDeps(cls, ctx) {
|
|
5844
6088
|
const injectable = parseInjectableOptions(cls, ctx);
|
|
5845
|
-
const ctor = cls.members.find(
|
|
6089
|
+
const ctor = cls.members.find(ts4.isConstructorDeclaration);
|
|
5846
6090
|
const deps = injectable?.deps ? [...injectable.deps] : [];
|
|
5847
6091
|
const optionalDeps = [];
|
|
5848
6092
|
const selfDeps = [];
|
|
@@ -5876,23 +6120,23 @@ function classDeps(cls, ctx) {
|
|
|
5876
6120
|
}
|
|
5877
6121
|
});
|
|
5878
6122
|
}
|
|
5879
|
-
for (const prop of cls.members.filter(
|
|
6123
|
+
for (const prop of cls.members.filter(ts4.isPropertyDeclaration)) {
|
|
5880
6124
|
const init = prop.initializer;
|
|
5881
|
-
if (init &&
|
|
6125
|
+
if (init && ts4.isCallExpression(init)) {
|
|
5882
6126
|
const callName = nodeText(init.expression).split(".").pop();
|
|
5883
6127
|
if (callName === "inject") {
|
|
5884
6128
|
const [tokenArg, optionsArg] = init.arguments;
|
|
5885
6129
|
if (tokenArg) {
|
|
5886
6130
|
const tokenName = tokenText(tokenArg, ctx);
|
|
5887
6131
|
const unwrappedToken = unwrapForwardRef(tokenArg);
|
|
5888
|
-
const known =
|
|
6132
|
+
const known = ts4.isStringLiteral(unwrappedToken) || ts4.isIdentifier(unwrappedToken) && (ctx.tokensByName.has(tokenName) || ctx.classesByName.has(tokenName));
|
|
5889
6133
|
if (!known) {
|
|
5890
6134
|
missing = true;
|
|
5891
6135
|
continue;
|
|
5892
6136
|
}
|
|
5893
6137
|
if (!deps.includes(tokenName))
|
|
5894
6138
|
deps.push(tokenName);
|
|
5895
|
-
const options = optionsArg &&
|
|
6139
|
+
const options = optionsArg && ts4.isObjectLiteralExpression(optionsArg) ? {
|
|
5896
6140
|
optional: booleanProp(optionsArg, "optional") ?? false,
|
|
5897
6141
|
self: booleanProp(optionsArg, "self") ?? false,
|
|
5898
6142
|
skipSelf: booleanProp(optionsArg, "skipSelf") ?? false,
|
|
@@ -5907,9 +6151,9 @@ function classDeps(cls, ctx) {
|
|
|
5907
6151
|
if (options.host && !hostDeps.includes(tokenName))
|
|
5908
6152
|
hostDeps.push(tokenName);
|
|
5909
6153
|
if (!functionalInjects.some((entry) => entry.token === tokenName)) {
|
|
5910
|
-
const declaration =
|
|
6154
|
+
const declaration = ts4.isIdentifier(unwrappedToken) ? resolveDeclaration(unwrappedToken, ctx)[0] : undefined;
|
|
5911
6155
|
const localFile = declaration && isProjectSourcePath(declaration.getSourceFile().fileName, ctx.rootDir) ? declaration.getSourceFile().fileName : undefined;
|
|
5912
|
-
const importModule = declaration && !localFile &&
|
|
6156
|
+
const importModule = declaration && !localFile && ts4.isIdentifier(unwrappedToken) ? importModuleOf(unwrappedToken, ctx) : undefined;
|
|
5913
6157
|
functionalInjects.push({
|
|
5914
6158
|
token: tokenName,
|
|
5915
6159
|
expression: nodeText(unwrappedToken),
|
|
@@ -5953,7 +6197,7 @@ function parseInjectableOptions(cls, ctx) {
|
|
|
5953
6197
|
}
|
|
5954
6198
|
function parseInjectParams(cls, ctx) {
|
|
5955
6199
|
const result = new Map;
|
|
5956
|
-
const ctor = cls.members.find(
|
|
6200
|
+
const ctor = cls.members.find(ts4.isConstructorDeclaration);
|
|
5957
6201
|
if (!ctor)
|
|
5958
6202
|
return result;
|
|
5959
6203
|
ctor.parameters.forEach((param, index) => {
|
|
@@ -5969,7 +6213,7 @@ function parseInjectParams(cls, ctx) {
|
|
|
5969
6213
|
}
|
|
5970
6214
|
function parseOptionalParams(cls) {
|
|
5971
6215
|
const result = new Set;
|
|
5972
|
-
const ctor = cls.members.find(
|
|
6216
|
+
const ctor = cls.members.find(ts4.isConstructorDeclaration);
|
|
5973
6217
|
if (!ctor)
|
|
5974
6218
|
return result;
|
|
5975
6219
|
ctor.parameters.forEach((param, index) => {
|
|
@@ -5984,7 +6228,7 @@ function parseOptionalParams(cls) {
|
|
|
5984
6228
|
}
|
|
5985
6229
|
function parseModifierParams(cls, modifierName) {
|
|
5986
6230
|
const result = new Set;
|
|
5987
|
-
const ctor = cls.members.find(
|
|
6231
|
+
const ctor = cls.members.find(ts4.isConstructorDeclaration);
|
|
5988
6232
|
if (!ctor)
|
|
5989
6233
|
return result;
|
|
5990
6234
|
ctor.parameters.forEach((param, index) => {
|
|
@@ -5996,13 +6240,13 @@ function parseModifierParams(cls, modifierName) {
|
|
|
5996
6240
|
return result;
|
|
5997
6241
|
}
|
|
5998
6242
|
function unwrapForwardRef(expr) {
|
|
5999
|
-
if (
|
|
6243
|
+
if (ts4.isCallExpression(expr)) {
|
|
6000
6244
|
const exprText = nodeText(expr.expression);
|
|
6001
6245
|
if (exprText === "forwardRef" || exprText.endsWith(".forwardRef")) {
|
|
6002
6246
|
const arg = expr.arguments[0];
|
|
6003
|
-
if (arg && (
|
|
6247
|
+
if (arg && (ts4.isArrowFunction(arg) || ts4.isFunctionExpression(arg))) {
|
|
6004
6248
|
const body = arg.body;
|
|
6005
|
-
if (body &&
|
|
6249
|
+
if (body && ts4.isExpression(body)) {
|
|
6006
6250
|
return unwrapForwardRef(body);
|
|
6007
6251
|
}
|
|
6008
6252
|
}
|
|
@@ -6012,13 +6256,13 @@ function unwrapForwardRef(expr) {
|
|
|
6012
6256
|
}
|
|
6013
6257
|
function tokenText(expr, ctx) {
|
|
6014
6258
|
const unwrapped = unwrapForwardRef(expr);
|
|
6015
|
-
if (
|
|
6259
|
+
if (ts4.isStringLiteral(unwrapped))
|
|
6016
6260
|
return unwrapped.text;
|
|
6017
|
-
if (
|
|
6261
|
+
if (ts4.isIdentifier(unwrapped)) {
|
|
6018
6262
|
const decl = resolveDeclaration(unwrapped, ctx)[0];
|
|
6019
|
-
if (decl &&
|
|
6263
|
+
if (decl && ts4.isClassDeclaration(decl))
|
|
6020
6264
|
return decl.name?.text ?? unwrapped.text;
|
|
6021
|
-
if (decl &&
|
|
6265
|
+
if (decl && ts4.isVariableDeclaration(decl))
|
|
6022
6266
|
return variableName(decl);
|
|
6023
6267
|
}
|
|
6024
6268
|
return nodeText(unwrapped);
|
|
@@ -6038,12 +6282,12 @@ function resolveScope(input, ctx) {
|
|
|
6038
6282
|
}
|
|
6039
6283
|
function tokenNameOf(expr, ctx) {
|
|
6040
6284
|
const unwrapped = unwrapForwardRef(expr);
|
|
6041
|
-
if (
|
|
6285
|
+
if (ts4.isIdentifier(unwrapped)) {
|
|
6042
6286
|
const decl = resolveDeclaration(unwrapped, ctx)[0];
|
|
6043
|
-
if (decl &&
|
|
6287
|
+
if (decl && ts4.isClassDeclaration(decl)) {
|
|
6044
6288
|
return { name: decl.name?.text ?? nodeText(expr), kind: "class" };
|
|
6045
6289
|
}
|
|
6046
|
-
if (decl &&
|
|
6290
|
+
if (decl && ts4.isVariableDeclaration(decl)) {
|
|
6047
6291
|
const name = variableName(decl);
|
|
6048
6292
|
return { name, kind: ctx.tokensByName.has(name) ? "injection-token" : "class" };
|
|
6049
6293
|
}
|
|
@@ -6059,10 +6303,10 @@ function resolveDeclaration(id, ctx) {
|
|
|
6059
6303
|
return [];
|
|
6060
6304
|
let declarations = symbol.declarations ?? [];
|
|
6061
6305
|
for (let guard = 0;guard < 4; guard += 1) {
|
|
6062
|
-
const isAlias = declarations.some((d) =>
|
|
6306
|
+
const isAlias = declarations.some((d) => ts4.isImportSpecifier(d) || ts4.isImportClause(d) || ts4.isNamespaceImport(d));
|
|
6063
6307
|
if (!isAlias)
|
|
6064
6308
|
break;
|
|
6065
|
-
if (!(symbol.flags &
|
|
6309
|
+
if (!(symbol.flags & ts4.SymbolFlags.Alias))
|
|
6066
6310
|
break;
|
|
6067
6311
|
const aliased = ctx.checker.getAliasedSymbol(symbol);
|
|
6068
6312
|
symbol = aliased;
|
|
@@ -6082,9 +6326,9 @@ function importModuleOf(id, ctx) {
|
|
|
6082
6326
|
for (const declaration of declarations) {
|
|
6083
6327
|
let current = declaration;
|
|
6084
6328
|
while (current) {
|
|
6085
|
-
if (
|
|
6329
|
+
if (ts4.isImportDeclaration(current)) {
|
|
6086
6330
|
const moduleSpecifier = current.moduleSpecifier;
|
|
6087
|
-
return
|
|
6331
|
+
return ts4.isStringLiteral(moduleSpecifier) ? moduleSpecifier.text : undefined;
|
|
6088
6332
|
}
|
|
6089
6333
|
current = current.parent;
|
|
6090
6334
|
}
|
|
@@ -6096,27 +6340,27 @@ function findDecorator(cls, name) {
|
|
|
6096
6340
|
}
|
|
6097
6341
|
function decoratorName2(dec) {
|
|
6098
6342
|
const expr = dec.expression;
|
|
6099
|
-
if (
|
|
6343
|
+
if (ts4.isCallExpression(expr)) {
|
|
6100
6344
|
return nodeText(expr.expression).split(".").pop();
|
|
6101
6345
|
}
|
|
6102
|
-
if (
|
|
6346
|
+
if (ts4.isIdentifier(expr))
|
|
6103
6347
|
return expr.text;
|
|
6104
6348
|
return;
|
|
6105
6349
|
}
|
|
6106
6350
|
function decoratorObjectArg(dec) {
|
|
6107
6351
|
const expr = dec.expression;
|
|
6108
|
-
if (!
|
|
6352
|
+
if (!ts4.isCallExpression(expr))
|
|
6109
6353
|
return;
|
|
6110
6354
|
const arg = expr.arguments[0];
|
|
6111
|
-
return arg &&
|
|
6355
|
+
return arg && ts4.isObjectLiteralExpression(arg) ? arg : undefined;
|
|
6112
6356
|
}
|
|
6113
6357
|
function getProp(obj, name) {
|
|
6114
|
-
const prop = obj.properties.find((item) => (
|
|
6358
|
+
const prop = obj.properties.find((item) => (ts4.isPropertyAssignment(item) || ts4.isShorthandPropertyAssignment(item)) && propertyName(item.name) === name);
|
|
6115
6359
|
if (!prop)
|
|
6116
6360
|
return;
|
|
6117
|
-
if (
|
|
6361
|
+
if (ts4.isPropertyAssignment(prop))
|
|
6118
6362
|
return prop.initializer;
|
|
6119
|
-
if (
|
|
6363
|
+
if (ts4.isShorthandPropertyAssignment(prop))
|
|
6120
6364
|
return prop.name;
|
|
6121
6365
|
return;
|
|
6122
6366
|
}
|
|
@@ -6124,10 +6368,10 @@ function toCompilerDiagnostic(diagnostic, rootDir) {
|
|
|
6124
6368
|
const file = diagnostic.file;
|
|
6125
6369
|
const position = file && diagnostic.start !== undefined ? file.getLineAndCharacterOfPosition(diagnostic.start) : undefined;
|
|
6126
6370
|
return {
|
|
6127
|
-
severity: diagnostic.category ===
|
|
6371
|
+
severity: diagnostic.category === ts4.DiagnosticCategory.Error ? "error" : "warn",
|
|
6128
6372
|
code: `typescript-${diagnostic.code}`,
|
|
6129
6373
|
errorCode: `TS${diagnostic.code}`,
|
|
6130
|
-
message:
|
|
6374
|
+
message: ts4.flattenDiagnosticMessageText(diagnostic.messageText, `
|
|
6131
6375
|
`),
|
|
6132
6376
|
...file ? { file: sourcePath(rootDir, file.fileName) } : {},
|
|
6133
6377
|
...position ? { line: position.line + 1 } : {}
|
|
@@ -6135,16 +6379,16 @@ function toCompilerDiagnostic(diagnostic, rootDir) {
|
|
|
6135
6379
|
}
|
|
6136
6380
|
function stringLiteralProp(obj, name) {
|
|
6137
6381
|
const expr = getProp(obj, name);
|
|
6138
|
-
return expr &&
|
|
6382
|
+
return expr && ts4.isStringLiteral(expr) ? expr.text : undefined;
|
|
6139
6383
|
}
|
|
6140
6384
|
function arrayProp(obj, name) {
|
|
6141
6385
|
const expr = getProp(obj, name);
|
|
6142
|
-
return expr &&
|
|
6386
|
+
return expr && ts4.isArrayLiteralExpression(expr) ? [...expr.elements] : [];
|
|
6143
6387
|
}
|
|
6144
6388
|
function parseAspectRefs(expression, ctx, owner) {
|
|
6145
6389
|
if (!expression)
|
|
6146
6390
|
return [];
|
|
6147
|
-
if (!
|
|
6391
|
+
if (!ts4.isArrayLiteralExpression(expression)) {
|
|
6148
6392
|
ctx.diagnostics.push({
|
|
6149
6393
|
severity: "error",
|
|
6150
6394
|
code: "dynamic-aspect-reference",
|
|
@@ -6159,7 +6403,7 @@ function parseAspectRefs(expression, ctx, owner) {
|
|
|
6159
6403
|
}
|
|
6160
6404
|
const refs = [];
|
|
6161
6405
|
for (const element of expression.elements) {
|
|
6162
|
-
if (
|
|
6406
|
+
if (ts4.isSpreadElement(element) || !ts4.isIdentifier(element)) {
|
|
6163
6407
|
ctx.diagnostics.push({
|
|
6164
6408
|
severity: "error",
|
|
6165
6409
|
code: "dynamic-aspect-reference",
|
|
@@ -6172,7 +6416,7 @@ function parseAspectRefs(expression, ctx, owner) {
|
|
|
6172
6416
|
});
|
|
6173
6417
|
continue;
|
|
6174
6418
|
}
|
|
6175
|
-
const declaration = resolveDeclaration(element, ctx).find((candidate) =>
|
|
6419
|
+
const declaration = resolveDeclaration(element, ctx).find((candidate) => ts4.isFunctionDeclaration(candidate) || ts4.isVariableDeclaration(candidate) && candidate.initializer !== undefined && (ts4.isArrowFunction(candidate.initializer) || ts4.isFunctionExpression(candidate.initializer)));
|
|
6176
6420
|
if (!declaration) {
|
|
6177
6421
|
ctx.diagnostics.push({
|
|
6178
6422
|
severity: "error",
|
|
@@ -6186,7 +6430,7 @@ function parseAspectRefs(expression, ctx, owner) {
|
|
|
6186
6430
|
});
|
|
6187
6431
|
continue;
|
|
6188
6432
|
}
|
|
6189
|
-
const name =
|
|
6433
|
+
const name = ts4.isFunctionDeclaration(declaration) ? declaration.name?.text : ts4.isVariableDeclaration(declaration) ? variableName(declaration) : undefined;
|
|
6190
6434
|
if (!name)
|
|
6191
6435
|
continue;
|
|
6192
6436
|
const declaredFile = declaration.getSourceFile().fileName;
|
|
@@ -6206,9 +6450,9 @@ function booleanProp(obj, name) {
|
|
|
6206
6450
|
const expr = getProp(obj, name);
|
|
6207
6451
|
if (!expr)
|
|
6208
6452
|
return;
|
|
6209
|
-
if (expr.kind ===
|
|
6453
|
+
if (expr.kind === ts4.SyntaxKind.TrueKeyword)
|
|
6210
6454
|
return true;
|
|
6211
|
-
if (expr.kind ===
|
|
6455
|
+
if (expr.kind === ts4.SyntaxKind.FalseKeyword)
|
|
6212
6456
|
return false;
|
|
6213
6457
|
return;
|
|
6214
6458
|
}
|
|
@@ -6222,15 +6466,15 @@ function parseBindingOptions(args, defaultName) {
|
|
|
6222
6466
|
let defaultValue;
|
|
6223
6467
|
const first = args[0];
|
|
6224
6468
|
const second = args[1];
|
|
6225
|
-
if (first &&
|
|
6469
|
+
if (first && ts4.isStringLiteral(first)) {
|
|
6226
6470
|
name = first.text;
|
|
6227
|
-
} else if (first &&
|
|
6471
|
+
} else if (first && ts4.isObjectLiteralExpression(first)) {
|
|
6228
6472
|
const nameProp = getProp(first, "name");
|
|
6229
|
-
if (nameProp &&
|
|
6473
|
+
if (nameProp && ts4.isStringLiteral(nameProp)) {
|
|
6230
6474
|
name = nameProp.text;
|
|
6231
6475
|
}
|
|
6232
6476
|
const trProp = getProp(first, "transform");
|
|
6233
|
-
if (trProp &&
|
|
6477
|
+
if (trProp && ts4.isStringLiteral(trProp)) {
|
|
6234
6478
|
const val = trProp.text;
|
|
6235
6479
|
if (val === "number" || val === "boolean" || val === "string") {
|
|
6236
6480
|
transform = val;
|
|
@@ -6241,9 +6485,9 @@ function parseBindingOptions(args, defaultName) {
|
|
|
6241
6485
|
defaultValue = parseLiteralValue(defProp);
|
|
6242
6486
|
}
|
|
6243
6487
|
}
|
|
6244
|
-
if (second &&
|
|
6488
|
+
if (second && ts4.isObjectLiteralExpression(second)) {
|
|
6245
6489
|
const trProp = getProp(second, "transform");
|
|
6246
|
-
if (trProp &&
|
|
6490
|
+
if (trProp && ts4.isStringLiteral(trProp)) {
|
|
6247
6491
|
const val = trProp.text;
|
|
6248
6492
|
if (val === "number" || val === "boolean" || val === "string") {
|
|
6249
6493
|
transform = val;
|
|
@@ -6257,18 +6501,18 @@ function parseBindingOptions(args, defaultName) {
|
|
|
6257
6501
|
return { name, ...transform ? { transform } : {}, default: defaultValue };
|
|
6258
6502
|
}
|
|
6259
6503
|
function parseLiteralValue(node) {
|
|
6260
|
-
if (
|
|
6504
|
+
if (ts4.isStringLiteral(node))
|
|
6261
6505
|
return node.text;
|
|
6262
|
-
if (
|
|
6506
|
+
if (ts4.isNumericLiteral(node))
|
|
6263
6507
|
return Number(node.text);
|
|
6264
|
-
if (node.kind ===
|
|
6508
|
+
if (node.kind === ts4.SyntaxKind.TrueKeyword)
|
|
6265
6509
|
return true;
|
|
6266
|
-
if (node.kind ===
|
|
6510
|
+
if (node.kind === ts4.SyntaxKind.FalseKeyword)
|
|
6267
6511
|
return false;
|
|
6268
|
-
if (
|
|
6512
|
+
if (ts4.isArrayLiteralExpression(node)) {
|
|
6269
6513
|
return node.elements.map(parseLiteralValue);
|
|
6270
6514
|
}
|
|
6271
|
-
if (
|
|
6515
|
+
if (ts4.isObjectLiteralExpression(node)) {
|
|
6272
6516
|
return parseObjectLiteralValues(node);
|
|
6273
6517
|
}
|
|
6274
6518
|
return;
|
|
@@ -6276,7 +6520,7 @@ function parseLiteralValue(node) {
|
|
|
6276
6520
|
function parseObjectLiteralValues(obj) {
|
|
6277
6521
|
const result = {};
|
|
6278
6522
|
for (const prop of obj.properties) {
|
|
6279
|
-
if (
|
|
6523
|
+
if (ts4.isPropertyAssignment(prop)) {
|
|
6280
6524
|
const name = propertyName(prop.name);
|
|
6281
6525
|
const init = prop.initializer;
|
|
6282
6526
|
if (init) {
|
|
@@ -6310,7 +6554,50 @@ import { join as join4 } from "node:path";
|
|
|
6310
6554
|
// src/type-safety.ts
|
|
6311
6555
|
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
|
|
6312
6556
|
import { dirname as dirname3, join as join3, relative as relative3, resolve as resolve2, sep as sep3 } from "node:path";
|
|
6313
|
-
import * as
|
|
6557
|
+
import * as ts6 from "@typescript/typescript6";
|
|
6558
|
+
|
|
6559
|
+
// src/sql-safety.ts
|
|
6560
|
+
import * as ts5 from "@typescript/typescript6";
|
|
6561
|
+
var SQL_SAFETY_DIAGNOSTIC_CODES = {
|
|
6562
|
+
"sql-result-assertion": { errorCode: "SC6007", docsUrl: "https://supacloud.dev/errors/SC6007" },
|
|
6563
|
+
"sql-raw-dynamic": { errorCode: "SC6008", docsUrl: "https://supacloud.dev/errors/SC6008" }
|
|
6564
|
+
};
|
|
6565
|
+
function scanDrizzleSql(sourceFile, checker, file, strict) {
|
|
6566
|
+
const diagnostics = [];
|
|
6567
|
+
const importedSql = (expression) => {
|
|
6568
|
+
const symbol = checker.getSymbolAtLocation(ts5.isPropertyAccessExpression(expression) ? expression.name : expression);
|
|
6569
|
+
if (!symbol)
|
|
6570
|
+
return false;
|
|
6571
|
+
const target = symbol.flags & ts5.SymbolFlags.Alias ? checker.getAliasedSymbol(symbol) : symbol;
|
|
6572
|
+
return target.name === "sql" && (target.declarations ?? []).some((declaration) => /(?:^|\/)node_modules\/drizzle-orm\//.test(declaration.getSourceFile().fileName.replaceAll("\\", "/")));
|
|
6573
|
+
};
|
|
6574
|
+
const report = (code, node, message) => {
|
|
6575
|
+
diagnostics.push({
|
|
6576
|
+
severity: strict ? "error" : "warn",
|
|
6577
|
+
code,
|
|
6578
|
+
...SQL_SAFETY_DIAGNOSTIC_CODES[code],
|
|
6579
|
+
message,
|
|
6580
|
+
file,
|
|
6581
|
+
line: sourceFile.getLineAndCharacterOfPosition(node.getStart()).line + 1
|
|
6582
|
+
});
|
|
6583
|
+
};
|
|
6584
|
+
const visit = (node) => {
|
|
6585
|
+
if (ts5.isTaggedTemplateExpression(node) && importedSql(node.tag) && node.typeArguments?.some((type) => type.kind !== ts5.SyntaxKind.UnknownKeyword)) {
|
|
6586
|
+
report("sql-result-assertion", node, "Drizzle sql<T> asserts a result type without checking SQL or decoding rows. Use sql<unknown> and a schema decoder at the result boundary.");
|
|
6587
|
+
}
|
|
6588
|
+
if (ts5.isCallExpression(node) && ts5.isPropertyAccessExpression(node.expression) && node.expression.name.text === "raw" && importedSql(node.expression.expression)) {
|
|
6589
|
+
const argument = node.arguments[0];
|
|
6590
|
+
if (!argument || !ts5.isStringLiteral(argument) && !ts5.isNoSubstitutionTemplateLiteral(argument)) {
|
|
6591
|
+
report("sql-raw-dynamic", node, "Dynamic sql.raw bypasses parameter binding. Interpolate values with sql templates; keep reviewed static DDL in migrations.");
|
|
6592
|
+
}
|
|
6593
|
+
}
|
|
6594
|
+
ts5.forEachChild(node, visit);
|
|
6595
|
+
};
|
|
6596
|
+
visit(sourceFile);
|
|
6597
|
+
return diagnostics;
|
|
6598
|
+
}
|
|
6599
|
+
|
|
6600
|
+
// src/type-safety.ts
|
|
6314
6601
|
var DEFAULT_EXCLUDES = [
|
|
6315
6602
|
"**/*.test.ts",
|
|
6316
6603
|
"**/*.spec.ts",
|
|
@@ -6323,6 +6610,7 @@ var DEFAULT_EXCLUDES = [
|
|
|
6323
6610
|
"**/*.d.ts"
|
|
6324
6611
|
];
|
|
6325
6612
|
var TYPE_SAFETY_DIAGNOSTIC_CODES = {
|
|
6613
|
+
...SQL_SAFETY_DIAGNOSTIC_CODES,
|
|
6326
6614
|
"generated-any": { errorCode: "SC6001", docsUrl: "https://supacloud.dev/errors/SC6001" },
|
|
6327
6615
|
"source-any": { errorCode: "SC6002", docsUrl: "https://supacloud.dev/errors/SC6002" },
|
|
6328
6616
|
"source-type-assertion": { errorCode: "SC6003", docsUrl: "https://supacloud.dev/errors/SC6003" },
|
|
@@ -6335,7 +6623,7 @@ function scanGeneratedArtifacts(artifacts, strict = true) {
|
|
|
6335
6623
|
for (const [file, content] of Object.entries(artifacts)) {
|
|
6336
6624
|
if (content === undefined)
|
|
6337
6625
|
continue;
|
|
6338
|
-
const sourceFile =
|
|
6626
|
+
const sourceFile = ts6.createSourceFile(file, content, ts6.ScriptTarget.Latest, true, ts6.ScriptKind.TS);
|
|
6339
6627
|
for (const node of descendantsOfKind2(sourceFile, isAnyKeyword)) {
|
|
6340
6628
|
diagnostics.push(makeDiagnostic("generated-any", `生成产物 ${file} 包含 any;严格生成模式要求使用 unknown、具体接口或泛型约束。`, sourceFile, node, strict));
|
|
6341
6629
|
}
|
|
@@ -6344,30 +6632,30 @@ function scanGeneratedArtifacts(artifacts, strict = true) {
|
|
|
6344
6632
|
}
|
|
6345
6633
|
function scanProductionSource(options) {
|
|
6346
6634
|
const rootDir = resolve2(options.rootDir);
|
|
6347
|
-
const configPath =
|
|
6635
|
+
const configPath = ts6.findConfigFile(rootDir, ts6.sys.fileExists) ?? join3(rootDir, "tsconfig.json");
|
|
6348
6636
|
const projectConfig = existsSync2(configPath) ? readProjectConfig2(configPath) : {
|
|
6349
6637
|
options: {
|
|
6350
6638
|
strict: true,
|
|
6351
6639
|
skipLibCheck: true,
|
|
6352
|
-
target:
|
|
6353
|
-
module:
|
|
6354
|
-
moduleResolution:
|
|
6640
|
+
target: ts6.ScriptTarget.ES2022,
|
|
6641
|
+
module: ts6.ModuleKind.ESNext,
|
|
6642
|
+
moduleResolution: ts6.ModuleResolutionKind.Bundler
|
|
6355
6643
|
},
|
|
6356
6644
|
errors: []
|
|
6357
6645
|
};
|
|
6358
6646
|
const include = options.include ?? ["**/*.ts", "**/*.tsx", "**/*.mts", "**/*.cts"];
|
|
6359
|
-
const rootNames =
|
|
6647
|
+
const rootNames = ts6.sys.readDirectory(rootDir, [".ts", ".tsx", ".mts", ".cts"], ["node_modules", "dist"], include).filter((file) => isProductionSourcePath(rootDir, file, [...DEFAULT_EXCLUDES, ...options.exclude ?? []]));
|
|
6360
6648
|
const compilerOptions = { ...projectConfig.options, noEmit: true };
|
|
6361
|
-
const host =
|
|
6649
|
+
const host = ts6.createCompilerHost(compilerOptions);
|
|
6362
6650
|
host.getCurrentDirectory = () => dirname3(configPath);
|
|
6363
|
-
const program =
|
|
6651
|
+
const program = ts6.createProgram(rootNames, compilerOptions, host);
|
|
6364
6652
|
const outDir = options.outDir ? normalizeRelative(rootDir, options.outDir) : undefined;
|
|
6365
6653
|
const excludes = [...DEFAULT_EXCLUDES, ...options.exclude ?? []];
|
|
6366
6654
|
const sourceFiles = program.getSourceFiles().filter((sourceFile) => isProductionSource(rootDir, sourceFile, excludes, outDir));
|
|
6367
6655
|
const diagnostics = [...projectConfig.errors, ...program.getOptionsDiagnostics()].map((diagnostic) => ({
|
|
6368
6656
|
severity: "error",
|
|
6369
6657
|
code: "source-config",
|
|
6370
|
-
message:
|
|
6658
|
+
message: ts6.flattenDiagnosticMessageText(diagnostic.messageText, `
|
|
6371
6659
|
`),
|
|
6372
6660
|
file: diagnostic.file ? normalizeRelative(rootDir, diagnostic.file.fileName) : normalizeRelative(rootDir, configPath),
|
|
6373
6661
|
...diagnostic.file && diagnostic.start !== undefined ? { line: diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start).line + 1 } : {},
|
|
@@ -6384,7 +6672,7 @@ function scanProductionSource(options) {
|
|
|
6384
6672
|
diagnostics.push({
|
|
6385
6673
|
severity: "error",
|
|
6386
6674
|
code: "source-typescript",
|
|
6387
|
-
message:
|
|
6675
|
+
message: ts6.flattenDiagnosticMessageText(diagnostic.messageText, `
|
|
6388
6676
|
`),
|
|
6389
6677
|
...diagnostic.file ? { file: normalizeRelative(rootDir, diagnostic.file.fileName) } : {},
|
|
6390
6678
|
...diagnostic.file && diagnostic.start !== undefined ? { line: diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start).line + 1 } : {},
|
|
@@ -6393,9 +6681,10 @@ function scanProductionSource(options) {
|
|
|
6393
6681
|
}
|
|
6394
6682
|
for (const sourceFile of sourceFiles) {
|
|
6395
6683
|
scanSourceFile(sourceFile, checker, rootDir, diagnostics, options.strict ?? false);
|
|
6396
|
-
|
|
6397
|
-
|
|
6398
|
-
|
|
6684
|
+
diagnostics.push(...scanDrizzleSql(sourceFile, checker, normalizeRelative(rootDir, sourceFile.fileName), options.strict ?? false));
|
|
6685
|
+
const scanner = ts6.createScanner(ts6.ScriptTarget.Latest, false, sourceFile.languageVariant, sourceFile.text);
|
|
6686
|
+
for (let kind = scanner.scan();kind !== ts6.SyntaxKind.EndOfFileToken; kind = scanner.scan()) {
|
|
6687
|
+
if ((kind === ts6.SyntaxKind.SingleLineCommentTrivia || kind === ts6.SyntaxKind.MultiLineCommentTrivia) && /@ts-(?:ignore|nocheck|expect-error)\b/.test(scanner.getTokenText())) {
|
|
6399
6688
|
diagnostics.push({
|
|
6400
6689
|
severity: "error",
|
|
6401
6690
|
code: "source-type-suppression",
|
|
@@ -6415,22 +6704,22 @@ function scanSourceFile(sourceFile, checker, rootDir, diagnostics, strict) {
|
|
|
6415
6704
|
diagnostics.push(makeDiagnostic("source-any", "生产源码使用了显式 any;请改用 unknown、具体接口或泛型约束。", sourceFile, node, strict, rootDir));
|
|
6416
6705
|
}
|
|
6417
6706
|
for (const node of descendants(sourceFile)) {
|
|
6418
|
-
if (
|
|
6419
|
-
if (
|
|
6707
|
+
if (ts6.isAsExpression(node)) {
|
|
6708
|
+
if (ts6.isAsExpression(node.parent) || ts6.isTypeAssertionExpression(node.parent))
|
|
6420
6709
|
continue;
|
|
6421
6710
|
const assertedType = node.type.getText(sourceFile);
|
|
6422
6711
|
if (assertedType === "const")
|
|
6423
6712
|
continue;
|
|
6424
6713
|
diagnostics.push(makeDiagnostic("source-type-assertion", `生产源码包含类型断言 ${node.getText(sourceFile)};请优先使用类型守卫、satisfies 或显式边界解析。`, sourceFile, node, strict, rootDir));
|
|
6425
|
-
} else if (
|
|
6426
|
-
if (
|
|
6714
|
+
} else if (ts6.isTypeAssertionExpression(node)) {
|
|
6715
|
+
if (ts6.isAsExpression(node.parent) || ts6.isTypeAssertionExpression(node.parent))
|
|
6427
6716
|
continue;
|
|
6428
6717
|
diagnostics.push(makeDiagnostic("source-type-assertion", `生产源码包含类型断言 ${node.getText(sourceFile)};请优先使用类型守卫、satisfies 或显式边界解析。`, sourceFile, node, strict, rootDir));
|
|
6429
|
-
} else if (
|
|
6718
|
+
} else if (ts6.isNonNullExpression(node)) {
|
|
6430
6719
|
diagnostics.push(makeDiagnostic("source-non-null-assertion", `生产源码包含非空断言 ${node.getText(sourceFile)};请显式处理 null/undefined。`, sourceFile, node, strict, rootDir));
|
|
6431
6720
|
}
|
|
6432
6721
|
}
|
|
6433
|
-
for (const declaration of descendantsOfKind2(sourceFile,
|
|
6722
|
+
for (const declaration of descendantsOfKind2(sourceFile, ts6.isVariableDeclaration)) {
|
|
6434
6723
|
const initializer = declaration.initializer;
|
|
6435
6724
|
if (!initializer || declaration.type)
|
|
6436
6725
|
continue;
|
|
@@ -6446,11 +6735,11 @@ function scanSourceFile(sourceFile, checker, rootDir, diagnostics, strict) {
|
|
|
6446
6735
|
if (isLetDeclaration(declaration) && isLiteralSyntax(initializer) && !isLiteralType(declarationType)) {
|
|
6447
6736
|
diagnostics.push(makeDiagnostic("source-implicit-widening", `变量 ${declaration.name.getText(sourceFile)} 的字面量类型从 ${checker.typeToString(initializerType, initializer)} 隐式宽化为 ${checker.typeToString(declarationType, declaration)};请补充类型或使用 const。`, sourceFile, declaration, strict, rootDir));
|
|
6448
6737
|
}
|
|
6449
|
-
if (
|
|
6738
|
+
if (ts6.isObjectLiteralExpression(initializer) && isConstDeclaration(declaration) && initializer.getText(sourceFile).length > 0 && initializer.properties.some((property) => ts6.isPropertyAssignment(property) && property.initializer !== undefined && !ts6.isAsExpression(property.initializer) && isLiteralExpression(property.initializer))) {
|
|
6450
6739
|
diagnostics.push(makeDiagnostic("source-implicit-widening", `常量对象 ${declaration.name.getText(sourceFile)} 的字面量属性会隐式宽化;请补充对象类型或使用 as const。`, sourceFile, declaration, strict, rootDir));
|
|
6451
6740
|
}
|
|
6452
6741
|
}
|
|
6453
|
-
for (const parameter of descendantsOfKind2(sourceFile,
|
|
6742
|
+
for (const parameter of descendantsOfKind2(sourceFile, ts6.isParameter)) {
|
|
6454
6743
|
if (parameter.type)
|
|
6455
6744
|
continue;
|
|
6456
6745
|
for (const name of bindingNames(parameter.name)) {
|
|
@@ -6461,10 +6750,10 @@ function scanSourceFile(sourceFile, checker, rootDir, diagnostics, strict) {
|
|
|
6461
6750
|
}
|
|
6462
6751
|
}
|
|
6463
6752
|
function readProjectConfig2(configPath) {
|
|
6464
|
-
const config =
|
|
6753
|
+
const config = ts6.readConfigFile(configPath, (file) => readFileSync2(file, "utf8"));
|
|
6465
6754
|
if (config.error)
|
|
6466
6755
|
return { options: {}, errors: [config.error] };
|
|
6467
|
-
const parsed =
|
|
6756
|
+
const parsed = ts6.parseJsonConfigFileContent(config.config, ts6.sys, dirname3(configPath));
|
|
6468
6757
|
return { options: parsed.options, errors: parsed.errors };
|
|
6469
6758
|
}
|
|
6470
6759
|
function isProductionSource(rootDir, sourceFile, excludes, outDir) {
|
|
@@ -6484,42 +6773,42 @@ function globMatches(value, pattern) {
|
|
|
6484
6773
|
return new RegExp(`^${escaped}$`).test(value);
|
|
6485
6774
|
}
|
|
6486
6775
|
function bindingNames(name) {
|
|
6487
|
-
if (
|
|
6776
|
+
if (ts6.isIdentifier(name))
|
|
6488
6777
|
return [name];
|
|
6489
|
-
return name.elements.flatMap((element) =>
|
|
6778
|
+
return name.elements.flatMap((element) => ts6.isBindingElement(element) ? bindingNames(element.name) : []);
|
|
6490
6779
|
}
|
|
6491
6780
|
function isLiteralExpression(node) {
|
|
6492
6781
|
if (!node)
|
|
6493
6782
|
return false;
|
|
6494
6783
|
return [
|
|
6495
|
-
|
|
6496
|
-
|
|
6497
|
-
|
|
6498
|
-
|
|
6784
|
+
ts6.SyntaxKind.StringLiteral,
|
|
6785
|
+
ts6.SyntaxKind.NumericLiteral,
|
|
6786
|
+
ts6.SyntaxKind.TrueKeyword,
|
|
6787
|
+
ts6.SyntaxKind.FalseKeyword
|
|
6499
6788
|
].includes(node.kind);
|
|
6500
6789
|
}
|
|
6501
6790
|
function isLiteralSyntax(node) {
|
|
6502
|
-
return
|
|
6791
|
+
return ts6.isStringLiteral(node) || ts6.isNumericLiteral(node) || node.kind === ts6.SyntaxKind.TrueKeyword || node.kind === ts6.SyntaxKind.FalseKeyword;
|
|
6503
6792
|
}
|
|
6504
6793
|
function isLiteralType(type) {
|
|
6505
|
-
return (type.flags & (
|
|
6794
|
+
return (type.flags & (ts6.TypeFlags.StringLiteral | ts6.TypeFlags.NumberLiteral | ts6.TypeFlags.BooleanLiteral | ts6.TypeFlags.BigIntLiteral)) !== 0;
|
|
6506
6795
|
}
|
|
6507
6796
|
function isAnyType(type) {
|
|
6508
|
-
return (type.flags &
|
|
6797
|
+
return (type.flags & ts6.TypeFlags.Any) !== 0;
|
|
6509
6798
|
}
|
|
6510
6799
|
function isLetDeclaration(declaration) {
|
|
6511
|
-
return
|
|
6800
|
+
return ts6.isVariableDeclarationList(declaration.parent) && (declaration.parent.flags & ts6.NodeFlags.Let) !== 0;
|
|
6512
6801
|
}
|
|
6513
6802
|
function isConstDeclaration(declaration) {
|
|
6514
|
-
return
|
|
6803
|
+
return ts6.isVariableDeclarationList(declaration.parent) && (declaration.parent.flags & ts6.NodeFlags.Const) !== 0;
|
|
6515
6804
|
}
|
|
6516
6805
|
function descendants(root) {
|
|
6517
6806
|
const result = [];
|
|
6518
6807
|
const visit = (node) => {
|
|
6519
6808
|
result.push(node);
|
|
6520
|
-
|
|
6809
|
+
ts6.forEachChild(node, visit);
|
|
6521
6810
|
};
|
|
6522
|
-
|
|
6811
|
+
ts6.forEachChild(root, visit);
|
|
6523
6812
|
return result;
|
|
6524
6813
|
}
|
|
6525
6814
|
function descendantsOfKind2(root, predicate) {
|
|
@@ -6527,9 +6816,9 @@ function descendantsOfKind2(root, predicate) {
|
|
|
6527
6816
|
const visit = (node) => {
|
|
6528
6817
|
if (predicate(node))
|
|
6529
6818
|
result.push(node);
|
|
6530
|
-
|
|
6819
|
+
ts6.forEachChild(node, visit);
|
|
6531
6820
|
};
|
|
6532
|
-
|
|
6821
|
+
ts6.forEachChild(root, visit);
|
|
6533
6822
|
return result;
|
|
6534
6823
|
}
|
|
6535
6824
|
function makeDiagnostic(code, message, fileOrSourceFile, node, strict, rootDir) {
|
|
@@ -6550,7 +6839,7 @@ function normalizeRelative(rootDir, filePath) {
|
|
|
6550
6839
|
return relative3(rootDir, filePath).split(sep3).join("/").replace(/^\.\//, "");
|
|
6551
6840
|
}
|
|
6552
6841
|
function isAnyKeyword(node) {
|
|
6553
|
-
return node.kind ===
|
|
6842
|
+
return node.kind === ts6.SyntaxKind.AnyKeyword;
|
|
6554
6843
|
}
|
|
6555
6844
|
|
|
6556
6845
|
// src/route-contracts.ts
|
|
@@ -6643,6 +6932,9 @@ async function compileProject(options) {
|
|
|
6643
6932
|
...graph.diagnostics ?? [],
|
|
6644
6933
|
...validateGraph(graph, options)
|
|
6645
6934
|
];
|
|
6935
|
+
if (diagnostics.some((item) => item.code === "runtime-injection-disallowed")) {
|
|
6936
|
+
return { diagnostics, graph, written: [] };
|
|
6937
|
+
}
|
|
6646
6938
|
if (options.strict) {
|
|
6647
6939
|
for (const diagnostic of diagnostics) {
|
|
6648
6940
|
if (diagnostic.severity === "warn")
|
|
@@ -6701,6 +6993,9 @@ async function checkProject(options) {
|
|
|
6701
6993
|
...graph.diagnostics ?? [],
|
|
6702
6994
|
...validateGraph(graph, options)
|
|
6703
6995
|
];
|
|
6996
|
+
if (diagnostics.some((item) => item.code === "runtime-injection-disallowed")) {
|
|
6997
|
+
return { diagnostics, graph, upToDate: false, mismatches: ["Runtime DI must be migrated to constructor injection."] };
|
|
6998
|
+
}
|
|
6704
6999
|
if (options.strict) {
|
|
6705
7000
|
for (const diagnostic of diagnostics) {
|
|
6706
7001
|
if (diagnostic.severity === "warn")
|
|
@@ -10839,7 +11134,7 @@ function compileOptionsFromConfig(config, cwd = process.cwd()) {
|
|
|
10839
11134
|
import { randomUUID } from "node:crypto";
|
|
10840
11135
|
import { lstat, readFile as readFile4, realpath, rename as rename2, unlink as unlink2, writeFile as writeFile2 } from "node:fs/promises";
|
|
10841
11136
|
import { dirname as dirname6, isAbsolute as isAbsolute2, relative as relative8, resolve as resolve8, sep as sep8 } from "node:path";
|
|
10842
|
-
import * as
|
|
11137
|
+
import * as ts9 from "@typescript/typescript6";
|
|
10843
11138
|
async function applyDiagnosticFix(fix, options = {}) {
|
|
10844
11139
|
if (!fix || typeof fix.targetFile !== "string")
|
|
10845
11140
|
throw new Error("Invalid DiagnosticFix");
|
|
@@ -10864,7 +11159,7 @@ async function applyDiagnosticFix(fix, options = {}) {
|
|
|
10864
11159
|
if (!current || current.initializer.getText(source) !== fix.expectedExpression) {
|
|
10865
11160
|
throw new Error("Command mode changed since diagnosis; analyze the project again");
|
|
10866
11161
|
}
|
|
10867
|
-
content = replaceProperty(source, object, fix.property,
|
|
11162
|
+
content = replaceProperty(source, object, fix.property, ts9.factory.createStringLiteral(fix.value));
|
|
10868
11163
|
break;
|
|
10869
11164
|
}
|
|
10870
11165
|
case "add_module_import": {
|
|
@@ -10875,15 +11170,15 @@ async function applyDiagnosticFix(fix, options = {}) {
|
|
|
10875
11170
|
source = parse3(file, withImport);
|
|
10876
11171
|
const object = unique(moduleObjects(source).filter((candidate) => !fix.targetModule || stringProperty(candidate, "name") === fix.targetModule), "target module");
|
|
10877
11172
|
const imports = property(object, "imports");
|
|
10878
|
-
if (imports && !
|
|
11173
|
+
if (imports && !ts9.isArrayLiteralExpression(imports.initializer)) {
|
|
10879
11174
|
throw new Error("Module imports must be a static array");
|
|
10880
11175
|
}
|
|
10881
|
-
const values = imports &&
|
|
10882
|
-
if (values.some(
|
|
11176
|
+
const values = imports && ts9.isArrayLiteralExpression(imports.initializer) ? imports.initializer.elements : [];
|
|
11177
|
+
if (values.some(ts9.isSpreadElement))
|
|
10883
11178
|
throw new Error("Module imports cannot contain spread elements");
|
|
10884
|
-
content = values.some((value) =>
|
|
11179
|
+
content = values.some((value) => ts9.isIdentifier(value) && value.text === fix.symbol) ? withImport : replaceProperty(source, object, "imports", ts9.factory.createArrayLiteralExpression([
|
|
10885
11180
|
...values,
|
|
10886
|
-
|
|
11181
|
+
ts9.factory.createIdentifier(fix.symbol)
|
|
10887
11182
|
]));
|
|
10888
11183
|
break;
|
|
10889
11184
|
}
|
|
@@ -10895,24 +11190,24 @@ async function applyDiagnosticFix(fix, options = {}) {
|
|
|
10895
11190
|
const command = findClass(source, fix.command);
|
|
10896
11191
|
const object = decoratorObject(command, "Command");
|
|
10897
11192
|
const current = property(object, "permission");
|
|
10898
|
-
if (current && (!
|
|
11193
|
+
if (current && (!ts9.isStringLiteral(current.initializer) || current.initializer.text !== permission)) {
|
|
10899
11194
|
throw new Error("Command permission already exists with a different value");
|
|
10900
11195
|
}
|
|
10901
|
-
content = current ? original : replaceProperty(source, object, "permission",
|
|
11196
|
+
content = current ? original : replaceProperty(source, object, "permission", ts9.factory.createStringLiteral(permission));
|
|
10902
11197
|
break;
|
|
10903
11198
|
}
|
|
10904
11199
|
case "add_route_parameter_binding": {
|
|
10905
11200
|
const controller = findClass(source, fix.controller);
|
|
10906
|
-
const method = unique(controller.members.filter((member) =>
|
|
10907
|
-
const parameter = unique(method.parameters.filter((candidate) =>
|
|
11201
|
+
const method = unique(controller.members.filter((member) => ts9.isMethodDeclaration(member) && nameOf(member.name) === fix.route), "route handler");
|
|
11202
|
+
const parameter = unique(method.parameters.filter((candidate) => ts9.isIdentifier(candidate.name) && candidate.name.text === fix.parameter), "same-named route parameter");
|
|
10908
11203
|
const binding = fix.binding === "param" ? "Param" : fix.binding === "query" ? "Query" : undefined;
|
|
10909
11204
|
if (!binding)
|
|
10910
11205
|
throw new Error("Invalid route binding");
|
|
10911
|
-
const decorators =
|
|
11206
|
+
const decorators = ts9.getDecorators(parameter) ?? [];
|
|
10912
11207
|
if (decorators.length > 0)
|
|
10913
11208
|
throw new Error("Parameter already has a decorator");
|
|
10914
|
-
const framework = unique(source.statements.filter((statement) =>
|
|
10915
|
-
if (!
|
|
11209
|
+
const framework = unique(source.statements.filter((statement) => ts9.isImportDeclaration(statement) && statement.importClause?.namedBindings && ts9.isNamedImports(statement.importClause.namedBindings) && statement.importClause.namedBindings.elements.some((element) => ["Controller", "Get", "Post", "Put", "Patch", "Delete", "Head", "Options"].includes(element.name.text))), "framework import");
|
|
11210
|
+
if (!ts9.isImportDeclaration(framework) || !ts9.isStringLiteral(framework.moduleSpecifier)) {
|
|
10916
11211
|
throw new Error("Framework import must be static");
|
|
10917
11212
|
}
|
|
10918
11213
|
const edited = original.slice(0, parameter.getStart(source)) + `@${binding}(${JSON.stringify(fix.parameter)}) ` + original.slice(parameter.getStart(source));
|
|
@@ -10938,15 +11233,15 @@ async function applyDiagnosticFix(fix, options = {}) {
|
|
|
10938
11233
|
return result;
|
|
10939
11234
|
}
|
|
10940
11235
|
function parse3(file, text) {
|
|
10941
|
-
const result =
|
|
11236
|
+
const result = ts9.transpileModule(text, {
|
|
10942
11237
|
fileName: file,
|
|
10943
11238
|
reportDiagnostics: true,
|
|
10944
|
-
compilerOptions: { target:
|
|
11239
|
+
compilerOptions: { target: ts9.ScriptTarget.ESNext, experimentalDecorators: true }
|
|
10945
11240
|
});
|
|
10946
|
-
if (result.diagnostics?.some((item) => item.category ===
|
|
11241
|
+
if (result.diagnostics?.some((item) => item.category === ts9.DiagnosticCategory.Error)) {
|
|
10947
11242
|
throw new Error("Cannot fix syntactically invalid TypeScript");
|
|
10948
11243
|
}
|
|
10949
|
-
return
|
|
11244
|
+
return ts9.createSourceFile(file, text, ts9.ScriptTarget.Latest, true, ts9.ScriptKind.TS);
|
|
10950
11245
|
}
|
|
10951
11246
|
function unique(items, description) {
|
|
10952
11247
|
if (items.length !== 1)
|
|
@@ -10960,50 +11255,50 @@ function identifier(value) {
|
|
|
10960
11255
|
function nameOf(name) {
|
|
10961
11256
|
if (!name)
|
|
10962
11257
|
return "";
|
|
10963
|
-
return
|
|
11258
|
+
return ts9.isIdentifier(name) || ts9.isStringLiteral(name) || ts9.isNumericLiteral(name) ? name.text : "";
|
|
10964
11259
|
}
|
|
10965
11260
|
function property(object, key) {
|
|
10966
|
-
if (object.properties.some((item) => !
|
|
11261
|
+
if (object.properties.some((item) => !ts9.isPropertyAssignment(item) || ts9.isComputedPropertyName(item.name))) {
|
|
10967
11262
|
throw new Error("Fix requires explicit static object properties");
|
|
10968
11263
|
}
|
|
10969
|
-
const values = object.properties.filter((item) =>
|
|
11264
|
+
const values = object.properties.filter((item) => ts9.isPropertyAssignment(item) && nameOf(item.name) === key);
|
|
10970
11265
|
if (values.length > 1)
|
|
10971
11266
|
throw new Error(`Duplicate '${key}' property`);
|
|
10972
11267
|
return values[0];
|
|
10973
11268
|
}
|
|
10974
11269
|
function stringProperty(object, key) {
|
|
10975
11270
|
const value = property(object, key)?.initializer;
|
|
10976
|
-
return value &&
|
|
11271
|
+
return value && ts9.isStringLiteral(value) ? value.text : undefined;
|
|
10977
11272
|
}
|
|
10978
11273
|
function replaceProperty(source, object, key, value) {
|
|
10979
11274
|
const previous = property(object, key);
|
|
10980
|
-
const replacement =
|
|
11275
|
+
const replacement = ts9.factory.createPropertyAssignment(key, value);
|
|
10981
11276
|
const properties = object.properties.map((item) => item === previous ? replacement : item);
|
|
10982
11277
|
if (!previous)
|
|
10983
11278
|
properties.push(replacement);
|
|
10984
|
-
const updated =
|
|
10985
|
-
return source.text.slice(0, object.getStart(source)) +
|
|
11279
|
+
const updated = ts9.factory.updateObjectLiteralExpression(object, properties);
|
|
11280
|
+
return source.text.slice(0, object.getStart(source)) + ts9.createPrinter().printNode(ts9.EmitHint.Expression, updated, source) + source.text.slice(object.end);
|
|
10986
11281
|
}
|
|
10987
11282
|
function findClass(source, name) {
|
|
10988
|
-
return unique(source.statements.filter((statement) =>
|
|
11283
|
+
return unique(source.statements.filter((statement) => ts9.isClassDeclaration(statement) && statement.name?.text === name), `class '${name}'`);
|
|
10989
11284
|
}
|
|
10990
11285
|
function decoratorObject(node, name) {
|
|
10991
|
-
const decorator = unique((
|
|
10992
|
-
const argument =
|
|
10993
|
-
if (!argument || !
|
|
11286
|
+
const decorator = unique((ts9.getDecorators(node) ?? []).filter((item) => ts9.isCallExpression(item.expression) && item.expression.expression.getText() === name), `@${name} decorator`);
|
|
11287
|
+
const argument = ts9.isCallExpression(decorator.expression) ? decorator.expression.arguments[0] : undefined;
|
|
11288
|
+
if (!argument || !ts9.isObjectLiteralExpression(argument))
|
|
10994
11289
|
throw new Error(`@${name} requires a static object`);
|
|
10995
11290
|
return argument;
|
|
10996
11291
|
}
|
|
10997
11292
|
function moduleObjects(source) {
|
|
10998
11293
|
const result = [];
|
|
10999
11294
|
for (const statement of source.statements) {
|
|
11000
|
-
if (
|
|
11295
|
+
if (ts9.isClassDeclaration(statement) && (ts9.getDecorators(statement) ?? []).some((item) => ts9.isCallExpression(item.expression) && item.expression.expression.getText() === "Module")) {
|
|
11001
11296
|
result.push(decoratorObject(statement, "Module"));
|
|
11002
11297
|
}
|
|
11003
|
-
if (
|
|
11298
|
+
if (ts9.isVariableStatement(statement)) {
|
|
11004
11299
|
for (const declaration of statement.declarationList.declarations) {
|
|
11005
11300
|
const call = declaration.initializer;
|
|
11006
|
-
if (call &&
|
|
11301
|
+
if (call && ts9.isCallExpression(call) && ["defineModule", "defineFeatureSlice"].includes(call.expression.getText()) && call.arguments[0] && ts9.isObjectLiteralExpression(call.arguments[0])) {
|
|
11007
11302
|
result.push(call.arguments[0]);
|
|
11008
11303
|
}
|
|
11009
11304
|
}
|
|
@@ -11017,19 +11312,19 @@ function importSymbol(source, path, symbol) {
|
|
|
11017
11312
|
const target = resolve8(dirname6(source.fileName), path).replace(/\.(tsx?|mts|cts)$/, "");
|
|
11018
11313
|
if (current === target)
|
|
11019
11314
|
return source.text;
|
|
11020
|
-
const matches = source.statements.filter((item) =>
|
|
11315
|
+
const matches = source.statements.filter((item) => ts9.isImportDeclaration(item) && ts9.isStringLiteral(item.moduleSpecifier) && item.moduleSpecifier.text === path);
|
|
11021
11316
|
if (matches.length > 1)
|
|
11022
11317
|
throw new Error(`Ambiguous imports from '${path}'`);
|
|
11023
11318
|
const match = matches[0];
|
|
11024
|
-
if (match &&
|
|
11319
|
+
if (match && ts9.isImportDeclaration(match) && match.importClause?.namedBindings && ts9.isNamedImports(match.importClause.namedBindings) && !match.importClause.isTypeOnly) {
|
|
11025
11320
|
if (match.importClause.namedBindings.elements.some((item) => item.name.text === symbol))
|
|
11026
11321
|
return source.text;
|
|
11027
11322
|
const bindings = match.importClause.namedBindings;
|
|
11028
|
-
const updated =
|
|
11323
|
+
const updated = ts9.factory.updateNamedImports(bindings, [
|
|
11029
11324
|
...bindings.elements,
|
|
11030
|
-
|
|
11325
|
+
ts9.factory.createImportSpecifier(false, undefined, ts9.factory.createIdentifier(symbol))
|
|
11031
11326
|
]);
|
|
11032
|
-
return source.text.slice(0, bindings.getStart(source)) +
|
|
11327
|
+
return source.text.slice(0, bindings.getStart(source)) + ts9.createPrinter().printNode(ts9.EmitHint.Unspecified, updated, source) + source.text.slice(bindings.end);
|
|
11033
11328
|
}
|
|
11034
11329
|
if (match)
|
|
11035
11330
|
throw new Error(`Import from '${path}' is not a named value import`);
|
|
@@ -11276,7 +11571,7 @@ function formatDeliveryPlan(result) {
|
|
|
11276
11571
|
import { mkdir as mkdir3, mkdtemp, readFile as readFile7, realpath as realpath3, rename as rename4, rm as rm2 } from "node:fs/promises";
|
|
11277
11572
|
import { readFileSync as readFileSync3 } from "node:fs";
|
|
11278
11573
|
import { dirname as dirname8, join as join8, relative as relative10, resolve as resolve11, sep as sep10 } from "node:path";
|
|
11279
|
-
import * as
|
|
11574
|
+
import * as ts11 from "@typescript/typescript6";
|
|
11280
11575
|
|
|
11281
11576
|
// src/delivery-files.ts
|
|
11282
11577
|
import { createHash as createHash8 } from "node:crypto";
|
|
@@ -11510,19 +11805,19 @@ function renderDeliveryTarget(graph, target, options) {
|
|
|
11510
11805
|
import { isBuiltin } from "node:module";
|
|
11511
11806
|
import { readFile as readFile6 } from "node:fs/promises";
|
|
11512
11807
|
import { resolve as resolve10 } from "node:path";
|
|
11513
|
-
import * as
|
|
11808
|
+
import * as ts10 from "@typescript/typescript6";
|
|
11514
11809
|
function checkStaticImports(path, contents) {
|
|
11515
11810
|
if (!/\.[cm]?[jt]sx?$/.test(path))
|
|
11516
11811
|
return;
|
|
11517
|
-
const source =
|
|
11812
|
+
const source = ts10.createSourceFile(path, new TextDecoder().decode(contents), ts10.ScriptTarget.Latest, true);
|
|
11518
11813
|
function visit(node) {
|
|
11519
|
-
if (
|
|
11814
|
+
if (ts10.isCallExpression(node) && (node.expression.kind === ts10.SyntaxKind.ImportKeyword || ts10.isIdentifier(node.expression) && node.expression.text === "require")) {
|
|
11520
11815
|
const argument = node.arguments[0];
|
|
11521
|
-
if (!argument || !
|
|
11816
|
+
if (!argument || !ts10.isStringLiteral(argument) && !ts10.isNoSubstitutionTemplateLiteral(argument)) {
|
|
11522
11817
|
throw new Error("Computed module loading is not supported in independent delivery bundles.");
|
|
11523
11818
|
}
|
|
11524
11819
|
}
|
|
11525
|
-
|
|
11820
|
+
ts10.forEachChild(node, visit);
|
|
11526
11821
|
}
|
|
11527
11822
|
visit(source);
|
|
11528
11823
|
}
|
|
@@ -11611,7 +11906,7 @@ async function buildDeliveryProject(options, delivery) {
|
|
|
11611
11906
|
const settings = parseDeliveryOptions(delivery);
|
|
11612
11907
|
if (typeof Bun === "undefined")
|
|
11613
11908
|
throw new Error("Independent delivery builds require Bun.");
|
|
11614
|
-
const configPath =
|
|
11909
|
+
const configPath = ts11.findConfigFile(resolve11(options.rootDir), ts11.sys.fileExists);
|
|
11615
11910
|
if (!configPath)
|
|
11616
11911
|
throw new Error("Independent delivery builds require a project tsconfig.json.");
|
|
11617
11912
|
const lexicalProject = dirname8(configPath);
|
|
@@ -11627,8 +11922,8 @@ async function buildDeliveryProject(options, delivery) {
|
|
|
11627
11922
|
if (inside(generatedRoot, sourceRoot))
|
|
11628
11923
|
throw new Error("Output must not contain the application source root.");
|
|
11629
11924
|
const configInputs = new Map;
|
|
11630
|
-
const parsedConfig =
|
|
11631
|
-
...
|
|
11925
|
+
const parsedConfig = ts11.getParsedCommandLineOfConfigFile(await realpath3(configPath), {}, {
|
|
11926
|
+
...ts11.sys,
|
|
11632
11927
|
readFile(path) {
|
|
11633
11928
|
const contents = readFileSync3(path, "utf8");
|
|
11634
11929
|
configInputs.set(resolve11(path), digest(contents));
|
|
@@ -11714,7 +12009,7 @@ async function buildDeliveryProject(options, delivery) {
|
|
|
11714
12009
|
for (const [name, contents] of Object.entries(generated))
|
|
11715
12010
|
await writeArtifact(stage, `generated/${name}`, contents);
|
|
11716
12011
|
{
|
|
11717
|
-
const program =
|
|
12012
|
+
const program = ts11.createProgram({
|
|
11718
12013
|
rootNames: [
|
|
11719
12014
|
...parsedConfig.fileNames.filter((path) => !inside(generatedRoot, path)),
|
|
11720
12015
|
...Object.keys(generated).map((name) => join8(stage, "generated", name))
|
|
@@ -11722,12 +12017,12 @@ async function buildDeliveryProject(options, delivery) {
|
|
|
11722
12017
|
options: { ...parsedConfig.options, rootDir: project },
|
|
11723
12018
|
...parsedConfig.projectReferences ? { projectReferences: parsedConfig.projectReferences } : {}
|
|
11724
12019
|
});
|
|
11725
|
-
const diagnostics =
|
|
11726
|
-
if (diagnostics.some((item) => item.category ===
|
|
11727
|
-
return failed(diagnostics.filter((item) => item.category ===
|
|
12020
|
+
const diagnostics = ts11.getPreEmitDiagnostics(program);
|
|
12021
|
+
if (diagnostics.some((item) => item.category === ts11.DiagnosticCategory.Error)) {
|
|
12022
|
+
return failed(diagnostics.filter((item) => item.category === ts11.DiagnosticCategory.Error).map((item) => ({
|
|
11728
12023
|
severity: "error",
|
|
11729
12024
|
code: "delivery-generated-type-error",
|
|
11730
|
-
message:
|
|
12025
|
+
message: ts11.flattenDiagnosticMessageText(item.messageText, `
|
|
11731
12026
|
`),
|
|
11732
12027
|
...item.file ? { file: item.file.fileName } : {}
|
|
11733
12028
|
})));
|
|
@@ -11859,7 +12154,7 @@ async function buildDeliveryProject(options, delivery) {
|
|
|
11859
12154
|
// src/migrations.ts
|
|
11860
12155
|
import { rename as rename5, readFile as readFile9, writeFile as writeFile4, rm as rm3 } from "node:fs/promises";
|
|
11861
12156
|
import { relative as relative11, resolve as resolve13 } from "node:path";
|
|
11862
|
-
import * as
|
|
12157
|
+
import * as ts12 from "@typescript/typescript6";
|
|
11863
12158
|
|
|
11864
12159
|
// src/migration-policy.ts
|
|
11865
12160
|
import { readFile as readFile8 } from "node:fs/promises";
|
|
@@ -11880,7 +12175,7 @@ function migrationDependencies() {
|
|
|
11880
12175
|
return {
|
|
11881
12176
|
"@supacloud/app": "0.14.0",
|
|
11882
12177
|
"@supacloud/compiler": compilerVersion(),
|
|
11883
|
-
"@supacloud/elysia": "0.
|
|
12178
|
+
"@supacloud/elysia": "0.17.0",
|
|
11884
12179
|
elysia: "1.4.30",
|
|
11885
12180
|
typescript: "7.0.2"
|
|
11886
12181
|
};
|
|
@@ -11903,29 +12198,29 @@ async function checkMigrationDependencies(rootDir) {
|
|
|
11903
12198
|
// src/migrations.ts
|
|
11904
12199
|
var ROUTE_DECORATORS2 = new Set(["Get", "Post", "Put", "Patch", "Delete", "Head", "Options"]);
|
|
11905
12200
|
var MIGRATION_COMPILER_OPTIONS = {
|
|
11906
|
-
target:
|
|
11907
|
-
module:
|
|
11908
|
-
moduleResolution:
|
|
12201
|
+
target: ts12.ScriptTarget.ES2022,
|
|
12202
|
+
module: ts12.ModuleKind.ESNext,
|
|
12203
|
+
moduleResolution: ts12.ModuleResolutionKind.Bundler,
|
|
11909
12204
|
noEmit: true,
|
|
11910
12205
|
skipLibCheck: true
|
|
11911
12206
|
};
|
|
11912
12207
|
function migrationCompilerOptions(rootDir) {
|
|
11913
12208
|
if (!rootDir)
|
|
11914
12209
|
return MIGRATION_COMPILER_OPTIONS;
|
|
11915
|
-
const configPath =
|
|
12210
|
+
const configPath = ts12.findConfigFile(rootDir, ts12.sys.fileExists);
|
|
11916
12211
|
if (!configPath)
|
|
11917
12212
|
return MIGRATION_COMPILER_OPTIONS;
|
|
11918
12213
|
const configHost = {
|
|
11919
|
-
...
|
|
12214
|
+
...ts12.sys,
|
|
11920
12215
|
onUnRecoverableConfigFileDiagnostic: (_diagnostic) => {}
|
|
11921
12216
|
};
|
|
11922
|
-
const parsed =
|
|
12217
|
+
const parsed = ts12.getParsedCommandLineOfConfigFile(configPath, {}, configHost);
|
|
11923
12218
|
if (!parsed || parsed.errors.length > 0)
|
|
11924
12219
|
return MIGRATION_COMPILER_OPTIONS;
|
|
11925
12220
|
return { ...parsed.options, noEmit: true, skipLibCheck: true };
|
|
11926
12221
|
}
|
|
11927
12222
|
function propertyName2(property) {
|
|
11928
|
-
if (
|
|
12223
|
+
if (ts12.isIdentifier(property) || ts12.isStringLiteral(property) || ts12.isNumericLiteral(property))
|
|
11929
12224
|
return property.text;
|
|
11930
12225
|
return;
|
|
11931
12226
|
}
|
|
@@ -11935,7 +12230,7 @@ function lineOf2(sourceFile, node) {
|
|
|
11935
12230
|
function resolveSymbol(symbol, checker) {
|
|
11936
12231
|
if (!symbol)
|
|
11937
12232
|
return;
|
|
11938
|
-
for (let guard = 0;guard < 4 && (symbol.flags &
|
|
12233
|
+
for (let guard = 0;guard < 4 && (symbol.flags & ts12.SymbolFlags.Alias) !== 0; guard += 1) {
|
|
11939
12234
|
const aliased = checker.getAliasedSymbol(symbol);
|
|
11940
12235
|
if (aliased === symbol)
|
|
11941
12236
|
break;
|
|
@@ -11944,12 +12239,12 @@ function resolveSymbol(symbol, checker) {
|
|
|
11944
12239
|
return symbol;
|
|
11945
12240
|
}
|
|
11946
12241
|
function symbolForExpression(expression, checker) {
|
|
11947
|
-
const location =
|
|
12242
|
+
const location = ts12.isIdentifier(expression) ? expression : ts12.isPropertyAccessExpression(expression) ? expression.name : ts12.isElementAccessExpression(expression) && expression.argumentExpression && ts12.isStringLiteral(expression.argumentExpression) ? expression : undefined;
|
|
11948
12243
|
return location ? resolveSymbol(checker.getSymbolAtLocation(location), checker) : undefined;
|
|
11949
12244
|
}
|
|
11950
12245
|
function unwrapExpression(expression) {
|
|
11951
12246
|
let current = expression;
|
|
11952
|
-
while (
|
|
12247
|
+
while (ts12.isAsExpression(current) || ts12.isSatisfiesExpression(current) || ts12.isParenthesizedExpression(current) || ts12.isTypeAssertionExpression(current)) {
|
|
11953
12248
|
current = current.expression;
|
|
11954
12249
|
}
|
|
11955
12250
|
return current;
|
|
@@ -11962,7 +12257,7 @@ function isDefineRouteContractCall(node, checker) {
|
|
|
11962
12257
|
}
|
|
11963
12258
|
function isRouteDecoratorCall(node, checker) {
|
|
11964
12259
|
const name = node.expression.getText(node.getSourceFile());
|
|
11965
|
-
if (
|
|
12260
|
+
if (ts12.isIdentifier(node.expression) && ROUTE_DECORATORS2.has(node.expression.text))
|
|
11966
12261
|
return true;
|
|
11967
12262
|
if (ROUTE_DECORATORS2.has(name))
|
|
11968
12263
|
return true;
|
|
@@ -11976,14 +12271,14 @@ function resolveStaticObjectLiteral2(input, checker, seen = new Set) {
|
|
|
11976
12271
|
if (seen.has(expression))
|
|
11977
12272
|
return;
|
|
11978
12273
|
seen.add(expression);
|
|
11979
|
-
if (
|
|
12274
|
+
if (ts12.isObjectLiteralExpression(expression))
|
|
11980
12275
|
return expression;
|
|
11981
|
-
if (
|
|
12276
|
+
if (ts12.isCallExpression(expression) && isDefineRouteContractCall(expression, checker)) {
|
|
11982
12277
|
return resolveStaticObjectLiteral2(expression.arguments[0], checker, seen);
|
|
11983
12278
|
}
|
|
11984
12279
|
const symbol = symbolForExpression(expression, checker);
|
|
11985
12280
|
for (const declaration of symbol?.declarations ?? []) {
|
|
11986
|
-
if (
|
|
12281
|
+
if (ts12.isVariableDeclaration(declaration) && declaration.initializer) {
|
|
11987
12282
|
const resolved = resolveStaticObjectLiteral2(declaration.initializer, checker, seen);
|
|
11988
12283
|
if (resolved)
|
|
11989
12284
|
return resolved;
|
|
@@ -11997,7 +12292,7 @@ function displayFile(rootDir, fileName) {
|
|
|
11997
12292
|
}
|
|
11998
12293
|
function createMigrationProgram(fileNames, sourceOverrides, rootDir) {
|
|
11999
12294
|
const compilerOptions = migrationCompilerOptions(rootDir);
|
|
12000
|
-
const host =
|
|
12295
|
+
const host = ts12.createCompilerHost(compilerOptions);
|
|
12001
12296
|
const getSourceFile = host.getSourceFile.bind(host);
|
|
12002
12297
|
const fileExists = host.fileExists.bind(host);
|
|
12003
12298
|
const readFile = host.readFile.bind(host);
|
|
@@ -12006,16 +12301,16 @@ function createMigrationProgram(fileNames, sourceOverrides, rootDir) {
|
|
|
12006
12301
|
host.readFile = (fileName) => sourceOverrides.get(resolve13(fileName)) ?? readFile(fileName);
|
|
12007
12302
|
host.getSourceFile = (fileName, languageVersion, onError, shouldCreateNewSourceFile) => {
|
|
12008
12303
|
const source = sourceOverrides.get(resolve13(fileName));
|
|
12009
|
-
return source === undefined ? getSourceFile(fileName, languageVersion, onError, shouldCreateNewSourceFile) :
|
|
12304
|
+
return source === undefined ? getSourceFile(fileName, languageVersion, onError, shouldCreateNewSourceFile) : ts12.createSourceFile(fileName, source, languageVersion, true);
|
|
12010
12305
|
};
|
|
12011
12306
|
host.getCurrentDirectory = () => rootDir ?? currentDirectory();
|
|
12012
|
-
return
|
|
12307
|
+
return ts12.createProgram(fileNames, compilerOptions, host);
|
|
12013
12308
|
}
|
|
12014
12309
|
function routeResponseProperties(object) {
|
|
12015
|
-
const responseProperties = object.properties.filter((property) =>
|
|
12310
|
+
const responseProperties = object.properties.filter((property) => ts12.isPropertyAssignment(property) && propertyName2(property.name) === "response");
|
|
12016
12311
|
return {
|
|
12017
12312
|
response: responseProperties[0],
|
|
12018
|
-
hasResponses: object.properties.some((property) => (
|
|
12313
|
+
hasResponses: object.properties.some((property) => (ts12.isPropertyAssignment(property) || ts12.isShorthandPropertyAssignment(property)) && propertyName2(property.name) === "responses"),
|
|
12019
12314
|
duplicateResponse: responseProperties.length > 1
|
|
12020
12315
|
};
|
|
12021
12316
|
}
|
|
@@ -12035,7 +12330,7 @@ function planRouteResponseMigration(sourceFiles, checker, rootDir, includedFiles
|
|
|
12035
12330
|
if (!includedFiles.has(sourcePath))
|
|
12036
12331
|
continue;
|
|
12037
12332
|
const visit = (node) => {
|
|
12038
|
-
if (
|
|
12333
|
+
if (ts12.isCallExpression(node) && isRouteDecoratorCall(node, checker)) {
|
|
12039
12334
|
const options = node.arguments[1];
|
|
12040
12335
|
const object = options && resolveStaticObjectLiteral2(options, checker);
|
|
12041
12336
|
if (object) {
|
|
@@ -12055,7 +12350,7 @@ function planRouteResponseMigration(sourceFiles, checker, rootDir, includedFiles
|
|
|
12055
12350
|
}
|
|
12056
12351
|
}
|
|
12057
12352
|
}
|
|
12058
|
-
|
|
12353
|
+
ts12.forEachChild(node, visit);
|
|
12059
12354
|
};
|
|
12060
12355
|
visit(sourceFile);
|
|
12061
12356
|
}
|
|
@@ -12215,7 +12510,7 @@ async function migrateProject(options) {
|
|
|
12215
12510
|
}
|
|
12216
12511
|
}
|
|
12217
12512
|
const include = options.include ?? ["**/*.ts", "**/*.tsx", "**/*.mts", "**/*.cts"];
|
|
12218
|
-
const files =
|
|
12513
|
+
const files = ts12.sys.readDirectory(rootDir, [".ts", ".tsx", ".mts", ".cts"], ["node_modules", "dist", "generated"], include).sort();
|
|
12219
12514
|
const results = [];
|
|
12220
12515
|
const issues = [];
|
|
12221
12516
|
const pendingWrites = new Map;
|
|
@@ -12827,6 +13122,7 @@ Usage:
|
|
|
12827
13122
|
supacloud-compiler openapi-diff <base.json> <current.json> [options]
|
|
12828
13123
|
supacloud-compiler fix <fix.json> [options]
|
|
12829
13124
|
supacloud-compiler graphql-schema --url <project-url> --key-env <name> [--token-env <name>]
|
|
13125
|
+
supacloud-compiler database-contracts <config.json> [--check]
|
|
12830
13126
|
|
|
12831
13127
|
Commands:
|
|
12832
13128
|
compile Compile application modules and generate artifacts
|
|
@@ -12878,6 +13174,18 @@ async function run() {
|
|
|
12878
13174
|
process.exit(0);
|
|
12879
13175
|
}
|
|
12880
13176
|
const command = args[0];
|
|
13177
|
+
if (command === "database-contracts") {
|
|
13178
|
+
const path = args[1];
|
|
13179
|
+
if (!path || path.startsWith("-") || args.slice(2).some((arg) => arg !== "--check")) {
|
|
13180
|
+
throw new Error("database-contracts requires <config.json> and optional --check");
|
|
13181
|
+
}
|
|
13182
|
+
await Promise.resolve().then(() => init_database_contracts());
|
|
13183
|
+
const result = await runDatabaseContractsFile(path, args.includes("--check"));
|
|
13184
|
+
console.log(JSON.stringify(result, null, 2));
|
|
13185
|
+
if (args.includes("--check") && !result.upToDate)
|
|
13186
|
+
process.exitCode = 1;
|
|
13187
|
+
return;
|
|
13188
|
+
}
|
|
12881
13189
|
if (!command || !["compile", "check", "dev", "graph", "explain", "context", "doctor", "migrate", "fix", "graphql-schema", "plan", "build-delivery", "openapi-export", "openapi-diff"].includes(command)) {
|
|
12882
13190
|
console.error(`Error: unknown command "${command}"`);
|
|
12883
13191
|
printUsage();
|
|
@@ -13049,7 +13357,7 @@ async function run() {
|
|
|
13049
13357
|
const currentPath = openApiDiffPaths[1];
|
|
13050
13358
|
if (!basePath || !currentPath)
|
|
13051
13359
|
throw new Error("openapi-diff requires two JSON file paths");
|
|
13052
|
-
const result = diffOpenApiDocuments(await readOpenApiJson(
|
|
13360
|
+
const result = diffOpenApiDocuments(await readOpenApiJson(resolve17(process.cwd(), basePath)), await readOpenApiJson(resolve17(process.cwd(), currentPath)));
|
|
13053
13361
|
console.log(json ? JSON.stringify(result, null, 2) : formatOpenApiDiff(result));
|
|
13054
13362
|
if (!result.ok)
|
|
13055
13363
|
process.exitCode = 1;
|
|
@@ -13064,8 +13372,8 @@ async function run() {
|
|
|
13064
13372
|
if (!modulePath || !outputPath)
|
|
13065
13373
|
throw new Error("openapi-export requires an OpenAPI module and output path");
|
|
13066
13374
|
const result = await exportGeneratedOpenApiJson({
|
|
13067
|
-
modulePath:
|
|
13068
|
-
outputPath:
|
|
13375
|
+
modulePath: resolve17(process.cwd(), modulePath),
|
|
13376
|
+
outputPath: resolve17(process.cwd(), outputPath),
|
|
13069
13377
|
...openApiExportSpace === undefined ? {} : { space: openApiExportSpace }
|
|
13070
13378
|
});
|
|
13071
13379
|
console.log(json ? JSON.stringify({ ok: true, ...result }, null, 2) : result.written ? `OpenAPI JSON written: ${result.path}` : `OpenAPI JSON matches: ${result.path}`);
|
|
@@ -13073,7 +13381,7 @@ async function run() {
|
|
|
13073
13381
|
}
|
|
13074
13382
|
if (command === "migrate") {
|
|
13075
13383
|
const result = await migrateProject({
|
|
13076
|
-
rootDir: rootDir ?
|
|
13384
|
+
rootDir: rootDir ? resolve17(process.cwd(), rootDir) : process.cwd(),
|
|
13077
13385
|
write: !dryRun,
|
|
13078
13386
|
...fromVersion === undefined ? {} : { fromVersion },
|
|
13079
13387
|
...toVersion === undefined ? {} : { toVersion }
|
|
@@ -13101,8 +13409,8 @@ async function run() {
|
|
|
13101
13409
|
if (checkSchema && command !== "graphql-schema")
|
|
13102
13410
|
throw new Error("--check is only supported by graphql-schema");
|
|
13103
13411
|
const defaults = resolveSupacloudConfig(loadedConfig, process.cwd());
|
|
13104
|
-
const resolvedRoot = rootDir ?
|
|
13105
|
-
const resolvedOut = outDir ?
|
|
13412
|
+
const resolvedRoot = rootDir ? resolve17(process.cwd(), rootDir) : defaults.rootDir;
|
|
13413
|
+
const resolvedOut = outDir ? resolve17(process.cwd(), outDir) : defaults.outDir;
|
|
13106
13414
|
const configured = compileOptionsFromConfig({
|
|
13107
13415
|
...loadedConfig,
|
|
13108
13416
|
root: resolvedRoot,
|
|
@@ -13121,7 +13429,7 @@ async function run() {
|
|
|
13121
13429
|
let delivery = loadedConfig.delivery;
|
|
13122
13430
|
if (deliveryPath !== undefined) {
|
|
13123
13431
|
try {
|
|
13124
|
-
delivery = JSON.parse(await
|
|
13432
|
+
delivery = JSON.parse(await readFile13(resolve17(process.cwd(), deliveryPath), "utf8"));
|
|
13125
13433
|
} catch {
|
|
13126
13434
|
throw new DeliveryConfigurationError;
|
|
13127
13435
|
}
|
|
@@ -13168,7 +13476,7 @@ ${item.suggestion ?? ""}`).join(`
|
|
|
13168
13476
|
} else if (command === "fix") {
|
|
13169
13477
|
if (!query)
|
|
13170
13478
|
throw new Error("fix requires a JSON file containing one DiagnosticFix");
|
|
13171
|
-
const fix = JSON.parse(await
|
|
13479
|
+
const fix = JSON.parse(await readFile13(resolve17(process.cwd(), query), "utf8"));
|
|
13172
13480
|
const result = await applyDiagnosticFix(fix, { rootDir: resolvedRoot, dryRun });
|
|
13173
13481
|
console.log(JSON.stringify({ ok: true, ...result }, null, 2));
|
|
13174
13482
|
} else if (command === "compile") {
|