@lass-lang/core 0.0.1 → 0.1.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Breaking: delimiter format change, removed shorthand, array join behavior
8
+
3
9
  All notable changes to this project will be documented in this file.
4
10
 
5
11
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
@@ -7,17 +13,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
13
 
8
14
  ## [Unreleased]
9
15
 
16
+ ### Removed
17
+
18
+ - **BREAKING:** `@prop` shorthand syntax removed — use `@(prop)` instead. The bare `@prop` form conflicted with CSS at-rules (`@slot`, `@custom-variant`, `@layer`, `@apply`, `@scope`, etc.) and was not future-proof as CSS continues adding new `@`-prefixed syntax. The explicit `@(prop)` form is unambiguous and remains fully supported.
19
+ - `StyleLookupShorthand` type no longer exported from `@lass-lang/core`
20
+ - `findStyleLookupShorthands()` and `findStyleLookupShorthandsStatic()` methods removed from Scanner
21
+ - `normalizeStyleLookupShorthands()` function removed from transpiler pipeline
22
+
10
23
  ### Added
24
+
11
25
  - Initial release preparation
12
26
 
13
27
  ## [0.0.1] - 2026-02-07
14
28
 
15
29
  ### Added
30
+
16
31
  - Initial development version
17
32
  - Two-zone model with `---` separator for JS preamble
18
33
  - Dollar substitution (`$name` variables)
19
34
  - Expression interpolation (`{{ expr }}`)
20
- - Property lookup (`@(prop)` and `@prop` shorthand)
35
+ - Property lookup (`@(prop)`)
21
36
  - Style blocks (`@{ }` CSS fragments from JS)
22
37
  - Single-line comment stripping (`//`)
23
38
  - Error handling with `LassTranspileError` and error categories
package/README.md CHANGED
@@ -69,10 +69,10 @@ try {
69
69
 
70
70
  The Lass transpiler supports the full language feature set:
71
71
 
72
- - **Two-zone model** - Use `---` separator to define a JS preamble zone followed by a CSS zone
72
+ - **Two-zone model** - Use opening/closing `---` delimiters to define a JS preamble zone followed by a CSS zone
73
73
  - **Dollar substitution** - `$name` text substitution from preamble variables
74
74
  - **Expression interpolation** - `{{ expr }}` for inline JavaScript expressions
75
- - **Property lookup** - `@(prop)` or `@prop` shorthand to reference previously defined CSS property values
75
+ - **Property lookup** - `@(prop)` to reference previously defined CSS property values
76
76
  - **Style blocks** - `@{ }` to embed CSS fragments generated from JavaScript
77
77
  - **Comment stripping** - `//` single-line comments are removed from output
78
78
 
@@ -12,11 +12,11 @@
12
12
  * - null/undefined/false -> '' (React-style silent handling)
13
13
  * - arrays -> recursively flattened, filtered, stringified, then joined
14
14
  * - If any element contains newline, join with newline (multi-line blocks)
15
- * - Otherwise join with empty string (inline values)
15
+ * - Otherwise join with space (CSS-friendly: padding, margin, etc.)
16
16
  * - other values -> String coercion
17
17
  * - Multi-line strings are re-indented using the optional indent parameter
18
18
  */
19
- export declare const LASS_SCRIPT_EXPRESSION_HELPER = "const __lassScriptExpression = (v, indent = '') => { if (v == null || v === false) return ''; if (Array.isArray(v)) { const a = v.flat(Infinity).map(x => (x == null || x === false) ? '' : String(x)).filter(x => x); const sep = a.some(x => x.includes('\\n')) ? '\\n' : ''; return a.join(sep); } const s = String(v); if (!indent || !s.includes('\\n')) return s; return s.split('\\n').map((l, i) => i === 0 ? l : indent + l).join('\\n'); };";
19
+ export declare const LASS_SCRIPT_EXPRESSION_HELPER = "const __lassScriptExpression = (v, indent = '') => { if (v == null || v === false) return ''; if (Array.isArray(v)) { const a = v.flat(Infinity).map(x => (x == null || x === false) ? '' : String(x)).filter(x => x); const sep = a.some(x => x.includes('\\n')) ? '\\n' : ' '; return a.join(sep); } const s = String(v); if (!indent || !s.includes('\\n')) return s; return s.split('\\n').map((l, i) => i === 0 ? l : indent + l).join('\\n'); };";
20
20
  /**
21
21
  * Runtime helper function for $param variable substitution.
22
22
  * Story 4.1: Variable Substitution
@@ -1 +1 @@
1
- {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,6BAA6B,0bAA0b,CAAC;AAEre;;;;;;;;;;GAUG;AACH,eAAO,MAAM,yBAAyB,4JAA4J,CAAC"}
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,6BAA6B,2bAA2b,CAAC;AAEte;;;;;;;;;;GAUG;AACH,eAAO,MAAM,yBAAyB,4JAA4J,CAAC"}
package/dist/constants.js CHANGED
@@ -12,11 +12,11 @@
12
12
  * - null/undefined/false -> '' (React-style silent handling)
13
13
  * - arrays -> recursively flattened, filtered, stringified, then joined
14
14
  * - If any element contains newline, join with newline (multi-line blocks)
15
- * - Otherwise join with empty string (inline values)
15
+ * - Otherwise join with space (CSS-friendly: padding, margin, etc.)
16
16
  * - other values -> String coercion
17
17
  * - Multi-line strings are re-indented using the optional indent parameter
18
18
  */
19
- export const LASS_SCRIPT_EXPRESSION_HELPER = `const __lassScriptExpression = (v, indent = '') => { if (v == null || v === false) return ''; if (Array.isArray(v)) { const a = v.flat(Infinity).map(x => (x == null || x === false) ? '' : String(x)).filter(x => x); const sep = a.some(x => x.includes('\\n')) ? '\\n' : ''; return a.join(sep); } const s = String(v); if (!indent || !s.includes('\\n')) return s; return s.split('\\n').map((l, i) => i === 0 ? l : indent + l).join('\\n'); };`;
19
+ export const LASS_SCRIPT_EXPRESSION_HELPER = `const __lassScriptExpression = (v, indent = '') => { if (v == null || v === false) return ''; if (Array.isArray(v)) { const a = v.flat(Infinity).map(x => (x == null || x === false) ? '' : String(x)).filter(x => x); const sep = a.some(x => x.includes('\\n')) ? '\\n' : ' '; return a.join(sep); } const s = String(v); if (!indent || !s.includes('\\n')) return s; return s.split('\\n').map((l, i) => i === 0 ? l : indent + l).join('\\n'); };`;
20
20
  /**
21
21
  * Runtime helper function for $param variable substitution.
22
22
  * Story 4.1: Variable Substitution
@@ -1 +1 @@
1
- {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,6BAA6B,GAAG,ubAAub,CAAC;AAEre;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,yJAAyJ,CAAC"}
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,6BAA6B,GAAG,wbAAwb,CAAC;AAEte;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,yJAAyJ,CAAC"}
@@ -1,10 +1,9 @@
1
1
  /**
2
2
  * Shared context tracking utilities for protected zones in CSS.
3
3
  *
4
- * Story 4.2: Extracted to reduce code duplication between
5
- * findDollarVariablesStatic() and findStyleLookupShorthandsStatic().
4
+ * Extracted to reduce code duplication between scanner methods.
6
5
  *
7
- * Protected contexts are zones where Lass symbols ($param, @prop) should
6
+ * Protected contexts are zones where Lass symbols ($param, @(prop)) should
8
7
  * NOT be detected:
9
8
  * - String literals ("..." or '...')
10
9
  * - Block comments (slash-star ... star-slash)
@@ -28,7 +27,7 @@ export interface ContextState {
28
27
  export declare function createContextState(): ContextState;
29
28
  /**
30
29
  * Checks if currently in a protected context (string or block comment).
31
- * Protected contexts prevent symbol detection for $param and @prop.
30
+ * Protected contexts prevent symbol detection for $param and @(prop).
32
31
  *
33
32
  * Note: url() is NOT a protected context - only strings and comments are.
34
33
  */
@@ -1 +1 @@
1
- {"version":3,"file":"context-tracker.d.ts","sourceRoot":"","sources":["../src/context-tracker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,yDAAyD;IACzD,QAAQ,EAAE,OAAO,CAAC;IAClB,yDAAyD;IACzD,UAAU,EAAE,MAAM,CAAC;IACnB,uCAAuC;IACvC,cAAc,EAAE,OAAO,CAAC;CACzB;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,YAAY,CAMjD;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAEjE;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,YAAY,GAClB,MAAM,CAoCR"}
1
+ {"version":3,"file":"context-tracker.d.ts","sourceRoot":"","sources":["../src/context-tracker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,yDAAyD;IACzD,QAAQ,EAAE,OAAO,CAAC;IAClB,yDAAyD;IACzD,UAAU,EAAE,MAAM,CAAC;IACnB,uCAAuC;IACvC,cAAc,EAAE,OAAO,CAAC;CACzB;AAED;;GAEG;AACH,wBAAgB,kBAAkB,IAAI,YAAY,CAMjD;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAEjE;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,YAAY,GAClB,MAAM,CAoCR"}
@@ -1,10 +1,9 @@
1
1
  /**
2
2
  * Shared context tracking utilities for protected zones in CSS.
3
3
  *
4
- * Story 4.2: Extracted to reduce code duplication between
5
- * findDollarVariablesStatic() and findStyleLookupShorthandsStatic().
4
+ * Extracted to reduce code duplication between scanner methods.
6
5
  *
7
- * Protected contexts are zones where Lass symbols ($param, @prop) should
6
+ * Protected contexts are zones where Lass symbols ($param, @(prop)) should
8
7
  * NOT be detected:
9
8
  * - String literals ("..." or '...')
10
9
  * - Block comments (slash-star ... star-slash)
@@ -23,7 +22,7 @@ export function createContextState() {
23
22
  }
24
23
  /**
25
24
  * Checks if currently in a protected context (string or block comment).
26
- * Protected contexts prevent symbol detection for $param and @prop.
25
+ * Protected contexts prevent symbol detection for $param and @(prop).
27
26
  *
28
27
  * Note: url() is NOT a protected context - only strings and comments are.
29
28
  */
@@ -1 +1 @@
1
- {"version":3,"file":"context-tracker.js","sourceRoot":"","sources":["../src/context-tracker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAcH;;GAEG;AACH,MAAM,UAAU,kBAAkB;IAChC,OAAO;QACL,QAAQ,EAAE,KAAK;QACf,UAAU,EAAE,EAAE;QACd,cAAc,EAAE,KAAK;KACtB,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,KAAmB;IACtD,OAAO,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,cAAc,CAAC;AAChD,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,kBAAkB,CAChC,IAAY,EACZ,KAAa,EACb,KAAmB;IAEnB,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAE,CAAC;IAC1B,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;IAEjC,gCAAgC;IAChC,IAAI,CAAC,KAAK,CAAC,QAAQ,IAAI,CAAC,KAAK,CAAC,cAAc,IAAI,IAAI,KAAK,GAAG,IAAI,QAAQ,KAAK,GAAG,EAAE,CAAC;QACjF,KAAK,CAAC,cAAc,GAAG,IAAI,CAAC;QAC5B,OAAO,CAAC,CAAC;IACX,CAAC;IAED,8BAA8B;IAC9B,IAAI,KAAK,CAAC,cAAc,IAAI,IAAI,KAAK,GAAG,IAAI,QAAQ,KAAK,GAAG,EAAE,CAAC;QAC7D,KAAK,CAAC,cAAc,GAAG,KAAK,CAAC;QAC7B,OAAO,CAAC,CAAC;IACX,CAAC;IAED,mDAAmD;IACnD,IAAI,CAAC,KAAK,CAAC,cAAc,IAAI,CAAC,IAAI,KAAK,GAAG,IAAI,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;QAC5D,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;YACpB,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAC;YACtB,KAAK,CAAC,UAAU,GAAG,IAAI,CAAC;QAC1B,CAAC;aAAM,IAAI,IAAI,KAAK,KAAK,CAAC,UAAU,EAAE,CAAC;YACrC,0DAA0D;YAC1D,IAAI,cAAc,GAAG,CAAC,CAAC;YACvB,KAAK,IAAI,CAAC,GAAG,KAAK,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC;gBACxD,cAAc,EAAE,CAAC;YACnB,CAAC;YACD,iDAAiD;YACjD,IAAI,cAAc,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC7B,KAAK,CAAC,QAAQ,GAAG,KAAK,CAAC;YACzB,CAAC;QACH,CAAC;QACD,OAAO,CAAC,CAAC;IACX,CAAC;IAED,OAAO,CAAC,CAAC;AACX,CAAC"}
1
+ {"version":3,"file":"context-tracker.js","sourceRoot":"","sources":["../src/context-tracker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAcH;;GAEG;AACH,MAAM,UAAU,kBAAkB;IAChC,OAAO;QACL,QAAQ,EAAE,KAAK;QACf,UAAU,EAAE,EAAE;QACd,cAAc,EAAE,KAAK;KACtB,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,KAAmB;IACtD,OAAO,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,cAAc,CAAC;AAChD,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,kBAAkB,CAChC,IAAY,EACZ,KAAa,EACb,KAAmB;IAEnB,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAE,CAAC;IAC1B,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;IAEjC,gCAAgC;IAChC,IAAI,CAAC,KAAK,CAAC,QAAQ,IAAI,CAAC,KAAK,CAAC,cAAc,IAAI,IAAI,KAAK,GAAG,IAAI,QAAQ,KAAK,GAAG,EAAE,CAAC;QACjF,KAAK,CAAC,cAAc,GAAG,IAAI,CAAC;QAC5B,OAAO,CAAC,CAAC;IACX,CAAC;IAED,8BAA8B;IAC9B,IAAI,KAAK,CAAC,cAAc,IAAI,IAAI,KAAK,GAAG,IAAI,QAAQ,KAAK,GAAG,EAAE,CAAC;QAC7D,KAAK,CAAC,cAAc,GAAG,KAAK,CAAC;QAC7B,OAAO,CAAC,CAAC;IACX,CAAC;IAED,mDAAmD;IACnD,IAAI,CAAC,KAAK,CAAC,cAAc,IAAI,CAAC,IAAI,KAAK,GAAG,IAAI,IAAI,KAAK,GAAG,CAAC,EAAE,CAAC;QAC5D,IAAI,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC;YACpB,KAAK,CAAC,QAAQ,GAAG,IAAI,CAAC;YACtB,KAAK,CAAC,UAAU,GAAG,IAAI,CAAC;QAC1B,CAAC;aAAM,IAAI,IAAI,KAAK,KAAK,CAAC,UAAU,EAAE,CAAC;YACrC,0DAA0D;YAC1D,IAAI,cAAc,GAAG,CAAC,CAAC;YACvB,KAAK,IAAI,CAAC,GAAG,KAAK,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC,EAAE,EAAE,CAAC;gBACxD,cAAc,EAAE,CAAC;YACnB,CAAC;YACD,iDAAiD;YACjD,IAAI,cAAc,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC7B,KAAK,CAAC,QAAQ,GAAG,KAAK,CAAC;YACzB,CAAC;QACH,CAAC;QACD,OAAO,CAAC,CAAC;IACX,CAAC;IAED,OAAO,CAAC,CAAC;AACX,CAAC"}
package/dist/index.d.ts CHANGED
@@ -7,11 +7,10 @@
7
7
  * Transpilation Pipeline (The Story):
8
8
  * 1. detectZones() - Split source into preamble and CSS zones
9
9
  * 2. stripLineComments() - Remove // comments from CSS zone
10
- * 3. normalizeStyleLookupShorthands() - @prop -> @(prop)
11
- * 4. resolvePropertyAccessors() - @(prop) -> value
12
- * 5. resolveDollarVariables() - $param -> ${...}
13
- * 6. processExpressions() - {{ expr }} -> ${...}
14
- * 7. buildOutput() - Assemble final JS module
10
+ * 3. resolvePropertyAccessors() - @(prop) -> value
11
+ * 4. resolveDollarVariables() - $param -> ${...}
12
+ * 5. processExpressions() - {{ expr }} -> ${...}
13
+ * 6. buildOutput() - Assemble final JS module
15
14
  *
16
15
  * This is the "igloo" view - each function is a building block.
17
16
  * Drill into transpiler.ts for step implementations.
@@ -23,11 +22,10 @@ import type { TranspileResult, TranspileOptions } from './types.js';
23
22
  * The Story (Igloo Principle):
24
23
  * 1. Split the file into preamble and CSS zones at the ---
25
24
  * 2. Strip // comments from CSS zone
26
- * 3. Normalize @prop -> @(prop) shorthands
27
- * 4. Resolve @(prop) accessors to their values
28
- * 5. Replace $param with ${...} for variable substitution
29
- * 6. Find {{ expressions }} and make them interpolations
30
- * 7. Wrap it all in a JS module that exports CSS
25
+ * 3. Resolve @(prop) accessors to their values
26
+ * 4. Replace $param with ${...} for variable substitution
27
+ * 5. Find {{ expressions }} and make them interpolations
28
+ * 6. Wrap it all in a JS module that exports CSS
31
29
  *
32
30
  * Implementation History:
33
31
  * - Story 1.4: CSS passthrough - wraps input in JS module export
@@ -40,8 +38,8 @@ import type { TranspileResult, TranspileOptions } from './types.js';
40
38
  * - Story 3.3: @(prop) in {{ }} - detects @(prop) inside expressions, quotes values for JS context
41
39
  * - Refactored: Changed from @prop to @(prop) for unambiguous syntax (supports custom properties)
42
40
  * - Story 4.1: $param substitution - replaces $param with ${$param} for template literal interpolation
43
- * - Story 4.2: @prop shorthand - normalizes @prop to @(prop) before resolution
44
41
  * - Story 4.4: // comment stripping - removes single-line comments from CSS zone
42
+ * - Story 8.2: @prop shorthand removed - was conflicting with CSS at-rules (@slot, @custom-variant, etc.)
45
43
  *
46
44
  * @param source - The Lass source code
47
45
  * @param options - Transpilation options
@@ -58,9 +56,8 @@ export { Scanner } from './scanner.js';
58
56
  * - ExpressionSplit: Result of {{ }} expression splitting
59
57
  * - PropertyAccessor: Info about detected @(prop) accessor (propName, indices)
60
58
  * - DollarVariable: Info about detected $param variable (varName, indices)
61
- * - StyleLookupShorthand: Info about detected @prop shorthand (propName, indices)
62
59
  */
63
- export type { ScanResult, ScanOptions, ZoneSplit, ExpressionSplit, PropertyAccessor, DollarVariable, StyleLookupShorthand } from './scanner.js';
60
+ export type { ScanResult, ScanOptions, ZoneSplit, ExpressionSplit, PropertyAccessor, DollarVariable } from './scanner.js';
64
61
  export { LassTranspileError, ErrorCategory, formatLocation, type SourceLocation, type FileLocation, } from './errors.js';
65
62
  export { cutByBraces, findPropertyValue, areSiblingTrees, isInsideAtRule, type ScopeSlice, type ScopeSlices, } from './scope-tracker.js';
66
63
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAWH,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAMpE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,SAAS,CACvB,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,gBAAqB,GAC7B,eAAe,CAuBjB;AAMD,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAMpE,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC;;;;;;;;;GASG;AACH,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAE,eAAe,EAAE,gBAAgB,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAMhJ,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,cAAc,EACd,KAAK,cAAc,EACnB,KAAK,YAAY,GAClB,MAAM,aAAa,CAAC;AAMrB,OAAO,EACL,WAAW,EACX,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,KAAK,UAAU,EACf,KAAK,WAAW,GACjB,MAAM,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAUH,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAMpE;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,SAAS,CACvB,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,gBAAqB,GAC7B,eAAe,CAoBjB;AAMD,YAAY,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAMpE,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC;;;;;;;;GAQG;AACH,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAE,eAAe,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAM1H,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,cAAc,EACd,KAAK,cAAc,EACnB,KAAK,YAAY,GAClB,MAAM,aAAa,CAAC;AAMrB,OAAO,EACL,WAAW,EACX,iBAAiB,EACjB,eAAe,EACf,cAAc,EACd,KAAK,UAAU,EACf,KAAK,WAAW,GACjB,MAAM,oBAAoB,CAAC"}
package/dist/index.js CHANGED
@@ -7,16 +7,15 @@
7
7
  * Transpilation Pipeline (The Story):
8
8
  * 1. detectZones() - Split source into preamble and CSS zones
9
9
  * 2. stripLineComments() - Remove // comments from CSS zone
10
- * 3. normalizeStyleLookupShorthands() - @prop -> @(prop)
11
- * 4. resolvePropertyAccessors() - @(prop) -> value
12
- * 5. resolveDollarVariables() - $param -> ${...}
13
- * 6. processExpressions() - {{ expr }} -> ${...}
14
- * 7. buildOutput() - Assemble final JS module
10
+ * 3. resolvePropertyAccessors() - @(prop) -> value
11
+ * 4. resolveDollarVariables() - $param -> ${...}
12
+ * 5. processExpressions() - {{ expr }} -> ${...}
13
+ * 6. buildOutput() - Assemble final JS module
15
14
  *
16
15
  * This is the "igloo" view - each function is a building block.
17
16
  * Drill into transpiler.ts for step implementations.
18
17
  */
19
- import { detectZones, stripLineComments, normalizeStyleLookupShorthands, resolvePropertyAccessors, resolveDollarVariables, processExpressions, buildOutput, } from './transpiler.js';
18
+ import { detectZones, stripLineComments, resolvePropertyAccessors, resolveDollarVariables, processExpressions, buildOutput, } from './transpiler.js';
20
19
  // ============================================================================
21
20
  // MAIN ENTRY POINT
22
21
  // ============================================================================
@@ -26,11 +25,10 @@ import { detectZones, stripLineComments, normalizeStyleLookupShorthands, resolve
26
25
  * The Story (Igloo Principle):
27
26
  * 1. Split the file into preamble and CSS zones at the ---
28
27
  * 2. Strip // comments from CSS zone
29
- * 3. Normalize @prop -> @(prop) shorthands
30
- * 4. Resolve @(prop) accessors to their values
31
- * 5. Replace $param with ${...} for variable substitution
32
- * 6. Find {{ expressions }} and make them interpolations
33
- * 7. Wrap it all in a JS module that exports CSS
28
+ * 3. Resolve @(prop) accessors to their values
29
+ * 4. Replace $param with ${...} for variable substitution
30
+ * 5. Find {{ expressions }} and make them interpolations
31
+ * 6. Wrap it all in a JS module that exports CSS
34
32
  *
35
33
  * Implementation History:
36
34
  * - Story 1.4: CSS passthrough - wraps input in JS module export
@@ -43,8 +41,8 @@ import { detectZones, stripLineComments, normalizeStyleLookupShorthands, resolve
43
41
  * - Story 3.3: @(prop) in {{ }} - detects @(prop) inside expressions, quotes values for JS context
44
42
  * - Refactored: Changed from @prop to @(prop) for unambiguous syntax (supports custom properties)
45
43
  * - Story 4.1: $param substitution - replaces $param with ${$param} for template literal interpolation
46
- * - Story 4.2: @prop shorthand - normalizes @prop to @(prop) before resolution
47
44
  * - Story 4.4: // comment stripping - removes single-line comments from CSS zone
45
+ * - Story 8.2: @prop shorthand removed - was conflicting with CSS at-rules (@slot, @custom-variant, etc.)
48
46
  *
49
47
  * @param source - The Lass source code
50
48
  * @param options - Transpilation options
@@ -55,10 +53,8 @@ export function transpile(source, options = {}) {
55
53
  const zones = detectZones(source, options);
56
54
  // Step 2: Strip // comments from CSS zone (before any symbol resolution)
57
55
  const strippedCssZone = stripLineComments(zones.cssZone);
58
- // Step 3a: Normalize @prop shorthands to @(prop) form
59
- const normalizedCssZone = normalizeStyleLookupShorthands(strippedCssZone);
60
- // Step 3b: Resolve @(prop) accessors (Phase 1 - before {{ }} processing)
61
- const resolvedCssZone = resolvePropertyAccessors(normalizedCssZone, options);
56
+ // Step 3: Resolve @(prop) accessors (Phase 1 - before {{ }} processing)
57
+ const resolvedCssZone = resolvePropertyAccessors(strippedCssZone, options);
62
58
  // Step 4: Replace $param with __lassScriptLookup() calls for variable substitution
63
59
  const dollarResult = resolveDollarVariables(resolvedCssZone, options);
64
60
  // Step 5: Process {{ expressions }} in CSS zone (Phase 2)
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EACL,WAAW,EACX,iBAAiB,EACjB,8BAA8B,EAC9B,wBAAwB,EACxB,sBAAsB,EACtB,kBAAkB,EAClB,WAAW,GACZ,MAAM,iBAAiB,CAAC;AAGzB,+EAA+E;AAC/E,mBAAmB;AACnB,+EAA+E;AAE/E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,MAAM,UAAU,SAAS,CACvB,MAAc,EACd,UAA4B,EAAE;IAE9B,mDAAmD;IACnD,MAAM,KAAK,GAAG,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAE3C,yEAAyE;IACzE,MAAM,eAAe,GAAG,iBAAiB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAEzD,sDAAsD;IACtD,MAAM,iBAAiB,GAAG,8BAA8B,CAAC,eAAe,CAAC,CAAC;IAE1E,yEAAyE;IACzE,MAAM,eAAe,GAAG,wBAAwB,CAAC,iBAAiB,EAAE,OAAO,CAAC,CAAC;IAE7E,mFAAmF;IACnF,MAAM,YAAY,GAAG,sBAAsB,CAAC,eAAe,EAAE,OAAO,CAAC,CAAC;IAEtE,0DAA0D;IAC1D,MAAM,QAAQ,GAAG,kBAAkB,CAAC,YAAY,CAAC,OAAO,EAAE,YAAY,CAAC,kBAAkB,EAAE,OAAO,CAAC,CAAC;IAEpG,mCAAmC;IACnC,MAAM,IAAI,GAAG,WAAW,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IAE1C,OAAO,EAAE,IAAI,EAAE,CAAC;AAClB,CAAC;AAQD,+EAA+E;AAC/E,sBAAsB;AACtB,+EAA+E;AAE/E,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAavC,+EAA+E;AAC/E,qBAAqB;AACrB,+EAA+E;AAE/E,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,cAAc,GAGf,MAAM,aAAa,CAAC;AAErB,+EAA+E;AAC/E,qEAAqE;AACrE,+EAA+E;AAE/E,OAAO,EACL,WAAW,EACX,iBAAiB,EACjB,eAAe,EACf,cAAc,GAGf,MAAM,oBAAoB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EACL,WAAW,EACX,iBAAiB,EACjB,wBAAwB,EACxB,sBAAsB,EACtB,kBAAkB,EAClB,WAAW,GACZ,MAAM,iBAAiB,CAAC;AAGzB,+EAA+E;AAC/E,mBAAmB;AACnB,+EAA+E;AAE/E;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,MAAM,UAAU,SAAS,CACvB,MAAc,EACd,UAA4B,EAAE;IAE9B,mDAAmD;IACnD,MAAM,KAAK,GAAG,WAAW,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAE3C,yEAAyE;IACzE,MAAM,eAAe,GAAG,iBAAiB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAEzD,wEAAwE;IACxE,MAAM,eAAe,GAAG,wBAAwB,CAAC,eAAe,EAAE,OAAO,CAAC,CAAC;IAE3E,mFAAmF;IACnF,MAAM,YAAY,GAAG,sBAAsB,CAAC,eAAe,EAAE,OAAO,CAAC,CAAC;IAEtE,0DAA0D;IAC1D,MAAM,QAAQ,GAAG,kBAAkB,CAAC,YAAY,CAAC,OAAO,EAAE,YAAY,CAAC,kBAAkB,EAAE,OAAO,CAAC,CAAC;IAEpG,mCAAmC;IACnC,MAAM,IAAI,GAAG,WAAW,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IAE1C,OAAO,EAAE,IAAI,EAAE,CAAC;AAClB,CAAC;AAQD,+EAA+E;AAC/E,sBAAsB;AACtB,+EAA+E;AAE/E,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAYvC,+EAA+E;AAC/E,qBAAqB;AACrB,+EAA+E;AAE/E,OAAO,EACL,kBAAkB,EAClB,aAAa,EACb,cAAc,GAGf,MAAM,aAAa,CAAC;AAErB,+EAA+E;AAC/E,qEAAqE;AACrE,+EAA+E;AAE/E,OAAO,EACL,WAAW,EACX,iBAAiB,EACjB,eAAe,EACf,cAAc,GAGf,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Plugin utilities for bundler integrations.
3
+ *
4
+ * Shared utilities for Vite, Bun, esbuild, Rollup, Webpack plugins.
5
+ * These handle the "transpile -> execute -> extract CSS" workflow that
6
+ * all bundler plugins need.
7
+ *
8
+ * Virtual Path Convention:
9
+ * - foo.lass -> foo.lass.css (regular CSS)
10
+ * - foo.module.lass -> foo.lass.module.css (CSS Modules)
11
+ *
12
+ * The ".lass" is preserved as a "brand" identifier, while ".module" is
13
+ * placed before ".css" where bundlers expect it for CSS Modules detection.
14
+ *
15
+ * @module plugin-utils
16
+ */
17
+ /** Extension for .lass source files */
18
+ export declare const LASS_EXT = ".lass";
19
+ /** Virtual CSS extension for regular .lass files: foo.lass -> foo.lass.css */
20
+ export declare const VIRTUAL_CSS_EXT = ".lass.css";
21
+ /** Virtual CSS extension for CSS Modules: foo.module.lass -> foo.lass.module.css */
22
+ export declare const VIRTUAL_MODULE_CSS_EXT = ".lass.module.css";
23
+ /**
24
+ * Regex for matching ES import statements with relative paths.
25
+ *
26
+ * Captures:
27
+ * - Group 1: Import prefix (e.g., "import x from '")
28
+ * - Group 2: Relative path (e.g., "./file.json")
29
+ * - Group 3: Quote suffix (e.g., "'")
30
+ * - Group 4: Trailing content (e.g., " with { type: 'json' };")
31
+ *
32
+ * Supports:
33
+ * - Default imports: import x from './file'
34
+ * - Named imports: import { x } from './file'
35
+ * - Namespace imports: import * as x from './file'
36
+ * - Side-effect imports: import './file'
37
+ * - Import assertions: import x from './file' with { type: 'json' }
38
+ */
39
+ export declare const IMPORT_STATEMENT_RE: RegExp;
40
+ /**
41
+ * Convert a .lass file path to its virtual CSS path.
42
+ *
43
+ * @param lassPath - Path to .lass file
44
+ * @returns Virtual CSS path for bundler consumption
45
+ *
46
+ * @example
47
+ * toVirtualCssPath('src/styles.lass')
48
+ * // => 'src/styles.lass.css'
49
+ *
50
+ * toVirtualCssPath('src/component.module.lass')
51
+ * // => 'src/component.lass.module.css'
52
+ */
53
+ export declare function toVirtualCssPath(lassPath: string): string;
54
+ /**
55
+ * Convert a virtual CSS path back to its .lass source path.
56
+ *
57
+ * @param virtualPath - Virtual CSS path
58
+ * @returns Original .lass file path
59
+ *
60
+ * @example
61
+ * fromVirtualCssPath('src/styles.lass.css')
62
+ * // => 'src/styles.lass'
63
+ *
64
+ * fromVirtualCssPath('src/component.lass.module.css')
65
+ * // => 'src/component.module.lass'
66
+ */
67
+ export declare function fromVirtualCssPath(virtualPath: string): string;
68
+ /**
69
+ * Check if a path is a virtual CSS path (regular or module).
70
+ *
71
+ * @param path - Path to check
72
+ * @returns True if path ends with .lass.css or .lass.module.css
73
+ */
74
+ export declare function isVirtualCssPath(path: string): boolean;
75
+ /**
76
+ * Check if a path is a virtual CSS Modules path.
77
+ *
78
+ * @param path - Path to check
79
+ * @returns True if path ends with .lass.module.css
80
+ */
81
+ export declare function isVirtualModuleCssPath(path: string): boolean;
82
+ /**
83
+ * Normalize path for cross-platform compatibility.
84
+ * Converts backslashes to forward slashes.
85
+ *
86
+ * @param path - Path to normalize
87
+ * @returns Path with forward slashes only
88
+ */
89
+ export declare function normalizePath(path: string): string;
90
+ /**
91
+ * Rewrite relative imports to absolute file:// URLs for isolated execution.
92
+ *
93
+ * When executing transpiled Lass code via data: URL, relative imports
94
+ * won't resolve correctly. This function converts them to absolute
95
+ * file:// URLs that work from any execution context.
96
+ *
97
+ * Also auto-adds JSON import assertions when missing (required by Node.js).
98
+ *
99
+ * @param code - Transpiled JS code with relative imports
100
+ * @param baseDir - Directory to resolve relative paths from
101
+ * @returns Code with absolute file:// URLs
102
+ *
103
+ * @example
104
+ * rewriteImportsForExecution(
105
+ * "import tokens from './tokens.json'",
106
+ * "/project/src"
107
+ * )
108
+ * // => "import tokens from 'file:///project/src/tokens.json' with { type: 'json' }"
109
+ */
110
+ export declare function rewriteImportsForExecution(code: string, baseDir: string): string;
111
+ /**
112
+ * Execute transpiled Lass JS module and extract the CSS string.
113
+ *
114
+ * Uses a temporary file to execute the module code. This approach
115
+ * is necessary because Bun's bundler statically analyzes import()
116
+ * statements and fails on data: URLs with "NameTooLong" errors.
117
+ *
118
+ * The transpiled module must export the CSS string as its default export.
119
+ *
120
+ * @param jsCode - Transpiled JS module code
121
+ * @returns The CSS string from the module's default export
122
+ *
123
+ * @example
124
+ * const css = await extractStyle(`
125
+ * const color = "blue";
126
+ * export default \`.box { color: \${color}; }\`;
127
+ * `);
128
+ * // => ".box { color: blue; }"
129
+ */
130
+ export declare function extractStyle(jsCode: string): Promise<string>;
131
+ //# sourceMappingURL=plugin-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin-utils.d.ts","sourceRoot":"","sources":["../src/plugin-utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AASH,uCAAuC;AACvC,eAAO,MAAM,QAAQ,UAAU,CAAC;AAEhC,8EAA8E;AAC9E,eAAO,MAAM,eAAe,cAAc,CAAC;AAE3C,oFAAoF;AACpF,eAAO,MAAM,sBAAsB,qBAAqB,CAAC;AAEzD;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,mBAAmB,QACsE,CAAC;AAMvG;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAOzD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAU9D;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEtD;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAE5D;AAMD;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAElD;AAMD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAiBhF;AAWD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAkClE"}
@@ -0,0 +1,223 @@
1
+ /**
2
+ * Plugin utilities for bundler integrations.
3
+ *
4
+ * Shared utilities for Vite, Bun, esbuild, Rollup, Webpack plugins.
5
+ * These handle the "transpile -> execute -> extract CSS" workflow that
6
+ * all bundler plugins need.
7
+ *
8
+ * Virtual Path Convention:
9
+ * - foo.lass -> foo.lass.css (regular CSS)
10
+ * - foo.module.lass -> foo.lass.module.css (CSS Modules)
11
+ *
12
+ * The ".lass" is preserved as a "brand" identifier, while ".module" is
13
+ * placed before ".css" where bundlers expect it for CSS Modules detection.
14
+ *
15
+ * @module plugin-utils
16
+ */
17
+ import { posix, resolve } from 'node:path';
18
+ import { pathToFileURL } from 'node:url';
19
+ // ============================================================================
20
+ // CONSTANTS
21
+ // ============================================================================
22
+ /** Extension for .lass source files */
23
+ export const LASS_EXT = '.lass';
24
+ /** Virtual CSS extension for regular .lass files: foo.lass -> foo.lass.css */
25
+ export const VIRTUAL_CSS_EXT = '.lass.css';
26
+ /** Virtual CSS extension for CSS Modules: foo.module.lass -> foo.lass.module.css */
27
+ export const VIRTUAL_MODULE_CSS_EXT = '.lass.module.css';
28
+ /**
29
+ * Regex for matching ES import statements with relative paths.
30
+ *
31
+ * Captures:
32
+ * - Group 1: Import prefix (e.g., "import x from '")
33
+ * - Group 2: Relative path (e.g., "./file.json")
34
+ * - Group 3: Quote suffix (e.g., "'")
35
+ * - Group 4: Trailing content (e.g., " with { type: 'json' };")
36
+ *
37
+ * Supports:
38
+ * - Default imports: import x from './file'
39
+ * - Named imports: import { x } from './file'
40
+ * - Namespace imports: import * as x from './file'
41
+ * - Side-effect imports: import './file'
42
+ * - Import assertions: import x from './file' with { type: 'json' }
43
+ */
44
+ export const IMPORT_STATEMENT_RE = /^(\s*import\s+(?:[\w*{}\s,]+\s+from\s+)?['"])(\.[^'"]+)(['"])(\s*(?:with\s+\{[^}]*\})?\s*;?\s*)$/gm;
45
+ // ============================================================================
46
+ // VIRTUAL PATH UTILITIES
47
+ // ============================================================================
48
+ /**
49
+ * Convert a .lass file path to its virtual CSS path.
50
+ *
51
+ * @param lassPath - Path to .lass file
52
+ * @returns Virtual CSS path for bundler consumption
53
+ *
54
+ * @example
55
+ * toVirtualCssPath('src/styles.lass')
56
+ * // => 'src/styles.lass.css'
57
+ *
58
+ * toVirtualCssPath('src/component.module.lass')
59
+ * // => 'src/component.lass.module.css'
60
+ */
61
+ export function toVirtualCssPath(lassPath) {
62
+ if (lassPath.endsWith('.module.lass')) {
63
+ // foo.module.lass -> foo.lass.module.css
64
+ return lassPath.slice(0, -'.module.lass'.length) + VIRTUAL_MODULE_CSS_EXT;
65
+ }
66
+ // foo.lass -> foo.lass.css
67
+ return lassPath.slice(0, -LASS_EXT.length) + VIRTUAL_CSS_EXT;
68
+ }
69
+ /**
70
+ * Convert a virtual CSS path back to its .lass source path.
71
+ *
72
+ * @param virtualPath - Virtual CSS path
73
+ * @returns Original .lass file path
74
+ *
75
+ * @example
76
+ * fromVirtualCssPath('src/styles.lass.css')
77
+ * // => 'src/styles.lass'
78
+ *
79
+ * fromVirtualCssPath('src/component.lass.module.css')
80
+ * // => 'src/component.module.lass'
81
+ */
82
+ export function fromVirtualCssPath(virtualPath) {
83
+ if (virtualPath.endsWith(VIRTUAL_MODULE_CSS_EXT)) {
84
+ // foo.lass.module.css -> foo.module.lass
85
+ return virtualPath.slice(0, -VIRTUAL_MODULE_CSS_EXT.length) + '.module.lass';
86
+ }
87
+ if (virtualPath.endsWith(VIRTUAL_CSS_EXT)) {
88
+ // foo.lass.css -> foo.lass
89
+ return virtualPath.slice(0, -VIRTUAL_CSS_EXT.length) + LASS_EXT;
90
+ }
91
+ return virtualPath;
92
+ }
93
+ /**
94
+ * Check if a path is a virtual CSS path (regular or module).
95
+ *
96
+ * @param path - Path to check
97
+ * @returns True if path ends with .lass.css or .lass.module.css
98
+ */
99
+ export function isVirtualCssPath(path) {
100
+ return path.endsWith(VIRTUAL_CSS_EXT) || path.endsWith(VIRTUAL_MODULE_CSS_EXT);
101
+ }
102
+ /**
103
+ * Check if a path is a virtual CSS Modules path.
104
+ *
105
+ * @param path - Path to check
106
+ * @returns True if path ends with .lass.module.css
107
+ */
108
+ export function isVirtualModuleCssPath(path) {
109
+ return path.endsWith(VIRTUAL_MODULE_CSS_EXT);
110
+ }
111
+ // ============================================================================
112
+ // PATH UTILITIES
113
+ // ============================================================================
114
+ /**
115
+ * Normalize path for cross-platform compatibility.
116
+ * Converts backslashes to forward slashes.
117
+ *
118
+ * @param path - Path to normalize
119
+ * @returns Path with forward slashes only
120
+ */
121
+ export function normalizePath(path) {
122
+ return path.split(/[\\/]/).join(posix.sep);
123
+ }
124
+ // ============================================================================
125
+ // IMPORT REWRITING
126
+ // ============================================================================
127
+ /**
128
+ * Rewrite relative imports to absolute file:// URLs for isolated execution.
129
+ *
130
+ * When executing transpiled Lass code via data: URL, relative imports
131
+ * won't resolve correctly. This function converts them to absolute
132
+ * file:// URLs that work from any execution context.
133
+ *
134
+ * Also auto-adds JSON import assertions when missing (required by Node.js).
135
+ *
136
+ * @param code - Transpiled JS code with relative imports
137
+ * @param baseDir - Directory to resolve relative paths from
138
+ * @returns Code with absolute file:// URLs
139
+ *
140
+ * @example
141
+ * rewriteImportsForExecution(
142
+ * "import tokens from './tokens.json'",
143
+ * "/project/src"
144
+ * )
145
+ * // => "import tokens from 'file:///project/src/tokens.json' with { type: 'json' }"
146
+ */
147
+ export function rewriteImportsForExecution(code, baseDir) {
148
+ return code.replace(IMPORT_STATEMENT_RE, (_match, prefix, relativePath, suffix, trailing) => {
149
+ const absolutePath = resolve(baseDir, relativePath);
150
+ const fileUrl = pathToFileURL(absolutePath).href;
151
+ // Handle import assertions
152
+ let assertion = '';
153
+ if (trailing.includes('with')) {
154
+ // Already has assertion, don't add another
155
+ assertion = '';
156
+ }
157
+ else if (relativePath.endsWith('.json')) {
158
+ // Auto-add JSON assertion (required by Node.js)
159
+ assertion = " with { type: 'json' }";
160
+ }
161
+ return `${prefix}${fileUrl}${suffix}${assertion}${trailing}`;
162
+ });
163
+ }
164
+ // ============================================================================
165
+ // MODULE EXECUTION
166
+ // ============================================================================
167
+ /**
168
+ * Detect if we're running in Bun runtime.
169
+ */
170
+ const isBun = typeof globalThis.Bun !== 'undefined';
171
+ /**
172
+ * Execute transpiled Lass JS module and extract the CSS string.
173
+ *
174
+ * Uses a temporary file to execute the module code. This approach
175
+ * is necessary because Bun's bundler statically analyzes import()
176
+ * statements and fails on data: URLs with "NameTooLong" errors.
177
+ *
178
+ * The transpiled module must export the CSS string as its default export.
179
+ *
180
+ * @param jsCode - Transpiled JS module code
181
+ * @returns The CSS string from the module's default export
182
+ *
183
+ * @example
184
+ * const css = await extractStyle(`
185
+ * const color = "blue";
186
+ * export default \`.box { color: \${color}; }\`;
187
+ * `);
188
+ * // => ".box { color: blue; }"
189
+ */
190
+ export async function extractStyle(jsCode) {
191
+ const { writeFile, unlink } = await import('node:fs/promises');
192
+ const { tmpdir } = await import('node:os');
193
+ const { join } = await import('node:path');
194
+ const { pathToFileURL } = await import('node:url');
195
+ // Generate unique temp file path
196
+ const tempPath = join(tmpdir(), `lass-${Date.now()}-${Math.random().toString(36).slice(2)}.mjs`);
197
+ try {
198
+ await writeFile(tempPath, jsCode, 'utf-8');
199
+ const fileUrl = pathToFileURL(tempPath).href;
200
+ let module;
201
+ if (isBun) {
202
+ // In Bun: use indirect Function to prevent bundler static analysis
203
+ // eslint-disable-next-line @typescript-eslint/no-implied-eval
204
+ const dynamicImport = new Function('url', 'return import(url)');
205
+ module = await dynamicImport(fileUrl);
206
+ }
207
+ else {
208
+ // In Node.js/Vitest: use direct dynamic import
209
+ module = await import(fileUrl);
210
+ }
211
+ return module.default;
212
+ }
213
+ finally {
214
+ // Clean up temp file
215
+ try {
216
+ await unlink(tempPath);
217
+ }
218
+ catch {
219
+ // Ignore cleanup errors
220
+ }
221
+ }
222
+ }
223
+ //# sourceMappingURL=plugin-utils.js.map