@pumpkin-plugins/build 0.1.0 → 0.1.2

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 (3) hide show
  1. package/README.md +4 -0
  2. package/dist/cli.js +153 -115
  3. package/package.json +6 -4
package/README.md CHANGED
@@ -10,6 +10,10 @@ This package is configured for public npm publication as `@pumpkin-plugins/build
10
10
  publish its packed tarball; see [CI and releases](../../docs/ci-and-releases.md#releases) and the
11
11
  [build guide](../../docs/building.md).
12
12
 
13
+ The builder and migration codemod share the dependency-free
14
+ [`@pumpkin-plugins/wasi-capabilities`](../wasi-capabilities/README.md) package for WASI names and
15
+ interface mapping.
16
+
13
17
  <!-- docs:begin reference -->
14
18
  <!-- Generated by pumpkin-plugins-docs from package, action, extension, and component docs metadata. Run `pnpm readme`. -->
15
19
 
package/dist/cli.js CHANGED
@@ -2,19 +2,20 @@
2
2
 
3
3
  // src/cli.ts
4
4
  import * as fs9 from "node:fs";
5
- import { createRequire as createRequire3 } from "node:module";
5
+ import { createRequire as createRequire4 } from "node:module";
6
6
  import * as os from "node:os";
7
7
  import * as path9 from "node:path";
8
8
  import { fileURLToPath } from "node:url";
9
9
  import { Command } from "@commander-js/extra-typings";
10
10
  import { ensurePumpkinWit } from "@pumpkin-plugins/pumpkin-api/wit";
11
+ import { wasiInterfaces } from "@pumpkin-plugins/wasi-capabilities";
11
12
  import { CommanderError } from "commander";
12
13
 
13
14
  // src/bundle.ts
14
15
  import { execFileSync as execFileSync2 } from "node:child_process";
15
- import * as fs3 from "node:fs";
16
+ import * as fs4 from "node:fs";
16
17
  import { createRequire as createRequire2 } from "node:module";
17
- import * as path3 from "node:path";
18
+ import * as path4 from "node:path";
18
19
  import { buildStaticHermesComponent, staticHermesImportsPlugin } from "@pumpkin-plugins/static-hermes-runtime";
19
20
  import * as esbuild from "esbuild";
20
21
 
@@ -40,22 +41,35 @@ function errorMessage(error) {
40
41
  return error instanceof Error ? error.message : String(error);
41
42
  }
42
43
 
44
+ // src/repo.ts
45
+ import * as fs from "node:fs";
46
+ import * as path from "node:path";
47
+ function findRepoRoot(from) {
48
+ for (let dir = path.resolve(from); ; dir = path.dirname(dir)) {
49
+ if (fs.existsSync(path.join(dir, "pnpm-workspace.yaml"))) return dir;
50
+ if (path.dirname(dir) === dir) return path.resolve(from);
51
+ }
52
+ }
53
+ function cacheDir(from) {
54
+ return process.env.PUMPKIN_PLUGINS_CACHE_DIR || path.join(findRepoRoot(from), ".cache");
55
+ }
56
+
43
57
  // src/types.ts
44
58
  import { execFileSync } from "node:child_process";
45
- import * as fs from "node:fs";
59
+ import * as fs2 from "node:fs";
46
60
  import { createRequire } from "node:module";
47
- import * as path from "node:path";
61
+ import * as path2 from "node:path";
48
62
  var require2 = createRequire(import.meta.url);
49
63
  function generateTypes(witDir, outDir) {
50
64
  const jco = resolveJcoCli();
51
- const output = path.resolve(outDir);
52
- const parent = path.dirname(output);
53
- fs.mkdirSync(parent, { recursive: true });
54
- const temporary = fs.mkdtempSync(path.join(parent, `.${path.basename(output)}-`));
55
- const generated = path.join(temporary, "generated");
65
+ const output = path2.resolve(outDir);
66
+ const parent = path2.dirname(output);
67
+ fs2.mkdirSync(parent, { recursive: true });
68
+ const temporary = fs2.mkdtempSync(path2.join(parent, `.${path2.basename(output)}-`));
69
+ const generated = path2.join(temporary, "generated");
56
70
  let preserveTemporary = false;
57
71
  try {
58
- fs.mkdirSync(generated);
72
+ fs2.mkdirSync(generated);
59
73
  execFileSync(
60
74
  process.execPath,
61
75
  [jco, "guest-types", witDir, "-n", "plugin", "-o", generated, "--name", "index", "--quiet"],
@@ -67,7 +81,7 @@ function generateTypes(witDir, outDir) {
67
81
  preserveTemporary = true;
68
82
  });
69
83
  } finally {
70
- if (!preserveTemporary) fs.rmSync(temporary, { recursive: true, force: true });
84
+ if (!preserveTemporary) fs2.rmSync(temporary, { recursive: true, force: true });
71
85
  }
72
86
  }
73
87
  function resolveJcoCli() {
@@ -77,37 +91,37 @@ function resolveJcoCli() {
77
91
  } catch {
78
92
  throw new Error("@bytecodealliance/jco is missing from @pumpkin-plugins/build dependencies");
79
93
  }
80
- for (let directory = path.dirname(entry); ; directory = path.dirname(directory)) {
81
- const packagePath = path.join(directory, "package.json");
82
- if (fs.existsSync(packagePath)) {
83
- const packageJson = JSON.parse(fs.readFileSync(packagePath, "utf8"));
94
+ for (let directory = path2.dirname(entry); ; directory = path2.dirname(directory)) {
95
+ const packagePath = path2.join(directory, "package.json");
96
+ if (fs2.existsSync(packagePath)) {
97
+ const packageJson = JSON.parse(fs2.readFileSync(packagePath, "utf8"));
84
98
  if (packageJson.name === "@bytecodealliance/jco") {
85
99
  const command = typeof packageJson.bin === "string" ? packageJson.bin : packageJson.bin?.jco;
86
100
  if (!command) throw new Error("@bytecodealliance/jco package metadata does not declare its jco binary");
87
- const binaryPath = path.resolve(directory, command);
88
- if (!fs.existsSync(binaryPath))
101
+ const binaryPath = path2.resolve(directory, command);
102
+ if (!fs2.existsSync(binaryPath))
89
103
  throw new Error(`jco binary is missing from its package: ${binaryPath}`);
90
104
  return binaryPath;
91
105
  }
92
106
  }
93
- const parent = path.dirname(directory);
107
+ const parent = path2.dirname(directory);
94
108
  if (parent === directory) break;
95
109
  }
96
110
  throw new Error(`could not locate @bytecodealliance/jco package metadata from ${entry}`);
97
111
  }
98
112
  function publishTypes(generated, output, temporary, preserveForRecovery) {
99
- if (!fs.existsSync(output)) {
100
- fs.renameSync(generated, output);
113
+ if (!fs2.existsSync(output)) {
114
+ fs2.renameSync(generated, output);
101
115
  return;
102
116
  }
103
- if (!fs.statSync(output).isDirectory()) throw new Error(`type output is not a directory: ${output}`);
104
- const previous = path.join(temporary, "previous");
105
- fs.renameSync(output, previous);
117
+ if (!fs2.statSync(output).isDirectory()) throw new Error(`type output is not a directory: ${output}`);
118
+ const previous = path2.join(temporary, "previous");
119
+ fs2.renameSync(output, previous);
106
120
  try {
107
- fs.renameSync(generated, output);
121
+ fs2.renameSync(generated, output);
108
122
  } catch (publishError) {
109
123
  try {
110
- fs.renameSync(previous, output);
124
+ fs2.renameSync(previous, output);
111
125
  } catch (restoreError) {
112
126
  preserveForRecovery();
113
127
  throw new AggregateError(
@@ -120,24 +134,38 @@ function publishTypes(generated, output, temporary, preserveForRecovery) {
120
134
  }
121
135
 
122
136
  // src/wit-method-usage.ts
123
- import * as fs2 from "node:fs";
124
- import * as path2 from "node:path";
137
+ import * as fs3 from "node:fs";
138
+ import * as path3 from "node:path";
125
139
  import * as ts from "typescript";
126
140
  function findUsedWitMethods(entry, additionalTypesDir) {
127
- const absoluteEntry = path2.resolve(entry);
128
- if (!fs2.existsSync(absoluteEntry)) return void 0;
129
- const configPath = ts.findConfigFile(path2.dirname(absoluteEntry), ts.sys.fileExists, "tsconfig.json");
130
- if (!configPath) return void 0;
131
- const config = ts.readConfigFile(configPath, ts.sys.readFile);
132
- if (config.error) throw new Error(ts.flattenDiagnosticMessageText(config.error.messageText, "\n"));
133
- const parsed = ts.parseJsonConfigFileContent(config.config, ts.sys, path2.dirname(configPath));
141
+ const absoluteEntry = path3.resolve(entry);
142
+ if (!fs3.existsSync(absoluteEntry)) return void 0;
143
+ const configPath = ts.findConfigFile(path3.dirname(absoluteEntry), ts.sys.fileExists, "tsconfig.json");
144
+ let parsed;
145
+ if (configPath) {
146
+ const config = ts.readConfigFile(configPath, ts.sys.readFile);
147
+ if (config.error) throw new Error(ts.flattenDiagnosticMessageText(config.error.messageText, "\n"));
148
+ parsed = ts.parseJsonConfigFileContent(config.config, ts.sys, path3.dirname(configPath));
149
+ } else {
150
+ parsed = ts.parseJsonConfigFileContent({}, ts.sys, path3.dirname(absoluteEntry));
151
+ }
134
152
  if (parsed.errors.length) {
135
153
  throw new Error(
136
154
  parsed.errors.map((error) => ts.flattenDiagnosticMessageText(error.messageText, "\n")).join("\n")
137
155
  );
138
156
  }
139
157
  const additionalTypeFiles = additionalTypesDir ? ts.sys.readDirectory(additionalTypesDir, [".d.ts"], void 0, ["**/*.d.ts"]) : [];
140
- const options = { ...parsed.options, allowJs: true, maxNodeModuleJsDepth: 100 };
158
+ const options = {
159
+ ...configPath ? {} : {
160
+ target: ts.ScriptTarget.ES2022,
161
+ module: ts.ModuleKind.ESNext,
162
+ moduleResolution: ts.ModuleResolutionKind.Bundler,
163
+ strict: true
164
+ },
165
+ ...parsed.options,
166
+ allowJs: true,
167
+ maxNodeModuleJsDepth: 100
168
+ };
141
169
  const implementationFiles = /* @__PURE__ */ new Set();
142
170
  const runtimeHost = {
143
171
  ...ts.sys,
@@ -577,22 +605,24 @@ function toWitName(name) {
577
605
 
578
606
  // src/bundle.ts
579
607
  var require3 = createRequire2(import.meta.url);
608
+ var buildPackageJsonPath = require3.resolve("../package.json");
609
+ var buildPackageRepositoryUrl = packageRepositoryUrl(require3(buildPackageJsonPath));
580
610
  var MAX_PRODUCTION_COMPONENT_BYTES = 5 * 1024 * 1024;
581
611
  var TEMPORARY_DIRECTORY_PREFIX = ".pumpkin-build-";
582
612
  var LEGACY_TEMPORARY_DIRECTORY_MAX_AGE_MS = 60 * 60 * 1e3;
583
613
  var activeTemporaryDirectories = /* @__PURE__ */ new Set();
584
614
  async function bundlePlugin(options) {
585
- const output = path3.resolve(options.output);
586
- const outputDir = path3.dirname(output);
587
- fs3.mkdirSync(outputDir, { recursive: true });
615
+ const output = path4.resolve(options.output);
616
+ const outputDir = path4.dirname(output);
617
+ fs4.mkdirSync(outputDir, { recursive: true });
588
618
  removeAbandonedTemporaryDirectories(outputDir);
589
- const temporaryDir = fs3.mkdtempSync(path3.join(outputDir, `${TEMPORARY_DIRECTORY_PREFIX}${process.pid}-`));
619
+ const temporaryDir = fs4.mkdtempSync(path4.join(outputDir, `${TEMPORARY_DIRECTORY_PREFIX}${process.pid}-`));
590
620
  activeTemporaryDirectories.add(temporaryDir);
591
- const bundle = path3.join(temporaryDir, "bundle.js");
592
- const component = path3.join(temporaryDir, "plugin.wasm");
621
+ const bundle = path4.join(temporaryDir, "bundle.js");
622
+ const component = path4.join(temporaryDir, "plugin.wasm");
593
623
  const engine = options.engine ?? "static-hermes";
594
624
  const quietRuntimeOutput = options.quietRuntimeOutput ?? false;
595
- const projectRoot = path3.resolve(options.projectRoot ?? process.cwd());
625
+ const projectRoot = path4.resolve(options.projectRoot ?? process.cwd());
596
626
  const resolvedWasiImports = /* @__PURE__ */ new Set();
597
627
  let preserveTemporaryDir = false;
598
628
  try {
@@ -606,7 +636,7 @@ async function bundlePlugin(options) {
606
636
  globalName: "__pumpkinPlugin",
607
637
  plugins: [
608
638
  staticHermesImportsPlugin({
609
- resolveDir: path3.dirname(path3.resolve(options.entry)),
639
+ resolveDir: path4.dirname(path4.resolve(options.entry)),
610
640
  workspaceRoot: projectRoot,
611
641
  witDir: options.witDir,
612
642
  developmentMode: options.developmentMode ?? false,
@@ -625,7 +655,7 @@ async function bundlePlugin(options) {
625
655
  }
626
656
  });
627
657
  if (engine === "static-hermes") {
628
- const source = fs3.readFileSync(bundle, "utf8");
658
+ const source = fs4.readFileSync(bundle, "utf8");
629
659
  const unsupportedWasiImports = staticHermesWasiImports(buildResult.metafile).filter(
630
660
  (specifier) => !resolvedWasiImports.has(specifier)
631
661
  );
@@ -634,8 +664,8 @@ async function bundlePlugin(options) {
634
664
  `Static Hermes JavaScript bundle imports WASI interface${unsupportedWasiImports.length === 1 ? "" : "s"} without a JSI adapter: ${unsupportedWasiImports.join(", ")}`
635
665
  );
636
666
  }
637
- fs3.writeFileSync(bundle, unwrapStaticHermesExports(source));
638
- const generatedTypes = path3.join(temporaryDir, "wit-types");
667
+ fs4.writeFileSync(bundle, unwrapStaticHermesExports(source));
668
+ const generatedTypes = path4.join(temporaryDir, "wit-types");
639
669
  generateTypes(options.witDir, generatedTypes);
640
670
  const usedWitMethods = findUsedWitMethods(options.entry, generatedTypes);
641
671
  options.onOutput?.({
@@ -678,13 +708,13 @@ async function bundlePlugin(options) {
678
708
  throw runtimeBuildError("Static Hermes", error);
679
709
  }
680
710
  } else {
681
- const componentizerPackage = path3.dirname(require3.resolve("@di-framework/componentize-qjs/package.json"));
711
+ const componentizerPackage = path4.dirname(require3.resolve("@di-framework/componentize-qjs/package.json"));
682
712
  options.onOutput?.({ type: "componentize", engine });
683
713
  try {
684
714
  execFileSync2(
685
715
  process.execPath,
686
716
  [
687
- path3.join(componentizerPackage, "bin/componentize-qjs.cjs"),
717
+ path4.join(componentizerPackage, "bin/componentize-qjs.cjs"),
688
718
  "--world",
689
719
  "plugin",
690
720
  "--wit",
@@ -692,7 +722,7 @@ async function bundlePlugin(options) {
692
722
  "--js",
693
723
  bundle,
694
724
  "--runtime",
695
- path3.join(import.meta.dirname, "../runtime/quickjs-runtime-bigint.wasm"),
725
+ path4.join(import.meta.dirname, "../runtime/quickjs-runtime-bigint.wasm"),
696
726
  "--output",
697
727
  component
698
728
  ],
@@ -706,11 +736,11 @@ async function bundlePlugin(options) {
706
736
  throw runtimeBuildError("QuickJS", error);
707
737
  }
708
738
  }
709
- const size = fs3.statSync(component).size;
739
+ const size = fs4.statSync(component).size;
710
740
  if (!options.developmentMode && size > MAX_PRODUCTION_COMPONENT_BYTES) {
711
- throw new Error(
712
- `Production WebAssembly component is ${size} bytes; maximum is ${MAX_PRODUCTION_COMPONENT_BYTES} bytes (5 MiB)`
713
- );
741
+ const message = `Production WebAssembly component is ${size} bytes; maximum is ${MAX_PRODUCTION_COMPONENT_BYTES} bytes (5 MiB)`;
742
+ if (isRepositoryProject(projectRoot)) throw new Error(message);
743
+ options.onOutput?.({ type: "warning", message });
714
744
  }
715
745
  publishComponent(component, output, temporaryDir, () => {
716
746
  preserveTemporaryDir = true;
@@ -718,25 +748,47 @@ async function bundlePlugin(options) {
718
748
  return size;
719
749
  } finally {
720
750
  activeTemporaryDirectories.delete(temporaryDir);
721
- if (!preserveTemporaryDir) fs3.rmSync(temporaryDir, { recursive: true, force: true });
751
+ if (!preserveTemporaryDir) fs4.rmSync(temporaryDir, { recursive: true, force: true });
722
752
  }
723
753
  }
754
+ function isRepositoryProject(projectRoot) {
755
+ if (buildPackageRepositoryUrl === void 0) return false;
756
+ let projectManifest;
757
+ try {
758
+ projectManifest = JSON.parse(fs4.readFileSync(path4.join(findRepoRoot(projectRoot), "package.json"), "utf8"));
759
+ } catch {
760
+ return false;
761
+ }
762
+ const projectRepositoryUrl = packageRepositoryUrl(projectManifest);
763
+ return projectRepositoryUrl !== void 0 && normalizeRepositoryUrl(projectRepositoryUrl) === normalizeRepositoryUrl(buildPackageRepositoryUrl);
764
+ }
765
+ function packageRepositoryUrl(manifest) {
766
+ if (typeof manifest !== "object" || manifest === null || Array.isArray(manifest)) return void 0;
767
+ const repository = manifest.repository;
768
+ if (typeof repository === "string") return repository;
769
+ if (typeof repository !== "object" || repository === null || Array.isArray(repository)) return void 0;
770
+ const url = repository.url;
771
+ return typeof url === "string" ? url : void 0;
772
+ }
773
+ function normalizeRepositoryUrl(url) {
774
+ return url.replace(/^git\+/, "").replace(/\.git$/, "").replace(/\/$/, "");
775
+ }
724
776
  function removeAbandonedTemporaryDirectories(outputDir) {
725
- for (const entry of fs3.readdirSync(outputDir, { withFileTypes: true })) {
777
+ for (const entry of fs4.readdirSync(outputDir, { withFileTypes: true })) {
726
778
  if (!entry.isDirectory() || !entry.name.startsWith(TEMPORARY_DIRECTORY_PREFIX)) continue;
727
- const temporaryDir = path3.join(outputDir, entry.name);
728
- if (activeTemporaryDirectories.has(temporaryDir) || fs3.existsSync(path3.join(temporaryDir, "previous.wasm"))) {
779
+ const temporaryDir = path4.join(outputDir, entry.name);
780
+ if (activeTemporaryDirectories.has(temporaryDir) || fs4.existsSync(path4.join(temporaryDir, "previous.wasm"))) {
729
781
  continue;
730
782
  }
731
783
  const owner = /^\.pumpkin-build-(\d+)-/.exec(entry.name);
732
784
  if (owner) {
733
785
  const pid = Number(owner[1]);
734
786
  if (pid !== process.pid && isProcessRunning(pid)) continue;
735
- fs3.rmSync(temporaryDir, { recursive: true, force: true });
787
+ fs4.rmSync(temporaryDir, { recursive: true, force: true });
736
788
  continue;
737
789
  }
738
- if (Date.now() - fs3.statSync(temporaryDir).mtimeMs >= LEGACY_TEMPORARY_DIRECTORY_MAX_AGE_MS) {
739
- fs3.rmSync(temporaryDir, { recursive: true, force: true });
790
+ if (Date.now() - fs4.statSync(temporaryDir).mtimeMs >= LEGACY_TEMPORARY_DIRECTORY_MAX_AGE_MS) {
791
+ fs4.rmSync(temporaryDir, { recursive: true, force: true });
740
792
  }
741
793
  }
742
794
  }
@@ -779,18 +831,18 @@ function isTrailingLicenseComment(source) {
779
831
  }
780
832
  function publishComponent(component, output, temporaryDir, preserveForRecovery) {
781
833
  try {
782
- fs3.renameSync(component, output);
834
+ fs4.renameSync(component, output);
783
835
  } catch (error) {
784
- if (!isReplaceError(error) || !fs3.existsSync(output)) throw error;
785
- const existing = fs3.lstatSync(output);
836
+ if (!isReplaceError(error) || !fs4.existsSync(output)) throw error;
837
+ const existing = fs4.lstatSync(output);
786
838
  if (!existing.isFile() && !existing.isSymbolicLink()) throw error;
787
- const previous = path3.join(temporaryDir, "previous.wasm");
788
- fs3.renameSync(output, previous);
839
+ const previous = path4.join(temporaryDir, "previous.wasm");
840
+ fs4.renameSync(output, previous);
789
841
  try {
790
- fs3.renameSync(component, output);
842
+ fs4.renameSync(component, output);
791
843
  } catch (publishError) {
792
844
  try {
793
- fs3.renameSync(previous, output);
845
+ fs4.renameSync(previous, output);
794
846
  } catch (restoreError) {
795
847
  preserveForRecovery();
796
848
  throw new AggregateError(
@@ -806,23 +858,6 @@ function isReplaceError(error) {
806
858
  return typeof error === "object" && error !== null && "code" in error && (error.code === "EEXIST" || error.code === "EPERM" || error.code === "EACCES");
807
859
  }
808
860
 
809
- // src/capabilities.ts
810
- var WASI_VERSION = "0.2.3";
811
- var CAPABILITIES = {
812
- http: ["http/types", "http/outgoing-handler"],
813
- filesystem: ["filesystem/types", "filesystem/preopens"],
814
- sockets: ["sockets/network", "sockets/instance-network", "sockets/tcp", "sockets/tcp-create-socket"],
815
- udp: ["sockets/network", "sockets/instance-network", "sockets/udp", "sockets/udp-create-socket"]
816
- };
817
- var SUPPORT_IMPORTS = ["io/poll", "io/streams", "clocks/monotonic-clock"];
818
- function isCapability(name) {
819
- return Object.hasOwn(CAPABILITIES, name);
820
- }
821
- function wasiInterfaces(capabilities) {
822
- if (capabilities.length === 0) return [];
823
- return [.../* @__PURE__ */ new Set([...capabilities.flatMap((c) => CAPABILITIES[c]), ...SUPPORT_IMPORTS])];
824
- }
825
-
826
861
  // ../minecraft-colors/src/index.ts
827
862
  var COLORS = {
828
863
  black: "0",
@@ -955,10 +990,11 @@ addNamedFormatters(ansi, "ansi");
955
990
 
956
991
  // src/output.ts
957
992
  function formatBuildOutput(message) {
958
- if (message.type === "error") {
993
+ if (message.type === "error" || message.type === "warning") {
994
+ const formatLine = message.type === "error" ? ansi.named.error : ansi.named.value;
959
995
  return message.message.split(/\r?\n/).map((line, index) => {
960
- const prefix = index === 0 ? "Error: " : " ";
961
- return `[build] ${prefix}${ansi.named.error(line)}`;
996
+ const prefix = index === 0 ? `${message.type === "error" ? "Error" : "Warning"}: ` : " ";
997
+ return `[build] ${prefix}${formatLine(line)}`;
962
998
  }).join("\n");
963
999
  }
964
1000
  if (message.type === "built") {
@@ -982,6 +1018,7 @@ function formatBuildOutput(message) {
982
1018
  function printBuildOutput(message) {
983
1019
  const formatted = formatBuildOutput(message);
984
1020
  if (message.type === "error") console.error(formatted);
1021
+ else if (message.type === "warning") console.warn(formatted);
985
1022
  else console.log(formatted);
986
1023
  }
987
1024
  function runtimeLabel(engine, runtimeVariant, gcVariant, parseTypeScript) {
@@ -997,12 +1034,13 @@ function formatSelectionOption(option, selection, omittedValue) {
997
1034
  }
998
1035
 
999
1036
  // src/plugin-config.ts
1000
- import * as fs4 from "node:fs";
1001
- import * as path4 from "node:path";
1037
+ import * as fs5 from "node:fs";
1038
+ import * as path5 from "node:path";
1039
+ import { CAPABILITIES, isCapability } from "@pumpkin-plugins/wasi-capabilities";
1002
1040
  function readPluginConfig(pluginDir) {
1003
1041
  let pkg;
1004
1042
  try {
1005
- pkg = JSON.parse(fs4.readFileSync(path4.join(pluginDir, "package.json"), "utf8"));
1043
+ pkg = JSON.parse(fs5.readFileSync(path5.join(pluginDir, "package.json"), "utf8"));
1006
1044
  } catch (error) {
1007
1045
  if (error instanceof SyntaxError) throw new BuildError(`package.json is not valid JSON (${error.message})`);
1008
1046
  if (typeof error === "object" && error !== null && "code" in error && (error.code === "ENOENT" || error.code === "ENOTDIR")) {
@@ -1077,11 +1115,11 @@ function optionalRelativePath(value, name) {
1077
1115
  }
1078
1116
  function requiredRelativePath(value, name) {
1079
1117
  if (typeof value !== "string") throw new BuildError(`"pumpkinPlugin.${name}" must be a relative path`);
1080
- const normalized = path4.posix.normalize(value.replaceAll("\\", "/"));
1081
- if (value.length === 0 || value.includes("\0") || path4.isAbsolute(value) || path4.win32.isAbsolute(value) || path4.win32.parse(value).root !== "" || normalized === "." || normalized === ".." || normalized.startsWith("../")) {
1118
+ const normalized = path5.posix.normalize(value.replaceAll("\\", "/"));
1119
+ if (value.length === 0 || value.includes("\0") || path5.isAbsolute(value) || path5.win32.isAbsolute(value) || path5.win32.parse(value).root !== "" || normalized === "." || normalized === ".." || normalized.startsWith("../")) {
1082
1120
  throw new BuildError(`"pumpkinPlugin.${name}" must be a path inside the package`);
1083
1121
  }
1084
- if (name === "output" && path4.posix.extname(normalized).toLowerCase() !== ".wasm") {
1122
+ if (name === "output" && path5.posix.extname(normalized).toLowerCase() !== ".wasm") {
1085
1123
  throw new BuildError('"pumpkinPlugin.output" must name a .wasm file');
1086
1124
  }
1087
1125
  return normalized;
@@ -1090,22 +1128,13 @@ function isRecord(value) {
1090
1128
  return typeof value === "object" && value !== null && !Array.isArray(value);
1091
1129
  }
1092
1130
 
1093
- // src/repo.ts
1094
- import * as fs5 from "node:fs";
1095
- import * as path5 from "node:path";
1096
- function findRepoRoot(from) {
1097
- for (let dir = path5.resolve(from); ; dir = path5.dirname(dir)) {
1098
- if (fs5.existsSync(path5.join(dir, "pnpm-workspace.yaml"))) return dir;
1099
- if (path5.dirname(dir) === dir) return path5.resolve(from);
1100
- }
1101
- }
1102
- function cacheDir(from) {
1103
- return process.env.PUMPKIN_PLUGINS_CACHE_DIR || path5.join(findRepoRoot(from), ".cache");
1104
- }
1105
-
1106
1131
  // src/update-check.ts
1132
+ import { createRequire as createRequire3 } from "node:module";
1107
1133
  import { latestVersion, ONE_DAY } from "@badisi/latest-version";
1108
- var packageName = "@pumpkin-plugins/build";
1134
+ var packageMetadata = createRequire3(import.meta.url)("../package.json");
1135
+ var packageName = packageMetadata.name;
1136
+ var repositoryUrl = packageMetadata.repository.url.replace(/^git\+/, "").replace(/\.git$/, "");
1137
+ var releaseComponent = packageName.split("/").at(-1) ?? packageName;
1109
1138
  var requestTimeout = 5e3;
1110
1139
  function isBuildUpdateCheckAllowed(args, environment, isTTY) {
1111
1140
  return isTTY && !environment.CI && environment.PUMPKIN_DEV_MODE !== "1" && environment.PUMPKIN_PLUGINS_NO_UPDATE_NOTIFIER !== "1" && !args.some((argument) => ["--help", "-h", "--version", "-V", "--no-update-notifier"].includes(argument));
@@ -1130,14 +1159,22 @@ async function startBuildUpdateCheck(args, options) {
1130
1159
  }
1131
1160
  function formatBuildUpdateNotice(currentVersion, latestVersion2) {
1132
1161
  const content = [
1133
- `[build] Update available: ${packageName} ${currentVersion} \u2192 ${latestVersion2}.`,
1134
- "Review the migration with:",
1135
- " pnpm dlx @pumpkin-plugins/codemod update-build ."
1162
+ "",
1163
+ `Update available v${currentVersion} \u226B v${latestVersion2}`,
1164
+ `Changelog: ${new URL(`releases/tag/${releaseComponent}-v${latestVersion2}`, `${repositoryUrl}/`).href}`,
1165
+ 'Run "pnpm dlx @pumpkin-plugins/codemod update-build ." to update',
1166
+ "",
1167
+ "Follow Filiph Sandstr\xF6m for updates: https://x.com/filiphsandstrom",
1168
+ ""
1136
1169
  ];
1137
1170
  const contentWidth = Math.max(...content.map((line) => line.length));
1138
1171
  const border = "\u2500".repeat(contentWidth + 2);
1139
- const body = content.map((line) => `\u2502 ${line.padEnd(contentWidth)} \u2502`);
1140
- return [`\u250C${border}\u2510`, ...body, `\u2514${border}\u2518`, ""].join("\n");
1172
+ const body = content.map((line) => {
1173
+ const leftPadding = Math.floor((contentWidth - line.length) / 2);
1174
+ const centeredLine = `${" ".repeat(leftPadding)}${line}${" ".repeat(contentWidth - line.length - leftPadding)}`;
1175
+ return `\u2502 ${centeredLine} \u2502`;
1176
+ });
1177
+ return [`\u256D${border}\u256E`, ...body, `\u2570${border}\u256F`, ""].join("\n");
1141
1178
  }
1142
1179
 
1143
1180
  // src/wasi-wit.ts
@@ -1457,6 +1494,7 @@ async function writeAtomically(file, bytes, expectedHash, stagingDirectory) {
1457
1494
  // src/wit.ts
1458
1495
  import * as fs8 from "node:fs";
1459
1496
  import * as path8 from "node:path";
1497
+ import { WASI_VERSION } from "@pumpkin-plugins/wasi-capabilities";
1460
1498
  function injectWasiImports(pluginWit, interfaces) {
1461
1499
  const firstExport = /^[ \t]*export /m.exec(pluginWit);
1462
1500
  if (!firstExport) throw new BuildError("could not find the exports in plugin.wit; the API layout changed");
@@ -1561,7 +1599,7 @@ function isInputOutputError(error) {
1561
1599
 
1562
1600
  // src/cli.ts
1563
1601
  async function run(args, pluginDir) {
1564
- const command = new Command().name("pumpkin-plugins-build").description("Build the Pumpkin plugin in the current directory.").version(createRequire3(import.meta.url)("../package.json").version).option("--types-only", "Generate guest declarations without building a component").option("--parse-ts", "Use Hermes TypeScript parsing for the component bundle").option("--no-update-notifier", "Skip the npm update check").configureOutput({ outputError: () => {
1602
+ const command = new Command().name("pumpkin-plugins-build").description("Build the Pumpkin plugin in the current directory.").version(createRequire4(import.meta.url)("../package.json").version).option("--types-only", "Generate guest declarations without building a component").option("--parse-ts", "Use Hermes TypeScript parsing for the component bundle").option("--no-update-notifier", "Skip the npm update check").configureOutput({ outputError: () => {
1565
1603
  } }).exitOverride();
1566
1604
  try {
1567
1605
  command.parse(args, { from: "user" });
@@ -1657,7 +1695,7 @@ async function executeCli(args, pluginDir, options = {}) {
1657
1695
  }
1658
1696
  await main(args, pluginDir);
1659
1697
  }
1660
- function resolveApiWit(pluginDir, resolvePackageJson = (root, packageSpecifier) => createRequire3(path9.join(root, "package.json")).resolve(packageSpecifier)) {
1698
+ function resolveApiWit(pluginDir, resolvePackageJson = (root, packageSpecifier) => createRequire4(path9.join(root, "package.json")).resolve(packageSpecifier)) {
1661
1699
  let packageJson;
1662
1700
  try {
1663
1701
  const canonicalSpecifier = "@pumpkin-plugins/pumpkin-api/package.json";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pumpkin-plugins/build",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Shared build tool for the plugins",
5
5
  "keywords": [
6
6
  "minecraft",
@@ -35,6 +35,7 @@
35
35
  "contributors": [
36
36
  "Filiph Sandström <filfat@hotmail.se> (https://github.com/filiphsps)"
37
37
  ],
38
+ "sideEffects": false,
38
39
  "type": "module",
39
40
  "bin": {
40
41
  "pumpkin-plugins-build": "dist/cli.js"
@@ -51,13 +52,14 @@
51
52
  "@di-framework/componentize-qjs": "0.4.4-di.3",
52
53
  "@pumpkin-plugins/minecraft-colors": "0.1.0",
53
54
  "@pumpkin-plugins/pumpkin-api": "0.1.0",
54
- "@pumpkin-plugins/static-hermes-runtime": "0.1.0",
55
+ "@pumpkin-plugins/static-hermes-runtime": "0.1.1",
56
+ "@pumpkin-plugins/wasi-capabilities": "0.1.0",
55
57
  "commander": "15.0.0",
56
58
  "esbuild": "0.28.2",
57
59
  "typescript": "6.0.3"
58
60
  },
59
61
  "devDependencies": {
60
- "@pumpkin-plugins/docs": "0.1.0",
62
+ "@pumpkin-plugins/docs": "0.1.2",
61
63
  "@types/node": "24.19.1",
62
64
  "@vitest/coverage-v8": "5.0.4",
63
65
  "vitest": "5.0.4"
@@ -69,7 +71,7 @@
69
71
  "access": "public"
70
72
  },
71
73
  "scripts": {
72
- "build": "esbuild src/cli.ts --bundle --platform=node --format=esm --target=node24 --packages=external --alias:@pumpkin-plugins/minecraft-colors=../minecraft-colors/src/index.ts --outfile=dist/cli.js",
74
+ "build": "esbuild src/cli.ts --bundle --platform=node --format=esm --target=node24 --packages=external --alias:@pumpkin-plugins/minecraft-colors=../minecraft-colors/src/index.ts --outdir=dist",
73
75
  "readme": "pumpkin-plugins-docs generate docs.ts",
74
76
  "readme:check": "pumpkin-plugins-docs generate docs.ts --check",
75
77
  "test": "vitest run",