@pulse-compute/wasm-compiler 0.0.0 → 1.0.0-beta.2
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 +53 -1
- package/bin/provider-proof-composition.js +34 -0
- package/bin/pulsewasm-extract.js +20 -0
- package/package.json +60 -5
- package/src/artifacts-dir.js +13 -0
- package/src/ast-json.js +52 -0
- package/src/build-manifest.js +354 -0
- package/src/canonical-api-compiler.js +269 -0
- package/src/canonical-native-compiler.js +411 -0
- package/src/canonical-native-plan.js +1478 -0
- package/src/canonical-project-compiler.js +1224 -0
- package/src/canonical-router-compiler.js +25 -0
- package/src/cli-intents.js +1235 -0
- package/src/cli.js +1927 -0
- package/src/codegen/assemblyscript-compile.js +3 -0
- package/src/codegen/assemblyscript-core.js +3 -0
- package/src/codegen/assemblyscript-shape.js +3 -0
- package/src/codegen/assemblyscript-wasm-smoke.js +3 -0
- package/src/codegen/backend-capabilities.js +3 -0
- package/src/codegen/channel-broadcaster.js +3 -0
- package/src/codegen/compiled-handlers.js +3 -0
- package/src/codegen/compiled-wasm-runtime.js +3 -0
- package/src/codegen/config-references.js +248 -0
- package/src/codegen/dispatch-ts.js +145 -0
- package/src/codegen/effect-composition.js +331 -0
- package/src/codegen/effect-runtime.js +418 -0
- package/src/codegen/execution-harness-ts.js +467 -0
- package/src/codegen/handler-bindings-ts.js +298 -0
- package/src/codegen/handler-library-contracts.js +3 -0
- package/src/codegen/host-capabilities.js +3 -0
- package/src/codegen/host-runtime-kernel.js +3 -0
- package/src/codegen/integrated-compiled-app.js +3 -0
- package/src/codegen/json-body.js +3 -0
- package/src/codegen/library-sidecars.js +3 -0
- package/src/codegen/local-harness-ts.js +453 -0
- package/src/codegen/pulse-wrapper.js +217 -0
- package/src/codegen/request-result-headers.js +3 -0
- package/src/codegen/schema-json-compile.js +3 -0
- package/src/codegen/schema-json-sidecar-v2.js +3 -0
- package/src/codegen/schema-json-sidecar.js +3 -0
- package/src/codegen/streaming-passthrough.js +3 -0
- package/src/codegen/wasm-host-abi.js +3 -0
- package/src/codegen/wasm-host-bridge.js +3 -0
- package/src/compiled-wasm-host-runtime-kv.js +3 -0
- package/src/config-resolver.js +813 -0
- package/src/crypto-requirement-planner.js +89 -0
- package/src/definitions/config-schema.js +14 -0
- package/src/definitions/handler-roles.js +14 -0
- package/src/definitions/path-grammar.js +14 -0
- package/src/definitions/router-api.js +14 -0
- package/src/diagnostics/codes.js +14 -0
- package/src/diagnostics/reporter.js +14 -0
- package/src/diagnostics.js +14 -0
- package/src/dispatch-table.js +400 -0
- package/src/events/event-emit.js +265 -0
- package/src/events/event-topology.js +127 -0
- package/src/execution-plan.js +463 -0
- package/src/extractor.js +2816 -0
- package/src/handler-eval.js +1154 -0
- package/src/handler-table.js +326 -0
- package/src/index.js +19 -0
- package/src/javascript-application-plan.js +181 -0
- package/src/kv-provider.js +3 -0
- package/src/path-table.js +60 -0
- package/src/path.js +14 -0
- package/src/patterns/config-define.js +30 -0
- package/src/patterns/dependency-call.js +18 -0
- package/src/patterns/env-lookup.js +13 -0
- package/src/patterns/handler-reference.js +29 -0
- package/src/patterns/path-literal.js +35 -0
- package/src/patterns/result.js +15 -0
- package/src/patterns/router-chain-call.js +50 -0
- package/src/patterns/router-construction.js +19 -0
- package/src/project/package-reachability.js +782 -0
- package/src/project/reachable-graph-builder.js +992 -0
- package/src/project/reachable-graph-contract.js +54 -0
- package/src/project/reachable-graph-implementation.js +36 -0
- package/src/project/router-module-linker.js +710 -0
- package/src/project-config-compiler.js +222 -0
- package/src/project-target-support.js +500 -0
- package/src/provider-toolchain.js +299 -0
- package/src/spine/async-surface-normalizer.js +328 -0
- package/src/spine/canonical-handler-ir.js +336 -0
- package/src/spine/canonical-native-module.js +87 -0
- package/src/spine/canonical-native-plan.js +89 -0
- package/src/spine/canonical-project.js +76 -0
- package/src/spine/canonical-router.js +155 -0
- package/src/spine/canonical-source.js +165 -0
- package/src/spine/diagnostic-authority.js +290 -0
- package/src/spine/equivalence.js +262 -0
- package/src/spine/guest-unit-stage.js +87 -0
- package/src/spine/handler-ir-emitter.js +455 -0
- package/src/spine/handler-ir-managed.js +1598 -0
- package/src/spine/handler-ir.js +797 -0
- package/src/spine/handler-surface-authority.js +588 -0
- package/src/spine/package-operation-seam.js +1015 -0
- package/src/spine/pipeline.js +202 -0
- package/src/spine/plain-handler-frontend.js +545 -0
- package/src/spine/provider-requirement-authority.js +208 -0
- package/src/spine/router-control-contract.js +17 -0
- package/src/spine/router-handler-frontend.js +514 -0
- package/src/spine/router-handler-ir.js +372 -0
- package/src/spine/router-topology-frontend.js +638 -0
- package/src/stable-id.js +14 -0
|
@@ -0,0 +1,710 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const ts = require('typescript');
|
|
4
|
+
const {
|
|
5
|
+
buildRouterIR
|
|
6
|
+
} = require('../extractor.js');
|
|
7
|
+
const {
|
|
8
|
+
projectGraphContext,
|
|
9
|
+
finalizeReachableProjectGraph
|
|
10
|
+
} = require('./reachable-graph-builder.js');
|
|
11
|
+
|
|
12
|
+
const ROUTER_MODULE_LINKER_VERSION = 'pulse.router-module-linker.v1';
|
|
13
|
+
|
|
14
|
+
class RouterModuleLinkError extends Error {
|
|
15
|
+
constructor(message, diagnostics = [], detail = {}) {
|
|
16
|
+
super(message);
|
|
17
|
+
this.name = 'RouterModuleLinkError';
|
|
18
|
+
this.code = 'PULSE_PROJECT_MODULE_LINK_FAILED';
|
|
19
|
+
this.diagnostics = Object.freeze([...diagnostics]);
|
|
20
|
+
this.detail = Object.freeze({ ...detail });
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function sourceLocationFromLoc(loc) {
|
|
25
|
+
const start = loc && (loc.start || loc);
|
|
26
|
+
return Object.freeze({
|
|
27
|
+
file: String(loc && loc.file || '<unknown>').replace(/\\/g, '/'),
|
|
28
|
+
line: Number(start && start.line || 1),
|
|
29
|
+
column: Number(start && start.column || 1)
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function referenceSource(module, loc) {
|
|
34
|
+
const source = sourceLocationFromLoc(loc);
|
|
35
|
+
return Object.freeze({ file: module.path, line: source.line, column: source.column });
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function declarationSource(module, declaration) {
|
|
39
|
+
const loc = declaration && (declaration.functionLoc || declaration.loc || declaration.initLoc);
|
|
40
|
+
return loc ? referenceSource(module, loc) : Object.freeze({ file: module.path, line: 1, column: 1 });
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function graphDiagnostic(code, message, loc, detail = {}) {
|
|
44
|
+
const source = sourceLocationFromLoc(loc);
|
|
45
|
+
return Object.freeze({
|
|
46
|
+
code,
|
|
47
|
+
kind: 'RouterModuleLinkDiagnostic',
|
|
48
|
+
severity: 'error',
|
|
49
|
+
message,
|
|
50
|
+
file: source.file,
|
|
51
|
+
position: Object.freeze({ line: source.line, column: source.column }),
|
|
52
|
+
detail: Object.freeze({ ...detail })
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function functionForNamedHandler(sourceFile, name) {
|
|
57
|
+
for (const statement of sourceFile.statements) {
|
|
58
|
+
if (ts.isFunctionDeclaration(statement) && statement.name && statement.name.text === name) return statement;
|
|
59
|
+
if (!ts.isVariableStatement(statement)) continue;
|
|
60
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
61
|
+
if (!ts.isIdentifier(declaration.name) || declaration.name.text !== name || !declaration.initializer) continue;
|
|
62
|
+
let value = declaration.initializer;
|
|
63
|
+
while (value && (ts.isParenthesizedExpression(value) || ts.isAsExpression(value) || ts.isSatisfiesExpression?.(value) || ts.isNonNullExpression(value) || ts.isTypeAssertionExpression(value))) value = value.expression;
|
|
64
|
+
if (value && (ts.isArrowFunction(value) || ts.isFunctionExpression(value))) return value;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function anonymousDefaultHandler(module) {
|
|
71
|
+
const sourceFile = module.sourceFile;
|
|
72
|
+
for (const statement of sourceFile.statements) {
|
|
73
|
+
if (ts.isFunctionDeclaration(statement)
|
|
74
|
+
&& !statement.name
|
|
75
|
+
&& statement.modifiers
|
|
76
|
+
&& statement.modifiers.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword)
|
|
77
|
+
&& statement.modifiers.some((modifier) => modifier.kind === ts.SyntaxKind.DefaultKeyword)) {
|
|
78
|
+
const start = sourceFile.getLineAndCharacterOfPosition(statement.getStart(sourceFile));
|
|
79
|
+
return Object.freeze({
|
|
80
|
+
kind: 'handler',
|
|
81
|
+
module,
|
|
82
|
+
localName: '<default-handler>',
|
|
83
|
+
exportName: 'default',
|
|
84
|
+
declaration: Object.freeze({
|
|
85
|
+
anonymous: true,
|
|
86
|
+
node: statement,
|
|
87
|
+
loc: Object.freeze({ file: module.path, start: Object.freeze({ line: start.line + 1, column: start.character + 1 }) })
|
|
88
|
+
})
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
if (ts.isExportAssignment(statement) && !statement.isExportEquals) {
|
|
92
|
+
const unwrapped = wrappersForExpression(statement.expression);
|
|
93
|
+
if (!unwrapped.expression || (!ts.isArrowFunction(unwrapped.expression) && !ts.isFunctionExpression(unwrapped.expression))) continue;
|
|
94
|
+
const start = sourceFile.getLineAndCharacterOfPosition(statement.getStart(sourceFile));
|
|
95
|
+
return Object.freeze({
|
|
96
|
+
kind: 'handler',
|
|
97
|
+
module,
|
|
98
|
+
localName: '<default-handler>',
|
|
99
|
+
exportName: 'default',
|
|
100
|
+
declaration: Object.freeze({
|
|
101
|
+
anonymous: true,
|
|
102
|
+
node: unwrapped.expression,
|
|
103
|
+
wrappers: unwrapped.wrappers,
|
|
104
|
+
loc: Object.freeze({ file: module.path, start: Object.freeze({ line: start.line + 1, column: start.character + 1 }) })
|
|
105
|
+
})
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return undefined;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function wrappersForExpression(node) {
|
|
113
|
+
const wrappers = [];
|
|
114
|
+
let current = node;
|
|
115
|
+
while (current) {
|
|
116
|
+
if (ts.isParenthesizedExpression(current)) wrappers.push('parentheses');
|
|
117
|
+
else if (ts.isAsExpression(current)) wrappers.push('as-cast');
|
|
118
|
+
else if (ts.isTypeAssertionExpression(current)) wrappers.push('type-assertion');
|
|
119
|
+
else if (ts.isSatisfiesExpression?.(current)) wrappers.push('satisfies');
|
|
120
|
+
else if (ts.isNonNullExpression(current)) wrappers.push('non-null');
|
|
121
|
+
else break;
|
|
122
|
+
current = current.expression;
|
|
123
|
+
}
|
|
124
|
+
return Object.freeze({ expression: current, wrappers: Object.freeze(wrappers) });
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function importResolutionForLocal(context, module, localName) {
|
|
128
|
+
for (const relation of module.imports) {
|
|
129
|
+
const binding = relation.bindings.find((entry) => entry.localName === localName);
|
|
130
|
+
if (!binding) continue;
|
|
131
|
+
const resolution = module.resolutions.find((entry) => entry.relation === relation);
|
|
132
|
+
return resolution ? Object.freeze({ relation, binding, resolution }) : undefined;
|
|
133
|
+
}
|
|
134
|
+
return undefined;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function localExportNames(module, localName) {
|
|
138
|
+
const names = [];
|
|
139
|
+
for (const [exportName, target] of module.localExports) if (target === localName) names.push(exportName);
|
|
140
|
+
return Object.freeze(names.sort());
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function directAuthoringImports(sourceFile) {
|
|
144
|
+
const imports = new Set();
|
|
145
|
+
for (const statement of sourceFile.statements) {
|
|
146
|
+
if (!ts.isImportDeclaration(statement) || !ts.isStringLiteral(statement.moduleSpecifier)) continue;
|
|
147
|
+
const expected = statement.moduleSpecifier.text === '@pulse-compute/runtime' ? 'Router'
|
|
148
|
+
: statement.moduleSpecifier.text === '@pulse-compute/pulse' ? 'Pulse'
|
|
149
|
+
: undefined;
|
|
150
|
+
if (!expected) continue;
|
|
151
|
+
const bindings = statement.importClause && statement.importClause.namedBindings;
|
|
152
|
+
if (!bindings || !ts.isNamedImports(bindings)) continue;
|
|
153
|
+
for (const element of bindings.elements) {
|
|
154
|
+
if (element.isTypeOnly || element.propertyName || element.name.text !== expected) continue;
|
|
155
|
+
imports.add(expected);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
return imports;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function applicationConstructions(sourceFile) {
|
|
162
|
+
const out = [];
|
|
163
|
+
for (const statement of sourceFile.statements) {
|
|
164
|
+
if (!ts.isVariableStatement(statement)) continue;
|
|
165
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
166
|
+
if (!ts.isIdentifier(declaration.name) || !declaration.initializer) continue;
|
|
167
|
+
const unwrapped = wrappersForExpression(declaration.initializer);
|
|
168
|
+
const value = unwrapped.expression;
|
|
169
|
+
if (!value || !ts.isNewExpression(value) || !ts.isIdentifier(value.expression) || !['Router', 'Pulse'].includes(value.expression.text)) continue;
|
|
170
|
+
out.push(Object.freeze({
|
|
171
|
+
localName: declaration.name.text,
|
|
172
|
+
className: value.expression.text,
|
|
173
|
+
node: value,
|
|
174
|
+
wrappers: unwrapped.wrappers
|
|
175
|
+
}));
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
return Object.freeze(out);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function sourceModuleForKey(context, key) {
|
|
182
|
+
return context.projectModules.get(key);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function buildModuleIndexes(context) {
|
|
186
|
+
const indexes = new Map();
|
|
187
|
+
const diagnostics = [];
|
|
188
|
+
for (const module of [...context.projectModules.values()].filter((entry) => entry.runtime).sort((a, b) => a.path.localeCompare(b.path))) {
|
|
189
|
+
const ir = buildRouterIR(module.sourceFile, { cwd: '.' });
|
|
190
|
+
diagnostics.push(...(ir.diagnostics || []));
|
|
191
|
+
const routers = new Map((ir.routerIR || []).map((entry) => [entry.name, entry]));
|
|
192
|
+
const handlers = new Map((ir.handlerIR || []).map((entry) => [entry.name, entry]));
|
|
193
|
+
const imports = directAuthoringImports(module.sourceFile);
|
|
194
|
+
const constructions = applicationConstructions(module.sourceFile);
|
|
195
|
+
indexes.set(module.path, Object.freeze({ module, ir, routers, handlers, imports, constructions }));
|
|
196
|
+
}
|
|
197
|
+
return Object.freeze({ indexes, diagnostics });
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function resolveExport(context, indexes, module, exportName, diagnostics, stack = [], consumedImports) {
|
|
201
|
+
const key = `${module.path}:${exportName}`;
|
|
202
|
+
if (stack.includes(key)) {
|
|
203
|
+
diagnostics.push(graphDiagnostic('PULSE_PROJECT_EXPORT_CYCLE_UNSUPPORTED', `Export resolution cycle detected for ${exportName} in ${module.path}.`, { file: module.path, start: { line: 1, column: 1 } }, { stack: [...stack, key] }));
|
|
204
|
+
return undefined;
|
|
205
|
+
}
|
|
206
|
+
const nextStack = [...stack, key];
|
|
207
|
+
if (module.localExports.has(exportName)) {
|
|
208
|
+
return resolveLocal(context, indexes, module, module.localExports.get(exportName), diagnostics, nextStack, exportName, consumedImports);
|
|
209
|
+
}
|
|
210
|
+
if (exportName === 'default') {
|
|
211
|
+
const anonymous = anonymousDefaultHandler(module);
|
|
212
|
+
if (anonymous) return anonymous;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const candidates = [];
|
|
216
|
+
for (const relation of module.reExports) {
|
|
217
|
+
const resolution = module.resolutions.find((entry) => entry.relation === relation);
|
|
218
|
+
const target = resolution && sourceModuleForKey(context, resolution.targetKey);
|
|
219
|
+
if (!target) continue;
|
|
220
|
+
if (relation.star && exportName !== 'default') {
|
|
221
|
+
const resolved = resolveExport(context, indexes, target, exportName, diagnostics, nextStack, consumedImports);
|
|
222
|
+
if (resolved) candidates.push(resolved);
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
for (const mapping of relation.mappings) {
|
|
226
|
+
if (mapping.exportName !== exportName) continue;
|
|
227
|
+
const resolved = resolveExport(context, indexes, target, mapping.localName, diagnostics, nextStack, consumedImports);
|
|
228
|
+
if (resolved) candidates.push(resolved);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
const unique = Array.from(new Map(candidates.map((entry) => [`${entry.module.path}:${entry.localName}:${entry.kind}`, entry])).values());
|
|
232
|
+
if (unique.length > 1) {
|
|
233
|
+
diagnostics.push(graphDiagnostic('PULSE_PROJECT_EXPORT_AMBIGUOUS', `Export ${exportName} in ${module.path} resolves to more than one project symbol.`, { file: module.path, start: { line: 1, column: 1 } }, { exportName, candidates: unique.map((entry) => `${entry.module.path}#${entry.localName}`) }));
|
|
234
|
+
return undefined;
|
|
235
|
+
}
|
|
236
|
+
return unique[0];
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function resolveLocal(context, indexes, module, localName, diagnostics, stack = [], requestedExportName, consumedImports) {
|
|
240
|
+
const index = indexes.get(module.path);
|
|
241
|
+
if (!index) return undefined;
|
|
242
|
+
if (index.routers.has(localName)) return Object.freeze({
|
|
243
|
+
kind: 'router',
|
|
244
|
+
module,
|
|
245
|
+
localName,
|
|
246
|
+
exportName: requestedExportName || localExportNames(module, localName)[0] || null,
|
|
247
|
+
declaration: index.routers.get(localName)
|
|
248
|
+
});
|
|
249
|
+
if (index.handlers.has(localName)) return Object.freeze({
|
|
250
|
+
kind: 'handler',
|
|
251
|
+
module,
|
|
252
|
+
localName,
|
|
253
|
+
exportName: requestedExportName || localExportNames(module, localName)[0] || null,
|
|
254
|
+
declaration: index.handlers.get(localName)
|
|
255
|
+
});
|
|
256
|
+
const imported = importResolutionForLocal(context, module, localName);
|
|
257
|
+
if (!imported) return undefined;
|
|
258
|
+
if (consumedImports) consumedImports.add(`${module.path}\u0000${localName}`);
|
|
259
|
+
const target = sourceModuleForKey(context, imported.resolution.targetKey);
|
|
260
|
+
if (!target) return Object.freeze({ kind: 'package', moduleKey: imported.resolution.targetKey, localName, importedName: imported.binding.importedName });
|
|
261
|
+
if (imported.binding.importedName === '*') {
|
|
262
|
+
diagnostics.push(graphDiagnostic('PULSE_PROJECT_NAMESPACE_HANDLER_REFERENCE_UNSUPPORTED', `Namespace import ${localName} cannot be used as a direct handler or Router reference.`, imported.relation.source, { localName, specifier: imported.relation.specifier }));
|
|
263
|
+
return undefined;
|
|
264
|
+
}
|
|
265
|
+
return resolveExport(context, indexes, target, imported.binding.importedName, diagnostics, stack, consumedImports);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function validateRuntimeProjectImports(context, consumedImports, diagnostics) {
|
|
269
|
+
for (const module of [...context.projectModules.values()].filter((entry) => entry.runtime).sort((a, b) => a.path.localeCompare(b.path))) {
|
|
270
|
+
for (const relation of module.imports) {
|
|
271
|
+
if (relation.kind !== 'runtime-import') continue;
|
|
272
|
+
const resolution = module.resolutions.find((entry) => entry.relation === relation);
|
|
273
|
+
if (!resolution || !sourceModuleForKey(context, resolution.targetKey)) continue;
|
|
274
|
+
if (relation.bindings.length === 0) {
|
|
275
|
+
diagnostics.push(graphDiagnostic(
|
|
276
|
+
'PULSE_PROJECT_RUNTIME_SIDE_EFFECT_IMPORT_UNSUPPORTED',
|
|
277
|
+
`Project runtime side-effect import ${JSON.stringify(relation.specifier)} is not supported by the first multi-module compiler implementation.`,
|
|
278
|
+
relation.source,
|
|
279
|
+
{ specifier: relation.specifier, automaticFallback: false }
|
|
280
|
+
));
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
for (const binding of relation.bindings) {
|
|
284
|
+
const key = `${module.path}\u0000${binding.localName}`;
|
|
285
|
+
if (consumedImports.has(key)) continue;
|
|
286
|
+
diagnostics.push(graphDiagnostic(
|
|
287
|
+
'PULSE_PROJECT_RUNTIME_VALUE_IMPORT_UNSUPPORTED',
|
|
288
|
+
`Project runtime import ${binding.localName} from ${JSON.stringify(relation.specifier)} is not a Router, middleware, route handler, event handler, error handler, mounted Router, or resolved application root.`,
|
|
289
|
+
relation.source,
|
|
290
|
+
{
|
|
291
|
+
specifier: relation.specifier,
|
|
292
|
+
localName: binding.localName,
|
|
293
|
+
importedName: binding.importedName,
|
|
294
|
+
supportedRoles: Object.freeze(['application-root', 'router', 'middleware', 'route', 'event', 'error-middleware']),
|
|
295
|
+
automaticFallback: false
|
|
296
|
+
}
|
|
297
|
+
));
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
function moduleIdsByPath(graphBuild) {
|
|
304
|
+
return new Map(graphBuild.graph.modules.filter((module) => module.kind === 'project').map((module) => [module.path, module.id]));
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function globalSymbol(moduleId, kind, localName) {
|
|
308
|
+
return `${moduleId}::${kind}::${localName}`;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function retainedDeclarations(context, diagnostics) {
|
|
312
|
+
const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed });
|
|
313
|
+
const declarations = [];
|
|
314
|
+
const typeNames = new Map();
|
|
315
|
+
const imports = new Map();
|
|
316
|
+
|
|
317
|
+
for (const module of [...context.projectModules.values()].filter((entry) => entry.runtime).sort((a, b) => a.path.localeCompare(b.path))) {
|
|
318
|
+
for (const statement of module.sourceFile.statements) {
|
|
319
|
+
if (ts.isInterfaceDeclaration(statement) || ts.isTypeAliasDeclaration(statement)) {
|
|
320
|
+
const name = statement.name && statement.name.text;
|
|
321
|
+
const text = printer.printNode(ts.EmitHint.Unspecified, statement, module.sourceFile);
|
|
322
|
+
if (name && typeNames.has(name) && typeNames.get(name) !== text) {
|
|
323
|
+
diagnostics.push(graphDiagnostic('PULSE_PROJECT_TYPE_NAME_COLLISION', `Type declaration ${name} is defined differently in more than one reachable module.`, { file: module.path, start: { line: 1, column: 1 } }, { name }));
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
if (name) typeNames.set(name, text);
|
|
327
|
+
declarations.push(text);
|
|
328
|
+
continue;
|
|
329
|
+
}
|
|
330
|
+
if (!ts.isImportDeclaration(statement) || !ts.isStringLiteral(statement.moduleSpecifier)) continue;
|
|
331
|
+
const relation = module.imports.find((entry) => entry.statement === statement);
|
|
332
|
+
const resolution = relation && module.resolutions.find((entry) => entry.relation === relation);
|
|
333
|
+
if (resolution && sourceModuleForKey(context, resolution.targetKey)) continue;
|
|
334
|
+
const specifier = statement.moduleSpecifier.text;
|
|
335
|
+
if (!['@pulse-compute/runtime', '@pulse-compute/pulse'].includes(specifier)) continue;
|
|
336
|
+
const clause = statement.importClause;
|
|
337
|
+
if (!clause || !clause.namedBindings || !ts.isNamedImports(clause.namedBindings)) continue;
|
|
338
|
+
const removed = specifier === '@pulse-compute/runtime' ? 'Router' : 'Pulse';
|
|
339
|
+
const kept = clause.namedBindings.elements.filter((element) => (element.propertyName ? element.propertyName.text : element.name.text) !== removed);
|
|
340
|
+
if (kept.length === 0 && !clause.name) continue;
|
|
341
|
+
const nextClause = ts.factory.updateImportClause(
|
|
342
|
+
clause,
|
|
343
|
+
clause.isTypeOnly || kept.every((element) => element.isTypeOnly),
|
|
344
|
+
clause.name,
|
|
345
|
+
ts.factory.updateNamedImports(clause.namedBindings, kept.map((element) => ts.factory.updateImportSpecifier(element, false, element.propertyName, element.name)))
|
|
346
|
+
);
|
|
347
|
+
const text = printer.printNode(ts.EmitHint.Unspecified, ts.factory.updateImportDeclaration(statement, statement.modifiers, nextClause, statement.moduleSpecifier, statement.attributes), module.sourceFile);
|
|
348
|
+
imports.set(text, text);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
return [...imports.values(), ...Array.from(new Set(declarations))].join('\n');
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function linkedAuthoringSource(context) {
|
|
355
|
+
return [...context.projectModules.values()]
|
|
356
|
+
.filter((entry) => entry.runtime)
|
|
357
|
+
.sort((left, right) => left.path.localeCompare(right.path))
|
|
358
|
+
.map((entry) => `// @pulse-project-module ${entry.path}\n${entry.sourceText}`)
|
|
359
|
+
.join('\n');
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
function orderLinkedRouters(routers, rootName) {
|
|
363
|
+
const byName = new Map(routers.map((router) => [router.name, router]));
|
|
364
|
+
const visited = new Set();
|
|
365
|
+
let routerOrder = 0;
|
|
366
|
+
let operationOrder = 0;
|
|
367
|
+
|
|
368
|
+
function visit(name) {
|
|
369
|
+
if (!name || visited.has(name)) return;
|
|
370
|
+
const router = byName.get(name);
|
|
371
|
+
if (!router) return;
|
|
372
|
+
visited.add(name);
|
|
373
|
+
router.order = routerOrder++;
|
|
374
|
+
for (const op of router.ops) {
|
|
375
|
+
op.order = operationOrder++;
|
|
376
|
+
if (op.kind === 'mount') visit(op.router);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
visit(rootName);
|
|
381
|
+
for (const router of [...routers].sort((left, right) => left.name.localeCompare(right.name))) visit(router.name);
|
|
382
|
+
routers.sort((left, right) => left.order - right.order || left.name.localeCompare(right.name));
|
|
383
|
+
return Object.freeze({ routers, operationCount: operationOrder });
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function linkProjectRouterModules(graphBuild, options = {}) {
|
|
387
|
+
const context = projectGraphContext(graphBuild);
|
|
388
|
+
if (!context) throw new TypeError('linkProjectRouterModules requires a reachable project graph build.');
|
|
389
|
+
const diagnostics = [];
|
|
390
|
+
const built = buildModuleIndexes(context);
|
|
391
|
+
diagnostics.push(...built.diagnostics);
|
|
392
|
+
const indexes = built.indexes;
|
|
393
|
+
const consumedImports = new Set();
|
|
394
|
+
const entryModule = context.projectModules.get(graphBuild.entryKey);
|
|
395
|
+
const root = resolveExport(context, indexes, entryModule, 'default', diagnostics, [], consumedImports);
|
|
396
|
+
for (const index of indexes.values()) {
|
|
397
|
+
for (const construction of index.constructions) {
|
|
398
|
+
if (!index.imports.has(construction.className)) {
|
|
399
|
+
const packageName = construction.className === 'Pulse' ? '@pulse-compute/pulse' : '@pulse-compute/runtime';
|
|
400
|
+
const point = index.module.sourceFile.getLineAndCharacterOfPosition(construction.node.getStart(index.module.sourceFile));
|
|
401
|
+
diagnostics.push(graphDiagnostic(
|
|
402
|
+
'PULSE_PROJECT_APPLICATION_IMPORT_REQUIRED',
|
|
403
|
+
`${construction.className} construction in ${index.module.path} requires a direct unaliased named import from ${packageName}.`,
|
|
404
|
+
{ file: index.module.path, start: { line: point.line + 1, column: point.character + 1 } },
|
|
405
|
+
{ className: construction.className, packageName, localName: construction.localName }
|
|
406
|
+
));
|
|
407
|
+
}
|
|
408
|
+
if (construction.className === 'Pulse' && (!root || root.kind !== 'router' || root.module.path !== index.module.path || root.localName !== construction.localName)) {
|
|
409
|
+
const point = index.module.sourceFile.getLineAndCharacterOfPosition(construction.node.getStart(index.module.sourceFile));
|
|
410
|
+
diagnostics.push(graphDiagnostic(
|
|
411
|
+
'PULSE_CANONICAL_PULSE_ROOT_ONLY',
|
|
412
|
+
'Pulse may be constructed only for the resolved default application root. Imported and mounted applications use Router.',
|
|
413
|
+
{ file: index.module.path, start: { line: point.line + 1, column: point.character + 1 } },
|
|
414
|
+
{ declaration: construction.localName }
|
|
415
|
+
));
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
if (!root || root.kind !== 'router') {
|
|
420
|
+
return Object.freeze({
|
|
421
|
+
version: ROUTER_MODULE_LINKER_VERSION,
|
|
422
|
+
kind: root && root.kind || 'unknown',
|
|
423
|
+
root,
|
|
424
|
+
diagnostics: Object.freeze([...diagnostics]),
|
|
425
|
+
graph: finalizeReachableProjectGraph(graphBuild, root ? [{
|
|
426
|
+
module: root.module.path,
|
|
427
|
+
exportName: root.exportName,
|
|
428
|
+
localName: root.localName,
|
|
429
|
+
role: root.kind === 'router' ? 'router' : 'handler',
|
|
430
|
+
wrappers: [],
|
|
431
|
+
source: { file: entryModule.path, line: 1, column: 1 }
|
|
432
|
+
}] : [])
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
const moduleIds = moduleIdsByPath(graphBuild);
|
|
437
|
+
const routerSymbols = new Map();
|
|
438
|
+
const handlerSymbols = new Map();
|
|
439
|
+
for (const [modulePath, index] of indexes) {
|
|
440
|
+
const moduleId = moduleIds.get(modulePath);
|
|
441
|
+
for (const name of index.routers.keys()) routerSymbols.set(`${modulePath}:${name}`, globalSymbol(moduleId, 'router', name));
|
|
442
|
+
for (const name of index.handlers.keys()) handlerSymbols.set(`${modulePath}:${name}`, globalSymbol(moduleId, 'handler', name));
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
function resolveReference(module, ref, role) {
|
|
446
|
+
if (!ref || ref.kind === 'inline' || ref.kind === 'static') return Object.freeze({ ref, target: undefined });
|
|
447
|
+
const target = resolveLocal(context, indexes, module, ref.value, diagnostics, [], undefined, consumedImports);
|
|
448
|
+
if (!target || !['handler', 'router'].includes(target.kind)) {
|
|
449
|
+
diagnostics.push(graphDiagnostic('PULSE_PROJECT_IMPORTED_SYMBOL_UNRESOLVED', `Unable to resolve ${role} reference ${ref.value} from ${module.path}.`, ref.loc, { role, reference: ref.value }));
|
|
450
|
+
return Object.freeze({ ref, target: undefined });
|
|
451
|
+
}
|
|
452
|
+
if (role === 'router' && target.kind !== 'router') {
|
|
453
|
+
diagnostics.push(graphDiagnostic('PULSE_PROJECT_MOUNT_TARGET_KIND_MISMATCH', `Mounted reference ${ref.value} resolves to a handler rather than a Router.`, ref.loc, { reference: ref.value }));
|
|
454
|
+
return Object.freeze({ ref, target: undefined });
|
|
455
|
+
}
|
|
456
|
+
if (role !== 'router' && target.kind !== 'handler') {
|
|
457
|
+
diagnostics.push(graphDiagnostic('PULSE_PROJECT_HANDLER_TARGET_KIND_MISMATCH', `Handler reference ${ref.value} resolves to a Router rather than a handler.`, ref.loc, { reference: ref.value, role }));
|
|
458
|
+
return Object.freeze({ ref, target: undefined });
|
|
459
|
+
}
|
|
460
|
+
if (role !== 'router' && target.declaration && target.declaration.anonymous) {
|
|
461
|
+
diagnostics.push(graphDiagnostic(
|
|
462
|
+
'PULSE_PROJECT_ANONYMOUS_IMPORTED_HANDLER_UNSUPPORTED',
|
|
463
|
+
`Imported handler reference ${ref.value} resolves to an anonymous default handler. Name the exported handler before registering it with a Router.`,
|
|
464
|
+
ref.loc,
|
|
465
|
+
{ reference: ref.value, module: target.module.path, role }
|
|
466
|
+
));
|
|
467
|
+
return Object.freeze({ ref, target: undefined });
|
|
468
|
+
}
|
|
469
|
+
const symbol = target.kind === 'router'
|
|
470
|
+
? routerSymbols.get(`${target.module.path}:${target.localName}`)
|
|
471
|
+
: handlerSymbols.get(`${target.module.path}:${target.localName}`);
|
|
472
|
+
const next = Object.freeze({
|
|
473
|
+
...ref,
|
|
474
|
+
value: symbol,
|
|
475
|
+
localName: target.localName,
|
|
476
|
+
exportName: target.exportName,
|
|
477
|
+
modulePath: target.module.path,
|
|
478
|
+
wrappers: Object.freeze([...(ref.wrappers || [])])
|
|
479
|
+
});
|
|
480
|
+
return Object.freeze({ ref: next, target });
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
const routers = [];
|
|
484
|
+
const handlers = [];
|
|
485
|
+
const handlerReferences = [{
|
|
486
|
+
module: root.module.path,
|
|
487
|
+
exportName: root.exportName || 'default',
|
|
488
|
+
localName: root.localName,
|
|
489
|
+
role: 'router',
|
|
490
|
+
wrappers: [],
|
|
491
|
+
source: declarationSource(root.module, root.declaration)
|
|
492
|
+
}];
|
|
493
|
+
let handlerOrder = 0;
|
|
494
|
+
|
|
495
|
+
for (const [modulePath, index] of [...indexes.entries()].sort(([a], [b]) => a.localeCompare(b))) {
|
|
496
|
+
for (const handler of index.ir.handlerIR || []) {
|
|
497
|
+
const exportNames = localExportNames(index.module, handler.name);
|
|
498
|
+
handlers.push({
|
|
499
|
+
...handler,
|
|
500
|
+
name: handlerSymbols.get(`${modulePath}:${handler.name}`),
|
|
501
|
+
symbolId: handlerSymbols.get(`${modulePath}:${handler.name}`),
|
|
502
|
+
localName: handler.name,
|
|
503
|
+
exportName: exportNames[0] || handler.exportName,
|
|
504
|
+
modulePath,
|
|
505
|
+
moduleId: moduleIds.get(modulePath),
|
|
506
|
+
order: handlerOrder++
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
for (const router of index.ir.routerIR || []) {
|
|
510
|
+
const ops = [];
|
|
511
|
+
for (const op of router.ops || []) {
|
|
512
|
+
const next = { ...op };
|
|
513
|
+
if (next.kind === 'mount') {
|
|
514
|
+
const resolved = resolveReference(index.module, { kind: 'identifier', value: next.router, loc: next.loc, ...(next.routerWrappers ? { wrappers: next.routerWrappers } : {}) }, 'router');
|
|
515
|
+
if (resolved.target) {
|
|
516
|
+
next.router = resolved.ref.value;
|
|
517
|
+
handlerReferences.push({
|
|
518
|
+
module: resolved.target.module.path,
|
|
519
|
+
exportName: resolved.target.exportName,
|
|
520
|
+
localName: resolved.target.localName,
|
|
521
|
+
role: 'router',
|
|
522
|
+
wrappers: resolved.ref.wrappers || [],
|
|
523
|
+
source: referenceSource(index.module, next.loc)
|
|
524
|
+
});
|
|
525
|
+
}
|
|
526
|
+
} else if (next.handler) {
|
|
527
|
+
const role = next.kind === 'use' ? 'middleware' : next.kind === 'error' ? 'error-middleware' : next.kind === 'event' ? 'event' : 'route';
|
|
528
|
+
if (next.handler.kind === 'inline') {
|
|
529
|
+
handlerReferences.push({
|
|
530
|
+
module: index.module.path,
|
|
531
|
+
exportName: null,
|
|
532
|
+
localName: `<inline:${referenceSource(index.module, next.handler.loc).line}:${referenceSource(index.module, next.handler.loc).column}>`,
|
|
533
|
+
role,
|
|
534
|
+
wrappers: next.handler.wrappers || [],
|
|
535
|
+
source: referenceSource(index.module, next.handler.loc)
|
|
536
|
+
});
|
|
537
|
+
} else {
|
|
538
|
+
const resolved = resolveReference(index.module, next.handler, role);
|
|
539
|
+
next.handler = resolved.ref;
|
|
540
|
+
if (resolved.target) handlerReferences.push({
|
|
541
|
+
module: resolved.target.module.path,
|
|
542
|
+
exportName: resolved.target.exportName,
|
|
543
|
+
localName: resolved.target.localName,
|
|
544
|
+
role,
|
|
545
|
+
wrappers: resolved.ref.wrappers || [],
|
|
546
|
+
source: referenceSource(index.module, op.handler && op.handler.loc || op.loc)
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
ops.push(next);
|
|
551
|
+
}
|
|
552
|
+
routers.push({
|
|
553
|
+
...router,
|
|
554
|
+
name: routerSymbols.get(`${modulePath}:${router.name}`),
|
|
555
|
+
localName: router.name,
|
|
556
|
+
modulePath,
|
|
557
|
+
moduleId: moduleIds.get(modulePath),
|
|
558
|
+
order: -1,
|
|
559
|
+
ops
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
const rootGlobalName = routerSymbols.get(`${root.module.path}:${root.localName}`);
|
|
565
|
+
const ordered = orderLinkedRouters(routers, rootGlobalName);
|
|
566
|
+
if (options.target !== 'javascript') validateRuntimeProjectImports(context, consumedImports, diagnostics);
|
|
567
|
+
const combinedIr = Object.freeze({
|
|
568
|
+
source: entryModule.path,
|
|
569
|
+
routerIR: Object.freeze(ordered.routers),
|
|
570
|
+
handlerIR: Object.freeze(handlers),
|
|
571
|
+
diagnostics: Object.freeze([...diagnostics]),
|
|
572
|
+
summary: Object.freeze({
|
|
573
|
+
routers: routers.length,
|
|
574
|
+
handlers: handlers.length,
|
|
575
|
+
operations: ordered.operationCount,
|
|
576
|
+
routersWithOps: routers.filter((router) => router.ops.length > 0).length,
|
|
577
|
+
statements: routers.reduce((total, router) => total + new Set(router.ops.map((op) => op.statementOrder)).size, 0)
|
|
578
|
+
})
|
|
579
|
+
});
|
|
580
|
+
const manifest = finalizeReachableProjectGraph(graphBuild, handlerReferences);
|
|
581
|
+
const retained = retainedDeclarations(context, diagnostics);
|
|
582
|
+
const rootIndex = indexes.get(root.module.path);
|
|
583
|
+
const rootSourceFile = root.module.sourceFile;
|
|
584
|
+
const functionNodeForHandler = (handler) => {
|
|
585
|
+
const module = context.projectModules.get(handler && handler.file);
|
|
586
|
+
const sourceFile = module ? module.sourceFile : rootSourceFile;
|
|
587
|
+
if (!sourceFile) return undefined;
|
|
588
|
+
if (handler && handler.localName) return functionForNamedHandler(sourceFile, handler.localName);
|
|
589
|
+
const offset = handler && handler.loc && handler.loc.start && handler.loc.start.offset;
|
|
590
|
+
let match;
|
|
591
|
+
function visit(node) {
|
|
592
|
+
if ((ts.isArrowFunction(node) || ts.isFunctionExpression(node)) && node.getStart(sourceFile) === offset) match = node;
|
|
593
|
+
if (!match) ts.forEachChild(node, visit);
|
|
594
|
+
}
|
|
595
|
+
if (Number.isInteger(offset)) visit(sourceFile);
|
|
596
|
+
return match;
|
|
597
|
+
};
|
|
598
|
+
|
|
599
|
+
if (diagnostics.some((entry) => entry.severity !== 'warning')) {
|
|
600
|
+
throw new RouterModuleLinkError(`Project module linking failed with ${diagnostics.length} diagnostic(s).`, diagnostics, { entry: entryModule.path });
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
return Object.freeze({
|
|
604
|
+
version: ROUTER_MODULE_LINKER_VERSION,
|
|
605
|
+
kind: 'router',
|
|
606
|
+
graph: manifest,
|
|
607
|
+
ir: combinedIr,
|
|
608
|
+
entryModule,
|
|
609
|
+
rootModule: root.module,
|
|
610
|
+
rootLocalName: root.localName,
|
|
611
|
+
rootGlobalName,
|
|
612
|
+
rootExportName: root.exportName || 'default',
|
|
613
|
+
rootSourceFile,
|
|
614
|
+
rootSourceText: root.module.sourceText,
|
|
615
|
+
authoringSourceText: linkedAuthoringSource(context),
|
|
616
|
+
entrySourceFile: entryModule.sourceFile,
|
|
617
|
+
entrySourceText: entryModule.sourceText,
|
|
618
|
+
retainedDeclarations: retained,
|
|
619
|
+
functionNodeForHandler,
|
|
620
|
+
sourceFileForPath(file) {
|
|
621
|
+
const module = context.projectModules.get(file);
|
|
622
|
+
return module && module.sourceFile;
|
|
623
|
+
},
|
|
624
|
+
projectFiles: graphBuild.projectModulePaths,
|
|
625
|
+
runtimeProjectFiles: graphBuild.runtimeProjectModulePaths,
|
|
626
|
+
diagnostics: Object.freeze([]),
|
|
627
|
+
summary: Object.freeze({
|
|
628
|
+
projectModules: graphBuild.summary.projectModules,
|
|
629
|
+
runtimeProjectModules: graphBuild.summary.runtimeProjectModules,
|
|
630
|
+
routers: routers.length,
|
|
631
|
+
handlers: handlers.length,
|
|
632
|
+
handlerReferences: manifest.handlers.length,
|
|
633
|
+
cycles: manifest.cycles.length
|
|
634
|
+
})
|
|
635
|
+
});
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
function linkProjectPlainHandler(graphBuild, options = {}) {
|
|
639
|
+
const context = projectGraphContext(graphBuild);
|
|
640
|
+
if (!context) throw new TypeError('linkProjectPlainHandler requires a reachable project graph build.');
|
|
641
|
+
const diagnostics = [];
|
|
642
|
+
const built = buildModuleIndexes(context);
|
|
643
|
+
diagnostics.push(...built.diagnostics);
|
|
644
|
+
const consumedImports = new Set(Array.isArray(options.consumedRuntimeImports) ? options.consumedRuntimeImports : []);
|
|
645
|
+
const entryModule = context.projectModules.get(graphBuild.entryKey);
|
|
646
|
+
const root = resolveExport(context, built.indexes, entryModule, 'default', diagnostics, [], consumedImports);
|
|
647
|
+
if (!root || root.kind !== 'handler') {
|
|
648
|
+
diagnostics.push(graphDiagnostic(
|
|
649
|
+
'PULSE_PROJECT_PLAIN_HANDLER_ROOT_REQUIRED',
|
|
650
|
+
'The configured entry must resolve its default export to one project-owned handler.',
|
|
651
|
+
{ file: entryModule.path, start: { line: 1, column: 1 } },
|
|
652
|
+
{ resolvedKind: root && root.kind || 'unknown' }
|
|
653
|
+
));
|
|
654
|
+
}
|
|
655
|
+
if (options.target !== 'javascript') validateRuntimeProjectImports(context, consumedImports, diagnostics);
|
|
656
|
+
if (diagnostics.some((entry) => entry.severity !== 'warning')) {
|
|
657
|
+
throw new RouterModuleLinkError(`Project plain-handler linking failed with ${diagnostics.length} diagnostic(s).`, diagnostics, { entry: entryModule.path });
|
|
658
|
+
}
|
|
659
|
+
const handlerReferences = [{
|
|
660
|
+
module: root.module.path,
|
|
661
|
+
exportName: root.exportName || 'default',
|
|
662
|
+
localName: root.localName,
|
|
663
|
+
role: 'handler',
|
|
664
|
+
wrappers: [],
|
|
665
|
+
source: declarationSource(root.module, root.declaration)
|
|
666
|
+
}];
|
|
667
|
+
return Object.freeze({
|
|
668
|
+
version: ROUTER_MODULE_LINKER_VERSION,
|
|
669
|
+
kind: 'handler',
|
|
670
|
+
graph: finalizeReachableProjectGraph(graphBuild, handlerReferences),
|
|
671
|
+
entryModule,
|
|
672
|
+
rootModule: root.module,
|
|
673
|
+
rootLocalName: root.localName,
|
|
674
|
+
rootExportName: root.exportName || 'default',
|
|
675
|
+
rootSourceFile: root.module.sourceFile,
|
|
676
|
+
rootSourceText: root.module.sourceText,
|
|
677
|
+
authoringSourceText: root.module.sourceText,
|
|
678
|
+
projectFiles: graphBuild.projectModulePaths,
|
|
679
|
+
runtimeProjectFiles: graphBuild.runtimeProjectModulePaths,
|
|
680
|
+
diagnostics: Object.freeze([]),
|
|
681
|
+
summary: Object.freeze({
|
|
682
|
+
projectModules: graphBuild.summary.projectModules,
|
|
683
|
+
runtimeProjectModules: graphBuild.summary.runtimeProjectModules,
|
|
684
|
+
handlers: 1,
|
|
685
|
+
handlerReferences: 1,
|
|
686
|
+
cycles: graphBuild.graph.cycles.length
|
|
687
|
+
})
|
|
688
|
+
});
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
function resolveProjectRoot(graphBuild) {
|
|
692
|
+
const context = projectGraphContext(graphBuild);
|
|
693
|
+
if (!context) throw new TypeError('resolveProjectRoot requires a reachable project graph build.');
|
|
694
|
+
const diagnostics = [];
|
|
695
|
+
const built = buildModuleIndexes(context);
|
|
696
|
+
diagnostics.push(...built.diagnostics);
|
|
697
|
+
const entry = context.projectModules.get(graphBuild.entryKey);
|
|
698
|
+
const root = resolveExport(context, built.indexes, entry, 'default', diagnostics);
|
|
699
|
+
if (diagnostics.length > 0) throw new RouterModuleLinkError(`Project root resolution failed with ${diagnostics.length} diagnostic(s).`, diagnostics, { entry: entry.path });
|
|
700
|
+
return Object.freeze({ root, indexes: built.indexes });
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
module.exports = Object.freeze({
|
|
704
|
+
ROUTER_MODULE_LINKER_VERSION,
|
|
705
|
+
RouterModuleLinkError,
|
|
706
|
+
linkProjectPlainHandler,
|
|
707
|
+
linkProjectRouterModules,
|
|
708
|
+
resolveProjectRoot,
|
|
709
|
+
wrappersForExpression
|
|
710
|
+
});
|