@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 +16 -1
- package/README.md +2 -2
- package/dist/constants.d.ts +2 -2
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +2 -2
- package/dist/constants.js.map +1 -1
- package/dist/context-tracker.d.ts +3 -4
- package/dist/context-tracker.d.ts.map +1 -1
- package/dist/context-tracker.js +3 -4
- package/dist/context-tracker.js.map +1 -1
- package/dist/index.d.ts +10 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -16
- package/dist/index.js.map +1 -1
- package/dist/plugin-utils.d.ts +131 -0
- package/dist/plugin-utils.d.ts.map +1 -0
- package/dist/plugin-utils.js +223 -0
- package/dist/plugin-utils.js.map +1 -0
- package/dist/scanner.d.ts +39 -63
- package/dist/scanner.d.ts.map +1 -1
- package/dist/scanner.js +70 -209
- package/dist/scanner.js.map +1 -1
- package/dist/scope-tracker.d.ts +2 -2
- package/dist/scope-tracker.js +2 -2
- package/dist/transpiler.d.ts +1 -18
- package/dist/transpiler.d.ts.map +1 -1
- package/dist/transpiler.js +2 -39
- package/dist/transpiler.js.map +1 -1
- package/package.json +5 -2
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)`
|
|
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 `---`
|
|
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)`
|
|
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
|
|
package/dist/constants.d.ts
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
|
|
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
|
package/dist/constants.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,6BAA6B,
|
|
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
|
|
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
|
package/dist/constants.js.map
CHANGED
|
@@ -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,
|
|
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
|
-
*
|
|
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
|
|
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"}
|
package/dist/context-tracker.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shared context tracking utilities for protected zones in CSS.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
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
|
|
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.
|
|
11
|
-
* 4.
|
|
12
|
-
* 5.
|
|
13
|
-
* 6.
|
|
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.
|
|
27
|
-
* 4.
|
|
28
|
-
* 5.
|
|
29
|
-
* 6.
|
|
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
|
|
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
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA
|
|
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.
|
|
11
|
-
* 4.
|
|
12
|
-
* 5.
|
|
13
|
-
* 6.
|
|
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,
|
|
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.
|
|
30
|
-
* 4.
|
|
31
|
-
* 5.
|
|
32
|
-
* 6.
|
|
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
|
|
59
|
-
const
|
|
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
|
|
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
|