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

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/lib/index.js CHANGED
@@ -1,10 +1,333 @@
1
+ import { AbstractPlugin } from "@jesscss/core";
2
+ import { HEX, defineFunction, emitValue, groupItems, makeColorRgb, makeDimension, makeKeyword, makeList, makeQuoted, sniffLiteral } from "@jesscss/core/value";
3
+ //#region src/less-api-bridge.ts
4
+ const isThenable = (value) => value !== null && typeof value === "object" && "then" in value && typeof value.then === "function";
5
+ function isLoadedPluginModule(value) {
6
+ if (typeof value !== "object" || value === null) return false;
7
+ if (!("functions" in value) || value.functions === void 0) return true;
8
+ return typeof value.functions === "object" && value.functions !== null && Object.values(value.functions).every((fn) => typeof fn === "function");
9
+ }
10
+ const nativeNil = () => {
11
+ const nil = {
12
+ type: "Nil",
13
+ value: "",
14
+ eval: () => nil
15
+ };
16
+ return nil;
17
+ };
18
+ var LessDimension = class {
19
+ type = "Dimension";
20
+ constructor(value, unit = "") {
21
+ this.value = value;
22
+ this.unit = unit;
23
+ }
24
+ valueOf() {
25
+ return this.value;
26
+ }
27
+ };
28
+ var LessQuoted = class {
29
+ type = "Quoted";
30
+ constructor(quote, value, escaped = false) {
31
+ this.quote = quote;
32
+ this.value = value;
33
+ this.escaped = escaped;
34
+ }
35
+ valueOf() {
36
+ return this.value;
37
+ }
38
+ };
39
+ var LessColor = class {
40
+ type = "Color";
41
+ value;
42
+ constructor(rgb, alpha = 1) {
43
+ this.rgb = rgb;
44
+ this.alpha = alpha;
45
+ this.value = typeof rgb === "string" ? rgb.startsWith("#") ? rgb : `#${rgb}` : `rgb(${rgb.join(", ")})`;
46
+ }
47
+ valueOf() {
48
+ return this.value;
49
+ }
50
+ };
51
+ var LessAnonymous = class {
52
+ type = "Anonymous";
53
+ value;
54
+ constructor(value) {
55
+ this.value = String(value);
56
+ }
57
+ valueOf() {
58
+ return this.value;
59
+ }
60
+ };
61
+ var LazyExpression = class {
62
+ type = "Expression";
63
+ #value;
64
+ constructor(source) {
65
+ this.source = source;
66
+ }
67
+ get value() {
68
+ return this.#value ??= this.source.map(toNativeLessValue);
69
+ }
70
+ valueOf() {
71
+ return emitValue(this.source);
72
+ }
73
+ };
74
+ var LazyValueList = class {
75
+ type = "Value";
76
+ #value;
77
+ constructor(source) {
78
+ this.source = source;
79
+ }
80
+ get value() {
81
+ return this.#value ??= this.source.value.map(toNativeLessValue);
82
+ }
83
+ get separator() {
84
+ return this.source.sep;
85
+ }
86
+ valueOf() {
87
+ return this.source.bytes;
88
+ }
89
+ };
90
+ var LazyDetachedDeclaration = class {
91
+ type = "Declaration";
92
+ #value;
93
+ #hasValue = false;
94
+ constructor(name, source) {
95
+ this.name = name;
96
+ this.source = source;
97
+ }
98
+ get value() {
99
+ if (!this.#hasValue) {
100
+ this.#value = toNativeLessValue(this.source);
101
+ this.#hasValue = true;
102
+ }
103
+ return this.#value;
104
+ }
105
+ eval() {
106
+ return this;
107
+ }
108
+ };
109
+ var LazyDetachedRuleset = class {
110
+ type = "Mixin";
111
+ name = nativeNil();
112
+ args = nativeNil();
113
+ #rules;
114
+ constructor(source) {
115
+ this.source = source;
116
+ }
117
+ get ruleset() {
118
+ return { rules: this.rules };
119
+ }
120
+ get rules() {
121
+ return this.#rules ??= this.source.rules.map((rule) => new LazyDetachedDeclaration(rule.name, rule.value));
122
+ }
123
+ eval() {
124
+ return this;
125
+ }
126
+ };
127
+ function isRawSequence(value) {
128
+ return Array.isArray(value);
129
+ }
130
+ function isPluginDetached(value) {
131
+ return !isRawSequence(value) && value.type === "DetachedRuleset";
132
+ }
133
+ function toNativeLessValue(value) {
134
+ if (isRawSequence(value)) return new LazyExpression(value);
135
+ if (isPluginDetached(value)) return new LazyDetachedRuleset(value);
136
+ switch (value.type) {
137
+ case "Dimension": return new LessDimension(value.number, value.unit);
138
+ case "Quoted": return new LessQuoted(value.quote, value.value, value.escaped);
139
+ case "Color": return {
140
+ type: "Color",
141
+ rgb: value.rgb,
142
+ alpha: value.alpha,
143
+ valueOf: () => value.bytes
144
+ };
145
+ case "List": return value.sep === "," || value.sep === "/" ? new LazyValueList(value) : new LessAnonymous(value.bytes);
146
+ default: return new LessAnonymous(value.bytes);
147
+ }
148
+ }
149
+ function isNativeValue(value) {
150
+ return value !== null && typeof value === "object" && "bytes" in value && typeof value.bytes === "string";
151
+ }
152
+ function fromNativeLessValue(value) {
153
+ if (isNativeValue(value)) return value;
154
+ if (typeof value === "number") return makeDimension(value);
155
+ if (typeof value === "string") return sniffLiteral(value);
156
+ if (value && typeof value === "object") {
157
+ const candidate = value;
158
+ if ((candidate.type === "Dimension" || candidate.type === "Num") && typeof candidate.value === "number") return makeDimension(candidate.value, typeof candidate.unit === "string" ? candidate.unit : "");
159
+ if (candidate.type === "Quoted" && typeof candidate.value === "string") return makeQuoted(candidate.value, candidate.quote === "'" ? "'" : "\"", candidate.escaped === true);
160
+ if (candidate.type === "Color") {
161
+ if (Array.isArray(candidate.rgb)) {
162
+ const [r = 0, g = 0, b = 0] = candidate.rgb.map(Number);
163
+ const alpha = typeof candidate.alpha === "number" ? candidate.alpha : 1;
164
+ return makeColorRgb([
165
+ r,
166
+ g,
167
+ b
168
+ ], alpha, HEX);
169
+ }
170
+ if (typeof candidate.rgb === "string") return sniffLiteral(candidate.rgb.startsWith("#") ? candidate.rgb : `#${candidate.rgb}`);
171
+ }
172
+ if (candidate.type === "Expression" && Array.isArray(candidate.value)) return candidate.value.map(fromNativeLessValue);
173
+ if (candidate.type === "Value" && Array.isArray(candidate.value)) {
174
+ const separator = candidate.separator === "/" ? "/" : ",";
175
+ return makeList(candidate.value.map((item) => fromNativeLessValue(item)), separator);
176
+ }
177
+ if (typeof candidate.value === "string") return sniffLiteral(candidate.value);
178
+ if (typeof candidate.valueOf === "function") return sniffLiteral(String(candidate.valueOf()));
179
+ }
180
+ return makeKeyword(value == null ? "" : String(value));
181
+ }
182
+ var LessApiBridge = class {
183
+ less;
184
+ registry;
185
+ globalFns;
186
+ #fns = [];
187
+ #nativeFns = /* @__PURE__ */ new WeakMap();
188
+ #contextualFns = /* @__PURE__ */ new WeakMap();
189
+ #registered = /* @__PURE__ */ new Map();
190
+ constructor(plugins = []) {
191
+ this.registry = {
192
+ add: (name, fn) => {
193
+ this.addFunction(name, fn);
194
+ },
195
+ addMultiple: (functions) => {
196
+ for (const [name, fn] of Object.entries(functions)) this.registry.add(name, fn);
197
+ },
198
+ get: (name) => this.#registered.get(name.toLowerCase())
199
+ };
200
+ this.less = {
201
+ functions: { functionRegistry: this.registry },
202
+ tree: {
203
+ Dimension: LessDimension,
204
+ Quoted: LessQuoted,
205
+ Color: LessColor,
206
+ Anonymous: LessAnonymous
207
+ }
208
+ };
209
+ for (const plugin of plugins) plugin.install?.(this.less, void 0, this.registry);
210
+ this.globalFns = this.#fns;
211
+ }
212
+ addFunction(name, fn) {
213
+ const lowerName = name.toLowerCase();
214
+ this.#registered.set(lowerName, fn);
215
+ const adapted = defineFunction(lowerName, {
216
+ variadic: true,
217
+ params: [],
218
+ body: (value) => this.invokeNativeFunction(fn, groupItems(value))
219
+ });
220
+ this.#nativeFns.set(adapted, fn);
221
+ this.#fns.push(adapted);
222
+ return adapted;
223
+ }
224
+ addContextualFunction(name, fn) {
225
+ const adapted = defineFunction(name.toLowerCase(), {
226
+ variadic: true,
227
+ params: [],
228
+ body: () => {
229
+ throw new Error(`Less @plugin function "${name}" needs live plugin invocation capabilities; it cannot run through plain function dispatch.`);
230
+ }
231
+ });
232
+ this.#contextualFns.set(adapted, fn);
233
+ this.#fns.push(adapted);
234
+ return adapted;
235
+ }
236
+ invokeNativeFunction(fn, args) {
237
+ const result = fn(...args.map(toNativeLessValue));
238
+ return isThenable(result) ? Promise.resolve(result).then(fromNativeLessValue) : fromNativeLessValue(result);
239
+ }
240
+ invokeContextualFunction(fn, args, ctx) {
241
+ const result = fn(args.map(toNativeLessValue), {
242
+ lookupVariable: (name) => {
243
+ const hit = ctx.lookupVariable(name);
244
+ return hit === null ? null : {
245
+ value: toNativeLessValue(hit.value),
246
+ important: hit.important
247
+ };
248
+ },
249
+ callFunction: (name, callArgs) => {
250
+ const answer = ctx.callFunction(name, callArgs.map(fromNativeLessValue));
251
+ return answer === void 0 ? void 0 : toNativeLessValue(answer);
252
+ },
253
+ currentFileInfo: ctx.currentFileInfo,
254
+ log: (record) => ctx.log(record),
255
+ markImportant: () => ctx.markImportant()
256
+ });
257
+ return isThenable(result) ? Promise.resolve(result).then(fromNativeLessValue) : fromNativeLessValue(result);
258
+ }
259
+ invokeRawFunction(fn, args, ctx) {
260
+ const contextual = this.#contextualFns.get(fn);
261
+ if (contextual) return this.invokeContextualFunction(contextual, args, ctx);
262
+ const native = this.#nativeFns.get(fn);
263
+ return native ? this.invokeNativeFunction(native, args) : void 0;
264
+ }
265
+ createPluginHost(optionsOrLoader) {
266
+ const loadPluginModule = typeof optionsOrLoader === "function" ? optionsOrLoader : optionsOrLoader?.loadPluginModule;
267
+ const host = {
268
+ ...this.globalFns.length === 0 ? {} : { globalFns: this.globalFns },
269
+ invokeRawFunction: (fn, args, ctx) => this.invokeRawFunction(fn, args, ctx)
270
+ };
271
+ if (loadPluginModule) host.loadPlugin = (request) => {
272
+ const loaded = loadPluginModule(request);
273
+ const addLoaded = (module) => {
274
+ const functions = isLoadedPluginModule(module) ? module.functions : void 0;
275
+ if (!functions) return [];
276
+ return Object.entries(functions).map(([name, fn]) => this.addContextualFunction(name, fn));
277
+ };
278
+ return isThenable(loaded) ? Promise.resolve(loaded).then(addLoaded) : addLoaded(loaded);
279
+ };
280
+ return host;
281
+ }
282
+ };
283
+ //#endregion
284
+ //#region src/plugin.ts
1
285
  /**
2
- * @jesscss/plugin-less-compat
3
- *
4
- * Less.js compatibility layer for Jess.
5
- * Enables Less.js plugins and visitors to work with Jess AST.
6
- */
7
- export { LessCompatPlugin, default as lessCompatPlugin } from './plugin.js';
8
- export * from './transform/index.js';
9
- export { isLessPlugin, isJessPlugin, filterPlugins } from './plugin-utils.js';
10
- //# sourceMappingURL=index.js.map
286
+ * Transitional package boundary for applications that previously installed the
287
+ * Less compatibility plugin. It contributes only native AST-v2 `Fn` values.
288
+ *
289
+ * Less 4 visitor hooks, `functionRegistry` callbacks, and `@plugin` script
290
+ * execution were legacy tree-runtime APIs. They are intentionally unsupported
291
+ * on the public AST-v2 compiler route: preserving them would require a second
292
+ * evaluator and Node-to-Less/ Less-to-Node conversion bridge.
293
+ */
294
+ var LessCompatPlugin = class extends AbstractPlugin {
295
+ name = "less-compat";
296
+ bridges = /* @__PURE__ */ new WeakMap();
297
+ constructor(opts = {}) {
298
+ super();
299
+ this.opts = opts;
300
+ }
301
+ setContext(context) {
302
+ let bridge = this.bridges.get(context);
303
+ if (!bridge) {
304
+ bridge = new LessApiBridge(this.opts.plugins ?? []);
305
+ this.bridges.set(context, bridge);
306
+ }
307
+ const bridgeHost = bridge.createPluginHost(({ specifier, options }) => context.getPluginModule(specifier, options));
308
+ const functions = this.opts.functions;
309
+ const host = context.pluginHost;
310
+ const globalFns = [
311
+ ...host?.globalFns ?? [],
312
+ ...bridgeHost.globalFns ?? [],
313
+ ...functions ?? []
314
+ ];
315
+ context.pluginHost = {
316
+ ...host,
317
+ ...globalFns.length === 0 ? {} : { globalFns },
318
+ loadPlugin: bridgeHost.loadPlugin || host?.loadPlugin ? (request) => {
319
+ const baseLoaded = host?.loadPlugin?.(request);
320
+ const bridgeLoaded = bridgeHost.loadPlugin?.(request);
321
+ const merge = (baseFns, bridgeFns) => [...baseFns ?? [], ...bridgeFns ?? []];
322
+ const baseThenable = baseLoaded && typeof baseLoaded === "object" && "then" in baseLoaded;
323
+ const bridgeThenable = bridgeLoaded && typeof bridgeLoaded === "object" && "then" in bridgeLoaded;
324
+ if (baseThenable || bridgeThenable) return Promise.all([baseLoaded ?? [], bridgeLoaded ?? []]).then(([baseFns, bridgeFns]) => merge(baseFns, bridgeFns));
325
+ return merge(baseLoaded, bridgeLoaded);
326
+ } : void 0,
327
+ invokeRawFunction: (fn, args, ctx) => bridgeHost.invokeRawFunction?.(fn, args, ctx) ?? host?.invokeRawFunction?.(fn, args, ctx)
328
+ };
329
+ }
330
+ };
331
+ const lessCompatPlugin = (opts) => new LessCompatPlugin(opts);
332
+ //#endregion
333
+ export { LessApiBridge, LessCompatPlugin, lessCompatPlugin as default, lessCompatPlugin, fromNativeLessValue, toNativeLessValue };
@@ -0,0 +1,85 @@
1
+ import type { MaybePromise } from '@jesscss/awaitable-pipe';
2
+ import { type Fn, type PluginCallCtx, type PluginHost, type PluginRawArgument, type ValueGroup } from '@jesscss/core/value';
3
+ export type NativeLessFunction = (...args: unknown[]) => unknown;
4
+ export type ContextualPluginFunction = (args: readonly unknown[], capabilities: {
5
+ lookupVariable?(name: string): {
6
+ value: unknown;
7
+ important?: boolean;
8
+ } | null;
9
+ callFunction?(name: string, args: unknown[]): unknown;
10
+ currentFileInfo?: {
11
+ filename: string;
12
+ entryPath: string;
13
+ };
14
+ log?(record: {
15
+ level: string;
16
+ message: string;
17
+ }): void;
18
+ markImportant?(): void;
19
+ }) => unknown | Promise<unknown>;
20
+ export type NativeLessPlugin = {
21
+ readonly name?: string;
22
+ readonly opts?: unknown;
23
+ install?(less: NativeLessApi, manager: undefined, functions: NativeLessFunctionRegistry): void;
24
+ };
25
+ export interface NativeLessFunctionRegistry {
26
+ add(name: string, fn: NativeLessFunction): void;
27
+ addMultiple(functions: Record<string, NativeLessFunction>): void;
28
+ get(name: string): NativeLessFunction | undefined;
29
+ }
30
+ export interface NativeLessApi {
31
+ functions: {
32
+ functionRegistry: NativeLessFunctionRegistry;
33
+ };
34
+ tree: {
35
+ Dimension: new (value: number, unit?: string) => NativeLessDimension;
36
+ Quoted: new (quote: string, value: string, escaped?: boolean) => NativeLessQuoted;
37
+ Color: new (rgb: string | readonly [number, number, number], alpha?: number) => NativeLessColor;
38
+ Anonymous: new (value: unknown) => NativeLessAnonymous;
39
+ };
40
+ }
41
+ export interface NativeLessDimension {
42
+ readonly type: 'Dimension';
43
+ readonly value: number;
44
+ readonly unit: string;
45
+ valueOf(): number;
46
+ }
47
+ export interface NativeLessQuoted {
48
+ readonly type: 'Quoted';
49
+ readonly quote: string;
50
+ readonly value: string;
51
+ readonly escaped: boolean;
52
+ valueOf(): string;
53
+ }
54
+ export interface NativeLessColor {
55
+ readonly type: 'Color';
56
+ readonly rgb: string | readonly [number, number, number];
57
+ readonly alpha: number;
58
+ readonly value: string;
59
+ valueOf(): string;
60
+ }
61
+ export interface NativeLessAnonymous {
62
+ readonly type: 'Anonymous';
63
+ readonly value: string;
64
+ valueOf(): string;
65
+ }
66
+ type PluginModuleLoader = (request: Parameters<NonNullable<PluginHost['loadPlugin']>>[0]) => MaybePromise<unknown>;
67
+ type PluginHostOptions = {
68
+ readonly loadPluginModule?: PluginModuleLoader;
69
+ };
70
+ export declare function toNativeLessValue(value: PluginRawArgument | ValueGroup): unknown;
71
+ export declare function fromNativeLessValue(value: unknown): ValueGroup;
72
+ export declare class LessApiBridge {
73
+ #private;
74
+ readonly less: NativeLessApi;
75
+ readonly registry: NativeLessFunctionRegistry;
76
+ readonly globalFns: readonly Fn[];
77
+ constructor(plugins?: readonly NativeLessPlugin[]);
78
+ addFunction(name: string, fn: NativeLessFunction): Fn;
79
+ addContextualFunction(name: string, fn: ContextualPluginFunction): Fn;
80
+ invokeNativeFunction(fn: NativeLessFunction, args: readonly PluginRawArgument[]): ValueGroup | Promise<ValueGroup>;
81
+ invokeContextualFunction(fn: ContextualPluginFunction, args: readonly PluginRawArgument[], ctx: PluginCallCtx): ValueGroup | Promise<ValueGroup>;
82
+ invokeRawFunction(fn: Fn, args: readonly PluginRawArgument[], ctx: PluginCallCtx): MaybePromise<ValueGroup | undefined>;
83
+ createPluginHost(optionsOrLoader?: PluginHostOptions | PluginModuleLoader): PluginHost;
84
+ }
85
+ export {};
package/lib/plugin.d.ts CHANGED
@@ -1,92 +1,31 @@
1
- import { AbstractPlugin, type Plugin, type Visitor } from '@jesscss/core';
2
- import type { LessVisitor } from './types.js';
3
- import { NodeModulesPlugin } from '@jesscss/plugin-node-modules';
1
+ import { AbstractPlugin, type Context } from '@jesscss/core';
2
+ import type { Fn } from '@jesscss/core/value';
3
+ import { type NativeLessPlugin } from './less-api-bridge.js';
4
+ /**
5
+ * Native AST-v2 functions contributed by an application. Function bodies receive
6
+ * typed value objects and the narrow `FnCtx` capability supplied by core; they
7
+ * never receive Less tree nodes or a legacy evaluation context.
8
+ */
4
9
  export interface LessCompatPluginOptions {
5
- /**
6
- * Less.js plugins - these will have their install() method called to extract visitors.
7
- * Can be mixed with Jess plugins - Less plugins will be handled by this compat layer,
8
- * Jess plugins will be passed through.
9
- */
10
- plugins?: any[];
11
- /**
12
- * @deprecated Less.js Visitor API - Less.js visitor implementations (advanced - use plugins instead).
13
- * This is for compatibility with Less.js visitor patterns. Prefer using Jess's native Visitor interface.
14
- */
15
- visitors?: LessVisitor[];
16
- /** Enable conversion caching (default: true) */
17
- cache?: boolean;
18
- /**
19
- * Plugin registry for @plugin directive support.
20
- * Maps plugin names/paths to plugin instances or factory functions.
21
- * Used when processing @plugin directives in the source.
22
- */
23
- pluginRegistry?: Record<string, any>;
24
- /**
25
- * Enable auto-loading of plugins by name (Less.js 4.x CLI behavior).
26
- * When true (default), @plugin directives will attempt to require/import
27
- * plugins by their npm package name (e.g., "less-plugin-autoprefix").
28
- * Set to false to disable and only use pluginRegistry.
29
- */
30
- autoLoadPlugins?: boolean;
31
- /**
32
- * Reference to the node-modules plugin for npm package resolution.
33
- * If provided, will be used to resolve npm packages.
34
- * If not provided, will try to use require() directly.
35
- */
36
- nodeModulesPlugin?: NodeModulesPlugin;
10
+ readonly functions?: readonly Fn[];
11
+ readonly plugins?: readonly NativeLessPlugin[];
37
12
  }
38
13
  /**
39
- * Plugin that enables Less.js compatibility by transforming
40
- * Jess nodes to Less-compatible format for visitor processing.
14
+ * Transitional package boundary for applications that previously installed the
15
+ * Less compatibility plugin. It contributes only native AST-v2 `Fn` values.
16
+ *
17
+ * Less 4 visitor hooks, `functionRegistry` callbacks, and `@plugin` script
18
+ * execution were legacy tree-runtime APIs. They are intentionally unsupported
19
+ * on the public AST-v2 compiler route: preserving them would require a second
20
+ * evaluator and Node-to-Less/ Less-to-Node conversion bridge.
41
21
  */
42
22
  export declare class LessCompatPlugin extends AbstractPlugin {
43
- opts: LessCompatPluginOptions;
23
+ readonly opts: LessCompatPluginOptions;
44
24
  name: string;
45
- private _cachedVisitor;
46
- private _lessPluginManager?;
47
- private _currentFilePath?;
48
- private _jessFunctionRegistry?;
25
+ private readonly bridges;
49
26
  constructor(opts?: LessCompatPluginOptions);
50
- /**
51
- * Return the visitor as a preEval visitor so it runs before evaluation.
52
- * This ensures @plugin directives are processed early, allowing their visitors
53
- * to run on subsequent nodes during the preEval phase.
54
- *
55
- * Less plugins can register visitors via:
56
- * - addVisitor() - these will run during preEval (default)
57
- * - addPreProcessor() - these will run during preEval
58
- * - addPostProcessor() - these will run during postEval (after evaluation)
59
- */
60
- get preEvalVisitor(): Visitor | Visitor[] | undefined;
61
- /**
62
- * Return postEval visitors from Less plugins that registered via addPostProcessor.
63
- * These visitors will run after node.eval() completes.
64
- */
65
- get postEvalVisitor(): undefined;
66
- runPostProcessors(css: string, extra?: Record<string, any>): string;
67
- setCurrentFilePath(filePath: string): void;
68
- setContext(context: any): void;
69
- /**
70
- * Filter and separate Less plugins from Jess plugins
71
- * This allows mixed plugin arrays to be handled correctly
72
- *
73
- * @deprecated Use filterPlugins from './plugin-utils.js' instead
74
- */
75
- static filterLessPlugins(plugins: any[]): {
76
- lessPlugins: any[];
77
- jessPlugins: import("@jesscss/core").PluginInterface[];
78
- };
79
- /**
80
- * Return a visitor that wraps Less visitors
81
- *
82
- * This visitor intercepts each node, converts it to Less format,
83
- * runs the Less visitors, and converts back if modified.
84
- */
85
- get visitor(): Visitor | Visitor[] | undefined;
27
+ setContext(context: Context): void;
86
28
  }
87
- /**
88
- * Create a Less.js compatibility plugin
89
- */
90
- declare const lessCompatPlugin: Plugin;
29
+ declare const lessCompatPlugin: (opts?: LessCompatPluginOptions) => LessCompatPlugin;
91
30
  export default lessCompatPlugin;
92
31
  export { lessCompatPlugin };
package/package.json CHANGED
@@ -3,26 +3,19 @@
3
3
  "publishConfig": {
4
4
  "access": "public"
5
5
  },
6
- "description": "Less.js compatibility layer for Jess - enables Less plugins and visitors to work with Jess AST",
7
- "version": "2.0.0-alpha.1",
6
+ "description": "Less function compatibility bridge for Jess",
7
+ "version": "2.0.0-alpha.10",
8
+ "engines": {
9
+ "node": "^20.19.0 || >=22.12.0"
10
+ },
8
11
  "type": "module",
9
- "main": "lib/index.js",
12
+ "main": "lib/index.cjs",
10
13
  "types": "lib/index.d.ts",
11
14
  "exports": {
12
15
  ".": {
13
- "import": "./lib/index.js",
14
16
  "types": "./lib/index.d.ts",
15
- "source": "./src/index.ts"
16
- },
17
- "./transform": {
18
- "import": "./lib/transform/index.js",
19
- "types": "./lib/transform/index.d.ts",
20
- "source": "./src/transform/index.ts"
21
- },
22
- "./less-compat-structures": {
23
- "import": "./lib/less-compat-structures.js",
24
- "types": "./lib/less-compat-structures.d.ts",
25
- "source": "./src/less-compat-structures.ts"
17
+ "import": "./lib/index.js",
18
+ "require": "./lib/index.cjs"
26
19
  },
27
20
  "./package.json": "./package.json"
28
21
  },
@@ -30,9 +23,10 @@
30
23
  "lib"
31
24
  ],
32
25
  "dependencies": {
33
- "@jesscss/core": "2.0.0-alpha.1",
34
- "@jesscss/less-parser": "2.0.0-alpha.1",
35
- "@jesscss/plugin-node-modules": "2.0.0-alpha.1"
26
+ "@jesscss/awaitable-pipe": "2.0.0-alpha.10",
27
+ "@jesscss/core": "2.0.0-alpha.10",
28
+ "@jesscss/less-parser": "2.0.0-alpha.10",
29
+ "@jesscss/plugin-node-modules": "2.0.0-alpha.10"
36
30
  },
37
31
  "devDependencies": {
38
32
  "less-plugin-autoprefix": "^2.0.0",
@@ -45,10 +39,11 @@
45
39
  "url": "https://github.com/jesscss/jess/issues"
46
40
  },
47
41
  "homepage": "https://github.com/jesscss/jess#readme",
42
+ "module": "lib/index.js",
48
43
  "scripts": {
49
44
  "ci": "pnpm build && pnpm test",
50
45
  "build": "pnpm compile",
51
- "compile": "tsc -b tsconfig.build.json",
46
+ "compile": "tsdown --tsconfig tsconfig.build.json --no-dts && tsc -p tsconfig.build.json --emitDeclarationOnly",
52
47
  "test": "cross-env TEST=true vitest --run",
53
48
  "lint:fix": "eslint --fix '**/*.{js,ts}'",
54
49
  "lint": "eslint '**/*.{js,ts}'"
package/lib/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,gBAAgB,EAAE,OAAO,IAAI,gBAAgB,EAAgC,MAAM,aAAa,CAAC;AAC1G,cAAc,sBAAsB,CAAC;AACrC,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC"}