markuplint 4.12.0 → 4.13.1

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/CHANGELOG.md CHANGED
@@ -3,6 +3,31 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## [4.13.1](https://github.com/markuplint/markuplint/compare/markuplint@4.13.0...markuplint@4.13.1) (2025-08-24)
7
+
8
+ **Note:** Version bump only for package markuplint
9
+
10
+
11
+
12
+
13
+
14
+ # [4.13.0](https://github.com/markuplint/markuplint/compare/markuplint@4.12.0...markuplint@4.13.0) (2025-08-13)
15
+
16
+ ### Bug Fixes
17
+
18
+ - ensure that each `clean` command correctly removes build files ([110b78e](https://github.com/markuplint/markuplint/commit/110b78e85379d29a84ca68325127344a87a570b6))
19
+ - **markuplint:** remove duplicate test for cross-platform compatibility ([d7af614](https://github.com/markuplint/markuplint/commit/d7af6142892a1b4ba4ae196e53df4480817044ff))
20
+
21
+ ### Features
22
+
23
+ - **markuplint:** add --max-violations CLI option ([3ec868d](https://github.com/markuplint/markuplint/commit/3ec868d5e666993a6220a352081100947c31f999))
24
+ - **markuplint:** add --max-warnings option for incremental adoption ([b14513d](https://github.com/markuplint/markuplint/commit/b14513d952b3c47330aba2ce8c4b31714bbc1625)), closes [#2523](https://github.com/markuplint/markuplint/issues/2523)
25
+ - **markuplint:** add maxViolations support to API layer ([cb6d577](https://github.com/markuplint/markuplint/commit/cb6d577483a38e32a378d89a13e950c0eb311b09))
26
+ - **markuplint:** add skip status display to reporters ([5c1f189](https://github.com/markuplint/markuplint/commit/5c1f1897c76b42fc97fce4ce2edd75dd31470abd))
27
+ - **markuplint:** add status field to MLResultInfo and simplify verification ([56deb99](https://github.com/markuplint/markuplint/commit/56deb999a330bb7d91333dc464a034cbc6010479))
28
+ - **markuplint:** add truncation info message to standard reporter ([9c9a21d](https://github.com/markuplint/markuplint/commit/9c9a21dcc01b391a68d23cc315254b69039d9afc))
29
+ - **markuplint:** implement max-count option with skip status tracking ([20ca12d](https://github.com/markuplint/markuplint/commit/20ca12d391bcf8f09eae4c8786ba688ef7192506))
30
+
6
31
  # [4.12.0](https://github.com/markuplint/markuplint/compare/markuplint@4.11.8...markuplint@4.12.0) (2025-04-13)
7
32
 
8
33
  ### Features
package/README.md CHANGED
@@ -87,6 +87,8 @@ Options
87
87
  --verbose Output with detailed information.
88
88
  --include-node-modules Include files in node_modules directory. Default: false.
89
89
  --severity-parse-error Specifies the severity level of parse errors. Supports "error", "warning", and "off". Default: "error".
90
+ --max-count Limit the number of violations shown. Default: 0 (no limit).
91
+ --max-warnings Number of warnings to trigger nonzero exit code. Default: -1 (no limit).
90
92
 
91
93
  --init Initialize settings interactively.
92
94
  --search Search lines of codes that include the target element by selectors.
@@ -96,6 +98,7 @@ Options
96
98
 
97
99
  Examples
98
100
  $ markuplint verifyee.html --config path/to/.markuplintrc
101
+ $ markuplint "**/*.html" --max-count=10
99
102
  $ cat verifyee.html | markuplint
100
103
  ```
101
104
 
@@ -101,6 +101,7 @@ export class MLEngine extends Emitter {
101
101
  filePath: __classPrivateFieldGet(this, _MLEngine_file, "f").path,
102
102
  sourceCode,
103
103
  fixedCode,
104
+ status: 'processed',
104
105
  };
105
106
  }
106
107
  const debugMap = 'debugMap' in core.document ? core.document.debugMap() : null;
@@ -111,6 +112,7 @@ export class MLEngine extends Emitter {
111
112
  filePath: __classPrivateFieldGet(this, _MLEngine_file, "f").path,
112
113
  sourceCode,
113
114
  fixedCode,
115
+ status: 'processed',
114
116
  };
115
117
  }
116
118
  async setCode(code) {
@@ -1,5 +1,5 @@
1
1
  import type { ReadonlyDeep } from 'type-fest';
2
- export declare const help = "\nUsage\n\t$ markuplint <HTML file paths (glob format)>\n\t$ <stdout> | markuplint\n\nOptions\n\t--config, -c FILE_PATH A configuration file path.\n\t--fix, Fix HTML.\n\t--format, -f FORMAT Output format. Support \"JSON\", \"Simple\", \"GitHub\" and \"Standard\". Default: \"Standard\".\n\t--no-search-config No search a configure file automatically.\n\t--ignore-ext Evaluate files that are received even though the type of extension.\n\t--no-import-preset-rules No import preset rules.\n\t--locale Locale of the message of violation. Default is an OS setting.\n\t--no-color, Output no color.\n\t--problem-only, -p Output only problems, without passeds.\n\t--allow-warnings Return status code 0 even if there are warnings.\n\t--allow-empty-input Return status code 1 even if there are no input files.\n\t--show-config Output computed configuration of the target file. Supports \"details\" and empty. Default: empty.\n\t--verbose Output with detailed information.\n\t--include-node-modules Include files in node_modules directory. Default: false.\n\t--severity-parse-error Specifies the severity level of parse errors. Supports \"error\", \"warning\", and \"off\". Default: \"error\".\n\n\t--init Initialize settings interactively.\n\t--search Search lines of codes that include the target element by selectors.\n\n\t--help, -h Show help.\n\t--version, -v Show version.\n\nExamples\n\t$ markuplint verifyee.html --config path/to/.markuplintrc\n\t$ cat verifyee.html | markuplint\n";
2
+ export declare const help = "\nUsage\n\t$ markuplint <HTML file paths (glob format)>\n\t$ <stdout> | markuplint\n\nOptions\n\t--config, -c FILE_PATH A configuration file path.\n\t--fix, Fix HTML.\n\t--format, -f FORMAT Output format. Support \"JSON\", \"Simple\", \"GitHub\" and \"Standard\". Default: \"Standard\".\n\t--no-search-config No search a configure file automatically.\n\t--ignore-ext Evaluate files that are received even though the type of extension.\n\t--no-import-preset-rules No import preset rules.\n\t--locale Locale of the message of violation. Default is an OS setting.\n\t--no-color, Output no color.\n\t--problem-only, -p Output only problems, without passeds.\n\t--allow-warnings Return status code 0 even if there are warnings.\n\t--allow-empty-input Return status code 1 even if there are no input files.\n\t--show-config Output computed configuration of the target file. Supports \"details\" and empty. Default: empty.\n\t--verbose Output with detailed information.\n\t--include-node-modules Include files in node_modules directory. Default: false.\n\t--severity-parse-error Specifies the severity level of parse errors. Supports \"error\", \"warning\", and \"off\". Default: \"error\".\n\t--max-count Limit the number of violations shown. Default: 0 (no limit).\n\t--max-warnings Number of warnings to trigger nonzero exit code. Default: -1 (no limit).\n\n\t--init Initialize settings interactively.\n\t--search Search lines of codes that include the target element by selectors.\n\n\t--help, -h Show help.\n\t--version, -v Show version.\n\nExamples\n\t$ markuplint verifyee.html --config path/to/.markuplintrc\n\t$ cat verifyee.html | markuplint\n";
3
3
  export declare const cli: import("meow").Result<{
4
4
  config: {
5
5
  type: "string";
@@ -71,5 +71,13 @@ export declare const cli: import("meow").Result<{
71
71
  type: "string";
72
72
  default: string;
73
73
  };
74
+ maxCount: {
75
+ type: "number";
76
+ default: number;
77
+ };
78
+ maxWarnings: {
79
+ type: "number";
80
+ default: number;
81
+ };
74
82
  }>;
75
83
  export type CLIOptions = ReadonlyDeep<typeof cli.flags>;
@@ -20,6 +20,8 @@ Options
20
20
  --verbose Output with detailed information.
21
21
  --include-node-modules Include files in node_modules directory. Default: false.
22
22
  --severity-parse-error Specifies the severity level of parse errors. Supports "error", "warning", and "off". Default: "error".
23
+ --max-count Limit the number of violations shown. Default: 0 (no limit).
24
+ --max-warnings Number of warnings to trigger nonzero exit code. Default: -1 (no limit).
23
25
 
24
26
  --init Initialize settings interactively.
25
27
  --search Search lines of codes that include the target element by selectors.
@@ -105,5 +107,13 @@ export const cli = meow(help, {
105
107
  type: 'string',
106
108
  default: 'error',
107
109
  },
110
+ maxCount: {
111
+ type: 'number',
112
+ default: 0,
113
+ },
114
+ maxWarnings: {
115
+ type: 'number',
116
+ default: -1,
117
+ },
108
118
  },
109
119
  });
@@ -1,6 +1,7 @@
1
1
  import { promises as fs } from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { resolveFiles } from '@markuplint/file-resolver';
4
+ import { ViolationCollector } from '@markuplint/ml-core';
4
5
  import { MLEngine } from '../api/index.js';
5
6
  import { log } from '../debug.js';
6
7
  import { output } from './output.js';
@@ -27,7 +28,11 @@ export async function command(files, options, apiOptions) {
27
28
  }
28
29
  const format = options.format?.toLowerCase().trim();
29
30
  let hasError = false;
30
- const jsonOutput = [];
31
+ let totalWarningCount = 0;
32
+ const collector = new ViolationCollector(options.maxCount);
33
+ const processedFiles = [];
34
+ const skippedFiles = [];
35
+ const filesContent = new Map();
31
36
  const severityParseError = options.severityParseError.toLowerCase();
32
37
  const severity = {
33
38
  parseError: ['error', 'warning', 'off'].includes(severityParseError)
@@ -35,6 +40,12 @@ export async function command(files, options, apiOptions) {
35
40
  : true,
36
41
  };
37
42
  for (const file of fileList) {
43
+ // Check if collector is already locked (max-count reached)
44
+ if (collector.isLocked()) {
45
+ log('Skipping file due to max-count limit: %s', file.path);
46
+ skippedFiles.push(file.path);
47
+ continue;
48
+ }
38
49
  const engine = new MLEngine(file, {
39
50
  configFile,
40
51
  fix,
@@ -72,8 +83,18 @@ export async function command(files, options, apiOptions) {
72
83
  if (!result) {
73
84
  continue;
74
85
  }
86
+ // Track processed file
87
+ processedFiles.push(result.filePath);
88
+ filesContent.set(result.filePath, {
89
+ sourceCode: result.sourceCode,
90
+ fixedCode: result.fixedCode,
91
+ });
92
+ // Add violations to collector
93
+ collector.pushWithFile(result.filePath, ...result.violations);
75
94
  const errorCount = result.violations.filter(v => v.severity === 'error').length;
76
95
  const warningCount = result.violations.filter(v => v.severity === 'warning').length;
96
+ // Track total warning count across all files
97
+ totalWarningCount += warningCount;
77
98
  if (!hasError && (errorCount > 0 || (warningCount > 0 && !options.allowWarnings))) {
78
99
  hasError = true;
79
100
  }
@@ -81,19 +102,50 @@ export async function command(files, options, apiOptions) {
81
102
  log('Overwrite file: %s', result.filePath);
82
103
  await fs.writeFile(result.filePath, result.fixedCode, { encoding: 'utf8' });
83
104
  }
84
- if (format === 'json') {
85
- jsonOutput.push(...result.violations.map(v => ({
86
- ...v,
87
- filePath: result.filePath,
88
- })));
89
- continue;
90
- }
91
- log('Output reports');
92
- output(result, options);
93
105
  }
106
+ // Output results
94
107
  if (format === 'json') {
108
+ const jsonOutput = collector.toArray();
95
109
  process.stdout.write(JSON.stringify(jsonOutput, null, 2) + '\n');
96
110
  return false;
97
111
  }
112
+ // For standard/simple/github output, group violations by file
113
+ const violationsByFile = collector.groupByFile();
114
+ // Output per file - include processed files without violations
115
+ for (const filePath of processedFiles) {
116
+ const violations = violationsByFile.get(filePath) || [];
117
+ const content = filesContent.get(filePath) || { sourceCode: '', fixedCode: '' };
118
+ if (violations.length === 0 && !options.problemOnly) {
119
+ log('Output reports');
120
+ output({
121
+ violations,
122
+ filePath,
123
+ sourceCode: content.sourceCode,
124
+ fixedCode: content.fixedCode,
125
+ status: 'processed',
126
+ }, options);
127
+ }
128
+ else if (violations.length > 0) {
129
+ log('Output reports');
130
+ output({
131
+ violations,
132
+ filePath,
133
+ sourceCode: content.sourceCode,
134
+ fixedCode: content.fixedCode,
135
+ status: 'processed',
136
+ }, options);
137
+ }
138
+ }
139
+ // Output skipped files
140
+ if (!options.problemOnly) {
141
+ for (const filePath of skippedFiles) {
142
+ log('Output skipped file report');
143
+ output({ violations: [], filePath, sourceCode: '', fixedCode: '', status: 'skipped' }, options);
144
+ }
145
+ }
146
+ // Check if max-warnings limit is exceeded (ESLint compatible)
147
+ if (!hasError && options.maxWarnings >= 0 && totalWarningCount > options.maxWarnings) {
148
+ hasError = true;
149
+ }
98
150
  return hasError;
99
151
  }
package/lib/cli/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import getStdin from 'get-stdin';
1
+ import { text } from 'node:stream/consumers';
2
2
  import { verbosely } from '../debug.js';
3
3
  import { cli } from './bootstrap.js';
4
4
  import { command } from './command.js';
@@ -40,7 +40,7 @@ if (files.length > 0) {
40
40
  process.exit(hasError ? 1 : 0);
41
41
  }
42
42
  if (usePipe()) {
43
- const stdin = await getStdin().catch(error => {
43
+ const stdin = await text(process.stdin).catch(error => {
44
44
  // eslint-disable-next-line no-console
45
45
  console.warn(error);
46
46
  process.exit(1);
@@ -24,7 +24,8 @@ export function simpleReporter(results, options) {
24
24
  }
25
25
  }
26
26
  else if (!options.problemOnly) {
27
- out.push(`<${commandName}> ${font.underline(results.filePath)}: ${font.green('✓')}`);
27
+ const icon = results.status === 'skipped' ? font.yellow('⚠') : font.green('✓');
28
+ out.push(`<${commandName}> ${font.underline(results.filePath)}: ${icon}`);
28
29
  }
29
30
  return out;
30
31
  }
@@ -52,7 +52,9 @@ export function standardReporter(results, options) {
52
52
  }
53
53
  }
54
54
  else if (!options.problemOnly) {
55
- out.push(`<${commandName}> ${font.green('passed')} ${font.underline(results.filePath)}`);
55
+ const message = results.status === 'skipped' ? 'skipped' : 'passed';
56
+ const color = results.status === 'skipped' ? font.yellow : font.green;
57
+ out.push(`<${commandName}> ${color(message)} ${font.underline(results.filePath)}`);
56
58
  }
57
59
  return out;
58
60
  }
package/lib/types.d.ts CHANGED
@@ -5,6 +5,7 @@ export interface MLResultInfo {
5
5
  readonly filePath: string;
6
6
  readonly sourceCode: string;
7
7
  readonly fixedCode: string;
8
+ readonly status: 'processed' | 'skipped';
8
9
  }
9
10
  /**
10
11
  * @deprecated
package/package.json CHANGED
@@ -1,10 +1,9 @@
1
1
  {
2
2
  "name": "markuplint",
3
- "version": "4.12.0",
3
+ "version": "4.13.1",
4
4
  "description": "An HTML linter for all markup developers",
5
5
  "author": "Yusuke Hirao",
6
6
  "license": "MIT",
7
- "private": false,
8
7
  "repository": "git@github.com:markuplint/markuplint.git",
9
8
  "type": "module",
10
9
  "exports": {
@@ -17,35 +16,32 @@
17
16
  "typedoc": {
18
17
  "entryPoint": "./src/index.ts"
19
18
  },
20
- "bin": {
21
- "markuplint": "./bin/markuplint.mjs"
22
- },
19
+ "bin": "./bin/markuplint.mjs",
23
20
  "scripts": {
24
21
  "build": "tsc --project tsconfig.build.json",
25
22
  "dev": "tsc --watch --project tsconfig.build.json",
26
- "clean": "tsc --build --clean"
23
+ "clean": "tsc --build --clean tsconfig.build.json"
27
24
  },
28
25
  "dependencies": {
29
- "@markuplint/cli-utils": "4.4.11",
30
- "@markuplint/file-resolver": "4.9.14",
31
- "@markuplint/html-parser": "4.6.19",
32
- "@markuplint/html-spec": "4.14.2",
33
- "@markuplint/i18n": "4.6.0",
34
- "@markuplint/ml-ast": "4.4.9",
35
- "@markuplint/ml-config": "4.8.11",
36
- "@markuplint/ml-core": "4.12.4",
37
- "@markuplint/ml-spec": "4.9.6",
38
- "@markuplint/rules": "4.10.12",
39
- "@markuplint/shared": "4.4.11",
26
+ "@markuplint/cli-utils": "4.4.12",
27
+ "@markuplint/file-resolver": "4.9.16",
28
+ "@markuplint/html-parser": "4.6.21",
29
+ "@markuplint/html-spec": "4.16.0",
30
+ "@markuplint/i18n": "4.7.0",
31
+ "@markuplint/ml-ast": "4.4.10",
32
+ "@markuplint/ml-config": "4.8.13",
33
+ "@markuplint/ml-core": "4.13.1",
34
+ "@markuplint/ml-spec": "4.10.0",
35
+ "@markuplint/rules": "4.11.1",
36
+ "@markuplint/shared": "4.4.12",
40
37
  "@types/debug": "4.1.12",
41
38
  "chokidar": "4.0.3",
42
- "debug": "4.4.0",
43
- "get-stdin": "9.0.0",
39
+ "debug": "4.4.1",
44
40
  "meow": "13.2.0",
45
41
  "os-locale": "6.0.2",
46
42
  "strict-event-emitter": "0.5.1",
47
43
  "strip-ansi": "7.1.0",
48
- "type-fest": "4.39.1"
44
+ "type-fest": "4.41.0"
49
45
  },
50
- "gitHead": "eb36d59f7e13d4e59ff3f3c4eabb5ec06c070eb0"
46
+ "gitHead": "ae97eb2d31ecedf4f0800fbbf18588aad4ebca04"
51
47
  }