@homebound/truss 2.1.0-next.4 → 2.1.0-next.6

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.
@@ -163,7 +163,7 @@ function collectVariableRules(rules, seg, mapping) {
163
163
  const prefix = conditionPrefix(seg.pseudoClass, seg.mediaQuery, seg.pseudoElement, mapping.breakpoints);
164
164
  const segmentBaseKey = seg.key.split("__")[0];
165
165
  for (const prop of seg.variableProps) {
166
- const baseKey = variableBaseKey(seg, prop);
166
+ const baseKey = seg.key.split("__")[0];
167
167
  const className = prefix ? `${prefix}${baseKey}_var` : `${baseKey}_var`;
168
168
  const varName = toCssVariableName(className, baseKey, prop);
169
169
  const declaration = { cssProperty: camelToKebab(prop), cssValue: `var(${varName})`, cssVarName: varName };
@@ -242,7 +242,7 @@ function collectWhenVariableRules(rules, seg, mapping) {
242
242
  const segmentBaseKey = seg.key.split("__")[0];
243
243
  const mClass = markerClassName(wp.markerNode);
244
244
  for (const prop of seg.variableProps) {
245
- const baseKey = variableBaseKey(seg, prop);
245
+ const baseKey = seg.key.split("__")[0];
246
246
  const className = `${prefix}${baseKey}_var`;
247
247
  const varName = toCssVariableName(className, baseKey, prop);
248
248
  const declaration = { cssProperty: camelToKebab(prop), cssValue: `var(${varName})`, cssVarName: varName };
@@ -469,7 +469,7 @@ function buildStyleHashProperties(segments, mapping, maybeIncHelperName) {
469
469
  const prefix = seg.whenPseudo ? whenPrefix(seg.whenPseudo) : conditionPrefix(seg.pseudoClass, seg.mediaQuery, seg.pseudoElement, mapping.breakpoints);
470
470
  const segmentBaseKey = seg.key.split("__")[0];
471
471
  for (const prop of seg.variableProps) {
472
- const baseKey = variableBaseKey(seg, prop);
472
+ const baseKey = seg.key.split("__")[0];
473
473
  const className = prefix ? `${prefix}${baseKey}_var` : `${baseKey}_var`;
474
474
  const varName = toCssVariableName(className, baseKey, prop);
475
475
  if (!propGroups.has(prop)) propGroups.set(prop, []);
@@ -530,12 +530,6 @@ function buildStyleHashProperties(segments, mapping, maybeIncHelperName) {
530
530
  }
531
531
  return properties;
532
532
  }
533
- function variableBaseKey(seg, cssProp) {
534
- if (seg.key.startsWith("add_")) {
535
- return cssProp;
536
- }
537
- return seg.key.split("__")[0];
538
- }
539
533
  function toCssVariableName(className, baseKey, cssProp) {
540
534
  const baseClassName = `${baseKey}_var`;
541
535
  const conditionPrefix2 = className.endsWith(baseClassName) ? className.slice(0, -baseClassName.length) : "";
@@ -1148,17 +1142,17 @@ function resolveAddCall(node, mapping, mediaQuery, pseudoClass, pseudoElement, w
1148
1142
  const wpSuffix = whenPseudoKeyName(whenPseudo);
1149
1143
  if (literalValue !== null) {
1150
1144
  const keySuffix = literalValue.replace(/[^a-zA-Z0-9]/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "");
1151
- const key = `add_${propName}__${keySuffix}__${wpSuffix}`;
1145
+ const key = `${propName}__${keySuffix}__${wpSuffix}`;
1152
1146
  return { key, defs: { [propName]: literalValue }, whenPseudo, argResolved: literalValue };
1153
1147
  } else {
1154
- const key = `add_${propName}__${wpSuffix}`;
1148
+ const key = `${propName}__${wpSuffix}`;
1155
1149
  return { key, defs: {}, whenPseudo, variableProps: [propName], incremented: false, argNode: valueArg };
1156
1150
  }
1157
1151
  }
1158
1152
  const suffix = conditionKeySuffix(mediaQuery, pseudoClass, pseudoElement, mapping.breakpoints);
1159
1153
  if (literalValue !== null) {
1160
1154
  const keySuffix = literalValue.replace(/[^a-zA-Z0-9]/g, "_").replace(/_+/g, "_").replace(/^_|_$/g, "");
1161
- const key = suffix ? `add_${propName}__${keySuffix}__${suffix}` : `add_${propName}__${keySuffix}`;
1155
+ const key = suffix ? `${propName}__${keySuffix}__${suffix}` : `${propName}__${keySuffix}`;
1162
1156
  const defs = { [propName]: literalValue };
1163
1157
  const wrappedDefs = wrapDefsWithConditions(defs, mediaQuery, pseudoClass);
1164
1158
  return {
@@ -1170,7 +1164,7 @@ function resolveAddCall(node, mapping, mediaQuery, pseudoClass, pseudoElement, w
1170
1164
  argResolved: literalValue
1171
1165
  };
1172
1166
  } else {
1173
- const key = suffix ? `add_${propName}__${suffix}` : `add_${propName}`;
1167
+ const key = suffix ? `${propName}__${suffix}` : propName;
1174
1168
  return {
1175
1169
  key,
1176
1170
  defs: {},
@@ -1623,6 +1617,29 @@ function findNamedImportBinding(ast, source, importedName) {
1623
1617
  }
1624
1618
  return null;
1625
1619
  }
1620
+ function findImportDeclaration(ast, source) {
1621
+ for (const node of ast.program.body) {
1622
+ if (t2.isImportDeclaration(node) && node.source.value === source) {
1623
+ return node;
1624
+ }
1625
+ }
1626
+ return null;
1627
+ }
1628
+ function replaceCssImportWithNamedImports(ast, cssBinding, source, imports) {
1629
+ for (const node of ast.program.body) {
1630
+ if (!t2.isImportDeclaration(node)) continue;
1631
+ const cssSpecIndex = node.specifiers.findIndex(function(spec) {
1632
+ return t2.isImportSpecifier(spec) && spec.local.name === cssBinding;
1633
+ });
1634
+ if (cssSpecIndex === -1 || node.specifiers.length !== 1) continue;
1635
+ node.source = t2.stringLiteral(source);
1636
+ node.specifiers = imports.map(function(entry) {
1637
+ return t2.importSpecifier(t2.identifier(entry.localName), t2.identifier(entry.importedName));
1638
+ });
1639
+ return true;
1640
+ }
1641
+ return false;
1642
+ }
1626
1643
  function upsertNamedImports(ast, source, imports) {
1627
1644
  if (imports.length === 0) return;
1628
1645
  for (const node of ast.program.body) {
@@ -1718,6 +1735,7 @@ function getCssAttributePath(path) {
1718
1735
  }
1719
1736
  function buildStyleHashFromChain(chain, options) {
1720
1737
  const members = [];
1738
+ const previousProperties = /* @__PURE__ */ new Map();
1721
1739
  if (chain.markers.length > 0) {
1722
1740
  const markerClasses = chain.markers.map(function(marker) {
1723
1741
  return markerClassName(marker.markerNode);
@@ -1726,10 +1744,22 @@ function buildStyleHashFromChain(chain, options) {
1726
1744
  }
1727
1745
  for (const part of chain.parts) {
1728
1746
  if (part.type === "unconditional") {
1729
- members.push(...buildStyleHashMembers(part.segments, options));
1747
+ const partMembers = buildStyleHashMembers(part.segments, options);
1748
+ members.push(...partMembers);
1749
+ for (const member of partMembers) {
1750
+ if (t3.isObjectProperty(member)) {
1751
+ previousProperties.set(propertyName(member.key), member);
1752
+ }
1753
+ }
1730
1754
  } else {
1731
- const thenMembers = buildStyleHashMembers(part.thenSegments, options);
1732
- const elseMembers = buildStyleHashMembers(part.elseSegments, options);
1755
+ const thenMembers = mergeConditionalBranchMembers(
1756
+ buildStyleHashMembers(part.thenSegments, options),
1757
+ previousProperties
1758
+ );
1759
+ const elseMembers = mergeConditionalBranchMembers(
1760
+ buildStyleHashMembers(part.elseSegments, options),
1761
+ previousProperties
1762
+ );
1733
1763
  members.push(
1734
1764
  t3.spreadElement(
1735
1765
  t3.conditionalExpression(part.conditionNode, t3.objectExpression(thenMembers), t3.objectExpression(elseMembers))
@@ -1773,6 +1803,82 @@ function buildStyleHashMembers(segments, options) {
1773
1803
  flushNormal();
1774
1804
  return members;
1775
1805
  }
1806
+ function mergeConditionalBranchMembers(members, previousProperties) {
1807
+ return members.map(function(member) {
1808
+ if (!t3.isObjectProperty(member)) {
1809
+ return member;
1810
+ }
1811
+ const prior = previousProperties.get(propertyName(member.key));
1812
+ if (!prior) {
1813
+ return member;
1814
+ }
1815
+ return t3.objectProperty(
1816
+ clonePropertyKey(member.key),
1817
+ mergePropertyValues(prior.value, member.value)
1818
+ );
1819
+ });
1820
+ }
1821
+ function mergePropertyValues(previousValue, currentValue) {
1822
+ if (t3.isStringLiteral(previousValue) && t3.isStringLiteral(currentValue)) {
1823
+ return t3.stringLiteral(`${previousValue.value} ${currentValue.value}`);
1824
+ }
1825
+ if (t3.isStringLiteral(previousValue) && t3.isArrayExpression(currentValue)) {
1826
+ return mergeTupleValue(currentValue, previousValue.value, true);
1827
+ }
1828
+ if (t3.isArrayExpression(previousValue) && t3.isStringLiteral(currentValue)) {
1829
+ return mergeTupleValue(previousValue, currentValue.value, false);
1830
+ }
1831
+ if (t3.isArrayExpression(previousValue) && t3.isArrayExpression(currentValue)) {
1832
+ const previousClassNames = tupleClassNames(previousValue);
1833
+ return mergeTupleValue(currentValue, previousClassNames, true, arrayElementExpression(previousValue.elements[1]));
1834
+ }
1835
+ return t3.cloneNode(currentValue, true);
1836
+ }
1837
+ function mergeTupleValue(tuple, classNames, prependClassNames, previousVars) {
1838
+ const currentClassNames = tupleClassNames(tuple);
1839
+ const mergedClassNames = prependClassNames ? `${classNames} ${currentClassNames}` : `${currentClassNames} ${classNames}`;
1840
+ const varsExpr = tuple.elements[1];
1841
+ const mergedVars = previousVars && arrayElementExpression(varsExpr) ? mergeVarsObject(previousVars, arrayElementExpression(varsExpr)) : arrayElementExpression(varsExpr) ?? previousVars ?? null;
1842
+ return t3.arrayExpression([
1843
+ t3.stringLiteral(mergedClassNames),
1844
+ mergedVars ? t3.cloneNode(mergedVars, true) : t3.objectExpression([])
1845
+ ]);
1846
+ }
1847
+ function tupleClassNames(tuple) {
1848
+ const classNames = tuple.elements[0];
1849
+ return t3.isStringLiteral(classNames) ? classNames.value : "";
1850
+ }
1851
+ function arrayElementExpression(element) {
1852
+ return element && !t3.isSpreadElement(element) ? element : null;
1853
+ }
1854
+ function mergeVarsObject(previousVars, currentVars) {
1855
+ if (t3.isObjectExpression(previousVars) && t3.isObjectExpression(currentVars)) {
1856
+ return t3.objectExpression([
1857
+ ...previousVars.properties.map(function(property) {
1858
+ return t3.cloneNode(property, true);
1859
+ }),
1860
+ ...currentVars.properties.map(function(property) {
1861
+ return t3.cloneNode(property, true);
1862
+ })
1863
+ ]);
1864
+ }
1865
+ return t3.cloneNode(currentVars, true);
1866
+ }
1867
+ function propertyName(key) {
1868
+ if (t3.isIdentifier(key)) {
1869
+ return key.name;
1870
+ }
1871
+ if (t3.isStringLiteral(key)) {
1872
+ return key.value;
1873
+ }
1874
+ return generate(key).code;
1875
+ }
1876
+ function clonePropertyKey(key) {
1877
+ if (t3.isPrivateName(key)) {
1878
+ return t3.identifier(key.id.name);
1879
+ }
1880
+ return t3.cloneNode(key, true);
1881
+ }
1776
1882
  function injectDebugInfo(expr, line, options) {
1777
1883
  if (!options.debug) return;
1778
1884
  const firstProp = expr.properties.find(function(p) {
@@ -1973,7 +2079,6 @@ function transformTruss(code, filename, mapping, options = {}) {
1973
2079
  skippedCssPropMessages: errorMessages,
1974
2080
  runtimeLookupNames
1975
2081
  });
1976
- removeCssImport(ast, cssBindingName);
1977
2082
  const runtimeImports = [];
1978
2083
  if (needsTrussPropsHelper.current) {
1979
2084
  runtimeImports.push({ importedName: "trussProps", localName: trussPropsHelperName });
@@ -1987,8 +2092,14 @@ function transformTruss(code, filename, mapping, options = {}) {
1987
2092
  if (options.injectCss) {
1988
2093
  runtimeImports.push({ importedName: "__injectTrussCSS", localName: "__injectTrussCSS" });
1989
2094
  }
2095
+ const reusedCssImportLine = runtimeImports.length > 0 && findImportDeclaration(ast, "@homebound/truss/runtime") === null && replaceCssImportWithNamedImports(ast, cssBindingName, "@homebound/truss/runtime", runtimeImports);
2096
+ if (!reusedCssImportLine) {
2097
+ removeCssImport(ast, cssBindingName);
2098
+ }
1990
2099
  if (runtimeImports.length > 0) {
1991
- upsertNamedImports(ast, "@homebound/truss/runtime", runtimeImports);
2100
+ if (!reusedCssImportLine) {
2101
+ upsertNamedImports(ast, "@homebound/truss/runtime", runtimeImports);
2102
+ }
1992
2103
  }
1993
2104
  const declarationsToInsert = [];
1994
2105
  if (maybeIncHelperName) {
@@ -2023,9 +2134,11 @@ function transformTruss(code, filename, mapping, options = {}) {
2023
2134
  }
2024
2135
  const output = generate2(ast, {
2025
2136
  sourceFileName: filename,
2137
+ sourceMaps: true,
2026
2138
  retainLines: false
2027
2139
  });
2028
- return { code: output.code, map: output.map, css: cssText, rules };
2140
+ const outputCode = preserveBlankLineAfterImports(code, output.code);
2141
+ return { code: outputCode, map: output.map, css: cssText, rules };
2029
2142
  }
2030
2143
  function collectRuntimeLookups(chains) {
2031
2144
  const lookups = /* @__PURE__ */ new Map();
@@ -2043,6 +2156,31 @@ function collectRuntimeLookups(chains) {
2043
2156
  }
2044
2157
  return lookups;
2045
2158
  }
2159
+ function preserveBlankLineAfterImports(input, output) {
2160
+ const inputLines = input.split("\n");
2161
+ const outputLines = output.split("\n");
2162
+ const lastInputImportLine = findLastImportLine(inputLines);
2163
+ const lastOutputImportLine = findLastImportLine(outputLines);
2164
+ if (lastInputImportLine === -1 || lastOutputImportLine === -1) {
2165
+ return output;
2166
+ }
2167
+ const inputHasBlankLineAfterImports = inputLines[lastInputImportLine + 1]?.trim() === "";
2168
+ const outputHasBlankLineAfterImports = outputLines[lastOutputImportLine + 1]?.trim() === "";
2169
+ if (!inputHasBlankLineAfterImports || outputHasBlankLineAfterImports) {
2170
+ return output;
2171
+ }
2172
+ outputLines.splice(lastOutputImportLine + 1, 0, "");
2173
+ return outputLines.join("\n");
2174
+ }
2175
+ function findLastImportLine(lines) {
2176
+ let lastImportLine = -1;
2177
+ for (let index = 0; index < lines.length; index++) {
2178
+ if (lines[index].trimStart().startsWith("import ")) {
2179
+ lastImportLine = index;
2180
+ }
2181
+ }
2182
+ return lastImportLine;
2183
+ }
2046
2184
 
2047
2185
  // src/plugin/transform-css.ts
2048
2186
  import { parse as parse2 } from "@babel/parser";