rumdl 0.1.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.
Files changed (3) hide show
  1. package/README.md +136 -0
  2. package/bin/rumdl +326 -0
  3. package/package.json +44 -0
package/README.md ADDED
@@ -0,0 +1,136 @@
1
+ # rumdl
2
+
3
+ A fast Markdown linter written in Rust. Drop-in replacement for markdownlint-cli2.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ # npm
9
+ npm install -g rumdl
10
+
11
+ # yarn
12
+ yarn global add rumdl
13
+
14
+ # pnpm
15
+ pnpm add -g rumdl
16
+ ```
17
+
18
+ Or as a dev dependency:
19
+
20
+ ```bash
21
+ npm install --save-dev rumdl
22
+ ```
23
+
24
+ ## Usage
25
+
26
+ ```bash
27
+ # Lint markdown files
28
+ rumdl check README.md
29
+
30
+ # Lint a directory
31
+ rumdl check docs/
32
+
33
+ # Lint with glob patterns
34
+ rumdl check "**/*.md"
35
+
36
+ # Auto-fix issues
37
+ rumdl check --fix README.md
38
+
39
+ # Show all available rules
40
+ rumdl rule
41
+
42
+ # Check specific rules only
43
+ rumdl check --rules MD013,MD032 README.md
44
+
45
+ # Output as JSON
46
+ rumdl check --output-format json README.md
47
+
48
+ # Get help
49
+ rumdl --help
50
+ ```
51
+
52
+ ## Configuration
53
+
54
+ rumdl reads configuration from `.rumdl.toml` in your project root:
55
+
56
+ ```toml
57
+ [global]
58
+ line-length = 120
59
+
60
+ [MD013]
61
+ enabled = true
62
+ line-length = 120
63
+ code-blocks = false
64
+ tables = false
65
+
66
+ [MD033]
67
+ # Allow specific HTML elements
68
+ allowed-elements = ["br", "img", "a"]
69
+ ```
70
+
71
+ Configuration can also be in `pyproject.toml`:
72
+
73
+ ```toml
74
+ [tool.rumdl]
75
+ line-length = 120
76
+
77
+ [tool.rumdl.MD013]
78
+ enabled = true
79
+ ```
80
+
81
+ ## Supported Platforms
82
+
83
+ | Platform | Architecture | Package |
84
+ | -------- | --------------------- | ----------------------------- |
85
+ | macOS | Intel (x64) | `@rumdl/cli-darwin-x64` |
86
+ | macOS | Apple Silicon (ARM64) | `@rumdl/cli-darwin-arm64` |
87
+ | Linux | x64 (glibc) | `@rumdl/cli-linux-x64` |
88
+ | Linux | ARM64 (glibc) | `@rumdl/cli-linux-arm64` |
89
+ | Linux | x64 (musl/Alpine) | `@rumdl/cli-linux-x64-musl` |
90
+ | Linux | ARM64 (musl/Alpine) | `@rumdl/cli-linux-arm64-musl` |
91
+ | Windows | x64 | `@rumdl/cli-win32-x64` |
92
+
93
+ ## Troubleshooting
94
+
95
+ ### Debug Mode
96
+
97
+ If you encounter issues, enable debug mode:
98
+
99
+ ```bash
100
+ RUMDL_DEBUG=1 rumdl check README.md
101
+ ```
102
+
103
+ ### Custom Binary Path
104
+
105
+ Override the binary path:
106
+
107
+ ```bash
108
+ RUMDL_BINARY=/path/to/rumdl rumdl check README.md
109
+ ```
110
+
111
+ ### Platform Package Not Found
112
+
113
+ If npm doesn't install the correct platform package:
114
+
115
+ ```bash
116
+ # Install the platform package explicitly
117
+ npm install @rumdl/cli-darwin-arm64 # For Apple Silicon
118
+ npm install @rumdl/cli-linux-x64 # For Linux x64
119
+ ```
120
+
121
+ ### Yarn PnP
122
+
123
+ rumdl supports Yarn Plug'n'Play (PnP) out of the box. If you encounter issues:
124
+
125
+ ```bash
126
+ # Ensure the package is unplugged
127
+ yarn unplug rumdl
128
+ ```
129
+
130
+ ## Documentation
131
+
132
+ For full documentation, visit [rumdl.dev](https://rumdl.dev).
133
+
134
+ ## License
135
+
136
+ MIT
package/bin/rumdl ADDED
@@ -0,0 +1,326 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * rumdl - A fast Markdown linter written in Rust
5
+ *
6
+ * This is a JavaScript wrapper that detects the platform and spawns
7
+ * the appropriate native binary from the platform-specific package.
8
+ *
9
+ * Supports: npm, yarn, pnpm, and Yarn PnP (Plug'n'Play)
10
+ */
11
+
12
+ const { spawnSync } = require("child_process");
13
+ const { createRequire } = require("module");
14
+ const path = require("path");
15
+ const fs = require("fs");
16
+ const os = require("os");
17
+
18
+ const DEBUG = process.env.RUMDL_DEBUG === "1";
19
+
20
+ function debug(message) {
21
+ if (DEBUG) {
22
+ console.error(`[rumdl-debug] ${message}`);
23
+ }
24
+ }
25
+
26
+ /**
27
+ * Detect if we're running on musl libc (Alpine Linux, etc.)
28
+ */
29
+ function isMusl() {
30
+ // Check for Alpine Linux
31
+ if (fs.existsSync("/etc/alpine-release")) {
32
+ debug("Detected Alpine Linux via /etc/alpine-release");
33
+ return true;
34
+ }
35
+
36
+ // Check ldd output for musl (musl outputs to stderr, glibc to stdout)
37
+ try {
38
+ const result = spawnSync("ldd", ["--version"], {
39
+ encoding: "utf8",
40
+ stdio: ["pipe", "pipe", "pipe"],
41
+ });
42
+ const output = (result.stdout || "") + (result.stderr || "");
43
+ if (output.toLowerCase().includes("musl")) {
44
+ debug("Detected musl via ldd --version");
45
+ return true;
46
+ }
47
+ } catch {
48
+ // ldd not available or failed
49
+ }
50
+
51
+ // Check if libc.musl-* or ld-musl-* exists in /lib
52
+ try {
53
+ const libDirs = ["/lib", "/lib64"];
54
+ for (const libDir of libDirs) {
55
+ if (fs.existsSync(libDir)) {
56
+ const files = fs.readdirSync(libDir);
57
+ if (files.some((f) => f.startsWith("libc.musl-") || f.startsWith("ld-musl-"))) {
58
+ debug(`Detected musl via ${libDir} directory scan`);
59
+ return true;
60
+ }
61
+ }
62
+ }
63
+ } catch {
64
+ // Can't read /lib
65
+ }
66
+
67
+ // Check LD_LIBRARY_PATH for musl
68
+ const ldPath = process.env.LD_LIBRARY_PATH || "";
69
+ if (ldPath.includes("musl")) {
70
+ debug("Detected musl via LD_LIBRARY_PATH");
71
+ return true;
72
+ }
73
+
74
+ return false;
75
+ }
76
+
77
+ /**
78
+ * Get the platform-specific package name
79
+ */
80
+ function getPlatformPackage() {
81
+ const platform = os.platform();
82
+ const arch = os.arch();
83
+
84
+ debug(`Platform: ${platform}, Architecture: ${arch}`);
85
+
86
+ // Map Node.js platform/arch to our package names
87
+ const platformMap = {
88
+ darwin: {
89
+ x64: "@rumdl/cli-darwin-x64",
90
+ arm64: "@rumdl/cli-darwin-arm64",
91
+ },
92
+ linux: {
93
+ x64: isMusl() ? "@rumdl/cli-linux-x64-musl" : "@rumdl/cli-linux-x64",
94
+ arm64: isMusl() ? "@rumdl/cli-linux-arm64-musl" : "@rumdl/cli-linux-arm64",
95
+ },
96
+ win32: {
97
+ x64: "@rumdl/cli-win32-x64",
98
+ arm64: "@rumdl/cli-win32-x64", // Use x64 via emulation on Windows ARM64
99
+ },
100
+ };
101
+
102
+ const platformPackages = platformMap[platform];
103
+ if (!platformPackages) {
104
+ throw new Error(
105
+ `Unsupported platform: ${platform}\n` +
106
+ `Supported platforms: darwin (macOS), linux, win32 (Windows)\n` +
107
+ `Set RUMDL_DEBUG=1 for more information.`
108
+ );
109
+ }
110
+
111
+ const packageName = platformPackages[arch];
112
+ if (!packageName) {
113
+ throw new Error(
114
+ `Unsupported architecture: ${arch} on ${platform}\n` +
115
+ `Supported architectures: ${Object.keys(platformPackages).join(", ")}\n` +
116
+ `Set RUMDL_DEBUG=1 for more information.`
117
+ );
118
+ }
119
+
120
+ debug(`Selected package: ${packageName}`);
121
+ return packageName;
122
+ }
123
+
124
+ /**
125
+ * Check if running under Yarn PnP (Plug'n'Play)
126
+ */
127
+ function isYarnPnP() {
128
+ // Yarn PnP sets this when running via yarn
129
+ return !!process.versions.pnp;
130
+ }
131
+
132
+ /**
133
+ * Try to resolve binary using Yarn PnP API
134
+ */
135
+ function findBinaryYarnPnP(packageName, binaryName) {
136
+ try {
137
+ // Yarn PnP provides a require function that handles PnP resolution
138
+ const pnpApi = require("pnpapi");
139
+ const packagePath = pnpApi.resolveToUnqualified(`${packageName}/package.json`, __filename);
140
+ if (packagePath) {
141
+ const binaryPath = path.join(path.dirname(packagePath), binaryName);
142
+ debug(`Yarn PnP resolved: ${binaryPath}`);
143
+ if (fs.existsSync(binaryPath)) {
144
+ return binaryPath;
145
+ }
146
+ }
147
+ } catch {
148
+ debug("Yarn PnP resolution failed");
149
+ }
150
+ return null;
151
+ }
152
+
153
+ /**
154
+ * Find the binary path from the platform package
155
+ */
156
+ function findBinary() {
157
+ const attemptedPaths = [];
158
+
159
+ // Allow override via environment variable
160
+ const envBinary = process.env.RUMDL_BINARY;
161
+ if (envBinary) {
162
+ debug(`RUMDL_BINARY override: ${envBinary}`);
163
+ if (fs.existsSync(envBinary)) {
164
+ return envBinary;
165
+ }
166
+ throw new Error(`RUMDL_BINARY path does not exist: ${envBinary}`);
167
+ }
168
+
169
+ const packageName = getPlatformPackage();
170
+ const binaryName = process.platform === "win32" ? "rumdl.exe" : "rumdl";
171
+
172
+ // Strategy 0: Yarn PnP (Plug'n'Play)
173
+ // Must check first as PnP has its own module resolution
174
+ if (isYarnPnP()) {
175
+ debug("Detected Yarn PnP environment");
176
+ const pnpPath = findBinaryYarnPnP(packageName, binaryName);
177
+ if (pnpPath) {
178
+ return pnpPath;
179
+ }
180
+ attemptedPaths.push("Yarn PnP resolution");
181
+ }
182
+
183
+ // Strategy 1: Use createRequire from the current working directory
184
+ // This works for project-level installations
185
+ try {
186
+ const cwdRequire = createRequire(path.join(process.cwd(), "package.json"));
187
+ const packagePath = cwdRequire.resolve(`${packageName}/package.json`);
188
+ const binaryPath = path.join(path.dirname(packagePath), binaryName);
189
+ debug(`Strategy 1 (cwd createRequire): ${binaryPath}`);
190
+ attemptedPaths.push(binaryPath);
191
+ if (fs.existsSync(binaryPath)) {
192
+ return binaryPath;
193
+ }
194
+ } catch (e) {
195
+ debug(`Strategy 1 failed: ${e.message}`);
196
+ }
197
+
198
+ // Strategy 2: Look in sibling @rumdl directory relative to this package
199
+ // This works when packages are installed as siblings in node_modules
200
+ try {
201
+ // __dirname is npm/rumdl/bin, go up to find node_modules/@rumdl
202
+ const nodeModulesDir = path.resolve(__dirname, "..", "..");
203
+ const platformDir = path.join(nodeModulesDir, "@rumdl", packageName.replace("@rumdl/", ""));
204
+ const binaryPath = path.join(platformDir, binaryName);
205
+ debug(`Strategy 2 (sibling path): ${binaryPath}`);
206
+ attemptedPaths.push(binaryPath);
207
+ if (fs.existsSync(binaryPath)) {
208
+ return binaryPath;
209
+ }
210
+ } catch (e) {
211
+ debug(`Strategy 2 failed: ${e.message}`);
212
+ }
213
+
214
+ // Strategy 3: Use standard require.resolve
215
+ // This works for global installations
216
+ try {
217
+ const packagePath = require.resolve(`${packageName}/package.json`);
218
+ const binaryPath = path.join(path.dirname(packagePath), binaryName);
219
+ debug(`Strategy 3 (require.resolve): ${binaryPath}`);
220
+ attemptedPaths.push(binaryPath);
221
+ if (fs.existsSync(binaryPath)) {
222
+ return binaryPath;
223
+ }
224
+ } catch (e) {
225
+ debug(`Strategy 3 failed: ${e.message}`);
226
+ }
227
+
228
+ // Strategy 4: Look relative to this script (for global npm installs)
229
+ try {
230
+ const globalPath = path.resolve(__dirname, "..", "..", packageName.replace("@rumdl/", "@rumdl" + path.sep), binaryName);
231
+ debug(`Strategy 4 (global relative): ${globalPath}`);
232
+ attemptedPaths.push(globalPath);
233
+ if (fs.existsSync(globalPath)) {
234
+ return globalPath;
235
+ }
236
+ } catch (e) {
237
+ debug(`Strategy 4 failed: ${e.message}`);
238
+ }
239
+
240
+ // Build helpful error message
241
+ const debugHint = DEBUG ? "" : "\nSet RUMDL_DEBUG=1 for detailed resolution info.";
242
+ const pathsInfo = DEBUG ? `\n\nAttempted paths:\n ${attemptedPaths.join("\n ")}` : "";
243
+
244
+ throw new Error(
245
+ `Platform package ${packageName} is not installed or binary not found.\n\n` +
246
+ `Try one of the following:\n` +
247
+ ` npm install rumdl\n` +
248
+ ` npm install ${packageName}\n` +
249
+ ` yarn add rumdl\n` +
250
+ ` pnpm add rumdl` +
251
+ pathsInfo +
252
+ debugHint
253
+ );
254
+ }
255
+
256
+ /**
257
+ * Verify the binary is executable
258
+ */
259
+ function verifyBinary(binaryPath) {
260
+ try {
261
+ fs.accessSync(binaryPath, fs.constants.X_OK);
262
+ return true;
263
+ } catch {
264
+ // On Windows, X_OK check may fail but binary can still run
265
+ if (process.platform === "win32") {
266
+ return fs.existsSync(binaryPath);
267
+ }
268
+ return false;
269
+ }
270
+ }
271
+
272
+ /**
273
+ * Main entry point
274
+ */
275
+ function main() {
276
+ try {
277
+ const binaryPath = findBinary();
278
+ debug(`Using binary: ${binaryPath}`);
279
+
280
+ // Verify the binary is executable
281
+ if (!verifyBinary(binaryPath)) {
282
+ throw new Error(
283
+ `Binary found but not executable: ${binaryPath}\n` +
284
+ `Try: chmod +x "${binaryPath}"`
285
+ );
286
+ }
287
+
288
+ // Spawn the binary with all arguments
289
+ const result = spawnSync(binaryPath, process.argv.slice(2), {
290
+ stdio: "inherit",
291
+ shell: false,
292
+ });
293
+
294
+ // Handle spawn errors
295
+ if (result.error) {
296
+ if (result.error.code === "ENOENT") {
297
+ throw new Error(
298
+ `Binary not found: ${binaryPath}\n` +
299
+ `The file may have been deleted or the package is corrupted.\n` +
300
+ `Try reinstalling: npm install rumdl`
301
+ );
302
+ }
303
+ if (result.error.code === "EACCES") {
304
+ throw new Error(
305
+ `Permission denied: ${binaryPath}\n` +
306
+ `Try: chmod +x "${binaryPath}"`
307
+ );
308
+ }
309
+ throw result.error;
310
+ }
311
+
312
+ // Handle signals (e.g., SIGINT, SIGTERM)
313
+ if (result.signal) {
314
+ debug(`Process terminated by signal: ${result.signal}`);
315
+ process.kill(process.pid, result.signal);
316
+ }
317
+
318
+ // Exit with the same code as the binary
319
+ process.exit(result.status ?? 1);
320
+ } catch (error) {
321
+ console.error(`rumdl: ${error.message}`);
322
+ process.exit(1);
323
+ }
324
+ }
325
+
326
+ main();
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "rumdl",
3
+ "version": "0.1.12",
4
+ "description": "A fast Markdown linter written in Rust",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/rvben/rumdl.git"
9
+ },
10
+ "homepage": "https://rumdl.dev",
11
+ "bugs": {
12
+ "url": "https://github.com/rvben/rumdl/issues"
13
+ },
14
+ "keywords": [
15
+ "markdown",
16
+ "linter",
17
+ "lint",
18
+ "markdown-linter",
19
+ "static-analysis",
20
+ "documentation",
21
+ "markdownlint",
22
+ "mdlint",
23
+ "md"
24
+ ],
25
+ "bin": {
26
+ "rumdl": "bin/rumdl"
27
+ },
28
+ "files": [
29
+ "bin/",
30
+ "README.md"
31
+ ],
32
+ "engines": {
33
+ "node": ">=18.0.0"
34
+ },
35
+ "optionalDependencies": {
36
+ "@rumdl/cli-darwin-x64": "0.1.12",
37
+ "@rumdl/cli-darwin-arm64": "0.1.12",
38
+ "@rumdl/cli-linux-x64": "0.1.12",
39
+ "@rumdl/cli-linux-arm64": "0.1.12",
40
+ "@rumdl/cli-linux-x64-musl": "0.1.12",
41
+ "@rumdl/cli-linux-arm64-musl": "0.1.12",
42
+ "@rumdl/cli-win32-x64": "0.1.12"
43
+ }
44
+ }