@homebound/truss 2.29.6 → 2.29.7

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.
@@ -1874,6 +1874,55 @@ function flattenWhenObjectParts(resolved) {
1874
1874
  return segments;
1875
1875
  }
1876
1876
 
1877
+ // src/css-order.ts
1878
+ function ruleSortKey(priority, className, atRulePrelude2) {
1879
+ const widthInterval = atRulePrelude2 === void 0 ? null : parseWidthInterval(atRulePrelude2);
1880
+ return { priority, className, widthInterval };
1881
+ }
1882
+ function compareRuleSortKeys(a, b) {
1883
+ return a.priority - b.priority || compareWidthIntervals(a.widthInterval, b.widthInterval) || compareClassNames(a.className, b.className);
1884
+ }
1885
+ function compareClassNames(a, b) {
1886
+ return a < b ? -1 : a > b ? 1 : 0;
1887
+ }
1888
+ function atRulePrelude(cssText) {
1889
+ if (!cssText.startsWith("@")) return void 0;
1890
+ const brace = cssText.indexOf("{");
1891
+ return brace === -1 ? void 0 : cssText.slice(0, brace).trim();
1892
+ }
1893
+ function parseWidthInterval(prelude) {
1894
+ if (/,|\bnot\b|\bor\b|[<>]/.test(prelude)) return null;
1895
+ const terms = Array.from(prelude.matchAll(/\((min|max)-width:\s*([^)]*)\)/g));
1896
+ if (terms.length === 0) return null;
1897
+ let lo = 0;
1898
+ let hi = Infinity;
1899
+ for (const term of terms) {
1900
+ const px = parsePxLength(term[2]);
1901
+ if (px === null) return null;
1902
+ if (term[1] === "min") {
1903
+ lo = Math.max(lo, px);
1904
+ } else {
1905
+ hi = Math.min(hi, px);
1906
+ }
1907
+ }
1908
+ return { lo, hi };
1909
+ }
1910
+ function parsePxLength(value) {
1911
+ const match = value.trim().match(/^(\d+(?:\.\d+)?)(px)?$/);
1912
+ if (!match) return null;
1913
+ if (match[2] === void 0 && Number(match[1]) !== 0) return null;
1914
+ return Number(match[1]);
1915
+ }
1916
+ function compareWidthIntervals(a, b) {
1917
+ if (a === null || b === null) {
1918
+ return (a === null ? 1 : 0) - (b === null ? 1 : 0);
1919
+ }
1920
+ const widthA = a.hi - a.lo;
1921
+ const widthB = b.hi - b.lo;
1922
+ if (widthA !== widthB) return widthA > widthB ? -1 : 1;
1923
+ return a.lo - b.lo;
1924
+ }
1925
+
1877
1926
  // src/plugin/property-priorities.ts
1878
1927
  var longHandPhysical = /* @__PURE__ */ new Set();
1879
1928
  var longHandLogical = /* @__PURE__ */ new Set();
@@ -2428,13 +2477,6 @@ function computeRulePriority(rule) {
2428
2477
  function isVariableRule(rule) {
2429
2478
  return rule.declarations.some((d) => d.cssVarName !== void 0);
2430
2479
  }
2431
- function ruleSortKey(priority, className, atRulePrelude2) {
2432
- const widthInterval = atRulePrelude2 === void 0 ? null : parseWidthInterval(atRulePrelude2);
2433
- return { priority, className, widthInterval };
2434
- }
2435
- function compareRuleSortKeys(a, b) {
2436
- return a.priority - b.priority || compareWidthIntervals(a.widthInterval, b.widthInterval) || compareClassNames(a.className, b.className);
2437
- }
2438
2480
  function sortRulesByPriority(rules) {
2439
2481
  const decorated = Array.from(rules, (rule) => {
2440
2482
  const priority = computeRulePriority(rule);
@@ -2443,41 +2485,6 @@ function sortRulesByPriority(rules) {
2443
2485
  decorated.sort((a, b) => compareRuleSortKeys(a.key, b.key));
2444
2486
  return decorated.map((d) => ({ rule: d.rule, priority: d.priority }));
2445
2487
  }
2446
- function compareClassNames(a, b) {
2447
- return a < b ? -1 : a > b ? 1 : 0;
2448
- }
2449
- function parseWidthInterval(prelude) {
2450
- if (/,|\bnot\b|\bor\b|[<>]/.test(prelude)) return null;
2451
- const terms = Array.from(prelude.matchAll(/\((min|max)-width:\s*([^)]*)\)/g));
2452
- if (terms.length === 0) return null;
2453
- let lo = 0;
2454
- let hi = Infinity;
2455
- for (const term of terms) {
2456
- const px = parsePxLength(term[2]);
2457
- if (px === null) return null;
2458
- if (term[1] === "min") {
2459
- lo = Math.max(lo, px);
2460
- } else {
2461
- hi = Math.min(hi, px);
2462
- }
2463
- }
2464
- return { lo, hi };
2465
- }
2466
- function parsePxLength(value) {
2467
- const match = value.trim().match(/^(\d+(?:\.\d+)?)(px)?$/);
2468
- if (!match) return null;
2469
- if (match[2] === void 0 && Number(match[1]) !== 0) return null;
2470
- return Number(match[1]);
2471
- }
2472
- function compareWidthIntervals(a, b) {
2473
- if (a === null || b === null) {
2474
- return (a === null ? 1 : 0) - (b === null ? 1 : 0);
2475
- }
2476
- const widthA = a.hi - a.lo;
2477
- const widthB = b.hi - b.lo;
2478
- if (widthA !== widthB) return widthA > widthB ? -1 : 1;
2479
- return a.lo - b.lo;
2480
- }
2481
2488
 
2482
2489
  // src/plugin/emit-css.ts
2483
2490
  function collectAtomicRules(chains, mapping) {
@@ -3429,6 +3436,8 @@ function findLastImportLine(lines) {
3429
3436
 
3430
3437
  // src/plugin/merge-css.ts
3431
3438
  import { readFileSync as readFileSync2 } from "fs";
3439
+
3440
+ // src/truss-css.ts
3432
3441
  var RULE_ANNOTATION_RE = /^\/\* @truss p:([\d.]+) c:(\S+) \*\/$/;
3433
3442
  var PROPERTY_ANNOTATION_RE = /^\/\* @truss @property \*\/$/;
3434
3443
  var ARBITRARY_START_RE = /^\/\* @truss arbitrary:start \*\/$/;
@@ -3485,10 +3494,6 @@ function parseTrussCss(cssText) {
3485
3494
  }
3486
3495
  return { rules, properties, arbitraryCssBlocks };
3487
3496
  }
3488
- function readTrussCss(filePath) {
3489
- const content = readFileSync2(filePath, "utf8");
3490
- return parseTrussCss(content);
3491
- }
3492
3497
  function annotateArbitraryCssBlock(cssText) {
3493
3498
  const trimmed = cssText.trim();
3494
3499
  if (trimmed.length === 0) {
@@ -3496,6 +3501,12 @@ function annotateArbitraryCssBlock(cssText) {
3496
3501
  }
3497
3502
  return ["/* @truss arbitrary:start */", trimmed, "/* @truss arbitrary:end */"].join("\n");
3498
3503
  }
3504
+
3505
+ // src/plugin/merge-css.ts
3506
+ function readTrussCss(filePath) {
3507
+ const content = readFileSync2(filePath, "utf8");
3508
+ return parseTrussCss(content);
3509
+ }
3499
3510
  function mergeTrussCss(sources) {
3500
3511
  const seenClasses = /* @__PURE__ */ new Set();
3501
3512
  const allRules = [];
@@ -3535,11 +3546,6 @@ function mergeTrussCss(sources) {
3535
3546
  }
3536
3547
  return lines.join("\n");
3537
3548
  }
3538
- function atRulePrelude(cssText) {
3539
- if (!cssText.startsWith("@")) return void 0;
3540
- const brace = cssText.indexOf("{");
3541
- return brace === -1 ? void 0 : cssText.slice(0, brace).trim();
3542
- }
3543
3549
 
3544
3550
  // src/plugin/transform-session.ts
3545
3551
  function createTrussTransformSession(options) {
@@ -3569,6 +3575,7 @@ function createTrussTransformSession(options) {
3569
3575
  libraryCache = null;
3570
3576
  }
3571
3577
  function updateArbitraryCssRegistry(sourcePath, sourceCode) {
3578
+ sourcePath = resolve2(sourcePath).replace(/\\/g, "/");
3572
3579
  const css = transformCssTs(sourceCode, sourcePath, ensureMapping()).trim();
3573
3580
  if (css.length > 0) {
3574
3581
  const prev = arbitraryCssRegistry.get(sourcePath);
@@ -3598,7 +3605,7 @@ function createTrussTransformSession(options) {
3598
3605
  function collectCss() {
3599
3606
  const mapping2 = ensureMapping();
3600
3607
  const appCssParts = [generateCssText(cssRegistry)];
3601
- const allArbitrary = Array.from(arbitraryCssRegistry.values()).join("\n\n");
3608
+ const allArbitrary = Array.from(arbitraryCssRegistry.entries()).sort((a, b) => compareClassNames(a[0], b[0])).map((entry) => entry[1]).join("\n\n");
3602
3609
  appCssParts.push(annotateArbitraryCssBlock(allArbitrary));
3603
3610
  const appCss = appCssParts.filter((part) => part.length > 0).join("\n");
3604
3611
  const libs = loadLibraries();
@@ -3610,8 +3617,16 @@ ${body}`;
3610
3617
  function hasCss() {
3611
3618
  return cssRegistry.size > 0 || arbitraryCssRegistry.size > 0 || libraryPaths.length > 0;
3612
3619
  }
3620
+ function collectTestCss() {
3621
+ return mergeTrussCss(loadLibraries());
3622
+ }
3623
+ function getArbitraryCss(sourcePath) {
3624
+ return arbitraryCssRegistry.get(resolve2(sourcePath).replace(/\\/g, "/")) ?? "";
3625
+ }
3613
3626
  return {
3614
3627
  collectCss,
3628
+ collectTestCss,
3629
+ getArbitraryCss,
3615
3630
  ensureMapping,
3616
3631
  hasCss,
3617
3632
  reset,
@@ -3620,6 +3635,9 @@ ${body}`;
3620
3635
  };
3621
3636
  }
3622
3637
 
3638
+ // src/plugin/index.ts
3639
+ import * as t16 from "@babel/types";
3640
+
3623
3641
  // src/plugin/esbuild-plugin.ts
3624
3642
  import { readFileSync as readFileSync3, writeFileSync, mkdirSync } from "fs";
3625
3643
  import { resolve as resolve3, join } from "path";
@@ -3667,6 +3685,7 @@ function loaderForPath(filePath) {
3667
3685
 
3668
3686
  // src/plugin/index.ts
3669
3687
  var VIRTUAL_CSS_PREFIX = "\0truss-css:";
3688
+ var VIRTUAL_TEST_CSS_PREFIX = "\0truss-test-css:";
3670
3689
  var CSS_TS_QUERY = "?truss-css";
3671
3690
  var TRUSS_CSS_PLACEHOLDER = "__TRUSS_CSS_HASH__";
3672
3691
  var VIRTUAL_CSS_ENDPOINT = "/virtual:truss.css";
@@ -3756,6 +3775,7 @@ function trussPlugin(opts) {
3756
3775
  if (!source.endsWith(CSS_TS_QUERY)) return null;
3757
3776
  const absolutePath = resolveImportPath(source.slice(0, -CSS_TS_QUERY.length), importer, projectRoot);
3758
3777
  if (!existsSync2(absolutePath)) return null;
3778
+ if (isTest) return VIRTUAL_TEST_CSS_PREFIX + absolutePath;
3759
3779
  return VIRTUAL_CSS_PREFIX + absolutePath.slice(0, -3);
3760
3780
  },
3761
3781
  load(id) {
@@ -3789,11 +3809,27 @@ function trussPlugin(opts) {
3789
3809
  `;
3790
3810
  }
3791
3811
  if (id === RESOLVED_VIRTUAL_TEST_CSS_ID) {
3792
- const css = session.collectCss();
3812
+ const css = session.collectTestCss();
3813
+ const options = {
3814
+ source: "libraries",
3815
+ order: 0,
3816
+ prelude: rootSpacingPreludeCss(session.ensureMapping().increment)
3817
+ };
3793
3818
  return `
3794
3819
  import { __injectTrussCSS } from "@homebound/truss/runtime";
3795
3820
 
3796
- __injectTrussCSS(${JSON.stringify(css)});
3821
+ __injectTrussCSS(${JSON.stringify(css)}, ${JSON.stringify(options)});
3822
+ `;
3823
+ }
3824
+ if (id.startsWith(VIRTUAL_TEST_CSS_PREFIX)) {
3825
+ const sourcePath2 = resolve4(id.slice(VIRTUAL_TEST_CSS_PREFIX.length)).replace(/\\/g, "/");
3826
+ session.updateArbitraryCssRegistry(sourcePath2, readFileSync4(sourcePath2, "utf8"));
3827
+ const css = annotateArbitraryCssBlock(session.getArbitraryCss(sourcePath2));
3828
+ return `
3829
+ import "${VIRTUAL_TEST_CSS_ID}";
3830
+ import { __injectTrussCSS } from "@homebound/truss/runtime";
3831
+
3832
+ __injectTrussCSS(${JSON.stringify(css)}, ${JSON.stringify({ source: sourcePath2 })});
3797
3833
  `;
3798
3834
  }
3799
3835
  if (!id.startsWith(VIRTUAL_CSS_PREFIX)) return null;
@@ -3803,16 +3839,45 @@ __injectTrussCSS(${JSON.stringify(css)});
3803
3839
  return `/* [truss] ${sourcePath} \u2014 included via truss.css */`;
3804
3840
  },
3805
3841
  transform(code, id) {
3842
+ if (id.startsWith(VIRTUAL_TEST_CSS_PREFIX)) return null;
3806
3843
  if (!/\.[cm]?[jt]sx?(\?|$)/.test(id)) return null;
3807
3844
  const fileId = stripQueryAndHash(id);
3808
3845
  if (isNodeModulesFile(fileId)) return null;
3809
3846
  const rewrittenImports = rewriteCssTsImports(code, id);
3810
- const shouldBootstrapTestCss = isTest && libraryPaths.length > 0;
3847
+ const shouldBootstrapTestCss = isTest;
3811
3848
  const transformedCode = shouldBootstrapTestCss ? `${rewrittenImports.code}
3812
3849
  import "${VIRTUAL_TEST_CSS_ID}";` : rewrittenImports.code;
3813
3850
  const importsOnlyResult = rewrittenImports.changed || shouldBootstrapTestCss ? { code: transformedCode, map: null } : null;
3814
3851
  if (fileId.endsWith(".css.ts")) {
3815
3852
  session.updateArbitraryCssRegistry(fileId, code);
3853
+ if (isTest) {
3854
+ const css = annotateArbitraryCssBlock(session.getArbitraryCss(fileId));
3855
+ const ast = parseModule(transformedCode, fileId);
3856
+ traverse(ast, {
3857
+ Program(path) {
3858
+ const inject = path.scope.generateUidIdentifier("injectTrussCSS");
3859
+ path.unshiftContainer(
3860
+ "body",
3861
+ t16.importDeclaration(
3862
+ [t16.importSpecifier(inject, t16.identifier("__injectTrussCSS"))],
3863
+ t16.stringLiteral("@homebound/truss/runtime")
3864
+ )
3865
+ );
3866
+ path.pushContainer(
3867
+ "body",
3868
+ t16.expressionStatement(
3869
+ t16.callExpression(inject, [
3870
+ t16.stringLiteral(css),
3871
+ t16.objectExpression([
3872
+ t16.objectProperty(t16.identifier("source"), t16.stringLiteral(resolve4(fileId).replace(/\\/g, "/")))
3873
+ ])
3874
+ ])
3875
+ )
3876
+ );
3877
+ }
3878
+ });
3879
+ return { code: generate(ast, { sourceFileName: fileId }).code, map: null };
3880
+ }
3816
3881
  return importsOnlyResult;
3817
3882
  }
3818
3883
  const hasCssDsl = rewrittenImports.code.includes("Css") || rewrittenImports.code.includes("css=");