@animus-ui/extract 0.1.19 → 0.1.20
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 +6 -0
- package/dist/index.cjs +3 -1
- 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 +14 -0
- package/dist/pipeline/asset-placeholders.d.ts.map +1 -1
- package/dist/pipeline/core-options.d.ts +4 -0
- package/dist/pipeline/core-options.d.ts.map +1 -1
- package/dist/pipeline/dynamic-prop-config.d.ts +3 -0
- package/dist/pipeline/dynamic-prop-config.d.ts.map +1 -1
- package/dist/pipeline/engine-adapter.d.ts +1 -1
- package/dist/pipeline/engine-adapter.d.ts.map +1 -1
- package/dist/pipeline/index.d.ts +2 -2
- package/dist/pipeline/index.d.ts.map +1 -1
- package/dist/pipeline/manifest-diagnostics.d.ts +17 -3
- package/dist/pipeline/manifest-diagnostics.d.ts.map +1 -1
- package/dist/pipeline/prefix.d.ts +35 -0
- package/dist/pipeline/prefix.d.ts.map +1 -1
- package/dist/pipeline/property-diagnostics.d.ts +5 -4
- package/dist/pipeline/property-diagnostics.d.ts.map +1 -1
- package/dist/pipeline/property-names.d.ts +42 -0
- package/dist/pipeline/property-names.d.ts.map +1 -0
- package/dist/pipeline/property-registrations.d.ts +4 -2
- package/dist/pipeline/property-registrations.d.ts.map +1 -1
- package/dist/pipeline/run-analysis.d.ts +16 -0
- package/dist/pipeline/run-analysis.d.ts.map +1 -1
- package/dist/pipeline/source-ingestion.d.ts +3 -0
- package/dist/pipeline/source-ingestion.d.ts.map +1 -1
- package/dist/pipeline/system-config.d.ts +9 -0
- package/dist/pipeline/system-config.d.ts.map +1 -1
- package/dist/{pipeline-BuiItKT-.mjs → pipeline-6wmD00rr.mjs} +697 -260
- package/dist/{pipeline-CD2qmEEn.cjs → pipeline-A54wEQEg.cjs} +707 -258
- package/dist/session/extraction-session.d.ts +16 -1
- package/dist/session/extraction-session.d.ts.map +1 -1
- package/dist/session/index.d.ts +1 -1
- package/dist/session/index.d.ts.map +1 -1
- package/dist/session.cjs +42 -9
- package/dist/session.mjs +43 -10
- package/package.json +4 -4
|
@@ -7,7 +7,7 @@ import { realpathSync as realpathSync$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
|
-
import { isUnitlessProperty } from "@animus-ui/properties";
|
|
10
|
+
import { dependsOnContext, foldInitialValue, isUnitlessProperty, substitutesValue } from "@animus-ui/properties";
|
|
11
11
|
//#region \0rolldown/runtime.js
|
|
12
12
|
var __require = /* #__PURE__ */ (() => createRequire(import.meta.url))();
|
|
13
13
|
//#endregion
|
|
@@ -85,7 +85,8 @@ function buildAnalyzeProjectArgs(inputs) {
|
|
|
85
85
|
inputs.transformSourcesJson,
|
|
86
86
|
inputs.transformProvenanceJson ?? null
|
|
87
87
|
];
|
|
88
|
-
if (inputs.declarationScalesJson) args.push(inputs.declarationScalesJson);
|
|
88
|
+
if (inputs.declarationScalesJson || inputs.analysisContextJson) args.push(inputs.declarationScalesJson);
|
|
89
|
+
if (inputs.analysisContextJson) args.push(inputs.analysisContextJson);
|
|
89
90
|
return args;
|
|
90
91
|
}
|
|
91
92
|
//#endregion
|
|
@@ -105,6 +106,224 @@ function parseInternalWire(json, wire) {
|
|
|
105
106
|
return parsed;
|
|
106
107
|
}
|
|
107
108
|
//#endregion
|
|
109
|
+
//#region pipeline/manifest-diagnostics.ts
|
|
110
|
+
/** Stable code for selector forms with no substitutable subject; the
|
|
111
|
+
* extraction engine mints the same string. */
|
|
112
|
+
const SELECTOR_UNSUPPORTED_SUBJECT = "animus.selector.unsupported-subject";
|
|
113
|
+
/** The one matcher for the engine's unresolved-parent chain-drop message;
|
|
114
|
+
* consumers match through the exported helpers, never their own copy. */
|
|
115
|
+
const UNRESOLVED_PARENT_RE = /chain dropped: could not resolve parent component '([^']+)'/;
|
|
116
|
+
function isUnresolvedParentDrop(diagnostic) {
|
|
117
|
+
return UNRESOLVED_PARENT_RE.test(diagnostic.message);
|
|
118
|
+
}
|
|
119
|
+
function unresolvedParentName(diagnostic) {
|
|
120
|
+
return UNRESOLVED_PARENT_RE.exec(diagnostic.message)?.[1] ?? null;
|
|
121
|
+
}
|
|
122
|
+
/** True when the selector carries at least one substitutable `&` subject
|
|
123
|
+
* outside quoted text. */
|
|
124
|
+
function hasSelectorSubject(value) {
|
|
125
|
+
let quote = null;
|
|
126
|
+
let escaped = false;
|
|
127
|
+
for (const c of value) {
|
|
128
|
+
if (escaped) {
|
|
129
|
+
escaped = false;
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
if (c === "\\") {
|
|
133
|
+
escaped = true;
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
if (quote !== null) {
|
|
137
|
+
if (c === quote) quote = null;
|
|
138
|
+
} else if (c === "\"" || c === "'") quote = c;
|
|
139
|
+
else if (c === "&") return true;
|
|
140
|
+
}
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* Coded diagnostics for selector-shaped alias values with no substitutable
|
|
145
|
+
* subject; alias values never reach the evaluator's own key guard.
|
|
146
|
+
*/
|
|
147
|
+
function collectSelectorAliasDiagnostics(selectorAliasesJson) {
|
|
148
|
+
if (!selectorAliasesJson) return [];
|
|
149
|
+
const aliases = parseInternalWire(selectorAliasesJson, "selectorAliasesJson (the system loader's selector-alias registry)");
|
|
150
|
+
const diagnostics = [];
|
|
151
|
+
for (const [name, value] of Object.entries(aliases)) if (value.includes("&") && !hasSelectorSubject(value)) diagnostics.push({
|
|
152
|
+
file: "system",
|
|
153
|
+
component: name,
|
|
154
|
+
kind: "warn",
|
|
155
|
+
message: `selector alias '${name}' value '${value}' has no substitutable '&' subject outside quoted text (${SELECTOR_UNSUPPORTED_SUBJECT})`,
|
|
156
|
+
code: SELECTOR_UNSUPPORTED_SUBJECT,
|
|
157
|
+
severity: severityFor(SELECTOR_UNSUPPORTED_SUBJECT)
|
|
158
|
+
});
|
|
159
|
+
return diagnostics;
|
|
160
|
+
}
|
|
161
|
+
const UNREADABLE_SOURCE_FILE = "animus.ingestion.unreadable-source-file";
|
|
162
|
+
/** What a skipped source file costs: the analysis never sees what it
|
|
163
|
+
* renders. */
|
|
164
|
+
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";
|
|
165
|
+
/**
|
|
166
|
+
* A configured source file that could not be read is a LOST INPUT: the
|
|
167
|
+
* emitted artifacts silently lack whatever that file declared.
|
|
168
|
+
*/
|
|
169
|
+
/** Whether a failed source read means the file was deleted after discovery
|
|
170
|
+
* (an editor's delete-and-recreate, a branch switch): a deletion, not lost
|
|
171
|
+
* input. */
|
|
172
|
+
function isDeletedSource(error) {
|
|
173
|
+
return error?.code === "ENOENT";
|
|
174
|
+
}
|
|
175
|
+
function unreadableSourceDiagnostic(relPath, error) {
|
|
176
|
+
return {
|
|
177
|
+
file: relPath,
|
|
178
|
+
component: "source",
|
|
179
|
+
kind: "warn",
|
|
180
|
+
message: `configured source file ${relPath} could not be read: ${String(error)} (${SKIPPED_SOURCE_COST})`,
|
|
181
|
+
code: UNREADABLE_SOURCE_FILE,
|
|
182
|
+
severity: severityFor(UNREADABLE_SOURCE_FILE)
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
/** The collision entry code minted by the system package's merge. */
|
|
186
|
+
const VOCABULARY_COLLISION = "animus.vocabulary.collision";
|
|
187
|
+
/** A theme `@property` registration browsers would ignore; it is not emitted. */
|
|
188
|
+
const INVALID_PROPERTY_REGISTRATION = "animus.theme.invalid-property-registration";
|
|
189
|
+
/** Minted when a sealed kit with registered vocabulary is consumed through
|
|
190
|
+
* the deprecated `from()`/`includes:` verbs. */
|
|
191
|
+
const VOCABULARY_LEGACY_VERB = "animus.vocabulary.legacy-verb";
|
|
192
|
+
/** A `var()` fallback that never applies: the property is registered with an
|
|
193
|
+
* initial value. */
|
|
194
|
+
const PROPERTY_FALLBACK_SUPPRESSED = "animus.property.fallback-suppressed";
|
|
195
|
+
/** The same, where the fallback heads a chain of further `var()` reads. */
|
|
196
|
+
const PROPERTY_FALLBACK_CHAIN_SUPPRESSED = "animus.property.fallback-chain-suppressed";
|
|
197
|
+
/** A declared property set in keyframes or transitioned without a typed
|
|
198
|
+
* registration, so it does not interpolate. */
|
|
199
|
+
const PROPERTY_UNREGISTERED_ANIMATION = "animus.property.unregistered-animation";
|
|
200
|
+
/** A custom property whose resolved value is `var()` of itself, which makes
|
|
201
|
+
* it invalid at computed-value time. */
|
|
202
|
+
const PROPERTY_SELF_REFERENCE = "animus.property.self-reference";
|
|
203
|
+
/** A custom property that reads itself only inside another `var()`'s
|
|
204
|
+
* fallback, so whether it is cyclic depends on the browser. */
|
|
205
|
+
const PROPERTY_FALLBACK_SELF_REFERENCE = "animus.property.fallback-self-reference";
|
|
206
|
+
/** A prefix renamed contextual variables without `prefixContextualVars`, so
|
|
207
|
+
* their declared names no longer resolve. */
|
|
208
|
+
const PREFIX_CONTEXTUAL_VARS_UNPREFIXED = "animus.prefix.contextual-vars-unprefixed";
|
|
209
|
+
/** Under `prefixContextualVars`, a final name that collides with a runtime
|
|
210
|
+
* transport variable or a theme variable of the same spelling. */
|
|
211
|
+
const PREFIX_NAME_CONFLICT = "animus.prefix.name-conflict";
|
|
212
|
+
const DIAGNOSTIC_SEVERITY = /* @__PURE__ */ new Map([
|
|
213
|
+
[SELECTOR_UNSUPPORTED_SUBJECT, "error"],
|
|
214
|
+
[UNREADABLE_SOURCE_FILE, "error"],
|
|
215
|
+
[VOCABULARY_COLLISION, "warn"],
|
|
216
|
+
[VOCABULARY_LEGACY_VERB, "warn"],
|
|
217
|
+
[INVALID_PROPERTY_REGISTRATION, "error"],
|
|
218
|
+
[PROPERTY_FALLBACK_SUPPRESSED, "info"],
|
|
219
|
+
[PROPERTY_FALLBACK_CHAIN_SUPPRESSED, "warn"],
|
|
220
|
+
[PROPERTY_UNREGISTERED_ANIMATION, "info"],
|
|
221
|
+
[PROPERTY_SELF_REFERENCE, "warn"],
|
|
222
|
+
[PROPERTY_FALLBACK_SELF_REFERENCE, "warn"],
|
|
223
|
+
[PREFIX_CONTEXTUAL_VARS_UNPREFIXED, "warn"],
|
|
224
|
+
[PREFIX_NAME_CONFLICT, "error"]
|
|
225
|
+
]);
|
|
226
|
+
/** An unlisted code is `warn`: a witness kind from a newer system package
|
|
227
|
+
* must not fail the strict build of a host that predates its writer. */
|
|
228
|
+
function severityFor(code) {
|
|
229
|
+
return (code === void 0 ? void 0 : DIAGNOSTIC_SEVERITY.get(code)) ?? "warn";
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* The one mapper of a sealed system's witness entries for every host: the
|
|
233
|
+
* loader's evaluation host shims `console`, so the record is the channel.
|
|
234
|
+
*/
|
|
235
|
+
function vocabularyWitnessDiagnostics(vocabularyWitnessesJson) {
|
|
236
|
+
if (!vocabularyWitnessesJson) return [];
|
|
237
|
+
const entries = parseInternalWire(vocabularyWitnessesJson, "vocabularyWitnessesJson (the sealed system's vocabulary witness record)");
|
|
238
|
+
const diagnostics = [];
|
|
239
|
+
for (const entry of entries) if (entry.code === "animus.vocabulary.collision") diagnostics.push({
|
|
240
|
+
file: "system",
|
|
241
|
+
component: entry.name ?? "keyframes",
|
|
242
|
+
kind: "warn",
|
|
243
|
+
message: `keyframes vocabulary "${entry.name}" is registered by both ${entry.loser} and ${entry.winner} — ${entry.winner} wins; rename one collection (${entry.code})`,
|
|
244
|
+
code: entry.code,
|
|
245
|
+
severity: severityFor(entry.code)
|
|
246
|
+
});
|
|
247
|
+
else if (entry.code === "animus.vocabulary.legacy-verb") diagnostics.push({
|
|
248
|
+
file: "system",
|
|
249
|
+
component: "keyframes",
|
|
250
|
+
kind: "warn",
|
|
251
|
+
message: `a sealed system (${entry.source ?? `'${entry.verb}' source`}) with registered vocabulary [${(entry.names ?? []).join(", ")}] was consumed through the deprecated '${entry.verb}' verb, which cannot carry it — those collections do NOT reach this consumer; use createSystem().extend(source) (${entry.code})`,
|
|
252
|
+
code: entry.code,
|
|
253
|
+
severity: severityFor(entry.code)
|
|
254
|
+
});
|
|
255
|
+
else diagnostics.push({
|
|
256
|
+
file: "system",
|
|
257
|
+
component: "vocabulary",
|
|
258
|
+
kind: "warn",
|
|
259
|
+
message: `unrecognized vocabulary witness entry ${JSON.stringify(entry)} — a newer @animus-ui/system may have recorded a witness kind this host predates${entry.code ? ` (${entry.code})` : ""}`,
|
|
260
|
+
code: entry.code,
|
|
261
|
+
severity: severityFor(entry.code)
|
|
262
|
+
});
|
|
263
|
+
return diagnostics;
|
|
264
|
+
}
|
|
265
|
+
/** The diagnostics a loaded system's own records carry: vocabulary
|
|
266
|
+
* witnesses and invalid `@property` registrations. */
|
|
267
|
+
function systemLoadDiagnostics(system) {
|
|
268
|
+
const registrations = (system.invalidPropertyRegistrations ?? []).map(({ name, reason }) => ({
|
|
269
|
+
file: "system",
|
|
270
|
+
component: name,
|
|
271
|
+
kind: "warn",
|
|
272
|
+
message: `@property ${name} was not emitted: ${reason}. Browsers ignore an invalid registration and minifying it fails; fix it where the theme declares ${name} (${INVALID_PROPERTY_REGISTRATION})`,
|
|
273
|
+
code: INVALID_PROPERTY_REGISTRATION,
|
|
274
|
+
severity: severityFor(INVALID_PROPERTY_REGISTRATION)
|
|
275
|
+
}));
|
|
276
|
+
const prefixed = system.legacyPrefixedContextualVars ?? [];
|
|
277
|
+
const unprefixed = prefixed.length === 0 ? [] : [{
|
|
278
|
+
file: "system",
|
|
279
|
+
component: "prefix",
|
|
280
|
+
kind: "warn",
|
|
281
|
+
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})`,
|
|
282
|
+
code: PREFIX_CONTEXTUAL_VARS_UNPREFIXED,
|
|
283
|
+
severity: severityFor(PREFIX_CONTEXTUAL_VARS_UNPREFIXED)
|
|
284
|
+
}];
|
|
285
|
+
const conflicts = (system.prefixNameConflicts ?? []).map(({ name, final, reason }) => ({
|
|
286
|
+
file: "system",
|
|
287
|
+
component: `--${name}`,
|
|
288
|
+
kind: "error",
|
|
289
|
+
message: reason === "transport" ? `the prefix would emit the contextual variable ${name} as --${final}, inside the --animus- names of the runtime's transport variables, where it can collide with a prop's own variable. Use a different prefix (${PREFIX_NAME_CONFLICT})` : `the theme variable --${name} is spelled like the final name of the contextual variable ${final}, so under prefixContextualVars a reference to --${name} reaches ${final} and the theme variable is unreachable. Rename one of them (${PREFIX_NAME_CONFLICT})`,
|
|
290
|
+
code: PREFIX_NAME_CONFLICT,
|
|
291
|
+
severity: severityFor(PREFIX_NAME_CONFLICT)
|
|
292
|
+
}));
|
|
293
|
+
return [
|
|
294
|
+
...vocabularyWitnessDiagnostics(system.vocabularyWitnessesJson),
|
|
295
|
+
...registrations,
|
|
296
|
+
...unprefixed,
|
|
297
|
+
...conflicts
|
|
298
|
+
];
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* The single strict-escalation policy point for both extraction plugins:
|
|
302
|
+
* error-severity diagnostics throw together under `strict`, else warn.
|
|
303
|
+
*/
|
|
304
|
+
function surfaceManifestDiagnostics(manifest, warn, policy = {}) {
|
|
305
|
+
const errors = [];
|
|
306
|
+
const diagnostics = policy.prepend?.length ? [...policy.prepend, ...manifest.diagnostics ?? []] : manifest.diagnostics ?? [];
|
|
307
|
+
for (const diagnostic of diagnostics) {
|
|
308
|
+
let line = null;
|
|
309
|
+
if (diagnostic.kind === "bail") line = `⚠ ${diagnostic.component} not extracted: ${diagnostic.message}`;
|
|
310
|
+
else if (diagnostic.kind === "skip") line = `⚠ ${diagnostic.component}: skipped ${diagnostic.message}`;
|
|
311
|
+
else if (diagnostic.kind === "warn") line = `${diagnostic.severity === "info" ? "ℹ" : "⚠"} ${diagnostic.file}: ${diagnostic.component}: ${diagnostic.message}`;
|
|
312
|
+
if (line === null) continue;
|
|
313
|
+
if (diagnostic.code && !diagnostic.message.includes(diagnostic.code)) line += ` [${diagnostic.code}]`;
|
|
314
|
+
if (diagnostic.severity === "info") {
|
|
315
|
+
policy.info?.(line);
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
if (policy.strict && diagnostic.severity === "error") {
|
|
319
|
+
errors.push(`${diagnostic.code ?? "error"} — ${diagnostic.component}: ${diagnostic.message}`);
|
|
320
|
+
continue;
|
|
321
|
+
}
|
|
322
|
+
warn(line);
|
|
323
|
+
}
|
|
324
|
+
if (errors.length > 0) throw new Error(`[animus] strict: ${errors.length} error diagnostic(s):\n${errors.join("\n")}`);
|
|
325
|
+
}
|
|
326
|
+
//#endregion
|
|
108
327
|
//#region pipeline/mdx-preprocessor.ts
|
|
109
328
|
/**
|
|
110
329
|
* MDX compiled to scanner-consumable JSX, plus the extension sets every
|
|
@@ -1029,7 +1248,8 @@ function withoutInvalidOriginals(result, invalidOriginals) {
|
|
|
1029
1248
|
...result,
|
|
1030
1249
|
originalEntries: result.originalEntries.filter((entry) => !invalidOriginals.has(entry.path)),
|
|
1031
1250
|
analysisEntries: result.analysisEntries.filter((entry) => analysisPaths.has(entry.path)),
|
|
1032
|
-
ownership
|
|
1251
|
+
ownership,
|
|
1252
|
+
skippedOriginals: [...invalidOriginals].sort()
|
|
1033
1253
|
};
|
|
1034
1254
|
}
|
|
1035
1255
|
/**
|
|
@@ -1069,11 +1289,12 @@ function createSourceIngestor(host) {
|
|
|
1069
1289
|
const invalidOriginals = new Set(fatal.map((diagnostic) => diagnostic.originalPath));
|
|
1070
1290
|
if (diagnostics.length === 0) return invalidOriginals;
|
|
1071
1291
|
if (host.strict() && fatal.length > 0) {
|
|
1072
|
-
const lines = fatal.map((diagnostic) => `${diagnostic.code} ${diagnostic.originalPath}: ${diagnostic.message}`);
|
|
1292
|
+
const lines = fatal.map((diagnostic) => `${diagnostic.code} ${diagnostic.originalPath}: ${diagnostic.message} (${SKIPPED_SOURCE_COST})`);
|
|
1073
1293
|
throw new Error(`${host.prefix} ${lines.join(`\n${host.prefix} `)}`);
|
|
1074
1294
|
}
|
|
1075
1295
|
for (const diagnostic of diagnostics) {
|
|
1076
|
-
const
|
|
1296
|
+
const skipped = isAdvisorySourceDiagnostic(diagnostic) ? "" : ` (${SKIPPED_SOURCE_COST})`;
|
|
1297
|
+
const line = `${diagnostic.code} ${diagnostic.originalPath}: ${diagnostic.message}${skipped}`;
|
|
1077
1298
|
let warned = warnedByOriginal.get(diagnostic.originalPath);
|
|
1078
1299
|
if (!warned) {
|
|
1079
1300
|
warned = /* @__PURE__ */ new Set();
|
|
@@ -1108,7 +1329,7 @@ function createV2EngineApi(deps) {
|
|
|
1108
1329
|
return {
|
|
1109
1330
|
loadSystemModule: (...args) => native.loadSystemModule(...args),
|
|
1110
1331
|
extractFacts: (filesJson) => native.extractFacts(filesJson),
|
|
1111
|
-
analyzeProject: (filesJsonRaw, scalesJson, variableMapJson, contextualVarsJson, propConfigJson, groupRegistryJson, packageResolutionJson, devMode, emitterConfigJson, selectorAliasesJson, _selectorOrderJson, globalStyleBlocksJson, pathAliasesJson, keyframesJson, staticCssJson, conditionAliasesJson, externalDirsJson, transformSourcesJson, transformProvenanceJson, declarationScalesJson) => {
|
|
1332
|
+
analyzeProject: (filesJsonRaw, scalesJson, variableMapJson, contextualVarsJson, propConfigJson, groupRegistryJson, packageResolutionJson, devMode, emitterConfigJson, selectorAliasesJson, _selectorOrderJson, globalStyleBlocksJson, pathAliasesJson, keyframesJson, staticCssJson, conditionAliasesJson, externalDirsJson, transformSourcesJson, transformProvenanceJson, declarationScalesJson, analysisContextJson) => {
|
|
1112
1333
|
const filesJson = deps.rehydrateFilesJson ? deps.rehydrateFilesJson(filesJsonRaw) : filesJsonRaw;
|
|
1113
1334
|
const sent = /* @__PURE__ */ new Map();
|
|
1114
1335
|
for (const entry of parseFilesJson(filesJson, label)) sent.set(entry.path, entry.source);
|
|
@@ -1135,6 +1356,7 @@ function createV2EngineApi(deps) {
|
|
|
1135
1356
|
transformSourcesJson: transformSourcesJson ?? void 0,
|
|
1136
1357
|
transformProvenanceJson: transformProvenanceJson ?? void 0,
|
|
1137
1358
|
declarationScalesJson,
|
|
1359
|
+
analysisContextJson,
|
|
1138
1360
|
devMode
|
|
1139
1361
|
};
|
|
1140
1362
|
const engine = new native.ExtractEngine(config);
|
|
@@ -1207,6 +1429,7 @@ const CORE_OPTION_KEYS = /* @__PURE__ */ new Set([
|
|
|
1207
1429
|
"strict",
|
|
1208
1430
|
"verbose",
|
|
1209
1431
|
"prefix",
|
|
1432
|
+
"prefixContextualVars",
|
|
1210
1433
|
"targets",
|
|
1211
1434
|
"minify",
|
|
1212
1435
|
"staticCss",
|
|
@@ -1300,6 +1523,11 @@ const CORE_VALUE_GATES = [
|
|
|
1300
1523
|
ok: isString,
|
|
1301
1524
|
expected: "a string"
|
|
1302
1525
|
},
|
|
1526
|
+
{
|
|
1527
|
+
key: "prefixContextualVars",
|
|
1528
|
+
ok: isBoolean,
|
|
1529
|
+
expected: "a boolean"
|
|
1530
|
+
},
|
|
1303
1531
|
{
|
|
1304
1532
|
key: "targets",
|
|
1305
1533
|
ok: isStringOrStringArray,
|
|
@@ -1998,31 +2226,113 @@ function createSourceCorpus(host, ingestor = createSourceIngestor(host)) {
|
|
|
1998
2226
|
//#endregion
|
|
1999
2227
|
//#region pipeline/asset-placeholders.ts
|
|
2000
2228
|
const ASSET_PLACEHOLDER_PREFIX = "animus-asset:";
|
|
2001
|
-
const
|
|
2002
|
-
const
|
|
2003
|
-
const
|
|
2004
|
-
|
|
2005
|
-
function
|
|
2229
|
+
const URL_OPEN_RE = /url\(\s*(['"]?)/gi;
|
|
2230
|
+
const IMAGE_SET_OPEN_RE = /(?:-webkit-)?image-set\(/gi;
|
|
2231
|
+
const BARE_SPECIFIER_RE = /^[^'")\s]+/;
|
|
2232
|
+
/** The index of the `)` that closes the `(` at `open`, outside strings. */
|
|
2233
|
+
function closingParen$1(css, open) {
|
|
2234
|
+
let depth = 0;
|
|
2235
|
+
let quote = null;
|
|
2236
|
+
for (let i = open; i < css.length; i += 1) {
|
|
2237
|
+
const c = css[i];
|
|
2238
|
+
if (quote !== null) {
|
|
2239
|
+
if (c === quote) quote = null;
|
|
2240
|
+
} else if (c === "\"" || c === "'") quote = c;
|
|
2241
|
+
else if (c === "(") depth += 1;
|
|
2242
|
+
else if (c === ")") {
|
|
2243
|
+
depth -= 1;
|
|
2244
|
+
if (depth === 0) return i;
|
|
2245
|
+
}
|
|
2246
|
+
}
|
|
2247
|
+
return css.length;
|
|
2248
|
+
}
|
|
2249
|
+
function quotedReference(css, quoteAt) {
|
|
2250
|
+
const start = quoteAt + 1;
|
|
2251
|
+
if (!css.startsWith("animus-asset:", start)) return null;
|
|
2252
|
+
const end = css.indexOf(css[quoteAt], start);
|
|
2253
|
+
if (end === -1) return null;
|
|
2254
|
+
return {
|
|
2255
|
+
start,
|
|
2256
|
+
end,
|
|
2257
|
+
specifier: css.slice(start + 13, end)
|
|
2258
|
+
};
|
|
2259
|
+
}
|
|
2260
|
+
function assetReferences(css) {
|
|
2006
2261
|
if (!css.includes("animus-asset:")) return [];
|
|
2007
|
-
const
|
|
2008
|
-
const
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2262
|
+
const references = [];
|
|
2263
|
+
for (const open of css.matchAll(URL_OPEN_RE)) {
|
|
2264
|
+
const at = (open.index ?? 0) + open[0].length;
|
|
2265
|
+
if (open[1]) {
|
|
2266
|
+
const reference = quotedReference(css, at - 1);
|
|
2267
|
+
if (reference) references.push(reference);
|
|
2268
|
+
continue;
|
|
2269
|
+
}
|
|
2270
|
+
if (!css.startsWith("animus-asset:", at)) continue;
|
|
2271
|
+
const specifier = BARE_SPECIFIER_RE.exec(css.slice(at + 13))?.[0];
|
|
2272
|
+
if (specifier === void 0) continue;
|
|
2273
|
+
const end = at + 13 + specifier.length;
|
|
2274
|
+
references.push({
|
|
2275
|
+
start: at,
|
|
2276
|
+
end,
|
|
2277
|
+
specifier
|
|
2278
|
+
});
|
|
2279
|
+
}
|
|
2280
|
+
for (const open of css.matchAll(IMAGE_SET_OPEN_RE)) {
|
|
2281
|
+
const paren = (open.index ?? 0) + open[0].length - 1;
|
|
2282
|
+
const close = closingParen$1(css, paren);
|
|
2283
|
+
let depth = 0;
|
|
2284
|
+
let candidateStart = true;
|
|
2285
|
+
for (let i = paren + 1; i < close; i += 1) {
|
|
2286
|
+
const c = css[i];
|
|
2287
|
+
if (/\s/.test(c)) continue;
|
|
2288
|
+
if (c === "\"" || c === "'") {
|
|
2289
|
+
if (depth === 0 && candidateStart) {
|
|
2290
|
+
const reference = quotedReference(css, i);
|
|
2291
|
+
if (reference) references.push(reference);
|
|
2292
|
+
}
|
|
2293
|
+
const end = css.indexOf(c, i + 1);
|
|
2294
|
+
i = end === -1 ? close : end;
|
|
2295
|
+
} else if (c === "(") depth += 1;
|
|
2296
|
+
else if (c === ")") depth -= 1;
|
|
2297
|
+
else if (c === "," && depth === 0) {
|
|
2298
|
+
candidateStart = true;
|
|
2299
|
+
continue;
|
|
2300
|
+
}
|
|
2301
|
+
candidateStart = false;
|
|
2302
|
+
}
|
|
2303
|
+
}
|
|
2304
|
+
return references.sort((a, b) => a.start - b.start);
|
|
2305
|
+
}
|
|
2306
|
+
function findAssetSpecifiers(css) {
|
|
2307
|
+
return [...new Set(assetReferences(css).map((reference) => reference.specifier))];
|
|
2014
2308
|
}
|
|
2015
2309
|
function substituteAssetPlaceholders(css, urlBySpecifier) {
|
|
2016
2310
|
if (urlBySpecifier.size === 0) return css;
|
|
2017
|
-
if (!css.includes("animus-asset:")) return css;
|
|
2018
|
-
const specifiers = [...urlBySpecifier.keys()].sort((a, b) => b.length - a.length);
|
|
2019
2311
|
let out = css;
|
|
2020
|
-
for (const
|
|
2021
|
-
const
|
|
2022
|
-
|
|
2312
|
+
for (const reference of assetReferences(css).reverse()) {
|
|
2313
|
+
const url = urlBySpecifier.get(reference.specifier);
|
|
2314
|
+
if (url === void 0) continue;
|
|
2315
|
+
out = out.slice(0, reference.start) + url + out.slice(reference.end);
|
|
2023
2316
|
}
|
|
2024
2317
|
return out;
|
|
2025
2318
|
}
|
|
2319
|
+
/**
|
|
2320
|
+
* Specifiers of placeholder text anywhere inside a `url()` or `image-set()`
|
|
2321
|
+
* call, a reference or not: after substitution any of it is a broken load.
|
|
2322
|
+
*/
|
|
2323
|
+
function placeholdersInAssetCalls(css) {
|
|
2324
|
+
if (!css.includes("animus-asset:")) return [];
|
|
2325
|
+
const calls = [...css.matchAll(URL_OPEN_RE), ...css.matchAll(IMAGE_SET_OPEN_RE)].map((open) => {
|
|
2326
|
+
const paren = css.indexOf("(", open.index ?? 0);
|
|
2327
|
+
return [paren, closingParen$1(css, paren)];
|
|
2328
|
+
});
|
|
2329
|
+
const found = /* @__PURE__ */ new Set();
|
|
2330
|
+
for (let at = css.indexOf(ASSET_PLACEHOLDER_PREFIX); at !== -1; at = css.indexOf(ASSET_PLACEHOLDER_PREFIX, at + 1)) {
|
|
2331
|
+
if (!calls.some(([open, close]) => open < at && at < close)) continue;
|
|
2332
|
+
found.add(/^[^'")\s,]*/.exec(css.slice(at + 13))[0]);
|
|
2333
|
+
}
|
|
2334
|
+
return [...found];
|
|
2335
|
+
}
|
|
2026
2336
|
function findSheetAssetSpecifiers(sheets) {
|
|
2027
2337
|
return [.../* @__PURE__ */ new Set([
|
|
2028
2338
|
...findAssetSpecifiers(sheets.variableCss),
|
|
@@ -2042,12 +2352,22 @@ const UNSUBSTITUTED_ASSET_CODE = "animus.asset.unsubstituted-placeholder";
|
|
|
2042
2352
|
/** A placeholder in emitted CSS is a broken URL in the browser: it fails a
|
|
2043
2353
|
* strict build and warns otherwise. */
|
|
2044
2354
|
function reportSurvivingAssetPlaceholders(css, report) {
|
|
2045
|
-
const specifiers =
|
|
2355
|
+
const specifiers = placeholdersInAssetCalls(css);
|
|
2046
2356
|
if (specifiers.length === 0) return;
|
|
2047
|
-
const message = `${report.prefix} asset() placeholders reached emitted CSS unsubstituted: ${specifiers.join(", ")} (${UNSUBSTITUTED_ASSET_CODE})`;
|
|
2357
|
+
const message = `${report.prefix} asset() placeholders reached ${report.surface ?? "emitted CSS"} unsubstituted: ${specifiers.join(", ")} (${UNSUBSTITUTED_ASSET_CODE})`;
|
|
2048
2358
|
if (report.strict) throw new Error(message);
|
|
2049
2359
|
report.warn(message);
|
|
2050
2360
|
}
|
|
2361
|
+
/**
|
|
2362
|
+
* The runtime code an analysis generates: each dynamic prop config, which the
|
|
2363
|
+
* shared prop map module carries, and each component's replacement. Extraction
|
|
2364
|
+
* lifts asset() out of them into root variables, so none should hold one.
|
|
2365
|
+
* Both embed values as JSON strings, so their quote and backslash escapes are
|
|
2366
|
+
* undone for scanning: the text is for `findAssetSpecifiers`, not to run.
|
|
2367
|
+
*/
|
|
2368
|
+
function generatedModuleCode(manifest) {
|
|
2369
|
+
return [JSON.stringify(manifest.dynamic_props), ...Object.values(manifest.components).map((component) => component.replacement)].join("\n").replace(/\\(["\\])/g, "$1");
|
|
2370
|
+
}
|
|
2051
2371
|
//#endregion
|
|
2052
2372
|
//#region pipeline/resolve-asset.ts
|
|
2053
2373
|
/**
|
|
@@ -2271,182 +2591,121 @@ function postProcessCss(css, opts) {
|
|
|
2271
2591
|
}
|
|
2272
2592
|
}
|
|
2273
2593
|
//#endregion
|
|
2274
|
-
//#region pipeline/
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
const
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
const
|
|
2281
|
-
|
|
2282
|
-
return
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2594
|
+
//#region pipeline/property-names.ts
|
|
2595
|
+
function createPropertyNames(declared, prefix, themeNames = []) {
|
|
2596
|
+
const finals = /* @__PURE__ */ new Map();
|
|
2597
|
+
for (const name of [...new Set(declared)].sort()) finals.set(name, `${prefix}-${name}`);
|
|
2598
|
+
const aliases = /* @__PURE__ */ new Map();
|
|
2599
|
+
for (const [name, final] of finals) if (!finals.has(final)) aliases.set(final, name);
|
|
2600
|
+
const others = /* @__PURE__ */ new Map();
|
|
2601
|
+
for (const name of [...new Set(themeNames)].sort()) if (!finals.has(name)) others.set(name, `${prefix}-${name}`);
|
|
2602
|
+
return {
|
|
2603
|
+
identity: (spelling) => finals.has(spelling) ? spelling : aliases.get(spelling) ?? (others.has(spelling) ? spelling : void 0),
|
|
2604
|
+
finalName: (name) => finals.get(name) ?? others.get(name),
|
|
2605
|
+
ambiguous: () => [...others.keys()].filter((name) => aliases.has(name)).map((name) => ({
|
|
2606
|
+
name,
|
|
2607
|
+
contextual: aliases.get(name) ?? name
|
|
2608
|
+
}))
|
|
2609
|
+
};
|
|
2286
2610
|
}
|
|
2287
|
-
|
|
2288
|
-
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
|
|
2298
|
-
|
|
2611
|
+
const NAME_CHAR = /[\w-]/;
|
|
2612
|
+
/**
|
|
2613
|
+
* The name starting at `from`, read as CSS tokenizes an identifier: ASCII
|
|
2614
|
+
* letters, digits, `-` and `_`, every non-ASCII code point, and escapes.
|
|
2615
|
+
*/
|
|
2616
|
+
function readCustomPropertyName(css, from) {
|
|
2617
|
+
let end = from;
|
|
2618
|
+
let name = "";
|
|
2619
|
+
while (end < css.length) {
|
|
2620
|
+
if (isNameChar(css[end])) {
|
|
2621
|
+
name += css[end];
|
|
2622
|
+
end += 1;
|
|
2299
2623
|
continue;
|
|
2300
2624
|
}
|
|
2301
|
-
|
|
2302
|
-
|
|
2303
|
-
|
|
2304
|
-
|
|
2625
|
+
const escape = escapeAt(css, end);
|
|
2626
|
+
if (escape === null) break;
|
|
2627
|
+
name += escape.codePoint;
|
|
2628
|
+
end += escape.length;
|
|
2305
2629
|
}
|
|
2306
|
-
return false;
|
|
2307
|
-
}
|
|
2308
|
-
/**
|
|
2309
|
-
* Coded diagnostics for selector-shaped alias values with no substitutable
|
|
2310
|
-
* subject; alias values never reach the evaluator's own key guard.
|
|
2311
|
-
*/
|
|
2312
|
-
function collectSelectorAliasDiagnostics(selectorAliasesJson) {
|
|
2313
|
-
if (!selectorAliasesJson) return [];
|
|
2314
|
-
const aliases = parseInternalWire(selectorAliasesJson, "selectorAliasesJson (the system loader's selector-alias registry)");
|
|
2315
|
-
const diagnostics = [];
|
|
2316
|
-
for (const [name, value] of Object.entries(aliases)) if (value.includes("&") && !hasSelectorSubject(value)) diagnostics.push({
|
|
2317
|
-
file: "system",
|
|
2318
|
-
component: name,
|
|
2319
|
-
kind: "warn",
|
|
2320
|
-
message: `selector alias '${name}' value '${value}' has no substitutable '&' subject outside quoted text (${SELECTOR_UNSUPPORTED_SUBJECT})`,
|
|
2321
|
-
code: SELECTOR_UNSUPPORTED_SUBJECT,
|
|
2322
|
-
severity: severityFor(SELECTOR_UNSUPPORTED_SUBJECT)
|
|
2323
|
-
});
|
|
2324
|
-
return diagnostics;
|
|
2325
|
-
}
|
|
2326
|
-
const UNREADABLE_SOURCE_FILE = "animus.ingestion.unreadable-source-file";
|
|
2327
|
-
/**
|
|
2328
|
-
* A configured source file that could not be read is a LOST INPUT: the
|
|
2329
|
-
* emitted artifacts silently lack whatever that file declared.
|
|
2330
|
-
*/
|
|
2331
|
-
function unreadableSourceDiagnostic(relPath, error) {
|
|
2332
2630
|
return {
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
kind: "warn",
|
|
2336
|
-
message: `configured source file ${relPath} could not be read and was skipped: ${String(error)}`,
|
|
2337
|
-
code: UNREADABLE_SOURCE_FILE,
|
|
2338
|
-
severity: severityFor(UNREADABLE_SOURCE_FILE)
|
|
2631
|
+
end,
|
|
2632
|
+
name
|
|
2339
2633
|
};
|
|
2340
2634
|
}
|
|
2341
|
-
/**
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2345
|
-
|
|
2346
|
-
|
|
2347
|
-
const
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
const
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
const
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
const PROPERTY_SELF_REFERENCE = "animus.property.self-reference";
|
|
2361
|
-
const DIAGNOSTIC_SEVERITY = /* @__PURE__ */ new Map([
|
|
2362
|
-
[SELECTOR_UNSUPPORTED_SUBJECT, "error"],
|
|
2363
|
-
[UNREADABLE_SOURCE_FILE, "error"],
|
|
2364
|
-
[VOCABULARY_COLLISION, "warn"],
|
|
2365
|
-
[VOCABULARY_LEGACY_VERB, "warn"],
|
|
2366
|
-
[INVALID_PROPERTY_REGISTRATION, "error"],
|
|
2367
|
-
[PROPERTY_FALLBACK_SUPPRESSED, "info"],
|
|
2368
|
-
[PROPERTY_FALLBACK_CHAIN_SUPPRESSED, "warn"],
|
|
2369
|
-
[PROPERTY_UNREGISTERED_ANIMATION, "info"],
|
|
2370
|
-
[PROPERTY_DISCRETE_ANIMATION, "warn"],
|
|
2371
|
-
[PROPERTY_SELF_REFERENCE, "warn"]
|
|
2372
|
-
]);
|
|
2373
|
-
/** An unlisted code is `warn`: a witness kind from a newer system package
|
|
2374
|
-
* must not fail the strict build of a host that predates its writer. */
|
|
2375
|
-
function severityFor(code) {
|
|
2376
|
-
return (code === void 0 ? void 0 : DIAGNOSTIC_SEVERITY.get(code)) ?? "warn";
|
|
2377
|
-
}
|
|
2378
|
-
/**
|
|
2379
|
-
* The one mapper of a sealed system's witness entries for every host: the
|
|
2380
|
-
* loader's evaluation host shims `console`, so the record is the channel.
|
|
2381
|
-
*/
|
|
2382
|
-
function vocabularyWitnessDiagnostics(vocabularyWitnessesJson) {
|
|
2383
|
-
if (!vocabularyWitnessesJson) return [];
|
|
2384
|
-
const entries = parseInternalWire(vocabularyWitnessesJson, "vocabularyWitnessesJson (the sealed system's vocabulary witness record)");
|
|
2385
|
-
const diagnostics = [];
|
|
2386
|
-
for (const entry of entries) if (entry.code === "animus.vocabulary.collision") diagnostics.push({
|
|
2387
|
-
file: "system",
|
|
2388
|
-
component: entry.name ?? "keyframes",
|
|
2389
|
-
kind: "warn",
|
|
2390
|
-
message: `keyframes vocabulary "${entry.name}" is registered by both ${entry.loser} and ${entry.winner} — ${entry.winner} wins; rename one collection (${entry.code})`,
|
|
2391
|
-
code: entry.code,
|
|
2392
|
-
severity: severityFor(entry.code)
|
|
2393
|
-
});
|
|
2394
|
-
else if (entry.code === "animus.vocabulary.legacy-verb") diagnostics.push({
|
|
2395
|
-
file: "system",
|
|
2396
|
-
component: "keyframes",
|
|
2397
|
-
kind: "warn",
|
|
2398
|
-
message: `a sealed system (${entry.source ?? `'${entry.verb}' source`}) with registered vocabulary [${(entry.names ?? []).join(", ")}] was consumed through the deprecated '${entry.verb}' verb, which cannot carry it — those collections do NOT reach this consumer; use createSystem().extend(source) (${entry.code})`,
|
|
2399
|
-
code: entry.code,
|
|
2400
|
-
severity: severityFor(entry.code)
|
|
2401
|
-
});
|
|
2402
|
-
else diagnostics.push({
|
|
2403
|
-
file: "system",
|
|
2404
|
-
component: "vocabulary",
|
|
2405
|
-
kind: "warn",
|
|
2406
|
-
message: `unrecognized vocabulary witness entry ${JSON.stringify(entry)} — a newer @animus-ui/system may have recorded a witness kind this host predates${entry.code ? ` (${entry.code})` : ""}`,
|
|
2407
|
-
code: entry.code,
|
|
2408
|
-
severity: severityFor(entry.code)
|
|
2409
|
-
});
|
|
2410
|
-
return diagnostics;
|
|
2411
|
-
}
|
|
2412
|
-
/** The diagnostics a loaded system's own records carry: vocabulary
|
|
2413
|
-
* witnesses and invalid `@property` registrations. */
|
|
2414
|
-
function systemLoadDiagnostics(system) {
|
|
2415
|
-
const registrations = (system.invalidPropertyRegistrations ?? []).map(({ name, reason }) => ({
|
|
2416
|
-
file: "system",
|
|
2417
|
-
component: name,
|
|
2418
|
-
kind: "warn",
|
|
2419
|
-
message: `@property ${name} was not emitted: ${reason}. Browsers ignore an invalid registration and minifying it fails; fix it where the theme declares ${name} (${INVALID_PROPERTY_REGISTRATION})`,
|
|
2420
|
-
code: INVALID_PROPERTY_REGISTRATION,
|
|
2421
|
-
severity: severityFor(INVALID_PROPERTY_REGISTRATION)
|
|
2422
|
-
}));
|
|
2423
|
-
return [...vocabularyWitnessDiagnostics(system.vocabularyWitnessesJson), ...registrations];
|
|
2635
|
+
/** A valid escape at `at`: up to six hex digits and one following
|
|
2636
|
+
* whitespace, or any other code point but a newline. */
|
|
2637
|
+
function escapeAt(css, at) {
|
|
2638
|
+
if (css[at] !== "\\") return null;
|
|
2639
|
+
const next = css.codePointAt(at + 1);
|
|
2640
|
+
if (next === void 0 || next === 10 || next === 13 || next === 12) return null;
|
|
2641
|
+
const digits = /^[0-9a-fA-F]{1,6}/.exec(css.slice(at + 1, at + 7))?.[0];
|
|
2642
|
+
if (digits === void 0) return {
|
|
2643
|
+
codePoint: String.fromCodePoint(next),
|
|
2644
|
+
length: 1 + String.fromCodePoint(next).length
|
|
2645
|
+
};
|
|
2646
|
+
const after = css.slice(at + 1 + digits.length);
|
|
2647
|
+
const space = after.startsWith("\r\n") ? 2 : /^[ \t\n\r\f]/.test(after) ? 1 : 0;
|
|
2648
|
+
const value = parseInt(digits, 16);
|
|
2649
|
+
const valid = value !== 0 && value <= 1114111 && (value < 55296 || value > 57343);
|
|
2650
|
+
return {
|
|
2651
|
+
codePoint: String.fromCodePoint(valid ? value : 65533),
|
|
2652
|
+
length: 1 + digits.length + space
|
|
2653
|
+
};
|
|
2424
2654
|
}
|
|
2425
2655
|
/**
|
|
2426
|
-
*
|
|
2427
|
-
*
|
|
2656
|
+
* Gives every custom-property token that names a managed property its final
|
|
2657
|
+
* name, in one pass: declaration keys, `var()` reads at any depth (fallbacks
|
|
2658
|
+
* included), `@property` names, transition-property lists and style queries.
|
|
2659
|
+
* A name is read whole, escapes decoded, so an undeclared name that starts
|
|
2660
|
+
* with a managed one is never renamed in part. Quoted strings, `url()` and
|
|
2661
|
+
* comments are copied unchanged.
|
|
2428
2662
|
*/
|
|
2429
|
-
function
|
|
2430
|
-
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2434
|
-
if (
|
|
2435
|
-
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
if (
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
if (
|
|
2444
|
-
|
|
2445
|
-
|
|
2663
|
+
function renameCustomProperties(css, names) {
|
|
2664
|
+
let out = "";
|
|
2665
|
+
let i = 0;
|
|
2666
|
+
while (i < css.length) {
|
|
2667
|
+
const c = css[i];
|
|
2668
|
+
if (c === "\"" || c === "'") {
|
|
2669
|
+
const end = closingQuote$1(css, i);
|
|
2670
|
+
out += css.slice(i, end);
|
|
2671
|
+
i = end;
|
|
2672
|
+
} else if (css.startsWith("/*", i)) {
|
|
2673
|
+
const close = css.indexOf("*/", i + 2);
|
|
2674
|
+
const end = close === -1 ? css.length : close + 2;
|
|
2675
|
+
out += css.slice(i, end);
|
|
2676
|
+
i = end;
|
|
2677
|
+
} else if (/^url\(/i.test(css.slice(i, i + 4)) && !isNameChar(css[i - 1])) {
|
|
2678
|
+
const end = closingUrl(css, i + 4);
|
|
2679
|
+
out += css.slice(i, end);
|
|
2680
|
+
i = end;
|
|
2681
|
+
} else if (css.startsWith("--", i) && !isNameChar(css[i - 1])) {
|
|
2682
|
+
const { end, name } = readCustomPropertyName(css, i + 2);
|
|
2683
|
+
const identity = names.identity(name);
|
|
2684
|
+
const final = identity === void 0 ? void 0 : names.finalName(identity);
|
|
2685
|
+
out += final === void 0 ? css.slice(i, end) : `--${final}`;
|
|
2686
|
+
i = end;
|
|
2687
|
+
} else {
|
|
2688
|
+
out += c;
|
|
2689
|
+
i += 1;
|
|
2446
2690
|
}
|
|
2447
|
-
warn(line);
|
|
2448
2691
|
}
|
|
2449
|
-
|
|
2692
|
+
return out;
|
|
2693
|
+
}
|
|
2694
|
+
/** A name code point, or a UTF-16 unit of a non-ASCII one. */
|
|
2695
|
+
function isNameChar(c) {
|
|
2696
|
+
return c !== void 0 && (NAME_CHAR.test(c) || c.charCodeAt(0) >= 128);
|
|
2697
|
+
}
|
|
2698
|
+
function closingQuote$1(css, open) {
|
|
2699
|
+
const quote = css[open];
|
|
2700
|
+
let i = open + 1;
|
|
2701
|
+
while (i < css.length && css[i] !== quote) i += css[i] === "\\" ? 2 : 1;
|
|
2702
|
+
return Math.min(i + 1, css.length);
|
|
2703
|
+
}
|
|
2704
|
+
/** The index after the `)` that closes an unquoted or quoted `url(`. */
|
|
2705
|
+
function closingUrl(css, from) {
|
|
2706
|
+
let i = from;
|
|
2707
|
+
while (i < css.length && css[i] !== ")") i = css[i] === "\"" || css[i] === "'" ? closingQuote$1(css, i) : i + 1;
|
|
2708
|
+
return Math.min(i + 1, css.length);
|
|
2450
2709
|
}
|
|
2451
2710
|
//#endregion
|
|
2452
2711
|
//#region pipeline/property-registrations.ts
|
|
@@ -2470,34 +2729,25 @@ const SYNTAX_TYPES = /* @__PURE__ */ new Set([
|
|
|
2470
2729
|
"url"
|
|
2471
2730
|
]);
|
|
2472
2731
|
const SYNTAX_COMPONENT = /^(?:<([a-z-]+)>[+#]?|-?[a-zA-Z_][\w-]*)$/;
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
const DIMENSION_TYPES = /* @__PURE__ */ new Set([
|
|
2476
|
-
"image",
|
|
2477
|
-
"length",
|
|
2478
|
-
"length-percentage",
|
|
2479
|
-
"transform-function",
|
|
2480
|
-
"transform-list"
|
|
2481
|
-
]);
|
|
2482
|
-
const RELATIVE_UNIT = /(?:\d|\.)(?:r?em|r?ex|r?cap|r?ch|r?ic|r?lh|cq(?:w|h|i|b|min|max))\b/i;
|
|
2483
|
-
const SUBSTITUTION = /\b(?:var|env|attr)\(/i;
|
|
2484
|
-
/** Quoted strings and `url()` hold text, never units or substitutions. */
|
|
2485
|
-
const LITERAL_TEXT = /"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|url\([^)]*\)/gi;
|
|
2732
|
+
const INITIAL_VALUE = /(initial-value\s*:\s*)([^;}]*)/;
|
|
2733
|
+
const MATH_FUNCTION = /\b(?:calc|min|max|clamp)\(/i;
|
|
2486
2734
|
/**
|
|
2487
2735
|
* Removes the `@property` rules browsers would ignore — an unknown syntax
|
|
2488
2736
|
* component, a syntax other than `*` without an initial value, an initial
|
|
2489
|
-
* value that depends on context, or one the
|
|
2490
|
-
* Minifying a stylesheet that contains such a rule
|
|
2737
|
+
* value that substitutes another value or depends on context, or one the
|
|
2738
|
+
* syntax does not accept. Minifying a stylesheet that contains such a rule
|
|
2739
|
+
* can also fail. An all-absolute math initial value is written as its
|
|
2740
|
+
* computed value, which the minifier reads.
|
|
2491
2741
|
*/
|
|
2492
2742
|
function splitInvalidPropertyRegistrations(css) {
|
|
2493
2743
|
const invalid = [];
|
|
2494
2744
|
return {
|
|
2495
2745
|
css: css.replace(PROPERTY_RULE, (rule, name) => {
|
|
2496
|
-
const
|
|
2497
|
-
if (
|
|
2746
|
+
const checked = checkRegistration(rule);
|
|
2747
|
+
if ("rule" in checked) return checked.rule;
|
|
2498
2748
|
invalid.push({
|
|
2499
2749
|
name,
|
|
2500
|
-
reason
|
|
2750
|
+
reason: checked.reason
|
|
2501
2751
|
});
|
|
2502
2752
|
return "";
|
|
2503
2753
|
}),
|
|
@@ -2516,42 +2766,48 @@ function readRegistration(rule) {
|
|
|
2516
2766
|
initialValue: /initial-value\s*:\s*([^;}]*)/.exec(rule)?.[1]?.trim()
|
|
2517
2767
|
};
|
|
2518
2768
|
}
|
|
2519
|
-
|
|
2769
|
+
/** The rule to keep, with an all-absolute math initial value folded, or why
|
|
2770
|
+
* a browser would ignore it. */
|
|
2771
|
+
function checkRegistration(rule) {
|
|
2520
2772
|
const { syntax, initialValue } = readRegistration(rule);
|
|
2773
|
+
if (initialValue !== void 0 && substitutesValue(initialValue)) return { reason: `initialValue "${initialValue}" substitutes another value; var(), env() and attr() are not allowed in an initial value` };
|
|
2774
|
+
let kept = rule;
|
|
2521
2775
|
if (syntax !== "*") {
|
|
2522
|
-
const
|
|
2776
|
+
const unknown = syntax.split("|").map((component) => ({
|
|
2523
2777
|
text: component.trim(),
|
|
2524
2778
|
match: SYNTAX_COMPONENT.exec(component.trim())
|
|
2525
|
-
}));
|
|
2526
|
-
|
|
2527
|
-
if (
|
|
2528
|
-
if (initialValue
|
|
2529
|
-
const
|
|
2530
|
-
|
|
2531
|
-
if (
|
|
2779
|
+
})).find(({ match }) => match === null || match[1] !== void 0 && !SYNTAX_TYPES.has(match[1]));
|
|
2780
|
+
if (unknown !== void 0) return { reason: `syntax "${syntax}" has the unknown component "${unknown.text}"` };
|
|
2781
|
+
if (initialValue === void 0) return { reason: `syntax "${syntax}" needs an initialValue` };
|
|
2782
|
+
if (dependsOnContext(initialValue)) return { reason: `initialValue "${initialValue}" depends on context; font- and container-relative units are not allowed` };
|
|
2783
|
+
const folded = foldInitialValue(syntax, initialValue);
|
|
2784
|
+
if (folded !== void 0 && !folded.accepted) return { reason: `initialValue "${initialValue}" computes to the ${folded.type} ${folded.text}, which syntax "${syntax}" does not accept` };
|
|
2785
|
+
if (folded !== void 0) kept = rule.replace(INITIAL_VALUE, `$1${folded.text}`);
|
|
2532
2786
|
}
|
|
2533
2787
|
try {
|
|
2534
2788
|
transform({
|
|
2535
2789
|
filename: "animus-registrations.css",
|
|
2536
|
-
code: Buffer.from(
|
|
2790
|
+
code: Buffer.from(kept)
|
|
2537
2791
|
});
|
|
2538
|
-
return
|
|
2792
|
+
return { rule: kept };
|
|
2539
2793
|
} catch (error) {
|
|
2540
2794
|
const detail = error instanceof Error ? error.message : String(error);
|
|
2541
|
-
return `the CSS parser
|
|
2795
|
+
if (initialValue !== void 0 && MATH_FUNCTION.test(initialValue)) return { reason: `the CSS parser that builds the stylesheet cannot read the math in initialValue "${initialValue}" (${detail}); write its computed value instead` };
|
|
2796
|
+
return { reason: `the CSS parser rejects \`${kept.trim()}\` (${detail})` };
|
|
2542
2797
|
}
|
|
2543
2798
|
}
|
|
2544
2799
|
//#endregion
|
|
2545
2800
|
//#region pipeline/property-diagnostics.ts
|
|
2546
2801
|
/**
|
|
2547
2802
|
* Reports custom properties that will not behave as their CSS suggests: a
|
|
2548
|
-
* property that refers to itself
|
|
2549
|
-
*
|
|
2550
|
-
*
|
|
2803
|
+
* property that refers to itself, a fallback a registered initial value
|
|
2804
|
+
* applies ahead of, and an animated property that cannot interpolate. Names
|
|
2805
|
+
* are read as CSS tokenizes them, so escapes, function case, comments and
|
|
2806
|
+
* quoted text match the prefix pass. The CSS itself is never changed.
|
|
2551
2807
|
*/
|
|
2552
2808
|
function checkCustomProperties(input) {
|
|
2553
2809
|
const registered = propertyRegistrations(input.system.variableCss);
|
|
2554
|
-
const declared =
|
|
2810
|
+
const declared = new Set(input.system.contextualProperties ?? []);
|
|
2555
2811
|
const findings = [];
|
|
2556
2812
|
const sheets = [{
|
|
2557
2813
|
css: input.componentCss,
|
|
@@ -2561,6 +2817,7 @@ function checkCustomProperties(input) {
|
|
|
2561
2817
|
owner: globalOwner
|
|
2562
2818
|
}];
|
|
2563
2819
|
for (const { css, owner } of sheets) {
|
|
2820
|
+
if (!/var\(|transition|@keyframes/i.test(css)) continue;
|
|
2564
2821
|
const parsed = declarations(css);
|
|
2565
2822
|
const discreteRules = new Set(parsed.filter((d) => d.property === "transition-behavior" && /\ballow-discrete\b/.test(d.value)).map((d) => d.rule));
|
|
2566
2823
|
for (const declaration of parsed) {
|
|
@@ -2574,29 +2831,23 @@ function checkCustomProperties(input) {
|
|
|
2574
2831
|
found(PROPERTY_SELF_REFERENCE, declaration.property, `${declaration.property} resolves to var(${declaration.property}), a reference to itself, so it is invalid at computed-value time. Give it a value other than its own name`);
|
|
2575
2832
|
continue;
|
|
2576
2833
|
}
|
|
2577
|
-
if (
|
|
2834
|
+
if (readsItselfOnlyInFallback(declaration)) found(PROPERTY_FALLBACK_SELF_REFERENCE, declaration.property, `${declaration.property}: ${declaration.value} reads ${declaration.property} only inside another var()'s fallback, so the result depends on how the browser treats fallback references: one that evaluates a fallback only when it is used keeps the value, one that counts every reference makes ${declaration.property} cyclic and invalid at computed-value time`);
|
|
2578
2835
|
for (const read of varReads(declaration.value)) {
|
|
2579
2836
|
const registration = registered.get(read.name);
|
|
2580
2837
|
if (read.fallback === void 0 || registration?.initialValue === void 0) continue;
|
|
2581
2838
|
const chain = read.fallback.startsWith("var(");
|
|
2582
|
-
found(chain ? PROPERTY_FALLBACK_CHAIN_SUPPRESSED : PROPERTY_FALLBACK_SUPPRESSED, read.name,
|
|
2839
|
+
found(chain ? PROPERTY_FALLBACK_CHAIN_SUPPRESSED : PROPERTY_FALLBACK_SUPPRESSED, read.name, `var(${read.name}, ${read.fallback}): where its registration applies, ${read.name}'s initial-value ${registration.initialValue} is used ahead of the ${chain ? "fallback chain" : "fallback"} whenever ${read.name} is unset; the fallback still serves browsers without the registration`);
|
|
2583
2840
|
}
|
|
2584
2841
|
for (const animated of animatedProperties(declaration, discreteRules)) {
|
|
2585
|
-
if (!declared.has(animated.name)) continue;
|
|
2842
|
+
if (animated.discrete || !declared.has(animated.name)) continue;
|
|
2586
2843
|
const reason = interpolationGap(registered.get(animated.name));
|
|
2587
2844
|
if (reason === null) continue;
|
|
2588
|
-
|
|
2589
|
-
else found(PROPERTY_UNREGISTERED_ANIMATION, animated.name, `${animated.name} is ${animated.how} but ${reason}, so it changes in one step instead of interpolating. Register it with a typed syntax to animate it`);
|
|
2845
|
+
found(PROPERTY_UNREGISTERED_ANIMATION, animated.name, `${animated.name} is ${animated.how} but ${reason}, so it changes in one step instead of interpolating. Register it with a typed syntax to animate it`);
|
|
2590
2846
|
}
|
|
2591
2847
|
}
|
|
2592
2848
|
}
|
|
2593
2849
|
return toDiagnostics(findings);
|
|
2594
2850
|
}
|
|
2595
|
-
function declaredProperties(contextualVarsJson) {
|
|
2596
|
-
if (!contextualVarsJson) return /* @__PURE__ */ new Set();
|
|
2597
|
-
const byScale = parseInternalWire(contextualVarsJson, "contextualVarsJson (the theme's contextual variable names)");
|
|
2598
|
-
return new Set(Object.values(byScale).flatMap((names) => names.map((name) => `--${name}`)));
|
|
2599
|
-
}
|
|
2600
2851
|
/** Declarations in engine-emitted CSS: one `property: value` per `;`. */
|
|
2601
2852
|
function declarations(css) {
|
|
2602
2853
|
const found = [];
|
|
@@ -2647,51 +2898,134 @@ function declarations(css) {
|
|
|
2647
2898
|
}
|
|
2648
2899
|
return found;
|
|
2649
2900
|
}
|
|
2650
|
-
|
|
2651
|
-
|
|
2901
|
+
/** `text` as a whole custom-property name, escapes decoded; `undefined`
|
|
2902
|
+
* when it is not one. */
|
|
2903
|
+
function customPropertyName(text) {
|
|
2904
|
+
if (!text.startsWith("--")) return void 0;
|
|
2905
|
+
const { end, name } = readCustomPropertyName(text, 2);
|
|
2906
|
+
return end === text.length ? `--${name}` : void 0;
|
|
2652
2907
|
}
|
|
2653
|
-
|
|
2908
|
+
function isSelfReference(declaration) {
|
|
2909
|
+
const property = customPropertyName(declaration.property);
|
|
2910
|
+
const reads = varReads(declaration.value);
|
|
2911
|
+
return property !== void 0 && reads.length === 1 && reads[0].name === property && reads[0].fallback === void 0 && withoutComments(declaration.value).trim() === withoutComments(declaration.value.slice(reads[0].start, reads[0].end)).trim();
|
|
2912
|
+
}
|
|
2913
|
+
/** Whether every read of the declared property sits inside another read's
|
|
2914
|
+
* fallback, as in `--a: var(--b, var(--a))`. */
|
|
2915
|
+
function readsItselfOnlyInFallback(declaration) {
|
|
2916
|
+
const property = customPropertyName(declaration.property);
|
|
2917
|
+
if (property === void 0) return false;
|
|
2918
|
+
const reads = varReads(declaration.value);
|
|
2919
|
+
const own = reads.filter((read) => read.name === property);
|
|
2920
|
+
return own.length > 0 && own.every((read) => reads.some((outer) => outer !== read && outer.start < read.start && read.end <= outer.end));
|
|
2921
|
+
}
|
|
2922
|
+
/** Every `var()` read in `value`, nested fallbacks included, in any case
|
|
2923
|
+
* and spacing. Quoted text and comments hold none. */
|
|
2654
2924
|
function varReads(value) {
|
|
2655
2925
|
const reads = [];
|
|
2656
|
-
for (
|
|
2657
|
-
const
|
|
2658
|
-
if (
|
|
2659
|
-
|
|
2660
|
-
name: match[1],
|
|
2661
|
-
fallback: void 0
|
|
2662
|
-
});
|
|
2926
|
+
for (let at = 0; at < value.length;) {
|
|
2927
|
+
const c = value[at];
|
|
2928
|
+
if (c === "\"" || c === "'") {
|
|
2929
|
+
at = closingQuote(value, at);
|
|
2663
2930
|
continue;
|
|
2664
2931
|
}
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2932
|
+
if (value.startsWith("/*", at)) {
|
|
2933
|
+
at = skipSpace(value, at);
|
|
2934
|
+
continue;
|
|
2935
|
+
}
|
|
2936
|
+
if (value.slice(at, at + 4).toLowerCase() !== "var(" || /[\w-]/.test(value[at - 1] ?? "")) {
|
|
2937
|
+
at += 1;
|
|
2938
|
+
continue;
|
|
2939
|
+
}
|
|
2940
|
+
const nameAt = skipSpace(value, at + 4);
|
|
2941
|
+
if (!value.startsWith("--", nameAt)) {
|
|
2942
|
+
at += 4;
|
|
2943
|
+
continue;
|
|
2944
|
+
}
|
|
2945
|
+
const { end: nameEnd, name } = readCustomPropertyName(value, nameAt + 2);
|
|
2946
|
+
const after = skipSpace(value, nameEnd);
|
|
2947
|
+
const close = closingParen(value, at + 4);
|
|
2669
2948
|
reads.push({
|
|
2670
|
-
name:
|
|
2671
|
-
fallback: value.slice(after + 1, close - 1).trim()
|
|
2949
|
+
name: `--${name}`,
|
|
2950
|
+
fallback: value[after] === "," ? value.slice(after + 1, close - 1).trim() : void 0,
|
|
2951
|
+
start: at,
|
|
2952
|
+
end: close
|
|
2672
2953
|
});
|
|
2954
|
+
at = after;
|
|
2673
2955
|
}
|
|
2674
2956
|
return reads;
|
|
2675
2957
|
}
|
|
2958
|
+
/** The index after the `)` that closes the call whose arguments start at
|
|
2959
|
+
* `from`. Quoted text and comments are skipped. */
|
|
2960
|
+
function closingParen(value, from) {
|
|
2961
|
+
let depth = 1;
|
|
2962
|
+
let at = from;
|
|
2963
|
+
while (at < value.length && depth > 0) {
|
|
2964
|
+
const c = value[at];
|
|
2965
|
+
if (c === "\"" || c === "'") at = closingQuote(value, at);
|
|
2966
|
+
else if (value.startsWith("/*", at)) at = skipSpace(value, at);
|
|
2967
|
+
else {
|
|
2968
|
+
if (c === "(") depth += 1;
|
|
2969
|
+
else if (c === ")") depth -= 1;
|
|
2970
|
+
at += 1;
|
|
2971
|
+
}
|
|
2972
|
+
}
|
|
2973
|
+
return at;
|
|
2974
|
+
}
|
|
2975
|
+
function closingQuote(value, open) {
|
|
2976
|
+
let at = open + 1;
|
|
2977
|
+
while (at < value.length && value[at] !== value[open]) at += value[at] === "\\" ? 2 : 1;
|
|
2978
|
+
return Math.min(at + 1, value.length);
|
|
2979
|
+
}
|
|
2980
|
+
/** The index after CSS whitespace and comments starting at `from`. */
|
|
2981
|
+
function skipSpace(value, from) {
|
|
2982
|
+
let at = from;
|
|
2983
|
+
for (;;) {
|
|
2984
|
+
while (/[ \t\n\r\f]/.test(value[at] ?? "")) at += 1;
|
|
2985
|
+
if (!value.startsWith("/*", at)) return at;
|
|
2986
|
+
const close = value.indexOf("*/", at + 2);
|
|
2987
|
+
at = close === -1 ? value.length : close + 2;
|
|
2988
|
+
}
|
|
2989
|
+
}
|
|
2990
|
+
function withoutComments(text) {
|
|
2991
|
+
return text.replace(/\/\*[\s\S]*?\*\//g, "");
|
|
2992
|
+
}
|
|
2676
2993
|
/** Custom properties this declaration sets in a keyframe or names in a
|
|
2677
2994
|
* transition. */
|
|
2678
2995
|
function animatedProperties(declaration, discreteRules) {
|
|
2679
2996
|
const keyframes = declaration.context.find((prelude) => prelude.startsWith("@keyframes"));
|
|
2680
|
-
|
|
2681
|
-
|
|
2997
|
+
const set = customPropertyName(declaration.property);
|
|
2998
|
+
if (keyframes !== void 0 && set !== void 0) return [{
|
|
2999
|
+
name: set,
|
|
2682
3000
|
how: `set in ${keyframes}`,
|
|
2683
3001
|
discrete: false
|
|
2684
3002
|
}];
|
|
2685
3003
|
if (declaration.property !== "transition" && declaration.property !== "transition-property") return [];
|
|
2686
3004
|
return topLevelItems(declaration.value).flatMap((item) => {
|
|
2687
|
-
const property = item.
|
|
2688
|
-
return property
|
|
3005
|
+
const property = topLevelTokens(item.replace(/\/\*[\s\S]*?\*\//g, " ")).map(customPropertyName).find((name) => name !== void 0);
|
|
3006
|
+
return property !== void 0 ? [{
|
|
2689
3007
|
name: property,
|
|
2690
3008
|
how: "transitioned",
|
|
2691
3009
|
discrete: /\ballow-discrete\b/.test(item) || discreteRules.has(declaration.rule)
|
|
2692
3010
|
}] : [];
|
|
2693
3011
|
});
|
|
2694
3012
|
}
|
|
3013
|
+
/** The whitespace-separated tokens of `item` outside any parentheses. */
|
|
3014
|
+
function topLevelTokens(item) {
|
|
3015
|
+
const tokens = [];
|
|
3016
|
+
let depth = 0;
|
|
3017
|
+
let token = "";
|
|
3018
|
+
for (const c of item) {
|
|
3019
|
+
if (c === "(") depth += 1;
|
|
3020
|
+
else if (c === ")") depth -= 1;
|
|
3021
|
+
if (depth === 0 && /\s/.test(c)) {
|
|
3022
|
+
if (token) tokens.push(token);
|
|
3023
|
+
token = "";
|
|
3024
|
+
} else token += c;
|
|
3025
|
+
}
|
|
3026
|
+
if (token) tokens.push(token);
|
|
3027
|
+
return tokens;
|
|
3028
|
+
}
|
|
2695
3029
|
function topLevelItems(value) {
|
|
2696
3030
|
const items = [];
|
|
2697
3031
|
let depth = 0;
|
|
@@ -2712,7 +3046,7 @@ function interpolationGap(registration) {
|
|
|
2712
3046
|
return null;
|
|
2713
3047
|
}
|
|
2714
3048
|
function componentOwner(manifest) {
|
|
2715
|
-
const components = Object.values(manifest.components);
|
|
3049
|
+
const components = Object.values(manifest.components ?? {});
|
|
2716
3050
|
return (declaration) => {
|
|
2717
3051
|
const selector = [...declaration.context].reverse().find((prelude) => !prelude.startsWith("@"));
|
|
2718
3052
|
const classes = (selector ?? "").match(/\.-?[_a-zA-Z][\w-]*/g) ?? [];
|
|
@@ -2834,12 +3168,33 @@ function buildAnalysisInputs(opts) {
|
|
|
2834
3168
|
externalDirsJson: opts.externalDirs?.length && hasSourceThemeManifests(opts.system) ? JSON.stringify(opts.externalDirs) : null
|
|
2835
3169
|
};
|
|
2836
3170
|
if (opts.system.declarationScalesJson) inputs.declarationScalesJson = opts.system.declarationScalesJson;
|
|
3171
|
+
const context = opts.analysisContext;
|
|
3172
|
+
if (context?.skippedSources?.length || context?.unbundledComputedImports || context?.packageDirs?.length) inputs.analysisContextJson = JSON.stringify({
|
|
3173
|
+
skippedSources: context.skippedSources ?? [],
|
|
3174
|
+
unbundledComputedImports: context.unbundledComputedImports ?? false,
|
|
3175
|
+
packageDirs: context.packageDirs ?? []
|
|
3176
|
+
});
|
|
2837
3177
|
return inputs;
|
|
2838
3178
|
}
|
|
2839
3179
|
function hasSourceThemeManifests(system) {
|
|
2840
3180
|
const json = system.sourceThemeManifestsJson ?? "";
|
|
2841
3181
|
return json.length > 0 && json !== "{}";
|
|
2842
3182
|
}
|
|
3183
|
+
/** Systems whose load diagnostics were surfaced: each loaded system reports
|
|
3184
|
+
* them once, not again on every analysis and hot update. The set is per
|
|
3185
|
+
* process. Next's webpack compilers share one analysis in one process, but
|
|
3186
|
+
* its build worker and Turbopack builds analyze in two processes, which
|
|
3187
|
+
* repeat every diagnostic alike, component ones included. */
|
|
3188
|
+
const surfacedSystems = /* @__PURE__ */ new WeakSet();
|
|
3189
|
+
/** The loaded system's own diagnostics, the first time it is analyzed. A
|
|
3190
|
+
* strict error keeps failing every analysis until the system changes. */
|
|
3191
|
+
function systemDiagnostics(system, strict) {
|
|
3192
|
+
const diagnostics = [...collectSelectorAliasDiagnostics(system.selectorAliasesJson), ...systemLoadDiagnostics(system)];
|
|
3193
|
+
const blocking = strict === true && diagnostics.some((d) => d.severity === "error");
|
|
3194
|
+
if (surfacedSystems.has(system) && !blocking) return [];
|
|
3195
|
+
surfacedSystems.add(system);
|
|
3196
|
+
return diagnostics;
|
|
3197
|
+
}
|
|
2843
3198
|
/**
|
|
2844
3199
|
* The one analysis invocation both plugins share. Error handling stays at
|
|
2845
3200
|
* the call site (strict-mode throw vs warn).
|
|
@@ -2854,18 +3209,20 @@ function runProjectAnalysis(engineApi, opts) {
|
|
|
2854
3209
|
const extractMs = Math.round(performance.now() - t);
|
|
2855
3210
|
t = performance.now();
|
|
2856
3211
|
const manifest = JSON.parse(manifestJson);
|
|
3212
|
+
const systemErrors = systemLoadDiagnostics(opts.system).filter((diagnostic) => diagnostic.kind === "error");
|
|
3213
|
+
if (systemErrors.length > 0) manifest.diagnostics = [...systemErrors, ...manifest.diagnostics ?? []];
|
|
3214
|
+
const componentCss = applyUnitFallback(manifest.css);
|
|
2857
3215
|
const propertyDiagnostics = checkCustomProperties({
|
|
2858
3216
|
system: opts.system,
|
|
2859
3217
|
manifest,
|
|
2860
|
-
componentCss
|
|
3218
|
+
componentCss,
|
|
2861
3219
|
globalCss: manifest.sheets.global
|
|
2862
3220
|
});
|
|
2863
3221
|
surfaceManifestDiagnostics(manifest, opts.warn, {
|
|
2864
3222
|
strict: opts.strict,
|
|
2865
3223
|
info: opts.info,
|
|
2866
3224
|
prepend: [
|
|
2867
|
-
...
|
|
2868
|
-
...systemLoadDiagnostics(opts.system),
|
|
3225
|
+
...systemDiagnostics(opts.system, opts.strict),
|
|
2869
3226
|
...propertyDiagnostics,
|
|
2870
3227
|
...opts.extraDiagnostics ?? []
|
|
2871
3228
|
]
|
|
@@ -2875,7 +3232,7 @@ function runProjectAnalysis(engineApi, opts) {
|
|
|
2875
3232
|
manifest,
|
|
2876
3233
|
manifestJson,
|
|
2877
3234
|
globalCss: manifest.sheets.global,
|
|
2878
|
-
componentCss
|
|
3235
|
+
componentCss,
|
|
2879
3236
|
inputs,
|
|
2880
3237
|
timings: {
|
|
2881
3238
|
serializeMs,
|
|
@@ -2960,6 +3317,61 @@ function applyPrefix(prefix, variableMapJson, variableCss, themeJson, contextual
|
|
|
2960
3317
|
}
|
|
2961
3318
|
return result;
|
|
2962
3319
|
}
|
|
3320
|
+
/** The runtime's transport variables are `--animus-<prop>`. */
|
|
3321
|
+
const TRANSPORT_NAMESPACE = "animus-";
|
|
3322
|
+
/**
|
|
3323
|
+
* The prefix applied through one map: every variable the theme defines and
|
|
3324
|
+
* every declared contextual variable takes `--${prefix}-${name}`, each
|
|
3325
|
+
* reference renamed once. Each contextual entry carries its declared and
|
|
3326
|
+
* final name, so lookups run on the declared identity and the extractor
|
|
3327
|
+
* emits the final name.
|
|
3328
|
+
*/
|
|
3329
|
+
function applyPropertyNames(prefix, artifacts) {
|
|
3330
|
+
const byScale = artifacts.contextualVarsJson ? parseInternalWire(artifacts.contextualVarsJson, "contextualVarsJson (the theme's contextual variable names)") : null;
|
|
3331
|
+
const declared = Object.values(byScale ?? {}).flat();
|
|
3332
|
+
const variableMap = parseInternalWire(artifacts.variableMapJson, "variableMapJson (the theme's token variables)");
|
|
3333
|
+
const managed = createPropertyNames(declared, prefix, [...Object.values(variableMap).map((name) => name.replace(/^--/, "")), ...[...artifacts.variableCss.matchAll(THEME_DEFINITION)].map((m) => m[1])]);
|
|
3334
|
+
const nameConflicts = [...[...new Set(declared)].flatMap((name) => {
|
|
3335
|
+
const final = managed.finalName(name) ?? name;
|
|
3336
|
+
return final.startsWith(TRANSPORT_NAMESPACE) ? [{
|
|
3337
|
+
name,
|
|
3338
|
+
final,
|
|
3339
|
+
reason: "transport"
|
|
3340
|
+
}] : [];
|
|
3341
|
+
}), ...managed.ambiguous().map(({ name, contextual }) => ({
|
|
3342
|
+
name,
|
|
3343
|
+
final: contextual,
|
|
3344
|
+
reason: "theme-variable"
|
|
3345
|
+
}))];
|
|
3346
|
+
const rename = (value) => renameCustomProperties(value, managed);
|
|
3347
|
+
return {
|
|
3348
|
+
variableMapJson: JSON.stringify(Object.fromEntries(Object.entries(variableMap).map(([key, name]) => [key, rename(name)]))),
|
|
3349
|
+
variableCss: rename(artifacts.variableCss),
|
|
3350
|
+
themeJson: renameTokenValues(artifacts.themeJson, rename),
|
|
3351
|
+
contextualVarsJson: byScale === null ? null : JSON.stringify(Object.fromEntries(Object.entries(byScale).map(([scale, names]) => [scale, names.map((name) => ({
|
|
3352
|
+
name,
|
|
3353
|
+
var: managed.finalName(name) ?? name
|
|
3354
|
+
}))]))),
|
|
3355
|
+
declarationScalesJson: artifacts.declarationScalesJson === null ? null : renameDeclarationRecords(artifacts.declarationScalesJson, rename),
|
|
3356
|
+
nameConflicts,
|
|
3357
|
+
contextualProperties: [...new Set(declared)].map((name) => `--${managed.finalName(name) ?? name}`)
|
|
3358
|
+
};
|
|
3359
|
+
}
|
|
3360
|
+
/** A custom property the theme's CSS defines or registers. */
|
|
3361
|
+
const THEME_DEFINITION = /(?:@property\s+|(?:^|[\s;{]))--([\w-]+)(?=\s*[:{])/g;
|
|
3362
|
+
/** Renames inside the token map's values. */
|
|
3363
|
+
function renameTokenValues(themeJson, rename) {
|
|
3364
|
+
const tokens = parseInternalWire(themeJson, "themeJson (the theme's flattened token values)");
|
|
3365
|
+
return JSON.stringify(Object.fromEntries(Object.entries(tokens).map(([path, value]) => [path, rename(value)])));
|
|
3366
|
+
}
|
|
3367
|
+
/** Renames inside every declaration-scale record value. */
|
|
3368
|
+
function renameDeclarationRecords(declarationScalesJson, rename) {
|
|
3369
|
+
const scales = parseInternalWire(declarationScalesJson, "declarationScalesJson (the theme's declaration scales)");
|
|
3370
|
+
return JSON.stringify(Object.fromEntries(Object.entries(scales).map(([name, scale]) => [name, {
|
|
3371
|
+
...scale,
|
|
3372
|
+
values: Object.fromEntries(Object.entries(scale.values).map(([key, record]) => [key, Object.fromEntries(Object.entries(record).map(([property, value]) => [property, rename(value)]))]))
|
|
3373
|
+
}])));
|
|
3374
|
+
}
|
|
2963
3375
|
//#endregion
|
|
2964
3376
|
//#region pipeline/system-config.ts
|
|
2965
3377
|
/**
|
|
@@ -2975,7 +3387,28 @@ function loadSystemConfig(engineApi, opts) {
|
|
|
2975
3387
|
let variableCss = registrations.css;
|
|
2976
3388
|
let contextualVarsJson = config.contextualVarsJson || null;
|
|
2977
3389
|
let declarationScalesJson = config.declarationScalesJson || null;
|
|
2978
|
-
|
|
3390
|
+
const declaredNames = Object.values(parseInternalWire(contextualVarsJson ?? "{}", "contextualVarsJson (the theme's contextual variable names)")).flat();
|
|
3391
|
+
let contextualProperties = declaredNames.map((name) => `--${name}`);
|
|
3392
|
+
let legacyPrefixedContextualVars = [];
|
|
3393
|
+
let prefixNameConflicts = [];
|
|
3394
|
+
if (opts.prefix && opts.prefixContextualVars) {
|
|
3395
|
+
const resolved = applyPropertyNames(opts.prefix, {
|
|
3396
|
+
variableMapJson,
|
|
3397
|
+
variableCss,
|
|
3398
|
+
themeJson: scalesJson,
|
|
3399
|
+
contextualVarsJson,
|
|
3400
|
+
declarationScalesJson
|
|
3401
|
+
});
|
|
3402
|
+
variableMapJson = resolved.variableMapJson;
|
|
3403
|
+
variableCss = resolved.variableCss;
|
|
3404
|
+
scalesJson = resolved.themeJson;
|
|
3405
|
+
contextualVarsJson = resolved.contextualVarsJson;
|
|
3406
|
+
contextualProperties = resolved.contextualProperties;
|
|
3407
|
+
prefixNameConflicts = resolved.nameConflicts;
|
|
3408
|
+
declarationScalesJson = resolved.declarationScalesJson;
|
|
3409
|
+
} else if (opts.prefix) {
|
|
3410
|
+
legacyPrefixedContextualVars = declaredNames;
|
|
3411
|
+
contextualProperties = declaredNames.map((name) => `--${opts.prefix}-${name}`);
|
|
2979
3412
|
const prefixed = applyPrefix(opts.prefix, variableMapJson, variableCss, scalesJson, contextualVarsJson || void 0);
|
|
2980
3413
|
variableMapJson = prefixed.variableMapJson;
|
|
2981
3414
|
variableCss = prefixed.variableCss;
|
|
@@ -2990,6 +3423,7 @@ function loadSystemConfig(engineApi, opts) {
|
|
|
2990
3423
|
variableMapJson,
|
|
2991
3424
|
variableCss,
|
|
2992
3425
|
contextualVarsJson,
|
|
3426
|
+
contextualProperties: [...new Set(contextualProperties)],
|
|
2993
3427
|
selectorAliasesJson: config.selectorAliases || null,
|
|
2994
3428
|
conditionAliasesJson: config.conditionAliases || null,
|
|
2995
3429
|
transformSourcesJson: config.transformSources || null,
|
|
@@ -3003,6 +3437,8 @@ function loadSystemConfig(engineApi, opts) {
|
|
|
3003
3437
|
if (declarationScalesJson) system.declarationScalesJson = declarationScalesJson;
|
|
3004
3438
|
if (config.propertyRecordsJson) system.propertyRecordsJson = config.propertyRecordsJson;
|
|
3005
3439
|
if (registrations.invalid.length > 0) system.invalidPropertyRegistrations = registrations.invalid;
|
|
3440
|
+
if (prefixNameConflicts.length > 0) system.prefixNameConflicts = prefixNameConflicts;
|
|
3441
|
+
if (legacyPrefixedContextualVars.length > 0) system.legacyPrefixedContextualVars = [...new Set(legacyPrefixedContextualVars)];
|
|
3006
3442
|
return system;
|
|
3007
3443
|
}
|
|
3008
3444
|
//#endregion
|
|
@@ -3056,6 +3492,7 @@ function buildDynamicPropConfig(dynamicProps) {
|
|
|
3056
3492
|
if (meta.negative) entry.negative = true;
|
|
3057
3493
|
if (meta.strict) entry.strict = true;
|
|
3058
3494
|
if (meta.keywords?.length) entry.keywords = meta.keywords;
|
|
3495
|
+
if (meta.currentVar) entry.currentVar = meta.currentVar;
|
|
3059
3496
|
configEntries[propName] = entry;
|
|
3060
3497
|
}
|
|
3061
3498
|
return configEntries;
|
|
@@ -3352,4 +3789,4 @@ function realpathThroughNearestExistingAncestor(path) {
|
|
|
3352
3789
|
}
|
|
3353
3790
|
}
|
|
3354
3791
|
//#endregion
|
|
3355
|
-
export {
|
|
3792
|
+
export { assertKnownOptionKeys as $, substituteSheetAssets as A, validateLayerOrder as At, unresolvableIncludesMessage as B, resolveAssetFile as C, unresolvedParentName as Ct, generatedModuleCode as D, ANIMUS_LAYERS as Dt, findSheetAssetSpecifiers as E, buildAnalyzeProjectArgs as Et, findPackageRoot as F, compareDiscoveryOrder as G, createSourceIdentity as H, firstOwners as I, CORE_OPTION_KEYS as J, discoverFiles as K, isExcludedPackageRelativePath as L, collectExternalPackageSources as M, excludeCollectedPackages as N, reportSurvivingAssetPlaceholders as O, assembleStylesheet as Ot, extractSystemFilePackages as P, STRUCTURAL_EXCLUDE as Q, resolveAbsolutePathSpecifier as R, enforceExternalTokenContracts as S, unreadableSourceDiagnostic as St, findAssetSpecifiers as T, contentHash as Tt, isPathWithinRoot as U, walkPackageSources as V, sharesVolumeRoot as W, DRIVER_NAMESPACES as X, DEFAULT_EXCLUDE as Y, REPLACEABLE_DEFAULT_EXCLUDE as Z, runProjectAnalysis as _, VOCABULARY_LEGACY_VERB as _t, assertNoErrorDiagnostics as a, createV2EngineApi as at, resolveLightningTargets as b, surfaceManifestDiagnostics as bt, buildDynamicPropConfig as c, parseFilesJson as ct, applyPrefix as d, DEFAULT_EXTENSIONS as dt, createExcludeMatcher as et, serializeStaticCss as f, ENGINE_TRANSFORM_EXTENSIONS as ft, clearEngineCache as g, VOCABULARY_COLLISION as gt, buildAnalysisInputs as h, INVALID_PROPERTY_REGISTRATION as ht, snapshotFilePlans as i, assertNoRetiredEngineSelection as it, createSourceCorpus as j, __require as jt, substituteAssetPlaceholders as k, stripLeadingLayerDeclaration as kt, runStructuralSelfCheck as l, projectExternalFileOwners as lt, stableStringify as m, preprocessMdx as mt, diffFilePlans as n, resolveVerbosity as nt, readTsconfigAliasPairs as o, ingestSourceEntries as ot, camelToKebab as p, isEngineTransformExtension as pt, AnimusConfigError as q, hashReplacementPlans as r, RETIRED_ENGINE_MESSAGE as rt, buildSystemPropsModule as s, isAdvisorySourceDiagnostic as st, toWatchKeys as t, resolveMode as tt, loadSystemConfig as u, adaptSvelteSource as ut, applyUnitFallback as v, isDeletedSource as vt, resolveThroughPathAliases as w, vocabularyWitnessDiagnostics as wt, buildPathAliasesJson as x, systemLoadDiagnostics as xt, postProcessCss as y, isUnresolvedParentDrop as yt, staleDistIncludesMessage as z };
|