@jesscss/plugin-js 2.0.0-alpha.8 → 2.0.0-alpha.9

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/README.md CHANGED
@@ -39,11 +39,10 @@ favor of `@-from` / `@-use`.
39
39
 
40
40
  ## Status
41
41
 
42
- **Alpha.** Part of Jess, the ground-up rewrite of Less.js (Jess *is* Less.js
43
- v5). Requires a Deno runtime for script execution. The programmatic
44
- plugin/compiler API is **not yet stabilized** — the `jess` / `lessc` CLIs are the
45
- public surface for the alpha. Watch the [docs site](https://jesscss.github.io/)
46
- for the API once it settles.
42
+ **Alpha.** Part of Jess. Requires a Deno runtime for script execution. The
43
+ programmatic plugin/compiler API is **not yet stabilized** the `jess` CLI is
44
+ the documented public surface for the alpha. Watch the
45
+ [docs site](https://jesscss.github.io/) for the API once it settles.
47
46
 
48
47
  - Project overview & positioning: <https://github.com/jesscss/jess#readme>
49
48
  - Docs: <https://jesscss.github.io/> (currently pre-alpha content)
package/lib/bridge.d.ts CHANGED
@@ -16,17 +16,12 @@ export type JsBridgeValue = {
16
16
  kind: 'color';
17
17
  rgb: [number, number, number];
18
18
  alpha?: number;
19
- format?: string;
20
19
  } | {
21
20
  __jessBridge: true;
22
21
  kind: 'quoted';
23
22
  value: string;
24
23
  quote?: '"' | '\'';
25
24
  escaped?: boolean;
26
- } | {
27
- __jessBridge: true;
28
- kind: 'keyword';
29
- value: string;
30
25
  } | {
31
26
  __jessBridge: true;
32
27
  kind: 'anonymous';
@@ -35,17 +30,17 @@ export type JsBridgeValue = {
35
30
  __jessBridge: true;
36
31
  kind: 'list';
37
32
  items: JsBridgeValue[];
38
- separator?: ',' | ';' | '/';
33
+ separator: ',' | '/' | ';';
39
34
  } | {
40
35
  __jessBridge: true;
41
- kind: 'sequence';
36
+ kind: 'expression';
42
37
  items: JsBridgeValue[];
43
38
  } | {
44
39
  __jessBridge: true;
45
- kind: 'detached';
40
+ kind: 'mixin';
46
41
  rules: JsBridgeDeclaration[];
47
42
  };
48
43
  export declare function encodeBridgeValue(value: unknown): unknown;
49
44
  export declare function decodeBridgeValue(value: unknown): unknown;
50
- export declare function encodeBridgeArgs(args: unknown[]): unknown[];
45
+ export declare function encodeBridgeArgs(args: readonly unknown[]): unknown[];
51
46
  //# sourceMappingURL=bridge.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"AAcA,MAAM,MAAM,mBAAmB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,aAAa,CAAA;CAAE,CAAC;AAEzE,MAAM,MAAM,aAAa,GACrB;IAAE,YAAY,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAA;CAAE,GACxE;IAAE,YAAY,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,WAAW,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GACvE;IAAE,YAAY,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GACrG;IAAE,YAAY,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,GAAG,GAAG,IAAI,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,GAC5F;IAAE,YAAY,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACtD;IAAE,YAAY,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,WAAW,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACxD;IAAE,YAAY,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,aAAa,EAAE,CAAC;IAAC,SAAS,CAAC,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,CAAA;CAAE,GACzF;IAAE,YAAY,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,aAAa,EAAE,CAAA;CAAE,GAChE;IAAE,YAAY,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,UAAU,CAAC;IAAC,KAAK,EAAE,mBAAmB,EAAE,CAAA;CAAE,CAAC;AAqC3E,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAmEzD;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CA+BzD;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,EAAE,CAE3D"}
1
+ {"version":3,"file":"bridge.d.ts","sourceRoot":"","sources":["../src/bridge.ts"],"names":[],"mappings":"AAaA,MAAM,MAAM,mBAAmB,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,aAAa,CAAA;CAAE,CAAC;AAEzE,MAAM,MAAM,aAAa,GACrB;IAAE,YAAY,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAA;CAAE,GACxE;IAAE,YAAY,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,WAAW,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GACvE;IAAE,YAAY,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GACpF;IAAE,YAAY,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,GAAG,GAAG,IAAI,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,GAC5F;IAAE,YAAY,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,WAAW,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GACxD;IAAE,YAAY,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,aAAa,EAAE,CAAC;IAAC,SAAS,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,CAAA;CAAE,GACxF;IAAE,YAAY,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,YAAY,CAAC;IAAC,KAAK,EAAE,aAAa,EAAE,CAAA;CAAE,GAClE;IAAE,YAAY,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,mBAAmB,EAAE,CAAA;CAAE,CAAC;AAiGxE,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CA8BzD;AAqCD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAKzD;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,SAAS,OAAO,EAAE,GAAG,OAAO,EAAE,CAEpE"}
package/lib/index.cjs CHANGED
@@ -33,100 +33,198 @@ let node_path = require("node:path");
33
33
  node_path = __toESM(node_path);
34
34
  let node_url = require("node:url");
35
35
  let node_child_process = require("node:child_process");
36
+ let _jesscss_core_value = require("@jesscss/core/value");
36
37
  //#region src/bridge.ts
37
- const isBridgeValue = (value) => typeof value === "object" && value !== null && value.__jessBridge === true && typeof value.kind === "string";
38
- const colorFormatFromString = (value) => {
39
- if (!value) return;
40
- if (value in _jesscss_core.ColorFormat) return _jesscss_core.ColorFormat[value];
41
- if (Object.values(_jesscss_core.ColorFormat).includes(value)) return value;
42
- };
38
+ const isRecord = (value) => typeof value === "object" && value !== null;
39
+ const isBridgeValue = (value) => isRecord(value) && value.__jessBridge === true && typeof value.kind === "string";
40
+ const isValueObj = (value) => isRecord(value) && typeof value.type === "string" && typeof value.bytes === "string";
41
+ const isDetached = (value) => isRecord(value) && value.type === "DetachedRuleset" && Array.isArray(value.rules);
43
42
  function encodeBridgeChildValue(value) {
44
43
  const encoded = encodeBridgeValue(value);
45
44
  if (isBridgeValue(encoded)) return encoded;
46
45
  return {
47
46
  __jessBridge: true,
48
47
  kind: "scalar",
49
- value: typeof encoded === "string" || typeof encoded === "number" || typeof encoded === "boolean" ? encoded : String(encoded)
48
+ value: String(encoded)
50
49
  };
51
50
  }
51
+ function encodeFacadeValue(value) {
52
+ switch (value.type) {
53
+ case "Dimension": return typeof value.value === "number" ? {
54
+ __jessBridge: true,
55
+ kind: "dimension",
56
+ value: value.value,
57
+ unit: typeof value.unit === "string" ? value.unit : void 0
58
+ } : void 0;
59
+ case "Color": return Array.isArray(value.rgb) && value.rgb.length === 3 ? {
60
+ __jessBridge: true,
61
+ kind: "color",
62
+ rgb: [
63
+ value.rgb[0],
64
+ value.rgb[1],
65
+ value.rgb[2]
66
+ ],
67
+ alpha: typeof value.alpha === "number" ? value.alpha : void 0
68
+ } : void 0;
69
+ case "Quoted": return typeof value.value === "string" ? {
70
+ __jessBridge: true,
71
+ kind: "quoted",
72
+ value: value.value,
73
+ quote: value.quote === "'" ? "'" : "\"",
74
+ escaped: value.escaped === true
75
+ } : void 0;
76
+ case "Anonymous":
77
+ case "Keyword": return typeof value.value === "string" ? {
78
+ __jessBridge: true,
79
+ kind: "anonymous",
80
+ value: value.value
81
+ } : void 0;
82
+ case "Expression": return Array.isArray(value.value) ? {
83
+ __jessBridge: true,
84
+ kind: "expression",
85
+ items: value.value.map(encodeBridgeChildValue)
86
+ } : void 0;
87
+ case "Value": return Array.isArray(value.value) ? {
88
+ __jessBridge: true,
89
+ kind: "list",
90
+ items: value.value.map(encodeBridgeChildValue),
91
+ separator: value.separator === "/" || value.separator === ";" ? value.separator : ","
92
+ } : void 0;
93
+ case "Mixin": {
94
+ const ruleset = value.ruleset;
95
+ if (!isRecord(ruleset) || !Array.isArray(ruleset.rules)) return;
96
+ const rules = [];
97
+ for (const rule of ruleset.rules) {
98
+ if (!isRecord(rule)) continue;
99
+ if (rule.type === "Declaration" && typeof rule.name === "string") rules.push({
100
+ name: rule.name,
101
+ value: encodeBridgeChildValue(rule.value)
102
+ });
103
+ }
104
+ return {
105
+ __jessBridge: true,
106
+ kind: "mixin",
107
+ rules
108
+ };
109
+ }
110
+ default: return;
111
+ }
112
+ }
52
113
  function encodeBridgeValue(value) {
53
- if (value instanceof _jesscss_core.Dimension) return {
54
- __jessBridge: true,
55
- kind: "dimension",
56
- value: value.number,
57
- unit: value.unit
58
- };
59
- if (value instanceof _jesscss_core.Color) return {
114
+ if (Array.isArray(value)) return {
60
115
  __jessBridge: true,
61
- kind: "color",
62
- rgb: value.rgb,
63
- alpha: value.alpha,
64
- format: value.options.format === void 0 ? void 0 : _jesscss_core.ColorFormat[value.options.format]
116
+ kind: "expression",
117
+ items: value.map(encodeBridgeChildValue)
65
118
  };
66
- if (value instanceof _jesscss_core.Quoted) return {
67
- __jessBridge: true,
68
- kind: "quoted",
69
- value: String(value.value),
70
- quote: value.quote,
71
- escaped: value.escaped
72
- };
73
- if (value instanceof _jesscss_core.Any) return {
74
- __jessBridge: true,
75
- kind: value.role === "keyword" ? "keyword" : "anonymous",
76
- value: value.value
77
- };
78
- if (value instanceof _jesscss_core.List) return {
79
- __jessBridge: true,
80
- kind: "list",
81
- items: value.value.map(encodeBridgeChildValue),
82
- separator: value.options.sep
83
- };
84
- if (value instanceof _jesscss_core.Sequence) return {
85
- __jessBridge: true,
86
- kind: "sequence",
87
- items: value.value.map(encodeBridgeChildValue)
88
- };
89
- if (value instanceof _jesscss_core.Rules) {
90
- const rules = [];
91
- for (const rule of value.rules ?? []) if (rule instanceof _jesscss_core.Declaration && typeof rule.name === "string") rules.push({
92
- name: rule.name,
93
- value: encodeBridgeChildValue(rule.value)
94
- });
95
- return {
119
+ if (isValueObj(value)) switch (value.type) {
120
+ case "Dimension": return {
121
+ __jessBridge: true,
122
+ kind: "dimension",
123
+ value: value.number,
124
+ unit: value.unit
125
+ };
126
+ case "Color": return {
127
+ __jessBridge: true,
128
+ kind: "color",
129
+ rgb: [
130
+ value.rgb[0],
131
+ value.rgb[1],
132
+ value.rgb[2]
133
+ ],
134
+ alpha: value.alpha
135
+ };
136
+ case "Quoted": return {
137
+ __jessBridge: true,
138
+ kind: "quoted",
139
+ value: value.value,
140
+ quote: value.quote === "'" ? "'" : "\"",
141
+ escaped: value.escaped
142
+ };
143
+ case "List": return value.sep === "," || value.sep === "/" ? {
144
+ __jessBridge: true,
145
+ kind: "list",
146
+ items: value.value.map(encodeBridgeChildValue),
147
+ separator: value.sep
148
+ } : {
149
+ __jessBridge: true,
150
+ kind: "anonymous",
151
+ value: value.bytes
152
+ };
153
+ case "Block": return {
154
+ __jessBridge: true,
155
+ kind: "anonymous",
156
+ value: value.bytes
157
+ };
158
+ default: return {
96
159
  __jessBridge: true,
97
- kind: "detached",
98
- rules
160
+ kind: "anonymous",
161
+ value: value.bytes
99
162
  };
100
163
  }
164
+ if (isDetached(value)) return {
165
+ __jessBridge: true,
166
+ kind: "mixin",
167
+ rules: value.rules.map((rule) => ({
168
+ name: rule.name,
169
+ value: encodeBridgeChildValue(rule.value)
170
+ }))
171
+ };
172
+ if (isRecord(value)) return encodeFacadeValue(value) ?? value;
101
173
  return value;
102
174
  }
103
- function decodeBridgeValue(value) {
104
- if (!isBridgeValue(value)) return value;
175
+ function decodeValue(value) {
105
176
  switch (value.kind) {
106
- case "scalar": return new _jesscss_core.Any(String(value.value));
107
- case "dimension": return new _jesscss_core.Dimension({
108
- number: value.value,
109
- unit: value.unit
110
- });
111
- case "color": return new _jesscss_core.Color({
112
- rgb: value.rgb,
113
- alpha: value.alpha
114
- }, { format: colorFormatFromString(value.format) });
115
- case "quoted": return new _jesscss_core.Quoted(value.value, {
116
- quote: value.quote,
117
- escaped: value.escaped
118
- });
119
- case "keyword": return new _jesscss_core.Any(value.value, { role: "keyword" });
120
- case "anonymous": return new _jesscss_core.Any(value.value);
121
- case "list": return new _jesscss_core.List(value.items.map((item) => decodeBridgeValue(item)), { sep: value.separator });
122
- case "sequence": return new _jesscss_core.Sequence(value.items.map((item) => decodeBridgeValue(item)));
177
+ case "scalar": return (0, _jesscss_core_value.makeKeyword)(String(value.value));
178
+ case "dimension": return (0, _jesscss_core_value.makeDimension)(value.value, value.unit ?? "");
179
+ case "color": return (0, _jesscss_core_value.makeColorRgb)(value.rgb, value.alpha ?? 1, _jesscss_core_value.RGB);
180
+ case "quoted": return (0, _jesscss_core_value.makeQuoted)(value.value, value.quote ?? "\"", value.escaped === true);
181
+ case "anonymous": return (0, _jesscss_core_value.makeKeyword)(value.value);
182
+ case "expression": return value.items.map(decodeValue);
183
+ case "list": return (0, _jesscss_core_value.makeList)(value.items.map(decodeValue), value.separator === ";" ? "," : value.separator);
184
+ case "mixin": return (0, _jesscss_core_value.makeKeyword)("");
123
185
  }
124
186
  }
187
+ function decodeMixin(value) {
188
+ const nil = {
189
+ type: "Nil",
190
+ value: ""
191
+ };
192
+ const mixin = {
193
+ type: "Mixin",
194
+ name: nil,
195
+ args: nil,
196
+ ruleset: { rules: value.rules.map((rule) => ({
197
+ type: "Declaration",
198
+ name: rule.name,
199
+ value: decodeValue(rule.value),
200
+ eval() {
201
+ return this;
202
+ }
203
+ })) },
204
+ eval() {
205
+ return mixin;
206
+ }
207
+ };
208
+ return mixin;
209
+ }
210
+ function decodeBridgeValue(value) {
211
+ if (!isBridgeValue(value)) return value;
212
+ return value.kind === "mixin" ? decodeMixin(value) : decodeValue(value);
213
+ }
125
214
  function encodeBridgeArgs(args) {
126
215
  return args.map(encodeBridgeValue);
127
216
  }
128
217
  //#endregion
129
218
  //#region src/index.ts
219
+ /**
220
+ * Child-process stdio streams are typed as bare `Writable`/`Readable`, which do
221
+ * not declare `unref`, but the underlying pipe sockets expose it at runtime.
222
+ * The optional structural member lets any stream be passed without an unsafe
223
+ * cast; the runtime `?.` guard covers streams that genuinely lack the method.
224
+ */
225
+ const unrefStream = (stream) => {
226
+ if (typeof stream === "object" && stream !== null && "unref" in stream && typeof stream.unref === "function") stream.unref();
227
+ };
130
228
  const SCRIPT_EXTENSIONS = new Set([
131
229
  ".js",
132
230
  ".mjs",
@@ -390,9 +488,9 @@ var JsPlugin = class JsPlugin extends _jesscss_core.AbstractPlugin {
390
488
  });
391
489
  this.worker = child;
392
490
  child.unref();
393
- child.stdin.unref?.();
394
- child.stdout.unref?.();
395
- child.stderr.unref?.();
491
+ unrefStream(child.stdin);
492
+ unrefStream(child.stdout);
493
+ unrefStream(child.stderr);
396
494
  child.stdout.setEncoding("utf8");
397
495
  child.stderr.setEncoding("utf8");
398
496
  child.stdout.on("data", (chunk) => this.onWorkerStdout(chunk));
@@ -578,7 +676,7 @@ var JsPlugin = class JsPlugin extends _jesscss_core.AbstractPlugin {
578
676
  * @deprecated Less `@plugin` is deprecated. Prefer `@-from` for
579
677
  * ESM-style script imports or `@-use` for Sass-module-style namespace imports.
580
678
  */
581
- async importLessPlugin(absoluteFilePath) {
679
+ async importLessPlugin(absoluteFilePath, options = null) {
582
680
  const ext = node_path.extname(absoluteFilePath);
583
681
  if (!SCRIPT_EXTENSIONS.has(ext)) throw new Error(`Plugin "${this.name}" cannot import Less plugin "${absoluteFilePath}"`);
584
682
  this.assertAllowedPath(absoluteFilePath);
@@ -586,7 +684,8 @@ var JsPlugin = class JsPlugin extends _jesscss_core.AbstractPlugin {
586
684
  const modulePath = node_path.resolve(absoluteFilePath);
587
685
  const loadResult = await this.callWorker({
588
686
  type: "loadLessPlugin",
589
- modulePath
687
+ modulePath,
688
+ options
590
689
  });
591
690
  if (!loadResult.ok) throw new Error(loadResult.error);
592
691
  const functions = {};
@@ -594,6 +693,7 @@ var JsPlugin = class JsPlugin extends _jesscss_core.AbstractPlugin {
594
693
  const invokeResult = await this.callWorker({
595
694
  type: "invokeLessPluginFunction",
596
695
  modulePath,
696
+ options,
597
697
  functionName,
598
698
  args: encodeBridgeArgs(args)
599
699
  });
@@ -602,6 +702,10 @@ var JsPlugin = class JsPlugin extends _jesscss_core.AbstractPlugin {
602
702
  };
603
703
  return { functions };
604
704
  }
705
+ /** Context-facing executable-plugin capability used by the direct AST path. */
706
+ async importPlugin(absoluteFilePath, options = null) {
707
+ return this.importLessPlugin(absoluteFilePath, options);
708
+ }
605
709
  };
606
710
  /**
607
711
  * Global flag set when @jesscss/plugin-js is loaded.
package/lib/index.d.ts CHANGED
@@ -63,7 +63,11 @@ export declare class JsPlugin extends AbstractPlugin {
63
63
  * @deprecated Less `@plugin` is deprecated. Prefer `@-from` for
64
64
  * ESM-style script imports or `@-use` for Sass-module-style namespace imports.
65
65
  */
66
- importLessPlugin(absoluteFilePath: string): Promise<{
66
+ importLessPlugin(absoluteFilePath: string, options?: string | null): Promise<{
67
+ functions: Record<string, (...args: unknown[]) => Promise<unknown>>;
68
+ }>;
69
+ /** Context-facing executable-plugin capability used by the direct AST path. */
70
+ importPlugin(absoluteFilePath: string, options?: string | null): Promise<{
67
71
  functions: Record<string, (...args: unknown[]) => Promise<unknown>>;
68
72
  }>;
69
73
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,cAAc,EACf,MAAM,eAAe,CAAC;AAQvB,MAAM,MAAM,uBAAuB,GAAG;IACpC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,WAAW,eAAgB,SAAQ,uBAAuB;IAC9D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;CAChC;AAmCD;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,GAC3B,MAAK,MAAM,CAAC,UAAwB,KACnC,MAAM,CAAC,UAYT,CAAC;AA0FF,qBAAa,QAAS,SAAQ,cAAc;IAoBvB,IAAI,EAAE,eAAe;IAnBxC,OAAO,CAAC,MAAM,CAAC,iBAAiB,CAAS;IACzC,OAAO,CAAC,MAAM,CAAC,aAAa,CAAuB;IACnD,IAAI,SAAQ;IACZ,mBAAmB,WAAiC;IACpD,OAAO,CAAC,YAAY,CAAoC;IACxD,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,YAAY,CAAyB;IAC7C,OAAO,CAAC,gBAAgB,CAAqB;IAC7C,OAAO,CAAC,MAAM,CAA6C;IAC3D,OAAO,CAAC,YAAY,CAAM;IAC1B,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,OAAO,CAIV;IAEL,OAAO,CAAC,SAAS,CAA6B;gBAE3B,IAAI,GAAE,eAAoB;IAM7C,OAAO;IAIP,OAAO,CAAC,MAAM,CAAC,sBAAsB;IAkBrC,OAAO,CAAC,cAAc;IAOtB,OAAO,CAAC,oBAAoB;IAY5B,OAAO,CAAC,sBAAsB;IAW9B,OAAO,CAAC,aAAa;IA4BrB,OAAO,CAAC,gBAAgB;IAUxB,OAAO,CAAC,aAAa;IAarB,OAAO,CAAC,YAAY;IAepB,OAAO,CAAC,mBAAmB;YA0Bb,WAAW;IA8CzB,OAAO,CAAC,WAAW;YAwFL,YAAY;IAW1B,OAAO,CAAC,cAAc;IAiCtB,OAAO,CAAC,gBAAgB;YAQV,UAAU;IAwBxB,OAAO,CAAC,QAAQ;IA0BhB,OAAO;IAMP,OAAO,CAAC,iBAAiB;IAgBnB,MAAM,CAAC,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IA+CpE;;;;;OAKG;IACG,gBAAgB,CAAC,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC,CAAA;KAAE,CAAC;CA6BnI;AAED;;;GAGG;AACH,eAAO,MAAM,qBAAqB,iCAAiC,CAAC;AAOpE,QAAA,MAAM,QAAQ,UAAY,eAAe,aAEtB,CAAC;AAEpB,eAAe,QAAQ,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,cAAc,EACf,MAAM,eAAe,CAAC;AAyBvB,MAAM,MAAM,uBAAuB,GAAG;IACpC,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,EAAE,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,WAAW,eAAgB,SAAQ,uBAAuB;IAC9D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;CAChC;AAmCD;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,GAC3B,MAAK,MAAM,CAAC,UAAwB,KACnC,MAAM,CAAC,UAYT,CAAC;AA0FF,qBAAa,QAAS,SAAQ,cAAc;IAoBvB,IAAI,EAAE,eAAe;IAnBxC,OAAO,CAAC,MAAM,CAAC,iBAAiB,CAAS;IACzC,OAAO,CAAC,MAAM,CAAC,aAAa,CAAuB;IACnD,IAAI,SAAQ;IACZ,mBAAmB,WAAiC;IACpD,OAAO,CAAC,YAAY,CAAoC;IACxD,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,YAAY,CAAyB;IAC7C,OAAO,CAAC,gBAAgB,CAAqB;IAC7C,OAAO,CAAC,MAAM,CAA6C;IAC3D,OAAO,CAAC,YAAY,CAAM;IAC1B,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,OAAO,CAIV;IAEL,OAAO,CAAC,SAAS,CAA6B;gBAE3B,IAAI,GAAE,eAAoB;IAM7C,OAAO;IAIP,OAAO,CAAC,MAAM,CAAC,sBAAsB;IAkBrC,OAAO,CAAC,cAAc;IAOtB,OAAO,CAAC,oBAAoB;IAY5B,OAAO,CAAC,sBAAsB;IAW9B,OAAO,CAAC,aAAa;IA4BrB,OAAO,CAAC,gBAAgB;IAUxB,OAAO,CAAC,aAAa;IAarB,OAAO,CAAC,YAAY;IAepB,OAAO,CAAC,mBAAmB;YA0Bb,WAAW;IA8CzB,OAAO,CAAC,WAAW;YAwFL,YAAY;IAW1B,OAAO,CAAC,cAAc;IAiCtB,OAAO,CAAC,gBAAgB;YAQV,UAAU;IAwBxB,OAAO,CAAC,QAAQ;IA0BhB,OAAO;IAMP,OAAO,CAAC,iBAAiB;IAgBnB,MAAM,CAAC,gBAAgB,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IA+CpE;;;;;OAKG;IACG,gBAAgB,CAAC,gBAAgB,EAAE,MAAM,EAAE,OAAO,GAAE,MAAM,GAAG,IAAW,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC,CAAA;KAAE,CAAC;IA+BjK,+EAA+E;IACzE,YAAY,CAAC,gBAAgB,EAAE,MAAM,EAAE,OAAO,GAAE,MAAM,GAAG,IAAW,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC,CAAA;KAAE,CAAC;CAG9J;AAED;;;GAGG;AACH,eAAO,MAAM,qBAAqB,iCAAiC,CAAC;AAOpE,QAAA,MAAM,QAAQ,UAAY,eAAe,aAEtB,CAAC;AAEpB,eAAe,QAAQ,CAAC"}
package/lib/index.js CHANGED
@@ -1,103 +1,201 @@
1
- import { AbstractPlugin, Any, Color, ColorFormat, Declaration, Dimension, List, Quoted, Rules, Sequence } from "@jesscss/core";
1
+ import { AbstractPlugin } from "@jesscss/core";
2
2
  import * as fs from "node:fs";
3
3
  import * as net from "node:net";
4
4
  import * as path from "node:path";
5
5
  import { fileURLToPath, pathToFileURL } from "node:url";
6
6
  import { spawn, spawnSync } from "node:child_process";
7
+ import { RGB, makeColorRgb, makeDimension, makeKeyword, makeList, makeQuoted } from "@jesscss/core/value";
7
8
  //#region src/bridge.ts
8
- const isBridgeValue = (value) => typeof value === "object" && value !== null && value.__jessBridge === true && typeof value.kind === "string";
9
- const colorFormatFromString = (value) => {
10
- if (!value) return;
11
- if (value in ColorFormat) return ColorFormat[value];
12
- if (Object.values(ColorFormat).includes(value)) return value;
13
- };
9
+ const isRecord = (value) => typeof value === "object" && value !== null;
10
+ const isBridgeValue = (value) => isRecord(value) && value.__jessBridge === true && typeof value.kind === "string";
11
+ const isValueObj = (value) => isRecord(value) && typeof value.type === "string" && typeof value.bytes === "string";
12
+ const isDetached = (value) => isRecord(value) && value.type === "DetachedRuleset" && Array.isArray(value.rules);
14
13
  function encodeBridgeChildValue(value) {
15
14
  const encoded = encodeBridgeValue(value);
16
15
  if (isBridgeValue(encoded)) return encoded;
17
16
  return {
18
17
  __jessBridge: true,
19
18
  kind: "scalar",
20
- value: typeof encoded === "string" || typeof encoded === "number" || typeof encoded === "boolean" ? encoded : String(encoded)
19
+ value: String(encoded)
21
20
  };
22
21
  }
22
+ function encodeFacadeValue(value) {
23
+ switch (value.type) {
24
+ case "Dimension": return typeof value.value === "number" ? {
25
+ __jessBridge: true,
26
+ kind: "dimension",
27
+ value: value.value,
28
+ unit: typeof value.unit === "string" ? value.unit : void 0
29
+ } : void 0;
30
+ case "Color": return Array.isArray(value.rgb) && value.rgb.length === 3 ? {
31
+ __jessBridge: true,
32
+ kind: "color",
33
+ rgb: [
34
+ value.rgb[0],
35
+ value.rgb[1],
36
+ value.rgb[2]
37
+ ],
38
+ alpha: typeof value.alpha === "number" ? value.alpha : void 0
39
+ } : void 0;
40
+ case "Quoted": return typeof value.value === "string" ? {
41
+ __jessBridge: true,
42
+ kind: "quoted",
43
+ value: value.value,
44
+ quote: value.quote === "'" ? "'" : "\"",
45
+ escaped: value.escaped === true
46
+ } : void 0;
47
+ case "Anonymous":
48
+ case "Keyword": return typeof value.value === "string" ? {
49
+ __jessBridge: true,
50
+ kind: "anonymous",
51
+ value: value.value
52
+ } : void 0;
53
+ case "Expression": return Array.isArray(value.value) ? {
54
+ __jessBridge: true,
55
+ kind: "expression",
56
+ items: value.value.map(encodeBridgeChildValue)
57
+ } : void 0;
58
+ case "Value": return Array.isArray(value.value) ? {
59
+ __jessBridge: true,
60
+ kind: "list",
61
+ items: value.value.map(encodeBridgeChildValue),
62
+ separator: value.separator === "/" || value.separator === ";" ? value.separator : ","
63
+ } : void 0;
64
+ case "Mixin": {
65
+ const ruleset = value.ruleset;
66
+ if (!isRecord(ruleset) || !Array.isArray(ruleset.rules)) return;
67
+ const rules = [];
68
+ for (const rule of ruleset.rules) {
69
+ if (!isRecord(rule)) continue;
70
+ if (rule.type === "Declaration" && typeof rule.name === "string") rules.push({
71
+ name: rule.name,
72
+ value: encodeBridgeChildValue(rule.value)
73
+ });
74
+ }
75
+ return {
76
+ __jessBridge: true,
77
+ kind: "mixin",
78
+ rules
79
+ };
80
+ }
81
+ default: return;
82
+ }
83
+ }
23
84
  function encodeBridgeValue(value) {
24
- if (value instanceof Dimension) return {
25
- __jessBridge: true,
26
- kind: "dimension",
27
- value: value.number,
28
- unit: value.unit
29
- };
30
- if (value instanceof Color) return {
85
+ if (Array.isArray(value)) return {
31
86
  __jessBridge: true,
32
- kind: "color",
33
- rgb: value.rgb,
34
- alpha: value.alpha,
35
- format: value.options.format === void 0 ? void 0 : ColorFormat[value.options.format]
87
+ kind: "expression",
88
+ items: value.map(encodeBridgeChildValue)
36
89
  };
37
- if (value instanceof Quoted) return {
38
- __jessBridge: true,
39
- kind: "quoted",
40
- value: String(value.value),
41
- quote: value.quote,
42
- escaped: value.escaped
43
- };
44
- if (value instanceof Any) return {
45
- __jessBridge: true,
46
- kind: value.role === "keyword" ? "keyword" : "anonymous",
47
- value: value.value
48
- };
49
- if (value instanceof List) return {
50
- __jessBridge: true,
51
- kind: "list",
52
- items: value.value.map(encodeBridgeChildValue),
53
- separator: value.options.sep
54
- };
55
- if (value instanceof Sequence) return {
56
- __jessBridge: true,
57
- kind: "sequence",
58
- items: value.value.map(encodeBridgeChildValue)
59
- };
60
- if (value instanceof Rules) {
61
- const rules = [];
62
- for (const rule of value.rules ?? []) if (rule instanceof Declaration && typeof rule.name === "string") rules.push({
63
- name: rule.name,
64
- value: encodeBridgeChildValue(rule.value)
65
- });
66
- return {
90
+ if (isValueObj(value)) switch (value.type) {
91
+ case "Dimension": return {
92
+ __jessBridge: true,
93
+ kind: "dimension",
94
+ value: value.number,
95
+ unit: value.unit
96
+ };
97
+ case "Color": return {
98
+ __jessBridge: true,
99
+ kind: "color",
100
+ rgb: [
101
+ value.rgb[0],
102
+ value.rgb[1],
103
+ value.rgb[2]
104
+ ],
105
+ alpha: value.alpha
106
+ };
107
+ case "Quoted": return {
108
+ __jessBridge: true,
109
+ kind: "quoted",
110
+ value: value.value,
111
+ quote: value.quote === "'" ? "'" : "\"",
112
+ escaped: value.escaped
113
+ };
114
+ case "List": return value.sep === "," || value.sep === "/" ? {
115
+ __jessBridge: true,
116
+ kind: "list",
117
+ items: value.value.map(encodeBridgeChildValue),
118
+ separator: value.sep
119
+ } : {
120
+ __jessBridge: true,
121
+ kind: "anonymous",
122
+ value: value.bytes
123
+ };
124
+ case "Block": return {
125
+ __jessBridge: true,
126
+ kind: "anonymous",
127
+ value: value.bytes
128
+ };
129
+ default: return {
67
130
  __jessBridge: true,
68
- kind: "detached",
69
- rules
131
+ kind: "anonymous",
132
+ value: value.bytes
70
133
  };
71
134
  }
135
+ if (isDetached(value)) return {
136
+ __jessBridge: true,
137
+ kind: "mixin",
138
+ rules: value.rules.map((rule) => ({
139
+ name: rule.name,
140
+ value: encodeBridgeChildValue(rule.value)
141
+ }))
142
+ };
143
+ if (isRecord(value)) return encodeFacadeValue(value) ?? value;
72
144
  return value;
73
145
  }
74
- function decodeBridgeValue(value) {
75
- if (!isBridgeValue(value)) return value;
146
+ function decodeValue(value) {
76
147
  switch (value.kind) {
77
- case "scalar": return new Any(String(value.value));
78
- case "dimension": return new Dimension({
79
- number: value.value,
80
- unit: value.unit
81
- });
82
- case "color": return new Color({
83
- rgb: value.rgb,
84
- alpha: value.alpha
85
- }, { format: colorFormatFromString(value.format) });
86
- case "quoted": return new Quoted(value.value, {
87
- quote: value.quote,
88
- escaped: value.escaped
89
- });
90
- case "keyword": return new Any(value.value, { role: "keyword" });
91
- case "anonymous": return new Any(value.value);
92
- case "list": return new List(value.items.map((item) => decodeBridgeValue(item)), { sep: value.separator });
93
- case "sequence": return new Sequence(value.items.map((item) => decodeBridgeValue(item)));
148
+ case "scalar": return makeKeyword(String(value.value));
149
+ case "dimension": return makeDimension(value.value, value.unit ?? "");
150
+ case "color": return makeColorRgb(value.rgb, value.alpha ?? 1, RGB);
151
+ case "quoted": return makeQuoted(value.value, value.quote ?? "\"", value.escaped === true);
152
+ case "anonymous": return makeKeyword(value.value);
153
+ case "expression": return value.items.map(decodeValue);
154
+ case "list": return makeList(value.items.map(decodeValue), value.separator === ";" ? "," : value.separator);
155
+ case "mixin": return makeKeyword("");
94
156
  }
95
157
  }
158
+ function decodeMixin(value) {
159
+ const nil = {
160
+ type: "Nil",
161
+ value: ""
162
+ };
163
+ const mixin = {
164
+ type: "Mixin",
165
+ name: nil,
166
+ args: nil,
167
+ ruleset: { rules: value.rules.map((rule) => ({
168
+ type: "Declaration",
169
+ name: rule.name,
170
+ value: decodeValue(rule.value),
171
+ eval() {
172
+ return this;
173
+ }
174
+ })) },
175
+ eval() {
176
+ return mixin;
177
+ }
178
+ };
179
+ return mixin;
180
+ }
181
+ function decodeBridgeValue(value) {
182
+ if (!isBridgeValue(value)) return value;
183
+ return value.kind === "mixin" ? decodeMixin(value) : decodeValue(value);
184
+ }
96
185
  function encodeBridgeArgs(args) {
97
186
  return args.map(encodeBridgeValue);
98
187
  }
99
188
  //#endregion
100
189
  //#region src/index.ts
190
+ /**
191
+ * Child-process stdio streams are typed as bare `Writable`/`Readable`, which do
192
+ * not declare `unref`, but the underlying pipe sockets expose it at runtime.
193
+ * The optional structural member lets any stream be passed without an unsafe
194
+ * cast; the runtime `?.` guard covers streams that genuinely lack the method.
195
+ */
196
+ const unrefStream = (stream) => {
197
+ if (typeof stream === "object" && stream !== null && "unref" in stream && typeof stream.unref === "function") stream.unref();
198
+ };
101
199
  const SCRIPT_EXTENSIONS = new Set([
102
200
  ".js",
103
201
  ".mjs",
@@ -361,9 +459,9 @@ var JsPlugin = class JsPlugin extends AbstractPlugin {
361
459
  });
362
460
  this.worker = child;
363
461
  child.unref();
364
- child.stdin.unref?.();
365
- child.stdout.unref?.();
366
- child.stderr.unref?.();
462
+ unrefStream(child.stdin);
463
+ unrefStream(child.stdout);
464
+ unrefStream(child.stderr);
367
465
  child.stdout.setEncoding("utf8");
368
466
  child.stderr.setEncoding("utf8");
369
467
  child.stdout.on("data", (chunk) => this.onWorkerStdout(chunk));
@@ -549,7 +647,7 @@ var JsPlugin = class JsPlugin extends AbstractPlugin {
549
647
  * @deprecated Less `@plugin` is deprecated. Prefer `@-from` for
550
648
  * ESM-style script imports or `@-use` for Sass-module-style namespace imports.
551
649
  */
552
- async importLessPlugin(absoluteFilePath) {
650
+ async importLessPlugin(absoluteFilePath, options = null) {
553
651
  const ext = path.extname(absoluteFilePath);
554
652
  if (!SCRIPT_EXTENSIONS.has(ext)) throw new Error(`Plugin "${this.name}" cannot import Less plugin "${absoluteFilePath}"`);
555
653
  this.assertAllowedPath(absoluteFilePath);
@@ -557,7 +655,8 @@ var JsPlugin = class JsPlugin extends AbstractPlugin {
557
655
  const modulePath = path.resolve(absoluteFilePath);
558
656
  const loadResult = await this.callWorker({
559
657
  type: "loadLessPlugin",
560
- modulePath
658
+ modulePath,
659
+ options
561
660
  });
562
661
  if (!loadResult.ok) throw new Error(loadResult.error);
563
662
  const functions = {};
@@ -565,6 +664,7 @@ var JsPlugin = class JsPlugin extends AbstractPlugin {
565
664
  const invokeResult = await this.callWorker({
566
665
  type: "invokeLessPluginFunction",
567
666
  modulePath,
667
+ options,
568
668
  functionName,
569
669
  args: encodeBridgeArgs(args)
570
670
  });
@@ -573,6 +673,10 @@ var JsPlugin = class JsPlugin extends AbstractPlugin {
573
673
  };
574
674
  return { functions };
575
675
  }
676
+ /** Context-facing executable-plugin capability used by the direct AST path. */
677
+ async importPlugin(absoluteFilePath, options = null) {
678
+ return this.importLessPlugin(absoluteFilePath, options);
679
+ }
576
680
  };
577
681
  /**
578
682
  * Global flag set when @jesscss/plugin-js is loaded.
@@ -78,8 +78,17 @@ var Declaration = class {
78
78
  return `${this.name}: ${v}`;
79
79
  }
80
80
  };
81
- var DetachedRuleset = class {
82
- type = "DetachedRuleset";
81
+ var Nil = class {
82
+ type = "Nil";
83
+ value = "";
84
+ eval() {
85
+ return this;
86
+ }
87
+ };
88
+ var Mixin = class {
89
+ type = "Mixin";
90
+ name = new Nil();
91
+ args = new Nil();
83
92
  ruleset;
84
93
  constructor(rules) {
85
94
  this.ruleset = { rules };
@@ -115,10 +124,11 @@ const lessFacade = {
115
124
  Anonymous,
116
125
  Color,
117
126
  Declaration,
118
- DetachedRuleset,
119
127
  Dimension,
120
128
  Expression,
121
129
  Keyword,
130
+ Mixin,
131
+ Nil,
122
132
  Quoted,
123
133
  Value
124
134
  },
@@ -150,11 +160,10 @@ const decodeBridgeValue = (value) => {
150
160
  case "dimension": return new Dimension(value.value, value.unit ?? "");
151
161
  case "color": return new Color(value.rgb, value.alpha ?? 1);
152
162
  case "quoted": return new Quoted(value.quote ?? "\"", value.value, value.escaped === true);
153
- case "keyword": return new Keyword(value.value);
154
163
  case "anonymous": return new Anonymous(value.value);
155
164
  case "list": return new Value((value.items ?? []).map(decodeBridgeValue), value.separator ?? ",");
156
- case "sequence": return new Expression((value.items ?? []).map(decodeBridgeValue));
157
- case "detached": return new DetachedRuleset((value.rules ?? []).map((decl) => {
165
+ case "expression": return new Expression((value.items ?? []).map(decodeBridgeValue));
166
+ case "mixin": return new Mixin((value.rules ?? []).map((decl) => {
158
167
  const decoded = decodeBridgeValue(decl.value);
159
168
  const cssText = decoded?.toCSS ? decoded.toCSS() : String(decoded);
160
169
  return new Declaration(decl.name, new Anonymous(cssText));
@@ -191,21 +200,29 @@ const encodeBridgeValue = (value) => {
191
200
  quote: value.quote,
192
201
  escaped: value.escaped
193
202
  };
194
- if (value instanceof Keyword) return {
203
+ if (value instanceof Keyword || value instanceof Anonymous) return {
195
204
  __jessBridge: true,
196
- kind: "keyword",
205
+ kind: "anonymous",
197
206
  value: String(value.value)
198
207
  };
199
- if (value instanceof Anonymous) return {
208
+ if (value instanceof Expression) return {
200
209
  __jessBridge: true,
201
- kind: "anonymous",
202
- value: String(value.value)
210
+ kind: "expression",
211
+ items: value.value.map(encodeBridgeChildValue)
203
212
  };
204
- if (value instanceof Value || value instanceof Expression) return {
213
+ if (value instanceof Value) return {
205
214
  __jessBridge: true,
206
- kind: value instanceof Expression ? "sequence" : "list",
215
+ kind: "list",
207
216
  items: value.value.map(encodeBridgeChildValue),
208
- separator: value instanceof Expression ? void 0 : value.separator
217
+ separator: value.separator === "/" || value.separator === ";" ? value.separator : ","
218
+ };
219
+ if (value instanceof Mixin) return {
220
+ __jessBridge: true,
221
+ kind: "mixin",
222
+ rules: value.ruleset.rules.filter((rule) => rule instanceof Declaration && typeof rule.name === "string").map((rule) => ({
223
+ name: rule.name,
224
+ value: encodeBridgeChildValue(rule.value)
225
+ }))
209
226
  };
210
227
  return value;
211
228
  };
@@ -243,7 +260,7 @@ const loadModule = async (modulePath) => {
243
260
  }
244
261
  return exports;
245
262
  };
246
- const createLegacyLessPluginRuntime = (modulePath) => {
263
+ const createLegacyLessPluginRuntime = (modulePath, options) => {
247
264
  const localFunctions = /* @__PURE__ */ new Map();
248
265
  const functions = {
249
266
  add(name, fn) {
@@ -280,7 +297,9 @@ const createLegacyLessPluginRuntime = (modulePath) => {
280
297
  return plugin;
281
298
  }
282
299
  })() : plugin;
300
+ if (candidate && options != null && typeof candidate.setOptions === "function") candidate.setOptions(options);
283
301
  if (candidate && typeof candidate.install === "function") candidate.install(less, manager, functions);
302
+ if (candidate && options != null && typeof candidate.setOptions === "function") candidate.setOptions(options);
284
303
  };
285
304
  const require = (specifier) => {
286
305
  throw new Error(`Less @plugin require("${specifier}") is not supported in the Deno sandbox yet.`);
@@ -298,11 +317,13 @@ const createLegacyLessPluginRuntime = (modulePath) => {
298
317
  fileInfo: { filename: modulePath }
299
318
  };
300
319
  };
301
- const loadLessPlugin = async (modulePath) => {
302
- let functions = lessPluginFunctionCache.get(modulePath);
320
+ const lessPluginCacheKey = (modulePath, options) => `${modulePath}\u0000${options ?? ""}`;
321
+ const loadLessPlugin = async (modulePath, options = null) => {
322
+ const cacheKey = lessPluginCacheKey(modulePath, options);
323
+ let functions = lessPluginFunctionCache.get(cacheKey);
303
324
  if (functions) return Array.from(functions.keys());
304
325
  const source = await Deno.readTextFile(modulePath);
305
- const runtime = createLegacyLessPluginRuntime(modulePath);
326
+ const runtime = createLegacyLessPluginRuntime(modulePath, options);
306
327
  const module = { exports: runtime.exports };
307
328
  new Function("module", "exports", "require", "registerPlugin", "functions", "tree", "less", "fileInfo", "process", source)(module, module.exports, runtime.require, runtime.registerPlugin, runtime.functions, lessFacade.tree, {
308
329
  ...lessFacade,
@@ -311,14 +332,15 @@ const loadLessPlugin = async (modulePath) => {
311
332
  const exported = module.exports?.default ?? module.exports;
312
333
  if (typeof exported === "function" || exported && typeof exported.install === "function") runtime.registerPlugin(exported);
313
334
  functions = runtime.localFunctions;
314
- lessPluginFunctionCache.set(modulePath, functions);
335
+ lessPluginFunctionCache.set(cacheKey, functions);
315
336
  return Array.from(functions.keys());
316
337
  };
317
- const invokeLessPluginFunction = async (modulePath, functionName, args) => {
318
- let functions = lessPluginFunctionCache.get(modulePath);
338
+ const invokeLessPluginFunction = async (modulePath, functionName, args, options = null) => {
339
+ const cacheKey = lessPluginCacheKey(modulePath, options);
340
+ let functions = lessPluginFunctionCache.get(cacheKey);
319
341
  if (!functions) {
320
- await loadLessPlugin(modulePath);
321
- functions = lessPluginFunctionCache.get(modulePath);
342
+ await loadLessPlugin(modulePath, options);
343
+ functions = lessPluginFunctionCache.get(cacheKey);
322
344
  }
323
345
  const fn = functions?.get(String(functionName).toLowerCase());
324
346
  if (typeof fn !== "function") throw new Error(`Less @plugin function "${functionName}" is not registered.`);
@@ -357,7 +379,7 @@ const handleRequest = async (req) => {
357
379
  return;
358
380
  }
359
381
  if (req.type === "loadLessPlugin") {
360
- const functions = await loadLessPlugin(req.modulePath);
382
+ const functions = await loadLessPlugin(req.modulePath, req.options ?? null);
361
383
  send({
362
384
  id: req.id,
363
385
  ok: true,
@@ -366,7 +388,7 @@ const handleRequest = async (req) => {
366
388
  return;
367
389
  }
368
390
  if (req.type === "invokeLessPluginFunction") {
369
- const value = await invokeLessPluginFunction(req.modulePath, req.functionName, req.args ?? []);
391
+ const value = await invokeLessPluginFunction(req.modulePath, req.functionName, req.args ?? [], req.options ?? null);
370
392
  send({
371
393
  id: req.id,
372
394
  ok: true,
@@ -76,8 +76,17 @@ var Declaration = class {
76
76
  return `${this.name}: ${v}`;
77
77
  }
78
78
  };
79
- var DetachedRuleset = class {
80
- type = "DetachedRuleset";
79
+ var Nil = class {
80
+ type = "Nil";
81
+ value = "";
82
+ eval() {
83
+ return this;
84
+ }
85
+ };
86
+ var Mixin = class {
87
+ type = "Mixin";
88
+ name = new Nil();
89
+ args = new Nil();
81
90
  ruleset;
82
91
  constructor(rules) {
83
92
  this.ruleset = { rules };
@@ -113,10 +122,11 @@ const lessFacade = {
113
122
  Anonymous,
114
123
  Color,
115
124
  Declaration,
116
- DetachedRuleset,
117
125
  Dimension,
118
126
  Expression,
119
127
  Keyword,
128
+ Mixin,
129
+ Nil,
120
130
  Quoted,
121
131
  Value
122
132
  },
@@ -148,11 +158,10 @@ const decodeBridgeValue = (value) => {
148
158
  case "dimension": return new Dimension(value.value, value.unit ?? "");
149
159
  case "color": return new Color(value.rgb, value.alpha ?? 1);
150
160
  case "quoted": return new Quoted(value.quote ?? "\"", value.value, value.escaped === true);
151
- case "keyword": return new Keyword(value.value);
152
161
  case "anonymous": return new Anonymous(value.value);
153
162
  case "list": return new Value((value.items ?? []).map(decodeBridgeValue), value.separator ?? ",");
154
- case "sequence": return new Expression((value.items ?? []).map(decodeBridgeValue));
155
- case "detached": return new DetachedRuleset((value.rules ?? []).map((decl) => {
163
+ case "expression": return new Expression((value.items ?? []).map(decodeBridgeValue));
164
+ case "mixin": return new Mixin((value.rules ?? []).map((decl) => {
156
165
  const decoded = decodeBridgeValue(decl.value);
157
166
  const cssText = decoded?.toCSS ? decoded.toCSS() : String(decoded);
158
167
  return new Declaration(decl.name, new Anonymous(cssText));
@@ -189,21 +198,29 @@ const encodeBridgeValue = (value) => {
189
198
  quote: value.quote,
190
199
  escaped: value.escaped
191
200
  };
192
- if (value instanceof Keyword) return {
201
+ if (value instanceof Keyword || value instanceof Anonymous) return {
193
202
  __jessBridge: true,
194
- kind: "keyword",
203
+ kind: "anonymous",
195
204
  value: String(value.value)
196
205
  };
197
- if (value instanceof Anonymous) return {
206
+ if (value instanceof Expression) return {
198
207
  __jessBridge: true,
199
- kind: "anonymous",
200
- value: String(value.value)
208
+ kind: "expression",
209
+ items: value.value.map(encodeBridgeChildValue)
201
210
  };
202
- if (value instanceof Value || value instanceof Expression) return {
211
+ if (value instanceof Value) return {
203
212
  __jessBridge: true,
204
- kind: value instanceof Expression ? "sequence" : "list",
213
+ kind: "list",
205
214
  items: value.value.map(encodeBridgeChildValue),
206
- separator: value instanceof Expression ? void 0 : value.separator
215
+ separator: value.separator === "/" || value.separator === ";" ? value.separator : ","
216
+ };
217
+ if (value instanceof Mixin) return {
218
+ __jessBridge: true,
219
+ kind: "mixin",
220
+ rules: value.ruleset.rules.filter((rule) => rule instanceof Declaration && typeof rule.name === "string").map((rule) => ({
221
+ name: rule.name,
222
+ value: encodeBridgeChildValue(rule.value)
223
+ }))
207
224
  };
208
225
  return value;
209
226
  };
@@ -241,7 +258,7 @@ const loadModule = async (modulePath) => {
241
258
  }
242
259
  return exports;
243
260
  };
244
- const createLegacyLessPluginRuntime = (modulePath) => {
261
+ const createLegacyLessPluginRuntime = (modulePath, options) => {
245
262
  const localFunctions = /* @__PURE__ */ new Map();
246
263
  const functions = {
247
264
  add(name, fn) {
@@ -278,7 +295,9 @@ const createLegacyLessPluginRuntime = (modulePath) => {
278
295
  return plugin;
279
296
  }
280
297
  })() : plugin;
298
+ if (candidate && options != null && typeof candidate.setOptions === "function") candidate.setOptions(options);
281
299
  if (candidate && typeof candidate.install === "function") candidate.install(less, manager, functions);
300
+ if (candidate && options != null && typeof candidate.setOptions === "function") candidate.setOptions(options);
282
301
  };
283
302
  const require = (specifier) => {
284
303
  throw new Error(`Less @plugin require("${specifier}") is not supported in the Deno sandbox yet.`);
@@ -296,11 +315,13 @@ const createLegacyLessPluginRuntime = (modulePath) => {
296
315
  fileInfo: { filename: modulePath }
297
316
  };
298
317
  };
299
- const loadLessPlugin = async (modulePath) => {
300
- let functions = lessPluginFunctionCache.get(modulePath);
318
+ const lessPluginCacheKey = (modulePath, options) => `${modulePath}\u0000${options ?? ""}`;
319
+ const loadLessPlugin = async (modulePath, options = null) => {
320
+ const cacheKey = lessPluginCacheKey(modulePath, options);
321
+ let functions = lessPluginFunctionCache.get(cacheKey);
301
322
  if (functions) return Array.from(functions.keys());
302
323
  const source = await Deno.readTextFile(modulePath);
303
- const runtime = createLegacyLessPluginRuntime(modulePath);
324
+ const runtime = createLegacyLessPluginRuntime(modulePath, options);
304
325
  const module = { exports: runtime.exports };
305
326
  new Function("module", "exports", "require", "registerPlugin", "functions", "tree", "less", "fileInfo", "process", source)(module, module.exports, runtime.require, runtime.registerPlugin, runtime.functions, lessFacade.tree, {
306
327
  ...lessFacade,
@@ -309,14 +330,15 @@ const loadLessPlugin = async (modulePath) => {
309
330
  const exported = module.exports?.default ?? module.exports;
310
331
  if (typeof exported === "function" || exported && typeof exported.install === "function") runtime.registerPlugin(exported);
311
332
  functions = runtime.localFunctions;
312
- lessPluginFunctionCache.set(modulePath, functions);
333
+ lessPluginFunctionCache.set(cacheKey, functions);
313
334
  return Array.from(functions.keys());
314
335
  };
315
- const invokeLessPluginFunction = async (modulePath, functionName, args) => {
316
- let functions = lessPluginFunctionCache.get(modulePath);
336
+ const invokeLessPluginFunction = async (modulePath, functionName, args, options = null) => {
337
+ const cacheKey = lessPluginCacheKey(modulePath, options);
338
+ let functions = lessPluginFunctionCache.get(cacheKey);
317
339
  if (!functions) {
318
- await loadLessPlugin(modulePath);
319
- functions = lessPluginFunctionCache.get(modulePath);
340
+ await loadLessPlugin(modulePath, options);
341
+ functions = lessPluginFunctionCache.get(cacheKey);
320
342
  }
321
343
  const fn = functions?.get(String(functionName).toLowerCase());
322
344
  if (typeof fn !== "function") throw new Error(`Less @plugin function "${functionName}" is not registered.`);
@@ -355,7 +377,7 @@ const handleRequest = async (req) => {
355
377
  return;
356
378
  }
357
379
  if (req.type === "loadLessPlugin") {
358
- const functions = await loadLessPlugin(req.modulePath);
380
+ const functions = await loadLessPlugin(req.modulePath, req.options ?? null);
359
381
  send({
360
382
  id: req.id,
361
383
  ok: true,
@@ -364,7 +386,7 @@ const handleRequest = async (req) => {
364
386
  return;
365
387
  }
366
388
  if (req.type === "invokeLessPluginFunction") {
367
- const value = await invokeLessPluginFunction(req.modulePath, req.functionName, req.args ?? []);
389
+ const value = await invokeLessPluginFunction(req.modulePath, req.functionName, req.args ?? [], req.options ?? null);
368
390
  send({
369
391
  id: req.id,
370
392
  ok: true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jesscss/plugin-js",
3
- "version": "2.0.0-alpha.8",
3
+ "version": "2.0.0-alpha.9",
4
4
  "description": "Jess plugin for JavaScript/TypeScript module imports with Deno runtime checks",
5
5
  "main": "lib/index.cjs",
6
6
  "types": "lib/index.d.ts",
@@ -18,7 +18,7 @@
18
18
  ],
19
19
  "dependencies": {
20
20
  "deno": "^2.6.8",
21
- "@jesscss/core": "2.0.0-alpha.8"
21
+ "@jesscss/core": "2.0.0-alpha.9"
22
22
  },
23
23
  "devDependencies": {
24
24
  "@types/node": "^20.0.0",