katana-markdown-linter 0.17.7

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,57 @@
1
+ # katana-markdown-linter
2
+
3
+ `katana-markdown-linter` is a thin npm launcher for the `kml` Markdown linter.
4
+ The package does not contain independent lint logic. On first use, it downloads
5
+ the matching `kml` binary archive from GitHub Releases, verifies the neighboring
6
+ SHA-256 checksum, installs the binary into the wrapper cache, and then delegates
7
+ all commands to that binary, including localized CLI help.
8
+
9
+ ## Install
10
+
11
+ ~~~bash
12
+ npm install -g katana-markdown-linter
13
+ kml --version
14
+ ~~~
15
+
16
+ Use `npx` for one-off runs:
17
+
18
+ ~~~bash
19
+ npx --yes katana-markdown-linter@0.17.7 --version
20
+ npx --yes katana-markdown-linter@0.17.7 check README.md
21
+ ~~~
22
+
23
+ ## Basic Usage
24
+
25
+ ~~~bash
26
+ kml check README.md
27
+ kml check docs --config .markdownlint.json
28
+ kml --locale ja help
29
+ kml fix README.md
30
+ ~~~
31
+
32
+ ## Supported Platforms
33
+
34
+ The npm launcher uses the same binary archives as the GitHub Release channel.
35
+ It currently supports:
36
+
37
+ - macOS arm64: `aarch64-apple-darwin`
38
+ - macOS x64: `x86_64-apple-darwin`
39
+ - Linux x64: `x86_64-unknown-linux-gnu`
40
+ - Windows x64: `x86_64-pc-windows-msvc`
41
+
42
+ Unsupported platforms fail before download with an explicit platform error.
43
+
44
+ ## Wrapper Contract
45
+
46
+ - The package version selects the GitHub Release tag.
47
+ - The launcher downloads `kml-vX.Y.Z-<target>.tar.gz` or
48
+ `kml-vX.Y.Z-<target>.zip`.
49
+ - The launcher downloads the matching `.sha256` file and verifies the archive
50
+ before extraction.
51
+ - The installed binary is cached under the package-local `vendor` directory by
52
+ default.
53
+
54
+ For full CLI usage, rule coverage, and other install channels, see the
55
+ [repository README](https://github.com/HiroyukiFuruno/katana-markdown-linter).
56
+ Report package issues through
57
+ [GitHub Issues](https://github.com/HiroyukiFuruno/katana-markdown-linter/issues).
package/bin/kml.js ADDED
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env node
2
+ const { spawnSync } = require("node:child_process");
3
+ const { KmlInstaller } = require("../lib/installer");
4
+
5
+ const installer = new KmlInstaller();
6
+ const binaryPath = installer.ensureBinary();
7
+ const result = spawnSync(binaryPath, process.argv.slice(2), { stdio: "inherit" });
8
+
9
+ if (result.error) {
10
+ throw result.error;
11
+ }
12
+
13
+ process.exit(result.status === null ? 1 : result.status);
@@ -0,0 +1,112 @@
1
+ const { execFileSync } = require("node:child_process");
2
+ const { createHash } = require("node:crypto");
3
+ const fs = require("node:fs");
4
+ const os = require("node:os");
5
+ const path = require("node:path");
6
+
7
+ class KmlInstaller {
8
+ constructor() {
9
+ this.packageRoot = path.resolve(__dirname, "..");
10
+ this.packageJson = require(path.join(this.packageRoot, "package.json"));
11
+ this.version = process.env.KML_WRAPPER_VERSION || `v${this.packageJson.version}`;
12
+ this.target = this.resolveTarget();
13
+ this.archiveName = this.resolveArchiveName();
14
+ this.installRoot = process.env.KML_WRAPPER_INSTALL_DIR || path.join(this.packageRoot, "vendor");
15
+ }
16
+
17
+ ensureBinary() {
18
+ const binaryPath = path.join(this.installRoot, this.version, this.target, "bin", this.binaryName());
19
+ if (fs.existsSync(binaryPath)) {
20
+ return binaryPath;
21
+ }
22
+ const workRoot = fs.mkdtempSync(path.join(os.tmpdir(), "kml-npm-wrapper-"));
23
+ const archivePath = this.prepareArchive(workRoot);
24
+ this.verifyChecksum(archivePath);
25
+ this.extractArchive(archivePath, workRoot);
26
+ const extractedBinary = this.findExtractedBinary(workRoot);
27
+ fs.mkdirSync(path.dirname(binaryPath), { recursive: true });
28
+ fs.copyFileSync(extractedBinary, binaryPath);
29
+ fs.chmodSync(binaryPath, 0o755);
30
+ return binaryPath;
31
+ }
32
+
33
+ prepareArchive(workRoot) {
34
+ const localDir = process.env.KML_WRAPPER_ARCHIVE_DIR;
35
+ if (localDir) {
36
+ return path.join(localDir, this.archiveName);
37
+ }
38
+ const archivePath = path.join(workRoot, this.archiveName);
39
+ this.download(this.archiveUrl(this.archiveName), archivePath);
40
+ this.download(this.archiveUrl(`${this.archiveName}.sha256`), `${archivePath}.sha256`);
41
+ return archivePath;
42
+ }
43
+
44
+ verifyChecksum(archivePath) {
45
+ const checksumPath = `${archivePath}.sha256`;
46
+ const expected = fs.readFileSync(checksumPath, "utf8").trim().split(/\s+/)[0];
47
+ const actual = createHash("sha256").update(fs.readFileSync(archivePath)).digest("hex");
48
+ if (actual !== expected) {
49
+ throw new Error(`Checksum mismatch for ${this.archiveName}`);
50
+ }
51
+ }
52
+
53
+ extractArchive(archivePath, workRoot) {
54
+ const extractRoot = path.join(workRoot, "extract");
55
+ fs.mkdirSync(extractRoot, { recursive: true });
56
+ execFileSync("tar", ["-xf", archivePath, "-C", extractRoot], { stdio: "inherit" });
57
+ }
58
+
59
+ findExtractedBinary(workRoot) {
60
+ const stack = [path.join(workRoot, "extract")];
61
+ while (stack.length > 0) {
62
+ const current = stack.pop();
63
+ for (const entry of fs.readdirSync(current, { withFileTypes: true })) {
64
+ const entryPath = path.join(current, entry.name);
65
+ if (entry.isDirectory()) {
66
+ stack.push(entryPath);
67
+ } else if (entry.name === this.binaryName()) {
68
+ return entryPath;
69
+ }
70
+ }
71
+ }
72
+ throw new Error(`Archive does not contain ${this.binaryName()}`);
73
+ }
74
+
75
+ download(url, outputPath) {
76
+ execFileSync("curl", ["-fsSL", url, "-o", outputPath], { stdio: "inherit" });
77
+ }
78
+
79
+ archiveUrl(name) {
80
+ const repo = "HiroyukiFuruno/katana-markdown-linter";
81
+ return `https://github.com/${repo}/releases/download/${this.version}/${name}`;
82
+ }
83
+
84
+ resolveArchiveName() {
85
+ const suffix = this.target.includes("windows") ? "zip" : "tar.gz";
86
+ return `kml-${this.version}-${this.target}.${suffix}`;
87
+ }
88
+
89
+ resolveTarget() {
90
+ const key = `${process.platform}/${process.arch}`;
91
+ const targets = {
92
+ "darwin/arm64": "aarch64-apple-darwin",
93
+ "darwin/x64": "x86_64-apple-darwin",
94
+ "linux/x64": "x86_64-unknown-linux-gnu",
95
+ "win32/x64": "x86_64-pc-windows-msvc"
96
+ };
97
+ if (!targets[key]) {
98
+ throw new Error(`Unsupported platform for kml wrapper: ${key}`);
99
+ }
100
+ return targets[key];
101
+ }
102
+
103
+ binaryName() {
104
+ return this.target.includes("windows") ? "kml.exe" : "kml";
105
+ }
106
+ }
107
+
108
+ module.exports = { KmlInstaller };
109
+
110
+ if (require.main === module) {
111
+ new KmlInstaller().ensureBinary();
112
+ }
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "katana-markdown-linter",
3
+ "version": "0.17.7",
4
+ "description": "Thin npm launcher for the kml Markdown linter binary with localized CLI help.",
5
+ "keywords": [
6
+ "markdown",
7
+ "markdownlint",
8
+ "linter",
9
+ "kml",
10
+ "cli"
11
+ ],
12
+ "homepage": "https://github.com/HiroyukiFuruno/katana-markdown-linter#readme",
13
+ "bugs": {
14
+ "url": "https://github.com/HiroyukiFuruno/katana-markdown-linter/issues"
15
+ },
16
+ "license": "MIT",
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "https://github.com/HiroyukiFuruno/katana-markdown-linter.git"
20
+ },
21
+ "bin": {
22
+ "kml": "bin/kml.js"
23
+ },
24
+ "scripts": {
25
+ "postinstall": "node lib/installer.js"
26
+ },
27
+ "files": [
28
+ "README.md",
29
+ "bin",
30
+ "lib"
31
+ ],
32
+ "engines": {
33
+ "node": ">=18"
34
+ }
35
+ }