@tomasko/mb 0.0.0-stage → 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.
package/README.md CHANGED
@@ -1,3 +1,88 @@
1
- # Temporary Holding Version
1
+ # mb (npm)
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ npm wrapper for [`mb`](../../README.md) — a MoonBit-native, uv-style toolchain
4
+ and package manager.
5
+
6
+ ```bash
7
+ pnpm install -g @tomasko/mb
8
+ mb --help
9
+ mb moon install 0.10.14
10
+ mb tool install bobzhang/zip
11
+ ```
12
+
13
+ ## How it works
14
+
15
+ The package itself contains only a small launcher. On install, `install.js`
16
+ downloads the prebuilt `mb` binary for the current platform from GitHub
17
+ Releases and writes it to `bin/mb`. The published `bin` entry (`bin/mb.js`)
18
+ then spawns it, forwarding arguments, standard IO and the exit code.
19
+
20
+ The postinstall download is best-effort, and the launcher downloads the
21
+ binary on first run if it is missing (for example when installing with
22
+ `--ignore-scripts`, or when the release was not available yet).
23
+
24
+ Supported targets:
25
+
26
+ | Node `platform-arch` | Release asset |
27
+ | -------------------- | ------------------- |
28
+ | `linux-x64` | `mb-linux-x86_64` |
29
+ | `linux-arm64` | `mb-linux-aarch64` |
30
+ | `darwin-arm64` | `mb-darwin-aarch64` |
31
+
32
+ Intel macOS (`darwin-x64`) is not supported: the MoonBit toolchain no longer
33
+ ships a `darwin-x86_64` target, so no such build can be produced.
34
+
35
+ Assets are produced by `.github/workflows/release.yml` when a `v*` tag is
36
+ pushed.
37
+
38
+ ## Install scripts are blocked by default
39
+
40
+ Recent npm (12+) and pnpm (10+) versions do **not** run a dependency's install
41
+ scripts unless the package is approved, so `postinstall` is skipped and
42
+ `bin/mb` is missing right after install:
43
+
44
+ ```
45
+ npm warn install-scripts 1 package had install scripts blocked because they are not covered by allowScripts:
46
+ npm warn install-scripts @tomasko/mb@0.1.1 (postinstall: node install.js)
47
+ mb: native binary not found at .../node_modules/@tomasko/mb/bin/mb
48
+ ```
49
+
50
+ Nothing is broken: `bin/mb.js` downloads the binary on first run. To have it
51
+ downloaded at install time instead, approve the build once:
52
+
53
+ ```bash
54
+ npm install-scripts approve @tomasko/mb # npm 12+ (writes `allowScripts`)
55
+ pnpm approve-builds # pnpm 10+
56
+ ```
57
+
58
+ Either way, re-run the download manually with:
59
+
60
+ ```bash
61
+ npm rebuild @tomasko/mb
62
+ # or
63
+ pnpm rebuild @tomasko/mb
64
+ ```
65
+
66
+ ## Overrides
67
+
68
+ | Variable | Purpose |
69
+ | -------------------- | ------------------------------------------------ |
70
+ | `MB_REPO` | `owner/repo` to download from |
71
+ | `MB_VERSION` | release version to download |
72
+ | `MB_DOWNLOAD_BASE` | full base URL (mirror), e.g. `https://host/assets` |
73
+ | `MB_FORCE_DOWNLOAD` | re-download even if the binary is present |
74
+ | `MB_SKIP_DOWNLOAD` | skip the download entirely |
75
+
76
+ ## Publishing
77
+
78
+ From this directory:
79
+
80
+ ```bash
81
+ npm publish --access public
82
+ # or
83
+ pnpm publish --access public
84
+ ```
85
+
86
+ `--access public` is required for scoped packages such as `@tomasko/mb`.
87
+ Change `name`, `repository` and `homepage` in `package.json` first if you
88
+ publish under a different scope or repository.
package/bin/mb.js ADDED
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env node
2
+ // Launcher for the native `mb` binary. Downloads it on first run if the
3
+ // postinstall step did not (e.g. `--ignore-scripts`), then forwards all
4
+ // arguments, stdio and the exit code.
5
+ "use strict";
6
+
7
+ const { spawnSync } = require("child_process");
8
+ const { ensureBinary, pkg } = require("../lib");
9
+
10
+ async function main() {
11
+ let binary;
12
+ try {
13
+ binary = await ensureBinary({
14
+ log: (line) => process.stderr.write(line + "\n"),
15
+ });
16
+ } catch (err) {
17
+ console.error(`mb: ${err.message}`);
18
+ console.error(
19
+ `mb: run \`npm rebuild ${pkg.name}\` to retry, or set MB_DOWNLOAD_BASE ` +
20
+ `(or MB_REPO/MB_VERSION) to a mirror.`
21
+ );
22
+ process.exit(1);
23
+ }
24
+
25
+ const result = spawnSync(binary, process.argv.slice(2), { stdio: "inherit" });
26
+
27
+ if (result.error) {
28
+ console.error(`mb: failed to launch ${binary}: ${result.error.message}`);
29
+ process.exit(1);
30
+ }
31
+
32
+ process.exit(typeof result.status === "number" ? result.status : 1);
33
+ }
34
+
35
+ main();
package/install.js ADDED
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env node
2
+ // Postinstall hook: download the prebuilt `mb` binary for this platform.
3
+ //
4
+ // This is best-effort: if the download fails the install still succeeds, and
5
+ // `bin/mb.js` retries on first run. Set MB_SKIP_DOWNLOAD=1 to opt out.
6
+ "use strict";
7
+
8
+ const { ensureBinary, pkg } = require("./lib");
9
+
10
+ if (process.env.MB_SKIP_DOWNLOAD) {
11
+ console.log("mb: MB_SKIP_DOWNLOAD set, skipping binary download");
12
+ process.exit(0);
13
+ }
14
+
15
+ ensureBinary({ log: console.log })
16
+ .then(() => process.exit(0))
17
+ .catch((err) => {
18
+ console.warn(`mb: could not download the native binary: ${err.message}`);
19
+ console.warn(
20
+ `mb: it will be downloaded on first run instead. If that also fails, ` +
21
+ `set MB_DOWNLOAD_BASE (or MB_REPO/MB_VERSION) to a mirror and run ` +
22
+ `\`npm rebuild ${pkg.name}\`.`
23
+ );
24
+ process.exit(0);
25
+ });
package/lib.js ADDED
@@ -0,0 +1,143 @@
1
+ "use strict";
2
+
3
+ // Shared install logic for the npm wrapper: works out which prebuilt `mb`
4
+ // binary to use and downloads it from GitHub Releases on demand.
5
+ //
6
+ // Overridable for forks / private mirrors:
7
+ // MB_REPO owner/repo (default: parsed from package.json)
8
+ // MB_VERSION release version (default: package.json version)
9
+ // MB_DOWNLOAD_BASE full base URL (overrides MB_REPO/MB_VERSION)
10
+ // MB_FORCE_DOWNLOAD re-download even if the binary is present
11
+ // MB_SKIP_DOWNLOAD never download
12
+
13
+ const fs = require("fs");
14
+ const path = require("path");
15
+ const https = require("https");
16
+ const http = require("http");
17
+
18
+ const pkg = require("./package.json");
19
+
20
+ // Node `platform-arch` -> MoonBit release target. Intel macOS is absent on
21
+ // purpose: the MoonBit toolchain no longer ships a darwin-x86_64 target.
22
+ const TARGETS = {
23
+ "linux-x64": "linux-x86_64",
24
+ "linux-arm64": "linux-aarch64",
25
+ "darwin-arm64": "darwin-aarch64",
26
+ };
27
+
28
+ function repoSlug(repo) {
29
+ if (!repo) return null;
30
+ const url = typeof repo === "string" ? repo : repo.url || "";
31
+ const m = url.match(/github\.com[/:]([^/]+)\/([^/.]+)/);
32
+ return m ? `${m[1]}/${m[2]}` : null;
33
+ }
34
+
35
+ function targetName() {
36
+ return TARGETS[`${process.platform}-${process.arch}`];
37
+ }
38
+
39
+ function downloadUrl(target) {
40
+ if (process.env.MB_DOWNLOAD_BASE) {
41
+ return `${process.env.MB_DOWNLOAD_BASE.replace(/\/$/, "")}/mb-${target}`;
42
+ }
43
+ const repo = process.env.MB_REPO || repoSlug(pkg.repository) || "tomasky/mb";
44
+ const version = process.env.MB_VERSION || pkg.version;
45
+ return `https://github.com/${repo}/releases/download/v${version}/mb-${target}`;
46
+ }
47
+
48
+ function binaryPath() {
49
+ return path.join(
50
+ __dirname,
51
+ "bin",
52
+ process.platform === "win32" ? "mb.exe" : "mb"
53
+ );
54
+ }
55
+
56
+ function isInstalled() {
57
+ try {
58
+ fs.accessSync(binaryPath(), fs.constants.X_OK);
59
+ return true;
60
+ } catch {
61
+ return false;
62
+ }
63
+ }
64
+
65
+ function fetch(url, redirects) {
66
+ return new Promise((resolve, reject) => {
67
+ if (redirects > 10) return reject(new Error("too many redirects"));
68
+ const client = url.startsWith("http:") ? http : https;
69
+ const req = client.get(
70
+ url,
71
+ { headers: { "user-agent": `mb-npm/${pkg.version}` } },
72
+ (res) => {
73
+ const { statusCode, headers } = res;
74
+ if (statusCode >= 300 && statusCode < 400 && headers.location) {
75
+ res.resume();
76
+ const next = new URL(headers.location, url).toString();
77
+ resolve(fetch(next, (redirects || 0) + 1));
78
+ return;
79
+ }
80
+ if (statusCode !== 200) {
81
+ res.resume();
82
+ reject(new Error(`GET ${url} -> HTTP ${statusCode}`));
83
+ return;
84
+ }
85
+ resolve(res);
86
+ }
87
+ );
88
+ req.on("error", reject);
89
+ });
90
+ }
91
+
92
+ async function download(target, dest) {
93
+ const url = downloadUrl(target);
94
+ const res = await fetch(url, 0);
95
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
96
+ await new Promise((resolve, reject) => {
97
+ const out = fs.createWriteStream(dest);
98
+ res.pipe(out);
99
+ out.on("finish", () => out.close(resolve));
100
+ out.on("error", reject);
101
+ res.on("error", reject);
102
+ });
103
+ fs.chmodSync(dest, 0o755);
104
+ return url;
105
+ }
106
+
107
+ // Resolves to a ready-to-run binary path, downloading it when missing.
108
+ async function ensureBinary({ force = false, log = () => {} } = {}) {
109
+ const dest = binaryPath();
110
+
111
+ if (!force && !process.env.MB_FORCE_DOWNLOAD && isInstalled()) {
112
+ return dest;
113
+ }
114
+
115
+ if (process.env.MB_SKIP_DOWNLOAD) {
116
+ if (isInstalled()) return dest;
117
+ throw new Error("MB_SKIP_DOWNLOAD is set but no binary is installed");
118
+ }
119
+
120
+ const target = targetName();
121
+ if (!target) {
122
+ throw new Error(
123
+ `unsupported platform ${process.platform}-${process.arch}; mb ` +
124
+ `supports linux-x64, linux-arm64 and darwin-arm64 (the MoonBit ` +
125
+ `toolchain no longer ships a darwin-x64 target)`
126
+ );
127
+ }
128
+
129
+ log(`mb: downloading ${downloadUrl(target)}`);
130
+ await download(target, dest);
131
+ log(`mb: installed ${dest}`);
132
+ return dest;
133
+ }
134
+
135
+ module.exports = {
136
+ pkg,
137
+ TARGETS,
138
+ targetName,
139
+ downloadUrl,
140
+ binaryPath,
141
+ isInstalled,
142
+ ensureBinary,
143
+ };
package/package.json CHANGED
@@ -1,6 +1,43 @@
1
1
  {
2
2
  "name": "@tomasko/mb",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.2",
4
+ "description": "A MoonBit-native, uv-style toolchain and package manager: manage MoonBit toolchains and install tools from mooncakes.io.",
5
+ "license": "Apache-2.0",
6
+ "bin": {
7
+ "mb": "bin/mb.js"
8
+ },
9
+ "scripts": {
10
+ "postinstall": "node install.js"
11
+ },
12
+ "files": [
13
+ "bin/mb.js",
14
+ "install.js",
15
+ "lib.js",
16
+ "README.md"
17
+ ],
18
+ "engines": {
19
+ "node": ">=20"
20
+ },
21
+ "os": [
22
+ "darwin",
23
+ "linux"
24
+ ],
25
+ "cpu": [
26
+ "x64",
27
+ "arm64"
28
+ ],
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "https://github.com/tomasky/mb.git"
32
+ },
33
+ "homepage": "https://github.com/tomasky/mb",
34
+ "bugs": "https://github.com/tomasky/mb/issues",
35
+ "keywords": [
36
+ "moonbit",
37
+ "mooncakes",
38
+ "toolchain",
39
+ "package-manager",
40
+ "uv"
41
+ ],
42
+ "preferGlobal": true
43
+ }