markuplint 4.0.0-alpha.1 → 4.0.0-alpha.11
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/LICENSE +1 -1
- package/README.md +7 -3
- package/lib/api/index.d.ts +1 -1
- package/lib/api/index.js +1 -1
- package/lib/api/lint.js +1 -1
- package/lib/api/ml-engine.d.ts +1 -1
- package/lib/api/ml-engine.js +34 -14
- package/lib/cli/bootstrap.d.ts +5 -1
- package/lib/cli/bootstrap.js +5 -1
- package/lib/cli/command.js +10 -12
- package/lib/cli/index.js +53 -62
- package/lib/cli/init/create-config.js +11 -1
- package/lib/cli/init/get-default-rules.d.ts +2 -2
- package/lib/cli/init/get-default-rules.js +12 -48
- package/lib/cli/init/index.js +15 -20
- package/lib/cli/init/select-modules.d.ts +2 -0
- package/lib/cli/init/select-modules.js +10 -0
- package/lib/cli/init/types.d.ts +3 -2
- package/lib/cli/search/index.d.ts +1 -1
- package/lib/cli/search/index.js +4 -4
- package/lib/debug.js +3 -2
- package/lib/get-json-module.js +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/reporter/github-reporter.js +5 -3
- package/lib/reporter/simple-reporter.js +8 -8
- package/lib/reporter/standard-reporter.js +34 -19
- package/lib/testing-tool/index.js +25 -23
- package/package.json +17 -30
- package/lib/cli/create-rule/index.d.ts +0 -1
- package/lib/cli/create-rule/index.js +0 -79
- package/lib/cli/init/install-module.d.ts +0 -7
- package/lib/cli/init/install-module.js +0 -74
- package/lib/cli/prompt.d.ts +0 -18
- package/lib/cli/prompt.js +0 -67
- package/lib/util.d.ts +0 -17
- package/lib/util.js +0 -76
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
$ npx markuplint target.html
|
|
26
26
|
```
|
|
27
27
|
|
|
28
|
-
Supported for _Node.js_ `
|
|
28
|
+
Supported for _Node.js_ `v18.18.0` or later.
|
|
29
29
|
|
|
30
30
|
## Usage
|
|
31
31
|
|
|
@@ -83,9 +83,9 @@ Options
|
|
|
83
83
|
--no-color, Output no color.
|
|
84
84
|
--problem-only, -p Output only problems, without passeds.
|
|
85
85
|
--verbose Output with detailed information.
|
|
86
|
+
--include-node-modules Include files in node_modules directory. Default: false.
|
|
86
87
|
|
|
87
88
|
--init Initialize settings interactively.
|
|
88
|
-
--create-rule Add the scaffold of a custom rule.
|
|
89
89
|
|
|
90
90
|
--help, -h Show help.
|
|
91
91
|
--version, -v Show version.
|
|
@@ -121,8 +121,12 @@ Need [Sponsors❤️🔥](https://github.com/sponsors/markuplint)
|
|
|
121
121
|
### Personal Supporters
|
|
122
122
|
|
|
123
123
|
[<img width="36" src="https://avatars.githubusercontent.com/u/91733847" alt="Tokitake" />](https://github.com/Tokitake)
|
|
124
|
-
[<img width="36" src="https://avatars.githubusercontent.com/u/1996642" alt="
|
|
124
|
+
[<img width="36" src="https://avatars.githubusercontent.com/u/1996642" alt="Okuto Oyama" />](https://github.com/yamanoku)
|
|
125
125
|
[<img width="36" src="https://avatars.githubusercontent.com/u/6581173" alt="miita" />](https://github.com/mikimhk)
|
|
126
|
+
[<img width="36" src="https://avatars.githubusercontent.com/u/111797" alt="Yasuo Fukuda" />](https://github.com/sigwyg)
|
|
127
|
+
[<img width="36" src="https://avatars.githubusercontent.com/u/802921" alt="Hideyuki Saito" />](https://github.com/hideyukisaito)
|
|
128
|
+
[<img width="36" src="https://avatars.githubusercontent.com/u/3056064" alt="tamshow" />](https://github.com/tamshow)
|
|
129
|
+
[<img width="36" src="https://avatars.githubusercontent.com/u/91047157" alt="shamokit" />](https://github.com/shamokit)
|
|
126
130
|
|
|
127
131
|
## Thanks
|
|
128
132
|
|
package/lib/api/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { MLEngine, FromCodeOptions } from './ml-engine.js';
|
|
2
2
|
export { lint } from './lint.js';
|
package/lib/api/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export { MLEngine } from './ml-engine.js';
|
|
2
2
|
export { lint } from './lint.js';
|
package/lib/api/lint.js
CHANGED
package/lib/api/ml-engine.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ export type FromCodeOptions = APIOptions & MLEngineOptions & {
|
|
|
12
12
|
readonly name?: string;
|
|
13
13
|
readonly dirname?: string;
|
|
14
14
|
};
|
|
15
|
-
export
|
|
15
|
+
export declare class MLEngine extends Emitter<MLEngineEventMap> {
|
|
16
16
|
#private;
|
|
17
17
|
static fromCode(sourceCode: string, options?: FromCodeOptions): Promise<MLEngine>;
|
|
18
18
|
static toMLFile(target: Target): Promise<MLFile | undefined>;
|
package/lib/api/ml-engine.js
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
2
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
3
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
4
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
5
|
+
};
|
|
6
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
7
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
8
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
9
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
10
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
11
|
+
};
|
|
1
12
|
var _MLEngine_configProvider, _MLEngine_core, _MLEngine_file, _MLEngine_options, _MLEngine_watcher;
|
|
2
|
-
import { __classPrivateFieldGet, __classPrivateFieldSet } from "tslib";
|
|
3
13
|
import { ConfigProvider, resolveFiles, resolveParser, resolveRules, resolveSpecs } from '@markuplint/file-resolver';
|
|
4
14
|
import { MLCore, convertRuleset } from '@markuplint/ml-core';
|
|
5
15
|
import { FSWatcher } from 'chokidar';
|
|
@@ -9,8 +19,12 @@ import { i18n } from '../i18n.js';
|
|
|
9
19
|
const log = coreLog.extend('ml-engine');
|
|
10
20
|
const fileLog = log.extend('file');
|
|
11
21
|
const configLog = log.extend('config');
|
|
12
|
-
class MLEngine extends Emitter {
|
|
22
|
+
export class MLEngine extends Emitter {
|
|
13
23
|
static async fromCode(sourceCode, options) {
|
|
24
|
+
if (options?.debug) {
|
|
25
|
+
verbosely();
|
|
26
|
+
}
|
|
27
|
+
log('[fromCode] Creates: %O', options);
|
|
14
28
|
const file = await MLEngine.toMLFile({
|
|
15
29
|
sourceCode,
|
|
16
30
|
name: options?.name,
|
|
@@ -19,7 +33,9 @@ class MLEngine extends Emitter {
|
|
|
19
33
|
if (!file) {
|
|
20
34
|
throw new Error('Never reach error');
|
|
21
35
|
}
|
|
22
|
-
|
|
36
|
+
log('[fromCode] Created file: %s', file.path);
|
|
37
|
+
const engine = new MLEngine(file, options);
|
|
38
|
+
return engine;
|
|
23
39
|
}
|
|
24
40
|
static async toMLFile(target) {
|
|
25
41
|
const files = await resolveFiles([target]);
|
|
@@ -32,13 +48,14 @@ class MLEngine extends Emitter {
|
|
|
32
48
|
_MLEngine_file.set(this, void 0);
|
|
33
49
|
_MLEngine_options.set(this, void 0);
|
|
34
50
|
_MLEngine_watcher.set(this, new FSWatcher());
|
|
51
|
+
if (__classPrivateFieldGet(this, _MLEngine_options, "f")?.debug) {
|
|
52
|
+
verbosely();
|
|
53
|
+
}
|
|
35
54
|
__classPrivateFieldSet(this, _MLEngine_file, file, "f");
|
|
36
55
|
__classPrivateFieldSet(this, _MLEngine_options, options, "f");
|
|
37
56
|
__classPrivateFieldSet(this, _MLEngine_configProvider, new ConfigProvider(), "f");
|
|
38
57
|
this.watchMode(!!__classPrivateFieldGet(this, _MLEngine_options, "f")?.watch);
|
|
39
|
-
|
|
40
|
-
verbosely();
|
|
41
|
-
}
|
|
58
|
+
log('[MLEngine] Initialized: %s', __classPrivateFieldGet(this, _MLEngine_file, "f").path);
|
|
42
59
|
}
|
|
43
60
|
get document() {
|
|
44
61
|
if (__classPrivateFieldGet(this, _MLEngine_core, "f")?.document instanceof Error) {
|
|
@@ -57,14 +74,14 @@ class MLEngine extends Emitter {
|
|
|
57
74
|
log('exec: cancel (unsetuped yet)');
|
|
58
75
|
return null;
|
|
59
76
|
}
|
|
60
|
-
const violations = await core.verify(__classPrivateFieldGet(this, _MLEngine_options, "f")?.fix).catch(
|
|
61
|
-
if (
|
|
62
|
-
return
|
|
77
|
+
const violations = await core.verify(__classPrivateFieldGet(this, _MLEngine_options, "f")?.fix).catch(error => {
|
|
78
|
+
if (error instanceof Error) {
|
|
79
|
+
return error;
|
|
63
80
|
}
|
|
64
|
-
throw
|
|
81
|
+
throw error;
|
|
65
82
|
});
|
|
66
83
|
const sourceCode = await __classPrivateFieldGet(this, _MLEngine_file, "f").getCode();
|
|
67
|
-
const fixedCode = core.document.toString();
|
|
84
|
+
const fixedCode = core.document.toString(true);
|
|
68
85
|
if (violations instanceof Error) {
|
|
69
86
|
this.emit('lint-error', __classPrivateFieldGet(this, _MLEngine_file, "f").path, sourceCode, violations);
|
|
70
87
|
const errMessage = violations.stack ?? violations.message;
|
|
@@ -225,12 +242,16 @@ class MLEngine extends Emitter {
|
|
|
225
242
|
};
|
|
226
243
|
}
|
|
227
244
|
async resolveConfig(cache) {
|
|
245
|
+
this.emit('log', 'resolveConfig', JSON.stringify(__classPrivateFieldGet(this, _MLEngine_configProvider, "f"), null, 2));
|
|
246
|
+
configLog('configProvider: %s', __classPrivateFieldGet(this, _MLEngine_configProvider, "f"));
|
|
228
247
|
const defaultConfigKey = __classPrivateFieldGet(this, _MLEngine_options, "f")?.defaultConfig && __classPrivateFieldGet(this, _MLEngine_configProvider, "f").set(__classPrivateFieldGet(this, _MLEngine_options, "f")?.defaultConfig);
|
|
229
248
|
configLog('defaultConfigKey: %s', defaultConfigKey ?? 'N/A');
|
|
230
249
|
this.emit('log', 'defaultConfigKey', defaultConfigKey ?? 'N/A');
|
|
250
|
+
const targetConfig = await __classPrivateFieldGet(this, _MLEngine_configProvider, "f").search(__classPrivateFieldGet(this, _MLEngine_file, "f"));
|
|
251
|
+
this.emit('log', 'targetConfig', targetConfig ?? 'N/A');
|
|
231
252
|
const configFilePathsFromTarget = __classPrivateFieldGet(this, _MLEngine_options, "f")?.noSearchConfig
|
|
232
253
|
? defaultConfigKey ?? null
|
|
233
|
-
:
|
|
254
|
+
: targetConfig ?? defaultConfigKey;
|
|
234
255
|
configLog('configFilePathsFromTarget: %s', configFilePathsFromTarget ?? 'N/A');
|
|
235
256
|
this.emit('log', 'configFilePathsFromTarget', configFilePathsFromTarget ?? 'N/A');
|
|
236
257
|
const configKey = __classPrivateFieldGet(this, _MLEngine_options, "f")?.config && __classPrivateFieldGet(this, _MLEngine_configProvider, "f").set(__classPrivateFieldGet(this, _MLEngine_options, "f").config);
|
|
@@ -248,7 +269,7 @@ class MLEngine extends Emitter {
|
|
|
248
269
|
this.emit('config', __classPrivateFieldGet(this, _MLEngine_file, "f").path, configSet);
|
|
249
270
|
if (__classPrivateFieldGet(this, _MLEngine_options, "f")?.watch) {
|
|
250
271
|
// It doesn't watch the main HTML file because it may is watched and managed by a language server or text editor or more.
|
|
251
|
-
__classPrivateFieldGet(this, _MLEngine_watcher, "f").add(
|
|
272
|
+
__classPrivateFieldGet(this, _MLEngine_watcher, "f").add([...configSet.files]);
|
|
252
273
|
}
|
|
253
274
|
return configSet;
|
|
254
275
|
}
|
|
@@ -297,4 +318,3 @@ class MLEngine extends Emitter {
|
|
|
297
318
|
}
|
|
298
319
|
}
|
|
299
320
|
_MLEngine_configProvider = new WeakMap(), _MLEngine_core = new WeakMap(), _MLEngine_file = new WeakMap(), _MLEngine_options = new WeakMap(), _MLEngine_watcher = new WeakMap();
|
|
300
|
-
export default MLEngine;
|
package/lib/cli/bootstrap.d.ts
CHANGED
|
@@ -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--verbose Output with detailed information.\n\n\t--init Initialize settings interactively.\n\t--
|
|
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--verbose Output with detailed information.\n\t--include-node-modules Include files in node_modules directory. Default: false.\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";
|
|
@@ -60,5 +60,9 @@ export declare const cli: import("meow").Result<{
|
|
|
60
60
|
search: {
|
|
61
61
|
type: "string";
|
|
62
62
|
};
|
|
63
|
+
includeNodeModules: {
|
|
64
|
+
type: "boolean";
|
|
65
|
+
default: false;
|
|
66
|
+
};
|
|
63
67
|
}>;
|
|
64
68
|
export type CLIOptions = ReadonlyDeep<typeof cli.flags>;
|
package/lib/cli/bootstrap.js
CHANGED
|
@@ -17,9 +17,9 @@ Options
|
|
|
17
17
|
--allow-warnings Return status code 0 even if there are warnings.
|
|
18
18
|
--allow-empty-input Return status code 1 even if there are no input files.
|
|
19
19
|
--verbose Output with detailed information.
|
|
20
|
+
--include-node-modules Include files in node_modules directory. Default: false.
|
|
20
21
|
|
|
21
22
|
--init Initialize settings interactively.
|
|
22
|
-
--create-rule Add the scaffold of a custom rule.
|
|
23
23
|
--search Search lines of codes that include the target element by selectors.
|
|
24
24
|
|
|
25
25
|
--help, -h Show help.
|
|
@@ -92,5 +92,9 @@ export const cli = meow(help, {
|
|
|
92
92
|
search: {
|
|
93
93
|
type: 'string',
|
|
94
94
|
},
|
|
95
|
+
includeNodeModules: {
|
|
96
|
+
type: 'boolean',
|
|
97
|
+
default: false,
|
|
98
|
+
},
|
|
95
99
|
},
|
|
96
100
|
});
|
package/lib/cli/command.js
CHANGED
|
@@ -12,7 +12,8 @@ export async function command(files, options, apiOptions) {
|
|
|
12
12
|
const ignoreExt = options.ignoreExt;
|
|
13
13
|
const importPresetRules = options.importPresetRules;
|
|
14
14
|
const verbose = options.verbose;
|
|
15
|
-
const
|
|
15
|
+
const ignoreGlob = options.includeNodeModules ? undefined : 'node_modules/**';
|
|
16
|
+
const fileList = await resolveFiles(files, ignoreGlob);
|
|
16
17
|
if (fileList.length === 0 && !options.allowEmptyInput) {
|
|
17
18
|
process.stderr.write('Markuplint: No target files.\n');
|
|
18
19
|
// Error
|
|
@@ -49,19 +50,16 @@ export async function command(files, options, apiOptions) {
|
|
|
49
50
|
if (fix) {
|
|
50
51
|
log('Overwrite file: %s', result.filePath);
|
|
51
52
|
await fs.writeFile(result.filePath, result.fixedCode, { encoding: 'utf8' });
|
|
52
|
-
process.stdout.write(`markuplint: Fix "${result.filePath}"\n`);
|
|
53
53
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
continue;
|
|
61
|
-
}
|
|
62
|
-
log('Output reports');
|
|
63
|
-
output(result, options);
|
|
54
|
+
if (format === 'json') {
|
|
55
|
+
jsonOutput.push(...result.violations.map(v => ({
|
|
56
|
+
...v,
|
|
57
|
+
filePath: result.filePath,
|
|
58
|
+
})));
|
|
59
|
+
continue;
|
|
64
60
|
}
|
|
61
|
+
log('Output reports');
|
|
62
|
+
output(result, options);
|
|
65
63
|
}
|
|
66
64
|
if (format === 'json') {
|
|
67
65
|
process.stdout.write(JSON.stringify(jsonOutput, null, 2) + '\n');
|
package/lib/cli/index.js
CHANGED
|
@@ -2,74 +2,65 @@ import getStdin from 'get-stdin';
|
|
|
2
2
|
import { verbosely } from '../debug.js';
|
|
3
3
|
import { cli } from './bootstrap.js';
|
|
4
4
|
import { command } from './command.js';
|
|
5
|
-
import { createRule } from './create-rule/index.js';
|
|
6
5
|
import { initialize } from './init/index.js';
|
|
7
|
-
import search from './search/index.js';
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
if (files.length > 0) {
|
|
35
|
-
if (cli.flags.search) {
|
|
36
|
-
await search(files, cli.flags, cli.flags.search).catch(err => {
|
|
37
|
-
process.stderr.write(err + '\n');
|
|
38
|
-
process.exit(1);
|
|
39
|
-
});
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
const hasError = await command(files, cli.flags).catch(err => {
|
|
43
|
-
throw err;
|
|
44
|
-
});
|
|
45
|
-
process.exit(hasError ? 1 : 0);
|
|
46
|
-
}
|
|
47
|
-
if (usePipe()) {
|
|
48
|
-
getStdin()
|
|
49
|
-
.then(async (stdin) => {
|
|
50
|
-
if (stdin) {
|
|
51
|
-
const hasError = await command([{ sourceCode: stdin }], {
|
|
52
|
-
...cli.flags,
|
|
53
|
-
ignoreExt: true,
|
|
54
|
-
}).catch(err => {
|
|
55
|
-
process.stderr.write(err + '\n');
|
|
56
|
-
process.exit(1);
|
|
57
|
-
});
|
|
58
|
-
process.exit(hasError ? 1 : 0);
|
|
59
|
-
}
|
|
60
|
-
// result is empty
|
|
61
|
-
cli.showHelp(1);
|
|
62
|
-
})
|
|
63
|
-
.catch(reason => {
|
|
64
|
-
// eslint-disable-next-line no-console
|
|
65
|
-
console.warn(reason);
|
|
6
|
+
import { search } from './search/index.js';
|
|
7
|
+
/* eslint-disable unicorn/no-process-exit */
|
|
8
|
+
if (cli.flags.v) {
|
|
9
|
+
cli.showVersion(); // And exit successfully.
|
|
10
|
+
}
|
|
11
|
+
if (cli.flags.h) {
|
|
12
|
+
cli.showHelp(0); // And exit successfully.
|
|
13
|
+
}
|
|
14
|
+
if (cli.flags.verbose) {
|
|
15
|
+
verbosely();
|
|
16
|
+
}
|
|
17
|
+
if (cli.flags.init) {
|
|
18
|
+
await initialize().catch(error => {
|
|
19
|
+
process.stderr.write(error + '\n');
|
|
20
|
+
process.exit(1);
|
|
21
|
+
});
|
|
22
|
+
process.exit(0);
|
|
23
|
+
}
|
|
24
|
+
if (cli.flags.createRule) {
|
|
25
|
+
process.stderr.write("Use to run 'npx @markuplint/create-rule' instead of 'markuplint --create-rule'.\n");
|
|
26
|
+
process.exit(1);
|
|
27
|
+
}
|
|
28
|
+
const files = cli.input;
|
|
29
|
+
if (files.length > 0) {
|
|
30
|
+
if (cli.flags.search) {
|
|
31
|
+
await search(files, cli.flags, cli.flags.search).catch(error => {
|
|
32
|
+
process.stderr.write(error + '\n');
|
|
66
33
|
process.exit(1);
|
|
67
34
|
});
|
|
35
|
+
process.exit(0);
|
|
68
36
|
}
|
|
69
|
-
|
|
37
|
+
const hasError = await command(files, cli.flags).catch(error => {
|
|
38
|
+
throw error;
|
|
39
|
+
});
|
|
40
|
+
process.exit(hasError ? 1 : 0);
|
|
41
|
+
}
|
|
42
|
+
if (usePipe()) {
|
|
43
|
+
const stdin = await getStdin().catch(error => {
|
|
44
|
+
// eslint-disable-next-line no-console
|
|
45
|
+
console.warn(error);
|
|
46
|
+
process.exit(1);
|
|
47
|
+
});
|
|
48
|
+
if (!stdin) {
|
|
49
|
+
// result is empty
|
|
70
50
|
cli.showHelp(1);
|
|
71
51
|
}
|
|
72
|
-
}
|
|
52
|
+
const hasError = await command([{ sourceCode: stdin }], {
|
|
53
|
+
...cli.flags,
|
|
54
|
+
ignoreExt: true,
|
|
55
|
+
}).catch(error => {
|
|
56
|
+
process.stderr.write(error + '\n');
|
|
57
|
+
process.exit(1);
|
|
58
|
+
});
|
|
59
|
+
process.exit(hasError ? 1 : 0);
|
|
60
|
+
}
|
|
61
|
+
// No arguments
|
|
62
|
+
cli.showHelp(1);
|
|
63
|
+
/* eslint-enable unicorn/no-process-exit */
|
|
73
64
|
function usePipe() {
|
|
74
65
|
return !process.stdin.isTTY && process.stdout.isTTY;
|
|
75
66
|
}
|
|
@@ -2,6 +2,7 @@ const extRExp = {
|
|
|
2
2
|
jsx: '\\.[jt]sx?$',
|
|
3
3
|
vue: '\\.vue$',
|
|
4
4
|
svelte: '\\.svelte$',
|
|
5
|
+
sveltekit: '\\.html$',
|
|
5
6
|
astro: '\\.astro$',
|
|
6
7
|
pug: '\\.pug$',
|
|
7
8
|
php: '\\.php$',
|
|
@@ -16,6 +17,7 @@ export const langs = {
|
|
|
16
17
|
jsx: 'React (JSX)',
|
|
17
18
|
vue: 'Vue',
|
|
18
19
|
svelte: 'Svelte',
|
|
20
|
+
sveltekit: 'SvelteKit',
|
|
19
21
|
astro: 'Astro',
|
|
20
22
|
pug: 'Pug',
|
|
21
23
|
php: 'PHP',
|
|
@@ -34,7 +36,15 @@ export function createConfig(langs, mode, defaultRules) {
|
|
|
34
36
|
if (!ext) {
|
|
35
37
|
continue;
|
|
36
38
|
}
|
|
37
|
-
|
|
39
|
+
switch (lang) {
|
|
40
|
+
case 'sveltekit': {
|
|
41
|
+
parser[ext] = '@markuplint/svelte-parser/kit';
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
default: {
|
|
45
|
+
parser[ext] = `@markuplint/${lang}-parser`;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
38
48
|
if (lang === 'vue') {
|
|
39
49
|
config = {
|
|
40
50
|
...config,
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export declare function getDefaultRules(
|
|
1
|
+
export declare function getDefaultRules(): {
|
|
2
2
|
[x: string]: import("./types.js").Rule;
|
|
3
|
-
}
|
|
3
|
+
};
|
|
@@ -1,52 +1,16 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
const RULES_SCHEMA_URL = 'https://raw.githubusercontent.com/markuplint/markuplint/[VERSION]/packages/@markuplint/rules/schema.json';
|
|
4
|
-
const RULES_README_URL = 'https://raw.githubusercontent.com/markuplint/markuplint/[VERSION]/packages/@markuplint/rules/src/[NAME]/README.md';
|
|
5
|
-
export async function getDefaultRules(version) {
|
|
6
|
-
const json = await safeFetch(RULES_SCHEMA_URL, version);
|
|
1
|
+
import builtinRules from '@markuplint/rules';
|
|
2
|
+
export function getDefaultRules() {
|
|
7
3
|
const rules = {};
|
|
8
|
-
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
4
|
+
for (const [ruleName, rule] of Object.entries(builtinRules)) {
|
|
5
|
+
const defaultSeverity = rule.defaultSeverity;
|
|
6
|
+
const defaultValue = defaultSeverity === 'warning' ? false : rule.defaultValue ?? true;
|
|
7
|
+
const category = rule.meta?.category;
|
|
8
|
+
if (category) {
|
|
9
|
+
rules[ruleName] = {
|
|
10
|
+
defaultValue,
|
|
11
|
+
category,
|
|
12
|
+
};
|
|
17
13
|
}
|
|
18
|
-
const defaultValue = severity === 'warning' ? false : json.definitions.value.default ?? true;
|
|
19
|
-
rules[name] = {
|
|
20
|
-
defaultValue,
|
|
21
|
-
category,
|
|
22
|
-
};
|
|
23
|
-
}));
|
|
24
|
-
return rules;
|
|
25
|
-
}
|
|
26
|
-
async function safeFetch(baseUrl, version, type = 'json') {
|
|
27
|
-
const url = baseUrl.replace('[VERSION]', `v${version}`);
|
|
28
|
-
const res = await fetch(url);
|
|
29
|
-
if (!res.ok) {
|
|
30
|
-
return safeFetch(baseUrl, '3.0.0', type);
|
|
31
|
-
}
|
|
32
|
-
if (type === 'json') {
|
|
33
|
-
return (await res.json());
|
|
34
14
|
}
|
|
35
|
-
|
|
36
|
-
const { data } = matter(md);
|
|
37
|
-
return data;
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* Fallback fetching until 3.0.x
|
|
41
|
-
*
|
|
42
|
-
* @param version
|
|
43
|
-
* @param name
|
|
44
|
-
* @returns
|
|
45
|
-
*/
|
|
46
|
-
async function getCatAndSeverityFromLegacy(version, name) {
|
|
47
|
-
const data = await safeFetch(RULES_README_URL.replace('[NAME]', name), version, 'md');
|
|
48
|
-
return {
|
|
49
|
-
category: data.category,
|
|
50
|
-
severity: data.severity,
|
|
51
|
-
};
|
|
15
|
+
return rules;
|
|
52
16
|
}
|
package/lib/cli/init/index.js
CHANGED
|
@@ -1,14 +1,9 @@
|
|
|
1
|
-
import fs from 'node:fs';
|
|
2
|
-
import module from 'node:module';
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
3
2
|
import path from 'node:path';
|
|
4
|
-
import
|
|
5
|
-
import { head, write, error } from '../../util.js';
|
|
6
|
-
import { confirm, confirmSequence, multiSelect } from '../prompt.js';
|
|
3
|
+
import { installModule, multiSelect, confirm, confirmSequence, header } from '@markuplint/cli-utils';
|
|
7
4
|
import { createConfig, langs } from './create-config.js';
|
|
8
5
|
import { getDefaultRules } from './get-default-rules.js';
|
|
9
|
-
import {
|
|
10
|
-
const require = module.createRequire(import.meta.url);
|
|
11
|
-
const writeFile = util.promisify(fs.writeFile);
|
|
6
|
+
import { selectModules } from './select-modules.js';
|
|
12
7
|
const ruleCategories = {
|
|
13
8
|
validation: {
|
|
14
9
|
message: 'Are you going to conformance check according to HTML standard?',
|
|
@@ -27,8 +22,9 @@ const ruleCategories = {
|
|
|
27
22
|
},
|
|
28
23
|
};
|
|
29
24
|
export async function initialize() {
|
|
30
|
-
write(
|
|
31
|
-
write
|
|
25
|
+
process.stdout.write(header('Initialization'));
|
|
26
|
+
process.stdout.write('\n');
|
|
27
|
+
process.stdout.write('\n');
|
|
32
28
|
const selectedLangs = await multiSelect({
|
|
33
29
|
message: 'Which do you use template engines?',
|
|
34
30
|
choices: Object.entries(langs).map(([key, name]) => ({ name, value: key })),
|
|
@@ -54,26 +50,25 @@ export async function initialize() {
|
|
|
54
50
|
}
|
|
55
51
|
let defaultRules = {};
|
|
56
52
|
if (ruleSettingMode !== 'recommended') {
|
|
57
|
-
|
|
58
|
-
defaultRules = await getDefaultRules(rulesVersion);
|
|
53
|
+
defaultRules = getDefaultRules();
|
|
59
54
|
}
|
|
60
55
|
const config = createConfig(selectedLangs, ruleSettingMode, defaultRules);
|
|
61
56
|
const filePath = path.resolve(process.cwd(), '.markuplintrc');
|
|
62
|
-
await writeFile(filePath, JSON.stringify(config, null, 2), { encoding: '
|
|
63
|
-
write(`✨Created: ${filePath}`);
|
|
57
|
+
await fs.writeFile(filePath, JSON.stringify(config, null, 2), { encoding: 'utf8' });
|
|
58
|
+
process.stdout.write(`✨Created: ${filePath}\n`);
|
|
64
59
|
if (autoInstall) {
|
|
65
|
-
write('Install automatically');
|
|
60
|
+
process.stdout.write('Install automatically\n');
|
|
66
61
|
const modules = selectModules(selectedLangs);
|
|
67
|
-
const result = await installModule(modules, true).catch(
|
|
62
|
+
const result = await installModule(modules, true).catch(error_ => new Error(error_));
|
|
68
63
|
if (result instanceof Error) {
|
|
69
|
-
|
|
70
|
-
|
|
64
|
+
// eslint-disable-next-line unicorn/no-process-exit
|
|
65
|
+
process.exit(1);
|
|
71
66
|
}
|
|
72
67
|
if (result.alreadyExists) {
|
|
73
|
-
write('Modules are installed already
|
|
68
|
+
process.stdout.write('Modules are installed already.\n');
|
|
74
69
|
}
|
|
75
70
|
else {
|
|
76
|
-
write('✨ Success');
|
|
71
|
+
process.stdout.write('✨ Success\n');
|
|
77
72
|
}
|
|
78
73
|
}
|
|
79
74
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export function selectModules(selectedLangs) {
|
|
2
|
+
const modules = ['markuplint', ...selectedLangs.map(lang => `@markuplint/${lang}-parser`)];
|
|
3
|
+
if (selectedLangs.includes('vue')) {
|
|
4
|
+
modules.push('@markuplint/vue-spec');
|
|
5
|
+
}
|
|
6
|
+
if (selectedLangs.includes('jsx')) {
|
|
7
|
+
modules.push('@markuplint/react-spec');
|
|
8
|
+
}
|
|
9
|
+
return modules;
|
|
10
|
+
}
|
package/lib/cli/init/types.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
import type { RuleConfigValue } from '@markuplint/ml-config';
|
|
2
|
+
export type Langs = 'jsx' | 'vue' | 'svelte' | 'sveltekit' | 'astro' | 'pug' | 'php' | 'smarty' | 'erb' | 'ejs' | 'mustache' | 'nunjucks' | 'liquid';
|
|
2
3
|
export type Category = 'validation' | 'a11y' | 'naming-convention' | 'style' | 'maintainability';
|
|
3
4
|
export type RuleSettingMode = readonly Category[] | 'recommended' | 'none';
|
|
4
5
|
export type DefaultRules = Readonly<Record<string, Rule>>;
|
|
5
6
|
export type Rule = {
|
|
6
7
|
readonly category: Category;
|
|
7
|
-
readonly defaultValue:
|
|
8
|
+
readonly defaultValue: RuleConfigValue;
|
|
8
9
|
};
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import type { CLIOptions } from '../bootstrap.js';
|
|
2
|
-
export
|
|
2
|
+
export declare function search(files: readonly string[], options: CLIOptions, selectors: string): Promise<void>;
|
package/lib/cli/search/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createRule, MLRule } from '@markuplint/ml-core';
|
|
2
2
|
import { command } from '../command.js';
|
|
3
|
-
export
|
|
3
|
+
export async function search(files, options, selectors) {
|
|
4
4
|
const name = '__CLI_SEARCH__';
|
|
5
5
|
const locations = [];
|
|
6
6
|
await command(files, {
|
|
@@ -14,7 +14,7 @@ export default async function (files, options, selectors) {
|
|
|
14
14
|
...createRule({
|
|
15
15
|
verify({ document }) {
|
|
16
16
|
const nodes = document.querySelectorAll(selectors);
|
|
17
|
-
locations.push(...
|
|
17
|
+
locations.push(...[...nodes].map(node => {
|
|
18
18
|
return {
|
|
19
19
|
file: document.filename ?? '_NO_FILE_',
|
|
20
20
|
line: node.startLine,
|
|
@@ -31,7 +31,7 @@ export default async function (files, options, selectors) {
|
|
|
31
31
|
},
|
|
32
32
|
},
|
|
33
33
|
});
|
|
34
|
-
|
|
34
|
+
for (const loc of locations) {
|
|
35
35
|
process.stdout.write(`${loc.file}:${loc.line}:${loc.col}\n`);
|
|
36
|
-
}
|
|
36
|
+
}
|
|
37
37
|
}
|
package/lib/debug.js
CHANGED
|
@@ -3,8 +3,9 @@ import debug from 'debug';
|
|
|
3
3
|
export const log = debug('markuplint-cli');
|
|
4
4
|
export function verbosely() {
|
|
5
5
|
if (!log.enabled) {
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
const namespace = `${log.namespace}*,ml-*`;
|
|
7
|
+
debug.enable(namespace);
|
|
8
|
+
log(`[Debug] Enable: "${namespace}"`);
|
|
8
9
|
}
|
|
9
10
|
enableDebug();
|
|
10
11
|
}
|