@animus-ui/extract 0.1.18 → 0.1.19

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 (35) hide show
  1. package/README.md +2 -2
  2. package/crates/extract-v2/animus-extract-v2.darwin-arm64.node +0 -0
  3. package/crates/extract-v2/animus-extract-v2.linux-arm64-gnu.node +0 -0
  4. package/crates/extract-v2/animus-extract-v2.linux-x64-gnu.node +0 -0
  5. package/crates/extract-v2/index.d.ts +5 -0
  6. package/dist/index.cjs +4 -2
  7. package/dist/index.mjs +2 -2
  8. package/dist/pipeline/asset-placeholders.d.ts +19 -0
  9. package/dist/pipeline/asset-placeholders.d.ts.map +1 -1
  10. package/dist/pipeline/engine-retirement.d.ts.map +1 -1
  11. package/dist/pipeline/index.d.ts +2 -2
  12. package/dist/pipeline/index.d.ts.map +1 -1
  13. package/dist/pipeline/manifest-diagnostics.d.ts +26 -1
  14. package/dist/pipeline/manifest-diagnostics.d.ts.map +1 -1
  15. package/dist/pipeline/manifest-schema.d.ts +5 -2
  16. package/dist/pipeline/manifest-schema.d.ts.map +1 -1
  17. package/dist/pipeline/property-diagnostics.d.ts +17 -0
  18. package/dist/pipeline/property-diagnostics.d.ts.map +1 -0
  19. package/dist/pipeline/property-registrations.d.ts +7 -0
  20. package/dist/pipeline/property-registrations.d.ts.map +1 -1
  21. package/dist/pipeline/run-analysis.d.ts +2 -0
  22. package/dist/pipeline/run-analysis.d.ts.map +1 -1
  23. package/dist/pipeline/system-config.d.ts +3 -0
  24. package/dist/pipeline/system-config.d.ts.map +1 -1
  25. package/dist/{pipeline-Dr-I4Xhl.mjs → pipeline-BuiItKT-.mjs} +369 -111
  26. package/dist/{pipeline-OKnmnyFu.cjs → pipeline-CD2qmEEn.cjs} +384 -114
  27. package/dist/session/extraction-session.d.ts +3 -2
  28. package/dist/session/extraction-session.d.ts.map +1 -1
  29. package/dist/session/timing.d.ts.map +1 -1
  30. package/dist/session.cjs +19 -14
  31. package/dist/session.mjs +19 -14
  32. package/index-v2.js +1 -1
  33. package/package.json +4 -4
  34. package/dist/pipeline/timing-waterfall.d.ts +0 -6
  35. package/dist/pipeline/timing-waterfall.d.ts.map +0 -1
@@ -1166,7 +1166,7 @@ function createV2EngineApi(deps) {
1166
1166
  }
1167
1167
  //#endregion
1168
1168
  //#region pipeline/engine-retirement.ts
1169
- const RETIRED_ENGINE_MESSAGE = "animus: extraction engine 'v1' was retired (openspec: retire-extract-v1) — v2 is the only engine; remove the engine option / ANIMUS_ENGINE override";
1169
+ const RETIRED_ENGINE_MESSAGE = "animus: the 'v1' extraction engine is no longer supported, and v2 is the only engine. Remove the engine option from your Animus config and unset the ANIMUS_ENGINE environment variable.";
1170
1170
  function assertNoRetiredEngineSelection(engineOption) {
1171
1171
  if (engineOption === "v1" || process.env.ANIMUS_ENGINE === "v1") throw new Error(RETIRED_ENGINE_MESSAGE);
1172
1172
  }
@@ -2023,6 +2023,31 @@ function substituteAssetPlaceholders(css, urlBySpecifier) {
2023
2023
  }
2024
2024
  return out;
2025
2025
  }
2026
+ function findSheetAssetSpecifiers(sheets) {
2027
+ return [.../* @__PURE__ */ new Set([
2028
+ ...findAssetSpecifiers(sheets.variableCss),
2029
+ ...findAssetSpecifiers(sheets.globalCss),
2030
+ ...findAssetSpecifiers(sheets.componentCss)
2031
+ ])];
2032
+ }
2033
+ /** The one substitution step every emitted sheet passes through. */
2034
+ function substituteSheetAssets(sheets, urlBySpecifier) {
2035
+ return {
2036
+ variableCss: substituteAssetPlaceholders(sheets.variableCss, urlBySpecifier),
2037
+ globalCss: substituteAssetPlaceholders(sheets.globalCss, urlBySpecifier),
2038
+ componentCss: substituteAssetPlaceholders(sheets.componentCss, urlBySpecifier)
2039
+ };
2040
+ }
2041
+ const UNSUBSTITUTED_ASSET_CODE = "animus.asset.unsubstituted-placeholder";
2042
+ /** A placeholder in emitted CSS is a broken URL in the browser: it fails a
2043
+ * strict build and warns otherwise. */
2044
+ function reportSurvivingAssetPlaceholders(css, report) {
2045
+ const specifiers = findAssetSpecifiers(css);
2046
+ if (specifiers.length === 0) return;
2047
+ const message = `${report.prefix} asset() placeholders reached emitted CSS unsubstituted: ${specifiers.join(", ")} (${UNSUBSTITUTED_ASSET_CODE})`;
2048
+ if (report.strict) throw new Error(message);
2049
+ report.warn(message);
2050
+ }
2026
2051
  //#endregion
2027
2052
  //#region pipeline/resolve-asset.ts
2028
2053
  /**
@@ -2320,12 +2345,30 @@ const INVALID_PROPERTY_REGISTRATION = "animus.theme.invalid-property-registratio
2320
2345
  /** Minted when a sealed kit with registered vocabulary is consumed through
2321
2346
  * the deprecated `from()`/`includes:` verbs. */
2322
2347
  const VOCABULARY_LEGACY_VERB = "animus.vocabulary.legacy-verb";
2348
+ /** A `var()` fallback that never applies: the property is registered with an
2349
+ * initial value. */
2350
+ const PROPERTY_FALLBACK_SUPPRESSED = "animus.property.fallback-suppressed";
2351
+ /** The same, where the fallback heads a chain of further `var()` reads. */
2352
+ const PROPERTY_FALLBACK_CHAIN_SUPPRESSED = "animus.property.fallback-chain-suppressed";
2353
+ /** A declared property set in keyframes or transitioned without a typed
2354
+ * registration, so it does not interpolate. */
2355
+ const PROPERTY_UNREGISTERED_ANIMATION = "animus.property.unregistered-animation";
2356
+ /** The same, where `allow-discrete` shows interpolation was intended. */
2357
+ const PROPERTY_DISCRETE_ANIMATION = "animus.property.discrete-animation";
2358
+ /** A custom property whose resolved value is `var()` of itself, which makes
2359
+ * it invalid at computed-value time. */
2360
+ const PROPERTY_SELF_REFERENCE = "animus.property.self-reference";
2323
2361
  const DIAGNOSTIC_SEVERITY = /* @__PURE__ */ new Map([
2324
2362
  [SELECTOR_UNSUPPORTED_SUBJECT, "error"],
2325
2363
  [UNREADABLE_SOURCE_FILE, "error"],
2326
2364
  [VOCABULARY_COLLISION, "warn"],
2327
2365
  [VOCABULARY_LEGACY_VERB, "warn"],
2328
- [INVALID_PROPERTY_REGISTRATION, "error"]
2366
+ [INVALID_PROPERTY_REGISTRATION, "error"],
2367
+ [PROPERTY_FALLBACK_SUPPRESSED, "info"],
2368
+ [PROPERTY_FALLBACK_CHAIN_SUPPRESSED, "warn"],
2369
+ [PROPERTY_UNREGISTERED_ANIMATION, "info"],
2370
+ [PROPERTY_DISCRETE_ANIMATION, "warn"],
2371
+ [PROPERTY_SELF_REFERENCE, "warn"]
2329
2372
  ]);
2330
2373
  /** An unlisted code is `warn`: a witness kind from a newer system package
2331
2374
  * must not fail the strict build of a host that predates its writer. */
@@ -2390,9 +2433,13 @@ function surfaceManifestDiagnostics(manifest, warn, policy = {}) {
2390
2433
  let line = null;
2391
2434
  if (diagnostic.kind === "bail") line = `⚠ ${diagnostic.component} not extracted: ${diagnostic.message}`;
2392
2435
  else if (diagnostic.kind === "skip") line = `⚠ ${diagnostic.component}: skipped ${diagnostic.message}`;
2393
- else if (diagnostic.kind === "warn") line = `⚠ ${diagnostic.file}: ${diagnostic.component}: ${diagnostic.message}`;
2436
+ else if (diagnostic.kind === "warn") line = `${diagnostic.severity === "info" ? "ℹ" : "⚠"} ${diagnostic.file}: ${diagnostic.component}: ${diagnostic.message}`;
2394
2437
  if (line === null) continue;
2395
2438
  if (diagnostic.code && !diagnostic.message.includes(diagnostic.code)) line += ` [${diagnostic.code}]`;
2439
+ if (diagnostic.severity === "info") {
2440
+ policy.info?.(line);
2441
+ continue;
2442
+ }
2396
2443
  if (policy.strict && diagnostic.severity === "error") {
2397
2444
  errors.push(`${diagnostic.code ?? "error"} — ${diagnostic.component}: ${diagnostic.message}`);
2398
2445
  continue;
@@ -2402,6 +2449,315 @@ function surfaceManifestDiagnostics(manifest, warn, policy = {}) {
2402
2449
  if (errors.length > 0) throw new Error(`[animus] strict: ${errors.length} error diagnostic(s):\n${errors.join("\n")}`);
2403
2450
  }
2404
2451
  //#endregion
2452
+ //#region pipeline/property-registrations.ts
2453
+ const PROPERTY_RULE = /@property\s+(--[\w-]+)\s*\{[^}]*\}\n?/g;
2454
+ /** The data types a registration's `syntax` may name. */
2455
+ const SYNTAX_TYPES = /* @__PURE__ */ new Set([
2456
+ "angle",
2457
+ "color",
2458
+ "custom-ident",
2459
+ "image",
2460
+ "integer",
2461
+ "length",
2462
+ "length-percentage",
2463
+ "number",
2464
+ "percentage",
2465
+ "resolution",
2466
+ "string",
2467
+ "time",
2468
+ "transform-function",
2469
+ "transform-list",
2470
+ "url"
2471
+ ]);
2472
+ const SYNTAX_COMPONENT = /^(?:<([a-z-]+)>[+#]?|-?[a-zA-Z_][\w-]*)$/;
2473
+ /** The syntax types whose values can carry a font- or container-relative
2474
+ * unit, which makes a typed initial value depend on context. */
2475
+ const DIMENSION_TYPES = /* @__PURE__ */ new Set([
2476
+ "image",
2477
+ "length",
2478
+ "length-percentage",
2479
+ "transform-function",
2480
+ "transform-list"
2481
+ ]);
2482
+ const RELATIVE_UNIT = /(?:\d|\.)(?:r?em|r?ex|r?cap|r?ch|r?ic|r?lh|cq(?:w|h|i|b|min|max))\b/i;
2483
+ const SUBSTITUTION = /\b(?:var|env|attr)\(/i;
2484
+ /** Quoted strings and `url()` hold text, never units or substitutions. */
2485
+ const LITERAL_TEXT = /"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|url\([^)]*\)/gi;
2486
+ /**
2487
+ * Removes the `@property` rules browsers would ignore — an unknown syntax
2488
+ * component, a syntax other than `*` without an initial value, an initial
2489
+ * value that depends on context, or one the syntax does not accept.
2490
+ * Minifying a stylesheet that contains such a rule can also fail.
2491
+ */
2492
+ function splitInvalidPropertyRegistrations(css) {
2493
+ const invalid = [];
2494
+ return {
2495
+ css: css.replace(PROPERTY_RULE, (rule, name) => {
2496
+ const reason = registrationFailure(rule);
2497
+ if (reason === null) return rule;
2498
+ invalid.push({
2499
+ name,
2500
+ reason
2501
+ });
2502
+ return "";
2503
+ }),
2504
+ invalid
2505
+ };
2506
+ }
2507
+ /** The registrations in `css`, by custom-property name. */
2508
+ function propertyRegistrations(css) {
2509
+ const registrations = /* @__PURE__ */ new Map();
2510
+ for (const match of css.matchAll(PROPERTY_RULE)) registrations.set(match[1], readRegistration(match[0]));
2511
+ return registrations;
2512
+ }
2513
+ function readRegistration(rule) {
2514
+ return {
2515
+ syntax: /syntax:\s*"([^"]*)"/.exec(rule)?.[1]?.trim() ?? "",
2516
+ initialValue: /initial-value\s*:\s*([^;}]*)/.exec(rule)?.[1]?.trim()
2517
+ };
2518
+ }
2519
+ function registrationFailure(rule) {
2520
+ const { syntax, initialValue } = readRegistration(rule);
2521
+ if (syntax !== "*") {
2522
+ const components = syntax.split("|").map((component) => ({
2523
+ text: component.trim(),
2524
+ match: SYNTAX_COMPONENT.exec(component.trim())
2525
+ }));
2526
+ const unknown = components.find(({ match }) => match === null || match[1] !== void 0 && !SYNTAX_TYPES.has(match[1]));
2527
+ if (unknown !== void 0) return `syntax "${syntax}" has the unknown component "${unknown.text}"`;
2528
+ if (initialValue === void 0) return `syntax "${syntax}" needs an initialValue`;
2529
+ const dimensional = components.some(({ match }) => match?.[1] !== void 0 && DIMENSION_TYPES.has(match[1]));
2530
+ const value = initialValue.replace(LITERAL_TEXT, "");
2531
+ if (SUBSTITUTION.test(value) || dimensional && RELATIVE_UNIT.test(value)) return `initialValue "${initialValue}" depends on context; font- and container-relative units and var(), env() and attr() are not allowed`;
2532
+ }
2533
+ try {
2534
+ transform({
2535
+ filename: "animus-registrations.css",
2536
+ code: Buffer.from(rule)
2537
+ });
2538
+ return null;
2539
+ } catch (error) {
2540
+ const detail = error instanceof Error ? error.message : String(error);
2541
+ return `the CSS parser rejects \`${rule.trim()}\` (${detail})`;
2542
+ }
2543
+ }
2544
+ //#endregion
2545
+ //#region pipeline/property-diagnostics.ts
2546
+ /**
2547
+ * Reports custom properties that will not behave as their CSS suggests: a
2548
+ * property that refers to itself and, once the system emits an `@property`
2549
+ * rule, a fallback an initial value suppresses and an animated property that
2550
+ * cannot interpolate. The CSS itself is never changed.
2551
+ */
2552
+ function checkCustomProperties(input) {
2553
+ const registered = propertyRegistrations(input.system.variableCss);
2554
+ const declared = declaredProperties(input.system.contextualVarsJson);
2555
+ const findings = [];
2556
+ const sheets = [{
2557
+ css: input.componentCss,
2558
+ owner: componentOwner(input.manifest)
2559
+ }, {
2560
+ css: input.globalCss,
2561
+ owner: globalOwner
2562
+ }];
2563
+ for (const { css, owner } of sheets) {
2564
+ const parsed = declarations(css);
2565
+ const discreteRules = new Set(parsed.filter((d) => d.property === "transition-behavior" && /\ballow-discrete\b/.test(d.value)).map((d) => d.rule));
2566
+ for (const declaration of parsed) {
2567
+ const found = (code, property, message) => findings.push({
2568
+ code,
2569
+ property,
2570
+ message,
2571
+ owner: owner(declaration)
2572
+ });
2573
+ if (isSelfReference(declaration)) {
2574
+ found(PROPERTY_SELF_REFERENCE, declaration.property, `${declaration.property} resolves to var(${declaration.property}), a reference to itself, so it is invalid at computed-value time. Give it a value other than its own name`);
2575
+ continue;
2576
+ }
2577
+ if (registered.size === 0) continue;
2578
+ for (const read of varReads(declaration.value)) {
2579
+ const registration = registered.get(read.name);
2580
+ if (read.fallback === void 0 || registration?.initialValue === void 0) continue;
2581
+ const chain = read.fallback.startsWith("var(");
2582
+ found(chain ? PROPERTY_FALLBACK_CHAIN_SUPPRESSED : PROPERTY_FALLBACK_SUPPRESSED, read.name, chain ? `var(${read.name}, ${read.fallback}) never reads its fallback chain: ${read.name} is registered with initial-value ${registration.initialValue}, which applies whenever it is unset. Remove the initial value or reorder the chain` : `var(${read.name}, ${read.fallback}) never uses its fallback: ${read.name} is registered with initial-value ${registration.initialValue}, which applies whenever it is unset`);
2583
+ }
2584
+ for (const animated of animatedProperties(declaration, discreteRules)) {
2585
+ if (!declared.has(animated.name)) continue;
2586
+ const reason = interpolationGap(registered.get(animated.name));
2587
+ if (reason === null) continue;
2588
+ if (animated.discrete) found(PROPERTY_DISCRETE_ANIMATION, animated.name, `${animated.name} is transitioned with allow-discrete but ${reason}, so it flips at the midpoint instead of interpolating. Register it with a typed syntax`);
2589
+ else found(PROPERTY_UNREGISTERED_ANIMATION, animated.name, `${animated.name} is ${animated.how} but ${reason}, so it changes in one step instead of interpolating. Register it with a typed syntax to animate it`);
2590
+ }
2591
+ }
2592
+ }
2593
+ return toDiagnostics(findings);
2594
+ }
2595
+ function declaredProperties(contextualVarsJson) {
2596
+ if (!contextualVarsJson) return /* @__PURE__ */ new Set();
2597
+ const byScale = parseInternalWire(contextualVarsJson, "contextualVarsJson (the theme's contextual variable names)");
2598
+ return new Set(Object.values(byScale).flatMap((names) => names.map((name) => `--${name}`)));
2599
+ }
2600
+ /** Declarations in engine-emitted CSS: one `property: value` per `;`. */
2601
+ function declarations(css) {
2602
+ const found = [];
2603
+ const stack = [];
2604
+ let rules = 0;
2605
+ let segment = 0;
2606
+ let parens = 0;
2607
+ let quote = null;
2608
+ const take = (end) => {
2609
+ const text = css.slice(segment, end);
2610
+ const colon = text.indexOf(":");
2611
+ const current = stack[stack.length - 1];
2612
+ if (current === void 0 || colon <= 0) return;
2613
+ found.push({
2614
+ property: text.slice(0, colon).trim(),
2615
+ value: text.slice(colon + 1).replace(/;$/, "").trim(),
2616
+ start: segment + (text.length - text.trimStart().length),
2617
+ end,
2618
+ rule: current.rule,
2619
+ context: stack.map((entry) => entry.prelude)
2620
+ });
2621
+ };
2622
+ for (let i = 0; i < css.length; i += 1) {
2623
+ const c = css[i];
2624
+ if (quote !== null) {
2625
+ if (c === "\\") i += 1;
2626
+ else if (c === quote) quote = null;
2627
+ continue;
2628
+ }
2629
+ if (c === "\"" || c === "'") quote = c;
2630
+ else if (c === "(") parens += 1;
2631
+ else if (c === ")") parens -= 1;
2632
+ else if (parens === 0 && c === "{") {
2633
+ stack.push({
2634
+ prelude: css.slice(segment, i).trim(),
2635
+ rule: rules
2636
+ });
2637
+ rules += 1;
2638
+ segment = i + 1;
2639
+ } else if (parens === 0 && c === ";") {
2640
+ take(i + 1);
2641
+ segment = i + 1;
2642
+ } else if (parens === 0 && c === "}") {
2643
+ take(i);
2644
+ stack.pop();
2645
+ segment = i + 1;
2646
+ }
2647
+ }
2648
+ return found;
2649
+ }
2650
+ function isSelfReference(declaration) {
2651
+ return declaration.property.startsWith("--") && declaration.value.replace(/\s+/g, "") === `var(${declaration.property})`;
2652
+ }
2653
+ /** Every `var()` read in `value`, nested fallbacks included. */
2654
+ function varReads(value) {
2655
+ const reads = [];
2656
+ for (const match of value.matchAll(/var\(\s*(--[\w-]+)\s*/g)) {
2657
+ const after = (match.index ?? 0) + match[0].length;
2658
+ if (value[after] !== ",") {
2659
+ reads.push({
2660
+ name: match[1],
2661
+ fallback: void 0
2662
+ });
2663
+ continue;
2664
+ }
2665
+ let depth = 1;
2666
+ let close = after + 1;
2667
+ for (; close < value.length && depth > 0; close += 1) if (value[close] === "(") depth += 1;
2668
+ else if (value[close] === ")") depth -= 1;
2669
+ reads.push({
2670
+ name: match[1],
2671
+ fallback: value.slice(after + 1, close - 1).trim()
2672
+ });
2673
+ }
2674
+ return reads;
2675
+ }
2676
+ /** Custom properties this declaration sets in a keyframe or names in a
2677
+ * transition. */
2678
+ function animatedProperties(declaration, discreteRules) {
2679
+ const keyframes = declaration.context.find((prelude) => prelude.startsWith("@keyframes"));
2680
+ if (keyframes !== void 0 && declaration.property.startsWith("--")) return [{
2681
+ name: declaration.property,
2682
+ how: `set in ${keyframes}`,
2683
+ discrete: false
2684
+ }];
2685
+ if (declaration.property !== "transition" && declaration.property !== "transition-property") return [];
2686
+ return topLevelItems(declaration.value).flatMap((item) => {
2687
+ const property = item.trim().split(/\s+/)[0] ?? "";
2688
+ return property.startsWith("--") ? [{
2689
+ name: property,
2690
+ how: "transitioned",
2691
+ discrete: /\ballow-discrete\b/.test(item) || discreteRules.has(declaration.rule)
2692
+ }] : [];
2693
+ });
2694
+ }
2695
+ function topLevelItems(value) {
2696
+ const items = [];
2697
+ let depth = 0;
2698
+ let start = 0;
2699
+ for (let i = 0; i < value.length; i += 1) if (value[i] === "(") depth += 1;
2700
+ else if (value[i] === ")") depth -= 1;
2701
+ else if (value[i] === "," && depth === 0) {
2702
+ items.push(value.slice(start, i));
2703
+ start = i + 1;
2704
+ }
2705
+ items.push(value.slice(start));
2706
+ return items;
2707
+ }
2708
+ /** Why a property cannot interpolate, or `null` when it can. */
2709
+ function interpolationGap(registration) {
2710
+ if (registration === void 0) return "it is not registered";
2711
+ if (registration.syntax === "*") return "it is registered with the universal syntax \"*\"";
2712
+ return null;
2713
+ }
2714
+ function componentOwner(manifest) {
2715
+ const components = Object.values(manifest.components);
2716
+ return (declaration) => {
2717
+ const selector = [...declaration.context].reverse().find((prelude) => !prelude.startsWith("@"));
2718
+ const classes = (selector ?? "").match(/\.-?[_a-zA-Z][\w-]*/g) ?? [];
2719
+ for (const dotted of classes) {
2720
+ const name = dotted.slice(1);
2721
+ const owner = components.find((component) => name === component.class_name || name.startsWith(`${component.class_name}--`) || component.replacement.includes(`"${name}"`));
2722
+ if (owner) return {
2723
+ file: owner.file,
2724
+ component: owner.binding
2725
+ };
2726
+ }
2727
+ return {
2728
+ file: "styles",
2729
+ component: selector ?? "stylesheet"
2730
+ };
2731
+ };
2732
+ }
2733
+ function globalOwner(declaration) {
2734
+ const keyframes = declaration.context.find((prelude) => prelude.startsWith("@keyframes"));
2735
+ const selector = [...declaration.context].reverse().find((prelude) => !prelude.startsWith("@"));
2736
+ return {
2737
+ file: "system",
2738
+ component: keyframes ?? selector ?? "global styles"
2739
+ };
2740
+ }
2741
+ /** One diagnostic per code, property and owner. */
2742
+ function toDiagnostics(findings) {
2743
+ const seen = /* @__PURE__ */ new Set();
2744
+ const diagnostics = [];
2745
+ for (const finding of findings) {
2746
+ const key = `${finding.code}|${finding.property}|${finding.owner.file}|${finding.owner.component}`;
2747
+ if (seen.has(key)) continue;
2748
+ seen.add(key);
2749
+ diagnostics.push({
2750
+ file: finding.owner.file,
2751
+ component: finding.owner.component,
2752
+ kind: "warn",
2753
+ message: `${finding.message} (${finding.code})`,
2754
+ code: finding.code,
2755
+ severity: severityFor(finding.code)
2756
+ });
2757
+ }
2758
+ return diagnostics;
2759
+ }
2760
+ //#endregion
2405
2761
  //#region pipeline/unit-fallback.ts
2406
2762
  /** A digit run after one of these continues a word (`#b1b1b7`, `ss01`,
2407
2763
  * `U+0025`), so it is not a bare number. The extractor predicts this pass
@@ -2498,11 +2854,19 @@ function runProjectAnalysis(engineApi, opts) {
2498
2854
  const extractMs = Math.round(performance.now() - t);
2499
2855
  t = performance.now();
2500
2856
  const manifest = JSON.parse(manifestJson);
2857
+ const propertyDiagnostics = checkCustomProperties({
2858
+ system: opts.system,
2859
+ manifest,
2860
+ componentCss: applyUnitFallback(manifest.css),
2861
+ globalCss: manifest.sheets.global
2862
+ });
2501
2863
  surfaceManifestDiagnostics(manifest, opts.warn, {
2502
2864
  strict: opts.strict,
2865
+ info: opts.info,
2503
2866
  prepend: [
2504
2867
  ...collectSelectorAliasDiagnostics(opts.system.selectorAliasesJson),
2505
2868
  ...systemLoadDiagnostics(opts.system),
2869
+ ...propertyDiagnostics,
2506
2870
  ...opts.extraDiagnostics ?? []
2507
2871
  ]
2508
2872
  });
@@ -2597,88 +2961,6 @@ function applyPrefix(prefix, variableMapJson, variableCss, themeJson, contextual
2597
2961
  return result;
2598
2962
  }
2599
2963
  //#endregion
2600
- //#region pipeline/property-registrations.ts
2601
- const PROPERTY_RULE = /@property\s+(--[\w-]+)\s*\{[^}]*\}\n?/g;
2602
- /** The data types a registration's `syntax` may name. */
2603
- const SYNTAX_TYPES = /* @__PURE__ */ new Set([
2604
- "angle",
2605
- "color",
2606
- "custom-ident",
2607
- "image",
2608
- "integer",
2609
- "length",
2610
- "length-percentage",
2611
- "number",
2612
- "percentage",
2613
- "resolution",
2614
- "string",
2615
- "time",
2616
- "transform-function",
2617
- "transform-list",
2618
- "url"
2619
- ]);
2620
- const SYNTAX_COMPONENT = /^(?:<([a-z-]+)>[+#]?|-?[a-zA-Z_][\w-]*)$/;
2621
- /** The syntax types whose values can carry a font- or container-relative
2622
- * unit, which makes a typed initial value depend on context. */
2623
- const DIMENSION_TYPES = /* @__PURE__ */ new Set([
2624
- "image",
2625
- "length",
2626
- "length-percentage",
2627
- "transform-function",
2628
- "transform-list"
2629
- ]);
2630
- const RELATIVE_UNIT = /(?:\d|\.)(?:r?em|r?ex|r?cap|r?ch|r?ic|r?lh|cq(?:w|h|i|b|min|max))\b/i;
2631
- const SUBSTITUTION = /\b(?:var|env|attr)\(/i;
2632
- /** Quoted strings and `url()` hold text, never units or substitutions. */
2633
- const LITERAL_TEXT = /"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'|url\([^)]*\)/gi;
2634
- /**
2635
- * Removes the `@property` rules browsers would ignore — an unknown syntax
2636
- * component, a syntax other than `*` without an initial value, an initial
2637
- * value that depends on context, or one the syntax does not accept.
2638
- * Minifying a stylesheet that contains such a rule can also fail.
2639
- */
2640
- function splitInvalidPropertyRegistrations(css) {
2641
- const invalid = [];
2642
- return {
2643
- css: css.replace(PROPERTY_RULE, (rule, name) => {
2644
- const reason = registrationFailure(rule);
2645
- if (reason === null) return rule;
2646
- invalid.push({
2647
- name,
2648
- reason
2649
- });
2650
- return "";
2651
- }),
2652
- invalid
2653
- };
2654
- }
2655
- function registrationFailure(rule) {
2656
- const syntax = /syntax:\s*"([^"]*)"/.exec(rule)?.[1]?.trim() ?? "";
2657
- const initialValue = /initial-value\s*:\s*([^;}]*)/.exec(rule)?.[1]?.trim();
2658
- if (syntax !== "*") {
2659
- const components = syntax.split("|").map((component) => ({
2660
- text: component.trim(),
2661
- match: SYNTAX_COMPONENT.exec(component.trim())
2662
- }));
2663
- const unknown = components.find(({ match }) => match === null || match[1] !== void 0 && !SYNTAX_TYPES.has(match[1]));
2664
- if (unknown !== void 0) return `syntax "${syntax}" has the unknown component "${unknown.text}"`;
2665
- if (initialValue === void 0) return `syntax "${syntax}" needs an initialValue`;
2666
- const dimensional = components.some(({ match }) => match?.[1] !== void 0 && DIMENSION_TYPES.has(match[1]));
2667
- const value = initialValue.replace(LITERAL_TEXT, "");
2668
- if (SUBSTITUTION.test(value) || dimensional && RELATIVE_UNIT.test(value)) return `initialValue "${initialValue}" depends on context; font- and container-relative units and var(), env() and attr() are not allowed`;
2669
- }
2670
- try {
2671
- transform({
2672
- filename: "animus-registrations.css",
2673
- code: Buffer.from(rule)
2674
- });
2675
- return null;
2676
- } catch (error) {
2677
- const detail = error instanceof Error ? error.message : String(error);
2678
- return `the CSS parser rejects \`${rule.trim()}\` (${detail})`;
2679
- }
2680
- }
2681
- //#endregion
2682
2964
  //#region pipeline/system-config.ts
2683
2965
  /**
2684
2966
  * Load and normalize a SystemInstance; `prefix` namespaces every CSS
@@ -2719,6 +3001,7 @@ function loadSystemConfig(engineApi, opts) {
2719
3001
  sourceThemeManifestsJson: config.sourceThemeManifests || null
2720
3002
  };
2721
3003
  if (declarationScalesJson) system.declarationScalesJson = declarationScalesJson;
3004
+ if (config.propertyRecordsJson) system.propertyRecordsJson = config.propertyRecordsJson;
2722
3005
  if (registrations.invalid.length > 0) system.invalidPropertyRegistrations = registrations.invalid;
2723
3006
  return system;
2724
3007
  }
@@ -2803,31 +3086,6 @@ function buildSystemPropsModule(opts) {
2803
3086
  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` : "");
2804
3087
  }
2805
3088
  //#endregion
2806
- //#region pipeline/timing-waterfall.ts
2807
- const RUST_TIMING_PHASES = [
2808
- ["parseAndWalk", "parse+walk"],
2809
- ["importResolution", "imports"],
2810
- ["extensionProvenance", "provenance"],
2811
- ["topologicalSort", "topo-sort"],
2812
- ["chainEvaluation", "chains"],
2813
- ["jsxScanning", "jsx-scan"],
2814
- ["systemPropAggregation", "sys-props"],
2815
- ["usageLedger", "usage"],
2816
- ["reconciliation", "reconcile"],
2817
- ["cssGeneration", "css-gen"],
2818
- ["manifestSerialization", "serialize"]
2819
- ];
2820
- function formatRustTimingWaterfall(timing, opts) {
2821
- const lines = [];
2822
- for (const [key, label] of RUST_TIMING_PHASES) {
2823
- const ms = timing[key] ?? 0;
2824
- const pad = " ".repeat(Math.max(0, opts.labelWidth - label.length));
2825
- const extra = key === "parseAndWalk" ? ` (${timing.fileCount ?? 0} files, ${timing.cacheHits ?? 0} cached)` : "";
2826
- lines.push(`${opts.indent}${label}${pad}${String(ms).padStart(5)}ms${extra}`);
2827
- }
2828
- return lines;
2829
- }
2830
- //#endregion
2831
3089
  //#region pipeline/tsconfig-paths.ts
2832
3090
  /**
2833
3091
  * tsconfig `paths` → alias pairs, for drivers with no live bundler config.
@@ -3094,4 +3352,4 @@ function realpathThroughNearestExistingAncestor(path) {
3094
3352
  }
3095
3353
  }
3096
3354
  //#endregion
3097
- export { AnimusConfigError as $, resolveLightningTargets as A, extractSystemFilePackages as B, isUnresolvedParentDrop as C, ANIMUS_LAYERS as Ct, unresolvedParentName as D, __require as Dt, unreadableSourceDiagnostic as E, validateLayerOrder as Et, findAssetSpecifiers as F, staleDistIncludesMessage as G, firstOwners as H, substituteAssetPlaceholders as I, createSourceIdentity as J, unresolvableIncludesMessage as K, createSourceCorpus as L, enforceExternalTokenContracts as M, resolveAssetFile as N, vocabularyWitnessDiagnostics as O, resolveThroughPathAliases as P, discoverFiles as Q, collectExternalPackageSources as R, VOCABULARY_LEGACY_VERB as S, buildAnalyzeProjectArgs as St, systemLoadDiagnostics as T, stripLeadingLayerDeclaration as Tt, isExcludedPackageRelativePath as U, findPackageRoot as V, resolveAbsolutePathSpecifier as W, sharesVolumeRoot as X, isPathWithinRoot as Y, compareDiscoveryOrder as Z, clearEngineCache as _, DEFAULT_EXTENSIONS as _t, assertNoErrorDiagnostics as a, assertKnownOptionKeys as at, INVALID_PROPERTY_REGISTRATION as b, preprocessMdx as bt, buildSystemPropsModule as c, resolveVerbosity as ct, loadSystemConfig as d, createV2EngineApi as dt, CORE_OPTION_KEYS as et, applyPrefix as f, ingestSourceEntries as ft, buildAnalysisInputs as g, adaptSvelteSource as gt, stableStringify as h, projectExternalFileOwners as ht, snapshotFilePlans as i, STRUCTURAL_EXCLUDE as it, buildPathAliasesJson as j, postProcessCss as k, buildDynamicPropConfig as l, RETIRED_ENGINE_MESSAGE as lt, camelToKebab as m, parseFilesJson as mt, diffFilePlans as n, DRIVER_NAMESPACES as nt, readTsconfigAliasPairs as o, createExcludeMatcher as ot, serializeStaticCss as p, isAdvisorySourceDiagnostic as pt, walkPackageSources as q, hashReplacementPlans as r, REPLACEABLE_DEFAULT_EXCLUDE as rt, formatRustTimingWaterfall as s, resolveMode as st, toWatchKeys as t, DEFAULT_EXCLUDE as tt, runStructuralSelfCheck as u, assertNoRetiredEngineSelection as ut, runProjectAnalysis as v, ENGINE_TRANSFORM_EXTENSIONS as vt, surfaceManifestDiagnostics as w, assembleStylesheet as wt, VOCABULARY_COLLISION as x, contentHash as xt, applyUnitFallback as y, isEngineTransformExtension as yt, excludeCollectedPackages as z };
3355
+ export { compareDiscoveryOrder as $, buildPathAliasesJson as A, collectExternalPackageSources as B, surfaceManifestDiagnostics as C, contentHash as Ct, vocabularyWitnessDiagnostics as D, stripLeadingLayerDeclaration as Dt, unresolvedParentName as E, assembleStylesheet as Et, findSheetAssetSpecifiers as F, isExcludedPackageRelativePath as G, extractSystemFilePackages as H, reportSurvivingAssetPlaceholders as I, unresolvableIncludesMessage as J, resolveAbsolutePathSpecifier as K, substituteAssetPlaceholders as L, resolveAssetFile as M, resolveThroughPathAliases as N, postProcessCss as O, validateLayerOrder as Ot, findAssetSpecifiers as P, sharesVolumeRoot as Q, substituteSheetAssets as R, isUnresolvedParentDrop as S, preprocessMdx as St, unreadableSourceDiagnostic as T, ANIMUS_LAYERS as Tt, findPackageRoot as U, excludeCollectedPackages as V, firstOwners as W, createSourceIdentity as X, walkPackageSources as Y, isPathWithinRoot as Z, runProjectAnalysis as _, projectExternalFileOwners as _t, assertNoErrorDiagnostics as a, REPLACEABLE_DEFAULT_EXCLUDE as at, VOCABULARY_COLLISION as b, ENGINE_TRANSFORM_EXTENSIONS as bt, buildDynamicPropConfig as c, createExcludeMatcher as ct, applyPrefix as d, RETIRED_ENGINE_MESSAGE as dt, discoverFiles as et, serializeStaticCss as f, assertNoRetiredEngineSelection as ft, clearEngineCache as g, parseFilesJson as gt, buildAnalysisInputs as h, isAdvisorySourceDiagnostic as ht, snapshotFilePlans as i, DRIVER_NAMESPACES as it, enforceExternalTokenContracts as j, resolveLightningTargets as k, __require as kt, runStructuralSelfCheck as l, resolveMode as lt, stableStringify as m, ingestSourceEntries as mt, diffFilePlans as n, CORE_OPTION_KEYS as nt, readTsconfigAliasPairs as o, STRUCTURAL_EXCLUDE as ot, camelToKebab as p, createV2EngineApi as pt, staleDistIncludesMessage as q, hashReplacementPlans as r, DEFAULT_EXCLUDE as rt, buildSystemPropsModule as s, assertKnownOptionKeys as st, toWatchKeys as t, AnimusConfigError as tt, loadSystemConfig as u, resolveVerbosity as ut, applyUnitFallback as v, adaptSvelteSource as vt, systemLoadDiagnostics as w, buildAnalyzeProjectArgs as wt, VOCABULARY_LEGACY_VERB as x, isEngineTransformExtension as xt, INVALID_PROPERTY_REGISTRATION as y, DEFAULT_EXTENSIONS as yt, createSourceCorpus as z };