quantum-forge 2.6.5 → 2.7.0
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/package.json +5 -1
- package/quantum-forge-sw.js +1 -1
- package/scripts/cli.mjs +137 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "quantum-forge",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.7.0",
|
|
4
4
|
"description": "Quantum Forge WASM loader, property manager, and Vite plugin for quantum game development (Qutrit Edition d3n12 + Qubit Edition d2n20).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -17,8 +17,12 @@
|
|
|
17
17
|
"import": "./dist/lib/vite-plugin.js"
|
|
18
18
|
}
|
|
19
19
|
},
|
|
20
|
+
"bin": {
|
|
21
|
+
"quantum-forge": "scripts/cli.mjs"
|
|
22
|
+
},
|
|
20
23
|
"files": [
|
|
21
24
|
"dist/",
|
|
25
|
+
"scripts/cli.mjs",
|
|
22
26
|
"scripts/prepare.mjs",
|
|
23
27
|
"scripts/copy-quantum-forge.mjs",
|
|
24
28
|
"scripts/build-quantum-forge-variant.mjs",
|
package/quantum-forge-sw.js
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
// published. An unprocessed copy falls back to "dev", which still works — the
|
|
21
21
|
// fetch handler revalidates in the background, so a deployment cannot pin old
|
|
22
22
|
// artifacts forever even when the cache name never changes.
|
|
23
|
-
const QF_VERSION = "2.
|
|
23
|
+
const QF_VERSION = "2.7.0";
|
|
24
24
|
|
|
25
25
|
// Scope the cache to this registration so two Quantum Forge games served from
|
|
26
26
|
// the same origin (e.g. /game1/ and /game2/ on GitHub Pages) cannot delete
|
package/scripts/cli.mjs
ADDED
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The `quantum-forge` command.
|
|
5
|
+
*
|
|
6
|
+
* The CLI itself (init, add-system, validate, doctor) lives in the engine
|
|
7
|
+
* package: scaffolding needs the engine's templates and its vendored example.
|
|
8
|
+
* npx resolves a command by package name, so `npx quantum-forge init` fetched
|
|
9
|
+
* this package, found no executable, and died with "could not determine
|
|
10
|
+
* executable to run". This file is that executable. It hands every argument
|
|
11
|
+
* to the engine CLI:
|
|
12
|
+
*
|
|
13
|
+
* 1. An engine installed next to this package (a project's node_modules,
|
|
14
|
+
* or the monorepo checkout) runs in this process.
|
|
15
|
+
* 2. Otherwise `npm exec` fetches the engine and runs its CLI, exactly as
|
|
16
|
+
* `npx quantum-forge-engine ...` would.
|
|
17
|
+
*
|
|
18
|
+
* Both spellings therefore work everywhere:
|
|
19
|
+
*
|
|
20
|
+
* npx quantum-forge init my-game
|
|
21
|
+
* npx quantum-forge-engine init my-game
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import fs from "fs";
|
|
25
|
+
import path from "path";
|
|
26
|
+
import { fileURLToPath, pathToFileURL } from "url";
|
|
27
|
+
import { spawnSync } from "child_process";
|
|
28
|
+
|
|
29
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
30
|
+
const PACKAGE_DIR = path.resolve(__dirname, "..");
|
|
31
|
+
|
|
32
|
+
function readPackageName() {
|
|
33
|
+
try {
|
|
34
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(PACKAGE_DIR, "package.json"), "utf-8"));
|
|
35
|
+
if (typeof pkg.name === "string" && pkg.name) return pkg.name;
|
|
36
|
+
} catch {
|
|
37
|
+
// fall through to the public name
|
|
38
|
+
}
|
|
39
|
+
return "quantum-forge";
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// "quantum-forge" -> "quantum-forge-engine", and the scoped name to its scoped
|
|
43
|
+
// sibling, so a GitHub Packages install never reaches for the public registry.
|
|
44
|
+
const ENGINE = `${readPackageName()}-engine`;
|
|
45
|
+
const ENGINE_CLI = path.join("scripts", "init.mjs");
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* The engine's CLI script, if an engine is installed where this command can
|
|
49
|
+
* see it. The engine's `exports` map hides scripts/ from import resolution,
|
|
50
|
+
* so this walks the filesystem the way Node's resolver would.
|
|
51
|
+
*/
|
|
52
|
+
function findLocalEngineCli() {
|
|
53
|
+
const candidates = [];
|
|
54
|
+
|
|
55
|
+
// The project the command runs in, walking up to the filesystem root.
|
|
56
|
+
let dir = process.cwd();
|
|
57
|
+
for (;;) {
|
|
58
|
+
candidates.push(path.join(dir, "node_modules", ENGINE, ENGINE_CLI));
|
|
59
|
+
const parent = path.dirname(dir);
|
|
60
|
+
if (parent === dir) break;
|
|
61
|
+
dir = parent;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// The node_modules this package sits in: one level up for an unscoped
|
|
65
|
+
// install, two for a scoped one.
|
|
66
|
+
const scoped = path.basename(path.dirname(PACKAGE_DIR)).startsWith("@");
|
|
67
|
+
const modulesRoot = scoped ? path.dirname(path.dirname(PACKAGE_DIR)) : path.dirname(PACKAGE_DIR);
|
|
68
|
+
candidates.push(path.join(modulesRoot, ENGINE, ENGINE_CLI));
|
|
69
|
+
|
|
70
|
+
// The monorepo checkout: packages/core next to packages/engine.
|
|
71
|
+
candidates.push(path.join(path.dirname(PACKAGE_DIR), "engine", ENGINE_CLI));
|
|
72
|
+
|
|
73
|
+
return candidates.find((candidate) => fs.existsSync(candidate)) || null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* How to invoke npm without a shell, so there is nothing to quote on Windows.
|
|
78
|
+
* npm and npx set `npm_execpath` for the processes they launch; pnpm and Yarn
|
|
79
|
+
* set it too, pointing at their own CLIs, which do not understand `npm exec`,
|
|
80
|
+
* so only npm's own files are accepted. Without it, npm sits next to the Node
|
|
81
|
+
* binary (or under libexec for Homebrew). On Unix the `npm` on PATH is a plain
|
|
82
|
+
* script, so it can be spawned by name as a last resort; on Windows it is a
|
|
83
|
+
* .cmd file that needs a shell, which is exactly what this avoids.
|
|
84
|
+
*/
|
|
85
|
+
function npmCommand() {
|
|
86
|
+
const candidates = [];
|
|
87
|
+
const execpath = process.env.npm_execpath;
|
|
88
|
+
if (execpath) {
|
|
89
|
+
const base = path.basename(execpath);
|
|
90
|
+
if (base === "npm-cli.js") candidates.push(execpath);
|
|
91
|
+
if (base === "npx-cli.js") candidates.push(path.join(path.dirname(execpath), "npm-cli.js"));
|
|
92
|
+
}
|
|
93
|
+
const nodeDir = path.dirname(process.execPath);
|
|
94
|
+
candidates.push(
|
|
95
|
+
// Windows: node.exe and node_modules/npm side by side.
|
|
96
|
+
path.join(nodeDir, "node_modules", "npm", "bin", "npm-cli.js"),
|
|
97
|
+
// Unix: bin/node next to lib/node_modules/npm.
|
|
98
|
+
path.join(nodeDir, "..", "lib", "node_modules", "npm", "bin", "npm-cli.js"),
|
|
99
|
+
// Homebrew: the Cellar keeps npm under libexec.
|
|
100
|
+
path.join(nodeDir, "..", "libexec", "lib", "node_modules", "npm", "bin", "npm-cli.js"),
|
|
101
|
+
);
|
|
102
|
+
const cli = candidates.find((candidate) => fs.existsSync(candidate));
|
|
103
|
+
if (cli) return { file: process.execPath, prefix: [cli] };
|
|
104
|
+
if (process.platform !== "win32") return { file: "npm", prefix: [] };
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const args = process.argv.slice(2);
|
|
109
|
+
const localCli = findLocalEngineCli();
|
|
110
|
+
|
|
111
|
+
if (localCli) {
|
|
112
|
+
// Same process, same argv: the engine CLI reads process.argv itself.
|
|
113
|
+
await import(pathToFileURL(localCli).href);
|
|
114
|
+
} else {
|
|
115
|
+
const direct = `npx ${ENGINE} ${args.join(" ")}`.trim();
|
|
116
|
+
const npm = npmCommand();
|
|
117
|
+
if (!npm) {
|
|
118
|
+
process.stderr.write(
|
|
119
|
+
`quantum-forge: the CLI ships in ${ENGINE}, and npm could not be located to fetch it.\n` +
|
|
120
|
+
`Run it directly: ${direct}\n`,
|
|
121
|
+
);
|
|
122
|
+
process.exit(1);
|
|
123
|
+
}
|
|
124
|
+
process.stderr.write(`quantum-forge: the CLI ships in ${ENGINE}; fetching it with npm exec...\n`);
|
|
125
|
+
// `npm exec -- <pkg> [args]` runs the package's only bin, whatever it is
|
|
126
|
+
// named, and downloads the package first when it is not installed.
|
|
127
|
+
const result = spawnSync(npm.file, [...npm.prefix, "exec", "--yes", "--", ENGINE, ...args], {
|
|
128
|
+
stdio: "inherit",
|
|
129
|
+
});
|
|
130
|
+
if (result.error) {
|
|
131
|
+
process.stderr.write(
|
|
132
|
+
`quantum-forge: could not run npm (${result.error.message}).\nRun it directly: ${direct}\n`,
|
|
133
|
+
);
|
|
134
|
+
process.exit(1);
|
|
135
|
+
}
|
|
136
|
+
process.exit(result.status ?? 1);
|
|
137
|
+
}
|