@animus-ui/extract 0.1.22 → 0.1.24
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/crates/extract-v2/animus-extract-v2.darwin-arm64.node +0 -0
- package/crates/extract-v2/animus-extract-v2.linux-arm64-gnu.node +0 -0
- package/crates/extract-v2/animus-extract-v2.linux-x64-gnu.node +0 -0
- package/crates/extract-v2/index.d.ts +16 -3
- package/dist/index.cjs +14 -2
- package/dist/index.mjs +2 -2
- package/dist/pipeline/analyze-project-args.d.ts +5 -1
- package/dist/pipeline/analyze-project-args.d.ts.map +1 -1
- package/dist/pipeline/asset-placeholders.d.ts +4 -1
- package/dist/pipeline/asset-placeholders.d.ts.map +1 -1
- package/dist/pipeline/core-options.d.ts +15 -3
- package/dist/pipeline/core-options.d.ts.map +1 -1
- package/dist/pipeline/correlate-external-tokens.d.ts +3 -0
- package/dist/pipeline/correlate-external-tokens.d.ts.map +1 -1
- package/dist/pipeline/discover-packages.d.ts +105 -7
- package/dist/pipeline/discover-packages.d.ts.map +1 -1
- package/dist/pipeline/dynamic-prop-config.d.ts +13 -2
- package/dist/pipeline/dynamic-prop-config.d.ts.map +1 -1
- package/dist/pipeline/engine-adapter.d.ts +5 -1
- package/dist/pipeline/engine-adapter.d.ts.map +1 -1
- package/dist/pipeline/index.d.ts +7 -4
- package/dist/pipeline/index.d.ts.map +1 -1
- package/dist/pipeline/kit-descriptor.d.ts +44 -0
- package/dist/pipeline/kit-descriptor.d.ts.map +1 -0
- package/dist/pipeline/kit-publication.d.ts +15 -0
- package/dist/pipeline/kit-publication.d.ts.map +1 -0
- package/dist/pipeline/manifest-diagnostics.d.ts +64 -5
- package/dist/pipeline/manifest-diagnostics.d.ts.map +1 -1
- package/dist/pipeline/manifest-schema.d.ts +4 -0
- package/dist/pipeline/manifest-schema.d.ts.map +1 -1
- package/dist/pipeline/prefix.d.ts +14 -3
- package/dist/pipeline/prefix.d.ts.map +1 -1
- package/dist/pipeline/run-analysis.d.ts +16 -2
- package/dist/pipeline/run-analysis.d.ts.map +1 -1
- package/dist/pipeline/source-ingestion.d.ts +13 -2
- package/dist/pipeline/source-ingestion.d.ts.map +1 -1
- package/dist/pipeline/structural-self-check.d.ts.map +1 -1
- package/dist/pipeline/system-config.d.ts +6 -3
- package/dist/pipeline/system-config.d.ts.map +1 -1
- package/dist/{pipeline-BXY7Pf_D.mjs → pipeline-Bl20tbu_.mjs} +1484 -546
- package/dist/{pipeline-D7B1z0-1.cjs → pipeline-CAH9mxnK.cjs} +1602 -591
- package/dist/session/extraction-session.d.ts +17 -1
- package/dist/session/extraction-session.d.ts.map +1 -1
- package/dist/session.cjs +60 -17
- package/dist/session.mjs +60 -17
- package/package.json +4 -4
|
@@ -31,6 +31,8 @@ let browserslist = require("browserslist");
|
|
|
31
31
|
browserslist = __toESM(browserslist);
|
|
32
32
|
let lightningcss = require("lightningcss");
|
|
33
33
|
let _animus_ui_properties = require("@animus-ui/properties");
|
|
34
|
+
let node_child_process = require("node:child_process");
|
|
35
|
+
let node_module = require("node:module");
|
|
34
36
|
//#region pipeline/assemble-stylesheet.ts
|
|
35
37
|
/**
|
|
36
38
|
* The cascade layer order, outermost first: its one definition. The engine
|
|
@@ -110,8 +112,13 @@ function buildAnalyzeProjectArgs(inputs) {
|
|
|
110
112
|
inputs.transformSourcesJson,
|
|
111
113
|
inputs.transformProvenanceJson ?? null
|
|
112
114
|
];
|
|
113
|
-
|
|
114
|
-
|
|
115
|
+
const tail = [
|
|
116
|
+
inputs.declarationScalesJson,
|
|
117
|
+
inputs.analysisContextJson,
|
|
118
|
+
inputs.generatedNamesJson
|
|
119
|
+
];
|
|
120
|
+
const last = tail.findLastIndex((slot) => slot !== void 0 && slot !== "");
|
|
121
|
+
for (const slot of tail.slice(0, last + 1)) args.push(slot || void 0);
|
|
115
122
|
return args;
|
|
116
123
|
}
|
|
117
124
|
//#endregion
|
|
@@ -131,7 +138,180 @@ function parseInternalWire(json, wire) {
|
|
|
131
138
|
return parsed;
|
|
132
139
|
}
|
|
133
140
|
//#endregion
|
|
141
|
+
//#region pipeline/asset-placeholders.ts
|
|
142
|
+
const ASSET_PLACEHOLDER_PREFIX = "animus-asset:";
|
|
143
|
+
const URL_OPEN_RE = /url\(\s*(['"]?)/gi;
|
|
144
|
+
const IMAGE_SET_OPEN_RE = /(?:-webkit-)?image-set\(/gi;
|
|
145
|
+
const BARE_SPECIFIER_RE = /^[^'")\s]+/;
|
|
146
|
+
/** The index of the `)` that closes the `(` at `open`, outside strings. */
|
|
147
|
+
function closingParen(css, open) {
|
|
148
|
+
let depth = 0;
|
|
149
|
+
let quote = null;
|
|
150
|
+
for (let i = open; i < css.length; i += 1) {
|
|
151
|
+
const c = css[i];
|
|
152
|
+
if (quote !== null) {
|
|
153
|
+
if (c === quote) quote = null;
|
|
154
|
+
} else if (c === "\"" || c === "'") quote = c;
|
|
155
|
+
else if (c === "(") depth += 1;
|
|
156
|
+
else if (c === ")") {
|
|
157
|
+
depth -= 1;
|
|
158
|
+
if (depth === 0) return i;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return css.length;
|
|
162
|
+
}
|
|
163
|
+
function quotedReference(css, quoteAt) {
|
|
164
|
+
const start = quoteAt + 1;
|
|
165
|
+
if (!css.startsWith("animus-asset:", start)) return null;
|
|
166
|
+
const end = css.indexOf(css[quoteAt], start);
|
|
167
|
+
if (end === -1) return null;
|
|
168
|
+
return {
|
|
169
|
+
start,
|
|
170
|
+
end,
|
|
171
|
+
specifier: css.slice(start + 13, end)
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
function assetReferences(css) {
|
|
175
|
+
if (!css.includes("animus-asset:")) return [];
|
|
176
|
+
const references = [];
|
|
177
|
+
for (const open of css.matchAll(URL_OPEN_RE)) {
|
|
178
|
+
const at = (open.index ?? 0) + open[0].length;
|
|
179
|
+
if (open[1]) {
|
|
180
|
+
const reference = quotedReference(css, at - 1);
|
|
181
|
+
if (reference) references.push(reference);
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
if (!css.startsWith("animus-asset:", at)) continue;
|
|
185
|
+
const specifier = BARE_SPECIFIER_RE.exec(css.slice(at + 13))?.[0];
|
|
186
|
+
if (specifier === void 0) continue;
|
|
187
|
+
const end = at + 13 + specifier.length;
|
|
188
|
+
references.push({
|
|
189
|
+
start: at,
|
|
190
|
+
end,
|
|
191
|
+
specifier
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
for (const open of css.matchAll(IMAGE_SET_OPEN_RE)) {
|
|
195
|
+
const paren = (open.index ?? 0) + open[0].length - 1;
|
|
196
|
+
const close = closingParen(css, paren);
|
|
197
|
+
let depth = 0;
|
|
198
|
+
let candidateStart = true;
|
|
199
|
+
for (let i = paren + 1; i < close; i += 1) {
|
|
200
|
+
const c = css[i];
|
|
201
|
+
if (/\s/.test(c)) continue;
|
|
202
|
+
if (c === "\"" || c === "'") {
|
|
203
|
+
if (depth === 0 && candidateStart) {
|
|
204
|
+
const reference = quotedReference(css, i);
|
|
205
|
+
if (reference) references.push(reference);
|
|
206
|
+
}
|
|
207
|
+
const end = css.indexOf(c, i + 1);
|
|
208
|
+
i = end === -1 ? close : end;
|
|
209
|
+
} else if (c === "(") depth += 1;
|
|
210
|
+
else if (c === ")") depth -= 1;
|
|
211
|
+
else if (c === "," && depth === 0) {
|
|
212
|
+
candidateStart = true;
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
candidateStart = false;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return references.sort((a, b) => a.start - b.start);
|
|
219
|
+
}
|
|
220
|
+
function findAssetSpecifiers(css) {
|
|
221
|
+
return [...new Set(assetReferences(css).map((reference) => reference.specifier))];
|
|
222
|
+
}
|
|
223
|
+
function substituteAssetPlaceholders(css, urlBySpecifier) {
|
|
224
|
+
if (urlBySpecifier.size === 0) return css;
|
|
225
|
+
let out = css;
|
|
226
|
+
for (const reference of assetReferences(css).reverse()) {
|
|
227
|
+
const url = urlBySpecifier.get(reference.specifier);
|
|
228
|
+
if (url === void 0) continue;
|
|
229
|
+
out = out.slice(0, reference.start) + url + out.slice(reference.end);
|
|
230
|
+
}
|
|
231
|
+
return out;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Specifiers of placeholder text anywhere inside a `url()` or `image-set()`
|
|
235
|
+
* call, a reference or not: after substitution any of it is a broken load.
|
|
236
|
+
*/
|
|
237
|
+
function placeholdersInAssetCalls(css) {
|
|
238
|
+
if (!css.includes("animus-asset:")) return [];
|
|
239
|
+
const calls = [...css.matchAll(URL_OPEN_RE), ...css.matchAll(IMAGE_SET_OPEN_RE)].map((open) => {
|
|
240
|
+
const paren = css.indexOf("(", open.index ?? 0);
|
|
241
|
+
return [paren, closingParen(css, paren)];
|
|
242
|
+
});
|
|
243
|
+
const found = /* @__PURE__ */ new Set();
|
|
244
|
+
for (let at = css.indexOf(ASSET_PLACEHOLDER_PREFIX); at !== -1; at = css.indexOf(ASSET_PLACEHOLDER_PREFIX, at + 1)) {
|
|
245
|
+
if (!calls.some(([open, close]) => open < at && at < close)) continue;
|
|
246
|
+
found.add(/^[^'")\s,]*/.exec(css.slice(at + 13))[0]);
|
|
247
|
+
}
|
|
248
|
+
return [...found];
|
|
249
|
+
}
|
|
250
|
+
function findSheetAssetSpecifiers(sheets) {
|
|
251
|
+
return [.../* @__PURE__ */ new Set([
|
|
252
|
+
...findAssetSpecifiers(sheets.variableCss),
|
|
253
|
+
...findAssetSpecifiers(sheets.globalCss),
|
|
254
|
+
...findAssetSpecifiers(sheets.componentCss)
|
|
255
|
+
])];
|
|
256
|
+
}
|
|
257
|
+
/** The one substitution step every emitted sheet passes through. */
|
|
258
|
+
function substituteSheetAssets(sheets, urlBySpecifier) {
|
|
259
|
+
return {
|
|
260
|
+
variableCss: substituteAssetPlaceholders(sheets.variableCss, urlBySpecifier),
|
|
261
|
+
globalCss: substituteAssetPlaceholders(sheets.globalCss, urlBySpecifier),
|
|
262
|
+
componentCss: substituteAssetPlaceholders(sheets.componentCss, urlBySpecifier)
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
const UNSUBSTITUTED_ASSET_CODE = "animus.asset.unsubstituted-placeholder";
|
|
266
|
+
/** A placeholder in emitted CSS is a broken URL in the browser: it fails a
|
|
267
|
+
* strict build, strict development reports it, and it warns otherwise. */
|
|
268
|
+
function reportSurvivingAssetPlaceholders(css, report) {
|
|
269
|
+
const specifiers = placeholdersInAssetCalls(css);
|
|
270
|
+
if (specifiers.length === 0) return;
|
|
271
|
+
const message = `${report.prefix} asset() placeholders reached ${report.surface ?? "emitted CSS"} unsubstituted: ${specifiers.join(", ")} (${UNSUBSTITUTED_ASSET_CODE})`;
|
|
272
|
+
if (!report.strict) {
|
|
273
|
+
report.warn(message);
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
if (!report.reportErrors) throw new Error(message);
|
|
277
|
+
report.reportErrors(message);
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* The runtime code an analysis generates: each dynamic prop config, which the
|
|
281
|
+
* shared prop map module carries, and each component's replacement. Extraction
|
|
282
|
+
* lifts asset() out of them into root variables, so none should hold one.
|
|
283
|
+
* Both embed values as JSON strings, so their quote and backslash escapes are
|
|
284
|
+
* undone for scanning: the text is for `findAssetSpecifiers`, not to run.
|
|
285
|
+
*/
|
|
286
|
+
function generatedModuleCode(manifest) {
|
|
287
|
+
return [JSON.stringify(manifest.dynamic_props), ...Object.values(manifest.components).map((component) => component.replacement)].join("\n").replace(/\\(["\\])/g, "$1");
|
|
288
|
+
}
|
|
289
|
+
//#endregion
|
|
134
290
|
//#region pipeline/manifest-diagnostics.ts
|
|
291
|
+
/** `file`, then its line and column where the record has them. */
|
|
292
|
+
function locationOf(diagnostic) {
|
|
293
|
+
if (diagnostic.line === void 0) return diagnostic.file;
|
|
294
|
+
if (diagnostic.column === void 0) return `${diagnostic.file}:${diagnostic.line}`;
|
|
295
|
+
return `${diagnostic.file}:${diagnostic.line}:${diagnostic.column}`;
|
|
296
|
+
}
|
|
297
|
+
/** A located record's `file:line:column: `; nothing for one without a line,
|
|
298
|
+
* whose message names its file. */
|
|
299
|
+
function locatedPrefix(diagnostic) {
|
|
300
|
+
return diagnostic.line === void 0 ? "" : `${locationOf(diagnostic)}: `;
|
|
301
|
+
}
|
|
302
|
+
/** What the record dropped, when its message does not already quote it. */
|
|
303
|
+
function droppedSuffix(diagnostic) {
|
|
304
|
+
return diagnostic.dropped && !diagnostic.message.includes(diagnostic.dropped) ? ` — dropped: ${diagnostic.dropped}` : "";
|
|
305
|
+
}
|
|
306
|
+
/** The record's code, when its message does not already quote it. */
|
|
307
|
+
function codeSuffix(diagnostic) {
|
|
308
|
+
return diagnostic.code && !diagnostic.message.includes(diagnostic.code) ? ` [${diagnostic.code}]` : "";
|
|
309
|
+
}
|
|
310
|
+
/** A warn-kind record's line after its mark:
|
|
311
|
+
* `file:line:column: component: message [code]`. */
|
|
312
|
+
function warnLine(diagnostic) {
|
|
313
|
+
return `${locationOf(diagnostic)}: ${diagnostic.component}: ${diagnostic.message}${droppedSuffix(diagnostic)}${codeSuffix(diagnostic)}`;
|
|
314
|
+
}
|
|
135
315
|
/** Stable code for selector forms with no substitutable subject; the
|
|
136
316
|
* extraction engine mints the same string. */
|
|
137
317
|
const SELECTOR_UNSUPPORTED_SUBJECT = "animus.selector.unsupported-subject";
|
|
@@ -144,6 +324,81 @@ function isUnresolvedParentDrop(diagnostic) {
|
|
|
144
324
|
function unresolvedParentName(diagnostic) {
|
|
145
325
|
return UNRESOLVED_PARENT_RE.exec(diagnostic.message)?.[1] ?? null;
|
|
146
326
|
}
|
|
327
|
+
const DIAGNOSTIC_LEVELS = /* @__PURE__ */ new Set([
|
|
328
|
+
"off",
|
|
329
|
+
"info",
|
|
330
|
+
"warn",
|
|
331
|
+
"error"
|
|
332
|
+
]);
|
|
333
|
+
/** A key that selects every record of a kind: `kind:bail`, `kind:skip`,
|
|
334
|
+
* `kind:warn` or `kind:error`. */
|
|
335
|
+
const KIND_KEY = "kind:";
|
|
336
|
+
const DIAGNOSTIC_KINDS = /* @__PURE__ */ new Set([
|
|
337
|
+
"bail",
|
|
338
|
+
"skip",
|
|
339
|
+
"warn",
|
|
340
|
+
"error"
|
|
341
|
+
]);
|
|
342
|
+
/** The level `levels` sets for `code`: its exact entry, else the entry of
|
|
343
|
+
* the longest prefix it starts with. */
|
|
344
|
+
function codeLevel(code, levels) {
|
|
345
|
+
if (code === void 0) return void 0;
|
|
346
|
+
if (Object.hasOwn(levels, code)) return levels[code];
|
|
347
|
+
let longest;
|
|
348
|
+
for (const [key, level] of Object.entries(levels)) {
|
|
349
|
+
if (!key.endsWith(".*")) continue;
|
|
350
|
+
const prefix = key.slice(0, -1);
|
|
351
|
+
if (code.startsWith(prefix) && prefix.length > (longest?.length ?? -1)) longest = {
|
|
352
|
+
length: prefix.length,
|
|
353
|
+
level
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
return longest?.level;
|
|
357
|
+
}
|
|
358
|
+
/** The level `levels` sets for a record: its code's level, else its kind's. */
|
|
359
|
+
function levelFor({ code, kind }, levels) {
|
|
360
|
+
if (levels === void 0) return void 0;
|
|
361
|
+
const kindKey = `${KIND_KEY}${kind}`;
|
|
362
|
+
return codeLevel(code, levels) ?? (Object.hasOwn(levels, kindKey) ? levels[kindKey] : void 0);
|
|
363
|
+
}
|
|
364
|
+
/** The level a record prints at: the option's entry for its code, else
|
|
365
|
+
* `error` for a hard error (an `error`-kind record) or an error-severity
|
|
366
|
+
* record under `strict`, else its own. */
|
|
367
|
+
function effectiveLevel(diagnostic, policy) {
|
|
368
|
+
const level = levelFor(diagnostic, policy.levels);
|
|
369
|
+
if (level !== void 0) return level;
|
|
370
|
+
if (diagnostic.kind === "error") return "error";
|
|
371
|
+
if (diagnostic.severity === "info") return "info";
|
|
372
|
+
return policy.strict && diagnostic.severity === "error" ? "error" : "warn";
|
|
373
|
+
}
|
|
374
|
+
/** Every code Animus is known to report: the engine's table, as the native
|
|
375
|
+
* module publishes it (`diagnosticCodes()`), the pipeline's, and the asset
|
|
376
|
+
* code. The system package mints vocabulary codes at run time, so a valid
|
|
377
|
+
* code can be missing. */
|
|
378
|
+
function knownDiagnosticCodes(engineCodesJson) {
|
|
379
|
+
const engineCodes = engineCodesJson ? Object.keys(parseInternalWire(engineCodesJson, "diagnosticCodes (the engine code table)")) : [];
|
|
380
|
+
return /* @__PURE__ */ new Set([
|
|
381
|
+
...engineCodes,
|
|
382
|
+
...DIAGNOSTIC_SEVERITY.keys(),
|
|
383
|
+
UNSUBSTITUTED_ASSET_CODE
|
|
384
|
+
]);
|
|
385
|
+
}
|
|
386
|
+
/** The keys of `levels` that name no known code, exactly or as a prefix. */
|
|
387
|
+
function unknownDiagnosticKeys(levels, known) {
|
|
388
|
+
return Object.keys(levels).filter((key) => {
|
|
389
|
+
if (key.startsWith(KIND_KEY)) return !DIAGNOSTIC_KINDS.has(key.slice(5));
|
|
390
|
+
if (!key.endsWith(".*")) return !known.has(key);
|
|
391
|
+
const prefix = key.slice(0, -1);
|
|
392
|
+
return ![...known].some((code) => code.startsWith(prefix));
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
/** The failure of diagnostics at `error` level, from `strict` or the
|
|
396
|
+
* `diagnostics` option: a host fails the build on it whatever its own
|
|
397
|
+
* strictness. It prints as the plain `Error` strict always threw. */
|
|
398
|
+
var DiagnosticFailure = class extends Error {};
|
|
399
|
+
/** Level options already checked against the known codes: one warning per
|
|
400
|
+
* unknown key, however often a host analyzes. */
|
|
401
|
+
const checkedLevels = /* @__PURE__ */ new WeakSet();
|
|
147
402
|
/** True when the selector carries at least one substitutable `&` subject
|
|
148
403
|
* outside quoted text. */
|
|
149
404
|
function hasSelectorSubject(value) {
|
|
@@ -186,6 +441,22 @@ function collectSelectorAliasDiagnostics(selectorAliasesJson) {
|
|
|
186
441
|
const UNREADABLE_SOURCE_FILE = "animus.ingestion.unreadable-source-file";
|
|
187
442
|
/** A kit the system extends resolved, yet discovery found none of its files. */
|
|
188
443
|
const NO_KIT_FILES = "animus.discovery.no-kit-files";
|
|
444
|
+
/** A kit with no `animus` export condition: discovery guesses its source
|
|
445
|
+
* from `src/`. */
|
|
446
|
+
const KIT_WITHOUT_SOURCE_CONDITION = "animus.discovery.kit-without-source-condition";
|
|
447
|
+
/** A kit's `animus` export condition names a file that is missing or outside
|
|
448
|
+
* its package, so that entry is not read from source. */
|
|
449
|
+
const INVALID_KIT_SOURCE_CONDITION = "animus.discovery.invalid-source-condition";
|
|
450
|
+
/** A kit the application imports whose system the application's system
|
|
451
|
+
* does not include: its components are not extracted. */
|
|
452
|
+
const KIT_SYSTEM_NOT_INCLUDED = "animus.kit.system-not-included";
|
|
453
|
+
/** A system file's `createSystem` binding that discovery could not follow to
|
|
454
|
+
* Animus's factory, so it is no root. */
|
|
455
|
+
const UNPROVEN_ROOT_BINDING = "animus.discovery.unproven-root-binding";
|
|
456
|
+
/** A system file calls `createSystem` with no import or local binding. The
|
|
457
|
+
* system loader evaluates the file without auto-imports, where the name is
|
|
458
|
+
* undefined, so the call is no root. */
|
|
459
|
+
const UNIMPORTED_CREATE_SYSTEM = "animus.discovery.unimported-create-system";
|
|
189
460
|
/** What a skipped source file costs: the analysis never sees what it
|
|
190
461
|
* renders. */
|
|
191
462
|
const SKIPPED_SOURCE_COST = "skipped: its renders are not seen, so nothing is pruned and every component, variant option and state is kept until it is analyzed";
|
|
@@ -221,6 +492,11 @@ function noKitFilesDiagnostics(outcomes) {
|
|
|
221
492
|
severity: severityFor(NO_KIT_FILES)
|
|
222
493
|
}));
|
|
223
494
|
}
|
|
495
|
+
/** A kit's descriptor declares a format this Animus does not read. */
|
|
496
|
+
const KIT_UNSUPPORTED_FORMAT = "animus.kit.unsupported-format";
|
|
497
|
+
/** A kit's descriptor records a definition its shipped source no longer
|
|
498
|
+
* declares. */
|
|
499
|
+
const KIT_STALE_DESCRIPTOR = "animus.kit.stale-descriptor";
|
|
224
500
|
/** The collision entry code minted by the system package's merge. */
|
|
225
501
|
const VOCABULARY_COLLISION = "animus.vocabulary.collision";
|
|
226
502
|
/** A theme `@property` registration browsers would ignore; it is not emitted. */
|
|
@@ -246,9 +522,6 @@ const PROPERTY_FALLBACK_SELF_REFERENCE = "animus.property.fallback-self-referenc
|
|
|
246
522
|
* another directly, so every property in the cycle is invalid at
|
|
247
523
|
* computed-value time. */
|
|
248
524
|
const PROPERTY_CURRENT_VAR_CYCLE = "animus.property.current-var-cycle";
|
|
249
|
-
/** A prefix renamed contextual variables without `prefixContextualVars`, so
|
|
250
|
-
* their declared names no longer resolve. */
|
|
251
|
-
const PREFIX_CONTEXTUAL_VARS_UNPREFIXED = "animus.prefix.contextual-vars-unprefixed";
|
|
252
525
|
/** Under `prefixContextualVars`, a final name that collides with a runtime
|
|
253
526
|
* transport variable or a theme variable of the same spelling. */
|
|
254
527
|
const PREFIX_NAME_CONFLICT = "animus.prefix.name-conflict";
|
|
@@ -262,6 +535,13 @@ const DIAGNOSTIC_SEVERITY = /* @__PURE__ */ new Map([
|
|
|
262
535
|
[SELECTOR_UNSUPPORTED_SUBJECT, "error"],
|
|
263
536
|
[UNREADABLE_SOURCE_FILE, "error"],
|
|
264
537
|
[NO_KIT_FILES, "warn"],
|
|
538
|
+
[KIT_WITHOUT_SOURCE_CONDITION, "warn"],
|
|
539
|
+
[INVALID_KIT_SOURCE_CONDITION, "error"],
|
|
540
|
+
[KIT_SYSTEM_NOT_INCLUDED, "error"],
|
|
541
|
+
[KIT_UNSUPPORTED_FORMAT, "error"],
|
|
542
|
+
[KIT_STALE_DESCRIPTOR, "warn"],
|
|
543
|
+
[UNPROVEN_ROOT_BINDING, "warn"],
|
|
544
|
+
[UNIMPORTED_CREATE_SYSTEM, "warn"],
|
|
265
545
|
[VOCABULARY_COLLISION, "warn"],
|
|
266
546
|
[VOCABULARY_LEGACY_VERB, "warn"],
|
|
267
547
|
[INVALID_PROPERTY_REGISTRATION, "error"],
|
|
@@ -271,7 +551,6 @@ const DIAGNOSTIC_SEVERITY = /* @__PURE__ */ new Map([
|
|
|
271
551
|
[PROPERTY_SELF_REFERENCE, "warn"],
|
|
272
552
|
[PROPERTY_FALLBACK_SELF_REFERENCE, "warn"],
|
|
273
553
|
[PROPERTY_CURRENT_VAR_CYCLE, "warn"],
|
|
274
|
-
[PREFIX_CONTEXTUAL_VARS_UNPREFIXED, "warn"],
|
|
275
554
|
[PREFIX_NAME_CONFLICT, "error"],
|
|
276
555
|
[PROPERTY_LEGACY_TOKEN_COLLISION, "warn"],
|
|
277
556
|
[PROPERTY_TOKEN_COLLISION, "error"]
|
|
@@ -326,15 +605,6 @@ function systemLoadDiagnostics(system) {
|
|
|
326
605
|
code: INVALID_PROPERTY_REGISTRATION,
|
|
327
606
|
severity: severityFor(INVALID_PROPERTY_REGISTRATION)
|
|
328
607
|
}));
|
|
329
|
-
const prefixed = system.legacyPrefixedContextualVars ?? [];
|
|
330
|
-
const unprefixed = prefixed.length === 0 ? [] : [{
|
|
331
|
-
file: "system",
|
|
332
|
-
component: "prefix",
|
|
333
|
-
kind: "warn",
|
|
334
|
-
message: `the prefix renames the contextual variables ${prefixed.join(", ")}, but their declared names are still read and written as written, so scale reads of them fail and their writes miss. Set prefixContextualVars: true to resolve them under the prefix (${PREFIX_CONTEXTUAL_VARS_UNPREFIXED})`,
|
|
335
|
-
code: PREFIX_CONTEXTUAL_VARS_UNPREFIXED,
|
|
336
|
-
severity: severityFor(PREFIX_CONTEXTUAL_VARS_UNPREFIXED)
|
|
337
|
-
}];
|
|
338
608
|
const conflicts = (system.prefixNameConflicts ?? []).map(({ name, final, reason }) => ({
|
|
339
609
|
file: "system",
|
|
340
610
|
component: `--${name}`,
|
|
@@ -346,7 +616,6 @@ function systemLoadDiagnostics(system) {
|
|
|
346
616
|
return [
|
|
347
617
|
...vocabularyWitnessDiagnostics(system.vocabularyWitnessesJson),
|
|
348
618
|
...registrations,
|
|
349
|
-
...unprefixed,
|
|
350
619
|
...conflicts,
|
|
351
620
|
...tokenCollisionDiagnostics(system)
|
|
352
621
|
];
|
|
@@ -382,29 +651,44 @@ function tokenCollisionDiagnostics(system) {
|
|
|
382
651
|
}
|
|
383
652
|
/**
|
|
384
653
|
* The single strict-escalation policy point for both extraction plugins:
|
|
385
|
-
* error-severity diagnostics throw together under `strict`, else warn.
|
|
654
|
+
* error-severity diagnostics throw together under `strict`, else warn. A
|
|
655
|
+
* development session reports them through `reportErrors` instead.
|
|
386
656
|
*/
|
|
387
657
|
function surfaceManifestDiagnostics(manifest, warn, policy = {}) {
|
|
388
|
-
const errors =
|
|
658
|
+
const errors = /* @__PURE__ */ new Set();
|
|
659
|
+
if (policy.levels && policy.knownCodes && !checkedLevels.has(policy.levels)) {
|
|
660
|
+
checkedLevels.add(policy.levels);
|
|
661
|
+
for (const key of unknownDiagnosticKeys(policy.levels, policy.knownCodes)) warn(key.startsWith(KIND_KEY) ? `⚠ diagnostics option: '${key}' names no diagnostic kind — use kind:bail, kind:skip, kind:warn or kind:error` : `⚠ diagnostics option: '${key}' matches no Animus diagnostic code — check its spelling (a code the system package mints at run time is known only once it is reported)`);
|
|
662
|
+
}
|
|
389
663
|
const diagnostics = policy.prepend?.length ? [...policy.prepend, ...manifest.diagnostics ?? []] : manifest.diagnostics ?? [];
|
|
390
664
|
for (const diagnostic of diagnostics) {
|
|
665
|
+
const level = effectiveLevel(diagnostic, policy);
|
|
666
|
+
if (level === "off") continue;
|
|
391
667
|
let line = null;
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
668
|
+
const mark = level === "info" ? "ℹ" : "⚠";
|
|
669
|
+
const message = `${diagnostic.message}${droppedSuffix(diagnostic)}`;
|
|
670
|
+
if (diagnostic.kind === "bail") line = `${mark} ${locatedPrefix(diagnostic)}${diagnostic.component} not extracted: ${message}${codeSuffix(diagnostic)}`;
|
|
671
|
+
else if (diagnostic.kind === "skip") line = `${mark} ${locatedPrefix(diagnostic)}${diagnostic.component}: skipped ${message}${codeSuffix(diagnostic)}`;
|
|
672
|
+
else if (diagnostic.kind === "warn" || diagnostic.kind === "error") line = `${mark} ${warnLine(diagnostic)}`;
|
|
395
673
|
if (line === null) continue;
|
|
396
|
-
if (
|
|
397
|
-
if (diagnostic.severity === "info") {
|
|
674
|
+
if (level === "info") {
|
|
398
675
|
policy.info?.(line);
|
|
399
676
|
continue;
|
|
400
677
|
}
|
|
401
|
-
if (
|
|
402
|
-
|
|
678
|
+
if (level === "error") {
|
|
679
|
+
const subject = diagnostic.kind === "warn" || diagnostic.kind === "error" ? `${locationOf(diagnostic)}: ` : locatedPrefix(diagnostic);
|
|
680
|
+
errors.add(`${diagnostic.code ?? "error"} — ${subject}${diagnostic.component}: ${message}`);
|
|
403
681
|
continue;
|
|
404
682
|
}
|
|
405
683
|
warn(line);
|
|
406
684
|
}
|
|
407
|
-
if (errors.
|
|
685
|
+
if (errors.size === 0) return;
|
|
686
|
+
const message = `[animus] strict: ${errors.size} error diagnostic(s):\n${[...errors].join("\n")}`;
|
|
687
|
+
if (policy.reportErrors) {
|
|
688
|
+
policy.reportErrors(message);
|
|
689
|
+
return;
|
|
690
|
+
}
|
|
691
|
+
throw new DiagnosticFailure(message);
|
|
408
692
|
}
|
|
409
693
|
//#endregion
|
|
410
694
|
//#region pipeline/mdx-preprocessor.ts
|
|
@@ -1056,17 +1340,17 @@ const NODE_NEXT_EXTENSION_MAP = {
|
|
|
1056
1340
|
".mjs": [".mts"],
|
|
1057
1341
|
".cjs": [".cts"]
|
|
1058
1342
|
};
|
|
1059
|
-
|
|
1060
|
-
|
|
1343
|
+
/** The paths a relative specifier can name from `importerPath`, in probe
|
|
1344
|
+
* order: as written with each source suffix, then a NodeNext emitted
|
|
1345
|
+
* extension's sources. Empty for a non-relative specifier. */
|
|
1346
|
+
function relativeSourceCandidates(importerPath, specifier) {
|
|
1347
|
+
if (!specifier.startsWith(".")) return [];
|
|
1061
1348
|
const base = node_path.posix.normalize(node_path.posix.join(node_path.posix.dirname(canonicalResolverPath(importerPath)), specifier));
|
|
1062
|
-
for (const suffix of RELATIVE_PROBE_SUFFIXES) {
|
|
1063
|
-
const candidate = `${base}${suffix}`;
|
|
1064
|
-
const actualPath = files.get(candidate);
|
|
1065
|
-
if (actualPath !== void 0) return actualPath;
|
|
1066
|
-
}
|
|
1067
1349
|
const explicitExtension = extension(base);
|
|
1068
|
-
|
|
1069
|
-
|
|
1350
|
+
return [...RELATIVE_PROBE_SUFFIXES.map((suffix) => `${base}${suffix}`), ...(NODE_NEXT_EXTENSION_MAP[explicitExtension] ?? []).map((sourceExtension) => `${base.slice(0, -explicitExtension.length)}${sourceExtension}`)];
|
|
1351
|
+
}
|
|
1352
|
+
function resolveRelativeSource(importerPath, specifier, files) {
|
|
1353
|
+
for (const candidate of relativeSourceCandidates(importerPath, specifier)) {
|
|
1070
1354
|
const actualPath = files.get(candidate);
|
|
1071
1355
|
if (actualPath !== void 0) return actualPath;
|
|
1072
1356
|
}
|
|
@@ -1371,7 +1655,8 @@ function createSourceIngestor(host) {
|
|
|
1371
1655
|
const fatal = diagnostics.filter((diagnostic) => !isAdvisorySourceDiagnostic(diagnostic));
|
|
1372
1656
|
const invalidOriginals = new Set(fatal.map((diagnostic) => diagnostic.originalPath));
|
|
1373
1657
|
if (diagnostics.length === 0) return invalidOriginals;
|
|
1374
|
-
|
|
1658
|
+
const reportError = host.strict() ? host.reportErrors?.() : void 0;
|
|
1659
|
+
if (host.strict() && fatal.length > 0 && !reportError) {
|
|
1375
1660
|
const lines = fatal.map((diagnostic) => `${diagnostic.code} ${diagnostic.originalPath}: ${diagnostic.message} (${SKIPPED_SOURCE_COST})`);
|
|
1376
1661
|
throw new Error(`${host.prefix} ${lines.join(`\n${host.prefix} `)}`);
|
|
1377
1662
|
}
|
|
@@ -1385,7 +1670,8 @@ function createSourceIngestor(host) {
|
|
|
1385
1670
|
}
|
|
1386
1671
|
if (warned.has(line)) continue;
|
|
1387
1672
|
warned.add(line);
|
|
1388
|
-
|
|
1673
|
+
if (reportError && skipped) reportError(`${host.prefix} ${line}`);
|
|
1674
|
+
else host.warn(`${host.prefix} ${line}`);
|
|
1389
1675
|
}
|
|
1390
1676
|
return invalidOriginals;
|
|
1391
1677
|
},
|
|
@@ -1412,7 +1698,9 @@ function createV2EngineApi(deps) {
|
|
|
1412
1698
|
return {
|
|
1413
1699
|
loadSystemModule: (...args) => native.loadSystemModule(...args),
|
|
1414
1700
|
extractFacts: (filesJson) => native.extractFacts(filesJson),
|
|
1415
|
-
|
|
1701
|
+
moduleSpecifiers: (filesJson) => native.moduleSpecifiers(filesJson),
|
|
1702
|
+
diagnosticCodes: () => native.diagnosticCodes(),
|
|
1703
|
+
analyzeProject: (filesJsonRaw, scalesJson, variableMapJson, contextualVarsJson, propConfigJson, groupRegistryJson, packageResolutionJson, devMode, emitterConfigJson, selectorAliasesJson, _selectorOrderJson, globalStyleBlocksJson, pathAliasesJson, keyframesJson, staticCssJson, conditionAliasesJson, externalDirsJson, transformSourcesJson, transformProvenanceJson, declarationScalesJson, analysisContextJson, generatedNamesJson) => {
|
|
1416
1704
|
const filesJson = deps.rehydrateFilesJson ? deps.rehydrateFilesJson(filesJsonRaw) : filesJsonRaw;
|
|
1417
1705
|
const sent = /* @__PURE__ */ new Map();
|
|
1418
1706
|
for (const entry of parseFilesJson(filesJson, label)) sent.set(entry.path, entry.source);
|
|
@@ -1440,6 +1728,7 @@ function createV2EngineApi(deps) {
|
|
|
1440
1728
|
transformProvenanceJson: transformProvenanceJson ?? void 0,
|
|
1441
1729
|
declarationScalesJson,
|
|
1442
1730
|
analysisContextJson,
|
|
1731
|
+
generatedNamesJson,
|
|
1443
1732
|
devMode
|
|
1444
1733
|
};
|
|
1445
1734
|
const engine = new native.ExtractEngine(config);
|
|
@@ -1477,6 +1766,10 @@ function assertNoRetiredEngineSelection(engineOption) {
|
|
|
1477
1766
|
}
|
|
1478
1767
|
//#endregion
|
|
1479
1768
|
//#region pipeline/core-options.ts
|
|
1769
|
+
/**
|
|
1770
|
+
* One option schema shared by the Vite, Next, and CLI drivers: key semantics,
|
|
1771
|
+
* default exclusions, and mode authority cannot drift per driver.
|
|
1772
|
+
*/
|
|
1480
1773
|
const DRIVER_NAMESPACES = [
|
|
1481
1774
|
"vite",
|
|
1482
1775
|
"next",
|
|
@@ -1510,6 +1803,7 @@ const CORE_OPTION_KEYS = /* @__PURE__ */ new Set([
|
|
|
1510
1803
|
"exclude",
|
|
1511
1804
|
"extensions",
|
|
1512
1805
|
"strict",
|
|
1806
|
+
"diagnostics",
|
|
1513
1807
|
"verbose",
|
|
1514
1808
|
"prefix",
|
|
1515
1809
|
"prefixContextualVars",
|
|
@@ -1569,6 +1863,7 @@ const isString = (value) => Object(value) !== value && Object.prototype.toString
|
|
|
1569
1863
|
const isBoolean = (value) => Object(value) !== value && Object.prototype.toString.call(value) === "[object Boolean]";
|
|
1570
1864
|
const isVerbose = (value) => isBoolean(value) || value === "trace";
|
|
1571
1865
|
const isStringArray = (value) => Array.isArray(value) && value.every(isString);
|
|
1866
|
+
const isDiagnosticLevels = (value) => Object.prototype.toString.call(value) === "[object Object]" && Object.values(Object(value)).every((level) => isString(level) && DIAGNOSTIC_LEVELS.has(level));
|
|
1572
1867
|
const isStringOrStringArray = (value) => isString(value) || isStringArray(value);
|
|
1573
1868
|
const CORE_VALUE_GATES = [
|
|
1574
1869
|
{
|
|
@@ -1591,6 +1886,11 @@ const CORE_VALUE_GATES = [
|
|
|
1591
1886
|
ok: isBoolean,
|
|
1592
1887
|
expected: "a boolean"
|
|
1593
1888
|
},
|
|
1889
|
+
{
|
|
1890
|
+
key: "diagnostics",
|
|
1891
|
+
ok: isDiagnosticLevels,
|
|
1892
|
+
expected: "an object mapping codes to 'off', 'info', 'warn' or 'error'"
|
|
1893
|
+
},
|
|
1594
1894
|
{
|
|
1595
1895
|
key: "verbose",
|
|
1596
1896
|
ok: isVerbose,
|
|
@@ -1764,177 +2064,69 @@ function walkFiles(dir, rootDir, exclude, extensionsSet) {
|
|
|
1764
2064
|
return results;
|
|
1765
2065
|
}
|
|
1766
2066
|
//#endregion
|
|
1767
|
-
//#region pipeline/
|
|
1768
|
-
/**
|
|
1769
|
-
* One derivation of source identity for discovery, watch ingestion, ownership
|
|
1770
|
-
* and deletion: event paths are lookup INPUTS only and never fork identity.
|
|
1771
|
-
*/
|
|
2067
|
+
//#region pipeline/tsconfig-paths.ts
|
|
1772
2068
|
/**
|
|
1773
|
-
*
|
|
1774
|
-
*
|
|
2069
|
+
* tsconfig `paths` → alias pairs, for drivers with no live bundler config.
|
|
2070
|
+
* The nearest config declaring `paths` wins wholesale — TS never merges.
|
|
1775
2071
|
*/
|
|
1776
|
-
function
|
|
1777
|
-
|
|
1778
|
-
|
|
2072
|
+
function stripJsonc(text) {
|
|
2073
|
+
let out = "";
|
|
2074
|
+
let inString = false;
|
|
2075
|
+
let inLine = false;
|
|
2076
|
+
let inBlock = false;
|
|
2077
|
+
for (let i = 0; i < text.length; i++) {
|
|
2078
|
+
const ch = text[i];
|
|
2079
|
+
const next = text[i + 1];
|
|
2080
|
+
if (inLine) {
|
|
2081
|
+
if (ch === "\n") {
|
|
2082
|
+
inLine = false;
|
|
2083
|
+
out += ch;
|
|
2084
|
+
}
|
|
2085
|
+
continue;
|
|
2086
|
+
}
|
|
2087
|
+
if (inBlock) {
|
|
2088
|
+
if (ch === "*" && next === "/") {
|
|
2089
|
+
inBlock = false;
|
|
2090
|
+
i++;
|
|
2091
|
+
}
|
|
2092
|
+
continue;
|
|
2093
|
+
}
|
|
2094
|
+
if (inString) {
|
|
2095
|
+
out += ch;
|
|
2096
|
+
if (ch === "\\") {
|
|
2097
|
+
out += next ?? "";
|
|
2098
|
+
i++;
|
|
2099
|
+
} else if (ch === "\"") inString = false;
|
|
2100
|
+
continue;
|
|
2101
|
+
}
|
|
2102
|
+
if (ch === "\"") {
|
|
2103
|
+
inString = true;
|
|
2104
|
+
out += ch;
|
|
2105
|
+
continue;
|
|
2106
|
+
}
|
|
2107
|
+
if (ch === "/" && next === "/") {
|
|
2108
|
+
inLine = true;
|
|
2109
|
+
i++;
|
|
2110
|
+
continue;
|
|
2111
|
+
}
|
|
2112
|
+
if (ch === "/" && next === "*") {
|
|
2113
|
+
inBlock = true;
|
|
2114
|
+
i++;
|
|
2115
|
+
continue;
|
|
2116
|
+
}
|
|
2117
|
+
out += ch;
|
|
2118
|
+
}
|
|
2119
|
+
return out.replace(/,(\s*[}\]])/g, "$1");
|
|
1779
2120
|
}
|
|
1780
|
-
/**
|
|
1781
|
-
*
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
function sharesVolumeRoot(a, b, pathApi = node_path.default) {
|
|
1785
|
-
const volumeOf = (p) => pathApi.parse(pathApi.resolve(p)).root.toLowerCase();
|
|
1786
|
-
return volumeOf(a) === volumeOf(b);
|
|
2121
|
+
/** `Object(value) === value` holds for exactly the blocks and lists
|
|
2122
|
+
* `JSON.parse` produces; the `[object Object]` tag separates the two. */
|
|
2123
|
+
function isJsonBlock(value) {
|
|
2124
|
+
return Object(value) === value && Object.prototype.toString.call(value) === "[object Object]";
|
|
1787
2125
|
}
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
return null;
|
|
1793
|
-
}
|
|
1794
|
-
}
|
|
1795
|
-
function createSourceIdentity(rootDir) {
|
|
1796
|
-
const normalizedRootDir = node_path.default.normalize(rootDir);
|
|
1797
|
-
const canonicalRootDir = canonicalize(normalizedRootDir) ?? normalizedRootDir;
|
|
1798
|
-
const roots = [];
|
|
1799
|
-
const aliases = /* @__PURE__ */ new Map();
|
|
1800
|
-
const owningRecord = (canonicalPath) => {
|
|
1801
|
-
let owner = null;
|
|
1802
|
-
for (const record of roots) {
|
|
1803
|
-
if (!isPathWithinRoot(record.canonical, canonicalPath)) continue;
|
|
1804
|
-
if (!owner || record.canonical.length > owner.canonical.length) owner = record;
|
|
1805
|
-
}
|
|
1806
|
-
return owner;
|
|
1807
|
-
};
|
|
1808
|
-
return {
|
|
1809
|
-
rootDir: normalizedRootDir,
|
|
1810
|
-
canonicalRootDir,
|
|
1811
|
-
registerExternalRoot(root) {
|
|
1812
|
-
const asRegistered = node_path.default.normalize(root);
|
|
1813
|
-
const canonical = canonicalize(asRegistered) ?? asRegistered;
|
|
1814
|
-
const existing = roots.find((record) => record.canonical === canonical);
|
|
1815
|
-
if (existing) return existing.canonical;
|
|
1816
|
-
roots.push({
|
|
1817
|
-
asRegistered,
|
|
1818
|
-
canonical
|
|
1819
|
-
});
|
|
1820
|
-
return canonical;
|
|
1821
|
-
},
|
|
1822
|
-
externalRoots() {
|
|
1823
|
-
return roots.map((record) => record.canonical);
|
|
1824
|
-
},
|
|
1825
|
-
resolveSourceId(inputPath) {
|
|
1826
|
-
const lexical = node_path.default.normalize(inputPath);
|
|
1827
|
-
const canonical = canonicalize(lexical);
|
|
1828
|
-
if (canonical === null) return null;
|
|
1829
|
-
const owner = owningRecord(canonical);
|
|
1830
|
-
const ownedBy = (record) => {
|
|
1831
|
-
const pathInRoot = node_path.default.relative(record.canonical, canonical);
|
|
1832
|
-
return {
|
|
1833
|
-
sourceKey: node_path.default.relative(normalizedRootDir, node_path.default.join(record.asRegistered, pathInRoot)),
|
|
1834
|
-
owningRoot: record.canonical,
|
|
1835
|
-
pathInRoot
|
|
1836
|
-
};
|
|
1837
|
-
};
|
|
1838
|
-
const ownerIsMoreSpecific = owner !== null && owner.canonical.length > canonicalRootDir.length;
|
|
1839
|
-
let resolved;
|
|
1840
|
-
if (!ownerIsMoreSpecific && isPathWithinRoot(canonicalRootDir, canonical)) {
|
|
1841
|
-
const key = node_path.default.relative(canonicalRootDir, canonical);
|
|
1842
|
-
resolved = {
|
|
1843
|
-
sourceKey: key,
|
|
1844
|
-
owningRoot: null,
|
|
1845
|
-
pathInRoot: key
|
|
1846
|
-
};
|
|
1847
|
-
} else if (owner) resolved = ownedBy(owner);
|
|
1848
|
-
else return null;
|
|
1849
|
-
aliases.set(lexical, resolved);
|
|
1850
|
-
aliases.set(canonical, resolved);
|
|
1851
|
-
return resolved;
|
|
1852
|
-
},
|
|
1853
|
-
resolveDeletedSourceId(inputPath) {
|
|
1854
|
-
return aliases.get(node_path.default.normalize(inputPath)) ?? null;
|
|
1855
|
-
},
|
|
1856
|
-
containingExternalRoot(inputPath) {
|
|
1857
|
-
const lexical = node_path.default.normalize(inputPath);
|
|
1858
|
-
let owner = null;
|
|
1859
|
-
for (const record of roots) {
|
|
1860
|
-
if (!(isPathWithinRoot(record.canonical, lexical) || isPathWithinRoot(record.asRegistered, lexical))) continue;
|
|
1861
|
-
if (!owner || record.canonical.length > owner.canonical.length) owner = record;
|
|
1862
|
-
}
|
|
1863
|
-
if (owner) return owner.canonical;
|
|
1864
|
-
const canonical = canonicalize(lexical);
|
|
1865
|
-
if (canonical === null) return null;
|
|
1866
|
-
for (const record of roots) {
|
|
1867
|
-
if (!isPathWithinRoot(record.canonical, canonical)) continue;
|
|
1868
|
-
if (!owner || record.canonical.length > owner.canonical.length) owner = record;
|
|
1869
|
-
}
|
|
1870
|
-
return owner?.canonical ?? null;
|
|
1871
|
-
}
|
|
1872
|
-
};
|
|
1873
|
-
}
|
|
1874
|
-
//#endregion
|
|
1875
|
-
//#region pipeline/tsconfig-paths.ts
|
|
1876
|
-
/**
|
|
1877
|
-
* tsconfig `paths` → alias pairs, for drivers with no live bundler config.
|
|
1878
|
-
* The nearest config declaring `paths` wins wholesale — TS never merges.
|
|
1879
|
-
*/
|
|
1880
|
-
function stripJsonc(text) {
|
|
1881
|
-
let out = "";
|
|
1882
|
-
let inString = false;
|
|
1883
|
-
let inLine = false;
|
|
1884
|
-
let inBlock = false;
|
|
1885
|
-
for (let i = 0; i < text.length; i++) {
|
|
1886
|
-
const ch = text[i];
|
|
1887
|
-
const next = text[i + 1];
|
|
1888
|
-
if (inLine) {
|
|
1889
|
-
if (ch === "\n") {
|
|
1890
|
-
inLine = false;
|
|
1891
|
-
out += ch;
|
|
1892
|
-
}
|
|
1893
|
-
continue;
|
|
1894
|
-
}
|
|
1895
|
-
if (inBlock) {
|
|
1896
|
-
if (ch === "*" && next === "/") {
|
|
1897
|
-
inBlock = false;
|
|
1898
|
-
i++;
|
|
1899
|
-
}
|
|
1900
|
-
continue;
|
|
1901
|
-
}
|
|
1902
|
-
if (inString) {
|
|
1903
|
-
out += ch;
|
|
1904
|
-
if (ch === "\\") {
|
|
1905
|
-
out += next ?? "";
|
|
1906
|
-
i++;
|
|
1907
|
-
} else if (ch === "\"") inString = false;
|
|
1908
|
-
continue;
|
|
1909
|
-
}
|
|
1910
|
-
if (ch === "\"") {
|
|
1911
|
-
inString = true;
|
|
1912
|
-
out += ch;
|
|
1913
|
-
continue;
|
|
1914
|
-
}
|
|
1915
|
-
if (ch === "/" && next === "/") {
|
|
1916
|
-
inLine = true;
|
|
1917
|
-
i++;
|
|
1918
|
-
continue;
|
|
1919
|
-
}
|
|
1920
|
-
if (ch === "/" && next === "*") {
|
|
1921
|
-
inBlock = true;
|
|
1922
|
-
i++;
|
|
1923
|
-
continue;
|
|
1924
|
-
}
|
|
1925
|
-
out += ch;
|
|
1926
|
-
}
|
|
1927
|
-
return out.replace(/,(\s*[}\]])/g, "$1");
|
|
1928
|
-
}
|
|
1929
|
-
/** `Object(value) === value` holds for exactly the blocks and lists
|
|
1930
|
-
* `JSON.parse` produces; the `[object Object]` tag separates the two. */
|
|
1931
|
-
function isJsonBlock(value) {
|
|
1932
|
-
return Object(value) === value && Object.prototype.toString.call(value) === "[object Object]";
|
|
1933
|
-
}
|
|
1934
|
-
/** A block or a list. A declared `paths` OWNS the setting and blocks its
|
|
1935
|
-
* parents' even when it contributes nothing usable. */
|
|
1936
|
-
function isJsonKeyed(value) {
|
|
1937
|
-
return Object(value) === value;
|
|
2126
|
+
/** A block or a list. A declared `paths` OWNS the setting and blocks its
|
|
2127
|
+
* parents' even when it contributes nothing usable. */
|
|
2128
|
+
function isJsonKeyed(value) {
|
|
2129
|
+
return Object(value) === value;
|
|
1938
2130
|
}
|
|
1939
2131
|
/** A JSON string, excluding the boxed `String` object, which no path join
|
|
1940
2132
|
* accepts. */
|
|
@@ -1955,10 +2147,10 @@ function decodePaths(value) {
|
|
|
1955
2147
|
function decodeExtends(value) {
|
|
1956
2148
|
return (Array.isArray(value) ? value : [value]).filter(isJsonString);
|
|
1957
2149
|
}
|
|
1958
|
-
function readConfig(path$
|
|
2150
|
+
function readConfig(path$8) {
|
|
1959
2151
|
let raw;
|
|
1960
2152
|
try {
|
|
1961
|
-
raw = (0, fs.readFileSync)(path$
|
|
2153
|
+
raw = (0, fs.readFileSync)(path$8, "utf-8");
|
|
1962
2154
|
} catch {
|
|
1963
2155
|
return null;
|
|
1964
2156
|
}
|
|
@@ -1971,7 +2163,7 @@ function readConfig(path$4) {
|
|
|
1971
2163
|
const root = isJsonBlock(parsed) ? parsed : {};
|
|
1972
2164
|
const compilerOptions = isJsonBlock(root.compilerOptions) ? root.compilerOptions : {};
|
|
1973
2165
|
return {
|
|
1974
|
-
dir: (0, path.dirname)(path$
|
|
2166
|
+
dir: (0, path.dirname)(path$8),
|
|
1975
2167
|
baseUrl: isJsonString(compilerOptions.baseUrl) ? compilerOptions.baseUrl : null,
|
|
1976
2168
|
paths: decodePaths(compilerOptions.paths),
|
|
1977
2169
|
extends: decodeExtends(root.extends)
|
|
@@ -2003,8 +2195,8 @@ function loadChain(entryPath) {
|
|
|
2003
2195
|
const visited = /* @__PURE__ */ new Set();
|
|
2004
2196
|
const queue = [entryPath];
|
|
2005
2197
|
while (queue.length > 0) {
|
|
2006
|
-
const path$
|
|
2007
|
-
const key = (0, path.resolve)(path$
|
|
2198
|
+
const path$9 = queue.shift();
|
|
2199
|
+
const key = (0, path.resolve)(path$9);
|
|
2008
2200
|
if (visited.has(key)) continue;
|
|
2009
2201
|
visited.add(key);
|
|
2010
2202
|
const node = readConfig(key);
|
|
@@ -2054,6 +2246,216 @@ function readTsconfigAliasPairs(rootDir) {
|
|
|
2054
2246
|
return pairs;
|
|
2055
2247
|
}
|
|
2056
2248
|
//#endregion
|
|
2249
|
+
//#region pipeline/kit-descriptor.ts
|
|
2250
|
+
/** A kit's description, which `animus build --kit` writes at its package
|
|
2251
|
+
* root and the kit exports as `./animus.json`. */
|
|
2252
|
+
const KIT_DESCRIPTOR_FILE = "animus.json";
|
|
2253
|
+
/** A path with `/` separators, as descriptors key their components. */
|
|
2254
|
+
function posixPath$1(path) {
|
|
2255
|
+
return path.split(node_path.sep).join("/");
|
|
2256
|
+
}
|
|
2257
|
+
/** The descriptor of the kit a build analysed. */
|
|
2258
|
+
function buildKitDescriptor(manifest) {
|
|
2259
|
+
const components = {};
|
|
2260
|
+
for (const id of Object.keys(manifest.components).sort()) {
|
|
2261
|
+
const component = manifest.components[id];
|
|
2262
|
+
components[posixPath$1(id)] = {
|
|
2263
|
+
definition: component.definition_fingerprint,
|
|
2264
|
+
runtimeProps: [...new Set(component.system_prop_names)].sort()
|
|
2265
|
+
};
|
|
2266
|
+
}
|
|
2267
|
+
return {
|
|
2268
|
+
format: 1,
|
|
2269
|
+
system: manifest.system_fingerprint,
|
|
2270
|
+
components
|
|
2271
|
+
};
|
|
2272
|
+
}
|
|
2273
|
+
/** Reads the descriptor at a kit's package root; `null` when it has none. */
|
|
2274
|
+
function readKitDescriptor(pkgRoot, rootDir) {
|
|
2275
|
+
const path = (0, node_path.join)(pkgRoot, KIT_DESCRIPTOR_FILE);
|
|
2276
|
+
if (!(0, node_fs.existsSync)(path)) return null;
|
|
2277
|
+
const packageRoot = posixPath$1((0, node_path.relative)(rootDir, pkgRoot));
|
|
2278
|
+
let wire;
|
|
2279
|
+
try {
|
|
2280
|
+
wire = JSON.parse((0, node_fs.readFileSync)(path, "utf-8"));
|
|
2281
|
+
} catch {
|
|
2282
|
+
return {
|
|
2283
|
+
packageRoot,
|
|
2284
|
+
unreadable: "is not valid JSON: rebuild it with animus build --kit"
|
|
2285
|
+
};
|
|
2286
|
+
}
|
|
2287
|
+
const block = isJsonBlock(wire) ? wire : {};
|
|
2288
|
+
if (block.format !== 1) return {
|
|
2289
|
+
packageRoot,
|
|
2290
|
+
unreadable: `declares descriptor format ${JSON.stringify(block.format ?? null)}, which this Animus does not read (it reads format 1): upgrade Animus, or install a kit built for this version`
|
|
2291
|
+
};
|
|
2292
|
+
const definitions = {};
|
|
2293
|
+
const components = isJsonBlock(block.components) ? block.components : {};
|
|
2294
|
+
for (const [id, component] of Object.entries(components)) {
|
|
2295
|
+
const definition = isJsonBlock(component) ? component.definition : null;
|
|
2296
|
+
if (isJsonString(definition)) definitions[posixPath$1(id)] = definition;
|
|
2297
|
+
}
|
|
2298
|
+
return {
|
|
2299
|
+
packageRoot,
|
|
2300
|
+
definitions
|
|
2301
|
+
};
|
|
2302
|
+
}
|
|
2303
|
+
/**
|
|
2304
|
+
* A descriptor in a format this Animus cannot read fails the build; one
|
|
2305
|
+
* whose component definitions differ from the kit's shipped source, as the
|
|
2306
|
+
* analysis read it, or that describes a component its analysed file no
|
|
2307
|
+
* longer declares, was built from other source and is stale.
|
|
2308
|
+
*/
|
|
2309
|
+
function kitDescriptorDiagnostics(records, manifest) {
|
|
2310
|
+
if (records.length === 0) return [];
|
|
2311
|
+
const diagnostics = [];
|
|
2312
|
+
const analysedById = new Map(Object.entries(manifest.components).map(([id, component]) => [posixPath$1(id), component]));
|
|
2313
|
+
const analysedFiles = new Set(Object.keys(manifest.fileFacts).map(posixPath$1));
|
|
2314
|
+
const declared = new Set(Object.entries(manifest.fileFacts).flatMap(([file, facts]) => facts.chains.map((chain) => `${posixPath$1(file)}::${chain.descriptor.binding}`)));
|
|
2315
|
+
for (const record of records) {
|
|
2316
|
+
const file = `${record.packageRoot}/${KIT_DESCRIPTOR_FILE}`;
|
|
2317
|
+
if ("unreadable" in record) {
|
|
2318
|
+
diagnostics.push({
|
|
2319
|
+
file,
|
|
2320
|
+
component: "kit",
|
|
2321
|
+
kind: "error",
|
|
2322
|
+
message: record.unreadable,
|
|
2323
|
+
code: KIT_UNSUPPORTED_FORMAT,
|
|
2324
|
+
severity: severityFor(KIT_UNSUPPORTED_FORMAT)
|
|
2325
|
+
});
|
|
2326
|
+
continue;
|
|
2327
|
+
}
|
|
2328
|
+
for (const [id, definition] of Object.entries(record.definitions)) {
|
|
2329
|
+
const key = `${record.packageRoot}/${id}`;
|
|
2330
|
+
const analysed = analysedById.get(key);
|
|
2331
|
+
const fileAnalysed = analysedFiles.has(key.slice(0, key.lastIndexOf("::")));
|
|
2332
|
+
let stale;
|
|
2333
|
+
if (analysed) {
|
|
2334
|
+
if (analysed.definition_fingerprint === definition) continue;
|
|
2335
|
+
stale = `describes ${id} with a definition other than the one its shipped source declares`;
|
|
2336
|
+
} else if (fileAnalysed && !declared.has(key)) stale = `describes ${id}, which its shipped source no longer declares`;
|
|
2337
|
+
else continue;
|
|
2338
|
+
diagnostics.push({
|
|
2339
|
+
file,
|
|
2340
|
+
component: analysed?.binding ?? id.slice(id.lastIndexOf("::") + 2),
|
|
2341
|
+
kind: "warn",
|
|
2342
|
+
message: `${stale}, so the descriptor is stale: rebuild it with animus build --kit`,
|
|
2343
|
+
code: KIT_STALE_DESCRIPTOR,
|
|
2344
|
+
severity: severityFor(KIT_STALE_DESCRIPTOR)
|
|
2345
|
+
});
|
|
2346
|
+
}
|
|
2347
|
+
}
|
|
2348
|
+
return diagnostics;
|
|
2349
|
+
}
|
|
2350
|
+
//#endregion
|
|
2351
|
+
//#region pipeline/source-identity.ts
|
|
2352
|
+
/**
|
|
2353
|
+
* One derivation of source identity for discovery, watch ingestion, ownership
|
|
2354
|
+
* and deletion: event paths are lookup INPUTS only and never fork identity.
|
|
2355
|
+
*/
|
|
2356
|
+
/**
|
|
2357
|
+
* True when `target` is `root` or a descendant, decided by `relative()`
|
|
2358
|
+
* shape: string prefixing would let `/ui` claim `/ui-old`.
|
|
2359
|
+
*/
|
|
2360
|
+
function isPathWithinRoot(root, target, pathApi = node_path.default) {
|
|
2361
|
+
const rel = pathApi.relative(root, target);
|
|
2362
|
+
return rel === "" || !pathApi.isAbsolute(rel) && rel !== ".." && !rel.startsWith(".." + pathApi.sep);
|
|
2363
|
+
}
|
|
2364
|
+
/**
|
|
2365
|
+
* True when two paths share a platform root (win32 drive letter or UNC
|
|
2366
|
+
* share; always true on posix). Drive letters compare case-insensitively.
|
|
2367
|
+
*/
|
|
2368
|
+
function sharesVolumeRoot(a, b, pathApi = node_path.default) {
|
|
2369
|
+
const volumeOf = (p) => pathApi.parse(pathApi.resolve(p)).root.toLowerCase();
|
|
2370
|
+
return volumeOf(a) === volumeOf(b);
|
|
2371
|
+
}
|
|
2372
|
+
function canonicalize(p) {
|
|
2373
|
+
try {
|
|
2374
|
+
return (0, node_fs.realpathSync)(p);
|
|
2375
|
+
} catch {
|
|
2376
|
+
return null;
|
|
2377
|
+
}
|
|
2378
|
+
}
|
|
2379
|
+
function createSourceIdentity(rootDir) {
|
|
2380
|
+
const normalizedRootDir = node_path.default.normalize(rootDir);
|
|
2381
|
+
const canonicalRootDir = canonicalize(normalizedRootDir) ?? normalizedRootDir;
|
|
2382
|
+
const roots = [];
|
|
2383
|
+
const aliases = /* @__PURE__ */ new Map();
|
|
2384
|
+
const owningRecord = (canonicalPath) => {
|
|
2385
|
+
let owner = null;
|
|
2386
|
+
for (const record of roots) {
|
|
2387
|
+
if (!isPathWithinRoot(record.canonical, canonicalPath)) continue;
|
|
2388
|
+
if (!owner || record.canonical.length > owner.canonical.length) owner = record;
|
|
2389
|
+
}
|
|
2390
|
+
return owner;
|
|
2391
|
+
};
|
|
2392
|
+
return {
|
|
2393
|
+
rootDir: normalizedRootDir,
|
|
2394
|
+
canonicalRootDir,
|
|
2395
|
+
registerExternalRoot(root) {
|
|
2396
|
+
const asRegistered = node_path.default.normalize(root);
|
|
2397
|
+
const canonical = canonicalize(asRegistered) ?? asRegistered;
|
|
2398
|
+
const existing = roots.find((record) => record.canonical === canonical);
|
|
2399
|
+
if (existing) return existing.canonical;
|
|
2400
|
+
roots.push({
|
|
2401
|
+
asRegistered,
|
|
2402
|
+
canonical
|
|
2403
|
+
});
|
|
2404
|
+
return canonical;
|
|
2405
|
+
},
|
|
2406
|
+
externalRoots() {
|
|
2407
|
+
return roots.map((record) => record.canonical);
|
|
2408
|
+
},
|
|
2409
|
+
resolveSourceId(inputPath) {
|
|
2410
|
+
const lexical = node_path.default.normalize(inputPath);
|
|
2411
|
+
const canonical = canonicalize(lexical);
|
|
2412
|
+
if (canonical === null) return null;
|
|
2413
|
+
const owner = owningRecord(canonical);
|
|
2414
|
+
const ownedBy = (record) => {
|
|
2415
|
+
const pathInRoot = node_path.default.relative(record.canonical, canonical);
|
|
2416
|
+
return {
|
|
2417
|
+
sourceKey: node_path.default.relative(normalizedRootDir, node_path.default.join(record.asRegistered, pathInRoot)),
|
|
2418
|
+
owningRoot: record.canonical,
|
|
2419
|
+
pathInRoot
|
|
2420
|
+
};
|
|
2421
|
+
};
|
|
2422
|
+
const ownerIsMoreSpecific = owner !== null && owner.canonical.length > canonicalRootDir.length;
|
|
2423
|
+
let resolved;
|
|
2424
|
+
if (!ownerIsMoreSpecific && isPathWithinRoot(canonicalRootDir, canonical)) {
|
|
2425
|
+
const key = node_path.default.relative(canonicalRootDir, canonical);
|
|
2426
|
+
resolved = {
|
|
2427
|
+
sourceKey: key,
|
|
2428
|
+
owningRoot: null,
|
|
2429
|
+
pathInRoot: key
|
|
2430
|
+
};
|
|
2431
|
+
} else if (owner) resolved = ownedBy(owner);
|
|
2432
|
+
else return null;
|
|
2433
|
+
aliases.set(lexical, resolved);
|
|
2434
|
+
aliases.set(canonical, resolved);
|
|
2435
|
+
return resolved;
|
|
2436
|
+
},
|
|
2437
|
+
resolveDeletedSourceId(inputPath) {
|
|
2438
|
+
return aliases.get(node_path.default.normalize(inputPath)) ?? null;
|
|
2439
|
+
},
|
|
2440
|
+
containingExternalRoot(inputPath) {
|
|
2441
|
+
const lexical = node_path.default.normalize(inputPath);
|
|
2442
|
+
let owner = null;
|
|
2443
|
+
for (const record of roots) {
|
|
2444
|
+
if (!(isPathWithinRoot(record.canonical, lexical) || isPathWithinRoot(record.asRegistered, lexical))) continue;
|
|
2445
|
+
if (!owner || record.canonical.length > owner.canonical.length) owner = record;
|
|
2446
|
+
}
|
|
2447
|
+
if (owner) return owner.canonical;
|
|
2448
|
+
const canonical = canonicalize(lexical);
|
|
2449
|
+
if (canonical === null) return null;
|
|
2450
|
+
for (const record of roots) {
|
|
2451
|
+
if (!isPathWithinRoot(record.canonical, canonical)) continue;
|
|
2452
|
+
if (!owner || record.canonical.length > owner.canonical.length) owner = record;
|
|
2453
|
+
}
|
|
2454
|
+
return owner?.canonical ?? null;
|
|
2455
|
+
}
|
|
2456
|
+
};
|
|
2457
|
+
}
|
|
2458
|
+
//#endregion
|
|
2057
2459
|
//#region pipeline/discover-packages.ts
|
|
2058
2460
|
function findPackageRoot(absEntryPath) {
|
|
2059
2461
|
let pkgRoot = (0, path.dirname)(absEntryPath);
|
|
@@ -2079,7 +2481,7 @@ const PACKAGE_OUTPUT_EXCLUDES = [
|
|
|
2079
2481
|
".d.ts",
|
|
2080
2482
|
".map"
|
|
2081
2483
|
];
|
|
2082
|
-
const isFile = (path$2) => {
|
|
2484
|
+
const isFile$1 = (path$2) => {
|
|
2083
2485
|
try {
|
|
2084
2486
|
return (0, fs.statSync)(path$2).isFile();
|
|
2085
2487
|
} catch {
|
|
@@ -2091,8 +2493,9 @@ function resolveAbsolutePathSpecifier(absSpecifier, extensionsSet) {
|
|
|
2091
2493
|
absSpecifier,
|
|
2092
2494
|
...Array.from(extensionsSet, (ext) => absSpecifier + ext),
|
|
2093
2495
|
...Array.from(extensionsSet, (ext) => (0, path.join)(absSpecifier, `index${ext}`))
|
|
2094
|
-
].find(isFile) ?? null;
|
|
2496
|
+
].find(isFile$1) ?? null;
|
|
2095
2497
|
}
|
|
2498
|
+
/** The package a bare specifier names: `@scope/name` or `name`. */
|
|
2096
2499
|
function bareSpecifierPackageName(specifier) {
|
|
2097
2500
|
const segments = specifier.split("/");
|
|
2098
2501
|
return specifier.startsWith("@") ? segments.slice(0, 2).join("/") : segments[0];
|
|
@@ -2107,6 +2510,76 @@ function sourceEntryForSpecifier(specifier, srcDir, extensionsSet) {
|
|
|
2107
2510
|
const subpath = specifier.slice(packageName.length + 1);
|
|
2108
2511
|
return resolveAbsolutePathSpecifier((0, path.join)(srcDir, subpath || "index"), extensionsSet);
|
|
2109
2512
|
}
|
|
2513
|
+
/** The package's parsed `package.json`, or null when it cannot be read. */
|
|
2514
|
+
function readPackageManifest(pkgRoot) {
|
|
2515
|
+
try {
|
|
2516
|
+
return JSON.parse((0, fs.readFileSync)((0, path.join)(pkgRoot, "package.json"), "utf-8"));
|
|
2517
|
+
} catch {
|
|
2518
|
+
return null;
|
|
2519
|
+
}
|
|
2520
|
+
}
|
|
2521
|
+
/** Each `exports` subpath and its value; a bare target or condition object
|
|
2522
|
+
* is the `.` entry. */
|
|
2523
|
+
function exportsSubpaths(manifest) {
|
|
2524
|
+
const exports = isJsonBlock(manifest) ? manifest.exports : void 0;
|
|
2525
|
+
if (exports === void 0 || exports === null) return [];
|
|
2526
|
+
const subpaths = isJsonBlock(exports) && Object.keys(exports).some((k) => k.startsWith(".")) ? exports : { ".": exports };
|
|
2527
|
+
return Object.entries(subpaths).filter(([subpath]) => subpath.startsWith("."));
|
|
2528
|
+
}
|
|
2529
|
+
/**
|
|
2530
|
+
* The export condition a kit gives each public entry's original source:
|
|
2531
|
+
* `{ "types": …, "animus": "./src/system.ts", "import": …, "default": … }`.
|
|
2532
|
+
* Hosts never resolve through it; discovery reads it, redirects the entry to
|
|
2533
|
+
* its source in every host, and the system loader prefers it.
|
|
2534
|
+
*/
|
|
2535
|
+
const KIT_SOURCE_CONDITION = "animus";
|
|
2536
|
+
/** The package's kit source condition, or null when no exact `exports`
|
|
2537
|
+
* entry declares it: the package is no source kit. */
|
|
2538
|
+
function readKitSourceCondition(pkgRoot) {
|
|
2539
|
+
const condition = {
|
|
2540
|
+
entries: [],
|
|
2541
|
+
invalid: [],
|
|
2542
|
+
root: null
|
|
2543
|
+
};
|
|
2544
|
+
for (const [subpath, value] of exportsSubpaths(readPackageManifest(pkgRoot))) {
|
|
2545
|
+
if (subpath.includes("*") || !isJsonBlock(value)) continue;
|
|
2546
|
+
const target = value[KIT_SOURCE_CONDITION];
|
|
2547
|
+
if (!isJsonString(target)) continue;
|
|
2548
|
+
const file = (0, path.resolve)(pkgRoot, target);
|
|
2549
|
+
if (isPathWithinRoot(pkgRoot, file) && isFile$1(file)) condition.entries.push([subpath, file]);
|
|
2550
|
+
else condition.invalid.push([subpath, target]);
|
|
2551
|
+
}
|
|
2552
|
+
if (condition.entries.length === 0 && condition.invalid.length === 0) return null;
|
|
2553
|
+
condition.root = condition.entries.reduce((root, [, file]) => root === null ? (0, path.dirname)(file) : commonDirectory(root, (0, path.dirname)(file)), null);
|
|
2554
|
+
return condition;
|
|
2555
|
+
}
|
|
2556
|
+
function commonDirectory(a, b) {
|
|
2557
|
+
let dir = a;
|
|
2558
|
+
while (!isPathWithinRoot(dir, b)) dir = (0, path.dirname)(dir);
|
|
2559
|
+
return dir;
|
|
2560
|
+
}
|
|
2561
|
+
/** Whether the manifest at `dir` names the package `name`. */
|
|
2562
|
+
function isPackageNamed(dir, name) {
|
|
2563
|
+
const manifest = readPackageManifest(dir);
|
|
2564
|
+
return isJsonBlock(manifest) && manifest.name === name;
|
|
2565
|
+
}
|
|
2566
|
+
function realPath(path$3) {
|
|
2567
|
+
try {
|
|
2568
|
+
return (0, fs.realpathSync)(path$3);
|
|
2569
|
+
} catch {
|
|
2570
|
+
return path$3;
|
|
2571
|
+
}
|
|
2572
|
+
}
|
|
2573
|
+
/** The root of the installed package `name` as Node finds it from `fromDir`,
|
|
2574
|
+
* the first `node_modules/<name>` up the directory tree, at its real path,
|
|
2575
|
+
* as host resolvers report a linked package. */
|
|
2576
|
+
function locatePackageRoot(name, fromDir) {
|
|
2577
|
+
for (let dir = fromDir;; dir = (0, path.dirname)(dir)) {
|
|
2578
|
+
const candidate = (0, path.join)(dir, "node_modules", name);
|
|
2579
|
+
if ((0, fs.existsSync)((0, path.join)(candidate, "package.json"))) return realPath(candidate);
|
|
2580
|
+
if (dir === (0, path.dirname)(dir)) return null;
|
|
2581
|
+
}
|
|
2582
|
+
}
|
|
2110
2583
|
/**
|
|
2111
2584
|
* A package's `sideEffects` field, decoded the way Vite reads it: a boolean
|
|
2112
2585
|
* as written, or the list's globs, one with no `/` matching the basename
|
|
@@ -2115,12 +2588,7 @@ function sourceEntryForSpecifier(specifier, srcDir, extensionsSet) {
|
|
|
2115
2588
|
* extglobs), which leaves the bundler's side-effectful default.
|
|
2116
2589
|
*/
|
|
2117
2590
|
function readPackageSideEffects(pkgRoot) {
|
|
2118
|
-
|
|
2119
|
-
try {
|
|
2120
|
-
manifest = JSON.parse((0, fs.readFileSync)((0, path.join)(pkgRoot, "package.json"), "utf-8"));
|
|
2121
|
-
} catch {
|
|
2122
|
-
return;
|
|
2123
|
-
}
|
|
2591
|
+
const manifest = readPackageManifest(pkgRoot);
|
|
2124
2592
|
const field = isJsonBlock(manifest) ? manifest.sideEffects : void 0;
|
|
2125
2593
|
if (field === true || field === false) return field;
|
|
2126
2594
|
if (!Array.isArray(field)) return void 0;
|
|
@@ -2128,6 +2596,77 @@ function readPackageSideEffects(pkgRoot) {
|
|
|
2128
2596
|
if (globs.some((glob) => /[{}[\]()!+@]/.test(glob))) return void 0;
|
|
2129
2597
|
return globs.map((glob) => globToRegExp(glob.includes("/") ? glob.replace(/^\.?\//, "") : `**/${glob}`));
|
|
2130
2598
|
}
|
|
2599
|
+
/** Whether a bundler that reads the nearest `package.json`'s `sideEffects`
|
|
2600
|
+
* against the file's own path keeps `absFile`; undefined without a field
|
|
2601
|
+
* it reads, which keeps the bundler's side-effectful default. */
|
|
2602
|
+
function fileSideEffects(absFile) {
|
|
2603
|
+
const pkgRoot = findPackageRoot(absFile);
|
|
2604
|
+
const field = readPackageSideEffects(pkgRoot);
|
|
2605
|
+
if (field === void 0 || field === true || field === false) return field;
|
|
2606
|
+
return matchesSideEffects(field, pkgRoot, [absFile]);
|
|
2607
|
+
}
|
|
2608
|
+
/** Whether a `sideEffects` glob can name a code file: a last segment with
|
|
2609
|
+
* no extension can name any file, and an extension with glob syntax or of
|
|
2610
|
+
* a script can name code; `*.css` cannot. Brace alternatives are each read,
|
|
2611
|
+
* and braces this reading cannot expand count as naming code. */
|
|
2612
|
+
function globNamesCode(glob) {
|
|
2613
|
+
const alternatives = expandBraces(glob);
|
|
2614
|
+
if (alternatives === null) return true;
|
|
2615
|
+
return alternatives.some((alternative) => {
|
|
2616
|
+
const last = alternative.split("/").at(-1) ?? "";
|
|
2617
|
+
const dot = last.lastIndexOf(".");
|
|
2618
|
+
if (dot === -1) return true;
|
|
2619
|
+
const extension = last.slice(dot + 1);
|
|
2620
|
+
return /[*?[]/.test(extension) || /^[cm]?[jt]sx?$/.test(extension);
|
|
2621
|
+
});
|
|
2622
|
+
}
|
|
2623
|
+
/** Every alternative a glob's `{a,b}` groups spell, nested groups included;
|
|
2624
|
+
* null when its braces do not balance. */
|
|
2625
|
+
function expandBraces(glob) {
|
|
2626
|
+
const open = glob.indexOf("{");
|
|
2627
|
+
if (open === -1) return glob.includes("}") ? null : [glob];
|
|
2628
|
+
let depth = 0;
|
|
2629
|
+
const commas = [];
|
|
2630
|
+
for (let at = open; at < glob.length; at++) if (glob[at] === "{") depth++;
|
|
2631
|
+
else if (glob[at] === "," && depth === 1) commas.push(at);
|
|
2632
|
+
else if (glob[at] === "}" && --depth === 0) {
|
|
2633
|
+
const bounds = [
|
|
2634
|
+
open,
|
|
2635
|
+
...commas,
|
|
2636
|
+
at
|
|
2637
|
+
];
|
|
2638
|
+
const rest = expandBraces(glob.slice(at + 1));
|
|
2639
|
+
if (rest === null) return null;
|
|
2640
|
+
const expanded = [];
|
|
2641
|
+
for (let i = 0; i < bounds.length - 1; i++) {
|
|
2642
|
+
const inner = expandBraces(glob.slice(bounds[i] + 1, bounds[i + 1]));
|
|
2643
|
+
if (inner === null) return null;
|
|
2644
|
+
for (const middle of inner) for (const tail of rest) expanded.push(glob.slice(0, open) + middle + tail);
|
|
2645
|
+
}
|
|
2646
|
+
return expanded;
|
|
2647
|
+
}
|
|
2648
|
+
return null;
|
|
2649
|
+
}
|
|
2650
|
+
/**
|
|
2651
|
+
* True when a source module of the package that owns `absFile` must stay
|
|
2652
|
+
* side-effectful: its `sideEffects` list names shipped code, and no proven
|
|
2653
|
+
* map takes a shipped file to its source module, so a bundler that reads
|
|
2654
|
+
* the list against the source path can drop a listed effect. Undefined
|
|
2655
|
+
* leaves the bundler's own reading, which no list naming code can mislead.
|
|
2656
|
+
*/
|
|
2657
|
+
function kitSourceModuleSideEffects(absFile) {
|
|
2658
|
+
const manifest = readPackageManifest(findPackageRoot(absFile));
|
|
2659
|
+
const field = isJsonBlock(manifest) ? manifest.sideEffects : void 0;
|
|
2660
|
+
if (!Array.isArray(field)) return void 0;
|
|
2661
|
+
return field.some((glob) => isJsonString(glob) && globNamesCode(glob)) ? true : void 0;
|
|
2662
|
+
}
|
|
2663
|
+
/** Whether a host keeps the module at `file` side-effectful: it lies under
|
|
2664
|
+
* one of the discovered kit source directories, and its package's list
|
|
2665
|
+
* could misread it (see `kitSourceModuleSideEffects`). A host asks this of
|
|
2666
|
+
* the resolved module, never of its importer alone. */
|
|
2667
|
+
function keepsKitSourceEffects(file, kitDirs) {
|
|
2668
|
+
return kitDirs.some((dir) => isPathWithinRoot(dir, file)) && kitSourceModuleSideEffects(file) === true;
|
|
2669
|
+
}
|
|
2131
2670
|
function matchesSideEffects(globs, pkgRoot, absFiles) {
|
|
2132
2671
|
return absFiles.some((file) => {
|
|
2133
2672
|
const relPath = (0, path.relative)(pkgRoot, file).split(path.sep).join("/");
|
|
@@ -2151,18 +2690,9 @@ function exportTarget(value) {
|
|
|
2151
2690
|
/** Each exact `exports` entry of the package at `pkgRoot`, as its subpath
|
|
2152
2691
|
* and absolute target; `*` patterns name no single file and are skipped. */
|
|
2153
2692
|
function packageExportEntries(pkgRoot) {
|
|
2154
|
-
let manifest;
|
|
2155
|
-
try {
|
|
2156
|
-
manifest = JSON.parse((0, fs.readFileSync)((0, path.join)(pkgRoot, "package.json"), "utf-8"));
|
|
2157
|
-
} catch {
|
|
2158
|
-
return [];
|
|
2159
|
-
}
|
|
2160
|
-
const exports = isJsonBlock(manifest) ? manifest.exports : void 0;
|
|
2161
|
-
if (exports === void 0 || exports === null) return [];
|
|
2162
|
-
const subpaths = isJsonBlock(exports) && Object.keys(exports).some((k) => k.startsWith(".")) ? exports : { ".": exports };
|
|
2163
2693
|
const entries = [];
|
|
2164
|
-
for (const [subpath, value] of
|
|
2165
|
-
if (
|
|
2694
|
+
for (const [subpath, value] of exportsSubpaths(readPackageManifest(pkgRoot))) {
|
|
2695
|
+
if (subpath.includes("*")) continue;
|
|
2166
2696
|
const target = exportTarget(value);
|
|
2167
2697
|
if (target !== null) entries.push([subpath, (0, path.resolve)(pkgRoot, target)]);
|
|
2168
2698
|
}
|
|
@@ -2201,6 +2731,16 @@ async function collectExternalPackageSources(opts) {
|
|
|
2201
2731
|
const sourceEntrySideEffects = /* @__PURE__ */ new Map();
|
|
2202
2732
|
/** `replaced` names the entry the redirect stands in for; it is only
|
|
2203
2733
|
* needed, and so only resolved, for a glob list. */
|
|
2734
|
+
/** The entry `subpath` of the package resolves to without the redirect.
|
|
2735
|
+
* A resolver with no `import` condition, as Node's `require` has none,
|
|
2736
|
+
* finds no ESM-only entry, so the manifest's own target answers then. */
|
|
2737
|
+
const shippedEntry = async (specifier, pkgRoot, subpath) => {
|
|
2738
|
+
try {
|
|
2739
|
+
const resolved = await resolveSpecifier(specifier);
|
|
2740
|
+
if (resolved) return resolved;
|
|
2741
|
+
} catch {}
|
|
2742
|
+
return packageExportEntries(pkgRoot).find(([entry]) => entry === subpath)?.[1] ?? null;
|
|
2743
|
+
};
|
|
2204
2744
|
const redirect = async (specifier, srcEntry, pkgRoot, replaced) => {
|
|
2205
2745
|
sourceEntries.set(specifier, srcEntry);
|
|
2206
2746
|
const field = readPackageSideEffects(pkgRoot);
|
|
@@ -2213,6 +2753,8 @@ async function collectExternalPackageSources(opts) {
|
|
|
2213
2753
|
sourceEntrySideEffects.set(specifier, replacedEntry === null || matchesSideEffects(field, pkgRoot, [srcEntry, replacedEntry]));
|
|
2214
2754
|
};
|
|
2215
2755
|
const packageDirs = [];
|
|
2756
|
+
const linkedDirs = [];
|
|
2757
|
+
const outputDirs = [];
|
|
2216
2758
|
const dirOwnerSets = {};
|
|
2217
2759
|
const dirExtensions = {};
|
|
2218
2760
|
const fileOwners = {};
|
|
@@ -2221,6 +2763,31 @@ async function collectExternalPackageSources(opts) {
|
|
|
2221
2763
|
(dirOwnerSets[dir] ??= []).push(specifier);
|
|
2222
2764
|
};
|
|
2223
2765
|
const alreadyIngested = (relPath) => hasEntry(relPath) || pushed.has(relPath);
|
|
2766
|
+
/** Adds `file` to the analysis set unless it is there; false when it
|
|
2767
|
+
* cannot be read. */
|
|
2768
|
+
const ingest = (file, specifier) => {
|
|
2769
|
+
const relPath = (0, path.relative)(rootDir, file);
|
|
2770
|
+
if (alreadyIngested(relPath)) return true;
|
|
2771
|
+
let source;
|
|
2772
|
+
try {
|
|
2773
|
+
source = (0, fs.readFileSync)(file, "utf-8");
|
|
2774
|
+
} catch (err) {
|
|
2775
|
+
onUnreadable(relPath, err);
|
|
2776
|
+
return false;
|
|
2777
|
+
}
|
|
2778
|
+
onSourceRead?.(source, relPath, file);
|
|
2779
|
+
entries.push({
|
|
2780
|
+
path: relPath,
|
|
2781
|
+
source
|
|
2782
|
+
});
|
|
2783
|
+
pushed.add(relPath);
|
|
2784
|
+
fileOwners[relPath] ??= specifier;
|
|
2785
|
+
return true;
|
|
2786
|
+
};
|
|
2787
|
+
const diagnostics = [];
|
|
2788
|
+
const reportedPackages = /* @__PURE__ */ new Set();
|
|
2789
|
+
const kitDescriptors = [];
|
|
2790
|
+
const describedRoots = /* @__PURE__ */ new Set();
|
|
2224
2791
|
for (const specifier of specifiers) {
|
|
2225
2792
|
let absEntry;
|
|
2226
2793
|
try {
|
|
@@ -2229,7 +2796,14 @@ async function collectExternalPackageSources(opts) {
|
|
|
2229
2796
|
absEntry = null;
|
|
2230
2797
|
}
|
|
2231
2798
|
if (!absEntry && (0, path.isAbsolute)(specifier)) absEntry = resolveAbsolutePathSpecifier(specifier, extensionsSet);
|
|
2232
|
-
|
|
2799
|
+
const packageName = (0, path.isAbsolute)(specifier) ? null : bareSpecifierPackageName(specifier);
|
|
2800
|
+
const resolvedRoot = absEntry ? findPackageRoot(absEntry) : null;
|
|
2801
|
+
const locatedRoot = packageName && locatePackageRoot(packageName, rootDir);
|
|
2802
|
+
const pkgRoot = resolvedRoot && (!locatedRoot || isPackageNamed(resolvedRoot, packageName) || realPath(resolvedRoot) === locatedRoot) ? resolvedRoot : locatedRoot || resolvedRoot;
|
|
2803
|
+
const condition = packageName && pkgRoot ? readKitSourceCondition(pkgRoot) : null;
|
|
2804
|
+
const entryKey = packageName && `.${specifier.slice(packageName.length)}`;
|
|
2805
|
+
absEntry ??= condition?.entries.find(([entry]) => entry === entryKey)?.[1] ?? null;
|
|
2806
|
+
if (!absEntry || !pkgRoot) {
|
|
2233
2807
|
outcomes.push({
|
|
2234
2808
|
specifier,
|
|
2235
2809
|
outcome: "unresolvable",
|
|
@@ -2237,12 +2811,38 @@ async function collectExternalPackageSources(opts) {
|
|
|
2237
2811
|
});
|
|
2238
2812
|
continue;
|
|
2239
2813
|
}
|
|
2240
|
-
const
|
|
2814
|
+
const realRoot = realPath(pkgRoot);
|
|
2815
|
+
if (!describedRoots.has(realRoot)) {
|
|
2816
|
+
describedRoots.add(realRoot);
|
|
2817
|
+
const described = readKitDescriptor(pkgRoot, rootDir);
|
|
2818
|
+
if (described) kitDescriptors.push(described);
|
|
2819
|
+
}
|
|
2820
|
+
const linked = !isInstalledPackage(pkgRoot);
|
|
2241
2821
|
const srcDir = (0, path.join)(pkgRoot, "src");
|
|
2242
2822
|
let fileCount = 0;
|
|
2243
2823
|
let staleDist = false;
|
|
2244
|
-
if (
|
|
2824
|
+
if (packageName && !reportedPackages.has(packageName)) {
|
|
2825
|
+
reportedPackages.add(packageName);
|
|
2826
|
+
diagnostics.push(...sourceConditionDiagnostics(packageName, condition));
|
|
2827
|
+
}
|
|
2828
|
+
if (condition && packageName) {
|
|
2829
|
+
for (const [entry, target] of condition.entries) {
|
|
2830
|
+
const entrySpecifier = packageName + entry.slice(1);
|
|
2831
|
+
packageMap[entrySpecifier] = (0, path.relative)(rootDir, target);
|
|
2832
|
+
await redirect(entrySpecifier, target, pkgRoot, () => shippedEntry(entrySpecifier, pkgRoot, entry));
|
|
2833
|
+
}
|
|
2834
|
+
packageMap[specifier] ??= (0, path.relative)(rootDir, absEntry);
|
|
2835
|
+
if (condition.root) {
|
|
2836
|
+
packageDirs.push(condition.root);
|
|
2837
|
+
if (linked) linkedDirs.push(condition.root);
|
|
2838
|
+
claimDir(condition.root, specifier);
|
|
2839
|
+
dirExtensions[condition.root] = [...extensionsSet];
|
|
2840
|
+
onPackageResolved?.(specifier, condition.root);
|
|
2841
|
+
for (const file of walkPackageSources(condition.root, extensionsSet)) if (ingest(file, specifier)) fileCount++;
|
|
2842
|
+
}
|
|
2843
|
+
} else if ((0, fs.existsSync)(srcDir)) {
|
|
2245
2844
|
packageDirs.push(srcDir);
|
|
2845
|
+
if (linked) linkedDirs.push(srcDir);
|
|
2246
2846
|
claimDir(srcDir, specifier);
|
|
2247
2847
|
dirExtensions[srcDir] = [...extensionsSet];
|
|
2248
2848
|
onPackageResolved?.(specifier, srcDir);
|
|
@@ -2252,49 +2852,23 @@ async function collectExternalPackageSources(opts) {
|
|
|
2252
2852
|
const replaced = absEntry;
|
|
2253
2853
|
await redirect(specifier, srcEntry, pkgRoot, async () => replaced);
|
|
2254
2854
|
} else packageMap[specifier] = (0, path.relative)(rootDir, absEntry);
|
|
2255
|
-
if (
|
|
2256
|
-
const packageName = bareSpecifierPackageName(specifier);
|
|
2855
|
+
if (packageName) {
|
|
2257
2856
|
if (packageName !== specifier && !(packageName in packageMap)) {
|
|
2258
2857
|
const rootEntry = sourceEntryForSpecifier(packageName, srcDir, extensionsSet);
|
|
2259
2858
|
if (rootEntry) {
|
|
2260
2859
|
packageMap[packageName] = (0, path.relative)(rootDir, rootEntry);
|
|
2261
|
-
await redirect(packageName, rootEntry, pkgRoot,
|
|
2262
|
-
try {
|
|
2263
|
-
return await resolveSpecifier(packageName);
|
|
2264
|
-
} catch {
|
|
2265
|
-
return null;
|
|
2266
|
-
}
|
|
2267
|
-
});
|
|
2860
|
+
await redirect(packageName, rootEntry, pkgRoot, () => shippedEntry(packageName, pkgRoot, "."));
|
|
2268
2861
|
}
|
|
2269
2862
|
}
|
|
2270
2863
|
}
|
|
2271
2864
|
const pkgFiles = walkPackageSources(srcDir, extensionsSet);
|
|
2272
|
-
staleDist =
|
|
2273
|
-
for (const pkgFile of pkgFiles)
|
|
2274
|
-
const relPath = (0, path.relative)(rootDir, pkgFile);
|
|
2275
|
-
if (alreadyIngested(relPath)) {
|
|
2276
|
-
fileCount++;
|
|
2277
|
-
continue;
|
|
2278
|
-
}
|
|
2279
|
-
let source;
|
|
2280
|
-
try {
|
|
2281
|
-
source = (0, fs.readFileSync)(pkgFile, "utf-8");
|
|
2282
|
-
} catch (err) {
|
|
2283
|
-
onUnreadable(relPath, err);
|
|
2284
|
-
continue;
|
|
2285
|
-
}
|
|
2286
|
-
onSourceRead?.(source, relPath, pkgFile);
|
|
2287
|
-
entries.push({
|
|
2288
|
-
path: relPath,
|
|
2289
|
-
source
|
|
2290
|
-
});
|
|
2291
|
-
pushed.add(relPath);
|
|
2292
|
-
fileOwners[relPath] ??= specifier;
|
|
2293
|
-
fileCount++;
|
|
2294
|
-
}
|
|
2865
|
+
staleDist = linked && distEntryIsStale(absEntry, srcDir, pkgFiles);
|
|
2866
|
+
for (const pkgFile of pkgFiles) if (ingest(pkgFile, specifier)) fileCount++;
|
|
2295
2867
|
} else {
|
|
2296
2868
|
const outputDir = (0, path.dirname)(absEntry);
|
|
2297
2869
|
packageDirs.push(outputDir);
|
|
2870
|
+
if (linked) linkedDirs.push(outputDir);
|
|
2871
|
+
outputDirs.push(outputDir);
|
|
2298
2872
|
claimDir(outputDir, specifier);
|
|
2299
2873
|
onPackageResolved?.(specifier, outputDir);
|
|
2300
2874
|
packageMap[specifier] = (0, path.relative)(rootDir, absEntry);
|
|
@@ -2306,37 +2880,13 @@ async function collectExternalPackageSources(opts) {
|
|
|
2306
2880
|
return !PACKAGE_OUTPUT_EXCLUDES.some((pattern) => relToOutput.includes(pattern));
|
|
2307
2881
|
});
|
|
2308
2882
|
if (!outputFiles.includes(absEntry)) outputFiles.unshift(absEntry);
|
|
2309
|
-
for (const outputFile of outputFiles)
|
|
2310
|
-
const outputRelPath = (0, path.relative)(rootDir, outputFile);
|
|
2311
|
-
if (alreadyIngested(outputRelPath)) {
|
|
2312
|
-
fileCount++;
|
|
2313
|
-
continue;
|
|
2314
|
-
}
|
|
2315
|
-
let source;
|
|
2316
|
-
try {
|
|
2317
|
-
source = (0, fs.readFileSync)(outputFile, "utf-8");
|
|
2318
|
-
} catch (err) {
|
|
2319
|
-
onUnreadable(outputRelPath, err);
|
|
2320
|
-
continue;
|
|
2321
|
-
}
|
|
2322
|
-
onSourceRead?.(source, outputRelPath, outputFile);
|
|
2323
|
-
entries.push({
|
|
2324
|
-
path: outputRelPath,
|
|
2325
|
-
source
|
|
2326
|
-
});
|
|
2327
|
-
pushed.add(outputRelPath);
|
|
2328
|
-
fileOwners[outputRelPath] ??= specifier;
|
|
2329
|
-
fileCount++;
|
|
2330
|
-
}
|
|
2883
|
+
for (const outputFile of outputFiles) if (ingest(outputFile, specifier)) fileCount++;
|
|
2331
2884
|
}
|
|
2332
|
-
if (
|
|
2333
|
-
const
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
if (entrySpecifier in packageMap || !alreadyIngested(targetRelPath)) continue;
|
|
2338
|
-
packageMap[entrySpecifier] = targetRelPath;
|
|
2339
|
-
}
|
|
2885
|
+
if (packageName) for (const [subpath, target] of packageExportEntries(pkgRoot)) {
|
|
2886
|
+
const entrySpecifier = packageName + subpath.slice(1);
|
|
2887
|
+
const targetRelPath = (0, path.relative)(rootDir, target);
|
|
2888
|
+
if (entrySpecifier in packageMap || !alreadyIngested(targetRelPath)) continue;
|
|
2889
|
+
packageMap[entrySpecifier] = targetRelPath;
|
|
2340
2890
|
}
|
|
2341
2891
|
outcomes.push({
|
|
2342
2892
|
specifier,
|
|
@@ -2350,12 +2900,112 @@ async function collectExternalPackageSources(opts) {
|
|
|
2350
2900
|
sourceEntries,
|
|
2351
2901
|
sourceEntrySideEffects,
|
|
2352
2902
|
packageDirs,
|
|
2903
|
+
linkedDirs,
|
|
2904
|
+
outputDirs,
|
|
2353
2905
|
dirOwnerSets,
|
|
2354
2906
|
dirExtensions,
|
|
2355
2907
|
fileOwners,
|
|
2356
|
-
outcomes
|
|
2908
|
+
outcomes,
|
|
2909
|
+
diagnostics: [...diagnostics, ...noKitFilesDiagnostics(outcomes)],
|
|
2910
|
+
kitDescriptors
|
|
2357
2911
|
};
|
|
2358
2912
|
}
|
|
2913
|
+
/** The dependency names a manifest lists in `fields`. */
|
|
2914
|
+
function dependencyNames(manifest, fields) {
|
|
2915
|
+
if (!isJsonBlock(manifest)) return [];
|
|
2916
|
+
const names = /* @__PURE__ */ new Set();
|
|
2917
|
+
for (const field of fields) {
|
|
2918
|
+
const deps = manifest[field];
|
|
2919
|
+
if (isJsonBlock(deps)) for (const name of Object.keys(deps)) names.add(name);
|
|
2920
|
+
}
|
|
2921
|
+
return [...names];
|
|
2922
|
+
}
|
|
2923
|
+
/** The source kits the application at `rootDir` declares as dependencies.
|
|
2924
|
+
* A bundler serves an installed one as compiled code unless told
|
|
2925
|
+
* otherwise: Next keeps it external on the server unless it is in
|
|
2926
|
+
* `transpilePackages`, and Vite's optimizer prebundles it. */
|
|
2927
|
+
function sourceKitDependencies(rootDir) {
|
|
2928
|
+
return dependencyNames(readPackageManifest(rootDir), ["dependencies", "devDependencies"]).flatMap((name) => {
|
|
2929
|
+
const pkgRoot = locatePackageRoot(name, rootDir);
|
|
2930
|
+
if (pkgRoot === null || readKitSourceCondition(pkgRoot) === null) return [];
|
|
2931
|
+
return [{
|
|
2932
|
+
name,
|
|
2933
|
+
installed: isInstalledPackage(pkgRoot),
|
|
2934
|
+
dependencies: dependencyNames(readPackageManifest(pkgRoot), ["dependencies"])
|
|
2935
|
+
}];
|
|
2936
|
+
});
|
|
2937
|
+
}
|
|
2938
|
+
/** A specifier that names a package: not relative or absolute, and no URL,
|
|
2939
|
+
* scheme (`node:`, `virtual:`), subpath import or virtual id. */
|
|
2940
|
+
function isPackageSpecifier(specifier) {
|
|
2941
|
+
return /^(?:@[^/\\:]+\/)?[^./\\#\0:][^:]*$/.test(specifier);
|
|
2942
|
+
}
|
|
2943
|
+
/**
|
|
2944
|
+
* An error for each kit the application's own `files` import or re-export
|
|
2945
|
+
* whose system the application's system does not include: a package that
|
|
2946
|
+
* declares the kit source condition, with no kit the system extends
|
|
2947
|
+
* (`systemKits`) in that package. One app build has exactly one system, and
|
|
2948
|
+
* every kit's system is part of it, so such a kit is not extracted against
|
|
2949
|
+
* a system it was not built for. One batch parse reads the imports of every
|
|
2950
|
+
* file the engine parses as it is; an adapted source (`.svelte`, `.mdx`)
|
|
2951
|
+
* reaches the engine only as its generated children.
|
|
2952
|
+
*/
|
|
2953
|
+
function importedKitDiagnostics(files, engine, rootDir, systemKits) {
|
|
2954
|
+
const { extractFacts } = engine;
|
|
2955
|
+
const parsed = files.filter(({ path: path$4 }) => isEngineTransformExtension(path$4));
|
|
2956
|
+
if (!extractFacts || parsed.length === 0) return [];
|
|
2957
|
+
let facts;
|
|
2958
|
+
try {
|
|
2959
|
+
facts = parseInternalWire(extractFacts(JSON.stringify(parsed.map(({ path: path$5, source }) => ({
|
|
2960
|
+
path: path$5,
|
|
2961
|
+
source
|
|
2962
|
+
})))), "extractFacts");
|
|
2963
|
+
} catch {
|
|
2964
|
+
return [];
|
|
2965
|
+
}
|
|
2966
|
+
const seen = new Set(systemKits.filter((specifier) => !(0, path.isAbsolute)(specifier)).map(bareSpecifierPackageName));
|
|
2967
|
+
const diagnostics = [];
|
|
2968
|
+
for (const [path$6, file] of Object.entries(facts.files)) {
|
|
2969
|
+
if (file.parsePanicked) continue;
|
|
2970
|
+
const specifiers = [...file.imports.map((fact) => fact.source), ...file.exports.flatMap((fact) => fact.source === null ? [] : [fact.source])];
|
|
2971
|
+
for (const specifier of specifiers) {
|
|
2972
|
+
if (!isPackageSpecifier(specifier)) continue;
|
|
2973
|
+
const name = bareSpecifierPackageName(specifier);
|
|
2974
|
+
if (seen.has(name)) continue;
|
|
2975
|
+
seen.add(name);
|
|
2976
|
+
const pkgRoot = locatePackageRoot(name, (0, path.dirname)((0, path.resolve)(rootDir, path$6)));
|
|
2977
|
+
if (pkgRoot && readKitSourceCondition(pkgRoot)) diagnostics.push({
|
|
2978
|
+
file: path$6,
|
|
2979
|
+
component: name,
|
|
2980
|
+
kind: "warn",
|
|
2981
|
+
message: `the application imports this kit, but its system is not included in the application's system, so its components are not extracted — include the kit's system in the application's system: createSystem().extend(<the kit's system>)`,
|
|
2982
|
+
code: KIT_SYSTEM_NOT_INCLUDED,
|
|
2983
|
+
severity: severityFor(KIT_SYSTEM_NOT_INCLUDED)
|
|
2984
|
+
});
|
|
2985
|
+
}
|
|
2986
|
+
}
|
|
2987
|
+
return diagnostics;
|
|
2988
|
+
}
|
|
2989
|
+
/** A kit without the source condition, warned once, or each condition entry
|
|
2990
|
+
* whose target is missing or outside the package. */
|
|
2991
|
+
function sourceConditionDiagnostics(packageName, condition) {
|
|
2992
|
+
if (condition === null) return [{
|
|
2993
|
+
file: packageName,
|
|
2994
|
+
component: "kit",
|
|
2995
|
+
kind: "warn",
|
|
2996
|
+
message: `declares no "${KIT_SOURCE_CONDITION}" export condition, so discovery guesses its source from src/ — give each public exports entry an "${KIT_SOURCE_CONDITION}" condition naming its original source`,
|
|
2997
|
+
code: KIT_WITHOUT_SOURCE_CONDITION,
|
|
2998
|
+
severity: severityFor(KIT_WITHOUT_SOURCE_CONDITION)
|
|
2999
|
+
}];
|
|
3000
|
+
return condition.invalid.map(([subpath, target]) => ({
|
|
3001
|
+
file: packageName,
|
|
3002
|
+
component: "kit",
|
|
3003
|
+
kind: "warn",
|
|
3004
|
+
message: `exports["${subpath}"].${KIT_SOURCE_CONDITION} names ${target}, which is missing or outside the package, so that entry is not read from source`,
|
|
3005
|
+
code: INVALID_KIT_SOURCE_CONDITION,
|
|
3006
|
+
severity: severityFor(INVALID_KIT_SOURCE_CONDITION)
|
|
3007
|
+
}));
|
|
3008
|
+
}
|
|
2359
3009
|
function firstOwners(dirOwnerSets) {
|
|
2360
3010
|
const owners = {};
|
|
2361
3011
|
for (const [dir, specifiers] of Object.entries(dirOwnerSets)) if (specifiers.length > 0) owners[dir] = specifiers[0];
|
|
@@ -2401,10 +3051,14 @@ function excludeCollectedPackages(collected, rejectedSpecifiers, rootDir) {
|
|
|
2401
3051
|
sourceEntries,
|
|
2402
3052
|
sourceEntrySideEffects,
|
|
2403
3053
|
packageDirs: collected.packageDirs.filter((dir) => !rejectedDirs.includes(dir)),
|
|
3054
|
+
linkedDirs: collected.linkedDirs.filter((dir) => !rejectedDirs.includes(dir)),
|
|
3055
|
+
outputDirs: collected.outputDirs.filter((dir) => !rejectedDirs.includes(dir)),
|
|
2404
3056
|
dirOwnerSets,
|
|
2405
3057
|
dirExtensions,
|
|
2406
3058
|
fileOwners,
|
|
2407
|
-
outcomes: collected.outcomes
|
|
3059
|
+
outcomes: collected.outcomes,
|
|
3060
|
+
diagnostics: collected.diagnostics,
|
|
3061
|
+
kitDescriptors: collected.kitDescriptors.filter((record) => !rejectedDirs.some((dir) => isPathWithinRoot((0, path.resolve)(rootDir, record.packageRoot), dir)))
|
|
2408
3062
|
};
|
|
2409
3063
|
}
|
|
2410
3064
|
function unresolvableIncludesMessage(outcomes) {
|
|
@@ -2419,48 +3073,239 @@ function staleDistIncludesMessage(outcomes) {
|
|
|
2419
3073
|
if (stale.length === 0) return null;
|
|
2420
3074
|
return `[animus-extract] stale dist for include specifier(s): ${stale.join(", ")} — dist entry is older than the newest src/ file; rebuild the package(s) before extracting`;
|
|
2421
3075
|
}
|
|
2422
|
-
/** The engine's own parse of a module's
|
|
3076
|
+
/** The engine's own parse of a module's bindings, through its
|
|
2423
3077
|
* `extractFacts`; undefined for an engine without one. A parse that
|
|
2424
|
-
* panicked
|
|
2425
|
-
function
|
|
3078
|
+
* panicked counts as no parse. */
|
|
3079
|
+
function engineModuleParser(engine) {
|
|
2426
3080
|
const { extractFacts } = engine;
|
|
2427
3081
|
if (!extractFacts) return void 0;
|
|
2428
|
-
return (source, path$
|
|
3082
|
+
return (source, path$7) => {
|
|
2429
3083
|
try {
|
|
2430
3084
|
const facts = parseInternalWire(extractFacts(JSON.stringify([{
|
|
2431
|
-
path: path$
|
|
3085
|
+
path: path$7,
|
|
2432
3086
|
source
|
|
2433
|
-
}])), "extractFacts").files[path$
|
|
2434
|
-
return facts && !facts.parsePanicked ?
|
|
3087
|
+
}])), "extractFacts").files[path$7];
|
|
3088
|
+
return facts && !facts.parsePanicked ? {
|
|
3089
|
+
imports: facts.imports,
|
|
3090
|
+
exports: facts.exports,
|
|
3091
|
+
unboundCreateSystemCalls: facts.unboundCreateSystemCalls ?? []
|
|
3092
|
+
} : null;
|
|
2435
3093
|
} catch {
|
|
2436
3094
|
return null;
|
|
2437
3095
|
}
|
|
2438
3096
|
};
|
|
2439
3097
|
}
|
|
3098
|
+
const ANIMUS_SYSTEM_SPECIFIER = /^@animus-ui\/system(?:\/.*)?$/;
|
|
3099
|
+
/** The file a specifier names from `importer`: a relative one by the
|
|
3100
|
+
* ingestion's probe order, a bare one by the host's package resolution. */
|
|
3101
|
+
async function resolveModuleFile(importer, specifier, resolvePackage) {
|
|
3102
|
+
if (specifier.startsWith(".")) return relativeSourceCandidates(importer, specifier).find(isFile$1) ?? null;
|
|
3103
|
+
try {
|
|
3104
|
+
return await resolvePackage?.(specifier) ?? null;
|
|
3105
|
+
} catch {
|
|
3106
|
+
return null;
|
|
3107
|
+
}
|
|
3108
|
+
}
|
|
3109
|
+
const escapeRegExp$1 = (text) => text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3110
|
+
/**
|
|
3111
|
+
* Follows `name`, exported by the module `specifier` names from `importer`,
|
|
3112
|
+
* through re-exports to its declaration. It is Animus's factory only when
|
|
3113
|
+
* that is `createSystem` of `@animus-ui/system`, and another identity only
|
|
3114
|
+
* when a module is shown to declare it; anything discovery cannot follow is
|
|
3115
|
+
* unknown.
|
|
3116
|
+
*/
|
|
3117
|
+
async function exportIdentity(importer, specifier, name, following, seen) {
|
|
3118
|
+
if (ANIMUS_SYSTEM_SPECIFIER.test(specifier)) return name === "createSystem" ? { kind: "animus" } : {
|
|
3119
|
+
kind: "other",
|
|
3120
|
+
what: `'${name}' of ${specifier}`
|
|
3121
|
+
};
|
|
3122
|
+
const file = await resolveModuleFile(importer, specifier, following.resolvePackage);
|
|
3123
|
+
if (file === null) return {
|
|
3124
|
+
kind: "unknown",
|
|
3125
|
+
why: `'${specifier}' could not be resolved`
|
|
3126
|
+
};
|
|
3127
|
+
const key = `${file}#${name}`;
|
|
3128
|
+
if (seen.has(key)) return {
|
|
3129
|
+
kind: "unknown",
|
|
3130
|
+
why: `'${specifier}' re-exports it in a cycle`
|
|
3131
|
+
};
|
|
3132
|
+
seen.add(key);
|
|
3133
|
+
let source;
|
|
3134
|
+
try {
|
|
3135
|
+
source = (0, fs.readFileSync)(file, "utf-8");
|
|
3136
|
+
} catch {
|
|
3137
|
+
return {
|
|
3138
|
+
kind: "unknown",
|
|
3139
|
+
why: `'${specifier}' could not be read`
|
|
3140
|
+
};
|
|
3141
|
+
}
|
|
3142
|
+
const record = following.parseModule(source, file);
|
|
3143
|
+
if (record === null) return {
|
|
3144
|
+
kind: "unknown",
|
|
3145
|
+
why: `'${specifier}' could not be parsed`
|
|
3146
|
+
};
|
|
3147
|
+
const exported = record.exports.find((entry) => entry.exported === name);
|
|
3148
|
+
if (exported?.source) return exportIdentity(file, exported.source, exported.original ?? name, following, seen);
|
|
3149
|
+
const local = exported?.local ?? name;
|
|
3150
|
+
const imported = record.imports.find((entry) => entry.local === local);
|
|
3151
|
+
if (exported && imported) return exportIdentity(file, imported.source, imported.imported, following, seen);
|
|
3152
|
+
if (exported || new RegExp(`\\bexport\\s+(?:async\\s+)?(?:function\\*?|class|const|let|var)\\s+${escapeRegExp$1(name)}\\b`).test(source)) return {
|
|
3153
|
+
kind: "other",
|
|
3154
|
+
what: `the '${name}' that '${specifier}' declares`
|
|
3155
|
+
};
|
|
3156
|
+
const stars = [];
|
|
3157
|
+
for (const match of source.matchAll(/\bexport\s*\*\s*from\s*['"]([^'"]+)['"]/g)) stars.push(await exportIdentity(file, match[1], name, following, seen));
|
|
3158
|
+
const definite = stars.filter((identity) => identity.kind !== "unknown");
|
|
3159
|
+
if (definite.length === 1) return definite[0];
|
|
3160
|
+
if (definite.length > 1) return {
|
|
3161
|
+
kind: "unknown",
|
|
3162
|
+
why: `more than one \`export *\` of '${specifier}' provides '${name}'`
|
|
3163
|
+
};
|
|
3164
|
+
return stars.find((identity) => identity.kind === "unknown") ?? {
|
|
3165
|
+
kind: "unknown",
|
|
3166
|
+
why: `'${specifier}' has no export '${name}' discovery can read`
|
|
3167
|
+
};
|
|
3168
|
+
}
|
|
3169
|
+
/** 1-based line and column of `offset` in `source`. */
|
|
3170
|
+
function locationIn(source, offset) {
|
|
3171
|
+
const before = source.slice(0, offset);
|
|
3172
|
+
const lineStart = before.lastIndexOf("\n") + 1;
|
|
3173
|
+
return {
|
|
3174
|
+
line: before.split("\n").length,
|
|
3175
|
+
column: offset - lineStart + 1
|
|
3176
|
+
};
|
|
3177
|
+
}
|
|
3178
|
+
const NAMESPACE_IMPORT = /\bimport\s+(?:[a-zA-Z_$][a-zA-Z0-9_$]*\s*,\s*)?\*\s*as\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\s+from\s+['"]([^'"]+)['"]/g;
|
|
3179
|
+
/**
|
|
3180
|
+
* The callees that are Animus's `createSystem` in a parsed system file. A
|
|
3181
|
+
* binding counts once its identity is followed to the factory: a named
|
|
3182
|
+
* import, through local and package re-exports; a namespace import's
|
|
3183
|
+
* member; or a property destructured from a namespace import, `require` or
|
|
3184
|
+
* `import()` of a module. A binding named `createSystem` shown to be
|
|
3185
|
+
* something else is no root. One whose identity discovery cannot follow
|
|
3186
|
+
* keeps the admission spelling gave it, so a resolution limit never drops a
|
|
3187
|
+
* kit. Both are reported. A call of the bare name, which the file neither
|
|
3188
|
+
* imports nor declares, is no root: the system loader evaluates the file
|
|
3189
|
+
* without auto-imports, where the name is undefined. It is reported once.
|
|
3190
|
+
*/
|
|
3191
|
+
async function systemRoots(systemFilePath, source, record, following) {
|
|
3192
|
+
const { imports } = record;
|
|
3193
|
+
const roots = /* @__PURE__ */ new Set();
|
|
3194
|
+
const identityOf = (specifier, name) => exportIdentity(systemFilePath, specifier, name, following, /* @__PURE__ */ new Set());
|
|
3195
|
+
const called = (callee) => callOf(callee).test(source);
|
|
3196
|
+
/** Admits a binding named `createSystem` by its identity: an unknown one
|
|
3197
|
+
* keeps `spellingAdmits`, and every unproven one is reported. */
|
|
3198
|
+
const admit = (local, offset, identity, spellingAdmits) => {
|
|
3199
|
+
if (identity.kind === "animus") {
|
|
3200
|
+
roots.add(local);
|
|
3201
|
+
return;
|
|
3202
|
+
}
|
|
3203
|
+
if (identity.kind === "unknown" && spellingAdmits) roots.add(local);
|
|
3204
|
+
const shown = identity.kind === "other" ? `it is ${identity.what}, so it is not read as a system root` : `discovery cannot follow what it is bound to (${identity.why}), so it is ${spellingAdmits ? "still read" : "not read"} as a system root by its name alone`;
|
|
3205
|
+
following.report?.({
|
|
3206
|
+
file: systemFilePath,
|
|
3207
|
+
component: local,
|
|
3208
|
+
kind: "warn",
|
|
3209
|
+
message: `'${local}' is not proven to be Animus's createSystem: ${shown} — import createSystem from '@animus-ui/system', directly or through a re-export discovery can follow`,
|
|
3210
|
+
code: UNPROVEN_ROOT_BINDING,
|
|
3211
|
+
severity: severityFor(UNPROVEN_ROOT_BINDING),
|
|
3212
|
+
...locationIn(source, offset)
|
|
3213
|
+
});
|
|
3214
|
+
};
|
|
3215
|
+
for (const binding of imports) {
|
|
3216
|
+
if (binding.imported === "createSystem" || binding.local === "createSystem") {
|
|
3217
|
+
const statement = new RegExp(`\\bimport\\b[^;]*?\\bfrom\\s*['"]${escapeRegExp$1(binding.source)}['"]`, "g");
|
|
3218
|
+
let offset = 0;
|
|
3219
|
+
for (const match of source.matchAll(statement)) {
|
|
3220
|
+
const at = match[0].search(new RegExp(`(?<![a-zA-Z0-9_$])${escapeRegExp$1(binding.local)}\\b`));
|
|
3221
|
+
if (at !== -1) {
|
|
3222
|
+
offset = match.index + at;
|
|
3223
|
+
break;
|
|
3224
|
+
}
|
|
3225
|
+
}
|
|
3226
|
+
admit(binding.local, offset, await identityOf(binding.source, binding.imported), binding.imported === "createSystem");
|
|
3227
|
+
continue;
|
|
3228
|
+
}
|
|
3229
|
+
if (binding.source.startsWith(".") && called(binding.local) && (await identityOf(binding.source, binding.imported)).kind === "animus") roots.add(binding.local);
|
|
3230
|
+
}
|
|
3231
|
+
const namespaces = /* @__PURE__ */ new Map();
|
|
3232
|
+
for (const [, namespace, specifier] of source.matchAll(NAMESPACE_IMPORT)) {
|
|
3233
|
+
namespaces.set(namespace, specifier);
|
|
3234
|
+
const member = `${namespace}.createSystem`;
|
|
3235
|
+
if (ANIMUS_SYSTEM_SPECIFIER.test(specifier) || specifier.startsWith(".") && called(member) && (await identityOf(specifier, "createSystem")).kind === "animus") roots.add(member);
|
|
3236
|
+
}
|
|
3237
|
+
const destructuredFrom = (initializer) => {
|
|
3238
|
+
const loaded = /^\(?\s*(?:(?:await\s+)?import|require)\(\s*['"]([^'"]+)['"]\s*\)\s*\)?$/.exec(initializer);
|
|
3239
|
+
if (loaded) return identityOf(loaded[1], "createSystem");
|
|
3240
|
+
const namespace = namespaces.get(initializer);
|
|
3241
|
+
if (namespace !== void 0) return identityOf(namespace, "createSystem");
|
|
3242
|
+
return Promise.resolve({
|
|
3243
|
+
kind: "unknown",
|
|
3244
|
+
why: `it is destructured from '${initializer}', which is not a module namespace`
|
|
3245
|
+
});
|
|
3246
|
+
};
|
|
3247
|
+
for (const { local, offset, initializer } of destructuredCreateSystems(source)) admit(local, offset, await destructuredFrom(initializer), local === "createSystem");
|
|
3248
|
+
const unimported = unimportedCreateSystemCall(systemFilePath, record);
|
|
3249
|
+
if (unimported) following.report?.(unimported);
|
|
3250
|
+
return roots;
|
|
3251
|
+
}
|
|
3252
|
+
/** A call of `callee` as a whole name, not a member. */
|
|
3253
|
+
function callOf(callee) {
|
|
3254
|
+
return new RegExp(`(?<![a-zA-Z0-9_$.])${escapeRegExp$1(callee)}\\s*\\(`);
|
|
3255
|
+
}
|
|
3256
|
+
/** Each `createSystem` property a declaration destructures: its local name,
|
|
3257
|
+
* its offset in `source`, and the destructured initializer. */
|
|
3258
|
+
function* destructuredCreateSystems(source) {
|
|
3259
|
+
for (const match of source.matchAll(/\b(?:const|let|var)\s*\{([^}]*)\}\s*=\s*([^;\n]+)/g)) {
|
|
3260
|
+
const [whole, pattern, initializer] = match;
|
|
3261
|
+
for (const property of pattern.split(",")) {
|
|
3262
|
+
const binding = /^\s*createSystem\s*(?::\s*([a-zA-Z_$][a-zA-Z0-9_$]*))?\s*(?:=[\s\S]*)?$/.exec(property);
|
|
3263
|
+
if (!binding) continue;
|
|
3264
|
+
yield {
|
|
3265
|
+
local: binding[1] ?? "createSystem",
|
|
3266
|
+
offset: match.index + whole.indexOf("createSystem"),
|
|
3267
|
+
initializer: initializer.trim()
|
|
3268
|
+
};
|
|
3269
|
+
}
|
|
3270
|
+
}
|
|
3271
|
+
}
|
|
3272
|
+
/** The warning for a `createSystem` call that the parser's scopes leave
|
|
3273
|
+
* unbound: no import, declaration or parameter names it. Null when there is
|
|
3274
|
+
* none, or when the parser reports no scopes. */
|
|
3275
|
+
function unimportedCreateSystemCall(systemFilePath, record) {
|
|
3276
|
+
const call = record.unboundCreateSystemCalls?.[0];
|
|
3277
|
+
if (!call) return null;
|
|
3278
|
+
return {
|
|
3279
|
+
file: systemFilePath,
|
|
3280
|
+
component: "createSystem",
|
|
3281
|
+
kind: "warn",
|
|
3282
|
+
message: `'createSystem' is called with no import or local binding, so it is not read as a system root: the system loader evaluates this file without auto-imports, where the name is undefined — import createSystem from '@animus-ui/system'`,
|
|
3283
|
+
code: UNIMPORTED_CREATE_SYSTEM,
|
|
3284
|
+
severity: severityFor(UNIMPORTED_CREATE_SYSTEM),
|
|
3285
|
+
line: call[0],
|
|
3286
|
+
column: call[1]
|
|
3287
|
+
};
|
|
3288
|
+
}
|
|
2440
3289
|
/** A pattern matching a call of any of `callees` as a whole name, or null
|
|
2441
3290
|
* when there is none to anchor on. */
|
|
2442
3291
|
function rootCallPattern(callees) {
|
|
2443
3292
|
if (callees.length === 0) return null;
|
|
2444
3293
|
return `(?<![a-zA-Z0-9_$.])(?:${callees.map((callee) => callee.replace(/[.$]/g, "\\$&")).join("|")})`;
|
|
2445
3294
|
}
|
|
2446
|
-
function extractSystemFilePackages(systemFilePath,
|
|
3295
|
+
async function extractSystemFilePackages(systemFilePath, parseModule, report, resolvePackage) {
|
|
2447
3296
|
let source;
|
|
2448
3297
|
try {
|
|
2449
3298
|
source = (0, fs.readFileSync)(systemFilePath, "utf-8");
|
|
2450
3299
|
} catch {
|
|
2451
3300
|
return [];
|
|
2452
3301
|
}
|
|
2453
|
-
const parsed =
|
|
2454
|
-
const parsedImports = parsed && parsed.length > 0 ? parsed : null;
|
|
3302
|
+
const parsed = parseModule?.(source, systemFilePath) ?? null;
|
|
2455
3303
|
let rootCall = "createSystem";
|
|
2456
|
-
if (
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
...shadowed ? [] : ["createSystem"]
|
|
2462
|
-
])]);
|
|
2463
|
-
}
|
|
3304
|
+
if (parseModule && parsed) rootCall = rootCallPattern([...await systemRoots(systemFilePath, source, parsed, {
|
|
3305
|
+
parseModule,
|
|
3306
|
+
resolvePackage,
|
|
3307
|
+
report
|
|
3308
|
+
})]);
|
|
2464
3309
|
const identifiers = /* @__PURE__ */ new Set();
|
|
2465
3310
|
const constructorRegex = rootCall === null ? null : new RegExp(`${rootCall}\\s*\\(\\s*\\{[^}]*?\\bincludes\\s*:\\s*\\[([^\\]]*)\\]`, "gs");
|
|
2466
3311
|
const chainRegex = /\.includes\s*\(\s*\[([^\]]*)\]\s*\)/gs;
|
|
@@ -2626,239 +3471,94 @@ function extractSystemFilePackages(systemFilePath, parseImports) {
|
|
|
2626
3471
|
}
|
|
2627
3472
|
}
|
|
2628
3473
|
}
|
|
2629
|
-
if (identifiers.size === 0) return [];
|
|
2630
|
-
const importMap = /* @__PURE__ */ new Map();
|
|
2631
|
-
for (const binding of
|
|
2632
|
-
const importRegex = /^\s*import\s+(?:([a-zA-Z_$][a-zA-Z0-9_$]*)\s*,\s*)?(?:\{([^}]*)\}|([a-zA-Z_$][a-zA-Z0-9_$]*))\s+from\s+['"]([^'"]+)['"]/gm;
|
|
2633
|
-
if (!
|
|
2634
|
-
let importMatch;
|
|
2635
|
-
while ((importMatch = importRegex.exec(source)) !== null) {
|
|
2636
|
-
const [, comboDefault, namedImports, defaultImport, specifier] = importMatch;
|
|
2637
|
-
if (comboDefault) importMap.set(comboDefault, specifier);
|
|
2638
|
-
if (defaultImport) importMap.set(defaultImport, specifier);
|
|
2639
|
-
if (namedImports) for (const binding of namedImports.split(",")) {
|
|
2640
|
-
const parts = binding.trim().split(/\s+as\s+/);
|
|
2641
|
-
const localName = (parts[1] || parts[0]).trim();
|
|
2642
|
-
if (localName) importMap.set(localName, specifier);
|
|
2643
|
-
}
|
|
2644
|
-
}
|
|
2645
|
-
}
|
|
2646
|
-
const systemFileDir = (0, path.dirname)(systemFilePath);
|
|
2647
|
-
const packages = /* @__PURE__ */ new Set();
|
|
2648
|
-
/** The import a local alias chain ends at, or the identifier itself. */
|
|
2649
|
-
const throughAliases = (id) => {
|
|
2650
|
-
const seen = /* @__PURE__ */ new Set();
|
|
2651
|
-
let current = id;
|
|
2652
|
-
while (!importMap.has(current) && !seen.has(current)) {
|
|
2653
|
-
seen.add(current);
|
|
2654
|
-
const target = aliasOf.get(current);
|
|
2655
|
-
if (!target) break;
|
|
2656
|
-
current = target;
|
|
2657
|
-
}
|
|
2658
|
-
return current;
|
|
2659
|
-
};
|
|
2660
|
-
for (const id of identifiers) {
|
|
2661
|
-
const specifier = importMap.get(throughAliases(id));
|
|
2662
|
-
if (!specifier) continue;
|
|
2663
|
-
if (specifier.startsWith(".")) packages.add((0, path.resolve)(systemFileDir, specifier));
|
|
2664
|
-
else packages.add(specifier);
|
|
2665
|
-
}
|
|
2666
|
-
return Array.from(packages);
|
|
2667
|
-
}
|
|
2668
|
-
//#endregion
|
|
2669
|
-
//#region pipeline/source-corpus.ts
|
|
2670
|
-
function assembleRawEntries({ fileCache, externalFileOwners }) {
|
|
2671
|
-
const project = [];
|
|
2672
|
-
const external = [];
|
|
2673
|
-
for (const [path, { hash, source }] of fileCache) (externalFileOwners[path] === void 0 ? project : external).push({
|
|
2674
|
-
path,
|
|
2675
|
-
source,
|
|
2676
|
-
hash
|
|
2677
|
-
});
|
|
2678
|
-
project.sort((a, b) => compareDiscoveryOrder(a.path, b.path));
|
|
2679
|
-
return [...project, ...external];
|
|
2680
|
-
}
|
|
2681
|
-
function createSourceCorpus(host, ingestor = createSourceIngestor(host)) {
|
|
2682
|
-
let published = {
|
|
2683
|
-
analysisEntries: /* @__PURE__ */ new Map(),
|
|
2684
|
-
ownership: {}
|
|
2685
|
-
};
|
|
2686
|
-
return {
|
|
2687
|
-
async prepare(input) {
|
|
2688
|
-
const rawEntries = "fileCache" in input ? assembleRawEntries(input) : input;
|
|
2689
|
-
const ingested = await ingestor.ingest(rawEntries);
|
|
2690
|
-
return withoutInvalidOriginals(ingested, ingestor.surfaceDiagnostics(ingested.diagnostics));
|
|
2691
|
-
},
|
|
2692
|
-
rejection(prepared) {
|
|
2693
|
-
const aborted = (prepared.abortedParses ?? []).filter(({ originalPath }) => prepared.ownership[originalPath] !== void 0);
|
|
2694
|
-
if (aborted.length === 0) return null;
|
|
2695
|
-
const files = aborted.map(({ originalPath, messages }) => `${originalPath} (${messages.join("; ")})`).join(", ");
|
|
2696
|
-
const owners = aborted.length === 1 ? "this file's" : "these files'";
|
|
2697
|
-
return {
|
|
2698
|
-
originals: new Map(aborted.map(({ originalPath, hash }) => [originalPath, hash])),
|
|
2699
|
-
message: `${host.prefix} analysis not published: the parser stopped before the end of ${files}; fix the syntax error to publish ${owners} changes`
|
|
2700
|
-
};
|
|
2701
|
-
},
|
|
2702
|
-
publish(accepted) {
|
|
2703
|
-
ingestor.markPublished(accepted);
|
|
2704
|
-
published = {
|
|
2705
|
-
analysisEntries: new Map(accepted.analysisEntries.map((entry) => [entry.path, {
|
|
2706
|
-
hash: entry.hash,
|
|
2707
|
-
source: entry.source
|
|
2708
|
-
}])),
|
|
2709
|
-
ownership: accepted.ownership
|
|
2710
|
-
};
|
|
2711
|
-
},
|
|
2712
|
-
get published() {
|
|
2713
|
-
return published;
|
|
2714
|
-
}
|
|
2715
|
-
};
|
|
2716
|
-
}
|
|
2717
|
-
//#endregion
|
|
2718
|
-
//#region pipeline/asset-placeholders.ts
|
|
2719
|
-
const ASSET_PLACEHOLDER_PREFIX = "animus-asset:";
|
|
2720
|
-
const URL_OPEN_RE = /url\(\s*(['"]?)/gi;
|
|
2721
|
-
const IMAGE_SET_OPEN_RE = /(?:-webkit-)?image-set\(/gi;
|
|
2722
|
-
const BARE_SPECIFIER_RE = /^[^'")\s]+/;
|
|
2723
|
-
/** The index of the `)` that closes the `(` at `open`, outside strings. */
|
|
2724
|
-
function closingParen(css, open) {
|
|
2725
|
-
let depth = 0;
|
|
2726
|
-
let quote = null;
|
|
2727
|
-
for (let i = open; i < css.length; i += 1) {
|
|
2728
|
-
const c = css[i];
|
|
2729
|
-
if (quote !== null) {
|
|
2730
|
-
if (c === quote) quote = null;
|
|
2731
|
-
} else if (c === "\"" || c === "'") quote = c;
|
|
2732
|
-
else if (c === "(") depth += 1;
|
|
2733
|
-
else if (c === ")") {
|
|
2734
|
-
depth -= 1;
|
|
2735
|
-
if (depth === 0) return i;
|
|
2736
|
-
}
|
|
2737
|
-
}
|
|
2738
|
-
return css.length;
|
|
2739
|
-
}
|
|
2740
|
-
function quotedReference(css, quoteAt) {
|
|
2741
|
-
const start = quoteAt + 1;
|
|
2742
|
-
if (!css.startsWith("animus-asset:", start)) return null;
|
|
2743
|
-
const end = css.indexOf(css[quoteAt], start);
|
|
2744
|
-
if (end === -1) return null;
|
|
2745
|
-
return {
|
|
2746
|
-
start,
|
|
2747
|
-
end,
|
|
2748
|
-
specifier: css.slice(start + 13, end)
|
|
2749
|
-
};
|
|
2750
|
-
}
|
|
2751
|
-
function assetReferences(css) {
|
|
2752
|
-
if (!css.includes("animus-asset:")) return [];
|
|
2753
|
-
const references = [];
|
|
2754
|
-
for (const open of css.matchAll(URL_OPEN_RE)) {
|
|
2755
|
-
const at = (open.index ?? 0) + open[0].length;
|
|
2756
|
-
if (open[1]) {
|
|
2757
|
-
const reference = quotedReference(css, at - 1);
|
|
2758
|
-
if (reference) references.push(reference);
|
|
2759
|
-
continue;
|
|
2760
|
-
}
|
|
2761
|
-
if (!css.startsWith("animus-asset:", at)) continue;
|
|
2762
|
-
const specifier = BARE_SPECIFIER_RE.exec(css.slice(at + 13))?.[0];
|
|
2763
|
-
if (specifier === void 0) continue;
|
|
2764
|
-
const end = at + 13 + specifier.length;
|
|
2765
|
-
references.push({
|
|
2766
|
-
start: at,
|
|
2767
|
-
end,
|
|
2768
|
-
specifier
|
|
2769
|
-
});
|
|
2770
|
-
}
|
|
2771
|
-
for (const open of css.matchAll(IMAGE_SET_OPEN_RE)) {
|
|
2772
|
-
const paren = (open.index ?? 0) + open[0].length - 1;
|
|
2773
|
-
const close = closingParen(css, paren);
|
|
2774
|
-
let depth = 0;
|
|
2775
|
-
let candidateStart = true;
|
|
2776
|
-
for (let i = paren + 1; i < close; i += 1) {
|
|
2777
|
-
const c = css[i];
|
|
2778
|
-
if (/\s/.test(c)) continue;
|
|
2779
|
-
if (c === "\"" || c === "'") {
|
|
2780
|
-
if (depth === 0 && candidateStart) {
|
|
2781
|
-
const reference = quotedReference(css, i);
|
|
2782
|
-
if (reference) references.push(reference);
|
|
2783
|
-
}
|
|
2784
|
-
const end = css.indexOf(c, i + 1);
|
|
2785
|
-
i = end === -1 ? close : end;
|
|
2786
|
-
} else if (c === "(") depth += 1;
|
|
2787
|
-
else if (c === ")") depth -= 1;
|
|
2788
|
-
else if (c === "," && depth === 0) {
|
|
2789
|
-
candidateStart = true;
|
|
2790
|
-
continue;
|
|
2791
|
-
}
|
|
2792
|
-
candidateStart = false;
|
|
2793
|
-
}
|
|
2794
|
-
}
|
|
2795
|
-
return references.sort((a, b) => a.start - b.start);
|
|
2796
|
-
}
|
|
2797
|
-
function findAssetSpecifiers(css) {
|
|
2798
|
-
return [...new Set(assetReferences(css).map((reference) => reference.specifier))];
|
|
2799
|
-
}
|
|
2800
|
-
function substituteAssetPlaceholders(css, urlBySpecifier) {
|
|
2801
|
-
if (urlBySpecifier.size === 0) return css;
|
|
2802
|
-
let out = css;
|
|
2803
|
-
for (const reference of assetReferences(css).reverse()) {
|
|
2804
|
-
const url = urlBySpecifier.get(reference.specifier);
|
|
2805
|
-
if (url === void 0) continue;
|
|
2806
|
-
out = out.slice(0, reference.start) + url + out.slice(reference.end);
|
|
3474
|
+
if (identifiers.size === 0) return [];
|
|
3475
|
+
const importMap = /* @__PURE__ */ new Map();
|
|
3476
|
+
for (const binding of parsed?.imports ?? []) importMap.set(binding.local, binding.source);
|
|
3477
|
+
const importRegex = /^\s*import\s+(?:([a-zA-Z_$][a-zA-Z0-9_$]*)\s*,\s*)?(?:\{([^}]*)\}|([a-zA-Z_$][a-zA-Z0-9_$]*))\s+from\s+['"]([^'"]+)['"]/gm;
|
|
3478
|
+
if (!parsed) {
|
|
3479
|
+
let importMatch;
|
|
3480
|
+
while ((importMatch = importRegex.exec(source)) !== null) {
|
|
3481
|
+
const [, comboDefault, namedImports, defaultImport, specifier] = importMatch;
|
|
3482
|
+
if (comboDefault) importMap.set(comboDefault, specifier);
|
|
3483
|
+
if (defaultImport) importMap.set(defaultImport, specifier);
|
|
3484
|
+
if (namedImports) for (const binding of namedImports.split(",")) {
|
|
3485
|
+
const parts = binding.trim().split(/\s+as\s+/);
|
|
3486
|
+
const localName = (parts[1] || parts[0]).trim();
|
|
3487
|
+
if (localName) importMap.set(localName, specifier);
|
|
3488
|
+
}
|
|
3489
|
+
}
|
|
2807
3490
|
}
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
/**
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
3491
|
+
const systemFileDir = (0, path.dirname)(systemFilePath);
|
|
3492
|
+
const packages = /* @__PURE__ */ new Set();
|
|
3493
|
+
/** The import a local alias chain ends at, or the identifier itself. */
|
|
3494
|
+
const throughAliases = (id) => {
|
|
3495
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3496
|
+
let current = id;
|
|
3497
|
+
while (!importMap.has(current) && !seen.has(current)) {
|
|
3498
|
+
seen.add(current);
|
|
3499
|
+
const target = aliasOf.get(current);
|
|
3500
|
+
if (!target) break;
|
|
3501
|
+
current = target;
|
|
3502
|
+
}
|
|
3503
|
+
return current;
|
|
3504
|
+
};
|
|
3505
|
+
for (const id of identifiers) {
|
|
3506
|
+
const specifier = importMap.get(throughAliases(id));
|
|
3507
|
+
if (!specifier) continue;
|
|
3508
|
+
if (specifier.startsWith(".")) packages.add((0, path.resolve)(systemFileDir, specifier));
|
|
3509
|
+
else packages.add(specifier);
|
|
2824
3510
|
}
|
|
2825
|
-
return
|
|
3511
|
+
return Array.from(packages);
|
|
2826
3512
|
}
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
])]
|
|
3513
|
+
//#endregion
|
|
3514
|
+
//#region pipeline/source-corpus.ts
|
|
3515
|
+
function assembleRawEntries({ fileCache, externalFileOwners }) {
|
|
3516
|
+
const project = [];
|
|
3517
|
+
const external = [];
|
|
3518
|
+
for (const [path, { hash, source }] of fileCache) (externalFileOwners[path] === void 0 ? project : external).push({
|
|
3519
|
+
path,
|
|
3520
|
+
source,
|
|
3521
|
+
hash
|
|
3522
|
+
});
|
|
3523
|
+
project.sort((a, b) => compareDiscoveryOrder(a.path, b.path));
|
|
3524
|
+
return [...project, ...external];
|
|
2833
3525
|
}
|
|
2834
|
-
|
|
2835
|
-
|
|
3526
|
+
function createSourceCorpus(host, ingestor = createSourceIngestor(host)) {
|
|
3527
|
+
let published = {
|
|
3528
|
+
analysisEntries: /* @__PURE__ */ new Map(),
|
|
3529
|
+
ownership: {}
|
|
3530
|
+
};
|
|
2836
3531
|
return {
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
3532
|
+
async prepare(input) {
|
|
3533
|
+
const rawEntries = "fileCache" in input ? assembleRawEntries(input) : input;
|
|
3534
|
+
const ingested = await ingestor.ingest(rawEntries);
|
|
3535
|
+
return withoutInvalidOriginals(ingested, ingestor.surfaceDiagnostics(ingested.diagnostics));
|
|
3536
|
+
},
|
|
3537
|
+
rejection(prepared) {
|
|
3538
|
+
const aborted = (prepared.abortedParses ?? []).filter(({ originalPath }) => prepared.ownership[originalPath] !== void 0);
|
|
3539
|
+
if (aborted.length === 0) return null;
|
|
3540
|
+
const files = aborted.map(({ originalPath, messages }) => `${originalPath} (${messages.join("; ")})`).join(", ");
|
|
3541
|
+
const owners = aborted.length === 1 ? "this file's" : "these files'";
|
|
3542
|
+
return {
|
|
3543
|
+
originals: new Map(aborted.map(({ originalPath, hash }) => [originalPath, hash])),
|
|
3544
|
+
message: `${host.prefix} analysis not published: the parser stopped before the end of ${files}; fix the syntax error to publish ${owners} changes`
|
|
3545
|
+
};
|
|
3546
|
+
},
|
|
3547
|
+
publish(accepted) {
|
|
3548
|
+
ingestor.markPublished(accepted);
|
|
3549
|
+
published = {
|
|
3550
|
+
analysisEntries: new Map(accepted.analysisEntries.map((entry) => [entry.path, {
|
|
3551
|
+
hash: entry.hash,
|
|
3552
|
+
source: entry.source
|
|
3553
|
+
}])),
|
|
3554
|
+
ownership: accepted.ownership
|
|
3555
|
+
};
|
|
3556
|
+
},
|
|
3557
|
+
get published() {
|
|
3558
|
+
return published;
|
|
3559
|
+
}
|
|
2840
3560
|
};
|
|
2841
3561
|
}
|
|
2842
|
-
const UNSUBSTITUTED_ASSET_CODE = "animus.asset.unsubstituted-placeholder";
|
|
2843
|
-
/** A placeholder in emitted CSS is a broken URL in the browser: it fails a
|
|
2844
|
-
* strict build and warns otherwise. */
|
|
2845
|
-
function reportSurvivingAssetPlaceholders(css, report) {
|
|
2846
|
-
const specifiers = placeholdersInAssetCalls(css);
|
|
2847
|
-
if (specifiers.length === 0) return;
|
|
2848
|
-
const message = `${report.prefix} asset() placeholders reached ${report.surface ?? "emitted CSS"} unsubstituted: ${specifiers.join(", ")} (${UNSUBSTITUTED_ASSET_CODE})`;
|
|
2849
|
-
if (report.strict) throw new Error(message);
|
|
2850
|
-
report.warn(message);
|
|
2851
|
-
}
|
|
2852
|
-
/**
|
|
2853
|
-
* The runtime code an analysis generates: each dynamic prop config, which the
|
|
2854
|
-
* shared prop map module carries, and each component's replacement. Extraction
|
|
2855
|
-
* lifts asset() out of them into root variables, so none should hold one.
|
|
2856
|
-
* Both embed values as JSON strings, so their quote and backslash escapes are
|
|
2857
|
-
* undone for scanning: the text is for `findAssetSpecifiers`, not to run.
|
|
2858
|
-
*/
|
|
2859
|
-
function generatedModuleCode(manifest) {
|
|
2860
|
-
return [JSON.stringify(manifest.dynamic_props), ...Object.values(manifest.components).map((component) => component.replacement)].join("\n").replace(/\\(["\\])/g, "$1");
|
|
2861
|
-
}
|
|
2862
3562
|
//#endregion
|
|
2863
3563
|
//#region pipeline/resolve-asset.ts
|
|
2864
3564
|
/**
|
|
@@ -3027,8 +3727,13 @@ function enforceExternalTokenContracts(opts) {
|
|
|
3027
3727
|
sourceTokens
|
|
3028
3728
|
});
|
|
3029
3729
|
if (messages.length === 0) return;
|
|
3030
|
-
if (opts.strict)
|
|
3031
|
-
|
|
3730
|
+
if (!opts.strict) {
|
|
3731
|
+
for (const message of messages) opts.warn(message);
|
|
3732
|
+
return;
|
|
3733
|
+
}
|
|
3734
|
+
const message = `${opts.prefix} ${messages.join("\n")}`;
|
|
3735
|
+
if (!opts.reportErrors) throw new Error(message);
|
|
3736
|
+
opts.reportErrors(message);
|
|
3032
3737
|
}
|
|
3033
3738
|
//#endregion
|
|
3034
3739
|
//#region pipeline/path-aliases.ts
|
|
@@ -3640,11 +4345,14 @@ function buildAnalysisInputs(opts) {
|
|
|
3640
4345
|
externalDirsJson: opts.externalDirs?.length && hasSourceThemeManifests(opts.system) ? JSON.stringify(opts.externalDirs) : null
|
|
3641
4346
|
};
|
|
3642
4347
|
if (opts.system.declarationScalesJson) inputs.declarationScalesJson = opts.system.declarationScalesJson;
|
|
4348
|
+
if (opts.system.generatedNamesJson) inputs.generatedNamesJson = opts.system.generatedNamesJson;
|
|
3643
4349
|
const context = opts.analysisContext;
|
|
3644
4350
|
if (context?.skippedSources?.length || context?.unbundledComputedImports || context?.packageDirs?.length) inputs.analysisContextJson = JSON.stringify({
|
|
3645
4351
|
skippedSources: context.skippedSources ?? [],
|
|
3646
4352
|
unbundledComputedImports: context.unbundledComputedImports ?? false,
|
|
3647
|
-
packageDirs: context.packageDirs ?? []
|
|
4353
|
+
packageDirs: context.packageDirs ?? [],
|
|
4354
|
+
linkedDirs: context.linkedDirs ?? [],
|
|
4355
|
+
outputDirs: context.outputDirs ?? []
|
|
3648
4356
|
});
|
|
3649
4357
|
return inputs;
|
|
3650
4358
|
}
|
|
@@ -3660,9 +4368,12 @@ function hasSourceThemeManifests(system) {
|
|
|
3660
4368
|
const surfacedSystems = /* @__PURE__ */ new WeakSet();
|
|
3661
4369
|
/** The loaded system's own diagnostics, the first time it is analyzed. A
|
|
3662
4370
|
* strict error keeps failing every analysis until the system changes. */
|
|
3663
|
-
function systemDiagnostics(system, strict) {
|
|
4371
|
+
function systemDiagnostics(system, strict, levels) {
|
|
3664
4372
|
const diagnostics = [...collectSelectorAliasDiagnostics(system.selectorAliasesJson), ...systemLoadDiagnostics(system)];
|
|
3665
|
-
const blocking =
|
|
4373
|
+
const blocking = diagnostics.some((d) => effectiveLevel(d, {
|
|
4374
|
+
strict,
|
|
4375
|
+
levels
|
|
4376
|
+
}) === "error");
|
|
3666
4377
|
if (surfacedSystems.has(system) && !blocking) return [];
|
|
3667
4378
|
surfacedSystems.add(system);
|
|
3668
4379
|
return diagnostics;
|
|
@@ -3672,7 +4383,7 @@ function systemDiagnostics(system, strict) {
|
|
|
3672
4383
|
* the call site (strict-mode throw vs warn).
|
|
3673
4384
|
*/
|
|
3674
4385
|
function runProjectAnalysis(engineApi, opts) {
|
|
3675
|
-
const { analyzeProject } = engineApi();
|
|
4386
|
+
const { analyzeProject, diagnosticCodes } = engineApi();
|
|
3676
4387
|
let t = performance.now();
|
|
3677
4388
|
const inputs = buildAnalysisInputs(opts);
|
|
3678
4389
|
const serializeMs = Math.round(performance.now() - t);
|
|
@@ -3682,7 +4393,12 @@ function runProjectAnalysis(engineApi, opts) {
|
|
|
3682
4393
|
t = performance.now();
|
|
3683
4394
|
const manifest = JSON.parse(manifestJson);
|
|
3684
4395
|
const systemErrors = systemLoadDiagnostics(opts.system).filter((diagnostic) => diagnostic.kind === "error");
|
|
3685
|
-
|
|
4396
|
+
const kitDiagnostics = kitDescriptorDiagnostics(opts.kitDescriptors ?? [], manifest);
|
|
4397
|
+
if (systemErrors.length > 0 || kitDiagnostics.length > 0) manifest.diagnostics = [
|
|
4398
|
+
...systemErrors,
|
|
4399
|
+
...kitDiagnostics,
|
|
4400
|
+
...manifest.diagnostics ?? []
|
|
4401
|
+
];
|
|
3686
4402
|
const componentCss = applyUnitFallback(manifest.css);
|
|
3687
4403
|
const propertyDiagnostics = checkCustomProperties({
|
|
3688
4404
|
system: opts.system,
|
|
@@ -3693,8 +4409,11 @@ function runProjectAnalysis(engineApi, opts) {
|
|
|
3693
4409
|
surfaceManifestDiagnostics(manifest, opts.warn, {
|
|
3694
4410
|
strict: opts.strict,
|
|
3695
4411
|
info: opts.info,
|
|
4412
|
+
reportErrors: opts.reportErrors,
|
|
4413
|
+
levels: opts.diagnostics,
|
|
4414
|
+
knownCodes: opts.diagnostics ? knownDiagnosticCodes(diagnosticCodes?.()) : void 0,
|
|
3696
4415
|
prepend: [
|
|
3697
|
-
...systemDiagnostics(opts.system, opts.strict),
|
|
4416
|
+
...systemDiagnostics(opts.system, opts.strict, opts.diagnostics),
|
|
3698
4417
|
...propertyDiagnostics,
|
|
3699
4418
|
...opts.extraDiagnostics ?? []
|
|
3700
4419
|
]
|
|
@@ -3798,36 +4517,34 @@ function renameCustomProperties(css, names) {
|
|
|
3798
4517
|
}
|
|
3799
4518
|
//#endregion
|
|
3800
4519
|
//#region pipeline/prefix.ts
|
|
3801
|
-
/**
|
|
3802
|
-
|
|
3803
|
-
|
|
3804
|
-
|
|
3805
|
-
|
|
3806
|
-
|
|
4520
|
+
/**
|
|
4521
|
+
* The prefix without `prefixContextualVars`: every name Animus generates (a
|
|
4522
|
+
* variable the theme's map or variable CSS defines) takes `--${prefix}-`
|
|
4523
|
+
* wherever it is defined, registered or read, `var()` fallbacks at any depth
|
|
4524
|
+
* included. Every other name stays as written: each declared contextual
|
|
4525
|
+
* variable, and any custom property an author writes.
|
|
4526
|
+
*/
|
|
4527
|
+
function applyPrefix(prefix, variableMapJson, variableCss, themeJson, contextualVarsJson, declarationScalesJson) {
|
|
3807
4528
|
if (!prefix) return {
|
|
3808
4529
|
variableMapJson,
|
|
3809
4530
|
variableCss,
|
|
3810
4531
|
themeJson,
|
|
3811
|
-
contextualVarsJson
|
|
4532
|
+
contextualVarsJson,
|
|
4533
|
+
declarationScalesJson
|
|
3812
4534
|
};
|
|
3813
|
-
const
|
|
3814
|
-
const
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
css = prefixVariableReferences(prefix, css);
|
|
3819
|
-
css = css.replace(/@property(\s+)--([a-zA-Z][\w-]*)/g, `@property$1--${prefix}-$2`);
|
|
4535
|
+
const contextual = new Set(contextualVarsJson ? Object.values(parseInternalWire(contextualVarsJson, "contextualVarsJson (the theme's contextual variable names)")).flat() : []);
|
|
4536
|
+
const variableMap = parseInternalWire(variableMapJson, "variableMapJson (the theme's token variables)");
|
|
4537
|
+
const generated = [...Object.values(variableMap).map((name) => name.replace(/^--/, "")), ...themeDefinitions(variableCss)].filter((name) => !contextual.has(name));
|
|
4538
|
+
const names = createPropertyNames([], prefix, generated);
|
|
4539
|
+
const rename = (value) => renameCustomProperties(value, names);
|
|
3820
4540
|
const result = {
|
|
3821
|
-
variableMapJson: JSON.stringify(
|
|
3822
|
-
variableCss:
|
|
4541
|
+
variableMapJson: JSON.stringify(Object.fromEntries(Object.entries(variableMap).map(([key, name]) => [key, rename(name)]))),
|
|
4542
|
+
variableCss: rename(variableCss)
|
|
3823
4543
|
};
|
|
3824
|
-
if (themeJson) result.themeJson =
|
|
3825
|
-
if (contextualVarsJson)
|
|
3826
|
-
|
|
3827
|
-
|
|
3828
|
-
for (const [scale, names] of Object.entries(ctxVars)) prefixedCtx[scale] = names.map((name) => `${prefix}-${name}`);
|
|
3829
|
-
result.contextualVarsJson = JSON.stringify(prefixedCtx);
|
|
3830
|
-
}
|
|
4544
|
+
if (themeJson) result.themeJson = renameTokenValues(themeJson, rename);
|
|
4545
|
+
if (contextualVarsJson) result.contextualVarsJson = contextualVarsJson;
|
|
4546
|
+
if (generated.length > 0) result.generatedNamesJson = JSON.stringify(Object.fromEntries(generated.map((name) => [name, `${prefix}-${name}`])));
|
|
4547
|
+
if (declarationScalesJson) result.declarationScalesJson = renameDeclarationRecords(declarationScalesJson, rename);
|
|
3831
4548
|
return result;
|
|
3832
4549
|
}
|
|
3833
4550
|
/** The runtime's transport variables are `--animus-<prop>`. */
|
|
@@ -3843,7 +4560,8 @@ function applyPropertyNames(prefix, artifacts) {
|
|
|
3843
4560
|
const byScale = artifacts.contextualVarsJson ? parseInternalWire(artifacts.contextualVarsJson, "contextualVarsJson (the theme's contextual variable names)") : null;
|
|
3844
4561
|
const declared = Object.values(byScale ?? {}).flat();
|
|
3845
4562
|
const variableMap = parseInternalWire(artifacts.variableMapJson, "variableMapJson (the theme's token variables)");
|
|
3846
|
-
const
|
|
4563
|
+
const themeDefined = [...Object.values(variableMap).map((name) => name.replace(/^--/, "")), ...themeDefinitions(artifacts.variableCss)];
|
|
4564
|
+
const managed = createPropertyNames(declared, prefix, themeDefined);
|
|
3847
4565
|
const nameConflicts = [...[...new Set(declared)].flatMap((name) => {
|
|
3848
4566
|
const final = managed.finalName(name) ?? name;
|
|
3849
4567
|
return final.startsWith(TRANSPORT_NAMESPACE) ? [{
|
|
@@ -3867,11 +4585,30 @@ function applyPropertyNames(prefix, artifacts) {
|
|
|
3867
4585
|
}))]))),
|
|
3868
4586
|
declarationScalesJson: artifacts.declarationScalesJson === null ? null : renameDeclarationRecords(artifacts.declarationScalesJson, rename),
|
|
3869
4587
|
nameConflicts,
|
|
3870
|
-
contextualProperties: [...new Set(declared)].map((name) => `--${managed.finalName(name) ?? name}`)
|
|
4588
|
+
contextualProperties: [...new Set(declared)].map((name) => `--${managed.finalName(name) ?? name}`),
|
|
4589
|
+
generatedNamesJson: generatedNamesJson(themeDefined.filter((name) => !declared.includes(name)), managed)
|
|
3871
4590
|
};
|
|
3872
4591
|
}
|
|
3873
|
-
/**
|
|
3874
|
-
|
|
4592
|
+
/** The generated names' final names, for authored component styles. */
|
|
4593
|
+
function generatedNamesJson(names, managed) {
|
|
4594
|
+
const entries = [...new Set(names)].flatMap((name) => {
|
|
4595
|
+
const final = managed.finalName(name);
|
|
4596
|
+
return final === void 0 ? [] : [[name, final]];
|
|
4597
|
+
});
|
|
4598
|
+
return entries.length === 0 ? null : JSON.stringify(Object.fromEntries(entries));
|
|
4599
|
+
}
|
|
4600
|
+
/** The custom properties the theme's CSS declares or `@property` registers,
|
|
4601
|
+
* read as CSS tokenizes it: a name in a string or comment defines nothing. */
|
|
4602
|
+
function themeDefinitions(variableCss) {
|
|
4603
|
+
const tokens = (0, _animus_ui_properties.tokenize)(variableCss).filter((token) => token.type !== "whitespace");
|
|
4604
|
+
return tokens.flatMap((token, index) => {
|
|
4605
|
+
if (token.type !== "ident" || !token.value.startsWith("--")) return [];
|
|
4606
|
+
const previous = tokens[index - 1];
|
|
4607
|
+
const declared = (previous === void 0 || previous.type === "{" || previous.type === ";") && tokens[index + 1]?.type === ":";
|
|
4608
|
+
const registered = previous?.type === "at-keyword" && previous.value.toLowerCase() === "property";
|
|
4609
|
+
return declared || registered ? [token.value.slice(2)] : [];
|
|
4610
|
+
});
|
|
4611
|
+
}
|
|
3875
4612
|
/** Renames inside the token map's values. */
|
|
3876
4613
|
function renameTokenValues(themeJson, rename) {
|
|
3877
4614
|
const tokens = parseInternalWire(themeJson, "themeJson (the theme's flattened token values)");
|
|
@@ -3887,23 +4624,40 @@ function renameDeclarationRecords(declarationScalesJson, rename) {
|
|
|
3887
4624
|
}
|
|
3888
4625
|
//#endregion
|
|
3889
4626
|
//#region pipeline/system-config.ts
|
|
4627
|
+
/** The loader's evaluation of the system file. A failure on a file that
|
|
4628
|
+
* calls `createSystem` with no binding leads with discovery's warning line
|
|
4629
|
+
* for it, then keeps the loader's own error. */
|
|
4630
|
+
function evaluateSystemModule(engine, systemPath, rootDir, conditions) {
|
|
4631
|
+
try {
|
|
4632
|
+
return engine.loadSystemModule(systemPath, rootDir, void 0, conditions);
|
|
4633
|
+
} catch (error) {
|
|
4634
|
+
let source;
|
|
4635
|
+
try {
|
|
4636
|
+
source = (0, fs.readFileSync)(systemPath, "utf-8");
|
|
4637
|
+
} catch {
|
|
4638
|
+
throw error;
|
|
4639
|
+
}
|
|
4640
|
+
const record = engineModuleParser(engine)?.(source, systemPath);
|
|
4641
|
+
const call = record && unimportedCreateSystemCall(systemPath, record);
|
|
4642
|
+
if (!call) throw error;
|
|
4643
|
+
throw new Error(`${warnLine(call)}\n${String(error)}`, { cause: error });
|
|
4644
|
+
}
|
|
4645
|
+
}
|
|
3890
4646
|
/**
|
|
3891
4647
|
* Load and normalize a SystemInstance; `prefix` namespaces every CSS
|
|
3892
4648
|
* variable name. Error handling stays at the call site.
|
|
3893
4649
|
*/
|
|
3894
4650
|
function loadSystemConfig(engineApi, opts) {
|
|
3895
|
-
const
|
|
3896
|
-
const config = loadSystemModule(opts.systemPath, opts.rootDir);
|
|
4651
|
+
const config = evaluateSystemModule(engineApi(), opts.systemPath, opts.rootDir, opts.conditions);
|
|
3897
4652
|
let scalesJson = config.scalesJson;
|
|
3898
4653
|
let variableMapJson = config.variableMapJson;
|
|
3899
4654
|
const registrations = splitInvalidPropertyRegistrations(config.variableCss);
|
|
3900
4655
|
let variableCss = registrations.css;
|
|
3901
4656
|
let contextualVarsJson = config.contextualVarsJson || null;
|
|
3902
4657
|
let declarationScalesJson = config.declarationScalesJson || null;
|
|
3903
|
-
|
|
3904
|
-
let contextualProperties = declaredNames.map((name) => `--${name}`);
|
|
3905
|
-
let legacyPrefixedContextualVars = [];
|
|
4658
|
+
let contextualProperties = Object.values(parseInternalWire(contextualVarsJson ?? "{}", "contextualVarsJson (the theme's contextual variable names)")).flat().map((name) => `--${name}`);
|
|
3906
4659
|
let prefixNameConflicts = [];
|
|
4660
|
+
let generatedNamesJson;
|
|
3907
4661
|
if (opts.prefix && opts.prefixContextualVars) {
|
|
3908
4662
|
const resolved = applyPropertyNames(opts.prefix, {
|
|
3909
4663
|
variableMapJson,
|
|
@@ -3919,15 +4673,14 @@ function loadSystemConfig(engineApi, opts) {
|
|
|
3919
4673
|
contextualProperties = resolved.contextualProperties;
|
|
3920
4674
|
prefixNameConflicts = resolved.nameConflicts;
|
|
3921
4675
|
declarationScalesJson = resolved.declarationScalesJson;
|
|
4676
|
+
generatedNamesJson = resolved.generatedNamesJson;
|
|
3922
4677
|
} else if (opts.prefix) {
|
|
3923
|
-
|
|
3924
|
-
contextualProperties = declaredNames.map((name) => `--${opts.prefix}-${name}`);
|
|
3925
|
-
const prefixed = applyPrefix(opts.prefix, variableMapJson, variableCss, scalesJson, contextualVarsJson || void 0);
|
|
4678
|
+
const prefixed = applyPrefix(opts.prefix, variableMapJson, variableCss, scalesJson, contextualVarsJson || void 0, declarationScalesJson || void 0);
|
|
3926
4679
|
variableMapJson = prefixed.variableMapJson;
|
|
3927
4680
|
variableCss = prefixed.variableCss;
|
|
3928
4681
|
if (prefixed.themeJson) scalesJson = prefixed.themeJson;
|
|
3929
|
-
if (prefixed.
|
|
3930
|
-
|
|
4682
|
+
if (prefixed.declarationScalesJson) declarationScalesJson = prefixed.declarationScalesJson;
|
|
4683
|
+
generatedNamesJson = prefixed.generatedNamesJson;
|
|
3931
4684
|
}
|
|
3932
4685
|
const system = {
|
|
3933
4686
|
propConfigJson: config.propConfig,
|
|
@@ -3951,7 +4704,7 @@ function loadSystemConfig(engineApi, opts) {
|
|
|
3951
4704
|
if (config.propertyRecordsJson) system.propertyRecordsJson = config.propertyRecordsJson;
|
|
3952
4705
|
if (registrations.invalid.length > 0) system.invalidPropertyRegistrations = registrations.invalid;
|
|
3953
4706
|
if (prefixNameConflicts.length > 0) system.prefixNameConflicts = prefixNameConflicts;
|
|
3954
|
-
if (
|
|
4707
|
+
if (generatedNamesJson) system.generatedNamesJson = generatedNamesJson;
|
|
3955
4708
|
return system;
|
|
3956
4709
|
}
|
|
3957
4710
|
//#endregion
|
|
@@ -3960,8 +4713,7 @@ function runStructuralSelfCheck(input) {
|
|
|
3960
4713
|
const failures = [];
|
|
3961
4714
|
if (input.componentCount === 0) failures.push("No component CSS produced — check the system file and its includes list");
|
|
3962
4715
|
else if (input.assembledCss !== void 0 ? !/@layer\s+anm-base\s*\{/.test(input.assembledCss) : input.componentCss.trim().length === 0) failures.push(`${input.componentCount} component(s) discovered but the emitted component CSS is empty`);
|
|
3963
|
-
for (const { specifier, outcome } of input.externalOutcomes ?? []) if (outcome === "
|
|
3964
|
-
else if (outcome === "unresolvable") failures.push(`include '${specifier}' could not be resolved`);
|
|
4716
|
+
for (const { specifier, outcome } of input.externalOutcomes ?? []) if (outcome === "unresolvable") failures.push(`include '${specifier}' could not be resolved`);
|
|
3965
4717
|
if ((input.variableMapJson == null || Object.keys(parseInternalWire(input.variableMapJson, "variableMapJson (the theme's token → variable map)")).length > 0) && !input.variableCss.includes(":root")) failures.push("No :root variable block found in variable CSS");
|
|
3966
4718
|
if ((input.assembledCss ?? `${input.variableCss}\n${input.globalCss}\n${input.componentCss}`).includes("__TRANSFORM__")) failures.push("Unresolved __TRANSFORM__ placeholders found in CSS output");
|
|
3967
4719
|
if (input.componentCount > 0 && (input.componentCss.length > 0 || input.assembledCss !== void 0)) {
|
|
@@ -3995,7 +4747,8 @@ function buildDynamicPropConfig(dynamicProps) {
|
|
|
3995
4747
|
kind: meta.kind,
|
|
3996
4748
|
slotClass: meta.slotClass,
|
|
3997
4749
|
memberVars: meta.memberVars,
|
|
3998
|
-
declarationScaleValues: meta.declarationScaleValues
|
|
4750
|
+
declarationScaleValues: meta.declarationScaleValues,
|
|
4751
|
+
...meta.productionConditions && { productionConditions: meta.productionConditions }
|
|
3999
4752
|
};
|
|
4000
4753
|
continue;
|
|
4001
4754
|
}
|
|
@@ -4013,6 +4766,7 @@ function buildDynamicPropConfig(dynamicProps) {
|
|
|
4013
4766
|
if (meta.strict) entry.strict = true;
|
|
4014
4767
|
if (meta.keywords?.length) entry.keywords = meta.keywords;
|
|
4015
4768
|
if (meta.currentVar) entry.currentVar = meta.currentVar;
|
|
4769
|
+
if (meta.productionConditions) entry.productionConditions = meta.productionConditions;
|
|
4016
4770
|
configEntries[propName] = entry;
|
|
4017
4771
|
}
|
|
4018
4772
|
return configEntries;
|
|
@@ -4051,6 +4805,191 @@ function assertNoErrorDiagnostics(diagnostics) {
|
|
|
4051
4805
|
throw new Error(lines.join("\n"));
|
|
4052
4806
|
}
|
|
4053
4807
|
//#endregion
|
|
4808
|
+
//#region pipeline/kit-publication.ts
|
|
4809
|
+
/** Source a kit publishes and the loader or a bundler parses for imports. */
|
|
4810
|
+
const CODE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
4811
|
+
".ts",
|
|
4812
|
+
".tsx",
|
|
4813
|
+
".mts",
|
|
4814
|
+
".cts",
|
|
4815
|
+
".js",
|
|
4816
|
+
".jsx",
|
|
4817
|
+
".mjs",
|
|
4818
|
+
".cjs"
|
|
4819
|
+
]);
|
|
4820
|
+
const isFile = (path) => {
|
|
4821
|
+
try {
|
|
4822
|
+
return (0, node_fs.statSync)(path).isFile();
|
|
4823
|
+
} catch {
|
|
4824
|
+
return false;
|
|
4825
|
+
}
|
|
4826
|
+
};
|
|
4827
|
+
const posixPath = (path) => path.split(node_path.sep).join("/");
|
|
4828
|
+
/**
|
|
4829
|
+
* The package-relative files `npm pack` publishes, read with npm's own rules
|
|
4830
|
+
* (`files`, `.npmignore`, `.gitignore` and the files npm always includes)
|
|
4831
|
+
* through a dry run that runs no scripts and needs no network.
|
|
4832
|
+
*/
|
|
4833
|
+
function npmPublishedFiles(pkgRoot) {
|
|
4834
|
+
const windows = process.platform === "win32";
|
|
4835
|
+
const run = (0, node_child_process.spawnSync)(windows ? "npm.cmd" : "npm", [
|
|
4836
|
+
"pack",
|
|
4837
|
+
"--dry-run",
|
|
4838
|
+
"--json",
|
|
4839
|
+
"--ignore-scripts"
|
|
4840
|
+
], {
|
|
4841
|
+
cwd: pkgRoot,
|
|
4842
|
+
encoding: "utf-8",
|
|
4843
|
+
shell: windows,
|
|
4844
|
+
env: {
|
|
4845
|
+
...process.env,
|
|
4846
|
+
npm_config_update_notifier: "false"
|
|
4847
|
+
}
|
|
4848
|
+
});
|
|
4849
|
+
if (run.error || run.status !== 0) return {
|
|
4850
|
+
kind: "failed",
|
|
4851
|
+
reason: `npm pack --dry-run could not list the published files: ${String(run.error ?? run.stderr.trim())}`
|
|
4852
|
+
};
|
|
4853
|
+
let output;
|
|
4854
|
+
try {
|
|
4855
|
+
output = JSON.parse(run.stdout);
|
|
4856
|
+
} catch (error) {
|
|
4857
|
+
return {
|
|
4858
|
+
kind: "failed",
|
|
4859
|
+
reason: `npm pack --dry-run printed no JSON: ${String(error)}`
|
|
4860
|
+
};
|
|
4861
|
+
}
|
|
4862
|
+
const pack = Array.isArray(output) ? output[0] : void 0;
|
|
4863
|
+
const files = isJsonBlock(pack) ? pack.files : void 0;
|
|
4864
|
+
if (!Array.isArray(files)) return {
|
|
4865
|
+
kind: "failed",
|
|
4866
|
+
reason: "npm pack --dry-run printed no file list"
|
|
4867
|
+
};
|
|
4868
|
+
return {
|
|
4869
|
+
kind: "listed",
|
|
4870
|
+
files: new Set(files.flatMap((file) => isJsonBlock(file) && isJsonString(file.path) ? [posixPath(file.path)] : []))
|
|
4871
|
+
};
|
|
4872
|
+
}
|
|
4873
|
+
function animusTargets(manifest) {
|
|
4874
|
+
const targets = [];
|
|
4875
|
+
const unreadable = [];
|
|
4876
|
+
const visit = (subpath, value, underAnimus) => {
|
|
4877
|
+
if (isJsonString(value)) {
|
|
4878
|
+
if (underAnimus) targets.push([subpath, value]);
|
|
4879
|
+
} else if (Array.isArray(value)) for (const entry of value) visit(subpath, entry, underAnimus);
|
|
4880
|
+
else if (isJsonBlock(value)) for (const [key, entry] of Object.entries(value)) visit(subpath, entry, underAnimus || key === "animus");
|
|
4881
|
+
else if (underAnimus && value !== null) unreadable.push([subpath, JSON.stringify(value)]);
|
|
4882
|
+
};
|
|
4883
|
+
for (const [subpath, value] of exportsSubpaths(manifest)) visit(subpath, value, false);
|
|
4884
|
+
return {
|
|
4885
|
+
targets,
|
|
4886
|
+
unreadable
|
|
4887
|
+
};
|
|
4888
|
+
}
|
|
4889
|
+
/** Every file in the package outside `node_modules` and `.git`,
|
|
4890
|
+
* package-relative. */
|
|
4891
|
+
function packageFiles(pkgRoot, dir = "") {
|
|
4892
|
+
return (0, node_fs.readdirSync)((0, node_path.join)(pkgRoot, dir), { withFileTypes: true }).flatMap((entry) => {
|
|
4893
|
+
const path = dir === "" ? entry.name : `${dir}/${entry.name}`;
|
|
4894
|
+
if (entry.isDirectory()) return entry.name === "node_modules" || entry.name === ".git" ? [] : packageFiles(pkgRoot, path);
|
|
4895
|
+
return entry.isFile() ? [path] : [];
|
|
4896
|
+
});
|
|
4897
|
+
}
|
|
4898
|
+
/** A `*` target pattern as a matcher of package-relative files. Node puts
|
|
4899
|
+
* one subpath into every `*`, so each later `*` repeats the first match. */
|
|
4900
|
+
function targetPattern(target) {
|
|
4901
|
+
const body = posixPath(target).replace(/^\.\//, "").split("*").map((part) => part.replace(/[.+?^${}()|[\]\\]/g, "\\$&")).reduce((pattern, part, index) => index === 0 ? part : `${pattern}${index === 1 ? "(.+)" : "\\1"}${part}`);
|
|
4902
|
+
return new RegExp(`^${body}$`);
|
|
4903
|
+
}
|
|
4904
|
+
/**
|
|
4905
|
+
* Why the kit at `pkgRoot` would fail an installed consumer: each `animus`
|
|
4906
|
+
* source target, at any condition depth or as a pattern, and each file its
|
|
4907
|
+
* relative imports reach, must be in the package and among the files `npm
|
|
4908
|
+
* pack` publishes, and each bare package import a declared dependency.
|
|
4909
|
+
* Each line names the file, the import and the cause; none means the
|
|
4910
|
+
* published source is complete. With `artifactDir`, a build's artifact
|
|
4911
|
+
* directory inside the package that `npm pack` would publish is a line too.
|
|
4912
|
+
*/
|
|
4913
|
+
function kitPublicationFailures(pkgRoot, engine, artifactDir) {
|
|
4914
|
+
let manifest;
|
|
4915
|
+
try {
|
|
4916
|
+
manifest = JSON.parse((0, node_fs.readFileSync)((0, node_path.join)(pkgRoot, "package.json"), "utf-8"));
|
|
4917
|
+
} catch (error) {
|
|
4918
|
+
return [`package.json: cannot be read: ${String(error)}`];
|
|
4919
|
+
}
|
|
4920
|
+
const { targets, unreadable } = animusTargets(manifest);
|
|
4921
|
+
if (targets.length === 0 && unreadable.length === 0) return [];
|
|
4922
|
+
const { moduleSpecifiers } = engine;
|
|
4923
|
+
if (moduleSpecifiers === void 0) throw new Error("the native engine does not expose moduleSpecifiers, which the kit publication check needs — rebuild @animus-ui/extract");
|
|
4924
|
+
const publication = npmPublishedFiles(pkgRoot);
|
|
4925
|
+
if (publication.kind === "failed") return [`package.json: ${publication.reason}`];
|
|
4926
|
+
const published = (packageRelative) => publication.files.has(packageRelative);
|
|
4927
|
+
const declared = new Set([
|
|
4928
|
+
"dependencies",
|
|
4929
|
+
"peerDependencies",
|
|
4930
|
+
"optionalDependencies"
|
|
4931
|
+
].flatMap((field) => {
|
|
4932
|
+
const block = isJsonBlock(manifest) ? manifest[field] : void 0;
|
|
4933
|
+
return isJsonBlock(block) ? Object.keys(block) : [];
|
|
4934
|
+
}));
|
|
4935
|
+
const ownName = isJsonBlock(manifest) && isJsonString(manifest.name) ? manifest.name : null;
|
|
4936
|
+
const failures = [];
|
|
4937
|
+
const rel = (file) => posixPath((0, node_path.relative)(pkgRoot, file));
|
|
4938
|
+
const outside = (relPath) => relPath.startsWith("../") || (0, node_path.isAbsolute)(relPath);
|
|
4939
|
+
for (const [subpath, value] of unreadable) failures.push(`package.json: exports["${subpath}"] has an "animus" value, ${value}, that is no target path`);
|
|
4940
|
+
const queue = [];
|
|
4941
|
+
const admit = (subpath, relPath) => {
|
|
4942
|
+
if (published(relPath)) queue.push((0, node_path.resolve)(pkgRoot, relPath));
|
|
4943
|
+
else failures.push(`package.json: the "animus" target of exports["${subpath}"], ${relPath}, is not in the published files`);
|
|
4944
|
+
};
|
|
4945
|
+
let files = null;
|
|
4946
|
+
for (const [subpath, target] of targets) {
|
|
4947
|
+
const relPath = rel((0, node_path.resolve)(pkgRoot, target));
|
|
4948
|
+
if (outside(relPath)) failures.push(`package.json: the "animus" target of exports["${subpath}"], ${target}, is outside the package`);
|
|
4949
|
+
else if (target.includes("*")) {
|
|
4950
|
+
files ??= packageFiles(pkgRoot);
|
|
4951
|
+
const pattern = targetPattern(target);
|
|
4952
|
+
const matched = files.filter((file) => pattern.test(file));
|
|
4953
|
+
if (matched.length === 0) failures.push(`package.json: the "animus" target of exports["${subpath}"], ${target}, matches no file in the package`);
|
|
4954
|
+
for (const file of matched) admit(subpath, file);
|
|
4955
|
+
} else if (!isFile((0, node_path.resolve)(pkgRoot, target))) failures.push(`package.json: the "animus" target of exports["${subpath}"], ${target}, does not exist in the package`);
|
|
4956
|
+
else admit(subpath, relPath);
|
|
4957
|
+
}
|
|
4958
|
+
const visited = /* @__PURE__ */ new Set();
|
|
4959
|
+
while (queue.length > 0) {
|
|
4960
|
+
const file = queue.shift();
|
|
4961
|
+
if (visited.has(file)) continue;
|
|
4962
|
+
visited.add(file);
|
|
4963
|
+
const specifiers = parseInternalWire(moduleSpecifiers(JSON.stringify([{
|
|
4964
|
+
path: file,
|
|
4965
|
+
source: (0, node_fs.readFileSync)(file, "utf-8")
|
|
4966
|
+
}])), "moduleSpecifiers").files[file] ?? [];
|
|
4967
|
+
for (const specifier of specifiers) {
|
|
4968
|
+
if (specifier.startsWith(".")) {
|
|
4969
|
+
const target = relativeSourceCandidates(file, specifier).find(isFile);
|
|
4970
|
+
if (target === void 0) failures.push(`${rel(file)}: import '${specifier}' resolves to no file in the package`);
|
|
4971
|
+
else if (outside(rel(target))) failures.push(`${rel(file)}: import '${specifier}' resolves to ${rel(target)}, outside the package`);
|
|
4972
|
+
else if (!published(rel(target))) failures.push(`${rel(file)}: import '${specifier}' resolves to ${rel(target)}, which is not in the published files`);
|
|
4973
|
+
else if (CODE_EXTENSIONS.has((0, node_path.extname)(target))) queue.push(target);
|
|
4974
|
+
continue;
|
|
4975
|
+
}
|
|
4976
|
+
if (specifier.startsWith("/") || /^[a-z][\w+.-]*:/i.test(specifier)) {
|
|
4977
|
+
if (!(0, node_module.isBuiltin)(specifier)) failures.push(`${rel(file)}: import '${specifier}' is not a package or relative import, so an installed consumer cannot resolve it`);
|
|
4978
|
+
continue;
|
|
4979
|
+
}
|
|
4980
|
+
const name = bareSpecifierPackageName(specifier);
|
|
4981
|
+
if ((0, node_module.isBuiltin)(name) || name === ownName || declared.has(name)) continue;
|
|
4982
|
+
failures.push(`${rel(file)}: import '${specifier}' names the package ${name}, which package.json does not declare in dependencies, peerDependencies or optionalDependencies`);
|
|
4983
|
+
}
|
|
4984
|
+
}
|
|
4985
|
+
const artifacts = artifactDir === void 0 ? "" : rel(artifactDir);
|
|
4986
|
+
if (artifacts !== "" && !outside(artifacts)) {
|
|
4987
|
+
const shipped = [...publication.files].filter((file) => file.startsWith(`${artifacts}/`));
|
|
4988
|
+
if (shipped.length > 0) failures.push(`${artifacts}/: npm pack would publish this build-artifact directory (${shipped.length} files) — add "${artifacts}/" to .npmignore or .gitignore, or list the published files in package.json "files"`);
|
|
4989
|
+
}
|
|
4990
|
+
return failures;
|
|
4991
|
+
}
|
|
4992
|
+
//#endregion
|
|
4054
4993
|
//#region pipeline/replacement-plans.ts
|
|
4055
4994
|
const snapshotByManifest = /* @__PURE__ */ new WeakMap();
|
|
4056
4995
|
/** `null` (a context before its first analysis) snapshots to the empty plan
|
|
@@ -4157,12 +5096,24 @@ Object.defineProperty(exports, "DEFAULT_EXTENSIONS", {
|
|
|
4157
5096
|
return DEFAULT_EXTENSIONS;
|
|
4158
5097
|
}
|
|
4159
5098
|
});
|
|
5099
|
+
Object.defineProperty(exports, "DIAGNOSTIC_LEVELS", {
|
|
5100
|
+
enumerable: true,
|
|
5101
|
+
get: function() {
|
|
5102
|
+
return DIAGNOSTIC_LEVELS;
|
|
5103
|
+
}
|
|
5104
|
+
});
|
|
4160
5105
|
Object.defineProperty(exports, "DRIVER_NAMESPACES", {
|
|
4161
5106
|
enumerable: true,
|
|
4162
5107
|
get: function() {
|
|
4163
5108
|
return DRIVER_NAMESPACES;
|
|
4164
5109
|
}
|
|
4165
5110
|
});
|
|
5111
|
+
Object.defineProperty(exports, "DiagnosticFailure", {
|
|
5112
|
+
enumerable: true,
|
|
5113
|
+
get: function() {
|
|
5114
|
+
return DiagnosticFailure;
|
|
5115
|
+
}
|
|
5116
|
+
});
|
|
4166
5117
|
Object.defineProperty(exports, "ENGINE_TRANSFORM_EXTENSIONS", {
|
|
4167
5118
|
enumerable: true,
|
|
4168
5119
|
get: function() {
|
|
@@ -4175,6 +5126,12 @@ Object.defineProperty(exports, "INVALID_PROPERTY_REGISTRATION", {
|
|
|
4175
5126
|
return INVALID_PROPERTY_REGISTRATION;
|
|
4176
5127
|
}
|
|
4177
5128
|
});
|
|
5129
|
+
Object.defineProperty(exports, "KIT_DESCRIPTOR_FILE", {
|
|
5130
|
+
enumerable: true,
|
|
5131
|
+
get: function() {
|
|
5132
|
+
return KIT_DESCRIPTOR_FILE;
|
|
5133
|
+
}
|
|
5134
|
+
});
|
|
4178
5135
|
Object.defineProperty(exports, "REPLACEABLE_DEFAULT_EXCLUDE", {
|
|
4179
5136
|
enumerable: true,
|
|
4180
5137
|
get: function() {
|
|
@@ -4265,6 +5222,12 @@ Object.defineProperty(exports, "buildDynamicPropConfig", {
|
|
|
4265
5222
|
return buildDynamicPropConfig;
|
|
4266
5223
|
}
|
|
4267
5224
|
});
|
|
5225
|
+
Object.defineProperty(exports, "buildKitDescriptor", {
|
|
5226
|
+
enumerable: true,
|
|
5227
|
+
get: function() {
|
|
5228
|
+
return buildKitDescriptor;
|
|
5229
|
+
}
|
|
5230
|
+
});
|
|
4268
5231
|
Object.defineProperty(exports, "buildPathAliasesJson", {
|
|
4269
5232
|
enumerable: true,
|
|
4270
5233
|
get: function() {
|
|
@@ -4349,10 +5312,10 @@ Object.defineProperty(exports, "enforceExternalTokenContracts", {
|
|
|
4349
5312
|
return enforceExternalTokenContracts;
|
|
4350
5313
|
}
|
|
4351
5314
|
});
|
|
4352
|
-
Object.defineProperty(exports, "
|
|
5315
|
+
Object.defineProperty(exports, "engineModuleParser", {
|
|
4353
5316
|
enumerable: true,
|
|
4354
5317
|
get: function() {
|
|
4355
|
-
return
|
|
5318
|
+
return engineModuleParser;
|
|
4356
5319
|
}
|
|
4357
5320
|
});
|
|
4358
5321
|
Object.defineProperty(exports, "excludeCollectedPackages", {
|
|
@@ -4367,6 +5330,12 @@ Object.defineProperty(exports, "extractSystemFilePackages", {
|
|
|
4367
5330
|
return extractSystemFilePackages;
|
|
4368
5331
|
}
|
|
4369
5332
|
});
|
|
5333
|
+
Object.defineProperty(exports, "fileSideEffects", {
|
|
5334
|
+
enumerable: true,
|
|
5335
|
+
get: function() {
|
|
5336
|
+
return fileSideEffects;
|
|
5337
|
+
}
|
|
5338
|
+
});
|
|
4370
5339
|
Object.defineProperty(exports, "findAssetSpecifiers", {
|
|
4371
5340
|
enumerable: true,
|
|
4372
5341
|
get: function() {
|
|
@@ -4403,6 +5372,12 @@ Object.defineProperty(exports, "hashReplacementPlans", {
|
|
|
4403
5372
|
return hashReplacementPlans;
|
|
4404
5373
|
}
|
|
4405
5374
|
});
|
|
5375
|
+
Object.defineProperty(exports, "importedKitDiagnostics", {
|
|
5376
|
+
enumerable: true,
|
|
5377
|
+
get: function() {
|
|
5378
|
+
return importedKitDiagnostics;
|
|
5379
|
+
}
|
|
5380
|
+
});
|
|
4406
5381
|
Object.defineProperty(exports, "ingestSourceEntries", {
|
|
4407
5382
|
enumerable: true,
|
|
4408
5383
|
get: function() {
|
|
@@ -4457,6 +5432,30 @@ Object.defineProperty(exports, "isUnresolvedParentDrop", {
|
|
|
4457
5432
|
return isUnresolvedParentDrop;
|
|
4458
5433
|
}
|
|
4459
5434
|
});
|
|
5435
|
+
Object.defineProperty(exports, "keepsKitSourceEffects", {
|
|
5436
|
+
enumerable: true,
|
|
5437
|
+
get: function() {
|
|
5438
|
+
return keepsKitSourceEffects;
|
|
5439
|
+
}
|
|
5440
|
+
});
|
|
5441
|
+
Object.defineProperty(exports, "kitDescriptorDiagnostics", {
|
|
5442
|
+
enumerable: true,
|
|
5443
|
+
get: function() {
|
|
5444
|
+
return kitDescriptorDiagnostics;
|
|
5445
|
+
}
|
|
5446
|
+
});
|
|
5447
|
+
Object.defineProperty(exports, "kitPublicationFailures", {
|
|
5448
|
+
enumerable: true,
|
|
5449
|
+
get: function() {
|
|
5450
|
+
return kitPublicationFailures;
|
|
5451
|
+
}
|
|
5452
|
+
});
|
|
5453
|
+
Object.defineProperty(exports, "kitSourceModuleSideEffects", {
|
|
5454
|
+
enumerable: true,
|
|
5455
|
+
get: function() {
|
|
5456
|
+
return kitSourceModuleSideEffects;
|
|
5457
|
+
}
|
|
5458
|
+
});
|
|
4460
5459
|
Object.defineProperty(exports, "loadSystemConfig", {
|
|
4461
5460
|
enumerable: true,
|
|
4462
5461
|
get: function() {
|
|
@@ -4499,6 +5498,12 @@ Object.defineProperty(exports, "projectExternalFileOwners", {
|
|
|
4499
5498
|
return projectExternalFileOwners;
|
|
4500
5499
|
}
|
|
4501
5500
|
});
|
|
5501
|
+
Object.defineProperty(exports, "readKitDescriptor", {
|
|
5502
|
+
enumerable: true,
|
|
5503
|
+
get: function() {
|
|
5504
|
+
return readKitDescriptor;
|
|
5505
|
+
}
|
|
5506
|
+
});
|
|
4502
5507
|
Object.defineProperty(exports, "readTsconfigAliasPairs", {
|
|
4503
5508
|
enumerable: true,
|
|
4504
5509
|
get: function() {
|
|
@@ -4577,6 +5582,12 @@ Object.defineProperty(exports, "snapshotFilePlans", {
|
|
|
4577
5582
|
return snapshotFilePlans;
|
|
4578
5583
|
}
|
|
4579
5584
|
});
|
|
5585
|
+
Object.defineProperty(exports, "sourceKitDependencies", {
|
|
5586
|
+
enumerable: true,
|
|
5587
|
+
get: function() {
|
|
5588
|
+
return sourceKitDependencies;
|
|
5589
|
+
}
|
|
5590
|
+
});
|
|
4580
5591
|
Object.defineProperty(exports, "stableStringify", {
|
|
4581
5592
|
enumerable: true,
|
|
4582
5593
|
get: function() {
|