@jesscss/plugin-js 2.0.0-alpha.7 → 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 CHANGED
@@ -1,6 +1,51 @@
1
1
  # @jesscss/plugin-js
2
2
 
3
- Jess plugin that enables JavaScript/TypeScript module imports.
3
+ **Import bridge for JavaScript/TypeScript modules in stylesheets — a seed of the
4
+ JavaScript-execution / CSS-in-JS story.**
4
5
 
5
- This package checks for a usable Deno runtime before executing non-`@jesscss/fns`
6
- modules. It is intended to be auto-loaded by `jess` when installed.
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/index.cjs CHANGED
@@ -144,6 +144,33 @@ const RUNTIME_MISSING_MESSAGE = [
144
144
  const BOOT_TIMEOUT_MS = 8e3;
145
145
  const REQUEST_TIMEOUT_MS = 1e4;
146
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
+ };
147
174
  const isPathInside = (candidatePath, rootPath) => {
148
175
  const rel = node_path.relative(rootPath, candidatePath);
149
176
  return rel === "" || !rel.startsWith("..") && !node_path.isAbsolute(rel);
@@ -227,7 +254,10 @@ var JsPlugin = class JsPlugin extends _jesscss_core.AbstractPlugin {
227
254
  this.idleTimer.unref?.();
228
255
  }
229
256
  ensureRuntimeAvailable() {
230
- if ((0, node_child_process.spawnSync)(this.opts.denoCommand ?? "deno", ["--version"], { stdio: "ignore" }).status !== 0) throw new Error(RUNTIME_MISSING_MESSAGE);
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);
231
261
  }
232
262
  ensureRuntime() {
233
263
  if (this.runtimeState.status === "ready") {
@@ -354,7 +384,7 @@ var JsPlugin = class JsPlugin extends _jesscss_core.AbstractPlugin {
354
384
  "pipe"
355
385
  ],
356
386
  env: {
357
- ...process.env,
387
+ ...sanitizeSpawnEnv(process.env),
358
388
  DENO_PERMISSION_BROKER_PATH: socketPath
359
389
  }
360
390
  });
@@ -587,3 +617,4 @@ exports.JESS_PLUGIN_JS_GLOBAL = JESS_PLUGIN_JS_GLOBAL;
587
617
  exports.JsPlugin = JsPlugin;
588
618
  exports.__toESM = __toESM;
589
619
  exports.default = jsPlugin;
620
+ exports.sanitizeSpawnEnv = sanitizeSpawnEnv;
package/lib/index.d.ts CHANGED
@@ -14,6 +14,13 @@ export interface JsPluginOptions extends JavaScriptSandboxConfig {
14
14
  */
15
15
  runtimeApi?: 'module' | 'less';
16
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;
17
24
  export declare class JsPlugin extends AbstractPlugin {
18
25
  opts: JsPluginOptions;
19
26
  private static cleanupRegistered;
@@ -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;AA8GD,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;IAU9B,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;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"}
package/lib/index.js CHANGED
@@ -115,6 +115,33 @@ const RUNTIME_MISSING_MESSAGE = [
115
115
  const BOOT_TIMEOUT_MS = 8e3;
116
116
  const REQUEST_TIMEOUT_MS = 1e4;
117
117
  const IDLE_SHUTDOWN_MS = 5e3;
118
+ /**
119
+ * Environment variables that inject a Node.js debugger/inspector bootloader
120
+ * into child processes (VS Code / Cursor "Auto Attach", `node --inspect`, etc.).
121
+ *
122
+ * These must never leak into the sandboxed Deno worker: the injected bootloader
123
+ * tries to read the environment, the Jess Deno sandbox denies `env` permission,
124
+ * and the worker never signals ready — producing a spurious
125
+ * "Timed out waiting for Deno worker startup" failure. Deno does not use any of
126
+ * these vars, so stripping them is safe.
127
+ */
128
+ const DEBUG_ENV_KEY_RE = /^(NODE_OPTIONS|NODE_INSPECT|VSCODE_INSPECTOR_OPTIONS)/i;
129
+ const DEBUG_ENV_VALUE_RE = /js-debug|bootloader/i;
130
+ /**
131
+ * Returns a copy of `env` with Node debugger/inspector-attach variables removed,
132
+ * so a spawned Deno subprocess starts regardless of the parent's debug env.
133
+ * The Deno permission sandbox is untouched; this only stops leaking the debugger
134
+ * into it.
135
+ */
136
+ const sanitizeSpawnEnv = (env = process.env) => {
137
+ const clean = {};
138
+ for (const [key, value] of Object.entries(env)) {
139
+ if (DEBUG_ENV_KEY_RE.test(key)) continue;
140
+ if (typeof value === "string" && DEBUG_ENV_VALUE_RE.test(value)) continue;
141
+ clean[key] = value;
142
+ }
143
+ return clean;
144
+ };
118
145
  const isPathInside = (candidatePath, rootPath) => {
119
146
  const rel = path.relative(rootPath, candidatePath);
120
147
  return rel === "" || !rel.startsWith("..") && !path.isAbsolute(rel);
@@ -198,7 +225,10 @@ var JsPlugin = class JsPlugin extends AbstractPlugin {
198
225
  this.idleTimer.unref?.();
199
226
  }
200
227
  ensureRuntimeAvailable() {
201
- if (spawnSync(this.opts.denoCommand ?? "deno", ["--version"], { stdio: "ignore" }).status !== 0) throw new Error(RUNTIME_MISSING_MESSAGE);
228
+ if (spawnSync(this.opts.denoCommand ?? "deno", ["--version"], {
229
+ stdio: "ignore",
230
+ env: sanitizeSpawnEnv(process.env)
231
+ }).status !== 0) throw new Error(RUNTIME_MISSING_MESSAGE);
202
232
  }
203
233
  ensureRuntime() {
204
234
  if (this.runtimeState.status === "ready") {
@@ -325,7 +355,7 @@ var JsPlugin = class JsPlugin extends AbstractPlugin {
325
355
  "pipe"
326
356
  ],
327
357
  env: {
328
- ...process.env,
358
+ ...sanitizeSpawnEnv(process.env),
329
359
  DENO_PERMISSION_BROKER_PATH: socketPath
330
360
  }
331
361
  });
@@ -554,4 +584,4 @@ const jsPlugin = ((opts) => {
554
584
  return new JsPlugin(opts);
555
585
  });
556
586
  //#endregion
557
- export { JESS_PLUGIN_JS_GLOBAL, JsPlugin, jsPlugin as default };
587
+ export { JESS_PLUGIN_JS_GLOBAL, JsPlugin, jsPlugin as default, sanitizeSpawnEnv };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jesscss/plugin-js",
3
- "version": "2.0.0-alpha.7",
3
+ "version": "2.0.0-alpha.8",
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.7"
21
+ "@jesscss/core": "2.0.0-alpha.8"
22
22
  },
23
23
  "devDependencies": {
24
24
  "@types/node": "^20.0.0",