@animus-ui/extract 0.1.20 → 0.1.22

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.
Files changed (41) hide show
  1. package/crates/extract-v2/animus-extract-v2.darwin-arm64.node +0 -0
  2. package/crates/extract-v2/animus-extract-v2.linux-arm64-gnu.node +0 -0
  3. package/crates/extract-v2/animus-extract-v2.linux-x64-gnu.node +0 -0
  4. package/crates/extract-v2/index.d.ts +7 -0
  5. package/dist/index.cjs +3 -1
  6. package/dist/index.mjs +2 -2
  7. package/dist/pipeline/assemble-stylesheet.d.ts +5 -0
  8. package/dist/pipeline/assemble-stylesheet.d.ts.map +1 -1
  9. package/dist/pipeline/core-options.d.ts +2 -0
  10. package/dist/pipeline/core-options.d.ts.map +1 -1
  11. package/dist/pipeline/discover-packages.d.ts +15 -1
  12. package/dist/pipeline/discover-packages.d.ts.map +1 -1
  13. package/dist/pipeline/index.d.ts +3 -3
  14. package/dist/pipeline/index.d.ts.map +1 -1
  15. package/dist/pipeline/manifest-diagnostics.d.ts +18 -2
  16. package/dist/pipeline/manifest-diagnostics.d.ts.map +1 -1
  17. package/dist/pipeline/manifest-schema.d.ts +6 -0
  18. package/dist/pipeline/manifest-schema.d.ts.map +1 -1
  19. package/dist/pipeline/post-process-css.d.ts.map +1 -1
  20. package/dist/pipeline/property-diagnostics.d.ts +5 -4
  21. package/dist/pipeline/property-diagnostics.d.ts.map +1 -1
  22. package/dist/pipeline/property-names.d.ts +4 -14
  23. package/dist/pipeline/property-names.d.ts.map +1 -1
  24. package/dist/pipeline/property-registrations.d.ts +5 -4
  25. package/dist/pipeline/property-registrations.d.ts.map +1 -1
  26. package/dist/pipeline/structural-self-check.d.ts +3 -0
  27. package/dist/pipeline/structural-self-check.d.ts.map +1 -1
  28. package/dist/pipeline/tsconfig-paths.d.ts +14 -0
  29. package/dist/pipeline/tsconfig-paths.d.ts.map +1 -1
  30. package/dist/{pipeline-6wmD00rr.mjs → pipeline-BXY7Pf_D.mjs} +846 -528
  31. package/dist/{pipeline-A54wEQEg.cjs → pipeline-D7B1z0-1.cjs} +875 -527
  32. package/dist/session/extraction-session.d.ts +5 -0
  33. package/dist/session/extraction-session.d.ts.map +1 -1
  34. package/dist/session/index.d.ts +1 -1
  35. package/dist/session/index.d.ts.map +1 -1
  36. package/dist/session/resolve-packages.d.ts.map +1 -1
  37. package/dist/session/singleton.d.ts +5 -1
  38. package/dist/session/singleton.d.ts.map +1 -1
  39. package/dist/session.cjs +90 -3
  40. package/dist/session.mjs +91 -5
  41. package/package.json +4 -4
@@ -2,16 +2,21 @@ import { createRequire } from "node:module";
2
2
  import path, { basename, dirname, join, normalize, posix } from "node:path";
3
3
  import { createHash } from "crypto";
4
4
  import { existsSync, readFileSync, readdirSync, realpathSync, statSync } from "fs";
5
- import { dirname as dirname$1, extname, isAbsolute, join as join$1, relative, resolve } from "path";
5
+ import { dirname as dirname$1, extname, isAbsolute, join as join$1, relative, resolve, sep } from "path";
6
6
  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 { dependsOnContext, foldInitialValue, isUnitlessProperty, substitutesValue } from "@animus-ui/properties";
10
+ import { componentValues, dependsOnContext, foldInitialValue, identifierAt, isSpace, isUnitlessProperty, someValue, substitutesValue, tokenize, variableReads } from "@animus-ui/properties";
11
11
  //#region \0rolldown/runtime.js
12
12
  var __require = /* #__PURE__ */ (() => createRequire(import.meta.url))();
13
13
  //#endregion
14
14
  //#region pipeline/assemble-stylesheet.ts
15
+ /**
16
+ * The cascade layer order, outermost first: its one definition. The engine
17
+ * reads it from `crates/extract-v2/src/layer_order.json`, which
18
+ * `scripts/layer-order.ts --write` writes from here.
19
+ */
15
20
  const ANIMUS_LAYERS = [
16
21
  "anm-global",
17
22
  "anm-base",
@@ -159,6 +164,8 @@ function collectSelectorAliasDiagnostics(selectorAliasesJson) {
159
164
  return diagnostics;
160
165
  }
161
166
  const UNREADABLE_SOURCE_FILE = "animus.ingestion.unreadable-source-file";
167
+ /** A kit the system extends resolved, yet discovery found none of its files. */
168
+ const NO_KIT_FILES = "animus.discovery.no-kit-files";
162
169
  /** What a skipped source file costs: the analysis never sees what it
163
170
  * renders. */
164
171
  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";
@@ -182,6 +189,18 @@ function unreadableSourceDiagnostic(relPath, error) {
182
189
  severity: severityFor(UNREADABLE_SOURCE_FILE)
183
190
  };
184
191
  }
192
+ /** One warning per kit the system extends that resolved, yet yielded no
193
+ * files: none of its components are extracted. */
194
+ function noKitFilesDiagnostics(outcomes) {
195
+ return outcomes.filter((record) => record.outcome === "empty").map((record) => ({
196
+ file: record.specifier,
197
+ component: "kit",
198
+ kind: "warn",
199
+ message: "resolved, but discovery found none of its files, so none of its components are extracted — check that the package ships its src/ directory or a readable entry",
200
+ code: NO_KIT_FILES,
201
+ severity: severityFor(NO_KIT_FILES)
202
+ }));
203
+ }
185
204
  /** The collision entry code minted by the system package's merge. */
186
205
  const VOCABULARY_COLLISION = "animus.vocabulary.collision";
187
206
  /** A theme `@property` registration browsers would ignore; it is not emitted. */
@@ -201,17 +220,28 @@ const PROPERTY_UNREGISTERED_ANIMATION = "animus.property.unregistered-animation"
201
220
  * it invalid at computed-value time. */
202
221
  const PROPERTY_SELF_REFERENCE = "animus.property.self-reference";
203
222
  /** A custom property that reads itself only inside another `var()`'s
204
- * fallback, so whether it is cyclic depends on the browser. */
223
+ * fallback: cyclic wherever that fallback is used. */
205
224
  const PROPERTY_FALLBACK_SELF_REFERENCE = "animus.property.fallback-self-reference";
225
+ /** Custom properties that `currentVar` writes in one rule, each reading
226
+ * another directly, so every property in the cycle is invalid at
227
+ * computed-value time. */
228
+ const PROPERTY_CURRENT_VAR_CYCLE = "animus.property.current-var-cycle";
206
229
  /** A prefix renamed contextual variables without `prefixContextualVars`, so
207
230
  * their declared names no longer resolve. */
208
231
  const PREFIX_CONTEXTUAL_VARS_UNPREFIXED = "animus.prefix.contextual-vars-unprefixed";
209
232
  /** Under `prefixContextualVars`, a final name that collides with a runtime
210
233
  * transport variable or a theme variable of the same spelling. */
211
234
  const PREFIX_NAME_CONFLICT = "animus.prefix.name-conflict";
235
+ /** A contextual variable declared on a scale that also holds a token of its
236
+ * name: scale reads resolve to the token. */
237
+ const PROPERTY_LEGACY_TOKEN_COLLISION = "animus.property.legacy-token-collision";
238
+ /** A theme property declared on a scale that also holds a token of its
239
+ * name. */
240
+ const PROPERTY_TOKEN_COLLISION = "animus.property.token-collision";
212
241
  const DIAGNOSTIC_SEVERITY = /* @__PURE__ */ new Map([
213
242
  [SELECTOR_UNSUPPORTED_SUBJECT, "error"],
214
243
  [UNREADABLE_SOURCE_FILE, "error"],
244
+ [NO_KIT_FILES, "warn"],
215
245
  [VOCABULARY_COLLISION, "warn"],
216
246
  [VOCABULARY_LEGACY_VERB, "warn"],
217
247
  [INVALID_PROPERTY_REGISTRATION, "error"],
@@ -220,8 +250,11 @@ const DIAGNOSTIC_SEVERITY = /* @__PURE__ */ new Map([
220
250
  [PROPERTY_UNREGISTERED_ANIMATION, "info"],
221
251
  [PROPERTY_SELF_REFERENCE, "warn"],
222
252
  [PROPERTY_FALLBACK_SELF_REFERENCE, "warn"],
253
+ [PROPERTY_CURRENT_VAR_CYCLE, "warn"],
223
254
  [PREFIX_CONTEXTUAL_VARS_UNPREFIXED, "warn"],
224
- [PREFIX_NAME_CONFLICT, "error"]
255
+ [PREFIX_NAME_CONFLICT, "error"],
256
+ [PROPERTY_LEGACY_TOKEN_COLLISION, "warn"],
257
+ [PROPERTY_TOKEN_COLLISION, "error"]
225
258
  ]);
226
259
  /** An unlisted code is `warn`: a witness kind from a newer system package
227
260
  * must not fail the strict build of a host that predates its writer. */
@@ -294,10 +327,40 @@ function systemLoadDiagnostics(system) {
294
327
  ...vocabularyWitnessDiagnostics(system.vocabularyWitnessesJson),
295
328
  ...registrations,
296
329
  ...unprefixed,
297
- ...conflicts
330
+ ...conflicts,
331
+ ...tokenCollisionDiagnostics(system)
298
332
  ];
299
333
  }
300
334
  /**
335
+ * Each declared property whose name is also a token on a scale it is
336
+ * declared on. A scale read of that key, a prop value or a `{scale.key}`
337
+ * reference, resolves to the token, so the property is unreachable through
338
+ * the scale. A legacy declaration warns and the token stays the winner; any
339
+ * other is an error in every mode.
340
+ */
341
+ function tokenCollisionDiagnostics(system) {
342
+ if (!system.propertyRecordsJson) return [];
343
+ const declared = parseInternalWire(system.propertyRecordsJson, "propertyRecordsJson (the theme's properties)").flatMap((record) => record.scales.map((scale) => ({
344
+ record,
345
+ scale,
346
+ path: `${scale}.${record.name}`
347
+ })));
348
+ if (declared.length === 0) return [];
349
+ const tokens = parseInternalWire(system.scalesJson, "scalesJson (the theme's scale tokens)");
350
+ return declared.filter(({ path }) => Object.hasOwn(tokens, path)).map(({ record, scale, path }) => {
351
+ const code = record.legacy ? PROPERTY_LEGACY_TOKEN_COLLISION : PROPERTY_TOKEN_COLLISION;
352
+ const declaration = record.legacy ? `the contextual variable ${record.name}, which declareContextualVars declares on ${scale},` : `the theme property ${record.name}, declared on ${scale},`;
353
+ return {
354
+ file: "system",
355
+ component: path,
356
+ kind: record.legacy ? "warn" : "error",
357
+ message: `the ${scale} token ${record.name} (${tokens[path]}) and ${declaration} share this key. A scale read of it, such as {${path}} or a prop on the ${scale} scale set to '${record.name}', resolves to the token, so the ${record.legacy ? "contextual variable" : "property"} is unreachable through ${scale}. Rename one of them (${code})`,
358
+ code,
359
+ severity: severityFor(code)
360
+ };
361
+ });
362
+ }
363
+ /**
301
364
  * The single strict-escalation policy point for both extraction plugins:
302
365
  * error-severity diagnostics throw together under `strict`, else warn.
303
366
  */
@@ -1789,6 +1852,188 @@ function createSourceIdentity(rootDir) {
1789
1852
  };
1790
1853
  }
1791
1854
  //#endregion
1855
+ //#region pipeline/tsconfig-paths.ts
1856
+ /**
1857
+ * tsconfig `paths` → alias pairs, for drivers with no live bundler config.
1858
+ * The nearest config declaring `paths` wins wholesale — TS never merges.
1859
+ */
1860
+ function stripJsonc(text) {
1861
+ let out = "";
1862
+ let inString = false;
1863
+ let inLine = false;
1864
+ let inBlock = false;
1865
+ for (let i = 0; i < text.length; i++) {
1866
+ const ch = text[i];
1867
+ const next = text[i + 1];
1868
+ if (inLine) {
1869
+ if (ch === "\n") {
1870
+ inLine = false;
1871
+ out += ch;
1872
+ }
1873
+ continue;
1874
+ }
1875
+ if (inBlock) {
1876
+ if (ch === "*" && next === "/") {
1877
+ inBlock = false;
1878
+ i++;
1879
+ }
1880
+ continue;
1881
+ }
1882
+ if (inString) {
1883
+ out += ch;
1884
+ if (ch === "\\") {
1885
+ out += next ?? "";
1886
+ i++;
1887
+ } else if (ch === "\"") inString = false;
1888
+ continue;
1889
+ }
1890
+ if (ch === "\"") {
1891
+ inString = true;
1892
+ out += ch;
1893
+ continue;
1894
+ }
1895
+ if (ch === "/" && next === "/") {
1896
+ inLine = true;
1897
+ i++;
1898
+ continue;
1899
+ }
1900
+ if (ch === "/" && next === "*") {
1901
+ inBlock = true;
1902
+ i++;
1903
+ continue;
1904
+ }
1905
+ out += ch;
1906
+ }
1907
+ return out.replace(/,(\s*[}\]])/g, "$1");
1908
+ }
1909
+ /** `Object(value) === value` holds for exactly the blocks and lists
1910
+ * `JSON.parse` produces; the `[object Object]` tag separates the two. */
1911
+ function isJsonBlock(value) {
1912
+ return Object(value) === value && Object.prototype.toString.call(value) === "[object Object]";
1913
+ }
1914
+ /** A block or a list. A declared `paths` OWNS the setting and blocks its
1915
+ * parents' even when it contributes nothing usable. */
1916
+ function isJsonKeyed(value) {
1917
+ return Object(value) === value;
1918
+ }
1919
+ /** A JSON string, excluding the boxed `String` object, which no path join
1920
+ * accepts. */
1921
+ function isJsonString(value) {
1922
+ return Object(value) !== value && Object.prototype.toString.call(value) === "[object String]";
1923
+ }
1924
+ function decodePaths(value) {
1925
+ if (!isJsonKeyed(value)) return null;
1926
+ const decoded = /* @__PURE__ */ new Map();
1927
+ for (const [pattern, targets] of Object.entries(value)) {
1928
+ const [first] = Array.isArray(targets) ? targets : [];
1929
+ if (isJsonString(first)) decoded.set(pattern, first);
1930
+ }
1931
+ return decoded;
1932
+ }
1933
+ /** TypeScript accepts one specifier or an array; a non-string member names
1934
+ * no config and is dropped here. */
1935
+ function decodeExtends(value) {
1936
+ return (Array.isArray(value) ? value : [value]).filter(isJsonString);
1937
+ }
1938
+ function readConfig(path) {
1939
+ let raw;
1940
+ try {
1941
+ raw = readFileSync(path, "utf-8");
1942
+ } catch {
1943
+ return null;
1944
+ }
1945
+ let parsed;
1946
+ try {
1947
+ parsed = JSON.parse(stripJsonc(raw));
1948
+ } catch {
1949
+ return null;
1950
+ }
1951
+ const root = isJsonBlock(parsed) ? parsed : {};
1952
+ const compilerOptions = isJsonBlock(root.compilerOptions) ? root.compilerOptions : {};
1953
+ return {
1954
+ dir: dirname$1(path),
1955
+ baseUrl: isJsonString(compilerOptions.baseUrl) ? compilerOptions.baseUrl : null,
1956
+ paths: decodePaths(compilerOptions.paths),
1957
+ extends: decodeExtends(root.extends)
1958
+ };
1959
+ }
1960
+ function resolveExtendsTarget(specifier, fromDir) {
1961
+ if (specifier.startsWith(".") || isAbsolute(specifier)) {
1962
+ const base = resolve(fromDir, specifier);
1963
+ for (const candidate of [
1964
+ base,
1965
+ `${base}.json`,
1966
+ join$1(base, "tsconfig.json")
1967
+ ]) if (existsSync(candidate)) return candidate;
1968
+ return null;
1969
+ }
1970
+ try {
1971
+ return __require.resolve(specifier.endsWith(".json") ? specifier : `${specifier}/tsconfig.json`, { paths: [fromDir] });
1972
+ } catch {
1973
+ try {
1974
+ return __require.resolve(specifier, { paths: [fromDir] });
1975
+ } catch {
1976
+ return null;
1977
+ }
1978
+ }
1979
+ }
1980
+ /** Child-first flattened extends chain starting at `entryPath`. */
1981
+ function loadChain(entryPath) {
1982
+ const chain = [];
1983
+ const visited = /* @__PURE__ */ new Set();
1984
+ const queue = [entryPath];
1985
+ while (queue.length > 0) {
1986
+ const path = queue.shift();
1987
+ const key = resolve(path);
1988
+ if (visited.has(key)) continue;
1989
+ visited.add(key);
1990
+ const node = readConfig(key);
1991
+ if (!node) continue;
1992
+ chain.push(node);
1993
+ for (const parent of node.extends) {
1994
+ const resolved = resolveExtendsTarget(parent, node.dir);
1995
+ if (resolved) queue.push(resolved);
1996
+ }
1997
+ }
1998
+ return chain;
1999
+ }
2000
+ /**
2001
+ * The project's tsconfig path aliases as `buildPathAliasesJson` pairs; empty
2002
+ * when no readable tsconfig, or no usable `paths`, exists.
2003
+ */
2004
+ function readTsconfigAliasPairs(rootDir) {
2005
+ const chain = loadChain(join$1(rootDir, "tsconfig.json"));
2006
+ if (chain.length === 0) return [];
2007
+ const pathsOwner = chain.find((node) => node.paths !== null);
2008
+ const paths = pathsOwner?.paths ?? null;
2009
+ if (pathsOwner === void 0 || paths === null) return [];
2010
+ const baseOwner = chain.find((node) => node.baseUrl !== null);
2011
+ const baseUrl = baseOwner?.baseUrl ?? null;
2012
+ const base = baseOwner !== void 0 && baseUrl !== null ? resolve(baseOwner.dir, baseUrl) : pathsOwner.dir;
2013
+ const pairs = [];
2014
+ for (const [pattern, target] of paths) {
2015
+ const patternStars = pattern.split("*").length - 1;
2016
+ const targetStars = target.split("*").length - 1;
2017
+ if (pattern === "*" || patternStars > 1 || targetStars > 1) continue;
2018
+ if (pattern.endsWith("/*")) {
2019
+ if (!target.endsWith("/*")) continue;
2020
+ pairs.push({
2021
+ pattern: pattern.slice(0, -2),
2022
+ target: resolve(base, target.slice(0, -2)),
2023
+ kind: "prefix"
2024
+ });
2025
+ continue;
2026
+ }
2027
+ if (patternStars > 0 || targetStars > 0) continue;
2028
+ pairs.push({
2029
+ pattern,
2030
+ target: resolve(base, target),
2031
+ kind: "exact"
2032
+ });
2033
+ }
2034
+ return pairs;
2035
+ }
2036
+ //#endregion
1792
2037
  //#region pipeline/discover-packages.ts
1793
2038
  function findPackageRoot(absEntryPath) {
1794
2039
  let pkgRoot = dirname$1(absEntryPath);
@@ -1842,6 +2087,77 @@ function sourceEntryForSpecifier(specifier, srcDir, extensionsSet) {
1842
2087
  const subpath = specifier.slice(packageName.length + 1);
1843
2088
  return resolveAbsolutePathSpecifier(join$1(srcDir, subpath || "index"), extensionsSet);
1844
2089
  }
2090
+ /**
2091
+ * A package's `sideEffects` field, decoded the way Vite reads it: a boolean
2092
+ * as written, or the list's globs, one with no `/` matching the basename
2093
+ * anywhere and any other rooted at the package. Undefined without a readable
2094
+ * field, or with glob syntax this matcher lacks (braces, classes, negation,
2095
+ * extglobs), which leaves the bundler's side-effectful default.
2096
+ */
2097
+ function readPackageSideEffects(pkgRoot) {
2098
+ let manifest;
2099
+ try {
2100
+ manifest = JSON.parse(readFileSync(join$1(pkgRoot, "package.json"), "utf-8"));
2101
+ } catch {
2102
+ return;
2103
+ }
2104
+ const field = isJsonBlock(manifest) ? manifest.sideEffects : void 0;
2105
+ if (field === true || field === false) return field;
2106
+ if (!Array.isArray(field)) return void 0;
2107
+ const globs = field.filter(isJsonString);
2108
+ if (globs.some((glob) => /[{}[\]()!+@]/.test(glob))) return void 0;
2109
+ return globs.map((glob) => globToRegExp(glob.includes("/") ? glob.replace(/^\.?\//, "") : `**/${glob}`));
2110
+ }
2111
+ function matchesSideEffects(globs, pkgRoot, absFiles) {
2112
+ return absFiles.some((file) => {
2113
+ const relPath = relative(pkgRoot, file).split(sep).join("/");
2114
+ return globs.some((glob) => glob.test(relPath));
2115
+ });
2116
+ }
2117
+ /** An exports target under the `import` condition, then `default`, as the
2118
+ * system loader reads it; an array offers its targets in order. */
2119
+ function exportTarget(value) {
2120
+ if (isJsonString(value)) return value;
2121
+ if (Array.isArray(value)) {
2122
+ for (const entry of value) {
2123
+ const target = exportTarget(entry);
2124
+ if (target !== null) return target;
2125
+ }
2126
+ return null;
2127
+ }
2128
+ if (!isJsonBlock(value)) return null;
2129
+ return exportTarget(value.import) ?? exportTarget(value.default);
2130
+ }
2131
+ /** Each exact `exports` entry of the package at `pkgRoot`, as its subpath
2132
+ * and absolute target; `*` patterns name no single file and are skipped. */
2133
+ function packageExportEntries(pkgRoot) {
2134
+ let manifest;
2135
+ try {
2136
+ manifest = JSON.parse(readFileSync(join$1(pkgRoot, "package.json"), "utf-8"));
2137
+ } catch {
2138
+ return [];
2139
+ }
2140
+ const exports = isJsonBlock(manifest) ? manifest.exports : void 0;
2141
+ if (exports === void 0 || exports === null) return [];
2142
+ const subpaths = isJsonBlock(exports) && Object.keys(exports).some((k) => k.startsWith(".")) ? exports : { ".": exports };
2143
+ const entries = [];
2144
+ for (const [subpath, value] of Object.entries(subpaths)) {
2145
+ if (!subpath.startsWith(".") || subpath.includes("*")) continue;
2146
+ const target = exportTarget(value);
2147
+ if (target !== null) entries.push([subpath, resolve(pkgRoot, target)]);
2148
+ }
2149
+ return entries;
2150
+ }
2151
+ /** Installed content is release content, and unpacking can leave any file
2152
+ * times, so only a package whose real path is outside `node_modules`, such
2153
+ * as a workspace link, is judged stale by them. */
2154
+ function isInstalledPackage(pkgRoot) {
2155
+ let realRoot = pkgRoot;
2156
+ try {
2157
+ realRoot = realpathSync(pkgRoot);
2158
+ } catch {}
2159
+ return realRoot.split(/[\\/]/).includes("node_modules");
2160
+ }
1845
2161
  function distEntryIsStale(absEntry, srcDir, srcFiles) {
1846
2162
  if (isPathWithinRoot(srcDir, absEntry)) return false;
1847
2163
  let distMtime;
@@ -1862,6 +2178,20 @@ async function collectExternalPackageSources(opts) {
1862
2178
  const pushed = /* @__PURE__ */ new Set();
1863
2179
  const packageMap = {};
1864
2180
  const sourceEntries = /* @__PURE__ */ new Map();
2181
+ const sourceEntrySideEffects = /* @__PURE__ */ new Map();
2182
+ /** `replaced` names the entry the redirect stands in for; it is only
2183
+ * needed, and so only resolved, for a glob list. */
2184
+ const redirect = async (specifier, srcEntry, pkgRoot, replaced) => {
2185
+ sourceEntries.set(specifier, srcEntry);
2186
+ const field = readPackageSideEffects(pkgRoot);
2187
+ if (field === void 0) return;
2188
+ if (field === true || field === false) {
2189
+ sourceEntrySideEffects.set(specifier, field);
2190
+ return;
2191
+ }
2192
+ const replacedEntry = await replaced();
2193
+ sourceEntrySideEffects.set(specifier, replacedEntry === null || matchesSideEffects(field, pkgRoot, [srcEntry, replacedEntry]));
2194
+ };
1865
2195
  const packageDirs = [];
1866
2196
  const dirOwnerSets = {};
1867
2197
  const dirExtensions = {};
@@ -1899,7 +2229,8 @@ async function collectExternalPackageSources(opts) {
1899
2229
  const srcEntry = sourceEntryForSpecifier(specifier, srcDir, extensionsSet);
1900
2230
  if (srcEntry) {
1901
2231
  packageMap[specifier] = relative(rootDir, srcEntry);
1902
- sourceEntries.set(specifier, srcEntry);
2232
+ const replaced = absEntry;
2233
+ await redirect(specifier, srcEntry, pkgRoot, async () => replaced);
1903
2234
  } else packageMap[specifier] = relative(rootDir, absEntry);
1904
2235
  if (!isAbsolute(specifier)) {
1905
2236
  const packageName = bareSpecifierPackageName(specifier);
@@ -1907,12 +2238,18 @@ async function collectExternalPackageSources(opts) {
1907
2238
  const rootEntry = sourceEntryForSpecifier(packageName, srcDir, extensionsSet);
1908
2239
  if (rootEntry) {
1909
2240
  packageMap[packageName] = relative(rootDir, rootEntry);
1910
- sourceEntries.set(packageName, rootEntry);
2241
+ await redirect(packageName, rootEntry, pkgRoot, async () => {
2242
+ try {
2243
+ return await resolveSpecifier(packageName);
2244
+ } catch {
2245
+ return null;
2246
+ }
2247
+ });
1911
2248
  }
1912
2249
  }
1913
2250
  }
1914
2251
  const pkgFiles = walkPackageSources(srcDir, extensionsSet);
1915
- staleDist = distEntryIsStale(absEntry, srcDir, pkgFiles);
2252
+ staleDist = !isInstalledPackage(pkgRoot) && distEntryIsStale(absEntry, srcDir, pkgFiles);
1916
2253
  for (const pkgFile of pkgFiles) {
1917
2254
  const relPath = relative(rootDir, pkgFile);
1918
2255
  if (alreadyIngested(relPath)) {
@@ -1972,6 +2309,15 @@ async function collectExternalPackageSources(opts) {
1972
2309
  fileCount++;
1973
2310
  }
1974
2311
  }
2312
+ if (!isAbsolute(specifier)) {
2313
+ const packageName = bareSpecifierPackageName(specifier);
2314
+ for (const [subpath, target] of packageExportEntries(pkgRoot)) {
2315
+ const entrySpecifier = packageName + subpath.slice(1);
2316
+ const targetRelPath = relative(rootDir, target);
2317
+ if (entrySpecifier in packageMap || !alreadyIngested(targetRelPath)) continue;
2318
+ packageMap[entrySpecifier] = targetRelPath;
2319
+ }
2320
+ }
1975
2321
  outcomes.push({
1976
2322
  specifier,
1977
2323
  outcome: staleDist ? "stale-dist" : fileCount > 0 ? "resolved" : "empty",
@@ -1982,6 +2328,7 @@ async function collectExternalPackageSources(opts) {
1982
2328
  entries,
1983
2329
  packageMap,
1984
2330
  sourceEntries,
2331
+ sourceEntrySideEffects,
1985
2332
  packageDirs,
1986
2333
  dirOwnerSets,
1987
2334
  dirExtensions,
@@ -2005,9 +2352,12 @@ function excludeCollectedPackages(collected, rejectedSpecifiers, rootDir) {
2005
2352
  packageMap[specifier] = relTarget;
2006
2353
  }
2007
2354
  const sourceEntries = /* @__PURE__ */ new Map();
2355
+ const sourceEntrySideEffects = /* @__PURE__ */ new Map();
2008
2356
  for (const [specifier, absEntry] of collected.sourceEntries) {
2009
2357
  if (targetRejected(specifier, absEntry)) continue;
2010
2358
  sourceEntries.set(specifier, absEntry);
2359
+ const sideEffects = collected.sourceEntrySideEffects.get(specifier);
2360
+ if (sideEffects !== void 0) sourceEntrySideEffects.set(specifier, sideEffects);
2011
2361
  }
2012
2362
  const dirOwnerSets = {};
2013
2363
  for (const [dir, specs] of Object.entries(collected.dirOwnerSets)) {
@@ -2029,6 +2379,7 @@ function excludeCollectedPackages(collected, rejectedSpecifiers, rootDir) {
2029
2379
  entries: collected.entries.filter((entry) => !rejectedSpecifiers.has(collected.fileOwners[entry.path])),
2030
2380
  packageMap,
2031
2381
  sourceEntries,
2382
+ sourceEntrySideEffects,
2032
2383
  packageDirs: collected.packageDirs.filter((dir) => !rejectedDirs.includes(dir)),
2033
2384
  dirOwnerSets,
2034
2385
  dirExtensions,
@@ -2048,15 +2399,50 @@ function staleDistIncludesMessage(outcomes) {
2048
2399
  if (stale.length === 0) return null;
2049
2400
  return `[animus-extract] stale dist for include specifier(s): ${stale.join(", ")} — dist entry is older than the newest src/ file; rebuild the package(s) before extracting`;
2050
2401
  }
2051
- function extractSystemFilePackages(systemFilePath) {
2402
+ /** The engine's own parse of a module's imports, through its
2403
+ * `extractFacts`; undefined for an engine without one. A parse that
2404
+ * panicked reports no imports, so it counts as no parse. */
2405
+ function engineImportParser(engine) {
2406
+ const { extractFacts } = engine;
2407
+ if (!extractFacts) return void 0;
2408
+ return (source, path) => {
2409
+ try {
2410
+ const facts = parseInternalWire(extractFacts(JSON.stringify([{
2411
+ path,
2412
+ source
2413
+ }])), "extractFacts").files[path];
2414
+ return facts && !facts.parsePanicked ? facts.imports : null;
2415
+ } catch {
2416
+ return null;
2417
+ }
2418
+ };
2419
+ }
2420
+ /** A pattern matching a call of any of `callees` as a whole name, or null
2421
+ * when there is none to anchor on. */
2422
+ function rootCallPattern(callees) {
2423
+ if (callees.length === 0) return null;
2424
+ return `(?<![a-zA-Z0-9_$.])(?:${callees.map((callee) => callee.replace(/[.$]/g, "\\$&")).join("|")})`;
2425
+ }
2426
+ function extractSystemFilePackages(systemFilePath, parseImports) {
2052
2427
  let source;
2053
2428
  try {
2054
2429
  source = readFileSync(systemFilePath, "utf-8");
2055
2430
  } catch {
2056
2431
  return [];
2057
2432
  }
2433
+ const parsed = parseImports?.(source, systemFilePath) ?? null;
2434
+ const parsedImports = parsed && parsed.length > 0 ? parsed : null;
2435
+ let rootCall = "createSystem";
2436
+ if (parsedImports) {
2437
+ const shadowed = parsedImports.some((binding) => binding.local === "createSystem" && binding.imported !== "createSystem") || /\b(?:function\*?|class)\s+createSystem\b|\b(?:const|let|var)\s+createSystem\b/.test(source);
2438
+ rootCall = rootCallPattern([.../* @__PURE__ */ new Set([
2439
+ ...parsedImports.filter((binding) => binding.imported === "createSystem").map((binding) => binding.local),
2440
+ ...Array.from(source.matchAll(/\bimport\s+(?:[a-zA-Z_$][a-zA-Z0-9_$]*\s*,\s*)?\*\s*as\s+([a-zA-Z_$][a-zA-Z0-9_$]*)\s+from\s+['"]@animus-ui\/system(?:\/[^'"]*)?['"]/g), (match) => `${match[1]}.createSystem`),
2441
+ ...shadowed ? [] : ["createSystem"]
2442
+ ])]);
2443
+ }
2058
2444
  const identifiers = /* @__PURE__ */ new Set();
2059
- const constructorRegex = /createSystem\s*\(\s*\{[^}]*?\bincludes\s*:\s*\[([^\]]*)\]/gs;
2445
+ const constructorRegex = rootCall === null ? null : new RegExp(`${rootCall}\\s*\\(\\s*\\{[^}]*?\\bincludes\\s*:\\s*\\[([^\\]]*)\\]`, "gs");
2060
2446
  const chainRegex = /\.includes\s*\(\s*\[([^\]]*)\]\s*\)/gs;
2061
2447
  const collectIdentifiers = (regex) => {
2062
2448
  let match;
@@ -2068,7 +2454,7 @@ function extractSystemFilePackages(systemFilePath) {
2068
2454
  }
2069
2455
  }
2070
2456
  };
2071
- collectIdentifiers(constructorRegex);
2457
+ if (constructorRegex) collectIdentifiers(constructorRegex);
2072
2458
  collectIdentifiers(chainRegex);
2073
2459
  const IDENT_START_RE = /^[a-zA-Z_$][a-zA-Z0-9_$]*/;
2074
2460
  const skipTrivia = (from) => {
@@ -2088,6 +2474,38 @@ function extractSystemFilePackages(systemFilePath) {
2088
2474
  return pos;
2089
2475
  }
2090
2476
  };
2477
+ /** The index after the string literal opening at `from`. */
2478
+ const skipString = (from) => {
2479
+ const quote = source[from];
2480
+ let pos = from + 1;
2481
+ while (pos < source.length && source[pos] !== quote) pos += source[pos] === "\\" ? 2 : 1;
2482
+ return pos + 1;
2483
+ };
2484
+ /** The index after the bracketed span opening at `from`, balanced across
2485
+ * `<>`, `()`, `[]` and `{}`, or -1 when it never closes. An arrow type's
2486
+ * `=>` closes nothing. */
2487
+ const skipBalanced = (from) => {
2488
+ let depth = 0;
2489
+ let pos = from;
2490
+ while (pos < source.length) {
2491
+ const ch = source[pos];
2492
+ if (ch === "'" || ch === "\"" || ch === "`") {
2493
+ pos = skipString(pos);
2494
+ continue;
2495
+ }
2496
+ if (source.startsWith("//", pos) || source.startsWith("/*", pos)) {
2497
+ pos = skipTrivia(pos);
2498
+ continue;
2499
+ }
2500
+ if ("<([{".includes(ch)) depth++;
2501
+ else if (")]}".includes(ch) || ch === ">" && source[pos - 1] !== "=") {
2502
+ depth--;
2503
+ if (depth === 0) return pos + 1;
2504
+ }
2505
+ pos++;
2506
+ }
2507
+ return -1;
2508
+ };
2091
2509
  const consumeChainLinks = (from) => {
2092
2510
  let pos = from;
2093
2511
  for (;;) {
@@ -2097,6 +2515,11 @@ function extractSystemFilePackages(systemFilePath) {
2097
2515
  const method = IDENT_START_RE.exec(source.slice(cursor))?.[0];
2098
2516
  if (method !== "extend" && method !== "from") return pos;
2099
2517
  cursor = skipTrivia(cursor + method.length);
2518
+ if (source[cursor] === "<") {
2519
+ const afterTypeArguments = skipBalanced(cursor);
2520
+ if (afterTypeArguments === -1) return pos;
2521
+ cursor = skipTrivia(afterTypeArguments);
2522
+ }
2100
2523
  if (source[cursor] !== "(") return pos;
2101
2524
  cursor = skipTrivia(cursor + 1);
2102
2525
  const base = IDENT_START_RE.exec(source.slice(cursor))?.[0];
@@ -2114,14 +2537,47 @@ function extractSystemFilePackages(systemFilePath) {
2114
2537
  pos = cursor + 1;
2115
2538
  }
2116
2539
  };
2540
+ const bindingAt = /* @__PURE__ */ new Map();
2541
+ const aliasOf = /* @__PURE__ */ new Map();
2542
+ const declarationRe = /\b(?:const|let|var)\s+([a-zA-Z_$][a-zA-Z0-9_$]*)/g;
2543
+ let declaration;
2544
+ while ((declaration = declarationRe.exec(source)) !== null) {
2545
+ const name = declaration[1];
2546
+ let cursor = skipTrivia(declaration.index + declaration[0].length);
2547
+ if (source[cursor] === ":") {
2548
+ cursor++;
2549
+ while (cursor < source.length) {
2550
+ const ch = source[cursor];
2551
+ if (ch === ";" || ch === ",") break;
2552
+ if (ch === "\n" && /^\s*(?:export\s+)?(?:const|let|var|function|class|type|interface|import)\b/.test(source.slice(cursor + 1, cursor + 80))) break;
2553
+ if (ch === "=" && source[cursor + 1] !== ">") break;
2554
+ if ("<([{".includes(ch) || ch === "'" || ch === "\"" || ch === "`") {
2555
+ const after = "<([{".includes(ch) ? skipBalanced(cursor) : skipString(cursor);
2556
+ if (after === -1) break;
2557
+ cursor = after;
2558
+ continue;
2559
+ }
2560
+ cursor = ch === "=" ? cursor + 2 : cursor + 1;
2561
+ }
2562
+ }
2563
+ if (source[cursor] !== "=" || source[cursor + 1] === "=") continue;
2564
+ const initializer = skipTrivia(cursor + 1);
2565
+ bindingAt.set(initializer, name);
2566
+ const target = IDENT_START_RE.exec(source.slice(initializer))?.[0];
2567
+ if (!target) continue;
2568
+ const rest = source.slice(initializer + target.length);
2569
+ if (/^[ \t]*(?:(?:as|satisfies)\s[^;\n]*)?(?:\/\/[^\n]*)?(?:;|\r?\n(?!\s*[.([?`])|$)/.test(rest)) aliasOf.set(name, target);
2570
+ }
2117
2571
  const boundIdentifierBefore = (index) => {
2572
+ const declared = bindingAt.get(index);
2573
+ if (declared) return declared;
2118
2574
  const match = /([a-zA-Z_$][a-zA-Z0-9_$]*)\s*=\s*$/.exec(source.slice(0, index));
2119
2575
  return match ? match[1] : null;
2120
2576
  };
2121
2577
  const chainRootIdentifiers = /* @__PURE__ */ new Set();
2122
- const createSystemAnchor = /createSystem\s*\(/g;
2578
+ const createSystemAnchor = rootCall === null ? null : new RegExp(`${rootCall}\\s*\\(`, "g");
2123
2579
  let anchorMatch;
2124
- while ((anchorMatch = createSystemAnchor.exec(source)) !== null) {
2580
+ while ((anchorMatch = createSystemAnchor?.exec(source) ?? null) !== null) {
2125
2581
  let pos = anchorMatch.index + anchorMatch[0].length;
2126
2582
  let depth = 1;
2127
2583
  while (pos < source.length && depth > 0) {
@@ -2152,22 +2608,37 @@ function extractSystemFilePackages(systemFilePath) {
2152
2608
  }
2153
2609
  if (identifiers.size === 0) return [];
2154
2610
  const importMap = /* @__PURE__ */ new Map();
2611
+ for (const binding of parsedImports ?? []) importMap.set(binding.local, binding.source);
2155
2612
  const importRegex = /^\s*import\s+(?:([a-zA-Z_$][a-zA-Z0-9_$]*)\s*,\s*)?(?:\{([^}]*)\}|([a-zA-Z_$][a-zA-Z0-9_$]*))\s+from\s+['"]([^'"]+)['"]/gm;
2156
- let importMatch;
2157
- while ((importMatch = importRegex.exec(source)) !== null) {
2158
- const [, comboDefault, namedImports, defaultImport, specifier] = importMatch;
2159
- if (comboDefault) importMap.set(comboDefault, specifier);
2160
- if (defaultImport) importMap.set(defaultImport, specifier);
2161
- if (namedImports) for (const binding of namedImports.split(",")) {
2162
- const parts = binding.trim().split(/\s+as\s+/);
2163
- const localName = (parts[1] || parts[0]).trim();
2164
- if (localName) importMap.set(localName, specifier);
2613
+ if (!parsedImports) {
2614
+ let importMatch;
2615
+ while ((importMatch = importRegex.exec(source)) !== null) {
2616
+ const [, comboDefault, namedImports, defaultImport, specifier] = importMatch;
2617
+ if (comboDefault) importMap.set(comboDefault, specifier);
2618
+ if (defaultImport) importMap.set(defaultImport, specifier);
2619
+ if (namedImports) for (const binding of namedImports.split(",")) {
2620
+ const parts = binding.trim().split(/\s+as\s+/);
2621
+ const localName = (parts[1] || parts[0]).trim();
2622
+ if (localName) importMap.set(localName, specifier);
2623
+ }
2165
2624
  }
2166
2625
  }
2167
2626
  const systemFileDir = dirname$1(systemFilePath);
2168
2627
  const packages = /* @__PURE__ */ new Set();
2628
+ /** The import a local alias chain ends at, or the identifier itself. */
2629
+ const throughAliases = (id) => {
2630
+ const seen = /* @__PURE__ */ new Set();
2631
+ let current = id;
2632
+ while (!importMap.has(current) && !seen.has(current)) {
2633
+ seen.add(current);
2634
+ const target = aliasOf.get(current);
2635
+ if (!target) break;
2636
+ current = target;
2637
+ }
2638
+ return current;
2639
+ };
2169
2640
  for (const id of identifiers) {
2170
- const specifier = importMap.get(id);
2641
+ const specifier = importMap.get(throughAliases(id));
2171
2642
  if (!specifier) continue;
2172
2643
  if (specifier.startsWith(".")) packages.add(resolve(systemFileDir, specifier));
2173
2644
  else packages.add(specifier);
@@ -2230,7 +2701,7 @@ const URL_OPEN_RE = /url\(\s*(['"]?)/gi;
2230
2701
  const IMAGE_SET_OPEN_RE = /(?:-webkit-)?image-set\(/gi;
2231
2702
  const BARE_SPECIFIER_RE = /^[^'")\s]+/;
2232
2703
  /** The index of the `)` that closes the `(` at `open`, outside strings. */
2233
- function closingParen$1(css, open) {
2704
+ function closingParen(css, open) {
2234
2705
  let depth = 0;
2235
2706
  let quote = null;
2236
2707
  for (let i = open; i < css.length; i += 1) {
@@ -2279,7 +2750,7 @@ function assetReferences(css) {
2279
2750
  }
2280
2751
  for (const open of css.matchAll(IMAGE_SET_OPEN_RE)) {
2281
2752
  const paren = (open.index ?? 0) + open[0].length - 1;
2282
- const close = closingParen$1(css, paren);
2753
+ const close = closingParen(css, paren);
2283
2754
  let depth = 0;
2284
2755
  let candidateStart = true;
2285
2756
  for (let i = paren + 1; i < close; i += 1) {
@@ -2324,7 +2795,7 @@ function placeholdersInAssetCalls(css) {
2324
2795
  if (!css.includes("animus-asset:")) return [];
2325
2796
  const calls = [...css.matchAll(URL_OPEN_RE), ...css.matchAll(IMAGE_SET_OPEN_RE)].map((open) => {
2326
2797
  const paren = css.indexOf("(", open.index ?? 0);
2327
- return [paren, closingParen$1(css, paren)];
2798
+ return [paren, closingParen(css, paren)];
2328
2799
  });
2329
2800
  const found = /* @__PURE__ */ new Set();
2330
2801
  for (let at = css.indexOf(ASSET_PLACEHOLDER_PREFIX); at !== -1; at = css.indexOf(ASSET_PLACEHOLDER_PREFIX, at + 1)) {
@@ -2566,14 +3037,9 @@ function buildPathAliasesJson(pairs, rootDir) {
2566
3037
  //#endregion
2567
3038
  //#region pipeline/post-process-css.ts
2568
3039
  function resolveLightningTargets(explicitTargets, rootDir) {
2569
- let queries;
2570
- if (explicitTargets) queries = Array.isArray(explicitTargets) ? explicitTargets : [explicitTargets];
2571
- else {
2572
- const detected = browserslist(void 0, { path: rootDir });
2573
- queries = detected.length > 0 ? detected : browserslist("defaults");
2574
- }
2575
- const [firstQuery] = queries;
2576
- return browserslistToTargets(firstQuery !== void 0 && firstQuery.includes(" ") ? browserslist(queries) : queries);
3040
+ if (explicitTargets) return browserslistToTargets(browserslist(explicitTargets));
3041
+ const detected = browserslist(void 0, { path: rootDir });
3042
+ return browserslistToTargets(detected.length > 0 ? detected : browserslist("defaults"));
2577
3043
  }
2578
3044
  function postProcessCss(css, opts) {
2579
3045
  if (!css) return css;
@@ -2591,125 +3057,28 @@ function postProcessCss(css, opts) {
2591
3057
  }
2592
3058
  }
2593
3059
  //#endregion
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
- };
2610
- }
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;
2623
- continue;
2624
- }
2625
- const escape = escapeAt(css, end);
2626
- if (escape === null) break;
2627
- name += escape.codePoint;
2628
- end += escape.length;
2629
- }
2630
- return {
2631
- end,
2632
- name
2633
- };
2634
- }
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
- };
2654
- }
2655
- /**
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.
2662
- */
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;
2690
- }
3060
+ //#region pipeline/property-registrations.ts
3061
+ const PROPERTY_AT_RULE = /@property\s+/gi;
3062
+ const RULE_BODY = /\s*\{[^}]*\}\n?/y;
3063
+ /** Each `@property` rule, through one newline after it, with its name read
3064
+ * as CSS tokenizes an identifier: escapes decoded, non-ASCII included. */
3065
+ function propertyRules(css) {
3066
+ const rules = [];
3067
+ for (const match of css.matchAll(PROPERTY_AT_RULE)) {
3068
+ if (match.index < (rules.at(-1)?.end ?? 0)) continue;
3069
+ const name = identifierAt(css, match.index + match[0].length);
3070
+ if (!name?.name.startsWith("--")) continue;
3071
+ RULE_BODY.lastIndex = name.end;
3072
+ const body = RULE_BODY.exec(css);
3073
+ if (body === null) continue;
3074
+ rules.push({
3075
+ start: match.index,
3076
+ end: name.end + body[0].length,
3077
+ name: name.name
3078
+ });
2691
3079
  }
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);
3080
+ return rules;
2697
3081
  }
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);
2709
- }
2710
- //#endregion
2711
- //#region pipeline/property-registrations.ts
2712
- const PROPERTY_RULE = /@property\s+(--[\w-]+)\s*\{[^}]*\}\n?/g;
2713
3082
  /** The data types a registration's `syntax` may name. */
2714
3083
  const SYNTAX_TYPES = /* @__PURE__ */ new Set([
2715
3084
  "angle",
@@ -2734,30 +3103,35 @@ const MATH_FUNCTION = /\b(?:calc|min|max|clamp)\(/i;
2734
3103
  /**
2735
3104
  * Removes the `@property` rules browsers would ignore — an unknown syntax
2736
3105
  * component, a syntax other than `*` without an initial value, an initial
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.
3106
+ * value that substitutes another value or depends on context, math that
3107
+ * breaks calculation syntax, or a value the syntax does not accept.
3108
+ * Minifying a stylesheet that contains such a rule can also fail. An
3109
+ * all-absolute math initial value is written as its computed value, which
3110
+ * the minifier reads.
2741
3111
  */
2742
3112
  function splitInvalidPropertyRegistrations(css) {
2743
3113
  const invalid = [];
3114
+ let kept = "";
3115
+ let copied = 0;
3116
+ for (const { start, end, name } of propertyRules(css)) {
3117
+ const checked = checkRegistration(css.slice(start, end));
3118
+ kept += css.slice(copied, start);
3119
+ if ("rule" in checked) kept += checked.rule;
3120
+ else invalid.push({
3121
+ name,
3122
+ reason: checked.reason
3123
+ });
3124
+ copied = end;
3125
+ }
2744
3126
  return {
2745
- css: css.replace(PROPERTY_RULE, (rule, name) => {
2746
- const checked = checkRegistration(rule);
2747
- if ("rule" in checked) return checked.rule;
2748
- invalid.push({
2749
- name,
2750
- reason: checked.reason
2751
- });
2752
- return "";
2753
- }),
3127
+ css: kept + css.slice(copied),
2754
3128
  invalid
2755
3129
  };
2756
3130
  }
2757
3131
  /** The registrations in `css`, by custom-property name. */
2758
3132
  function propertyRegistrations(css) {
2759
3133
  const registrations = /* @__PURE__ */ new Map();
2760
- for (const match of css.matchAll(PROPERTY_RULE)) registrations.set(match[1], readRegistration(match[0]));
3134
+ for (const { start, end, name } of propertyRules(css)) registrations.set(name, readRegistration(css.slice(start, end)));
2761
3135
  return registrations;
2762
3136
  }
2763
3137
  function readRegistration(rule) {
@@ -2772,6 +3146,7 @@ function checkRegistration(rule) {
2772
3146
  const { syntax, initialValue } = readRegistration(rule);
2773
3147
  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
3148
  let kept = rule;
3149
+ let folded;
2775
3150
  if (syntax !== "*") {
2776
3151
  const unknown = syntax.split("|").map((component) => ({
2777
3152
  text: component.trim(),
@@ -2780,9 +3155,10 @@ function checkRegistration(rule) {
2780
3155
  if (unknown !== void 0) return { reason: `syntax "${syntax}" has the unknown component "${unknown.text}"` };
2781
3156
  if (initialValue === void 0) return { reason: `syntax "${syntax}" needs an initialValue` };
2782
3157
  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}`);
3158
+ folded = foldInitialValue(syntax, initialValue);
3159
+ if (folded?.kind === "invalid") return { reason: `initialValue "${initialValue}" is not a valid calculation: ${folded.reason}` };
3160
+ if (folded?.kind === "rejected") return { reason: `initialValue "${initialValue}" computes to the ${folded.type} ${folded.text}, which syntax "${syntax}" does not accept` };
3161
+ if (folded?.kind === "folded") kept = rule.replace(INITIAL_VALUE, `$1${folded.text}`);
2786
3162
  }
2787
3163
  try {
2788
3164
  transform({
@@ -2792,6 +3168,7 @@ function checkRegistration(rule) {
2792
3168
  return { rule: kept };
2793
3169
  } catch (error) {
2794
3170
  const detail = error instanceof Error ? error.message : String(error);
3171
+ if (folded?.kind === "not-finite") return { reason: `initialValue "${initialValue}" computes to an infinite or NaN value, which browsers clamp and which has no plain CSS value to write instead, and the CSS parser that builds the stylesheet cannot read the math (${detail})` };
2795
3172
  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
3173
  return { reason: `the CSS parser rejects \`${kept.trim()}\` (${detail})` };
2797
3174
  }
@@ -2801,13 +3178,18 @@ function checkRegistration(rule) {
2801
3178
  /**
2802
3179
  * Reports custom properties that will not behave as their CSS suggests: a
2803
3180
  * 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.
3181
+ * applies ahead of, and an animated property that cannot interpolate. Sheets
3182
+ * are read through the shared CSS tokenizer, as the prefix pass reads them:
3183
+ * comments are trivia, quoted text holds no names, and escapes and function
3184
+ * case are decoded. The CSS itself is never changed.
2807
3185
  */
2808
3186
  function checkCustomProperties(input) {
2809
3187
  const registered = propertyRegistrations(input.system.variableCss);
2810
- const declared = new Set(input.system.contextualProperties ?? []);
3188
+ const declaredNames = input.system.contextualProperties ?? [];
3189
+ const declared = new Set(declaredNames);
3190
+ const withInitialValue = [...registered].filter(([, registration]) => registration.initialValue !== void 0).map(([name]) => name);
3191
+ const writers = currentVarWriters(input.system.propConfigJson);
3192
+ const currentVars = new Set(writers.keys());
2811
3193
  const findings = [];
2812
3194
  const sheets = [{
2813
3195
  css: input.componentCss,
@@ -2817,9 +3199,20 @@ function checkCustomProperties(input) {
2817
3199
  owner: globalOwner
2818
3200
  }];
2819
3201
  for (const { css, owner } of sheets) {
2820
- if (!/var\(|transition|@keyframes/i.test(css)) continue;
3202
+ if (!mayHoldFinding(css, withInitialValue, declaredNames, currentVars)) continue;
2821
3203
  const parsed = declarations(css);
2822
- const discreteRules = new Set(parsed.filter((d) => d.property === "transition-behavior" && /\ballow-discrete\b/.test(d.value)).map((d) => d.rule));
3204
+ for (const cycle of currentVarCycles(parsed, currentVars)) {
3205
+ const [first] = cycle;
3206
+ const names = cycle.map((d) => d.property);
3207
+ const props = [...new Set(cycle.flatMap((d) => writers.get(d.property) ?? []))];
3208
+ findings.push({
3209
+ code: PROPERTY_CURRENT_VAR_CYCLE,
3210
+ property: first.property,
3211
+ message: `${cycle.map((d) => `${d.property}: ${d.value}`).join("; ")} are written by the currentVar of ${props.join(", ")} in one rule and read each other, a cycle, so ${names.join(", ")} are invalid at computed-value time wherever this rule applies. Give one of the props a value that does not read another's current variable`,
3212
+ owner: owner(first)
3213
+ });
3214
+ }
3215
+ const discreteRules = new Set(parsed.filter((d) => d.property === "transition-behavior" && topLevelItems(d.values).some(isDiscrete)).map((d) => d.rule));
2823
3216
  for (const declaration of parsed) {
2824
3217
  const found = (code, property, message) => findings.push({
2825
3218
  code,
@@ -2827,16 +3220,26 @@ function checkCustomProperties(input) {
2827
3220
  message,
2828
3221
  owner: owner(declaration)
2829
3222
  });
2830
- if (isSelfReference(declaration)) {
3223
+ const reads = variableReads(declaration.values);
3224
+ if (isSelfReference(declaration, reads)) {
2831
3225
  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`);
2832
3226
  continue;
2833
3227
  }
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`);
2835
- for (const read of varReads(declaration.value)) {
3228
+ if (readsItselfDirectly(declaration, reads)) {
3229
+ const { property, value } = declaration;
3230
+ found(PROPERTY_SELF_REFERENCE, property, `${property}: ${value} reads ${property} directly, a reference to itself, so it is invalid at computed-value time; a fallback inside that var() does not break the cycle. Give it a value that does not read its own name`);
3231
+ continue;
3232
+ }
3233
+ if (readsItselfOnlyInFallback(declaration, reads)) {
3234
+ const { property, value } = declaration;
3235
+ found(PROPERTY_FALLBACK_SELF_REFERENCE, property, `${property}: ${value} reads ${property} inside another var()'s fallback. Wherever that fallback is used, ${property} refers to itself, a cycle in every browser, so ${property} is invalid at computed-value time, or takes its registered initial value, and so is every property that reads it; where it is not used, browsers differ on whether the cycle still counts. Without this declaration, descendants read the inherited ${property} instead, also where the outer variable is set, which is what a prop with currentVar does for such a value`);
3236
+ }
3237
+ for (const read of reads) {
2836
3238
  const registration = registered.get(read.name);
2837
3239
  if (read.fallback === void 0 || registration?.initialValue === void 0) continue;
2838
- const chain = read.fallback.startsWith("var(");
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`);
3240
+ const { fallback } = read;
3241
+ const chain = reads.some((other) => other.start === fallback.first);
3242
+ found(chain ? PROPERTY_FALLBACK_CHAIN_SUPPRESSED : PROPERTY_FALLBACK_SUPPRESSED, read.name, `var(${read.name}, ${css.slice(fallback.start, fallback.end).trim()}): 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`);
2840
3243
  }
2841
3244
  for (const animated of animatedProperties(declaration, discreteRules)) {
2842
3245
  if (animated.discrete || !declared.has(animated.name)) continue;
@@ -2848,195 +3251,244 @@ function checkCustomProperties(input) {
2848
3251
  }
2849
3252
  return toDiagnostics(findings);
2850
3253
  }
2851
- /** Declarations in engine-emitted CSS: one `property: value` per `;`. */
3254
+ /**
3255
+ * Whether a sheet can hold a finding, shown by its text before anything is
3256
+ * parsed: a custom property reading its own name, a `var(` with a fallback
3257
+ * of a registration that has an initial value, a transition or keyframes
3258
+ * and a declared contextual variable's name, or a `var(` of a currentVar name
3259
+ * when two or more props write one. An escape or a comment can hide
3260
+ * a name or a function from the text, so a sheet with either is always
3261
+ * parsed.
3262
+ */
3263
+ function mayHoldFinding(css, withInitialValue, declared, currentVars) {
3264
+ return css.includes("\\") || css.includes("/*") || readsOwnName(css) || withInitialValue.some((name) => new RegExp(String.raw`var\(\s*${escapeRegExp(name)}\s*,`, "i").test(css)) || declared.some((name) => css.includes(name)) && /transition|@keyframes/i.test(css) || currentVars.size > 1 && [...currentVars].some((name) => new RegExp(String.raw`var\(\s*${escapeRegExp(name)}`, "i").test(css));
3265
+ }
3266
+ const escapeRegExp = (text) => text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
3267
+ /** A custom-property declaration key, as the text shows one: anything but
3268
+ * CSS whitespace and the characters that end a name, so a non-ASCII space
3269
+ * stays part of the name, as CSS reads it. */
3270
+ const CUSTOM_PROPERTY_KEY = /(--[^\t\n\f\r :;{}()"'\\/]+)[\t\n\f\r ]*:/g;
3271
+ const VAR_OPEN = /var\(\s*/iy;
3272
+ const isNameCode = (c) => c >= 48 && c <= 57 || c >= 65 && c <= 90 || c >= 97 && c <= 122 || c === 45 || c === 95 || c >= 128;
3273
+ /**
3274
+ * Whether a custom-property declaration has `var(` of its own whole name in
3275
+ * its value, bounded as the declaration scanner bounds one: by a `;`, `{` or
3276
+ * `}` outside parentheses, brackets and quoted text. `var` matches in any
3277
+ * case, so this errs toward parsing.
3278
+ */
3279
+ function readsOwnName(css) {
3280
+ for (const match of css.matchAll(CUSTOM_PROPERTY_KEY)) {
3281
+ const name = match[1];
3282
+ let depth = 0;
3283
+ let quote = -1;
3284
+ for (let i = match.index + match[0].length; i < css.length; i += 1) {
3285
+ const c = css.charCodeAt(i);
3286
+ if (quote !== -1) {
3287
+ if (c === quote || c === 10 || c === 13 || c === 12) quote = -1;
3288
+ } else if (c === 34 || c === 39) quote = c;
3289
+ else if (c === 40 || c === 91) depth += 1;
3290
+ else if (c === 41 || c === 93) depth = Math.max(0, depth - 1);
3291
+ else if (depth === 0 && (c === 59 || c === 123 || c === 125)) break;
3292
+ else if (c === 118 || c === 86) {
3293
+ VAR_OPEN.lastIndex = i;
3294
+ const at = VAR_OPEN.test(css) ? VAR_OPEN.lastIndex : -1;
3295
+ if (at !== -1 && css.startsWith(name, at) && !isNameCode(css.charCodeAt(at + name.length))) return true;
3296
+ }
3297
+ }
3298
+ }
3299
+ return false;
3300
+ }
3301
+ /** `items` as text without comments, at-keywords decoded. */
3302
+ function textOf(css, items) {
3303
+ let text = "";
3304
+ const add = (token) => {
3305
+ if (token.type === "whitespace") text += " ";
3306
+ else if (token.type === "at-keyword") text += `@${token.value.toLowerCase()}`;
3307
+ else text += css.slice(token.start, token.end);
3308
+ };
3309
+ const walk = (list) => {
3310
+ for (const item of list) if (item.kind === "token") add(item.token);
3311
+ else {
3312
+ add(item.open);
3313
+ walk(item.children);
3314
+ if (item.close) add(item.close);
3315
+ }
3316
+ };
3317
+ walk(items);
3318
+ return text.trim();
3319
+ }
3320
+ /** Declarations in a sheet: one `property: value` per `;` inside a `{}`
3321
+ * block, read from component values, so comments separate nothing. */
2852
3322
  function declarations(css) {
2853
3323
  const found = [];
2854
- const stack = [];
2855
3324
  let rules = 0;
2856
- let segment = 0;
2857
- let parens = 0;
2858
- let quote = null;
2859
- const take = (end) => {
2860
- const text = css.slice(segment, end);
2861
- const colon = text.indexOf(":");
2862
- const current = stack[stack.length - 1];
2863
- if (current === void 0 || colon <= 0) return;
2864
- found.push({
2865
- property: text.slice(0, colon).trim(),
2866
- value: text.slice(colon + 1).replace(/;$/, "").trim(),
2867
- start: segment + (text.length - text.trimStart().length),
2868
- end,
2869
- rule: current.rule,
2870
- context: stack.map((entry) => entry.prelude)
2871
- });
2872
- };
2873
- for (let i = 0; i < css.length; i += 1) {
2874
- const c = css[i];
2875
- if (quote !== null) {
2876
- if (c === "\\") i += 1;
2877
- else if (c === quote) quote = null;
2878
- continue;
2879
- }
2880
- if (c === "\"" || c === "'") quote = c;
2881
- else if (c === "(") parens += 1;
2882
- else if (c === ")") parens -= 1;
2883
- else if (parens === 0 && c === "{") {
2884
- stack.push({
2885
- prelude: css.slice(segment, i).trim(),
2886
- rule: rules
3325
+ const visit = (items, context, rule) => {
3326
+ let segment = [];
3327
+ const take = () => {
3328
+ const declaration = declarationOf(css, segment);
3329
+ if (rule !== void 0 && declaration !== void 0) found.push({
3330
+ ...declaration,
3331
+ rule,
3332
+ context
2887
3333
  });
3334
+ segment = [];
3335
+ };
3336
+ for (const item of items) if (item.kind === "block" && item.open.type === "{") {
3337
+ const prelude = textOf(css, segment);
3338
+ segment = [];
2888
3339
  rules += 1;
2889
- segment = i + 1;
2890
- } else if (parens === 0 && c === ";") {
2891
- take(i + 1);
2892
- segment = i + 1;
2893
- } else if (parens === 0 && c === "}") {
2894
- take(i);
2895
- stack.pop();
2896
- segment = i + 1;
2897
- }
2898
- }
3340
+ visit(item.children, [...context, prelude], rules - 1);
3341
+ } else if (item.kind === "token" && item.token.type === ";") take();
3342
+ else segment.push(item);
3343
+ take();
3344
+ };
3345
+ visit(componentValues(tokenize(css)), [], void 0);
2899
3346
  return found;
2900
3347
  }
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;
3348
+ /** The declaration a `;`-separated segment holds, if any. A bad string or
3349
+ * url makes a declaration invalid, so browsers drop it, and so does this. */
3350
+ function declarationOf(css, segment) {
3351
+ const colon = segment.findIndex((item) => item.kind === "token" && item.token.type === ":");
3352
+ const named = segment.slice(0, Math.max(colon, 0)).filter((i) => !isSpace(i));
3353
+ const values = trimSpace(segment.slice(colon + 1));
3354
+ if (colon === -1 || named.length === 0 || someValue(values, (v) => v.kind === "token" && (v.token.type === "bad-url" || v.token.type === "bad-string"))) return;
3355
+ const [only] = named;
3356
+ const ident = named.length === 1 && only.kind === "token" && only.token.type === "ident" ? only.token.value : void 0;
3357
+ const custom = ident?.startsWith("--") === true;
3358
+ return {
3359
+ property: custom && ident !== void 0 ? ident : (ident ?? textOf(css, named)).toLowerCase(),
3360
+ custom,
3361
+ value: values.length > 0 ? css.slice(values[0].start, values[values.length - 1].end) : "",
3362
+ values
3363
+ };
2907
3364
  }
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();
3365
+ function trimSpace(items) {
3366
+ let start = 0;
3367
+ let end = items.length;
3368
+ while (start < end && isSpace(items[start])) start += 1;
3369
+ while (end > start && isSpace(items[end - 1])) end -= 1;
3370
+ return items.slice(start, end);
3371
+ }
3372
+ /** Whether the value is exactly `var()` of the declared property. */
3373
+ function isSelfReference(declaration, reads) {
3374
+ return declaration.custom && reads.length === 1 && reads[0].name === declaration.property && reads[0].fallback === void 0 && declaration.values.length === 1 && declaration.values[0].start === reads[0].start;
3375
+ }
3376
+ /** Whether a read of the declared property sits outside every other read's
3377
+ * fallback, as in `--a: var(--a, red)` or `--a: calc(var(--a) + 1px)`: cyclic
3378
+ * whatever the fallback, as the bare `var(--a)` is. */
3379
+ function readsItselfDirectly(declaration, reads) {
3380
+ return declaration.custom && reads.some((read) => read.name === declaration.property && isDirectRead(read, reads));
3381
+ }
3382
+ /** Whether `read` sits outside every other read's fallback, so it is taken
3383
+ * whenever the declaration is. */
3384
+ function isDirectRead(read, reads) {
3385
+ return !reads.some((outer) => outer !== read && outer.fallback !== void 0 && outer.fallback.start <= read.start && read.end <= outer.fallback.end);
3386
+ }
3387
+ /** Each custom property a `currentVar` writes, with the props writing it. */
3388
+ function currentVarWriters(propConfigJson) {
3389
+ const writers = /* @__PURE__ */ new Map();
3390
+ if (propConfigJson === void 0) return writers;
3391
+ const config = JSON.parse(propConfigJson);
3392
+ for (const [prop, entry] of Object.entries(config)) {
3393
+ if (!entry.currentVar) continue;
3394
+ writers.set(entry.currentVar, [...writers.get(entry.currentVar) ?? [], prop]);
3395
+ }
3396
+ return writers;
2912
3397
  }
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. */
2924
- function varReads(value) {
2925
- const reads = [];
2926
- for (let at = 0; at < value.length;) {
2927
- const c = value[at];
2928
- if (c === "\"" || c === "'") {
2929
- at = closingQuote(value, at);
2930
- continue;
2931
- }
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;
3398
+ /**
3399
+ * Cycles among the `currentVar` writes of one rule, read directly. One rule
3400
+ * is one element under one set of conditions, so its writes are always
3401
+ * active together; writes in different rules, or a read only inside another
3402
+ * read's fallback, are left alone. Each cycle is listed once, in declaration
3403
+ * order.
3404
+ */
3405
+ function currentVarCycles(parsed, currentVars) {
3406
+ if (currentVars.size === 0) return [];
3407
+ const byRule = /* @__PURE__ */ new Map();
3408
+ for (const declaration of parsed) {
3409
+ if (!declaration.custom || !currentVars.has(declaration.property)) continue;
3410
+ const writes = byRule.get(declaration.rule) ?? /* @__PURE__ */ new Map();
3411
+ writes.set(declaration.property, declaration);
3412
+ byRule.set(declaration.rule, writes);
3413
+ }
3414
+ const cycles = [];
3415
+ for (const writes of byRule.values()) {
3416
+ const edges = /* @__PURE__ */ new Map();
3417
+ for (const [name, declaration] of writes) {
3418
+ const reads = variableReads(declaration.values);
3419
+ edges.set(name, reads.filter((read) => read.name !== name && writes.has(read.name) && isDirectRead(read, reads)).map((read) => read.name));
2944
3420
  }
2945
- const { end: nameEnd, name } = readCustomPropertyName(value, nameAt + 2);
2946
- const after = skipSpace(value, nameEnd);
2947
- const close = closingParen(value, at + 4);
2948
- reads.push({
2949
- name: `--${name}`,
2950
- fallback: value[after] === "," ? value.slice(after + 1, close - 1).trim() : void 0,
2951
- start: at,
2952
- end: close
2953
- });
2954
- at = after;
2955
- }
2956
- return reads;
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;
3421
+ for (const component of stronglyConnected(edges)) {
3422
+ if (component.length < 2) continue;
3423
+ cycles.push([...writes.values()].filter((d) => component.includes(d.property)));
2971
3424
  }
2972
3425
  }
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);
3426
+ return cycles;
2979
3427
  }
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
- }
3428
+ /** Tarjan's strongly connected components of a small graph. */
3429
+ function stronglyConnected(edges) {
3430
+ const index = /* @__PURE__ */ new Map();
3431
+ const low = /* @__PURE__ */ new Map();
3432
+ const stack = [];
3433
+ const onStack = /* @__PURE__ */ new Set();
3434
+ const components = [];
3435
+ const visit = (node) => {
3436
+ index.set(node, index.size);
3437
+ low.set(node, index.get(node));
3438
+ stack.push(node);
3439
+ onStack.add(node);
3440
+ for (const next of edges.get(node) ?? []) if (!index.has(next)) {
3441
+ visit(next);
3442
+ low.set(node, Math.min(low.get(node), low.get(next)));
3443
+ } else if (onStack.has(next)) low.set(node, Math.min(low.get(node), index.get(next)));
3444
+ if (low.get(node) === index.get(node)) {
3445
+ const component = [];
3446
+ let member;
3447
+ do {
3448
+ member = stack.pop();
3449
+ onStack.delete(member);
3450
+ component.push(member);
3451
+ } while (member !== node);
3452
+ components.push(component);
3453
+ }
3454
+ };
3455
+ for (const node of edges.keys()) if (!index.has(node)) visit(node);
3456
+ return components;
2989
3457
  }
2990
- function withoutComments(text) {
2991
- return text.replace(/\/\*[\s\S]*?\*\//g, "");
3458
+ /** Whether every read of the declared property sits inside another read's
3459
+ * fallback, as in `--a: var(--b, var(--a))`. */
3460
+ function readsItselfOnlyInFallback(declaration, reads) {
3461
+ if (!declaration.custom) return false;
3462
+ const own = reads.filter((read) => read.name === declaration.property);
3463
+ return own.length > 0 && own.every((read) => reads.some((outer) => outer.fallback !== void 0 && outer.fallback.start <= read.start && read.end <= outer.fallback.end));
2992
3464
  }
2993
3465
  /** Custom properties this declaration sets in a keyframe or names in a
2994
3466
  * transition. */
2995
3467
  function animatedProperties(declaration, discreteRules) {
2996
3468
  const keyframes = declaration.context.find((prelude) => prelude.startsWith("@keyframes"));
2997
- const set = customPropertyName(declaration.property);
2998
- if (keyframes !== void 0 && set !== void 0) return [{
2999
- name: set,
3469
+ if (keyframes !== void 0 && declaration.custom) return [{
3470
+ name: declaration.property,
3000
3471
  how: `set in ${keyframes}`,
3001
3472
  discrete: false
3002
3473
  }];
3003
3474
  if (declaration.property !== "transition" && declaration.property !== "transition-property") return [];
3004
- return topLevelItems(declaration.value).flatMap((item) => {
3005
- const property = topLevelTokens(item.replace(/\/\*[\s\S]*?\*\//g, " ")).map(customPropertyName).find((name) => name !== void 0);
3006
- return property !== void 0 ? [{
3007
- name: property,
3475
+ return topLevelItems(declaration.values).flatMap((item) => {
3476
+ const named = item.find((value) => value.kind === "token" && value.token.type === "ident" && value.token.value.startsWith("--"));
3477
+ return named?.kind === "token" ? [{
3478
+ name: named.token.value,
3008
3479
  how: "transitioned",
3009
- discrete: /\ballow-discrete\b/.test(item) || discreteRules.has(declaration.rule)
3480
+ discrete: isDiscrete(item) || discreteRules.has(declaration.rule)
3010
3481
  }] : [];
3011
3482
  });
3012
3483
  }
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
- }
3029
- function topLevelItems(value) {
3030
- const items = [];
3031
- let depth = 0;
3032
- let start = 0;
3033
- for (let i = 0; i < value.length; i += 1) if (value[i] === "(") depth += 1;
3034
- else if (value[i] === ")") depth -= 1;
3035
- else if (value[i] === "," && depth === 0) {
3036
- items.push(value.slice(start, i));
3037
- start = i + 1;
3038
- }
3039
- items.push(value.slice(start));
3484
+ function isDiscrete(item) {
3485
+ return item.some((value) => value.kind === "token" && value.token.type === "ident" && value.token.value.toLowerCase() === "allow-discrete");
3486
+ }
3487
+ /** The comma-separated items of a value, each its top-level values. */
3488
+ function topLevelItems(values) {
3489
+ const items = [[]];
3490
+ for (const value of values) if (value.kind === "token" && value.token.type === ",") items.push([]);
3491
+ else items[items.length - 1].push(value);
3040
3492
  return items;
3041
3493
  }
3042
3494
  /** Why a property cannot interpolate, or `null` when it can. */
@@ -3284,6 +3736,47 @@ function serializeStaticCss(config) {
3284
3736
  return json === "{}" ? null : json;
3285
3737
  }
3286
3738
  //#endregion
3739
+ //#region pipeline/property-names.ts
3740
+ function createPropertyNames(declared, prefix, themeNames = []) {
3741
+ const finals = /* @__PURE__ */ new Map();
3742
+ for (const name of [...new Set(declared)].sort()) finals.set(name, `${prefix}-${name}`);
3743
+ const aliases = /* @__PURE__ */ new Map();
3744
+ for (const [name, final] of finals) if (!finals.has(final)) aliases.set(final, name);
3745
+ const others = /* @__PURE__ */ new Map();
3746
+ for (const name of [...new Set(themeNames)].sort()) if (!finals.has(name)) others.set(name, `${prefix}-${name}`);
3747
+ return {
3748
+ identity: (spelling) => finals.has(spelling) ? spelling : aliases.get(spelling) ?? (others.has(spelling) ? spelling : void 0),
3749
+ finalName: (name) => finals.get(name) ?? others.get(name),
3750
+ ambiguous: () => [...others.keys()].filter((name) => aliases.has(name)).map((name) => ({
3751
+ name,
3752
+ contextual: aliases.get(name) ?? name
3753
+ }))
3754
+ };
3755
+ }
3756
+ /**
3757
+ * Gives every custom-property token that names a managed property its final
3758
+ * name, in one pass: declaration keys, `var()` reads at any depth (fallbacks
3759
+ * included), `@property` names, transition-property lists and style queries.
3760
+ * Names are read through the shared CSS tokenizer, whole and with escapes
3761
+ * decoded, escaped leading dashes included, so an undeclared identifier that
3762
+ * contains or starts with a managed name is never renamed in part. Quoted
3763
+ * strings, `url()` and comments are copied unchanged.
3764
+ */
3765
+ function renameCustomProperties(css, names) {
3766
+ let out = "";
3767
+ let copied = 0;
3768
+ for (const token of tokenize(css)) {
3769
+ if (token.type !== "ident" || !token.value.startsWith("--")) continue;
3770
+ const spelling = token.value.slice(2);
3771
+ const identity = names.identity(spelling);
3772
+ const final = identity === void 0 ? void 0 : names.finalName(identity);
3773
+ if (final === void 0 || final === spelling) continue;
3774
+ out += `${css.slice(copied, token.start)}--${final}`;
3775
+ copied = token.end;
3776
+ }
3777
+ return out + css.slice(copied);
3778
+ }
3779
+ //#endregion
3287
3780
  //#region pipeline/prefix.ts
3288
3781
  /** Prefix variable references without changing their matching rules. */
3289
3782
  function prefixVariableReferences(prefix, value) {
@@ -3449,7 +3942,7 @@ function runStructuralSelfCheck(input) {
3449
3942
  else if (input.assembledCss !== void 0 ? !/@layer\s+anm-base\s*\{/.test(input.assembledCss) : input.componentCss.trim().length === 0) failures.push(`${input.componentCount} component(s) discovered but the emitted component CSS is empty`);
3450
3943
  for (const { specifier, outcome } of input.externalOutcomes ?? []) if (outcome === "empty") failures.push(`include '${specifier}' resolved but discovered no component sources`);
3451
3944
  else if (outcome === "unresolvable") failures.push(`include '${specifier}' could not be resolved`);
3452
- if (!input.variableCss.includes(":root")) failures.push("No :root variable block found in variable CSS");
3945
+ if ((input.variableMapJson == null || Object.keys(parseInternalWire(input.variableMapJson, "variableMapJson (the theme's token → variable map)")).length > 0) && !input.variableCss.includes(":root")) failures.push("No :root variable block found in variable CSS");
3453
3946
  if ((input.assembledCss ?? `${input.variableCss}\n${input.globalCss}\n${input.componentCss}`).includes("__TRANSFORM__")) failures.push("Unresolved __TRANSFORM__ placeholders found in CSS output");
3454
3947
  if (input.componentCount > 0 && (input.componentCss.length > 0 || input.assembledCss !== void 0)) {
3455
3948
  const assembled = input.assembledCss ?? assembleStylesheet({
@@ -3458,9 +3951,16 @@ function runStructuralSelfCheck(input) {
3458
3951
  globalCss: input.globalCss,
3459
3952
  componentCss: input.componentCss
3460
3953
  });
3461
- const baseIdx = assembled.search(/@layer\s+anm-base\s*\{/);
3462
- const variantsIdx = assembled.search(/@layer\s+anm-variants\s*\{/);
3463
- if (baseIdx !== -1 && variantsIdx !== -1 && baseIdx >= variantsIdx) failures.push(`CSS layer ordering violated — @layer anm-base (offset ${baseIdx}) must precede @layer anm-variants (offset ${variantsIdx})`);
3954
+ const blocks = ANIMUS_LAYERS.map((layer) => ({
3955
+ layer,
3956
+ offset: assembled.search(new RegExp(`@layer\\s+${layer}\\s*\\{`))
3957
+ })).filter(({ offset }) => offset !== -1);
3958
+ const misplaced = blocks.findIndex(({ offset }, index) => index > 0 && offset <= blocks[index - 1].offset);
3959
+ if (misplaced !== -1) {
3960
+ const earlier = blocks[misplaced - 1];
3961
+ const later = blocks[misplaced];
3962
+ failures.push(`CSS layer ordering violated — @layer ${earlier.layer} (offset ${earlier.offset}) must precede @layer ${later.layer} (offset ${later.offset})`);
3963
+ }
3464
3964
  }
3465
3965
  return failures;
3466
3966
  }
@@ -3523,188 +4023,6 @@ function buildSystemPropsModule(opts) {
3523
4023
  return `export const systemPropMap = ${opts.systemPropMapJson};\nexport const systemPropGroups = ${opts.groupRegistryJson};\nexport const dynamicPropConfig = ${JSON.stringify(dynamicPropConfig)};\nexport const transforms = ${transformsSource(opts.admittedTransforms)};\n` + (opts.typedSystemProps.length > 0 ? `export const typedSystemProps = ${JSON.stringify(opts.typedSystemProps)};\n` : "");
3524
4024
  }
3525
4025
  //#endregion
3526
- //#region pipeline/tsconfig-paths.ts
3527
- /**
3528
- * tsconfig `paths` → alias pairs, for drivers with no live bundler config.
3529
- * The nearest config declaring `paths` wins wholesale — TS never merges.
3530
- */
3531
- function stripJsonc(text) {
3532
- let out = "";
3533
- let inString = false;
3534
- let inLine = false;
3535
- let inBlock = false;
3536
- for (let i = 0; i < text.length; i++) {
3537
- const ch = text[i];
3538
- const next = text[i + 1];
3539
- if (inLine) {
3540
- if (ch === "\n") {
3541
- inLine = false;
3542
- out += ch;
3543
- }
3544
- continue;
3545
- }
3546
- if (inBlock) {
3547
- if (ch === "*" && next === "/") {
3548
- inBlock = false;
3549
- i++;
3550
- }
3551
- continue;
3552
- }
3553
- if (inString) {
3554
- out += ch;
3555
- if (ch === "\\") {
3556
- out += next ?? "";
3557
- i++;
3558
- } else if (ch === "\"") inString = false;
3559
- continue;
3560
- }
3561
- if (ch === "\"") {
3562
- inString = true;
3563
- out += ch;
3564
- continue;
3565
- }
3566
- if (ch === "/" && next === "/") {
3567
- inLine = true;
3568
- i++;
3569
- continue;
3570
- }
3571
- if (ch === "/" && next === "*") {
3572
- inBlock = true;
3573
- i++;
3574
- continue;
3575
- }
3576
- out += ch;
3577
- }
3578
- return out.replace(/,(\s*[}\]])/g, "$1");
3579
- }
3580
- /** `Object(value) === value` holds for exactly the blocks and lists
3581
- * `JSON.parse` produces; the `[object Object]` tag separates the two. */
3582
- function isJsonBlock(value) {
3583
- return Object(value) === value && Object.prototype.toString.call(value) === "[object Object]";
3584
- }
3585
- /** A block or a list. A declared `paths` OWNS the setting and blocks its
3586
- * parents' even when it contributes nothing usable. */
3587
- function isJsonKeyed(value) {
3588
- return Object(value) === value;
3589
- }
3590
- /** A JSON string, excluding the boxed `String` object, which no path join
3591
- * accepts. */
3592
- function isJsonString(value) {
3593
- return Object(value) !== value && Object.prototype.toString.call(value) === "[object String]";
3594
- }
3595
- function decodePaths(value) {
3596
- if (!isJsonKeyed(value)) return null;
3597
- const decoded = /* @__PURE__ */ new Map();
3598
- for (const [pattern, targets] of Object.entries(value)) {
3599
- const [first] = Array.isArray(targets) ? targets : [];
3600
- if (isJsonString(first)) decoded.set(pattern, first);
3601
- }
3602
- return decoded;
3603
- }
3604
- /** TypeScript accepts one specifier or an array; a non-string member names
3605
- * no config and is dropped here. */
3606
- function decodeExtends(value) {
3607
- return (Array.isArray(value) ? value : [value]).filter(isJsonString);
3608
- }
3609
- function readConfig(path) {
3610
- let raw;
3611
- try {
3612
- raw = readFileSync(path, "utf-8");
3613
- } catch {
3614
- return null;
3615
- }
3616
- let parsed;
3617
- try {
3618
- parsed = JSON.parse(stripJsonc(raw));
3619
- } catch {
3620
- return null;
3621
- }
3622
- const root = isJsonBlock(parsed) ? parsed : {};
3623
- const compilerOptions = isJsonBlock(root.compilerOptions) ? root.compilerOptions : {};
3624
- return {
3625
- dir: dirname$1(path),
3626
- baseUrl: isJsonString(compilerOptions.baseUrl) ? compilerOptions.baseUrl : null,
3627
- paths: decodePaths(compilerOptions.paths),
3628
- extends: decodeExtends(root.extends)
3629
- };
3630
- }
3631
- function resolveExtendsTarget(specifier, fromDir) {
3632
- if (specifier.startsWith(".") || isAbsolute(specifier)) {
3633
- const base = resolve(fromDir, specifier);
3634
- for (const candidate of [
3635
- base,
3636
- `${base}.json`,
3637
- join$1(base, "tsconfig.json")
3638
- ]) if (existsSync(candidate)) return candidate;
3639
- return null;
3640
- }
3641
- try {
3642
- return __require.resolve(specifier.endsWith(".json") ? specifier : `${specifier}/tsconfig.json`, { paths: [fromDir] });
3643
- } catch {
3644
- try {
3645
- return __require.resolve(specifier, { paths: [fromDir] });
3646
- } catch {
3647
- return null;
3648
- }
3649
- }
3650
- }
3651
- /** Child-first flattened extends chain starting at `entryPath`. */
3652
- function loadChain(entryPath) {
3653
- const chain = [];
3654
- const visited = /* @__PURE__ */ new Set();
3655
- const queue = [entryPath];
3656
- while (queue.length > 0) {
3657
- const path = queue.shift();
3658
- const key = resolve(path);
3659
- if (visited.has(key)) continue;
3660
- visited.add(key);
3661
- const node = readConfig(key);
3662
- if (!node) continue;
3663
- chain.push(node);
3664
- for (const parent of node.extends) {
3665
- const resolved = resolveExtendsTarget(parent, node.dir);
3666
- if (resolved) queue.push(resolved);
3667
- }
3668
- }
3669
- return chain;
3670
- }
3671
- /**
3672
- * The project's tsconfig path aliases as `buildPathAliasesJson` pairs; empty
3673
- * when no readable tsconfig, or no usable `paths`, exists.
3674
- */
3675
- function readTsconfigAliasPairs(rootDir) {
3676
- const chain = loadChain(join$1(rootDir, "tsconfig.json"));
3677
- if (chain.length === 0) return [];
3678
- const pathsOwner = chain.find((node) => node.paths !== null);
3679
- const paths = pathsOwner?.paths ?? null;
3680
- if (pathsOwner === void 0 || paths === null) return [];
3681
- const baseOwner = chain.find((node) => node.baseUrl !== null);
3682
- const baseUrl = baseOwner?.baseUrl ?? null;
3683
- const base = baseOwner !== void 0 && baseUrl !== null ? resolve(baseOwner.dir, baseUrl) : pathsOwner.dir;
3684
- const pairs = [];
3685
- for (const [pattern, target] of paths) {
3686
- const patternStars = pattern.split("*").length - 1;
3687
- const targetStars = target.split("*").length - 1;
3688
- if (pattern === "*" || patternStars > 1 || targetStars > 1) continue;
3689
- if (pattern.endsWith("/*")) {
3690
- if (!target.endsWith("/*")) continue;
3691
- pairs.push({
3692
- pattern: pattern.slice(0, -2),
3693
- target: resolve(base, target.slice(0, -2)),
3694
- kind: "prefix"
3695
- });
3696
- continue;
3697
- }
3698
- if (patternStars > 0 || targetStars > 0) continue;
3699
- pairs.push({
3700
- pattern,
3701
- target: resolve(base, target),
3702
- kind: "exact"
3703
- });
3704
- }
3705
- return pairs;
3706
- }
3707
- //#endregion
3708
4026
  //#region pipeline/error-diagnostics.ts
3709
4027
  function assertNoErrorDiagnostics(diagnostics) {
3710
4028
  const errors = (diagnostics ?? []).filter((diagnostic) => diagnostic.kind === "error");
@@ -3789,4 +4107,4 @@ function realpathThroughNearestExistingAncestor(path) {
3789
4107
  }
3790
4108
  }
3791
4109
  //#endregion
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 };
4110
+ export { DRIVER_NAMESPACES as $, createSourceCorpus as A, contentHash as At, unresolvableIncludesMessage as B, resolveThroughPathAliases as C, noKitFilesDiagnostics as Ct, reportSurvivingAssetPlaceholders as D, unresolvedParentName as Dt, generatedModuleCode as E, unreadableSourceDiagnostic as Et, findPackageRoot as F, validateLayerOrder as Ft, createSourceIdentity as G, isJsonBlock as H, firstOwners as I, __require as It, compareDiscoveryOrder as J, isPathWithinRoot as K, isExcludedPackageRelativePath as L, engineImportParser as M, ANIMUS_LAYERS as Mt, excludeCollectedPackages as N, assembleStylesheet as Nt, substituteAssetPlaceholders as O, vocabularyWitnessDiagnostics as Ot, extractSystemFilePackages as P, stripLeadingLayerDeclaration as Pt, DEFAULT_EXCLUDE as Q, resolveAbsolutePathSpecifier as R, resolveAssetFile as S, isUnresolvedParentDrop as St, findSheetAssetSpecifiers as T, systemLoadDiagnostics as Tt, isJsonString as U, walkPackageSources as V, readTsconfigAliasPairs as W, AnimusConfigError as X, discoverFiles as Y, CORE_OPTION_KEYS as Z, applyUnitFallback as _, preprocessMdx as _t, assertNoErrorDiagnostics as a, resolveVerbosity as at, buildPathAliasesJson as b, VOCABULARY_LEGACY_VERB as bt, runStructuralSelfCheck as c, createV2EngineApi as ct, serializeStaticCss as d, parseFilesJson as dt, REPLACEABLE_DEFAULT_EXCLUDE as et, camelToKebab as f, projectExternalFileOwners as ft, runProjectAnalysis as g, isEngineTransformExtension as gt, clearEngineCache as h, ENGINE_TRANSFORM_EXTENSIONS as ht, snapshotFilePlans as i, resolveMode as it, collectExternalPackageSources as j, buildAnalyzeProjectArgs as jt, substituteSheetAssets as k, parseInternalWire as kt, loadSystemConfig as l, ingestSourceEntries as lt, buildAnalysisInputs as m, DEFAULT_EXTENSIONS as mt, diffFilePlans as n, assertKnownOptionKeys as nt, buildSystemPropsModule as o, RETIRED_ENGINE_MESSAGE as ot, stableStringify as p, adaptSvelteSource as pt, sharesVolumeRoot as q, hashReplacementPlans as r, createExcludeMatcher as rt, buildDynamicPropConfig as s, assertNoRetiredEngineSelection as st, toWatchKeys as t, STRUCTURAL_EXCLUDE as tt, applyPrefix as u, isAdvisorySourceDiagnostic as ut, postProcessCss as v, INVALID_PROPERTY_REGISTRATION as vt, findAssetSpecifiers as w, surfaceManifestDiagnostics as wt, enforceExternalTokenContracts as x, isDeletedSource as xt, resolveLightningTargets as y, VOCABULARY_COLLISION as yt, staleDistIncludesMessage as z };