@crustjs/crust-darwin-arm64 0.6.0 → 0.6.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.
@@ -1,4 +1,4 @@
1
1
  [diffend] Oversized file quarantined before diffing.
2
2
  name: package/bin/crust-bun-darwin-arm64
3
- size: 91867874 bytes
4
- sha256: 4f256de9fbb7d9fefdb804bf03692431aca5975eb0e9bf0ea029bd91dad94fb1
3
+ size: 91917410 bytes
4
+ sha256: eed7ae8ad28e16c5af1f5488dd8f54c554802696c85b5249489289fdb958d676
package/bin/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { copyFileSync, cpSync, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, realpathSync, rmSync, rmdirSync, statSync, writeFileSync } from "node:fs";
2
- import { chmod, copyFile, mkdtemp, readFile, readdir, rm, writeFile } from "node:fs/promises";
2
+ import { chmod, copyFile, mkdtemp, readFile, readdir, realpath, rm, writeFile } from "node:fs/promises";
3
3
  import { tmpdir } from "node:os";
4
- import { dirname, isAbsolute, join, posix, relative, resolve, sep, win32 } from "node:path";
4
+ import { basename, dirname, isAbsolute, join, posix, relative, resolve, sep, win32 } from "node:path";
5
5
  import { randomBytes } from "node:crypto";
6
6
  import { fileURLToPath, pathToFileURL } from "node:url";
7
7
  import { spawn } from "node:child_process";
@@ -62,7 +62,7 @@ function readIgnoredMask() {
62
62
  function isErrnoException(error) {
63
63
  return error instanceof Error && "code" in error && typeof error.code === "string";
64
64
  }
65
- // ../core/dist/invocation-B_3ZsKUB.js
65
+ // ../core/dist/invocation-DKSTuFCO.js
66
66
  /**
67
67
  * A typed error for runtime recipe, Extension, Context, documentation, argv, and validation failures.
68
68
  *
@@ -170,6 +170,9 @@ function readForceColor(overrides) {
170
170
  function forceColorDisables(forceColor) {
171
171
  return forceColor === "0" || forceColor === "false";
172
172
  }
173
+ function isDumbTerm(term) {
174
+ return term?.toLowerCase() === "dumb";
175
+ }
173
176
  function isTrueColorTerm(term) {
174
177
  const lower = term.toLowerCase();
175
178
  return lower.includes("24bit") || lower.includes("truecolor") || lower.endsWith("-direct");
@@ -193,19 +196,20 @@ function detectDepth(colorTerm, term) {
193
196
  * - `"never"` → `"none"`.
194
197
  * - `"always"` → `"truecolor"`.
195
198
  * - `"auto"`:
196
- * 1. `FORCE_COLOR` set → decides unconditionally (overrides TTY and
197
- * `NO_COLOR`, chalk convention): `0` / `false` → `"none"`; `1` →
199
+ * 1. `FORCE_COLOR` set → decides unconditionally (overrides TTY,
200
+ * `NO_COLOR`, and `TERM=dumb`, as in Node's `tty` color detection):
201
+ * `0` / `false` → `"none"`; `1` →
198
202
  * `"16"`; `2` → `"256"`; `3` → `"truecolor"`; any other value
199
203
  * (including empty) → forced on at the `COLORTERM` / `TERM`
200
204
  * detected depth.
201
- * 2. Not a TTY OR `NO_COLOR` set non-empty → `"none"`.
205
+ * 2. Not a TTY OR `NO_COLOR` set non-empty OR `TERM=dumb`
206
+ * (case-insensitive) → `"none"`.
202
207
  * 3. `COLORTERM` is `"truecolor"` or `"24bit"` (case-insensitive) →
203
208
  * `"truecolor"`.
204
209
  * 4. `TERM` ends with `-direct` OR contains `truecolor` / `24bit` →
205
210
  * `"truecolor"`.
206
- * 5. `TERM === "dumb"` → `"none"`.
207
- * 6. `TERM` contains `256color` → `"256"`.
208
- * 7. Any other TTY value → `"16"`.
211
+ * 5. `TERM` contains `256color` → `"256"`.
212
+ * 6. Any other TTY value → `"16"`.
209
213
  *
210
214
  * Detection follows the ecosystem `FORCE_COLOR` / `NO_COLOR` / `COLORTERM`
211
215
  * / `TERM` conventions; no bespoke environment variables are introduced.
@@ -247,14 +251,15 @@ function resolveColorDepth(mode, overrides) {
247
251
  if (!readTTY(overrides)) return "none";
248
252
  const noColor = read(overrides, "noColor", process.env.NO_COLOR);
249
253
  if (noColor !== void 0 && noColor !== "") return "none";
250
- if (term !== void 0 && term.toLowerCase() === "dumb" && !detectsTruecolor(colorTerm, term)) return "none";
254
+ if (isDumbTerm(term)) return "none";
251
255
  return detectDepth(colorTerm, term);
252
256
  }
253
257
  /**
254
258
  * Resolve whether non-color ANSI modifiers should be emitted.
255
259
  *
256
- * In `"auto"` mode, modifiers are enabled when stdout is a TTY, but are
257
- * **not** affected by `NO_COLOR` (which only controls color output).
260
+ * In `"auto"` mode, modifiers are enabled when stdout is a TTY and `TERM`
261
+ * is not `dumb`, but are **not** affected by `NO_COLOR` (which only
262
+ * controls color output).
258
263
  * `FORCE_COLOR`, when set, decides unconditionally — it is the all-ANSI
259
264
  * switch, while `NO_COLOR` is the colors-only switch.
260
265
  *
@@ -266,7 +271,7 @@ function resolveModifierCapability(mode, overrides) {
266
271
  if (mode === "never") return false;
267
272
  const forceColor = readForceColor(overrides);
268
273
  if (forceColor !== void 0) return !forceColorDisables(forceColor);
269
- return readTTY(overrides);
274
+ return readTTY(overrides) && !isDumbTerm(read(overrides, "term", process.env.TERM));
270
275
  }
271
276
  /**
272
277
  * Apply an ANSI style pair to a string with nesting-safe composition.
@@ -379,20 +384,35 @@ function collectIncludeDirs(cwd, stageDir, include, artifactNames) {
379
384
  return includeDirs;
380
385
  }
381
386
  /**
387
+ * Names a template ships on purpose that `npm pack`/`npm publish` drop from
388
+ * every directory of a package whatever its `files` says, in any letter case.
389
+ * A dropped `.gitignore` or `.npmignore` also acts as ignore rules for its own
390
+ * directory. The rest of npm's ignore list (`.DS_Store`, `*.orig`, …) is junk
391
+ * nobody means to publish, so it stays silently dropped.
392
+ */
393
+ const NPM_DROPPED_NAME = /^\.(?:gitignore|npmignore|npmrc)$/i;
394
+ /**
382
395
  * Walks `dir` the way the dereferencing copy will (through symlinked
383
- * directories) and rejects any path whose real location leaves the project.
396
+ * directories) and rejects any path whose real location leaves the project,
397
+ * and any {@link NPM_DROPPED_NAME} name.
384
398
  */
385
399
  function assertResolvesInsideProject(cwd, entry, dir) {
386
400
  const realCwd = realpathSync(cwd);
387
401
  const seen = /* @__PURE__ */ new Set();
402
+ const npmDropped = [];
388
403
  const walk = (path) => {
389
404
  const real = realpathSync(path);
390
405
  if (!isWithin(realCwd, real)) throw new Error(`package.json crust.include entry ${JSON.stringify(entry)} resolves outside the project root: ${relative(cwd, path)} -> ${real}`);
406
+ if (NPM_DROPPED_NAME.test(basename(path))) npmDropped.push(relative(cwd, path));
391
407
  if (seen.has(real) || !statSync(path).isDirectory()) return;
392
408
  seen.add(real);
393
409
  for (const child of readdirSync(path)) walk(join(path, child));
394
410
  };
395
411
  walk(dir);
412
+ if (npmDropped.length > 0) {
413
+ const listed = npmDropped.sort().map((path) => `\n ${path}`).join("");
414
+ throw new Error(`package.json crust.include entry ${JSON.stringify(entry)} contains files npm leaves out of published packages:${listed}\n Rename each (e.g. .gitignore -> _gitignore) and restore the name when your CLI copies it; scaffold() from @crustjs/create turns a leading _ on a file name into a dot.`);
415
+ }
396
416
  }
397
417
  /**
398
418
  * Copies one entry's Extension build hook output into the shared artifact
@@ -553,6 +573,7 @@ function killProcessTree(child) {
553
573
  * calls are passed through unchanged and own their shell escaping.
554
574
  */
555
575
  async function runProcess(command, args = [], options = {}) {
576
+ options.signal?.throwIfAborted();
556
577
  const windowsShimCommand = getWindowsShimCommand(command, args, options.shell);
557
578
  const collect = (options.stdio ?? "collect") === "collect";
558
579
  const collectStdout = collect && options.stdout !== "ignore";
@@ -595,6 +616,17 @@ async function runProcess(command, args = [], options = {}) {
595
616
  } catch {}
596
617
  }
597
618
  }) : () => {};
619
+ let forceKillTimer;
620
+ const onAbort = () => {
621
+ try {
622
+ if (detached || process.platform === "win32") killProcessTree(proc);
623
+ else {
624
+ proc.kill("SIGTERM");
625
+ forceKillTimer = setTimeout(() => proc.kill("SIGKILL"), 5e3);
626
+ }
627
+ } catch {}
628
+ };
629
+ options.signal?.addEventListener("abort", onAbort, { once: true });
598
630
  const { text } = process.getBuiltinModule("node:stream/consumers");
599
631
  const { once } = process.getBuiltinModule("node:events");
600
632
  try {
@@ -603,6 +635,7 @@ async function runProcess(command, args = [], options = {}) {
603
635
  collect ? text(proc.stderr) : "",
604
636
  once(proc, "close")
605
637
  ]), stopped]);
638
+ options.signal?.throwIfAborted();
606
639
  return {
607
640
  exitCode,
608
641
  stdout,
@@ -610,7 +643,9 @@ async function runProcess(command, args = [], options = {}) {
610
643
  };
611
644
  } finally {
612
645
  clearTimeout(timer);
646
+ clearTimeout(forceKillTimer);
613
647
  release();
648
+ options.signal?.removeEventListener("abort", onAbort);
614
649
  }
615
650
  }
616
651
  function isExecutableFile(path) {
@@ -1804,6 +1839,12 @@ for (const { specifier, source } of options.plugins) {
1804
1839
  }
1805
1840
  plugins.push(plugin);
1806
1841
  }
1842
+ if (options.stripShebang) {
1843
+ // Last, so a project plugin's onLoad for the entry still wins. The emptied line keeps line numbers.
1844
+ plugins.push({ name: "crust-strip-shebang", setup(build) {
1845
+ build.onLoad({ filter: new RegExp(\`^(?:\${options.stripShebang.map(RegExp.escape).join("|")})$\`) }, async ({ path, loader }) => ({ contents: (await Bun.file(path).text()).replace(/^#![^\\n]*/, ""), loader }));
1846
+ } });
1847
+ }
1807
1848
  const result = await Bun.build({ ...options.build, define: ${JSON.stringify(CRUST_BUILD_DEFINE)}, plugins, throw: false });
1808
1849
  if (!result.success) {
1809
1850
  for (const log of result.logs) console.error(log);
@@ -1819,7 +1860,7 @@ if (!options.build.compile) {
1819
1860
  }
1820
1861
  `;
1821
1862
  }
1822
- async function runBunPluginDriver(build, outfilePath, { cwd, envFiles, bunPlugins }, runner) {
1863
+ async function runBunPluginDriver(build, outfilePath, { cwd, envFiles, bunPlugins }, runner, stripShebang) {
1823
1864
  const driverPath = join(cwd, `.crust-build-${randomBytes(6).toString("hex")}.ts`);
1824
1865
  await writeFile(driverPath, createBunPluginDriverScript({
1825
1866
  plugins: bunPlugins.map((specifier) => ({
@@ -1827,7 +1868,8 @@ async function runBunPluginDriver(build, outfilePath, { cwd, envFiles, bunPlugin
1827
1868
  source: resolveBunPluginSource(specifier, cwd)
1828
1869
  })),
1829
1870
  build,
1830
- outfile: outfilePath
1871
+ outfile: outfilePath,
1872
+ stripShebang
1831
1873
  }));
1832
1874
  try {
1833
1875
  await runBuildProcess(runner, [...toBunEnvFileArgs(envFiles), driverPath], outfilePath, cwd);
@@ -1906,14 +1948,15 @@ async function runBuildProcess(runner, args, outfilePath, cwd) {
1906
1948
  */
1907
1949
  async function execScriptBuild(target, entryPath, outfilePath, settings, runner = resolveBunBuildRunner()) {
1908
1950
  const { cwd, minify, envFiles, external } = settings;
1909
- if (settings.bunPlugins.length > 0) await runBunPluginDriver({
1951
+ const stripShebang = target === "node" && /^#![^\n]*\bbun\b/.test(await readFile(entryPath, "utf8")) ? [entryPath, await realpath(entryPath)] : void 0;
1952
+ if (settings.bunPlugins.length > 0 || stripShebang !== void 0) await runBunPluginDriver({
1910
1953
  entrypoints: [entryPath],
1911
1954
  minify,
1912
1955
  env: "PUBLIC_*",
1913
1956
  target,
1914
1957
  format: "esm",
1915
1958
  external
1916
- }, outfilePath, settings, runner);
1959
+ }, outfilePath, settings, runner, stripShebang);
1917
1960
  else await runBuildProcess(runner, [
1918
1961
  "build",
1919
1962
  ...toBunEnvFileArgs(envFiles),
@@ -1930,9 +1973,9 @@ async function execScriptBuild(target, entryPath, outfilePath, settings, runner
1930
1973
  ...minify ? ["--minify"] : [],
1931
1974
  entryPath
1932
1975
  ], outfilePath, cwd);
1933
- const output = await readFile(outfilePath, "utf8");
1934
- const shebang = `#!/usr/bin/env ${target}\n`;
1935
- await writeFile(outfilePath, shebang + output.replace(/^#![^\n]*(?:\n|$)/, ""));
1976
+ const output = (await readFile(outfilePath, "utf8")).replace(/^#![^\n]*(?:\n|$)/, "");
1977
+ if (target === "node" && output.startsWith("// @bun")) throw new Error(`Build failed for ${outfilePath}: Bun emitted Bun-only code for a Node bundle because the entry reached it starting with "#!/usr/bin/env bun".\n Remove that line from ${entryPath}, or from what a crust.bunPlugins onLoad hook returns for it.`);
1978
+ await writeFile(outfilePath, `#!/usr/bin/env ${target}\n${output}`);
1936
1979
  if (process.platform !== "win32") await chmod(outfilePath, 493);
1937
1980
  }
1938
1981
  // src/launcher.ts
@@ -2027,6 +2070,7 @@ if (process.platform === "win32") {
2027
2070
  \t}
2028
2071
  \t// Become the binary: one process, so signals and exit status reach it with no relay.
2029
2072
  \t// Node ignores SIGPIPE and SIGXFSZ, and ignored signals survive execve; handled ones reset.
2073
+ \t// Ignores Node inherited (nohup's SIGHUP) were reset at its startup, so none pass on.
2030
2074
  \tprocess.on("SIGPIPE", () => {});
2031
2075
  \tprocess.on("SIGXFSZ", () => {});
2032
2076
  \t// Early execve versions (e.g. Node 24.0.0) default to an empty environment.
@@ -3260,6 +3304,10 @@ async function build(options = {}) {
3260
3304
  const plan = planBuild(options, cwd);
3261
3305
  io.stdout(`${dim("Runtime:")} ${plan.runtime} ${dim(`(${plan.runtimeSource})`)}`);
3262
3306
  io.stdout(`${dim("Artifact:")} ${plan.artifact}`);
3307
+ for (const { entryPath } of plan.entries) {
3308
+ const named = /^#![^\n]*\b(bun|node)\b/.exec(readFileSync(entryPath, "utf8"))?.[1];
3309
+ if (named !== void 0 && named !== plan.runtime) io.stderr(`${yellow("Warning:")} ${entryPath} starts with a ${named} shebang but crust.runtime is "${plan.runtime}". The build targets ${plan.runtime}; a linked command (npm link) still starts ${named}.`);
3310
+ }
3263
3311
  const compilers = await selectCompilers(plan, io);
3264
3312
  rmSync(plan.stageDir, {
3265
3313
  recursive: true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@crustjs/crust-darwin-arm64",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "description": "CLI to build and publish Crust apps as npm packages or standalone binaries",
5
5
  "license": "MIT",
6
6
  "author": "chenxin-yan",