@loozo/angular-router 0.0.2 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +565 -80
- package/builders.json +2 -2
- package/fesm2022/loozo-angular-router-_core.mjs +332 -595
- package/fesm2022/loozo-angular-router-_core.mjs.map +1 -1
- package/fesm2022/loozo-angular-router-_generated.mjs +335 -627
- package/fesm2022/loozo-angular-router-_generated.mjs.map +1 -1
- package/fesm2022/loozo-angular-router.mjs +2 -19
- package/node/_core/src/path.d.ts +17 -0
- package/node/src/builders/application.js +19 -7
- package/node/src/builders/options.js +47 -61
- package/node/src/builders/serve.js +66 -16
- package/node/src/generation/anchor.d.ts +13 -0
- package/node/src/generation/anchor.js +92 -0
- package/node/src/generation/compiler.d.ts +6 -0
- package/node/src/generation/compiler.js +68 -0
- package/node/src/generation/emit.d.ts +3 -0
- package/node/src/generation/emit.js +26 -8
- package/node/src/generation/files.d.ts +26 -0
- package/node/src/generation/files.js +14 -3
- package/node/src/generation/filesystem.d.ts +7 -0
- package/node/src/generation/filesystem.js +261 -69
- package/node/src/generation/generate.d.ts +16 -0
- package/node/src/generation/generate.js +10 -4
- package/node/src/generation/index.d.ts +16 -0
- package/node/src/generation/index.js +15 -0
- package/node/src/generation/overlay.d.ts +3 -0
- package/node/src/generation/selectors.d.ts +2 -0
- package/node/src/generation/sources.d.ts +50 -0
- package/node/src/generation/sources.js +57 -12
- package/node/src/generation/validate.d.ts +12 -0
- package/node/src/generation/validate.js +53 -57
- package/package.json +7 -2
- package/schemas/application.json +764 -20
- package/schemas/serve.json +144 -30
- package/snippets/angular.json +29 -0
- package/snippets/generated/destinations.generated.ts +126 -0
- package/snippets/generated/routes.generated.ts +35 -0
- package/snippets/src/catalog.ts +20 -0
- package/snippets/src/docs-url.ts +8 -0
- package/snippets/src/index.html +11 -0
- package/snippets/src/main.ts +6 -0
- package/snippets/src/product-url.ts +8 -0
- package/snippets/src/routes/products/$productId/page.ts +20 -0
- package/snippets/src/routes/products/$productId/route.generated.ts +24 -0
- package/snippets/src/routes/products/$productId/route.ts +7 -0
- package/snippets/src/routes/reports/$reportId/page.ts +9 -0
- package/snippets/src/routes/reports/$reportId/route.generated.ts +24 -0
- package/snippets/src/routes/reports/$reportId/route.ts +6 -0
- package/snippets/src/routes/teams/$teamId/items/$id/inputs.ts +15 -0
- package/snippets/src/routes/teams/$teamId/items/$id/links.ts +13 -0
- package/snippets/src/routes/teams/$teamId/items/$id/page.ts +9 -0
- package/snippets/src/routes/teams/$teamId/items/$id/route.generated.ts +24 -0
- package/snippets/src/routes/teams/$teamId/items/$id/route.ts +18 -0
- package/snippets/src/routes/teams/$teamId/members/$memberId/page.ts +9 -0
- package/snippets/src/routes/teams/$teamId/members/$memberId/route.generated.ts +24 -0
- package/snippets/src/routes/teams/$teamId/members/$memberId/route.ts +7 -0
- package/snippets/src/routes/teams/$teamId/old-items/$id/route.ts +13 -0
- package/snippets/src/routes/teams/$teamId/route.generated.ts +24 -0
- package/snippets/src/routes/teams/$teamId/route.ts +6 -0
- package/snippets/src/shell.ts +9 -0
- package/snippets/src/slug.ts +14 -0
- package/snippets/tsconfig.app.json +14 -0
- package/snippets/tsconfig.routes.json +5 -0
- package/types/loozo-angular-router-_core.d.ts +262 -242
- package/types/loozo-angular-router-_generated.d.ts +155 -116
- package/types/loozo-angular-router.d.ts +2 -1
- package/.npmignore +0 -3
- package/fesm2022/loozo-angular-router.mjs.map +0 -1
|
@@ -41,7 +41,7 @@ export function properties(node, label) {
|
|
|
41
41
|
export function resolveModule(name, source, options, host = ts.sys) {
|
|
42
42
|
return ts.resolveModuleName(name, source, options, host).resolvedModule?.resolvedFileName;
|
|
43
43
|
}
|
|
44
|
-
function namedImport(source, name, imported) {
|
|
44
|
+
export function namedImport(source, name, imported) {
|
|
45
45
|
if (!ts.isIdentifier(name)) {
|
|
46
46
|
return undefined;
|
|
47
47
|
}
|
|
@@ -90,7 +90,7 @@ function moduleText(statement) {
|
|
|
90
90
|
}
|
|
91
91
|
return statement.moduleSpecifier.text;
|
|
92
92
|
}
|
|
93
|
-
async function genuineImport(statement, expected, options, host = ts.sys) {
|
|
93
|
+
export async function genuineImport(statement, expected, options, host = ts.sys) {
|
|
94
94
|
const actual = resolveModule(moduleText(statement), statement.getSourceFile().fileName, options, host);
|
|
95
95
|
if (!actual) {
|
|
96
96
|
return false;
|
|
@@ -167,12 +167,13 @@ const keys = new Set([
|
|
|
167
167
|
"canActivate",
|
|
168
168
|
"canActivateChild",
|
|
169
169
|
"canMatch",
|
|
170
|
+
"onInvalidParams",
|
|
170
171
|
"canDeactivate",
|
|
171
172
|
"providers",
|
|
172
173
|
"title",
|
|
173
174
|
"data",
|
|
174
175
|
]);
|
|
175
|
-
export async function discover(workspace, config, scans, out, auto, authored = new Map(), filesystem = auto !== undefined) {
|
|
176
|
+
export async function discover(workspace, config, scans, out, auto, authored = new Map(), filesystem = auto !== undefined, launch, compiler) {
|
|
176
177
|
if (scans.length !== 1) {
|
|
177
178
|
throw new Error("scanDirectories must contain exactly one directory");
|
|
178
179
|
}
|
|
@@ -223,7 +224,7 @@ export async function discover(workspace, config, scans, out, auto, authored = n
|
|
|
223
224
|
continue;
|
|
224
225
|
}
|
|
225
226
|
await auditPath(normalized);
|
|
226
|
-
if (contains(root, normalized)) {
|
|
227
|
+
if (launch || contains(root, normalized)) {
|
|
227
228
|
candidates.push(normalized);
|
|
228
229
|
}
|
|
229
230
|
}
|
|
@@ -233,14 +234,22 @@ export async function discover(workspace, config, scans, out, auto, authored = n
|
|
|
233
234
|
const manifest = JSON.parse(manifestBytes);
|
|
234
235
|
const factoryEntry = await realpath(resolve(packageRoot, manifest.exports["."].types));
|
|
235
236
|
const declarations = [];
|
|
237
|
+
const launchDeclarations = [];
|
|
238
|
+
const launchEntry = launch && (await realpath(launch.typeEntry));
|
|
236
239
|
const pages = new Map();
|
|
237
240
|
const deferredFactories = [];
|
|
238
241
|
const syntaxHost = overlayHost(parsed.options, authored);
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
242
|
+
let syntaxProgram;
|
|
243
|
+
if (compiler) {
|
|
244
|
+
syntaxProgram = compiler.createProgram(candidates, parsed.options, syntaxHost);
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
syntaxProgram = ts.createProgram({
|
|
248
|
+
rootNames: candidates,
|
|
249
|
+
options: parsed.options,
|
|
250
|
+
host: syntaxHost,
|
|
251
|
+
});
|
|
252
|
+
}
|
|
244
253
|
for (const file of candidates) {
|
|
245
254
|
const bytes = syntaxHost.readFile(file);
|
|
246
255
|
if (bytes === undefined) {
|
|
@@ -281,6 +290,33 @@ export async function discover(workspace, config, scans, out, auto, authored = n
|
|
|
281
290
|
fail(defaults[1], "Multiple competing default exports");
|
|
282
291
|
}
|
|
283
292
|
const node = defaults[0];
|
|
293
|
+
const inRoutes = contains(root, file);
|
|
294
|
+
if (launchEntry && ts.isExportAssignment(node) && ts.isCallExpression(node.expression)) {
|
|
295
|
+
const call = node.expression;
|
|
296
|
+
let kind;
|
|
297
|
+
let imported = namedImport(source, call.expression, "defineContext");
|
|
298
|
+
if (imported) {
|
|
299
|
+
kind = "defineContext";
|
|
300
|
+
}
|
|
301
|
+
else if (inRoutes) {
|
|
302
|
+
imported = namedImport(source, call.expression, "defineRpc");
|
|
303
|
+
if (imported) {
|
|
304
|
+
kind = "defineRpc";
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
if (kind && imported) {
|
|
308
|
+
if (!(await genuineImport(imported, launchEntry, parsed.options, syntaxHost))) {
|
|
309
|
+
fail(imported, `${kind} must resolve to Launch's concrete server entry`);
|
|
310
|
+
}
|
|
311
|
+
launchDeclarations.push({ file, kind, call });
|
|
312
|
+
continue;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
// Context is audited over all application roots; unrelated bootstrap defaults
|
|
316
|
+
// outside route scans are not route declarations.
|
|
317
|
+
if (!inRoutes) {
|
|
318
|
+
continue;
|
|
319
|
+
}
|
|
284
320
|
if (ts.isClassDeclaration(node)) {
|
|
285
321
|
await component(source, node, parsed.options);
|
|
286
322
|
if (pages.has(dirname(file))) {
|
|
@@ -319,6 +355,9 @@ export async function discover(workspace, config, scans, out, auto, authored = n
|
|
|
319
355
|
if (!auto && !ignored && needsParams !== config.has("params")) {
|
|
320
356
|
fail(configNode, "Local params must be present exactly when the path declares placeholders");
|
|
321
357
|
}
|
|
358
|
+
if (!auto && !ignored && !needsParams && config.has("onInvalidParams")) {
|
|
359
|
+
fail(configNode, "onInvalidParams requires local path placeholders");
|
|
360
|
+
}
|
|
322
361
|
const data = config.get("data");
|
|
323
362
|
if (data && ts.isObjectLiteralExpression(data)) {
|
|
324
363
|
for (const property of data.properties) {
|
|
@@ -363,7 +402,7 @@ export async function discover(workspace, config, scans, out, auto, authored = n
|
|
|
363
402
|
}
|
|
364
403
|
if (auto) {
|
|
365
404
|
const edits = await synchronize(roots[0], declarations, pages, parsed.options, auto, syntaxProgram.getTypeChecker());
|
|
366
|
-
return discover(workspace, config, scans, out, undefined, edits, true);
|
|
405
|
+
return discover(workspace, config, scans, out, undefined, edits, true, launch, compiler);
|
|
367
406
|
}
|
|
368
407
|
if (filesystem) {
|
|
369
408
|
for (let index = declarations.length - 1; index >= 0; index -= 1) {
|
|
@@ -435,8 +474,9 @@ export async function discover(workspace, config, scans, out, auto, authored = n
|
|
|
435
474
|
if (declaration.page ||
|
|
436
475
|
declaration.children.length ||
|
|
437
476
|
declaration.config.has("canActivate") ||
|
|
438
|
-
declaration.config.has("canMatch")
|
|
439
|
-
|
|
477
|
+
declaration.config.has("canMatch") ||
|
|
478
|
+
declaration.config.has("onInvalidParams")) {
|
|
479
|
+
fail(redirect, "Redirect cannot have a component, children, canActivate, canMatch or onInvalidParams");
|
|
440
480
|
}
|
|
441
481
|
declaration.retained = false;
|
|
442
482
|
}
|
|
@@ -519,6 +559,10 @@ export async function discover(workspace, config, scans, out, auto, authored = n
|
|
|
519
559
|
fail(declaration.source, "Factory facade is not a final retained generated location");
|
|
520
560
|
}
|
|
521
561
|
}
|
|
562
|
+
let launchSources;
|
|
563
|
+
if (launchEntry) {
|
|
564
|
+
launchSources = { typeEntry: launchEntry, declarations: launchDeclarations };
|
|
565
|
+
}
|
|
522
566
|
return {
|
|
523
567
|
configFile,
|
|
524
568
|
parsed,
|
|
@@ -530,5 +574,6 @@ export async function discover(workspace, config, scans, out, auto, authored = n
|
|
|
530
574
|
deferredFactories,
|
|
531
575
|
authored,
|
|
532
576
|
filesystem,
|
|
577
|
+
launch: launchSources,
|
|
533
578
|
};
|
|
534
579
|
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import ts from "typescript";
|
|
2
|
+
import { type Declaration, type Sources } from "./sources.js";
|
|
3
|
+
import type { CompilerState } from "./compiler.js";
|
|
4
|
+
import { AnchorState } from "./anchor.js";
|
|
5
|
+
export declare function authoredPath(declaration: Declaration): string;
|
|
6
|
+
export declare function selections(sources: Sources): Map<string, Declaration>;
|
|
7
|
+
export declare function createValidationProgram(sources: Sources, generated: ReadonlyMap<string, string>, stale: readonly string[], compiler?: CompilerState): {
|
|
8
|
+
program: ts.Program;
|
|
9
|
+
host: ts.CompilerHost;
|
|
10
|
+
obsolete: Set<string>;
|
|
11
|
+
};
|
|
12
|
+
export declare function validateRoutes(sources: Sources, generated: ReadonlyMap<string, string>, validation: ReturnType<typeof createValidationProgram>, canonical?: AnchorState): Promise<void>;
|
|
@@ -4,6 +4,7 @@ import { basename, dirname, resolve, sep } from "node:path";
|
|
|
4
4
|
import { auditPath, contains } from "./files.js";
|
|
5
5
|
import { fail, properties, resolveModule } from "./sources.js";
|
|
6
6
|
import { overlayHost } from "./overlay.js";
|
|
7
|
+
import { AnchorState } from "./anchor.js";
|
|
7
8
|
export function authoredPath(declaration) {
|
|
8
9
|
const parts = [];
|
|
9
10
|
for (const segment of declaration.effective) {
|
|
@@ -111,9 +112,13 @@ export function selections(sources) {
|
|
|
111
112
|
}
|
|
112
113
|
return result;
|
|
113
114
|
}
|
|
114
|
-
// Overlay only
|
|
115
|
-
export
|
|
116
|
-
const
|
|
115
|
+
// Overlay only final generated locations. Never evaluate application code.
|
|
116
|
+
export function createValidationProgram(sources, generated, stale, compiler) {
|
|
117
|
+
const options = { ...sources.parsed.options };
|
|
118
|
+
if (sources.launch) {
|
|
119
|
+
options.skipLibCheck = false;
|
|
120
|
+
}
|
|
121
|
+
const host = overlayHost(options, sources.authored);
|
|
117
122
|
const originalRead = host.readFile.bind(host);
|
|
118
123
|
const originalExists = host.fileExists.bind(host);
|
|
119
124
|
const originalDirectoryExists = host.directoryExists.bind(host);
|
|
@@ -141,14 +146,24 @@ export async function validateProgram(sources, generated, stale) {
|
|
|
141
146
|
}
|
|
142
147
|
return ts.createSourceFile(file, text, language, true);
|
|
143
148
|
};
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
149
|
+
const rootNames = [
|
|
150
|
+
...sources.parsed.fileNames.filter((file) => !obsolete.has(resolve(file))),
|
|
151
|
+
sources.factoryEntry,
|
|
152
|
+
];
|
|
153
|
+
if (sources.launch) {
|
|
154
|
+
rootNames.push(sources.launch.typeEntry, ...generated.keys());
|
|
155
|
+
}
|
|
156
|
+
let program;
|
|
157
|
+
if (compiler) {
|
|
158
|
+
program = compiler.createProgram(rootNames, options, host);
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
program = ts.createProgram({ rootNames, options, host });
|
|
162
|
+
}
|
|
163
|
+
return { program, host, obsolete };
|
|
164
|
+
}
|
|
165
|
+
export async function validateRoutes(sources, generated, validation, canonical) {
|
|
166
|
+
const { program, host, obsolete } = validation;
|
|
152
167
|
if (sources.filesystem) {
|
|
153
168
|
const diagnostics = [];
|
|
154
169
|
for (const declaration of sources.declarations) {
|
|
@@ -176,20 +191,8 @@ export async function validateProgram(sources, generated, stale) {
|
|
|
176
191
|
// Anchor the public export's declaration independently of application paths,
|
|
177
192
|
// which can also redirect imports inside the concrete public type entry.
|
|
178
193
|
const canonicalOptions = { ...sources.parsed.options, paths: undefined, baseUrl: undefined };
|
|
179
|
-
const
|
|
180
|
-
|
|
181
|
-
options: canonicalOptions,
|
|
182
|
-
host: ts.createCompilerHost(canonicalOptions, true),
|
|
183
|
-
});
|
|
184
|
-
const canonicalChecker = canonicalProgram.getTypeChecker();
|
|
185
|
-
const canonicalEntry = canonicalProgram.getSourceFile(sources.factoryEntry);
|
|
186
|
-
const canonicalModule = canonicalChecker.getSymbolAtLocation(canonicalEntry);
|
|
187
|
-
let canonicalFactory = canonicalChecker
|
|
188
|
-
.getExportsOfModule(canonicalModule)
|
|
189
|
-
.find((symbol) => symbol.name === "defineRoute");
|
|
190
|
-
if (canonicalFactory.flags & ts.SymbolFlags.Alias) {
|
|
191
|
-
canonicalFactory = canonicalChecker.getAliasedSymbol(canonicalFactory);
|
|
192
|
-
}
|
|
194
|
+
const anchor = await (canonical ?? new AnchorState()).anchor(sources.factoryEntry, ["defineRoute"], canonicalOptions);
|
|
195
|
+
const canonicalFactory = anchor.get("defineRoute");
|
|
193
196
|
// Tentative own-facade admission is not authority for its re-exported binding.
|
|
194
197
|
// Prove both the concrete module boundary and the final native alias identity.
|
|
195
198
|
for (const declaration of sources.declarations) {
|
|
@@ -215,17 +218,14 @@ export async function validateProgram(sources, generated, stale) {
|
|
|
215
218
|
}
|
|
216
219
|
if (!actualFactory?.declarations?.length ||
|
|
217
220
|
actualFactory !== concreteFactory ||
|
|
218
|
-
actualFactory.declarations.length !== canonicalFactory
|
|
221
|
+
actualFactory.declarations.length !== canonicalFactory?.length) {
|
|
219
222
|
fail(call, "Factory binding does not resolve to the concrete defineRoute export");
|
|
220
223
|
}
|
|
221
224
|
for (let index = 0; index < actualFactory.declarations.length; index += 1) {
|
|
222
225
|
const actual = actualFactory.declarations[index];
|
|
223
|
-
const canonical = canonicalFactory
|
|
224
|
-
const
|
|
225
|
-
|
|
226
|
-
realpath(canonical.getSourceFile().fileName),
|
|
227
|
-
]);
|
|
228
|
-
if (actualFile !== canonicalFile ||
|
|
226
|
+
const canonical = canonicalFactory[index];
|
|
227
|
+
const actualFile = await realpath(actual.getSourceFile().fileName);
|
|
228
|
+
if (actualFile !== canonical.file ||
|
|
229
229
|
actual.kind !== canonical.kind ||
|
|
230
230
|
actual.pos !== canonical.pos ||
|
|
231
231
|
actual.end !== canonical.end) {
|
|
@@ -237,6 +237,10 @@ export async function validateProgram(sources, generated, stale) {
|
|
|
237
237
|
resolve(sources.output, "routes.generated.ts"),
|
|
238
238
|
resolve(sources.output, "destinations.generated.ts"),
|
|
239
239
|
]);
|
|
240
|
+
if (sources.launch) {
|
|
241
|
+
globalTargets.add(resolve(sources.output, "context.generated.d.ts"));
|
|
242
|
+
globalTargets.add(resolve(sources.output, "server.generated.ts"));
|
|
243
|
+
}
|
|
240
244
|
for (const source of program.getSourceFiles()) {
|
|
241
245
|
if (source.fileName.includes("/node_modules/") ||
|
|
242
246
|
program.isSourceFileFromExternalLibrary(source) ||
|
|
@@ -292,7 +296,8 @@ export async function validateProgram(sources, generated, stale) {
|
|
|
292
296
|
candidates.add(resolve(file));
|
|
293
297
|
}
|
|
294
298
|
for (const candidate of candidates) {
|
|
295
|
-
const facadeTarget = basename(candidate) === "route.generated.ts"
|
|
299
|
+
const facadeTarget = (basename(candidate) === "route.generated.ts" ||
|
|
300
|
+
(sources.launch && basename(candidate) === "rpc.generated.ts")) &&
|
|
296
301
|
!candidate.split(sep).includes("node_modules") &&
|
|
297
302
|
sources.roots.some((root) => contains(root, candidate));
|
|
298
303
|
if ((facadeTarget || globalTargets.has(candidate)) && !generated.has(candidate)) {
|
|
@@ -370,7 +375,7 @@ export async function validateProgram(sources, generated, stale) {
|
|
|
370
375
|
fail(redirect, `Incompatible logical redirect params: ${sourceKey} -> ${targetKey.name}`);
|
|
371
376
|
}
|
|
372
377
|
}
|
|
373
|
-
|
|
378
|
+
// Every search key is optional when writing a URL, so only replacement values need checking.
|
|
374
379
|
const targetSearch = typeWitness(declaration.target, "__search");
|
|
375
380
|
const search = fields.get("search");
|
|
376
381
|
const fragment = fields.get("fragment");
|
|
@@ -415,22 +420,6 @@ export async function validateProgram(sources, generated, stale) {
|
|
|
415
420
|
fail(value, "Redirect replacement fragment is not declared by its destination");
|
|
416
421
|
}
|
|
417
422
|
}
|
|
418
|
-
const required = targetSearch
|
|
419
|
-
.getProperties()
|
|
420
|
-
.filter((property) => !(property.flags & ts.SymbolFlags.Optional));
|
|
421
|
-
if (mode.text === "drop" && required.length > 0) {
|
|
422
|
-
fail(search, `Cannot drop required destination search: ${required.map((property) => property.name).join(", ")}`);
|
|
423
|
-
}
|
|
424
|
-
if (mode.text === "preserve") {
|
|
425
|
-
for (const key of required) {
|
|
426
|
-
const sourceKey = sourceSearch.getProperty(key.name);
|
|
427
|
-
if (!sourceKey ||
|
|
428
|
-
sourceKey.flags & ts.SymbolFlags.Optional ||
|
|
429
|
-
!checker.isTypeAssignableTo(checker.getTypeOfSymbolAtLocation(sourceKey, search), checker.getTypeOfSymbolAtLocation(key, search))) {
|
|
430
|
-
fail(search, `Source cannot guarantee required destination search: ${key.name}`);
|
|
431
|
-
}
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
423
|
if (mode.text === "replace") {
|
|
435
424
|
const values = policy.get("values");
|
|
436
425
|
if (!values) {
|
|
@@ -439,15 +428,22 @@ export async function validateProgram(sources, generated, stale) {
|
|
|
439
428
|
const type = checker.getTypeAtLocation(values);
|
|
440
429
|
for (const key of type.getProperties()) {
|
|
441
430
|
const targetKey = targetSearch.getProperty(key.name);
|
|
442
|
-
if (!targetKey
|
|
443
|
-
!checker.isTypeAssignableTo(checker.getTypeOfSymbolAtLocation(key, values), checker.getTypeOfSymbolAtLocation(targetKey, values))) {
|
|
431
|
+
if (!targetKey) {
|
|
444
432
|
fail(values, `Unknown/incompatible redirect replacement key: ${key.name}`);
|
|
445
433
|
}
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
const supplied =
|
|
449
|
-
|
|
450
|
-
|
|
434
|
+
// Replacements use the write type, `Value | undefined`: undefined omits the key, even
|
|
435
|
+
// one that reads a fallback, so only the other constituents must be codec values.
|
|
436
|
+
const supplied = checker.getTypeOfSymbolAtLocation(key, values);
|
|
437
|
+
const target = checker.getTypeOfSymbolAtLocation(targetKey, values);
|
|
438
|
+
let constituents = [supplied];
|
|
439
|
+
if (supplied.isUnion()) {
|
|
440
|
+
constituents = supplied.types;
|
|
441
|
+
}
|
|
442
|
+
for (const constituent of constituents) {
|
|
443
|
+
if (!(constituent.flags & ts.TypeFlags.Undefined) &&
|
|
444
|
+
!checker.isTypeAssignableTo(constituent, target)) {
|
|
445
|
+
fail(values, `Unknown/incompatible redirect replacement key: ${key.name}`);
|
|
446
|
+
}
|
|
451
447
|
}
|
|
452
448
|
}
|
|
453
449
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@loozo/angular-router",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"description": "Typed route declarations, URLs, navigation and route handles for Angular's native router.",
|
|
5
5
|
"bugs": "https://github.com/theo-matzavinos/loozo/issues",
|
|
6
6
|
"license": "MIT",
|
|
@@ -24,6 +24,10 @@
|
|
|
24
24
|
"types": "./types/loozo-angular-router-_generated.d.ts",
|
|
25
25
|
"default": "./fesm2022/loozo-angular-router-_generated.mjs"
|
|
26
26
|
},
|
|
27
|
+
"./_generation": {
|
|
28
|
+
"types": "./node/src/generation/index.d.ts",
|
|
29
|
+
"node": "./node/src/generation/index.js"
|
|
30
|
+
},
|
|
27
31
|
"./package.json": {
|
|
28
32
|
"default": "./package.json"
|
|
29
33
|
}
|
|
@@ -33,13 +37,14 @@
|
|
|
33
37
|
"registry": "https://registry.npmjs.org/"
|
|
34
38
|
},
|
|
35
39
|
"dependencies": {
|
|
36
|
-
"@angular-devkit/architect": "0.
|
|
40
|
+
"@angular-devkit/architect": "0.2202.2",
|
|
37
41
|
"chokidar": "5.0.0",
|
|
38
42
|
"rxjs": "7.8.2",
|
|
39
43
|
"tslib": "2.8.1",
|
|
40
44
|
"typescript": "6.0.3"
|
|
41
45
|
},
|
|
42
46
|
"peerDependencies": {
|
|
47
|
+
"@angular/build": "~22.2.0",
|
|
43
48
|
"@angular/core": "^22.0.0",
|
|
44
49
|
"@angular/router": "^22.0.0"
|
|
45
50
|
},
|