@asamarts/alint 0.5.12

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 ADDED
@@ -0,0 +1,71 @@
1
+ # @asamarts/alint
2
+
3
+ npm install path for [alint](https://github.com/asamarts/alint), a
4
+ language-agnostic linter for repository structure, file existence,
5
+ filename conventions, and file content rules.
6
+
7
+ This package downloads and wraps the platform-matched native alint
8
+ binary at install time. The package itself ships **no JavaScript runtime
9
+ behaviour** — it's a thin shim around the same release tarballs the
10
+ `install.sh` and Homebrew paths consume. Same SHA-256 verification, same
11
+ upstream artifacts.
12
+
13
+ ## Install
14
+
15
+ ```bash
16
+ # project-local
17
+ npm install --save-dev @asamarts/alint
18
+ npx alint check
19
+
20
+ # global (puts `alint` on PATH)
21
+ npm install -g @asamarts/alint
22
+ alint check
23
+ ```
24
+
25
+ The npm package version tracks alint releases exactly: `npm install
26
+ @asamarts/alint@0.5.11` downloads alint v0.5.11.
27
+
28
+ ## Supported platforms
29
+
30
+ | OS | Architectures |
31
+ |---------|----------------------|
32
+ | Linux | x64, arm64 (musl) |
33
+ | macOS | x64, arm64 |
34
+ | Windows | x64 |
35
+
36
+ For unsupported platforms, install via `cargo install alint`,
37
+ [Homebrew](https://github.com/asamarts/alint#homebrew), or by
38
+ downloading the tarball directly from
39
+ [GitHub Releases](https://github.com/asamarts/alint/releases).
40
+
41
+ ## Skipping the postinstall download
42
+
43
+ Set `ALINT_SKIP_INSTALL=1` before `npm install`. The shim will install
44
+ without staging a binary, and `alint` invocations will print a clear
45
+ "binary not found" error pointing at the missing step. Useful for CI
46
+ systems that snapshot `node_modules` and don't want a network hop on
47
+ every restore.
48
+
49
+ ## How it works
50
+
51
+ 1. `package.json` declares `bin/alint.js` as the npm-exposed bin.
52
+ 2. `npm install` runs `install.js` (postinstall):
53
+ - Detects `process.platform` + `process.arch`.
54
+ - Downloads
55
+ `https://github.com/asamarts/alint/releases/download/v<ver>/alint-v<ver>-<target>.tar.gz`
56
+ plus its `.sha256` companion.
57
+ - Verifies SHA-256.
58
+ - Extracts the `alint` (or `alint.exe`) binary into `bin-platform/`.
59
+ 3. `bin/alint.js` is invoked when the user runs `alint`. It locates the
60
+ staged binary and spawns it with the original argv.
61
+
62
+ ## License
63
+
64
+ Apache-2.0 OR MIT, matching the upstream alint project.
65
+
66
+ ## Links
67
+
68
+ - Project: https://github.com/asamarts/alint
69
+ - Releases: https://github.com/asamarts/alint/releases
70
+ - Issues: https://github.com/asamarts/alint/issues
71
+ - Documentation: https://alint.org (when published)
package/bin/alint.js ADDED
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env node
2
+ // alint npm shim — runtime wrapper.
3
+ //
4
+ // Locates the platform-matched native binary that `install.js`
5
+ // staged under `bin-platform/` at install time, then spawns it
6
+ // with the user's argv. On Unix we'd ideally `execvp` to replace
7
+ // the Node process (zero overhead), but that's not portable to
8
+ // Windows; spawnSync+stdio:inherit is the universal path.
9
+
10
+ 'use strict';
11
+
12
+ const path = require('path');
13
+ const fs = require('fs');
14
+ const child_process = require('child_process');
15
+
16
+ const isWindows = process.platform === 'win32';
17
+ const binaryName = isWindows ? 'alint.exe' : 'alint';
18
+ const binaryPath = path.join(__dirname, '..', 'bin-platform', binaryName);
19
+
20
+ if (!fs.existsSync(binaryPath)) {
21
+ process.stderr.write(
22
+ `@asamarts/alint: binary not found at ${binaryPath}\n` +
23
+ ` this usually means the postinstall step failed; try:\n` +
24
+ ` npm uninstall -g @asamarts/alint && npm install -g @asamarts/alint\n` +
25
+ ` or set ALINT_SKIP_INSTALL=0 if your CI suppressed it.\n`,
26
+ );
27
+ process.exit(1);
28
+ }
29
+
30
+ const result = child_process.spawnSync(binaryPath, process.argv.slice(2), {
31
+ stdio: 'inherit',
32
+ });
33
+
34
+ if (result.error) {
35
+ process.stderr.write(`@asamarts/alint: failed to spawn ${binaryPath}: ${result.error.message}\n`);
36
+ process.exit(1);
37
+ }
38
+
39
+ // `spawnSync` returns null for status when killed by a signal
40
+ // (e.g. user ^C). Forward exit code 130 (SIGINT-conventional) so
41
+ // CI runners interpret it correctly.
42
+ process.exit(result.status === null ? 130 : result.status);
package/install.js ADDED
@@ -0,0 +1,176 @@
1
+ #!/usr/bin/env node
2
+ // alint npm shim — postinstall script.
3
+ //
4
+ // Resolves the native alint binary for the running platform/arch
5
+ // from `https://github.com/asamarts/alint/releases/<tag>`, verifies
6
+ // the SHA-256 hash that ships alongside the tarball, extracts the
7
+ // binary, and stages it under `bin-platform/`. The bin shim
8
+ // (`bin/alint.js`) execs that binary at runtime.
9
+ //
10
+ // Mirrors the existing `install.sh` URL conventions exactly so the
11
+ // two install paths download byte-identical tarballs.
12
+ //
13
+ // Skipped during local development checkouts (when this script
14
+ // runs from the alint monorepo's own `npm install`) — the upstream
15
+ // release for the in-flight version may not exist yet.
16
+
17
+ 'use strict';
18
+
19
+ const fs = require('fs');
20
+ const path = require('path');
21
+ const https = require('https');
22
+ const crypto = require('crypto');
23
+ const os = require('os');
24
+ const tar = require('tar');
25
+
26
+ const REPO = 'asamarts/alint';
27
+ const PKG_VERSION = require('./package.json').version;
28
+ const VERSION_TAG = `v${PKG_VERSION}`;
29
+
30
+ // Skip when the consumer set ALINT_SKIP_INSTALL=1, e.g. for CI
31
+ // systems that snapshot node_modules and don't want the postinstall
32
+ // network hop. Also skip when running from the alint monorepo's own
33
+ // pnpm/npm install — the marker is the presence of a Cargo.toml two
34
+ // levels up. That keeps `cargo xtask` flows from accidentally trying
35
+ // to download a not-yet-released version of itself.
36
+ function shouldSkip() {
37
+ if (process.env.ALINT_SKIP_INSTALL === '1') {
38
+ return 'ALINT_SKIP_INSTALL=1';
39
+ }
40
+ const monorepoMarker = path.resolve(__dirname, '..', 'Cargo.toml');
41
+ if (fs.existsSync(monorepoMarker)) {
42
+ return 'detected alint monorepo (../Cargo.toml exists)';
43
+ }
44
+ return null;
45
+ }
46
+
47
+ // Map Node platform/arch to the alint release-target triple.
48
+ // The list mirrors install.sh and the release.yml build matrix.
49
+ function resolveTarget() {
50
+ const platform = process.platform;
51
+ const arch = process.arch;
52
+ const map = {
53
+ 'linux/x64': 'x86_64-unknown-linux-musl',
54
+ 'linux/arm64': 'aarch64-unknown-linux-musl',
55
+ 'darwin/x64': 'x86_64-apple-darwin',
56
+ 'darwin/arm64': 'aarch64-apple-darwin',
57
+ 'win32/x64': 'x86_64-pc-windows-msvc',
58
+ };
59
+ const key = `${platform}/${arch}`;
60
+ const target = map[key];
61
+ if (!target) {
62
+ throw new Error(
63
+ `unsupported platform ${platform}/${arch}\n` +
64
+ ` supported: ${Object.keys(map).join(', ')}\n` +
65
+ ` download manually from https://github.com/${REPO}/releases`,
66
+ );
67
+ }
68
+ return target;
69
+ }
70
+
71
+ // Follow up to 5 redirects (GitHub Releases hops through S3).
72
+ function fetch(url, redirects = 5) {
73
+ return new Promise((resolve, reject) => {
74
+ const req = https.get(url, (res) => {
75
+ const status = res.statusCode || 0;
76
+ if (status >= 300 && status < 400 && res.headers.location) {
77
+ if (redirects <= 0) {
78
+ reject(new Error(`too many redirects fetching ${url}`));
79
+ return;
80
+ }
81
+ res.resume();
82
+ resolve(fetch(res.headers.location, redirects - 1));
83
+ return;
84
+ }
85
+ if (status !== 200) {
86
+ reject(new Error(`HTTP ${status} fetching ${url}`));
87
+ res.resume();
88
+ return;
89
+ }
90
+ const chunks = [];
91
+ res.on('data', (c) => chunks.push(c));
92
+ res.on('end', () => resolve(Buffer.concat(chunks)));
93
+ res.on('error', reject);
94
+ });
95
+ req.on('error', reject);
96
+ req.setTimeout(30000, () => {
97
+ req.destroy(new Error(`timeout fetching ${url}`));
98
+ });
99
+ });
100
+ }
101
+
102
+ function sha256Hex(buf) {
103
+ return crypto.createHash('sha256').update(buf).digest('hex');
104
+ }
105
+
106
+ async function main() {
107
+ const skipReason = shouldSkip();
108
+ if (skipReason) {
109
+ process.stderr.write(`@asamarts/alint: skipping postinstall (${skipReason})\n`);
110
+ return;
111
+ }
112
+
113
+ const target = resolveTarget();
114
+ const archive = `alint-${VERSION_TAG}-${target}.tar.gz`;
115
+ const baseUrl = `https://github.com/${REPO}/releases/download/${VERSION_TAG}`;
116
+ const tarUrl = `${baseUrl}/${archive}`;
117
+ const shaUrl = `${tarUrl}.sha256`;
118
+
119
+ process.stderr.write(`@asamarts/alint: downloading ${archive}\n`);
120
+ const [tarBuf, shaBuf] = await Promise.all([fetch(tarUrl), fetch(shaUrl)]);
121
+
122
+ // The .sha256 file format mirrors install.sh's expectation:
123
+ // <hex> <filename>\n
124
+ // We verify against the hex column, ignoring the trailing
125
+ // filename column.
126
+ const expectedHash = shaBuf.toString('utf8').trim().split(/\s+/)[0];
127
+ const actualHash = sha256Hex(tarBuf);
128
+ if (expectedHash !== actualHash) {
129
+ throw new Error(
130
+ `SHA-256 mismatch for ${archive}\n` +
131
+ ` expected: ${expectedHash}\n` +
132
+ ` got: ${actualHash}`,
133
+ );
134
+ }
135
+ process.stderr.write(`@asamarts/alint: SHA-256 verified\n`);
136
+
137
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'alint-npm-'));
138
+ const tarPath = path.join(tmpDir, archive);
139
+ fs.writeFileSync(tarPath, tarBuf);
140
+
141
+ // Tarball layout: alint-<tag>-<target>/alint (or alint.exe).
142
+ // We extract the whole thing under tmp, then move just the
143
+ // binary into ./bin-platform/.
144
+ await tar.x({ file: tarPath, cwd: tmpDir });
145
+ const isWindows = process.platform === 'win32';
146
+ const binaryName = isWindows ? 'alint.exe' : 'alint';
147
+ const extractedDir = path.join(tmpDir, `alint-${VERSION_TAG}-${target}`);
148
+ const sourceBinary = path.join(extractedDir, binaryName);
149
+ if (!fs.existsSync(sourceBinary)) {
150
+ throw new Error(
151
+ `extracted tarball missing expected ${binaryName} at ${sourceBinary}`,
152
+ );
153
+ }
154
+
155
+ const destDir = path.join(__dirname, 'bin-platform');
156
+ fs.mkdirSync(destDir, { recursive: true });
157
+ const destBinary = path.join(destDir, binaryName);
158
+ fs.copyFileSync(sourceBinary, destBinary);
159
+ if (!isWindows) {
160
+ fs.chmodSync(destBinary, 0o755);
161
+ }
162
+
163
+ // Clean up scratch dir; ignore failures (tmp is OS-managed).
164
+ try {
165
+ fs.rmSync(tmpDir, { recursive: true, force: true });
166
+ } catch {
167
+ // ignore — tmp dir is OS-managed
168
+ }
169
+
170
+ process.stderr.write(`@asamarts/alint: installed ${binaryName} for ${target}\n`);
171
+ }
172
+
173
+ main().catch((err) => {
174
+ process.stderr.write(`@asamarts/alint: postinstall failed: ${err.message}\n`);
175
+ process.exit(1);
176
+ });
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "@asamarts/alint",
3
+ "version": "0.5.12",
4
+ "description": "Language-agnostic linter for repository structure, file existence, filename conventions, and file content rules. This package downloads and wraps the platform-matched native alint binary.",
5
+ "keywords": [
6
+ "lint",
7
+ "linter",
8
+ "repository",
9
+ "filename",
10
+ "policy",
11
+ "compliance"
12
+ ],
13
+ "homepage": "https://github.com/asamarts/alint",
14
+ "bugs": "https://github.com/asamarts/alint/issues",
15
+ "license": "(Apache-2.0 OR MIT)",
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "https://github.com/asamarts/alint.git",
19
+ "directory": "npm"
20
+ },
21
+ "bin": {
22
+ "alint": "bin/alint.js"
23
+ },
24
+ "files": [
25
+ "install.js",
26
+ "bin/alint.js",
27
+ "README.md"
28
+ ],
29
+ "scripts": {
30
+ "postinstall": "node install.js"
31
+ },
32
+ "dependencies": {
33
+ "tar": "^7.4.0"
34
+ },
35
+ "engines": {
36
+ "node": ">=18"
37
+ },
38
+ "os": [
39
+ "linux",
40
+ "darwin",
41
+ "win32"
42
+ ],
43
+ "cpu": [
44
+ "x64",
45
+ "arm64"
46
+ ]
47
+ }