@supacloud/compiler 0.28.0 → 0.30.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/LICENSE +202 -661
- package/NOTICE +8 -13
- package/README.md +1 -1
- package/dist/api-source.d.ts +11 -0
- package/dist/cli.js +1122 -667
- package/dist/diagnostics.d.ts +61 -0
- package/dist/diagnostics.js +333 -0
- package/dist/fixes.d.ts +3 -0
- package/dist/http-provider-migration.d.ts +6 -0
- package/dist/index.js +1040 -659
- package/dist/inspect.d.ts +6 -0
- package/dist/reactive-analysis.d.ts +4 -0
- package/dist/types.d.ts +8 -0
- package/package.json +9 -4
package/dist/cli.js
CHANGED
|
@@ -1663,6 +1663,12 @@ class ModuleGenerator {
|
|
|
1663
1663
|
}
|
|
1664
1664
|
renderFactories() {
|
|
1665
1665
|
const sections = [this.renderServicesFactory()];
|
|
1666
|
+
if (this.hasApplicationLifecycle("init")) {
|
|
1667
|
+
sections.push(this.renderApplicationLifecycle("init"));
|
|
1668
|
+
}
|
|
1669
|
+
if (this.hasApplicationLifecycle("destroy")) {
|
|
1670
|
+
sections.push(this.renderApplicationLifecycle("destroy"));
|
|
1671
|
+
}
|
|
1666
1672
|
if (this.hasFactoryContent("request")) {
|
|
1667
1673
|
sections.push(this.renderScopeFactory("request"));
|
|
1668
1674
|
sections.push(this.renderScopeDestroyer("request"));
|
|
@@ -1679,6 +1685,12 @@ class ModuleGenerator {
|
|
|
1679
1685
|
` name: ${JSON.stringify(this.module.name)} as const,`,
|
|
1680
1686
|
` createServices: create${this.pascal}Services,`
|
|
1681
1687
|
];
|
|
1688
|
+
if (this.hasApplicationLifecycle("init")) {
|
|
1689
|
+
lines.push(` initializeServices: initialize${this.pascal}Services,`);
|
|
1690
|
+
}
|
|
1691
|
+
if (this.hasApplicationLifecycle("destroy")) {
|
|
1692
|
+
lines.push(` destroyServices: destroy${this.pascal}Services,`);
|
|
1693
|
+
}
|
|
1682
1694
|
if (this.hasFactoryContent("request")) {
|
|
1683
1695
|
lines.push(` createRequestScope: create${this.pascal}RequestScope,`);
|
|
1684
1696
|
lines.push(` destroyRequestScope: destroy${this.pascal}RequestScope,`);
|
|
@@ -1701,6 +1713,40 @@ class ModuleGenerator {
|
|
|
1701
1713
|
hasFactoryContent(kind) {
|
|
1702
1714
|
return this.module.providers.some((p) => factoryOfScope(p.scope) === kind) || this.module.controllers.some((c) => factoryOfScope(c.scope) === kind);
|
|
1703
1715
|
}
|
|
1716
|
+
hasApplicationLifecycle(kind) {
|
|
1717
|
+
return this.applicationLifecyclePlan(kind).length > 0;
|
|
1718
|
+
}
|
|
1719
|
+
applicationLifecyclePlan(kind) {
|
|
1720
|
+
const result = [];
|
|
1721
|
+
const multiIndices = new Map;
|
|
1722
|
+
for (const provider of orderProviders(this.module.providers.filter((p) => factoryOfScope(p.scope) === "services"))) {
|
|
1723
|
+
const index = provider.multi ? multiIndices.get(provider.token) ?? 0 : undefined;
|
|
1724
|
+
if (index !== undefined)
|
|
1725
|
+
multiIndices.set(provider.token, index + 1);
|
|
1726
|
+
const owned = provider.kind !== "existing";
|
|
1727
|
+
const selected = kind === "init" ? provider.hasOnInit : provider.hasOnDestroy;
|
|
1728
|
+
if (owned && selected)
|
|
1729
|
+
result.push({ key: camelName(provider.token), ...index === undefined ? {} : { index } });
|
|
1730
|
+
}
|
|
1731
|
+
for (const controller of this.module.controllers) {
|
|
1732
|
+
if (factoryOfScope(controller.scope) !== "services")
|
|
1733
|
+
continue;
|
|
1734
|
+
const selected = kind === "init" ? controller.hasOnInit : controller.hasOnDestroy;
|
|
1735
|
+
if (selected)
|
|
1736
|
+
result.push({ key: camelName(controller.className) });
|
|
1737
|
+
}
|
|
1738
|
+
return result;
|
|
1739
|
+
}
|
|
1740
|
+
renderApplicationLifecycle(kind) {
|
|
1741
|
+
const suffix = kind === "init" ? "initialize" : "destroy";
|
|
1742
|
+
const helper = kind === "init" ? "initializeServiceInstances" : "destroyServiceInstances";
|
|
1743
|
+
return [
|
|
1744
|
+
`async function ${suffix}${this.pascal}Services(services: Record<string, unknown>): Promise<void> {`,
|
|
1745
|
+
` await ${helper}(services, ${JSON.stringify(this.applicationLifecyclePlan(kind))});`,
|
|
1746
|
+
`}`
|
|
1747
|
+
].join(`
|
|
1748
|
+
`);
|
|
1749
|
+
}
|
|
1704
1750
|
renderControllers() {
|
|
1705
1751
|
if (this.module.controllers.length === 0)
|
|
1706
1752
|
return "[]";
|
|
@@ -3419,6 +3465,10 @@ export interface CompiledModule {
|
|
|
3419
3465
|
deps: Record<string, unknown>,
|
|
3420
3466
|
imported: Record<string, Record<string, unknown>>,
|
|
3421
3467
|
): Record<string, unknown>;
|
|
3468
|
+
/** Initializes application-scoped instances owned by this module. */
|
|
3469
|
+
initializeServices?(services: Record<string, unknown>): Promise<void>;
|
|
3470
|
+
/** Destroys application-scoped instances owned by this module. */
|
|
3471
|
+
destroyServices?(services: Record<string, unknown>): Promise<void>;
|
|
3422
3472
|
createRequestScope?(
|
|
3423
3473
|
services: Record<string, unknown>,
|
|
3424
3474
|
ctx: unknown,
|
|
@@ -3502,7 +3552,54 @@ function destroyScopeInstances(
|
|
|
3502
3552
|
});
|
|
3503
3553
|
scopeDestructions.set(scope, destruction);
|
|
3504
3554
|
return destruction;
|
|
3505
|
-
}
|
|
3555
|
+
}
|
|
3556
|
+
|
|
3557
|
+
type CompiledServiceLifecycleEntry = { key: string; index?: number };
|
|
3558
|
+
|
|
3559
|
+
async function initializeServiceInstances(
|
|
3560
|
+
services: Record<string, unknown>,
|
|
3561
|
+
plan: readonly CompiledServiceLifecycleEntry[],
|
|
3562
|
+
): Promise<void> {
|
|
3563
|
+
const seen = new Set<unknown>();
|
|
3564
|
+
for (const entry of plan) {
|
|
3565
|
+
const value = services[entry.key];
|
|
3566
|
+
const instance = entry.index === undefined
|
|
3567
|
+
? value
|
|
3568
|
+
: Array.isArray(value) ? value[entry.index] : undefined;
|
|
3569
|
+
if (seen.has(instance)) continue;
|
|
3570
|
+
seen.add(instance);
|
|
3571
|
+
if (!isRecord(instance) || !isFunction(instance.onInit)) continue;
|
|
3572
|
+
await instance.onInit();
|
|
3573
|
+
}
|
|
3574
|
+
}
|
|
3575
|
+
|
|
3576
|
+
async function destroyServiceInstances(
|
|
3577
|
+
services: Record<string, unknown>,
|
|
3578
|
+
plan: readonly CompiledServiceLifecycleEntry[],
|
|
3579
|
+
): Promise<void> {
|
|
3580
|
+
const errors: unknown[] = [];
|
|
3581
|
+
const seen = new Set<unknown>();
|
|
3582
|
+
for (const entry of [...plan].reverse()) {
|
|
3583
|
+
const value = services[entry.key];
|
|
3584
|
+
const instance = entry.index === undefined
|
|
3585
|
+
? value
|
|
3586
|
+
: Array.isArray(value) ? value[entry.index] : undefined;
|
|
3587
|
+
if (seen.has(instance)) continue;
|
|
3588
|
+
seen.add(instance);
|
|
3589
|
+
if (!isRecord(instance)) continue;
|
|
3590
|
+
const hook = isFunction(instance.onDestroy)
|
|
3591
|
+
? instance.onDestroy
|
|
3592
|
+
: isFunction(instance.ngOnDestroy) ? instance.ngOnDestroy : undefined;
|
|
3593
|
+
if (!hook) continue;
|
|
3594
|
+
try {
|
|
3595
|
+
await hook.call(instance);
|
|
3596
|
+
} catch (error) {
|
|
3597
|
+
errors.push(error);
|
|
3598
|
+
}
|
|
3599
|
+
}
|
|
3600
|
+
if (errors.length > 0) throw new AggregateError(errors, "Application service destruction failed");
|
|
3601
|
+
}
|
|
3602
|
+
`;
|
|
3506
3603
|
var init_generate = __esm(() => {
|
|
3507
3604
|
init_contract_manifest();
|
|
3508
3605
|
});
|
|
@@ -3724,8 +3821,8 @@ var init_graphql_options = __esm(() => {
|
|
|
3724
3821
|
});
|
|
3725
3822
|
|
|
3726
3823
|
// src/graphql-inputs.ts
|
|
3727
|
-
import { resolve as
|
|
3728
|
-
import * as
|
|
3824
|
+
import { resolve as resolve4 } from "node:path";
|
|
3825
|
+
import * as ts10 from "@typescript/typescript6";
|
|
3729
3826
|
function graphqlInputPaths(options) {
|
|
3730
3827
|
if (!options.graphql)
|
|
3731
3828
|
return [];
|
|
@@ -3734,9 +3831,9 @@ function graphqlInputPaths(options) {
|
|
|
3734
3831
|
} catch {
|
|
3735
3832
|
return [];
|
|
3736
3833
|
}
|
|
3737
|
-
const root =
|
|
3738
|
-
const schema =
|
|
3739
|
-
const documents =
|
|
3834
|
+
const root = resolve4(options.rootDir);
|
|
3835
|
+
const schema = resolve4(root, options.graphql.schema);
|
|
3836
|
+
const documents = ts10.sys.readDirectory(root, [".graphql", ".gql"], ["**/node_modules/**", "**/.git/**", resolve4(options.outDir)], options.graphql.documents ?? ["**/*.graphql", "**/*.gql"]).map((path) => resolve4(path)).filter((path) => path !== schema);
|
|
3740
3837
|
return [schema, ...[...new Set(documents)].sort()];
|
|
3741
3838
|
}
|
|
3742
3839
|
var init_graphql_inputs = __esm(() => {
|
|
@@ -3744,10 +3841,10 @@ var init_graphql_inputs = __esm(() => {
|
|
|
3744
3841
|
});
|
|
3745
3842
|
|
|
3746
3843
|
// src/graphql-runtime.ts
|
|
3747
|
-
import * as
|
|
3748
|
-
import { resolve as
|
|
3844
|
+
import * as ts11 from "@typescript/typescript6";
|
|
3845
|
+
import { resolve as resolve5 } from "node:path";
|
|
3749
3846
|
function renderGraphqlValidators(source, operationNames) {
|
|
3750
|
-
const fileName =
|
|
3847
|
+
const fileName = resolve5("/__supacloud_graphql__/contracts.ts");
|
|
3751
3848
|
const options = {
|
|
3752
3849
|
strict: true,
|
|
3753
3850
|
noUncheckedIndexedAccess: true,
|
|
@@ -3756,18 +3853,18 @@ function renderGraphqlValidators(source, operationNames) {
|
|
|
3756
3853
|
noPropertyAccessFromIndexSignature: true,
|
|
3757
3854
|
noFallthroughCasesInSwitch: true,
|
|
3758
3855
|
skipLibCheck: false,
|
|
3759
|
-
target:
|
|
3856
|
+
target: ts11.ScriptTarget.ES2022,
|
|
3760
3857
|
lib: ["lib.es2022.d.ts"],
|
|
3761
3858
|
types: [],
|
|
3762
3859
|
noEmit: true
|
|
3763
3860
|
};
|
|
3764
|
-
const host =
|
|
3861
|
+
const host = ts11.createCompilerHost(options);
|
|
3765
3862
|
const getSourceFile = host.getSourceFile.bind(host);
|
|
3766
|
-
host.getSourceFile = (path, languageVersion, onError, shouldCreateNewSourceFile) =>
|
|
3767
|
-
const program =
|
|
3768
|
-
const diagnostics =
|
|
3863
|
+
host.getSourceFile = (path, languageVersion, onError, shouldCreateNewSourceFile) => resolve5(path) === fileName ? ts11.createSourceFile(path, source, languageVersion, true) : getSourceFile(path, languageVersion, onError, shouldCreateNewSourceFile);
|
|
3864
|
+
const program = ts11.createProgram([fileName], options, host);
|
|
3865
|
+
const diagnostics = ts11.getPreEmitDiagnostics(program);
|
|
3769
3866
|
if (diagnostics.length) {
|
|
3770
|
-
throw new Error(`Generated GraphQL types cannot be validated: ${diagnostics.map((item) =>
|
|
3867
|
+
throw new Error(`Generated GraphQL types cannot be validated: ${diagnostics.map((item) => ts11.flattenDiagnosticMessageText(item.messageText, `
|
|
3771
3868
|
`)).join("; ")}`);
|
|
3772
3869
|
}
|
|
3773
3870
|
const checker = program.getTypeChecker();
|
|
@@ -3797,25 +3894,25 @@ function renderGraphqlValidators(source, operationNames) {
|
|
|
3797
3894
|
return name;
|
|
3798
3895
|
}
|
|
3799
3896
|
function expression(type) {
|
|
3800
|
-
if (type.flags &
|
|
3897
|
+
if (type.flags & ts11.TypeFlags.Any)
|
|
3801
3898
|
return unsupported(type);
|
|
3802
|
-
if (type.flags &
|
|
3899
|
+
if (type.flags & ts11.TypeFlags.Unknown)
|
|
3803
3900
|
return "true";
|
|
3804
|
-
if (type.flags &
|
|
3901
|
+
if (type.flags & ts11.TypeFlags.Never)
|
|
3805
3902
|
return "false";
|
|
3806
|
-
if (type.flags &
|
|
3903
|
+
if (type.flags & ts11.TypeFlags.Null)
|
|
3807
3904
|
return "value === null";
|
|
3808
|
-
if (type.flags &
|
|
3905
|
+
if (type.flags & ts11.TypeFlags.Undefined)
|
|
3809
3906
|
return "value === undefined";
|
|
3810
3907
|
if (type.isStringLiteral() || type.isNumberLiteral())
|
|
3811
3908
|
return `value === ${JSON.stringify(type.value)}`;
|
|
3812
|
-
if (type.flags &
|
|
3909
|
+
if (type.flags & ts11.TypeFlags.BooleanLiteral)
|
|
3813
3910
|
return `value === ${checker.typeToString(type)}`;
|
|
3814
|
-
if (type.flags &
|
|
3911
|
+
if (type.flags & ts11.TypeFlags.String)
|
|
3815
3912
|
return 'typeof value === "string"';
|
|
3816
|
-
if (type.flags &
|
|
3913
|
+
if (type.flags & ts11.TypeFlags.Number)
|
|
3817
3914
|
return 'typeof value === "number" && Number.isFinite(value)';
|
|
3818
|
-
if (type.flags &
|
|
3915
|
+
if (type.flags & ts11.TypeFlags.Boolean)
|
|
3819
3916
|
return 'typeof value === "boolean"';
|
|
3820
3917
|
if (type.isUnion())
|
|
3821
3918
|
return type.types.map((part) => `${reference(part)}(value)`).join(" || ");
|
|
@@ -3824,16 +3921,16 @@ function renderGraphqlValidators(source, operationNames) {
|
|
|
3824
3921
|
if (checker.isTupleType(type))
|
|
3825
3922
|
return unsupported(type);
|
|
3826
3923
|
if (checker.isArrayType(type)) {
|
|
3827
|
-
const item = checker.getIndexTypeOfType(type,
|
|
3924
|
+
const item = checker.getIndexTypeOfType(type, ts11.IndexKind.Number);
|
|
3828
3925
|
if (!item)
|
|
3829
3926
|
return unsupported(type);
|
|
3830
3927
|
return `isGraphqlArray(value) && Array.from(value).every(${reference(item)})`;
|
|
3831
3928
|
}
|
|
3832
|
-
if (type.flags &
|
|
3929
|
+
if (type.flags & ts11.TypeFlags.Object) {
|
|
3833
3930
|
if (type.getCallSignatures().length || type.getConstructSignatures().length)
|
|
3834
3931
|
return unsupported(type);
|
|
3835
3932
|
const indexes = checker.getIndexInfosOfType(type);
|
|
3836
|
-
if (indexes.some((index) => !(index.keyType.flags &
|
|
3933
|
+
if (indexes.some((index) => !(index.keyType.flags & ts11.TypeFlags.String)))
|
|
3837
3934
|
return unsupported(type);
|
|
3838
3935
|
const properties = checker.getPropertiesOfType(type).map((property) => {
|
|
3839
3936
|
const declaration = property.valueDeclaration ?? property.declarations?.[0];
|
|
@@ -3842,7 +3939,7 @@ function renderGraphqlValidators(source, operationNames) {
|
|
|
3842
3939
|
const check = reference(checker.getTypeOfSymbolAtLocation(property, declaration));
|
|
3843
3940
|
const key = JSON.stringify(property.name);
|
|
3844
3941
|
const present = `Object.prototype.hasOwnProperty.call(value, ${key})`;
|
|
3845
|
-
return property.flags &
|
|
3942
|
+
return property.flags & ts11.SymbolFlags.Optional ? `(!${present} || ${check}(value[${key}]))` : `(${present} && ${check}(value[${key}]))`;
|
|
3846
3943
|
});
|
|
3847
3944
|
const indexedValues = indexes.map((index) => `Object.values(value).every(${reference(index.type)})`);
|
|
3848
3945
|
return ["isGraphqlRecord(value)", ...properties, ...indexedValues].join(" && ");
|
|
@@ -4212,6 +4309,12 @@ function createContextPack(graph, subject) {
|
|
|
4212
4309
|
routeContracts: inspectRouteContracts({ ...graph, modules }),
|
|
4213
4310
|
...graphql ? { graphql } : {},
|
|
4214
4311
|
diagnostics: (graph.diagnostics ?? []).filter((diagnostic) => diagnostic.file === undefined || files.includes(diagnostic.file)),
|
|
4312
|
+
conventions: {
|
|
4313
|
+
singleResult: "async-await",
|
|
4314
|
+
eventComposition: "rxjs",
|
|
4315
|
+
angularHost: "@supacloud/app/angular",
|
|
4316
|
+
cancellation: "explicit-owner-signal"
|
|
4317
|
+
},
|
|
4215
4318
|
relatedModules: {
|
|
4216
4319
|
imports: subjectModule.imports.filter((name) => selected.has(name)),
|
|
4217
4320
|
importedBy: graph.modules.filter((module) => module.imports.includes(subjectModule.name)).map((module) => module.name).sort()
|
|
@@ -15323,7 +15426,7 @@ var init_delivery_schema = __esm(() => {
|
|
|
15323
15426
|
import { createHash as createHash6 } from "node:crypto";
|
|
15324
15427
|
import { constants as constants2 } from "node:fs";
|
|
15325
15428
|
import { lstat, mkdir as mkdir2, open as open3, readFile as readFile3, readdir, realpath, rename as rename2, rm, writeFile as writeFile2 } from "node:fs/promises";
|
|
15326
|
-
import { dirname as dirname5, isAbsolute as isAbsolute2, join as join6, relative as relative6, resolve as
|
|
15429
|
+
import { dirname as dirname5, isAbsolute as isAbsolute2, join as join6, relative as relative6, resolve as resolve7, sep as sep6 } from "node:path";
|
|
15327
15430
|
function canonical2(value) {
|
|
15328
15431
|
return JSON.stringify(value, (_key, item) => item !== null && typeof item === "object" && !Array.isArray(item) ? Object.fromEntries(Object.entries(item).sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0)) : item);
|
|
15329
15432
|
}
|
|
@@ -15386,7 +15489,7 @@ async function reserveOutput(project, requested) {
|
|
|
15386
15489
|
if (!inside(project, requested))
|
|
15387
15490
|
throw new Error("Delivery output must be inside the application project.");
|
|
15388
15491
|
const base = await realpath(project);
|
|
15389
|
-
const path =
|
|
15492
|
+
const path = resolve7(base, relative6(project, requested));
|
|
15390
15493
|
if (relative6(base, path).split(sep6).some((part) => part === ".git" || part === "node_modules")) {
|
|
15391
15494
|
throw new Error("Delivery output cannot use repository or dependency internals.");
|
|
15392
15495
|
}
|
|
@@ -15579,9 +15682,9 @@ var init_delivery_build_schema = __esm(() => {
|
|
|
15579
15682
|
|
|
15580
15683
|
// node_modules/@supacloud/delivery/src/delivery-artifact.ts
|
|
15581
15684
|
import { readdir as readdir2, realpath as realpath2 } from "node:fs/promises";
|
|
15582
|
-
import { basename, dirname as dirname6, join as join7, resolve as
|
|
15685
|
+
import { basename, dirname as dirname6, join as join7, resolve as resolve8 } from "node:path";
|
|
15583
15686
|
async function readDeliveryManifest(manifestPath) {
|
|
15584
|
-
const selected =
|
|
15687
|
+
const selected = resolve8(manifestPath);
|
|
15585
15688
|
const root = await realpath2(dirname6(selected));
|
|
15586
15689
|
const manifest = parseDeliveryBuildManifest(JSON.parse((await readBoundedOwned(root, join7(root, basename(selected)), JSON_LIMIT)).toString("utf8")));
|
|
15587
15690
|
return { root, manifest };
|
|
@@ -15730,6 +15833,213 @@ var init_delivery_context = __esm(() => {
|
|
|
15730
15833
|
};
|
|
15731
15834
|
});
|
|
15732
15835
|
|
|
15836
|
+
// src/fixes.ts
|
|
15837
|
+
import { createHash as createHash8, randomUUID } from "node:crypto";
|
|
15838
|
+
import { lstat as lstat2, readFile as readFile5, realpath as realpath3, rename as rename3, unlink as unlink2, writeFile as writeFile3 } from "node:fs/promises";
|
|
15839
|
+
import { dirname as dirname8, isAbsolute as isAbsolute4, relative as relative9, resolve as resolve12, sep as sep9 } from "node:path";
|
|
15840
|
+
import * as ts12 from "@typescript/typescript6";
|
|
15841
|
+
async function applyDiagnosticFix(fix, options = {}) {
|
|
15842
|
+
if (!fix || typeof fix.targetFile !== "string")
|
|
15843
|
+
throw new Error("Invalid DiagnosticFix");
|
|
15844
|
+
const root = await realpath3(options.rootDir ?? process.cwd());
|
|
15845
|
+
const file = resolve12(root, fix.targetFile);
|
|
15846
|
+
const stat = await lstat2(file);
|
|
15847
|
+
const resolved = await realpath3(file);
|
|
15848
|
+
const relativePath = relative9(root, resolved);
|
|
15849
|
+
if (stat.isSymbolicLink() || !stat.isFile() || isAbsolute4(relativePath) || relativePath === ".." || relativePath.startsWith(`..${sep9}`)) {
|
|
15850
|
+
throw new Error("Fix target must be a regular file inside rootDir");
|
|
15851
|
+
}
|
|
15852
|
+
const original = await readFile5(file, "utf8");
|
|
15853
|
+
const sourceHash = createHash8("sha256").update(original).digest("hex");
|
|
15854
|
+
if (options.expectedSourceHash !== undefined && options.expectedSourceHash !== sourceHash) {
|
|
15855
|
+
throw new Error("Fix source changed since preview; analyze the project again");
|
|
15856
|
+
}
|
|
15857
|
+
let source = parse5(file, original);
|
|
15858
|
+
let content;
|
|
15859
|
+
switch (fix.type) {
|
|
15860
|
+
case "set_command_mode": {
|
|
15861
|
+
if (fix.property !== "transaction" && fix.property !== "idempotency" || fix.value !== "required" && fix.value !== "none") {
|
|
15862
|
+
throw new Error("Command mode fix requires an explicit property and policy value");
|
|
15863
|
+
}
|
|
15864
|
+
const object = decoratorObject(findClass(source, fix.command), "Command");
|
|
15865
|
+
const current = property(object, fix.property);
|
|
15866
|
+
if (!current || current.initializer.getText(source) !== fix.expectedExpression) {
|
|
15867
|
+
throw new Error("Command mode changed since diagnosis; analyze the project again");
|
|
15868
|
+
}
|
|
15869
|
+
content = replaceProperty(source, object, fix.property, ts12.factory.createStringLiteral(fix.value));
|
|
15870
|
+
break;
|
|
15871
|
+
}
|
|
15872
|
+
case "add_module_import": {
|
|
15873
|
+
if (!fix.importPath || !fix.symbol)
|
|
15874
|
+
throw new Error("Module fix requires importPath and symbol");
|
|
15875
|
+
identifier2(fix.symbol);
|
|
15876
|
+
const withImport = importSymbol(source, fix.importPath, fix.symbol);
|
|
15877
|
+
source = parse5(file, withImport);
|
|
15878
|
+
const object = unique(moduleObjects(source).filter((candidate) => !fix.targetModule || stringProperty(candidate, "name") === fix.targetModule), "target module");
|
|
15879
|
+
const imports = property(object, "imports");
|
|
15880
|
+
if (imports && !ts12.isArrayLiteralExpression(imports.initializer)) {
|
|
15881
|
+
throw new Error("Module imports must be a static array");
|
|
15882
|
+
}
|
|
15883
|
+
const values = imports && ts12.isArrayLiteralExpression(imports.initializer) ? imports.initializer.elements : [];
|
|
15884
|
+
if (values.some(ts12.isSpreadElement))
|
|
15885
|
+
throw new Error("Module imports cannot contain spread elements");
|
|
15886
|
+
content = values.some((value) => ts12.isIdentifier(value) && value.text === fix.symbol) ? withImport : replaceProperty(source, object, "imports", ts12.factory.createArrayLiteralExpression([
|
|
15887
|
+
...values,
|
|
15888
|
+
ts12.factory.createIdentifier(fix.symbol)
|
|
15889
|
+
]));
|
|
15890
|
+
break;
|
|
15891
|
+
}
|
|
15892
|
+
case "add_command_permission": {
|
|
15893
|
+
const permission = options.permission ?? fix.permission;
|
|
15894
|
+
if (!permission?.trim()) {
|
|
15895
|
+
throw new Error("Permission fix requires an explicit permission; privileges are never inferred");
|
|
15896
|
+
}
|
|
15897
|
+
const command = findClass(source, fix.command);
|
|
15898
|
+
const object = decoratorObject(command, "Command");
|
|
15899
|
+
const current = property(object, "permission");
|
|
15900
|
+
if (current && (!ts12.isStringLiteral(current.initializer) || current.initializer.text !== permission)) {
|
|
15901
|
+
throw new Error("Command permission already exists with a different value");
|
|
15902
|
+
}
|
|
15903
|
+
content = current ? original : replaceProperty(source, object, "permission", ts12.factory.createStringLiteral(permission));
|
|
15904
|
+
break;
|
|
15905
|
+
}
|
|
15906
|
+
case "add_route_parameter_binding": {
|
|
15907
|
+
const controller = findClass(source, fix.controller);
|
|
15908
|
+
const method = unique(controller.members.filter((member) => ts12.isMethodDeclaration(member) && nameOf(member.name) === fix.route), "route handler");
|
|
15909
|
+
const parameter = unique(method.parameters.filter((candidate) => ts12.isIdentifier(candidate.name) && candidate.name.text === fix.parameter), "same-named route parameter");
|
|
15910
|
+
const binding = fix.binding === "param" ? "Param" : fix.binding === "query" ? "Query" : undefined;
|
|
15911
|
+
if (!binding)
|
|
15912
|
+
throw new Error("Invalid route binding");
|
|
15913
|
+
const decorators = ts12.getDecorators(parameter) ?? [];
|
|
15914
|
+
if (decorators.length > 0)
|
|
15915
|
+
throw new Error("Parameter already has a decorator");
|
|
15916
|
+
const framework = unique(source.statements.filter((statement) => ts12.isImportDeclaration(statement) && statement.importClause?.namedBindings && ts12.isNamedImports(statement.importClause.namedBindings) && statement.importClause.namedBindings.elements.some((element) => ["Controller", "Get", "Post", "Put", "Patch", "Delete", "Head", "Options"].includes(element.name.text))), "framework import");
|
|
15917
|
+
if (!ts12.isImportDeclaration(framework) || !ts12.isStringLiteral(framework.moduleSpecifier)) {
|
|
15918
|
+
throw new Error("Framework import must be static");
|
|
15919
|
+
}
|
|
15920
|
+
const edited = original.slice(0, parameter.getStart(source)) + `@${binding}(${JSON.stringify(fix.parameter)}) ` + original.slice(parameter.getStart(source));
|
|
15921
|
+
content = importSymbol(parse5(file, edited), framework.moduleSpecifier.text, binding);
|
|
15922
|
+
break;
|
|
15923
|
+
}
|
|
15924
|
+
default:
|
|
15925
|
+
throw new Error(`Diagnostic fix '${fix.type}' requires a manual semantic decision; no files changed`);
|
|
15926
|
+
}
|
|
15927
|
+
parse5(file, content);
|
|
15928
|
+
const result = { file, changed: content !== original, content, sourceHash };
|
|
15929
|
+
if (options.dryRun === false && result.changed) {
|
|
15930
|
+
const temporary = `${file}.supacloud-fix-${randomUUID()}`;
|
|
15931
|
+
try {
|
|
15932
|
+
await writeFile3(temporary, content, { encoding: "utf8", flag: "wx", mode: stat.mode });
|
|
15933
|
+
if (await readFile5(file, "utf8") !== original)
|
|
15934
|
+
throw new Error("Target changed while preparing the fix");
|
|
15935
|
+
await rename3(temporary, file);
|
|
15936
|
+
} finally {
|
|
15937
|
+
await unlink2(temporary).catch(() => {});
|
|
15938
|
+
}
|
|
15939
|
+
}
|
|
15940
|
+
return result;
|
|
15941
|
+
}
|
|
15942
|
+
function parse5(file, text) {
|
|
15943
|
+
const result = ts12.transpileModule(text, {
|
|
15944
|
+
fileName: file,
|
|
15945
|
+
reportDiagnostics: true,
|
|
15946
|
+
compilerOptions: { target: ts12.ScriptTarget.ESNext, experimentalDecorators: true }
|
|
15947
|
+
});
|
|
15948
|
+
if (result.diagnostics?.some((item) => item.category === ts12.DiagnosticCategory.Error)) {
|
|
15949
|
+
throw new Error("Cannot fix syntactically invalid TypeScript");
|
|
15950
|
+
}
|
|
15951
|
+
return ts12.createSourceFile(file, text, ts12.ScriptTarget.Latest, true, ts12.ScriptKind.TS);
|
|
15952
|
+
}
|
|
15953
|
+
function unique(items, description) {
|
|
15954
|
+
if (items.length !== 1)
|
|
15955
|
+
throw new Error(`Expected exactly one ${description}; found ${items.length}`);
|
|
15956
|
+
return items[0];
|
|
15957
|
+
}
|
|
15958
|
+
function identifier2(value) {
|
|
15959
|
+
if (!/^[A-Za-z_$][\w$]*$/.test(value))
|
|
15960
|
+
throw new Error(`Invalid identifier '${value}'`);
|
|
15961
|
+
}
|
|
15962
|
+
function nameOf(name) {
|
|
15963
|
+
if (!name)
|
|
15964
|
+
return "";
|
|
15965
|
+
return ts12.isIdentifier(name) || ts12.isStringLiteral(name) || ts12.isNumericLiteral(name) ? name.text : "";
|
|
15966
|
+
}
|
|
15967
|
+
function property(object, key) {
|
|
15968
|
+
if (object.properties.some((item) => !ts12.isPropertyAssignment(item) || ts12.isComputedPropertyName(item.name))) {
|
|
15969
|
+
throw new Error("Fix requires explicit static object properties");
|
|
15970
|
+
}
|
|
15971
|
+
const values = object.properties.filter((item) => ts12.isPropertyAssignment(item) && nameOf(item.name) === key);
|
|
15972
|
+
if (values.length > 1)
|
|
15973
|
+
throw new Error(`Duplicate '${key}' property`);
|
|
15974
|
+
return values[0];
|
|
15975
|
+
}
|
|
15976
|
+
function stringProperty(object, key) {
|
|
15977
|
+
const value = property(object, key)?.initializer;
|
|
15978
|
+
return value && ts12.isStringLiteral(value) ? value.text : undefined;
|
|
15979
|
+
}
|
|
15980
|
+
function replaceProperty(source, object, key, value) {
|
|
15981
|
+
const previous = property(object, key);
|
|
15982
|
+
const replacement = ts12.factory.createPropertyAssignment(key, value);
|
|
15983
|
+
const properties = object.properties.map((item) => item === previous ? replacement : item);
|
|
15984
|
+
if (!previous)
|
|
15985
|
+
properties.push(replacement);
|
|
15986
|
+
const updated = ts12.factory.updateObjectLiteralExpression(object, properties);
|
|
15987
|
+
return source.text.slice(0, object.getStart(source)) + ts12.createPrinter().printNode(ts12.EmitHint.Expression, updated, source) + source.text.slice(object.end);
|
|
15988
|
+
}
|
|
15989
|
+
function findClass(source, name) {
|
|
15990
|
+
return unique(source.statements.filter((statement) => ts12.isClassDeclaration(statement) && statement.name?.text === name), `class '${name}'`);
|
|
15991
|
+
}
|
|
15992
|
+
function decoratorObject(node, name) {
|
|
15993
|
+
const decorator = unique((ts12.getDecorators(node) ?? []).filter((item) => ts12.isCallExpression(item.expression) && item.expression.expression.getText() === name), `@${name} decorator`);
|
|
15994
|
+
const argument = ts12.isCallExpression(decorator.expression) ? decorator.expression.arguments[0] : undefined;
|
|
15995
|
+
if (!argument || !ts12.isObjectLiteralExpression(argument))
|
|
15996
|
+
throw new Error(`@${name} requires a static object`);
|
|
15997
|
+
return argument;
|
|
15998
|
+
}
|
|
15999
|
+
function moduleObjects(source) {
|
|
16000
|
+
const result = [];
|
|
16001
|
+
for (const statement of source.statements) {
|
|
16002
|
+
if (ts12.isClassDeclaration(statement) && (ts12.getDecorators(statement) ?? []).some((item) => ts12.isCallExpression(item.expression) && item.expression.expression.getText() === "Module")) {
|
|
16003
|
+
result.push(decoratorObject(statement, "Module"));
|
|
16004
|
+
}
|
|
16005
|
+
if (ts12.isVariableStatement(statement)) {
|
|
16006
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
16007
|
+
const call = declaration.initializer;
|
|
16008
|
+
if (call && ts12.isCallExpression(call) && ["defineModule", "defineFeatureSlice"].includes(call.expression.getText()) && call.arguments[0] && ts12.isObjectLiteralExpression(call.arguments[0])) {
|
|
16009
|
+
result.push(call.arguments[0]);
|
|
16010
|
+
}
|
|
16011
|
+
}
|
|
16012
|
+
}
|
|
16013
|
+
}
|
|
16014
|
+
return result;
|
|
16015
|
+
}
|
|
16016
|
+
function importSymbol(source, path, symbol) {
|
|
16017
|
+
identifier2(symbol);
|
|
16018
|
+
const current = resolve12(source.fileName).replace(/\.(tsx?|mts|cts)$/, "");
|
|
16019
|
+
const target = resolve12(dirname8(source.fileName), path).replace(/\.(tsx?|mts|cts)$/, "");
|
|
16020
|
+
if (current === target)
|
|
16021
|
+
return source.text;
|
|
16022
|
+
const matches = source.statements.filter((item) => ts12.isImportDeclaration(item) && ts12.isStringLiteral(item.moduleSpecifier) && item.moduleSpecifier.text === path);
|
|
16023
|
+
if (matches.length > 1)
|
|
16024
|
+
throw new Error(`Ambiguous imports from '${path}'`);
|
|
16025
|
+
const match = matches[0];
|
|
16026
|
+
if (match && ts12.isImportDeclaration(match) && match.importClause?.namedBindings && ts12.isNamedImports(match.importClause.namedBindings) && !match.importClause.isTypeOnly) {
|
|
16027
|
+
if (match.importClause.namedBindings.elements.some((item) => item.name.text === symbol))
|
|
16028
|
+
return source.text;
|
|
16029
|
+
const bindings = match.importClause.namedBindings;
|
|
16030
|
+
const updated = ts12.factory.updateNamedImports(bindings, [
|
|
16031
|
+
...bindings.elements,
|
|
16032
|
+
ts12.factory.createImportSpecifier(false, undefined, ts12.factory.createIdentifier(symbol))
|
|
16033
|
+
]);
|
|
16034
|
+
return source.text.slice(0, bindings.getStart(source)) + ts12.createPrinter().printNode(ts12.EmitHint.Unspecified, updated, source) + source.text.slice(bindings.end);
|
|
16035
|
+
}
|
|
16036
|
+
if (match)
|
|
16037
|
+
throw new Error(`Import from '${path}' is not a named value import`);
|
|
16038
|
+
return `import { ${symbol} } from ${JSON.stringify(path)};
|
|
16039
|
+
${source.text}`;
|
|
16040
|
+
}
|
|
16041
|
+
var init_fixes = () => {};
|
|
16042
|
+
|
|
15733
16043
|
// src/delivery-files.ts
|
|
15734
16044
|
var init_delivery_files2 = __esm(() => {
|
|
15735
16045
|
init_delivery_files();
|
|
@@ -16095,7 +16405,7 @@ var init_release_evidence = __esm(() => {
|
|
|
16095
16405
|
});
|
|
16096
16406
|
|
|
16097
16407
|
// src/release-evidence-cli.ts
|
|
16098
|
-
import { resolve as
|
|
16408
|
+
import { resolve as resolve20 } from "node:path";
|
|
16099
16409
|
async function runReleaseEvidenceCommand(args) {
|
|
16100
16410
|
try {
|
|
16101
16411
|
const values = new Map;
|
|
@@ -16119,7 +16429,7 @@ async function runReleaseEvidenceCommand(args) {
|
|
|
16119
16429
|
if (!manifest || !target || manifest.length > 4096 || !/^[a-z][a-z0-9-]{0,62}$/.test(target)) {
|
|
16120
16430
|
throw new ReleaseEvidenceError("RELEASE_EVIDENCE_INVALID");
|
|
16121
16431
|
}
|
|
16122
|
-
const evidence = await createReleaseEvidence(
|
|
16432
|
+
const evidence = await createReleaseEvidence(resolve20(manifest), target);
|
|
16123
16433
|
console.log(json ? JSON.stringify(evidence, null, 2) : formatReleaseEvidence(evidence));
|
|
16124
16434
|
} catch (error) {
|
|
16125
16435
|
console.error(`Error: ${error instanceof ReleaseEvidenceError ? error.code : "RELEASE_EVIDENCE_INVALID"}`);
|
|
@@ -16131,12 +16441,12 @@ var init_release_evidence_cli = __esm(() => {
|
|
|
16131
16441
|
});
|
|
16132
16442
|
|
|
16133
16443
|
// src/database-contracts.ts
|
|
16134
|
-
import { createHash as
|
|
16444
|
+
import { createHash as createHash11 } from "node:crypto";
|
|
16135
16445
|
import { mkdir as mkdir5, readFile as readFile12 } from "node:fs/promises";
|
|
16136
|
-
import { dirname as dirname11, relative as relative12, resolve as
|
|
16137
|
-
import * as
|
|
16446
|
+
import { dirname as dirname11, relative as relative12, resolve as resolve21 } from "node:path";
|
|
16447
|
+
import * as ts17 from "@typescript/typescript6";
|
|
16138
16448
|
function hash3(value) {
|
|
16139
|
-
return
|
|
16449
|
+
return createHash11("sha256").update(value).digest("hex");
|
|
16140
16450
|
}
|
|
16141
16451
|
function importPath(out, path) {
|
|
16142
16452
|
const value = relative12(out, path).replaceAll("\\", "/").replace(/\.(?:d\.)?[cm]?ts$/, "");
|
|
@@ -16161,33 +16471,33 @@ function parseDatabaseContractsOptions(value, directory) {
|
|
|
16161
16471
|
throw new TypeError("migrations must be an ordered list of SQL files");
|
|
16162
16472
|
}
|
|
16163
16473
|
return {
|
|
16164
|
-
rootDir:
|
|
16165
|
-
outDir:
|
|
16166
|
-
postgrestTypes:
|
|
16167
|
-
drizzleSchema:
|
|
16474
|
+
rootDir: resolve21(directory, field("rootDir")),
|
|
16475
|
+
outDir: resolve21(directory, field("outDir")),
|
|
16476
|
+
postgrestTypes: resolve21(directory, field("postgrestTypes")),
|
|
16477
|
+
drizzleSchema: resolve21(directory, field("drizzleSchema")),
|
|
16168
16478
|
role: field("role"),
|
|
16169
|
-
graphql: { ...graphql, schema:
|
|
16170
|
-
migrations: migrations.map((file) =>
|
|
16479
|
+
graphql: { ...graphql, schema: resolve21(directory, graphql.schema) },
|
|
16480
|
+
migrations: migrations.map((file) => resolve21(directory, file))
|
|
16171
16481
|
};
|
|
16172
16482
|
}
|
|
16173
16483
|
async function generateDatabaseContracts(options, check = false) {
|
|
16174
|
-
const rootDir =
|
|
16175
|
-
const postgrestTypes =
|
|
16484
|
+
const rootDir = resolve21(options.rootDir), outDir = resolve21(options.outDir);
|
|
16485
|
+
const postgrestTypes = resolve21(rootDir, options.postgrestTypes), drizzleSchema = resolve21(rootDir, options.drizzleSchema);
|
|
16176
16486
|
const snapshot = await readFile12(postgrestTypes, "utf8");
|
|
16177
|
-
const syntax =
|
|
16178
|
-
const database = syntax.statements.find((node) => (
|
|
16487
|
+
const syntax = ts17.createSourceFile(postgrestTypes, snapshot, ts17.ScriptTarget.Latest, true);
|
|
16488
|
+
const database = syntax.statements.find((node) => (ts17.isTypeAliasDeclaration(node) || ts17.isInterfaceDeclaration(node)) && node.name.text === "Database" && node.modifiers?.some((modifier) => modifier.kind === ts17.SyntaxKind.ExportKeyword));
|
|
16179
16489
|
if (!database)
|
|
16180
16490
|
throw new Error("PostgREST snapshot must export Database from the official type generator");
|
|
16181
|
-
const program =
|
|
16491
|
+
const program = ts17.createProgram([postgrestTypes], {
|
|
16182
16492
|
strict: true,
|
|
16183
16493
|
noEmit: true,
|
|
16184
16494
|
skipLibCheck: true,
|
|
16185
16495
|
types: [],
|
|
16186
|
-
target:
|
|
16187
|
-
module:
|
|
16188
|
-
moduleResolution:
|
|
16496
|
+
target: ts17.ScriptTarget.ES2022,
|
|
16497
|
+
module: ts17.ModuleKind.ESNext,
|
|
16498
|
+
moduleResolution: ts17.ModuleResolutionKind.Bundler
|
|
16189
16499
|
});
|
|
16190
|
-
if (
|
|
16500
|
+
if (ts17.getPreEmitDiagnostics(program).length)
|
|
16191
16501
|
throw new Error("PostgREST snapshot has TypeScript errors");
|
|
16192
16502
|
const artifacts = await renderGraphql({
|
|
16193
16503
|
rootDir,
|
|
@@ -16200,16 +16510,16 @@ async function generateDatabaseContracts(options, check = false) {
|
|
|
16200
16510
|
}
|
|
16201
16511
|
const inputs = {};
|
|
16202
16512
|
const addInput = async (path) => {
|
|
16203
|
-
const absolute =
|
|
16513
|
+
const absolute = resolve21(rootDir, path);
|
|
16204
16514
|
inputs[relative12(rootDir, absolute).replaceAll("\\", "/")] = hash3(await readFile12(absolute, "utf8"));
|
|
16205
16515
|
};
|
|
16206
16516
|
await addInput(postgrestTypes);
|
|
16207
16517
|
await addInput(drizzleSchema);
|
|
16208
|
-
const drizzleProgram =
|
|
16518
|
+
const drizzleProgram = ts17.createProgram([drizzleSchema], {
|
|
16209
16519
|
noEmit: true,
|
|
16210
|
-
moduleResolution:
|
|
16211
|
-
module:
|
|
16212
|
-
target:
|
|
16520
|
+
moduleResolution: ts17.ModuleResolutionKind.Bundler,
|
|
16521
|
+
module: ts17.ModuleKind.ESNext,
|
|
16522
|
+
target: ts17.ScriptTarget.ES2022,
|
|
16213
16523
|
types: [],
|
|
16214
16524
|
skipLibCheck: true
|
|
16215
16525
|
});
|
|
@@ -16217,8 +16527,8 @@ async function generateDatabaseContracts(options, check = false) {
|
|
|
16217
16527
|
if (!source.isDeclarationFile && !source.fileName.includes("/node_modules/"))
|
|
16218
16528
|
await addInput(source.fileName);
|
|
16219
16529
|
}
|
|
16220
|
-
await addInput(
|
|
16221
|
-
if (new Set(options.migrations.map((path) =>
|
|
16530
|
+
await addInput(resolve21(rootDir, options.graphql.schema));
|
|
16531
|
+
if (new Set(options.migrations.map((path) => resolve21(rootDir, path))).size !== options.migrations.length) {
|
|
16222
16532
|
throw new Error("Duplicate migration in database contracts configuration");
|
|
16223
16533
|
}
|
|
16224
16534
|
for (const path of options.migrations)
|
|
@@ -16239,14 +16549,14 @@ async function generateDatabaseContracts(options, check = false) {
|
|
|
16239
16549
|
version: 1,
|
|
16240
16550
|
role: options.role,
|
|
16241
16551
|
inputs: Object.fromEntries(Object.entries(inputs).sort(([a], [b]) => a.localeCompare(b))),
|
|
16242
|
-
migrationOrder: options.migrations.map((path) => relative12(rootDir,
|
|
16552
|
+
migrationOrder: options.migrations.map((path) => relative12(rootDir, resolve21(rootDir, path)).replaceAll("\\", "/")),
|
|
16243
16553
|
outputs: Object.fromEntries(Object.entries(files).sort(([a], [b]) => a.localeCompare(b)).map(([file, text]) => [file, hash3(text)]))
|
|
16244
16554
|
};
|
|
16245
16555
|
files["database.manifest.json"] = JSON.stringify(manifest, null, 2) + `
|
|
16246
16556
|
`;
|
|
16247
16557
|
const mismatches = [];
|
|
16248
16558
|
for (const [file, content] of Object.entries(files)) {
|
|
16249
|
-
const path =
|
|
16559
|
+
const path = resolve21(outDir, file);
|
|
16250
16560
|
let current;
|
|
16251
16561
|
try {
|
|
16252
16562
|
current = await readFile12(path, "utf8");
|
|
@@ -16260,12 +16570,12 @@ async function generateDatabaseContracts(options, check = false) {
|
|
|
16260
16570
|
if (!check) {
|
|
16261
16571
|
await mkdir5(outDir, { recursive: true });
|
|
16262
16572
|
for (const [file, content] of Object.entries(files))
|
|
16263
|
-
await writeFileIfChanged(
|
|
16573
|
+
await writeFileIfChanged(resolve21(outDir, file), content);
|
|
16264
16574
|
}
|
|
16265
16575
|
return { upToDate: mismatches.length === 0, mismatches, written: check ? [] : mismatches, manifest };
|
|
16266
16576
|
}
|
|
16267
16577
|
async function runDatabaseContractsFile(path, check = false) {
|
|
16268
|
-
const absolute =
|
|
16578
|
+
const absolute = resolve21(path);
|
|
16269
16579
|
const value = JSON.parse(await readFile12(absolute, "utf8"));
|
|
16270
16580
|
return generateDatabaseContracts(parseDatabaseContractsOptions(value, dirname11(absolute)), check);
|
|
16271
16581
|
}
|
|
@@ -16277,8 +16587,8 @@ var init_database_contracts = __esm(() => {
|
|
|
16277
16587
|
|
|
16278
16588
|
// src/graphql-schema.ts
|
|
16279
16589
|
import { mkdir as mkdir6, readFile as readFile13 } from "node:fs/promises";
|
|
16280
|
-
import { createHash as
|
|
16281
|
-
import { dirname as dirname12, resolve as
|
|
16590
|
+
import { createHash as createHash12 } from "node:crypto";
|
|
16591
|
+
import { dirname as dirname12, resolve as resolve22 } from "node:path";
|
|
16282
16592
|
async function pullGraphqlSchema(options) {
|
|
16283
16593
|
assertGraphqlOptions({ schema: options.output });
|
|
16284
16594
|
const endpoint = new URL(options.url);
|
|
@@ -16318,7 +16628,7 @@ async function pullGraphqlSchema(options) {
|
|
|
16318
16628
|
throw new Error("GraphQL schema export failed. Verify caller grants and enable introspection only in the intended development environment.");
|
|
16319
16629
|
}
|
|
16320
16630
|
const schema = lexicographicSortSchema(buildClientSchema(data));
|
|
16321
|
-
const path =
|
|
16631
|
+
const path = resolve22(options.output);
|
|
16322
16632
|
const content = path.endsWith(".json") ? JSON.stringify(introspectionFromSchema(schema), null, 2) + `
|
|
16323
16633
|
` : `# GENERATED BY supacloud-compiler graphql-schema. DO NOT EDIT.
|
|
16324
16634
|
# Database First: change database declarations, apply migrations, then re-export for the intended role.
|
|
@@ -16332,7 +16642,7 @@ async function pullGraphqlSchema(options) {
|
|
|
16332
16642
|
throw error;
|
|
16333
16643
|
}
|
|
16334
16644
|
const upToDate = previous === content;
|
|
16335
|
-
const schemaHash =
|
|
16645
|
+
const schemaHash = createHash12("sha256").update(content).digest("hex");
|
|
16336
16646
|
if (options.check)
|
|
16337
16647
|
return { path, schemaHash, upToDate, written: false };
|
|
16338
16648
|
await mkdir6(dirname12(path), { recursive: true });
|
|
@@ -16345,13 +16655,13 @@ var init_graphql_schema = __esm(() => {
|
|
|
16345
16655
|
});
|
|
16346
16656
|
|
|
16347
16657
|
// src/cli.ts
|
|
16348
|
-
import { resolve as
|
|
16658
|
+
import { resolve as resolve23 } from "node:path";
|
|
16349
16659
|
import { readFile as readFile14 } from "node:fs/promises";
|
|
16350
16660
|
|
|
16351
16661
|
// src/analyze.ts
|
|
16352
16662
|
import { createHash as createHash3 } from "node:crypto";
|
|
16353
16663
|
import { relative as relative2, resolve as resolvePath, sep as sep2 } from "node:path";
|
|
16354
|
-
import * as
|
|
16664
|
+
import * as ts7 from "@typescript/typescript6";
|
|
16355
16665
|
|
|
16356
16666
|
// src/program.ts
|
|
16357
16667
|
import { createHash as createHash2 } from "node:crypto";
|
|
@@ -16687,12 +16997,258 @@ function readProjectConfig(rootDir) {
|
|
|
16687
16997
|
init_validate();
|
|
16688
16998
|
|
|
16689
16999
|
// src/static-di.ts
|
|
17000
|
+
import * as ts6 from "@typescript/typescript6";
|
|
17001
|
+
|
|
17002
|
+
// src/reactive-analysis.ts
|
|
17003
|
+
import * as ts4 from "@typescript/typescript6";
|
|
17004
|
+
|
|
17005
|
+
// src/api-source.ts
|
|
16690
17006
|
import * as ts3 from "@typescript/typescript6";
|
|
17007
|
+
import { resolve as resolve2 } from "node:path";
|
|
17008
|
+
function bindApiSource(source) {
|
|
17009
|
+
const file = resolve2(source.fileName);
|
|
17010
|
+
const bound = ts3.createSourceFile(file, source.text, ts3.ScriptTarget.Latest, true);
|
|
17011
|
+
const options = { noLib: true, noResolve: true, target: ts3.ScriptTarget.ES2022 };
|
|
17012
|
+
const host = {
|
|
17013
|
+
getSourceFile: (name) => resolve2(name) === file ? bound : undefined,
|
|
17014
|
+
getDefaultLibFileName: () => "",
|
|
17015
|
+
writeFile: () => {},
|
|
17016
|
+
getCurrentDirectory: () => process.cwd(),
|
|
17017
|
+
getDirectories: () => [],
|
|
17018
|
+
fileExists: (name) => resolve2(name) === file,
|
|
17019
|
+
readFile: (name) => resolve2(name) === file ? source.text : undefined,
|
|
17020
|
+
getCanonicalFileName: (name) => name,
|
|
17021
|
+
useCaseSensitiveFileNames: () => true,
|
|
17022
|
+
getNewLine: () => `
|
|
17023
|
+
`
|
|
17024
|
+
};
|
|
17025
|
+
const program = ts3.createProgram([file], options, host);
|
|
17026
|
+
return { source: bound, checker: program.getTypeChecker() };
|
|
17027
|
+
}
|
|
17028
|
+
function importedFrom(node) {
|
|
17029
|
+
let current = node;
|
|
17030
|
+
while (current && !ts3.isImportDeclaration(current))
|
|
17031
|
+
current = current.parent;
|
|
17032
|
+
return current && ts3.isImportDeclaration(current) && ts3.isStringLiteral(current.moduleSpecifier) ? current.moduleSpecifier.text : undefined;
|
|
17033
|
+
}
|
|
17034
|
+
function apiOrigin(expression, checker, seen = new Set) {
|
|
17035
|
+
if (seen.has(expression))
|
|
17036
|
+
return;
|
|
17037
|
+
seen.add(expression);
|
|
17038
|
+
if (ts3.isParenthesizedExpression(expression) || ts3.isAsExpression(expression) || ts3.isSatisfiesExpression(expression) || ts3.isTypeAssertionExpression(expression)) {
|
|
17039
|
+
return apiOrigin(expression.expression, checker, seen);
|
|
17040
|
+
}
|
|
17041
|
+
if (ts3.isPropertyAccessExpression(expression) || ts3.isElementAccessExpression(expression)) {
|
|
17042
|
+
const name = ts3.isPropertyAccessExpression(expression) ? expression.name.text : ts3.isStringLiteral(expression.argumentExpression) ? expression.argumentExpression.text : undefined;
|
|
17043
|
+
const namespace = apiOrigin(expression.expression, checker, seen);
|
|
17044
|
+
return namespace?.name === "*" && name ? { module: namespace.module, name } : undefined;
|
|
17045
|
+
}
|
|
17046
|
+
if (!ts3.isIdentifier(expression))
|
|
17047
|
+
return;
|
|
17048
|
+
const symbol = checker.getSymbolAtLocation(expression);
|
|
17049
|
+
for (const declaration of symbol?.declarations ?? []) {
|
|
17050
|
+
if (ts3.isImportSpecifier(declaration) && !declaration.isTypeOnly && !declaration.parent.parent.isTypeOnly) {
|
|
17051
|
+
const module = importedFrom(declaration);
|
|
17052
|
+
if (module)
|
|
17053
|
+
return { module, name: (declaration.propertyName ?? declaration.name).text };
|
|
17054
|
+
}
|
|
17055
|
+
if (ts3.isNamespaceImport(declaration) && !declaration.parent.isTypeOnly) {
|
|
17056
|
+
const module = importedFrom(declaration);
|
|
17057
|
+
if (module)
|
|
17058
|
+
return { module, name: "*" };
|
|
17059
|
+
}
|
|
17060
|
+
if (ts3.isVariableDeclaration(declaration) && declaration.initializer && ts3.isVariableDeclarationList(declaration.parent) && declaration.parent.flags & ts3.NodeFlags.Const) {
|
|
17061
|
+
return apiOrigin(declaration.initializer, checker, seen);
|
|
17062
|
+
}
|
|
17063
|
+
}
|
|
17064
|
+
return;
|
|
17065
|
+
}
|
|
17066
|
+
|
|
17067
|
+
// src/reactive-analysis.ts
|
|
17068
|
+
var angularState = new Set(["@angular/core", "@supacloud/app/angular"]);
|
|
17069
|
+
var signalBridges = new Set(["@angular/core/rxjs-interop", "@supacloud/app/angular", "@supacloud/app/rxjs"]);
|
|
17070
|
+
function scanReactiveSource(input, file) {
|
|
17071
|
+
if (!input.statements.some((node) => ts4.isImportDeclaration(node) && ts4.isStringLiteral(node.moduleSpecifier) && (angularState.has(node.moduleSpecifier.text) || signalBridges.has(node.moduleSpecifier.text))))
|
|
17072
|
+
return [];
|
|
17073
|
+
const { source, checker } = bindApiSource(input);
|
|
17074
|
+
const diagnostics = [];
|
|
17075
|
+
const reported = new Set;
|
|
17076
|
+
const visit = (node, reactive) => {
|
|
17077
|
+
if (ts4.isCallExpression(node)) {
|
|
17078
|
+
const origin = apiOrigin(node.expression, checker);
|
|
17079
|
+
if (reactive && origin && signalBridges.has(origin.module) && ["toSignal", "toScopedSignal", "rxResource"].includes(origin.name) && !reported.has(node.pos)) {
|
|
17080
|
+
reported.add(node.pos);
|
|
17081
|
+
const start = source.getLineAndCharacterOfPosition(node.getStart(source));
|
|
17082
|
+
const end = source.getLineAndCharacterOfPosition(node.end);
|
|
17083
|
+
diagnostics.push({
|
|
17084
|
+
severity: "error",
|
|
17085
|
+
code: "reactive-subscription-in-computation",
|
|
17086
|
+
file,
|
|
17087
|
+
line: start.line + 1,
|
|
17088
|
+
column: start.character,
|
|
17089
|
+
endLine: end.line + 1,
|
|
17090
|
+
endColumn: end.character,
|
|
17091
|
+
message: `${origin.name} creates a subscription inside a reactive computation.`,
|
|
17092
|
+
suggestion: "Create the subscription once in its explicit owner scope, then derive state with computed. Hoisting requires ownership review; no automatic edit is safe.",
|
|
17093
|
+
docsUrl: "https://supacloud.dev/errors/reactive-subscription-in-computation"
|
|
17094
|
+
});
|
|
17095
|
+
}
|
|
17096
|
+
if (origin && angularState.has(origin.module) && ["computed", "effect", "untracked"].includes(origin.name)) {
|
|
17097
|
+
const callback = node.arguments[0];
|
|
17098
|
+
if (callback && (ts4.isArrowFunction(callback) || ts4.isFunctionExpression(callback))) {
|
|
17099
|
+
visit(callback.body, origin.name === "untracked" ? false : true);
|
|
17100
|
+
for (const argument of node.arguments.slice(1))
|
|
17101
|
+
visit(argument, reactive);
|
|
17102
|
+
return;
|
|
17103
|
+
}
|
|
17104
|
+
}
|
|
17105
|
+
let expression = node.expression;
|
|
17106
|
+
while (ts4.isParenthesizedExpression(expression))
|
|
17107
|
+
expression = expression.expression;
|
|
17108
|
+
if (ts4.isArrowFunction(expression) || ts4.isFunctionExpression(expression))
|
|
17109
|
+
visit(expression.body, reactive);
|
|
17110
|
+
}
|
|
17111
|
+
if (ts4.isFunctionLike(node)) {
|
|
17112
|
+
if (!reactive)
|
|
17113
|
+
ts4.forEachChild(node, (child) => visit(child, false));
|
|
17114
|
+
return;
|
|
17115
|
+
}
|
|
17116
|
+
ts4.forEachChild(node, (child) => visit(child, reactive));
|
|
17117
|
+
};
|
|
17118
|
+
visit(source, false);
|
|
17119
|
+
return diagnostics;
|
|
17120
|
+
}
|
|
17121
|
+
|
|
17122
|
+
// src/http-provider-migration.ts
|
|
17123
|
+
import * as ts5 from "@typescript/typescript6";
|
|
17124
|
+
function providerArgument(node, checker) {
|
|
17125
|
+
let current = node;
|
|
17126
|
+
while (ts5.isParenthesizedExpression(current.parent))
|
|
17127
|
+
current = current.parent;
|
|
17128
|
+
if (!ts5.isCallExpression(current.parent) || current.parent.expression !== current)
|
|
17129
|
+
return false;
|
|
17130
|
+
current = current.parent;
|
|
17131
|
+
while (ts5.isParenthesizedExpression(current.parent))
|
|
17132
|
+
current = current.parent;
|
|
17133
|
+
const parent = current.parent;
|
|
17134
|
+
if (!ts5.isCallExpression(parent) || !parent.arguments.some((argument) => argument === current))
|
|
17135
|
+
return false;
|
|
17136
|
+
const owner = apiOrigin(parent.expression, checker);
|
|
17137
|
+
return owner?.name === "provideHttpClient" && ["@supacloud/app", "@supacloud/app/http"].includes(owner.module);
|
|
17138
|
+
}
|
|
17139
|
+
function migrateHttpProviderImports(source, fileName) {
|
|
17140
|
+
const bound = bindApiSource(ts5.createSourceFile(fileName, source, ts5.ScriptTarget.Latest, true));
|
|
17141
|
+
const { source: sf, checker } = bound;
|
|
17142
|
+
const edits = [];
|
|
17143
|
+
const issues = [];
|
|
17144
|
+
const printer = ts5.createPrinter({ newLine: source.includes(`\r
|
|
17145
|
+
`) ? ts5.NewLineKind.CarriageReturnLineFeed : ts5.NewLineKind.LineFeed });
|
|
17146
|
+
const newline = source.includes(`\r
|
|
17147
|
+
`) ? `\r
|
|
17148
|
+
` : `
|
|
17149
|
+
`;
|
|
17150
|
+
for (const statement of sf.statements) {
|
|
17151
|
+
if (!ts5.isImportDeclaration(statement) || !ts5.isStringLiteral(statement.moduleSpecifier) || statement.moduleSpecifier.text !== "@supacloud/app" || !statement.importClause || statement.importClause.isTypeOnly)
|
|
17152
|
+
continue;
|
|
17153
|
+
const named = statement.importClause.namedBindings;
|
|
17154
|
+
if (!named || !ts5.isNamedImports(named))
|
|
17155
|
+
continue;
|
|
17156
|
+
const specifier = named.elements.find((item) => !item.isTypeOnly && (item.propertyName ?? item.name).text === "withInterceptors");
|
|
17157
|
+
if (!specifier)
|
|
17158
|
+
continue;
|
|
17159
|
+
const symbol = checker.getSymbolAtLocation(specifier.name);
|
|
17160
|
+
if (!symbol)
|
|
17161
|
+
continue;
|
|
17162
|
+
const uses = [];
|
|
17163
|
+
const visit = (node) => {
|
|
17164
|
+
if (ts5.isImportDeclaration(node))
|
|
17165
|
+
return;
|
|
17166
|
+
if (ts5.isIdentifier(node) && checker.getSymbolAtLocation(node) === symbol)
|
|
17167
|
+
uses.push(node);
|
|
17168
|
+
ts5.forEachChild(node, visit);
|
|
17169
|
+
};
|
|
17170
|
+
visit(sf);
|
|
17171
|
+
if (!uses.some((node) => providerArgument(node, checker)))
|
|
17172
|
+
continue;
|
|
17173
|
+
if (!uses.every((node) => providerArgument(node, checker)) || statement.attributes) {
|
|
17174
|
+
issues.push({
|
|
17175
|
+
code: "http-provider-migration-ambiguous",
|
|
17176
|
+
file: fileName,
|
|
17177
|
+
line: sf.getLineAndCharacterOfPosition(specifier.getStart(sf)).line + 1,
|
|
17178
|
+
message: "withInterceptors also has non-provider uses; split the bindings manually before migration."
|
|
17179
|
+
});
|
|
17180
|
+
continue;
|
|
17181
|
+
}
|
|
17182
|
+
const remaining = named.elements.filter((item) => item !== specifier);
|
|
17183
|
+
if (remaining.length === 0 && !statement.importClause.name) {
|
|
17184
|
+
edits.push({ start: statement.moduleSpecifier.getStart(sf), end: statement.moduleSpecifier.end, text: '"@supacloud/app/http"' });
|
|
17185
|
+
} else {
|
|
17186
|
+
const clause = ts5.factory.updateImportClause(statement.importClause, false, statement.importClause.name, remaining.length ? ts5.factory.updateNamedImports(named, remaining) : undefined);
|
|
17187
|
+
const updated = ts5.factory.updateImportDeclaration(statement, statement.modifiers, clause, statement.moduleSpecifier, statement.attributes);
|
|
17188
|
+
const text = printer.printNode(ts5.EmitHint.Unspecified, updated, sf).trimStart() + newline + `import { ${specifier.getText(sf)} } from "@supacloud/app/http";`;
|
|
17189
|
+
const withoutLeading = text.replace(/^(?:\/\/[^\n]*\n|\/\*[\s\S]*?\*\/\s*)+/, "");
|
|
17190
|
+
edits.push({ start: statement.getStart(sf), end: statement.end, text: withoutLeading });
|
|
17191
|
+
}
|
|
17192
|
+
}
|
|
17193
|
+
const checkNamespace = (node) => {
|
|
17194
|
+
if ((ts5.isPropertyAccessExpression(node) || ts5.isElementAccessExpression(node)) && providerArgument(node, checker)) {
|
|
17195
|
+
const origin = apiOrigin(node, checker);
|
|
17196
|
+
if (origin?.module === "@supacloud/app" && origin.name === "withInterceptors")
|
|
17197
|
+
issues.push({
|
|
17198
|
+
code: "http-provider-migration-namespace",
|
|
17199
|
+
file: fileName,
|
|
17200
|
+
line: sf.getLineAndCharacterOfPosition(node.getStart(sf)).line + 1,
|
|
17201
|
+
message: "Use a dedicated named import from @supacloud/app/http; namespace rewrites require manual review."
|
|
17202
|
+
});
|
|
17203
|
+
}
|
|
17204
|
+
ts5.forEachChild(node, checkNamespace);
|
|
17205
|
+
};
|
|
17206
|
+
checkNamespace(sf);
|
|
17207
|
+
if (issues.length)
|
|
17208
|
+
return { changed: false, content: source, replacements: 0, issues };
|
|
17209
|
+
let content = source;
|
|
17210
|
+
for (const edit of edits.sort((a, b) => b.start - a.start))
|
|
17211
|
+
content = content.slice(0, edit.start) + edit.text + content.slice(edit.end);
|
|
17212
|
+
return { changed: edits.length > 0, content, replacements: edits.length, issues: [] };
|
|
17213
|
+
}
|
|
17214
|
+
function scanHttpProviderImports(source, file) {
|
|
17215
|
+
if (!source.text.includes("@supacloud/app"))
|
|
17216
|
+
return [];
|
|
17217
|
+
const { source: sf, checker } = bindApiSource(source);
|
|
17218
|
+
const diagnostics = [];
|
|
17219
|
+
const visit = (node) => {
|
|
17220
|
+
if (ts5.isCallExpression(node)) {
|
|
17221
|
+
const origin = apiOrigin(node.expression, checker);
|
|
17222
|
+
if (origin?.module === "@supacloud/app" && origin.name === "withInterceptors" && providerArgument(node.expression, checker)) {
|
|
17223
|
+
const start = sf.getLineAndCharacterOfPosition(node.getStart(sf));
|
|
17224
|
+
const end = sf.getLineAndCharacterOfPosition(node.end);
|
|
17225
|
+
diagnostics.push({
|
|
17226
|
+
severity: "error",
|
|
17227
|
+
code: "http-provider-import-mismatch",
|
|
17228
|
+
file,
|
|
17229
|
+
line: start.line + 1,
|
|
17230
|
+
column: start.character,
|
|
17231
|
+
endLine: end.line + 1,
|
|
17232
|
+
endColumn: end.character,
|
|
17233
|
+
message: "The root withInterceptors returns an interceptor array, not a provideHttpClient feature.",
|
|
17234
|
+
suggestion: "Import withInterceptors from @supacloud/app/http. Preview the http-provider-entrypoint source migration; mixed/namespace uses require manual review.",
|
|
17235
|
+
docsUrl: "https://supacloud.dev/errors/http-provider-import-mismatch"
|
|
17236
|
+
});
|
|
17237
|
+
}
|
|
17238
|
+
}
|
|
17239
|
+
ts5.forEachChild(node, visit);
|
|
17240
|
+
};
|
|
17241
|
+
visit(sf);
|
|
17242
|
+
return diagnostics;
|
|
17243
|
+
}
|
|
17244
|
+
|
|
17245
|
+
// src/static-di.ts
|
|
16691
17246
|
var runtimeApis = new Set([
|
|
16692
17247
|
"inject",
|
|
16693
17248
|
"createEnvironmentInjector",
|
|
16694
17249
|
"runInInjectionContext",
|
|
16695
17250
|
"EnvironmentInjector",
|
|
17251
|
+
"Injector",
|
|
16696
17252
|
"bootstrapBun",
|
|
16697
17253
|
"runInScope",
|
|
16698
17254
|
"runInRequestContext",
|
|
@@ -16702,25 +17258,33 @@ var runtimeApis = new Set([
|
|
|
16702
17258
|
function scanRuntimeDi(source, file) {
|
|
16703
17259
|
const diagnostics = [];
|
|
16704
17260
|
const namespaces = new Set;
|
|
16705
|
-
const report = (node) =>
|
|
16706
|
-
|
|
16707
|
-
|
|
16708
|
-
|
|
16709
|
-
|
|
16710
|
-
|
|
16711
|
-
|
|
16712
|
-
|
|
16713
|
-
|
|
17261
|
+
const report = (node) => {
|
|
17262
|
+
const start = source.getLineAndCharacterOfPosition(node.getStart());
|
|
17263
|
+
const end = source.getLineAndCharacterOfPosition(node.end);
|
|
17264
|
+
diagnostics.push({
|
|
17265
|
+
severity: "error",
|
|
17266
|
+
code: "runtime-injection-disallowed",
|
|
17267
|
+
errorCode: "SC2012",
|
|
17268
|
+
docsUrl: "https://supacloud.dev/errors/SC2012",
|
|
17269
|
+
file,
|
|
17270
|
+
line: start.line + 1,
|
|
17271
|
+
column: start.character,
|
|
17272
|
+
endLine: end.line + 1,
|
|
17273
|
+
endColumn: end.character,
|
|
17274
|
+
message: "Compiled applications cannot import runtime DI. Use explicit constructors and generated scope factories.",
|
|
17275
|
+
suggestion: "Use constructor dependencies and generated request/job factories. Keep Angular runtime services outside the compiled application source root."
|
|
17276
|
+
});
|
|
17277
|
+
};
|
|
16714
17278
|
for (const statement of source.statements) {
|
|
16715
|
-
if (!(
|
|
17279
|
+
if (!(ts6.isImportDeclaration(statement) || ts6.isExportDeclaration(statement)) || !statement.moduleSpecifier || !ts6.isStringLiteral(statement.moduleSpecifier) || !(/^@supacloud\/app(?:\/|$)/.test(statement.moduleSpecifier.text) || statement.moduleSpecifier.text === "@angular/core"))
|
|
16716
17280
|
continue;
|
|
16717
|
-
if (
|
|
17281
|
+
if (ts6.isImportDeclaration(statement)) {
|
|
16718
17282
|
if (statement.importClause?.isTypeOnly)
|
|
16719
17283
|
continue;
|
|
16720
17284
|
const binding = statement.importClause?.namedBindings;
|
|
16721
|
-
if (binding &&
|
|
17285
|
+
if (binding && ts6.isNamespaceImport(binding))
|
|
16722
17286
|
namespaces.add(binding.name.text);
|
|
16723
|
-
if (binding &&
|
|
17287
|
+
if (binding && ts6.isNamedImports(binding))
|
|
16724
17288
|
for (const item of binding.elements) {
|
|
16725
17289
|
if (!item.isTypeOnly && runtimeApis.has((item.propertyName ?? item.name).text))
|
|
16726
17290
|
report(item);
|
|
@@ -16728,24 +17292,29 @@ function scanRuntimeDi(source, file) {
|
|
|
16728
17292
|
} else if (!statement.isTypeOnly) {
|
|
16729
17293
|
if (!statement.exportClause)
|
|
16730
17294
|
report(statement);
|
|
16731
|
-
else if (
|
|
17295
|
+
else if (ts6.isNamedExports(statement.exportClause))
|
|
16732
17296
|
for (const item of statement.exportClause.elements) {
|
|
16733
17297
|
if (!item.isTypeOnly && runtimeApis.has((item.propertyName ?? item.name).text))
|
|
16734
17298
|
report(item);
|
|
16735
17299
|
}
|
|
16736
17300
|
}
|
|
16737
17301
|
}
|
|
16738
|
-
|
|
16739
|
-
|
|
16740
|
-
|
|
16741
|
-
|
|
16742
|
-
|
|
16743
|
-
|
|
16744
|
-
|
|
16745
|
-
|
|
16746
|
-
|
|
16747
|
-
|
|
16748
|
-
|
|
17302
|
+
if (namespaces.size > 0) {
|
|
17303
|
+
const bound = bindApiSource(source);
|
|
17304
|
+
const protectedNamespace = (expression) => {
|
|
17305
|
+
const origin = apiOrigin(expression, bound.checker);
|
|
17306
|
+
return origin?.name === "*" && (origin.module === "@angular/core" || /^@supacloud\/app(?:\/|$)/.test(origin.module));
|
|
17307
|
+
};
|
|
17308
|
+
const visit = (node) => {
|
|
17309
|
+
if (ts6.isPropertyAccessExpression(node) && protectedNamespace(node.expression) && runtimeApis.has(node.name.text))
|
|
17310
|
+
report(node);
|
|
17311
|
+
if (ts6.isElementAccessExpression(node) && protectedNamespace(node.expression) && (!ts6.isStringLiteral(node.argumentExpression) || runtimeApis.has(node.argumentExpression.text)))
|
|
17312
|
+
report(node);
|
|
17313
|
+
ts6.forEachChild(node, visit);
|
|
17314
|
+
};
|
|
17315
|
+
visit(bound.source);
|
|
17316
|
+
}
|
|
17317
|
+
return [...diagnostics, ...scanReactiveSource(source, file), ...scanHttpProviderImports(source, file)];
|
|
16749
17318
|
}
|
|
16750
17319
|
|
|
16751
17320
|
// src/analyze.ts
|
|
@@ -16796,36 +17365,39 @@ function lineOf(node) {
|
|
|
16796
17365
|
return sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile)).line + 1;
|
|
16797
17366
|
}
|
|
16798
17367
|
function variableName(decl) {
|
|
16799
|
-
return
|
|
17368
|
+
return ts7.isIdentifier(decl.name) ? decl.name.text : nodeText(decl.name);
|
|
16800
17369
|
}
|
|
16801
17370
|
function propertyName(name) {
|
|
16802
|
-
if (
|
|
17371
|
+
if (ts7.isIdentifier(name) || ts7.isPrivateIdentifier(name))
|
|
16803
17372
|
return name.text;
|
|
16804
|
-
if (
|
|
17373
|
+
if (ts7.isStringLiteral(name) || ts7.isNumericLiteral(name))
|
|
16805
17374
|
return name.text;
|
|
16806
17375
|
return nodeText(name);
|
|
16807
17376
|
}
|
|
16808
17377
|
function parameterName(param) {
|
|
16809
|
-
return
|
|
17378
|
+
return ts7.isIdentifier(param.name) ? param.name.text : nodeText(param.name);
|
|
16810
17379
|
}
|
|
16811
17380
|
function decoratorsOf(node) {
|
|
16812
|
-
return
|
|
17381
|
+
return ts7.canHaveDecorators(node) ? ts7.getDecorators(node) ?? [] : [];
|
|
16813
17382
|
}
|
|
16814
17383
|
function decoratorArguments(dec) {
|
|
16815
|
-
return
|
|
17384
|
+
return ts7.isCallExpression(dec.expression) ? dec.expression.arguments : [];
|
|
16816
17385
|
}
|
|
16817
17386
|
function hasMethod(cls, name) {
|
|
16818
|
-
return cls.members.some((member) => (
|
|
17387
|
+
return cls.members.some((member) => (ts7.isMethodDeclaration(member) || ts7.isGetAccessorDeclaration(member) || ts7.isSetAccessorDeclaration(member)) && member.name !== undefined && propertyName(member.name) === name);
|
|
16819
17388
|
}
|
|
16820
17389
|
function hasDestroyHook(cls) {
|
|
16821
17390
|
return hasMethod(cls, "onDestroy") || hasMethod(cls, "ngOnDestroy");
|
|
16822
17391
|
}
|
|
17392
|
+
function hasInitHook(cls) {
|
|
17393
|
+
return hasMethod(cls, "onInit");
|
|
17394
|
+
}
|
|
16823
17395
|
function descendantsOfKind(root, predicate) {
|
|
16824
17396
|
const result = [];
|
|
16825
17397
|
const visit = (node) => {
|
|
16826
17398
|
if (predicate(node))
|
|
16827
17399
|
result.push(node);
|
|
16828
|
-
|
|
17400
|
+
ts7.forEachChild(node, visit);
|
|
16829
17401
|
};
|
|
16830
17402
|
visit(root);
|
|
16831
17403
|
return result;
|
|
@@ -16834,7 +17406,7 @@ async function analyzeProject(rootDir, include, cache, changedPaths) {
|
|
|
16834
17406
|
const session = cache?.programSession ?? createIncrementalProgramSession(rootDir);
|
|
16835
17407
|
if (cache)
|
|
16836
17408
|
cache.programSession = session;
|
|
16837
|
-
const rootNames =
|
|
17409
|
+
const rootNames = ts7.sys.readDirectory(rootDir, [".ts", ".tsx"], ["node_modules", "dist"], include ?? DEFAULT_INCLUDE);
|
|
16838
17410
|
const update = session.update(rootNames, changedPaths);
|
|
16839
17411
|
const program = update.program;
|
|
16840
17412
|
const checker = program.getTypeChecker();
|
|
@@ -16870,7 +17442,7 @@ async function analyzeProject(rootDir, include, cache, changedPaths) {
|
|
|
16870
17442
|
for (const sf of sourceFiles) {
|
|
16871
17443
|
const traits = nativeTraitFiles.get(sf.fileName);
|
|
16872
17444
|
if (!cache || traits?.has("module")) {
|
|
16873
|
-
for (const cls of sf.statements.filter(
|
|
17445
|
+
for (const cls of sf.statements.filter(ts7.isClassDeclaration)) {
|
|
16874
17446
|
const moduleDec = findDecorator(cls, "Module");
|
|
16875
17447
|
if (!moduleDec)
|
|
16876
17448
|
continue;
|
|
@@ -16887,14 +17459,14 @@ async function analyzeProject(rootDir, include, cache, changedPaths) {
|
|
|
16887
17459
|
}
|
|
16888
17460
|
}
|
|
16889
17461
|
if (!cache || traits?.has("defineModule") || traits?.has("defineFeatureSlice")) {
|
|
16890
|
-
for (const call of descendantsOfKind(sf,
|
|
17462
|
+
for (const call of descendantsOfKind(sf, ts7.isCallExpression)) {
|
|
16891
17463
|
if (!["defineModule", "defineFeatureSlice"].includes(nodeText(call.expression)))
|
|
16892
17464
|
continue;
|
|
16893
17465
|
const parent = call.parent;
|
|
16894
|
-
if (!parent || !
|
|
17466
|
+
if (!parent || !ts7.isVariableDeclaration(parent))
|
|
16895
17467
|
continue;
|
|
16896
17468
|
const arg = call.arguments[0];
|
|
16897
|
-
if (!arg || !
|
|
17469
|
+
if (!arg || !ts7.isObjectLiteralExpression(arg))
|
|
16898
17470
|
continue;
|
|
16899
17471
|
candidates.push({
|
|
16900
17472
|
node: parent,
|
|
@@ -17030,6 +17602,7 @@ async function analyzeProject(rootDir, include, cache, changedPaths) {
|
|
|
17030
17602
|
...functionalInjects.length > 0 ? { functionalInjects } : {},
|
|
17031
17603
|
providedIn: "root",
|
|
17032
17604
|
...hasDestroyHook(classInfo.decl) ? { hasOnDestroy: true } : {},
|
|
17605
|
+
...hasInitHook(classInfo.decl) ? { hasOnInit: true } : {},
|
|
17033
17606
|
exported: true,
|
|
17034
17607
|
file,
|
|
17035
17608
|
line,
|
|
@@ -17041,7 +17614,7 @@ async function analyzeProject(rootDir, include, cache, changedPaths) {
|
|
|
17041
17614
|
const controllerDec = findDecorator(classInfo.decl, "Controller");
|
|
17042
17615
|
if (controllerDec) {
|
|
17043
17616
|
const arg = decoratorArguments(controllerDec)[0];
|
|
17044
|
-
const isStandalone = arg &&
|
|
17617
|
+
const isStandalone = arg && ts7.isObjectLiteralExpression(arg) && booleanProp(arg, "standalone");
|
|
17045
17618
|
if (isStandalone) {
|
|
17046
17619
|
const ctrl = parseController(classInfo.decl, ctx);
|
|
17047
17620
|
if (ctrl)
|
|
@@ -17201,16 +17774,16 @@ function collectModuleSourceClosure(module, ctx) {
|
|
|
17201
17774
|
ownedFiles.add(relativeFile);
|
|
17202
17775
|
for (const statement of sourceFile.statements) {
|
|
17203
17776
|
let moduleName;
|
|
17204
|
-
if (
|
|
17777
|
+
if (ts7.isImportDeclaration(statement) && ts7.isStringLiteral(statement.moduleSpecifier)) {
|
|
17205
17778
|
moduleName = statement.moduleSpecifier.text;
|
|
17206
|
-
} else if (
|
|
17779
|
+
} else if (ts7.isExportDeclaration(statement) && statement.moduleSpecifier && ts7.isStringLiteral(statement.moduleSpecifier)) {
|
|
17207
17780
|
moduleName = statement.moduleSpecifier.text;
|
|
17208
|
-
} else if (
|
|
17781
|
+
} else if (ts7.isImportEqualsDeclaration(statement) && ts7.isExternalModuleReference(statement.moduleReference) && ts7.isStringLiteral(statement.moduleReference.expression)) {
|
|
17209
17782
|
moduleName = statement.moduleReference.expression.text;
|
|
17210
17783
|
}
|
|
17211
17784
|
if (!moduleName || moduleName.startsWith("node:"))
|
|
17212
17785
|
continue;
|
|
17213
|
-
const resolved =
|
|
17786
|
+
const resolved = ts7.resolveModuleName(moduleName, sourceFile.fileName, ctx.program.getCompilerOptions(), ts7.sys).resolvedModule?.resolvedFileName;
|
|
17214
17787
|
if (resolved && isProjectSourcePath(resolved, ctx.rootDir) && !enqueued.has(resolved)) {
|
|
17215
17788
|
enqueued.add(resolved);
|
|
17216
17789
|
queue.push(resolved);
|
|
@@ -17232,7 +17805,7 @@ function isProjectSourceFile(sourceFile, rootDir) {
|
|
|
17232
17805
|
return isProjectSourcePath(sourceFile.fileName, rootDir) && /\.(tsx?|mts|cts)$/.test(sourceFile.fileName);
|
|
17233
17806
|
}
|
|
17234
17807
|
function indexFile(sf, ctx) {
|
|
17235
|
-
for (const cls of sf.statements.filter(
|
|
17808
|
+
for (const cls of sf.statements.filter(ts7.isClassDeclaration)) {
|
|
17236
17809
|
const name = cls.name?.text;
|
|
17237
17810
|
if (name && !ctx.classesByName.has(name)) {
|
|
17238
17811
|
ctx.classesByName.set(name, { name, decl: cls, file: sf.fileName });
|
|
@@ -17240,9 +17813,9 @@ function indexFile(sf, ctx) {
|
|
|
17240
17813
|
if (name)
|
|
17241
17814
|
indexInfraResource(cls, name, sf, ctx);
|
|
17242
17815
|
}
|
|
17243
|
-
for (const statement of sf.statements.filter(
|
|
17816
|
+
for (const statement of sf.statements.filter(ts7.isVariableStatement)) {
|
|
17244
17817
|
for (const decl of statement.declarationList.declarations) {
|
|
17245
|
-
if (
|
|
17818
|
+
if (ts7.isIdentifier(decl.name) && !ctx.variablesByName.has(decl.name.text)) {
|
|
17246
17819
|
ctx.variablesByName.set(decl.name.text, decl);
|
|
17247
17820
|
}
|
|
17248
17821
|
const info = parseTokenVariable(decl, sf.fileName);
|
|
@@ -17254,16 +17827,16 @@ function indexFile(sf, ctx) {
|
|
|
17254
17827
|
}
|
|
17255
17828
|
function parseTokenVariable(decl, file) {
|
|
17256
17829
|
const init = decl.initializer;
|
|
17257
|
-
if (!init || !
|
|
17830
|
+
if (!init || !ts7.isNewExpression(init))
|
|
17258
17831
|
return;
|
|
17259
17832
|
if (nodeText(init.expression) !== "InjectionToken")
|
|
17260
17833
|
return;
|
|
17261
17834
|
const [nameArg, optionsArg] = init.arguments ?? [];
|
|
17262
17835
|
const info = { name: variableName(decl), file, line: lineOf(decl) };
|
|
17263
|
-
if (nameArg &&
|
|
17836
|
+
if (nameArg && ts7.isStringLiteral(nameArg)) {
|
|
17264
17837
|
info.stringName = nameArg.text;
|
|
17265
17838
|
}
|
|
17266
|
-
if (optionsArg &&
|
|
17839
|
+
if (optionsArg && ts7.isObjectLiteralExpression(optionsArg)) {
|
|
17267
17840
|
const scope = stringLiteralProp(optionsArg, "scope");
|
|
17268
17841
|
if (scope && isScope(scope)) {
|
|
17269
17842
|
info.scope = scope;
|
|
@@ -17283,23 +17856,23 @@ function parseModule(candidate, nameByNode, ctx) {
|
|
|
17283
17856
|
const { options, className, file, line } = candidate;
|
|
17284
17857
|
const name = nameByNode.get(candidate.node) ?? className;
|
|
17285
17858
|
const featureSpec = parseFeatureSpec(getProp(options, "spec"), ctx);
|
|
17286
|
-
const tags = arrayProp(options, "tags").map((el) =>
|
|
17859
|
+
const tags = arrayProp(options, "tags").map((el) => ts7.isStringLiteral(el) ? el.text : nodeText(el).replace(/['"]/g, "")).filter(Boolean);
|
|
17287
17860
|
const aspects = parseAspectRefs(getProp(options, "aspects"), ctx, `module ${name}`);
|
|
17288
17861
|
const moduleResourceNames = parseModuleInfraResources(infraResourceProp(options, "resources", ctx), ctx, `module ${name}`, sourcePath(ctx.rootDir, file), line);
|
|
17289
17862
|
const imports = arrayProp(options, "imports").map((el) => {
|
|
17290
17863
|
const unwrapped = unwrapForwardRef(el);
|
|
17291
|
-
const decl =
|
|
17864
|
+
const decl = ts7.isIdentifier(unwrapped) ? resolveDeclaration(unwrapped, ctx)[0] : undefined;
|
|
17292
17865
|
if (decl) {
|
|
17293
17866
|
const known = nameByNode.get(decl);
|
|
17294
17867
|
if (known)
|
|
17295
17868
|
return known;
|
|
17296
|
-
if (
|
|
17869
|
+
if (ts7.isClassDeclaration(decl)) {
|
|
17297
17870
|
const dec = findDecorator(decl, "Module");
|
|
17298
17871
|
const decOptions = dec && decoratorObjectArg(dec);
|
|
17299
17872
|
const decName = decOptions && stringLiteralProp(decOptions, "name");
|
|
17300
17873
|
return decName ?? decl.name?.text ?? nodeText(el);
|
|
17301
17874
|
}
|
|
17302
|
-
if (
|
|
17875
|
+
if (ts7.isVariableDeclaration(decl))
|
|
17303
17876
|
return variableName(decl);
|
|
17304
17877
|
}
|
|
17305
17878
|
return nodeText(el);
|
|
@@ -17313,7 +17886,7 @@ function parseModule(candidate, nameByNode, ctx) {
|
|
|
17313
17886
|
providers.push(...parsedProviders);
|
|
17314
17887
|
continue;
|
|
17315
17888
|
}
|
|
17316
|
-
if (
|
|
17889
|
+
if (ts7.isCallExpression(el)) {
|
|
17317
17890
|
const helper = nodeText(el.expression).split(".").pop() ?? nodeText(el.expression);
|
|
17318
17891
|
warn(ctx, "unsupported-provider-helper", `无法静态展开 provider helper '${helper}';请改用显式 Provider 或实现编译器支持的 helper`, sourcePath(ctx.rootDir, el.getSourceFile().fileName), lineOf(el));
|
|
17319
17892
|
continue;
|
|
@@ -17323,10 +17896,10 @@ function parseModule(candidate, nameByNode, ctx) {
|
|
|
17323
17896
|
providers.push(provider);
|
|
17324
17897
|
}
|
|
17325
17898
|
for (const el of arrayProp(options, "jobs")) {
|
|
17326
|
-
if (!
|
|
17899
|
+
if (!ts7.isIdentifier(el))
|
|
17327
17900
|
continue;
|
|
17328
17901
|
const decl = resolveDeclaration(el, ctx)[0];
|
|
17329
|
-
if (!decl || !
|
|
17902
|
+
if (!decl || !ts7.isClassDeclaration(decl))
|
|
17330
17903
|
continue;
|
|
17331
17904
|
const className = decl.name?.text ?? el.text;
|
|
17332
17905
|
const registeredProvider = providers.find((provider) => provider.token === className || provider.useClass === className);
|
|
@@ -17348,6 +17921,7 @@ function parseModule(candidate, nameByNode, ctx) {
|
|
|
17348
17921
|
...deps.hostDeps.length > 0 ? { hostDeps: deps.hostDeps } : {},
|
|
17349
17922
|
...deps.functionalInjects.length > 0 ? { functionalInjects: deps.functionalInjects } : {},
|
|
17350
17923
|
...hasDestroyHook(decl) ? { hasOnDestroy: true } : {},
|
|
17924
|
+
...hasInitHook(decl) ? { hasOnInit: true } : {},
|
|
17351
17925
|
exported: exportsSet.has(className),
|
|
17352
17926
|
file: sourcePath(ctx.rootDir, decl.getSourceFile().fileName),
|
|
17353
17927
|
line: lineOf(decl),
|
|
@@ -17363,18 +17937,18 @@ function parseModule(candidate, nameByNode, ctx) {
|
|
|
17363
17937
|
const handlerClasses = [];
|
|
17364
17938
|
const seenHandlers = new Set;
|
|
17365
17939
|
const collectHandler = (expr) => {
|
|
17366
|
-
if (!
|
|
17940
|
+
if (!ts7.isIdentifier(expr))
|
|
17367
17941
|
return;
|
|
17368
17942
|
const decl = resolveDeclaration(expr, ctx)[0];
|
|
17369
|
-
if (decl &&
|
|
17943
|
+
if (decl && ts7.isClassDeclaration(decl) && !seenHandlers.has(decl.name?.text ?? "")) {
|
|
17370
17944
|
seenHandlers.add(decl.name?.text ?? "");
|
|
17371
17945
|
handlerClasses.push(decl);
|
|
17372
17946
|
}
|
|
17373
17947
|
};
|
|
17374
17948
|
for (const el of arrayProp(options, "providers")) {
|
|
17375
|
-
if (
|
|
17949
|
+
if (ts7.isIdentifier(el))
|
|
17376
17950
|
collectHandler(el);
|
|
17377
|
-
if (
|
|
17951
|
+
if (ts7.isObjectLiteralExpression(el)) {
|
|
17378
17952
|
const useClass = getProp(el, "useClass");
|
|
17379
17953
|
if (useClass)
|
|
17380
17954
|
collectHandler(useClass);
|
|
@@ -17483,17 +18057,17 @@ function parseFeatureSpec(input, ctx, seen = new Set) {
|
|
|
17483
18057
|
if (seen.has(input))
|
|
17484
18058
|
return;
|
|
17485
18059
|
seen.add(input);
|
|
17486
|
-
if (
|
|
17487
|
-
const local = input.getSourceFile().statements.flatMap((statement) =>
|
|
18060
|
+
if (ts7.isIdentifier(input)) {
|
|
18061
|
+
const local = input.getSourceFile().statements.flatMap((statement) => ts7.isVariableStatement(statement) ? [...statement.declarationList.declarations] : []);
|
|
17488
18062
|
const resolved = resolveDeclaration(input, ctx)[0];
|
|
17489
|
-
const decl = (resolved &&
|
|
17490
|
-
if (decl &&
|
|
18063
|
+
const decl = (resolved && ts7.isVariableDeclaration(resolved) ? resolved : undefined) ?? ctx.variablesByName.get(input.text) ?? local.find((candidate) => ts7.isIdentifier(candidate.name) && candidate.name.text === input.text) ?? descendantsOfKind(input.getSourceFile(), ts7.isVariableDeclaration).find((candidate) => ts7.isIdentifier(candidate.name) && candidate.name.text === input.text);
|
|
18064
|
+
if (decl && ts7.isVariableDeclaration(decl))
|
|
17491
18065
|
return parseFeatureSpec(decl.initializer, ctx, seen);
|
|
17492
18066
|
}
|
|
17493
|
-
if (
|
|
18067
|
+
if (ts7.isCallExpression(input) && nodeText(input.expression) === "defineFeatureSpec") {
|
|
17494
18068
|
return parseFeatureSpec(input.arguments[0], ctx, seen);
|
|
17495
18069
|
}
|
|
17496
|
-
if (
|
|
18070
|
+
if (ts7.isAsExpression(input) || ts7.isSatisfiesExpression(input) || ts7.isParenthesizedExpression(input)) {
|
|
17497
18071
|
return parseFeatureSpec(input.expression, ctx, seen);
|
|
17498
18072
|
}
|
|
17499
18073
|
const invalid = () => {
|
|
@@ -17506,28 +18080,28 @@ function parseFeatureSpec(input, ctx, seen = new Set) {
|
|
|
17506
18080
|
});
|
|
17507
18081
|
return;
|
|
17508
18082
|
};
|
|
17509
|
-
if (!
|
|
18083
|
+
if (!ts7.isObjectLiteralExpression(input))
|
|
17510
18084
|
return invalid();
|
|
17511
18085
|
const name = stringLiteralProp(input, "name");
|
|
17512
18086
|
const statesExpr = getProp(input, "states");
|
|
17513
18087
|
const transitionObject = getProp(input, "transitions");
|
|
17514
|
-
if (!name || !statesExpr || !
|
|
18088
|
+
if (!name || !statesExpr || !ts7.isArrayLiteralExpression(statesExpr) || statesExpr.elements.some((state) => !ts7.isStringLiteral(state)) || !transitionObject || !ts7.isObjectLiteralExpression(transitionObject) || input.properties.some((property) => !ts7.isPropertyAssignment(property))) {
|
|
17515
18089
|
return invalid();
|
|
17516
18090
|
}
|
|
17517
18091
|
const states = [];
|
|
17518
18092
|
for (const state of statesExpr.elements) {
|
|
17519
|
-
if (!
|
|
18093
|
+
if (!ts7.isStringLiteral(state))
|
|
17520
18094
|
return invalid();
|
|
17521
18095
|
states.push(state.text);
|
|
17522
18096
|
}
|
|
17523
18097
|
const transitions = [];
|
|
17524
18098
|
for (const property of transitionObject.properties) {
|
|
17525
|
-
if (!
|
|
18099
|
+
if (!ts7.isPropertyAssignment(property) || ts7.isComputedPropertyName(property.name) || !ts7.isObjectLiteralExpression(property.initializer))
|
|
17526
18100
|
return invalid();
|
|
17527
18101
|
const options = property.initializer;
|
|
17528
18102
|
const from = stringLiteralProp(options, "from");
|
|
17529
18103
|
const to = stringLiteralProp(options, "to");
|
|
17530
|
-
if (!from || !to || options.properties.some((prop) => !
|
|
18104
|
+
if (!from || !to || options.properties.some((prop) => !ts7.isPropertyAssignment(prop)) || ["permission", "command", "route", "audit"].some((key) => getProp(options, key) && !stringLiteralProp(options, key)) || ["transaction", "idempotency"].some((key) => getProp(options, key) && !commandModeProp(options, key))) {
|
|
17531
18105
|
return invalid();
|
|
17532
18106
|
}
|
|
17533
18107
|
const permission = stringLiteralProp(options, "permission");
|
|
@@ -17554,21 +18128,21 @@ function resolveStaticObjectLiteral(input, ctx, seen = new Set) {
|
|
|
17554
18128
|
if (!input || seen.has(input))
|
|
17555
18129
|
return;
|
|
17556
18130
|
seen.add(input);
|
|
17557
|
-
if (
|
|
18131
|
+
if (ts7.isAsExpression(input) || ts7.isSatisfiesExpression(input) || ts7.isParenthesizedExpression(input)) {
|
|
17558
18132
|
return resolveStaticObjectLiteral(input.expression, ctx, seen);
|
|
17559
18133
|
}
|
|
17560
|
-
if (
|
|
17561
|
-
const declaration = resolveDeclaration(input, ctx).find(
|
|
18134
|
+
if (ts7.isIdentifier(input)) {
|
|
18135
|
+
const declaration = resolveDeclaration(input, ctx).find(ts7.isVariableDeclaration);
|
|
17562
18136
|
return declaration?.initializer ? resolveStaticObjectLiteral(declaration.initializer, ctx, seen) : undefined;
|
|
17563
18137
|
}
|
|
17564
|
-
if (
|
|
18138
|
+
if (ts7.isCallExpression(input)) {
|
|
17565
18139
|
const expressionName = nodeText(input.expression);
|
|
17566
18140
|
if (expressionName === "defineRouteContract" || expressionName.endsWith(".defineRouteContract")) {
|
|
17567
18141
|
return resolveStaticObjectLiteral(input.arguments[0], ctx, seen);
|
|
17568
18142
|
}
|
|
17569
18143
|
return;
|
|
17570
18144
|
}
|
|
17571
|
-
return
|
|
18145
|
+
return ts7.isObjectLiteralExpression(input) ? input : undefined;
|
|
17572
18146
|
}
|
|
17573
18147
|
function routePolicyProperty(object, name, ctx, seen = new Set) {
|
|
17574
18148
|
if (seen.has(object))
|
|
@@ -17576,15 +18150,15 @@ function routePolicyProperty(object, name, ctx, seen = new Set) {
|
|
|
17576
18150
|
const ancestry = new Set(seen).add(object);
|
|
17577
18151
|
let result = { unresolved: false };
|
|
17578
18152
|
for (const property of object.properties) {
|
|
17579
|
-
if (
|
|
18153
|
+
if (ts7.isSpreadAssignment(property)) {
|
|
17580
18154
|
const spread = resolveStaticObjectLiteral(property.expression, ctx);
|
|
17581
18155
|
const nested = spread ? routePolicyProperty(spread, name, ctx, ancestry) : { unresolved: true };
|
|
17582
18156
|
if (nested.unresolved || nested.value !== undefined)
|
|
17583
18157
|
result = nested;
|
|
17584
|
-
} else if (
|
|
18158
|
+
} else if (ts7.isComputedPropertyName(property.name)) {
|
|
17585
18159
|
result = { unresolved: true };
|
|
17586
18160
|
} else if (propertyName(property.name) === name) {
|
|
17587
|
-
result =
|
|
18161
|
+
result = ts7.isPropertyAssignment(property) ? { value: property.initializer, unresolved: false } : { unresolved: true };
|
|
17588
18162
|
}
|
|
17589
18163
|
}
|
|
17590
18164
|
return result;
|
|
@@ -17622,9 +18196,9 @@ function parseProvider(el, exportsSet, ctx) {
|
|
|
17622
18196
|
const file = sourcePath(ctx.rootDir, el.getSourceFile().fileName);
|
|
17623
18197
|
const line = lineOf(el);
|
|
17624
18198
|
const unwrappedEl = unwrapForwardRef(el);
|
|
17625
|
-
if (
|
|
18199
|
+
if (ts7.isIdentifier(unwrappedEl)) {
|
|
17626
18200
|
const decl = resolveDeclaration(unwrappedEl, ctx)[0];
|
|
17627
|
-
const cls = decl &&
|
|
18201
|
+
const cls = decl && ts7.isClassDeclaration(decl) ? decl : undefined;
|
|
17628
18202
|
const className = cls?.name?.text ?? unwrappedEl.text;
|
|
17629
18203
|
const { deps, optionalDeps, selfDeps, skipSelfDeps, hostDeps, functionalInjects, missing } = cls ? classDeps(cls, ctx) : { deps: [], optionalDeps: [], selfDeps: [], skipSelfDeps: [], hostDeps: [], functionalInjects: [], missing: false };
|
|
17630
18204
|
const injectable = cls ? parseInjectableOptions(cls, ctx) : undefined;
|
|
@@ -17645,13 +18219,14 @@ function parseProvider(el, exportsSet, ctx) {
|
|
|
17645
18219
|
...functionalInjects.length > 0 ? { functionalInjects } : {},
|
|
17646
18220
|
...injectable?.providedIn ? { providedIn: injectable.providedIn } : {},
|
|
17647
18221
|
...cls && hasDestroyHook(cls) ? { hasOnDestroy: true } : {},
|
|
18222
|
+
...cls && hasInitHook(cls) ? { hasOnInit: true } : {},
|
|
17648
18223
|
exported: exportsSet.has(className),
|
|
17649
18224
|
file,
|
|
17650
18225
|
line,
|
|
17651
18226
|
...cls ? { importPath: modulePath(ctx.rootDir, cls.getSourceFile().fileName) } : {}
|
|
17652
18227
|
};
|
|
17653
18228
|
}
|
|
17654
|
-
if (!
|
|
18229
|
+
if (!ts7.isObjectLiteralExpression(el))
|
|
17655
18230
|
return;
|
|
17656
18231
|
const provideExpr = getProp(el, "provide");
|
|
17657
18232
|
if (!provideExpr)
|
|
@@ -17666,8 +18241,8 @@ function parseProvider(el, exportsSet, ctx) {
|
|
|
17666
18241
|
const useExistingExpr = getProp(el, "useExisting");
|
|
17667
18242
|
if (useClassExpr) {
|
|
17668
18243
|
const unwrappedClass = unwrapForwardRef(useClassExpr);
|
|
17669
|
-
const decl =
|
|
17670
|
-
const cls = decl &&
|
|
18244
|
+
const decl = ts7.isIdentifier(unwrappedClass) ? resolveDeclaration(unwrappedClass, ctx)[0] : undefined;
|
|
18245
|
+
const cls = decl && ts7.isClassDeclaration(decl) ? decl : undefined;
|
|
17671
18246
|
const useClass = cls?.name?.text ?? nodeText(unwrappedClass);
|
|
17672
18247
|
let deps = explicitDeps;
|
|
17673
18248
|
let optionalDeps = [];
|
|
@@ -17715,6 +18290,7 @@ function parseProvider(el, exportsSet, ctx) {
|
|
|
17715
18290
|
...multi === undefined ? {} : { multi },
|
|
17716
18291
|
...injectable?.providedIn ? { providedIn: injectable.providedIn } : {},
|
|
17717
18292
|
...cls && hasMethod(cls, "onDestroy") ? { hasOnDestroy: true } : {},
|
|
18293
|
+
...cls && hasInitHook(cls) ? { hasOnInit: true } : {},
|
|
17718
18294
|
exported: exportsSet.has(token),
|
|
17719
18295
|
file,
|
|
17720
18296
|
line,
|
|
@@ -17723,7 +18299,7 @@ function parseProvider(el, exportsSet, ctx) {
|
|
|
17723
18299
|
}
|
|
17724
18300
|
if (useValueExpr) {
|
|
17725
18301
|
validateProviderCompatibility(provideExpr, useValueExpr, "value", token, ctx, file, line);
|
|
17726
|
-
const importPath =
|
|
18302
|
+
const importPath = ts7.isIdentifier(useValueExpr) ? importPathOf(useValueExpr, ctx) : undefined;
|
|
17727
18303
|
return {
|
|
17728
18304
|
token,
|
|
17729
18305
|
tokenKind,
|
|
@@ -17739,12 +18315,12 @@ function parseProvider(el, exportsSet, ctx) {
|
|
|
17739
18315
|
};
|
|
17740
18316
|
}
|
|
17741
18317
|
if (useFactoryExpr) {
|
|
17742
|
-
const factoryName =
|
|
18318
|
+
const factoryName = ts7.isIdentifier(useFactoryExpr) ? (() => {
|
|
17743
18319
|
const decl = resolveDeclaration(useFactoryExpr, ctx)[0];
|
|
17744
|
-
return decl && (
|
|
18320
|
+
return decl && (ts7.isFunctionDeclaration(decl) || ts7.isVariableDeclaration(decl)) ? (ts7.isFunctionDeclaration(decl) ? decl.name?.text : variableName(decl)) ?? useFactoryExpr.text : useFactoryExpr.text;
|
|
17745
18321
|
})() : nodeText(useFactoryExpr);
|
|
17746
18322
|
validateProviderCompatibility(provideExpr, useFactoryExpr, "factory", token, ctx, file, line);
|
|
17747
|
-
const importPath =
|
|
18323
|
+
const importPath = ts7.isIdentifier(useFactoryExpr) ? importPathOf(useFactoryExpr, ctx) : undefined;
|
|
17748
18324
|
return {
|
|
17749
18325
|
token,
|
|
17750
18326
|
tokenKind,
|
|
@@ -17780,20 +18356,20 @@ function parseProvider(el, exportsSet, ctx) {
|
|
|
17780
18356
|
function expandProviderExpressions(expressions, ctx, seen = new Set) {
|
|
17781
18357
|
const result = [];
|
|
17782
18358
|
for (const expression of expressions) {
|
|
17783
|
-
if (
|
|
18359
|
+
if (ts7.isSpreadElement(expression)) {
|
|
17784
18360
|
result.push(...expandProviderExpressions([expression.expression], ctx, seen));
|
|
17785
18361
|
continue;
|
|
17786
18362
|
}
|
|
17787
|
-
if (
|
|
18363
|
+
if (ts7.isIdentifier(expression)) {
|
|
17788
18364
|
const declaration = resolveDeclaration(expression, ctx)[0];
|
|
17789
|
-
if (declaration &&
|
|
18365
|
+
if (declaration && ts7.isVariableDeclaration(declaration) && declaration.initializer) {
|
|
17790
18366
|
const key = `${declaration.getSourceFile().fileName}:${declaration.pos}`;
|
|
17791
18367
|
if (seen.has(key))
|
|
17792
18368
|
continue;
|
|
17793
18369
|
const initializer = declaration.initializer;
|
|
17794
|
-
if (
|
|
18370
|
+
if (ts7.isCallExpression(initializer) && isProviderHelper(initializer, "makeEnvironmentProviders")) {
|
|
17795
18371
|
const nested = initializer.arguments[0];
|
|
17796
|
-
if (nested &&
|
|
18372
|
+
if (nested && ts7.isArrayLiteralExpression(nested)) {
|
|
17797
18373
|
seen.add(key);
|
|
17798
18374
|
result.push(...expandProviderExpressions([...nested.elements], ctx, seen));
|
|
17799
18375
|
seen.delete(key);
|
|
@@ -17802,9 +18378,9 @@ function expandProviderExpressions(expressions, ctx, seen = new Set) {
|
|
|
17802
18378
|
}
|
|
17803
18379
|
}
|
|
17804
18380
|
}
|
|
17805
|
-
if (
|
|
18381
|
+
if (ts7.isCallExpression(expression) && isProviderHelper(expression, "makeEnvironmentProviders")) {
|
|
17806
18382
|
const nested = expression.arguments[0];
|
|
17807
|
-
if (nested &&
|
|
18383
|
+
if (nested && ts7.isArrayLiteralExpression(nested)) {
|
|
17808
18384
|
result.push(...expandProviderExpressions([...nested.elements], ctx, seen));
|
|
17809
18385
|
continue;
|
|
17810
18386
|
}
|
|
@@ -17817,7 +18393,7 @@ function isProviderHelper(expression, name) {
|
|
|
17817
18393
|
return nodeText(expression.expression).split(".").pop() === name;
|
|
17818
18394
|
}
|
|
17819
18395
|
function parseFunctionalProvider(expression, exportsSet, ctx) {
|
|
17820
|
-
if (!
|
|
18396
|
+
if (!ts7.isCallExpression(expression))
|
|
17821
18397
|
return;
|
|
17822
18398
|
const helper = nodeText(expression.expression).split(".").pop();
|
|
17823
18399
|
const args = expression.arguments;
|
|
@@ -17830,7 +18406,7 @@ function parseFunctionalProvider(expression, exportsSet, ctx) {
|
|
|
17830
18406
|
return [];
|
|
17831
18407
|
const { name: token, kind: tokenKind } = tokenNameOf(tokenExpr, ctx);
|
|
17832
18408
|
validateProviderCompatibility(tokenExpr, valueExpr, "value", token, ctx, file, line);
|
|
17833
|
-
const importPath =
|
|
18409
|
+
const importPath = ts7.isIdentifier(valueExpr) ? importPathOf(valueExpr, ctx) : undefined;
|
|
17834
18410
|
return [{
|
|
17835
18411
|
token,
|
|
17836
18412
|
tokenKind,
|
|
@@ -17849,7 +18425,7 @@ function parseFunctionalProvider(expression, exportsSet, ctx) {
|
|
|
17849
18425
|
if (!initializer)
|
|
17850
18426
|
return [];
|
|
17851
18427
|
const token = helper === "provideAppInitializer" ? "APP_INITIALIZER" : "ENVIRONMENT_INITIALIZER";
|
|
17852
|
-
const importPath =
|
|
18428
|
+
const importPath = ts7.isIdentifier(initializer) ? importPathOf(initializer, ctx) : undefined;
|
|
17853
18429
|
return [{
|
|
17854
18430
|
token,
|
|
17855
18431
|
tokenKind: "injection-token",
|
|
@@ -17868,7 +18444,7 @@ function parseFunctionalProvider(expression, exportsSet, ctx) {
|
|
|
17868
18444
|
const providers = [];
|
|
17869
18445
|
const routes = args[0];
|
|
17870
18446
|
if (routes) {
|
|
17871
|
-
const importPath =
|
|
18447
|
+
const importPath = ts7.isIdentifier(routes) ? importPathOf(routes, ctx) : undefined;
|
|
17872
18448
|
providers.push({
|
|
17873
18449
|
token: "ROUTE_CONFIG",
|
|
17874
18450
|
tokenKind: "injection-token",
|
|
@@ -17883,7 +18459,7 @@ function parseFunctionalProvider(expression, exportsSet, ctx) {
|
|
|
17883
18459
|
});
|
|
17884
18460
|
}
|
|
17885
18461
|
for (const feature of args.slice(1)) {
|
|
17886
|
-
if (!
|
|
18462
|
+
if (!ts7.isCallExpression(feature))
|
|
17887
18463
|
continue;
|
|
17888
18464
|
const featureName = nodeText(feature.expression).split(".").pop();
|
|
17889
18465
|
if (featureName === "withRouterConfig" && feature.arguments[0]) {
|
|
@@ -17900,8 +18476,8 @@ function parseFunctionalProvider(expression, exportsSet, ctx) {
|
|
|
17900
18476
|
});
|
|
17901
18477
|
} else if (featureName === "withTitleStrategy" && feature.arguments[0]) {
|
|
17902
18478
|
const strategy = feature.arguments[0];
|
|
17903
|
-
const isClass =
|
|
17904
|
-
const importPath =
|
|
18479
|
+
const isClass = ts7.isIdentifier(strategy) && Boolean(resolveDeclaration(strategy, ctx).find((declaration) => ts7.isClassDeclaration(declaration)));
|
|
18480
|
+
const importPath = ts7.isIdentifier(strategy) ? importPathOf(strategy, ctx) : undefined;
|
|
17905
18481
|
providers.push({
|
|
17906
18482
|
token: "TITLE_STRATEGY",
|
|
17907
18483
|
tokenKind: "injection-token",
|
|
@@ -17933,14 +18509,14 @@ function parseFunctionalProvider(expression, exportsSet, ctx) {
|
|
|
17933
18509
|
importModule: "@supacloud/app"
|
|
17934
18510
|
}];
|
|
17935
18511
|
for (const feature of args) {
|
|
17936
|
-
if (!
|
|
18512
|
+
if (!ts7.isCallExpression(feature))
|
|
17937
18513
|
continue;
|
|
17938
18514
|
const featureName = nodeText(feature.expression).split(".").pop();
|
|
17939
18515
|
if (featureName === "withInterceptors") {
|
|
17940
18516
|
for (const interceptorArg of feature.arguments) {
|
|
17941
|
-
const values =
|
|
18517
|
+
const values = ts7.isArrayLiteralExpression(interceptorArg) ? [...interceptorArg.elements] : [interceptorArg];
|
|
17942
18518
|
for (const value of values) {
|
|
17943
|
-
const importPath =
|
|
18519
|
+
const importPath = ts7.isIdentifier(value) ? importPathOf(value, ctx) : undefined;
|
|
17944
18520
|
providers.push({
|
|
17945
18521
|
token: "HTTP_INTERCEPTORS",
|
|
17946
18522
|
tokenKind: "injection-token",
|
|
@@ -17987,9 +18563,9 @@ function providerTokenValueType(expr, ctx) {
|
|
|
17987
18563
|
const typeArguments = typeArgumentsOf(type, ctx);
|
|
17988
18564
|
if (typeArguments.length > 0)
|
|
17989
18565
|
return typeArguments[0];
|
|
17990
|
-
if (
|
|
18566
|
+
if (ts7.isIdentifier(expr)) {
|
|
17991
18567
|
const declaration = resolveDeclaration(expr, ctx)[0];
|
|
17992
|
-
if (declaration &&
|
|
18568
|
+
if (declaration && ts7.isClassDeclaration(declaration)) {
|
|
17993
18569
|
return declaredClassType(declaration, ctx);
|
|
17994
18570
|
}
|
|
17995
18571
|
}
|
|
@@ -17997,9 +18573,9 @@ function providerTokenValueType(expr, ctx) {
|
|
|
17997
18573
|
}
|
|
17998
18574
|
function providerImplementationType(expr, kind, ctx) {
|
|
17999
18575
|
if (kind === "class" || kind === "existing") {
|
|
18000
|
-
if (
|
|
18576
|
+
if (ts7.isIdentifier(expr)) {
|
|
18001
18577
|
const declaration = resolveDeclaration(expr, ctx)[0];
|
|
18002
|
-
if (declaration &&
|
|
18578
|
+
if (declaration && ts7.isClassDeclaration(declaration)) {
|
|
18003
18579
|
return declaredClassType(declaration, ctx);
|
|
18004
18580
|
}
|
|
18005
18581
|
}
|
|
@@ -18009,7 +18585,7 @@ function providerImplementationType(expr, kind, ctx) {
|
|
|
18009
18585
|
}
|
|
18010
18586
|
if (kind === "factory") {
|
|
18011
18587
|
const type = ctx.checker.getTypeAtLocation(expr);
|
|
18012
|
-
const signature = ctx.checker.getSignaturesOfType(type,
|
|
18588
|
+
const signature = ctx.checker.getSignaturesOfType(type, ts7.SignatureKind.Call)[0];
|
|
18013
18589
|
return signature?.getReturnType();
|
|
18014
18590
|
}
|
|
18015
18591
|
return ctx.checker.getTypeAtLocation(expr);
|
|
@@ -18028,16 +18604,16 @@ function isTypeReference(type) {
|
|
|
18028
18604
|
return "target" in type;
|
|
18029
18605
|
}
|
|
18030
18606
|
function isUnknownOrAny(type) {
|
|
18031
|
-
return (type.flags & (
|
|
18607
|
+
return (type.flags & (ts7.TypeFlags.Any | ts7.TypeFlags.Unknown)) !== 0;
|
|
18032
18608
|
}
|
|
18033
18609
|
function parseController(input, ctx) {
|
|
18034
18610
|
let decl;
|
|
18035
|
-
if (
|
|
18611
|
+
if (ts7.isClassDeclaration(input)) {
|
|
18036
18612
|
decl = input;
|
|
18037
18613
|
} else {
|
|
18038
18614
|
const unwrapped = unwrapForwardRef(input);
|
|
18039
|
-
const resolved =
|
|
18040
|
-
if (resolved &&
|
|
18615
|
+
const resolved = ts7.isIdentifier(unwrapped) ? resolveDeclaration(unwrapped, ctx)[0] : undefined;
|
|
18616
|
+
if (resolved && ts7.isClassDeclaration(resolved)) {
|
|
18041
18617
|
decl = resolved;
|
|
18042
18618
|
}
|
|
18043
18619
|
}
|
|
@@ -18050,9 +18626,9 @@ function parseController(input, ctx) {
|
|
|
18050
18626
|
let standalone;
|
|
18051
18627
|
const pathArg = decoratorArguments(controllerDec)[0];
|
|
18052
18628
|
if (pathArg) {
|
|
18053
|
-
if (
|
|
18629
|
+
if (ts7.isStringLiteral(pathArg)) {
|
|
18054
18630
|
path = pathArg.text;
|
|
18055
|
-
} else if (
|
|
18631
|
+
} else if (ts7.isObjectLiteralExpression(pathArg)) {
|
|
18056
18632
|
const p = stringLiteralProp(pathArg, "path");
|
|
18057
18633
|
if (p)
|
|
18058
18634
|
path = p;
|
|
@@ -18075,7 +18651,7 @@ function parseController(input, ctx) {
|
|
|
18075
18651
|
}
|
|
18076
18652
|
}
|
|
18077
18653
|
}
|
|
18078
|
-
for (const method of decl.members.filter(
|
|
18654
|
+
for (const method of decl.members.filter(ts7.isMethodDeclaration)) {
|
|
18079
18655
|
for (const dec of decoratorsOf(method)) {
|
|
18080
18656
|
const name = decoratorName2(dec);
|
|
18081
18657
|
const httpMethod = name ? ROUTE_DECORATORS[name] : undefined;
|
|
@@ -18083,7 +18659,7 @@ function parseController(input, ctx) {
|
|
|
18083
18659
|
continue;
|
|
18084
18660
|
const args = decoratorArguments(dec);
|
|
18085
18661
|
const pathArg = args[0];
|
|
18086
|
-
const routePath = pathArg &&
|
|
18662
|
+
const routePath = pathArg && ts7.isStringLiteral(pathArg) ? pathArg.text : "/";
|
|
18087
18663
|
const route = {
|
|
18088
18664
|
method: httpMethod,
|
|
18089
18665
|
path: routePath,
|
|
@@ -18157,12 +18733,12 @@ function parseController(input, ctx) {
|
|
|
18157
18733
|
} else if (dName === "Headers") {
|
|
18158
18734
|
hasBindingDecorator = true;
|
|
18159
18735
|
const argument = dArgs[0];
|
|
18160
|
-
const bindingName = argument !== undefined &&
|
|
18736
|
+
const bindingName = argument !== undefined && ts7.isStringLiteral(argument) ? argument.text : undefined;
|
|
18161
18737
|
paramNode = { name: pName, kind: "headers", ...bindingName === undefined ? {} : { bindingName } };
|
|
18162
18738
|
} else if (dName === "Cookie") {
|
|
18163
18739
|
hasBindingDecorator = true;
|
|
18164
18740
|
const argument = dArgs[0];
|
|
18165
|
-
const bindingName = argument !== undefined &&
|
|
18741
|
+
const bindingName = argument !== undefined && ts7.isStringLiteral(argument) ? argument.text : undefined;
|
|
18166
18742
|
paramNode = { name: pName, kind: "cookie", ...bindingName === undefined ? {} : { bindingName } };
|
|
18167
18743
|
}
|
|
18168
18744
|
}
|
|
@@ -18224,20 +18800,20 @@ function parseController(input, ctx) {
|
|
|
18224
18800
|
}
|
|
18225
18801
|
} else if (dName === "Title") {
|
|
18226
18802
|
const tArg = mArgs[0];
|
|
18227
|
-
if (tArg &&
|
|
18803
|
+
if (tArg && ts7.isStringLiteral(tArg)) {
|
|
18228
18804
|
route.title = tArg.text;
|
|
18229
18805
|
}
|
|
18230
18806
|
} else if (dName === "Data") {
|
|
18231
18807
|
const dArg = mArgs[0];
|
|
18232
|
-
if (dArg &&
|
|
18808
|
+
if (dArg && ts7.isObjectLiteralExpression(dArg)) {
|
|
18233
18809
|
route.data = { ...route.data, ...parseObjectLiteralValues(dArg) };
|
|
18234
18810
|
}
|
|
18235
18811
|
} else if (dName === "Resolve") {
|
|
18236
18812
|
const rArg = mArgs[0];
|
|
18237
|
-
if (rArg &&
|
|
18813
|
+
if (rArg && ts7.isObjectLiteralExpression(rArg)) {
|
|
18238
18814
|
const resolvers = route.resolvers ?? {};
|
|
18239
18815
|
for (const prop of rArg.properties) {
|
|
18240
|
-
if (
|
|
18816
|
+
if (ts7.isPropertyAssignment(prop)) {
|
|
18241
18817
|
const rName = propertyName(prop.name);
|
|
18242
18818
|
const init = prop.initializer;
|
|
18243
18819
|
if (init)
|
|
@@ -18265,7 +18841,7 @@ function parseController(input, ctx) {
|
|
|
18265
18841
|
}
|
|
18266
18842
|
const parse = parsePolicy.value;
|
|
18267
18843
|
if (parse) {
|
|
18268
|
-
if (
|
|
18844
|
+
if (ts7.isStringLiteral(parse) && parse.text === "none")
|
|
18269
18845
|
route.parse = "none";
|
|
18270
18846
|
else
|
|
18271
18847
|
ctx.diagnostics.push({
|
|
@@ -18277,7 +18853,7 @@ function parseController(input, ctx) {
|
|
|
18277
18853
|
}
|
|
18278
18854
|
const allowDeleteBody = deletePolicy.value;
|
|
18279
18855
|
if (allowDeleteBody) {
|
|
18280
|
-
if (allowDeleteBody.kind ===
|
|
18856
|
+
if (allowDeleteBody.kind === ts7.SyntaxKind.TrueKeyword)
|
|
18281
18857
|
route.allowDeleteBody = true;
|
|
18282
18858
|
else
|
|
18283
18859
|
ctx.diagnostics.push({
|
|
@@ -18302,7 +18878,7 @@ function parseController(input, ctx) {
|
|
|
18302
18878
|
});
|
|
18303
18879
|
}
|
|
18304
18880
|
const contract = getProp(optionsObject, "contract");
|
|
18305
|
-
if (contract &&
|
|
18881
|
+
if (contract && ts7.isObjectLiteralExpression(contract)) {
|
|
18306
18882
|
route.contract = {};
|
|
18307
18883
|
for (const field of ["body", "response", "evidence"]) {
|
|
18308
18884
|
const value = stringLiteralProp(contract, field);
|
|
@@ -18320,7 +18896,7 @@ function parseController(input, ctx) {
|
|
|
18320
18896
|
}
|
|
18321
18897
|
for (const field of ["body", "params", "query", "headers", "cookie", "response"]) {
|
|
18322
18898
|
const schemaExpr = getProp(optionsObject, field);
|
|
18323
|
-
if (schemaExpr &&
|
|
18899
|
+
if (schemaExpr && ts7.isIdentifier(schemaExpr)) {
|
|
18324
18900
|
route[field] = nodeText(schemaExpr);
|
|
18325
18901
|
const importPath = importPathOf(schemaExpr, ctx);
|
|
18326
18902
|
if (importPath)
|
|
@@ -18335,7 +18911,7 @@ function parseController(input, ctx) {
|
|
|
18335
18911
|
const responses = {};
|
|
18336
18912
|
const selectors = new Map;
|
|
18337
18913
|
for (const property of responsesObject.properties) {
|
|
18338
|
-
if (!
|
|
18914
|
+
if (!ts7.isPropertyAssignment(property) || ts7.isComputedPropertyName(property.name))
|
|
18339
18915
|
continue;
|
|
18340
18916
|
const status = propertyName(property.name);
|
|
18341
18917
|
if (!isRouteResponseSelector(status)) {
|
|
@@ -18367,7 +18943,7 @@ function parseController(input, ctx) {
|
|
|
18367
18943
|
}
|
|
18368
18944
|
selectors.set(canonical, status);
|
|
18369
18945
|
const schemaExpr = property.initializer;
|
|
18370
|
-
if (!
|
|
18946
|
+
if (!ts7.isIdentifier(schemaExpr)) {
|
|
18371
18947
|
ctx.diagnostics.push({
|
|
18372
18948
|
severity: "error",
|
|
18373
18949
|
code: "invalid-route-response-map",
|
|
@@ -18389,18 +18965,18 @@ function parseController(input, ctx) {
|
|
|
18389
18965
|
route.responses = responses;
|
|
18390
18966
|
}
|
|
18391
18967
|
const commandExpr = getProp(optionsObject, "command");
|
|
18392
|
-
if (commandExpr &&
|
|
18968
|
+
if (commandExpr && ts7.isIdentifier(commandExpr)) {
|
|
18393
18969
|
const commandDecl = resolveDeclaration(commandExpr, ctx)[0];
|
|
18394
|
-
route.command = commandDecl &&
|
|
18970
|
+
route.command = commandDecl && ts7.isClassDeclaration(commandDecl) ? commandDecl.name?.text ?? commandExpr.text : commandExpr.text;
|
|
18395
18971
|
}
|
|
18396
18972
|
const guardsExpr = getProp(optionsObject, "guards");
|
|
18397
|
-
if (guardsExpr &&
|
|
18973
|
+
if (guardsExpr && ts7.isArrayLiteralExpression(guardsExpr)) {
|
|
18398
18974
|
for (const el of guardsExpr.elements) {
|
|
18399
18975
|
routeGuards.push(tokenText(el, ctx));
|
|
18400
18976
|
}
|
|
18401
18977
|
}
|
|
18402
18978
|
const canMatchExpr = getProp(optionsObject, "canMatch");
|
|
18403
|
-
if (canMatchExpr &&
|
|
18979
|
+
if (canMatchExpr && ts7.isArrayLiteralExpression(canMatchExpr)) {
|
|
18404
18980
|
const canMatchList = [];
|
|
18405
18981
|
for (const el of canMatchExpr.elements) {
|
|
18406
18982
|
canMatchList.push(tokenText(el, ctx));
|
|
@@ -18410,16 +18986,16 @@ function parseController(input, ctx) {
|
|
|
18410
18986
|
}
|
|
18411
18987
|
}
|
|
18412
18988
|
const canDeactivateExpr = getProp(optionsObject, "canDeactivate");
|
|
18413
|
-
if (canDeactivateExpr &&
|
|
18989
|
+
if (canDeactivateExpr && ts7.isArrayLiteralExpression(canDeactivateExpr)) {
|
|
18414
18990
|
for (const el of canDeactivateExpr.elements) {
|
|
18415
18991
|
routeCanDeactivate.push(tokenText(el, ctx));
|
|
18416
18992
|
}
|
|
18417
18993
|
}
|
|
18418
18994
|
const resolversExpr = getProp(optionsObject, "resolvers");
|
|
18419
|
-
if (resolversExpr &&
|
|
18995
|
+
if (resolversExpr && ts7.isObjectLiteralExpression(resolversExpr)) {
|
|
18420
18996
|
const resolvers = {};
|
|
18421
18997
|
for (const prop of resolversExpr.properties) {
|
|
18422
|
-
if (
|
|
18998
|
+
if (ts7.isPropertyAssignment(prop)) {
|
|
18423
18999
|
const rName = propertyName(prop.name);
|
|
18424
19000
|
const init = prop.initializer;
|
|
18425
19001
|
if (init)
|
|
@@ -18431,22 +19007,22 @@ function parseController(input, ctx) {
|
|
|
18431
19007
|
}
|
|
18432
19008
|
}
|
|
18433
19009
|
const redirectToExpr = getProp(optionsObject, "redirectTo");
|
|
18434
|
-
if (redirectToExpr &&
|
|
19010
|
+
if (redirectToExpr && ts7.isStringLiteral(redirectToExpr)) {
|
|
18435
19011
|
route.redirectTo = redirectToExpr.text;
|
|
18436
19012
|
}
|
|
18437
19013
|
const pathMatchExpr = getProp(optionsObject, "pathMatch");
|
|
18438
|
-
if (pathMatchExpr &&
|
|
19014
|
+
if (pathMatchExpr && ts7.isStringLiteral(pathMatchExpr)) {
|
|
18439
19015
|
const val = pathMatchExpr.text;
|
|
18440
19016
|
if (val === "full" || val === "prefix") {
|
|
18441
19017
|
route.pathMatch = val;
|
|
18442
19018
|
}
|
|
18443
19019
|
}
|
|
18444
19020
|
const titleExpr = getProp(optionsObject, "title");
|
|
18445
|
-
if (titleExpr &&
|
|
19021
|
+
if (titleExpr && ts7.isStringLiteral(titleExpr)) {
|
|
18446
19022
|
route.title = titleExpr.text;
|
|
18447
19023
|
}
|
|
18448
19024
|
const dataExpr = getProp(optionsObject, "data");
|
|
18449
|
-
if (dataExpr &&
|
|
19025
|
+
if (dataExpr && ts7.isObjectLiteralExpression(dataExpr)) {
|
|
18450
19026
|
route.data = { ...route.data, ...parseObjectLiteralValues(dataExpr) };
|
|
18451
19027
|
}
|
|
18452
19028
|
const aspects = parseAspectRefs(getProp(optionsObject, "aspects"), ctx, `route ${httpMethod} ${routePath}`);
|
|
@@ -18468,6 +19044,7 @@ function parseController(input, ctx) {
|
|
|
18468
19044
|
scope: injectable?.scope ?? "request",
|
|
18469
19045
|
deps,
|
|
18470
19046
|
...hasDestroyHook(decl) ? { hasOnDestroy: true } : {},
|
|
19047
|
+
...hasInitHook(decl) ? { hasOnInit: true } : {},
|
|
18471
19048
|
...optionalDeps.length > 0 ? { optionalDeps } : {},
|
|
18472
19049
|
...selfDeps.length > 0 ? { selfDeps } : {},
|
|
18473
19050
|
...skipSelfDeps.length > 0 ? { skipSelfDeps } : {},
|
|
@@ -18501,7 +19078,7 @@ function parseJobOptions(meta, owner, ctx) {
|
|
|
18501
19078
|
const expression = getProp(meta, field);
|
|
18502
19079
|
if (!expression)
|
|
18503
19080
|
continue;
|
|
18504
|
-
if (!
|
|
19081
|
+
if (!ts7.isIdentifier(expression)) {
|
|
18505
19082
|
jobOptionError(ctx, "invalid-job-schema", `${owner} 的 ${field} schema 必须是可静态解析的标识符引用,不能使用内联调用或动态表达式`, expression, "SC4019", `将 schema 提取为命名导出,例如 ${field}: ${field === "input" ? "JobInput" : "JobOutput"}。`);
|
|
18506
19083
|
continue;
|
|
18507
19084
|
}
|
|
@@ -18525,7 +19102,7 @@ function parseJobEnum(meta, field, allowed, owner, ctx, code, errorCode) {
|
|
|
18525
19102
|
const expression = getProp(meta, field);
|
|
18526
19103
|
if (!expression)
|
|
18527
19104
|
return;
|
|
18528
|
-
if (!
|
|
19105
|
+
if (!ts7.isStringLiteral(expression) || !allowed.includes(expression.text)) {
|
|
18529
19106
|
jobOptionError(ctx, code, `${owner} 的 ${field} 必须是 ${allowed.map((value) => JSON.stringify(value)).join(" 或 ")} 字符串字面量`, expression, errorCode);
|
|
18530
19107
|
return;
|
|
18531
19108
|
}
|
|
@@ -18535,7 +19112,7 @@ function parseJobInteger(meta, field, min, max, owner, ctx, code, errorCode) {
|
|
|
18535
19112
|
const expression = getProp(meta, field);
|
|
18536
19113
|
if (!expression)
|
|
18537
19114
|
return;
|
|
18538
|
-
const value =
|
|
19115
|
+
const value = ts7.isNumericLiteral(expression) ? Number(expression.text) : Number.NaN;
|
|
18539
19116
|
if (!Number.isSafeInteger(value) || value < min || value > max) {
|
|
18540
19117
|
jobOptionError(ctx, code, `${owner} 的 ${field} 必须是 ${min} 到 ${max} 之间的安全整数`, expression, errorCode);
|
|
18541
19118
|
return;
|
|
@@ -18571,7 +19148,7 @@ function isOpaqueSchema(identifier, declaration, ctx) {
|
|
|
18571
19148
|
const property = type.getProperty(kind.value === "Union" ? "anyOf" : "allOf");
|
|
18572
19149
|
if (!property)
|
|
18573
19150
|
return true;
|
|
18574
|
-
const members = ctx.checker.getIndexTypeOfType(ctx.checker.getTypeOfSymbolAtLocation(property, identifier),
|
|
19151
|
+
const members = ctx.checker.getIndexTypeOfType(ctx.checker.getTypeOfSymbolAtLocation(property, identifier), ts7.IndexKind.Number);
|
|
18575
19152
|
if (!members)
|
|
18576
19153
|
return true;
|
|
18577
19154
|
const branches = members.isUnion() ? members.types : [members];
|
|
@@ -18583,8 +19160,8 @@ function isOpaqueSchema(identifier, declaration, ctx) {
|
|
|
18583
19160
|
}
|
|
18584
19161
|
if (opaque(ctx.checker.getTypeAtLocation(identifier), new Set))
|
|
18585
19162
|
return true;
|
|
18586
|
-
const initializer = declaration &&
|
|
18587
|
-
return Boolean(initializer &&
|
|
19163
|
+
const initializer = declaration && ts7.isVariableDeclaration(declaration) ? declaration.initializer : undefined;
|
|
19164
|
+
return Boolean(initializer && ts7.isCallExpression(initializer) && ts7.isPropertyAccessExpression(initializer.expression) && ["Unknown", "Any"].includes(initializer.expression.name.text));
|
|
18588
19165
|
}
|
|
18589
19166
|
function checkedRpc(meta, ctx) {
|
|
18590
19167
|
const expression = getProp(meta, "rpc");
|
|
@@ -18605,7 +19182,7 @@ function checkedRpc(meta, ctx) {
|
|
|
18605
19182
|
}
|
|
18606
19183
|
function classDeps(cls, ctx) {
|
|
18607
19184
|
const injectable = parseInjectableOptions(cls, ctx);
|
|
18608
|
-
const ctor = cls.members.find(
|
|
19185
|
+
const ctor = cls.members.find(ts7.isConstructorDeclaration);
|
|
18609
19186
|
const deps = injectable?.deps ? [...injectable.deps] : [];
|
|
18610
19187
|
const optionalDeps = [];
|
|
18611
19188
|
const selfDeps = [];
|
|
@@ -18639,23 +19216,23 @@ function classDeps(cls, ctx) {
|
|
|
18639
19216
|
}
|
|
18640
19217
|
});
|
|
18641
19218
|
}
|
|
18642
|
-
for (const prop of cls.members.filter(
|
|
19219
|
+
for (const prop of cls.members.filter(ts7.isPropertyDeclaration)) {
|
|
18643
19220
|
const init = prop.initializer;
|
|
18644
|
-
if (init &&
|
|
19221
|
+
if (init && ts7.isCallExpression(init)) {
|
|
18645
19222
|
const callName = nodeText(init.expression).split(".").pop();
|
|
18646
19223
|
if (callName === "inject") {
|
|
18647
19224
|
const [tokenArg, optionsArg] = init.arguments;
|
|
18648
19225
|
if (tokenArg) {
|
|
18649
19226
|
const tokenName = tokenText(tokenArg, ctx);
|
|
18650
19227
|
const unwrappedToken = unwrapForwardRef(tokenArg);
|
|
18651
|
-
const known =
|
|
19228
|
+
const known = ts7.isStringLiteral(unwrappedToken) || ts7.isIdentifier(unwrappedToken) && (ctx.tokensByName.has(tokenName) || ctx.classesByName.has(tokenName));
|
|
18652
19229
|
if (!known) {
|
|
18653
19230
|
missing = true;
|
|
18654
19231
|
continue;
|
|
18655
19232
|
}
|
|
18656
19233
|
if (!deps.includes(tokenName))
|
|
18657
19234
|
deps.push(tokenName);
|
|
18658
|
-
const options = optionsArg &&
|
|
19235
|
+
const options = optionsArg && ts7.isObjectLiteralExpression(optionsArg) ? {
|
|
18659
19236
|
optional: booleanProp(optionsArg, "optional") ?? false,
|
|
18660
19237
|
self: booleanProp(optionsArg, "self") ?? false,
|
|
18661
19238
|
skipSelf: booleanProp(optionsArg, "skipSelf") ?? false,
|
|
@@ -18670,9 +19247,9 @@ function classDeps(cls, ctx) {
|
|
|
18670
19247
|
if (options.host && !hostDeps.includes(tokenName))
|
|
18671
19248
|
hostDeps.push(tokenName);
|
|
18672
19249
|
if (!functionalInjects.some((entry) => entry.token === tokenName)) {
|
|
18673
|
-
const declaration =
|
|
19250
|
+
const declaration = ts7.isIdentifier(unwrappedToken) ? resolveDeclaration(unwrappedToken, ctx)[0] : undefined;
|
|
18674
19251
|
const localFile = declaration && isProjectSourcePath(declaration.getSourceFile().fileName, ctx.rootDir) ? declaration.getSourceFile().fileName : undefined;
|
|
18675
|
-
const importModule = declaration && !localFile &&
|
|
19252
|
+
const importModule = declaration && !localFile && ts7.isIdentifier(unwrappedToken) ? importModuleOf(unwrappedToken, ctx) : undefined;
|
|
18676
19253
|
functionalInjects.push({
|
|
18677
19254
|
token: tokenName,
|
|
18678
19255
|
expression: nodeText(unwrappedToken),
|
|
@@ -18716,7 +19293,7 @@ function parseInjectableOptions(cls, ctx) {
|
|
|
18716
19293
|
}
|
|
18717
19294
|
function parseInjectParams(cls, ctx) {
|
|
18718
19295
|
const result = new Map;
|
|
18719
|
-
const ctor = cls.members.find(
|
|
19296
|
+
const ctor = cls.members.find(ts7.isConstructorDeclaration);
|
|
18720
19297
|
if (!ctor)
|
|
18721
19298
|
return result;
|
|
18722
19299
|
ctor.parameters.forEach((param, index) => {
|
|
@@ -18732,7 +19309,7 @@ function parseInjectParams(cls, ctx) {
|
|
|
18732
19309
|
}
|
|
18733
19310
|
function parseOptionalParams(cls) {
|
|
18734
19311
|
const result = new Set;
|
|
18735
|
-
const ctor = cls.members.find(
|
|
19312
|
+
const ctor = cls.members.find(ts7.isConstructorDeclaration);
|
|
18736
19313
|
if (!ctor)
|
|
18737
19314
|
return result;
|
|
18738
19315
|
ctor.parameters.forEach((param, index) => {
|
|
@@ -18747,7 +19324,7 @@ function parseOptionalParams(cls) {
|
|
|
18747
19324
|
}
|
|
18748
19325
|
function parseModifierParams(cls, modifierName) {
|
|
18749
19326
|
const result = new Set;
|
|
18750
|
-
const ctor = cls.members.find(
|
|
19327
|
+
const ctor = cls.members.find(ts7.isConstructorDeclaration);
|
|
18751
19328
|
if (!ctor)
|
|
18752
19329
|
return result;
|
|
18753
19330
|
ctor.parameters.forEach((param, index) => {
|
|
@@ -18759,13 +19336,13 @@ function parseModifierParams(cls, modifierName) {
|
|
|
18759
19336
|
return result;
|
|
18760
19337
|
}
|
|
18761
19338
|
function unwrapForwardRef(expr) {
|
|
18762
|
-
if (
|
|
19339
|
+
if (ts7.isCallExpression(expr)) {
|
|
18763
19340
|
const exprText = nodeText(expr.expression);
|
|
18764
19341
|
if (exprText === "forwardRef" || exprText.endsWith(".forwardRef")) {
|
|
18765
19342
|
const arg = expr.arguments[0];
|
|
18766
|
-
if (arg && (
|
|
19343
|
+
if (arg && (ts7.isArrowFunction(arg) || ts7.isFunctionExpression(arg))) {
|
|
18767
19344
|
const body = arg.body;
|
|
18768
|
-
if (body &&
|
|
19345
|
+
if (body && ts7.isExpression(body)) {
|
|
18769
19346
|
return unwrapForwardRef(body);
|
|
18770
19347
|
}
|
|
18771
19348
|
}
|
|
@@ -18775,13 +19352,13 @@ function unwrapForwardRef(expr) {
|
|
|
18775
19352
|
}
|
|
18776
19353
|
function tokenText(expr, ctx) {
|
|
18777
19354
|
const unwrapped = unwrapForwardRef(expr);
|
|
18778
|
-
if (
|
|
19355
|
+
if (ts7.isStringLiteral(unwrapped))
|
|
18779
19356
|
return unwrapped.text;
|
|
18780
|
-
if (
|
|
19357
|
+
if (ts7.isIdentifier(unwrapped)) {
|
|
18781
19358
|
const decl = resolveDeclaration(unwrapped, ctx)[0];
|
|
18782
|
-
if (decl &&
|
|
19359
|
+
if (decl && ts7.isClassDeclaration(decl))
|
|
18783
19360
|
return decl.name?.text ?? unwrapped.text;
|
|
18784
|
-
if (decl &&
|
|
19361
|
+
if (decl && ts7.isVariableDeclaration(decl))
|
|
18785
19362
|
return variableName(decl);
|
|
18786
19363
|
}
|
|
18787
19364
|
return nodeText(unwrapped);
|
|
@@ -18801,12 +19378,12 @@ function resolveScope(input, ctx) {
|
|
|
18801
19378
|
}
|
|
18802
19379
|
function tokenNameOf(expr, ctx) {
|
|
18803
19380
|
const unwrapped = unwrapForwardRef(expr);
|
|
18804
|
-
if (
|
|
19381
|
+
if (ts7.isIdentifier(unwrapped)) {
|
|
18805
19382
|
const decl = resolveDeclaration(unwrapped, ctx)[0];
|
|
18806
|
-
if (decl &&
|
|
19383
|
+
if (decl && ts7.isClassDeclaration(decl)) {
|
|
18807
19384
|
return { name: decl.name?.text ?? nodeText(expr), kind: "class" };
|
|
18808
19385
|
}
|
|
18809
|
-
if (decl &&
|
|
19386
|
+
if (decl && ts7.isVariableDeclaration(decl)) {
|
|
18810
19387
|
const name = variableName(decl);
|
|
18811
19388
|
return { name, kind: ctx.tokensByName.has(name) ? "injection-token" : "class" };
|
|
18812
19389
|
}
|
|
@@ -18822,10 +19399,10 @@ function resolveDeclaration(id, ctx) {
|
|
|
18822
19399
|
return [];
|
|
18823
19400
|
let declarations = symbol.declarations ?? [];
|
|
18824
19401
|
for (let guard = 0;guard < 4; guard += 1) {
|
|
18825
|
-
const isAlias = declarations.some((d) =>
|
|
19402
|
+
const isAlias = declarations.some((d) => ts7.isImportSpecifier(d) || ts7.isImportClause(d) || ts7.isNamespaceImport(d));
|
|
18826
19403
|
if (!isAlias)
|
|
18827
19404
|
break;
|
|
18828
|
-
if (!(symbol.flags &
|
|
19405
|
+
if (!(symbol.flags & ts7.SymbolFlags.Alias))
|
|
18829
19406
|
break;
|
|
18830
19407
|
const aliased = ctx.checker.getAliasedSymbol(symbol);
|
|
18831
19408
|
symbol = aliased;
|
|
@@ -18845,9 +19422,9 @@ function importModuleOf(id, ctx) {
|
|
|
18845
19422
|
for (const declaration of declarations) {
|
|
18846
19423
|
let current = declaration;
|
|
18847
19424
|
while (current) {
|
|
18848
|
-
if (
|
|
19425
|
+
if (ts7.isImportDeclaration(current)) {
|
|
18849
19426
|
const moduleSpecifier = current.moduleSpecifier;
|
|
18850
|
-
return
|
|
19427
|
+
return ts7.isStringLiteral(moduleSpecifier) ? moduleSpecifier.text : undefined;
|
|
18851
19428
|
}
|
|
18852
19429
|
current = current.parent;
|
|
18853
19430
|
}
|
|
@@ -18859,27 +19436,27 @@ function findDecorator(cls, name) {
|
|
|
18859
19436
|
}
|
|
18860
19437
|
function decoratorName2(dec) {
|
|
18861
19438
|
const expr = dec.expression;
|
|
18862
|
-
if (
|
|
19439
|
+
if (ts7.isCallExpression(expr)) {
|
|
18863
19440
|
return nodeText(expr.expression).split(".").pop();
|
|
18864
19441
|
}
|
|
18865
|
-
if (
|
|
19442
|
+
if (ts7.isIdentifier(expr))
|
|
18866
19443
|
return expr.text;
|
|
18867
19444
|
return;
|
|
18868
19445
|
}
|
|
18869
19446
|
function decoratorObjectArg(dec) {
|
|
18870
19447
|
const expr = dec.expression;
|
|
18871
|
-
if (!
|
|
19448
|
+
if (!ts7.isCallExpression(expr))
|
|
18872
19449
|
return;
|
|
18873
19450
|
const arg = expr.arguments[0];
|
|
18874
|
-
return arg &&
|
|
19451
|
+
return arg && ts7.isObjectLiteralExpression(arg) ? arg : undefined;
|
|
18875
19452
|
}
|
|
18876
19453
|
function getProp(obj, name) {
|
|
18877
|
-
const prop = obj.properties.find((item) => (
|
|
19454
|
+
const prop = obj.properties.find((item) => (ts7.isPropertyAssignment(item) || ts7.isShorthandPropertyAssignment(item)) && propertyName(item.name) === name);
|
|
18878
19455
|
if (!prop)
|
|
18879
19456
|
return;
|
|
18880
|
-
if (
|
|
19457
|
+
if (ts7.isPropertyAssignment(prop))
|
|
18881
19458
|
return prop.initializer;
|
|
18882
|
-
if (
|
|
19459
|
+
if (ts7.isShorthandPropertyAssignment(prop))
|
|
18883
19460
|
return prop.name;
|
|
18884
19461
|
return;
|
|
18885
19462
|
}
|
|
@@ -18887,10 +19464,10 @@ function toCompilerDiagnostic(diagnostic, rootDir) {
|
|
|
18887
19464
|
const file = diagnostic.file;
|
|
18888
19465
|
const position = file && diagnostic.start !== undefined ? file.getLineAndCharacterOfPosition(diagnostic.start) : undefined;
|
|
18889
19466
|
return {
|
|
18890
|
-
severity: diagnostic.category ===
|
|
19467
|
+
severity: diagnostic.category === ts7.DiagnosticCategory.Error ? "error" : "warn",
|
|
18891
19468
|
code: `typescript-${diagnostic.code}`,
|
|
18892
19469
|
errorCode: `TS${diagnostic.code}`,
|
|
18893
|
-
message:
|
|
19470
|
+
message: ts7.flattenDiagnosticMessageText(diagnostic.messageText, `
|
|
18894
19471
|
`),
|
|
18895
19472
|
...file ? { file: sourcePath(rootDir, file.fileName) } : {},
|
|
18896
19473
|
...position ? { line: position.line + 1 } : {}
|
|
@@ -18898,16 +19475,16 @@ function toCompilerDiagnostic(diagnostic, rootDir) {
|
|
|
18898
19475
|
}
|
|
18899
19476
|
function stringLiteralProp(obj, name) {
|
|
18900
19477
|
const expr = getProp(obj, name);
|
|
18901
|
-
return expr &&
|
|
19478
|
+
return expr && ts7.isStringLiteral(expr) ? expr.text : undefined;
|
|
18902
19479
|
}
|
|
18903
19480
|
function arrayProp(obj, name) {
|
|
18904
19481
|
const expr = getProp(obj, name);
|
|
18905
|
-
return expr &&
|
|
19482
|
+
return expr && ts7.isArrayLiteralExpression(expr) ? [...expr.elements] : [];
|
|
18906
19483
|
}
|
|
18907
19484
|
function parseAspectRefs(expression, ctx, owner) {
|
|
18908
19485
|
if (!expression)
|
|
18909
19486
|
return [];
|
|
18910
|
-
if (!
|
|
19487
|
+
if (!ts7.isArrayLiteralExpression(expression)) {
|
|
18911
19488
|
ctx.diagnostics.push({
|
|
18912
19489
|
severity: "error",
|
|
18913
19490
|
code: "dynamic-aspect-reference",
|
|
@@ -18922,7 +19499,7 @@ function parseAspectRefs(expression, ctx, owner) {
|
|
|
18922
19499
|
}
|
|
18923
19500
|
const refs = [];
|
|
18924
19501
|
for (const element of expression.elements) {
|
|
18925
|
-
if (
|
|
19502
|
+
if (ts7.isSpreadElement(element) || !ts7.isIdentifier(element)) {
|
|
18926
19503
|
ctx.diagnostics.push({
|
|
18927
19504
|
severity: "error",
|
|
18928
19505
|
code: "dynamic-aspect-reference",
|
|
@@ -18935,7 +19512,7 @@ function parseAspectRefs(expression, ctx, owner) {
|
|
|
18935
19512
|
});
|
|
18936
19513
|
continue;
|
|
18937
19514
|
}
|
|
18938
|
-
const declaration = resolveDeclaration(element, ctx).find((candidate) =>
|
|
19515
|
+
const declaration = resolveDeclaration(element, ctx).find((candidate) => ts7.isFunctionDeclaration(candidate) || ts7.isVariableDeclaration(candidate) && candidate.initializer !== undefined && (ts7.isArrowFunction(candidate.initializer) || ts7.isFunctionExpression(candidate.initializer)));
|
|
18939
19516
|
if (!declaration) {
|
|
18940
19517
|
ctx.diagnostics.push({
|
|
18941
19518
|
severity: "error",
|
|
@@ -18949,7 +19526,7 @@ function parseAspectRefs(expression, ctx, owner) {
|
|
|
18949
19526
|
});
|
|
18950
19527
|
continue;
|
|
18951
19528
|
}
|
|
18952
|
-
const name =
|
|
19529
|
+
const name = ts7.isFunctionDeclaration(declaration) ? declaration.name?.text : ts7.isVariableDeclaration(declaration) ? variableName(declaration) : undefined;
|
|
18953
19530
|
if (!name)
|
|
18954
19531
|
continue;
|
|
18955
19532
|
const declaredFile = declaration.getSourceFile().fileName;
|
|
@@ -18969,9 +19546,9 @@ function booleanProp(obj, name) {
|
|
|
18969
19546
|
const expr = getProp(obj, name);
|
|
18970
19547
|
if (!expr)
|
|
18971
19548
|
return;
|
|
18972
|
-
if (expr.kind ===
|
|
19549
|
+
if (expr.kind === ts7.SyntaxKind.TrueKeyword)
|
|
18973
19550
|
return true;
|
|
18974
|
-
if (expr.kind ===
|
|
19551
|
+
if (expr.kind === ts7.SyntaxKind.FalseKeyword)
|
|
18975
19552
|
return false;
|
|
18976
19553
|
return;
|
|
18977
19554
|
}
|
|
@@ -18985,15 +19562,15 @@ function parseBindingOptions(args, defaultName) {
|
|
|
18985
19562
|
let defaultValue;
|
|
18986
19563
|
const first = args[0];
|
|
18987
19564
|
const second = args[1];
|
|
18988
|
-
if (first &&
|
|
19565
|
+
if (first && ts7.isStringLiteral(first)) {
|
|
18989
19566
|
name = first.text;
|
|
18990
|
-
} else if (first &&
|
|
19567
|
+
} else if (first && ts7.isObjectLiteralExpression(first)) {
|
|
18991
19568
|
const nameProp = getProp(first, "name");
|
|
18992
|
-
if (nameProp &&
|
|
19569
|
+
if (nameProp && ts7.isStringLiteral(nameProp)) {
|
|
18993
19570
|
name = nameProp.text;
|
|
18994
19571
|
}
|
|
18995
19572
|
const trProp = getProp(first, "transform");
|
|
18996
|
-
if (trProp &&
|
|
19573
|
+
if (trProp && ts7.isStringLiteral(trProp)) {
|
|
18997
19574
|
const val = trProp.text;
|
|
18998
19575
|
if (val === "number" || val === "boolean" || val === "string") {
|
|
18999
19576
|
transform = val;
|
|
@@ -19004,9 +19581,9 @@ function parseBindingOptions(args, defaultName) {
|
|
|
19004
19581
|
defaultValue = parseLiteralValue(defProp);
|
|
19005
19582
|
}
|
|
19006
19583
|
}
|
|
19007
|
-
if (second &&
|
|
19584
|
+
if (second && ts7.isObjectLiteralExpression(second)) {
|
|
19008
19585
|
const trProp = getProp(second, "transform");
|
|
19009
|
-
if (trProp &&
|
|
19586
|
+
if (trProp && ts7.isStringLiteral(trProp)) {
|
|
19010
19587
|
const val = trProp.text;
|
|
19011
19588
|
if (val === "number" || val === "boolean" || val === "string") {
|
|
19012
19589
|
transform = val;
|
|
@@ -19020,18 +19597,18 @@ function parseBindingOptions(args, defaultName) {
|
|
|
19020
19597
|
return { name, ...transform ? { transform } : {}, default: defaultValue };
|
|
19021
19598
|
}
|
|
19022
19599
|
function parseLiteralValue(node) {
|
|
19023
|
-
if (
|
|
19600
|
+
if (ts7.isStringLiteral(node))
|
|
19024
19601
|
return node.text;
|
|
19025
|
-
if (
|
|
19602
|
+
if (ts7.isNumericLiteral(node))
|
|
19026
19603
|
return Number(node.text);
|
|
19027
|
-
if (node.kind ===
|
|
19604
|
+
if (node.kind === ts7.SyntaxKind.TrueKeyword)
|
|
19028
19605
|
return true;
|
|
19029
|
-
if (node.kind ===
|
|
19606
|
+
if (node.kind === ts7.SyntaxKind.FalseKeyword)
|
|
19030
19607
|
return false;
|
|
19031
|
-
if (
|
|
19608
|
+
if (ts7.isArrayLiteralExpression(node)) {
|
|
19032
19609
|
return node.elements.map(parseLiteralValue);
|
|
19033
19610
|
}
|
|
19034
|
-
if (
|
|
19611
|
+
if (ts7.isObjectLiteralExpression(node)) {
|
|
19035
19612
|
return parseObjectLiteralValues(node);
|
|
19036
19613
|
}
|
|
19037
19614
|
return;
|
|
@@ -19039,7 +19616,7 @@ function parseLiteralValue(node) {
|
|
|
19039
19616
|
function parseObjectLiteralValues(obj) {
|
|
19040
19617
|
const result = {};
|
|
19041
19618
|
for (const prop of obj.properties) {
|
|
19042
|
-
if (
|
|
19619
|
+
if (ts7.isPropertyAssignment(prop)) {
|
|
19043
19620
|
const name = propertyName(prop.name);
|
|
19044
19621
|
const init = prop.initializer;
|
|
19045
19622
|
if (init) {
|
|
@@ -19056,7 +19633,7 @@ function sourcePath(rootDir, absFile) {
|
|
|
19056
19633
|
return relative2(rootDir, absFile).split(sep2).join("/");
|
|
19057
19634
|
}
|
|
19058
19635
|
function unwrapInfraResourceExpression(value) {
|
|
19059
|
-
while (
|
|
19636
|
+
while (ts7.isParenthesizedExpression(value) || ts7.isAsExpression(value) || ts7.isTypeAssertionExpression(value) || ts7.isSatisfiesExpression(value) || ts7.isNonNullExpression(value)) {
|
|
19060
19637
|
value = value.expression;
|
|
19061
19638
|
}
|
|
19062
19639
|
return value;
|
|
@@ -19066,7 +19643,7 @@ function infraResourceProp(obj, name, ctx) {
|
|
|
19066
19643
|
if (members.length === 0)
|
|
19067
19644
|
return;
|
|
19068
19645
|
const member = members[0];
|
|
19069
|
-
if (members.length !== 1 || !
|
|
19646
|
+
if (members.length !== 1 || !ts7.isPropertyAssignment(member) || ts7.isComputedPropertyName(member.name) || obj.properties.some(ts7.isSpreadAssignment)) {
|
|
19070
19647
|
pushResourceError(ctx, "unknown-resource", `${name} 必须是唯一、显式的静态属性,且不能被对象展开覆盖`, sourcePath(ctx.rootDir, obj.getSourceFile().fileName), lineOf(member), `使用 ${name}: [...],不要使用简写属性、计算属性或对象展开。`);
|
|
19071
19648
|
return;
|
|
19072
19649
|
}
|
|
@@ -19079,7 +19656,7 @@ function indexInfraResource(cls, className, sf, ctx) {
|
|
|
19079
19656
|
const file = sourcePath(ctx.rootDir, sf.fileName);
|
|
19080
19657
|
const line = lineOf(cls);
|
|
19081
19658
|
const options = decoratorObjectArg(decorator);
|
|
19082
|
-
if (!options || options.properties.some((property) => !
|
|
19659
|
+
if (!options || options.properties.some((property) => !ts7.isPropertyAssignment(property) || ts7.isComputedPropertyName(property.name)) || new Set(options.properties.map((property) => property.name && propertyName(property.name))).size !== options.properties.length) {
|
|
19083
19660
|
pushResourceError(ctx, "invalid-resource-kind", "@InfraResource 需要静态的 { name, kind } 对象参数", file, line, '使用 @InfraResource({ name: "orders-db", kind: "database" }),不要使用对象展开或重复属性。');
|
|
19084
19661
|
return;
|
|
19085
19662
|
}
|
|
@@ -19110,14 +19687,14 @@ function indexInfraResource(cls, className, sf, ctx) {
|
|
|
19110
19687
|
}
|
|
19111
19688
|
function resolveInfraResourceRef(el, ctx, owner, file, line) {
|
|
19112
19689
|
const unwrapped = unwrapInfraResourceExpression(unwrapForwardRef(unwrapInfraResourceExpression(el)));
|
|
19113
|
-
const identifier =
|
|
19690
|
+
const identifier = ts7.isIdentifier(unwrapped) ? unwrapped : ts7.isPropertyAccessExpression(unwrapped) && ts7.isIdentifier(unwrapped.name) ? unwrapped.name : undefined;
|
|
19114
19691
|
let symbol = identifier ? ctx.checker.getSymbolAtLocation(identifier) : undefined;
|
|
19115
19692
|
const visited = new Set;
|
|
19116
|
-
while (symbol && symbol.flags &
|
|
19693
|
+
while (symbol && symbol.flags & ts7.SymbolFlags.Alias && !visited.has(symbol)) {
|
|
19117
19694
|
visited.add(symbol);
|
|
19118
19695
|
symbol = ctx.checker.getAliasedSymbol(symbol);
|
|
19119
19696
|
}
|
|
19120
|
-
const declaration = symbol?.declarations?.find(
|
|
19697
|
+
const declaration = symbol?.declarations?.find(ts7.isClassDeclaration);
|
|
19121
19698
|
const resource = declaration ? ctx.infraResources.get(declaration) : undefined;
|
|
19122
19699
|
if (!resource) {
|
|
19123
19700
|
pushResourceError(ctx, "unknown-resource", `${owner} 引用的资源 '${nodeText(el)}' 未声明为 @InfraResource`, file, line, "引用实际声明了 @InfraResource({ name, kind }) 的类,不能依赖其他文件的同名类。");
|
|
@@ -19130,7 +19707,7 @@ function parseModuleInfraResources(value, ctx, owner, file, line) {
|
|
|
19130
19707
|
if (!value)
|
|
19131
19708
|
return names;
|
|
19132
19709
|
const array = unwrapInfraResourceExpression(value);
|
|
19133
|
-
if (!
|
|
19710
|
+
if (!ts7.isArrayLiteralExpression(array)) {
|
|
19134
19711
|
pushResourceError(ctx, "unknown-resource", `${owner} 的 resources 必须是静态数组`, file, line, "使用 resources: [ResourceClass];省略该属性表示不声明资源。");
|
|
19135
19712
|
return names;
|
|
19136
19713
|
}
|
|
@@ -19145,14 +19722,14 @@ function parseInfraResourceUseRefs(value, ctx, owner, file, line) {
|
|
|
19145
19722
|
if (!value)
|
|
19146
19723
|
return [];
|
|
19147
19724
|
const array = unwrapInfraResourceExpression(value);
|
|
19148
|
-
if (!
|
|
19725
|
+
if (!ts7.isArrayLiteralExpression(array)) {
|
|
19149
19726
|
pushResourceError(ctx, "unknown-resource", `${owner} 的 uses 必须是静态数组`, file, line, '使用 uses: [{ resource: ResourceClass, operations: ["read"] }]。');
|
|
19150
19727
|
return [];
|
|
19151
19728
|
}
|
|
19152
19729
|
const uses = new Map;
|
|
19153
19730
|
for (const element of array.elements) {
|
|
19154
19731
|
const entry = unwrapInfraResourceExpression(element);
|
|
19155
|
-
if (!
|
|
19732
|
+
if (!ts7.isObjectLiteralExpression(entry) || entry.properties.some((property) => !ts7.isPropertyAssignment(property) || ts7.isComputedPropertyName(property.name) || !["resource", "operations"].includes(propertyName(property.name))) || new Set(entry.properties.map((property) => property.name && propertyName(property.name))).size !== entry.properties.length) {
|
|
19156
19733
|
pushResourceError(ctx, "unknown-resource", `${owner} 的 uses 项必须是显式的 { resource, operations } 对象`, file, lineOf(element), "使用显式属性,不要使用对象/数组展开、简写、计算属性或重复属性。");
|
|
19157
19734
|
continue;
|
|
19158
19735
|
}
|
|
@@ -19170,14 +19747,14 @@ function parseInfraResourceUseRefs(value, ctx, owner, file, line) {
|
|
|
19170
19747
|
operations.add("read");
|
|
19171
19748
|
} else {
|
|
19172
19749
|
const operationArray = unwrapInfraResourceExpression(operationsEl);
|
|
19173
|
-
if (!
|
|
19750
|
+
if (!ts7.isArrayLiteralExpression(operationArray) || operationArray.elements.length === 0) {
|
|
19174
19751
|
pushResourceError(ctx, "invalid-resource-operation", `${owner} 对资源 '${resource}' 的 operations 必须是非空静态数组`, file, lineOf(operationsEl), "仅省略 operations 时默认 read;显式填写时必须包含有效操作。");
|
|
19175
19752
|
continue;
|
|
19176
19753
|
}
|
|
19177
19754
|
let invalid = false;
|
|
19178
19755
|
for (const operationEl of operationArray.elements) {
|
|
19179
19756
|
const operation = unwrapInfraResourceExpression(operationEl);
|
|
19180
|
-
if (!
|
|
19757
|
+
if (!ts7.isStringLiteral(operation) || !isInfraResourceOperation(operation.text)) {
|
|
19181
19758
|
pushResourceError(ctx, "invalid-resource-operation", `${owner} 对资源 '${resource}' 声明了无效操作 '${nodeText(operationEl)}'`, file, lineOf(operationEl), `operations 只能包含字符串字面量 ${INFRA_RESOURCE_OPERATIONS.join(" | ")}。`);
|
|
19182
19759
|
invalid = true;
|
|
19183
19760
|
continue;
|
|
@@ -19222,11 +19799,11 @@ import { join as join4 } from "node:path";
|
|
|
19222
19799
|
|
|
19223
19800
|
// src/type-safety.ts
|
|
19224
19801
|
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
|
|
19225
|
-
import { dirname as dirname3, join as join3, relative as relative3, resolve as
|
|
19226
|
-
import * as
|
|
19802
|
+
import { dirname as dirname3, join as join3, relative as relative3, resolve as resolve3, sep as sep3 } from "node:path";
|
|
19803
|
+
import * as ts9 from "@typescript/typescript6";
|
|
19227
19804
|
|
|
19228
19805
|
// src/sql-safety.ts
|
|
19229
|
-
import * as
|
|
19806
|
+
import * as ts8 from "@typescript/typescript6";
|
|
19230
19807
|
var SQL_SAFETY_DIAGNOSTIC_CODES = {
|
|
19231
19808
|
"sql-result-assertion": { errorCode: "SC6007", docsUrl: "https://supacloud.dev/errors/SC6007" },
|
|
19232
19809
|
"sql-raw-dynamic": { errorCode: "SC6008", docsUrl: "https://supacloud.dev/errors/SC6008" }
|
|
@@ -19234,10 +19811,10 @@ var SQL_SAFETY_DIAGNOSTIC_CODES = {
|
|
|
19234
19811
|
function scanDrizzleSql(sourceFile, checker, file, strict) {
|
|
19235
19812
|
const diagnostics = [];
|
|
19236
19813
|
const importedSql = (expression) => {
|
|
19237
|
-
const symbol = checker.getSymbolAtLocation(
|
|
19814
|
+
const symbol = checker.getSymbolAtLocation(ts8.isPropertyAccessExpression(expression) ? expression.name : expression);
|
|
19238
19815
|
if (!symbol)
|
|
19239
19816
|
return false;
|
|
19240
|
-
const target = symbol.flags &
|
|
19817
|
+
const target = symbol.flags & ts8.SymbolFlags.Alias ? checker.getAliasedSymbol(symbol) : symbol;
|
|
19241
19818
|
return target.name === "sql" && (target.declarations ?? []).some((declaration) => /(?:^|\/)node_modules\/drizzle-orm\//.test(declaration.getSourceFile().fileName.replaceAll("\\", "/")));
|
|
19242
19819
|
};
|
|
19243
19820
|
const report = (code, node, message) => {
|
|
@@ -19251,16 +19828,16 @@ function scanDrizzleSql(sourceFile, checker, file, strict) {
|
|
|
19251
19828
|
});
|
|
19252
19829
|
};
|
|
19253
19830
|
const visit = (node) => {
|
|
19254
|
-
if (
|
|
19831
|
+
if (ts8.isTaggedTemplateExpression(node) && importedSql(node.tag) && node.typeArguments?.some((type) => type.kind !== ts8.SyntaxKind.UnknownKeyword)) {
|
|
19255
19832
|
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.");
|
|
19256
19833
|
}
|
|
19257
|
-
if (
|
|
19834
|
+
if (ts8.isCallExpression(node) && ts8.isPropertyAccessExpression(node.expression) && node.expression.name.text === "raw" && importedSql(node.expression.expression)) {
|
|
19258
19835
|
const argument = node.arguments[0];
|
|
19259
|
-
if (!argument || !
|
|
19836
|
+
if (!argument || !ts8.isStringLiteral(argument) && !ts8.isNoSubstitutionTemplateLiteral(argument)) {
|
|
19260
19837
|
report("sql-raw-dynamic", node, "Dynamic sql.raw bypasses parameter binding. Interpolate values with sql templates; keep reviewed static DDL in migrations.");
|
|
19261
19838
|
}
|
|
19262
19839
|
}
|
|
19263
|
-
|
|
19840
|
+
ts8.forEachChild(node, visit);
|
|
19264
19841
|
};
|
|
19265
19842
|
visit(sourceFile);
|
|
19266
19843
|
return diagnostics;
|
|
@@ -19292,7 +19869,7 @@ function scanGeneratedArtifacts(artifacts, strict = true) {
|
|
|
19292
19869
|
for (const [file, content] of Object.entries(artifacts)) {
|
|
19293
19870
|
if (content === undefined)
|
|
19294
19871
|
continue;
|
|
19295
|
-
const sourceFile =
|
|
19872
|
+
const sourceFile = ts9.createSourceFile(file, content, ts9.ScriptTarget.Latest, true, ts9.ScriptKind.TS);
|
|
19296
19873
|
for (const node of descendantsOfKind2(sourceFile, isAnyKeyword)) {
|
|
19297
19874
|
diagnostics.push(makeDiagnostic("generated-any", `生成产物 ${file} 包含 any;严格生成模式要求使用 unknown、具体接口或泛型约束。`, sourceFile, node, strict));
|
|
19298
19875
|
}
|
|
@@ -19300,31 +19877,31 @@ function scanGeneratedArtifacts(artifacts, strict = true) {
|
|
|
19300
19877
|
return diagnostics;
|
|
19301
19878
|
}
|
|
19302
19879
|
function scanProductionSource(options) {
|
|
19303
|
-
const rootDir =
|
|
19304
|
-
const configPath =
|
|
19880
|
+
const rootDir = resolve3(options.rootDir);
|
|
19881
|
+
const configPath = ts9.findConfigFile(rootDir, ts9.sys.fileExists) ?? join3(rootDir, "tsconfig.json");
|
|
19305
19882
|
const projectConfig = existsSync2(configPath) ? readProjectConfig2(configPath) : {
|
|
19306
19883
|
options: {
|
|
19307
19884
|
strict: true,
|
|
19308
19885
|
skipLibCheck: true,
|
|
19309
|
-
target:
|
|
19310
|
-
module:
|
|
19311
|
-
moduleResolution:
|
|
19886
|
+
target: ts9.ScriptTarget.ES2022,
|
|
19887
|
+
module: ts9.ModuleKind.ESNext,
|
|
19888
|
+
moduleResolution: ts9.ModuleResolutionKind.Bundler
|
|
19312
19889
|
},
|
|
19313
19890
|
errors: []
|
|
19314
19891
|
};
|
|
19315
19892
|
const include = options.include ?? ["**/*.ts", "**/*.tsx", "**/*.mts", "**/*.cts"];
|
|
19316
|
-
const rootNames =
|
|
19893
|
+
const rootNames = ts9.sys.readDirectory(rootDir, [".ts", ".tsx", ".mts", ".cts"], ["node_modules", "dist"], include).filter((file) => isProductionSourcePath(rootDir, file, [...DEFAULT_EXCLUDES, ...options.exclude ?? []]));
|
|
19317
19894
|
const compilerOptions = { ...projectConfig.options, noEmit: true };
|
|
19318
|
-
const host =
|
|
19895
|
+
const host = ts9.createCompilerHost(compilerOptions);
|
|
19319
19896
|
host.getCurrentDirectory = () => dirname3(configPath);
|
|
19320
|
-
const program =
|
|
19897
|
+
const program = ts9.createProgram(rootNames, compilerOptions, host);
|
|
19321
19898
|
const outDir = options.outDir ? normalizeRelative(rootDir, options.outDir) : undefined;
|
|
19322
19899
|
const excludes = [...DEFAULT_EXCLUDES, ...options.exclude ?? []];
|
|
19323
19900
|
const sourceFiles = program.getSourceFiles().filter((sourceFile) => isProductionSource(rootDir, sourceFile, excludes, outDir));
|
|
19324
19901
|
const diagnostics = [...projectConfig.errors, ...program.getOptionsDiagnostics()].map((diagnostic) => ({
|
|
19325
19902
|
severity: "error",
|
|
19326
19903
|
code: "source-config",
|
|
19327
|
-
message:
|
|
19904
|
+
message: ts9.flattenDiagnosticMessageText(diagnostic.messageText, `
|
|
19328
19905
|
`),
|
|
19329
19906
|
file: diagnostic.file ? normalizeRelative(rootDir, diagnostic.file.fileName) : normalizeRelative(rootDir, configPath),
|
|
19330
19907
|
...diagnostic.file && diagnostic.start !== undefined ? { line: diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start).line + 1 } : {},
|
|
@@ -19341,7 +19918,7 @@ function scanProductionSource(options) {
|
|
|
19341
19918
|
diagnostics.push({
|
|
19342
19919
|
severity: "error",
|
|
19343
19920
|
code: "source-typescript",
|
|
19344
|
-
message:
|
|
19921
|
+
message: ts9.flattenDiagnosticMessageText(diagnostic.messageText, `
|
|
19345
19922
|
`),
|
|
19346
19923
|
...diagnostic.file ? { file: normalizeRelative(rootDir, diagnostic.file.fileName) } : {},
|
|
19347
19924
|
...diagnostic.file && diagnostic.start !== undefined ? { line: diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start).line + 1 } : {},
|
|
@@ -19351,9 +19928,9 @@ function scanProductionSource(options) {
|
|
|
19351
19928
|
for (const sourceFile of sourceFiles) {
|
|
19352
19929
|
scanSourceFile(sourceFile, checker, rootDir, diagnostics, options.strict ?? false);
|
|
19353
19930
|
diagnostics.push(...scanDrizzleSql(sourceFile, checker, normalizeRelative(rootDir, sourceFile.fileName), options.strict ?? false));
|
|
19354
|
-
const scanner =
|
|
19355
|
-
for (let kind = scanner.scan();kind !==
|
|
19356
|
-
if ((kind ===
|
|
19931
|
+
const scanner = ts9.createScanner(ts9.ScriptTarget.Latest, false, sourceFile.languageVariant, sourceFile.text);
|
|
19932
|
+
for (let kind = scanner.scan();kind !== ts9.SyntaxKind.EndOfFileToken; kind = scanner.scan()) {
|
|
19933
|
+
if ((kind === ts9.SyntaxKind.SingleLineCommentTrivia || kind === ts9.SyntaxKind.MultiLineCommentTrivia) && /@ts-(?:ignore|nocheck|expect-error)\b/.test(scanner.getTokenText())) {
|
|
19357
19934
|
diagnostics.push({
|
|
19358
19935
|
severity: "error",
|
|
19359
19936
|
code: "source-type-suppression",
|
|
@@ -19373,22 +19950,22 @@ function scanSourceFile(sourceFile, checker, rootDir, diagnostics, strict) {
|
|
|
19373
19950
|
diagnostics.push(makeDiagnostic("source-any", "生产源码使用了显式 any;请改用 unknown、具体接口或泛型约束。", sourceFile, node, strict, rootDir));
|
|
19374
19951
|
}
|
|
19375
19952
|
for (const node of descendants(sourceFile)) {
|
|
19376
|
-
if (
|
|
19377
|
-
if (
|
|
19953
|
+
if (ts9.isAsExpression(node)) {
|
|
19954
|
+
if (ts9.isAsExpression(node.parent) || ts9.isTypeAssertionExpression(node.parent))
|
|
19378
19955
|
continue;
|
|
19379
19956
|
const assertedType = node.type.getText(sourceFile);
|
|
19380
19957
|
if (assertedType === "const")
|
|
19381
19958
|
continue;
|
|
19382
19959
|
diagnostics.push(makeDiagnostic("source-type-assertion", `生产源码包含类型断言 ${node.getText(sourceFile)};请优先使用类型守卫、satisfies 或显式边界解析。`, sourceFile, node, strict, rootDir));
|
|
19383
|
-
} else if (
|
|
19384
|
-
if (
|
|
19960
|
+
} else if (ts9.isTypeAssertionExpression(node)) {
|
|
19961
|
+
if (ts9.isAsExpression(node.parent) || ts9.isTypeAssertionExpression(node.parent))
|
|
19385
19962
|
continue;
|
|
19386
19963
|
diagnostics.push(makeDiagnostic("source-type-assertion", `生产源码包含类型断言 ${node.getText(sourceFile)};请优先使用类型守卫、satisfies 或显式边界解析。`, sourceFile, node, strict, rootDir));
|
|
19387
|
-
} else if (
|
|
19964
|
+
} else if (ts9.isNonNullExpression(node)) {
|
|
19388
19965
|
diagnostics.push(makeDiagnostic("source-non-null-assertion", `生产源码包含非空断言 ${node.getText(sourceFile)};请显式处理 null/undefined。`, sourceFile, node, strict, rootDir));
|
|
19389
19966
|
}
|
|
19390
19967
|
}
|
|
19391
|
-
for (const declaration of descendantsOfKind2(sourceFile,
|
|
19968
|
+
for (const declaration of descendantsOfKind2(sourceFile, ts9.isVariableDeclaration)) {
|
|
19392
19969
|
const initializer = declaration.initializer;
|
|
19393
19970
|
if (!initializer || declaration.type)
|
|
19394
19971
|
continue;
|
|
@@ -19404,11 +19981,11 @@ function scanSourceFile(sourceFile, checker, rootDir, diagnostics, strict) {
|
|
|
19404
19981
|
if (isLetDeclaration(declaration) && isLiteralSyntax(initializer) && !isLiteralType(declarationType)) {
|
|
19405
19982
|
diagnostics.push(makeDiagnostic("source-implicit-widening", `变量 ${declaration.name.getText(sourceFile)} 的字面量类型从 ${checker.typeToString(initializerType, initializer)} 隐式宽化为 ${checker.typeToString(declarationType, declaration)};请补充类型或使用 const。`, sourceFile, declaration, strict, rootDir));
|
|
19406
19983
|
}
|
|
19407
|
-
if (
|
|
19984
|
+
if (ts9.isObjectLiteralExpression(initializer) && isConstDeclaration(declaration) && initializer.getText(sourceFile).length > 0 && initializer.properties.some((property) => ts9.isPropertyAssignment(property) && property.initializer !== undefined && !ts9.isAsExpression(property.initializer) && isLiteralExpression(property.initializer))) {
|
|
19408
19985
|
diagnostics.push(makeDiagnostic("source-implicit-widening", `常量对象 ${declaration.name.getText(sourceFile)} 的字面量属性会隐式宽化;请补充对象类型或使用 as const。`, sourceFile, declaration, strict, rootDir));
|
|
19409
19986
|
}
|
|
19410
19987
|
}
|
|
19411
|
-
for (const parameter of descendantsOfKind2(sourceFile,
|
|
19988
|
+
for (const parameter of descendantsOfKind2(sourceFile, ts9.isParameter)) {
|
|
19412
19989
|
if (parameter.type)
|
|
19413
19990
|
continue;
|
|
19414
19991
|
for (const name of bindingNames(parameter.name)) {
|
|
@@ -19419,10 +19996,10 @@ function scanSourceFile(sourceFile, checker, rootDir, diagnostics, strict) {
|
|
|
19419
19996
|
}
|
|
19420
19997
|
}
|
|
19421
19998
|
function readProjectConfig2(configPath) {
|
|
19422
|
-
const config =
|
|
19999
|
+
const config = ts9.readConfigFile(configPath, (file) => readFileSync2(file, "utf8"));
|
|
19423
20000
|
if (config.error)
|
|
19424
20001
|
return { options: {}, errors: [config.error] };
|
|
19425
|
-
const parsed =
|
|
20002
|
+
const parsed = ts9.parseJsonConfigFileContent(config.config, ts9.sys, dirname3(configPath));
|
|
19426
20003
|
return { options: parsed.options, errors: parsed.errors };
|
|
19427
20004
|
}
|
|
19428
20005
|
function isProductionSource(rootDir, sourceFile, excludes, outDir) {
|
|
@@ -19442,42 +20019,42 @@ function globMatches(value, pattern) {
|
|
|
19442
20019
|
return new RegExp(`^${escaped}$`).test(value);
|
|
19443
20020
|
}
|
|
19444
20021
|
function bindingNames(name) {
|
|
19445
|
-
if (
|
|
20022
|
+
if (ts9.isIdentifier(name))
|
|
19446
20023
|
return [name];
|
|
19447
|
-
return name.elements.flatMap((element) =>
|
|
20024
|
+
return name.elements.flatMap((element) => ts9.isBindingElement(element) ? bindingNames(element.name) : []);
|
|
19448
20025
|
}
|
|
19449
20026
|
function isLiteralExpression(node) {
|
|
19450
20027
|
if (!node)
|
|
19451
20028
|
return false;
|
|
19452
20029
|
return [
|
|
19453
|
-
|
|
19454
|
-
|
|
19455
|
-
|
|
19456
|
-
|
|
20030
|
+
ts9.SyntaxKind.StringLiteral,
|
|
20031
|
+
ts9.SyntaxKind.NumericLiteral,
|
|
20032
|
+
ts9.SyntaxKind.TrueKeyword,
|
|
20033
|
+
ts9.SyntaxKind.FalseKeyword
|
|
19457
20034
|
].includes(node.kind);
|
|
19458
20035
|
}
|
|
19459
20036
|
function isLiteralSyntax(node) {
|
|
19460
|
-
return
|
|
20037
|
+
return ts9.isStringLiteral(node) || ts9.isNumericLiteral(node) || node.kind === ts9.SyntaxKind.TrueKeyword || node.kind === ts9.SyntaxKind.FalseKeyword;
|
|
19461
20038
|
}
|
|
19462
20039
|
function isLiteralType(type) {
|
|
19463
|
-
return (type.flags & (
|
|
20040
|
+
return (type.flags & (ts9.TypeFlags.StringLiteral | ts9.TypeFlags.NumberLiteral | ts9.TypeFlags.BooleanLiteral | ts9.TypeFlags.BigIntLiteral)) !== 0;
|
|
19464
20041
|
}
|
|
19465
20042
|
function isAnyType(type) {
|
|
19466
|
-
return (type.flags &
|
|
20043
|
+
return (type.flags & ts9.TypeFlags.Any) !== 0;
|
|
19467
20044
|
}
|
|
19468
20045
|
function isLetDeclaration(declaration) {
|
|
19469
|
-
return
|
|
20046
|
+
return ts9.isVariableDeclarationList(declaration.parent) && (declaration.parent.flags & ts9.NodeFlags.Let) !== 0;
|
|
19470
20047
|
}
|
|
19471
20048
|
function isConstDeclaration(declaration) {
|
|
19472
|
-
return
|
|
20049
|
+
return ts9.isVariableDeclarationList(declaration.parent) && (declaration.parent.flags & ts9.NodeFlags.Const) !== 0;
|
|
19473
20050
|
}
|
|
19474
20051
|
function descendants(root) {
|
|
19475
20052
|
const result = [];
|
|
19476
20053
|
const visit = (node) => {
|
|
19477
20054
|
result.push(node);
|
|
19478
|
-
|
|
20055
|
+
ts9.forEachChild(node, visit);
|
|
19479
20056
|
};
|
|
19480
|
-
|
|
20057
|
+
ts9.forEachChild(root, visit);
|
|
19481
20058
|
return result;
|
|
19482
20059
|
}
|
|
19483
20060
|
function descendantsOfKind2(root, predicate) {
|
|
@@ -19485,9 +20062,9 @@ function descendantsOfKind2(root, predicate) {
|
|
|
19485
20062
|
const visit = (node) => {
|
|
19486
20063
|
if (predicate(node))
|
|
19487
20064
|
result.push(node);
|
|
19488
|
-
|
|
20065
|
+
ts9.forEachChild(node, visit);
|
|
19489
20066
|
};
|
|
19490
|
-
|
|
20067
|
+
ts9.forEachChild(root, visit);
|
|
19491
20068
|
return result;
|
|
19492
20069
|
}
|
|
19493
20070
|
function makeDiagnostic(code, message, fileOrSourceFile, node, strict, rootDir) {
|
|
@@ -19508,7 +20085,7 @@ function normalizeRelative(rootDir, filePath) {
|
|
|
19508
20085
|
return relative3(rootDir, filePath).split(sep3).join("/").replace(/^\.\//, "");
|
|
19509
20086
|
}
|
|
19510
20087
|
function isAnyKeyword(node) {
|
|
19511
|
-
return node.kind ===
|
|
20088
|
+
return node.kind === ts9.SyntaxKind.AnyKeyword;
|
|
19512
20089
|
}
|
|
19513
20090
|
|
|
19514
20091
|
// src/compile.ts
|
|
@@ -19927,12 +20504,12 @@ init_delivery_context();
|
|
|
19927
20504
|
|
|
19928
20505
|
// src/watch.ts
|
|
19929
20506
|
import { existsSync as existsSync4, watch } from "node:fs";
|
|
19930
|
-
import { dirname as dirname7, relative as relative8, resolve as
|
|
20507
|
+
import { dirname as dirname7, relative as relative8, resolve as resolve10, sep as sep8 } from "node:path";
|
|
19931
20508
|
|
|
19932
20509
|
// src/incremental.ts
|
|
19933
20510
|
import { createHash as createHash7 } from "node:crypto";
|
|
19934
20511
|
import { access as access3, readdir as readdir3, readFile as readFile4 } from "node:fs/promises";
|
|
19935
|
-
import { isAbsolute as isAbsolute3, relative as relative7, resolve as
|
|
20512
|
+
import { isAbsolute as isAbsolute3, relative as relative7, resolve as resolve9, sep as sep7 } from "node:path";
|
|
19936
20513
|
init_graphql_inputs();
|
|
19937
20514
|
function createDependencyGraphCache() {
|
|
19938
20515
|
return {
|
|
@@ -20013,11 +20590,11 @@ async function readSourceBytes(path) {
|
|
|
20013
20590
|
return readFile4(path);
|
|
20014
20591
|
}
|
|
20015
20592
|
async function updateSnapshot(previous, options, changedPaths) {
|
|
20016
|
-
const rootDir =
|
|
20017
|
-
const outDir =
|
|
20593
|
+
const rootDir = resolve9(options.rootDir);
|
|
20594
|
+
const outDir = resolve9(options.outDir);
|
|
20018
20595
|
const files = { ...previous.files };
|
|
20019
20596
|
await Promise.all(changedPaths.map(async (changedPath) => {
|
|
20020
|
-
const absolutePath = isAbsolute3(changedPath) ?
|
|
20597
|
+
const absolutePath = isAbsolute3(changedPath) ? resolve9(changedPath) : resolve9(rootDir, changedPath);
|
|
20021
20598
|
const relativeChangedPath = relative7(rootDir, absolutePath);
|
|
20022
20599
|
if (relativeChangedPath === ".." || relativeChangedPath.startsWith(`..${sep7}`))
|
|
20023
20600
|
return;
|
|
@@ -20035,8 +20612,8 @@ async function updateSnapshot(previous, options, changedPaths) {
|
|
|
20035
20612
|
return { files, optionsKey: optionsKeyOf(options) };
|
|
20036
20613
|
}
|
|
20037
20614
|
async function createSnapshot(options) {
|
|
20038
|
-
const rootDir =
|
|
20039
|
-
const outDir =
|
|
20615
|
+
const rootDir = resolve9(options.rootDir);
|
|
20616
|
+
const outDir = resolve9(options.outDir);
|
|
20040
20617
|
const paths = await listSourceFiles(rootDir, outDir);
|
|
20041
20618
|
const files = {};
|
|
20042
20619
|
const entries = await Promise.all(paths.map(async (path) => {
|
|
@@ -20059,8 +20636,8 @@ async function createSnapshot(options) {
|
|
|
20059
20636
|
}
|
|
20060
20637
|
function optionsKeyOf(options) {
|
|
20061
20638
|
return JSON.stringify({
|
|
20062
|
-
rootDir:
|
|
20063
|
-
outDir:
|
|
20639
|
+
rootDir: resolve9(options.rootDir),
|
|
20640
|
+
outDir: resolve9(options.outDir),
|
|
20064
20641
|
include: options.include,
|
|
20065
20642
|
strict: options.strict,
|
|
20066
20643
|
writeOnError: options.writeOnError,
|
|
@@ -20084,7 +20661,7 @@ async function listSourceFiles(rootDir, outDir) {
|
|
|
20084
20661
|
const result = [];
|
|
20085
20662
|
const visit = async (directory) => {
|
|
20086
20663
|
for (const entry of await readdir3(directory, { withFileTypes: true })) {
|
|
20087
|
-
const path =
|
|
20664
|
+
const path = resolve9(directory, entry.name);
|
|
20088
20665
|
if (entry.isDirectory()) {
|
|
20089
20666
|
if (entry.name === "node_modules" || entry.name === ".git" || path === outDir)
|
|
20090
20667
|
continue;
|
|
@@ -20219,8 +20796,8 @@ function isCompilerConfigurationPath(rootDir, changedPath) {
|
|
|
20219
20796
|
].includes(relativePath) || /^tsconfig\.[^/]+\.json$/.test(relativePath);
|
|
20220
20797
|
}
|
|
20221
20798
|
function watchProject(options) {
|
|
20222
|
-
const rootDir =
|
|
20223
|
-
const outDir =
|
|
20799
|
+
const rootDir = resolve10(options.rootDir);
|
|
20800
|
+
const outDir = resolve10(options.outDir);
|
|
20224
20801
|
const debounceMs = options.debounceMs ?? DEFAULT_DEBOUNCE_MS;
|
|
20225
20802
|
let timer;
|
|
20226
20803
|
let closed = false;
|
|
@@ -20230,7 +20807,7 @@ function watchProject(options) {
|
|
|
20230
20807
|
const pendingPaths = new Set;
|
|
20231
20808
|
let watcher;
|
|
20232
20809
|
let schemaWatcher;
|
|
20233
|
-
const schemaPath = options.graphql ?
|
|
20810
|
+
const schemaPath = options.graphql ? resolve10(rootDir, options.graphql.schema) : undefined;
|
|
20234
20811
|
const incremental = createIncrementalCompiler();
|
|
20235
20812
|
let initialEvent;
|
|
20236
20813
|
let resolveReady = () => {
|
|
@@ -20340,7 +20917,7 @@ function watchProject(options) {
|
|
|
20340
20917
|
watcher = watch(rootDir, { recursive: true }, (_eventType, filename) => {
|
|
20341
20918
|
if (!filename)
|
|
20342
20919
|
return schedule();
|
|
20343
|
-
const changedPath =
|
|
20920
|
+
const changedPath = resolve10(rootDir, filename.toString());
|
|
20344
20921
|
const relativePath = relative8(outDir, changedPath);
|
|
20345
20922
|
if (!relativePath.startsWith("..") && relativePath !== "")
|
|
20346
20923
|
return;
|
|
@@ -20353,7 +20930,7 @@ function watchProject(options) {
|
|
|
20353
20930
|
while (!existsSync4(directory) && dirname7(directory) !== directory)
|
|
20354
20931
|
directory = dirname7(directory);
|
|
20355
20932
|
schemaWatcher = watch(directory, { recursive: true }, (_eventType, filename) => {
|
|
20356
|
-
if (!filename ||
|
|
20933
|
+
if (!filename || resolve10(directory, filename.toString()) === schemaPath)
|
|
20357
20934
|
schedule(schemaPath);
|
|
20358
20935
|
});
|
|
20359
20936
|
}
|
|
@@ -20378,7 +20955,7 @@ function watchProject(options) {
|
|
|
20378
20955
|
// src/config.ts
|
|
20379
20956
|
init_graphql_options();
|
|
20380
20957
|
import { existsSync as existsSync5 } from "node:fs";
|
|
20381
|
-
import { join as join8, resolve as
|
|
20958
|
+
import { join as join8, resolve as resolve11 } from "node:path";
|
|
20382
20959
|
import { pathToFileURL } from "node:url";
|
|
20383
20960
|
|
|
20384
20961
|
// src/delivery-schema.ts
|
|
@@ -20476,8 +21053,8 @@ function validateGovernanceConfig(config) {
|
|
|
20476
21053
|
function resolveSupacloudConfig(config = {}, cwd = process.cwd()) {
|
|
20477
21054
|
const resolved = defineSupacloudConfig(config);
|
|
20478
21055
|
return {
|
|
20479
|
-
rootDir:
|
|
20480
|
-
outDir:
|
|
21056
|
+
rootDir: resolve11(cwd, resolved.root ?? DEFAULT_SUPACLOUD_CONFIG.root),
|
|
21057
|
+
outDir: resolve11(cwd, resolved.outDir ?? DEFAULT_SUPACLOUD_CONFIG.outDir),
|
|
20481
21058
|
include: resolved.include ?? [...DEFAULT_SUPACLOUD_CONFIG.include],
|
|
20482
21059
|
strict: resolved.strict ?? DEFAULT_SUPACLOUD_CONFIG.strict,
|
|
20483
21060
|
requireRouteContracts: resolved.requireRouteContracts ?? DEFAULT_SUPACLOUD_CONFIG.requireRouteContracts,
|
|
@@ -20495,7 +21072,7 @@ function resolveSupacloudConfig(config = {}, cwd = process.cwd()) {
|
|
|
20495
21072
|
treeShakeUnusedProviders: resolved.treeShakeUnusedProviders ?? DEFAULT_SUPACLOUD_CONFIG.treeShakeUnusedProviders,
|
|
20496
21073
|
graphql: resolved.graphql ? {
|
|
20497
21074
|
...resolved.graphql,
|
|
20498
|
-
schema:
|
|
21075
|
+
schema: resolve11(cwd, resolved.graphql.schema)
|
|
20499
21076
|
} : undefined
|
|
20500
21077
|
};
|
|
20501
21078
|
}
|
|
@@ -20516,213 +21093,67 @@ function compileOptionsFromConfig(config, cwd = process.cwd()) {
|
|
|
20516
21093
|
return resolveSupacloudConfig(config, cwd);
|
|
20517
21094
|
}
|
|
20518
21095
|
|
|
20519
|
-
// src/
|
|
20520
|
-
|
|
20521
|
-
|
|
20522
|
-
|
|
20523
|
-
|
|
20524
|
-
|
|
20525
|
-
|
|
20526
|
-
|
|
20527
|
-
|
|
20528
|
-
|
|
20529
|
-
|
|
20530
|
-
|
|
20531
|
-
|
|
20532
|
-
|
|
20533
|
-
|
|
20534
|
-
|
|
20535
|
-
|
|
20536
|
-
|
|
20537
|
-
|
|
20538
|
-
|
|
20539
|
-
|
|
20540
|
-
|
|
20541
|
-
|
|
20542
|
-
|
|
20543
|
-
const object = decoratorObject(findClass(source, fix.command), "Command");
|
|
20544
|
-
const current = property(object, fix.property);
|
|
20545
|
-
if (!current || current.initializer.getText(source) !== fix.expectedExpression) {
|
|
20546
|
-
throw new Error("Command mode changed since diagnosis; analyze the project again");
|
|
20547
|
-
}
|
|
20548
|
-
content = replaceProperty(source, object, fix.property, ts9.factory.createStringLiteral(fix.value));
|
|
20549
|
-
break;
|
|
20550
|
-
}
|
|
20551
|
-
case "add_module_import": {
|
|
20552
|
-
if (!fix.importPath || !fix.symbol)
|
|
20553
|
-
throw new Error("Module fix requires importPath and symbol");
|
|
20554
|
-
identifier2(fix.symbol);
|
|
20555
|
-
const withImport = importSymbol(source, fix.importPath, fix.symbol);
|
|
20556
|
-
source = parse5(file, withImport);
|
|
20557
|
-
const object = unique(moduleObjects(source).filter((candidate) => !fix.targetModule || stringProperty(candidate, "name") === fix.targetModule), "target module");
|
|
20558
|
-
const imports = property(object, "imports");
|
|
20559
|
-
if (imports && !ts9.isArrayLiteralExpression(imports.initializer)) {
|
|
20560
|
-
throw new Error("Module imports must be a static array");
|
|
20561
|
-
}
|
|
20562
|
-
const values = imports && ts9.isArrayLiteralExpression(imports.initializer) ? imports.initializer.elements : [];
|
|
20563
|
-
if (values.some(ts9.isSpreadElement))
|
|
20564
|
-
throw new Error("Module imports cannot contain spread elements");
|
|
20565
|
-
content = values.some((value) => ts9.isIdentifier(value) && value.text === fix.symbol) ? withImport : replaceProperty(source, object, "imports", ts9.factory.createArrayLiteralExpression([
|
|
20566
|
-
...values,
|
|
20567
|
-
ts9.factory.createIdentifier(fix.symbol)
|
|
20568
|
-
]));
|
|
20569
|
-
break;
|
|
20570
|
-
}
|
|
20571
|
-
case "add_command_permission": {
|
|
20572
|
-
const permission = options.permission ?? fix.permission;
|
|
20573
|
-
if (!permission?.trim()) {
|
|
20574
|
-
throw new Error("Permission fix requires an explicit permission; privileges are never inferred");
|
|
20575
|
-
}
|
|
20576
|
-
const command = findClass(source, fix.command);
|
|
20577
|
-
const object = decoratorObject(command, "Command");
|
|
20578
|
-
const current = property(object, "permission");
|
|
20579
|
-
if (current && (!ts9.isStringLiteral(current.initializer) || current.initializer.text !== permission)) {
|
|
20580
|
-
throw new Error("Command permission already exists with a different value");
|
|
20581
|
-
}
|
|
20582
|
-
content = current ? original : replaceProperty(source, object, "permission", ts9.factory.createStringLiteral(permission));
|
|
20583
|
-
break;
|
|
20584
|
-
}
|
|
20585
|
-
case "add_route_parameter_binding": {
|
|
20586
|
-
const controller = findClass(source, fix.controller);
|
|
20587
|
-
const method = unique(controller.members.filter((member) => ts9.isMethodDeclaration(member) && nameOf(member.name) === fix.route), "route handler");
|
|
20588
|
-
const parameter = unique(method.parameters.filter((candidate) => ts9.isIdentifier(candidate.name) && candidate.name.text === fix.parameter), "same-named route parameter");
|
|
20589
|
-
const binding = fix.binding === "param" ? "Param" : fix.binding === "query" ? "Query" : undefined;
|
|
20590
|
-
if (!binding)
|
|
20591
|
-
throw new Error("Invalid route binding");
|
|
20592
|
-
const decorators = ts9.getDecorators(parameter) ?? [];
|
|
20593
|
-
if (decorators.length > 0)
|
|
20594
|
-
throw new Error("Parameter already has a decorator");
|
|
20595
|
-
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");
|
|
20596
|
-
if (!ts9.isImportDeclaration(framework) || !ts9.isStringLiteral(framework.moduleSpecifier)) {
|
|
20597
|
-
throw new Error("Framework import must be static");
|
|
20598
|
-
}
|
|
20599
|
-
const edited = original.slice(0, parameter.getStart(source)) + `@${binding}(${JSON.stringify(fix.parameter)}) ` + original.slice(parameter.getStart(source));
|
|
20600
|
-
content = importSymbol(parse5(file, edited), framework.moduleSpecifier.text, binding);
|
|
20601
|
-
break;
|
|
20602
|
-
}
|
|
20603
|
-
default:
|
|
20604
|
-
throw new Error(`Diagnostic fix '${fix.type}' requires a manual semantic decision; no files changed`);
|
|
20605
|
-
}
|
|
20606
|
-
parse5(file, content);
|
|
20607
|
-
const result = { file, changed: content !== original, content };
|
|
20608
|
-
if (options.dryRun === false && result.changed) {
|
|
20609
|
-
const temporary = `${file}.supacloud-fix-${randomUUID()}`;
|
|
20610
|
-
try {
|
|
20611
|
-
await writeFile3(temporary, content, { encoding: "utf8", flag: "wx", mode: stat.mode });
|
|
20612
|
-
if (await readFile5(file, "utf8") !== original)
|
|
20613
|
-
throw new Error("Target changed while preparing the fix");
|
|
20614
|
-
await rename3(temporary, file);
|
|
20615
|
-
} finally {
|
|
20616
|
-
await unlink2(temporary).catch(() => {});
|
|
20617
|
-
}
|
|
20618
|
-
}
|
|
20619
|
-
return result;
|
|
20620
|
-
}
|
|
20621
|
-
function parse5(file, text) {
|
|
20622
|
-
const result = ts9.transpileModule(text, {
|
|
20623
|
-
fileName: file,
|
|
20624
|
-
reportDiagnostics: true,
|
|
20625
|
-
compilerOptions: { target: ts9.ScriptTarget.ESNext, experimentalDecorators: true }
|
|
20626
|
-
});
|
|
20627
|
-
if (result.diagnostics?.some((item) => item.category === ts9.DiagnosticCategory.Error)) {
|
|
20628
|
-
throw new Error("Cannot fix syntactically invalid TypeScript");
|
|
20629
|
-
}
|
|
20630
|
-
return ts9.createSourceFile(file, text, ts9.ScriptTarget.Latest, true, ts9.ScriptKind.TS);
|
|
20631
|
-
}
|
|
20632
|
-
function unique(items, description) {
|
|
20633
|
-
if (items.length !== 1)
|
|
20634
|
-
throw new Error(`Expected exactly one ${description}; found ${items.length}`);
|
|
20635
|
-
return items[0];
|
|
20636
|
-
}
|
|
20637
|
-
function identifier2(value) {
|
|
20638
|
-
if (!/^[A-Za-z_$][\w$]*$/.test(value))
|
|
20639
|
-
throw new Error(`Invalid identifier '${value}'`);
|
|
20640
|
-
}
|
|
20641
|
-
function nameOf(name) {
|
|
20642
|
-
if (!name)
|
|
20643
|
-
return "";
|
|
20644
|
-
return ts9.isIdentifier(name) || ts9.isStringLiteral(name) || ts9.isNumericLiteral(name) ? name.text : "";
|
|
20645
|
-
}
|
|
20646
|
-
function property(object, key) {
|
|
20647
|
-
if (object.properties.some((item) => !ts9.isPropertyAssignment(item) || ts9.isComputedPropertyName(item.name))) {
|
|
20648
|
-
throw new Error("Fix requires explicit static object properties");
|
|
20649
|
-
}
|
|
20650
|
-
const values = object.properties.filter((item) => ts9.isPropertyAssignment(item) && nameOf(item.name) === key);
|
|
20651
|
-
if (values.length > 1)
|
|
20652
|
-
throw new Error(`Duplicate '${key}' property`);
|
|
20653
|
-
return values[0];
|
|
20654
|
-
}
|
|
20655
|
-
function stringProperty(object, key) {
|
|
20656
|
-
const value = property(object, key)?.initializer;
|
|
20657
|
-
return value && ts9.isStringLiteral(value) ? value.text : undefined;
|
|
20658
|
-
}
|
|
20659
|
-
function replaceProperty(source, object, key, value) {
|
|
20660
|
-
const previous = property(object, key);
|
|
20661
|
-
const replacement = ts9.factory.createPropertyAssignment(key, value);
|
|
20662
|
-
const properties = object.properties.map((item) => item === previous ? replacement : item);
|
|
20663
|
-
if (!previous)
|
|
20664
|
-
properties.push(replacement);
|
|
20665
|
-
const updated = ts9.factory.updateObjectLiteralExpression(object, properties);
|
|
20666
|
-
return source.text.slice(0, object.getStart(source)) + ts9.createPrinter().printNode(ts9.EmitHint.Expression, updated, source) + source.text.slice(object.end);
|
|
20667
|
-
}
|
|
20668
|
-
function findClass(source, name) {
|
|
20669
|
-
return unique(source.statements.filter((statement) => ts9.isClassDeclaration(statement) && statement.name?.text === name), `class '${name}'`);
|
|
20670
|
-
}
|
|
20671
|
-
function decoratorObject(node, name) {
|
|
20672
|
-
const decorator = unique((ts9.getDecorators(node) ?? []).filter((item) => ts9.isCallExpression(item.expression) && item.expression.expression.getText() === name), `@${name} decorator`);
|
|
20673
|
-
const argument = ts9.isCallExpression(decorator.expression) ? decorator.expression.arguments[0] : undefined;
|
|
20674
|
-
if (!argument || !ts9.isObjectLiteralExpression(argument))
|
|
20675
|
-
throw new Error(`@${name} requires a static object`);
|
|
20676
|
-
return argument;
|
|
20677
|
-
}
|
|
20678
|
-
function moduleObjects(source) {
|
|
20679
|
-
const result = [];
|
|
20680
|
-
for (const statement of source.statements) {
|
|
20681
|
-
if (ts9.isClassDeclaration(statement) && (ts9.getDecorators(statement) ?? []).some((item) => ts9.isCallExpression(item.expression) && item.expression.expression.getText() === "Module")) {
|
|
20682
|
-
result.push(decoratorObject(statement, "Module"));
|
|
20683
|
-
}
|
|
20684
|
-
if (ts9.isVariableStatement(statement)) {
|
|
20685
|
-
for (const declaration of statement.declarationList.declarations) {
|
|
20686
|
-
const call = declaration.initializer;
|
|
20687
|
-
if (call && ts9.isCallExpression(call) && ["defineModule", "defineFeatureSlice"].includes(call.expression.getText()) && call.arguments[0] && ts9.isObjectLiteralExpression(call.arguments[0])) {
|
|
20688
|
-
result.push(call.arguments[0]);
|
|
20689
|
-
}
|
|
21096
|
+
// src/cli.ts
|
|
21097
|
+
init_fixes();
|
|
21098
|
+
|
|
21099
|
+
// src/diagnostics.ts
|
|
21100
|
+
function createDiagnosticReport(diagnostics) {
|
|
21101
|
+
return { format: 1, entries: diagnostics.map((diagnostic) => {
|
|
21102
|
+
const repair = createDiagnosticRepairPlan([diagnostic])[0];
|
|
21103
|
+
return {
|
|
21104
|
+
diagnostic: { ...diagnostic },
|
|
21105
|
+
...repair ? { repair } : {},
|
|
21106
|
+
...diagnostic.suggestion ? { guidance: diagnostic.suggestion } : {}
|
|
21107
|
+
};
|
|
21108
|
+
}) };
|
|
21109
|
+
}
|
|
21110
|
+
function toEditorDiagnostics(diagnostics) {
|
|
21111
|
+
return createDiagnosticReport(diagnostics).entries.map((entry) => ({
|
|
21112
|
+
range: {
|
|
21113
|
+
start: {
|
|
21114
|
+
line: Math.max(0, (entry.diagnostic.line ?? 1) - 1),
|
|
21115
|
+
character: Math.max(0, entry.diagnostic.column ?? 0)
|
|
21116
|
+
},
|
|
21117
|
+
end: {
|
|
21118
|
+
line: Math.max(0, (entry.diagnostic.endLine ?? entry.diagnostic.line ?? 1) - 1),
|
|
21119
|
+
character: Math.max(0, entry.diagnostic.endColumn ?? entry.diagnostic.column ?? 0)
|
|
20690
21120
|
}
|
|
20691
|
-
}
|
|
20692
|
-
|
|
20693
|
-
|
|
21121
|
+
},
|
|
21122
|
+
severity: entry.diagnostic.severity === "error" ? 1 : 2,
|
|
21123
|
+
source: "supacloud",
|
|
21124
|
+
code: entry.diagnostic.errorCode ?? entry.diagnostic.code,
|
|
21125
|
+
...entry.diagnostic.docsUrl ? { codeDescription: { href: entry.diagnostic.docsUrl } } : {},
|
|
21126
|
+
message: entry.diagnostic.message + (entry.guidance ? `
|
|
21127
|
+
${entry.guidance}` : ""),
|
|
21128
|
+
data: { format: 1, ...entry }
|
|
21129
|
+
}));
|
|
20694
21130
|
}
|
|
20695
|
-
function
|
|
20696
|
-
|
|
20697
|
-
const
|
|
20698
|
-
const
|
|
20699
|
-
|
|
20700
|
-
|
|
20701
|
-
|
|
20702
|
-
|
|
20703
|
-
|
|
20704
|
-
|
|
20705
|
-
|
|
20706
|
-
|
|
20707
|
-
|
|
20708
|
-
|
|
20709
|
-
|
|
20710
|
-
|
|
20711
|
-
|
|
20712
|
-
]);
|
|
20713
|
-
return source.text.slice(0, bindings.getStart(source)) + ts9.createPrinter().printNode(ts9.EmitHint.Unspecified, updated, source) + source.text.slice(bindings.end);
|
|
21131
|
+
async function createEditorCodeActions(diagnostics, options = {}) {
|
|
21132
|
+
await Promise.resolve().then(() => init_fixes());
|
|
21133
|
+
const actions = [];
|
|
21134
|
+
for (const entry of createDiagnosticReport(diagnostics).entries) {
|
|
21135
|
+
if (entry.repair?.readiness !== "preview")
|
|
21136
|
+
continue;
|
|
21137
|
+
try {
|
|
21138
|
+
const preview = await applyDiagnosticFix(entry.repair.fix, { ...options, dryRun: true });
|
|
21139
|
+
if (!preview.changed)
|
|
21140
|
+
continue;
|
|
21141
|
+
const title = `Apply ${entry.repair.type}`;
|
|
21142
|
+
actions.push({ title, kind: "quickfix", command: {
|
|
21143
|
+
title,
|
|
21144
|
+
command: "supacloud.applyDiagnosticFix",
|
|
21145
|
+
arguments: [{ fix: entry.repair.fix, expectedSourceHash: preview.sourceHash }]
|
|
21146
|
+
}, data: { file: preview.file, preview: preview.content } });
|
|
21147
|
+
} catch {}
|
|
20714
21148
|
}
|
|
20715
|
-
|
|
20716
|
-
throw new Error(`Import from '${path}' is not a named value import`);
|
|
20717
|
-
return `import { ${symbol} } from ${JSON.stringify(path)};
|
|
20718
|
-
${source.text}`;
|
|
21149
|
+
return actions;
|
|
20719
21150
|
}
|
|
20720
21151
|
|
|
20721
21152
|
// src/cli.ts
|
|
20722
21153
|
init_graphql_options();
|
|
20723
21154
|
|
|
20724
21155
|
// src/delivery-plan.ts
|
|
20725
|
-
import { createHash as
|
|
21156
|
+
import { createHash as createHash9 } from "node:crypto";
|
|
20726
21157
|
init_validate();
|
|
20727
21158
|
var compare2 = (a, b) => a < b ? -1 : a > b ? 1 : 0;
|
|
20728
21159
|
var sorted = (values) => [...new Set(values)].sort(compare2);
|
|
@@ -20920,7 +21351,7 @@ function createDeliveryPlan(graph, input) {
|
|
|
20920
21351
|
deploymentReady: false,
|
|
20921
21352
|
targets: [...targets.values()].sort((a, b) => compare2(a.name, b.name))
|
|
20922
21353
|
};
|
|
20923
|
-
const topologyDigest =
|
|
21354
|
+
const topologyDigest = createHash9("sha256").update(JSON.stringify(topology)).digest("hex");
|
|
20924
21355
|
return parseDeliveryPlanResult({
|
|
20925
21356
|
ok: true,
|
|
20926
21357
|
plan: { ...topology, topologyDigest },
|
|
@@ -20957,8 +21388,8 @@ function formatDeliveryPlan(result) {
|
|
|
20957
21388
|
// src/delivery-build.ts
|
|
20958
21389
|
import { mkdir as mkdir3, mkdtemp, readFile as readFile7, realpath as realpath4, rename as rename4, rm as rm2 } from "node:fs/promises";
|
|
20959
21390
|
import { readFileSync as readFileSync3 } from "node:fs";
|
|
20960
|
-
import { dirname as dirname9, join as join10, relative as relative10, resolve as
|
|
20961
|
-
import * as
|
|
21391
|
+
import { dirname as dirname9, join as join10, relative as relative10, resolve as resolve15, sep as sep10 } from "node:path";
|
|
21392
|
+
import * as ts15 from "@typescript/typescript6";
|
|
20962
21393
|
|
|
20963
21394
|
// src/delivery-build-schema.ts
|
|
20964
21395
|
init_delivery_build_schema();
|
|
@@ -20999,23 +21430,23 @@ function renderDeliveryTarget(graph, target, options) {
|
|
|
20999
21430
|
init_delivery_files2();
|
|
21000
21431
|
import { isBuiltin } from "node:module";
|
|
21001
21432
|
import { readFile as readFile6 } from "node:fs/promises";
|
|
21002
|
-
import { join as join9, resolve as
|
|
21003
|
-
import * as
|
|
21433
|
+
import { join as join9, resolve as resolve13 } from "node:path";
|
|
21434
|
+
import * as ts14 from "@typescript/typescript6";
|
|
21004
21435
|
|
|
21005
21436
|
// src/delivery-imports.ts
|
|
21006
|
-
import * as
|
|
21437
|
+
import * as ts13 from "@typescript/typescript6";
|
|
21007
21438
|
function staticDeliveryImports(path, contents) {
|
|
21008
21439
|
if (!/\.[cm]?[jt]sx?$/.test(path))
|
|
21009
21440
|
return contents;
|
|
21010
|
-
const source =
|
|
21441
|
+
const source = ts13.createSourceFile(path, new TextDecoder().decode(contents), ts13.ScriptTarget.Latest, true);
|
|
21011
21442
|
const computed = [];
|
|
21012
21443
|
function scan(node) {
|
|
21013
|
-
if (
|
|
21444
|
+
if (ts13.isCallExpression(node) && (node.expression.kind === ts13.SyntaxKind.ImportKeyword || ts13.isIdentifier(node.expression) && node.expression.text === "require")) {
|
|
21014
21445
|
const argument = node.arguments[0];
|
|
21015
|
-
if (!argument || !
|
|
21446
|
+
if (!argument || !ts13.isStringLiteral(argument) && !ts13.isNoSubstitutionTemplateLiteral(argument))
|
|
21016
21447
|
computed.push(node);
|
|
21017
21448
|
}
|
|
21018
|
-
|
|
21449
|
+
ts13.forEachChild(node, scan);
|
|
21019
21450
|
}
|
|
21020
21451
|
scan(source);
|
|
21021
21452
|
if (computed.length === 0)
|
|
@@ -21023,36 +21454,36 @@ function staticDeliveryImports(path, contents) {
|
|
|
21023
21454
|
const reject = () => {
|
|
21024
21455
|
throw new Error("Computed module loading is not supported in independent delivery bundles.");
|
|
21025
21456
|
};
|
|
21026
|
-
if (!
|
|
21457
|
+
if (!ts13.isExternalModule(source))
|
|
21027
21458
|
return reject();
|
|
21028
|
-
const host =
|
|
21459
|
+
const host = ts13.createCompilerHost({ noLib: true, noResolve: true, allowJs: true });
|
|
21029
21460
|
host.getSourceFile = (name) => name === path ? source : undefined;
|
|
21030
|
-
const checker =
|
|
21461
|
+
const checker = ts13.createProgram([path], { noLib: true, noResolve: true, allowJs: true }, host).getTypeChecker();
|
|
21031
21462
|
const replacements = new Map;
|
|
21032
21463
|
const removed = new Set;
|
|
21033
21464
|
for (const call of computed) {
|
|
21034
21465
|
let visit = function(node) {
|
|
21035
|
-
const referenced =
|
|
21036
|
-
if (
|
|
21466
|
+
const referenced = ts13.isIdentifier(node) ? ts13.isShorthandPropertyAssignment(node.parent) ? checker.getShorthandAssignmentValueSymbol(node.parent) : ts13.isExportSpecifier(node.parent) ? checker.getExportSpecifierLocalTargetSymbol(node.parent) : checker.getSymbolAtLocation(node) : undefined;
|
|
21467
|
+
if (ts13.isIdentifier(node) && node !== declarationName && referenced === symbol) {
|
|
21037
21468
|
const use = node.parent;
|
|
21038
|
-
if (!
|
|
21469
|
+
if (!ts13.isCallExpression(use) || use.expression !== node || use.arguments.length !== 1 || use.questionDotToken || use.typeArguments?.length)
|
|
21039
21470
|
return reject();
|
|
21040
21471
|
const value = use.arguments[0];
|
|
21041
|
-
if (!value || !
|
|
21472
|
+
if (!value || !ts13.isStringLiteral(value) && !ts13.isNoSubstitutionTemplateLiteral(value))
|
|
21042
21473
|
return reject();
|
|
21043
21474
|
references++;
|
|
21044
|
-
replacements.set(use,
|
|
21475
|
+
replacements.set(use, ts13.factory.createCallExpression(call.expression, undefined, [value]));
|
|
21045
21476
|
}
|
|
21046
|
-
|
|
21477
|
+
ts13.forEachChild(node, visit);
|
|
21047
21478
|
};
|
|
21048
21479
|
const statement = call.parent;
|
|
21049
21480
|
const block = statement.parent;
|
|
21050
21481
|
const declaration = block.parent;
|
|
21051
|
-
if (call.expression.kind !==
|
|
21482
|
+
if (call.expression.kind !== ts13.SyntaxKind.ImportKeyword || call.arguments.length !== 1 || !ts13.isReturnStatement(statement) || !ts13.isBlock(block) || block.statements.length !== 1 || !ts13.isFunctionDeclaration(declaration) || declaration.parent !== source || !declaration.name || declaration.modifiers?.length || declaration.asteriskToken || declaration.typeParameters?.length || declaration.parameters.length !== 1)
|
|
21052
21483
|
return reject();
|
|
21053
21484
|
const parameter = declaration.parameters[0];
|
|
21054
21485
|
const argument = call.arguments[0];
|
|
21055
|
-
if (!parameter || !
|
|
21486
|
+
if (!parameter || !ts13.isIdentifier(parameter.name) || parameter.initializer || parameter.dotDotDotToken || !argument || !ts13.isIdentifier(argument) || checker.getSymbolAtLocation(argument) !== checker.getSymbolAtLocation(parameter.name))
|
|
21056
21487
|
return reject();
|
|
21057
21488
|
const symbol = checker.getSymbolAtLocation(declaration.name);
|
|
21058
21489
|
if (!symbol || symbol.declarations?.length !== 1)
|
|
@@ -21064,22 +21495,22 @@ function staticDeliveryImports(path, contents) {
|
|
|
21064
21495
|
return reject();
|
|
21065
21496
|
removed.add(declaration);
|
|
21066
21497
|
}
|
|
21067
|
-
const transformed =
|
|
21498
|
+
const transformed = ts13.transform(source, [(context) => {
|
|
21068
21499
|
const visit = (node) => {
|
|
21069
|
-
if (
|
|
21500
|
+
if (ts13.isFunctionDeclaration(node) && removed.has(node))
|
|
21070
21501
|
return;
|
|
21071
|
-
const replacement =
|
|
21502
|
+
const replacement = ts13.isCallExpression(node) ? replacements.get(node) : undefined;
|
|
21072
21503
|
if (replacement)
|
|
21073
21504
|
return replacement;
|
|
21074
|
-
return
|
|
21505
|
+
return ts13.visitEachChild(node, visit, context);
|
|
21075
21506
|
};
|
|
21076
|
-
return (node) =>
|
|
21507
|
+
return (node) => ts13.visitNode(node, visit, ts13.isSourceFile) ?? node;
|
|
21077
21508
|
}]);
|
|
21078
21509
|
try {
|
|
21079
21510
|
const transformedSource = transformed.transformed[0];
|
|
21080
21511
|
if (!transformedSource)
|
|
21081
21512
|
return reject();
|
|
21082
|
-
return new TextEncoder().encode(
|
|
21513
|
+
return new TextEncoder().encode(ts13.createPrinter().printFile(transformedSource));
|
|
21083
21514
|
} finally {
|
|
21084
21515
|
transformed.dispose();
|
|
21085
21516
|
}
|
|
@@ -21176,7 +21607,7 @@ async function bundleDeliveryTarget(name, code, project, generatedDirectory, opt
|
|
|
21176
21607
|
async function bundleDeliveryTargetInProcess(name, code, project, generatedDirectory, options) {
|
|
21177
21608
|
const inputs = new Map;
|
|
21178
21609
|
const workingDirectory = process.cwd();
|
|
21179
|
-
const entry =
|
|
21610
|
+
const entry = resolve13(generatedDirectory, `delivery-${name}.ts`);
|
|
21180
21611
|
const entryPattern = new RegExp(`^${entry.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`);
|
|
21181
21612
|
const result = await Bun.build({
|
|
21182
21613
|
entrypoints: [entry],
|
|
@@ -21199,7 +21630,7 @@ async function bundleDeliveryTargetInProcess(name, code, project, generatedDirec
|
|
|
21199
21630
|
if (args.path === entry)
|
|
21200
21631
|
return { contents: code, loader: "ts" };
|
|
21201
21632
|
const bytes = await readFile6(args.path);
|
|
21202
|
-
inputs.set(
|
|
21633
|
+
inputs.set(resolve13(args.path), digest(bytes));
|
|
21203
21634
|
return { contents: staticDeliveryImports(args.path, bytes), loader: args.loader };
|
|
21204
21635
|
});
|
|
21205
21636
|
}
|
|
@@ -21209,7 +21640,7 @@ async function bundleDeliveryTargetInProcess(name, code, project, generatedDirec
|
|
|
21209
21640
|
throw new Error("Independent bundle failed or has no dependency metadata.");
|
|
21210
21641
|
const runtimeImports = new Set;
|
|
21211
21642
|
for (const [path, metadata] of Object.entries(result.metafile.inputs)) {
|
|
21212
|
-
if (
|
|
21643
|
+
if (resolve13(workingDirectory, path) !== entry && !inputs.has(resolve13(workingDirectory, path))) {
|
|
21213
21644
|
throw new Error("Bundler reported an input that was not captured by the delivery snapshot.");
|
|
21214
21645
|
}
|
|
21215
21646
|
for (const imported of metadata.imports) {
|
|
@@ -21227,16 +21658,16 @@ async function bundleDeliveryTargetInProcess(name, code, project, generatedDirec
|
|
|
21227
21658
|
const bytes = new Uint8Array(await output.arrayBuffer());
|
|
21228
21659
|
if (/\.[cm]?js$/.test(path)) {
|
|
21229
21660
|
let check = function(node) {
|
|
21230
|
-
const specifier =
|
|
21231
|
-
if (specifier &&
|
|
21661
|
+
const specifier = ts14.isImportDeclaration(node) || ts14.isExportDeclaration(node) ? node.moduleSpecifier : ts14.isCallExpression(node) && (node.expression.kind === ts14.SyntaxKind.ImportKeyword || ts14.isIdentifier(node.expression) && /^(?:__)?require$/.test(node.expression.text) || ts14.isPropertyAccessExpression(node.expression) && node.expression.name.text === "require" && ts14.isMetaProperty(node.expression.expression) && node.expression.expression.keywordToken === ts14.SyntaxKind.ImportKeyword) ? node.arguments[0] : undefined;
|
|
21662
|
+
if (specifier && ts14.isStringLiteralLike(specifier)) {
|
|
21232
21663
|
if (!isBuiltin(specifier.text) && specifier.text !== "bun" && !specifier.text.startsWith("bun:")) {
|
|
21233
21664
|
throw new Error("Independent bundles cannot retain external package imports.");
|
|
21234
21665
|
}
|
|
21235
21666
|
runtimeImports.add(specifier.text);
|
|
21236
21667
|
}
|
|
21237
|
-
|
|
21668
|
+
ts14.forEachChild(node, check);
|
|
21238
21669
|
};
|
|
21239
|
-
const source =
|
|
21670
|
+
const source = ts14.createSourceFile(path, new TextDecoder().decode(bytes), ts14.ScriptTarget.Latest, true);
|
|
21240
21671
|
check(source);
|
|
21241
21672
|
}
|
|
21242
21673
|
files.set(`bundle/${path}`, bytes);
|
|
@@ -21526,14 +21957,14 @@ interface DeliveryWorkerHost {
|
|
|
21526
21957
|
|
|
21527
21958
|
// src/delivery-migrations.ts
|
|
21528
21959
|
init_delivery_files2();
|
|
21529
|
-
import { resolve as
|
|
21960
|
+
import { resolve as resolve14 } from "node:path";
|
|
21530
21961
|
async function prepareDeliveryMigrations(project, generatedRoot, declarations = []) {
|
|
21531
21962
|
const entries = [];
|
|
21532
21963
|
const files = new Map;
|
|
21533
21964
|
const inputs = new Map;
|
|
21534
21965
|
let totalBytes = 0;
|
|
21535
21966
|
for (const declaration of [...declarations].sort((a, b) => BigInt(a.version) < BigInt(b.version) ? -1 : BigInt(a.version) > BigInt(b.version) ? 1 : 0)) {
|
|
21536
|
-
const source =
|
|
21967
|
+
const source = resolve14(project, declaration.source);
|
|
21537
21968
|
if (!inside(project, source) || inside(generatedRoot, source))
|
|
21538
21969
|
throw new Error("Invalid migration source.");
|
|
21539
21970
|
const bytes = await readBoundedOwned(project, source, 1048576);
|
|
@@ -21584,7 +22015,7 @@ async function buildDeliveryProject(options, delivery) {
|
|
|
21584
22015
|
const settings = parseDeliveryOptions(delivery);
|
|
21585
22016
|
if (typeof Bun === "undefined")
|
|
21586
22017
|
throw new Error("Independent delivery builds require Bun.");
|
|
21587
|
-
const configPath =
|
|
22018
|
+
const configPath = ts15.findConfigFile(resolve15(options.rootDir), ts15.sys.fileExists);
|
|
21588
22019
|
if (!configPath)
|
|
21589
22020
|
throw new Error("Independent delivery builds require a project tsconfig.json.");
|
|
21590
22021
|
const lexicalProject = dirname9(configPath);
|
|
@@ -21592,19 +22023,19 @@ async function buildDeliveryProject(options, delivery) {
|
|
|
21592
22023
|
const sourceRoot = await realpath4(options.rootDir);
|
|
21593
22024
|
if (!inside(project, sourceRoot))
|
|
21594
22025
|
throw new Error("Application sources must be inside the project.");
|
|
21595
|
-
const requested =
|
|
22026
|
+
const requested = resolve15(options.outDir, "delivery");
|
|
21596
22027
|
if (!inside(lexicalProject, requested))
|
|
21597
22028
|
throw new Error("Output must be inside the application project.");
|
|
21598
|
-
const buildRoot =
|
|
21599
|
-
const generatedRoot =
|
|
22029
|
+
const buildRoot = resolve15(project, relative10(lexicalProject, requested));
|
|
22030
|
+
const generatedRoot = resolve15(project, relative10(lexicalProject, options.outDir));
|
|
21600
22031
|
if (inside(generatedRoot, sourceRoot))
|
|
21601
22032
|
throw new Error("Output must not contain the application source root.");
|
|
21602
22033
|
const configInputs = new Map;
|
|
21603
|
-
const parsedConfig =
|
|
21604
|
-
...
|
|
22034
|
+
const parsedConfig = ts15.getParsedCommandLineOfConfigFile(await realpath4(configPath), {}, {
|
|
22035
|
+
...ts15.sys,
|
|
21605
22036
|
readFile(path) {
|
|
21606
22037
|
const contents = readFileSync3(path, "utf8");
|
|
21607
|
-
configInputs.set(
|
|
22038
|
+
configInputs.set(resolve15(path), digest(contents));
|
|
21608
22039
|
return contents;
|
|
21609
22040
|
},
|
|
21610
22041
|
onUnRecoverableConfigFileDiagnostic() {
|
|
@@ -21642,7 +22073,7 @@ async function buildDeliveryProject(options, delivery) {
|
|
|
21642
22073
|
if (!target || (kind === "worker" ? target.kind !== "jobs" : target.kind === "jobs")) {
|
|
21643
22074
|
throw new Error("Delivery host kind does not match its target.");
|
|
21644
22075
|
}
|
|
21645
|
-
const path =
|
|
22076
|
+
const path = resolve15(sourceRoot, app.source);
|
|
21646
22077
|
if (inside(generatedRoot, path) || !/\.[cm]?ts$/.test(path))
|
|
21647
22078
|
throw new Error("Hosts require application TypeScript source.");
|
|
21648
22079
|
hosts.set(app.target, { path, kind, hash: digest(await readOwned(sourceRoot, path)) });
|
|
@@ -21721,7 +22152,7 @@ async function buildDeliveryProject(options, delivery) {
|
|
|
21721
22152
|
for (const [name, contents] of Object.entries(generated))
|
|
21722
22153
|
await writeArtifact(stage, `generated/${name}`, contents);
|
|
21723
22154
|
{
|
|
21724
|
-
const program =
|
|
22155
|
+
const program = ts15.createProgram({
|
|
21725
22156
|
rootNames: [
|
|
21726
22157
|
...parsedConfig.fileNames.filter((path) => !inside(generatedRoot, path)),
|
|
21727
22158
|
...Object.keys(generated).map((name) => join10(stage, "generated", name))
|
|
@@ -21729,12 +22160,12 @@ async function buildDeliveryProject(options, delivery) {
|
|
|
21729
22160
|
options: { ...parsedConfig.options, rootDir: project },
|
|
21730
22161
|
...parsedConfig.projectReferences ? { projectReferences: parsedConfig.projectReferences } : {}
|
|
21731
22162
|
});
|
|
21732
|
-
const diagnostics =
|
|
21733
|
-
if (diagnostics.some((item) => item.category ===
|
|
21734
|
-
return failed(diagnostics.filter((item) => item.category ===
|
|
22163
|
+
const diagnostics = ts15.getPreEmitDiagnostics(program);
|
|
22164
|
+
if (diagnostics.some((item) => item.category === ts15.DiagnosticCategory.Error)) {
|
|
22165
|
+
return failed(diagnostics.filter((item) => item.category === ts15.DiagnosticCategory.Error).map((item) => ({
|
|
21735
22166
|
severity: "error",
|
|
21736
22167
|
code: "delivery-generated-type-error",
|
|
21737
|
-
message:
|
|
22168
|
+
message: ts15.flattenDiagnosticMessageText(item.messageText, `
|
|
21738
22169
|
`),
|
|
21739
22170
|
...item.file ? { file: item.file.fileName } : {}
|
|
21740
22171
|
})));
|
|
@@ -21769,7 +22200,7 @@ async function buildDeliveryProject(options, delivery) {
|
|
|
21769
22200
|
}));
|
|
21770
22201
|
const assetInputs = {};
|
|
21771
22202
|
for (const asset of assets.filter((asset) => asset.target === target.name)) {
|
|
21772
|
-
const path =
|
|
22203
|
+
const path = resolve15(sourceRoot, asset.source);
|
|
21773
22204
|
const bytes = await readOwned(sourceRoot, path);
|
|
21774
22205
|
const hash = digest(bytes);
|
|
21775
22206
|
const previousHash = snapshots.get(path);
|
|
@@ -21879,13 +22310,13 @@ async function buildDeliveryProject(options, delivery) {
|
|
|
21879
22310
|
|
|
21880
22311
|
// src/migrations.ts
|
|
21881
22312
|
import { rename as rename5, readFile as readFile9, writeFile as writeFile4, rm as rm3 } from "node:fs/promises";
|
|
21882
|
-
import { relative as relative11, resolve as
|
|
21883
|
-
import * as
|
|
22313
|
+
import { relative as relative11, resolve as resolve17 } from "node:path";
|
|
22314
|
+
import * as ts16 from "@typescript/typescript6";
|
|
21884
22315
|
|
|
21885
22316
|
// src/migration-policy.ts
|
|
21886
22317
|
import { readFile as readFile8 } from "node:fs/promises";
|
|
21887
22318
|
import { readFileSync as readFileSync4 } from "node:fs";
|
|
21888
|
-
import { resolve as
|
|
22319
|
+
import { resolve as resolve16 } from "node:path";
|
|
21889
22320
|
var cachedCompilerVersion;
|
|
21890
22321
|
function compilerVersion() {
|
|
21891
22322
|
if (cachedCompilerVersion !== undefined)
|
|
@@ -21899,9 +22330,9 @@ function compilerVersion() {
|
|
|
21899
22330
|
}
|
|
21900
22331
|
function migrationDependencies() {
|
|
21901
22332
|
return {
|
|
21902
|
-
"@supacloud/app": "0.
|
|
22333
|
+
"@supacloud/app": "0.21.0",
|
|
21903
22334
|
"@supacloud/compiler": compilerVersion(),
|
|
21904
|
-
"@supacloud/elysia": "0.
|
|
22335
|
+
"@supacloud/elysia": "0.23.0",
|
|
21905
22336
|
elysia: "2.0.0-beta.19",
|
|
21906
22337
|
typescript: "7.0.2"
|
|
21907
22338
|
};
|
|
@@ -21910,7 +22341,7 @@ async function checkMigrationDependencies(rootDir) {
|
|
|
21910
22341
|
const problems = [];
|
|
21911
22342
|
for (const [name, expected] of Object.entries(migrationDependencies())) {
|
|
21912
22343
|
try {
|
|
21913
|
-
const manifest = JSON.parse(await readFile8(
|
|
22344
|
+
const manifest = JSON.parse(await readFile8(resolve16(rootDir, "node_modules", name, "package.json"), "utf8"));
|
|
21914
22345
|
if (!manifest || typeof manifest !== "object" || !("name" in manifest) || manifest.name !== name || !("version" in manifest) || manifest.version !== expected) {
|
|
21915
22346
|
problems.push(`${name}: requires tested installed version ${expected}`);
|
|
21916
22347
|
}
|
|
@@ -21924,29 +22355,29 @@ async function checkMigrationDependencies(rootDir) {
|
|
|
21924
22355
|
// src/migrations.ts
|
|
21925
22356
|
var ROUTE_DECORATORS2 = new Set(["Get", "Post", "Put", "Patch", "Delete", "Head", "Options"]);
|
|
21926
22357
|
var MIGRATION_COMPILER_OPTIONS = {
|
|
21927
|
-
target:
|
|
21928
|
-
module:
|
|
21929
|
-
moduleResolution:
|
|
22358
|
+
target: ts16.ScriptTarget.ES2022,
|
|
22359
|
+
module: ts16.ModuleKind.ESNext,
|
|
22360
|
+
moduleResolution: ts16.ModuleResolutionKind.Bundler,
|
|
21930
22361
|
noEmit: true,
|
|
21931
22362
|
skipLibCheck: true
|
|
21932
22363
|
};
|
|
21933
22364
|
function migrationCompilerOptions(rootDir) {
|
|
21934
22365
|
if (!rootDir)
|
|
21935
22366
|
return MIGRATION_COMPILER_OPTIONS;
|
|
21936
|
-
const configPath =
|
|
22367
|
+
const configPath = ts16.findConfigFile(rootDir, ts16.sys.fileExists);
|
|
21937
22368
|
if (!configPath)
|
|
21938
22369
|
return MIGRATION_COMPILER_OPTIONS;
|
|
21939
22370
|
const configHost = {
|
|
21940
|
-
...
|
|
22371
|
+
...ts16.sys,
|
|
21941
22372
|
onUnRecoverableConfigFileDiagnostic: (_diagnostic) => {}
|
|
21942
22373
|
};
|
|
21943
|
-
const parsed =
|
|
22374
|
+
const parsed = ts16.getParsedCommandLineOfConfigFile(configPath, {}, configHost);
|
|
21944
22375
|
if (!parsed || parsed.errors.length > 0)
|
|
21945
22376
|
return MIGRATION_COMPILER_OPTIONS;
|
|
21946
22377
|
return { ...parsed.options, noEmit: true, skipLibCheck: true };
|
|
21947
22378
|
}
|
|
21948
22379
|
function propertyName2(property) {
|
|
21949
|
-
if (
|
|
22380
|
+
if (ts16.isIdentifier(property) || ts16.isStringLiteral(property) || ts16.isNumericLiteral(property))
|
|
21950
22381
|
return property.text;
|
|
21951
22382
|
return;
|
|
21952
22383
|
}
|
|
@@ -21956,7 +22387,7 @@ function lineOf2(sourceFile, node) {
|
|
|
21956
22387
|
function resolveSymbol(symbol, checker) {
|
|
21957
22388
|
if (!symbol)
|
|
21958
22389
|
return;
|
|
21959
|
-
for (let guard = 0;guard < 4 && (symbol.flags &
|
|
22390
|
+
for (let guard = 0;guard < 4 && (symbol.flags & ts16.SymbolFlags.Alias) !== 0; guard += 1) {
|
|
21960
22391
|
const aliased = checker.getAliasedSymbol(symbol);
|
|
21961
22392
|
if (aliased === symbol)
|
|
21962
22393
|
break;
|
|
@@ -21965,12 +22396,12 @@ function resolveSymbol(symbol, checker) {
|
|
|
21965
22396
|
return symbol;
|
|
21966
22397
|
}
|
|
21967
22398
|
function symbolForExpression(expression, checker) {
|
|
21968
|
-
const location =
|
|
22399
|
+
const location = ts16.isIdentifier(expression) ? expression : ts16.isPropertyAccessExpression(expression) ? expression.name : ts16.isElementAccessExpression(expression) && expression.argumentExpression && ts16.isStringLiteral(expression.argumentExpression) ? expression : undefined;
|
|
21969
22400
|
return location ? resolveSymbol(checker.getSymbolAtLocation(location), checker) : undefined;
|
|
21970
22401
|
}
|
|
21971
22402
|
function unwrapExpression(expression) {
|
|
21972
22403
|
let current = expression;
|
|
21973
|
-
while (
|
|
22404
|
+
while (ts16.isAsExpression(current) || ts16.isSatisfiesExpression(current) || ts16.isParenthesizedExpression(current) || ts16.isTypeAssertionExpression(current)) {
|
|
21974
22405
|
current = current.expression;
|
|
21975
22406
|
}
|
|
21976
22407
|
return current;
|
|
@@ -21985,7 +22416,7 @@ function isRouteDecoratorCall(node, checker) {
|
|
|
21985
22416
|
if (node.arguments.length < 2)
|
|
21986
22417
|
return false;
|
|
21987
22418
|
const name = node.expression.getText(node.getSourceFile());
|
|
21988
|
-
if (
|
|
22419
|
+
if (ts16.isIdentifier(node.expression) && ROUTE_DECORATORS2.has(node.expression.text))
|
|
21989
22420
|
return true;
|
|
21990
22421
|
if (ROUTE_DECORATORS2.has(name))
|
|
21991
22422
|
return true;
|
|
@@ -21999,14 +22430,14 @@ function resolveStaticObjectLiteral2(input, checker, seen = new Set) {
|
|
|
21999
22430
|
if (seen.has(expression))
|
|
22000
22431
|
return;
|
|
22001
22432
|
seen.add(expression);
|
|
22002
|
-
if (
|
|
22433
|
+
if (ts16.isObjectLiteralExpression(expression))
|
|
22003
22434
|
return expression;
|
|
22004
|
-
if (
|
|
22435
|
+
if (ts16.isCallExpression(expression) && isDefineRouteContractCall(expression, checker)) {
|
|
22005
22436
|
return resolveStaticObjectLiteral2(expression.arguments[0], checker, seen);
|
|
22006
22437
|
}
|
|
22007
22438
|
const symbol = symbolForExpression(expression, checker);
|
|
22008
22439
|
for (const declaration of symbol?.declarations ?? []) {
|
|
22009
|
-
if (
|
|
22440
|
+
if (ts16.isVariableDeclaration(declaration) && declaration.initializer) {
|
|
22010
22441
|
const resolved = resolveStaticObjectLiteral2(declaration.initializer, checker, seen);
|
|
22011
22442
|
if (resolved)
|
|
22012
22443
|
return resolved;
|
|
@@ -22020,25 +22451,25 @@ function displayFile(rootDir, fileName) {
|
|
|
22020
22451
|
}
|
|
22021
22452
|
function createMigrationProgram(fileNames, sourceOverrides, rootDir) {
|
|
22022
22453
|
const compilerOptions = migrationCompilerOptions(rootDir);
|
|
22023
|
-
const host =
|
|
22454
|
+
const host = ts16.createCompilerHost(compilerOptions);
|
|
22024
22455
|
const getSourceFile = host.getSourceFile.bind(host);
|
|
22025
22456
|
const fileExists = host.fileExists.bind(host);
|
|
22026
22457
|
const readFile = host.readFile.bind(host);
|
|
22027
22458
|
const currentDirectory = host.getCurrentDirectory.bind(host);
|
|
22028
|
-
host.fileExists = (fileName) => sourceOverrides.has(
|
|
22029
|
-
host.readFile = (fileName) => sourceOverrides.get(
|
|
22459
|
+
host.fileExists = (fileName) => sourceOverrides.has(resolve17(fileName)) || fileExists(fileName);
|
|
22460
|
+
host.readFile = (fileName) => sourceOverrides.get(resolve17(fileName)) ?? readFile(fileName);
|
|
22030
22461
|
host.getSourceFile = (fileName, languageVersion, onError, shouldCreateNewSourceFile) => {
|
|
22031
|
-
const source = sourceOverrides.get(
|
|
22032
|
-
return source === undefined ? getSourceFile(fileName, languageVersion, onError, shouldCreateNewSourceFile) :
|
|
22462
|
+
const source = sourceOverrides.get(resolve17(fileName));
|
|
22463
|
+
return source === undefined ? getSourceFile(fileName, languageVersion, onError, shouldCreateNewSourceFile) : ts16.createSourceFile(fileName, source, languageVersion, true);
|
|
22033
22464
|
};
|
|
22034
22465
|
host.getCurrentDirectory = () => rootDir ?? currentDirectory();
|
|
22035
|
-
return
|
|
22466
|
+
return ts16.createProgram(fileNames, compilerOptions, host);
|
|
22036
22467
|
}
|
|
22037
22468
|
function routeResponseProperties(object) {
|
|
22038
|
-
const responseProperties = object.properties.filter((property) =>
|
|
22469
|
+
const responseProperties = object.properties.filter((property) => ts16.isPropertyAssignment(property) && propertyName2(property.name) === "response");
|
|
22039
22470
|
return {
|
|
22040
22471
|
response: responseProperties[0],
|
|
22041
|
-
hasResponses: object.properties.some((property) => (
|
|
22472
|
+
hasResponses: object.properties.some((property) => (ts16.isPropertyAssignment(property) || ts16.isShorthandPropertyAssignment(property)) && propertyName2(property.name) === "responses"),
|
|
22042
22473
|
duplicateResponse: responseProperties.length > 1
|
|
22043
22474
|
};
|
|
22044
22475
|
}
|
|
@@ -22054,15 +22485,15 @@ function planRouteResponseMigration(sourceFiles, checker, rootDir, includedFiles
|
|
|
22054
22485
|
issues.push(issue);
|
|
22055
22486
|
};
|
|
22056
22487
|
for (const sourceFile of sourceFiles) {
|
|
22057
|
-
const sourcePath =
|
|
22488
|
+
const sourcePath = resolve17(sourceFile.fileName);
|
|
22058
22489
|
if (!includedFiles.has(sourcePath))
|
|
22059
22490
|
continue;
|
|
22060
22491
|
const visit = (node) => {
|
|
22061
|
-
if (
|
|
22492
|
+
if (ts16.isCallExpression(node) && isRouteDecoratorCall(node, checker)) {
|
|
22062
22493
|
const options = node.arguments[1];
|
|
22063
22494
|
const object = options && resolveStaticObjectLiteral2(options, checker);
|
|
22064
22495
|
if (object) {
|
|
22065
|
-
const objectPath =
|
|
22496
|
+
const objectPath = resolve17(object.getSourceFile().fileName);
|
|
22066
22497
|
const properties = routeResponseProperties(object);
|
|
22067
22498
|
if (properties.response) {
|
|
22068
22499
|
if (!includedFiles.has(objectPath)) {
|
|
@@ -22078,7 +22509,7 @@ function planRouteResponseMigration(sourceFiles, checker, rootDir, includedFiles
|
|
|
22078
22509
|
}
|
|
22079
22510
|
}
|
|
22080
22511
|
}
|
|
22081
|
-
|
|
22512
|
+
ts16.forEachChild(node, visit);
|
|
22082
22513
|
};
|
|
22083
22514
|
visit(sourceFile);
|
|
22084
22515
|
}
|
|
@@ -22105,13 +22536,13 @@ function planRouteResponseMigration(sourceFiles, checker, rootDir, includedFiles
|
|
|
22105
22536
|
line: lineOf2(sourceFile, response)
|
|
22106
22537
|
});
|
|
22107
22538
|
} else {
|
|
22108
|
-
const replacements = replacementsByFile.get(
|
|
22539
|
+
const replacements = replacementsByFile.get(resolve17(sourceFile.fileName)) ?? [];
|
|
22109
22540
|
replacements.push({
|
|
22110
22541
|
start: response.getStart(sourceFile),
|
|
22111
22542
|
end: response.getEnd(),
|
|
22112
22543
|
text: `responses: { 200: ${response.initializer.getText(sourceFile)} }`
|
|
22113
22544
|
});
|
|
22114
|
-
replacementsByFile.set(
|
|
22545
|
+
replacementsByFile.set(resolve17(sourceFile.fileName), replacements);
|
|
22115
22546
|
}
|
|
22116
22547
|
}
|
|
22117
22548
|
return { replacementsByFile, issues };
|
|
@@ -22124,7 +22555,7 @@ function applyReplacements(source, replacements) {
|
|
|
22124
22555
|
return content;
|
|
22125
22556
|
}
|
|
22126
22557
|
function migrateRouteResponse(source, fileName) {
|
|
22127
|
-
const absoluteFile =
|
|
22558
|
+
const absoluteFile = resolve17(fileName);
|
|
22128
22559
|
const sourceOverrides = new Map([[absoluteFile, source]]);
|
|
22129
22560
|
const program = createMigrationProgram([absoluteFile], sourceOverrides);
|
|
22130
22561
|
const sourceFile = program.getSourceFile(absoluteFile);
|
|
@@ -22140,7 +22571,7 @@ function migrateRouteResponse(source, fileName) {
|
|
|
22140
22571
|
}]
|
|
22141
22572
|
};
|
|
22142
22573
|
}
|
|
22143
|
-
const plan = planRouteResponseMigration([sourceFile], program.getTypeChecker(),
|
|
22574
|
+
const plan = planRouteResponseMigration([sourceFile], program.getTypeChecker(), resolve17("."), new Set([absoluteFile]));
|
|
22144
22575
|
const replacements = plan.replacementsByFile.get(absoluteFile) ?? [];
|
|
22145
22576
|
return {
|
|
22146
22577
|
changed: replacements.length > 0,
|
|
@@ -22150,7 +22581,7 @@ function migrateRouteResponse(source, fileName) {
|
|
|
22150
22581
|
};
|
|
22151
22582
|
}
|
|
22152
22583
|
function migrateRouteResponseProject(files, rootDir, sourceByPath) {
|
|
22153
|
-
const absoluteFiles = files.map((file) =>
|
|
22584
|
+
const absoluteFiles = files.map((file) => resolve17(file));
|
|
22154
22585
|
const program = createMigrationProgram(absoluteFiles, sourceByPath, rootDir);
|
|
22155
22586
|
const sourceFiles = absoluteFiles.map((file) => program.getSourceFile(file)).filter((file) => file !== undefined);
|
|
22156
22587
|
const plan = planRouteResponseMigration(sourceFiles, program.getTypeChecker(), rootDir, new Set(absoluteFiles));
|
|
@@ -22163,11 +22594,11 @@ function migrateRouteResponseProject(files, rootDir, sourceByPath) {
|
|
|
22163
22594
|
changed: replacements.length > 0,
|
|
22164
22595
|
content: applyReplacements(source, replacements),
|
|
22165
22596
|
replacements: replacements.length,
|
|
22166
|
-
issues: plan.issues.filter((issue) =>
|
|
22597
|
+
issues: plan.issues.filter((issue) => resolve17(rootDir, issue.file) === file)
|
|
22167
22598
|
});
|
|
22168
22599
|
}
|
|
22169
22600
|
for (const issue of plan.issues) {
|
|
22170
|
-
const path =
|
|
22601
|
+
const path = resolve17(rootDir, issue.file);
|
|
22171
22602
|
if (!results.has(path) && sourceByPath.has(path)) {
|
|
22172
22603
|
results.set(path, {
|
|
22173
22604
|
changed: false,
|
|
@@ -22186,6 +22617,13 @@ var SUPACLOUD_MIGRATIONS = [
|
|
|
22186
22617
|
to: "0.12.0",
|
|
22187
22618
|
description: "Replace deprecated route response schemas with explicit HTTP status maps.",
|
|
22188
22619
|
apply: migrateRouteResponse
|
|
22620
|
+
},
|
|
22621
|
+
{
|
|
22622
|
+
id: "http-provider-entrypoint",
|
|
22623
|
+
from: "0.12.0",
|
|
22624
|
+
to: "0.13.0",
|
|
22625
|
+
description: "Move provider-only withInterceptors imports to the shared HTTP feature entry; reject ambiguous uses.",
|
|
22626
|
+
apply: migrateHttpProviderImports
|
|
22189
22627
|
}
|
|
22190
22628
|
];
|
|
22191
22629
|
async function writeAtomically(path, content) {
|
|
@@ -22198,7 +22636,7 @@ async function writeAtomically(path, content) {
|
|
|
22198
22636
|
}
|
|
22199
22637
|
}
|
|
22200
22638
|
async function migrateProject(options) {
|
|
22201
|
-
const rootDir =
|
|
22639
|
+
const rootDir = resolve17(options.rootDir);
|
|
22202
22640
|
let migrations = SUPACLOUD_MIGRATIONS;
|
|
22203
22641
|
const preflightIssues = [];
|
|
22204
22642
|
if (options.fromVersion !== undefined || options.toVersion !== undefined) {
|
|
@@ -22238,7 +22676,7 @@ async function migrateProject(options) {
|
|
|
22238
22676
|
}
|
|
22239
22677
|
}
|
|
22240
22678
|
const include = options.include ?? ["**/*.ts", "**/*.tsx", "**/*.mts", "**/*.cts"];
|
|
22241
|
-
const files =
|
|
22679
|
+
const files = ts16.sys.readDirectory(rootDir, [".ts", ".tsx", ".mts", ".cts"], ["node_modules", "dist", "generated"], include).sort();
|
|
22242
22680
|
const results = [];
|
|
22243
22681
|
const issues = [];
|
|
22244
22682
|
const pendingWrites = new Map;
|
|
@@ -22254,7 +22692,7 @@ async function migrateProject(options) {
|
|
|
22254
22692
|
}
|
|
22255
22693
|
};
|
|
22256
22694
|
for (const filePath of files) {
|
|
22257
|
-
sourceByPath.set(
|
|
22695
|
+
sourceByPath.set(resolve17(filePath), await readFile9(filePath, "utf8"));
|
|
22258
22696
|
}
|
|
22259
22697
|
const originalSources = new Map(sourceByPath);
|
|
22260
22698
|
const writtenFiles = new Set;
|
|
@@ -22263,7 +22701,7 @@ async function migrateProject(options) {
|
|
|
22263
22701
|
if (projectResults)
|
|
22264
22702
|
appendIssues(projectResults.issues);
|
|
22265
22703
|
for (const filePath of files) {
|
|
22266
|
-
const absoluteFile =
|
|
22704
|
+
const absoluteFile = resolve17(filePath);
|
|
22267
22705
|
const file = relative11(rootDir, absoluteFile) || absoluteFile;
|
|
22268
22706
|
const before = sourceByPath.get(absoluteFile);
|
|
22269
22707
|
if (before === undefined)
|
|
@@ -22323,13 +22761,13 @@ async function migrateProject(options) {
|
|
|
22323
22761
|
}
|
|
22324
22762
|
|
|
22325
22763
|
// src/migration-assess.ts
|
|
22326
|
-
import { createHash as
|
|
22764
|
+
import { createHash as createHash10 } from "node:crypto";
|
|
22327
22765
|
import { readFile as readFile11 } from "node:fs/promises";
|
|
22328
|
-
import { join as join11, resolve as
|
|
22766
|
+
import { join as join11, resolve as resolve19 } from "node:path";
|
|
22329
22767
|
|
|
22330
22768
|
// src/openapi-tools.ts
|
|
22331
22769
|
import { mkdir as mkdir4, readFile as readFile10, rename as rename6, unlink as unlink3, writeFile as writeFile5 } from "node:fs/promises";
|
|
22332
|
-
import { dirname as dirname10, resolve as
|
|
22770
|
+
import { dirname as dirname10, resolve as resolve18 } from "node:path";
|
|
22333
22771
|
import { pathToFileURL as pathToFileURL2 } from "node:url";
|
|
22334
22772
|
|
|
22335
22773
|
class OpenApiDocumentError extends Error {
|
|
@@ -22720,7 +23158,7 @@ function serializeOpenApiJson(document, space = 2) {
|
|
|
22720
23158
|
}
|
|
22721
23159
|
async function readOpenApiJson(path) {
|
|
22722
23160
|
try {
|
|
22723
|
-
const value = JSON.parse(await readFile10(
|
|
23161
|
+
const value = JSON.parse(await readFile10(resolve18(path), "utf8"));
|
|
22724
23162
|
return parseOpenApiDocument(value);
|
|
22725
23163
|
} catch (error) {
|
|
22726
23164
|
if (error instanceof OpenApiDocumentError)
|
|
@@ -22729,7 +23167,7 @@ async function readOpenApiJson(path) {
|
|
|
22729
23167
|
}
|
|
22730
23168
|
}
|
|
22731
23169
|
async function writeOpenApiJson(document, outputPath, space = 2) {
|
|
22732
|
-
const path =
|
|
23170
|
+
const path = resolve18(outputPath);
|
|
22733
23171
|
const content = serializeOpenApiJson(document, space);
|
|
22734
23172
|
await mkdir4(dirname10(path), { recursive: true });
|
|
22735
23173
|
try {
|
|
@@ -22750,7 +23188,7 @@ async function writeOpenApiJson(document, outputPath, space = 2) {
|
|
|
22750
23188
|
}
|
|
22751
23189
|
async function loadGeneratedOpenApiDocument(modulePath) {
|
|
22752
23190
|
try {
|
|
22753
|
-
const moduleUrl = pathToFileURL2(
|
|
23191
|
+
const moduleUrl = pathToFileURL2(resolve18(modulePath));
|
|
22754
23192
|
moduleUrl.searchParams.set("supacloud-openapi-export", "1");
|
|
22755
23193
|
const loaded = await import(moduleUrl.href);
|
|
22756
23194
|
if (!isRecord(loaded))
|
|
@@ -22847,17 +23285,17 @@ function arrayLength(value) {
|
|
|
22847
23285
|
return Array.isArray(value) ? value.length : undefined;
|
|
22848
23286
|
}
|
|
22849
23287
|
async function sha256(path) {
|
|
22850
|
-
return
|
|
23288
|
+
return createHash10("sha256").update(await readFile11(path)).digest("hex");
|
|
22851
23289
|
}
|
|
22852
23290
|
function overallStatus(findings) {
|
|
22853
23291
|
return findings.reduce((current, finding) => STATUS_PRIORITY[finding.status] > STATUS_PRIORITY[current] ? finding.status : current, "compatible");
|
|
22854
23292
|
}
|
|
22855
23293
|
async function assessMigration(options) {
|
|
22856
|
-
const projectDir =
|
|
23294
|
+
const projectDir = resolve19(options.projectDir);
|
|
22857
23295
|
const compile = {
|
|
22858
23296
|
...options.compile,
|
|
22859
|
-
rootDir:
|
|
22860
|
-
outDir:
|
|
23297
|
+
rootDir: resolve19(options.compile.rootDir),
|
|
23298
|
+
outDir: resolve19(options.compile.outDir)
|
|
22861
23299
|
};
|
|
22862
23300
|
const findings = [];
|
|
22863
23301
|
const dependencyProblems = await checkMigrationDependencies(projectDir);
|
|
@@ -22937,8 +23375,8 @@ async function assessMigration(options) {
|
|
|
22937
23375
|
}
|
|
22938
23376
|
let openApi;
|
|
22939
23377
|
if (options.baselineOpenApiPath && options.currentOpenApiPath) {
|
|
22940
|
-
const baselinePath =
|
|
22941
|
-
const currentPath =
|
|
23378
|
+
const baselinePath = resolve19(projectDir, options.baselineOpenApiPath);
|
|
23379
|
+
const currentPath = resolve19(projectDir, options.currentOpenApiPath);
|
|
22942
23380
|
try {
|
|
22943
23381
|
const diff = diffOpenApiDocuments(await readOpenApiJson(baselinePath), await readOpenApiJson(currentPath));
|
|
22944
23382
|
openApi = {
|
|
@@ -23032,6 +23470,7 @@ function printUsage() {
|
|
|
23032
23470
|
Usage:
|
|
23033
23471
|
supacloud-compiler compile [rootDir] [options]
|
|
23034
23472
|
supacloud-compiler check [rootDir] [options]
|
|
23473
|
+
supacloud-compiler diagnostics [rootDir] [--json]
|
|
23035
23474
|
supacloud-compiler dev [rootDir] [options]
|
|
23036
23475
|
supacloud-compiler graph [rootDir] [options]
|
|
23037
23476
|
supacloud-compiler explain <name> [rootDir] [options]
|
|
@@ -23053,6 +23492,7 @@ Usage:
|
|
|
23053
23492
|
Commands:
|
|
23054
23493
|
compile Compile application modules and generate artifacts
|
|
23055
23494
|
check Check artifact drift and run governance gates
|
|
23495
|
+
diagnostics Read-only editor diagnostics and validated semantic code actions
|
|
23056
23496
|
dev Watch source files and recompile on changes
|
|
23057
23497
|
graph Print the discovered application graph
|
|
23058
23498
|
explain Explain a module, provider, or external token
|
|
@@ -23131,7 +23571,7 @@ async function run() {
|
|
|
23131
23571
|
process.exitCode = 1;
|
|
23132
23572
|
return;
|
|
23133
23573
|
}
|
|
23134
|
-
if (!command || !["compile", "check", "dev", "graph", "explain", "context", "dev-context", "environment-bindings", "doctor", "migrate", "migration-assess", "fix", "graphql-schema", "plan", "build-delivery", "openapi-export", "openapi-diff"].includes(command)) {
|
|
23574
|
+
if (!command || !["compile", "check", "diagnostics", "dev", "graph", "explain", "context", "dev-context", "environment-bindings", "doctor", "migrate", "migration-assess", "fix", "graphql-schema", "plan", "build-delivery", "openapi-export", "openapi-diff"].includes(command)) {
|
|
23135
23575
|
console.error(`Error: unknown command "${command}"`);
|
|
23136
23576
|
printUsage();
|
|
23137
23577
|
process.exit(1);
|
|
@@ -23300,7 +23740,7 @@ async function run() {
|
|
|
23300
23740
|
} else if (arg === "--dry-run") {
|
|
23301
23741
|
dryRun = true;
|
|
23302
23742
|
} else if (arg === "--write") {
|
|
23303
|
-
if (command === "context" || command === "dev-context" || command === "environment-bindings")
|
|
23743
|
+
if (command === "context" || command === "dev-context" || command === "diagnostics" || command === "environment-bindings")
|
|
23304
23744
|
throw new Error(`${command} is read-only; --write is not supported`);
|
|
23305
23745
|
if (command === "plan" || command === "migration-assess")
|
|
23306
23746
|
throw new Error(`${command} is read-only; --write is not supported`);
|
|
@@ -23365,7 +23805,7 @@ async function run() {
|
|
|
23365
23805
|
throw new Error("Build context requires a manifest, target, subject, events, request ID and --json");
|
|
23366
23806
|
}
|
|
23367
23807
|
try {
|
|
23368
|
-
const pack = await createDeliveryExecutionContextPack(contextManifest, contextTarget, query, await readExecutionMetadata(
|
|
23808
|
+
const pack = await createDeliveryExecutionContextPack(contextManifest, contextTarget, query, await readExecutionMetadata(resolve23(executionEventsPath)), executionRequestId);
|
|
23369
23809
|
console.log(JSON.stringify(pack, null, 2));
|
|
23370
23810
|
} catch (error) {
|
|
23371
23811
|
console.log(JSON.stringify({
|
|
@@ -23384,7 +23824,7 @@ async function run() {
|
|
|
23384
23824
|
const currentPath = openApiDiffPaths[1];
|
|
23385
23825
|
if (!basePath || !currentPath)
|
|
23386
23826
|
throw new Error("openapi-diff requires two JSON file paths");
|
|
23387
|
-
const result = diffOpenApiDocuments(await readOpenApiJson(
|
|
23827
|
+
const result = diffOpenApiDocuments(await readOpenApiJson(resolve23(process.cwd(), basePath)), await readOpenApiJson(resolve23(process.cwd(), currentPath)));
|
|
23388
23828
|
console.log(json ? JSON.stringify(result, null, 2) : formatOpenApiDiff(result));
|
|
23389
23829
|
if (!result.ok)
|
|
23390
23830
|
process.exitCode = 1;
|
|
@@ -23399,8 +23839,8 @@ async function run() {
|
|
|
23399
23839
|
if (!modulePath || !outputPath)
|
|
23400
23840
|
throw new Error("openapi-export requires an OpenAPI module and output path");
|
|
23401
23841
|
const result = await exportGeneratedOpenApiJson({
|
|
23402
|
-
modulePath:
|
|
23403
|
-
outputPath:
|
|
23842
|
+
modulePath: resolve23(process.cwd(), modulePath),
|
|
23843
|
+
outputPath: resolve23(process.cwd(), outputPath),
|
|
23404
23844
|
...openApiExportSpace === undefined ? {} : { space: openApiExportSpace }
|
|
23405
23845
|
});
|
|
23406
23846
|
console.log(json ? JSON.stringify({ ok: true, ...result }, null, 2) : result.written ? `OpenAPI JSON written: ${result.path}` : `OpenAPI JSON matches: ${result.path}`);
|
|
@@ -23408,7 +23848,7 @@ async function run() {
|
|
|
23408
23848
|
}
|
|
23409
23849
|
if (command === "migrate") {
|
|
23410
23850
|
const result = await migrateProject({
|
|
23411
|
-
rootDir: rootDir ?
|
|
23851
|
+
rootDir: rootDir ? resolve23(process.cwd(), rootDir) : process.cwd(),
|
|
23412
23852
|
write: !dryRun,
|
|
23413
23853
|
...fromVersion === undefined ? {} : { fromVersion },
|
|
23414
23854
|
...toVersion === undefined ? {} : { toVersion }
|
|
@@ -23446,8 +23886,8 @@ async function run() {
|
|
|
23446
23886
|
if (checkSchema && command !== "graphql-schema")
|
|
23447
23887
|
throw new Error("--check is only supported by graphql-schema");
|
|
23448
23888
|
const defaults = resolveSupacloudConfig(loadedConfig, process.cwd());
|
|
23449
|
-
const resolvedRoot = rootDir ?
|
|
23450
|
-
const resolvedOut = outDir ?
|
|
23889
|
+
const resolvedRoot = rootDir ? resolve23(process.cwd(), rootDir) : defaults.rootDir;
|
|
23890
|
+
const resolvedOut = outDir ? resolve23(process.cwd(), outDir) : defaults.outDir;
|
|
23451
23891
|
const configured = compileOptionsFromConfig({
|
|
23452
23892
|
...loadedConfig,
|
|
23453
23893
|
root: resolvedRoot,
|
|
@@ -23466,7 +23906,7 @@ async function run() {
|
|
|
23466
23906
|
let delivery = loadedConfig.delivery;
|
|
23467
23907
|
if (deliveryPath !== undefined) {
|
|
23468
23908
|
try {
|
|
23469
|
-
delivery = JSON.parse(await readFile14(
|
|
23909
|
+
delivery = JSON.parse(await readFile14(resolve23(process.cwd(), deliveryPath), "utf8"));
|
|
23470
23910
|
} catch {
|
|
23471
23911
|
throw new DeliveryConfigurationError;
|
|
23472
23912
|
}
|
|
@@ -23524,9 +23964,24 @@ ${item.suggestion ?? ""}`).join(`
|
|
|
23524
23964
|
} else if (command === "fix") {
|
|
23525
23965
|
if (!query)
|
|
23526
23966
|
throw new Error("fix requires a JSON file containing one DiagnosticFix");
|
|
23527
|
-
const
|
|
23528
|
-
const
|
|
23967
|
+
const input = JSON.parse(await readFile14(resolve23(process.cwd(), query), "utf8"));
|
|
23968
|
+
const envelope = input && typeof input === "object" && "fix" in input;
|
|
23969
|
+
if (envelope && (typeof input.expectedSourceHash !== "string" || !/^[a-f0-9]{64}$/.test(input.expectedSourceHash))) {
|
|
23970
|
+
throw new Error("Editor fix requires the source hash from its validated preview");
|
|
23971
|
+
}
|
|
23972
|
+
const result = await applyDiagnosticFix(envelope ? input.fix : input, {
|
|
23973
|
+
rootDir: resolvedRoot,
|
|
23974
|
+
dryRun,
|
|
23975
|
+
...envelope ? { expectedSourceHash: input.expectedSourceHash } : {}
|
|
23976
|
+
});
|
|
23529
23977
|
console.log(JSON.stringify({ ok: true, ...result }, null, 2));
|
|
23978
|
+
} else if (command === "diagnostics") {
|
|
23979
|
+
const result = await checkProject(compileDefaults);
|
|
23980
|
+
console.log(JSON.stringify({
|
|
23981
|
+
report: createDiagnosticReport(result.diagnostics),
|
|
23982
|
+
diagnostics: toEditorDiagnostics(result.diagnostics),
|
|
23983
|
+
actions: await createEditorCodeActions(result.diagnostics, { rootDir: resolvedRoot })
|
|
23984
|
+
}, null, 2));
|
|
23530
23985
|
} else if (command === "compile") {
|
|
23531
23986
|
const result = await compileProject(compileDefaults);
|
|
23532
23987
|
const errors = result.diagnostics.filter((d) => d.severity === "error");
|
|
@@ -23650,7 +24105,7 @@ Source change detected; compiling...`);
|
|
|
23650
24105
|
process.exitCode = 1;
|
|
23651
24106
|
return;
|
|
23652
24107
|
}
|
|
23653
|
-
const file =
|
|
24108
|
+
const file = resolve23(rootDir ? resolve23(process.cwd(), rootDir) : process.cwd(), bindingsFile ?? "supacloud.environments.json");
|
|
23654
24109
|
try {
|
|
23655
24110
|
const document = await readEnvironmentBindingsFile(file);
|
|
23656
24111
|
const graph = await analyzeProject(resolvedRoot);
|
|
@@ -23685,7 +24140,7 @@ Source change detected; compiling...`);
|
|
|
23685
24140
|
try {
|
|
23686
24141
|
const result = await checkProject(compileDefaults);
|
|
23687
24142
|
if (executionEventsPath && executionRequestId) {
|
|
23688
|
-
const pack = createExecutionContextPack({ ...result.graph, diagnostics: result.diagnostics }, query, await readExecutionMetadata(
|
|
24143
|
+
const pack = createExecutionContextPack({ ...result.graph, diagnostics: result.diagnostics }, query, await readExecutionMetadata(resolve23(process.cwd(), executionEventsPath)), executionRequestId);
|
|
23689
24144
|
console.log(JSON.stringify(pack, null, 2));
|
|
23690
24145
|
return;
|
|
23691
24146
|
}
|