@animus-ui/extract 0.1.22 → 0.1.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/crates/extract-v2/animus-extract-v2.darwin-arm64.node +0 -0
- package/crates/extract-v2/animus-extract-v2.linux-arm64-gnu.node +0 -0
- package/crates/extract-v2/animus-extract-v2.linux-x64-gnu.node +0 -0
- package/crates/extract-v2/index.d.ts +16 -3
- package/dist/index.cjs +14 -2
- package/dist/index.mjs +2 -2
- package/dist/pipeline/analyze-project-args.d.ts +5 -1
- package/dist/pipeline/analyze-project-args.d.ts.map +1 -1
- package/dist/pipeline/asset-placeholders.d.ts +4 -1
- package/dist/pipeline/asset-placeholders.d.ts.map +1 -1
- package/dist/pipeline/core-options.d.ts +15 -3
- package/dist/pipeline/core-options.d.ts.map +1 -1
- package/dist/pipeline/correlate-external-tokens.d.ts +3 -0
- package/dist/pipeline/correlate-external-tokens.d.ts.map +1 -1
- package/dist/pipeline/discover-packages.d.ts +105 -7
- package/dist/pipeline/discover-packages.d.ts.map +1 -1
- package/dist/pipeline/dynamic-prop-config.d.ts +13 -2
- package/dist/pipeline/dynamic-prop-config.d.ts.map +1 -1
- package/dist/pipeline/engine-adapter.d.ts +5 -1
- package/dist/pipeline/engine-adapter.d.ts.map +1 -1
- package/dist/pipeline/index.d.ts +7 -4
- package/dist/pipeline/index.d.ts.map +1 -1
- package/dist/pipeline/kit-descriptor.d.ts +44 -0
- package/dist/pipeline/kit-descriptor.d.ts.map +1 -0
- package/dist/pipeline/kit-publication.d.ts +15 -0
- package/dist/pipeline/kit-publication.d.ts.map +1 -0
- package/dist/pipeline/manifest-diagnostics.d.ts +64 -5
- package/dist/pipeline/manifest-diagnostics.d.ts.map +1 -1
- package/dist/pipeline/manifest-schema.d.ts +4 -0
- package/dist/pipeline/manifest-schema.d.ts.map +1 -1
- package/dist/pipeline/prefix.d.ts +14 -3
- package/dist/pipeline/prefix.d.ts.map +1 -1
- package/dist/pipeline/run-analysis.d.ts +16 -2
- package/dist/pipeline/run-analysis.d.ts.map +1 -1
- package/dist/pipeline/source-ingestion.d.ts +13 -2
- package/dist/pipeline/source-ingestion.d.ts.map +1 -1
- package/dist/pipeline/structural-self-check.d.ts.map +1 -1
- package/dist/pipeline/system-config.d.ts +6 -3
- package/dist/pipeline/system-config.d.ts.map +1 -1
- package/dist/{pipeline-BXY7Pf_D.mjs → pipeline-Bl20tbu_.mjs} +1484 -546
- package/dist/{pipeline-D7B1z0-1.cjs → pipeline-CAH9mxnK.cjs} +1602 -591
- package/dist/session/extraction-session.d.ts +17 -1
- package/dist/session/extraction-session.d.ts.map +1 -1
- package/dist/session.cjs +60 -17
- package/dist/session.mjs +60 -17
- package/package.json +4 -4
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import { createRequire } from "node:module";
|
|
2
|
-
import path, { basename, dirname, join, normalize, posix } from "node:path";
|
|
1
|
+
import { createRequire, isBuiltin } from "node:module";
|
|
2
|
+
import path, { basename, dirname, extname, isAbsolute, join, normalize, posix, relative, resolve, sep } from "node:path";
|
|
3
3
|
import { createHash } from "crypto";
|
|
4
4
|
import { existsSync, readFileSync, readdirSync, realpathSync, statSync } from "fs";
|
|
5
|
-
import { dirname as dirname$1, extname, isAbsolute, join as join$1, relative, resolve, sep } from "path";
|
|
6
|
-
import { realpathSync as realpathSync$1 } from "node:fs";
|
|
5
|
+
import { dirname as dirname$1, extname as extname$1, isAbsolute as isAbsolute$1, join as join$1, relative as relative$1, resolve as resolve$1, sep as sep$1 } from "path";
|
|
6
|
+
import { existsSync as existsSync$1, readFileSync as readFileSync$1, readdirSync as readdirSync$1, realpathSync as realpathSync$1, statSync as statSync$1 } from "node:fs";
|
|
7
7
|
import { createRequire as createRequire$1 } from "module";
|
|
8
8
|
import browserslist from "browserslist";
|
|
9
9
|
import { browserslistToTargets, transform } from "lightningcss";
|
|
10
10
|
import { componentValues, dependsOnContext, foldInitialValue, identifierAt, isSpace, isUnitlessProperty, someValue, substitutesValue, tokenize, variableReads } from "@animus-ui/properties";
|
|
11
|
+
import { spawnSync } from "node:child_process";
|
|
11
12
|
//#region \0rolldown/runtime.js
|
|
12
13
|
var __require = /* #__PURE__ */ (() => createRequire(import.meta.url))();
|
|
13
14
|
//#endregion
|
|
@@ -90,8 +91,13 @@ function buildAnalyzeProjectArgs(inputs) {
|
|
|
90
91
|
inputs.transformSourcesJson,
|
|
91
92
|
inputs.transformProvenanceJson ?? null
|
|
92
93
|
];
|
|
93
|
-
|
|
94
|
-
|
|
94
|
+
const tail = [
|
|
95
|
+
inputs.declarationScalesJson,
|
|
96
|
+
inputs.analysisContextJson,
|
|
97
|
+
inputs.generatedNamesJson
|
|
98
|
+
];
|
|
99
|
+
const last = tail.findLastIndex((slot) => slot !== void 0 && slot !== "");
|
|
100
|
+
for (const slot of tail.slice(0, last + 1)) args.push(slot || void 0);
|
|
95
101
|
return args;
|
|
96
102
|
}
|
|
97
103
|
//#endregion
|
|
@@ -111,7 +117,180 @@ function parseInternalWire(json, wire) {
|
|
|
111
117
|
return parsed;
|
|
112
118
|
}
|
|
113
119
|
//#endregion
|
|
120
|
+
//#region pipeline/asset-placeholders.ts
|
|
121
|
+
const ASSET_PLACEHOLDER_PREFIX = "animus-asset:";
|
|
122
|
+
const URL_OPEN_RE = /url\(\s*(['"]?)/gi;
|
|
123
|
+
const IMAGE_SET_OPEN_RE = /(?:-webkit-)?image-set\(/gi;
|
|
124
|
+
const BARE_SPECIFIER_RE = /^[^'")\s]+/;
|
|
125
|
+
/** The index of the `)` that closes the `(` at `open`, outside strings. */
|
|
126
|
+
function closingParen(css, open) {
|
|
127
|
+
let depth = 0;
|
|
128
|
+
let quote = null;
|
|
129
|
+
for (let i = open; i < css.length; i += 1) {
|
|
130
|
+
const c = css[i];
|
|
131
|
+
if (quote !== null) {
|
|
132
|
+
if (c === quote) quote = null;
|
|
133
|
+
} else if (c === "\"" || c === "'") quote = c;
|
|
134
|
+
else if (c === "(") depth += 1;
|
|
135
|
+
else if (c === ")") {
|
|
136
|
+
depth -= 1;
|
|
137
|
+
if (depth === 0) return i;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return css.length;
|
|
141
|
+
}
|
|
142
|
+
function quotedReference(css, quoteAt) {
|
|
143
|
+
const start = quoteAt + 1;
|
|
144
|
+
if (!css.startsWith("animus-asset:", start)) return null;
|
|
145
|
+
const end = css.indexOf(css[quoteAt], start);
|
|
146
|
+
if (end === -1) return null;
|
|
147
|
+
return {
|
|
148
|
+
start,
|
|
149
|
+
end,
|
|
150
|
+
specifier: css.slice(start + 13, end)
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
function assetReferences(css) {
|
|
154
|
+
if (!css.includes("animus-asset:")) return [];
|
|
155
|
+
const references = [];
|
|
156
|
+
for (const open of css.matchAll(URL_OPEN_RE)) {
|
|
157
|
+
const at = (open.index ?? 0) + open[0].length;
|
|
158
|
+
if (open[1]) {
|
|
159
|
+
const reference = quotedReference(css, at - 1);
|
|
160
|
+
if (reference) references.push(reference);
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
if (!css.startsWith("animus-asset:", at)) continue;
|
|
164
|
+
const specifier = BARE_SPECIFIER_RE.exec(css.slice(at + 13))?.[0];
|
|
165
|
+
if (specifier === void 0) continue;
|
|
166
|
+
const end = at + 13 + specifier.length;
|
|
167
|
+
references.push({
|
|
168
|
+
start: at,
|
|
169
|
+
end,
|
|
170
|
+
specifier
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
for (const open of css.matchAll(IMAGE_SET_OPEN_RE)) {
|
|
174
|
+
const paren = (open.index ?? 0) + open[0].length - 1;
|
|
175
|
+
const close = closingParen(css, paren);
|
|
176
|
+
let depth = 0;
|
|
177
|
+
let candidateStart = true;
|
|
178
|
+
for (let i = paren + 1; i < close; i += 1) {
|
|
179
|
+
const c = css[i];
|
|
180
|
+
if (/\s/.test(c)) continue;
|
|
181
|
+
if (c === "\"" || c === "'") {
|
|
182
|
+
if (depth === 0 && candidateStart) {
|
|
183
|
+
const reference = quotedReference(css, i);
|
|
184
|
+
if (reference) references.push(reference);
|
|
185
|
+
}
|
|
186
|
+
const end = css.indexOf(c, i + 1);
|
|
187
|
+
i = end === -1 ? close : end;
|
|
188
|
+
} else if (c === "(") depth += 1;
|
|
189
|
+
else if (c === ")") depth -= 1;
|
|
190
|
+
else if (c === "," && depth === 0) {
|
|
191
|
+
candidateStart = true;
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
candidateStart = false;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return references.sort((a, b) => a.start - b.start);
|
|
198
|
+
}
|
|
199
|
+
function findAssetSpecifiers(css) {
|
|
200
|
+
return [...new Set(assetReferences(css).map((reference) => reference.specifier))];
|
|
201
|
+
}
|
|
202
|
+
function substituteAssetPlaceholders(css, urlBySpecifier) {
|
|
203
|
+
if (urlBySpecifier.size === 0) return css;
|
|
204
|
+
let out = css;
|
|
205
|
+
for (const reference of assetReferences(css).reverse()) {
|
|
206
|
+
const url = urlBySpecifier.get(reference.specifier);
|
|
207
|
+
if (url === void 0) continue;
|
|
208
|
+
out = out.slice(0, reference.start) + url + out.slice(reference.end);
|
|
209
|
+
}
|
|
210
|
+
return out;
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Specifiers of placeholder text anywhere inside a `url()` or `image-set()`
|
|
214
|
+
* call, a reference or not: after substitution any of it is a broken load.
|
|
215
|
+
*/
|
|
216
|
+
function placeholdersInAssetCalls(css) {
|
|
217
|
+
if (!css.includes("animus-asset:")) return [];
|
|
218
|
+
const calls = [...css.matchAll(URL_OPEN_RE), ...css.matchAll(IMAGE_SET_OPEN_RE)].map((open) => {
|
|
219
|
+
const paren = css.indexOf("(", open.index ?? 0);
|
|
220
|
+
return [paren, closingParen(css, paren)];
|
|
221
|
+
});
|
|
222
|
+
const found = /* @__PURE__ */ new Set();
|
|
223
|
+
for (let at = css.indexOf(ASSET_PLACEHOLDER_PREFIX); at !== -1; at = css.indexOf(ASSET_PLACEHOLDER_PREFIX, at + 1)) {
|
|
224
|
+
if (!calls.some(([open, close]) => open < at && at < close)) continue;
|
|
225
|
+
found.add(/^[^'")\s,]*/.exec(css.slice(at + 13))[0]);
|
|
226
|
+
}
|
|
227
|
+
return [...found];
|
|
228
|
+
}
|
|
229
|
+
function findSheetAssetSpecifiers(sheets) {
|
|
230
|
+
return [.../* @__PURE__ */ new Set([
|
|
231
|
+
...findAssetSpecifiers(sheets.variableCss),
|
|
232
|
+
...findAssetSpecifiers(sheets.globalCss),
|
|
233
|
+
...findAssetSpecifiers(sheets.componentCss)
|
|
234
|
+
])];
|
|
235
|
+
}
|
|
236
|
+
/** The one substitution step every emitted sheet passes through. */
|
|
237
|
+
function substituteSheetAssets(sheets, urlBySpecifier) {
|
|
238
|
+
return {
|
|
239
|
+
variableCss: substituteAssetPlaceholders(sheets.variableCss, urlBySpecifier),
|
|
240
|
+
globalCss: substituteAssetPlaceholders(sheets.globalCss, urlBySpecifier),
|
|
241
|
+
componentCss: substituteAssetPlaceholders(sheets.componentCss, urlBySpecifier)
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
const UNSUBSTITUTED_ASSET_CODE = "animus.asset.unsubstituted-placeholder";
|
|
245
|
+
/** A placeholder in emitted CSS is a broken URL in the browser: it fails a
|
|
246
|
+
* strict build, strict development reports it, and it warns otherwise. */
|
|
247
|
+
function reportSurvivingAssetPlaceholders(css, report) {
|
|
248
|
+
const specifiers = placeholdersInAssetCalls(css);
|
|
249
|
+
if (specifiers.length === 0) return;
|
|
250
|
+
const message = `${report.prefix} asset() placeholders reached ${report.surface ?? "emitted CSS"} unsubstituted: ${specifiers.join(", ")} (${UNSUBSTITUTED_ASSET_CODE})`;
|
|
251
|
+
if (!report.strict) {
|
|
252
|
+
report.warn(message);
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
if (!report.reportErrors) throw new Error(message);
|
|
256
|
+
report.reportErrors(message);
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* The runtime code an analysis generates: each dynamic prop config, which the
|
|
260
|
+
* shared prop map module carries, and each component's replacement. Extraction
|
|
261
|
+
* lifts asset() out of them into root variables, so none should hold one.
|
|
262
|
+
* Both embed values as JSON strings, so their quote and backslash escapes are
|
|
263
|
+
* undone for scanning: the text is for `findAssetSpecifiers`, not to run.
|
|
264
|
+
*/
|
|
265
|
+
function generatedModuleCode(manifest) {
|
|
266
|
+
return [JSON.stringify(manifest.dynamic_props), ...Object.values(manifest.components).map((component) => component.replacement)].join("\n").replace(/\\(["\\])/g, "$1");
|
|
267
|
+
}
|
|
268
|
+
//#endregion
|
|
114
269
|
//#region pipeline/manifest-diagnostics.ts
|
|
270
|
+
/** `file`, then its line and column where the record has them. */
|
|
271
|
+
function locationOf(diagnostic) {
|
|
272
|
+
if (diagnostic.line === void 0) return diagnostic.file;
|
|
273
|
+
if (diagnostic.column === void 0) return `${diagnostic.file}:${diagnostic.line}`;
|
|
274
|
+
return `${diagnostic.file}:${diagnostic.line}:${diagnostic.column}`;
|
|
275
|
+
}
|
|
276
|
+
/** A located record's `file:line:column: `; nothing for one without a line,
|
|
277
|
+
* whose message names its file. */
|
|
278
|
+
function locatedPrefix(diagnostic) {
|
|
279
|
+
return diagnostic.line === void 0 ? "" : `${locationOf(diagnostic)}: `;
|
|
280
|
+
}
|
|
281
|
+
/** What the record dropped, when its message does not already quote it. */
|
|
282
|
+
function droppedSuffix(diagnostic) {
|
|
283
|
+
return diagnostic.dropped && !diagnostic.message.includes(diagnostic.dropped) ? ` — dropped: ${diagnostic.dropped}` : "";
|
|
284
|
+
}
|
|
285
|
+
/** The record's code, when its message does not already quote it. */
|
|
286
|
+
function codeSuffix(diagnostic) {
|
|
287
|
+
return diagnostic.code && !diagnostic.message.includes(diagnostic.code) ? ` [${diagnostic.code}]` : "";
|
|
288
|
+
}
|
|
289
|
+
/** A warn-kind record's line after its mark:
|
|
290
|
+
* `file:line:column: component: message [code]`. */
|
|
291
|
+
function warnLine(diagnostic) {
|
|
292
|
+
return `${locationOf(diagnostic)}: ${diagnostic.component}: ${diagnostic.message}${droppedSuffix(diagnostic)}${codeSuffix(diagnostic)}`;
|
|
293
|
+
}
|
|
115
294
|
/** Stable code for selector forms with no substitutable subject; the
|
|
116
295
|
* extraction engine mints the same string. */
|
|
117
296
|
const SELECTOR_UNSUPPORTED_SUBJECT = "animus.selector.unsupported-subject";
|
|
@@ -124,6 +303,81 @@ function isUnresolvedParentDrop(diagnostic) {
|
|
|
124
303
|
function unresolvedParentName(diagnostic) {
|
|
125
304
|
return UNRESOLVED_PARENT_RE.exec(diagnostic.message)?.[1] ?? null;
|
|
126
305
|
}
|
|
306
|
+
const DIAGNOSTIC_LEVELS = /* @__PURE__ */ new Set([
|
|
307
|
+
"off",
|
|
308
|
+
"info",
|
|
309
|
+
"warn",
|
|
310
|
+
"error"
|
|
311
|
+
]);
|
|
312
|
+
/** A key that selects every record of a kind: `kind:bail`, `kind:skip`,
|
|
313
|
+
* `kind:warn` or `kind:error`. */
|
|
314
|
+
const KIND_KEY = "kind:";
|
|
315
|
+
const DIAGNOSTIC_KINDS = /* @__PURE__ */ new Set([
|
|
316
|
+
"bail",
|
|
317
|
+
"skip",
|
|
318
|
+
"warn",
|
|
319
|
+
"error"
|
|
320
|
+
]);
|
|
321
|
+
/** The level `levels` sets for `code`: its exact entry, else the entry of
|
|
322
|
+
* the longest prefix it starts with. */
|
|
323
|
+
function codeLevel(code, levels) {
|
|
324
|
+
if (code === void 0) return void 0;
|
|
325
|
+
if (Object.hasOwn(levels, code)) return levels[code];
|
|
326
|
+
let longest;
|
|
327
|
+
for (const [key, level] of Object.entries(levels)) {
|
|
328
|
+
if (!key.endsWith(".*")) continue;
|
|
329
|
+
const prefix = key.slice(0, -1);
|
|
330
|
+
if (code.startsWith(prefix) && prefix.length > (longest?.length ?? -1)) longest = {
|
|
331
|
+
length: prefix.length,
|
|
332
|
+
level
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
return longest?.level;
|
|
336
|
+
}
|
|
337
|
+
/** The level `levels` sets for a record: its code's level, else its kind's. */
|
|
338
|
+
function levelFor({ code, kind }, levels) {
|
|
339
|
+
if (levels === void 0) return void 0;
|
|
340
|
+
const kindKey = `${KIND_KEY}${kind}`;
|
|
341
|
+
return codeLevel(code, levels) ?? (Object.hasOwn(levels, kindKey) ? levels[kindKey] : void 0);
|
|
342
|
+
}
|
|
343
|
+
/** The level a record prints at: the option's entry for its code, else
|
|
344
|
+
* `error` for a hard error (an `error`-kind record) or an error-severity
|
|
345
|
+
* record under `strict`, else its own. */
|
|
346
|
+
function effectiveLevel(diagnostic, policy) {
|
|
347
|
+
const level = levelFor(diagnostic, policy.levels);
|
|
348
|
+
if (level !== void 0) return level;
|
|
349
|
+
if (diagnostic.kind === "error") return "error";
|
|
350
|
+
if (diagnostic.severity === "info") return "info";
|
|
351
|
+
return policy.strict && diagnostic.severity === "error" ? "error" : "warn";
|
|
352
|
+
}
|
|
353
|
+
/** Every code Animus is known to report: the engine's table, as the native
|
|
354
|
+
* module publishes it (`diagnosticCodes()`), the pipeline's, and the asset
|
|
355
|
+
* code. The system package mints vocabulary codes at run time, so a valid
|
|
356
|
+
* code can be missing. */
|
|
357
|
+
function knownDiagnosticCodes(engineCodesJson) {
|
|
358
|
+
const engineCodes = engineCodesJson ? Object.keys(parseInternalWire(engineCodesJson, "diagnosticCodes (the engine code table)")) : [];
|
|
359
|
+
return /* @__PURE__ */ new Set([
|
|
360
|
+
...engineCodes,
|
|
361
|
+
...DIAGNOSTIC_SEVERITY.keys(),
|
|
362
|
+
UNSUBSTITUTED_ASSET_CODE
|
|
363
|
+
]);
|
|
364
|
+
}
|
|
365
|
+
/** The keys of `levels` that name no known code, exactly or as a prefix. */
|
|
366
|
+
function unknownDiagnosticKeys(levels, known) {
|
|
367
|
+
return Object.keys(levels).filter((key) => {
|
|
368
|
+
if (key.startsWith(KIND_KEY)) return !DIAGNOSTIC_KINDS.has(key.slice(5));
|
|
369
|
+
if (!key.endsWith(".*")) return !known.has(key);
|
|
370
|
+
const prefix = key.slice(0, -1);
|
|
371
|
+
return ![...known].some((code) => code.startsWith(prefix));
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
/** The failure of diagnostics at `error` level, from `strict` or the
|
|
375
|
+
* `diagnostics` option: a host fails the build on it whatever its own
|
|
376
|
+
* strictness. It prints as the plain `Error` strict always threw. */
|
|
377
|
+
var DiagnosticFailure = class extends Error {};
|
|
378
|
+
/** Level options already checked against the known codes: one warning per
|
|
379
|
+
* unknown key, however often a host analyzes. */
|
|
380
|
+
const checkedLevels = /* @__PURE__ */ new WeakSet();
|
|
127
381
|
/** True when the selector carries at least one substitutable `&` subject
|
|
128
382
|
* outside quoted text. */
|
|
129
383
|
function hasSelectorSubject(value) {
|
|
@@ -166,6 +420,22 @@ function collectSelectorAliasDiagnostics(selectorAliasesJson) {
|
|
|
166
420
|
const UNREADABLE_SOURCE_FILE = "animus.ingestion.unreadable-source-file";
|
|
167
421
|
/** A kit the system extends resolved, yet discovery found none of its files. */
|
|
168
422
|
const NO_KIT_FILES = "animus.discovery.no-kit-files";
|
|
423
|
+
/** A kit with no `animus` export condition: discovery guesses its source
|
|
424
|
+
* from `src/`. */
|
|
425
|
+
const KIT_WITHOUT_SOURCE_CONDITION = "animus.discovery.kit-without-source-condition";
|
|
426
|
+
/** A kit's `animus` export condition names a file that is missing or outside
|
|
427
|
+
* its package, so that entry is not read from source. */
|
|
428
|
+
const INVALID_KIT_SOURCE_CONDITION = "animus.discovery.invalid-source-condition";
|
|
429
|
+
/** A kit the application imports whose system the application's system
|
|
430
|
+
* does not include: its components are not extracted. */
|
|
431
|
+
const KIT_SYSTEM_NOT_INCLUDED = "animus.kit.system-not-included";
|
|
432
|
+
/** A system file's `createSystem` binding that discovery could not follow to
|
|
433
|
+
* Animus's factory, so it is no root. */
|
|
434
|
+
const UNPROVEN_ROOT_BINDING = "animus.discovery.unproven-root-binding";
|
|
435
|
+
/** A system file calls `createSystem` with no import or local binding. The
|
|
436
|
+
* system loader evaluates the file without auto-imports, where the name is
|
|
437
|
+
* undefined, so the call is no root. */
|
|
438
|
+
const UNIMPORTED_CREATE_SYSTEM = "animus.discovery.unimported-create-system";
|
|
169
439
|
/** What a skipped source file costs: the analysis never sees what it
|
|
170
440
|
* renders. */
|
|
171
441
|
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";
|
|
@@ -201,6 +471,11 @@ function noKitFilesDiagnostics(outcomes) {
|
|
|
201
471
|
severity: severityFor(NO_KIT_FILES)
|
|
202
472
|
}));
|
|
203
473
|
}
|
|
474
|
+
/** A kit's descriptor declares a format this Animus does not read. */
|
|
475
|
+
const KIT_UNSUPPORTED_FORMAT = "animus.kit.unsupported-format";
|
|
476
|
+
/** A kit's descriptor records a definition its shipped source no longer
|
|
477
|
+
* declares. */
|
|
478
|
+
const KIT_STALE_DESCRIPTOR = "animus.kit.stale-descriptor";
|
|
204
479
|
/** The collision entry code minted by the system package's merge. */
|
|
205
480
|
const VOCABULARY_COLLISION = "animus.vocabulary.collision";
|
|
206
481
|
/** A theme `@property` registration browsers would ignore; it is not emitted. */
|
|
@@ -226,9 +501,6 @@ const PROPERTY_FALLBACK_SELF_REFERENCE = "animus.property.fallback-self-referenc
|
|
|
226
501
|
* another directly, so every property in the cycle is invalid at
|
|
227
502
|
* computed-value time. */
|
|
228
503
|
const PROPERTY_CURRENT_VAR_CYCLE = "animus.property.current-var-cycle";
|
|
229
|
-
/** A prefix renamed contextual variables without `prefixContextualVars`, so
|
|
230
|
-
* their declared names no longer resolve. */
|
|
231
|
-
const PREFIX_CONTEXTUAL_VARS_UNPREFIXED = "animus.prefix.contextual-vars-unprefixed";
|
|
232
504
|
/** Under `prefixContextualVars`, a final name that collides with a runtime
|
|
233
505
|
* transport variable or a theme variable of the same spelling. */
|
|
234
506
|
const PREFIX_NAME_CONFLICT = "animus.prefix.name-conflict";
|
|
@@ -242,6 +514,13 @@ const DIAGNOSTIC_SEVERITY = /* @__PURE__ */ new Map([
|
|
|
242
514
|
[SELECTOR_UNSUPPORTED_SUBJECT, "error"],
|
|
243
515
|
[UNREADABLE_SOURCE_FILE, "error"],
|
|
244
516
|
[NO_KIT_FILES, "warn"],
|
|
517
|
+
[KIT_WITHOUT_SOURCE_CONDITION, "warn"],
|
|
518
|
+
[INVALID_KIT_SOURCE_CONDITION, "error"],
|
|
519
|
+
[KIT_SYSTEM_NOT_INCLUDED, "error"],
|
|
520
|
+
[KIT_UNSUPPORTED_FORMAT, "error"],
|
|
521
|
+
[KIT_STALE_DESCRIPTOR, "warn"],
|
|
522
|
+
[UNPROVEN_ROOT_BINDING, "warn"],
|
|
523
|
+
[UNIMPORTED_CREATE_SYSTEM, "warn"],
|
|
245
524
|
[VOCABULARY_COLLISION, "warn"],
|
|
246
525
|
[VOCABULARY_LEGACY_VERB, "warn"],
|
|
247
526
|
[INVALID_PROPERTY_REGISTRATION, "error"],
|
|
@@ -251,7 +530,6 @@ const DIAGNOSTIC_SEVERITY = /* @__PURE__ */ new Map([
|
|
|
251
530
|
[PROPERTY_SELF_REFERENCE, "warn"],
|
|
252
531
|
[PROPERTY_FALLBACK_SELF_REFERENCE, "warn"],
|
|
253
532
|
[PROPERTY_CURRENT_VAR_CYCLE, "warn"],
|
|
254
|
-
[PREFIX_CONTEXTUAL_VARS_UNPREFIXED, "warn"],
|
|
255
533
|
[PREFIX_NAME_CONFLICT, "error"],
|
|
256
534
|
[PROPERTY_LEGACY_TOKEN_COLLISION, "warn"],
|
|
257
535
|
[PROPERTY_TOKEN_COLLISION, "error"]
|
|
@@ -306,15 +584,6 @@ function systemLoadDiagnostics(system) {
|
|
|
306
584
|
code: INVALID_PROPERTY_REGISTRATION,
|
|
307
585
|
severity: severityFor(INVALID_PROPERTY_REGISTRATION)
|
|
308
586
|
}));
|
|
309
|
-
const prefixed = system.legacyPrefixedContextualVars ?? [];
|
|
310
|
-
const unprefixed = prefixed.length === 0 ? [] : [{
|
|
311
|
-
file: "system",
|
|
312
|
-
component: "prefix",
|
|
313
|
-
kind: "warn",
|
|
314
|
-
message: `the prefix renames the contextual variables ${prefixed.join(", ")}, but their declared names are still read and written as written, so scale reads of them fail and their writes miss. Set prefixContextualVars: true to resolve them under the prefix (${PREFIX_CONTEXTUAL_VARS_UNPREFIXED})`,
|
|
315
|
-
code: PREFIX_CONTEXTUAL_VARS_UNPREFIXED,
|
|
316
|
-
severity: severityFor(PREFIX_CONTEXTUAL_VARS_UNPREFIXED)
|
|
317
|
-
}];
|
|
318
587
|
const conflicts = (system.prefixNameConflicts ?? []).map(({ name, final, reason }) => ({
|
|
319
588
|
file: "system",
|
|
320
589
|
component: `--${name}`,
|
|
@@ -326,7 +595,6 @@ function systemLoadDiagnostics(system) {
|
|
|
326
595
|
return [
|
|
327
596
|
...vocabularyWitnessDiagnostics(system.vocabularyWitnessesJson),
|
|
328
597
|
...registrations,
|
|
329
|
-
...unprefixed,
|
|
330
598
|
...conflicts,
|
|
331
599
|
...tokenCollisionDiagnostics(system)
|
|
332
600
|
];
|
|
@@ -362,29 +630,44 @@ function tokenCollisionDiagnostics(system) {
|
|
|
362
630
|
}
|
|
363
631
|
/**
|
|
364
632
|
* The single strict-escalation policy point for both extraction plugins:
|
|
365
|
-
* error-severity diagnostics throw together under `strict`, else warn.
|
|
633
|
+
* error-severity diagnostics throw together under `strict`, else warn. A
|
|
634
|
+
* development session reports them through `reportErrors` instead.
|
|
366
635
|
*/
|
|
367
636
|
function surfaceManifestDiagnostics(manifest, warn, policy = {}) {
|
|
368
|
-
const errors =
|
|
637
|
+
const errors = /* @__PURE__ */ new Set();
|
|
638
|
+
if (policy.levels && policy.knownCodes && !checkedLevels.has(policy.levels)) {
|
|
639
|
+
checkedLevels.add(policy.levels);
|
|
640
|
+
for (const key of unknownDiagnosticKeys(policy.levels, policy.knownCodes)) warn(key.startsWith(KIND_KEY) ? `⚠ diagnostics option: '${key}' names no diagnostic kind — use kind:bail, kind:skip, kind:warn or kind:error` : `⚠ diagnostics option: '${key}' matches no Animus diagnostic code — check its spelling (a code the system package mints at run time is known only once it is reported)`);
|
|
641
|
+
}
|
|
369
642
|
const diagnostics = policy.prepend?.length ? [...policy.prepend, ...manifest.diagnostics ?? []] : manifest.diagnostics ?? [];
|
|
370
643
|
for (const diagnostic of diagnostics) {
|
|
644
|
+
const level = effectiveLevel(diagnostic, policy);
|
|
645
|
+
if (level === "off") continue;
|
|
371
646
|
let line = null;
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
647
|
+
const mark = level === "info" ? "ℹ" : "⚠";
|
|
648
|
+
const message = `${diagnostic.message}${droppedSuffix(diagnostic)}`;
|
|
649
|
+
if (diagnostic.kind === "bail") line = `${mark} ${locatedPrefix(diagnostic)}${diagnostic.component} not extracted: ${message}${codeSuffix(diagnostic)}`;
|
|
650
|
+
else if (diagnostic.kind === "skip") line = `${mark} ${locatedPrefix(diagnostic)}${diagnostic.component}: skipped ${message}${codeSuffix(diagnostic)}`;
|
|
651
|
+
else if (diagnostic.kind === "warn" || diagnostic.kind === "error") line = `${mark} ${warnLine(diagnostic)}`;
|
|
375
652
|
if (line === null) continue;
|
|
376
|
-
if (
|
|
377
|
-
if (diagnostic.severity === "info") {
|
|
653
|
+
if (level === "info") {
|
|
378
654
|
policy.info?.(line);
|
|
379
655
|
continue;
|
|
380
656
|
}
|
|
381
|
-
if (
|
|
382
|
-
|
|
657
|
+
if (level === "error") {
|
|
658
|
+
const subject = diagnostic.kind === "warn" || diagnostic.kind === "error" ? `${locationOf(diagnostic)}: ` : locatedPrefix(diagnostic);
|
|
659
|
+
errors.add(`${diagnostic.code ?? "error"} — ${subject}${diagnostic.component}: ${message}`);
|
|
383
660
|
continue;
|
|
384
661
|
}
|
|
385
662
|
warn(line);
|
|
386
663
|
}
|
|
387
|
-
if (errors.
|
|
664
|
+
if (errors.size === 0) return;
|
|
665
|
+
const message = `[animus] strict: ${errors.size} error diagnostic(s):\n${[...errors].join("\n")}`;
|
|
666
|
+
if (policy.reportErrors) {
|
|
667
|
+
policy.reportErrors(message);
|
|
668
|
+
return;
|
|
669
|
+
}
|
|
670
|
+
throw new DiagnosticFailure(message);
|
|
388
671
|
}
|
|
389
672
|
//#endregion
|
|
390
673
|
//#region pipeline/mdx-preprocessor.ts
|
|
@@ -1036,17 +1319,17 @@ const NODE_NEXT_EXTENSION_MAP = {
|
|
|
1036
1319
|
".mjs": [".mts"],
|
|
1037
1320
|
".cjs": [".cts"]
|
|
1038
1321
|
};
|
|
1039
|
-
|
|
1040
|
-
|
|
1322
|
+
/** The paths a relative specifier can name from `importerPath`, in probe
|
|
1323
|
+
* order: as written with each source suffix, then a NodeNext emitted
|
|
1324
|
+
* extension's sources. Empty for a non-relative specifier. */
|
|
1325
|
+
function relativeSourceCandidates(importerPath, specifier) {
|
|
1326
|
+
if (!specifier.startsWith(".")) return [];
|
|
1041
1327
|
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
1328
|
const explicitExtension = extension(base);
|
|
1048
|
-
|
|
1049
|
-
|
|
1329
|
+
return [...RELATIVE_PROBE_SUFFIXES.map((suffix) => `${base}${suffix}`), ...(NODE_NEXT_EXTENSION_MAP[explicitExtension] ?? []).map((sourceExtension) => `${base.slice(0, -explicitExtension.length)}${sourceExtension}`)];
|
|
1330
|
+
}
|
|
1331
|
+
function resolveRelativeSource(importerPath, specifier, files) {
|
|
1332
|
+
for (const candidate of relativeSourceCandidates(importerPath, specifier)) {
|
|
1050
1333
|
const actualPath = files.get(candidate);
|
|
1051
1334
|
if (actualPath !== void 0) return actualPath;
|
|
1052
1335
|
}
|
|
@@ -1351,7 +1634,8 @@ function createSourceIngestor(host) {
|
|
|
1351
1634
|
const fatal = diagnostics.filter((diagnostic) => !isAdvisorySourceDiagnostic(diagnostic));
|
|
1352
1635
|
const invalidOriginals = new Set(fatal.map((diagnostic) => diagnostic.originalPath));
|
|
1353
1636
|
if (diagnostics.length === 0) return invalidOriginals;
|
|
1354
|
-
|
|
1637
|
+
const reportError = host.strict() ? host.reportErrors?.() : void 0;
|
|
1638
|
+
if (host.strict() && fatal.length > 0 && !reportError) {
|
|
1355
1639
|
const lines = fatal.map((diagnostic) => `${diagnostic.code} ${diagnostic.originalPath}: ${diagnostic.message} (${SKIPPED_SOURCE_COST})`);
|
|
1356
1640
|
throw new Error(`${host.prefix} ${lines.join(`\n${host.prefix} `)}`);
|
|
1357
1641
|
}
|
|
@@ -1365,7 +1649,8 @@ function createSourceIngestor(host) {
|
|
|
1365
1649
|
}
|
|
1366
1650
|
if (warned.has(line)) continue;
|
|
1367
1651
|
warned.add(line);
|
|
1368
|
-
|
|
1652
|
+
if (reportError && skipped) reportError(`${host.prefix} ${line}`);
|
|
1653
|
+
else host.warn(`${host.prefix} ${line}`);
|
|
1369
1654
|
}
|
|
1370
1655
|
return invalidOriginals;
|
|
1371
1656
|
},
|
|
@@ -1392,7 +1677,9 @@ function createV2EngineApi(deps) {
|
|
|
1392
1677
|
return {
|
|
1393
1678
|
loadSystemModule: (...args) => native.loadSystemModule(...args),
|
|
1394
1679
|
extractFacts: (filesJson) => native.extractFacts(filesJson),
|
|
1395
|
-
|
|
1680
|
+
moduleSpecifiers: (filesJson) => native.moduleSpecifiers(filesJson),
|
|
1681
|
+
diagnosticCodes: () => native.diagnosticCodes(),
|
|
1682
|
+
analyzeProject: (filesJsonRaw, scalesJson, variableMapJson, contextualVarsJson, propConfigJson, groupRegistryJson, packageResolutionJson, devMode, emitterConfigJson, selectorAliasesJson, _selectorOrderJson, globalStyleBlocksJson, pathAliasesJson, keyframesJson, staticCssJson, conditionAliasesJson, externalDirsJson, transformSourcesJson, transformProvenanceJson, declarationScalesJson, analysisContextJson, generatedNamesJson) => {
|
|
1396
1683
|
const filesJson = deps.rehydrateFilesJson ? deps.rehydrateFilesJson(filesJsonRaw) : filesJsonRaw;
|
|
1397
1684
|
const sent = /* @__PURE__ */ new Map();
|
|
1398
1685
|
for (const entry of parseFilesJson(filesJson, label)) sent.set(entry.path, entry.source);
|
|
@@ -1420,6 +1707,7 @@ function createV2EngineApi(deps) {
|
|
|
1420
1707
|
transformProvenanceJson: transformProvenanceJson ?? void 0,
|
|
1421
1708
|
declarationScalesJson,
|
|
1422
1709
|
analysisContextJson,
|
|
1710
|
+
generatedNamesJson,
|
|
1423
1711
|
devMode
|
|
1424
1712
|
};
|
|
1425
1713
|
const engine = new native.ExtractEngine(config);
|
|
@@ -1457,6 +1745,10 @@ function assertNoRetiredEngineSelection(engineOption) {
|
|
|
1457
1745
|
}
|
|
1458
1746
|
//#endregion
|
|
1459
1747
|
//#region pipeline/core-options.ts
|
|
1748
|
+
/**
|
|
1749
|
+
* One option schema shared by the Vite, Next, and CLI drivers: key semantics,
|
|
1750
|
+
* default exclusions, and mode authority cannot drift per driver.
|
|
1751
|
+
*/
|
|
1460
1752
|
const DRIVER_NAMESPACES = [
|
|
1461
1753
|
"vite",
|
|
1462
1754
|
"next",
|
|
@@ -1490,6 +1782,7 @@ const CORE_OPTION_KEYS = /* @__PURE__ */ new Set([
|
|
|
1490
1782
|
"exclude",
|
|
1491
1783
|
"extensions",
|
|
1492
1784
|
"strict",
|
|
1785
|
+
"diagnostics",
|
|
1493
1786
|
"verbose",
|
|
1494
1787
|
"prefix",
|
|
1495
1788
|
"prefixContextualVars",
|
|
@@ -1549,6 +1842,7 @@ const isString = (value) => Object(value) !== value && Object.prototype.toString
|
|
|
1549
1842
|
const isBoolean = (value) => Object(value) !== value && Object.prototype.toString.call(value) === "[object Boolean]";
|
|
1550
1843
|
const isVerbose = (value) => isBoolean(value) || value === "trace";
|
|
1551
1844
|
const isStringArray = (value) => Array.isArray(value) && value.every(isString);
|
|
1845
|
+
const isDiagnosticLevels = (value) => Object.prototype.toString.call(value) === "[object Object]" && Object.values(Object(value)).every((level) => isString(level) && DIAGNOSTIC_LEVELS.has(level));
|
|
1552
1846
|
const isStringOrStringArray = (value) => isString(value) || isStringArray(value);
|
|
1553
1847
|
const CORE_VALUE_GATES = [
|
|
1554
1848
|
{
|
|
@@ -1571,6 +1865,11 @@ const CORE_VALUE_GATES = [
|
|
|
1571
1865
|
ok: isBoolean,
|
|
1572
1866
|
expected: "a boolean"
|
|
1573
1867
|
},
|
|
1868
|
+
{
|
|
1869
|
+
key: "diagnostics",
|
|
1870
|
+
ok: isDiagnosticLevels,
|
|
1871
|
+
expected: "an object mapping codes to 'off', 'info', 'warn' or 'error'"
|
|
1872
|
+
},
|
|
1574
1873
|
{
|
|
1575
1874
|
key: "verbose",
|
|
1576
1875
|
ok: isVerbose,
|
|
@@ -1731,7 +2030,7 @@ function walkFiles(dir, rootDir, exclude, extensionsSet) {
|
|
|
1731
2030
|
const isExcluded = exclude ? (full, rel) => exclude.matches(full, rel) : () => false;
|
|
1732
2031
|
for (const entry of entries) {
|
|
1733
2032
|
const fullPath = join$1(dir, entry);
|
|
1734
|
-
if (isExcluded(fullPath, relative(rootDir, fullPath))) continue;
|
|
2033
|
+
if (isExcluded(fullPath, relative$1(rootDir, fullPath))) continue;
|
|
1735
2034
|
let stat;
|
|
1736
2035
|
try {
|
|
1737
2036
|
stat = statSync(fullPath);
|
|
@@ -1739,182 +2038,74 @@ function walkFiles(dir, rootDir, exclude, extensionsSet) {
|
|
|
1739
2038
|
continue;
|
|
1740
2039
|
}
|
|
1741
2040
|
if (stat.isDirectory()) results.push(...walkFiles(fullPath, rootDir, exclude, extensionsSet));
|
|
1742
|
-
else if (extensionsSet.has(extname(entry))) results.push(fullPath);
|
|
2041
|
+
else if (extensionsSet.has(extname$1(entry))) results.push(fullPath);
|
|
1743
2042
|
}
|
|
1744
2043
|
return results;
|
|
1745
2044
|
}
|
|
1746
2045
|
//#endregion
|
|
1747
|
-
//#region pipeline/
|
|
1748
|
-
/**
|
|
1749
|
-
* One derivation of source identity for discovery, watch ingestion, ownership
|
|
1750
|
-
* and deletion: event paths are lookup INPUTS only and never fork identity.
|
|
1751
|
-
*/
|
|
2046
|
+
//#region pipeline/tsconfig-paths.ts
|
|
1752
2047
|
/**
|
|
1753
|
-
*
|
|
1754
|
-
*
|
|
2048
|
+
* tsconfig `paths` → alias pairs, for drivers with no live bundler config.
|
|
2049
|
+
* The nearest config declaring `paths` wins wholesale — TS never merges.
|
|
1755
2050
|
*/
|
|
1756
|
-
function
|
|
1757
|
-
|
|
1758
|
-
|
|
2051
|
+
function stripJsonc(text) {
|
|
2052
|
+
let out = "";
|
|
2053
|
+
let inString = false;
|
|
2054
|
+
let inLine = false;
|
|
2055
|
+
let inBlock = false;
|
|
2056
|
+
for (let i = 0; i < text.length; i++) {
|
|
2057
|
+
const ch = text[i];
|
|
2058
|
+
const next = text[i + 1];
|
|
2059
|
+
if (inLine) {
|
|
2060
|
+
if (ch === "\n") {
|
|
2061
|
+
inLine = false;
|
|
2062
|
+
out += ch;
|
|
2063
|
+
}
|
|
2064
|
+
continue;
|
|
2065
|
+
}
|
|
2066
|
+
if (inBlock) {
|
|
2067
|
+
if (ch === "*" && next === "/") {
|
|
2068
|
+
inBlock = false;
|
|
2069
|
+
i++;
|
|
2070
|
+
}
|
|
2071
|
+
continue;
|
|
2072
|
+
}
|
|
2073
|
+
if (inString) {
|
|
2074
|
+
out += ch;
|
|
2075
|
+
if (ch === "\\") {
|
|
2076
|
+
out += next ?? "";
|
|
2077
|
+
i++;
|
|
2078
|
+
} else if (ch === "\"") inString = false;
|
|
2079
|
+
continue;
|
|
2080
|
+
}
|
|
2081
|
+
if (ch === "\"") {
|
|
2082
|
+
inString = true;
|
|
2083
|
+
out += ch;
|
|
2084
|
+
continue;
|
|
2085
|
+
}
|
|
2086
|
+
if (ch === "/" && next === "/") {
|
|
2087
|
+
inLine = true;
|
|
2088
|
+
i++;
|
|
2089
|
+
continue;
|
|
2090
|
+
}
|
|
2091
|
+
if (ch === "/" && next === "*") {
|
|
2092
|
+
inBlock = true;
|
|
2093
|
+
i++;
|
|
2094
|
+
continue;
|
|
2095
|
+
}
|
|
2096
|
+
out += ch;
|
|
2097
|
+
}
|
|
2098
|
+
return out.replace(/,(\s*[}\]])/g, "$1");
|
|
1759
2099
|
}
|
|
1760
|
-
/**
|
|
1761
|
-
*
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
function sharesVolumeRoot(a, b, pathApi = path) {
|
|
1765
|
-
const volumeOf = (p) => pathApi.parse(pathApi.resolve(p)).root.toLowerCase();
|
|
1766
|
-
return volumeOf(a) === volumeOf(b);
|
|
2100
|
+
/** `Object(value) === value` holds for exactly the blocks and lists
|
|
2101
|
+
* `JSON.parse` produces; the `[object Object]` tag separates the two. */
|
|
2102
|
+
function isJsonBlock(value) {
|
|
2103
|
+
return Object(value) === value && Object.prototype.toString.call(value) === "[object Object]";
|
|
1767
2104
|
}
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
return null;
|
|
1773
|
-
}
|
|
1774
|
-
}
|
|
1775
|
-
function createSourceIdentity(rootDir) {
|
|
1776
|
-
const normalizedRootDir = path.normalize(rootDir);
|
|
1777
|
-
const canonicalRootDir = canonicalize(normalizedRootDir) ?? normalizedRootDir;
|
|
1778
|
-
const roots = [];
|
|
1779
|
-
const aliases = /* @__PURE__ */ new Map();
|
|
1780
|
-
const owningRecord = (canonicalPath) => {
|
|
1781
|
-
let owner = null;
|
|
1782
|
-
for (const record of roots) {
|
|
1783
|
-
if (!isPathWithinRoot(record.canonical, canonicalPath)) continue;
|
|
1784
|
-
if (!owner || record.canonical.length > owner.canonical.length) owner = record;
|
|
1785
|
-
}
|
|
1786
|
-
return owner;
|
|
1787
|
-
};
|
|
1788
|
-
return {
|
|
1789
|
-
rootDir: normalizedRootDir,
|
|
1790
|
-
canonicalRootDir,
|
|
1791
|
-
registerExternalRoot(root) {
|
|
1792
|
-
const asRegistered = path.normalize(root);
|
|
1793
|
-
const canonical = canonicalize(asRegistered) ?? asRegistered;
|
|
1794
|
-
const existing = roots.find((record) => record.canonical === canonical);
|
|
1795
|
-
if (existing) return existing.canonical;
|
|
1796
|
-
roots.push({
|
|
1797
|
-
asRegistered,
|
|
1798
|
-
canonical
|
|
1799
|
-
});
|
|
1800
|
-
return canonical;
|
|
1801
|
-
},
|
|
1802
|
-
externalRoots() {
|
|
1803
|
-
return roots.map((record) => record.canonical);
|
|
1804
|
-
},
|
|
1805
|
-
resolveSourceId(inputPath) {
|
|
1806
|
-
const lexical = path.normalize(inputPath);
|
|
1807
|
-
const canonical = canonicalize(lexical);
|
|
1808
|
-
if (canonical === null) return null;
|
|
1809
|
-
const owner = owningRecord(canonical);
|
|
1810
|
-
const ownedBy = (record) => {
|
|
1811
|
-
const pathInRoot = path.relative(record.canonical, canonical);
|
|
1812
|
-
return {
|
|
1813
|
-
sourceKey: path.relative(normalizedRootDir, path.join(record.asRegistered, pathInRoot)),
|
|
1814
|
-
owningRoot: record.canonical,
|
|
1815
|
-
pathInRoot
|
|
1816
|
-
};
|
|
1817
|
-
};
|
|
1818
|
-
const ownerIsMoreSpecific = owner !== null && owner.canonical.length > canonicalRootDir.length;
|
|
1819
|
-
let resolved;
|
|
1820
|
-
if (!ownerIsMoreSpecific && isPathWithinRoot(canonicalRootDir, canonical)) {
|
|
1821
|
-
const key = path.relative(canonicalRootDir, canonical);
|
|
1822
|
-
resolved = {
|
|
1823
|
-
sourceKey: key,
|
|
1824
|
-
owningRoot: null,
|
|
1825
|
-
pathInRoot: key
|
|
1826
|
-
};
|
|
1827
|
-
} else if (owner) resolved = ownedBy(owner);
|
|
1828
|
-
else return null;
|
|
1829
|
-
aliases.set(lexical, resolved);
|
|
1830
|
-
aliases.set(canonical, resolved);
|
|
1831
|
-
return resolved;
|
|
1832
|
-
},
|
|
1833
|
-
resolveDeletedSourceId(inputPath) {
|
|
1834
|
-
return aliases.get(path.normalize(inputPath)) ?? null;
|
|
1835
|
-
},
|
|
1836
|
-
containingExternalRoot(inputPath) {
|
|
1837
|
-
const lexical = path.normalize(inputPath);
|
|
1838
|
-
let owner = null;
|
|
1839
|
-
for (const record of roots) {
|
|
1840
|
-
if (!(isPathWithinRoot(record.canonical, lexical) || isPathWithinRoot(record.asRegistered, lexical))) continue;
|
|
1841
|
-
if (!owner || record.canonical.length > owner.canonical.length) owner = record;
|
|
1842
|
-
}
|
|
1843
|
-
if (owner) return owner.canonical;
|
|
1844
|
-
const canonical = canonicalize(lexical);
|
|
1845
|
-
if (canonical === null) return null;
|
|
1846
|
-
for (const record of roots) {
|
|
1847
|
-
if (!isPathWithinRoot(record.canonical, canonical)) continue;
|
|
1848
|
-
if (!owner || record.canonical.length > owner.canonical.length) owner = record;
|
|
1849
|
-
}
|
|
1850
|
-
return owner?.canonical ?? null;
|
|
1851
|
-
}
|
|
1852
|
-
};
|
|
1853
|
-
}
|
|
1854
|
-
//#endregion
|
|
1855
|
-
//#region pipeline/tsconfig-paths.ts
|
|
1856
|
-
/**
|
|
1857
|
-
* tsconfig `paths` → alias pairs, for drivers with no live bundler config.
|
|
1858
|
-
* The nearest config declaring `paths` wins wholesale — TS never merges.
|
|
1859
|
-
*/
|
|
1860
|
-
function stripJsonc(text) {
|
|
1861
|
-
let out = "";
|
|
1862
|
-
let inString = false;
|
|
1863
|
-
let inLine = false;
|
|
1864
|
-
let inBlock = false;
|
|
1865
|
-
for (let i = 0; i < text.length; i++) {
|
|
1866
|
-
const ch = text[i];
|
|
1867
|
-
const next = text[i + 1];
|
|
1868
|
-
if (inLine) {
|
|
1869
|
-
if (ch === "\n") {
|
|
1870
|
-
inLine = false;
|
|
1871
|
-
out += ch;
|
|
1872
|
-
}
|
|
1873
|
-
continue;
|
|
1874
|
-
}
|
|
1875
|
-
if (inBlock) {
|
|
1876
|
-
if (ch === "*" && next === "/") {
|
|
1877
|
-
inBlock = false;
|
|
1878
|
-
i++;
|
|
1879
|
-
}
|
|
1880
|
-
continue;
|
|
1881
|
-
}
|
|
1882
|
-
if (inString) {
|
|
1883
|
-
out += ch;
|
|
1884
|
-
if (ch === "\\") {
|
|
1885
|
-
out += next ?? "";
|
|
1886
|
-
i++;
|
|
1887
|
-
} else if (ch === "\"") inString = false;
|
|
1888
|
-
continue;
|
|
1889
|
-
}
|
|
1890
|
-
if (ch === "\"") {
|
|
1891
|
-
inString = true;
|
|
1892
|
-
out += ch;
|
|
1893
|
-
continue;
|
|
1894
|
-
}
|
|
1895
|
-
if (ch === "/" && next === "/") {
|
|
1896
|
-
inLine = true;
|
|
1897
|
-
i++;
|
|
1898
|
-
continue;
|
|
1899
|
-
}
|
|
1900
|
-
if (ch === "/" && next === "*") {
|
|
1901
|
-
inBlock = true;
|
|
1902
|
-
i++;
|
|
1903
|
-
continue;
|
|
1904
|
-
}
|
|
1905
|
-
out += ch;
|
|
1906
|
-
}
|
|
1907
|
-
return out.replace(/,(\s*[}\]])/g, "$1");
|
|
1908
|
-
}
|
|
1909
|
-
/** `Object(value) === value` holds for exactly the blocks and lists
|
|
1910
|
-
* `JSON.parse` produces; the `[object Object]` tag separates the two. */
|
|
1911
|
-
function isJsonBlock(value) {
|
|
1912
|
-
return Object(value) === value && Object.prototype.toString.call(value) === "[object Object]";
|
|
1913
|
-
}
|
|
1914
|
-
/** A block or a list. A declared `paths` OWNS the setting and blocks its
|
|
1915
|
-
* parents' even when it contributes nothing usable. */
|
|
1916
|
-
function isJsonKeyed(value) {
|
|
1917
|
-
return Object(value) === value;
|
|
2105
|
+
/** A block or a list. A declared `paths` OWNS the setting and blocks its
|
|
2106
|
+
* parents' even when it contributes nothing usable. */
|
|
2107
|
+
function isJsonKeyed(value) {
|
|
2108
|
+
return Object(value) === value;
|
|
1918
2109
|
}
|
|
1919
2110
|
/** A JSON string, excluding the boxed `String` object, which no path join
|
|
1920
2111
|
* accepts. */
|
|
@@ -1958,8 +2149,8 @@ function readConfig(path) {
|
|
|
1958
2149
|
};
|
|
1959
2150
|
}
|
|
1960
2151
|
function resolveExtendsTarget(specifier, fromDir) {
|
|
1961
|
-
if (specifier.startsWith(".") || isAbsolute(specifier)) {
|
|
1962
|
-
const base = resolve(fromDir, specifier);
|
|
2152
|
+
if (specifier.startsWith(".") || isAbsolute$1(specifier)) {
|
|
2153
|
+
const base = resolve$1(fromDir, specifier);
|
|
1963
2154
|
for (const candidate of [
|
|
1964
2155
|
base,
|
|
1965
2156
|
`${base}.json`,
|
|
@@ -1984,7 +2175,7 @@ function loadChain(entryPath) {
|
|
|
1984
2175
|
const queue = [entryPath];
|
|
1985
2176
|
while (queue.length > 0) {
|
|
1986
2177
|
const path = queue.shift();
|
|
1987
|
-
const key = resolve(path);
|
|
2178
|
+
const key = resolve$1(path);
|
|
1988
2179
|
if (visited.has(key)) continue;
|
|
1989
2180
|
visited.add(key);
|
|
1990
2181
|
const node = readConfig(key);
|
|
@@ -2009,7 +2200,7 @@ function readTsconfigAliasPairs(rootDir) {
|
|
|
2009
2200
|
if (pathsOwner === void 0 || paths === null) return [];
|
|
2010
2201
|
const baseOwner = chain.find((node) => node.baseUrl !== null);
|
|
2011
2202
|
const baseUrl = baseOwner?.baseUrl ?? null;
|
|
2012
|
-
const base = baseOwner !== void 0 && baseUrl !== null ? resolve(baseOwner.dir, baseUrl) : pathsOwner.dir;
|
|
2203
|
+
const base = baseOwner !== void 0 && baseUrl !== null ? resolve$1(baseOwner.dir, baseUrl) : pathsOwner.dir;
|
|
2013
2204
|
const pairs = [];
|
|
2014
2205
|
for (const [pattern, target] of paths) {
|
|
2015
2206
|
const patternStars = pattern.split("*").length - 1;
|
|
@@ -2019,7 +2210,7 @@ function readTsconfigAliasPairs(rootDir) {
|
|
|
2019
2210
|
if (!target.endsWith("/*")) continue;
|
|
2020
2211
|
pairs.push({
|
|
2021
2212
|
pattern: pattern.slice(0, -2),
|
|
2022
|
-
target: resolve(base, target.slice(0, -2)),
|
|
2213
|
+
target: resolve$1(base, target.slice(0, -2)),
|
|
2023
2214
|
kind: "prefix"
|
|
2024
2215
|
});
|
|
2025
2216
|
continue;
|
|
@@ -2027,13 +2218,223 @@ function readTsconfigAliasPairs(rootDir) {
|
|
|
2027
2218
|
if (patternStars > 0 || targetStars > 0) continue;
|
|
2028
2219
|
pairs.push({
|
|
2029
2220
|
pattern,
|
|
2030
|
-
target: resolve(base, target),
|
|
2221
|
+
target: resolve$1(base, target),
|
|
2031
2222
|
kind: "exact"
|
|
2032
2223
|
});
|
|
2033
2224
|
}
|
|
2034
2225
|
return pairs;
|
|
2035
2226
|
}
|
|
2036
2227
|
//#endregion
|
|
2228
|
+
//#region pipeline/kit-descriptor.ts
|
|
2229
|
+
/** A kit's description, which `animus build --kit` writes at its package
|
|
2230
|
+
* root and the kit exports as `./animus.json`. */
|
|
2231
|
+
const KIT_DESCRIPTOR_FILE = "animus.json";
|
|
2232
|
+
/** A path with `/` separators, as descriptors key their components. */
|
|
2233
|
+
function posixPath$1(path) {
|
|
2234
|
+
return path.split(sep).join("/");
|
|
2235
|
+
}
|
|
2236
|
+
/** The descriptor of the kit a build analysed. */
|
|
2237
|
+
function buildKitDescriptor(manifest) {
|
|
2238
|
+
const components = {};
|
|
2239
|
+
for (const id of Object.keys(manifest.components).sort()) {
|
|
2240
|
+
const component = manifest.components[id];
|
|
2241
|
+
components[posixPath$1(id)] = {
|
|
2242
|
+
definition: component.definition_fingerprint,
|
|
2243
|
+
runtimeProps: [...new Set(component.system_prop_names)].sort()
|
|
2244
|
+
};
|
|
2245
|
+
}
|
|
2246
|
+
return {
|
|
2247
|
+
format: 1,
|
|
2248
|
+
system: manifest.system_fingerprint,
|
|
2249
|
+
components
|
|
2250
|
+
};
|
|
2251
|
+
}
|
|
2252
|
+
/** Reads the descriptor at a kit's package root; `null` when it has none. */
|
|
2253
|
+
function readKitDescriptor(pkgRoot, rootDir) {
|
|
2254
|
+
const path = join(pkgRoot, KIT_DESCRIPTOR_FILE);
|
|
2255
|
+
if (!existsSync$1(path)) return null;
|
|
2256
|
+
const packageRoot = posixPath$1(relative(rootDir, pkgRoot));
|
|
2257
|
+
let wire;
|
|
2258
|
+
try {
|
|
2259
|
+
wire = JSON.parse(readFileSync$1(path, "utf-8"));
|
|
2260
|
+
} catch {
|
|
2261
|
+
return {
|
|
2262
|
+
packageRoot,
|
|
2263
|
+
unreadable: "is not valid JSON: rebuild it with animus build --kit"
|
|
2264
|
+
};
|
|
2265
|
+
}
|
|
2266
|
+
const block = isJsonBlock(wire) ? wire : {};
|
|
2267
|
+
if (block.format !== 1) return {
|
|
2268
|
+
packageRoot,
|
|
2269
|
+
unreadable: `declares descriptor format ${JSON.stringify(block.format ?? null)}, which this Animus does not read (it reads format 1): upgrade Animus, or install a kit built for this version`
|
|
2270
|
+
};
|
|
2271
|
+
const definitions = {};
|
|
2272
|
+
const components = isJsonBlock(block.components) ? block.components : {};
|
|
2273
|
+
for (const [id, component] of Object.entries(components)) {
|
|
2274
|
+
const definition = isJsonBlock(component) ? component.definition : null;
|
|
2275
|
+
if (isJsonString(definition)) definitions[posixPath$1(id)] = definition;
|
|
2276
|
+
}
|
|
2277
|
+
return {
|
|
2278
|
+
packageRoot,
|
|
2279
|
+
definitions
|
|
2280
|
+
};
|
|
2281
|
+
}
|
|
2282
|
+
/**
|
|
2283
|
+
* A descriptor in a format this Animus cannot read fails the build; one
|
|
2284
|
+
* whose component definitions differ from the kit's shipped source, as the
|
|
2285
|
+
* analysis read it, or that describes a component its analysed file no
|
|
2286
|
+
* longer declares, was built from other source and is stale.
|
|
2287
|
+
*/
|
|
2288
|
+
function kitDescriptorDiagnostics(records, manifest) {
|
|
2289
|
+
if (records.length === 0) return [];
|
|
2290
|
+
const diagnostics = [];
|
|
2291
|
+
const analysedById = new Map(Object.entries(manifest.components).map(([id, component]) => [posixPath$1(id), component]));
|
|
2292
|
+
const analysedFiles = new Set(Object.keys(manifest.fileFacts).map(posixPath$1));
|
|
2293
|
+
const declared = new Set(Object.entries(manifest.fileFacts).flatMap(([file, facts]) => facts.chains.map((chain) => `${posixPath$1(file)}::${chain.descriptor.binding}`)));
|
|
2294
|
+
for (const record of records) {
|
|
2295
|
+
const file = `${record.packageRoot}/${KIT_DESCRIPTOR_FILE}`;
|
|
2296
|
+
if ("unreadable" in record) {
|
|
2297
|
+
diagnostics.push({
|
|
2298
|
+
file,
|
|
2299
|
+
component: "kit",
|
|
2300
|
+
kind: "error",
|
|
2301
|
+
message: record.unreadable,
|
|
2302
|
+
code: KIT_UNSUPPORTED_FORMAT,
|
|
2303
|
+
severity: severityFor(KIT_UNSUPPORTED_FORMAT)
|
|
2304
|
+
});
|
|
2305
|
+
continue;
|
|
2306
|
+
}
|
|
2307
|
+
for (const [id, definition] of Object.entries(record.definitions)) {
|
|
2308
|
+
const key = `${record.packageRoot}/${id}`;
|
|
2309
|
+
const analysed = analysedById.get(key);
|
|
2310
|
+
const fileAnalysed = analysedFiles.has(key.slice(0, key.lastIndexOf("::")));
|
|
2311
|
+
let stale;
|
|
2312
|
+
if (analysed) {
|
|
2313
|
+
if (analysed.definition_fingerprint === definition) continue;
|
|
2314
|
+
stale = `describes ${id} with a definition other than the one its shipped source declares`;
|
|
2315
|
+
} else if (fileAnalysed && !declared.has(key)) stale = `describes ${id}, which its shipped source no longer declares`;
|
|
2316
|
+
else continue;
|
|
2317
|
+
diagnostics.push({
|
|
2318
|
+
file,
|
|
2319
|
+
component: analysed?.binding ?? id.slice(id.lastIndexOf("::") + 2),
|
|
2320
|
+
kind: "warn",
|
|
2321
|
+
message: `${stale}, so the descriptor is stale: rebuild it with animus build --kit`,
|
|
2322
|
+
code: KIT_STALE_DESCRIPTOR,
|
|
2323
|
+
severity: severityFor(KIT_STALE_DESCRIPTOR)
|
|
2324
|
+
});
|
|
2325
|
+
}
|
|
2326
|
+
}
|
|
2327
|
+
return diagnostics;
|
|
2328
|
+
}
|
|
2329
|
+
//#endregion
|
|
2330
|
+
//#region pipeline/source-identity.ts
|
|
2331
|
+
/**
|
|
2332
|
+
* One derivation of source identity for discovery, watch ingestion, ownership
|
|
2333
|
+
* and deletion: event paths are lookup INPUTS only and never fork identity.
|
|
2334
|
+
*/
|
|
2335
|
+
/**
|
|
2336
|
+
* True when `target` is `root` or a descendant, decided by `relative()`
|
|
2337
|
+
* shape: string prefixing would let `/ui` claim `/ui-old`.
|
|
2338
|
+
*/
|
|
2339
|
+
function isPathWithinRoot(root, target, pathApi = path) {
|
|
2340
|
+
const rel = pathApi.relative(root, target);
|
|
2341
|
+
return rel === "" || !pathApi.isAbsolute(rel) && rel !== ".." && !rel.startsWith(".." + pathApi.sep);
|
|
2342
|
+
}
|
|
2343
|
+
/**
|
|
2344
|
+
* True when two paths share a platform root (win32 drive letter or UNC
|
|
2345
|
+
* share; always true on posix). Drive letters compare case-insensitively.
|
|
2346
|
+
*/
|
|
2347
|
+
function sharesVolumeRoot(a, b, pathApi = path) {
|
|
2348
|
+
const volumeOf = (p) => pathApi.parse(pathApi.resolve(p)).root.toLowerCase();
|
|
2349
|
+
return volumeOf(a) === volumeOf(b);
|
|
2350
|
+
}
|
|
2351
|
+
function canonicalize(p) {
|
|
2352
|
+
try {
|
|
2353
|
+
return realpathSync$1(p);
|
|
2354
|
+
} catch {
|
|
2355
|
+
return null;
|
|
2356
|
+
}
|
|
2357
|
+
}
|
|
2358
|
+
function createSourceIdentity(rootDir) {
|
|
2359
|
+
const normalizedRootDir = path.normalize(rootDir);
|
|
2360
|
+
const canonicalRootDir = canonicalize(normalizedRootDir) ?? normalizedRootDir;
|
|
2361
|
+
const roots = [];
|
|
2362
|
+
const aliases = /* @__PURE__ */ new Map();
|
|
2363
|
+
const owningRecord = (canonicalPath) => {
|
|
2364
|
+
let owner = null;
|
|
2365
|
+
for (const record of roots) {
|
|
2366
|
+
if (!isPathWithinRoot(record.canonical, canonicalPath)) continue;
|
|
2367
|
+
if (!owner || record.canonical.length > owner.canonical.length) owner = record;
|
|
2368
|
+
}
|
|
2369
|
+
return owner;
|
|
2370
|
+
};
|
|
2371
|
+
return {
|
|
2372
|
+
rootDir: normalizedRootDir,
|
|
2373
|
+
canonicalRootDir,
|
|
2374
|
+
registerExternalRoot(root) {
|
|
2375
|
+
const asRegistered = path.normalize(root);
|
|
2376
|
+
const canonical = canonicalize(asRegistered) ?? asRegistered;
|
|
2377
|
+
const existing = roots.find((record) => record.canonical === canonical);
|
|
2378
|
+
if (existing) return existing.canonical;
|
|
2379
|
+
roots.push({
|
|
2380
|
+
asRegistered,
|
|
2381
|
+
canonical
|
|
2382
|
+
});
|
|
2383
|
+
return canonical;
|
|
2384
|
+
},
|
|
2385
|
+
externalRoots() {
|
|
2386
|
+
return roots.map((record) => record.canonical);
|
|
2387
|
+
},
|
|
2388
|
+
resolveSourceId(inputPath) {
|
|
2389
|
+
const lexical = path.normalize(inputPath);
|
|
2390
|
+
const canonical = canonicalize(lexical);
|
|
2391
|
+
if (canonical === null) return null;
|
|
2392
|
+
const owner = owningRecord(canonical);
|
|
2393
|
+
const ownedBy = (record) => {
|
|
2394
|
+
const pathInRoot = path.relative(record.canonical, canonical);
|
|
2395
|
+
return {
|
|
2396
|
+
sourceKey: path.relative(normalizedRootDir, path.join(record.asRegistered, pathInRoot)),
|
|
2397
|
+
owningRoot: record.canonical,
|
|
2398
|
+
pathInRoot
|
|
2399
|
+
};
|
|
2400
|
+
};
|
|
2401
|
+
const ownerIsMoreSpecific = owner !== null && owner.canonical.length > canonicalRootDir.length;
|
|
2402
|
+
let resolved;
|
|
2403
|
+
if (!ownerIsMoreSpecific && isPathWithinRoot(canonicalRootDir, canonical)) {
|
|
2404
|
+
const key = path.relative(canonicalRootDir, canonical);
|
|
2405
|
+
resolved = {
|
|
2406
|
+
sourceKey: key,
|
|
2407
|
+
owningRoot: null,
|
|
2408
|
+
pathInRoot: key
|
|
2409
|
+
};
|
|
2410
|
+
} else if (owner) resolved = ownedBy(owner);
|
|
2411
|
+
else return null;
|
|
2412
|
+
aliases.set(lexical, resolved);
|
|
2413
|
+
aliases.set(canonical, resolved);
|
|
2414
|
+
return resolved;
|
|
2415
|
+
},
|
|
2416
|
+
resolveDeletedSourceId(inputPath) {
|
|
2417
|
+
return aliases.get(path.normalize(inputPath)) ?? null;
|
|
2418
|
+
},
|
|
2419
|
+
containingExternalRoot(inputPath) {
|
|
2420
|
+
const lexical = path.normalize(inputPath);
|
|
2421
|
+
let owner = null;
|
|
2422
|
+
for (const record of roots) {
|
|
2423
|
+
if (!(isPathWithinRoot(record.canonical, lexical) || isPathWithinRoot(record.asRegistered, lexical))) continue;
|
|
2424
|
+
if (!owner || record.canonical.length > owner.canonical.length) owner = record;
|
|
2425
|
+
}
|
|
2426
|
+
if (owner) return owner.canonical;
|
|
2427
|
+
const canonical = canonicalize(lexical);
|
|
2428
|
+
if (canonical === null) return null;
|
|
2429
|
+
for (const record of roots) {
|
|
2430
|
+
if (!isPathWithinRoot(record.canonical, canonical)) continue;
|
|
2431
|
+
if (!owner || record.canonical.length > owner.canonical.length) owner = record;
|
|
2432
|
+
}
|
|
2433
|
+
return owner?.canonical ?? null;
|
|
2434
|
+
}
|
|
2435
|
+
};
|
|
2436
|
+
}
|
|
2437
|
+
//#endregion
|
|
2037
2438
|
//#region pipeline/discover-packages.ts
|
|
2038
2439
|
function findPackageRoot(absEntryPath) {
|
|
2039
2440
|
let pkgRoot = dirname$1(absEntryPath);
|
|
@@ -2050,7 +2451,7 @@ function isExcludedPackageRelativePath(rel) {
|
|
|
2050
2451
|
return PACKAGE_SRC_EXCLUDES.some((pattern) => rel.includes(pattern));
|
|
2051
2452
|
}
|
|
2052
2453
|
function walkPackageSources(packageDir, extensionsSet) {
|
|
2053
|
-
return discoverFiles(packageDir, packageDir, void 0, extensionsSet).filter((absPath) => !isExcludedPackageRelativePath(relative(packageDir, absPath)));
|
|
2454
|
+
return discoverFiles(packageDir, packageDir, void 0, extensionsSet).filter((absPath) => !isExcludedPackageRelativePath(relative$1(packageDir, absPath)));
|
|
2054
2455
|
}
|
|
2055
2456
|
const PACKAGE_OUTPUT_EXCLUDES = [
|
|
2056
2457
|
"node_modules",
|
|
@@ -2059,7 +2460,7 @@ const PACKAGE_OUTPUT_EXCLUDES = [
|
|
|
2059
2460
|
".d.ts",
|
|
2060
2461
|
".map"
|
|
2061
2462
|
];
|
|
2062
|
-
const isFile = (path) => {
|
|
2463
|
+
const isFile$1 = (path) => {
|
|
2063
2464
|
try {
|
|
2064
2465
|
return statSync(path).isFile();
|
|
2065
2466
|
} catch {
|
|
@@ -2071,14 +2472,15 @@ function resolveAbsolutePathSpecifier(absSpecifier, extensionsSet) {
|
|
|
2071
2472
|
absSpecifier,
|
|
2072
2473
|
...Array.from(extensionsSet, (ext) => absSpecifier + ext),
|
|
2073
2474
|
...Array.from(extensionsSet, (ext) => join$1(absSpecifier, `index${ext}`))
|
|
2074
|
-
].find(isFile) ?? null;
|
|
2475
|
+
].find(isFile$1) ?? null;
|
|
2075
2476
|
}
|
|
2477
|
+
/** The package a bare specifier names: `@scope/name` or `name`. */
|
|
2076
2478
|
function bareSpecifierPackageName(specifier) {
|
|
2077
2479
|
const segments = specifier.split("/");
|
|
2078
2480
|
return specifier.startsWith("@") ? segments.slice(0, 2).join("/") : segments[0];
|
|
2079
2481
|
}
|
|
2080
2482
|
function sourceEntryForSpecifier(specifier, srcDir, extensionsSet) {
|
|
2081
|
-
if (isAbsolute(specifier)) {
|
|
2483
|
+
if (isAbsolute$1(specifier)) {
|
|
2082
2484
|
const resolved = resolveAbsolutePathSpecifier(specifier, extensionsSet);
|
|
2083
2485
|
if (!resolved) return null;
|
|
2084
2486
|
return isPathWithinRoot(srcDir, resolved) ? resolved : null;
|
|
@@ -2087,6 +2489,76 @@ function sourceEntryForSpecifier(specifier, srcDir, extensionsSet) {
|
|
|
2087
2489
|
const subpath = specifier.slice(packageName.length + 1);
|
|
2088
2490
|
return resolveAbsolutePathSpecifier(join$1(srcDir, subpath || "index"), extensionsSet);
|
|
2089
2491
|
}
|
|
2492
|
+
/** The package's parsed `package.json`, or null when it cannot be read. */
|
|
2493
|
+
function readPackageManifest(pkgRoot) {
|
|
2494
|
+
try {
|
|
2495
|
+
return JSON.parse(readFileSync(join$1(pkgRoot, "package.json"), "utf-8"));
|
|
2496
|
+
} catch {
|
|
2497
|
+
return null;
|
|
2498
|
+
}
|
|
2499
|
+
}
|
|
2500
|
+
/** Each `exports` subpath and its value; a bare target or condition object
|
|
2501
|
+
* is the `.` entry. */
|
|
2502
|
+
function exportsSubpaths(manifest) {
|
|
2503
|
+
const exports = isJsonBlock(manifest) ? manifest.exports : void 0;
|
|
2504
|
+
if (exports === void 0 || exports === null) return [];
|
|
2505
|
+
const subpaths = isJsonBlock(exports) && Object.keys(exports).some((k) => k.startsWith(".")) ? exports : { ".": exports };
|
|
2506
|
+
return Object.entries(subpaths).filter(([subpath]) => subpath.startsWith("."));
|
|
2507
|
+
}
|
|
2508
|
+
/**
|
|
2509
|
+
* The export condition a kit gives each public entry's original source:
|
|
2510
|
+
* `{ "types": …, "animus": "./src/system.ts", "import": …, "default": … }`.
|
|
2511
|
+
* Hosts never resolve through it; discovery reads it, redirects the entry to
|
|
2512
|
+
* its source in every host, and the system loader prefers it.
|
|
2513
|
+
*/
|
|
2514
|
+
const KIT_SOURCE_CONDITION = "animus";
|
|
2515
|
+
/** The package's kit source condition, or null when no exact `exports`
|
|
2516
|
+
* entry declares it: the package is no source kit. */
|
|
2517
|
+
function readKitSourceCondition(pkgRoot) {
|
|
2518
|
+
const condition = {
|
|
2519
|
+
entries: [],
|
|
2520
|
+
invalid: [],
|
|
2521
|
+
root: null
|
|
2522
|
+
};
|
|
2523
|
+
for (const [subpath, value] of exportsSubpaths(readPackageManifest(pkgRoot))) {
|
|
2524
|
+
if (subpath.includes("*") || !isJsonBlock(value)) continue;
|
|
2525
|
+
const target = value[KIT_SOURCE_CONDITION];
|
|
2526
|
+
if (!isJsonString(target)) continue;
|
|
2527
|
+
const file = resolve$1(pkgRoot, target);
|
|
2528
|
+
if (isPathWithinRoot(pkgRoot, file) && isFile$1(file)) condition.entries.push([subpath, file]);
|
|
2529
|
+
else condition.invalid.push([subpath, target]);
|
|
2530
|
+
}
|
|
2531
|
+
if (condition.entries.length === 0 && condition.invalid.length === 0) return null;
|
|
2532
|
+
condition.root = condition.entries.reduce((root, [, file]) => root === null ? dirname$1(file) : commonDirectory(root, dirname$1(file)), null);
|
|
2533
|
+
return condition;
|
|
2534
|
+
}
|
|
2535
|
+
function commonDirectory(a, b) {
|
|
2536
|
+
let dir = a;
|
|
2537
|
+
while (!isPathWithinRoot(dir, b)) dir = dirname$1(dir);
|
|
2538
|
+
return dir;
|
|
2539
|
+
}
|
|
2540
|
+
/** Whether the manifest at `dir` names the package `name`. */
|
|
2541
|
+
function isPackageNamed(dir, name) {
|
|
2542
|
+
const manifest = readPackageManifest(dir);
|
|
2543
|
+
return isJsonBlock(manifest) && manifest.name === name;
|
|
2544
|
+
}
|
|
2545
|
+
function realPath(path) {
|
|
2546
|
+
try {
|
|
2547
|
+
return realpathSync(path);
|
|
2548
|
+
} catch {
|
|
2549
|
+
return path;
|
|
2550
|
+
}
|
|
2551
|
+
}
|
|
2552
|
+
/** The root of the installed package `name` as Node finds it from `fromDir`,
|
|
2553
|
+
* the first `node_modules/<name>` up the directory tree, at its real path,
|
|
2554
|
+
* as host resolvers report a linked package. */
|
|
2555
|
+
function locatePackageRoot(name, fromDir) {
|
|
2556
|
+
for (let dir = fromDir;; dir = dirname$1(dir)) {
|
|
2557
|
+
const candidate = join$1(dir, "node_modules", name);
|
|
2558
|
+
if (existsSync(join$1(candidate, "package.json"))) return realPath(candidate);
|
|
2559
|
+
if (dir === dirname$1(dir)) return null;
|
|
2560
|
+
}
|
|
2561
|
+
}
|
|
2090
2562
|
/**
|
|
2091
2563
|
* A package's `sideEffects` field, decoded the way Vite reads it: a boolean
|
|
2092
2564
|
* as written, or the list's globs, one with no `/` matching the basename
|
|
@@ -2095,12 +2567,7 @@ function sourceEntryForSpecifier(specifier, srcDir, extensionsSet) {
|
|
|
2095
2567
|
* extglobs), which leaves the bundler's side-effectful default.
|
|
2096
2568
|
*/
|
|
2097
2569
|
function readPackageSideEffects(pkgRoot) {
|
|
2098
|
-
|
|
2099
|
-
try {
|
|
2100
|
-
manifest = JSON.parse(readFileSync(join$1(pkgRoot, "package.json"), "utf-8"));
|
|
2101
|
-
} catch {
|
|
2102
|
-
return;
|
|
2103
|
-
}
|
|
2570
|
+
const manifest = readPackageManifest(pkgRoot);
|
|
2104
2571
|
const field = isJsonBlock(manifest) ? manifest.sideEffects : void 0;
|
|
2105
2572
|
if (field === true || field === false) return field;
|
|
2106
2573
|
if (!Array.isArray(field)) return void 0;
|
|
@@ -2108,9 +2575,80 @@ function readPackageSideEffects(pkgRoot) {
|
|
|
2108
2575
|
if (globs.some((glob) => /[{}[\]()!+@]/.test(glob))) return void 0;
|
|
2109
2576
|
return globs.map((glob) => globToRegExp(glob.includes("/") ? glob.replace(/^\.?\//, "") : `**/${glob}`));
|
|
2110
2577
|
}
|
|
2578
|
+
/** Whether a bundler that reads the nearest `package.json`'s `sideEffects`
|
|
2579
|
+
* against the file's own path keeps `absFile`; undefined without a field
|
|
2580
|
+
* it reads, which keeps the bundler's side-effectful default. */
|
|
2581
|
+
function fileSideEffects(absFile) {
|
|
2582
|
+
const pkgRoot = findPackageRoot(absFile);
|
|
2583
|
+
const field = readPackageSideEffects(pkgRoot);
|
|
2584
|
+
if (field === void 0 || field === true || field === false) return field;
|
|
2585
|
+
return matchesSideEffects(field, pkgRoot, [absFile]);
|
|
2586
|
+
}
|
|
2587
|
+
/** Whether a `sideEffects` glob can name a code file: a last segment with
|
|
2588
|
+
* no extension can name any file, and an extension with glob syntax or of
|
|
2589
|
+
* a script can name code; `*.css` cannot. Brace alternatives are each read,
|
|
2590
|
+
* and braces this reading cannot expand count as naming code. */
|
|
2591
|
+
function globNamesCode(glob) {
|
|
2592
|
+
const alternatives = expandBraces(glob);
|
|
2593
|
+
if (alternatives === null) return true;
|
|
2594
|
+
return alternatives.some((alternative) => {
|
|
2595
|
+
const last = alternative.split("/").at(-1) ?? "";
|
|
2596
|
+
const dot = last.lastIndexOf(".");
|
|
2597
|
+
if (dot === -1) return true;
|
|
2598
|
+
const extension = last.slice(dot + 1);
|
|
2599
|
+
return /[*?[]/.test(extension) || /^[cm]?[jt]sx?$/.test(extension);
|
|
2600
|
+
});
|
|
2601
|
+
}
|
|
2602
|
+
/** Every alternative a glob's `{a,b}` groups spell, nested groups included;
|
|
2603
|
+
* null when its braces do not balance. */
|
|
2604
|
+
function expandBraces(glob) {
|
|
2605
|
+
const open = glob.indexOf("{");
|
|
2606
|
+
if (open === -1) return glob.includes("}") ? null : [glob];
|
|
2607
|
+
let depth = 0;
|
|
2608
|
+
const commas = [];
|
|
2609
|
+
for (let at = open; at < glob.length; at++) if (glob[at] === "{") depth++;
|
|
2610
|
+
else if (glob[at] === "," && depth === 1) commas.push(at);
|
|
2611
|
+
else if (glob[at] === "}" && --depth === 0) {
|
|
2612
|
+
const bounds = [
|
|
2613
|
+
open,
|
|
2614
|
+
...commas,
|
|
2615
|
+
at
|
|
2616
|
+
];
|
|
2617
|
+
const rest = expandBraces(glob.slice(at + 1));
|
|
2618
|
+
if (rest === null) return null;
|
|
2619
|
+
const expanded = [];
|
|
2620
|
+
for (let i = 0; i < bounds.length - 1; i++) {
|
|
2621
|
+
const inner = expandBraces(glob.slice(bounds[i] + 1, bounds[i + 1]));
|
|
2622
|
+
if (inner === null) return null;
|
|
2623
|
+
for (const middle of inner) for (const tail of rest) expanded.push(glob.slice(0, open) + middle + tail);
|
|
2624
|
+
}
|
|
2625
|
+
return expanded;
|
|
2626
|
+
}
|
|
2627
|
+
return null;
|
|
2628
|
+
}
|
|
2629
|
+
/**
|
|
2630
|
+
* True when a source module of the package that owns `absFile` must stay
|
|
2631
|
+
* side-effectful: its `sideEffects` list names shipped code, and no proven
|
|
2632
|
+
* map takes a shipped file to its source module, so a bundler that reads
|
|
2633
|
+
* the list against the source path can drop a listed effect. Undefined
|
|
2634
|
+
* leaves the bundler's own reading, which no list naming code can mislead.
|
|
2635
|
+
*/
|
|
2636
|
+
function kitSourceModuleSideEffects(absFile) {
|
|
2637
|
+
const manifest = readPackageManifest(findPackageRoot(absFile));
|
|
2638
|
+
const field = isJsonBlock(manifest) ? manifest.sideEffects : void 0;
|
|
2639
|
+
if (!Array.isArray(field)) return void 0;
|
|
2640
|
+
return field.some((glob) => isJsonString(glob) && globNamesCode(glob)) ? true : void 0;
|
|
2641
|
+
}
|
|
2642
|
+
/** Whether a host keeps the module at `file` side-effectful: it lies under
|
|
2643
|
+
* one of the discovered kit source directories, and its package's list
|
|
2644
|
+
* could misread it (see `kitSourceModuleSideEffects`). A host asks this of
|
|
2645
|
+
* the resolved module, never of its importer alone. */
|
|
2646
|
+
function keepsKitSourceEffects(file, kitDirs) {
|
|
2647
|
+
return kitDirs.some((dir) => isPathWithinRoot(dir, file)) && kitSourceModuleSideEffects(file) === true;
|
|
2648
|
+
}
|
|
2111
2649
|
function matchesSideEffects(globs, pkgRoot, absFiles) {
|
|
2112
2650
|
return absFiles.some((file) => {
|
|
2113
|
-
const relPath = relative(pkgRoot, file).split(sep).join("/");
|
|
2651
|
+
const relPath = relative$1(pkgRoot, file).split(sep$1).join("/");
|
|
2114
2652
|
return globs.some((glob) => glob.test(relPath));
|
|
2115
2653
|
});
|
|
2116
2654
|
}
|
|
@@ -2131,20 +2669,11 @@ function exportTarget(value) {
|
|
|
2131
2669
|
/** Each exact `exports` entry of the package at `pkgRoot`, as its subpath
|
|
2132
2670
|
* and absolute target; `*` patterns name no single file and are skipped. */
|
|
2133
2671
|
function packageExportEntries(pkgRoot) {
|
|
2134
|
-
let manifest;
|
|
2135
|
-
try {
|
|
2136
|
-
manifest = JSON.parse(readFileSync(join$1(pkgRoot, "package.json"), "utf-8"));
|
|
2137
|
-
} catch {
|
|
2138
|
-
return [];
|
|
2139
|
-
}
|
|
2140
|
-
const exports = isJsonBlock(manifest) ? manifest.exports : void 0;
|
|
2141
|
-
if (exports === void 0 || exports === null) return [];
|
|
2142
|
-
const subpaths = isJsonBlock(exports) && Object.keys(exports).some((k) => k.startsWith(".")) ? exports : { ".": exports };
|
|
2143
2672
|
const entries = [];
|
|
2144
|
-
for (const [subpath, value] of
|
|
2145
|
-
if (
|
|
2673
|
+
for (const [subpath, value] of exportsSubpaths(readPackageManifest(pkgRoot))) {
|
|
2674
|
+
if (subpath.includes("*")) continue;
|
|
2146
2675
|
const target = exportTarget(value);
|
|
2147
|
-
if (target !== null) entries.push([subpath, resolve(pkgRoot, target)]);
|
|
2676
|
+
if (target !== null) entries.push([subpath, resolve$1(pkgRoot, target)]);
|
|
2148
2677
|
}
|
|
2149
2678
|
return entries;
|
|
2150
2679
|
}
|
|
@@ -2181,6 +2710,16 @@ async function collectExternalPackageSources(opts) {
|
|
|
2181
2710
|
const sourceEntrySideEffects = /* @__PURE__ */ new Map();
|
|
2182
2711
|
/** `replaced` names the entry the redirect stands in for; it is only
|
|
2183
2712
|
* needed, and so only resolved, for a glob list. */
|
|
2713
|
+
/** The entry `subpath` of the package resolves to without the redirect.
|
|
2714
|
+
* A resolver with no `import` condition, as Node's `require` has none,
|
|
2715
|
+
* finds no ESM-only entry, so the manifest's own target answers then. */
|
|
2716
|
+
const shippedEntry = async (specifier, pkgRoot, subpath) => {
|
|
2717
|
+
try {
|
|
2718
|
+
const resolved = await resolveSpecifier(specifier);
|
|
2719
|
+
if (resolved) return resolved;
|
|
2720
|
+
} catch {}
|
|
2721
|
+
return packageExportEntries(pkgRoot).find(([entry]) => entry === subpath)?.[1] ?? null;
|
|
2722
|
+
};
|
|
2184
2723
|
const redirect = async (specifier, srcEntry, pkgRoot, replaced) => {
|
|
2185
2724
|
sourceEntries.set(specifier, srcEntry);
|
|
2186
2725
|
const field = readPackageSideEffects(pkgRoot);
|
|
@@ -2193,6 +2732,8 @@ async function collectExternalPackageSources(opts) {
|
|
|
2193
2732
|
sourceEntrySideEffects.set(specifier, replacedEntry === null || matchesSideEffects(field, pkgRoot, [srcEntry, replacedEntry]));
|
|
2194
2733
|
};
|
|
2195
2734
|
const packageDirs = [];
|
|
2735
|
+
const linkedDirs = [];
|
|
2736
|
+
const outputDirs = [];
|
|
2196
2737
|
const dirOwnerSets = {};
|
|
2197
2738
|
const dirExtensions = {};
|
|
2198
2739
|
const fileOwners = {};
|
|
@@ -2201,6 +2742,31 @@ async function collectExternalPackageSources(opts) {
|
|
|
2201
2742
|
(dirOwnerSets[dir] ??= []).push(specifier);
|
|
2202
2743
|
};
|
|
2203
2744
|
const alreadyIngested = (relPath) => hasEntry(relPath) || pushed.has(relPath);
|
|
2745
|
+
/** Adds `file` to the analysis set unless it is there; false when it
|
|
2746
|
+
* cannot be read. */
|
|
2747
|
+
const ingest = (file, specifier) => {
|
|
2748
|
+
const relPath = relative$1(rootDir, file);
|
|
2749
|
+
if (alreadyIngested(relPath)) return true;
|
|
2750
|
+
let source;
|
|
2751
|
+
try {
|
|
2752
|
+
source = readFileSync(file, "utf-8");
|
|
2753
|
+
} catch (err) {
|
|
2754
|
+
onUnreadable(relPath, err);
|
|
2755
|
+
return false;
|
|
2756
|
+
}
|
|
2757
|
+
onSourceRead?.(source, relPath, file);
|
|
2758
|
+
entries.push({
|
|
2759
|
+
path: relPath,
|
|
2760
|
+
source
|
|
2761
|
+
});
|
|
2762
|
+
pushed.add(relPath);
|
|
2763
|
+
fileOwners[relPath] ??= specifier;
|
|
2764
|
+
return true;
|
|
2765
|
+
};
|
|
2766
|
+
const diagnostics = [];
|
|
2767
|
+
const reportedPackages = /* @__PURE__ */ new Set();
|
|
2768
|
+
const kitDescriptors = [];
|
|
2769
|
+
const describedRoots = /* @__PURE__ */ new Set();
|
|
2204
2770
|
for (const specifier of specifiers) {
|
|
2205
2771
|
let absEntry;
|
|
2206
2772
|
try {
|
|
@@ -2208,8 +2774,15 @@ async function collectExternalPackageSources(opts) {
|
|
|
2208
2774
|
} catch {
|
|
2209
2775
|
absEntry = null;
|
|
2210
2776
|
}
|
|
2211
|
-
if (!absEntry && isAbsolute(specifier)) absEntry = resolveAbsolutePathSpecifier(specifier, extensionsSet);
|
|
2212
|
-
|
|
2777
|
+
if (!absEntry && isAbsolute$1(specifier)) absEntry = resolveAbsolutePathSpecifier(specifier, extensionsSet);
|
|
2778
|
+
const packageName = isAbsolute$1(specifier) ? null : bareSpecifierPackageName(specifier);
|
|
2779
|
+
const resolvedRoot = absEntry ? findPackageRoot(absEntry) : null;
|
|
2780
|
+
const locatedRoot = packageName && locatePackageRoot(packageName, rootDir);
|
|
2781
|
+
const pkgRoot = resolvedRoot && (!locatedRoot || isPackageNamed(resolvedRoot, packageName) || realPath(resolvedRoot) === locatedRoot) ? resolvedRoot : locatedRoot || resolvedRoot;
|
|
2782
|
+
const condition = packageName && pkgRoot ? readKitSourceCondition(pkgRoot) : null;
|
|
2783
|
+
const entryKey = packageName && `.${specifier.slice(packageName.length)}`;
|
|
2784
|
+
absEntry ??= condition?.entries.find(([entry]) => entry === entryKey)?.[1] ?? null;
|
|
2785
|
+
if (!absEntry || !pkgRoot) {
|
|
2213
2786
|
outcomes.push({
|
|
2214
2787
|
specifier,
|
|
2215
2788
|
outcome: "unresolvable",
|
|
@@ -2217,106 +2790,82 @@ async function collectExternalPackageSources(opts) {
|
|
|
2217
2790
|
});
|
|
2218
2791
|
continue;
|
|
2219
2792
|
}
|
|
2220
|
-
const
|
|
2793
|
+
const realRoot = realPath(pkgRoot);
|
|
2794
|
+
if (!describedRoots.has(realRoot)) {
|
|
2795
|
+
describedRoots.add(realRoot);
|
|
2796
|
+
const described = readKitDescriptor(pkgRoot, rootDir);
|
|
2797
|
+
if (described) kitDescriptors.push(described);
|
|
2798
|
+
}
|
|
2799
|
+
const linked = !isInstalledPackage(pkgRoot);
|
|
2221
2800
|
const srcDir = join$1(pkgRoot, "src");
|
|
2222
2801
|
let fileCount = 0;
|
|
2223
2802
|
let staleDist = false;
|
|
2224
|
-
if (
|
|
2803
|
+
if (packageName && !reportedPackages.has(packageName)) {
|
|
2804
|
+
reportedPackages.add(packageName);
|
|
2805
|
+
diagnostics.push(...sourceConditionDiagnostics(packageName, condition));
|
|
2806
|
+
}
|
|
2807
|
+
if (condition && packageName) {
|
|
2808
|
+
for (const [entry, target] of condition.entries) {
|
|
2809
|
+
const entrySpecifier = packageName + entry.slice(1);
|
|
2810
|
+
packageMap[entrySpecifier] = relative$1(rootDir, target);
|
|
2811
|
+
await redirect(entrySpecifier, target, pkgRoot, () => shippedEntry(entrySpecifier, pkgRoot, entry));
|
|
2812
|
+
}
|
|
2813
|
+
packageMap[specifier] ??= relative$1(rootDir, absEntry);
|
|
2814
|
+
if (condition.root) {
|
|
2815
|
+
packageDirs.push(condition.root);
|
|
2816
|
+
if (linked) linkedDirs.push(condition.root);
|
|
2817
|
+
claimDir(condition.root, specifier);
|
|
2818
|
+
dirExtensions[condition.root] = [...extensionsSet];
|
|
2819
|
+
onPackageResolved?.(specifier, condition.root);
|
|
2820
|
+
for (const file of walkPackageSources(condition.root, extensionsSet)) if (ingest(file, specifier)) fileCount++;
|
|
2821
|
+
}
|
|
2822
|
+
} else if (existsSync(srcDir)) {
|
|
2225
2823
|
packageDirs.push(srcDir);
|
|
2824
|
+
if (linked) linkedDirs.push(srcDir);
|
|
2226
2825
|
claimDir(srcDir, specifier);
|
|
2227
2826
|
dirExtensions[srcDir] = [...extensionsSet];
|
|
2228
2827
|
onPackageResolved?.(specifier, srcDir);
|
|
2229
2828
|
const srcEntry = sourceEntryForSpecifier(specifier, srcDir, extensionsSet);
|
|
2230
2829
|
if (srcEntry) {
|
|
2231
|
-
packageMap[specifier] = relative(rootDir, srcEntry);
|
|
2830
|
+
packageMap[specifier] = relative$1(rootDir, srcEntry);
|
|
2232
2831
|
const replaced = absEntry;
|
|
2233
2832
|
await redirect(specifier, srcEntry, pkgRoot, async () => replaced);
|
|
2234
|
-
} else packageMap[specifier] = relative(rootDir, absEntry);
|
|
2235
|
-
if (
|
|
2236
|
-
const packageName = bareSpecifierPackageName(specifier);
|
|
2833
|
+
} else packageMap[specifier] = relative$1(rootDir, absEntry);
|
|
2834
|
+
if (packageName) {
|
|
2237
2835
|
if (packageName !== specifier && !(packageName in packageMap)) {
|
|
2238
2836
|
const rootEntry = sourceEntryForSpecifier(packageName, srcDir, extensionsSet);
|
|
2239
2837
|
if (rootEntry) {
|
|
2240
|
-
packageMap[packageName] = relative(rootDir, rootEntry);
|
|
2241
|
-
await redirect(packageName, rootEntry, pkgRoot,
|
|
2242
|
-
try {
|
|
2243
|
-
return await resolveSpecifier(packageName);
|
|
2244
|
-
} catch {
|
|
2245
|
-
return null;
|
|
2246
|
-
}
|
|
2247
|
-
});
|
|
2838
|
+
packageMap[packageName] = relative$1(rootDir, rootEntry);
|
|
2839
|
+
await redirect(packageName, rootEntry, pkgRoot, () => shippedEntry(packageName, pkgRoot, "."));
|
|
2248
2840
|
}
|
|
2249
2841
|
}
|
|
2250
2842
|
}
|
|
2251
2843
|
const pkgFiles = walkPackageSources(srcDir, extensionsSet);
|
|
2252
|
-
staleDist =
|
|
2253
|
-
for (const pkgFile of pkgFiles)
|
|
2254
|
-
const relPath = relative(rootDir, pkgFile);
|
|
2255
|
-
if (alreadyIngested(relPath)) {
|
|
2256
|
-
fileCount++;
|
|
2257
|
-
continue;
|
|
2258
|
-
}
|
|
2259
|
-
let source;
|
|
2260
|
-
try {
|
|
2261
|
-
source = readFileSync(pkgFile, "utf-8");
|
|
2262
|
-
} catch (err) {
|
|
2263
|
-
onUnreadable(relPath, err);
|
|
2264
|
-
continue;
|
|
2265
|
-
}
|
|
2266
|
-
onSourceRead?.(source, relPath, pkgFile);
|
|
2267
|
-
entries.push({
|
|
2268
|
-
path: relPath,
|
|
2269
|
-
source
|
|
2270
|
-
});
|
|
2271
|
-
pushed.add(relPath);
|
|
2272
|
-
fileOwners[relPath] ??= specifier;
|
|
2273
|
-
fileCount++;
|
|
2274
|
-
}
|
|
2844
|
+
staleDist = linked && distEntryIsStale(absEntry, srcDir, pkgFiles);
|
|
2845
|
+
for (const pkgFile of pkgFiles) if (ingest(pkgFile, specifier)) fileCount++;
|
|
2275
2846
|
} else {
|
|
2276
2847
|
const outputDir = dirname$1(absEntry);
|
|
2277
2848
|
packageDirs.push(outputDir);
|
|
2849
|
+
if (linked) linkedDirs.push(outputDir);
|
|
2850
|
+
outputDirs.push(outputDir);
|
|
2278
2851
|
claimDir(outputDir, specifier);
|
|
2279
2852
|
onPackageResolved?.(specifier, outputDir);
|
|
2280
|
-
packageMap[specifier] = relative(rootDir, absEntry);
|
|
2853
|
+
packageMap[specifier] = relative$1(rootDir, absEntry);
|
|
2281
2854
|
const outputExtensions = new Set(extensionsSet);
|
|
2282
|
-
outputExtensions.add(extname(absEntry));
|
|
2855
|
+
outputExtensions.add(extname$1(absEntry));
|
|
2283
2856
|
dirExtensions[outputDir] = [...outputExtensions];
|
|
2284
2857
|
const outputFiles = discoverFiles(outputDir, outputDir, void 0, outputExtensions).filter((file) => {
|
|
2285
|
-
const relToOutput = relative(outputDir, file);
|
|
2858
|
+
const relToOutput = relative$1(outputDir, file);
|
|
2286
2859
|
return !PACKAGE_OUTPUT_EXCLUDES.some((pattern) => relToOutput.includes(pattern));
|
|
2287
2860
|
});
|
|
2288
2861
|
if (!outputFiles.includes(absEntry)) outputFiles.unshift(absEntry);
|
|
2289
|
-
for (const outputFile of outputFiles)
|
|
2290
|
-
const outputRelPath = relative(rootDir, outputFile);
|
|
2291
|
-
if (alreadyIngested(outputRelPath)) {
|
|
2292
|
-
fileCount++;
|
|
2293
|
-
continue;
|
|
2294
|
-
}
|
|
2295
|
-
let source;
|
|
2296
|
-
try {
|
|
2297
|
-
source = readFileSync(outputFile, "utf-8");
|
|
2298
|
-
} catch (err) {
|
|
2299
|
-
onUnreadable(outputRelPath, err);
|
|
2300
|
-
continue;
|
|
2301
|
-
}
|
|
2302
|
-
onSourceRead?.(source, outputRelPath, outputFile);
|
|
2303
|
-
entries.push({
|
|
2304
|
-
path: outputRelPath,
|
|
2305
|
-
source
|
|
2306
|
-
});
|
|
2307
|
-
pushed.add(outputRelPath);
|
|
2308
|
-
fileOwners[outputRelPath] ??= specifier;
|
|
2309
|
-
fileCount++;
|
|
2310
|
-
}
|
|
2862
|
+
for (const outputFile of outputFiles) if (ingest(outputFile, specifier)) fileCount++;
|
|
2311
2863
|
}
|
|
2312
|
-
if (
|
|
2313
|
-
const
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
if (entrySpecifier in packageMap || !alreadyIngested(targetRelPath)) continue;
|
|
2318
|
-
packageMap[entrySpecifier] = targetRelPath;
|
|
2319
|
-
}
|
|
2864
|
+
if (packageName) for (const [subpath, target] of packageExportEntries(pkgRoot)) {
|
|
2865
|
+
const entrySpecifier = packageName + subpath.slice(1);
|
|
2866
|
+
const targetRelPath = relative$1(rootDir, target);
|
|
2867
|
+
if (entrySpecifier in packageMap || !alreadyIngested(targetRelPath)) continue;
|
|
2868
|
+
packageMap[entrySpecifier] = targetRelPath;
|
|
2320
2869
|
}
|
|
2321
2870
|
outcomes.push({
|
|
2322
2871
|
specifier,
|
|
@@ -2330,12 +2879,112 @@ async function collectExternalPackageSources(opts) {
|
|
|
2330
2879
|
sourceEntries,
|
|
2331
2880
|
sourceEntrySideEffects,
|
|
2332
2881
|
packageDirs,
|
|
2882
|
+
linkedDirs,
|
|
2883
|
+
outputDirs,
|
|
2333
2884
|
dirOwnerSets,
|
|
2334
2885
|
dirExtensions,
|
|
2335
2886
|
fileOwners,
|
|
2336
|
-
outcomes
|
|
2887
|
+
outcomes,
|
|
2888
|
+
diagnostics: [...diagnostics, ...noKitFilesDiagnostics(outcomes)],
|
|
2889
|
+
kitDescriptors
|
|
2337
2890
|
};
|
|
2338
2891
|
}
|
|
2892
|
+
/** The dependency names a manifest lists in `fields`. */
|
|
2893
|
+
function dependencyNames(manifest, fields) {
|
|
2894
|
+
if (!isJsonBlock(manifest)) return [];
|
|
2895
|
+
const names = /* @__PURE__ */ new Set();
|
|
2896
|
+
for (const field of fields) {
|
|
2897
|
+
const deps = manifest[field];
|
|
2898
|
+
if (isJsonBlock(deps)) for (const name of Object.keys(deps)) names.add(name);
|
|
2899
|
+
}
|
|
2900
|
+
return [...names];
|
|
2901
|
+
}
|
|
2902
|
+
/** The source kits the application at `rootDir` declares as dependencies.
|
|
2903
|
+
* A bundler serves an installed one as compiled code unless told
|
|
2904
|
+
* otherwise: Next keeps it external on the server unless it is in
|
|
2905
|
+
* `transpilePackages`, and Vite's optimizer prebundles it. */
|
|
2906
|
+
function sourceKitDependencies(rootDir) {
|
|
2907
|
+
return dependencyNames(readPackageManifest(rootDir), ["dependencies", "devDependencies"]).flatMap((name) => {
|
|
2908
|
+
const pkgRoot = locatePackageRoot(name, rootDir);
|
|
2909
|
+
if (pkgRoot === null || readKitSourceCondition(pkgRoot) === null) return [];
|
|
2910
|
+
return [{
|
|
2911
|
+
name,
|
|
2912
|
+
installed: isInstalledPackage(pkgRoot),
|
|
2913
|
+
dependencies: dependencyNames(readPackageManifest(pkgRoot), ["dependencies"])
|
|
2914
|
+
}];
|
|
2915
|
+
});
|
|
2916
|
+
}
|
|
2917
|
+
/** A specifier that names a package: not relative or absolute, and no URL,
|
|
2918
|
+
* scheme (`node:`, `virtual:`), subpath import or virtual id. */
|
|
2919
|
+
function isPackageSpecifier(specifier) {
|
|
2920
|
+
return /^(?:@[^/\\:]+\/)?[^./\\#\0:][^:]*$/.test(specifier);
|
|
2921
|
+
}
|
|
2922
|
+
/**
|
|
2923
|
+
* An error for each kit the application's own `files` import or re-export
|
|
2924
|
+
* whose system the application's system does not include: a package that
|
|
2925
|
+
* declares the kit source condition, with no kit the system extends
|
|
2926
|
+
* (`systemKits`) in that package. One app build has exactly one system, and
|
|
2927
|
+
* every kit's system is part of it, so such a kit is not extracted against
|
|
2928
|
+
* a system it was not built for. One batch parse reads the imports of every
|
|
2929
|
+
* file the engine parses as it is; an adapted source (`.svelte`, `.mdx`)
|
|
2930
|
+
* reaches the engine only as its generated children.
|
|
2931
|
+
*/
|
|
2932
|
+
function importedKitDiagnostics(files, engine, rootDir, systemKits) {
|
|
2933
|
+
const { extractFacts } = engine;
|
|
2934
|
+
const parsed = files.filter(({ path }) => isEngineTransformExtension(path));
|
|
2935
|
+
if (!extractFacts || parsed.length === 0) return [];
|
|
2936
|
+
let facts;
|
|
2937
|
+
try {
|
|
2938
|
+
facts = parseInternalWire(extractFacts(JSON.stringify(parsed.map(({ path, source }) => ({
|
|
2939
|
+
path,
|
|
2940
|
+
source
|
|
2941
|
+
})))), "extractFacts");
|
|
2942
|
+
} catch {
|
|
2943
|
+
return [];
|
|
2944
|
+
}
|
|
2945
|
+
const seen = new Set(systemKits.filter((specifier) => !isAbsolute$1(specifier)).map(bareSpecifierPackageName));
|
|
2946
|
+
const diagnostics = [];
|
|
2947
|
+
for (const [path, file] of Object.entries(facts.files)) {
|
|
2948
|
+
if (file.parsePanicked) continue;
|
|
2949
|
+
const specifiers = [...file.imports.map((fact) => fact.source), ...file.exports.flatMap((fact) => fact.source === null ? [] : [fact.source])];
|
|
2950
|
+
for (const specifier of specifiers) {
|
|
2951
|
+
if (!isPackageSpecifier(specifier)) continue;
|
|
2952
|
+
const name = bareSpecifierPackageName(specifier);
|
|
2953
|
+
if (seen.has(name)) continue;
|
|
2954
|
+
seen.add(name);
|
|
2955
|
+
const pkgRoot = locatePackageRoot(name, dirname$1(resolve$1(rootDir, path)));
|
|
2956
|
+
if (pkgRoot && readKitSourceCondition(pkgRoot)) diagnostics.push({
|
|
2957
|
+
file: path,
|
|
2958
|
+
component: name,
|
|
2959
|
+
kind: "warn",
|
|
2960
|
+
message: `the application imports this kit, but its system is not included in the application's system, so its components are not extracted — include the kit's system in the application's system: createSystem().extend(<the kit's system>)`,
|
|
2961
|
+
code: KIT_SYSTEM_NOT_INCLUDED,
|
|
2962
|
+
severity: severityFor(KIT_SYSTEM_NOT_INCLUDED)
|
|
2963
|
+
});
|
|
2964
|
+
}
|
|
2965
|
+
}
|
|
2966
|
+
return diagnostics;
|
|
2967
|
+
}
|
|
2968
|
+
/** A kit without the source condition, warned once, or each condition entry
|
|
2969
|
+
* whose target is missing or outside the package. */
|
|
2970
|
+
function sourceConditionDiagnostics(packageName, condition) {
|
|
2971
|
+
if (condition === null) return [{
|
|
2972
|
+
file: packageName,
|
|
2973
|
+
component: "kit",
|
|
2974
|
+
kind: "warn",
|
|
2975
|
+
message: `declares no "${KIT_SOURCE_CONDITION}" export condition, so discovery guesses its source from src/ — give each public exports entry an "${KIT_SOURCE_CONDITION}" condition naming its original source`,
|
|
2976
|
+
code: KIT_WITHOUT_SOURCE_CONDITION,
|
|
2977
|
+
severity: severityFor(KIT_WITHOUT_SOURCE_CONDITION)
|
|
2978
|
+
}];
|
|
2979
|
+
return condition.invalid.map(([subpath, target]) => ({
|
|
2980
|
+
file: packageName,
|
|
2981
|
+
component: "kit",
|
|
2982
|
+
kind: "warn",
|
|
2983
|
+
message: `exports["${subpath}"].${KIT_SOURCE_CONDITION} names ${target}, which is missing or outside the package, so that entry is not read from source`,
|
|
2984
|
+
code: INVALID_KIT_SOURCE_CONDITION,
|
|
2985
|
+
severity: severityFor(INVALID_KIT_SOURCE_CONDITION)
|
|
2986
|
+
}));
|
|
2987
|
+
}
|
|
2339
2988
|
function firstOwners(dirOwnerSets) {
|
|
2340
2989
|
const owners = {};
|
|
2341
2990
|
for (const [dir, specifiers] of Object.entries(dirOwnerSets)) if (specifiers.length > 0) owners[dir] = specifiers[0];
|
|
@@ -2348,7 +2997,7 @@ function excludeCollectedPackages(collected, rejectedSpecifiers, rootDir) {
|
|
|
2348
2997
|
const targetRejected = (specifier, absTarget) => rejectedSpecifiers.has(specifier) || underRejectedDir(absTarget);
|
|
2349
2998
|
const packageMap = {};
|
|
2350
2999
|
for (const [specifier, relTarget] of Object.entries(collected.packageMap)) {
|
|
2351
|
-
if (targetRejected(specifier, resolve(rootDir, relTarget))) continue;
|
|
3000
|
+
if (targetRejected(specifier, resolve$1(rootDir, relTarget))) continue;
|
|
2352
3001
|
packageMap[specifier] = relTarget;
|
|
2353
3002
|
}
|
|
2354
3003
|
const sourceEntries = /* @__PURE__ */ new Map();
|
|
@@ -2381,10 +3030,14 @@ function excludeCollectedPackages(collected, rejectedSpecifiers, rootDir) {
|
|
|
2381
3030
|
sourceEntries,
|
|
2382
3031
|
sourceEntrySideEffects,
|
|
2383
3032
|
packageDirs: collected.packageDirs.filter((dir) => !rejectedDirs.includes(dir)),
|
|
3033
|
+
linkedDirs: collected.linkedDirs.filter((dir) => !rejectedDirs.includes(dir)),
|
|
3034
|
+
outputDirs: collected.outputDirs.filter((dir) => !rejectedDirs.includes(dir)),
|
|
2384
3035
|
dirOwnerSets,
|
|
2385
3036
|
dirExtensions,
|
|
2386
3037
|
fileOwners,
|
|
2387
|
-
outcomes: collected.outcomes
|
|
3038
|
+
outcomes: collected.outcomes,
|
|
3039
|
+
diagnostics: collected.diagnostics,
|
|
3040
|
+
kitDescriptors: collected.kitDescriptors.filter((record) => !rejectedDirs.some((dir) => isPathWithinRoot(resolve$1(rootDir, record.packageRoot), dir)))
|
|
2388
3041
|
};
|
|
2389
3042
|
}
|
|
2390
3043
|
function unresolvableIncludesMessage(outcomes) {
|
|
@@ -2392,29 +3045,224 @@ function unresolvableIncludesMessage(outcomes) {
|
|
|
2392
3045
|
if (unresolvable.length === 0) return null;
|
|
2393
3046
|
return `[animus-extract] unresolvable include specifier(s): ${unresolvable.join(", ")}`;
|
|
2394
3047
|
}
|
|
2395
|
-
/** A stale dist silently skews merged registry content while discovery
|
|
2396
|
-
* compiles the fresh sources; null when no specifier is stale. */
|
|
2397
|
-
function staleDistIncludesMessage(outcomes) {
|
|
2398
|
-
const stale = outcomes.filter((record) => record.outcome === "stale-dist").map((record) => record.specifier);
|
|
2399
|
-
if (stale.length === 0) return null;
|
|
2400
|
-
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`;
|
|
3048
|
+
/** A stale dist silently skews merged registry content while discovery
|
|
3049
|
+
* compiles the fresh sources; null when no specifier is stale. */
|
|
3050
|
+
function staleDistIncludesMessage(outcomes) {
|
|
3051
|
+
const stale = outcomes.filter((record) => record.outcome === "stale-dist").map((record) => record.specifier);
|
|
3052
|
+
if (stale.length === 0) return null;
|
|
3053
|
+
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`;
|
|
3054
|
+
}
|
|
3055
|
+
/** The engine's own parse of a module's bindings, through its
|
|
3056
|
+
* `extractFacts`; undefined for an engine without one. A parse that
|
|
3057
|
+
* panicked counts as no parse. */
|
|
3058
|
+
function engineModuleParser(engine) {
|
|
3059
|
+
const { extractFacts } = engine;
|
|
3060
|
+
if (!extractFacts) return void 0;
|
|
3061
|
+
return (source, path) => {
|
|
3062
|
+
try {
|
|
3063
|
+
const facts = parseInternalWire(extractFacts(JSON.stringify([{
|
|
3064
|
+
path,
|
|
3065
|
+
source
|
|
3066
|
+
}])), "extractFacts").files[path];
|
|
3067
|
+
return facts && !facts.parsePanicked ? {
|
|
3068
|
+
imports: facts.imports,
|
|
3069
|
+
exports: facts.exports,
|
|
3070
|
+
unboundCreateSystemCalls: facts.unboundCreateSystemCalls ?? []
|
|
3071
|
+
} : null;
|
|
3072
|
+
} catch {
|
|
3073
|
+
return null;
|
|
3074
|
+
}
|
|
3075
|
+
};
|
|
3076
|
+
}
|
|
3077
|
+
const ANIMUS_SYSTEM_SPECIFIER = /^@animus-ui\/system(?:\/.*)?$/;
|
|
3078
|
+
/** The file a specifier names from `importer`: a relative one by the
|
|
3079
|
+
* ingestion's probe order, a bare one by the host's package resolution. */
|
|
3080
|
+
async function resolveModuleFile(importer, specifier, resolvePackage) {
|
|
3081
|
+
if (specifier.startsWith(".")) return relativeSourceCandidates(importer, specifier).find(isFile$1) ?? null;
|
|
3082
|
+
try {
|
|
3083
|
+
return await resolvePackage?.(specifier) ?? null;
|
|
3084
|
+
} catch {
|
|
3085
|
+
return null;
|
|
3086
|
+
}
|
|
3087
|
+
}
|
|
3088
|
+
const escapeRegExp$1 = (text) => text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3089
|
+
/**
|
|
3090
|
+
* Follows `name`, exported by the module `specifier` names from `importer`,
|
|
3091
|
+
* through re-exports to its declaration. It is Animus's factory only when
|
|
3092
|
+
* that is `createSystem` of `@animus-ui/system`, and another identity only
|
|
3093
|
+
* when a module is shown to declare it; anything discovery cannot follow is
|
|
3094
|
+
* unknown.
|
|
3095
|
+
*/
|
|
3096
|
+
async function exportIdentity(importer, specifier, name, following, seen) {
|
|
3097
|
+
if (ANIMUS_SYSTEM_SPECIFIER.test(specifier)) return name === "createSystem" ? { kind: "animus" } : {
|
|
3098
|
+
kind: "other",
|
|
3099
|
+
what: `'${name}' of ${specifier}`
|
|
3100
|
+
};
|
|
3101
|
+
const file = await resolveModuleFile(importer, specifier, following.resolvePackage);
|
|
3102
|
+
if (file === null) return {
|
|
3103
|
+
kind: "unknown",
|
|
3104
|
+
why: `'${specifier}' could not be resolved`
|
|
3105
|
+
};
|
|
3106
|
+
const key = `${file}#${name}`;
|
|
3107
|
+
if (seen.has(key)) return {
|
|
3108
|
+
kind: "unknown",
|
|
3109
|
+
why: `'${specifier}' re-exports it in a cycle`
|
|
3110
|
+
};
|
|
3111
|
+
seen.add(key);
|
|
3112
|
+
let source;
|
|
3113
|
+
try {
|
|
3114
|
+
source = readFileSync(file, "utf-8");
|
|
3115
|
+
} catch {
|
|
3116
|
+
return {
|
|
3117
|
+
kind: "unknown",
|
|
3118
|
+
why: `'${specifier}' could not be read`
|
|
3119
|
+
};
|
|
3120
|
+
}
|
|
3121
|
+
const record = following.parseModule(source, file);
|
|
3122
|
+
if (record === null) return {
|
|
3123
|
+
kind: "unknown",
|
|
3124
|
+
why: `'${specifier}' could not be parsed`
|
|
3125
|
+
};
|
|
3126
|
+
const exported = record.exports.find((entry) => entry.exported === name);
|
|
3127
|
+
if (exported?.source) return exportIdentity(file, exported.source, exported.original ?? name, following, seen);
|
|
3128
|
+
const local = exported?.local ?? name;
|
|
3129
|
+
const imported = record.imports.find((entry) => entry.local === local);
|
|
3130
|
+
if (exported && imported) return exportIdentity(file, imported.source, imported.imported, following, seen);
|
|
3131
|
+
if (exported || new RegExp(`\\bexport\\s+(?:async\\s+)?(?:function\\*?|class|const|let|var)\\s+${escapeRegExp$1(name)}\\b`).test(source)) return {
|
|
3132
|
+
kind: "other",
|
|
3133
|
+
what: `the '${name}' that '${specifier}' declares`
|
|
3134
|
+
};
|
|
3135
|
+
const stars = [];
|
|
3136
|
+
for (const match of source.matchAll(/\bexport\s*\*\s*from\s*['"]([^'"]+)['"]/g)) stars.push(await exportIdentity(file, match[1], name, following, seen));
|
|
3137
|
+
const definite = stars.filter((identity) => identity.kind !== "unknown");
|
|
3138
|
+
if (definite.length === 1) return definite[0];
|
|
3139
|
+
if (definite.length > 1) return {
|
|
3140
|
+
kind: "unknown",
|
|
3141
|
+
why: `more than one \`export *\` of '${specifier}' provides '${name}'`
|
|
3142
|
+
};
|
|
3143
|
+
return stars.find((identity) => identity.kind === "unknown") ?? {
|
|
3144
|
+
kind: "unknown",
|
|
3145
|
+
why: `'${specifier}' has no export '${name}' discovery can read`
|
|
3146
|
+
};
|
|
3147
|
+
}
|
|
3148
|
+
/** 1-based line and column of `offset` in `source`. */
|
|
3149
|
+
function locationIn(source, offset) {
|
|
3150
|
+
const before = source.slice(0, offset);
|
|
3151
|
+
const lineStart = before.lastIndexOf("\n") + 1;
|
|
3152
|
+
return {
|
|
3153
|
+
line: before.split("\n").length,
|
|
3154
|
+
column: offset - lineStart + 1
|
|
3155
|
+
};
|
|
2401
3156
|
}
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
*
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
3157
|
+
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;
|
|
3158
|
+
/**
|
|
3159
|
+
* The callees that are Animus's `createSystem` in a parsed system file. A
|
|
3160
|
+
* binding counts once its identity is followed to the factory: a named
|
|
3161
|
+
* import, through local and package re-exports; a namespace import's
|
|
3162
|
+
* member; or a property destructured from a namespace import, `require` or
|
|
3163
|
+
* `import()` of a module. A binding named `createSystem` shown to be
|
|
3164
|
+
* something else is no root. One whose identity discovery cannot follow
|
|
3165
|
+
* keeps the admission spelling gave it, so a resolution limit never drops a
|
|
3166
|
+
* kit. Both are reported. A call of the bare name, which the file neither
|
|
3167
|
+
* imports nor declares, is no root: the system loader evaluates the file
|
|
3168
|
+
* without auto-imports, where the name is undefined. It is reported once.
|
|
3169
|
+
*/
|
|
3170
|
+
async function systemRoots(systemFilePath, source, record, following) {
|
|
3171
|
+
const { imports } = record;
|
|
3172
|
+
const roots = /* @__PURE__ */ new Set();
|
|
3173
|
+
const identityOf = (specifier, name) => exportIdentity(systemFilePath, specifier, name, following, /* @__PURE__ */ new Set());
|
|
3174
|
+
const called = (callee) => callOf(callee).test(source);
|
|
3175
|
+
/** Admits a binding named `createSystem` by its identity: an unknown one
|
|
3176
|
+
* keeps `spellingAdmits`, and every unproven one is reported. */
|
|
3177
|
+
const admit = (local, offset, identity, spellingAdmits) => {
|
|
3178
|
+
if (identity.kind === "animus") {
|
|
3179
|
+
roots.add(local);
|
|
3180
|
+
return;
|
|
3181
|
+
}
|
|
3182
|
+
if (identity.kind === "unknown" && spellingAdmits) roots.add(local);
|
|
3183
|
+
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`;
|
|
3184
|
+
following.report?.({
|
|
3185
|
+
file: systemFilePath,
|
|
3186
|
+
component: local,
|
|
3187
|
+
kind: "warn",
|
|
3188
|
+
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`,
|
|
3189
|
+
code: UNPROVEN_ROOT_BINDING,
|
|
3190
|
+
severity: severityFor(UNPROVEN_ROOT_BINDING),
|
|
3191
|
+
...locationIn(source, offset)
|
|
3192
|
+
});
|
|
3193
|
+
};
|
|
3194
|
+
for (const binding of imports) {
|
|
3195
|
+
if (binding.imported === "createSystem" || binding.local === "createSystem") {
|
|
3196
|
+
const statement = new RegExp(`\\bimport\\b[^;]*?\\bfrom\\s*['"]${escapeRegExp$1(binding.source)}['"]`, "g");
|
|
3197
|
+
let offset = 0;
|
|
3198
|
+
for (const match of source.matchAll(statement)) {
|
|
3199
|
+
const at = match[0].search(new RegExp(`(?<![a-zA-Z0-9_$])${escapeRegExp$1(binding.local)}\\b`));
|
|
3200
|
+
if (at !== -1) {
|
|
3201
|
+
offset = match.index + at;
|
|
3202
|
+
break;
|
|
3203
|
+
}
|
|
3204
|
+
}
|
|
3205
|
+
admit(binding.local, offset, await identityOf(binding.source, binding.imported), binding.imported === "createSystem");
|
|
3206
|
+
continue;
|
|
3207
|
+
}
|
|
3208
|
+
if (binding.source.startsWith(".") && called(binding.local) && (await identityOf(binding.source, binding.imported)).kind === "animus") roots.add(binding.local);
|
|
3209
|
+
}
|
|
3210
|
+
const namespaces = /* @__PURE__ */ new Map();
|
|
3211
|
+
for (const [, namespace, specifier] of source.matchAll(NAMESPACE_IMPORT)) {
|
|
3212
|
+
namespaces.set(namespace, specifier);
|
|
3213
|
+
const member = `${namespace}.createSystem`;
|
|
3214
|
+
if (ANIMUS_SYSTEM_SPECIFIER.test(specifier) || specifier.startsWith(".") && called(member) && (await identityOf(specifier, "createSystem")).kind === "animus") roots.add(member);
|
|
3215
|
+
}
|
|
3216
|
+
const destructuredFrom = (initializer) => {
|
|
3217
|
+
const loaded = /^\(?\s*(?:(?:await\s+)?import|require)\(\s*['"]([^'"]+)['"]\s*\)\s*\)?$/.exec(initializer);
|
|
3218
|
+
if (loaded) return identityOf(loaded[1], "createSystem");
|
|
3219
|
+
const namespace = namespaces.get(initializer);
|
|
3220
|
+
if (namespace !== void 0) return identityOf(namespace, "createSystem");
|
|
3221
|
+
return Promise.resolve({
|
|
3222
|
+
kind: "unknown",
|
|
3223
|
+
why: `it is destructured from '${initializer}', which is not a module namespace`
|
|
3224
|
+
});
|
|
3225
|
+
};
|
|
3226
|
+
for (const { local, offset, initializer } of destructuredCreateSystems(source)) admit(local, offset, await destructuredFrom(initializer), local === "createSystem");
|
|
3227
|
+
const unimported = unimportedCreateSystemCall(systemFilePath, record);
|
|
3228
|
+
if (unimported) following.report?.(unimported);
|
|
3229
|
+
return roots;
|
|
3230
|
+
}
|
|
3231
|
+
/** A call of `callee` as a whole name, not a member. */
|
|
3232
|
+
function callOf(callee) {
|
|
3233
|
+
return new RegExp(`(?<![a-zA-Z0-9_$.])${escapeRegExp$1(callee)}\\s*\\(`);
|
|
3234
|
+
}
|
|
3235
|
+
/** Each `createSystem` property a declaration destructures: its local name,
|
|
3236
|
+
* its offset in `source`, and the destructured initializer. */
|
|
3237
|
+
function* destructuredCreateSystems(source) {
|
|
3238
|
+
for (const match of source.matchAll(/\b(?:const|let|var)\s*\{([^}]*)\}\s*=\s*([^;\n]+)/g)) {
|
|
3239
|
+
const [whole, pattern, initializer] = match;
|
|
3240
|
+
for (const property of pattern.split(",")) {
|
|
3241
|
+
const binding = /^\s*createSystem\s*(?::\s*([a-zA-Z_$][a-zA-Z0-9_$]*))?\s*(?:=[\s\S]*)?$/.exec(property);
|
|
3242
|
+
if (!binding) continue;
|
|
3243
|
+
yield {
|
|
3244
|
+
local: binding[1] ?? "createSystem",
|
|
3245
|
+
offset: match.index + whole.indexOf("createSystem"),
|
|
3246
|
+
initializer: initializer.trim()
|
|
3247
|
+
};
|
|
2417
3248
|
}
|
|
3249
|
+
}
|
|
3250
|
+
}
|
|
3251
|
+
/** The warning for a `createSystem` call that the parser's scopes leave
|
|
3252
|
+
* unbound: no import, declaration or parameter names it. Null when there is
|
|
3253
|
+
* none, or when the parser reports no scopes. */
|
|
3254
|
+
function unimportedCreateSystemCall(systemFilePath, record) {
|
|
3255
|
+
const call = record.unboundCreateSystemCalls?.[0];
|
|
3256
|
+
if (!call) return null;
|
|
3257
|
+
return {
|
|
3258
|
+
file: systemFilePath,
|
|
3259
|
+
component: "createSystem",
|
|
3260
|
+
kind: "warn",
|
|
3261
|
+
message: `'createSystem' is called with no import or local binding, so it is not read as a system root: the system loader evaluates this file without auto-imports, where the name is undefined — import createSystem from '@animus-ui/system'`,
|
|
3262
|
+
code: UNIMPORTED_CREATE_SYSTEM,
|
|
3263
|
+
severity: severityFor(UNIMPORTED_CREATE_SYSTEM),
|
|
3264
|
+
line: call[0],
|
|
3265
|
+
column: call[1]
|
|
2418
3266
|
};
|
|
2419
3267
|
}
|
|
2420
3268
|
/** A pattern matching a call of any of `callees` as a whole name, or null
|
|
@@ -2423,24 +3271,20 @@ function rootCallPattern(callees) {
|
|
|
2423
3271
|
if (callees.length === 0) return null;
|
|
2424
3272
|
return `(?<![a-zA-Z0-9_$.])(?:${callees.map((callee) => callee.replace(/[.$]/g, "\\$&")).join("|")})`;
|
|
2425
3273
|
}
|
|
2426
|
-
function extractSystemFilePackages(systemFilePath,
|
|
3274
|
+
async function extractSystemFilePackages(systemFilePath, parseModule, report, resolvePackage) {
|
|
2427
3275
|
let source;
|
|
2428
3276
|
try {
|
|
2429
3277
|
source = readFileSync(systemFilePath, "utf-8");
|
|
2430
3278
|
} catch {
|
|
2431
3279
|
return [];
|
|
2432
3280
|
}
|
|
2433
|
-
const parsed =
|
|
2434
|
-
const parsedImports = parsed && parsed.length > 0 ? parsed : null;
|
|
3281
|
+
const parsed = parseModule?.(source, systemFilePath) ?? null;
|
|
2435
3282
|
let rootCall = "createSystem";
|
|
2436
|
-
if (
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
...shadowed ? [] : ["createSystem"]
|
|
2442
|
-
])]);
|
|
2443
|
-
}
|
|
3283
|
+
if (parseModule && parsed) rootCall = rootCallPattern([...await systemRoots(systemFilePath, source, parsed, {
|
|
3284
|
+
parseModule,
|
|
3285
|
+
resolvePackage,
|
|
3286
|
+
report
|
|
3287
|
+
})]);
|
|
2444
3288
|
const identifiers = /* @__PURE__ */ new Set();
|
|
2445
3289
|
const constructorRegex = rootCall === null ? null : new RegExp(`${rootCall}\\s*\\(\\s*\\{[^}]*?\\bincludes\\s*:\\s*\\[([^\\]]*)\\]`, "gs");
|
|
2446
3290
|
const chainRegex = /\.includes\s*\(\s*\[([^\]]*)\]\s*\)/gs;
|
|
@@ -2608,9 +3452,9 @@ function extractSystemFilePackages(systemFilePath, parseImports) {
|
|
|
2608
3452
|
}
|
|
2609
3453
|
if (identifiers.size === 0) return [];
|
|
2610
3454
|
const importMap = /* @__PURE__ */ new Map();
|
|
2611
|
-
for (const binding of
|
|
3455
|
+
for (const binding of parsed?.imports ?? []) importMap.set(binding.local, binding.source);
|
|
2612
3456
|
const importRegex = /^\s*import\s+(?:([a-zA-Z_$][a-zA-Z0-9_$]*)\s*,\s*)?(?:\{([^}]*)\}|([a-zA-Z_$][a-zA-Z0-9_$]*))\s+from\s+['"]([^'"]+)['"]/gm;
|
|
2613
|
-
if (!
|
|
3457
|
+
if (!parsed) {
|
|
2614
3458
|
let importMatch;
|
|
2615
3459
|
while ((importMatch = importRegex.exec(source)) !== null) {
|
|
2616
3460
|
const [, comboDefault, namedImports, defaultImport, specifier] = importMatch;
|
|
@@ -2640,7 +3484,7 @@ function extractSystemFilePackages(systemFilePath, parseImports) {
|
|
|
2640
3484
|
for (const id of identifiers) {
|
|
2641
3485
|
const specifier = importMap.get(throughAliases(id));
|
|
2642
3486
|
if (!specifier) continue;
|
|
2643
|
-
if (specifier.startsWith(".")) packages.add(resolve(systemFileDir, specifier));
|
|
3487
|
+
if (specifier.startsWith(".")) packages.add(resolve$1(systemFileDir, specifier));
|
|
2644
3488
|
else packages.add(specifier);
|
|
2645
3489
|
}
|
|
2646
3490
|
return Array.from(packages);
|
|
@@ -2695,151 +3539,6 @@ function createSourceCorpus(host, ingestor = createSourceIngestor(host)) {
|
|
|
2695
3539
|
};
|
|
2696
3540
|
}
|
|
2697
3541
|
//#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
3542
|
//#region pipeline/resolve-asset.ts
|
|
2844
3543
|
/**
|
|
2845
3544
|
* Asset resolution for hosts with no bundler hook. Host path aliases apply
|
|
@@ -2888,7 +3587,7 @@ function resolveThroughPathAliases(specifier, rootDir, pathAliasesJson) {
|
|
|
2888
3587
|
if (alias.pattern === specifier) mapped = alias.replacement;
|
|
2889
3588
|
} else if (specifier.startsWith(alias.pattern)) mapped = alias.replacement + specifier.slice(alias.pattern.length);
|
|
2890
3589
|
if (mapped === null) continue;
|
|
2891
|
-
const absolute = isAbsolute(mapped) ? mapped : join$1(rootDir, mapped);
|
|
3590
|
+
const absolute = isAbsolute$1(mapped) ? mapped : join$1(rootDir, mapped);
|
|
2892
3591
|
if (existsSync(absolute)) return absolute;
|
|
2893
3592
|
}
|
|
2894
3593
|
return null;
|
|
@@ -3007,8 +3706,13 @@ function enforceExternalTokenContracts(opts) {
|
|
|
3007
3706
|
sourceTokens
|
|
3008
3707
|
});
|
|
3009
3708
|
if (messages.length === 0) return;
|
|
3010
|
-
if (opts.strict)
|
|
3011
|
-
|
|
3709
|
+
if (!opts.strict) {
|
|
3710
|
+
for (const message of messages) opts.warn(message);
|
|
3711
|
+
return;
|
|
3712
|
+
}
|
|
3713
|
+
const message = `${opts.prefix} ${messages.join("\n")}`;
|
|
3714
|
+
if (!opts.reportErrors) throw new Error(message);
|
|
3715
|
+
opts.reportErrors(message);
|
|
3012
3716
|
}
|
|
3013
3717
|
//#endregion
|
|
3014
3718
|
//#region pipeline/path-aliases.ts
|
|
@@ -3620,11 +4324,14 @@ function buildAnalysisInputs(opts) {
|
|
|
3620
4324
|
externalDirsJson: opts.externalDirs?.length && hasSourceThemeManifests(opts.system) ? JSON.stringify(opts.externalDirs) : null
|
|
3621
4325
|
};
|
|
3622
4326
|
if (opts.system.declarationScalesJson) inputs.declarationScalesJson = opts.system.declarationScalesJson;
|
|
4327
|
+
if (opts.system.generatedNamesJson) inputs.generatedNamesJson = opts.system.generatedNamesJson;
|
|
3623
4328
|
const context = opts.analysisContext;
|
|
3624
4329
|
if (context?.skippedSources?.length || context?.unbundledComputedImports || context?.packageDirs?.length) inputs.analysisContextJson = JSON.stringify({
|
|
3625
4330
|
skippedSources: context.skippedSources ?? [],
|
|
3626
4331
|
unbundledComputedImports: context.unbundledComputedImports ?? false,
|
|
3627
|
-
packageDirs: context.packageDirs ?? []
|
|
4332
|
+
packageDirs: context.packageDirs ?? [],
|
|
4333
|
+
linkedDirs: context.linkedDirs ?? [],
|
|
4334
|
+
outputDirs: context.outputDirs ?? []
|
|
3628
4335
|
});
|
|
3629
4336
|
return inputs;
|
|
3630
4337
|
}
|
|
@@ -3640,9 +4347,12 @@ function hasSourceThemeManifests(system) {
|
|
|
3640
4347
|
const surfacedSystems = /* @__PURE__ */ new WeakSet();
|
|
3641
4348
|
/** The loaded system's own diagnostics, the first time it is analyzed. A
|
|
3642
4349
|
* strict error keeps failing every analysis until the system changes. */
|
|
3643
|
-
function systemDiagnostics(system, strict) {
|
|
4350
|
+
function systemDiagnostics(system, strict, levels) {
|
|
3644
4351
|
const diagnostics = [...collectSelectorAliasDiagnostics(system.selectorAliasesJson), ...systemLoadDiagnostics(system)];
|
|
3645
|
-
const blocking =
|
|
4352
|
+
const blocking = diagnostics.some((d) => effectiveLevel(d, {
|
|
4353
|
+
strict,
|
|
4354
|
+
levels
|
|
4355
|
+
}) === "error");
|
|
3646
4356
|
if (surfacedSystems.has(system) && !blocking) return [];
|
|
3647
4357
|
surfacedSystems.add(system);
|
|
3648
4358
|
return diagnostics;
|
|
@@ -3652,7 +4362,7 @@ function systemDiagnostics(system, strict) {
|
|
|
3652
4362
|
* the call site (strict-mode throw vs warn).
|
|
3653
4363
|
*/
|
|
3654
4364
|
function runProjectAnalysis(engineApi, opts) {
|
|
3655
|
-
const { analyzeProject } = engineApi();
|
|
4365
|
+
const { analyzeProject, diagnosticCodes } = engineApi();
|
|
3656
4366
|
let t = performance.now();
|
|
3657
4367
|
const inputs = buildAnalysisInputs(opts);
|
|
3658
4368
|
const serializeMs = Math.round(performance.now() - t);
|
|
@@ -3662,7 +4372,12 @@ function runProjectAnalysis(engineApi, opts) {
|
|
|
3662
4372
|
t = performance.now();
|
|
3663
4373
|
const manifest = JSON.parse(manifestJson);
|
|
3664
4374
|
const systemErrors = systemLoadDiagnostics(opts.system).filter((diagnostic) => diagnostic.kind === "error");
|
|
3665
|
-
|
|
4375
|
+
const kitDiagnostics = kitDescriptorDiagnostics(opts.kitDescriptors ?? [], manifest);
|
|
4376
|
+
if (systemErrors.length > 0 || kitDiagnostics.length > 0) manifest.diagnostics = [
|
|
4377
|
+
...systemErrors,
|
|
4378
|
+
...kitDiagnostics,
|
|
4379
|
+
...manifest.diagnostics ?? []
|
|
4380
|
+
];
|
|
3666
4381
|
const componentCss = applyUnitFallback(manifest.css);
|
|
3667
4382
|
const propertyDiagnostics = checkCustomProperties({
|
|
3668
4383
|
system: opts.system,
|
|
@@ -3673,8 +4388,11 @@ function runProjectAnalysis(engineApi, opts) {
|
|
|
3673
4388
|
surfaceManifestDiagnostics(manifest, opts.warn, {
|
|
3674
4389
|
strict: opts.strict,
|
|
3675
4390
|
info: opts.info,
|
|
4391
|
+
reportErrors: opts.reportErrors,
|
|
4392
|
+
levels: opts.diagnostics,
|
|
4393
|
+
knownCodes: opts.diagnostics ? knownDiagnosticCodes(diagnosticCodes?.()) : void 0,
|
|
3676
4394
|
prepend: [
|
|
3677
|
-
...systemDiagnostics(opts.system, opts.strict),
|
|
4395
|
+
...systemDiagnostics(opts.system, opts.strict, opts.diagnostics),
|
|
3678
4396
|
...propertyDiagnostics,
|
|
3679
4397
|
...opts.extraDiagnostics ?? []
|
|
3680
4398
|
]
|
|
@@ -3778,36 +4496,34 @@ function renameCustomProperties(css, names) {
|
|
|
3778
4496
|
}
|
|
3779
4497
|
//#endregion
|
|
3780
4498
|
//#region pipeline/prefix.ts
|
|
3781
|
-
/**
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
|
|
3786
|
-
|
|
4499
|
+
/**
|
|
4500
|
+
* The prefix without `prefixContextualVars`: every name Animus generates (a
|
|
4501
|
+
* variable the theme's map or variable CSS defines) takes `--${prefix}-`
|
|
4502
|
+
* wherever it is defined, registered or read, `var()` fallbacks at any depth
|
|
4503
|
+
* included. Every other name stays as written: each declared contextual
|
|
4504
|
+
* variable, and any custom property an author writes.
|
|
4505
|
+
*/
|
|
4506
|
+
function applyPrefix(prefix, variableMapJson, variableCss, themeJson, contextualVarsJson, declarationScalesJson) {
|
|
3787
4507
|
if (!prefix) return {
|
|
3788
4508
|
variableMapJson,
|
|
3789
4509
|
variableCss,
|
|
3790
4510
|
themeJson,
|
|
3791
|
-
contextualVarsJson
|
|
4511
|
+
contextualVarsJson,
|
|
4512
|
+
declarationScalesJson
|
|
3792
4513
|
};
|
|
3793
|
-
const
|
|
3794
|
-
const
|
|
3795
|
-
|
|
3796
|
-
|
|
3797
|
-
|
|
3798
|
-
css = prefixVariableReferences(prefix, css);
|
|
3799
|
-
css = css.replace(/@property(\s+)--([a-zA-Z][\w-]*)/g, `@property$1--${prefix}-$2`);
|
|
4514
|
+
const contextual = new Set(contextualVarsJson ? Object.values(parseInternalWire(contextualVarsJson, "contextualVarsJson (the theme's contextual variable names)")).flat() : []);
|
|
4515
|
+
const variableMap = parseInternalWire(variableMapJson, "variableMapJson (the theme's token variables)");
|
|
4516
|
+
const generated = [...Object.values(variableMap).map((name) => name.replace(/^--/, "")), ...themeDefinitions(variableCss)].filter((name) => !contextual.has(name));
|
|
4517
|
+
const names = createPropertyNames([], prefix, generated);
|
|
4518
|
+
const rename = (value) => renameCustomProperties(value, names);
|
|
3800
4519
|
const result = {
|
|
3801
|
-
variableMapJson: JSON.stringify(
|
|
3802
|
-
variableCss:
|
|
4520
|
+
variableMapJson: JSON.stringify(Object.fromEntries(Object.entries(variableMap).map(([key, name]) => [key, rename(name)]))),
|
|
4521
|
+
variableCss: rename(variableCss)
|
|
3803
4522
|
};
|
|
3804
|
-
if (themeJson) result.themeJson =
|
|
3805
|
-
if (contextualVarsJson)
|
|
3806
|
-
|
|
3807
|
-
|
|
3808
|
-
for (const [scale, names] of Object.entries(ctxVars)) prefixedCtx[scale] = names.map((name) => `${prefix}-${name}`);
|
|
3809
|
-
result.contextualVarsJson = JSON.stringify(prefixedCtx);
|
|
3810
|
-
}
|
|
4523
|
+
if (themeJson) result.themeJson = renameTokenValues(themeJson, rename);
|
|
4524
|
+
if (contextualVarsJson) result.contextualVarsJson = contextualVarsJson;
|
|
4525
|
+
if (generated.length > 0) result.generatedNamesJson = JSON.stringify(Object.fromEntries(generated.map((name) => [name, `${prefix}-${name}`])));
|
|
4526
|
+
if (declarationScalesJson) result.declarationScalesJson = renameDeclarationRecords(declarationScalesJson, rename);
|
|
3811
4527
|
return result;
|
|
3812
4528
|
}
|
|
3813
4529
|
/** The runtime's transport variables are `--animus-<prop>`. */
|
|
@@ -3823,7 +4539,8 @@ function applyPropertyNames(prefix, artifacts) {
|
|
|
3823
4539
|
const byScale = artifacts.contextualVarsJson ? parseInternalWire(artifacts.contextualVarsJson, "contextualVarsJson (the theme's contextual variable names)") : null;
|
|
3824
4540
|
const declared = Object.values(byScale ?? {}).flat();
|
|
3825
4541
|
const variableMap = parseInternalWire(artifacts.variableMapJson, "variableMapJson (the theme's token variables)");
|
|
3826
|
-
const
|
|
4542
|
+
const themeDefined = [...Object.values(variableMap).map((name) => name.replace(/^--/, "")), ...themeDefinitions(artifacts.variableCss)];
|
|
4543
|
+
const managed = createPropertyNames(declared, prefix, themeDefined);
|
|
3827
4544
|
const nameConflicts = [...[...new Set(declared)].flatMap((name) => {
|
|
3828
4545
|
const final = managed.finalName(name) ?? name;
|
|
3829
4546
|
return final.startsWith(TRANSPORT_NAMESPACE) ? [{
|
|
@@ -3847,11 +4564,30 @@ function applyPropertyNames(prefix, artifacts) {
|
|
|
3847
4564
|
}))]))),
|
|
3848
4565
|
declarationScalesJson: artifacts.declarationScalesJson === null ? null : renameDeclarationRecords(artifacts.declarationScalesJson, rename),
|
|
3849
4566
|
nameConflicts,
|
|
3850
|
-
contextualProperties: [...new Set(declared)].map((name) => `--${managed.finalName(name) ?? name}`)
|
|
4567
|
+
contextualProperties: [...new Set(declared)].map((name) => `--${managed.finalName(name) ?? name}`),
|
|
4568
|
+
generatedNamesJson: generatedNamesJson(themeDefined.filter((name) => !declared.includes(name)), managed)
|
|
3851
4569
|
};
|
|
3852
4570
|
}
|
|
3853
|
-
/**
|
|
3854
|
-
|
|
4571
|
+
/** The generated names' final names, for authored component styles. */
|
|
4572
|
+
function generatedNamesJson(names, managed) {
|
|
4573
|
+
const entries = [...new Set(names)].flatMap((name) => {
|
|
4574
|
+
const final = managed.finalName(name);
|
|
4575
|
+
return final === void 0 ? [] : [[name, final]];
|
|
4576
|
+
});
|
|
4577
|
+
return entries.length === 0 ? null : JSON.stringify(Object.fromEntries(entries));
|
|
4578
|
+
}
|
|
4579
|
+
/** The custom properties the theme's CSS declares or `@property` registers,
|
|
4580
|
+
* read as CSS tokenizes it: a name in a string or comment defines nothing. */
|
|
4581
|
+
function themeDefinitions(variableCss) {
|
|
4582
|
+
const tokens = tokenize(variableCss).filter((token) => token.type !== "whitespace");
|
|
4583
|
+
return tokens.flatMap((token, index) => {
|
|
4584
|
+
if (token.type !== "ident" || !token.value.startsWith("--")) return [];
|
|
4585
|
+
const previous = tokens[index - 1];
|
|
4586
|
+
const declared = (previous === void 0 || previous.type === "{" || previous.type === ";") && tokens[index + 1]?.type === ":";
|
|
4587
|
+
const registered = previous?.type === "at-keyword" && previous.value.toLowerCase() === "property";
|
|
4588
|
+
return declared || registered ? [token.value.slice(2)] : [];
|
|
4589
|
+
});
|
|
4590
|
+
}
|
|
3855
4591
|
/** Renames inside the token map's values. */
|
|
3856
4592
|
function renameTokenValues(themeJson, rename) {
|
|
3857
4593
|
const tokens = parseInternalWire(themeJson, "themeJson (the theme's flattened token values)");
|
|
@@ -3867,23 +4603,40 @@ function renameDeclarationRecords(declarationScalesJson, rename) {
|
|
|
3867
4603
|
}
|
|
3868
4604
|
//#endregion
|
|
3869
4605
|
//#region pipeline/system-config.ts
|
|
4606
|
+
/** The loader's evaluation of the system file. A failure on a file that
|
|
4607
|
+
* calls `createSystem` with no binding leads with discovery's warning line
|
|
4608
|
+
* for it, then keeps the loader's own error. */
|
|
4609
|
+
function evaluateSystemModule(engine, systemPath, rootDir, conditions) {
|
|
4610
|
+
try {
|
|
4611
|
+
return engine.loadSystemModule(systemPath, rootDir, void 0, conditions);
|
|
4612
|
+
} catch (error) {
|
|
4613
|
+
let source;
|
|
4614
|
+
try {
|
|
4615
|
+
source = readFileSync(systemPath, "utf-8");
|
|
4616
|
+
} catch {
|
|
4617
|
+
throw error;
|
|
4618
|
+
}
|
|
4619
|
+
const record = engineModuleParser(engine)?.(source, systemPath);
|
|
4620
|
+
const call = record && unimportedCreateSystemCall(systemPath, record);
|
|
4621
|
+
if (!call) throw error;
|
|
4622
|
+
throw new Error(`${warnLine(call)}\n${String(error)}`, { cause: error });
|
|
4623
|
+
}
|
|
4624
|
+
}
|
|
3870
4625
|
/**
|
|
3871
4626
|
* Load and normalize a SystemInstance; `prefix` namespaces every CSS
|
|
3872
4627
|
* variable name. Error handling stays at the call site.
|
|
3873
4628
|
*/
|
|
3874
4629
|
function loadSystemConfig(engineApi, opts) {
|
|
3875
|
-
const
|
|
3876
|
-
const config = loadSystemModule(opts.systemPath, opts.rootDir);
|
|
4630
|
+
const config = evaluateSystemModule(engineApi(), opts.systemPath, opts.rootDir, opts.conditions);
|
|
3877
4631
|
let scalesJson = config.scalesJson;
|
|
3878
4632
|
let variableMapJson = config.variableMapJson;
|
|
3879
4633
|
const registrations = splitInvalidPropertyRegistrations(config.variableCss);
|
|
3880
4634
|
let variableCss = registrations.css;
|
|
3881
4635
|
let contextualVarsJson = config.contextualVarsJson || null;
|
|
3882
4636
|
let declarationScalesJson = config.declarationScalesJson || null;
|
|
3883
|
-
|
|
3884
|
-
let contextualProperties = declaredNames.map((name) => `--${name}`);
|
|
3885
|
-
let legacyPrefixedContextualVars = [];
|
|
4637
|
+
let contextualProperties = Object.values(parseInternalWire(contextualVarsJson ?? "{}", "contextualVarsJson (the theme's contextual variable names)")).flat().map((name) => `--${name}`);
|
|
3886
4638
|
let prefixNameConflicts = [];
|
|
4639
|
+
let generatedNamesJson;
|
|
3887
4640
|
if (opts.prefix && opts.prefixContextualVars) {
|
|
3888
4641
|
const resolved = applyPropertyNames(opts.prefix, {
|
|
3889
4642
|
variableMapJson,
|
|
@@ -3899,15 +4652,14 @@ function loadSystemConfig(engineApi, opts) {
|
|
|
3899
4652
|
contextualProperties = resolved.contextualProperties;
|
|
3900
4653
|
prefixNameConflicts = resolved.nameConflicts;
|
|
3901
4654
|
declarationScalesJson = resolved.declarationScalesJson;
|
|
4655
|
+
generatedNamesJson = resolved.generatedNamesJson;
|
|
3902
4656
|
} else if (opts.prefix) {
|
|
3903
|
-
|
|
3904
|
-
contextualProperties = declaredNames.map((name) => `--${opts.prefix}-${name}`);
|
|
3905
|
-
const prefixed = applyPrefix(opts.prefix, variableMapJson, variableCss, scalesJson, contextualVarsJson || void 0);
|
|
4657
|
+
const prefixed = applyPrefix(opts.prefix, variableMapJson, variableCss, scalesJson, contextualVarsJson || void 0, declarationScalesJson || void 0);
|
|
3906
4658
|
variableMapJson = prefixed.variableMapJson;
|
|
3907
4659
|
variableCss = prefixed.variableCss;
|
|
3908
4660
|
if (prefixed.themeJson) scalesJson = prefixed.themeJson;
|
|
3909
|
-
if (prefixed.
|
|
3910
|
-
|
|
4661
|
+
if (prefixed.declarationScalesJson) declarationScalesJson = prefixed.declarationScalesJson;
|
|
4662
|
+
generatedNamesJson = prefixed.generatedNamesJson;
|
|
3911
4663
|
}
|
|
3912
4664
|
const system = {
|
|
3913
4665
|
propConfigJson: config.propConfig,
|
|
@@ -3931,7 +4683,7 @@ function loadSystemConfig(engineApi, opts) {
|
|
|
3931
4683
|
if (config.propertyRecordsJson) system.propertyRecordsJson = config.propertyRecordsJson;
|
|
3932
4684
|
if (registrations.invalid.length > 0) system.invalidPropertyRegistrations = registrations.invalid;
|
|
3933
4685
|
if (prefixNameConflicts.length > 0) system.prefixNameConflicts = prefixNameConflicts;
|
|
3934
|
-
if (
|
|
4686
|
+
if (generatedNamesJson) system.generatedNamesJson = generatedNamesJson;
|
|
3935
4687
|
return system;
|
|
3936
4688
|
}
|
|
3937
4689
|
//#endregion
|
|
@@ -3940,8 +4692,7 @@ function runStructuralSelfCheck(input) {
|
|
|
3940
4692
|
const failures = [];
|
|
3941
4693
|
if (input.componentCount === 0) failures.push("No component CSS produced — check the system file and its includes list");
|
|
3942
4694
|
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 === "
|
|
3944
|
-
else if (outcome === "unresolvable") failures.push(`include '${specifier}' could not be resolved`);
|
|
4695
|
+
for (const { specifier, outcome } of input.externalOutcomes ?? []) if (outcome === "unresolvable") failures.push(`include '${specifier}' could not be resolved`);
|
|
3945
4696
|
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
4697
|
if ((input.assembledCss ?? `${input.variableCss}\n${input.globalCss}\n${input.componentCss}`).includes("__TRANSFORM__")) failures.push("Unresolved __TRANSFORM__ placeholders found in CSS output");
|
|
3947
4698
|
if (input.componentCount > 0 && (input.componentCss.length > 0 || input.assembledCss !== void 0)) {
|
|
@@ -3975,7 +4726,8 @@ function buildDynamicPropConfig(dynamicProps) {
|
|
|
3975
4726
|
kind: meta.kind,
|
|
3976
4727
|
slotClass: meta.slotClass,
|
|
3977
4728
|
memberVars: meta.memberVars,
|
|
3978
|
-
declarationScaleValues: meta.declarationScaleValues
|
|
4729
|
+
declarationScaleValues: meta.declarationScaleValues,
|
|
4730
|
+
...meta.productionConditions && { productionConditions: meta.productionConditions }
|
|
3979
4731
|
};
|
|
3980
4732
|
continue;
|
|
3981
4733
|
}
|
|
@@ -3993,6 +4745,7 @@ function buildDynamicPropConfig(dynamicProps) {
|
|
|
3993
4745
|
if (meta.strict) entry.strict = true;
|
|
3994
4746
|
if (meta.keywords?.length) entry.keywords = meta.keywords;
|
|
3995
4747
|
if (meta.currentVar) entry.currentVar = meta.currentVar;
|
|
4748
|
+
if (meta.productionConditions) entry.productionConditions = meta.productionConditions;
|
|
3996
4749
|
configEntries[propName] = entry;
|
|
3997
4750
|
}
|
|
3998
4751
|
return configEntries;
|
|
@@ -4031,6 +4784,191 @@ function assertNoErrorDiagnostics(diagnostics) {
|
|
|
4031
4784
|
throw new Error(lines.join("\n"));
|
|
4032
4785
|
}
|
|
4033
4786
|
//#endregion
|
|
4787
|
+
//#region pipeline/kit-publication.ts
|
|
4788
|
+
/** Source a kit publishes and the loader or a bundler parses for imports. */
|
|
4789
|
+
const CODE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
4790
|
+
".ts",
|
|
4791
|
+
".tsx",
|
|
4792
|
+
".mts",
|
|
4793
|
+
".cts",
|
|
4794
|
+
".js",
|
|
4795
|
+
".jsx",
|
|
4796
|
+
".mjs",
|
|
4797
|
+
".cjs"
|
|
4798
|
+
]);
|
|
4799
|
+
const isFile = (path) => {
|
|
4800
|
+
try {
|
|
4801
|
+
return statSync$1(path).isFile();
|
|
4802
|
+
} catch {
|
|
4803
|
+
return false;
|
|
4804
|
+
}
|
|
4805
|
+
};
|
|
4806
|
+
const posixPath = (path) => path.split(sep).join("/");
|
|
4807
|
+
/**
|
|
4808
|
+
* The package-relative files `npm pack` publishes, read with npm's own rules
|
|
4809
|
+
* (`files`, `.npmignore`, `.gitignore` and the files npm always includes)
|
|
4810
|
+
* through a dry run that runs no scripts and needs no network.
|
|
4811
|
+
*/
|
|
4812
|
+
function npmPublishedFiles(pkgRoot) {
|
|
4813
|
+
const windows = process.platform === "win32";
|
|
4814
|
+
const run = spawnSync(windows ? "npm.cmd" : "npm", [
|
|
4815
|
+
"pack",
|
|
4816
|
+
"--dry-run",
|
|
4817
|
+
"--json",
|
|
4818
|
+
"--ignore-scripts"
|
|
4819
|
+
], {
|
|
4820
|
+
cwd: pkgRoot,
|
|
4821
|
+
encoding: "utf-8",
|
|
4822
|
+
shell: windows,
|
|
4823
|
+
env: {
|
|
4824
|
+
...process.env,
|
|
4825
|
+
npm_config_update_notifier: "false"
|
|
4826
|
+
}
|
|
4827
|
+
});
|
|
4828
|
+
if (run.error || run.status !== 0) return {
|
|
4829
|
+
kind: "failed",
|
|
4830
|
+
reason: `npm pack --dry-run could not list the published files: ${String(run.error ?? run.stderr.trim())}`
|
|
4831
|
+
};
|
|
4832
|
+
let output;
|
|
4833
|
+
try {
|
|
4834
|
+
output = JSON.parse(run.stdout);
|
|
4835
|
+
} catch (error) {
|
|
4836
|
+
return {
|
|
4837
|
+
kind: "failed",
|
|
4838
|
+
reason: `npm pack --dry-run printed no JSON: ${String(error)}`
|
|
4839
|
+
};
|
|
4840
|
+
}
|
|
4841
|
+
const pack = Array.isArray(output) ? output[0] : void 0;
|
|
4842
|
+
const files = isJsonBlock(pack) ? pack.files : void 0;
|
|
4843
|
+
if (!Array.isArray(files)) return {
|
|
4844
|
+
kind: "failed",
|
|
4845
|
+
reason: "npm pack --dry-run printed no file list"
|
|
4846
|
+
};
|
|
4847
|
+
return {
|
|
4848
|
+
kind: "listed",
|
|
4849
|
+
files: new Set(files.flatMap((file) => isJsonBlock(file) && isJsonString(file.path) ? [posixPath(file.path)] : []))
|
|
4850
|
+
};
|
|
4851
|
+
}
|
|
4852
|
+
function animusTargets(manifest) {
|
|
4853
|
+
const targets = [];
|
|
4854
|
+
const unreadable = [];
|
|
4855
|
+
const visit = (subpath, value, underAnimus) => {
|
|
4856
|
+
if (isJsonString(value)) {
|
|
4857
|
+
if (underAnimus) targets.push([subpath, value]);
|
|
4858
|
+
} else if (Array.isArray(value)) for (const entry of value) visit(subpath, entry, underAnimus);
|
|
4859
|
+
else if (isJsonBlock(value)) for (const [key, entry] of Object.entries(value)) visit(subpath, entry, underAnimus || key === "animus");
|
|
4860
|
+
else if (underAnimus && value !== null) unreadable.push([subpath, JSON.stringify(value)]);
|
|
4861
|
+
};
|
|
4862
|
+
for (const [subpath, value] of exportsSubpaths(manifest)) visit(subpath, value, false);
|
|
4863
|
+
return {
|
|
4864
|
+
targets,
|
|
4865
|
+
unreadable
|
|
4866
|
+
};
|
|
4867
|
+
}
|
|
4868
|
+
/** Every file in the package outside `node_modules` and `.git`,
|
|
4869
|
+
* package-relative. */
|
|
4870
|
+
function packageFiles(pkgRoot, dir = "") {
|
|
4871
|
+
return readdirSync$1(join(pkgRoot, dir), { withFileTypes: true }).flatMap((entry) => {
|
|
4872
|
+
const path = dir === "" ? entry.name : `${dir}/${entry.name}`;
|
|
4873
|
+
if (entry.isDirectory()) return entry.name === "node_modules" || entry.name === ".git" ? [] : packageFiles(pkgRoot, path);
|
|
4874
|
+
return entry.isFile() ? [path] : [];
|
|
4875
|
+
});
|
|
4876
|
+
}
|
|
4877
|
+
/** A `*` target pattern as a matcher of package-relative files. Node puts
|
|
4878
|
+
* one subpath into every `*`, so each later `*` repeats the first match. */
|
|
4879
|
+
function targetPattern(target) {
|
|
4880
|
+
const body = posixPath(target).replace(/^\.\//, "").split("*").map((part) => part.replace(/[.+?^${}()|[\]\\]/g, "\\$&")).reduce((pattern, part, index) => index === 0 ? part : `${pattern}${index === 1 ? "(.+)" : "\\1"}${part}`);
|
|
4881
|
+
return new RegExp(`^${body}$`);
|
|
4882
|
+
}
|
|
4883
|
+
/**
|
|
4884
|
+
* Why the kit at `pkgRoot` would fail an installed consumer: each `animus`
|
|
4885
|
+
* source target, at any condition depth or as a pattern, and each file its
|
|
4886
|
+
* relative imports reach, must be in the package and among the files `npm
|
|
4887
|
+
* pack` publishes, and each bare package import a declared dependency.
|
|
4888
|
+
* Each line names the file, the import and the cause; none means the
|
|
4889
|
+
* published source is complete. With `artifactDir`, a build's artifact
|
|
4890
|
+
* directory inside the package that `npm pack` would publish is a line too.
|
|
4891
|
+
*/
|
|
4892
|
+
function kitPublicationFailures(pkgRoot, engine, artifactDir) {
|
|
4893
|
+
let manifest;
|
|
4894
|
+
try {
|
|
4895
|
+
manifest = JSON.parse(readFileSync$1(join(pkgRoot, "package.json"), "utf-8"));
|
|
4896
|
+
} catch (error) {
|
|
4897
|
+
return [`package.json: cannot be read: ${String(error)}`];
|
|
4898
|
+
}
|
|
4899
|
+
const { targets, unreadable } = animusTargets(manifest);
|
|
4900
|
+
if (targets.length === 0 && unreadable.length === 0) return [];
|
|
4901
|
+
const { moduleSpecifiers } = engine;
|
|
4902
|
+
if (moduleSpecifiers === void 0) throw new Error("the native engine does not expose moduleSpecifiers, which the kit publication check needs — rebuild @animus-ui/extract");
|
|
4903
|
+
const publication = npmPublishedFiles(pkgRoot);
|
|
4904
|
+
if (publication.kind === "failed") return [`package.json: ${publication.reason}`];
|
|
4905
|
+
const published = (packageRelative) => publication.files.has(packageRelative);
|
|
4906
|
+
const declared = new Set([
|
|
4907
|
+
"dependencies",
|
|
4908
|
+
"peerDependencies",
|
|
4909
|
+
"optionalDependencies"
|
|
4910
|
+
].flatMap((field) => {
|
|
4911
|
+
const block = isJsonBlock(manifest) ? manifest[field] : void 0;
|
|
4912
|
+
return isJsonBlock(block) ? Object.keys(block) : [];
|
|
4913
|
+
}));
|
|
4914
|
+
const ownName = isJsonBlock(manifest) && isJsonString(manifest.name) ? manifest.name : null;
|
|
4915
|
+
const failures = [];
|
|
4916
|
+
const rel = (file) => posixPath(relative(pkgRoot, file));
|
|
4917
|
+
const outside = (relPath) => relPath.startsWith("../") || isAbsolute(relPath);
|
|
4918
|
+
for (const [subpath, value] of unreadable) failures.push(`package.json: exports["${subpath}"] has an "animus" value, ${value}, that is no target path`);
|
|
4919
|
+
const queue = [];
|
|
4920
|
+
const admit = (subpath, relPath) => {
|
|
4921
|
+
if (published(relPath)) queue.push(resolve(pkgRoot, relPath));
|
|
4922
|
+
else failures.push(`package.json: the "animus" target of exports["${subpath}"], ${relPath}, is not in the published files`);
|
|
4923
|
+
};
|
|
4924
|
+
let files = null;
|
|
4925
|
+
for (const [subpath, target] of targets) {
|
|
4926
|
+
const relPath = rel(resolve(pkgRoot, target));
|
|
4927
|
+
if (outside(relPath)) failures.push(`package.json: the "animus" target of exports["${subpath}"], ${target}, is outside the package`);
|
|
4928
|
+
else if (target.includes("*")) {
|
|
4929
|
+
files ??= packageFiles(pkgRoot);
|
|
4930
|
+
const pattern = targetPattern(target);
|
|
4931
|
+
const matched = files.filter((file) => pattern.test(file));
|
|
4932
|
+
if (matched.length === 0) failures.push(`package.json: the "animus" target of exports["${subpath}"], ${target}, matches no file in the package`);
|
|
4933
|
+
for (const file of matched) admit(subpath, file);
|
|
4934
|
+
} else if (!isFile(resolve(pkgRoot, target))) failures.push(`package.json: the "animus" target of exports["${subpath}"], ${target}, does not exist in the package`);
|
|
4935
|
+
else admit(subpath, relPath);
|
|
4936
|
+
}
|
|
4937
|
+
const visited = /* @__PURE__ */ new Set();
|
|
4938
|
+
while (queue.length > 0) {
|
|
4939
|
+
const file = queue.shift();
|
|
4940
|
+
if (visited.has(file)) continue;
|
|
4941
|
+
visited.add(file);
|
|
4942
|
+
const specifiers = parseInternalWire(moduleSpecifiers(JSON.stringify([{
|
|
4943
|
+
path: file,
|
|
4944
|
+
source: readFileSync$1(file, "utf-8")
|
|
4945
|
+
}])), "moduleSpecifiers").files[file] ?? [];
|
|
4946
|
+
for (const specifier of specifiers) {
|
|
4947
|
+
if (specifier.startsWith(".")) {
|
|
4948
|
+
const target = relativeSourceCandidates(file, specifier).find(isFile);
|
|
4949
|
+
if (target === void 0) failures.push(`${rel(file)}: import '${specifier}' resolves to no file in the package`);
|
|
4950
|
+
else if (outside(rel(target))) failures.push(`${rel(file)}: import '${specifier}' resolves to ${rel(target)}, outside the package`);
|
|
4951
|
+
else if (!published(rel(target))) failures.push(`${rel(file)}: import '${specifier}' resolves to ${rel(target)}, which is not in the published files`);
|
|
4952
|
+
else if (CODE_EXTENSIONS.has(extname(target))) queue.push(target);
|
|
4953
|
+
continue;
|
|
4954
|
+
}
|
|
4955
|
+
if (specifier.startsWith("/") || /^[a-z][\w+.-]*:/i.test(specifier)) {
|
|
4956
|
+
if (!isBuiltin(specifier)) failures.push(`${rel(file)}: import '${specifier}' is not a package or relative import, so an installed consumer cannot resolve it`);
|
|
4957
|
+
continue;
|
|
4958
|
+
}
|
|
4959
|
+
const name = bareSpecifierPackageName(specifier);
|
|
4960
|
+
if (isBuiltin(name) || name === ownName || declared.has(name)) continue;
|
|
4961
|
+
failures.push(`${rel(file)}: import '${specifier}' names the package ${name}, which package.json does not declare in dependencies, peerDependencies or optionalDependencies`);
|
|
4962
|
+
}
|
|
4963
|
+
}
|
|
4964
|
+
const artifacts = artifactDir === void 0 ? "" : rel(artifactDir);
|
|
4965
|
+
if (artifacts !== "" && !outside(artifacts)) {
|
|
4966
|
+
const shipped = [...publication.files].filter((file) => file.startsWith(`${artifacts}/`));
|
|
4967
|
+
if (shipped.length > 0) failures.push(`${artifacts}/: npm pack would publish this build-artifact directory (${shipped.length} files) — add "${artifacts}/" to .npmignore or .gitignore, or list the published files in package.json "files"`);
|
|
4968
|
+
}
|
|
4969
|
+
return failures;
|
|
4970
|
+
}
|
|
4971
|
+
//#endregion
|
|
4034
4972
|
//#region pipeline/replacement-plans.ts
|
|
4035
4973
|
const snapshotByManifest = /* @__PURE__ */ new WeakMap();
|
|
4036
4974
|
/** `null` (a context before its first analysis) snapshots to the empty plan
|
|
@@ -4107,4 +5045,4 @@ function realpathThroughNearestExistingAncestor(path) {
|
|
|
4107
5045
|
}
|
|
4108
5046
|
}
|
|
4109
5047
|
//#endregion
|
|
4110
|
-
export {
|
|
5048
|
+
export { discoverFiles as $, fileSideEffects as A, surfaceManifestDiagnostics as At, unresolvableIncludesMessage as B, substituteSheetAssets as Bt, resolveAssetFile as C, DiagnosticFailure as Ct, engineModuleParser as D, isDeletedSource as Dt, collectExternalPackageSources as E, VOCABULARY_LEGACY_VERB as Et, keepsKitSourceEffects as F, findAssetSpecifiers as Ft, KIT_DESCRIPTOR_FILE as G, assembleStylesheet as Gt, createSourceIdentity as H, contentHash as Ht, kitSourceModuleSideEffects as I, findSheetAssetSpecifiers as It, readKitDescriptor as J, __require as Jt, buildKitDescriptor as K, stripLeadingLayerDeclaration as Kt, resolveAbsolutePathSpecifier as L, generatedModuleCode as Lt, firstOwners as M, unreadableSourceDiagnostic as Mt, importedKitDiagnostics as N, unresolvedParentName as Nt, excludeCollectedPackages as O, isUnresolvedParentDrop as Ot, isExcludedPackageRelativePath as P, vocabularyWitnessDiagnostics as Pt, compareDiscoveryOrder as Q, sourceKitDependencies as R, reportSurvivingAssetPlaceholders as Rt, enforceExternalTokenContracts as S, DIAGNOSTIC_LEVELS as St, createSourceCorpus as T, VOCABULARY_COLLISION as Tt, isPathWithinRoot as U, buildAnalyzeProjectArgs as Ut, walkPackageSources as V, parseInternalWire as Vt, sharesVolumeRoot as W, ANIMUS_LAYERS as Wt, isJsonString as X, isJsonBlock as Y, readTsconfigAliasPairs as Z, runProjectAnalysis as _, adaptSvelteSource as _t, kitPublicationFailures as a, STRUCTURAL_EXCLUDE as at, resolveLightningTargets as b, isEngineTransformExtension as bt, buildDynamicPropConfig as c, resolveMode as ct, applyPrefix as d, assertNoRetiredEngineSelection as dt, AnimusConfigError as et, serializeStaticCss as f, createV2EngineApi as ft, clearEngineCache as g, projectExternalFileOwners as gt, buildAnalysisInputs as h, parseFilesJson as ht, snapshotFilePlans as i, REPLACEABLE_DEFAULT_EXCLUDE as it, findPackageRoot as j, systemLoadDiagnostics as jt, extractSystemFilePackages as k, noKitFilesDiagnostics as kt, runStructuralSelfCheck as l, resolveVerbosity as lt, stableStringify as m, isAdvisorySourceDiagnostic as mt, diffFilePlans as n, DEFAULT_EXCLUDE as nt, assertNoErrorDiagnostics as o, assertKnownOptionKeys as ot, camelToKebab as p, ingestSourceEntries as pt, kitDescriptorDiagnostics as q, validateLayerOrder as qt, hashReplacementPlans as r, DRIVER_NAMESPACES as rt, buildSystemPropsModule as s, createExcludeMatcher as st, toWatchKeys as t, CORE_OPTION_KEYS as tt, loadSystemConfig as u, RETIRED_ENGINE_MESSAGE as ut, applyUnitFallback as v, DEFAULT_EXTENSIONS as vt, resolveThroughPathAliases as w, INVALID_PROPERTY_REGISTRATION as wt, buildPathAliasesJson as x, preprocessMdx as xt, postProcessCss as y, ENGINE_TRANSFORM_EXTENSIONS as yt, staleDistIncludesMessage as z, substituteAssetPlaceholders as zt };
|