markdown-table-formatter 2.0.0 → 2.1.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/README.md +12 -0
- package/lib/cli.js +14 -3
- package/lib/engine.js +4 -0
- package/lib/markdown-table-formatter.js +68 -31
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -32,6 +32,7 @@ The formatting rules come from [markdown-table-prettify](https://github.com/dark
|
|
|
32
32
|
- [Continuous Integration](#continuous-integration)
|
|
33
33
|
- [pre-commit hook](#pre-commit-hook)
|
|
34
34
|
- [Ignoring Code Blocks](#ignoring-code-blocks)
|
|
35
|
+
- [Performance](#performance)
|
|
35
36
|
- [Debug](#debug)
|
|
36
37
|
- [Contribute](#contribute)
|
|
37
38
|
- [Release notes](#release-notes)
|
|
@@ -132,6 +133,17 @@ To achieve this, place tags `<!-- markdown-table-prettify-ignore-start -->` and
|
|
|
132
133
|
|
|
133
134
|
Tables inside fenced code blocks (` ``` ` or `~~~`) and indented code blocks are never formatted.
|
|
134
135
|
|
|
136
|
+
## Performance
|
|
137
|
+
|
|
138
|
+
Formatting the 630 markdown files (11 MB) of the [MegaLinter](https://github.com/oxsecurity/megalinter) repository, with the command of its [format-tables.sh](https://github.com/oxsecurity/megalinter/blob/main/.automation/format-tables.sh) script (`find . -name "*.md" -print0 | xargs -0 -n 200 markdown-table-formatter --verbose`), on Windows 11 with Node.js 24:
|
|
139
|
+
|
|
140
|
+
| Version | Run 1 | Run 2 | Run 3 | Average | Speed |
|
|
141
|
+
|---------|--------|--------|--------|-----------|-------------|
|
|
142
|
+
| 1.7.0 | 6.38 s | 6.07 s | 5.87 s | **6.1 s** | 1x |
|
|
143
|
+
| 2.1.0 | 1.41 s | 1.44 s | 1.40 s | **1.4 s** | 4.3x faster |
|
|
144
|
+
|
|
145
|
+
Tip: send all files in a single call when possible, as each call starts Node.js again.
|
|
146
|
+
|
|
135
147
|
## Debug
|
|
136
148
|
|
|
137
149
|
Set environment variable `NODE_DEBUG=markdown-table-formatter` to display debug logs.
|
package/lib/cli.js
CHANGED
|
@@ -16,9 +16,16 @@ function findFiles(pattern) {
|
|
|
16
16
|
EXCLUDED_DIRS.has(
|
|
17
17
|
path.basename(typeof entry === "string" ? entry : entry.name),
|
|
18
18
|
);
|
|
19
|
+
// Entries come with their type, so only symbolic links need a stat call
|
|
19
20
|
return fs
|
|
20
|
-
.globSync(pattern, { exclude })
|
|
21
|
-
.
|
|
21
|
+
.globSync(pattern, { exclude, withFileTypes: true })
|
|
22
|
+
.flatMap((entry) => {
|
|
23
|
+
const file = path.join(entry.parentPath, entry.name);
|
|
24
|
+
const isFile = entry.isSymbolicLink()
|
|
25
|
+
? fs.statSync(file).isFile()
|
|
26
|
+
: entry.isFile();
|
|
27
|
+
return isFile ? [file] : [];
|
|
28
|
+
})
|
|
22
29
|
.sort();
|
|
23
30
|
}
|
|
24
31
|
|
|
@@ -62,7 +69,11 @@ class MarkdownTableFormatterCli {
|
|
|
62
69
|
// use sent glob expression
|
|
63
70
|
files = findFiles(files[0]);
|
|
64
71
|
}
|
|
65
|
-
|
|
72
|
+
// The CLI does not use file contents: don't keep them in memory
|
|
73
|
+
const { status, updates } = await formatter.run(files, {
|
|
74
|
+
...this.options,
|
|
75
|
+
keepContents: false,
|
|
76
|
+
});
|
|
66
77
|
// Manage result
|
|
67
78
|
if (status === 0 && updates.length === 0) {
|
|
68
79
|
console.log("No markdown table formatting has been applied.");
|
package/lib/engine.js
CHANGED
|
@@ -25,6 +25,10 @@ function getWasm() {
|
|
|
25
25
|
* @return {string} - Formatted markdown content
|
|
26
26
|
*/
|
|
27
27
|
function formatTables(text, columnPadding = 0) {
|
|
28
|
+
// A text without pipe holds no table: the engine would return it unchanged
|
|
29
|
+
if (!text.includes("|")) {
|
|
30
|
+
return text;
|
|
31
|
+
}
|
|
28
32
|
const engine = getWasm();
|
|
29
33
|
const input = Buffer.from(text, "utf8");
|
|
30
34
|
const inputPtr = engine.mtf_alloc(input.length);
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
#! /usr/bin/env node
|
|
2
2
|
const fs = require("fs/promises");
|
|
3
|
+
const path = require("path");
|
|
3
4
|
const debug = require("util").debuglog("markdown-table-formatter");
|
|
4
5
|
const { formatTables } = require("./engine");
|
|
5
6
|
|
|
7
|
+
// Number of files read and written at the same time
|
|
8
|
+
const MAX_PARALLEL_FILES = 32;
|
|
9
|
+
|
|
6
10
|
class MarkdownTableFormatter {
|
|
7
11
|
"use strict";
|
|
8
12
|
|
|
@@ -26,10 +30,50 @@ class MarkdownTableFormatter {
|
|
|
26
30
|
return formatTables(markdownString, options.columnpadding || 0);
|
|
27
31
|
}
|
|
28
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Formats one file
|
|
35
|
+
* @param {string} file - File path
|
|
36
|
+
* @param {object} options - Run options (see run)
|
|
37
|
+
* @return {Promise<object>} - File result (file, updated, before, after)
|
|
38
|
+
*/
|
|
39
|
+
async formatFile(file, options) {
|
|
40
|
+
if (debug.enabled) {
|
|
41
|
+
debug(`[file] ${file}`);
|
|
42
|
+
}
|
|
43
|
+
const markdownString = await fs.readFile(file, "utf8");
|
|
44
|
+
if (debug.enabled) {
|
|
45
|
+
debug(`[file before]\n${markdownString}`);
|
|
46
|
+
}
|
|
47
|
+
const markdownStringPrettified = MarkdownTableFormatter.formatText(
|
|
48
|
+
markdownString,
|
|
49
|
+
options,
|
|
50
|
+
);
|
|
51
|
+
const updated = markdownStringPrettified !== markdownString;
|
|
52
|
+
// Update file if necessary
|
|
53
|
+
if (updated && !this.checkOnly) {
|
|
54
|
+
await fs.writeFile(file, markdownStringPrettified);
|
|
55
|
+
debug(`[updated] ${file} with:\n${markdownStringPrettified}`);
|
|
56
|
+
if (options.verbose) {
|
|
57
|
+
console.info(`- Updated file ${file}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
const keepContents = updated && options.keepContents !== false;
|
|
61
|
+
return {
|
|
62
|
+
file,
|
|
63
|
+
updated,
|
|
64
|
+
before: keepContents ? markdownString : null,
|
|
65
|
+
after: keepContents ? markdownStringPrettified : null,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
29
69
|
/**
|
|
30
70
|
* Runs markdown table formatter
|
|
31
71
|
* @param {string[]} [files] - List of files to format
|
|
32
|
-
* @
|
|
72
|
+
* @param {object} [options] - Run options
|
|
73
|
+
* @param {number} [options.columnpadding] - Number of spaces around cell values
|
|
74
|
+
* @param {boolean} [options.verbose] - Log analyzed and updated files
|
|
75
|
+
* @param {boolean} [options.keepContents] - Set to false to return null before / after contents (saves memory)
|
|
76
|
+
* @return {Promise<any>} - Command result (status, fileResults, updates)
|
|
33
77
|
*/
|
|
34
78
|
async run(files, options = {}) {
|
|
35
79
|
if (options.verbose) {
|
|
@@ -38,38 +82,31 @@ class MarkdownTableFormatter {
|
|
|
38
82
|
console.info(`- ${file}`);
|
|
39
83
|
}
|
|
40
84
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
if (options.verbose) {
|
|
62
|
-
console.info(`- Updated file ${file}`);
|
|
85
|
+
// Several files are read and written at the same time; results keep the order of files
|
|
86
|
+
const fileResults = new Array(files.length);
|
|
87
|
+
// A file listed twice is processed after its previous occurrence, never at the same time
|
|
88
|
+
const pending = new Map();
|
|
89
|
+
let next = 0;
|
|
90
|
+
let failed = false;
|
|
91
|
+
const worker = async () => {
|
|
92
|
+
while (next < files.length && !failed) {
|
|
93
|
+
const index = next++;
|
|
94
|
+
const file = files[index];
|
|
95
|
+
const key = path.resolve(file);
|
|
96
|
+
const result = (pending.get(key) || Promise.resolve())
|
|
97
|
+
.catch(() => {})
|
|
98
|
+
.then(() => this.formatFile(file, options));
|
|
99
|
+
pending.set(key, result);
|
|
100
|
+
try {
|
|
101
|
+
fileResults[index] = await result;
|
|
102
|
+
} catch (error) {
|
|
103
|
+
failed = true;
|
|
104
|
+
throw error;
|
|
63
105
|
}
|
|
64
106
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
before: updated ? markdownString : null,
|
|
69
|
-
after: updated ? markdownStringPrettified : null,
|
|
70
|
-
};
|
|
71
|
-
fileResults.push(fileResult);
|
|
72
|
-
}
|
|
107
|
+
};
|
|
108
|
+
const workerCount = Math.min(MAX_PARALLEL_FILES, files.length);
|
|
109
|
+
await Promise.all(Array.from({ length: workerCount }, worker));
|
|
73
110
|
const updates = fileResults
|
|
74
111
|
.filter((fileResult) => fileResult.updated)
|
|
75
112
|
.map((fileResult) => fileResult.file);
|