@gjsify/cli 0.26.1 → 0.27.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/dist/cli.gjs.mjs +188 -183
  2. package/lib/bundler-pick.js +12 -4
  3. package/lib/cli-app.js +3 -1
  4. package/lib/commands/affected.js +1 -1
  5. package/lib/commands/build.js +21 -14
  6. package/lib/commands/check.js +40 -4
  7. package/lib/commands/clear.d.ts +8 -0
  8. package/lib/commands/clear.js +90 -0
  9. package/lib/commands/copy.d.ts +8 -0
  10. package/lib/commands/copy.js +87 -0
  11. package/lib/commands/dlx.js +8 -1
  12. package/lib/commands/index.d.ts +2 -0
  13. package/lib/commands/index.js +2 -0
  14. package/lib/commands/install.d.ts +4 -0
  15. package/lib/commands/install.js +164 -15
  16. package/lib/commands/onboard.js +17 -14
  17. package/lib/commands/pack.d.ts +32 -0
  18. package/lib/commands/pack.js +11 -2
  19. package/lib/commands/publish.js +18 -4
  20. package/lib/commands/storybook.js +5 -1
  21. package/lib/commands/test.js +29 -2
  22. package/lib/commands/uninstall.js +44 -2
  23. package/lib/commands/upgrade.js +1 -4
  24. package/lib/templates/install.mjs.tmpl +110 -19
  25. package/lib/utils/bin-shim.d.ts +73 -0
  26. package/lib/utils/bin-shim.js +97 -2
  27. package/lib/utils/build-cache.d.ts +7 -27
  28. package/lib/utils/build-cache.js +27 -2
  29. package/lib/utils/check-system-deps.d.ts +15 -1
  30. package/lib/utils/check-system-deps.js +126 -2
  31. package/lib/utils/clear-targets.d.ts +30 -0
  32. package/lib/utils/clear-targets.js +114 -0
  33. package/lib/utils/copy-targets.d.ts +41 -0
  34. package/lib/utils/copy-targets.js +145 -0
  35. package/lib/utils/detect-native-packages.d.ts +160 -5
  36. package/lib/utils/detect-native-packages.js +326 -50
  37. package/lib/utils/dir-link.d.ts +34 -6
  38. package/lib/utils/dir-link.js +23 -9
  39. package/lib/utils/install-backend-native.d.ts +109 -1
  40. package/lib/utils/install-backend-native.js +622 -59
  41. package/lib/utils/install-backend.js +17 -15
  42. package/lib/utils/install-global.js +6 -34
  43. package/lib/utils/install-lock.js +1 -1
  44. package/lib/utils/install-packument-cache.d.ts +36 -17
  45. package/lib/utils/install-packument-cache.js +59 -31
  46. package/lib/utils/oxc-resolve.d.ts +27 -1
  47. package/lib/utils/oxc-resolve.js +53 -26
  48. package/lib/utils/pkg-json.d.ts +24 -0
  49. package/lib/utils/pkg-json.js +27 -1
  50. package/lib/utils/platform-check.d.ts +215 -0
  51. package/lib/utils/platform-check.js +377 -0
  52. package/lib/utils/resolve-npm-package.js +9 -2
  53. package/lib/utils/run-gjs.d.ts +1 -1
  54. package/lib/utils/run-gjs.js +19 -3
  55. package/lib/utils/spawn.js +50 -2
  56. package/lib/utils/win32-command.d.ts +54 -0
  57. package/lib/utils/win32-command.js +140 -0
  58. package/package.json +19 -20
@@ -45,8 +45,34 @@
45
45
  // {@link SpawnToCompletionOptions.completion} field. It is required on
46
46
  // purpose: a new call site must state which side of the table it is on rather
47
47
  // than silently inherit a default that may be wrong for it.
48
+ //
49
+ // ## When a raw `node:child_process` spawn is still fine — and when it is not
50
+ //
51
+ // This helper is also where the Windows command rewrite lives (`forWin32`
52
+ // below), so the rule is about WHAT is being spawned, not about house style:
53
+ //
54
+ // • spawning a real executable — `git`, `gh`, `node`, `gjs`, `flatpak`, or
55
+ // `process.execPath` — may use `node:child_process` directly. Windows
56
+ // resolves those via `.exe` on its own.
57
+ // • spawning with `shell: true` is fine too: Node routes it through
58
+ // `%COMSPEC%`, which finds `.cmd` shims.
59
+ // • spawning a PACKAGE MANAGER or any other `node_modules/.bin` entry by
60
+ // BARE NAME — `npm`, `yarn`, `npx`, `tsc`, `oxlint`, or a `pm` variable
61
+ // from `detectPackageManager()` — MUST come through here. On Windows those
62
+ // are `.cmd` shims: `spawn('npm')` is ENOENT and `spawn('npm.cmd')` is
63
+ // EINVAL, so a raw spawn cannot work at all.
64
+ //
65
+ // The third case is not hypothetical. Three sites kept a raw spawn after the
66
+ // consolidation above and each one broke on win32 in its own way:
67
+ // `commands/check.ts` (single-package mode) read only `r.status` and so exited
68
+ // 1 with no diagnostic whatsoever; `utils/install-backend.ts` reported "npm not
69
+ // found on PATH" on a host where npm was on PATH; `commands/onboard.ts` broke
70
+ // the pre-publish build. All three were invisible to Linux and macOS CI.
48
71
  import { spawn, spawnSync } from 'node:child_process';
72
+ import { existsSync } from 'node:fs';
73
+ import { join } from 'node:path';
49
74
  import { isGjs } from '@gjsify/rolldown-plugin-gjsify/runtime';
75
+ import { resolveWin32Command } from './win32-command.js';
50
76
  /** Default capture ceiling on the blocking path — 64 MiB. */
51
77
  const DEFAULT_MAX_BUFFER = 64 * 1024 * 1024;
52
78
  /**
@@ -60,6 +86,23 @@ function resolveEnv(opts) {
60
86
  const seed = process.env.FORCE_COLOR !== undefined || process.env.NO_COLOR !== undefined ? {} : { FORCE_COLOR: '1' };
61
87
  return { ...base, ...seed };
62
88
  }
89
+ /**
90
+ * Rewrite a bare command for Windows, where `spawn('npm', …)` is ENOENT and
91
+ * `spawn('npm.cmd', …)` is EINVAL — see `utils/win32-command.ts`. A no-op on
92
+ * every other platform, and on win32 for anything already carrying a path or an
93
+ * extension. Applied to BOTH paths below so the blocking GJS branch and the
94
+ * streaming one agree; `env` is the child's, because its `PATH` (with
95
+ * `node_modules/.bin` prepended) is what has to be searched.
96
+ */
97
+ function forWin32(cmd, args, env) {
98
+ const rewritten = resolveWin32Command(cmd, args, {
99
+ platform: process.platform,
100
+ env: env ?? process.env,
101
+ exists: existsSync,
102
+ join,
103
+ });
104
+ return rewritten ?? { cmd, args: [...args] };
105
+ }
63
106
  /** Turn a spawn-time failure into the caller's domain error when it is ENOENT. */
64
107
  function mapSpawnError(err, opts) {
65
108
  if (err.code === 'ENOENT' && opts.notFound)
@@ -96,11 +139,13 @@ export function spawnToCompletion(cmd, args, opts) {
96
139
  // `communicateWithTimeout`), so the child's diagnostics would be
97
140
  // silently dropped. Capture them and re-emit after the child exits.
98
141
  // stdin stays inherited so an interactive child still reads the tty.
99
- const r = spawnSync(cmd, [...args], {
142
+ const win = forWin32(cmd, args, env);
143
+ const r = spawnSync(win.cmd, win.args, {
100
144
  stdio: ['inherit', 'pipe', 'pipe'],
101
145
  cwd: opts.cwd,
102
146
  env,
103
147
  maxBuffer: opts.maxBuffer ?? DEFAULT_MAX_BUFFER,
148
+ windowsVerbatimArguments: win.windowsVerbatimArguments,
104
149
  });
105
150
  if (r.error)
106
151
  return Promise.reject(mapSpawnError(r.error, opts));
@@ -111,13 +156,16 @@ export function spawnToCompletion(cmd, args, opts) {
111
156
  return Promise.resolve({ code: r.status, signal: r.signal ?? null });
112
157
  }
113
158
  return new Promise((resolvePromise, reject) => {
159
+ const win = forWin32(cmd, args, env);
114
160
  const spawnOpts = {
115
161
  cwd: opts.cwd,
116
162
  stdio: opts.stdio === 'pipe' ? ['ignore', 'pipe', 'pipe'] : 'inherit',
117
163
  };
118
164
  if (env)
119
165
  spawnOpts.env = env;
120
- const child = spawn(cmd, [...args], spawnOpts);
166
+ if (win.windowsVerbatimArguments)
167
+ spawnOpts.windowsVerbatimArguments = true;
168
+ const child = spawn(win.cmd, win.args, spawnOpts);
121
169
  opts.onSpawn?.(child);
122
170
  // `close` (not `exit`) so piped stdio is fully drained before the
123
171
  // caller's flushers run. With `stdio: 'inherit'` the two are emitted
@@ -0,0 +1,54 @@
1
+ /** A command rewritten for Windows: what to spawn, with which argv. */
2
+ export interface Win32Invocation {
3
+ /** Executable to spawn — an absolute path, or the command interpreter. */
4
+ cmd: string;
5
+ /** Arguments for {@link cmd}. Pre-escaped when {@link windowsVerbatimArguments} is set. */
6
+ args: string[];
7
+ /** Tells Node the argv is already escaped and must be passed through untouched. */
8
+ windowsVerbatimArguments?: boolean;
9
+ }
10
+ /** Everything {@link resolveWin32Command} needs from the outside world. */
11
+ export interface Win32ResolveContext {
12
+ /** `process.platform`. Injected so the branch is testable off Windows. */
13
+ platform: string;
14
+ /** The environment the CHILD will get — its `PATH` is what must be searched. */
15
+ env: NodeJS.ProcessEnv;
16
+ /** Filesystem probe. Injected so tests need no real files. */
17
+ exists: (path: string) => boolean;
18
+ /** Path joiner. Injected so a Linux host can exercise Windows-shaped paths. */
19
+ join: (dir: string, file: string) => string;
20
+ }
21
+ /**
22
+ * Read `name` from `env` case-insensitively.
23
+ *
24
+ * Windows environment variables are case-insensitive and `process.env` mirrors
25
+ * that, but a plain object handed in as `env` (which is what every spawn call
26
+ * site builds) does not — a child env carrying `Path` rather than `PATH` would
27
+ * otherwise silently resolve nothing.
28
+ */
29
+ export declare function lookupEnv(env: NodeJS.ProcessEnv, name: string): string | undefined;
30
+ /** Escape a command path for cmd.exe. Port of cross-spawn's `escape.command`. */
31
+ export declare function escapeCmdCommand(arg: string): string;
32
+ /**
33
+ * Escape one argument for cmd.exe. Port of cross-spawn's `escape.argument`,
34
+ * whose algorithm is https://qntm.org/cmd with the backtracking removed.
35
+ */
36
+ export declare function escapeCmdArgument(arg: string, doubleEscapeMetaChars?: boolean): string;
37
+ /**
38
+ * Build the `cmd.exe /d /s /c "…"` invocation for a resolved, non-directly-
39
+ * executable target. Pure — the unit tests drive this from any host.
40
+ *
41
+ * @param commandFile Resolved absolute path of the target (e.g. the `.cmd` shim).
42
+ * @param args The caller's arguments, unescaped.
43
+ * @param comspec The interpreter, normally `%COMSPEC%`.
44
+ */
45
+ export declare function buildCmdExeInvocation(commandFile: string, args: readonly string[], comspec?: string): Win32Invocation;
46
+ /**
47
+ * Rewrite a bare command name into something Windows can actually spawn.
48
+ *
49
+ * Returns `undefined` when nothing needs rewriting — not win32, not a bare
50
+ * name, or not found on `PATH`. The last case is deliberate: letting `spawn`
51
+ * fail with `ENOENT` preserves the contract callers key their install hints on
52
+ * (see the module header).
53
+ */
54
+ export declare function resolveWin32Command(cmd: string, args: readonly string[], ctx: Win32ResolveContext): Win32Invocation | undefined;
@@ -0,0 +1,140 @@
1
+ // SPDX-License-Identifier: MIT
2
+ // Adapted from `cross-spawn` (node_modules/cross-spawn/lib/parse.js and
3
+ // lib/util/escape.js, v7.0.6). Copyright (c) 2018 Made With MOXY Lda. MIT.
4
+ // Modifications: rewritten as pure TypeScript with `platform`, `env` and the
5
+ // filesystem probe INJECTED, so the Windows branch is unit-testable from a
6
+ // Linux host (the same shape `utils/bin-shim.ts` uses), and narrowed to the one
7
+ // case the CLI needs — resolving a bare command name before `spawn`.
8
+ //
9
+ // WHY THIS EXISTS
10
+ //
11
+ // `spawn('npm', …)` cannot work on Windows, and the failure is not a PATH
12
+ // problem. `CreateProcess` appends only `.exe` when it searches PATH for a bare
13
+ // name, and every npm-installed bin on Windows is a `.cmd` shim. Measured on
14
+ // win32 x64 / Node 24.18.1:
15
+ //
16
+ // spawn('npm', ['--version']) → ENOENT (.cmd is never tried)
17
+ // spawn('node', ['--version']) → ok (.exe is appended)
18
+ // spawn('npm.cmd', ['--version']) → EINVAL (Node refuses a batch file)
19
+ //
20
+ // The EINVAL is Node's CVE-2024-27980 mitigation: a `.cmd`/`.bat` target has to
21
+ // go through a command interpreter. So resolving the shim path is NOT enough —
22
+ // it must be invoked as `cmd.exe /d /s /c "…"` with every argument escaped for
23
+ // cmd's parser, which is what {@link buildCmdExeInvocation} does.
24
+ //
25
+ // That gap took out the whole orchestration layer, not one command: `gjsify
26
+ // workspace`/`foreach`/`check` spawn `<pm> run <script>` per package and the
27
+ // package manager is `npm` on every non-GJS host, so on Windows each one died
28
+ // with `spawn npm ENOENT` before running a single script.
29
+ //
30
+ // WHY RESOLUTION HAPPENS HERE AND NOT IN cmd.exe
31
+ //
32
+ // Handing the bare name to `cmd.exe /c` would also find the `.cmd` — and would
33
+ // destroy the ENOENT contract every caller depends on. A missing command would
34
+ // come back as a generic exit 1 with an unparsable message instead of an
35
+ // `ENOENT` error, and `SpawnToCompletionOptions.notFound` (the install hints)
36
+ // plus `commands/tsc.ts`'s transparent fall back to upstream tsc when `gjs` is
37
+ // absent both key on exactly that code. Resolving first keeps "not found" a
38
+ // spawn-time error and routes only what really is a batch file through cmd.exe.
39
+ /** Default when `PATHEXT` is unset — the extensions `CreateProcess` users expect. */
40
+ const DEFAULT_PATHEXT = '.COM;.EXE;.BAT;.CMD';
41
+ /** Only `.com`/`.exe` can be executed without an interpreter. Verbatim from cross-spawn. */
42
+ const DIRECTLY_EXECUTABLE_RE = /\.(?:com|exe)$/i;
43
+ /** A cmd-shim under `node_modules/.bin/` re-enters cmd.exe, so meta chars need escaping twice. */
44
+ const CMD_SHIM_RE = /node_modules[\\/]\.bin[\\/][^\\/]+\.cmd$/i;
45
+ // See http://www.robvanderwoude.com/escapechars.php — verbatim from cross-spawn.
46
+ const META_CHARS_RE = /([()\][%!^"`<>&|;, *?])/g;
47
+ /**
48
+ * Read `name` from `env` case-insensitively.
49
+ *
50
+ * Windows environment variables are case-insensitive and `process.env` mirrors
51
+ * that, but a plain object handed in as `env` (which is what every spawn call
52
+ * site builds) does not — a child env carrying `Path` rather than `PATH` would
53
+ * otherwise silently resolve nothing.
54
+ */
55
+ export function lookupEnv(env, name) {
56
+ const wanted = name.toLowerCase();
57
+ for (const key of Object.keys(env)) {
58
+ // A key present but explicitly `undefined` means NOT SET — `spawn`
59
+ // drops it from the child env. Returning it here would shadow a later
60
+ // spelling (`{ PATH: undefined, Path: 'C:\\bin' }`) and resolve nothing.
61
+ if (key.toLowerCase() === wanted && env[key] !== undefined)
62
+ return env[key];
63
+ }
64
+ return undefined;
65
+ }
66
+ /** Escape a command path for cmd.exe. Port of cross-spawn's `escape.command`. */
67
+ export function escapeCmdCommand(arg) {
68
+ return arg.replace(META_CHARS_RE, '^$1');
69
+ }
70
+ /**
71
+ * Escape one argument for cmd.exe. Port of cross-spawn's `escape.argument`,
72
+ * whose algorithm is https://qntm.org/cmd with the backtracking removed.
73
+ */
74
+ export function escapeCmdArgument(arg, doubleEscapeMetaChars = false) {
75
+ let out = `${arg}`;
76
+ // A run of backslashes before a double quote: double the run, escape the quote.
77
+ out = out.replace(/(?=(\\+?)?)\1"/g, '$1$1\\"');
78
+ // A run of backslashes at the end (about to precede the closing quote): double it.
79
+ out = out.replace(/(?=(\\+?)?)\1$/, '$1$1');
80
+ out = `"${out}"`;
81
+ out = out.replace(META_CHARS_RE, '^$1');
82
+ if (doubleEscapeMetaChars)
83
+ out = out.replace(META_CHARS_RE, '^$1');
84
+ return out;
85
+ }
86
+ /**
87
+ * Build the `cmd.exe /d /s /c "…"` invocation for a resolved, non-directly-
88
+ * executable target. Pure — the unit tests drive this from any host.
89
+ *
90
+ * @param commandFile Resolved absolute path of the target (e.g. the `.cmd` shim).
91
+ * @param args The caller's arguments, unescaped.
92
+ * @param comspec The interpreter, normally `%COMSPEC%`.
93
+ */
94
+ export function buildCmdExeInvocation(commandFile, args, comspec = 'cmd.exe') {
95
+ // A cmd-shim under `node_modules/.bin/` is itself a batch file that re-invokes
96
+ // cmd.exe, so its meta chars are interpreted twice and must be escaped twice.
97
+ const doubleEscape = CMD_SHIM_RE.test(commandFile);
98
+ const line = [escapeCmdCommand(commandFile), ...args.map((a) => escapeCmdArgument(a, doubleEscape))].join(' ');
99
+ return { cmd: comspec, args: ['/d', '/s', '/c', `"${line}"`], windowsVerbatimArguments: true };
100
+ }
101
+ /** True when `cmd` is a bare name — no directory part and no extension. */
102
+ function isBareName(cmd) {
103
+ return !cmd.includes('/') && !cmd.includes('\\') && !/\.[^.]+$/.test(cmd);
104
+ }
105
+ /**
106
+ * Find `cmd` on the child's `PATH`, trying each `PATHEXT` extension per
107
+ * directory — the order `CreateProcess` itself uses.
108
+ */
109
+ function lookupOnPath(cmd, ctx) {
110
+ const dirs = (lookupEnv(ctx.env, 'PATH') ?? '').split(';').filter(Boolean);
111
+ const exts = (lookupEnv(ctx.env, 'PATHEXT') ?? DEFAULT_PATHEXT).split(';').filter(Boolean);
112
+ for (const dir of dirs) {
113
+ for (const ext of exts) {
114
+ const candidate = ctx.join(dir, cmd + ext);
115
+ if (ctx.exists(candidate))
116
+ return candidate;
117
+ }
118
+ }
119
+ return undefined;
120
+ }
121
+ /**
122
+ * Rewrite a bare command name into something Windows can actually spawn.
123
+ *
124
+ * Returns `undefined` when nothing needs rewriting — not win32, not a bare
125
+ * name, or not found on `PATH`. The last case is deliberate: letting `spawn`
126
+ * fail with `ENOENT` preserves the contract callers key their install hints on
127
+ * (see the module header).
128
+ */
129
+ export function resolveWin32Command(cmd, args, ctx) {
130
+ if (ctx.platform !== 'win32' || !isBareName(cmd))
131
+ return undefined;
132
+ const resolved = lookupOnPath(cmd, ctx);
133
+ if (!resolved)
134
+ return undefined;
135
+ // `.exe`/`.com` run without an interpreter; spawning the resolved absolute
136
+ // path skips a second PATH search and cannot be shadowed in between.
137
+ if (DIRECTLY_EXECUTABLE_RE.test(resolved))
138
+ return { cmd: resolved, args: [...args] };
139
+ return buildCmdExeInvocation(resolved, args, lookupEnv(ctx.env, 'COMSPEC') ?? 'cmd.exe');
140
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gjsify/cli",
3
- "version": "0.26.1",
3
+ "version": "0.27.1",
4
4
  "description": "CLI for Gjsify",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -109,13 +109,12 @@
109
109
  "showcases.json"
110
110
  ],
111
111
  "scripts": {
112
- "clear": "rm -rf lib dist tsconfig.tsbuildinfo || exit 0",
112
+ "clear": "gjsify clear lib dist tsconfig.tsbuildinfo",
113
113
  "check": "gjsify tsc --noEmit",
114
114
  "start": "node lib/index.js",
115
- "build": "tsc && mkdir -p lib/templates/flatpak && cp -L src/templates/install.mjs.tmpl lib/templates/install.mjs.tmpl && cp src/templates/flatpak/*.tmpl lib/templates/flatpak/ && cp src/templates/oxlintrc.json.tmpl lib/templates/oxlintrc.json.tmpl && cp src/templates/oxfmtrc.tmpl lib/templates/oxfmtrc.tmpl && gjsify run chmod",
115
+ "build": "gjsify tsc && node scripts/build-assets.mjs",
116
116
  "build:gjs-bundle": "node lib/index.js build src/index.ts --app gjs --outfile dist/cli.gjs.mjs --shebang",
117
117
  "build:affected-bundle": "node lib/index.js build src/affected-entry.ts --app gjs --outfile dist/affected.gjs.mjs",
118
- "chmod": "chmod +x ./lib/index.js",
119
118
  "build:test:node": "node lib/index.js build src/test.mts --app node --outfile dist/test.node.mjs",
120
119
  "test:node": "node dist/test.node.mjs",
121
120
  "test:bun": "bun dist/test.node.mjs",
@@ -130,19 +129,19 @@
130
129
  "cli"
131
130
  ],
132
131
  "dependencies": {
133
- "@gjsify/buffer": "^0.26.1",
134
- "@gjsify/create-app": "^0.26.1",
135
- "@gjsify/node-globals": "^0.26.1",
136
- "@gjsify/node-polyfills": "^0.26.1",
137
- "@gjsify/npm-registry": "^0.26.1",
138
- "@gjsify/resolve-npm": "^0.26.1",
139
- "@gjsify/rolldown-plugin-gjsify": "^0.26.1",
140
- "@gjsify/rolldown-plugin-pnp": "^0.26.1",
141
- "@gjsify/semver": "^0.26.1",
142
- "@gjsify/tar": "^0.26.1",
143
- "@gjsify/tsc": "^0.26.1",
144
- "@gjsify/web-polyfills": "^0.26.1",
145
- "@gjsify/workspace": "^0.26.1",
132
+ "@gjsify/buffer": "^0.27.1",
133
+ "@gjsify/create-app": "^0.27.1",
134
+ "@gjsify/node-globals": "^0.27.1",
135
+ "@gjsify/node-polyfills": "^0.27.1",
136
+ "@gjsify/npm-registry": "^0.27.1",
137
+ "@gjsify/resolve-npm": "^0.27.1",
138
+ "@gjsify/rolldown-plugin-gjsify": "^0.27.1",
139
+ "@gjsify/rolldown-plugin-pnp": "^0.27.1",
140
+ "@gjsify/semver": "^0.27.1",
141
+ "@gjsify/tar": "^0.27.1",
142
+ "@gjsify/tsc": "^0.27.1",
143
+ "@gjsify/web-polyfills": "^0.27.1",
144
+ "@gjsify/workspace": "^0.27.1",
146
145
  "cosmiconfig": "^9.0.2",
147
146
  "get-tsconfig": "^4.14.0",
148
147
  "pkg-types": "^2.3.1",
@@ -150,13 +149,13 @@
150
149
  "yargs": "^18.0.0"
151
150
  },
152
151
  "devDependencies": {
153
- "@gjsify/unit": "^0.26.1",
152
+ "@gjsify/unit": "^0.27.1",
154
153
  "@types/yargs": "^17.0.35",
155
154
  "typescript": "^6.0.3"
156
155
  },
157
156
  "peerDependencies": {
158
- "@gjsify/oxfmt-native": "^0.26.1",
159
- "@gjsify/rolldown-native": "^0.26.1"
157
+ "@gjsify/oxfmt-native": "^0.27.1",
158
+ "@gjsify/rolldown-native": "^0.27.1"
160
159
  },
161
160
  "peerDependenciesMeta": {
162
161
  "@gjsify/oxfmt-native": {