@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
|
@@ -105,7 +105,8 @@ function buildAnalyzeProjectArgs(inputs) {
|
|
|
105
105
|
inputs.transformSourcesJson,
|
|
106
106
|
inputs.transformProvenanceJson ?? null
|
|
107
107
|
];
|
|
108
|
-
if (inputs.declarationScalesJson) args.push(inputs.declarationScalesJson);
|
|
108
|
+
if (inputs.declarationScalesJson || inputs.analysisContextJson) args.push(inputs.declarationScalesJson);
|
|
109
|
+
if (inputs.analysisContextJson) args.push(inputs.analysisContextJson);
|
|
109
110
|
return args;
|
|
110
111
|
}
|
|
111
112
|
//#endregion
|
|
@@ -125,6 +126,224 @@ function parseInternalWire(json, wire) {
|
|
|
125
126
|
return parsed;
|
|
126
127
|
}
|
|
127
128
|
//#endregion
|
|
129
|
+
//#region pipeline/manifest-diagnostics.ts
|
|
130
|
+
/** Stable code for selector forms with no substitutable subject; the
|
|
131
|
+
* extraction engine mints the same string. */
|
|
132
|
+
const SELECTOR_UNSUPPORTED_SUBJECT = "animus.selector.unsupported-subject";
|
|
133
|
+
/** The one matcher for the engine's unresolved-parent chain-drop message;
|
|
134
|
+
* consumers match through the exported helpers, never their own copy. */
|
|
135
|
+
const UNRESOLVED_PARENT_RE = /chain dropped: could not resolve parent component '([^']+)'/;
|
|
136
|
+
function isUnresolvedParentDrop(diagnostic) {
|
|
137
|
+
return UNRESOLVED_PARENT_RE.test(diagnostic.message);
|
|
138
|
+
}
|
|
139
|
+
function unresolvedParentName(diagnostic) {
|
|
140
|
+
return UNRESOLVED_PARENT_RE.exec(diagnostic.message)?.[1] ?? null;
|
|
141
|
+
}
|
|
142
|
+
/** True when the selector carries at least one substitutable `&` subject
|
|
143
|
+
* outside quoted text. */
|
|
144
|
+
function hasSelectorSubject(value) {
|
|
145
|
+
let quote = null;
|
|
146
|
+
let escaped = false;
|
|
147
|
+
for (const c of value) {
|
|
148
|
+
if (escaped) {
|
|
149
|
+
escaped = false;
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
if (c === "\\") {
|
|
153
|
+
escaped = true;
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
if (quote !== null) {
|
|
157
|
+
if (c === quote) quote = null;
|
|
158
|
+
} else if (c === "\"" || c === "'") quote = c;
|
|
159
|
+
else if (c === "&") return true;
|
|
160
|
+
}
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Coded diagnostics for selector-shaped alias values with no substitutable
|
|
165
|
+
* subject; alias values never reach the evaluator's own key guard.
|
|
166
|
+
*/
|
|
167
|
+
function collectSelectorAliasDiagnostics(selectorAliasesJson) {
|
|
168
|
+
if (!selectorAliasesJson) return [];
|
|
169
|
+
const aliases = parseInternalWire(selectorAliasesJson, "selectorAliasesJson (the system loader's selector-alias registry)");
|
|
170
|
+
const diagnostics = [];
|
|
171
|
+
for (const [name, value] of Object.entries(aliases)) if (value.includes("&") && !hasSelectorSubject(value)) diagnostics.push({
|
|
172
|
+
file: "system",
|
|
173
|
+
component: name,
|
|
174
|
+
kind: "warn",
|
|
175
|
+
message: `selector alias '${name}' value '${value}' has no substitutable '&' subject outside quoted text (${SELECTOR_UNSUPPORTED_SUBJECT})`,
|
|
176
|
+
code: SELECTOR_UNSUPPORTED_SUBJECT,
|
|
177
|
+
severity: severityFor(SELECTOR_UNSUPPORTED_SUBJECT)
|
|
178
|
+
});
|
|
179
|
+
return diagnostics;
|
|
180
|
+
}
|
|
181
|
+
const UNREADABLE_SOURCE_FILE = "animus.ingestion.unreadable-source-file";
|
|
182
|
+
/** What a skipped source file costs: the analysis never sees what it
|
|
183
|
+
* renders. */
|
|
184
|
+
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";
|
|
185
|
+
/**
|
|
186
|
+
* A configured source file that could not be read is a LOST INPUT: the
|
|
187
|
+
* emitted artifacts silently lack whatever that file declared.
|
|
188
|
+
*/
|
|
189
|
+
/** Whether a failed source read means the file was deleted after discovery
|
|
190
|
+
* (an editor's delete-and-recreate, a branch switch): a deletion, not lost
|
|
191
|
+
* input. */
|
|
192
|
+
function isDeletedSource(error) {
|
|
193
|
+
return error?.code === "ENOENT";
|
|
194
|
+
}
|
|
195
|
+
function unreadableSourceDiagnostic(relPath, error) {
|
|
196
|
+
return {
|
|
197
|
+
file: relPath,
|
|
198
|
+
component: "source",
|
|
199
|
+
kind: "warn",
|
|
200
|
+
message: `configured source file ${relPath} could not be read: ${String(error)} (${SKIPPED_SOURCE_COST})`,
|
|
201
|
+
code: UNREADABLE_SOURCE_FILE,
|
|
202
|
+
severity: severityFor(UNREADABLE_SOURCE_FILE)
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
/** The collision entry code minted by the system package's merge. */
|
|
206
|
+
const VOCABULARY_COLLISION = "animus.vocabulary.collision";
|
|
207
|
+
/** A theme `@property` registration browsers would ignore; it is not emitted. */
|
|
208
|
+
const INVALID_PROPERTY_REGISTRATION = "animus.theme.invalid-property-registration";
|
|
209
|
+
/** Minted when a sealed kit with registered vocabulary is consumed through
|
|
210
|
+
* the deprecated `from()`/`includes:` verbs. */
|
|
211
|
+
const VOCABULARY_LEGACY_VERB = "animus.vocabulary.legacy-verb";
|
|
212
|
+
/** A `var()` fallback that never applies: the property is registered with an
|
|
213
|
+
* initial value. */
|
|
214
|
+
const PROPERTY_FALLBACK_SUPPRESSED = "animus.property.fallback-suppressed";
|
|
215
|
+
/** The same, where the fallback heads a chain of further `var()` reads. */
|
|
216
|
+
const PROPERTY_FALLBACK_CHAIN_SUPPRESSED = "animus.property.fallback-chain-suppressed";
|
|
217
|
+
/** A declared property set in keyframes or transitioned without a typed
|
|
218
|
+
* registration, so it does not interpolate. */
|
|
219
|
+
const PROPERTY_UNREGISTERED_ANIMATION = "animus.property.unregistered-animation";
|
|
220
|
+
/** A custom property whose resolved value is `var()` of itself, which makes
|
|
221
|
+
* it invalid at computed-value time. */
|
|
222
|
+
const PROPERTY_SELF_REFERENCE = "animus.property.self-reference";
|
|
223
|
+
/** A custom property that reads itself only inside another `var()`'s
|
|
224
|
+
* fallback, so whether it is cyclic depends on the browser. */
|
|
225
|
+
const PROPERTY_FALLBACK_SELF_REFERENCE = "animus.property.fallback-self-reference";
|
|
226
|
+
/** A prefix renamed contextual variables without `prefixContextualVars`, so
|
|
227
|
+
* their declared names no longer resolve. */
|
|
228
|
+
const PREFIX_CONTEXTUAL_VARS_UNPREFIXED = "animus.prefix.contextual-vars-unprefixed";
|
|
229
|
+
/** Under `prefixContextualVars`, a final name that collides with a runtime
|
|
230
|
+
* transport variable or a theme variable of the same spelling. */
|
|
231
|
+
const PREFIX_NAME_CONFLICT = "animus.prefix.name-conflict";
|
|
232
|
+
const DIAGNOSTIC_SEVERITY = /* @__PURE__ */ new Map([
|
|
233
|
+
[SELECTOR_UNSUPPORTED_SUBJECT, "error"],
|
|
234
|
+
[UNREADABLE_SOURCE_FILE, "error"],
|
|
235
|
+
[VOCABULARY_COLLISION, "warn"],
|
|
236
|
+
[VOCABULARY_LEGACY_VERB, "warn"],
|
|
237
|
+
[INVALID_PROPERTY_REGISTRATION, "error"],
|
|
238
|
+
[PROPERTY_FALLBACK_SUPPRESSED, "info"],
|
|
239
|
+
[PROPERTY_FALLBACK_CHAIN_SUPPRESSED, "warn"],
|
|
240
|
+
[PROPERTY_UNREGISTERED_ANIMATION, "info"],
|
|
241
|
+
[PROPERTY_SELF_REFERENCE, "warn"],
|
|
242
|
+
[PROPERTY_FALLBACK_SELF_REFERENCE, "warn"],
|
|
243
|
+
[PREFIX_CONTEXTUAL_VARS_UNPREFIXED, "warn"],
|
|
244
|
+
[PREFIX_NAME_CONFLICT, "error"]
|
|
245
|
+
]);
|
|
246
|
+
/** An unlisted code is `warn`: a witness kind from a newer system package
|
|
247
|
+
* must not fail the strict build of a host that predates its writer. */
|
|
248
|
+
function severityFor(code) {
|
|
249
|
+
return (code === void 0 ? void 0 : DIAGNOSTIC_SEVERITY.get(code)) ?? "warn";
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* The one mapper of a sealed system's witness entries for every host: the
|
|
253
|
+
* loader's evaluation host shims `console`, so the record is the channel.
|
|
254
|
+
*/
|
|
255
|
+
function vocabularyWitnessDiagnostics(vocabularyWitnessesJson) {
|
|
256
|
+
if (!vocabularyWitnessesJson) return [];
|
|
257
|
+
const entries = parseInternalWire(vocabularyWitnessesJson, "vocabularyWitnessesJson (the sealed system's vocabulary witness record)");
|
|
258
|
+
const diagnostics = [];
|
|
259
|
+
for (const entry of entries) if (entry.code === "animus.vocabulary.collision") diagnostics.push({
|
|
260
|
+
file: "system",
|
|
261
|
+
component: entry.name ?? "keyframes",
|
|
262
|
+
kind: "warn",
|
|
263
|
+
message: `keyframes vocabulary "${entry.name}" is registered by both ${entry.loser} and ${entry.winner} — ${entry.winner} wins; rename one collection (${entry.code})`,
|
|
264
|
+
code: entry.code,
|
|
265
|
+
severity: severityFor(entry.code)
|
|
266
|
+
});
|
|
267
|
+
else if (entry.code === "animus.vocabulary.legacy-verb") diagnostics.push({
|
|
268
|
+
file: "system",
|
|
269
|
+
component: "keyframes",
|
|
270
|
+
kind: "warn",
|
|
271
|
+
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})`,
|
|
272
|
+
code: entry.code,
|
|
273
|
+
severity: severityFor(entry.code)
|
|
274
|
+
});
|
|
275
|
+
else diagnostics.push({
|
|
276
|
+
file: "system",
|
|
277
|
+
component: "vocabulary",
|
|
278
|
+
kind: "warn",
|
|
279
|
+
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})` : ""}`,
|
|
280
|
+
code: entry.code,
|
|
281
|
+
severity: severityFor(entry.code)
|
|
282
|
+
});
|
|
283
|
+
return diagnostics;
|
|
284
|
+
}
|
|
285
|
+
/** The diagnostics a loaded system's own records carry: vocabulary
|
|
286
|
+
* witnesses and invalid `@property` registrations. */
|
|
287
|
+
function systemLoadDiagnostics(system) {
|
|
288
|
+
const registrations = (system.invalidPropertyRegistrations ?? []).map(({ name, reason }) => ({
|
|
289
|
+
file: "system",
|
|
290
|
+
component: name,
|
|
291
|
+
kind: "warn",
|
|
292
|
+
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})`,
|
|
293
|
+
code: INVALID_PROPERTY_REGISTRATION,
|
|
294
|
+
severity: severityFor(INVALID_PROPERTY_REGISTRATION)
|
|
295
|
+
}));
|
|
296
|
+
const prefixed = system.legacyPrefixedContextualVars ?? [];
|
|
297
|
+
const unprefixed = prefixed.length === 0 ? [] : [{
|
|
298
|
+
file: "system",
|
|
299
|
+
component: "prefix",
|
|
300
|
+
kind: "warn",
|
|
301
|
+
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})`,
|
|
302
|
+
code: PREFIX_CONTEXTUAL_VARS_UNPREFIXED,
|
|
303
|
+
severity: severityFor(PREFIX_CONTEXTUAL_VARS_UNPREFIXED)
|
|
304
|
+
}];
|
|
305
|
+
const conflicts = (system.prefixNameConflicts ?? []).map(({ name, final, reason }) => ({
|
|
306
|
+
file: "system",
|
|
307
|
+
component: `--${name}`,
|
|
308
|
+
kind: "error",
|
|
309
|
+
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})`,
|
|
310
|
+
code: PREFIX_NAME_CONFLICT,
|
|
311
|
+
severity: severityFor(PREFIX_NAME_CONFLICT)
|
|
312
|
+
}));
|
|
313
|
+
return [
|
|
314
|
+
...vocabularyWitnessDiagnostics(system.vocabularyWitnessesJson),
|
|
315
|
+
...registrations,
|
|
316
|
+
...unprefixed,
|
|
317
|
+
...conflicts
|
|
318
|
+
];
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* The single strict-escalation policy point for both extraction plugins:
|
|
322
|
+
* error-severity diagnostics throw together under `strict`, else warn.
|
|
323
|
+
*/
|
|
324
|
+
function surfaceManifestDiagnostics(manifest, warn, policy = {}) {
|
|
325
|
+
const errors = [];
|
|
326
|
+
const diagnostics = policy.prepend?.length ? [...policy.prepend, ...manifest.diagnostics ?? []] : manifest.diagnostics ?? [];
|
|
327
|
+
for (const diagnostic of diagnostics) {
|
|
328
|
+
let line = null;
|
|
329
|
+
if (diagnostic.kind === "bail") line = `⚠ ${diagnostic.component} not extracted: ${diagnostic.message}`;
|
|
330
|
+
else if (diagnostic.kind === "skip") line = `⚠ ${diagnostic.component}: skipped ${diagnostic.message}`;
|
|
331
|
+
else if (diagnostic.kind === "warn") line = `${diagnostic.severity === "info" ? "ℹ" : "⚠"} ${diagnostic.file}: ${diagnostic.component}: ${diagnostic.message}`;
|
|
332
|
+
if (line === null) continue;
|
|
333
|
+
if (diagnostic.code && !diagnostic.message.includes(diagnostic.code)) line += ` [${diagnostic.code}]`;
|
|
334
|
+
if (diagnostic.severity === "info") {
|
|
335
|
+
policy.info?.(line);
|
|
336
|
+
continue;
|
|
337
|
+
}
|
|
338
|
+
if (policy.strict && diagnostic.severity === "error") {
|
|
339
|
+
errors.push(`${diagnostic.code ?? "error"} — ${diagnostic.component}: ${diagnostic.message}`);
|
|
340
|
+
continue;
|
|
341
|
+
}
|
|
342
|
+
warn(line);
|
|
343
|
+
}
|
|
344
|
+
if (errors.length > 0) throw new Error(`[animus] strict: ${errors.length} error diagnostic(s):\n${errors.join("\n")}`);
|
|
345
|
+
}
|
|
346
|
+
//#endregion
|
|
128
347
|
//#region pipeline/mdx-preprocessor.ts
|
|
129
348
|
/**
|
|
130
349
|
* MDX compiled to scanner-consumable JSX, plus the extension sets every
|
|
@@ -1049,7 +1268,8 @@ function withoutInvalidOriginals(result, invalidOriginals) {
|
|
|
1049
1268
|
...result,
|
|
1050
1269
|
originalEntries: result.originalEntries.filter((entry) => !invalidOriginals.has(entry.path)),
|
|
1051
1270
|
analysisEntries: result.analysisEntries.filter((entry) => analysisPaths.has(entry.path)),
|
|
1052
|
-
ownership
|
|
1271
|
+
ownership,
|
|
1272
|
+
skippedOriginals: [...invalidOriginals].sort()
|
|
1053
1273
|
};
|
|
1054
1274
|
}
|
|
1055
1275
|
/**
|
|
@@ -1089,11 +1309,12 @@ function createSourceIngestor(host) {
|
|
|
1089
1309
|
const invalidOriginals = new Set(fatal.map((diagnostic) => diagnostic.originalPath));
|
|
1090
1310
|
if (diagnostics.length === 0) return invalidOriginals;
|
|
1091
1311
|
if (host.strict() && fatal.length > 0) {
|
|
1092
|
-
const lines = fatal.map((diagnostic) => `${diagnostic.code} ${diagnostic.originalPath}: ${diagnostic.message}`);
|
|
1312
|
+
const lines = fatal.map((diagnostic) => `${diagnostic.code} ${diagnostic.originalPath}: ${diagnostic.message} (${SKIPPED_SOURCE_COST})`);
|
|
1093
1313
|
throw new Error(`${host.prefix} ${lines.join(`\n${host.prefix} `)}`);
|
|
1094
1314
|
}
|
|
1095
1315
|
for (const diagnostic of diagnostics) {
|
|
1096
|
-
const
|
|
1316
|
+
const skipped = isAdvisorySourceDiagnostic(diagnostic) ? "" : ` (${SKIPPED_SOURCE_COST})`;
|
|
1317
|
+
const line = `${diagnostic.code} ${diagnostic.originalPath}: ${diagnostic.message}${skipped}`;
|
|
1097
1318
|
let warned = warnedByOriginal.get(diagnostic.originalPath);
|
|
1098
1319
|
if (!warned) {
|
|
1099
1320
|
warned = /* @__PURE__ */ new Set();
|
|
@@ -1128,7 +1349,7 @@ function createV2EngineApi(deps) {
|
|
|
1128
1349
|
return {
|
|
1129
1350
|
loadSystemModule: (...args) => native.loadSystemModule(...args),
|
|
1130
1351
|
extractFacts: (filesJson) => native.extractFacts(filesJson),
|
|
1131
|
-
analyzeProject: (filesJsonRaw, scalesJson, variableMapJson, contextualVarsJson, propConfigJson, groupRegistryJson, packageResolutionJson, devMode, emitterConfigJson, selectorAliasesJson, _selectorOrderJson, globalStyleBlocksJson, pathAliasesJson, keyframesJson, staticCssJson, conditionAliasesJson, externalDirsJson, transformSourcesJson, transformProvenanceJson, declarationScalesJson) => {
|
|
1352
|
+
analyzeProject: (filesJsonRaw, scalesJson, variableMapJson, contextualVarsJson, propConfigJson, groupRegistryJson, packageResolutionJson, devMode, emitterConfigJson, selectorAliasesJson, _selectorOrderJson, globalStyleBlocksJson, pathAliasesJson, keyframesJson, staticCssJson, conditionAliasesJson, externalDirsJson, transformSourcesJson, transformProvenanceJson, declarationScalesJson, analysisContextJson) => {
|
|
1132
1353
|
const filesJson = deps.rehydrateFilesJson ? deps.rehydrateFilesJson(filesJsonRaw) : filesJsonRaw;
|
|
1133
1354
|
const sent = /* @__PURE__ */ new Map();
|
|
1134
1355
|
for (const entry of parseFilesJson(filesJson, label)) sent.set(entry.path, entry.source);
|
|
@@ -1155,6 +1376,7 @@ function createV2EngineApi(deps) {
|
|
|
1155
1376
|
transformSourcesJson: transformSourcesJson ?? void 0,
|
|
1156
1377
|
transformProvenanceJson: transformProvenanceJson ?? void 0,
|
|
1157
1378
|
declarationScalesJson,
|
|
1379
|
+
analysisContextJson,
|
|
1158
1380
|
devMode
|
|
1159
1381
|
};
|
|
1160
1382
|
const engine = new native.ExtractEngine(config);
|
|
@@ -1227,6 +1449,7 @@ const CORE_OPTION_KEYS = /* @__PURE__ */ new Set([
|
|
|
1227
1449
|
"strict",
|
|
1228
1450
|
"verbose",
|
|
1229
1451
|
"prefix",
|
|
1452
|
+
"prefixContextualVars",
|
|
1230
1453
|
"targets",
|
|
1231
1454
|
"minify",
|
|
1232
1455
|
"staticCss",
|
|
@@ -1320,6 +1543,11 @@ const CORE_VALUE_GATES = [
|
|
|
1320
1543
|
ok: isString,
|
|
1321
1544
|
expected: "a string"
|
|
1322
1545
|
},
|
|
1546
|
+
{
|
|
1547
|
+
key: "prefixContextualVars",
|
|
1548
|
+
ok: isBoolean,
|
|
1549
|
+
expected: "a boolean"
|
|
1550
|
+
},
|
|
1323
1551
|
{
|
|
1324
1552
|
key: "targets",
|
|
1325
1553
|
ok: isStringOrStringArray,
|
|
@@ -2018,31 +2246,113 @@ function createSourceCorpus(host, ingestor = createSourceIngestor(host)) {
|
|
|
2018
2246
|
//#endregion
|
|
2019
2247
|
//#region pipeline/asset-placeholders.ts
|
|
2020
2248
|
const ASSET_PLACEHOLDER_PREFIX = "animus-asset:";
|
|
2021
|
-
const
|
|
2022
|
-
const
|
|
2023
|
-
const
|
|
2024
|
-
|
|
2025
|
-
function
|
|
2249
|
+
const URL_OPEN_RE = /url\(\s*(['"]?)/gi;
|
|
2250
|
+
const IMAGE_SET_OPEN_RE = /(?:-webkit-)?image-set\(/gi;
|
|
2251
|
+
const BARE_SPECIFIER_RE = /^[^'")\s]+/;
|
|
2252
|
+
/** The index of the `)` that closes the `(` at `open`, outside strings. */
|
|
2253
|
+
function closingParen$1(css, open) {
|
|
2254
|
+
let depth = 0;
|
|
2255
|
+
let quote = null;
|
|
2256
|
+
for (let i = open; i < css.length; i += 1) {
|
|
2257
|
+
const c = css[i];
|
|
2258
|
+
if (quote !== null) {
|
|
2259
|
+
if (c === quote) quote = null;
|
|
2260
|
+
} else if (c === "\"" || c === "'") quote = c;
|
|
2261
|
+
else if (c === "(") depth += 1;
|
|
2262
|
+
else if (c === ")") {
|
|
2263
|
+
depth -= 1;
|
|
2264
|
+
if (depth === 0) return i;
|
|
2265
|
+
}
|
|
2266
|
+
}
|
|
2267
|
+
return css.length;
|
|
2268
|
+
}
|
|
2269
|
+
function quotedReference(css, quoteAt) {
|
|
2270
|
+
const start = quoteAt + 1;
|
|
2271
|
+
if (!css.startsWith("animus-asset:", start)) return null;
|
|
2272
|
+
const end = css.indexOf(css[quoteAt], start);
|
|
2273
|
+
if (end === -1) return null;
|
|
2274
|
+
return {
|
|
2275
|
+
start,
|
|
2276
|
+
end,
|
|
2277
|
+
specifier: css.slice(start + 13, end)
|
|
2278
|
+
};
|
|
2279
|
+
}
|
|
2280
|
+
function assetReferences(css) {
|
|
2026
2281
|
if (!css.includes("animus-asset:")) return [];
|
|
2027
|
-
const
|
|
2028
|
-
const
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2282
|
+
const references = [];
|
|
2283
|
+
for (const open of css.matchAll(URL_OPEN_RE)) {
|
|
2284
|
+
const at = (open.index ?? 0) + open[0].length;
|
|
2285
|
+
if (open[1]) {
|
|
2286
|
+
const reference = quotedReference(css, at - 1);
|
|
2287
|
+
if (reference) references.push(reference);
|
|
2288
|
+
continue;
|
|
2289
|
+
}
|
|
2290
|
+
if (!css.startsWith("animus-asset:", at)) continue;
|
|
2291
|
+
const specifier = BARE_SPECIFIER_RE.exec(css.slice(at + 13))?.[0];
|
|
2292
|
+
if (specifier === void 0) continue;
|
|
2293
|
+
const end = at + 13 + specifier.length;
|
|
2294
|
+
references.push({
|
|
2295
|
+
start: at,
|
|
2296
|
+
end,
|
|
2297
|
+
specifier
|
|
2298
|
+
});
|
|
2299
|
+
}
|
|
2300
|
+
for (const open of css.matchAll(IMAGE_SET_OPEN_RE)) {
|
|
2301
|
+
const paren = (open.index ?? 0) + open[0].length - 1;
|
|
2302
|
+
const close = closingParen$1(css, paren);
|
|
2303
|
+
let depth = 0;
|
|
2304
|
+
let candidateStart = true;
|
|
2305
|
+
for (let i = paren + 1; i < close; i += 1) {
|
|
2306
|
+
const c = css[i];
|
|
2307
|
+
if (/\s/.test(c)) continue;
|
|
2308
|
+
if (c === "\"" || c === "'") {
|
|
2309
|
+
if (depth === 0 && candidateStart) {
|
|
2310
|
+
const reference = quotedReference(css, i);
|
|
2311
|
+
if (reference) references.push(reference);
|
|
2312
|
+
}
|
|
2313
|
+
const end = css.indexOf(c, i + 1);
|
|
2314
|
+
i = end === -1 ? close : end;
|
|
2315
|
+
} else if (c === "(") depth += 1;
|
|
2316
|
+
else if (c === ")") depth -= 1;
|
|
2317
|
+
else if (c === "," && depth === 0) {
|
|
2318
|
+
candidateStart = true;
|
|
2319
|
+
continue;
|
|
2320
|
+
}
|
|
2321
|
+
candidateStart = false;
|
|
2322
|
+
}
|
|
2323
|
+
}
|
|
2324
|
+
return references.sort((a, b) => a.start - b.start);
|
|
2325
|
+
}
|
|
2326
|
+
function findAssetSpecifiers(css) {
|
|
2327
|
+
return [...new Set(assetReferences(css).map((reference) => reference.specifier))];
|
|
2034
2328
|
}
|
|
2035
2329
|
function substituteAssetPlaceholders(css, urlBySpecifier) {
|
|
2036
2330
|
if (urlBySpecifier.size === 0) return css;
|
|
2037
|
-
if (!css.includes("animus-asset:")) return css;
|
|
2038
|
-
const specifiers = [...urlBySpecifier.keys()].sort((a, b) => b.length - a.length);
|
|
2039
2331
|
let out = css;
|
|
2040
|
-
for (const
|
|
2041
|
-
const
|
|
2042
|
-
|
|
2332
|
+
for (const reference of assetReferences(css).reverse()) {
|
|
2333
|
+
const url = urlBySpecifier.get(reference.specifier);
|
|
2334
|
+
if (url === void 0) continue;
|
|
2335
|
+
out = out.slice(0, reference.start) + url + out.slice(reference.end);
|
|
2043
2336
|
}
|
|
2044
2337
|
return out;
|
|
2045
2338
|
}
|
|
2339
|
+
/**
|
|
2340
|
+
* Specifiers of placeholder text anywhere inside a `url()` or `image-set()`
|
|
2341
|
+
* call, a reference or not: after substitution any of it is a broken load.
|
|
2342
|
+
*/
|
|
2343
|
+
function placeholdersInAssetCalls(css) {
|
|
2344
|
+
if (!css.includes("animus-asset:")) return [];
|
|
2345
|
+
const calls = [...css.matchAll(URL_OPEN_RE), ...css.matchAll(IMAGE_SET_OPEN_RE)].map((open) => {
|
|
2346
|
+
const paren = css.indexOf("(", open.index ?? 0);
|
|
2347
|
+
return [paren, closingParen$1(css, paren)];
|
|
2348
|
+
});
|
|
2349
|
+
const found = /* @__PURE__ */ new Set();
|
|
2350
|
+
for (let at = css.indexOf(ASSET_PLACEHOLDER_PREFIX); at !== -1; at = css.indexOf(ASSET_PLACEHOLDER_PREFIX, at + 1)) {
|
|
2351
|
+
if (!calls.some(([open, close]) => open < at && at < close)) continue;
|
|
2352
|
+
found.add(/^[^'")\s,]*/.exec(css.slice(at + 13))[0]);
|
|
2353
|
+
}
|
|
2354
|
+
return [...found];
|
|
2355
|
+
}
|
|
2046
2356
|
function findSheetAssetSpecifiers(sheets) {
|
|
2047
2357
|
return [.../* @__PURE__ */ new Set([
|
|
2048
2358
|
...findAssetSpecifiers(sheets.variableCss),
|
|
@@ -2062,12 +2372,22 @@ const UNSUBSTITUTED_ASSET_CODE = "animus.asset.unsubstituted-placeholder";
|
|
|
2062
2372
|
/** A placeholder in emitted CSS is a broken URL in the browser: it fails a
|
|
2063
2373
|
* strict build and warns otherwise. */
|
|
2064
2374
|
function reportSurvivingAssetPlaceholders(css, report) {
|
|
2065
|
-
const specifiers =
|
|
2375
|
+
const specifiers = placeholdersInAssetCalls(css);
|
|
2066
2376
|
if (specifiers.length === 0) return;
|
|
2067
|
-
const message = `${report.prefix} asset() placeholders reached emitted CSS unsubstituted: ${specifiers.join(", ")} (${UNSUBSTITUTED_ASSET_CODE})`;
|
|
2377
|
+
const message = `${report.prefix} asset() placeholders reached ${report.surface ?? "emitted CSS"} unsubstituted: ${specifiers.join(", ")} (${UNSUBSTITUTED_ASSET_CODE})`;
|
|
2068
2378
|
if (report.strict) throw new Error(message);
|
|
2069
2379
|
report.warn(message);
|
|
2070
2380
|
}
|
|
2381
|
+
/**
|
|
2382
|
+
* The runtime code an analysis generates: each dynamic prop config, which the
|
|
2383
|
+
* shared prop map module carries, and each component's replacement. Extraction
|
|
2384
|
+
* lifts asset() out of them into root variables, so none should hold one.
|
|
2385
|
+
* Both embed values as JSON strings, so their quote and backslash escapes are
|
|
2386
|
+
* undone for scanning: the text is for `findAssetSpecifiers`, not to run.
|
|
2387
|
+
*/
|
|
2388
|
+
function generatedModuleCode(manifest) {
|
|
2389
|
+
return [JSON.stringify(manifest.dynamic_props), ...Object.values(manifest.components).map((component) => component.replacement)].join("\n").replace(/\\(["\\])/g, "$1");
|
|
2390
|
+
}
|
|
2071
2391
|
//#endregion
|
|
2072
2392
|
//#region pipeline/resolve-asset.ts
|
|
2073
2393
|
/**
|
|
@@ -2291,182 +2611,121 @@ function postProcessCss(css, opts) {
|
|
|
2291
2611
|
}
|
|
2292
2612
|
}
|
|
2293
2613
|
//#endregion
|
|
2294
|
-
//#region pipeline/
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
const
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
const
|
|
2301
|
-
|
|
2302
|
-
return
|
|
2303
|
-
|
|
2304
|
-
|
|
2305
|
-
|
|
2614
|
+
//#region pipeline/property-names.ts
|
|
2615
|
+
function createPropertyNames(declared, prefix, themeNames = []) {
|
|
2616
|
+
const finals = /* @__PURE__ */ new Map();
|
|
2617
|
+
for (const name of [...new Set(declared)].sort()) finals.set(name, `${prefix}-${name}`);
|
|
2618
|
+
const aliases = /* @__PURE__ */ new Map();
|
|
2619
|
+
for (const [name, final] of finals) if (!finals.has(final)) aliases.set(final, name);
|
|
2620
|
+
const others = /* @__PURE__ */ new Map();
|
|
2621
|
+
for (const name of [...new Set(themeNames)].sort()) if (!finals.has(name)) others.set(name, `${prefix}-${name}`);
|
|
2622
|
+
return {
|
|
2623
|
+
identity: (spelling) => finals.has(spelling) ? spelling : aliases.get(spelling) ?? (others.has(spelling) ? spelling : void 0),
|
|
2624
|
+
finalName: (name) => finals.get(name) ?? others.get(name),
|
|
2625
|
+
ambiguous: () => [...others.keys()].filter((name) => aliases.has(name)).map((name) => ({
|
|
2626
|
+
name,
|
|
2627
|
+
contextual: aliases.get(name) ?? name
|
|
2628
|
+
}))
|
|
2629
|
+
};
|
|
2306
2630
|
}
|
|
2307
|
-
|
|
2308
|
-
|
|
2309
|
-
|
|
2310
|
-
|
|
2311
|
-
|
|
2312
|
-
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2316
|
-
|
|
2317
|
-
|
|
2318
|
-
|
|
2631
|
+
const NAME_CHAR = /[\w-]/;
|
|
2632
|
+
/**
|
|
2633
|
+
* The name starting at `from`, read as CSS tokenizes an identifier: ASCII
|
|
2634
|
+
* letters, digits, `-` and `_`, every non-ASCII code point, and escapes.
|
|
2635
|
+
*/
|
|
2636
|
+
function readCustomPropertyName(css, from) {
|
|
2637
|
+
let end = from;
|
|
2638
|
+
let name = "";
|
|
2639
|
+
while (end < css.length) {
|
|
2640
|
+
if (isNameChar(css[end])) {
|
|
2641
|
+
name += css[end];
|
|
2642
|
+
end += 1;
|
|
2319
2643
|
continue;
|
|
2320
2644
|
}
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2645
|
+
const escape = escapeAt(css, end);
|
|
2646
|
+
if (escape === null) break;
|
|
2647
|
+
name += escape.codePoint;
|
|
2648
|
+
end += escape.length;
|
|
2325
2649
|
}
|
|
2326
|
-
return false;
|
|
2327
|
-
}
|
|
2328
|
-
/**
|
|
2329
|
-
* Coded diagnostics for selector-shaped alias values with no substitutable
|
|
2330
|
-
* subject; alias values never reach the evaluator's own key guard.
|
|
2331
|
-
*/
|
|
2332
|
-
function collectSelectorAliasDiagnostics(selectorAliasesJson) {
|
|
2333
|
-
if (!selectorAliasesJson) return [];
|
|
2334
|
-
const aliases = parseInternalWire(selectorAliasesJson, "selectorAliasesJson (the system loader's selector-alias registry)");
|
|
2335
|
-
const diagnostics = [];
|
|
2336
|
-
for (const [name, value] of Object.entries(aliases)) if (value.includes("&") && !hasSelectorSubject(value)) diagnostics.push({
|
|
2337
|
-
file: "system",
|
|
2338
|
-
component: name,
|
|
2339
|
-
kind: "warn",
|
|
2340
|
-
message: `selector alias '${name}' value '${value}' has no substitutable '&' subject outside quoted text (${SELECTOR_UNSUPPORTED_SUBJECT})`,
|
|
2341
|
-
code: SELECTOR_UNSUPPORTED_SUBJECT,
|
|
2342
|
-
severity: severityFor(SELECTOR_UNSUPPORTED_SUBJECT)
|
|
2343
|
-
});
|
|
2344
|
-
return diagnostics;
|
|
2345
|
-
}
|
|
2346
|
-
const UNREADABLE_SOURCE_FILE = "animus.ingestion.unreadable-source-file";
|
|
2347
|
-
/**
|
|
2348
|
-
* A configured source file that could not be read is a LOST INPUT: the
|
|
2349
|
-
* emitted artifacts silently lack whatever that file declared.
|
|
2350
|
-
*/
|
|
2351
|
-
function unreadableSourceDiagnostic(relPath, error) {
|
|
2352
2650
|
return {
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
kind: "warn",
|
|
2356
|
-
message: `configured source file ${relPath} could not be read and was skipped: ${String(error)}`,
|
|
2357
|
-
code: UNREADABLE_SOURCE_FILE,
|
|
2358
|
-
severity: severityFor(UNREADABLE_SOURCE_FILE)
|
|
2651
|
+
end,
|
|
2652
|
+
name
|
|
2359
2653
|
};
|
|
2360
2654
|
}
|
|
2361
|
-
/**
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2367
|
-
const
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
const
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
const
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
const PROPERTY_SELF_REFERENCE = "animus.property.self-reference";
|
|
2381
|
-
const DIAGNOSTIC_SEVERITY = /* @__PURE__ */ new Map([
|
|
2382
|
-
[SELECTOR_UNSUPPORTED_SUBJECT, "error"],
|
|
2383
|
-
[UNREADABLE_SOURCE_FILE, "error"],
|
|
2384
|
-
[VOCABULARY_COLLISION, "warn"],
|
|
2385
|
-
[VOCABULARY_LEGACY_VERB, "warn"],
|
|
2386
|
-
[INVALID_PROPERTY_REGISTRATION, "error"],
|
|
2387
|
-
[PROPERTY_FALLBACK_SUPPRESSED, "info"],
|
|
2388
|
-
[PROPERTY_FALLBACK_CHAIN_SUPPRESSED, "warn"],
|
|
2389
|
-
[PROPERTY_UNREGISTERED_ANIMATION, "info"],
|
|
2390
|
-
[PROPERTY_DISCRETE_ANIMATION, "warn"],
|
|
2391
|
-
[PROPERTY_SELF_REFERENCE, "warn"]
|
|
2392
|
-
]);
|
|
2393
|
-
/** An unlisted code is `warn`: a witness kind from a newer system package
|
|
2394
|
-
* must not fail the strict build of a host that predates its writer. */
|
|
2395
|
-
function severityFor(code) {
|
|
2396
|
-
return (code === void 0 ? void 0 : DIAGNOSTIC_SEVERITY.get(code)) ?? "warn";
|
|
2397
|
-
}
|
|
2398
|
-
/**
|
|
2399
|
-
* The one mapper of a sealed system's witness entries for every host: the
|
|
2400
|
-
* loader's evaluation host shims `console`, so the record is the channel.
|
|
2401
|
-
*/
|
|
2402
|
-
function vocabularyWitnessDiagnostics(vocabularyWitnessesJson) {
|
|
2403
|
-
if (!vocabularyWitnessesJson) return [];
|
|
2404
|
-
const entries = parseInternalWire(vocabularyWitnessesJson, "vocabularyWitnessesJson (the sealed system's vocabulary witness record)");
|
|
2405
|
-
const diagnostics = [];
|
|
2406
|
-
for (const entry of entries) if (entry.code === "animus.vocabulary.collision") diagnostics.push({
|
|
2407
|
-
file: "system",
|
|
2408
|
-
component: entry.name ?? "keyframes",
|
|
2409
|
-
kind: "warn",
|
|
2410
|
-
message: `keyframes vocabulary "${entry.name}" is registered by both ${entry.loser} and ${entry.winner} — ${entry.winner} wins; rename one collection (${entry.code})`,
|
|
2411
|
-
code: entry.code,
|
|
2412
|
-
severity: severityFor(entry.code)
|
|
2413
|
-
});
|
|
2414
|
-
else if (entry.code === "animus.vocabulary.legacy-verb") diagnostics.push({
|
|
2415
|
-
file: "system",
|
|
2416
|
-
component: "keyframes",
|
|
2417
|
-
kind: "warn",
|
|
2418
|
-
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})`,
|
|
2419
|
-
code: entry.code,
|
|
2420
|
-
severity: severityFor(entry.code)
|
|
2421
|
-
});
|
|
2422
|
-
else diagnostics.push({
|
|
2423
|
-
file: "system",
|
|
2424
|
-
component: "vocabulary",
|
|
2425
|
-
kind: "warn",
|
|
2426
|
-
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})` : ""}`,
|
|
2427
|
-
code: entry.code,
|
|
2428
|
-
severity: severityFor(entry.code)
|
|
2429
|
-
});
|
|
2430
|
-
return diagnostics;
|
|
2431
|
-
}
|
|
2432
|
-
/** The diagnostics a loaded system's own records carry: vocabulary
|
|
2433
|
-
* witnesses and invalid `@property` registrations. */
|
|
2434
|
-
function systemLoadDiagnostics(system) {
|
|
2435
|
-
const registrations = (system.invalidPropertyRegistrations ?? []).map(({ name, reason }) => ({
|
|
2436
|
-
file: "system",
|
|
2437
|
-
component: name,
|
|
2438
|
-
kind: "warn",
|
|
2439
|
-
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})`,
|
|
2440
|
-
code: INVALID_PROPERTY_REGISTRATION,
|
|
2441
|
-
severity: severityFor(INVALID_PROPERTY_REGISTRATION)
|
|
2442
|
-
}));
|
|
2443
|
-
return [...vocabularyWitnessDiagnostics(system.vocabularyWitnessesJson), ...registrations];
|
|
2655
|
+
/** A valid escape at `at`: up to six hex digits and one following
|
|
2656
|
+
* whitespace, or any other code point but a newline. */
|
|
2657
|
+
function escapeAt(css, at) {
|
|
2658
|
+
if (css[at] !== "\\") return null;
|
|
2659
|
+
const next = css.codePointAt(at + 1);
|
|
2660
|
+
if (next === void 0 || next === 10 || next === 13 || next === 12) return null;
|
|
2661
|
+
const digits = /^[0-9a-fA-F]{1,6}/.exec(css.slice(at + 1, at + 7))?.[0];
|
|
2662
|
+
if (digits === void 0) return {
|
|
2663
|
+
codePoint: String.fromCodePoint(next),
|
|
2664
|
+
length: 1 + String.fromCodePoint(next).length
|
|
2665
|
+
};
|
|
2666
|
+
const after = css.slice(at + 1 + digits.length);
|
|
2667
|
+
const space = after.startsWith("\r\n") ? 2 : /^[ \t\n\r\f]/.test(after) ? 1 : 0;
|
|
2668
|
+
const value = parseInt(digits, 16);
|
|
2669
|
+
const valid = value !== 0 && value <= 1114111 && (value < 55296 || value > 57343);
|
|
2670
|
+
return {
|
|
2671
|
+
codePoint: String.fromCodePoint(valid ? value : 65533),
|
|
2672
|
+
length: 1 + digits.length + space
|
|
2673
|
+
};
|
|
2444
2674
|
}
|
|
2445
2675
|
/**
|
|
2446
|
-
*
|
|
2447
|
-
*
|
|
2676
|
+
* Gives every custom-property token that names a managed property its final
|
|
2677
|
+
* name, in one pass: declaration keys, `var()` reads at any depth (fallbacks
|
|
2678
|
+
* included), `@property` names, transition-property lists and style queries.
|
|
2679
|
+
* A name is read whole, escapes decoded, so an undeclared name that starts
|
|
2680
|
+
* with a managed one is never renamed in part. Quoted strings, `url()` and
|
|
2681
|
+
* comments are copied unchanged.
|
|
2448
2682
|
*/
|
|
2449
|
-
function
|
|
2450
|
-
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
|
|
2454
|
-
if (
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
2458
|
-
if (
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
if (
|
|
2464
|
-
|
|
2465
|
-
|
|
2683
|
+
function renameCustomProperties(css, names) {
|
|
2684
|
+
let out = "";
|
|
2685
|
+
let i = 0;
|
|
2686
|
+
while (i < css.length) {
|
|
2687
|
+
const c = css[i];
|
|
2688
|
+
if (c === "\"" || c === "'") {
|
|
2689
|
+
const end = closingQuote$1(css, i);
|
|
2690
|
+
out += css.slice(i, end);
|
|
2691
|
+
i = end;
|
|
2692
|
+
} else if (css.startsWith("/*", i)) {
|
|
2693
|
+
const close = css.indexOf("*/", i + 2);
|
|
2694
|
+
const end = close === -1 ? css.length : close + 2;
|
|
2695
|
+
out += css.slice(i, end);
|
|
2696
|
+
i = end;
|
|
2697
|
+
} else if (/^url\(/i.test(css.slice(i, i + 4)) && !isNameChar(css[i - 1])) {
|
|
2698
|
+
const end = closingUrl(css, i + 4);
|
|
2699
|
+
out += css.slice(i, end);
|
|
2700
|
+
i = end;
|
|
2701
|
+
} else if (css.startsWith("--", i) && !isNameChar(css[i - 1])) {
|
|
2702
|
+
const { end, name } = readCustomPropertyName(css, i + 2);
|
|
2703
|
+
const identity = names.identity(name);
|
|
2704
|
+
const final = identity === void 0 ? void 0 : names.finalName(identity);
|
|
2705
|
+
out += final === void 0 ? css.slice(i, end) : `--${final}`;
|
|
2706
|
+
i = end;
|
|
2707
|
+
} else {
|
|
2708
|
+
out += c;
|
|
2709
|
+
i += 1;
|
|
2466
2710
|
}
|
|
2467
|
-
warn(line);
|
|
2468
2711
|
}
|
|
2469
|
-
|
|
2712
|
+
return out;
|
|
2713
|
+
}
|
|
2714
|
+
/** A name code point, or a UTF-16 unit of a non-ASCII one. */
|
|
2715
|
+
function isNameChar(c) {
|
|
2716
|
+
return c !== void 0 && (NAME_CHAR.test(c) || c.charCodeAt(0) >= 128);
|
|
2717
|
+
}
|
|
2718
|
+
function closingQuote$1(css, open) {
|
|
2719
|
+
const quote = css[open];
|
|
2720
|
+
let i = open + 1;
|
|
2721
|
+
while (i < css.length && css[i] !== quote) i += css[i] === "\\" ? 2 : 1;
|
|
2722
|
+
return Math.min(i + 1, css.length);
|
|
2723
|
+
}
|
|
2724
|
+
/** The index after the `)` that closes an unquoted or quoted `url(`. */
|
|
2725
|
+
function closingUrl(css, from) {
|
|
2726
|
+
let i = from;
|
|
2727
|
+
while (i < css.length && css[i] !== ")") i = css[i] === "\"" || css[i] === "'" ? closingQuote$1(css, i) : i + 1;
|
|
2728
|
+
return Math.min(i + 1, css.length);
|
|
2470
2729
|
}
|
|
2471
2730
|
//#endregion
|
|
2472
2731
|
//#region pipeline/property-registrations.ts
|
|
@@ -2490,34 +2749,25 @@ const SYNTAX_TYPES = /* @__PURE__ */ new Set([
|
|
|
2490
2749
|
"url"
|
|
2491
2750
|
]);
|
|
2492
2751
|
const SYNTAX_COMPONENT = /^(?:<([a-z-]+)>[+#]?|-?[a-zA-Z_][\w-]*)$/;
|
|
2493
|
-
|
|
2494
|
-
|
|
2495
|
-
const DIMENSION_TYPES = /* @__PURE__ */ new Set([
|
|
2496
|
-
"image",
|
|
2497
|
-
"length",
|
|
2498
|
-
"length-percentage",
|
|
2499
|
-
"transform-function",
|
|
2500
|
-
"transform-list"
|
|
2501
|
-
]);
|
|
2502
|
-
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;
|
|
2503
|
-
const SUBSTITUTION = /\b(?:var|env|attr)\(/i;
|
|
2504
|
-
/** Quoted strings and `url()` hold text, never units or substitutions. */
|
|
2505
|
-
const LITERAL_TEXT = /"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|url\([^)]*\)/gi;
|
|
2752
|
+
const INITIAL_VALUE = /(initial-value\s*:\s*)([^;}]*)/;
|
|
2753
|
+
const MATH_FUNCTION = /\b(?:calc|min|max|clamp)\(/i;
|
|
2506
2754
|
/**
|
|
2507
2755
|
* Removes the `@property` rules browsers would ignore — an unknown syntax
|
|
2508
2756
|
* component, a syntax other than `*` without an initial value, an initial
|
|
2509
|
-
* value that depends on context, or one the
|
|
2510
|
-
* Minifying a stylesheet that contains such a rule
|
|
2757
|
+
* value that substitutes another value or depends on context, or one the
|
|
2758
|
+
* syntax does not accept. Minifying a stylesheet that contains such a rule
|
|
2759
|
+
* can also fail. An all-absolute math initial value is written as its
|
|
2760
|
+
* computed value, which the minifier reads.
|
|
2511
2761
|
*/
|
|
2512
2762
|
function splitInvalidPropertyRegistrations(css) {
|
|
2513
2763
|
const invalid = [];
|
|
2514
2764
|
return {
|
|
2515
2765
|
css: css.replace(PROPERTY_RULE, (rule, name) => {
|
|
2516
|
-
const
|
|
2517
|
-
if (
|
|
2766
|
+
const checked = checkRegistration(rule);
|
|
2767
|
+
if ("rule" in checked) return checked.rule;
|
|
2518
2768
|
invalid.push({
|
|
2519
2769
|
name,
|
|
2520
|
-
reason
|
|
2770
|
+
reason: checked.reason
|
|
2521
2771
|
});
|
|
2522
2772
|
return "";
|
|
2523
2773
|
}),
|
|
@@ -2536,42 +2786,48 @@ function readRegistration(rule) {
|
|
|
2536
2786
|
initialValue: /initial-value\s*:\s*([^;}]*)/.exec(rule)?.[1]?.trim()
|
|
2537
2787
|
};
|
|
2538
2788
|
}
|
|
2539
|
-
|
|
2789
|
+
/** The rule to keep, with an all-absolute math initial value folded, or why
|
|
2790
|
+
* a browser would ignore it. */
|
|
2791
|
+
function checkRegistration(rule) {
|
|
2540
2792
|
const { syntax, initialValue } = readRegistration(rule);
|
|
2793
|
+
if (initialValue !== void 0 && (0, _animus_ui_properties.substitutesValue)(initialValue)) return { reason: `initialValue "${initialValue}" substitutes another value; var(), env() and attr() are not allowed in an initial value` };
|
|
2794
|
+
let kept = rule;
|
|
2541
2795
|
if (syntax !== "*") {
|
|
2542
|
-
const
|
|
2796
|
+
const unknown = syntax.split("|").map((component) => ({
|
|
2543
2797
|
text: component.trim(),
|
|
2544
2798
|
match: SYNTAX_COMPONENT.exec(component.trim())
|
|
2545
|
-
}));
|
|
2546
|
-
|
|
2547
|
-
if (
|
|
2548
|
-
if (initialValue
|
|
2549
|
-
const
|
|
2550
|
-
|
|
2551
|
-
if (
|
|
2799
|
+
})).find(({ match }) => match === null || match[1] !== void 0 && !SYNTAX_TYPES.has(match[1]));
|
|
2800
|
+
if (unknown !== void 0) return { reason: `syntax "${syntax}" has the unknown component "${unknown.text}"` };
|
|
2801
|
+
if (initialValue === void 0) return { reason: `syntax "${syntax}" needs an initialValue` };
|
|
2802
|
+
if ((0, _animus_ui_properties.dependsOnContext)(initialValue)) return { reason: `initialValue "${initialValue}" depends on context; font- and container-relative units are not allowed` };
|
|
2803
|
+
const folded = (0, _animus_ui_properties.foldInitialValue)(syntax, initialValue);
|
|
2804
|
+
if (folded !== void 0 && !folded.accepted) return { reason: `initialValue "${initialValue}" computes to the ${folded.type} ${folded.text}, which syntax "${syntax}" does not accept` };
|
|
2805
|
+
if (folded !== void 0) kept = rule.replace(INITIAL_VALUE, `$1${folded.text}`);
|
|
2552
2806
|
}
|
|
2553
2807
|
try {
|
|
2554
2808
|
(0, lightningcss.transform)({
|
|
2555
2809
|
filename: "animus-registrations.css",
|
|
2556
|
-
code: Buffer.from(
|
|
2810
|
+
code: Buffer.from(kept)
|
|
2557
2811
|
});
|
|
2558
|
-
return
|
|
2812
|
+
return { rule: kept };
|
|
2559
2813
|
} catch (error) {
|
|
2560
2814
|
const detail = error instanceof Error ? error.message : String(error);
|
|
2561
|
-
return `the CSS parser
|
|
2815
|
+
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` };
|
|
2816
|
+
return { reason: `the CSS parser rejects \`${kept.trim()}\` (${detail})` };
|
|
2562
2817
|
}
|
|
2563
2818
|
}
|
|
2564
2819
|
//#endregion
|
|
2565
2820
|
//#region pipeline/property-diagnostics.ts
|
|
2566
2821
|
/**
|
|
2567
2822
|
* Reports custom properties that will not behave as their CSS suggests: a
|
|
2568
|
-
* property that refers to itself
|
|
2569
|
-
*
|
|
2570
|
-
*
|
|
2823
|
+
* property that refers to itself, a fallback a registered initial value
|
|
2824
|
+
* applies ahead of, and an animated property that cannot interpolate. Names
|
|
2825
|
+
* are read as CSS tokenizes them, so escapes, function case, comments and
|
|
2826
|
+
* quoted text match the prefix pass. The CSS itself is never changed.
|
|
2571
2827
|
*/
|
|
2572
2828
|
function checkCustomProperties(input) {
|
|
2573
2829
|
const registered = propertyRegistrations(input.system.variableCss);
|
|
2574
|
-
const declared =
|
|
2830
|
+
const declared = new Set(input.system.contextualProperties ?? []);
|
|
2575
2831
|
const findings = [];
|
|
2576
2832
|
const sheets = [{
|
|
2577
2833
|
css: input.componentCss,
|
|
@@ -2581,6 +2837,7 @@ function checkCustomProperties(input) {
|
|
|
2581
2837
|
owner: globalOwner
|
|
2582
2838
|
}];
|
|
2583
2839
|
for (const { css, owner } of sheets) {
|
|
2840
|
+
if (!/var\(|transition|@keyframes/i.test(css)) continue;
|
|
2584
2841
|
const parsed = declarations(css);
|
|
2585
2842
|
const discreteRules = new Set(parsed.filter((d) => d.property === "transition-behavior" && /\ballow-discrete\b/.test(d.value)).map((d) => d.rule));
|
|
2586
2843
|
for (const declaration of parsed) {
|
|
@@ -2594,29 +2851,23 @@ function checkCustomProperties(input) {
|
|
|
2594
2851
|
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`);
|
|
2595
2852
|
continue;
|
|
2596
2853
|
}
|
|
2597
|
-
if (
|
|
2854
|
+
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`);
|
|
2598
2855
|
for (const read of varReads(declaration.value)) {
|
|
2599
2856
|
const registration = registered.get(read.name);
|
|
2600
2857
|
if (read.fallback === void 0 || registration?.initialValue === void 0) continue;
|
|
2601
2858
|
const chain = read.fallback.startsWith("var(");
|
|
2602
|
-
found(chain ? PROPERTY_FALLBACK_CHAIN_SUPPRESSED : PROPERTY_FALLBACK_SUPPRESSED, read.name,
|
|
2859
|
+
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`);
|
|
2603
2860
|
}
|
|
2604
2861
|
for (const animated of animatedProperties(declaration, discreteRules)) {
|
|
2605
|
-
if (!declared.has(animated.name)) continue;
|
|
2862
|
+
if (animated.discrete || !declared.has(animated.name)) continue;
|
|
2606
2863
|
const reason = interpolationGap(registered.get(animated.name));
|
|
2607
2864
|
if (reason === null) continue;
|
|
2608
|
-
|
|
2609
|
-
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`);
|
|
2865
|
+
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`);
|
|
2610
2866
|
}
|
|
2611
2867
|
}
|
|
2612
2868
|
}
|
|
2613
2869
|
return toDiagnostics(findings);
|
|
2614
2870
|
}
|
|
2615
|
-
function declaredProperties(contextualVarsJson) {
|
|
2616
|
-
if (!contextualVarsJson) return /* @__PURE__ */ new Set();
|
|
2617
|
-
const byScale = parseInternalWire(contextualVarsJson, "contextualVarsJson (the theme's contextual variable names)");
|
|
2618
|
-
return new Set(Object.values(byScale).flatMap((names) => names.map((name) => `--${name}`)));
|
|
2619
|
-
}
|
|
2620
2871
|
/** Declarations in engine-emitted CSS: one `property: value` per `;`. */
|
|
2621
2872
|
function declarations(css) {
|
|
2622
2873
|
const found = [];
|
|
@@ -2667,51 +2918,134 @@ function declarations(css) {
|
|
|
2667
2918
|
}
|
|
2668
2919
|
return found;
|
|
2669
2920
|
}
|
|
2670
|
-
|
|
2671
|
-
|
|
2921
|
+
/** `text` as a whole custom-property name, escapes decoded; `undefined`
|
|
2922
|
+
* when it is not one. */
|
|
2923
|
+
function customPropertyName(text) {
|
|
2924
|
+
if (!text.startsWith("--")) return void 0;
|
|
2925
|
+
const { end, name } = readCustomPropertyName(text, 2);
|
|
2926
|
+
return end === text.length ? `--${name}` : void 0;
|
|
2672
2927
|
}
|
|
2673
|
-
|
|
2928
|
+
function isSelfReference(declaration) {
|
|
2929
|
+
const property = customPropertyName(declaration.property);
|
|
2930
|
+
const reads = varReads(declaration.value);
|
|
2931
|
+
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();
|
|
2932
|
+
}
|
|
2933
|
+
/** Whether every read of the declared property sits inside another read's
|
|
2934
|
+
* fallback, as in `--a: var(--b, var(--a))`. */
|
|
2935
|
+
function readsItselfOnlyInFallback(declaration) {
|
|
2936
|
+
const property = customPropertyName(declaration.property);
|
|
2937
|
+
if (property === void 0) return false;
|
|
2938
|
+
const reads = varReads(declaration.value);
|
|
2939
|
+
const own = reads.filter((read) => read.name === property);
|
|
2940
|
+
return own.length > 0 && own.every((read) => reads.some((outer) => outer !== read && outer.start < read.start && read.end <= outer.end));
|
|
2941
|
+
}
|
|
2942
|
+
/** Every `var()` read in `value`, nested fallbacks included, in any case
|
|
2943
|
+
* and spacing. Quoted text and comments hold none. */
|
|
2674
2944
|
function varReads(value) {
|
|
2675
2945
|
const reads = [];
|
|
2676
|
-
for (
|
|
2677
|
-
const
|
|
2678
|
-
if (
|
|
2679
|
-
|
|
2680
|
-
name: match[1],
|
|
2681
|
-
fallback: void 0
|
|
2682
|
-
});
|
|
2946
|
+
for (let at = 0; at < value.length;) {
|
|
2947
|
+
const c = value[at];
|
|
2948
|
+
if (c === "\"" || c === "'") {
|
|
2949
|
+
at = closingQuote(value, at);
|
|
2683
2950
|
continue;
|
|
2684
2951
|
}
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2952
|
+
if (value.startsWith("/*", at)) {
|
|
2953
|
+
at = skipSpace(value, at);
|
|
2954
|
+
continue;
|
|
2955
|
+
}
|
|
2956
|
+
if (value.slice(at, at + 4).toLowerCase() !== "var(" || /[\w-]/.test(value[at - 1] ?? "")) {
|
|
2957
|
+
at += 1;
|
|
2958
|
+
continue;
|
|
2959
|
+
}
|
|
2960
|
+
const nameAt = skipSpace(value, at + 4);
|
|
2961
|
+
if (!value.startsWith("--", nameAt)) {
|
|
2962
|
+
at += 4;
|
|
2963
|
+
continue;
|
|
2964
|
+
}
|
|
2965
|
+
const { end: nameEnd, name } = readCustomPropertyName(value, nameAt + 2);
|
|
2966
|
+
const after = skipSpace(value, nameEnd);
|
|
2967
|
+
const close = closingParen(value, at + 4);
|
|
2689
2968
|
reads.push({
|
|
2690
|
-
name:
|
|
2691
|
-
fallback: value.slice(after + 1, close - 1).trim()
|
|
2969
|
+
name: `--${name}`,
|
|
2970
|
+
fallback: value[after] === "," ? value.slice(after + 1, close - 1).trim() : void 0,
|
|
2971
|
+
start: at,
|
|
2972
|
+
end: close
|
|
2692
2973
|
});
|
|
2974
|
+
at = after;
|
|
2693
2975
|
}
|
|
2694
2976
|
return reads;
|
|
2695
2977
|
}
|
|
2978
|
+
/** The index after the `)` that closes the call whose arguments start at
|
|
2979
|
+
* `from`. Quoted text and comments are skipped. */
|
|
2980
|
+
function closingParen(value, from) {
|
|
2981
|
+
let depth = 1;
|
|
2982
|
+
let at = from;
|
|
2983
|
+
while (at < value.length && depth > 0) {
|
|
2984
|
+
const c = value[at];
|
|
2985
|
+
if (c === "\"" || c === "'") at = closingQuote(value, at);
|
|
2986
|
+
else if (value.startsWith("/*", at)) at = skipSpace(value, at);
|
|
2987
|
+
else {
|
|
2988
|
+
if (c === "(") depth += 1;
|
|
2989
|
+
else if (c === ")") depth -= 1;
|
|
2990
|
+
at += 1;
|
|
2991
|
+
}
|
|
2992
|
+
}
|
|
2993
|
+
return at;
|
|
2994
|
+
}
|
|
2995
|
+
function closingQuote(value, open) {
|
|
2996
|
+
let at = open + 1;
|
|
2997
|
+
while (at < value.length && value[at] !== value[open]) at += value[at] === "\\" ? 2 : 1;
|
|
2998
|
+
return Math.min(at + 1, value.length);
|
|
2999
|
+
}
|
|
3000
|
+
/** The index after CSS whitespace and comments starting at `from`. */
|
|
3001
|
+
function skipSpace(value, from) {
|
|
3002
|
+
let at = from;
|
|
3003
|
+
for (;;) {
|
|
3004
|
+
while (/[ \t\n\r\f]/.test(value[at] ?? "")) at += 1;
|
|
3005
|
+
if (!value.startsWith("/*", at)) return at;
|
|
3006
|
+
const close = value.indexOf("*/", at + 2);
|
|
3007
|
+
at = close === -1 ? value.length : close + 2;
|
|
3008
|
+
}
|
|
3009
|
+
}
|
|
3010
|
+
function withoutComments(text) {
|
|
3011
|
+
return text.replace(/\/\*[\s\S]*?\*\//g, "");
|
|
3012
|
+
}
|
|
2696
3013
|
/** Custom properties this declaration sets in a keyframe or names in a
|
|
2697
3014
|
* transition. */
|
|
2698
3015
|
function animatedProperties(declaration, discreteRules) {
|
|
2699
3016
|
const keyframes = declaration.context.find((prelude) => prelude.startsWith("@keyframes"));
|
|
2700
|
-
|
|
2701
|
-
|
|
3017
|
+
const set = customPropertyName(declaration.property);
|
|
3018
|
+
if (keyframes !== void 0 && set !== void 0) return [{
|
|
3019
|
+
name: set,
|
|
2702
3020
|
how: `set in ${keyframes}`,
|
|
2703
3021
|
discrete: false
|
|
2704
3022
|
}];
|
|
2705
3023
|
if (declaration.property !== "transition" && declaration.property !== "transition-property") return [];
|
|
2706
3024
|
return topLevelItems(declaration.value).flatMap((item) => {
|
|
2707
|
-
const property = item.
|
|
2708
|
-
return property
|
|
3025
|
+
const property = topLevelTokens(item.replace(/\/\*[\s\S]*?\*\//g, " ")).map(customPropertyName).find((name) => name !== void 0);
|
|
3026
|
+
return property !== void 0 ? [{
|
|
2709
3027
|
name: property,
|
|
2710
3028
|
how: "transitioned",
|
|
2711
3029
|
discrete: /\ballow-discrete\b/.test(item) || discreteRules.has(declaration.rule)
|
|
2712
3030
|
}] : [];
|
|
2713
3031
|
});
|
|
2714
3032
|
}
|
|
3033
|
+
/** The whitespace-separated tokens of `item` outside any parentheses. */
|
|
3034
|
+
function topLevelTokens(item) {
|
|
3035
|
+
const tokens = [];
|
|
3036
|
+
let depth = 0;
|
|
3037
|
+
let token = "";
|
|
3038
|
+
for (const c of item) {
|
|
3039
|
+
if (c === "(") depth += 1;
|
|
3040
|
+
else if (c === ")") depth -= 1;
|
|
3041
|
+
if (depth === 0 && /\s/.test(c)) {
|
|
3042
|
+
if (token) tokens.push(token);
|
|
3043
|
+
token = "";
|
|
3044
|
+
} else token += c;
|
|
3045
|
+
}
|
|
3046
|
+
if (token) tokens.push(token);
|
|
3047
|
+
return tokens;
|
|
3048
|
+
}
|
|
2715
3049
|
function topLevelItems(value) {
|
|
2716
3050
|
const items = [];
|
|
2717
3051
|
let depth = 0;
|
|
@@ -2732,7 +3066,7 @@ function interpolationGap(registration) {
|
|
|
2732
3066
|
return null;
|
|
2733
3067
|
}
|
|
2734
3068
|
function componentOwner(manifest) {
|
|
2735
|
-
const components = Object.values(manifest.components);
|
|
3069
|
+
const components = Object.values(manifest.components ?? {});
|
|
2736
3070
|
return (declaration) => {
|
|
2737
3071
|
const selector = [...declaration.context].reverse().find((prelude) => !prelude.startsWith("@"));
|
|
2738
3072
|
const classes = (selector ?? "").match(/\.-?[_a-zA-Z][\w-]*/g) ?? [];
|
|
@@ -2854,12 +3188,33 @@ function buildAnalysisInputs(opts) {
|
|
|
2854
3188
|
externalDirsJson: opts.externalDirs?.length && hasSourceThemeManifests(opts.system) ? JSON.stringify(opts.externalDirs) : null
|
|
2855
3189
|
};
|
|
2856
3190
|
if (opts.system.declarationScalesJson) inputs.declarationScalesJson = opts.system.declarationScalesJson;
|
|
3191
|
+
const context = opts.analysisContext;
|
|
3192
|
+
if (context?.skippedSources?.length || context?.unbundledComputedImports || context?.packageDirs?.length) inputs.analysisContextJson = JSON.stringify({
|
|
3193
|
+
skippedSources: context.skippedSources ?? [],
|
|
3194
|
+
unbundledComputedImports: context.unbundledComputedImports ?? false,
|
|
3195
|
+
packageDirs: context.packageDirs ?? []
|
|
3196
|
+
});
|
|
2857
3197
|
return inputs;
|
|
2858
3198
|
}
|
|
2859
3199
|
function hasSourceThemeManifests(system) {
|
|
2860
3200
|
const json = system.sourceThemeManifestsJson ?? "";
|
|
2861
3201
|
return json.length > 0 && json !== "{}";
|
|
2862
3202
|
}
|
|
3203
|
+
/** Systems whose load diagnostics were surfaced: each loaded system reports
|
|
3204
|
+
* them once, not again on every analysis and hot update. The set is per
|
|
3205
|
+
* process. Next's webpack compilers share one analysis in one process, but
|
|
3206
|
+
* its build worker and Turbopack builds analyze in two processes, which
|
|
3207
|
+
* repeat every diagnostic alike, component ones included. */
|
|
3208
|
+
const surfacedSystems = /* @__PURE__ */ new WeakSet();
|
|
3209
|
+
/** The loaded system's own diagnostics, the first time it is analyzed. A
|
|
3210
|
+
* strict error keeps failing every analysis until the system changes. */
|
|
3211
|
+
function systemDiagnostics(system, strict) {
|
|
3212
|
+
const diagnostics = [...collectSelectorAliasDiagnostics(system.selectorAliasesJson), ...systemLoadDiagnostics(system)];
|
|
3213
|
+
const blocking = strict === true && diagnostics.some((d) => d.severity === "error");
|
|
3214
|
+
if (surfacedSystems.has(system) && !blocking) return [];
|
|
3215
|
+
surfacedSystems.add(system);
|
|
3216
|
+
return diagnostics;
|
|
3217
|
+
}
|
|
2863
3218
|
/**
|
|
2864
3219
|
* The one analysis invocation both plugins share. Error handling stays at
|
|
2865
3220
|
* the call site (strict-mode throw vs warn).
|
|
@@ -2874,18 +3229,20 @@ function runProjectAnalysis(engineApi, opts) {
|
|
|
2874
3229
|
const extractMs = Math.round(performance.now() - t);
|
|
2875
3230
|
t = performance.now();
|
|
2876
3231
|
const manifest = JSON.parse(manifestJson);
|
|
3232
|
+
const systemErrors = systemLoadDiagnostics(opts.system).filter((diagnostic) => diagnostic.kind === "error");
|
|
3233
|
+
if (systemErrors.length > 0) manifest.diagnostics = [...systemErrors, ...manifest.diagnostics ?? []];
|
|
3234
|
+
const componentCss = applyUnitFallback(manifest.css);
|
|
2877
3235
|
const propertyDiagnostics = checkCustomProperties({
|
|
2878
3236
|
system: opts.system,
|
|
2879
3237
|
manifest,
|
|
2880
|
-
componentCss
|
|
3238
|
+
componentCss,
|
|
2881
3239
|
globalCss: manifest.sheets.global
|
|
2882
3240
|
});
|
|
2883
3241
|
surfaceManifestDiagnostics(manifest, opts.warn, {
|
|
2884
3242
|
strict: opts.strict,
|
|
2885
3243
|
info: opts.info,
|
|
2886
3244
|
prepend: [
|
|
2887
|
-
...
|
|
2888
|
-
...systemLoadDiagnostics(opts.system),
|
|
3245
|
+
...systemDiagnostics(opts.system, opts.strict),
|
|
2889
3246
|
...propertyDiagnostics,
|
|
2890
3247
|
...opts.extraDiagnostics ?? []
|
|
2891
3248
|
]
|
|
@@ -2895,7 +3252,7 @@ function runProjectAnalysis(engineApi, opts) {
|
|
|
2895
3252
|
manifest,
|
|
2896
3253
|
manifestJson,
|
|
2897
3254
|
globalCss: manifest.sheets.global,
|
|
2898
|
-
componentCss
|
|
3255
|
+
componentCss,
|
|
2899
3256
|
inputs,
|
|
2900
3257
|
timings: {
|
|
2901
3258
|
serializeMs,
|
|
@@ -2980,6 +3337,61 @@ function applyPrefix(prefix, variableMapJson, variableCss, themeJson, contextual
|
|
|
2980
3337
|
}
|
|
2981
3338
|
return result;
|
|
2982
3339
|
}
|
|
3340
|
+
/** The runtime's transport variables are `--animus-<prop>`. */
|
|
3341
|
+
const TRANSPORT_NAMESPACE = "animus-";
|
|
3342
|
+
/**
|
|
3343
|
+
* The prefix applied through one map: every variable the theme defines and
|
|
3344
|
+
* every declared contextual variable takes `--${prefix}-${name}`, each
|
|
3345
|
+
* reference renamed once. Each contextual entry carries its declared and
|
|
3346
|
+
* final name, so lookups run on the declared identity and the extractor
|
|
3347
|
+
* emits the final name.
|
|
3348
|
+
*/
|
|
3349
|
+
function applyPropertyNames(prefix, artifacts) {
|
|
3350
|
+
const byScale = artifacts.contextualVarsJson ? parseInternalWire(artifacts.contextualVarsJson, "contextualVarsJson (the theme's contextual variable names)") : null;
|
|
3351
|
+
const declared = Object.values(byScale ?? {}).flat();
|
|
3352
|
+
const variableMap = parseInternalWire(artifacts.variableMapJson, "variableMapJson (the theme's token variables)");
|
|
3353
|
+
const managed = createPropertyNames(declared, prefix, [...Object.values(variableMap).map((name) => name.replace(/^--/, "")), ...[...artifacts.variableCss.matchAll(THEME_DEFINITION)].map((m) => m[1])]);
|
|
3354
|
+
const nameConflicts = [...[...new Set(declared)].flatMap((name) => {
|
|
3355
|
+
const final = managed.finalName(name) ?? name;
|
|
3356
|
+
return final.startsWith(TRANSPORT_NAMESPACE) ? [{
|
|
3357
|
+
name,
|
|
3358
|
+
final,
|
|
3359
|
+
reason: "transport"
|
|
3360
|
+
}] : [];
|
|
3361
|
+
}), ...managed.ambiguous().map(({ name, contextual }) => ({
|
|
3362
|
+
name,
|
|
3363
|
+
final: contextual,
|
|
3364
|
+
reason: "theme-variable"
|
|
3365
|
+
}))];
|
|
3366
|
+
const rename = (value) => renameCustomProperties(value, managed);
|
|
3367
|
+
return {
|
|
3368
|
+
variableMapJson: JSON.stringify(Object.fromEntries(Object.entries(variableMap).map(([key, name]) => [key, rename(name)]))),
|
|
3369
|
+
variableCss: rename(artifacts.variableCss),
|
|
3370
|
+
themeJson: renameTokenValues(artifacts.themeJson, rename),
|
|
3371
|
+
contextualVarsJson: byScale === null ? null : JSON.stringify(Object.fromEntries(Object.entries(byScale).map(([scale, names]) => [scale, names.map((name) => ({
|
|
3372
|
+
name,
|
|
3373
|
+
var: managed.finalName(name) ?? name
|
|
3374
|
+
}))]))),
|
|
3375
|
+
declarationScalesJson: artifacts.declarationScalesJson === null ? null : renameDeclarationRecords(artifacts.declarationScalesJson, rename),
|
|
3376
|
+
nameConflicts,
|
|
3377
|
+
contextualProperties: [...new Set(declared)].map((name) => `--${managed.finalName(name) ?? name}`)
|
|
3378
|
+
};
|
|
3379
|
+
}
|
|
3380
|
+
/** A custom property the theme's CSS defines or registers. */
|
|
3381
|
+
const THEME_DEFINITION = /(?:@property\s+|(?:^|[\s;{]))--([\w-]+)(?=\s*[:{])/g;
|
|
3382
|
+
/** Renames inside the token map's values. */
|
|
3383
|
+
function renameTokenValues(themeJson, rename) {
|
|
3384
|
+
const tokens = parseInternalWire(themeJson, "themeJson (the theme's flattened token values)");
|
|
3385
|
+
return JSON.stringify(Object.fromEntries(Object.entries(tokens).map(([path, value]) => [path, rename(value)])));
|
|
3386
|
+
}
|
|
3387
|
+
/** Renames inside every declaration-scale record value. */
|
|
3388
|
+
function renameDeclarationRecords(declarationScalesJson, rename) {
|
|
3389
|
+
const scales = parseInternalWire(declarationScalesJson, "declarationScalesJson (the theme's declaration scales)");
|
|
3390
|
+
return JSON.stringify(Object.fromEntries(Object.entries(scales).map(([name, scale]) => [name, {
|
|
3391
|
+
...scale,
|
|
3392
|
+
values: Object.fromEntries(Object.entries(scale.values).map(([key, record]) => [key, Object.fromEntries(Object.entries(record).map(([property, value]) => [property, rename(value)]))]))
|
|
3393
|
+
}])));
|
|
3394
|
+
}
|
|
2983
3395
|
//#endregion
|
|
2984
3396
|
//#region pipeline/system-config.ts
|
|
2985
3397
|
/**
|
|
@@ -2995,7 +3407,28 @@ function loadSystemConfig(engineApi, opts) {
|
|
|
2995
3407
|
let variableCss = registrations.css;
|
|
2996
3408
|
let contextualVarsJson = config.contextualVarsJson || null;
|
|
2997
3409
|
let declarationScalesJson = config.declarationScalesJson || null;
|
|
2998
|
-
|
|
3410
|
+
const declaredNames = Object.values(parseInternalWire(contextualVarsJson ?? "{}", "contextualVarsJson (the theme's contextual variable names)")).flat();
|
|
3411
|
+
let contextualProperties = declaredNames.map((name) => `--${name}`);
|
|
3412
|
+
let legacyPrefixedContextualVars = [];
|
|
3413
|
+
let prefixNameConflicts = [];
|
|
3414
|
+
if (opts.prefix && opts.prefixContextualVars) {
|
|
3415
|
+
const resolved = applyPropertyNames(opts.prefix, {
|
|
3416
|
+
variableMapJson,
|
|
3417
|
+
variableCss,
|
|
3418
|
+
themeJson: scalesJson,
|
|
3419
|
+
contextualVarsJson,
|
|
3420
|
+
declarationScalesJson
|
|
3421
|
+
});
|
|
3422
|
+
variableMapJson = resolved.variableMapJson;
|
|
3423
|
+
variableCss = resolved.variableCss;
|
|
3424
|
+
scalesJson = resolved.themeJson;
|
|
3425
|
+
contextualVarsJson = resolved.contextualVarsJson;
|
|
3426
|
+
contextualProperties = resolved.contextualProperties;
|
|
3427
|
+
prefixNameConflicts = resolved.nameConflicts;
|
|
3428
|
+
declarationScalesJson = resolved.declarationScalesJson;
|
|
3429
|
+
} else if (opts.prefix) {
|
|
3430
|
+
legacyPrefixedContextualVars = declaredNames;
|
|
3431
|
+
contextualProperties = declaredNames.map((name) => `--${opts.prefix}-${name}`);
|
|
2999
3432
|
const prefixed = applyPrefix(opts.prefix, variableMapJson, variableCss, scalesJson, contextualVarsJson || void 0);
|
|
3000
3433
|
variableMapJson = prefixed.variableMapJson;
|
|
3001
3434
|
variableCss = prefixed.variableCss;
|
|
@@ -3010,6 +3443,7 @@ function loadSystemConfig(engineApi, opts) {
|
|
|
3010
3443
|
variableMapJson,
|
|
3011
3444
|
variableCss,
|
|
3012
3445
|
contextualVarsJson,
|
|
3446
|
+
contextualProperties: [...new Set(contextualProperties)],
|
|
3013
3447
|
selectorAliasesJson: config.selectorAliases || null,
|
|
3014
3448
|
conditionAliasesJson: config.conditionAliases || null,
|
|
3015
3449
|
transformSourcesJson: config.transformSources || null,
|
|
@@ -3023,6 +3457,8 @@ function loadSystemConfig(engineApi, opts) {
|
|
|
3023
3457
|
if (declarationScalesJson) system.declarationScalesJson = declarationScalesJson;
|
|
3024
3458
|
if (config.propertyRecordsJson) system.propertyRecordsJson = config.propertyRecordsJson;
|
|
3025
3459
|
if (registrations.invalid.length > 0) system.invalidPropertyRegistrations = registrations.invalid;
|
|
3460
|
+
if (prefixNameConflicts.length > 0) system.prefixNameConflicts = prefixNameConflicts;
|
|
3461
|
+
if (legacyPrefixedContextualVars.length > 0) system.legacyPrefixedContextualVars = [...new Set(legacyPrefixedContextualVars)];
|
|
3026
3462
|
return system;
|
|
3027
3463
|
}
|
|
3028
3464
|
//#endregion
|
|
@@ -3076,6 +3512,7 @@ function buildDynamicPropConfig(dynamicProps) {
|
|
|
3076
3512
|
if (meta.negative) entry.negative = true;
|
|
3077
3513
|
if (meta.strict) entry.strict = true;
|
|
3078
3514
|
if (meta.keywords?.length) entry.keywords = meta.keywords;
|
|
3515
|
+
if (meta.currentVar) entry.currentVar = meta.currentVar;
|
|
3079
3516
|
configEntries[propName] = entry;
|
|
3080
3517
|
}
|
|
3081
3518
|
return configEntries;
|
|
@@ -3630,6 +4067,12 @@ Object.defineProperty(exports, "firstOwners", {
|
|
|
3630
4067
|
return firstOwners;
|
|
3631
4068
|
}
|
|
3632
4069
|
});
|
|
4070
|
+
Object.defineProperty(exports, "generatedModuleCode", {
|
|
4071
|
+
enumerable: true,
|
|
4072
|
+
get: function() {
|
|
4073
|
+
return generatedModuleCode;
|
|
4074
|
+
}
|
|
4075
|
+
});
|
|
3633
4076
|
Object.defineProperty(exports, "hashReplacementPlans", {
|
|
3634
4077
|
enumerable: true,
|
|
3635
4078
|
get: function() {
|
|
@@ -3648,6 +4091,12 @@ Object.defineProperty(exports, "isAdvisorySourceDiagnostic", {
|
|
|
3648
4091
|
return isAdvisorySourceDiagnostic;
|
|
3649
4092
|
}
|
|
3650
4093
|
});
|
|
4094
|
+
Object.defineProperty(exports, "isDeletedSource", {
|
|
4095
|
+
enumerable: true,
|
|
4096
|
+
get: function() {
|
|
4097
|
+
return isDeletedSource;
|
|
4098
|
+
}
|
|
4099
|
+
});
|
|
3651
4100
|
Object.defineProperty(exports, "isEngineTransformExtension", {
|
|
3652
4101
|
enumerable: true,
|
|
3653
4102
|
get: function() {
|