@jesscss/plugin-less-compat 2.0.0-alpha.1 → 2.0.0-alpha.11

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.
Files changed (112) hide show
  1. package/README.md +50 -115
  2. package/lib/index.cjs +342 -0
  3. package/lib/index.d.ts +2 -2
  4. package/lib/index.js +332 -9
  5. package/lib/less-api-bridge.d.ts +85 -0
  6. package/lib/plugin.d.ts +21 -82
  7. package/package.json +14 -19
  8. package/lib/index.js.map +0 -1
  9. package/lib/less-compat-structures.d.ts +0 -108
  10. package/lib/less-compat-structures.js +0 -519
  11. package/lib/less-compat-structures.js.map +0 -1
  12. package/lib/nodes/at-rule.d.ts +0 -6
  13. package/lib/nodes/at-rule.js +0 -72
  14. package/lib/nodes/at-rule.js.map +0 -1
  15. package/lib/nodes/attribute-selector.d.ts +0 -6
  16. package/lib/nodes/attribute-selector.js +0 -54
  17. package/lib/nodes/attribute-selector.js.map +0 -1
  18. package/lib/nodes/call.d.ts +0 -6
  19. package/lib/nodes/call.js +0 -83
  20. package/lib/nodes/call.js.map +0 -1
  21. package/lib/nodes/color.d.ts +0 -6
  22. package/lib/nodes/color.js +0 -57
  23. package/lib/nodes/color.js.map +0 -1
  24. package/lib/nodes/combinator.d.ts +0 -6
  25. package/lib/nodes/combinator.js +0 -34
  26. package/lib/nodes/combinator.js.map +0 -1
  27. package/lib/nodes/comment.d.ts +0 -6
  28. package/lib/nodes/comment.js +0 -41
  29. package/lib/nodes/comment.js.map +0 -1
  30. package/lib/nodes/condition.d.ts +0 -6
  31. package/lib/nodes/condition.js +0 -60
  32. package/lib/nodes/condition.js.map +0 -1
  33. package/lib/nodes/declaration.d.ts +0 -6
  34. package/lib/nodes/declaration.js +0 -81
  35. package/lib/nodes/declaration.js.map +0 -1
  36. package/lib/nodes/dimension.d.ts +0 -6
  37. package/lib/nodes/dimension.js +0 -47
  38. package/lib/nodes/dimension.js.map +0 -1
  39. package/lib/nodes/expression.d.ts +0 -6
  40. package/lib/nodes/expression.js +0 -44
  41. package/lib/nodes/expression.js.map +0 -1
  42. package/lib/nodes/extend.d.ts +0 -6
  43. package/lib/nodes/extend.js +0 -57
  44. package/lib/nodes/extend.js.map +0 -1
  45. package/lib/nodes/import.d.ts +0 -6
  46. package/lib/nodes/import.js +0 -63
  47. package/lib/nodes/import.js.map +0 -1
  48. package/lib/nodes/index.d.ts +0 -45
  49. package/lib/nodes/index.js +0 -308
  50. package/lib/nodes/index.js.map +0 -1
  51. package/lib/nodes/keyword.d.ts +0 -6
  52. package/lib/nodes/keyword.js +0 -36
  53. package/lib/nodes/keyword.js.map +0 -1
  54. package/lib/nodes/list.d.ts +0 -6
  55. package/lib/nodes/list.js +0 -150
  56. package/lib/nodes/list.js.map +0 -1
  57. package/lib/nodes/mixin.d.ts +0 -6
  58. package/lib/nodes/mixin.js +0 -62
  59. package/lib/nodes/mixin.js.map +0 -1
  60. package/lib/nodes/negative.d.ts +0 -6
  61. package/lib/nodes/negative.js +0 -42
  62. package/lib/nodes/negative.js.map +0 -1
  63. package/lib/nodes/operation.d.ts +0 -6
  64. package/lib/nodes/operation.js +0 -63
  65. package/lib/nodes/operation.js.map +0 -1
  66. package/lib/nodes/paren.d.ts +0 -6
  67. package/lib/nodes/paren.js +0 -42
  68. package/lib/nodes/paren.js.map +0 -1
  69. package/lib/nodes/quoted.d.ts +0 -6
  70. package/lib/nodes/quoted.js +0 -57
  71. package/lib/nodes/quoted.js.map +0 -1
  72. package/lib/nodes/reference.d.ts +0 -9
  73. package/lib/nodes/reference.js +0 -80
  74. package/lib/nodes/reference.js.map +0 -1
  75. package/lib/nodes/ruleset.d.ts +0 -6
  76. package/lib/nodes/ruleset.js +0 -108
  77. package/lib/nodes/ruleset.js.map +0 -1
  78. package/lib/nodes/selector.d.ts +0 -8
  79. package/lib/nodes/selector.js +0 -226
  80. package/lib/nodes/selector.js.map +0 -1
  81. package/lib/nodes/sequence.d.ts +0 -9
  82. package/lib/nodes/sequence.js +0 -75
  83. package/lib/nodes/sequence.js.map +0 -1
  84. package/lib/nodes/url.d.ts +0 -6
  85. package/lib/nodes/url.js +0 -42
  86. package/lib/nodes/url.js.map +0 -1
  87. package/lib/nodes/var-declaration.d.ts +0 -6
  88. package/lib/nodes/var-declaration.js +0 -60
  89. package/lib/nodes/var-declaration.js.map +0 -1
  90. package/lib/plugin-utils.d.ts +0 -20
  91. package/lib/plugin-utils.js +0 -100
  92. package/lib/plugin-utils.js.map +0 -1
  93. package/lib/plugin.js +0 -1027
  94. package/lib/plugin.js.map +0 -1
  95. package/lib/transform/from-less.d.ts +0 -30
  96. package/lib/transform/from-less.js +0 -170
  97. package/lib/transform/from-less.js.map +0 -1
  98. package/lib/transform/index.d.ts +0 -7
  99. package/lib/transform/index.js +0 -8
  100. package/lib/transform/index.js.map +0 -1
  101. package/lib/transform/proxy.d.ts +0 -32
  102. package/lib/transform/proxy.js +0 -138
  103. package/lib/transform/proxy.js.map +0 -1
  104. package/lib/transform/to-less.d.ts +0 -17
  105. package/lib/transform/to-less.js +0 -128
  106. package/lib/transform/to-less.js.map +0 -1
  107. package/lib/transform/type-map.d.ts +0 -27
  108. package/lib/transform/type-map.js +0 -105
  109. package/lib/transform/type-map.js.map +0 -1
  110. package/lib/types.d.ts +0 -33
  111. package/lib/types.js +0 -5
  112. package/lib/types.js.map +0 -1
package/README.md CHANGED
@@ -1,139 +1,74 @@
1
1
  # @jesscss/plugin-less-compat
2
2
 
3
- Less.js compatibility layer for Jess. This plugin enables Less.js plugins and visitors to work seamlessly with Jess-compiled stylesheets by providing bidirectional transformation between Jess AST nodes and Less.js AST nodes.
3
+ **A Less compatibility bridge for Jess.**
4
4
 
5
- ## ⚠️ Experimental Status
5
+ [Jess](https://github.com/jesscss/jess) accepts typed AST-v2 `Fn` values here
6
+ and provides the shared Jess-owned bridge for Less-style plugin function
7
+ registration and Less-shaped function values.
6
8
 
7
- **This package is experimental and largely LLM-generated.** It was created to explore compatibility between Jess and Less.js plugins, but:
9
+ The public compiler does **not** support Less 4 visitors, post-processors, file
10
+ manager plugins, or a full Less tree AST adapter.
8
11
 
9
- - **No long-term maintenance commitment**: This package is unlikely to be actively maintained in the future
10
- - **Use at your own risk**: While it may work for some use cases, it has not been thoroughly tested across all Less.js plugins
11
- - **Breaking changes likely**: The API and implementation may change or be removed without notice
12
- - **Not production-ready**: This is an experimental exploration, not a production-grade compatibility layer
12
+ ## How it works
13
13
 
14
- If you need Less.js plugin compatibility, consider:
15
- - Using Less.js directly for projects that require extensive Less.js plugin support
16
- - Contributing improvements if you find this package useful
17
- - Creating your own compatibility layer tailored to your specific needs
14
+ - **Native functions only** pass `Fn` values from `@jesscss/core/value`; their
15
+ bodies receive typed values and `FnCtx` capabilities.
16
+ - **Less plugin function bridge** pass Less-style plugins with
17
+ `install(less, manager, functions)` through `plugins`. The bridge supplies a
18
+ Less-shaped `functions.functionRegistry` and the supported `less.tree`
19
+ constructor facades, then lazily adapts function arguments and return values at
20
+ the Jess boundary.
18
21
 
19
- ## Features
22
+ For example, migrate a Less `functionRegistry.add('increment', fn)` contribution
23
+ to a typed function and register it through `functions`:
20
24
 
21
- - 🔄 **Bidirectional Transformation**: Convert between Jess and Less AST nodes
22
- - 🎯 **Lazy Conversion**: Proxy-based lazy conversion for optimal performance
23
- - 🔌 **Plugin Compatibility**: Use existing Less.js plugins with Jess
24
- - 🧩 **Visitor Support**: Run Less.js visitors on Jess AST trees
25
- - 📦 **Type Safe**: Full TypeScript support
25
+ ```ts
26
+ import { Compiler } from 'jess';
27
+ import { defineFunction, makeDimension } from '@jesscss/core/value';
28
+ import lessCompatPlugin from '@jesscss/plugin-less-compat';
26
29
 
27
- ## Installation
28
-
29
- ```bash
30
- pnpm add @jesscss/plugin-less-compat
31
- ```
32
-
33
- ## Usage
34
-
35
- ### Basic Usage with Less Plugins
36
-
37
- ```typescript
38
- import { Compiler } from '@jesscss/jess';
39
- import lessPlugin from '@jesscss/plugin-less';
40
- import { lessCompatPlugin } from '@jesscss/plugin-less-compat';
41
- import autoprefix from 'less-plugin-autoprefix';
30
+ const increment = defineFunction('increment', {
31
+ params: [{ kinds: ['Dimension'] }] as const,
32
+ body: value => makeDimension(value.number + 1, value.unit)
33
+ });
42
34
 
43
35
  const compiler = new Compiler({
44
- plugins: [
45
- lessPlugin(),
46
- lessCompatPlugin({
47
- visitors: [autoprefix]
48
- })
49
- ]
36
+ compile: { plugins: [lessCompatPlugin({ functions: [increment] })] }
50
37
  });
51
-
52
- const result = await compiler.compile('styles.less');
53
- ```
54
-
55
- ### Advanced: Direct Transformation
56
-
57
- ```typescript
58
- import { toLessTree, fromLessTree } from '@jesscss/plugin-less-compat/transform';
59
- import customLessVisitor from './custom-visitor';
60
-
61
- const jessTree = parseJess(source);
62
- const lessTree = toLessTree(jessTree);
63
-
64
- // Apply custom Less visitor
65
- lessTree.accept(customLessVisitor);
66
-
67
- // Convert back to Jess
68
- const modifiedJessTree = fromLessTree(lessTree);
69
38
  ```
70
39
 
71
- ### Using with Multiple Less Plugins
40
+ For legacy Less function plugins:
72
41
 
73
- ```typescript
74
- import { lessCompatPlugin } from '@jesscss/plugin-less-compat';
75
- import autoprefix from 'less-plugin-autoprefix';
76
- import cleanCSS from 'less-plugin-clean-css';
42
+ ```ts
43
+ const legacyPlugin = {
44
+ install(less, _manager, functions) {
45
+ functions.add('increment', value =>
46
+ new less.tree.Dimension(value.value + 1, value.unit)
47
+ );
48
+ }
49
+ };
77
50
 
78
51
  const compiler = new Compiler({
79
- plugins: [
80
- lessPlugin(),
81
- lessCompatPlugin({
82
- visitors: [
83
- autoprefix,
84
- cleanCSS
85
- ]
86
- })
87
- ]
52
+ compile: { plugins: [lessCompatPlugin({ plugins: [legacyPlugin] })] }
88
53
  });
89
54
  ```
90
55
 
91
- ## Supported Less Plugins
92
-
93
- This package has been tested with:
94
-
95
- - ✅ `less-plugin-autoprefix` - Automatic vendor prefixing
96
- - ✅ `less-plugin-clean-css` - CSS minification
97
- - ✅ `less-plugin-dls` - Design Language System support
98
-
99
- Other Less.js plugins should work, but may require additional testing.
100
-
101
- ## API
102
-
103
- ### `lessCompatPlugin(options?)`
104
-
105
- Creates a Jess plugin that enables Less.js compatibility.
106
-
107
- **Options:**
108
- - `visitors?: LessVisitor[]` - Array of Less.js visitors to apply
109
- - `cache?: boolean` - Enable conversion caching (default: `true`)
110
-
111
- ### `toLessTree(jessRules: Rules)`
112
-
113
- Converts a Jess `Rules` tree to a Less.js-compatible tree.
114
-
115
- ### `fromLessTree(lessTree: LessNode)`
116
-
117
- Converts a Less.js tree back to a Jess `Rules` tree.
118
-
119
- ### `toLessNode(jessNode: Node, options?)`
120
-
121
- Converts a single Jess node to a Less.js-compatible node.
122
-
123
- ### `fromLessNode(lessNode: LessNode, options?)`
124
-
125
- Converts a single Less.js node back to a Jess node.
126
-
127
- ## Implementation Status
128
-
129
- This package is currently in **alpha**.
130
-
131
- Developer notes and historical analyses live in [DESIGN.md](./DESIGN.md) and `_archive/`.
56
+ Less-shaped values are boundary values only. Jess owns their conversion back
57
+ into typed AST-v2 values.
132
58
 
133
- ## Contributing
59
+ ## Status
134
60
 
135
- Contributions welcome! Please see the main Jess repository for contribution guidelines.
61
+ **Alpha / experimental.** The supported public routes are native function
62
+ contribution and Less plugin function bridging. If you need Less visitors,
63
+ post-processors, file managers, or broad Less tree AST mutation support today,
64
+ use Less.js directly.
136
65
 
137
- ## License
66
+ The programmatic plugin/compiler API is **not yet stabilized** — the `jess` CLI
67
+ is the documented public surface for the alpha. Watch the
68
+ [docs site](https://jesscss.github.io/) for the API once it settles. Current
69
+ design notes live in [DESIGN.md](./DESIGN.md).
138
70
 
139
- MIT
71
+ - Project overview & positioning: <https://github.com/jesscss/jess#readme>
72
+ - Docs: <https://jesscss.github.io/> (currently pre-alpha content)
73
+ - Issues: <https://github.com/jesscss/jess/issues>
74
+ - License: MIT
package/lib/index.cjs ADDED
@@ -0,0 +1,342 @@
1
+ Object.defineProperties(exports, {
2
+ __esModule: { value: true },
3
+ [Symbol.toStringTag]: { value: "Module" }
4
+ });
5
+ let _jesscss_core = require("@jesscss/core");
6
+ let _jesscss_core_value = require("@jesscss/core/value");
7
+ //#region src/less-api-bridge.ts
8
+ const isThenable = (value) => value !== null && typeof value === "object" && "then" in value && typeof value.then === "function";
9
+ function isLoadedPluginModule(value) {
10
+ if (typeof value !== "object" || value === null) return false;
11
+ if (!("functions" in value) || value.functions === void 0) return true;
12
+ return typeof value.functions === "object" && value.functions !== null && Object.values(value.functions).every((fn) => typeof fn === "function");
13
+ }
14
+ const nativeNil = () => {
15
+ const nil = {
16
+ type: "Nil",
17
+ value: "",
18
+ eval: () => nil
19
+ };
20
+ return nil;
21
+ };
22
+ var LessDimension = class {
23
+ type = "Dimension";
24
+ constructor(value, unit = "") {
25
+ this.value = value;
26
+ this.unit = unit;
27
+ }
28
+ valueOf() {
29
+ return this.value;
30
+ }
31
+ };
32
+ var LessQuoted = class {
33
+ type = "Quoted";
34
+ constructor(quote, value, escaped = false) {
35
+ this.quote = quote;
36
+ this.value = value;
37
+ this.escaped = escaped;
38
+ }
39
+ valueOf() {
40
+ return this.value;
41
+ }
42
+ };
43
+ var LessColor = class {
44
+ type = "Color";
45
+ value;
46
+ constructor(rgb, alpha = 1) {
47
+ this.rgb = rgb;
48
+ this.alpha = alpha;
49
+ this.value = typeof rgb === "string" ? rgb.startsWith("#") ? rgb : `#${rgb}` : `rgb(${rgb.join(", ")})`;
50
+ }
51
+ valueOf() {
52
+ return this.value;
53
+ }
54
+ };
55
+ var LessAnonymous = class {
56
+ type = "Anonymous";
57
+ value;
58
+ constructor(value) {
59
+ this.value = String(value);
60
+ }
61
+ valueOf() {
62
+ return this.value;
63
+ }
64
+ };
65
+ var LazyExpression = class {
66
+ type = "Expression";
67
+ #value;
68
+ constructor(source) {
69
+ this.source = source;
70
+ }
71
+ get value() {
72
+ return this.#value ??= this.source.map(toNativeLessValue);
73
+ }
74
+ valueOf() {
75
+ return (0, _jesscss_core_value.emitValue)(this.source);
76
+ }
77
+ };
78
+ var LazyValueList = class {
79
+ type = "Value";
80
+ #value;
81
+ constructor(source) {
82
+ this.source = source;
83
+ }
84
+ get value() {
85
+ return this.#value ??= this.source.value.map(toNativeLessValue);
86
+ }
87
+ get separator() {
88
+ return this.source.sep;
89
+ }
90
+ valueOf() {
91
+ return this.source.bytes;
92
+ }
93
+ };
94
+ var LazyDetachedDeclaration = class {
95
+ type = "Declaration";
96
+ #value;
97
+ #hasValue = false;
98
+ constructor(name, source) {
99
+ this.name = name;
100
+ this.source = source;
101
+ }
102
+ get value() {
103
+ if (!this.#hasValue) {
104
+ this.#value = toNativeLessValue(this.source);
105
+ this.#hasValue = true;
106
+ }
107
+ return this.#value;
108
+ }
109
+ eval() {
110
+ return this;
111
+ }
112
+ };
113
+ var LazyDetachedRuleset = class {
114
+ type = "Mixin";
115
+ name = nativeNil();
116
+ args = nativeNil();
117
+ #rules;
118
+ constructor(source) {
119
+ this.source = source;
120
+ }
121
+ get ruleset() {
122
+ return { rules: this.rules };
123
+ }
124
+ get rules() {
125
+ return this.#rules ??= this.source.rules.map((rule) => new LazyDetachedDeclaration(rule.name, rule.value));
126
+ }
127
+ eval() {
128
+ return this;
129
+ }
130
+ };
131
+ function isRawSequence(value) {
132
+ return Array.isArray(value);
133
+ }
134
+ function isPluginDetached(value) {
135
+ return !isRawSequence(value) && value.type === "DetachedRuleset";
136
+ }
137
+ function toNativeLessValue(value) {
138
+ if (isRawSequence(value)) return new LazyExpression(value);
139
+ if (isPluginDetached(value)) return new LazyDetachedRuleset(value);
140
+ switch (value.type) {
141
+ case "Dimension": return new LessDimension(value.number, value.unit);
142
+ case "Quoted": return new LessQuoted(value.quote, value.value, value.escaped);
143
+ case "Color": return {
144
+ type: "Color",
145
+ rgb: value.rgb,
146
+ alpha: value.alpha,
147
+ valueOf: () => value.bytes
148
+ };
149
+ case "List": return value.sep === "," || value.sep === "/" ? new LazyValueList(value) : new LessAnonymous(value.bytes);
150
+ default: return new LessAnonymous(value.bytes);
151
+ }
152
+ }
153
+ function isNativeValue(value) {
154
+ return value !== null && typeof value === "object" && "bytes" in value && typeof value.bytes === "string";
155
+ }
156
+ function fromNativeLessValue(value) {
157
+ if (isNativeValue(value)) return value;
158
+ if (typeof value === "number") return (0, _jesscss_core_value.makeDimension)(value);
159
+ if (typeof value === "string") return (0, _jesscss_core_value.sniffLiteral)(value);
160
+ if (value && typeof value === "object") {
161
+ const candidate = value;
162
+ if ((candidate.type === "Dimension" || candidate.type === "Num") && typeof candidate.value === "number") return (0, _jesscss_core_value.makeDimension)(candidate.value, typeof candidate.unit === "string" ? candidate.unit : "");
163
+ if (candidate.type === "Quoted" && typeof candidate.value === "string") return (0, _jesscss_core_value.makeQuoted)(candidate.value, candidate.quote === "'" ? "'" : "\"", candidate.escaped === true);
164
+ if (candidate.type === "Color") {
165
+ if (Array.isArray(candidate.rgb)) {
166
+ const [r = 0, g = 0, b = 0] = candidate.rgb.map(Number);
167
+ const alpha = typeof candidate.alpha === "number" ? candidate.alpha : 1;
168
+ return (0, _jesscss_core_value.makeColorRgb)([
169
+ r,
170
+ g,
171
+ b
172
+ ], alpha, _jesscss_core_value.HEX);
173
+ }
174
+ if (typeof candidate.rgb === "string") return (0, _jesscss_core_value.sniffLiteral)(candidate.rgb.startsWith("#") ? candidate.rgb : `#${candidate.rgb}`);
175
+ }
176
+ if (candidate.type === "Expression" && Array.isArray(candidate.value)) return candidate.value.map(fromNativeLessValue);
177
+ if (candidate.type === "Value" && Array.isArray(candidate.value)) {
178
+ const separator = candidate.separator === "/" ? "/" : ",";
179
+ return (0, _jesscss_core_value.makeList)(candidate.value.map((item) => fromNativeLessValue(item)), separator);
180
+ }
181
+ if (typeof candidate.value === "string") return (0, _jesscss_core_value.sniffLiteral)(candidate.value);
182
+ if (typeof candidate.valueOf === "function") return (0, _jesscss_core_value.sniffLiteral)(String(candidate.valueOf()));
183
+ }
184
+ return (0, _jesscss_core_value.makeKeyword)(value == null ? "" : String(value));
185
+ }
186
+ var LessApiBridge = class {
187
+ less;
188
+ registry;
189
+ globalFns;
190
+ #fns = [];
191
+ #nativeFns = /* @__PURE__ */ new WeakMap();
192
+ #contextualFns = /* @__PURE__ */ new WeakMap();
193
+ #registered = /* @__PURE__ */ new Map();
194
+ constructor(plugins = []) {
195
+ this.registry = {
196
+ add: (name, fn) => {
197
+ this.addFunction(name, fn);
198
+ },
199
+ addMultiple: (functions) => {
200
+ for (const [name, fn] of Object.entries(functions)) this.registry.add(name, fn);
201
+ },
202
+ get: (name) => this.#registered.get(name.toLowerCase())
203
+ };
204
+ this.less = {
205
+ functions: { functionRegistry: this.registry },
206
+ tree: {
207
+ Dimension: LessDimension,
208
+ Quoted: LessQuoted,
209
+ Color: LessColor,
210
+ Anonymous: LessAnonymous
211
+ }
212
+ };
213
+ for (const plugin of plugins) plugin.install?.(this.less, void 0, this.registry);
214
+ this.globalFns = this.#fns;
215
+ }
216
+ addFunction(name, fn) {
217
+ const lowerName = name.toLowerCase();
218
+ this.#registered.set(lowerName, fn);
219
+ const adapted = (0, _jesscss_core_value.defineFunction)(lowerName, {
220
+ variadic: true,
221
+ params: [],
222
+ body: (value) => this.invokeNativeFunction(fn, (0, _jesscss_core_value.groupItems)(value))
223
+ });
224
+ this.#nativeFns.set(adapted, fn);
225
+ this.#fns.push(adapted);
226
+ return adapted;
227
+ }
228
+ addContextualFunction(name, fn) {
229
+ const adapted = (0, _jesscss_core_value.defineFunction)(name.toLowerCase(), {
230
+ variadic: true,
231
+ params: [],
232
+ body: () => {
233
+ throw new Error(`Less @plugin function "${name}" needs live plugin invocation capabilities; it cannot run through plain function dispatch.`);
234
+ }
235
+ });
236
+ this.#contextualFns.set(adapted, fn);
237
+ this.#fns.push(adapted);
238
+ return adapted;
239
+ }
240
+ invokeNativeFunction(fn, args) {
241
+ const result = fn(...args.map(toNativeLessValue));
242
+ return isThenable(result) ? Promise.resolve(result).then(fromNativeLessValue) : fromNativeLessValue(result);
243
+ }
244
+ invokeContextualFunction(fn, args, ctx) {
245
+ const result = fn(args.map(toNativeLessValue), {
246
+ lookupVariable: (name) => {
247
+ const hit = ctx.lookupVariable(name);
248
+ return hit === null ? null : {
249
+ value: toNativeLessValue(hit.value),
250
+ important: hit.important
251
+ };
252
+ },
253
+ callFunction: (name, callArgs) => {
254
+ const answer = ctx.callFunction(name, callArgs.map(fromNativeLessValue));
255
+ return answer === void 0 ? void 0 : toNativeLessValue(answer);
256
+ },
257
+ currentFileInfo: ctx.currentFileInfo,
258
+ log: (record) => ctx.log(record),
259
+ markImportant: () => ctx.markImportant()
260
+ });
261
+ return isThenable(result) ? Promise.resolve(result).then(fromNativeLessValue) : fromNativeLessValue(result);
262
+ }
263
+ invokeRawFunction(fn, args, ctx) {
264
+ const contextual = this.#contextualFns.get(fn);
265
+ if (contextual) return this.invokeContextualFunction(contextual, args, ctx);
266
+ const native = this.#nativeFns.get(fn);
267
+ return native ? this.invokeNativeFunction(native, args) : void 0;
268
+ }
269
+ createPluginHost(optionsOrLoader) {
270
+ const loadPluginModule = typeof optionsOrLoader === "function" ? optionsOrLoader : optionsOrLoader?.loadPluginModule;
271
+ const host = {
272
+ ...this.globalFns.length === 0 ? {} : { globalFns: this.globalFns },
273
+ invokeRawFunction: (fn, args, ctx) => this.invokeRawFunction(fn, args, ctx)
274
+ };
275
+ if (loadPluginModule) host.loadPlugin = (request) => {
276
+ const loaded = loadPluginModule(request);
277
+ const addLoaded = (module) => {
278
+ const functions = isLoadedPluginModule(module) ? module.functions : void 0;
279
+ if (!functions) return [];
280
+ return Object.entries(functions).map(([name, fn]) => this.addContextualFunction(name, fn));
281
+ };
282
+ return isThenable(loaded) ? Promise.resolve(loaded).then(addLoaded) : addLoaded(loaded);
283
+ };
284
+ return host;
285
+ }
286
+ };
287
+ //#endregion
288
+ //#region src/plugin.ts
289
+ /**
290
+ * Transitional package boundary for applications that previously installed the
291
+ * Less compatibility plugin. It contributes only native AST-v2 `Fn` values.
292
+ *
293
+ * Less 4 visitor hooks, `functionRegistry` callbacks, and `@plugin` script
294
+ * execution were legacy tree-runtime APIs. They are intentionally unsupported
295
+ * on the public AST-v2 compiler route: preserving them would require a second
296
+ * evaluator and Node-to-Less/ Less-to-Node conversion bridge.
297
+ */
298
+ var LessCompatPlugin = class extends _jesscss_core.AbstractPlugin {
299
+ name = "less-compat";
300
+ bridges = /* @__PURE__ */ new WeakMap();
301
+ constructor(opts = {}) {
302
+ super();
303
+ this.opts = opts;
304
+ }
305
+ setContext(context) {
306
+ let bridge = this.bridges.get(context);
307
+ if (!bridge) {
308
+ bridge = new LessApiBridge(this.opts.plugins ?? []);
309
+ this.bridges.set(context, bridge);
310
+ }
311
+ const bridgeHost = bridge.createPluginHost(({ specifier, options }) => context.getPluginModule(specifier, options));
312
+ const functions = this.opts.functions;
313
+ const host = context.pluginHost;
314
+ const globalFns = [
315
+ ...host?.globalFns ?? [],
316
+ ...bridgeHost.globalFns ?? [],
317
+ ...functions ?? []
318
+ ];
319
+ context.pluginHost = {
320
+ ...host,
321
+ ...globalFns.length === 0 ? {} : { globalFns },
322
+ loadPlugin: bridgeHost.loadPlugin || host?.loadPlugin ? (request) => {
323
+ const baseLoaded = host?.loadPlugin?.(request);
324
+ const bridgeLoaded = bridgeHost.loadPlugin?.(request);
325
+ const merge = (baseFns, bridgeFns) => [...baseFns ?? [], ...bridgeFns ?? []];
326
+ const baseThenable = baseLoaded && typeof baseLoaded === "object" && "then" in baseLoaded;
327
+ const bridgeThenable = bridgeLoaded && typeof bridgeLoaded === "object" && "then" in bridgeLoaded;
328
+ if (baseThenable || bridgeThenable) return Promise.all([baseLoaded ?? [], bridgeLoaded ?? []]).then(([baseFns, bridgeFns]) => merge(baseFns, bridgeFns));
329
+ return merge(baseLoaded, bridgeLoaded);
330
+ } : void 0,
331
+ invokeRawFunction: (fn, args, ctx) => bridgeHost.invokeRawFunction?.(fn, args, ctx) ?? host?.invokeRawFunction?.(fn, args, ctx)
332
+ };
333
+ }
334
+ };
335
+ const lessCompatPlugin = (opts) => new LessCompatPlugin(opts);
336
+ //#endregion
337
+ exports.LessApiBridge = LessApiBridge;
338
+ exports.LessCompatPlugin = LessCompatPlugin;
339
+ exports.default = lessCompatPlugin;
340
+ exports.fromNativeLessValue = fromNativeLessValue;
341
+ exports.lessCompatPlugin = lessCompatPlugin;
342
+ exports.toNativeLessValue = toNativeLessValue;
package/lib/index.d.ts CHANGED
@@ -5,5 +5,5 @@
5
5
  * Enables Less.js plugins and visitors to work with Jess AST.
6
6
  */
7
7
  export { LessCompatPlugin, default as lessCompatPlugin, type LessCompatPluginOptions } from './plugin.js';
8
- export * from './transform/index.js';
9
- export { isLessPlugin, isJessPlugin, filterPlugins } from './plugin-utils.js';
8
+ export { LessApiBridge, fromNativeLessValue, toNativeLessValue, type ContextualPluginFunction, type NativeLessApi, type NativeLessFunction, type NativeLessFunctionRegistry, type NativeLessPlugin } from './less-api-bridge.js';
9
+ export { default } from './plugin.js';