@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
|
@@ -111,7 +111,167 @@ function parseInternalWire(json, wire) {
|
|
|
111
111
|
return parsed;
|
|
112
112
|
}
|
|
113
113
|
//#endregion
|
|
114
|
+
//#region pipeline/asset-placeholders.ts
|
|
115
|
+
const ASSET_PLACEHOLDER_PREFIX = "animus-asset:";
|
|
116
|
+
const URL_OPEN_RE = /url\(\s*(['"]?)/gi;
|
|
117
|
+
const IMAGE_SET_OPEN_RE = /(?:-webkit-)?image-set\(/gi;
|
|
118
|
+
const BARE_SPECIFIER_RE = /^[^'")\s]+/;
|
|
119
|
+
/** The index of the `)` that closes the `(` at `open`, outside strings. */
|
|
120
|
+
function closingParen(css, open) {
|
|
121
|
+
let depth = 0;
|
|
122
|
+
let quote = null;
|
|
123
|
+
for (let i = open; i < css.length; i += 1) {
|
|
124
|
+
const c = css[i];
|
|
125
|
+
if (quote !== null) {
|
|
126
|
+
if (c === quote) quote = null;
|
|
127
|
+
} else if (c === "\"" || c === "'") quote = c;
|
|
128
|
+
else if (c === "(") depth += 1;
|
|
129
|
+
else if (c === ")") {
|
|
130
|
+
depth -= 1;
|
|
131
|
+
if (depth === 0) return i;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return css.length;
|
|
135
|
+
}
|
|
136
|
+
function quotedReference(css, quoteAt) {
|
|
137
|
+
const start = quoteAt + 1;
|
|
138
|
+
if (!css.startsWith("animus-asset:", start)) return null;
|
|
139
|
+
const end = css.indexOf(css[quoteAt], start);
|
|
140
|
+
if (end === -1) return null;
|
|
141
|
+
return {
|
|
142
|
+
start,
|
|
143
|
+
end,
|
|
144
|
+
specifier: css.slice(start + 13, end)
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
function assetReferences(css) {
|
|
148
|
+
if (!css.includes("animus-asset:")) return [];
|
|
149
|
+
const references = [];
|
|
150
|
+
for (const open of css.matchAll(URL_OPEN_RE)) {
|
|
151
|
+
const at = (open.index ?? 0) + open[0].length;
|
|
152
|
+
if (open[1]) {
|
|
153
|
+
const reference = quotedReference(css, at - 1);
|
|
154
|
+
if (reference) references.push(reference);
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
if (!css.startsWith("animus-asset:", at)) continue;
|
|
158
|
+
const specifier = BARE_SPECIFIER_RE.exec(css.slice(at + 13))?.[0];
|
|
159
|
+
if (specifier === void 0) continue;
|
|
160
|
+
const end = at + 13 + specifier.length;
|
|
161
|
+
references.push({
|
|
162
|
+
start: at,
|
|
163
|
+
end,
|
|
164
|
+
specifier
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
for (const open of css.matchAll(IMAGE_SET_OPEN_RE)) {
|
|
168
|
+
const paren = (open.index ?? 0) + open[0].length - 1;
|
|
169
|
+
const close = closingParen(css, paren);
|
|
170
|
+
let depth = 0;
|
|
171
|
+
let candidateStart = true;
|
|
172
|
+
for (let i = paren + 1; i < close; i += 1) {
|
|
173
|
+
const c = css[i];
|
|
174
|
+
if (/\s/.test(c)) continue;
|
|
175
|
+
if (c === "\"" || c === "'") {
|
|
176
|
+
if (depth === 0 && candidateStart) {
|
|
177
|
+
const reference = quotedReference(css, i);
|
|
178
|
+
if (reference) references.push(reference);
|
|
179
|
+
}
|
|
180
|
+
const end = css.indexOf(c, i + 1);
|
|
181
|
+
i = end === -1 ? close : end;
|
|
182
|
+
} else if (c === "(") depth += 1;
|
|
183
|
+
else if (c === ")") depth -= 1;
|
|
184
|
+
else if (c === "," && depth === 0) {
|
|
185
|
+
candidateStart = true;
|
|
186
|
+
continue;
|
|
187
|
+
}
|
|
188
|
+
candidateStart = false;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return references.sort((a, b) => a.start - b.start);
|
|
192
|
+
}
|
|
193
|
+
function findAssetSpecifiers(css) {
|
|
194
|
+
return [...new Set(assetReferences(css).map((reference) => reference.specifier))];
|
|
195
|
+
}
|
|
196
|
+
function substituteAssetPlaceholders(css, urlBySpecifier) {
|
|
197
|
+
if (urlBySpecifier.size === 0) return css;
|
|
198
|
+
let out = css;
|
|
199
|
+
for (const reference of assetReferences(css).reverse()) {
|
|
200
|
+
const url = urlBySpecifier.get(reference.specifier);
|
|
201
|
+
if (url === void 0) continue;
|
|
202
|
+
out = out.slice(0, reference.start) + url + out.slice(reference.end);
|
|
203
|
+
}
|
|
204
|
+
return out;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Specifiers of placeholder text anywhere inside a `url()` or `image-set()`
|
|
208
|
+
* call, a reference or not: after substitution any of it is a broken load.
|
|
209
|
+
*/
|
|
210
|
+
function placeholdersInAssetCalls(css) {
|
|
211
|
+
if (!css.includes("animus-asset:")) return [];
|
|
212
|
+
const calls = [...css.matchAll(URL_OPEN_RE), ...css.matchAll(IMAGE_SET_OPEN_RE)].map((open) => {
|
|
213
|
+
const paren = css.indexOf("(", open.index ?? 0);
|
|
214
|
+
return [paren, closingParen(css, paren)];
|
|
215
|
+
});
|
|
216
|
+
const found = /* @__PURE__ */ new Set();
|
|
217
|
+
for (let at = css.indexOf(ASSET_PLACEHOLDER_PREFIX); at !== -1; at = css.indexOf(ASSET_PLACEHOLDER_PREFIX, at + 1)) {
|
|
218
|
+
if (!calls.some(([open, close]) => open < at && at < close)) continue;
|
|
219
|
+
found.add(/^[^'")\s,]*/.exec(css.slice(at + 13))[0]);
|
|
220
|
+
}
|
|
221
|
+
return [...found];
|
|
222
|
+
}
|
|
223
|
+
function findSheetAssetSpecifiers(sheets) {
|
|
224
|
+
return [.../* @__PURE__ */ new Set([
|
|
225
|
+
...findAssetSpecifiers(sheets.variableCss),
|
|
226
|
+
...findAssetSpecifiers(sheets.globalCss),
|
|
227
|
+
...findAssetSpecifiers(sheets.componentCss)
|
|
228
|
+
])];
|
|
229
|
+
}
|
|
230
|
+
/** The one substitution step every emitted sheet passes through. */
|
|
231
|
+
function substituteSheetAssets(sheets, urlBySpecifier) {
|
|
232
|
+
return {
|
|
233
|
+
variableCss: substituteAssetPlaceholders(sheets.variableCss, urlBySpecifier),
|
|
234
|
+
globalCss: substituteAssetPlaceholders(sheets.globalCss, urlBySpecifier),
|
|
235
|
+
componentCss: substituteAssetPlaceholders(sheets.componentCss, urlBySpecifier)
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
const UNSUBSTITUTED_ASSET_CODE = "animus.asset.unsubstituted-placeholder";
|
|
239
|
+
/** A placeholder in emitted CSS is a broken URL in the browser: it fails a
|
|
240
|
+
* strict build and warns otherwise. */
|
|
241
|
+
function reportSurvivingAssetPlaceholders(css, report) {
|
|
242
|
+
const specifiers = placeholdersInAssetCalls(css);
|
|
243
|
+
if (specifiers.length === 0) return;
|
|
244
|
+
const message = `${report.prefix} asset() placeholders reached ${report.surface ?? "emitted CSS"} unsubstituted: ${specifiers.join(", ")} (${UNSUBSTITUTED_ASSET_CODE})`;
|
|
245
|
+
if (report.strict) throw new Error(message);
|
|
246
|
+
report.warn(message);
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* The runtime code an analysis generates: each dynamic prop config, which the
|
|
250
|
+
* shared prop map module carries, and each component's replacement. Extraction
|
|
251
|
+
* lifts asset() out of them into root variables, so none should hold one.
|
|
252
|
+
* Both embed values as JSON strings, so their quote and backslash escapes are
|
|
253
|
+
* undone for scanning: the text is for `findAssetSpecifiers`, not to run.
|
|
254
|
+
*/
|
|
255
|
+
function generatedModuleCode(manifest) {
|
|
256
|
+
return [JSON.stringify(manifest.dynamic_props), ...Object.values(manifest.components).map((component) => component.replacement)].join("\n").replace(/\\(["\\])/g, "$1");
|
|
257
|
+
}
|
|
258
|
+
//#endregion
|
|
114
259
|
//#region pipeline/manifest-diagnostics.ts
|
|
260
|
+
/** `file`, then its line and column where the record has them. */
|
|
261
|
+
function locationOf(diagnostic) {
|
|
262
|
+
if (diagnostic.line === void 0) return diagnostic.file;
|
|
263
|
+
if (diagnostic.column === void 0) return `${diagnostic.file}:${diagnostic.line}`;
|
|
264
|
+
return `${diagnostic.file}:${diagnostic.line}:${diagnostic.column}`;
|
|
265
|
+
}
|
|
266
|
+
/** A located record's `file:line:column: `; nothing for one without a line,
|
|
267
|
+
* whose message names its file. */
|
|
268
|
+
function locatedPrefix(diagnostic) {
|
|
269
|
+
return diagnostic.line === void 0 ? "" : `${locationOf(diagnostic)}: `;
|
|
270
|
+
}
|
|
271
|
+
/** What the record dropped, when its message does not already quote it. */
|
|
272
|
+
function droppedSuffix(diagnostic) {
|
|
273
|
+
return diagnostic.dropped && !diagnostic.message.includes(diagnostic.dropped) ? ` — dropped: ${diagnostic.dropped}` : "";
|
|
274
|
+
}
|
|
115
275
|
/** Stable code for selector forms with no substitutable subject; the
|
|
116
276
|
* extraction engine mints the same string. */
|
|
117
277
|
const SELECTOR_UNSUPPORTED_SUBJECT = "animus.selector.unsupported-subject";
|
|
@@ -124,6 +284,63 @@ function isUnresolvedParentDrop(diagnostic) {
|
|
|
124
284
|
function unresolvedParentName(diagnostic) {
|
|
125
285
|
return UNRESOLVED_PARENT_RE.exec(diagnostic.message)?.[1] ?? null;
|
|
126
286
|
}
|
|
287
|
+
const DIAGNOSTIC_LEVELS = /* @__PURE__ */ new Set([
|
|
288
|
+
"off",
|
|
289
|
+
"info",
|
|
290
|
+
"warn",
|
|
291
|
+
"error"
|
|
292
|
+
]);
|
|
293
|
+
/** The level `levels` sets for `code`: its exact entry, else the entry of
|
|
294
|
+
* the longest prefix it starts with. */
|
|
295
|
+
function levelFor(code, levels) {
|
|
296
|
+
if (code === void 0 || levels === void 0) return void 0;
|
|
297
|
+
if (Object.hasOwn(levels, code)) return levels[code];
|
|
298
|
+
let longest;
|
|
299
|
+
for (const [key, level] of Object.entries(levels)) {
|
|
300
|
+
if (!key.endsWith(".*")) continue;
|
|
301
|
+
const prefix = key.slice(0, -1);
|
|
302
|
+
if (code.startsWith(prefix) && prefix.length > (longest?.length ?? -1)) longest = {
|
|
303
|
+
length: prefix.length,
|
|
304
|
+
level
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
return longest?.level;
|
|
308
|
+
}
|
|
309
|
+
/** The level a record prints at: the option's entry for its code, else
|
|
310
|
+
* `error` for an error-severity record under `strict`, else its own. */
|
|
311
|
+
function effectiveLevel(diagnostic, policy) {
|
|
312
|
+
const level = levelFor(diagnostic.code, policy.levels);
|
|
313
|
+
if (level !== void 0) return level;
|
|
314
|
+
if (diagnostic.severity === "info") return "info";
|
|
315
|
+
return policy.strict && diagnostic.severity === "error" ? "error" : "warn";
|
|
316
|
+
}
|
|
317
|
+
/** Every code Animus is known to report: the engine's table, as the native
|
|
318
|
+
* module publishes it (`diagnosticCodes()`), the pipeline's, and the asset
|
|
319
|
+
* code. The system package mints vocabulary codes at run time, so a valid
|
|
320
|
+
* code can be missing. */
|
|
321
|
+
function knownDiagnosticCodes(engineCodesJson) {
|
|
322
|
+
const engineCodes = engineCodesJson ? Object.keys(parseInternalWire(engineCodesJson, "diagnosticCodes (the engine code table)")) : [];
|
|
323
|
+
return /* @__PURE__ */ new Set([
|
|
324
|
+
...engineCodes,
|
|
325
|
+
...DIAGNOSTIC_SEVERITY.keys(),
|
|
326
|
+
UNSUBSTITUTED_ASSET_CODE
|
|
327
|
+
]);
|
|
328
|
+
}
|
|
329
|
+
/** The keys of `levels` that name no known code, exactly or as a prefix. */
|
|
330
|
+
function unknownDiagnosticKeys(levels, known) {
|
|
331
|
+
return Object.keys(levels).filter((key) => {
|
|
332
|
+
if (!key.endsWith(".*")) return !known.has(key);
|
|
333
|
+
const prefix = key.slice(0, -1);
|
|
334
|
+
return ![...known].some((code) => code.startsWith(prefix));
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
/** The failure of diagnostics at `error` level, from `strict` or the
|
|
338
|
+
* `diagnostics` option: a host fails the build on it whatever its own
|
|
339
|
+
* strictness. It prints as the plain `Error` strict always threw. */
|
|
340
|
+
var DiagnosticFailure = class extends Error {};
|
|
341
|
+
/** Level options already checked against the known codes: one warning per
|
|
342
|
+
* unknown key, however often a host analyzes. */
|
|
343
|
+
const checkedLevels = /* @__PURE__ */ new WeakSet();
|
|
127
344
|
/** True when the selector carries at least one substitutable `&` subject
|
|
128
345
|
* outside quoted text. */
|
|
129
346
|
function hasSelectorSubject(value) {
|
|
@@ -166,6 +383,9 @@ function collectSelectorAliasDiagnostics(selectorAliasesJson) {
|
|
|
166
383
|
const UNREADABLE_SOURCE_FILE = "animus.ingestion.unreadable-source-file";
|
|
167
384
|
/** A kit the system extends resolved, yet discovery found none of its files. */
|
|
168
385
|
const NO_KIT_FILES = "animus.discovery.no-kit-files";
|
|
386
|
+
/** A system file's `createSystem` binding that discovery could not follow to
|
|
387
|
+
* Animus's factory, so it is no root. */
|
|
388
|
+
const UNPROVEN_ROOT_BINDING = "animus.discovery.unproven-root-binding";
|
|
169
389
|
/** What a skipped source file costs: the analysis never sees what it
|
|
170
390
|
* renders. */
|
|
171
391
|
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";
|
|
@@ -242,6 +462,7 @@ const DIAGNOSTIC_SEVERITY = /* @__PURE__ */ new Map([
|
|
|
242
462
|
[SELECTOR_UNSUPPORTED_SUBJECT, "error"],
|
|
243
463
|
[UNREADABLE_SOURCE_FILE, "error"],
|
|
244
464
|
[NO_KIT_FILES, "warn"],
|
|
465
|
+
[UNPROVEN_ROOT_BINDING, "warn"],
|
|
245
466
|
[VOCABULARY_COLLISION, "warn"],
|
|
246
467
|
[VOCABULARY_LEGACY_VERB, "warn"],
|
|
247
468
|
[INVALID_PROPERTY_REGISTRATION, "error"],
|
|
@@ -366,25 +587,33 @@ function tokenCollisionDiagnostics(system) {
|
|
|
366
587
|
*/
|
|
367
588
|
function surfaceManifestDiagnostics(manifest, warn, policy = {}) {
|
|
368
589
|
const errors = [];
|
|
590
|
+
if (policy.levels && policy.knownCodes && !checkedLevels.has(policy.levels)) {
|
|
591
|
+
checkedLevels.add(policy.levels);
|
|
592
|
+
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)`);
|
|
593
|
+
}
|
|
369
594
|
const diagnostics = policy.prepend?.length ? [...policy.prepend, ...manifest.diagnostics ?? []] : manifest.diagnostics ?? [];
|
|
370
595
|
for (const diagnostic of diagnostics) {
|
|
596
|
+
const level = effectiveLevel(diagnostic, policy);
|
|
597
|
+
if (level === "off") continue;
|
|
371
598
|
let line = null;
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
599
|
+
const mark = level === "info" ? "ℹ" : "⚠";
|
|
600
|
+
const message = `${diagnostic.message}${droppedSuffix(diagnostic)}`;
|
|
601
|
+
if (diagnostic.kind === "bail") line = `${mark} ${locatedPrefix(diagnostic)}${diagnostic.component} not extracted: ${message}`;
|
|
602
|
+
else if (diagnostic.kind === "skip") line = `${mark} ${locatedPrefix(diagnostic)}${diagnostic.component}: skipped ${message}`;
|
|
603
|
+
else if (diagnostic.kind === "warn") line = `${mark} ${locationOf(diagnostic)}: ${diagnostic.component}: ${message}`;
|
|
375
604
|
if (line === null) continue;
|
|
376
605
|
if (diagnostic.code && !diagnostic.message.includes(diagnostic.code)) line += ` [${diagnostic.code}]`;
|
|
377
|
-
if (
|
|
606
|
+
if (level === "info") {
|
|
378
607
|
policy.info?.(line);
|
|
379
608
|
continue;
|
|
380
609
|
}
|
|
381
|
-
if (
|
|
382
|
-
errors.push(`${diagnostic.code ?? "error"} — ${diagnostic.component}: ${
|
|
610
|
+
if (level === "error") {
|
|
611
|
+
errors.push(`${diagnostic.code ?? "error"} — ${locatedPrefix(diagnostic)}${diagnostic.component}: ${message}`);
|
|
383
612
|
continue;
|
|
384
613
|
}
|
|
385
614
|
warn(line);
|
|
386
615
|
}
|
|
387
|
-
if (errors.length > 0) throw new
|
|
616
|
+
if (errors.length > 0) throw new DiagnosticFailure(`[animus] strict: ${errors.length} error diagnostic(s):\n${errors.join("\n")}`);
|
|
388
617
|
}
|
|
389
618
|
//#endregion
|
|
390
619
|
//#region pipeline/mdx-preprocessor.ts
|
|
@@ -1036,17 +1265,17 @@ const NODE_NEXT_EXTENSION_MAP = {
|
|
|
1036
1265
|
".mjs": [".mts"],
|
|
1037
1266
|
".cjs": [".cts"]
|
|
1038
1267
|
};
|
|
1039
|
-
|
|
1040
|
-
|
|
1268
|
+
/** The paths a relative specifier can name from `importerPath`, in probe
|
|
1269
|
+
* order: as written with each source suffix, then a NodeNext emitted
|
|
1270
|
+
* extension's sources. Empty for a non-relative specifier. */
|
|
1271
|
+
function relativeSourceCandidates(importerPath, specifier) {
|
|
1272
|
+
if (!specifier.startsWith(".")) return [];
|
|
1041
1273
|
const base = posix.normalize(posix.join(posix.dirname(canonicalResolverPath(importerPath)), specifier));
|
|
1042
|
-
for (const suffix of RELATIVE_PROBE_SUFFIXES) {
|
|
1043
|
-
const candidate = `${base}${suffix}`;
|
|
1044
|
-
const actualPath = files.get(candidate);
|
|
1045
|
-
if (actualPath !== void 0) return actualPath;
|
|
1046
|
-
}
|
|
1047
1274
|
const explicitExtension = extension(base);
|
|
1048
|
-
|
|
1049
|
-
|
|
1275
|
+
return [...RELATIVE_PROBE_SUFFIXES.map((suffix) => `${base}${suffix}`), ...(NODE_NEXT_EXTENSION_MAP[explicitExtension] ?? []).map((sourceExtension) => `${base.slice(0, -explicitExtension.length)}${sourceExtension}`)];
|
|
1276
|
+
}
|
|
1277
|
+
function resolveRelativeSource(importerPath, specifier, files) {
|
|
1278
|
+
for (const candidate of relativeSourceCandidates(importerPath, specifier)) {
|
|
1050
1279
|
const actualPath = files.get(candidate);
|
|
1051
1280
|
if (actualPath !== void 0) return actualPath;
|
|
1052
1281
|
}
|
|
@@ -1392,6 +1621,7 @@ function createV2EngineApi(deps) {
|
|
|
1392
1621
|
return {
|
|
1393
1622
|
loadSystemModule: (...args) => native.loadSystemModule(...args),
|
|
1394
1623
|
extractFacts: (filesJson) => native.extractFacts(filesJson),
|
|
1624
|
+
diagnosticCodes: () => native.diagnosticCodes(),
|
|
1395
1625
|
analyzeProject: (filesJsonRaw, scalesJson, variableMapJson, contextualVarsJson, propConfigJson, groupRegistryJson, packageResolutionJson, devMode, emitterConfigJson, selectorAliasesJson, _selectorOrderJson, globalStyleBlocksJson, pathAliasesJson, keyframesJson, staticCssJson, conditionAliasesJson, externalDirsJson, transformSourcesJson, transformProvenanceJson, declarationScalesJson, analysisContextJson) => {
|
|
1396
1626
|
const filesJson = deps.rehydrateFilesJson ? deps.rehydrateFilesJson(filesJsonRaw) : filesJsonRaw;
|
|
1397
1627
|
const sent = /* @__PURE__ */ new Map();
|
|
@@ -1457,6 +1687,10 @@ function assertNoRetiredEngineSelection(engineOption) {
|
|
|
1457
1687
|
}
|
|
1458
1688
|
//#endregion
|
|
1459
1689
|
//#region pipeline/core-options.ts
|
|
1690
|
+
/**
|
|
1691
|
+
* One option schema shared by the Vite, Next, and CLI drivers: key semantics,
|
|
1692
|
+
* default exclusions, and mode authority cannot drift per driver.
|
|
1693
|
+
*/
|
|
1460
1694
|
const DRIVER_NAMESPACES = [
|
|
1461
1695
|
"vite",
|
|
1462
1696
|
"next",
|
|
@@ -1490,6 +1724,7 @@ const CORE_OPTION_KEYS = /* @__PURE__ */ new Set([
|
|
|
1490
1724
|
"exclude",
|
|
1491
1725
|
"extensions",
|
|
1492
1726
|
"strict",
|
|
1727
|
+
"diagnostics",
|
|
1493
1728
|
"verbose",
|
|
1494
1729
|
"prefix",
|
|
1495
1730
|
"prefixContextualVars",
|
|
@@ -1549,6 +1784,7 @@ const isString = (value) => Object(value) !== value && Object.prototype.toString
|
|
|
1549
1784
|
const isBoolean = (value) => Object(value) !== value && Object.prototype.toString.call(value) === "[object Boolean]";
|
|
1550
1785
|
const isVerbose = (value) => isBoolean(value) || value === "trace";
|
|
1551
1786
|
const isStringArray = (value) => Array.isArray(value) && value.every(isString);
|
|
1787
|
+
const isDiagnosticLevels = (value) => Object.prototype.toString.call(value) === "[object Object]" && Object.values(Object(value)).every((level) => isString(level) && DIAGNOSTIC_LEVELS.has(level));
|
|
1552
1788
|
const isStringOrStringArray = (value) => isString(value) || isStringArray(value);
|
|
1553
1789
|
const CORE_VALUE_GATES = [
|
|
1554
1790
|
{
|
|
@@ -1571,6 +1807,11 @@ const CORE_VALUE_GATES = [
|
|
|
1571
1807
|
ok: isBoolean,
|
|
1572
1808
|
expected: "a boolean"
|
|
1573
1809
|
},
|
|
1810
|
+
{
|
|
1811
|
+
key: "diagnostics",
|
|
1812
|
+
ok: isDiagnosticLevels,
|
|
1813
|
+
expected: "an object mapping codes to 'off', 'info', 'warn' or 'error'"
|
|
1814
|
+
},
|
|
1574
1815
|
{
|
|
1575
1816
|
key: "verbose",
|
|
1576
1817
|
ok: isVerbose,
|
|
@@ -2399,10 +2640,10 @@ function staleDistIncludesMessage(outcomes) {
|
|
|
2399
2640
|
if (stale.length === 0) return null;
|
|
2400
2641
|
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`;
|
|
2401
2642
|
}
|
|
2402
|
-
/** The engine's own parse of a module's
|
|
2643
|
+
/** The engine's own parse of a module's bindings, through its
|
|
2403
2644
|
* `extractFacts`; undefined for an engine without one. A parse that
|
|
2404
|
-
* panicked
|
|
2405
|
-
function
|
|
2645
|
+
* panicked counts as no parse. */
|
|
2646
|
+
function engineModuleParser(engine) {
|
|
2406
2647
|
const { extractFacts } = engine;
|
|
2407
2648
|
if (!extractFacts) return void 0;
|
|
2408
2649
|
return (source, path) => {
|
|
@@ -2411,36 +2652,200 @@ function engineImportParser(engine) {
|
|
|
2411
2652
|
path,
|
|
2412
2653
|
source
|
|
2413
2654
|
}])), "extractFacts").files[path];
|
|
2414
|
-
return facts && !facts.parsePanicked ?
|
|
2655
|
+
return facts && !facts.parsePanicked ? {
|
|
2656
|
+
imports: facts.imports,
|
|
2657
|
+
exports: facts.exports
|
|
2658
|
+
} : null;
|
|
2415
2659
|
} catch {
|
|
2416
2660
|
return null;
|
|
2417
2661
|
}
|
|
2418
2662
|
};
|
|
2419
2663
|
}
|
|
2664
|
+
const ANIMUS_SYSTEM_SPECIFIER = /^@animus-ui\/system(?:\/.*)?$/;
|
|
2665
|
+
/** The file a specifier names from `importer`: a relative one by the
|
|
2666
|
+
* ingestion's probe order, a bare one by the host's package resolution. */
|
|
2667
|
+
async function resolveModuleFile(importer, specifier, resolvePackage) {
|
|
2668
|
+
if (specifier.startsWith(".")) return relativeSourceCandidates(importer, specifier).find(isFile) ?? null;
|
|
2669
|
+
try {
|
|
2670
|
+
return await resolvePackage?.(specifier) ?? null;
|
|
2671
|
+
} catch {
|
|
2672
|
+
return null;
|
|
2673
|
+
}
|
|
2674
|
+
}
|
|
2675
|
+
const escapeRegExp$1 = (text) => text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
2676
|
+
/**
|
|
2677
|
+
* Follows `name`, exported by the module `specifier` names from `importer`,
|
|
2678
|
+
* through re-exports to its declaration. It is Animus's factory only when
|
|
2679
|
+
* that is `createSystem` of `@animus-ui/system`, and another identity only
|
|
2680
|
+
* when a module is shown to declare it; anything discovery cannot follow is
|
|
2681
|
+
* unknown.
|
|
2682
|
+
*/
|
|
2683
|
+
async function exportIdentity(importer, specifier, name, following, seen) {
|
|
2684
|
+
if (ANIMUS_SYSTEM_SPECIFIER.test(specifier)) return name === "createSystem" ? { kind: "animus" } : {
|
|
2685
|
+
kind: "other",
|
|
2686
|
+
what: `'${name}' of ${specifier}`
|
|
2687
|
+
};
|
|
2688
|
+
const file = await resolveModuleFile(importer, specifier, following.resolvePackage);
|
|
2689
|
+
if (file === null) return {
|
|
2690
|
+
kind: "unknown",
|
|
2691
|
+
why: `'${specifier}' could not be resolved`
|
|
2692
|
+
};
|
|
2693
|
+
const key = `${file}#${name}`;
|
|
2694
|
+
if (seen.has(key)) return {
|
|
2695
|
+
kind: "unknown",
|
|
2696
|
+
why: `'${specifier}' re-exports it in a cycle`
|
|
2697
|
+
};
|
|
2698
|
+
seen.add(key);
|
|
2699
|
+
let source;
|
|
2700
|
+
try {
|
|
2701
|
+
source = readFileSync(file, "utf-8");
|
|
2702
|
+
} catch {
|
|
2703
|
+
return {
|
|
2704
|
+
kind: "unknown",
|
|
2705
|
+
why: `'${specifier}' could not be read`
|
|
2706
|
+
};
|
|
2707
|
+
}
|
|
2708
|
+
const record = following.parseModule(source, file);
|
|
2709
|
+
if (record === null) return {
|
|
2710
|
+
kind: "unknown",
|
|
2711
|
+
why: `'${specifier}' could not be parsed`
|
|
2712
|
+
};
|
|
2713
|
+
const exported = record.exports.find((entry) => entry.exported === name);
|
|
2714
|
+
if (exported?.source) return exportIdentity(file, exported.source, exported.original ?? name, following, seen);
|
|
2715
|
+
const local = exported?.local ?? name;
|
|
2716
|
+
const imported = record.imports.find((entry) => entry.local === local);
|
|
2717
|
+
if (exported && imported) return exportIdentity(file, imported.source, imported.imported, following, seen);
|
|
2718
|
+
if (exported || new RegExp(`\\bexport\\s+(?:async\\s+)?(?:function\\*?|class|const|let|var)\\s+${escapeRegExp$1(name)}\\b`).test(source)) return {
|
|
2719
|
+
kind: "other",
|
|
2720
|
+
what: `the '${name}' that '${specifier}' declares`
|
|
2721
|
+
};
|
|
2722
|
+
const stars = [];
|
|
2723
|
+
for (const match of source.matchAll(/\bexport\s*\*\s*from\s*['"]([^'"]+)['"]/g)) stars.push(await exportIdentity(file, match[1], name, following, seen));
|
|
2724
|
+
const definite = stars.filter((identity) => identity.kind !== "unknown");
|
|
2725
|
+
if (definite.length === 1) return definite[0];
|
|
2726
|
+
if (definite.length > 1) return {
|
|
2727
|
+
kind: "unknown",
|
|
2728
|
+
why: `more than one \`export *\` of '${specifier}' provides '${name}'`
|
|
2729
|
+
};
|
|
2730
|
+
return stars.find((identity) => identity.kind === "unknown") ?? {
|
|
2731
|
+
kind: "unknown",
|
|
2732
|
+
why: `'${specifier}' has no export '${name}' discovery can read`
|
|
2733
|
+
};
|
|
2734
|
+
}
|
|
2735
|
+
/** 1-based line and column of `offset` in `source`. */
|
|
2736
|
+
function locationIn(source, offset) {
|
|
2737
|
+
const before = source.slice(0, offset);
|
|
2738
|
+
const lineStart = before.lastIndexOf("\n") + 1;
|
|
2739
|
+
return {
|
|
2740
|
+
line: before.split("\n").length,
|
|
2741
|
+
column: offset - lineStart + 1
|
|
2742
|
+
};
|
|
2743
|
+
}
|
|
2744
|
+
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;
|
|
2745
|
+
/**
|
|
2746
|
+
* The callees that are Animus's `createSystem` in a parsed system file. A
|
|
2747
|
+
* binding counts once its identity is followed to the factory: a named
|
|
2748
|
+
* import, through local and package re-exports; a namespace import's
|
|
2749
|
+
* member; or a property destructured from a namespace import, `require` or
|
|
2750
|
+
* `import()` of a module. A binding named `createSystem` shown to be
|
|
2751
|
+
* something else is no root. One whose identity discovery cannot follow
|
|
2752
|
+
* keeps the admission spelling gave it, so a resolution limit never drops a
|
|
2753
|
+
* kit. Both are reported. The bare name also anchors, as a global, unless
|
|
2754
|
+
* the file binds it.
|
|
2755
|
+
*/
|
|
2756
|
+
async function systemRoots(systemFilePath, source, imports, following) {
|
|
2757
|
+
const roots = /* @__PURE__ */ new Set();
|
|
2758
|
+
const identityOf = (specifier, name) => exportIdentity(systemFilePath, specifier, name, following, /* @__PURE__ */ new Set());
|
|
2759
|
+
const called = (callee) => new RegExp(`(?<![a-zA-Z0-9_$.])${escapeRegExp$1(callee)}\\s*\\(`).test(source);
|
|
2760
|
+
/** Admits a binding named `createSystem` by its identity: an unknown one
|
|
2761
|
+
* keeps `spellingAdmits`, and every unproven one is reported. */
|
|
2762
|
+
const admit = (local, offset, identity, spellingAdmits) => {
|
|
2763
|
+
if (identity.kind === "animus") {
|
|
2764
|
+
roots.add(local);
|
|
2765
|
+
return;
|
|
2766
|
+
}
|
|
2767
|
+
if (identity.kind === "unknown" && spellingAdmits) roots.add(local);
|
|
2768
|
+
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`;
|
|
2769
|
+
following.report?.({
|
|
2770
|
+
file: systemFilePath,
|
|
2771
|
+
component: local,
|
|
2772
|
+
kind: "warn",
|
|
2773
|
+
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`,
|
|
2774
|
+
code: UNPROVEN_ROOT_BINDING,
|
|
2775
|
+
severity: severityFor(UNPROVEN_ROOT_BINDING),
|
|
2776
|
+
...locationIn(source, offset)
|
|
2777
|
+
});
|
|
2778
|
+
};
|
|
2779
|
+
let bindsName = false;
|
|
2780
|
+
for (const binding of imports) {
|
|
2781
|
+
const named = binding.imported === "createSystem" || binding.local === "createSystem";
|
|
2782
|
+
if (binding.local === "createSystem") bindsName = true;
|
|
2783
|
+
if (named) {
|
|
2784
|
+
const statement = new RegExp(`\\bimport\\b[^;]*?\\bfrom\\s*['"]${escapeRegExp$1(binding.source)}['"]`, "g");
|
|
2785
|
+
let offset = 0;
|
|
2786
|
+
for (const match of source.matchAll(statement)) {
|
|
2787
|
+
const at = match[0].search(new RegExp(`(?<![a-zA-Z0-9_$])${escapeRegExp$1(binding.local)}\\b`));
|
|
2788
|
+
if (at !== -1) {
|
|
2789
|
+
offset = match.index + at;
|
|
2790
|
+
break;
|
|
2791
|
+
}
|
|
2792
|
+
}
|
|
2793
|
+
admit(binding.local, offset, await identityOf(binding.source, binding.imported), binding.imported === "createSystem");
|
|
2794
|
+
continue;
|
|
2795
|
+
}
|
|
2796
|
+
if (binding.source.startsWith(".") && called(binding.local) && (await identityOf(binding.source, binding.imported)).kind === "animus") roots.add(binding.local);
|
|
2797
|
+
}
|
|
2798
|
+
const namespaces = /* @__PURE__ */ new Map();
|
|
2799
|
+
for (const [, namespace, specifier] of source.matchAll(NAMESPACE_IMPORT)) {
|
|
2800
|
+
namespaces.set(namespace, specifier);
|
|
2801
|
+
const member = `${namespace}.createSystem`;
|
|
2802
|
+
if (ANIMUS_SYSTEM_SPECIFIER.test(specifier) || specifier.startsWith(".") && called(member) && (await identityOf(specifier, "createSystem")).kind === "animus") roots.add(member);
|
|
2803
|
+
}
|
|
2804
|
+
const destructuredFrom = (initializer) => {
|
|
2805
|
+
const loaded = /^\(?\s*(?:(?:await\s+)?import|require)\(\s*['"]([^'"]+)['"]\s*\)\s*\)?$/.exec(initializer);
|
|
2806
|
+
if (loaded) return identityOf(loaded[1], "createSystem");
|
|
2807
|
+
const namespace = namespaces.get(initializer);
|
|
2808
|
+
if (namespace !== void 0) return identityOf(namespace, "createSystem");
|
|
2809
|
+
return Promise.resolve({
|
|
2810
|
+
kind: "unknown",
|
|
2811
|
+
why: `it is destructured from '${initializer}', which is not a module namespace`
|
|
2812
|
+
});
|
|
2813
|
+
};
|
|
2814
|
+
for (const match of source.matchAll(/\b(?:const|let|var)\s*\{([^}]*)\}\s*=\s*([^;\n]+)/g)) {
|
|
2815
|
+
const [whole, pattern, initializer] = match;
|
|
2816
|
+
for (const property of pattern.split(",")) {
|
|
2817
|
+
const binding = /^\s*createSystem\s*(?::\s*([a-zA-Z_$][a-zA-Z0-9_$]*))?\s*(?:=[\s\S]*)?$/.exec(property);
|
|
2818
|
+
if (!binding) continue;
|
|
2819
|
+
const local = binding[1] ?? "createSystem";
|
|
2820
|
+
if (local === "createSystem") bindsName = true;
|
|
2821
|
+
admit(local, match.index + whole.indexOf("createSystem"), await destructuredFrom(initializer.trim()), local === "createSystem");
|
|
2822
|
+
}
|
|
2823
|
+
}
|
|
2824
|
+
const declaresName = /\b(?:function\*?|class)\s+createSystem\b|\b(?:const|let|var)\s+createSystem\b/.test(source);
|
|
2825
|
+
if (!bindsName && !declaresName) roots.add("createSystem");
|
|
2826
|
+
return roots;
|
|
2827
|
+
}
|
|
2420
2828
|
/** A pattern matching a call of any of `callees` as a whole name, or null
|
|
2421
2829
|
* when there is none to anchor on. */
|
|
2422
2830
|
function rootCallPattern(callees) {
|
|
2423
2831
|
if (callees.length === 0) return null;
|
|
2424
2832
|
return `(?<![a-zA-Z0-9_$.])(?:${callees.map((callee) => callee.replace(/[.$]/g, "\\$&")).join("|")})`;
|
|
2425
2833
|
}
|
|
2426
|
-
function extractSystemFilePackages(systemFilePath,
|
|
2834
|
+
async function extractSystemFilePackages(systemFilePath, parseModule, report, resolvePackage) {
|
|
2427
2835
|
let source;
|
|
2428
2836
|
try {
|
|
2429
2837
|
source = readFileSync(systemFilePath, "utf-8");
|
|
2430
2838
|
} catch {
|
|
2431
2839
|
return [];
|
|
2432
2840
|
}
|
|
2433
|
-
const parsed =
|
|
2434
|
-
const parsedImports = parsed && parsed.length > 0 ? parsed : null;
|
|
2841
|
+
const parsed = parseModule?.(source, systemFilePath) ?? null;
|
|
2842
|
+
const parsedImports = parsed && parsed.imports.length > 0 ? parsed.imports : null;
|
|
2435
2843
|
let rootCall = "createSystem";
|
|
2436
|
-
if (parsedImports) {
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
...shadowed ? [] : ["createSystem"]
|
|
2442
|
-
])]);
|
|
2443
|
-
}
|
|
2844
|
+
if (parseModule && parsedImports) rootCall = rootCallPattern([...await systemRoots(systemFilePath, source, parsedImports, {
|
|
2845
|
+
parseModule,
|
|
2846
|
+
resolvePackage,
|
|
2847
|
+
report
|
|
2848
|
+
})]);
|
|
2444
2849
|
const identifiers = /* @__PURE__ */ new Set();
|
|
2445
2850
|
const constructorRegex = rootCall === null ? null : new RegExp(`${rootCall}\\s*\\(\\s*\\{[^}]*?\\bincludes\\s*:\\s*\\[([^\\]]*)\\]`, "gs");
|
|
2446
2851
|
const chainRegex = /\.includes\s*\(\s*\[([^\]]*)\]\s*\)/gs;
|
|
@@ -2695,151 +3100,6 @@ function createSourceCorpus(host, ingestor = createSourceIngestor(host)) {
|
|
|
2695
3100
|
};
|
|
2696
3101
|
}
|
|
2697
3102
|
//#endregion
|
|
2698
|
-
//#region pipeline/asset-placeholders.ts
|
|
2699
|
-
const ASSET_PLACEHOLDER_PREFIX = "animus-asset:";
|
|
2700
|
-
const URL_OPEN_RE = /url\(\s*(['"]?)/gi;
|
|
2701
|
-
const IMAGE_SET_OPEN_RE = /(?:-webkit-)?image-set\(/gi;
|
|
2702
|
-
const BARE_SPECIFIER_RE = /^[^'")\s]+/;
|
|
2703
|
-
/** The index of the `)` that closes the `(` at `open`, outside strings. */
|
|
2704
|
-
function closingParen(css, open) {
|
|
2705
|
-
let depth = 0;
|
|
2706
|
-
let quote = null;
|
|
2707
|
-
for (let i = open; i < css.length; i += 1) {
|
|
2708
|
-
const c = css[i];
|
|
2709
|
-
if (quote !== null) {
|
|
2710
|
-
if (c === quote) quote = null;
|
|
2711
|
-
} else if (c === "\"" || c === "'") quote = c;
|
|
2712
|
-
else if (c === "(") depth += 1;
|
|
2713
|
-
else if (c === ")") {
|
|
2714
|
-
depth -= 1;
|
|
2715
|
-
if (depth === 0) return i;
|
|
2716
|
-
}
|
|
2717
|
-
}
|
|
2718
|
-
return css.length;
|
|
2719
|
-
}
|
|
2720
|
-
function quotedReference(css, quoteAt) {
|
|
2721
|
-
const start = quoteAt + 1;
|
|
2722
|
-
if (!css.startsWith("animus-asset:", start)) return null;
|
|
2723
|
-
const end = css.indexOf(css[quoteAt], start);
|
|
2724
|
-
if (end === -1) return null;
|
|
2725
|
-
return {
|
|
2726
|
-
start,
|
|
2727
|
-
end,
|
|
2728
|
-
specifier: css.slice(start + 13, end)
|
|
2729
|
-
};
|
|
2730
|
-
}
|
|
2731
|
-
function assetReferences(css) {
|
|
2732
|
-
if (!css.includes("animus-asset:")) return [];
|
|
2733
|
-
const references = [];
|
|
2734
|
-
for (const open of css.matchAll(URL_OPEN_RE)) {
|
|
2735
|
-
const at = (open.index ?? 0) + open[0].length;
|
|
2736
|
-
if (open[1]) {
|
|
2737
|
-
const reference = quotedReference(css, at - 1);
|
|
2738
|
-
if (reference) references.push(reference);
|
|
2739
|
-
continue;
|
|
2740
|
-
}
|
|
2741
|
-
if (!css.startsWith("animus-asset:", at)) continue;
|
|
2742
|
-
const specifier = BARE_SPECIFIER_RE.exec(css.slice(at + 13))?.[0];
|
|
2743
|
-
if (specifier === void 0) continue;
|
|
2744
|
-
const end = at + 13 + specifier.length;
|
|
2745
|
-
references.push({
|
|
2746
|
-
start: at,
|
|
2747
|
-
end,
|
|
2748
|
-
specifier
|
|
2749
|
-
});
|
|
2750
|
-
}
|
|
2751
|
-
for (const open of css.matchAll(IMAGE_SET_OPEN_RE)) {
|
|
2752
|
-
const paren = (open.index ?? 0) + open[0].length - 1;
|
|
2753
|
-
const close = closingParen(css, paren);
|
|
2754
|
-
let depth = 0;
|
|
2755
|
-
let candidateStart = true;
|
|
2756
|
-
for (let i = paren + 1; i < close; i += 1) {
|
|
2757
|
-
const c = css[i];
|
|
2758
|
-
if (/\s/.test(c)) continue;
|
|
2759
|
-
if (c === "\"" || c === "'") {
|
|
2760
|
-
if (depth === 0 && candidateStart) {
|
|
2761
|
-
const reference = quotedReference(css, i);
|
|
2762
|
-
if (reference) references.push(reference);
|
|
2763
|
-
}
|
|
2764
|
-
const end = css.indexOf(c, i + 1);
|
|
2765
|
-
i = end === -1 ? close : end;
|
|
2766
|
-
} else if (c === "(") depth += 1;
|
|
2767
|
-
else if (c === ")") depth -= 1;
|
|
2768
|
-
else if (c === "," && depth === 0) {
|
|
2769
|
-
candidateStart = true;
|
|
2770
|
-
continue;
|
|
2771
|
-
}
|
|
2772
|
-
candidateStart = false;
|
|
2773
|
-
}
|
|
2774
|
-
}
|
|
2775
|
-
return references.sort((a, b) => a.start - b.start);
|
|
2776
|
-
}
|
|
2777
|
-
function findAssetSpecifiers(css) {
|
|
2778
|
-
return [...new Set(assetReferences(css).map((reference) => reference.specifier))];
|
|
2779
|
-
}
|
|
2780
|
-
function substituteAssetPlaceholders(css, urlBySpecifier) {
|
|
2781
|
-
if (urlBySpecifier.size === 0) return css;
|
|
2782
|
-
let out = css;
|
|
2783
|
-
for (const reference of assetReferences(css).reverse()) {
|
|
2784
|
-
const url = urlBySpecifier.get(reference.specifier);
|
|
2785
|
-
if (url === void 0) continue;
|
|
2786
|
-
out = out.slice(0, reference.start) + url + out.slice(reference.end);
|
|
2787
|
-
}
|
|
2788
|
-
return out;
|
|
2789
|
-
}
|
|
2790
|
-
/**
|
|
2791
|
-
* Specifiers of placeholder text anywhere inside a `url()` or `image-set()`
|
|
2792
|
-
* call, a reference or not: after substitution any of it is a broken load.
|
|
2793
|
-
*/
|
|
2794
|
-
function placeholdersInAssetCalls(css) {
|
|
2795
|
-
if (!css.includes("animus-asset:")) return [];
|
|
2796
|
-
const calls = [...css.matchAll(URL_OPEN_RE), ...css.matchAll(IMAGE_SET_OPEN_RE)].map((open) => {
|
|
2797
|
-
const paren = css.indexOf("(", open.index ?? 0);
|
|
2798
|
-
return [paren, closingParen(css, paren)];
|
|
2799
|
-
});
|
|
2800
|
-
const found = /* @__PURE__ */ new Set();
|
|
2801
|
-
for (let at = css.indexOf(ASSET_PLACEHOLDER_PREFIX); at !== -1; at = css.indexOf(ASSET_PLACEHOLDER_PREFIX, at + 1)) {
|
|
2802
|
-
if (!calls.some(([open, close]) => open < at && at < close)) continue;
|
|
2803
|
-
found.add(/^[^'")\s,]*/.exec(css.slice(at + 13))[0]);
|
|
2804
|
-
}
|
|
2805
|
-
return [...found];
|
|
2806
|
-
}
|
|
2807
|
-
function findSheetAssetSpecifiers(sheets) {
|
|
2808
|
-
return [.../* @__PURE__ */ new Set([
|
|
2809
|
-
...findAssetSpecifiers(sheets.variableCss),
|
|
2810
|
-
...findAssetSpecifiers(sheets.globalCss),
|
|
2811
|
-
...findAssetSpecifiers(sheets.componentCss)
|
|
2812
|
-
])];
|
|
2813
|
-
}
|
|
2814
|
-
/** The one substitution step every emitted sheet passes through. */
|
|
2815
|
-
function substituteSheetAssets(sheets, urlBySpecifier) {
|
|
2816
|
-
return {
|
|
2817
|
-
variableCss: substituteAssetPlaceholders(sheets.variableCss, urlBySpecifier),
|
|
2818
|
-
globalCss: substituteAssetPlaceholders(sheets.globalCss, urlBySpecifier),
|
|
2819
|
-
componentCss: substituteAssetPlaceholders(sheets.componentCss, urlBySpecifier)
|
|
2820
|
-
};
|
|
2821
|
-
}
|
|
2822
|
-
const UNSUBSTITUTED_ASSET_CODE = "animus.asset.unsubstituted-placeholder";
|
|
2823
|
-
/** A placeholder in emitted CSS is a broken URL in the browser: it fails a
|
|
2824
|
-
* strict build and warns otherwise. */
|
|
2825
|
-
function reportSurvivingAssetPlaceholders(css, report) {
|
|
2826
|
-
const specifiers = placeholdersInAssetCalls(css);
|
|
2827
|
-
if (specifiers.length === 0) return;
|
|
2828
|
-
const message = `${report.prefix} asset() placeholders reached ${report.surface ?? "emitted CSS"} unsubstituted: ${specifiers.join(", ")} (${UNSUBSTITUTED_ASSET_CODE})`;
|
|
2829
|
-
if (report.strict) throw new Error(message);
|
|
2830
|
-
report.warn(message);
|
|
2831
|
-
}
|
|
2832
|
-
/**
|
|
2833
|
-
* The runtime code an analysis generates: each dynamic prop config, which the
|
|
2834
|
-
* shared prop map module carries, and each component's replacement. Extraction
|
|
2835
|
-
* lifts asset() out of them into root variables, so none should hold one.
|
|
2836
|
-
* Both embed values as JSON strings, so their quote and backslash escapes are
|
|
2837
|
-
* undone for scanning: the text is for `findAssetSpecifiers`, not to run.
|
|
2838
|
-
*/
|
|
2839
|
-
function generatedModuleCode(manifest) {
|
|
2840
|
-
return [JSON.stringify(manifest.dynamic_props), ...Object.values(manifest.components).map((component) => component.replacement)].join("\n").replace(/\\(["\\])/g, "$1");
|
|
2841
|
-
}
|
|
2842
|
-
//#endregion
|
|
2843
3103
|
//#region pipeline/resolve-asset.ts
|
|
2844
3104
|
/**
|
|
2845
3105
|
* Asset resolution for hosts with no bundler hook. Host path aliases apply
|
|
@@ -3640,9 +3900,12 @@ function hasSourceThemeManifests(system) {
|
|
|
3640
3900
|
const surfacedSystems = /* @__PURE__ */ new WeakSet();
|
|
3641
3901
|
/** The loaded system's own diagnostics, the first time it is analyzed. A
|
|
3642
3902
|
* strict error keeps failing every analysis until the system changes. */
|
|
3643
|
-
function systemDiagnostics(system, strict) {
|
|
3903
|
+
function systemDiagnostics(system, strict, levels) {
|
|
3644
3904
|
const diagnostics = [...collectSelectorAliasDiagnostics(system.selectorAliasesJson), ...systemLoadDiagnostics(system)];
|
|
3645
|
-
const blocking =
|
|
3905
|
+
const blocking = diagnostics.some((d) => effectiveLevel(d, {
|
|
3906
|
+
strict,
|
|
3907
|
+
levels
|
|
3908
|
+
}) === "error");
|
|
3646
3909
|
if (surfacedSystems.has(system) && !blocking) return [];
|
|
3647
3910
|
surfacedSystems.add(system);
|
|
3648
3911
|
return diagnostics;
|
|
@@ -3652,7 +3915,7 @@ function systemDiagnostics(system, strict) {
|
|
|
3652
3915
|
* the call site (strict-mode throw vs warn).
|
|
3653
3916
|
*/
|
|
3654
3917
|
function runProjectAnalysis(engineApi, opts) {
|
|
3655
|
-
const { analyzeProject } = engineApi();
|
|
3918
|
+
const { analyzeProject, diagnosticCodes } = engineApi();
|
|
3656
3919
|
let t = performance.now();
|
|
3657
3920
|
const inputs = buildAnalysisInputs(opts);
|
|
3658
3921
|
const serializeMs = Math.round(performance.now() - t);
|
|
@@ -3673,8 +3936,10 @@ function runProjectAnalysis(engineApi, opts) {
|
|
|
3673
3936
|
surfaceManifestDiagnostics(manifest, opts.warn, {
|
|
3674
3937
|
strict: opts.strict,
|
|
3675
3938
|
info: opts.info,
|
|
3939
|
+
levels: opts.diagnostics,
|
|
3940
|
+
knownCodes: opts.diagnostics ? knownDiagnosticCodes(diagnosticCodes?.()) : void 0,
|
|
3676
3941
|
prepend: [
|
|
3677
|
-
...systemDiagnostics(opts.system, opts.strict),
|
|
3942
|
+
...systemDiagnostics(opts.system, opts.strict, opts.diagnostics),
|
|
3678
3943
|
...propertyDiagnostics,
|
|
3679
3944
|
...opts.extraDiagnostics ?? []
|
|
3680
3945
|
]
|
|
@@ -3940,7 +4205,7 @@ function runStructuralSelfCheck(input) {
|
|
|
3940
4205
|
const failures = [];
|
|
3941
4206
|
if (input.componentCount === 0) failures.push("No component CSS produced — check the system file and its includes list");
|
|
3942
4207
|
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`);
|
|
3943
|
-
for (const { specifier, outcome } of input.externalOutcomes ?? []) if (outcome === "empty") failures.push(`include '${specifier}' resolved but discovered no component sources`);
|
|
4208
|
+
for (const { specifier, outcome } of input.externalOutcomes ?? []) if (outcome === "empty") failures.push(`include '${specifier}' resolved but discovered no component sources [${NO_KIT_FILES}]`);
|
|
3944
4209
|
else if (outcome === "unresolvable") failures.push(`include '${specifier}' could not be resolved`);
|
|
3945
4210
|
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");
|
|
3946
4211
|
if ((input.assembledCss ?? `${input.variableCss}\n${input.globalCss}\n${input.componentCss}`).includes("__TRANSFORM__")) failures.push("Unresolved __TRANSFORM__ placeholders found in CSS output");
|
|
@@ -4107,4 +4372,4 @@ function realpathThroughNearestExistingAncestor(path) {
|
|
|
4107
4372
|
}
|
|
4108
4373
|
}
|
|
4109
4374
|
//#endregion
|
|
4110
|
-
export {
|
|
4375
|
+
export { resolveVerbosity as $, firstOwners as A, substituteSheetAssets as At, isPathWithinRoot as B, resolveThroughPathAliases as C, unresolvedParentName as Ct, excludeCollectedPackages as D, generatedModuleCode as Dt, engineModuleParser as E, findSheetAssetSpecifiers as Et, walkPackageSources as F, assembleStylesheet as Ft, CORE_OPTION_KEYS as G, compareDiscoveryOrder as H, isJsonBlock as I, stripLeadingLayerDeclaration as It, REPLACEABLE_DEFAULT_EXCLUDE as J, DEFAULT_EXCLUDE as K, isJsonString as L, validateLayerOrder as Lt, resolveAbsolutePathSpecifier as M, contentHash as Mt, staleDistIncludesMessage as N, buildAnalyzeProjectArgs as Nt, extractSystemFilePackages as O, reportSurvivingAssetPlaceholders as Ot, unresolvableIncludesMessage as P, ANIMUS_LAYERS as Pt, resolveMode as Q, readTsconfigAliasPairs as R, __require as Rt, resolveAssetFile as S, unreadableSourceDiagnostic as St, collectExternalPackageSources as T, findAssetSpecifiers as Tt, discoverFiles as U, sharesVolumeRoot as V, AnimusConfigError as W, assertKnownOptionKeys as X, STRUCTURAL_EXCLUDE as Y, createExcludeMatcher as Z, applyUnitFallback as _, isDeletedSource as _t, assertNoErrorDiagnostics as a, parseFilesJson as at, buildPathAliasesJson as b, surfaceManifestDiagnostics as bt, runStructuralSelfCheck as c, DEFAULT_EXTENSIONS as ct, serializeStaticCss as d, preprocessMdx as dt, RETIRED_ENGINE_MESSAGE as et, camelToKebab as f, DIAGNOSTIC_LEVELS as ft, runProjectAnalysis as g, VOCABULARY_LEGACY_VERB as gt, clearEngineCache as h, VOCABULARY_COLLISION as ht, snapshotFilePlans as i, isAdvisorySourceDiagnostic as it, isExcludedPackageRelativePath as j, parseInternalWire as jt, findPackageRoot as k, substituteAssetPlaceholders as kt, loadSystemConfig as l, ENGINE_TRANSFORM_EXTENSIONS as lt, buildAnalysisInputs as m, INVALID_PROPERTY_REGISTRATION as mt, diffFilePlans as n, createV2EngineApi as nt, buildSystemPropsModule as o, projectExternalFileOwners as ot, stableStringify as p, DiagnosticFailure as pt, DRIVER_NAMESPACES as q, hashReplacementPlans as r, ingestSourceEntries as rt, buildDynamicPropConfig as s, adaptSvelteSource as st, toWatchKeys as t, assertNoRetiredEngineSelection as tt, applyPrefix as u, isEngineTransformExtension as ut, postProcessCss as v, isUnresolvedParentDrop as vt, createSourceCorpus as w, vocabularyWitnessDiagnostics as wt, enforceExternalTokenContracts as x, systemLoadDiagnostics as xt, resolveLightningTargets as y, noKitFilesDiagnostics as yt, createSourceIdentity as z };
|