@jesscss/plugin-js 2.0.0-alpha.6 → 2.0.0-alpha.8
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 +48 -3
- package/lib/bridge.d.ts +51 -0
- package/lib/bridge.d.ts.map +1 -0
- package/lib/index.cjs +191 -8
- package/lib/index.d.ts +24 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +191 -10
- package/lib/runtime-worker.cjs +412 -0
- package/lib/runtime-worker.js +411 -0
- package/package.json +3 -4
package/README.md
CHANGED
|
@@ -1,6 +1,51 @@
|
|
|
1
1
|
# @jesscss/plugin-js
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Import bridge for JavaScript/TypeScript modules in stylesheets — a seed of the
|
|
4
|
+
JavaScript-execution / CSS-in-JS story.**
|
|
4
5
|
|
|
5
|
-
This
|
|
6
|
-
|
|
6
|
+
This plugin lets a stylesheet pull in JavaScript/TypeScript modules
|
|
7
|
+
(`.js`, `.mjs`, `.cjs`, `.ts`, `.mts`, `.cts`) — the mechanism behind `@use` /
|
|
8
|
+
`@-from` script imports and legacy Less `@plugin` loading. When installed, it is
|
|
9
|
+
auto-loaded by `jess`.
|
|
10
|
+
|
|
11
|
+
## Sandboxed execution
|
|
12
|
+
|
|
13
|
+
`plugin-js` does **not** run untrusted module code in your Node process. Before
|
|
14
|
+
executing anything, it checks for a usable **Deno** runtime (`deno --version`)
|
|
15
|
+
and runs the module in a Deno subprocess behind a permission broker:
|
|
16
|
+
|
|
17
|
+
- **read** is limited to `node_modules` (and an optional `jsReadRoot`),
|
|
18
|
+
- **net** is denied unless you opt in (`allowHttp`, optionally scoped to
|
|
19
|
+
`allowNetHosts`),
|
|
20
|
+
- **env / write / run / ffi / sys** are denied outright.
|
|
21
|
+
|
|
22
|
+
Values cross the boundary through a small typed bridge (dimensions, colors,
|
|
23
|
+
quoted strings, lists, detached rules, …). Built-in `@jesscss/fns` modules are
|
|
24
|
+
trusted and imported directly, without the worker. If no Deno binary is found,
|
|
25
|
+
the plugin fails with a clear message instead of falling back to unsandboxed
|
|
26
|
+
execution.
|
|
27
|
+
|
|
28
|
+
## Why it exists — the convergence angle
|
|
29
|
+
|
|
30
|
+
One of the four tools [Jess](https://github.com/jesscss/jess) aims to converge is
|
|
31
|
+
**CSS-in-JS**: running real JavaScript inside stylesheets so styles can be
|
|
32
|
+
dynamic without leaving CSS files. This plugin — together with
|
|
33
|
+
[`@jesscss/plugin-node-modules`](../jess-plugin-node-modules), which resolves the
|
|
34
|
+
packages — is a seed of that story.
|
|
35
|
+
|
|
36
|
+
That convergence is **roadmap — being proven through the alpha, not claimed as
|
|
37
|
+
done.** Legacy Less `@plugin` is supported for compatibility but deprecated in
|
|
38
|
+
favor of `@-from` / `@-use`.
|
|
39
|
+
|
|
40
|
+
## Status
|
|
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.
|
|
47
|
+
|
|
48
|
+
- Project overview & positioning: <https://github.com/jesscss/jess#readme>
|
|
49
|
+
- Docs: <https://jesscss.github.io/> (currently pre-alpha content)
|
|
50
|
+
- Issues: <https://github.com/jesscss/jess/issues>
|
|
51
|
+
- License: MIT
|
package/lib/bridge.d.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export type JsBridgeDeclaration = {
|
|
2
|
+
name: string;
|
|
3
|
+
value: JsBridgeValue;
|
|
4
|
+
};
|
|
5
|
+
export type JsBridgeValue = {
|
|
6
|
+
__jessBridge: true;
|
|
7
|
+
kind: 'scalar';
|
|
8
|
+
value: string | number | boolean;
|
|
9
|
+
} | {
|
|
10
|
+
__jessBridge: true;
|
|
11
|
+
kind: 'dimension';
|
|
12
|
+
value: number;
|
|
13
|
+
unit?: string;
|
|
14
|
+
} | {
|
|
15
|
+
__jessBridge: true;
|
|
16
|
+
kind: 'color';
|
|
17
|
+
rgb: [number, number, number];
|
|
18
|
+
alpha?: number;
|
|
19
|
+
format?: string;
|
|
20
|
+
} | {
|
|
21
|
+
__jessBridge: true;
|
|
22
|
+
kind: 'quoted';
|
|
23
|
+
value: string;
|
|
24
|
+
quote?: '"' | '\'';
|
|
25
|
+
escaped?: boolean;
|
|
26
|
+
} | {
|
|
27
|
+
__jessBridge: true;
|
|
28
|
+
kind: 'keyword';
|
|
29
|
+
value: string;
|
|
30
|
+
} | {
|
|
31
|
+
__jessBridge: true;
|
|
32
|
+
kind: 'anonymous';
|
|
33
|
+
value: string;
|
|
34
|
+
} | {
|
|
35
|
+
__jessBridge: true;
|
|
36
|
+
kind: 'list';
|
|
37
|
+
items: JsBridgeValue[];
|
|
38
|
+
separator?: ',' | ';' | '/';
|
|
39
|
+
} | {
|
|
40
|
+
__jessBridge: true;
|
|
41
|
+
kind: 'sequence';
|
|
42
|
+
items: JsBridgeValue[];
|
|
43
|
+
} | {
|
|
44
|
+
__jessBridge: true;
|
|
45
|
+
kind: 'detached';
|
|
46
|
+
rules: JsBridgeDeclaration[];
|
|
47
|
+
};
|
|
48
|
+
export declare function encodeBridgeValue(value: unknown): unknown;
|
|
49
|
+
export declare function decodeBridgeValue(value: unknown): unknown;
|
|
50
|
+
export declare function encodeBridgeArgs(args: unknown[]): unknown[];
|
|
51
|
+
//# sourceMappingURL=bridge.d.ts.map
|
|
@@ -0,0 +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"}
|
package/lib/index.cjs
CHANGED
|
@@ -33,6 +33,99 @@ 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
|
+
//#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
|
+
};
|
|
43
|
+
function encodeBridgeChildValue(value) {
|
|
44
|
+
const encoded = encodeBridgeValue(value);
|
|
45
|
+
if (isBridgeValue(encoded)) return encoded;
|
|
46
|
+
return {
|
|
47
|
+
__jessBridge: true,
|
|
48
|
+
kind: "scalar",
|
|
49
|
+
value: typeof encoded === "string" || typeof encoded === "number" || typeof encoded === "boolean" ? encoded : String(encoded)
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
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 {
|
|
60
|
+
__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]
|
|
65
|
+
};
|
|
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 {
|
|
96
|
+
__jessBridge: true,
|
|
97
|
+
kind: "detached",
|
|
98
|
+
rules
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
return value;
|
|
102
|
+
}
|
|
103
|
+
function decodeBridgeValue(value) {
|
|
104
|
+
if (!isBridgeValue(value)) return value;
|
|
105
|
+
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)));
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
function encodeBridgeArgs(args) {
|
|
126
|
+
return args.map(encodeBridgeValue);
|
|
127
|
+
}
|
|
128
|
+
//#endregion
|
|
36
129
|
//#region src/index.ts
|
|
37
130
|
const SCRIPT_EXTENSIONS = new Set([
|
|
38
131
|
".js",
|
|
@@ -51,6 +144,33 @@ const RUNTIME_MISSING_MESSAGE = [
|
|
|
51
144
|
const BOOT_TIMEOUT_MS = 8e3;
|
|
52
145
|
const REQUEST_TIMEOUT_MS = 1e4;
|
|
53
146
|
const IDLE_SHUTDOWN_MS = 5e3;
|
|
147
|
+
/**
|
|
148
|
+
* Environment variables that inject a Node.js debugger/inspector bootloader
|
|
149
|
+
* into child processes (VS Code / Cursor "Auto Attach", `node --inspect`, etc.).
|
|
150
|
+
*
|
|
151
|
+
* These must never leak into the sandboxed Deno worker: the injected bootloader
|
|
152
|
+
* tries to read the environment, the Jess Deno sandbox denies `env` permission,
|
|
153
|
+
* and the worker never signals ready — producing a spurious
|
|
154
|
+
* "Timed out waiting for Deno worker startup" failure. Deno does not use any of
|
|
155
|
+
* these vars, so stripping them is safe.
|
|
156
|
+
*/
|
|
157
|
+
const DEBUG_ENV_KEY_RE = /^(NODE_OPTIONS|NODE_INSPECT|VSCODE_INSPECTOR_OPTIONS)/i;
|
|
158
|
+
const DEBUG_ENV_VALUE_RE = /js-debug|bootloader/i;
|
|
159
|
+
/**
|
|
160
|
+
* Returns a copy of `env` with Node debugger/inspector-attach variables removed,
|
|
161
|
+
* so a spawned Deno subprocess starts regardless of the parent's debug env.
|
|
162
|
+
* The Deno permission sandbox is untouched; this only stops leaking the debugger
|
|
163
|
+
* into it.
|
|
164
|
+
*/
|
|
165
|
+
const sanitizeSpawnEnv = (env = process.env) => {
|
|
166
|
+
const clean = {};
|
|
167
|
+
for (const [key, value] of Object.entries(env)) {
|
|
168
|
+
if (DEBUG_ENV_KEY_RE.test(key)) continue;
|
|
169
|
+
if (typeof value === "string" && DEBUG_ENV_VALUE_RE.test(value)) continue;
|
|
170
|
+
clean[key] = value;
|
|
171
|
+
}
|
|
172
|
+
return clean;
|
|
173
|
+
};
|
|
54
174
|
const isPathInside = (candidatePath, rootPath) => {
|
|
55
175
|
const rel = node_path.relative(rootPath, candidatePath);
|
|
56
176
|
return rel === "" || !rel.startsWith("..") && !node_path.isAbsolute(rel);
|
|
@@ -90,6 +210,7 @@ var JsPlugin = class JsPlugin extends _jesscss_core.AbstractPlugin {
|
|
|
90
210
|
name = "js";
|
|
91
211
|
supportedExtensions = Array.from(SCRIPT_EXTENSIONS);
|
|
92
212
|
runtimeState = { status: "idle" };
|
|
213
|
+
shuttingDown = false;
|
|
93
214
|
brokerServer;
|
|
94
215
|
brokerSocketPath;
|
|
95
216
|
worker;
|
|
@@ -133,7 +254,10 @@ var JsPlugin = class JsPlugin extends _jesscss_core.AbstractPlugin {
|
|
|
133
254
|
this.idleTimer.unref?.();
|
|
134
255
|
}
|
|
135
256
|
ensureRuntimeAvailable() {
|
|
136
|
-
if ((0, node_child_process.spawnSync)(this.opts.denoCommand ?? "deno", ["--version"], {
|
|
257
|
+
if ((0, node_child_process.spawnSync)(this.opts.denoCommand ?? "deno", ["--version"], {
|
|
258
|
+
stdio: "ignore",
|
|
259
|
+
env: sanitizeSpawnEnv(process.env)
|
|
260
|
+
}).status !== 0) throw new Error(RUNTIME_MISSING_MESSAGE);
|
|
137
261
|
}
|
|
138
262
|
ensureRuntime() {
|
|
139
263
|
if (this.runtimeState.status === "ready") {
|
|
@@ -142,6 +266,7 @@ var JsPlugin = class JsPlugin extends _jesscss_core.AbstractPlugin {
|
|
|
142
266
|
}
|
|
143
267
|
if (this.runtimeState.status === "initializing") return this.runtimeState.promise;
|
|
144
268
|
if (this.runtimeState.status === "failed") return Promise.reject(this.runtimeState.error);
|
|
269
|
+
if (this.runtimeState.status === "disposed") return Promise.reject(/* @__PURE__ */ new Error("Deno worker has been disposed."));
|
|
145
270
|
const promise = this.startRuntime().then(() => {
|
|
146
271
|
this.runtimeState = { status: "ready" };
|
|
147
272
|
this.scheduleIdleShutdown();
|
|
@@ -206,6 +331,7 @@ var JsPlugin = class JsPlugin extends _jesscss_core.AbstractPlugin {
|
|
|
206
331
|
const socketPath = this.createBrokerPath();
|
|
207
332
|
if (process.platform !== "win32" && node_fs.existsSync(socketPath)) node_fs.unlinkSync(socketPath);
|
|
208
333
|
const server = node_net.createServer((socket) => {
|
|
334
|
+
socket.unref();
|
|
209
335
|
let buf = "";
|
|
210
336
|
socket.setEncoding("utf8");
|
|
211
337
|
socket.on("data", (chunk) => {
|
|
@@ -236,6 +362,7 @@ var JsPlugin = class JsPlugin extends _jesscss_core.AbstractPlugin {
|
|
|
236
362
|
server.once("error", reject);
|
|
237
363
|
server.listen(socketPath, () => resolve());
|
|
238
364
|
});
|
|
365
|
+
server.unref();
|
|
239
366
|
this.brokerServer = server;
|
|
240
367
|
this.brokerSocketPath = socketPath;
|
|
241
368
|
return socketPath;
|
|
@@ -248,7 +375,8 @@ var JsPlugin = class JsPlugin extends _jesscss_core.AbstractPlugin {
|
|
|
248
375
|
const child = (0, node_child_process.spawn)(denoCommand, [
|
|
249
376
|
"run",
|
|
250
377
|
"--no-prompt",
|
|
251
|
-
node_fs.existsSync(compiledWorkerPath) ? compiledWorkerPath : sourceWorkerPath
|
|
378
|
+
node_fs.existsSync(compiledWorkerPath) ? compiledWorkerPath : sourceWorkerPath,
|
|
379
|
+
`--runtime-api=${this.opts.runtimeApi ?? "module"}`
|
|
252
380
|
], {
|
|
253
381
|
stdio: [
|
|
254
382
|
"pipe",
|
|
@@ -256,15 +384,24 @@ var JsPlugin = class JsPlugin extends _jesscss_core.AbstractPlugin {
|
|
|
256
384
|
"pipe"
|
|
257
385
|
],
|
|
258
386
|
env: {
|
|
259
|
-
...process.env,
|
|
387
|
+
...sanitizeSpawnEnv(process.env),
|
|
260
388
|
DENO_PERMISSION_BROKER_PATH: socketPath
|
|
261
389
|
}
|
|
262
390
|
});
|
|
263
391
|
this.worker = child;
|
|
392
|
+
child.unref();
|
|
393
|
+
child.stdin.unref?.();
|
|
394
|
+
child.stdout.unref?.();
|
|
395
|
+
child.stderr.unref?.();
|
|
264
396
|
child.stdout.setEncoding("utf8");
|
|
265
397
|
child.stderr.setEncoding("utf8");
|
|
266
398
|
child.stdout.on("data", (chunk) => this.onWorkerStdout(chunk));
|
|
267
399
|
child.on("exit", () => {
|
|
400
|
+
this.worker = void 0;
|
|
401
|
+
if (this.shuttingDown) {
|
|
402
|
+
this.shuttingDown = false;
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
268
405
|
const err = /* @__PURE__ */ new Error("Deno worker exited unexpectedly.");
|
|
269
406
|
this.rejectAllPending(err);
|
|
270
407
|
if (this.runtimeState.status !== "failed") this.runtimeState = {
|
|
@@ -273,9 +410,13 @@ var JsPlugin = class JsPlugin extends _jesscss_core.AbstractPlugin {
|
|
|
273
410
|
};
|
|
274
411
|
});
|
|
275
412
|
return new Promise((resolve, reject) => {
|
|
413
|
+
let stderrText = "";
|
|
276
414
|
const timer = setTimeout(() => {
|
|
277
|
-
reject(/* @__PURE__ */ new Error("Timed out waiting for Deno worker startup."));
|
|
415
|
+
reject(/* @__PURE__ */ new Error(stderrText.trim() ? `Timed out waiting for Deno worker startup.\n${stderrText.trim()}` : "Timed out waiting for Deno worker startup."));
|
|
278
416
|
}, BOOT_TIMEOUT_MS);
|
|
417
|
+
const onStderr = (chunk) => {
|
|
418
|
+
stderrText += chunk;
|
|
419
|
+
};
|
|
279
420
|
const onData = (chunk) => {
|
|
280
421
|
this.workerBuffer += chunk;
|
|
281
422
|
let idx = this.workerBuffer.indexOf("\n");
|
|
@@ -288,6 +429,7 @@ var JsPlugin = class JsPlugin extends _jesscss_core.AbstractPlugin {
|
|
|
288
429
|
if (JSON.parse(line).type === "ready") {
|
|
289
430
|
clearTimeout(timer);
|
|
290
431
|
child.stdout.off("data", onData);
|
|
432
|
+
child.stderr.off("data", onStderr);
|
|
291
433
|
resolve();
|
|
292
434
|
return;
|
|
293
435
|
}
|
|
@@ -295,9 +437,11 @@ var JsPlugin = class JsPlugin extends _jesscss_core.AbstractPlugin {
|
|
|
295
437
|
}
|
|
296
438
|
};
|
|
297
439
|
child.stdout.on("data", onData);
|
|
440
|
+
child.stderr.on("data", onStderr);
|
|
298
441
|
child.once("error", (err) => {
|
|
299
442
|
clearTimeout(timer);
|
|
300
443
|
child.stdout.off("data", onData);
|
|
444
|
+
child.stderr.off("data", onStderr);
|
|
301
445
|
reject(err);
|
|
302
446
|
});
|
|
303
447
|
});
|
|
@@ -366,7 +510,13 @@ var JsPlugin = class JsPlugin extends _jesscss_core.AbstractPlugin {
|
|
|
366
510
|
}
|
|
367
511
|
shutdown() {
|
|
368
512
|
this.clearIdleTimer();
|
|
369
|
-
if (this.worker && !this.worker.killed)
|
|
513
|
+
if (this.worker && !this.worker.killed) {
|
|
514
|
+
this.shuttingDown = true;
|
|
515
|
+
this.worker.stdin.destroy();
|
|
516
|
+
this.worker.stdout.destroy();
|
|
517
|
+
this.worker.stderr.destroy();
|
|
518
|
+
this.worker.kill();
|
|
519
|
+
}
|
|
370
520
|
this.worker = void 0;
|
|
371
521
|
if (this.brokerServer) {
|
|
372
522
|
this.brokerServer.close();
|
|
@@ -379,7 +529,8 @@ var JsPlugin = class JsPlugin extends _jesscss_core.AbstractPlugin {
|
|
|
379
529
|
}
|
|
380
530
|
dispose() {
|
|
381
531
|
this.shutdown();
|
|
382
|
-
this
|
|
532
|
+
JsPlugin.liveInstances.delete(this);
|
|
533
|
+
this.runtimeState = { status: "disposed" };
|
|
383
534
|
}
|
|
384
535
|
assertAllowedPath(absoluteFilePath) {
|
|
385
536
|
const resolvedPath = node_path.resolve(absoluteFilePath);
|
|
@@ -408,10 +559,10 @@ var JsPlugin = class JsPlugin extends _jesscss_core.AbstractPlugin {
|
|
|
408
559
|
type: "invoke",
|
|
409
560
|
modulePath,
|
|
410
561
|
exportName: item.name,
|
|
411
|
-
args
|
|
562
|
+
args: encodeBridgeArgs(args)
|
|
412
563
|
});
|
|
413
564
|
if (!invokeResult.ok) throw new Error(invokeResult.error);
|
|
414
|
-
return invokeResult.value;
|
|
565
|
+
return decodeBridgeValue(invokeResult.value);
|
|
415
566
|
};
|
|
416
567
|
else moduleObject[item.name] = item.value;
|
|
417
568
|
return moduleObject;
|
|
@@ -421,6 +572,36 @@ var JsPlugin = class JsPlugin extends _jesscss_core.AbstractPlugin {
|
|
|
421
572
|
for (const [key, value] of Object.entries(module)) if (typeof value === "function" || isJsonValue(value)) safeModule[key] = value;
|
|
422
573
|
return safeModule;
|
|
423
574
|
}
|
|
575
|
+
/**
|
|
576
|
+
* Loads a legacy Less `@plugin` wrapper file in Deno Less-compat mode.
|
|
577
|
+
*
|
|
578
|
+
* @deprecated Less `@plugin` is deprecated. Prefer `@-from` for
|
|
579
|
+
* ESM-style script imports or `@-use` for Sass-module-style namespace imports.
|
|
580
|
+
*/
|
|
581
|
+
async importLessPlugin(absoluteFilePath) {
|
|
582
|
+
const ext = node_path.extname(absoluteFilePath);
|
|
583
|
+
if (!SCRIPT_EXTENSIONS.has(ext)) throw new Error(`Plugin "${this.name}" cannot import Less plugin "${absoluteFilePath}"`);
|
|
584
|
+
this.assertAllowedPath(absoluteFilePath);
|
|
585
|
+
await this.ensureRuntime();
|
|
586
|
+
const modulePath = node_path.resolve(absoluteFilePath);
|
|
587
|
+
const loadResult = await this.callWorker({
|
|
588
|
+
type: "loadLessPlugin",
|
|
589
|
+
modulePath
|
|
590
|
+
});
|
|
591
|
+
if (!loadResult.ok) throw new Error(loadResult.error);
|
|
592
|
+
const functions = {};
|
|
593
|
+
for (const functionName of loadResult.functions ?? []) functions[functionName] = async (...args) => {
|
|
594
|
+
const invokeResult = await this.callWorker({
|
|
595
|
+
type: "invokeLessPluginFunction",
|
|
596
|
+
modulePath,
|
|
597
|
+
functionName,
|
|
598
|
+
args: encodeBridgeArgs(args)
|
|
599
|
+
});
|
|
600
|
+
if (!invokeResult.ok) throw new Error(invokeResult.error);
|
|
601
|
+
return decodeBridgeValue(invokeResult.value);
|
|
602
|
+
};
|
|
603
|
+
return { functions };
|
|
604
|
+
}
|
|
424
605
|
};
|
|
425
606
|
/**
|
|
426
607
|
* Global flag set when @jesscss/plugin-js is loaded.
|
|
@@ -434,4 +615,6 @@ const jsPlugin = ((opts) => {
|
|
|
434
615
|
//#endregion
|
|
435
616
|
exports.JESS_PLUGIN_JS_GLOBAL = JESS_PLUGIN_JS_GLOBAL;
|
|
436
617
|
exports.JsPlugin = JsPlugin;
|
|
618
|
+
exports.__toESM = __toESM;
|
|
437
619
|
exports.default = jsPlugin;
|
|
620
|
+
exports.sanitizeSpawnEnv = sanitizeSpawnEnv;
|
package/lib/index.d.ts
CHANGED
|
@@ -6,7 +6,21 @@ export type JavaScriptSandboxConfig = {
|
|
|
6
6
|
};
|
|
7
7
|
export interface JsPluginOptions extends JavaScriptSandboxConfig {
|
|
8
8
|
denoCommand?: string;
|
|
9
|
+
/**
|
|
10
|
+
* Runtime API exposed inside the Deno worker.
|
|
11
|
+
*
|
|
12
|
+
* Jess `@-use`/script imports run as plain ESM modules. Legacy Less
|
|
13
|
+
* `@plugin` loading can opt into the Less-compatible global shape.
|
|
14
|
+
*/
|
|
15
|
+
runtimeApi?: 'module' | 'less';
|
|
9
16
|
}
|
|
17
|
+
/**
|
|
18
|
+
* Returns a copy of `env` with Node debugger/inspector-attach variables removed,
|
|
19
|
+
* so a spawned Deno subprocess starts regardless of the parent's debug env.
|
|
20
|
+
* The Deno permission sandbox is untouched; this only stops leaking the debugger
|
|
21
|
+
* into it.
|
|
22
|
+
*/
|
|
23
|
+
export declare const sanitizeSpawnEnv: (env?: NodeJS.ProcessEnv) => NodeJS.ProcessEnv;
|
|
10
24
|
export declare class JsPlugin extends AbstractPlugin {
|
|
11
25
|
opts: JsPluginOptions;
|
|
12
26
|
private static cleanupRegistered;
|
|
@@ -14,6 +28,7 @@ export declare class JsPlugin extends AbstractPlugin {
|
|
|
14
28
|
name: string;
|
|
15
29
|
supportedExtensions: string[];
|
|
16
30
|
private runtimeState;
|
|
31
|
+
private shuttingDown;
|
|
17
32
|
private brokerServer;
|
|
18
33
|
private brokerSocketPath;
|
|
19
34
|
private worker;
|
|
@@ -42,6 +57,15 @@ export declare class JsPlugin extends AbstractPlugin {
|
|
|
42
57
|
dispose(): void;
|
|
43
58
|
private assertAllowedPath;
|
|
44
59
|
import(absoluteFilePath: string): Promise<Record<string, any>>;
|
|
60
|
+
/**
|
|
61
|
+
* Loads a legacy Less `@plugin` wrapper file in Deno Less-compat mode.
|
|
62
|
+
*
|
|
63
|
+
* @deprecated Less `@plugin` is deprecated. Prefer `@-from` for
|
|
64
|
+
* ESM-style script imports or `@-use` for Sass-module-style namespace imports.
|
|
65
|
+
*/
|
|
66
|
+
importLessPlugin(absoluteFilePath: string): Promise<{
|
|
67
|
+
functions: Record<string, (...args: unknown[]) => Promise<unknown>>;
|
|
68
|
+
}>;
|
|
45
69
|
}
|
|
46
70
|
/**
|
|
47
71
|
* Global flag set when @jesscss/plugin-js is loaded.
|
package/lib/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,cAAc,EACf,MAAM,eAAe,CAAC;
|
|
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"}
|