@animus-ui/extract 0.1.22 → 0.1.23
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/dist/index.cjs +4 -2
- package/dist/index.mjs +2 -2
- package/dist/pipeline/core-options.d.ts +6 -0
- package/dist/pipeline/core-options.d.ts.map +1 -1
- package/dist/pipeline/discover-packages.d.ts +16 -7
- package/dist/pipeline/discover-packages.d.ts.map +1 -1
- package/dist/pipeline/engine-adapter.d.ts +2 -0
- package/dist/pipeline/engine-adapter.d.ts.map +1 -1
- package/dist/pipeline/index.d.ts +4 -4
- package/dist/pipeline/index.d.ts.map +1 -1
- package/dist/pipeline/manifest-diagnostics.d.ts +34 -0
- package/dist/pipeline/manifest-diagnostics.d.ts.map +1 -1
- package/dist/pipeline/run-analysis.d.ts +3 -0
- package/dist/pipeline/run-analysis.d.ts.map +1 -1
- package/dist/pipeline/source-ingestion.d.ts +4 -0
- package/dist/pipeline/source-ingestion.d.ts.map +1 -1
- package/dist/pipeline/structural-self-check.d.ts.map +1 -1
- package/dist/{pipeline-D7B1z0-1.cjs → pipeline-B3JzXiSY.cjs} +460 -183
- package/dist/{pipeline-BXY7Pf_D.mjs → pipeline-kA3VEMgt.mjs} +447 -182
- package/dist/session/extraction-session.d.ts +3 -0
- package/dist/session/extraction-session.d.ts.map +1 -1
- package/dist/session.cjs +11 -3
- package/dist/session.mjs +11 -3
- package/package.json +4 -4
|
@@ -131,7 +131,167 @@ function parseInternalWire(json, wire) {
|
|
|
131
131
|
return parsed;
|
|
132
132
|
}
|
|
133
133
|
//#endregion
|
|
134
|
+
//#region pipeline/asset-placeholders.ts
|
|
135
|
+
const ASSET_PLACEHOLDER_PREFIX = "animus-asset:";
|
|
136
|
+
const URL_OPEN_RE = /url\(\s*(['"]?)/gi;
|
|
137
|
+
const IMAGE_SET_OPEN_RE = /(?:-webkit-)?image-set\(/gi;
|
|
138
|
+
const BARE_SPECIFIER_RE = /^[^'")\s]+/;
|
|
139
|
+
/** The index of the `)` that closes the `(` at `open`, outside strings. */
|
|
140
|
+
function closingParen(css, open) {
|
|
141
|
+
let depth = 0;
|
|
142
|
+
let quote = null;
|
|
143
|
+
for (let i = open; i < css.length; i += 1) {
|
|
144
|
+
const c = css[i];
|
|
145
|
+
if (quote !== null) {
|
|
146
|
+
if (c === quote) quote = null;
|
|
147
|
+
} else if (c === "\"" || c === "'") quote = c;
|
|
148
|
+
else if (c === "(") depth += 1;
|
|
149
|
+
else if (c === ")") {
|
|
150
|
+
depth -= 1;
|
|
151
|
+
if (depth === 0) return i;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
return css.length;
|
|
155
|
+
}
|
|
156
|
+
function quotedReference(css, quoteAt) {
|
|
157
|
+
const start = quoteAt + 1;
|
|
158
|
+
if (!css.startsWith("animus-asset:", start)) return null;
|
|
159
|
+
const end = css.indexOf(css[quoteAt], start);
|
|
160
|
+
if (end === -1) return null;
|
|
161
|
+
return {
|
|
162
|
+
start,
|
|
163
|
+
end,
|
|
164
|
+
specifier: css.slice(start + 13, end)
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
function assetReferences(css) {
|
|
168
|
+
if (!css.includes("animus-asset:")) return [];
|
|
169
|
+
const references = [];
|
|
170
|
+
for (const open of css.matchAll(URL_OPEN_RE)) {
|
|
171
|
+
const at = (open.index ?? 0) + open[0].length;
|
|
172
|
+
if (open[1]) {
|
|
173
|
+
const reference = quotedReference(css, at - 1);
|
|
174
|
+
if (reference) references.push(reference);
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
if (!css.startsWith("animus-asset:", at)) continue;
|
|
178
|
+
const specifier = BARE_SPECIFIER_RE.exec(css.slice(at + 13))?.[0];
|
|
179
|
+
if (specifier === void 0) continue;
|
|
180
|
+
const end = at + 13 + specifier.length;
|
|
181
|
+
references.push({
|
|
182
|
+
start: at,
|
|
183
|
+
end,
|
|
184
|
+
specifier
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
for (const open of css.matchAll(IMAGE_SET_OPEN_RE)) {
|
|
188
|
+
const paren = (open.index ?? 0) + open[0].length - 1;
|
|
189
|
+
const close = closingParen(css, paren);
|
|
190
|
+
let depth = 0;
|
|
191
|
+
let candidateStart = true;
|
|
192
|
+
for (let i = paren + 1; i < close; i += 1) {
|
|
193
|
+
const c = css[i];
|
|
194
|
+
if (/\s/.test(c)) continue;
|
|
195
|
+
if (c === "\"" || c === "'") {
|
|
196
|
+
if (depth === 0 && candidateStart) {
|
|
197
|
+
const reference = quotedReference(css, i);
|
|
198
|
+
if (reference) references.push(reference);
|
|
199
|
+
}
|
|
200
|
+
const end = css.indexOf(c, i + 1);
|
|
201
|
+
i = end === -1 ? close : end;
|
|
202
|
+
} else if (c === "(") depth += 1;
|
|
203
|
+
else if (c === ")") depth -= 1;
|
|
204
|
+
else if (c === "," && depth === 0) {
|
|
205
|
+
candidateStart = true;
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
candidateStart = false;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return references.sort((a, b) => a.start - b.start);
|
|
212
|
+
}
|
|
213
|
+
function findAssetSpecifiers(css) {
|
|
214
|
+
return [...new Set(assetReferences(css).map((reference) => reference.specifier))];
|
|
215
|
+
}
|
|
216
|
+
function substituteAssetPlaceholders(css, urlBySpecifier) {
|
|
217
|
+
if (urlBySpecifier.size === 0) return css;
|
|
218
|
+
let out = css;
|
|
219
|
+
for (const reference of assetReferences(css).reverse()) {
|
|
220
|
+
const url = urlBySpecifier.get(reference.specifier);
|
|
221
|
+
if (url === void 0) continue;
|
|
222
|
+
out = out.slice(0, reference.start) + url + out.slice(reference.end);
|
|
223
|
+
}
|
|
224
|
+
return out;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Specifiers of placeholder text anywhere inside a `url()` or `image-set()`
|
|
228
|
+
* call, a reference or not: after substitution any of it is a broken load.
|
|
229
|
+
*/
|
|
230
|
+
function placeholdersInAssetCalls(css) {
|
|
231
|
+
if (!css.includes("animus-asset:")) return [];
|
|
232
|
+
const calls = [...css.matchAll(URL_OPEN_RE), ...css.matchAll(IMAGE_SET_OPEN_RE)].map((open) => {
|
|
233
|
+
const paren = css.indexOf("(", open.index ?? 0);
|
|
234
|
+
return [paren, closingParen(css, paren)];
|
|
235
|
+
});
|
|
236
|
+
const found = /* @__PURE__ */ new Set();
|
|
237
|
+
for (let at = css.indexOf(ASSET_PLACEHOLDER_PREFIX); at !== -1; at = css.indexOf(ASSET_PLACEHOLDER_PREFIX, at + 1)) {
|
|
238
|
+
if (!calls.some(([open, close]) => open < at && at < close)) continue;
|
|
239
|
+
found.add(/^[^'")\s,]*/.exec(css.slice(at + 13))[0]);
|
|
240
|
+
}
|
|
241
|
+
return [...found];
|
|
242
|
+
}
|
|
243
|
+
function findSheetAssetSpecifiers(sheets) {
|
|
244
|
+
return [.../* @__PURE__ */ new Set([
|
|
245
|
+
...findAssetSpecifiers(sheets.variableCss),
|
|
246
|
+
...findAssetSpecifiers(sheets.globalCss),
|
|
247
|
+
...findAssetSpecifiers(sheets.componentCss)
|
|
248
|
+
])];
|
|
249
|
+
}
|
|
250
|
+
/** The one substitution step every emitted sheet passes through. */
|
|
251
|
+
function substituteSheetAssets(sheets, urlBySpecifier) {
|
|
252
|
+
return {
|
|
253
|
+
variableCss: substituteAssetPlaceholders(sheets.variableCss, urlBySpecifier),
|
|
254
|
+
globalCss: substituteAssetPlaceholders(sheets.globalCss, urlBySpecifier),
|
|
255
|
+
componentCss: substituteAssetPlaceholders(sheets.componentCss, urlBySpecifier)
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
const UNSUBSTITUTED_ASSET_CODE = "animus.asset.unsubstituted-placeholder";
|
|
259
|
+
/** A placeholder in emitted CSS is a broken URL in the browser: it fails a
|
|
260
|
+
* strict build and warns otherwise. */
|
|
261
|
+
function reportSurvivingAssetPlaceholders(css, report) {
|
|
262
|
+
const specifiers = placeholdersInAssetCalls(css);
|
|
263
|
+
if (specifiers.length === 0) return;
|
|
264
|
+
const message = `${report.prefix} asset() placeholders reached ${report.surface ?? "emitted CSS"} unsubstituted: ${specifiers.join(", ")} (${UNSUBSTITUTED_ASSET_CODE})`;
|
|
265
|
+
if (report.strict) throw new Error(message);
|
|
266
|
+
report.warn(message);
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* The runtime code an analysis generates: each dynamic prop config, which the
|
|
270
|
+
* shared prop map module carries, and each component's replacement. Extraction
|
|
271
|
+
* lifts asset() out of them into root variables, so none should hold one.
|
|
272
|
+
* Both embed values as JSON strings, so their quote and backslash escapes are
|
|
273
|
+
* undone for scanning: the text is for `findAssetSpecifiers`, not to run.
|
|
274
|
+
*/
|
|
275
|
+
function generatedModuleCode(manifest) {
|
|
276
|
+
return [JSON.stringify(manifest.dynamic_props), ...Object.values(manifest.components).map((component) => component.replacement)].join("\n").replace(/\\(["\\])/g, "$1");
|
|
277
|
+
}
|
|
278
|
+
//#endregion
|
|
134
279
|
//#region pipeline/manifest-diagnostics.ts
|
|
280
|
+
/** `file`, then its line and column where the record has them. */
|
|
281
|
+
function locationOf(diagnostic) {
|
|
282
|
+
if (diagnostic.line === void 0) return diagnostic.file;
|
|
283
|
+
if (diagnostic.column === void 0) return `${diagnostic.file}:${diagnostic.line}`;
|
|
284
|
+
return `${diagnostic.file}:${diagnostic.line}:${diagnostic.column}`;
|
|
285
|
+
}
|
|
286
|
+
/** A located record's `file:line:column: `; nothing for one without a line,
|
|
287
|
+
* whose message names its file. */
|
|
288
|
+
function locatedPrefix(diagnostic) {
|
|
289
|
+
return diagnostic.line === void 0 ? "" : `${locationOf(diagnostic)}: `;
|
|
290
|
+
}
|
|
291
|
+
/** What the record dropped, when its message does not already quote it. */
|
|
292
|
+
function droppedSuffix(diagnostic) {
|
|
293
|
+
return diagnostic.dropped && !diagnostic.message.includes(diagnostic.dropped) ? ` — dropped: ${diagnostic.dropped}` : "";
|
|
294
|
+
}
|
|
135
295
|
/** Stable code for selector forms with no substitutable subject; the
|
|
136
296
|
* extraction engine mints the same string. */
|
|
137
297
|
const SELECTOR_UNSUPPORTED_SUBJECT = "animus.selector.unsupported-subject";
|
|
@@ -144,6 +304,63 @@ function isUnresolvedParentDrop(diagnostic) {
|
|
|
144
304
|
function unresolvedParentName(diagnostic) {
|
|
145
305
|
return UNRESOLVED_PARENT_RE.exec(diagnostic.message)?.[1] ?? null;
|
|
146
306
|
}
|
|
307
|
+
const DIAGNOSTIC_LEVELS = /* @__PURE__ */ new Set([
|
|
308
|
+
"off",
|
|
309
|
+
"info",
|
|
310
|
+
"warn",
|
|
311
|
+
"error"
|
|
312
|
+
]);
|
|
313
|
+
/** The level `levels` sets for `code`: its exact entry, else the entry of
|
|
314
|
+
* the longest prefix it starts with. */
|
|
315
|
+
function levelFor(code, levels) {
|
|
316
|
+
if (code === void 0 || levels === void 0) return void 0;
|
|
317
|
+
if (Object.hasOwn(levels, code)) return levels[code];
|
|
318
|
+
let longest;
|
|
319
|
+
for (const [key, level] of Object.entries(levels)) {
|
|
320
|
+
if (!key.endsWith(".*")) continue;
|
|
321
|
+
const prefix = key.slice(0, -1);
|
|
322
|
+
if (code.startsWith(prefix) && prefix.length > (longest?.length ?? -1)) longest = {
|
|
323
|
+
length: prefix.length,
|
|
324
|
+
level
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
return longest?.level;
|
|
328
|
+
}
|
|
329
|
+
/** The level a record prints at: the option's entry for its code, else
|
|
330
|
+
* `error` for an error-severity record under `strict`, else its own. */
|
|
331
|
+
function effectiveLevel(diagnostic, policy) {
|
|
332
|
+
const level = levelFor(diagnostic.code, policy.levels);
|
|
333
|
+
if (level !== void 0) return level;
|
|
334
|
+
if (diagnostic.severity === "info") return "info";
|
|
335
|
+
return policy.strict && diagnostic.severity === "error" ? "error" : "warn";
|
|
336
|
+
}
|
|
337
|
+
/** Every code Animus is known to report: the engine's table, as the native
|
|
338
|
+
* module publishes it (`diagnosticCodes()`), the pipeline's, and the asset
|
|
339
|
+
* code. The system package mints vocabulary codes at run time, so a valid
|
|
340
|
+
* code can be missing. */
|
|
341
|
+
function knownDiagnosticCodes(engineCodesJson) {
|
|
342
|
+
const engineCodes = engineCodesJson ? Object.keys(parseInternalWire(engineCodesJson, "diagnosticCodes (the engine code table)")) : [];
|
|
343
|
+
return /* @__PURE__ */ new Set([
|
|
344
|
+
...engineCodes,
|
|
345
|
+
...DIAGNOSTIC_SEVERITY.keys(),
|
|
346
|
+
UNSUBSTITUTED_ASSET_CODE
|
|
347
|
+
]);
|
|
348
|
+
}
|
|
349
|
+
/** The keys of `levels` that name no known code, exactly or as a prefix. */
|
|
350
|
+
function unknownDiagnosticKeys(levels, known) {
|
|
351
|
+
return Object.keys(levels).filter((key) => {
|
|
352
|
+
if (!key.endsWith(".*")) return !known.has(key);
|
|
353
|
+
const prefix = key.slice(0, -1);
|
|
354
|
+
return ![...known].some((code) => code.startsWith(prefix));
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
/** The failure of diagnostics at `error` level, from `strict` or the
|
|
358
|
+
* `diagnostics` option: a host fails the build on it whatever its own
|
|
359
|
+
* strictness. It prints as the plain `Error` strict always threw. */
|
|
360
|
+
var DiagnosticFailure = class extends Error {};
|
|
361
|
+
/** Level options already checked against the known codes: one warning per
|
|
362
|
+
* unknown key, however often a host analyzes. */
|
|
363
|
+
const checkedLevels = /* @__PURE__ */ new WeakSet();
|
|
147
364
|
/** True when the selector carries at least one substitutable `&` subject
|
|
148
365
|
* outside quoted text. */
|
|
149
366
|
function hasSelectorSubject(value) {
|
|
@@ -186,6 +403,9 @@ function collectSelectorAliasDiagnostics(selectorAliasesJson) {
|
|
|
186
403
|
const UNREADABLE_SOURCE_FILE = "animus.ingestion.unreadable-source-file";
|
|
187
404
|
/** A kit the system extends resolved, yet discovery found none of its files. */
|
|
188
405
|
const NO_KIT_FILES = "animus.discovery.no-kit-files";
|
|
406
|
+
/** A system file's `createSystem` binding that discovery could not follow to
|
|
407
|
+
* Animus's factory, so it is no root. */
|
|
408
|
+
const UNPROVEN_ROOT_BINDING = "animus.discovery.unproven-root-binding";
|
|
189
409
|
/** What a skipped source file costs: the analysis never sees what it
|
|
190
410
|
* renders. */
|
|
191
411
|
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";
|
|
@@ -262,6 +482,7 @@ const DIAGNOSTIC_SEVERITY = /* @__PURE__ */ new Map([
|
|
|
262
482
|
[SELECTOR_UNSUPPORTED_SUBJECT, "error"],
|
|
263
483
|
[UNREADABLE_SOURCE_FILE, "error"],
|
|
264
484
|
[NO_KIT_FILES, "warn"],
|
|
485
|
+
[UNPROVEN_ROOT_BINDING, "warn"],
|
|
265
486
|
[VOCABULARY_COLLISION, "warn"],
|
|
266
487
|
[VOCABULARY_LEGACY_VERB, "warn"],
|
|
267
488
|
[INVALID_PROPERTY_REGISTRATION, "error"],
|
|
@@ -386,25 +607,33 @@ function tokenCollisionDiagnostics(system) {
|
|
|
386
607
|
*/
|
|
387
608
|
function surfaceManifestDiagnostics(manifest, warn, policy = {}) {
|
|
388
609
|
const errors = [];
|
|
610
|
+
if (policy.levels && policy.knownCodes && !checkedLevels.has(policy.levels)) {
|
|
611
|
+
checkedLevels.add(policy.levels);
|
|
612
|
+
for (const key of unknownDiagnosticKeys(policy.levels, policy.knownCodes)) warn(`⚠ 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)`);
|
|
613
|
+
}
|
|
389
614
|
const diagnostics = policy.prepend?.length ? [...policy.prepend, ...manifest.diagnostics ?? []] : manifest.diagnostics ?? [];
|
|
390
615
|
for (const diagnostic of diagnostics) {
|
|
616
|
+
const level = effectiveLevel(diagnostic, policy);
|
|
617
|
+
if (level === "off") continue;
|
|
391
618
|
let line = null;
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
619
|
+
const mark = level === "info" ? "ℹ" : "⚠";
|
|
620
|
+
const message = `${diagnostic.message}${droppedSuffix(diagnostic)}`;
|
|
621
|
+
if (diagnostic.kind === "bail") line = `${mark} ${locatedPrefix(diagnostic)}${diagnostic.component} not extracted: ${message}`;
|
|
622
|
+
else if (diagnostic.kind === "skip") line = `${mark} ${locatedPrefix(diagnostic)}${diagnostic.component}: skipped ${message}`;
|
|
623
|
+
else if (diagnostic.kind === "warn") line = `${mark} ${locationOf(diagnostic)}: ${diagnostic.component}: ${message}`;
|
|
395
624
|
if (line === null) continue;
|
|
396
625
|
if (diagnostic.code && !diagnostic.message.includes(diagnostic.code)) line += ` [${diagnostic.code}]`;
|
|
397
|
-
if (
|
|
626
|
+
if (level === "info") {
|
|
398
627
|
policy.info?.(line);
|
|
399
628
|
continue;
|
|
400
629
|
}
|
|
401
|
-
if (
|
|
402
|
-
errors.push(`${diagnostic.code ?? "error"} — ${diagnostic.component}: ${
|
|
630
|
+
if (level === "error") {
|
|
631
|
+
errors.push(`${diagnostic.code ?? "error"} — ${locatedPrefix(diagnostic)}${diagnostic.component}: ${message}`);
|
|
403
632
|
continue;
|
|
404
633
|
}
|
|
405
634
|
warn(line);
|
|
406
635
|
}
|
|
407
|
-
if (errors.length > 0) throw new
|
|
636
|
+
if (errors.length > 0) throw new DiagnosticFailure(`[animus] strict: ${errors.length} error diagnostic(s):\n${errors.join("\n")}`);
|
|
408
637
|
}
|
|
409
638
|
//#endregion
|
|
410
639
|
//#region pipeline/mdx-preprocessor.ts
|
|
@@ -1056,17 +1285,17 @@ const NODE_NEXT_EXTENSION_MAP = {
|
|
|
1056
1285
|
".mjs": [".mts"],
|
|
1057
1286
|
".cjs": [".cts"]
|
|
1058
1287
|
};
|
|
1059
|
-
|
|
1060
|
-
|
|
1288
|
+
/** The paths a relative specifier can name from `importerPath`, in probe
|
|
1289
|
+
* order: as written with each source suffix, then a NodeNext emitted
|
|
1290
|
+
* extension's sources. Empty for a non-relative specifier. */
|
|
1291
|
+
function relativeSourceCandidates(importerPath, specifier) {
|
|
1292
|
+
if (!specifier.startsWith(".")) return [];
|
|
1061
1293
|
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
1294
|
const explicitExtension = extension(base);
|
|
1068
|
-
|
|
1069
|
-
|
|
1295
|
+
return [...RELATIVE_PROBE_SUFFIXES.map((suffix) => `${base}${suffix}`), ...(NODE_NEXT_EXTENSION_MAP[explicitExtension] ?? []).map((sourceExtension) => `${base.slice(0, -explicitExtension.length)}${sourceExtension}`)];
|
|
1296
|
+
}
|
|
1297
|
+
function resolveRelativeSource(importerPath, specifier, files) {
|
|
1298
|
+
for (const candidate of relativeSourceCandidates(importerPath, specifier)) {
|
|
1070
1299
|
const actualPath = files.get(candidate);
|
|
1071
1300
|
if (actualPath !== void 0) return actualPath;
|
|
1072
1301
|
}
|
|
@@ -1412,6 +1641,7 @@ function createV2EngineApi(deps) {
|
|
|
1412
1641
|
return {
|
|
1413
1642
|
loadSystemModule: (...args) => native.loadSystemModule(...args),
|
|
1414
1643
|
extractFacts: (filesJson) => native.extractFacts(filesJson),
|
|
1644
|
+
diagnosticCodes: () => native.diagnosticCodes(),
|
|
1415
1645
|
analyzeProject: (filesJsonRaw, scalesJson, variableMapJson, contextualVarsJson, propConfigJson, groupRegistryJson, packageResolutionJson, devMode, emitterConfigJson, selectorAliasesJson, _selectorOrderJson, globalStyleBlocksJson, pathAliasesJson, keyframesJson, staticCssJson, conditionAliasesJson, externalDirsJson, transformSourcesJson, transformProvenanceJson, declarationScalesJson, analysisContextJson) => {
|
|
1416
1646
|
const filesJson = deps.rehydrateFilesJson ? deps.rehydrateFilesJson(filesJsonRaw) : filesJsonRaw;
|
|
1417
1647
|
const sent = /* @__PURE__ */ new Map();
|
|
@@ -1477,6 +1707,10 @@ function assertNoRetiredEngineSelection(engineOption) {
|
|
|
1477
1707
|
}
|
|
1478
1708
|
//#endregion
|
|
1479
1709
|
//#region pipeline/core-options.ts
|
|
1710
|
+
/**
|
|
1711
|
+
* One option schema shared by the Vite, Next, and CLI drivers: key semantics,
|
|
1712
|
+
* default exclusions, and mode authority cannot drift per driver.
|
|
1713
|
+
*/
|
|
1480
1714
|
const DRIVER_NAMESPACES = [
|
|
1481
1715
|
"vite",
|
|
1482
1716
|
"next",
|
|
@@ -1510,6 +1744,7 @@ const CORE_OPTION_KEYS = /* @__PURE__ */ new Set([
|
|
|
1510
1744
|
"exclude",
|
|
1511
1745
|
"extensions",
|
|
1512
1746
|
"strict",
|
|
1747
|
+
"diagnostics",
|
|
1513
1748
|
"verbose",
|
|
1514
1749
|
"prefix",
|
|
1515
1750
|
"prefixContextualVars",
|
|
@@ -1569,6 +1804,7 @@ const isString = (value) => Object(value) !== value && Object.prototype.toString
|
|
|
1569
1804
|
const isBoolean = (value) => Object(value) !== value && Object.prototype.toString.call(value) === "[object Boolean]";
|
|
1570
1805
|
const isVerbose = (value) => isBoolean(value) || value === "trace";
|
|
1571
1806
|
const isStringArray = (value) => Array.isArray(value) && value.every(isString);
|
|
1807
|
+
const isDiagnosticLevels = (value) => Object.prototype.toString.call(value) === "[object Object]" && Object.values(Object(value)).every((level) => isString(level) && DIAGNOSTIC_LEVELS.has(level));
|
|
1572
1808
|
const isStringOrStringArray = (value) => isString(value) || isStringArray(value);
|
|
1573
1809
|
const CORE_VALUE_GATES = [
|
|
1574
1810
|
{
|
|
@@ -1591,6 +1827,11 @@ const CORE_VALUE_GATES = [
|
|
|
1591
1827
|
ok: isBoolean,
|
|
1592
1828
|
expected: "a boolean"
|
|
1593
1829
|
},
|
|
1830
|
+
{
|
|
1831
|
+
key: "diagnostics",
|
|
1832
|
+
ok: isDiagnosticLevels,
|
|
1833
|
+
expected: "an object mapping codes to 'off', 'info', 'warn' or 'error'"
|
|
1834
|
+
},
|
|
1594
1835
|
{
|
|
1595
1836
|
key: "verbose",
|
|
1596
1837
|
ok: isVerbose,
|
|
@@ -2419,10 +2660,10 @@ function staleDistIncludesMessage(outcomes) {
|
|
|
2419
2660
|
if (stale.length === 0) return null;
|
|
2420
2661
|
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
2662
|
}
|
|
2422
|
-
/** The engine's own parse of a module's
|
|
2663
|
+
/** The engine's own parse of a module's bindings, through its
|
|
2423
2664
|
* `extractFacts`; undefined for an engine without one. A parse that
|
|
2424
|
-
* panicked
|
|
2425
|
-
function
|
|
2665
|
+
* panicked counts as no parse. */
|
|
2666
|
+
function engineModuleParser(engine) {
|
|
2426
2667
|
const { extractFacts } = engine;
|
|
2427
2668
|
if (!extractFacts) return void 0;
|
|
2428
2669
|
return (source, path$3) => {
|
|
@@ -2431,36 +2672,200 @@ function engineImportParser(engine) {
|
|
|
2431
2672
|
path: path$3,
|
|
2432
2673
|
source
|
|
2433
2674
|
}])), "extractFacts").files[path$3];
|
|
2434
|
-
return facts && !facts.parsePanicked ?
|
|
2675
|
+
return facts && !facts.parsePanicked ? {
|
|
2676
|
+
imports: facts.imports,
|
|
2677
|
+
exports: facts.exports
|
|
2678
|
+
} : null;
|
|
2435
2679
|
} catch {
|
|
2436
2680
|
return null;
|
|
2437
2681
|
}
|
|
2438
2682
|
};
|
|
2439
2683
|
}
|
|
2684
|
+
const ANIMUS_SYSTEM_SPECIFIER = /^@animus-ui\/system(?:\/.*)?$/;
|
|
2685
|
+
/** The file a specifier names from `importer`: a relative one by the
|
|
2686
|
+
* ingestion's probe order, a bare one by the host's package resolution. */
|
|
2687
|
+
async function resolveModuleFile(importer, specifier, resolvePackage) {
|
|
2688
|
+
if (specifier.startsWith(".")) return relativeSourceCandidates(importer, specifier).find(isFile) ?? null;
|
|
2689
|
+
try {
|
|
2690
|
+
return await resolvePackage?.(specifier) ?? null;
|
|
2691
|
+
} catch {
|
|
2692
|
+
return null;
|
|
2693
|
+
}
|
|
2694
|
+
}
|
|
2695
|
+
const escapeRegExp$1 = (text) => text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2696
|
+
/**
|
|
2697
|
+
* Follows `name`, exported by the module `specifier` names from `importer`,
|
|
2698
|
+
* through re-exports to its declaration. It is Animus's factory only when
|
|
2699
|
+
* that is `createSystem` of `@animus-ui/system`, and another identity only
|
|
2700
|
+
* when a module is shown to declare it; anything discovery cannot follow is
|
|
2701
|
+
* unknown.
|
|
2702
|
+
*/
|
|
2703
|
+
async function exportIdentity(importer, specifier, name, following, seen) {
|
|
2704
|
+
if (ANIMUS_SYSTEM_SPECIFIER.test(specifier)) return name === "createSystem" ? { kind: "animus" } : {
|
|
2705
|
+
kind: "other",
|
|
2706
|
+
what: `'${name}' of ${specifier}`
|
|
2707
|
+
};
|
|
2708
|
+
const file = await resolveModuleFile(importer, specifier, following.resolvePackage);
|
|
2709
|
+
if (file === null) return {
|
|
2710
|
+
kind: "unknown",
|
|
2711
|
+
why: `'${specifier}' could not be resolved`
|
|
2712
|
+
};
|
|
2713
|
+
const key = `${file}#${name}`;
|
|
2714
|
+
if (seen.has(key)) return {
|
|
2715
|
+
kind: "unknown",
|
|
2716
|
+
why: `'${specifier}' re-exports it in a cycle`
|
|
2717
|
+
};
|
|
2718
|
+
seen.add(key);
|
|
2719
|
+
let source;
|
|
2720
|
+
try {
|
|
2721
|
+
source = (0, fs.readFileSync)(file, "utf-8");
|
|
2722
|
+
} catch {
|
|
2723
|
+
return {
|
|
2724
|
+
kind: "unknown",
|
|
2725
|
+
why: `'${specifier}' could not be read`
|
|
2726
|
+
};
|
|
2727
|
+
}
|
|
2728
|
+
const record = following.parseModule(source, file);
|
|
2729
|
+
if (record === null) return {
|
|
2730
|
+
kind: "unknown",
|
|
2731
|
+
why: `'${specifier}' could not be parsed`
|
|
2732
|
+
};
|
|
2733
|
+
const exported = record.exports.find((entry) => entry.exported === name);
|
|
2734
|
+
if (exported?.source) return exportIdentity(file, exported.source, exported.original ?? name, following, seen);
|
|
2735
|
+
const local = exported?.local ?? name;
|
|
2736
|
+
const imported = record.imports.find((entry) => entry.local === local);
|
|
2737
|
+
if (exported && imported) return exportIdentity(file, imported.source, imported.imported, following, seen);
|
|
2738
|
+
if (exported || new RegExp(`\\bexport\\s+(?:async\\s+)?(?:function\\*?|class|const|let|var)\\s+${escapeRegExp$1(name)}\\b`).test(source)) return {
|
|
2739
|
+
kind: "other",
|
|
2740
|
+
what: `the '${name}' that '${specifier}' declares`
|
|
2741
|
+
};
|
|
2742
|
+
const stars = [];
|
|
2743
|
+
for (const match of source.matchAll(/\bexport\s*\*\s*from\s*['"]([^'"]+)['"]/g)) stars.push(await exportIdentity(file, match[1], name, following, seen));
|
|
2744
|
+
const definite = stars.filter((identity) => identity.kind !== "unknown");
|
|
2745
|
+
if (definite.length === 1) return definite[0];
|
|
2746
|
+
if (definite.length > 1) return {
|
|
2747
|
+
kind: "unknown",
|
|
2748
|
+
why: `more than one \`export *\` of '${specifier}' provides '${name}'`
|
|
2749
|
+
};
|
|
2750
|
+
return stars.find((identity) => identity.kind === "unknown") ?? {
|
|
2751
|
+
kind: "unknown",
|
|
2752
|
+
why: `'${specifier}' has no export '${name}' discovery can read`
|
|
2753
|
+
};
|
|
2754
|
+
}
|
|
2755
|
+
/** 1-based line and column of `offset` in `source`. */
|
|
2756
|
+
function locationIn(source, offset) {
|
|
2757
|
+
const before = source.slice(0, offset);
|
|
2758
|
+
const lineStart = before.lastIndexOf("\n") + 1;
|
|
2759
|
+
return {
|
|
2760
|
+
line: before.split("\n").length,
|
|
2761
|
+
column: offset - lineStart + 1
|
|
2762
|
+
};
|
|
2763
|
+
}
|
|
2764
|
+
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;
|
|
2765
|
+
/**
|
|
2766
|
+
* The callees that are Animus's `createSystem` in a parsed system file. A
|
|
2767
|
+
* binding counts once its identity is followed to the factory: a named
|
|
2768
|
+
* import, through local and package re-exports; a namespace import's
|
|
2769
|
+
* member; or a property destructured from a namespace import, `require` or
|
|
2770
|
+
* `import()` of a module. A binding named `createSystem` shown to be
|
|
2771
|
+
* something else is no root. One whose identity discovery cannot follow
|
|
2772
|
+
* keeps the admission spelling gave it, so a resolution limit never drops a
|
|
2773
|
+
* kit. Both are reported. The bare name also anchors, as a global, unless
|
|
2774
|
+
* the file binds it.
|
|
2775
|
+
*/
|
|
2776
|
+
async function systemRoots(systemFilePath, source, imports, following) {
|
|
2777
|
+
const roots = /* @__PURE__ */ new Set();
|
|
2778
|
+
const identityOf = (specifier, name) => exportIdentity(systemFilePath, specifier, name, following, /* @__PURE__ */ new Set());
|
|
2779
|
+
const called = (callee) => new RegExp(`(?<![a-zA-Z0-9_$.])${escapeRegExp$1(callee)}\\s*\\(`).test(source);
|
|
2780
|
+
/** Admits a binding named `createSystem` by its identity: an unknown one
|
|
2781
|
+
* keeps `spellingAdmits`, and every unproven one is reported. */
|
|
2782
|
+
const admit = (local, offset, identity, spellingAdmits) => {
|
|
2783
|
+
if (identity.kind === "animus") {
|
|
2784
|
+
roots.add(local);
|
|
2785
|
+
return;
|
|
2786
|
+
}
|
|
2787
|
+
if (identity.kind === "unknown" && spellingAdmits) roots.add(local);
|
|
2788
|
+
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`;
|
|
2789
|
+
following.report?.({
|
|
2790
|
+
file: systemFilePath,
|
|
2791
|
+
component: local,
|
|
2792
|
+
kind: "warn",
|
|
2793
|
+
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`,
|
|
2794
|
+
code: UNPROVEN_ROOT_BINDING,
|
|
2795
|
+
severity: severityFor(UNPROVEN_ROOT_BINDING),
|
|
2796
|
+
...locationIn(source, offset)
|
|
2797
|
+
});
|
|
2798
|
+
};
|
|
2799
|
+
let bindsName = false;
|
|
2800
|
+
for (const binding of imports) {
|
|
2801
|
+
const named = binding.imported === "createSystem" || binding.local === "createSystem";
|
|
2802
|
+
if (binding.local === "createSystem") bindsName = true;
|
|
2803
|
+
if (named) {
|
|
2804
|
+
const statement = new RegExp(`\\bimport\\b[^;]*?\\bfrom\\s*['"]${escapeRegExp$1(binding.source)}['"]`, "g");
|
|
2805
|
+
let offset = 0;
|
|
2806
|
+
for (const match of source.matchAll(statement)) {
|
|
2807
|
+
const at = match[0].search(new RegExp(`(?<![a-zA-Z0-9_$])${escapeRegExp$1(binding.local)}\\b`));
|
|
2808
|
+
if (at !== -1) {
|
|
2809
|
+
offset = match.index + at;
|
|
2810
|
+
break;
|
|
2811
|
+
}
|
|
2812
|
+
}
|
|
2813
|
+
admit(binding.local, offset, await identityOf(binding.source, binding.imported), binding.imported === "createSystem");
|
|
2814
|
+
continue;
|
|
2815
|
+
}
|
|
2816
|
+
if (binding.source.startsWith(".") && called(binding.local) && (await identityOf(binding.source, binding.imported)).kind === "animus") roots.add(binding.local);
|
|
2817
|
+
}
|
|
2818
|
+
const namespaces = /* @__PURE__ */ new Map();
|
|
2819
|
+
for (const [, namespace, specifier] of source.matchAll(NAMESPACE_IMPORT)) {
|
|
2820
|
+
namespaces.set(namespace, specifier);
|
|
2821
|
+
const member = `${namespace}.createSystem`;
|
|
2822
|
+
if (ANIMUS_SYSTEM_SPECIFIER.test(specifier) || specifier.startsWith(".") && called(member) && (await identityOf(specifier, "createSystem")).kind === "animus") roots.add(member);
|
|
2823
|
+
}
|
|
2824
|
+
const destructuredFrom = (initializer) => {
|
|
2825
|
+
const loaded = /^\(?\s*(?:(?:await\s+)?import|require)\(\s*['"]([^'"]+)['"]\s*\)\s*\)?$/.exec(initializer);
|
|
2826
|
+
if (loaded) return identityOf(loaded[1], "createSystem");
|
|
2827
|
+
const namespace = namespaces.get(initializer);
|
|
2828
|
+
if (namespace !== void 0) return identityOf(namespace, "createSystem");
|
|
2829
|
+
return Promise.resolve({
|
|
2830
|
+
kind: "unknown",
|
|
2831
|
+
why: `it is destructured from '${initializer}', which is not a module namespace`
|
|
2832
|
+
});
|
|
2833
|
+
};
|
|
2834
|
+
for (const match of source.matchAll(/\b(?:const|let|var)\s*\{([^}]*)\}\s*=\s*([^;\n]+)/g)) {
|
|
2835
|
+
const [whole, pattern, initializer] = match;
|
|
2836
|
+
for (const property of pattern.split(",")) {
|
|
2837
|
+
const binding = /^\s*createSystem\s*(?::\s*([a-zA-Z_$][a-zA-Z0-9_$]*))?\s*(?:=[\s\S]*)?$/.exec(property);
|
|
2838
|
+
if (!binding) continue;
|
|
2839
|
+
const local = binding[1] ?? "createSystem";
|
|
2840
|
+
if (local === "createSystem") bindsName = true;
|
|
2841
|
+
admit(local, match.index + whole.indexOf("createSystem"), await destructuredFrom(initializer.trim()), local === "createSystem");
|
|
2842
|
+
}
|
|
2843
|
+
}
|
|
2844
|
+
const declaresName = /\b(?:function\*?|class)\s+createSystem\b|\b(?:const|let|var)\s+createSystem\b/.test(source);
|
|
2845
|
+
if (!bindsName && !declaresName) roots.add("createSystem");
|
|
2846
|
+
return roots;
|
|
2847
|
+
}
|
|
2440
2848
|
/** A pattern matching a call of any of `callees` as a whole name, or null
|
|
2441
2849
|
* when there is none to anchor on. */
|
|
2442
2850
|
function rootCallPattern(callees) {
|
|
2443
2851
|
if (callees.length === 0) return null;
|
|
2444
2852
|
return `(?<![a-zA-Z0-9_$.])(?:${callees.map((callee) => callee.replace(/[.$]/g, "\\$&")).join("|")})`;
|
|
2445
2853
|
}
|
|
2446
|
-
function extractSystemFilePackages(systemFilePath,
|
|
2854
|
+
async function extractSystemFilePackages(systemFilePath, parseModule, report, resolvePackage) {
|
|
2447
2855
|
let source;
|
|
2448
2856
|
try {
|
|
2449
2857
|
source = (0, fs.readFileSync)(systemFilePath, "utf-8");
|
|
2450
2858
|
} catch {
|
|
2451
2859
|
return [];
|
|
2452
2860
|
}
|
|
2453
|
-
const parsed =
|
|
2454
|
-
const parsedImports = parsed && parsed.length > 0 ? parsed : null;
|
|
2861
|
+
const parsed = parseModule?.(source, systemFilePath) ?? null;
|
|
2862
|
+
const parsedImports = parsed && parsed.imports.length > 0 ? parsed.imports : null;
|
|
2455
2863
|
let rootCall = "createSystem";
|
|
2456
|
-
if (parsedImports) {
|
|
2457
|
-
|
|
2458
|
-
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
...shadowed ? [] : ["createSystem"]
|
|
2462
|
-
])]);
|
|
2463
|
-
}
|
|
2864
|
+
if (parseModule && parsedImports) rootCall = rootCallPattern([...await systemRoots(systemFilePath, source, parsedImports, {
|
|
2865
|
+
parseModule,
|
|
2866
|
+
resolvePackage,
|
|
2867
|
+
report
|
|
2868
|
+
})]);
|
|
2464
2869
|
const identifiers = /* @__PURE__ */ new Set();
|
|
2465
2870
|
const constructorRegex = rootCall === null ? null : new RegExp(`${rootCall}\\s*\\(\\s*\\{[^}]*?\\bincludes\\s*:\\s*\\[([^\\]]*)\\]`, "gs");
|
|
2466
2871
|
const chainRegex = /\.includes\s*\(\s*\[([^\]]*)\]\s*\)/gs;
|
|
@@ -2715,151 +3120,6 @@ function createSourceCorpus(host, ingestor = createSourceIngestor(host)) {
|
|
|
2715
3120
|
};
|
|
2716
3121
|
}
|
|
2717
3122
|
//#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);
|
|
2807
|
-
}
|
|
2808
|
-
return out;
|
|
2809
|
-
}
|
|
2810
|
-
/**
|
|
2811
|
-
* Specifiers of placeholder text anywhere inside a `url()` or `image-set()`
|
|
2812
|
-
* call, a reference or not: after substitution any of it is a broken load.
|
|
2813
|
-
*/
|
|
2814
|
-
function placeholdersInAssetCalls(css) {
|
|
2815
|
-
if (!css.includes("animus-asset:")) return [];
|
|
2816
|
-
const calls = [...css.matchAll(URL_OPEN_RE), ...css.matchAll(IMAGE_SET_OPEN_RE)].map((open) => {
|
|
2817
|
-
const paren = css.indexOf("(", open.index ?? 0);
|
|
2818
|
-
return [paren, closingParen(css, paren)];
|
|
2819
|
-
});
|
|
2820
|
-
const found = /* @__PURE__ */ new Set();
|
|
2821
|
-
for (let at = css.indexOf(ASSET_PLACEHOLDER_PREFIX); at !== -1; at = css.indexOf(ASSET_PLACEHOLDER_PREFIX, at + 1)) {
|
|
2822
|
-
if (!calls.some(([open, close]) => open < at && at < close)) continue;
|
|
2823
|
-
found.add(/^[^'")\s,]*/.exec(css.slice(at + 13))[0]);
|
|
2824
|
-
}
|
|
2825
|
-
return [...found];
|
|
2826
|
-
}
|
|
2827
|
-
function findSheetAssetSpecifiers(sheets) {
|
|
2828
|
-
return [.../* @__PURE__ */ new Set([
|
|
2829
|
-
...findAssetSpecifiers(sheets.variableCss),
|
|
2830
|
-
...findAssetSpecifiers(sheets.globalCss),
|
|
2831
|
-
...findAssetSpecifiers(sheets.componentCss)
|
|
2832
|
-
])];
|
|
2833
|
-
}
|
|
2834
|
-
/** The one substitution step every emitted sheet passes through. */
|
|
2835
|
-
function substituteSheetAssets(sheets, urlBySpecifier) {
|
|
2836
|
-
return {
|
|
2837
|
-
variableCss: substituteAssetPlaceholders(sheets.variableCss, urlBySpecifier),
|
|
2838
|
-
globalCss: substituteAssetPlaceholders(sheets.globalCss, urlBySpecifier),
|
|
2839
|
-
componentCss: substituteAssetPlaceholders(sheets.componentCss, urlBySpecifier)
|
|
2840
|
-
};
|
|
2841
|
-
}
|
|
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
|
-
//#endregion
|
|
2863
3123
|
//#region pipeline/resolve-asset.ts
|
|
2864
3124
|
/**
|
|
2865
3125
|
* Asset resolution for hosts with no bundler hook. Host path aliases apply
|
|
@@ -3660,9 +3920,12 @@ function hasSourceThemeManifests(system) {
|
|
|
3660
3920
|
const surfacedSystems = /* @__PURE__ */ new WeakSet();
|
|
3661
3921
|
/** The loaded system's own diagnostics, the first time it is analyzed. A
|
|
3662
3922
|
* strict error keeps failing every analysis until the system changes. */
|
|
3663
|
-
function systemDiagnostics(system, strict) {
|
|
3923
|
+
function systemDiagnostics(system, strict, levels) {
|
|
3664
3924
|
const diagnostics = [...collectSelectorAliasDiagnostics(system.selectorAliasesJson), ...systemLoadDiagnostics(system)];
|
|
3665
|
-
const blocking =
|
|
3925
|
+
const blocking = diagnostics.some((d) => effectiveLevel(d, {
|
|
3926
|
+
strict,
|
|
3927
|
+
levels
|
|
3928
|
+
}) === "error");
|
|
3666
3929
|
if (surfacedSystems.has(system) && !blocking) return [];
|
|
3667
3930
|
surfacedSystems.add(system);
|
|
3668
3931
|
return diagnostics;
|
|
@@ -3672,7 +3935,7 @@ function systemDiagnostics(system, strict) {
|
|
|
3672
3935
|
* the call site (strict-mode throw vs warn).
|
|
3673
3936
|
*/
|
|
3674
3937
|
function runProjectAnalysis(engineApi, opts) {
|
|
3675
|
-
const { analyzeProject } = engineApi();
|
|
3938
|
+
const { analyzeProject, diagnosticCodes } = engineApi();
|
|
3676
3939
|
let t = performance.now();
|
|
3677
3940
|
const inputs = buildAnalysisInputs(opts);
|
|
3678
3941
|
const serializeMs = Math.round(performance.now() - t);
|
|
@@ -3693,8 +3956,10 @@ function runProjectAnalysis(engineApi, opts) {
|
|
|
3693
3956
|
surfaceManifestDiagnostics(manifest, opts.warn, {
|
|
3694
3957
|
strict: opts.strict,
|
|
3695
3958
|
info: opts.info,
|
|
3959
|
+
levels: opts.diagnostics,
|
|
3960
|
+
knownCodes: opts.diagnostics ? knownDiagnosticCodes(diagnosticCodes?.()) : void 0,
|
|
3696
3961
|
prepend: [
|
|
3697
|
-
...systemDiagnostics(opts.system, opts.strict),
|
|
3962
|
+
...systemDiagnostics(opts.system, opts.strict, opts.diagnostics),
|
|
3698
3963
|
...propertyDiagnostics,
|
|
3699
3964
|
...opts.extraDiagnostics ?? []
|
|
3700
3965
|
]
|
|
@@ -3960,7 +4225,7 @@ function runStructuralSelfCheck(input) {
|
|
|
3960
4225
|
const failures = [];
|
|
3961
4226
|
if (input.componentCount === 0) failures.push("No component CSS produced — check the system file and its includes list");
|
|
3962
4227
|
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 === "empty") failures.push(`include '${specifier}' resolved but discovered no component sources`);
|
|
4228
|
+
for (const { specifier, outcome } of input.externalOutcomes ?? []) if (outcome === "empty") failures.push(`include '${specifier}' resolved but discovered no component sources [${NO_KIT_FILES}]`);
|
|
3964
4229
|
else if (outcome === "unresolvable") failures.push(`include '${specifier}' could not be resolved`);
|
|
3965
4230
|
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
4231
|
if ((input.assembledCss ?? `${input.variableCss}\n${input.globalCss}\n${input.componentCss}`).includes("__TRANSFORM__")) failures.push("Unresolved __TRANSFORM__ placeholders found in CSS output");
|
|
@@ -4157,12 +4422,24 @@ Object.defineProperty(exports, "DEFAULT_EXTENSIONS", {
|
|
|
4157
4422
|
return DEFAULT_EXTENSIONS;
|
|
4158
4423
|
}
|
|
4159
4424
|
});
|
|
4425
|
+
Object.defineProperty(exports, "DIAGNOSTIC_LEVELS", {
|
|
4426
|
+
enumerable: true,
|
|
4427
|
+
get: function() {
|
|
4428
|
+
return DIAGNOSTIC_LEVELS;
|
|
4429
|
+
}
|
|
4430
|
+
});
|
|
4160
4431
|
Object.defineProperty(exports, "DRIVER_NAMESPACES", {
|
|
4161
4432
|
enumerable: true,
|
|
4162
4433
|
get: function() {
|
|
4163
4434
|
return DRIVER_NAMESPACES;
|
|
4164
4435
|
}
|
|
4165
4436
|
});
|
|
4437
|
+
Object.defineProperty(exports, "DiagnosticFailure", {
|
|
4438
|
+
enumerable: true,
|
|
4439
|
+
get: function() {
|
|
4440
|
+
return DiagnosticFailure;
|
|
4441
|
+
}
|
|
4442
|
+
});
|
|
4166
4443
|
Object.defineProperty(exports, "ENGINE_TRANSFORM_EXTENSIONS", {
|
|
4167
4444
|
enumerable: true,
|
|
4168
4445
|
get: function() {
|
|
@@ -4349,10 +4626,10 @@ Object.defineProperty(exports, "enforceExternalTokenContracts", {
|
|
|
4349
4626
|
return enforceExternalTokenContracts;
|
|
4350
4627
|
}
|
|
4351
4628
|
});
|
|
4352
|
-
Object.defineProperty(exports, "
|
|
4629
|
+
Object.defineProperty(exports, "engineModuleParser", {
|
|
4353
4630
|
enumerable: true,
|
|
4354
4631
|
get: function() {
|
|
4355
|
-
return
|
|
4632
|
+
return engineModuleParser;
|
|
4356
4633
|
}
|
|
4357
4634
|
});
|
|
4358
4635
|
Object.defineProperty(exports, "excludeCollectedPackages", {
|