@homebound/truss 2.11.3 → 2.13.0

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.
@@ -2773,6 +2773,7 @@ function rewriteCssPropsAndCssAttributes(options) {
2773
2773
  // I.e. css={someVariable}, css={{ ...a, ...b }}, css={cond ? a : b}
2774
2774
  JSXAttribute(path) {
2775
2775
  if (!t3.isJSXIdentifier(path.node.name, { name: "css" })) return;
2776
+ if (isRuntimeStyleCssAttribute(path)) return;
2776
2777
  const value = path.node.value;
2777
2778
  if (!t3.isJSXExpressionContainer(value)) return;
2778
2779
  if (!t3.isExpression(value.expression)) return;
@@ -2820,6 +2821,11 @@ function extractSiblingClassName(callPath) {
2820
2821
  function isMatchingPropertyName(key, name) {
2821
2822
  return t3.isIdentifier(key) && key.name === name || t3.isStringLiteral(key) && key.value === name;
2822
2823
  }
2824
+ function isRuntimeStyleCssAttribute(path) {
2825
+ const openingElementPath = path.parentPath;
2826
+ if (!openingElementPath || !openingElementPath.isJSXOpeningElement()) return false;
2827
+ return t3.isJSXIdentifier(openingElementPath.node.name, { name: "RuntimeStyle" });
2828
+ }
2823
2829
  function isFullyStaticStyleHash(hash) {
2824
2830
  for (const prop of hash.properties) {
2825
2831
  if (!t3.isObjectProperty(prop)) return false;
@@ -2860,7 +2866,8 @@ function transformTruss(code, filename, mapping, options = {}) {
2860
2866
  const sites = [];
2861
2867
  const errorMessages = [];
2862
2868
  let hasCssPropsCall = false;
2863
- let hasJsxCssAttribute = false;
2869
+ let hasBuildtimeJsxCssAttribute = false;
2870
+ let hasRuntimeStyleCssUsage = false;
2864
2871
  traverse2(ast, {
2865
2872
  // -- Css.*.$ chain collection --
2866
2873
  MemberExpression(path) {
@@ -2869,6 +2876,10 @@ function transformTruss(code, filename, mapping, options = {}) {
2869
2876
  if (path.node.computed) return;
2870
2877
  const chain = extractChain(path.node.object, cssBindingName);
2871
2878
  if (!chain) return;
2879
+ if (isInsideRuntimeStyleCssObject(path)) {
2880
+ hasRuntimeStyleCssUsage = true;
2881
+ return;
2882
+ }
2872
2883
  const parentPath = path.parentPath;
2873
2884
  if (parentPath && parentPath.isMemberExpression() && t4.isIdentifier(parentPath.node.property, { name: "$" })) {
2874
2885
  return;
@@ -2890,13 +2901,12 @@ function transformTruss(code, filename, mapping, options = {}) {
2890
2901
  },
2891
2902
  // -- JSX css={...} attribute detection (so we don't bail when there are only css props) --
2892
2903
  JSXAttribute(path) {
2893
- if (hasJsxCssAttribute) return;
2894
- if (t4.isJSXIdentifier(path.node.name, { name: "css" })) {
2895
- hasJsxCssAttribute = true;
2896
- }
2904
+ if (!t4.isJSXIdentifier(path.node.name, { name: "css" })) return;
2905
+ if (isRuntimeStyleCssAttribute2(path)) return;
2906
+ hasBuildtimeJsxCssAttribute = true;
2897
2907
  }
2898
2908
  });
2899
- if (sites.length === 0 && !hasCssPropsCall && !hasJsxCssAttribute) return null;
2909
+ if (sites.length === 0 && !hasCssPropsCall && !hasBuildtimeJsxCssAttribute) return null;
2900
2910
  const chains = sites.map((s) => s.resolvedChain);
2901
2911
  const { rules, needsMaybeInc } = collectAtomicRules(chains, mapping);
2902
2912
  const cssText = generateCssText(rules);
@@ -2946,7 +2956,7 @@ function transformTruss(code, filename, mapping, options = {}) {
2946
2956
  runtimeImports.push({ importedName: "__injectTrussCSS", localName: "__injectTrussCSS" });
2947
2957
  }
2948
2958
  let reusedCssImportLine = false;
2949
- if (cssIsImported) {
2959
+ if (cssIsImported && !hasRuntimeStyleCssUsage) {
2950
2960
  reusedCssImportLine = runtimeImports.length > 0 && findImportDeclaration(ast, "@homebound/truss/runtime") === null && replaceCssImportWithNamedImports(ast, cssImportBinding, "@homebound/truss/runtime", runtimeImports);
2951
2961
  if (!reusedCssImportLine) {
2952
2962
  removeCssImport(ast, cssImportBinding);
@@ -2994,6 +3004,24 @@ function transformTruss(code, filename, mapping, options = {}) {
2994
3004
  const outputCode = preserveBlankLineAfterImports(code, output.code);
2995
3005
  return { code: outputCode, map: output.map, css: cssText, rules };
2996
3006
  }
3007
+ function isInsideRuntimeStyleCssObject(path) {
3008
+ let current = path.parentPath;
3009
+ while (current) {
3010
+ if (current.isJSXExpressionContainer()) {
3011
+ const attrPath = current.parentPath;
3012
+ if (!attrPath || !attrPath.isJSXAttribute()) return false;
3013
+ return t4.isObjectExpression(current.node.expression) && isRuntimeStyleCssAttribute2(attrPath);
3014
+ }
3015
+ current = current.parentPath;
3016
+ }
3017
+ return false;
3018
+ }
3019
+ function isRuntimeStyleCssAttribute2(path) {
3020
+ if (!t4.isJSXIdentifier(path.node.name, { name: "css" })) return false;
3021
+ const openingElementPath = path.parentPath;
3022
+ if (!openingElementPath || !openingElementPath.isJSXOpeningElement()) return false;
3023
+ return t4.isJSXIdentifier(openingElementPath.node.name, { name: "RuntimeStyle" });
3024
+ }
2997
3025
  function collectRuntimeLookups(chains) {
2998
3026
  const lookups = /* @__PURE__ */ new Map();
2999
3027
  for (const chain of chains) {
@@ -3111,10 +3139,6 @@ function transformCssTs(code, filename, mapping) {
3111
3139
  sourceFilename: filename
3112
3140
  });
3113
3141
  const cssBindingName = findCssImportBinding(ast);
3114
- if (!cssBindingName) {
3115
- return `/* [truss] ${filename}: no Css import found */
3116
- `;
3117
- }
3118
3142
  const cssExport = findNamedCssExportObject(ast);
3119
3143
  if (!cssExport) {
3120
3144
  return `/* [truss] ${filename}: expected \`export const css = { ... }\` with an object literal */
@@ -3137,10 +3161,19 @@ function transformCssTs(code, filename, mapping) {
3137
3161
  continue;
3138
3162
  }
3139
3163
  const valueNode = prop.value;
3164
+ const rawCss = extractStaticStringValue(valueNode, cssBindingName);
3165
+ if (rawCss !== null) {
3166
+ rules.push(formatRawCssRule(selector, rawCss));
3167
+ continue;
3168
+ }
3140
3169
  if (!t6.isExpression(valueNode)) {
3141
3170
  rules.push(`/* [truss] unsupported: "${selector}" value is not an expression */`);
3142
3171
  continue;
3143
3172
  }
3173
+ if (!cssBindingName) {
3174
+ rules.push(`/* [truss] unsupported: "${selector}" \u2014 Css.*.$ chain requires a Css import */`);
3175
+ continue;
3176
+ }
3144
3177
  const cssResult = resolveCssExpression(valueNode, cssBindingName, mapping, filename);
3145
3178
  if ("error" in cssResult) {
3146
3179
  rules.push(`/* [truss] unsupported: "${selector}" \u2014 ${cssResult.error} */`);
@@ -3174,6 +3207,16 @@ function objectPropertyStringKey(prop, stringBindings) {
3174
3207
  if (prop.computed) return resolveStaticString(prop.key, stringBindings);
3175
3208
  return null;
3176
3209
  }
3210
+ function extractStaticStringValue(node, cssBindingName) {
3211
+ if (t6.isStringLiteral(node)) return node.value;
3212
+ if (t6.isTemplateLiteral(node) && node.expressions.length === 0 && node.quasis.length === 1) {
3213
+ return node.quasis[0].value.cooked ?? node.quasis[0].value.raw;
3214
+ }
3215
+ if (t6.isTaggedTemplateExpression(node) && t6.isMemberExpression(node.tag) && !node.tag.computed && t6.isIdentifier(node.tag.property, { name: "raw" }) && t6.isIdentifier(node.tag.object, { name: cssBindingName ?? "" }) && node.quasi.expressions.length === 0 && node.quasi.quasis.length === 1) {
3216
+ return node.quasi.quasis[0].value.cooked ?? node.quasi.quasis[0].value.raw;
3217
+ }
3218
+ return null;
3219
+ }
3177
3220
  function resolveCssExpression(node, cssBindingName, mapping, filename) {
3178
3221
  if (!t6.isMemberExpression(node) || node.computed || !t6.isIdentifier(node.property, { name: "$" })) {
3179
3222
  return { error: "value must be a Css.*.$ expression" };
@@ -3234,6 +3277,14 @@ function resolveCssExpression(node, cssBindingName, mapping, filename) {
3234
3277
  }
3235
3278
  return { declarations };
3236
3279
  }
3280
+ function formatRawCssRule(selector, raw) {
3281
+ const trimmed = raw.trim();
3282
+ if (!trimmed) return `${selector} {}`;
3283
+ const body = trimmed.split("\n").map((line) => ` ${line.trim()}`).filter((line) => line.trim().length > 0).join("\n");
3284
+ return `${selector} {
3285
+ ${body}
3286
+ }`;
3287
+ }
3237
3288
  function formatCssRule(selector, declarations) {
3238
3289
  if (declarations.length === 0) {
3239
3290
  return `${selector} {}`;