@nubjs/nub-win32-x64 0.0.17 → 0.0.19

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/bin/nub.exe CHANGED
Binary file
package/bin/nubx.exe ADDED
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nubjs/nub-win32-x64",
3
- "version": "0.0.17",
3
+ "version": "0.0.19",
4
4
  "description": "Nub binary for win32-x64",
5
5
  "license": "MIT",
6
6
  "repository": "https://github.com/nubjs/nub",
Binary file
@@ -0,0 +1,85 @@
1
+ // nubx under Yarn PnP — resolve a bin name to its script and RUN it, the way
2
+ // `yarn exec` does. nub runs this file as its (augmented) entry, having injected
3
+ // `--require .pnp.cjs`, so `findPnpApi` is set up and the zipfs `fs` patch is live.
4
+ //
5
+ // Invoked as: nub <this> <binName> [args...]
6
+ //
7
+ // Two things matter:
8
+ // 1. PnP has no `node_modules/.bin`, so we find the bin by walking the top-level
9
+ // package's dependencies and matching `package.json#bin` (what Yarn's own bin
10
+ // registry does), via the public pnpapi.
11
+ // 2. We load the resolved script with `require()` — the CJS path, where PnP's `fs`
12
+ // patch reads zip-stored packages. Running it as a node *entry* instead would,
13
+ // on the compat tier (Node <22.15, where nub augments via an `--import` ESM
14
+ // preload), route the entry through the ESM loader whose existence check
15
+ // bypasses PnP's `fs` patch and throws ERR_MODULE_NOT_FOUND on the zip path.
16
+ // `require()` (with a dynamic-import fallback for an ESM bin) sidesteps that and
17
+ // keeps nub's augmentation active in-process — matching `yarn exec` on every
18
+ // supported Node.
19
+ const path = require("node:path");
20
+ const fs = require("node:fs");
21
+ const { pathToFileURL } = require("node:url");
22
+ const { cwdIssuer } = require("./pnp-util.cjs");
23
+
24
+ const want = process.argv[2];
25
+ const rest = process.argv.slice(3);
26
+
27
+ // `require("pnpapi")` throws for an out-of-tree issuer (this file lives in nub's
28
+ // install dir); `findPnpApi` resolves by the queried path, so it works here.
29
+ const api = require("node:module").findPnpApi(cwdIssuer());
30
+ if (!api) {
31
+ process.stderr.write("nubx: not a Yarn PnP project\n");
32
+ process.exit(127);
33
+ }
34
+
35
+ // bin name -> relative script path. A string `bin` is named after the package
36
+ // (its unscoped tail); an object maps explicit names.
37
+ function binsOf(pkg) {
38
+ const b = pkg.bin;
39
+ if (!b) return {};
40
+ if (typeof b === "string") return { [(pkg.name || "").split("/").pop()]: b };
41
+ return b;
42
+ }
43
+
44
+ let script = null;
45
+ const top = api.getPackageInformation(api.topLevel);
46
+ for (const [name, reference] of top.packageDependencies) {
47
+ if (reference == null) continue;
48
+ const info = api.getPackageInformation(api.getLocator(name, reference));
49
+ if (!info) continue;
50
+ let pkg;
51
+ try {
52
+ pkg = JSON.parse(fs.readFileSync(path.join(info.packageLocation, "package.json"), "utf8"));
53
+ } catch {
54
+ continue;
55
+ }
56
+ const rel = binsOf(pkg)[want];
57
+ if (rel) {
58
+ script = path.join(info.packageLocation, rel);
59
+ break;
60
+ }
61
+ }
62
+
63
+ if (!script) {
64
+ process.stderr.write(
65
+ `nubx: '${want}' not found in Yarn PnP dependencies.\n` +
66
+ ` add it (yarn add ${want}), or run it ad-hoc with: yarn dlx ${want}\n`,
67
+ );
68
+ process.exit(127);
69
+ }
70
+
71
+ // Run the bin as if it were the entry: present its own argv, then load it via the
72
+ // zip-safe CJS path. Fall back to dynamic import for an ESM bin.
73
+ process.argv = [process.argv[0], script, ...rest];
74
+ try {
75
+ require(script);
76
+ } catch (e) {
77
+ if (e && e.code === "ERR_REQUIRE_ESM") {
78
+ import(pathToFileURL(script).href).catch((err) => {
79
+ console.error(err);
80
+ process.exit(1);
81
+ });
82
+ } else {
83
+ throw e;
84
+ }
85
+ }
@@ -0,0 +1,51 @@
1
+ // Shared Yarn PnP ESM-resolution helper, used by BOTH realms that resolve modules:
2
+ // the main-thread preload (preload-common.cjs, fast tier) and the compat-tier
3
+ // loader worker (preload-async-hooks.mjs). Both resolve PnP specifiers identically
4
+ // — through PnP's public, conditions-free `resolveRequest` — and both must hand
5
+ // Node an explicit module `format`, so the logic lives here once rather than being
6
+ // copy-pasted across two threads where it could drift.
7
+ //
8
+ // Why the explicit format: without it, Node <= 20.11 mis-detects a zip-stored `.js`
9
+ // file from a `"type":"module"` package as CommonJS, routes it through the CJS
10
+ // translator, and `require()`s the ESM source -> ERR_REQUIRE_ESM. Newer Node gets
11
+ // it right on its own; emitting the format makes PnP ESM deps work down to the
12
+ // 18.19 floor. (CJS resolution stays in preload-common.cjs's `_resolveFilename`
13
+ // branch — it returns a path, not a hook result, and is main-thread-only.)
14
+ const { readFileSync } = require("node:fs");
15
+ const { join, sep } = require("node:path");
16
+ const { fileURLToPath, pathToFileURL } = require("node:url");
17
+
18
+ // A directory issuer for pnpapi.resolveRequest: cwd with a trailing separator so
19
+ // PnP treats it as a directory. `path.sep` (not a literal "/") keeps it correct on
20
+ // Windows, where cwd uses backslashes and a mixed "C:\x/" can confuse resolution.
21
+ const cwdIssuer = () => process.cwd() + sep;
22
+
23
+ // Module format of a PnP-resolved file. `.mjs`/`.cjs` are unambiguous; a `.js` file
24
+ // inherits its package's `type` (read via PnP — `fs` is zip-patched in both realms).
25
+ // `null` lets Node decide (non-JS, or detection failed).
26
+ function pnpFormat(pnp, resolvedPath) {
27
+ if (resolvedPath.endsWith(".mjs")) return "module";
28
+ if (resolvedPath.endsWith(".cjs")) return "commonjs";
29
+ if (!resolvedPath.endsWith(".js")) return null;
30
+ try {
31
+ const info = pnp.getPackageInformation(pnp.findPackageLocator(resolvedPath));
32
+ const pj = JSON.parse(readFileSync(join(info.packageLocation, "package.json"), "utf8"));
33
+ return pj.type === "module" ? "module" : "commonjs";
34
+ } catch {
35
+ return null;
36
+ }
37
+ }
38
+
39
+ // Resolve an ESM `specifier` (from `parentURL`) through PnP and return a Node
40
+ // resolve-hook result `{ url, format?, shortCircuit }`, or `null` if PnP can't
41
+ // resolve it (then the caller delegates to Node's default resolver). Throwing is
42
+ // the caller's signal to fall through too — callers wrap in try/catch.
43
+ function pnpResolveEsm(pnp, specifier, parentURL) {
44
+ const issuer = parentURL ? fileURLToPath(parentURL) : cwdIssuer();
45
+ const resolved = pnp.resolveRequest(specifier, issuer);
46
+ if (!resolved) return null;
47
+ const format = pnpFormat(pnp, resolved);
48
+ return { url: pathToFileURL(resolved).href, shortCircuit: true, ...(format && { format }) };
49
+ }
50
+
51
+ module.exports = { pnpResolveEsm, cwdIssuer };
@@ -21,6 +21,34 @@ import {
21
21
  TRANSPILE_EXTS, DATA_EXTS,
22
22
  extname, resolveSpec, loadTranspile, loadData,
23
23
  } from "./transform-core.mjs";
24
+ import { createRequire, isBuiltin } from "node:module";
25
+ import { existsSync } from "node:fs";
26
+ import { join, dirname } from "node:path";
27
+ import { pnpResolveEsm } from "./pnp-util.cjs";
28
+
29
+ // Yarn PnP handle for this loader worker. The worker runs in its own thread where
30
+ // `.pnp.cjs` was never --require'd, so neither the `pnpapi` builtin nor
31
+ // `module.findPnpApi` is installed here (the main-thread preload uses findPnpApi; it
32
+ // can't reach across to this realm). So bootstrap PnP for this thread directly: walk
33
+ // up from cwd to the `.pnp.cjs` Rust located and require it by absolute path — that
34
+ // returns the pnpapi object. nub then resolves PnP specifiers via
35
+ // `pnpapi.resolveRequest` (its public, conditions-free resolver), mirroring the main
36
+ // thread, so there is no need to register Yarn's `.pnp.loader.mjs` (which deadlocks
37
+ // against the fast tier's `module.registerHooks`). `null` when not a PnP run.
38
+ const __pnp = (() => {
39
+ if (!process.versions.pnp) return null;
40
+ const req = createRequire(import.meta.url);
41
+ try {
42
+ let dir = process.cwd();
43
+ for (;;) {
44
+ const candidate = join(dir, ".pnp.cjs");
45
+ if (existsSync(candidate)) return req(candidate);
46
+ const parent = dirname(dir);
47
+ if (parent === dir) return null;
48
+ dir = parent;
49
+ }
50
+ } catch { return null; }
51
+ })();
24
52
 
25
53
  // Node calls this once per worker when the main thread invokes
26
54
  // `module.register(url, parentURL, { data })`. We accept and ignore the payload
@@ -31,7 +59,16 @@ export async function initialize(_data) {}
31
59
  // ── Resolve hook ────────────────────────────────────────────────────
32
60
  export async function resolve(specifier, context, nextResolve) {
33
61
  const r = resolveSpec(specifier, context.parentURL);
34
- return r ?? nextResolve(specifier, context);
62
+ if (r) return r;
63
+ // Yarn PnP: resolve deps through PnP's own resolver — identical to the fast tier,
64
+ // via the shared helper (resolveRequest + module-format detection).
65
+ if (__pnp && !isBuiltin(specifier) && !specifier.startsWith("node:")) {
66
+ try {
67
+ const res = pnpResolveEsm(__pnp, specifier, context.parentURL);
68
+ if (res) return res;
69
+ } catch { /* fall through to Node's resolver */ }
70
+ }
71
+ return nextResolve(specifier, context);
35
72
  }
36
73
 
37
74
  // ── Load hook ───────────────────────────────────────────────────────
@@ -20,6 +20,44 @@ const { readdirSync } = require("node:fs");
20
20
  const { fileURLToPath, pathToFileURL } = require("node:url");
21
21
  const { join, dirname, extname: pathExtname } = require("node:path");
22
22
 
23
+ // Yarn PnP API handle, fetched lazily via Node's `module.findPnpApi`. `.pnp.cjs`
24
+ // (injected by the Rust spawn layer via --require, ahead of nub's preload) sets
25
+ // `process.versions.pnp` and installs `findPnpApi`, which returns the pnpapi object
26
+ // governing a given path. Unlike a bare `require("pnpapi")` — which throws here,
27
+ // since this preload lives in nub's install dir, OUTSIDE the user's PnP tree —
28
+ // `findPnpApi` resolves by the queried path, so an out-of-tree issuer works. Being
29
+ // a plain query it never re-enters nub's resolve hooks, so there is no ordering
30
+ // constraint with `module.registerHooks` (the reason the previous abs-path require
31
+ // was load-bearing-fragile). nub resolves PnP specifiers through
32
+ // `pnpapi.resolveRequest` (its public, conditions-free resolver) in both the
33
+ // registerHooks resolve hook and the `_resolveFilename` override below. No env var
34
+ // (brand boundary); `null` when this is not a PnP run.
35
+ let __pnpApi;
36
+ function pnpApi() {
37
+ if (__pnpApi) return __pnpApi; // cache only a SUCCESSFUL lookup (see below)
38
+ if (!process.versions.pnp) return null;
39
+ // `findPnpApi` matches by the queried path. A single synthesized `cwd + sep` anchor
40
+ // can miss on Windows (drive-letter casing, 8.3 short paths, trailing separator),
41
+ // and a transient early miss must NOT be cached sticky — otherwise every later
42
+ // resolution falls through to PnP's `_resolveFilename`, which rejects the
43
+ // `conditions` option Node injects under a registered hook (the intermittent
44
+ // Windows `conditions` crash). So try several real in-tree anchors and cache only
45
+ // on success: `argv[1]` is the user's entry file (in-tree for `nub <file>`); cwd
46
+ // covers `nub run` / `nub exec`.
47
+ for (const anchor of [process.argv[1], cwdIssuer(), process.cwd()]) {
48
+ if (!anchor) continue;
49
+ try {
50
+ const api = module_.findPnpApi(anchor);
51
+ if (api) return (__pnpApi = api);
52
+ } catch {}
53
+ }
54
+ return null;
55
+ }
56
+
57
+ // Shared PnP ESM resolution (resolveRequest + format) + the directory-issuer
58
+ // helper, identical to the compat worker's — see runtime/pnp-util.cjs.
59
+ const { pnpResolveEsm, cwdIssuer } = require("./pnp-util.cjs");
60
+
23
61
  // ── Watch-mode dependency reporting (main thread only) ──────────────
24
62
  // Under `nub watch`, Node's FilesWatcher only watches files in the import graph;
25
63
  // config files (tsconfig.json, package.json) and `.env*` are NOT in any graph, so
@@ -103,7 +141,24 @@ function makeHooks(core, watchReporting) {
103
141
 
104
142
  function resolve(specifier, context, nextResolve) {
105
143
  const r = core.resolveSpec(specifier, context.parentURL);
106
- return r ?? nextResolve(specifier, context);
144
+ if (r) return r;
145
+ // Under Yarn PnP, delegating to `nextResolve` flows into Node's default
146
+ // resolution, which calls PnP's patched `_resolveFilename` WITH a `conditions`
147
+ // option PnP rejects once a customization hook is registered ("aren't supported
148
+ // by PnP yet (conditions)"). PnP exposes its own conditions-free resolver —
149
+ // `pnpapi.resolveRequest` — so resolve through THAT and hand Node the file URL.
150
+ // Still "PnP does the resolution" (its public API); nub adds none of its own.
151
+ // Returns a virtual `.zip` path for zip-stored deps, which Node reads via the
152
+ // zipfs patch `.pnp.cjs` installed. Falls back to `nextResolve` if PnP can't
153
+ // resolve (e.g. a builtin or a relative path PnP defers to Node).
154
+ const pnp = pnpApi();
155
+ if (pnp && !module_.isBuiltin(specifier) && !specifier.startsWith("node:")) {
156
+ try {
157
+ const res = pnpResolveEsm(pnp, specifier, context && context.parentURL);
158
+ if (res) return res;
159
+ } catch { /* fall through to Node's resolver */ }
160
+ }
161
+ return nextResolve(specifier, context);
107
162
  }
108
163
 
109
164
  function load(url, context, nextLoad) {
@@ -247,6 +302,45 @@ function installCjsRequireHooks(core, withClassicTranspile) {
247
302
  }
248
303
  return resolved;
249
304
  }
305
+ // Yarn PnP, FAST TIER ONLY. On the fast tier nub registers a sync
306
+ // `module.registerHooks` resolve hook, which makes Node thread a `conditions`
307
+ // option into PnP's patched `_resolveFilename` ("aren't supported by PnP yet
308
+ // (conditions)") AND re-inject it even if we pass none — so we must resolve
309
+ // everything ourselves via PnP's conditions-free `pnpapi.resolveRequest` and the
310
+ // native path primitives, never falling through to PnP's `_resolveFilename`. On
311
+ // the COMPAT tier there is no sync hook, so PnP's own `_resolveFilename` resolves
312
+ // CJS deps fine (no conditions option) and interposing here instead recurses to
313
+ // OOM on the Node 18 line — so the PnP branch is gated to where registerHooks
314
+ // exists. `pnpApi()` is null off-PnP, making this doubly safe.
315
+ // On a PnP tree + fast tier (sync `registerHooks` registered), resolve through
316
+ // PnP's conditions-free `resolveRequest` and NEVER fall through to
317
+ // `origResolveFilename` — that is PnP's patched `_resolveFilename`, which rejects
318
+ // the `conditions` option Node injects under a registered hook. The gate is
319
+ // `process.versions.pnp` (set the moment `.pnp.cjs` runs), NOT a non-null api:
320
+ // if `findPnpApi` momentarily misses we still avoid PnP's resolver and use Node's
321
+ // native `_findPath`, so a transient lookup miss can't leak a `conditions` crash.
322
+ const inPnp = typeof module_.registerHooks === "function" && !!process.versions.pnp;
323
+ if (inPnp) {
324
+ if (module_.isBuiltin(request)) return request; // PnP defers builtins to Node
325
+ const pnp = pnpApi();
326
+ if (pnp) {
327
+ try {
328
+ const issuer = parent && typeof parent.filename === "string" ? parent.filename : cwdIssuer();
329
+ const r = pnp.resolveRequest(request, issuer);
330
+ if (r) return r;
331
+ } catch { /* fall through to native path resolution below */ }
332
+ }
333
+ // PnP couldn't resolve it (nub's OWN vendored deps, whose issuer is nub's
334
+ // out-of-tree install dir) or the api was momentarily unavailable. Use Node's
335
+ // native path primitives (`_resolveLookupPaths` + `_findPath`), which PnP does
336
+ // NOT patch — never `origResolveFilename` (PnP's, + `conditions`).
337
+ const lookupPaths = module_._resolveLookupPaths(request, parent) || [];
338
+ const found = module_._findPath(request, lookupPaths, isMain);
339
+ if (found) return found;
340
+ const err = new Error(`Cannot find module '${request}'`);
341
+ err.code = "MODULE_NOT_FOUND";
342
+ throw err;
343
+ }
250
344
  return origResolveFilename.call(this, request, parent, isMain, options);
251
345
  };
252
346
 
@@ -114,6 +114,12 @@ if (!requireEsmDisabled) {
114
114
  const { resolve, load } = common.makeHooks(core, watchReporting);
115
115
  module_.registerHooks({ resolve, load });
116
116
  common.installCjsRequireHooks(core, false);
117
+ // NOTE: no Yarn `.pnp.loader.mjs` registration. nub's own `resolve` hook already
118
+ // routes PnP specifiers through `pnpapi.resolveRequest` (see makeHooks /
119
+ // installCjsRequireHooks), covering both `import` and `require` of PnP deps.
120
+ // Registering Yarn's ESM loader ON TOP of the sync `module.registerHooks` hooks
121
+ // deadlocks ESM entry loading (silent exit) — both hook systems intercept ESM
122
+ // resolution. The compat tier (preload-async-hooks.mjs) resolves PnP the same way.
117
123
 
118
124
  // ── Sync polyfills + lazy ESM-side-effect polyfills ───────────────
119
125
  installSyncPolyfills(__preloadedPolyfills);
@@ -65,7 +65,9 @@ if (__isFastTier) {
65
65
  module.registerHooks({ resolve, load });
66
66
  common.installCjsRequireHooks(core, false);
67
67
  } else if (__isCompatTier) {
68
- // Compat path: ESM `import` hooks run in a dedicated loader worker thread.
68
+ // Compat path: ESM `import` hooks run in a dedicated loader worker thread. That
69
+ // worker resolves PnP deps via pnpapi.resolveRequest itself (preload-async-
70
+ // hooks.mjs), so no Yarn `.pnp.loader.mjs` registration is needed here either.
69
71
  module.register("./preload-async-hooks.mjs", import.meta.url);
70
72
  // (The main-thread require() shim's module-format + decorator detection is a
71
73
  // synchronous native addon call now — no parser warm-up; the old
@@ -9,4 +9,4 @@
9
9
  // previously lived as a literal inside preload.mjs, which `make version` patched,
10
10
  // while the worker carried a hand-maintained "…-compat" copy that `make version`
11
11
  // never touched — a latent staleness bug this module closes.)
12
- export const NUB_VERSION = "0.0.17";
12
+ export const NUB_VERSION = "0.0.19";