markdown-table-formatter 2.0.1-202611010112.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 CHANGED
@@ -6,7 +6,7 @@
6
6
  [![Downloads/week](https://img.shields.io/npm/dw/markdown-table-formatter.svg)](https://npmjs.org/package/markdown-table-formatter)
7
7
  [![Downloads/total](https://img.shields.io/npm/dt/markdown-table-formatter.svg)](https://npmjs.org/package/markdown-table-formatter)
8
8
  [![Mega-Linter](https://github.com/nvuillam/markdown-table-formatter/workflows/Mega-Linter/badge.svg)](https://github.com/nvuillam/mega-linter#readme)<!-- gh-dependents-info-used-by-start -->
9
- [![Generated by github-dependents-info](https://img.shields.io/static/v1?label=Used%20by&message=92&color=informational&logo=slickpic)](https://github.com/nvuillam/markdown-table-formatter/blob/main/docs/github-dependents-info.md)<!-- gh-dependents-info-used-by-end -->
9
+ [![Generated by github-dependents-info](https://img.shields.io/static/v1?label=Used%20by&message=164&color=informational&logo=slickpic)](https://github.com/nvuillam/markdown-table-formatter/blob/main/docs/github-dependents-info.md)<!-- gh-dependents-info-used-by-end -->
10
10
  [![codecov](https://codecov.io/gh/nvuillam/markdown-table-formatter/branch/master/graph/badge.svg)](https://codecov.io/gh/nvuillam/markdown-table-formatter)
11
11
  [![GitHub contributors](https://img.shields.io/github/contributors/nvuillam/markdown-table-formatter.svg)](https://gitHub.com/nvuillam/markdown-table-formatter/graphs/contributors/)
12
12
  [![GitHub stars](https://img.shields.io/github/stars/nvuillam/markdown-table-formatter?label=Star&maxAge=2592000)](https://GitHub.com/nvuillam/markdown-table-formatter/stargazers/)
@@ -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
- .filter((file) => fs.statSync(file).isFile())
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
- const { status, updates } = await formatter.run(files, this.options);
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
- * @return {Promise<any>} - Command result (status, stdout, stderr, childJavaProcess)
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
- const fileResults = [];
42
- // Read files and call prettify on each of them
43
- for (const file of files) {
44
- if (debug.enabled) {
45
- debug(`[file] ${file}`);
46
- }
47
- const markdown = await fs.readFile(file);
48
- const markdownString = markdown.toString();
49
- if (debug.enabled) {
50
- debug(`[file before]\n${markdownString}`);
51
- }
52
- const markdownStringPrettified = MarkdownTableFormatter.formatText(
53
- markdownString,
54
- options,
55
- );
56
- const updated = markdownStringPrettified !== markdownString;
57
- // Update file if necessary
58
- if (updated && !this.checkOnly) {
59
- await fs.writeFile(file, markdownStringPrettified);
60
- debug(`[updated] ${file} with:\n${markdownStringPrettified}`);
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
- const fileResult = {
66
- file,
67
- updated,
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);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "markdown-table-formatter",
3
- "version": "2.0.1-202611010112.0",
3
+ "version": "2.1.0",
4
4
  "description": "Format markdown tables in files (Rust engine compiled to WebAssembly, no dependencies)",
5
5
  "main": "./lib/index.js",
6
6
  "files": [