@markuplint/file-resolver 4.9.17 → 5.0.0-alpha.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/CHANGELOG.md +26 -2
- package/lib/config-load-error.js +3 -1
- package/lib/config-provider.d.ts +37 -0
- package/lib/config-provider.js +61 -36
- package/lib/cosmiconfig.js +30 -11
- package/lib/ml-file/ml-file.js +35 -48
- package/lib/resolve-files.d.ts +8 -0
- package/lib/resolve-files.js +8 -0
- package/lib/resolve-parser.d.ts +13 -2
- package/lib/resolve-parser.js +11 -4
- package/lib/resolve-pretenders.d.ts +7 -0
- package/lib/resolve-pretenders.js +7 -0
- package/lib/resolve-rules.d.ts +12 -0
- package/lib/resolve-rules.js +12 -0
- package/lib/resolve-specs.d.ts +3 -3
- package/lib/resolve-specs.js +3 -3
- package/lib/types.d.ts +11 -0
- package/package.json +15 -12
package/CHANGELOG.md
CHANGED
|
@@ -3,13 +3,37 @@
|
|
|
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
|
-
|
|
6
|
+
# [5.0.0-alpha.0](https://github.com/markuplint/markuplint/compare/v4.14.1...v5.0.0-alpha.0) (2026-02-20)
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
- **file-resolver:** use "options" instead of deprecated "option" in test fixtures ([98a53f2](https://github.com/markuplint/markuplint/commit/98a53f27c4a6e640f20e2c74421c1cdeba3e7db5))
|
|
11
|
+
|
|
12
|
+
- refactor(file-resolver)!: drop MLMarkupLanguageParser support ([3272ee7](https://github.com/markuplint/markuplint/commit/3272ee72a7c4fb3105fbecd41ec4ba5eff030092))
|
|
13
|
+
|
|
14
|
+
### Features
|
|
15
|
+
|
|
16
|
+
- **file-resolver:** add .jsonc config file support via cosmiconfig ([755848d](https://github.com/markuplint/markuplint/commit/755848dbec75105e3cdf9de6becb84546b66deec))
|
|
9
17
|
|
|
18
|
+
### BREAKING CHANGES
|
|
10
19
|
|
|
20
|
+
- Remove MLMarkupLanguageParser compatibility from
|
|
21
|
+
resolve-parser. Parser modules must now export MLParserModule
|
|
22
|
+
with a parser property.
|
|
11
23
|
|
|
24
|
+
* Remove MLMarkupLanguageParser import and union types
|
|
25
|
+
* Remove deprecated 'parser' in parserMod check
|
|
26
|
+
* Update test mock to use Parser class instance
|
|
12
27
|
|
|
28
|
+
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
|
29
|
+
|
|
30
|
+
## [4.9.18](https://github.com/markuplint/markuplint/compare/@markuplint/file-resolver@4.9.17...@markuplint/file-resolver@4.9.18) (2026-02-10)
|
|
31
|
+
|
|
32
|
+
**Note:** Version bump only for package @markuplint/file-resolver
|
|
33
|
+
|
|
34
|
+
## [4.9.17](https://github.com/markuplint/markuplint/compare/@markuplint/file-resolver@4.9.16...@markuplint/file-resolver@4.9.17) (2025-11-05)
|
|
35
|
+
|
|
36
|
+
**Note:** Version bump only for package @markuplint/file-resolver
|
|
13
37
|
|
|
14
38
|
## [4.9.16](https://github.com/markuplint/markuplint/compare/@markuplint/file-resolver@4.9.15...@markuplint/file-resolver@4.9.16) (2025-08-24)
|
|
15
39
|
|
package/lib/config-load-error.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
export class ConfigLoadError extends Error {
|
|
2
|
+
filePath;
|
|
3
|
+
name = 'ConfigLoadError';
|
|
4
|
+
referrer;
|
|
2
5
|
constructor(message, filePath, referrer) {
|
|
3
6
|
super(message + ` in ${referrer}`);
|
|
4
|
-
this.name = 'ConfigLoadError';
|
|
5
7
|
this.filePath = filePath;
|
|
6
8
|
this.referrer = referrer;
|
|
7
9
|
}
|
package/lib/config-provider.d.ts
CHANGED
|
@@ -2,14 +2,51 @@ import type { MLFile } from './ml-file/index.js';
|
|
|
2
2
|
import type { ConfigSet } from './types.js';
|
|
3
3
|
import type { OptimizedConfig } from '@markuplint/ml-config';
|
|
4
4
|
import type { Nullable } from '@markuplint/shared';
|
|
5
|
+
/**
|
|
6
|
+
* Manages loading, caching, and resolving markuplint configuration files.
|
|
7
|
+
*
|
|
8
|
+
* Handles `extends` chains, plugins, presets, overrides, and circular reference detection.
|
|
9
|
+
* Configuration files are searched via cosmiconfig and cached by file path.
|
|
10
|
+
*/
|
|
5
11
|
export declare class ConfigProvider {
|
|
6
12
|
#private;
|
|
13
|
+
/**
|
|
14
|
+
* Recursively loads a configuration and all its `extends` dependencies.
|
|
15
|
+
*
|
|
16
|
+
* @param key - The config file path or module name to load
|
|
17
|
+
* @param cache - Whether to use cached results
|
|
18
|
+
* @param referrer - The file path of the config that referenced this key
|
|
19
|
+
* @param depth - Current recursion depth (for circular reference detection)
|
|
20
|
+
* @returns A set of loaded config keys and any errors encountered
|
|
21
|
+
*/
|
|
7
22
|
recursiveLoad(key: string, cache: boolean, referrer: string, depth?: number): Promise<{
|
|
8
23
|
stack: Set<string>;
|
|
9
24
|
errs: Error[];
|
|
10
25
|
}>;
|
|
26
|
+
/**
|
|
27
|
+
* Resolves the full configuration for a target file by merging all named configs,
|
|
28
|
+
* resolving plugins, and applying file-specific overrides.
|
|
29
|
+
*
|
|
30
|
+
* @param targetFile - The file being linted
|
|
31
|
+
* @param names - Config file paths or module names to merge
|
|
32
|
+
* @param cache - Whether to use cached results
|
|
33
|
+
* @returns The fully resolved configuration set including plugins and errors
|
|
34
|
+
*/
|
|
11
35
|
resolve(targetFile: Readonly<MLFile>, names: readonly Nullable<string>[], cache?: boolean): Promise<ConfigSet>;
|
|
36
|
+
/**
|
|
37
|
+
* Searches for a markuplint configuration file starting from the target file's directory.
|
|
38
|
+
*
|
|
39
|
+
* @param targetFile - The file whose directory to search from
|
|
40
|
+
* @returns The file path of the found config, or `null` if none was found
|
|
41
|
+
*/
|
|
12
42
|
search(targetFile: Readonly<MLFile>): Promise<string | null>;
|
|
43
|
+
/**
|
|
44
|
+
* Stores a pre-built configuration in the provider's internal store.
|
|
45
|
+
*
|
|
46
|
+
* @param config - The optimized configuration to store
|
|
47
|
+
* @param key - An optional key to store the config under; auto-generated if omitted
|
|
48
|
+
* @returns The key under which the config was stored
|
|
49
|
+
*/
|
|
13
50
|
set(config: OptimizedConfig, key?: string): string;
|
|
14
51
|
private _load;
|
|
15
52
|
private _mergeConfigs;
|
package/lib/config-provider.js
CHANGED
|
@@ -1,9 +1,3 @@
|
|
|
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 _ConfigProvider_cache, _ConfigProvider_held, _ConfigProvider_recursiveLoadKeyAndDepth, _ConfigProvider_store;
|
|
7
1
|
import path from 'node:path';
|
|
8
2
|
import { mergeConfig } from '@markuplint/ml-config';
|
|
9
3
|
import { ConfigParserError } from '@markuplint/parser-utils';
|
|
@@ -22,25 +16,38 @@ import { cacheClear, resolvePlugins } from './resolve-plugins.js';
|
|
|
22
16
|
import { fileExists, uuid } from './utils.js';
|
|
23
17
|
const cpLog = log.extend('config-provider');
|
|
24
18
|
const KEY_SEPARATOR = '__ML_CONFIG_MERGE__';
|
|
19
|
+
/**
|
|
20
|
+
* Manages loading, caching, and resolving markuplint configuration files.
|
|
21
|
+
*
|
|
22
|
+
* Handles `extends` chains, plugins, presets, overrides, and circular reference detection.
|
|
23
|
+
* Configuration files are searched via cosmiconfig and cached by file path.
|
|
24
|
+
*/
|
|
25
25
|
export class ConfigProvider {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
26
|
+
#cache = new Map();
|
|
27
|
+
#held = new Set();
|
|
28
|
+
#recursiveLoadKeyAndDepth = new Map();
|
|
29
|
+
#store = new Map();
|
|
30
|
+
/**
|
|
31
|
+
* Recursively loads a configuration and all its `extends` dependencies.
|
|
32
|
+
*
|
|
33
|
+
* @param key - The config file path or module name to load
|
|
34
|
+
* @param cache - Whether to use cached results
|
|
35
|
+
* @param referrer - The file path of the config that referenced this key
|
|
36
|
+
* @param depth - Current recursion depth (for circular reference detection)
|
|
37
|
+
* @returns A set of loaded config keys and any errors encountered
|
|
38
|
+
*/
|
|
32
39
|
async recursiveLoad(key, cache, referrer, depth = 1) {
|
|
33
40
|
const stack = new Set();
|
|
34
41
|
const errs = [];
|
|
35
|
-
const ancestorDepth =
|
|
42
|
+
const ancestorDepth = this.#recursiveLoadKeyAndDepth.get(key);
|
|
36
43
|
if (ancestorDepth != null && ancestorDepth < depth) {
|
|
37
44
|
return {
|
|
38
45
|
stack,
|
|
39
46
|
errs: [new CircularReferenceError(`Circular reference detected: ${key}`)],
|
|
40
47
|
};
|
|
41
48
|
}
|
|
42
|
-
|
|
43
|
-
let config =
|
|
49
|
+
this.#recursiveLoadKeyAndDepth.set(key, depth);
|
|
50
|
+
let config = this.#store.get(key);
|
|
44
51
|
if (!config) {
|
|
45
52
|
config = await this._load(key, cache, referrer);
|
|
46
53
|
}
|
|
@@ -67,20 +74,29 @@ export class ConfigProvider {
|
|
|
67
74
|
stack.add(key);
|
|
68
75
|
return { stack, errs };
|
|
69
76
|
}
|
|
77
|
+
/**
|
|
78
|
+
* Resolves the full configuration for a target file by merging all named configs,
|
|
79
|
+
* resolving plugins, and applying file-specific overrides.
|
|
80
|
+
*
|
|
81
|
+
* @param targetFile - The file being linted
|
|
82
|
+
* @param names - Config file paths or module names to merge
|
|
83
|
+
* @param cache - Whether to use cached results
|
|
84
|
+
* @returns The fully resolved configuration set including plugins and errors
|
|
85
|
+
*/
|
|
70
86
|
async resolve(targetFile, names, cache = true) {
|
|
71
87
|
if (!cache) {
|
|
72
|
-
|
|
73
|
-
|
|
88
|
+
this.#store.clear();
|
|
89
|
+
this.#cache.clear();
|
|
74
90
|
cacheClear();
|
|
75
91
|
}
|
|
76
92
|
const keys = names.filter(nonNullableFilter);
|
|
77
93
|
const key = keys.join(KEY_SEPARATOR);
|
|
78
|
-
const currentConfig =
|
|
94
|
+
const currentConfig = this.#cache.get(key);
|
|
79
95
|
if (currentConfig) {
|
|
80
96
|
return currentConfig;
|
|
81
97
|
}
|
|
82
98
|
let configSet = await this._mergeConfigs(keys, cache, targetFile.path);
|
|
83
|
-
const filePath = [...configSet.files].
|
|
99
|
+
const filePath = [...configSet.files].toReversed()[0];
|
|
84
100
|
if (!filePath) {
|
|
85
101
|
throw new ConfigParserError('Config file not found', {
|
|
86
102
|
filePath: targetFile.path,
|
|
@@ -90,8 +106,8 @@ export class ConfigProvider {
|
|
|
90
106
|
configSet.errs.push(...errors);
|
|
91
107
|
const { plugins, errors: pluginErrors } = await resolvePlugins(configSet.config.plugins);
|
|
92
108
|
configSet.errs.push(...pluginErrors);
|
|
93
|
-
if (
|
|
94
|
-
const extendHelds = [...
|
|
109
|
+
if (this.#held.size > 0) {
|
|
110
|
+
const extendHelds = [...this.#held.values()];
|
|
95
111
|
for (const held of extendHelds) {
|
|
96
112
|
const [, prefix, namespace, name] = held.match(/^([a-z]+:)([^/]+)(?:\/(.+))?$/) ?? [];
|
|
97
113
|
switch (prefix) {
|
|
@@ -106,7 +122,7 @@ export class ConfigProvider {
|
|
|
106
122
|
}
|
|
107
123
|
}
|
|
108
124
|
configSet = await this._mergeConfigs([...keys, ...extendHelds], cache, targetFile.path);
|
|
109
|
-
|
|
125
|
+
this.#held.clear();
|
|
110
126
|
}
|
|
111
127
|
// Resolves `overrides`
|
|
112
128
|
if (configSet.config.overrides) {
|
|
@@ -133,9 +149,15 @@ export class ConfigProvider {
|
|
|
133
149
|
...configSet,
|
|
134
150
|
plugins,
|
|
135
151
|
};
|
|
136
|
-
|
|
152
|
+
this.#cache.set(key, result);
|
|
137
153
|
return result;
|
|
138
154
|
}
|
|
155
|
+
/**
|
|
156
|
+
* Searches for a markuplint configuration file starting from the target file's directory.
|
|
157
|
+
*
|
|
158
|
+
* @param targetFile - The file whose directory to search from
|
|
159
|
+
* @returns The file path of the found config, or `null` if none was found
|
|
160
|
+
*/
|
|
139
161
|
async search(targetFile) {
|
|
140
162
|
const isExists = await targetFile.dirExists();
|
|
141
163
|
cpLog('search: %s', targetFile.path);
|
|
@@ -150,17 +172,24 @@ export class ConfigProvider {
|
|
|
150
172
|
}
|
|
151
173
|
const { filePath, config } = res;
|
|
152
174
|
const pathResolvedConfig = await this._pathResolve(config, filePath);
|
|
153
|
-
|
|
175
|
+
this.#store.set(filePath, pathResolvedConfig);
|
|
154
176
|
cpLog('Store key: %s', filePath);
|
|
155
177
|
return filePath;
|
|
156
178
|
}
|
|
179
|
+
/**
|
|
180
|
+
* Stores a pre-built configuration in the provider's internal store.
|
|
181
|
+
*
|
|
182
|
+
* @param config - The optimized configuration to store
|
|
183
|
+
* @param key - An optional key to store the config under; auto-generated if omitted
|
|
184
|
+
* @returns The key under which the config was stored
|
|
185
|
+
*/
|
|
157
186
|
set(config, key) {
|
|
158
187
|
key = key ?? uuid();
|
|
159
|
-
|
|
188
|
+
this.#store.set(key, config);
|
|
160
189
|
return key;
|
|
161
190
|
}
|
|
162
191
|
async _load(filePath, cache, referrer) {
|
|
163
|
-
const entity =
|
|
192
|
+
const entity = this.#store.get(filePath);
|
|
164
193
|
if (entity) {
|
|
165
194
|
return entity;
|
|
166
195
|
}
|
|
@@ -168,11 +197,11 @@ export class ConfigProvider {
|
|
|
168
197
|
const [, name] = filePath.match(/^markuplint:(.+)$/i) ?? [];
|
|
169
198
|
const config = await getPreset(name ?? filePath);
|
|
170
199
|
const pathResolvedConfig = await this._pathResolve(config, filePath);
|
|
171
|
-
|
|
200
|
+
this.#store.set(filePath, pathResolvedConfig);
|
|
172
201
|
return pathResolvedConfig;
|
|
173
202
|
}
|
|
174
203
|
if (isPluginModuleName(filePath)) {
|
|
175
|
-
|
|
204
|
+
this.#held.add(filePath);
|
|
176
205
|
return;
|
|
177
206
|
}
|
|
178
207
|
if (!(await moduleExists(filePath)) && !path.isAbsolute(filePath)) {
|
|
@@ -183,21 +212,21 @@ export class ConfigProvider {
|
|
|
183
212
|
return config;
|
|
184
213
|
}
|
|
185
214
|
const pathResolvedConfig = await this._pathResolve(config, filePath);
|
|
186
|
-
|
|
215
|
+
this.#store.set(filePath, pathResolvedConfig);
|
|
187
216
|
return pathResolvedConfig;
|
|
188
217
|
}
|
|
189
218
|
async _mergeConfigs(keys, cache, referrer) {
|
|
190
219
|
const resolvedKeys = new Set();
|
|
191
220
|
const errs = [];
|
|
192
221
|
for (const key of keys) {
|
|
193
|
-
|
|
222
|
+
this.#recursiveLoadKeyAndDepth.clear();
|
|
194
223
|
const keySet = await this.recursiveLoad(key, cache, referrer);
|
|
195
224
|
for (const k of keySet.stack) {
|
|
196
225
|
resolvedKeys.add(k);
|
|
197
226
|
}
|
|
198
227
|
errs.push(...keySet.errs);
|
|
199
228
|
}
|
|
200
|
-
const configs = [...resolvedKeys].map(name =>
|
|
229
|
+
const configs = [...resolvedKeys].map(name => this.#store.get(name)).filter(nonNullableFilter);
|
|
201
230
|
let resultConfig = {};
|
|
202
231
|
for (const config of configs) {
|
|
203
232
|
if (config instanceof ConfigLoadError) {
|
|
@@ -252,7 +281,6 @@ export class ConfigProvider {
|
|
|
252
281
|
return errors;
|
|
253
282
|
}
|
|
254
283
|
}
|
|
255
|
-
_ConfigProvider_cache = new WeakMap(), _ConfigProvider_held = new WeakMap(), _ConfigProvider_recursiveLoadKeyAndDepth = new WeakMap(), _ConfigProvider_store = new WeakMap();
|
|
256
284
|
async function load(filePath, cache, referrer) {
|
|
257
285
|
if (!fileExists(filePath) && (await moduleExists(filePath))) {
|
|
258
286
|
const config = (await generalImport(filePath)) ?? new ConfigLoadError('Module is not found', filePath, referrer);
|
|
@@ -270,8 +298,5 @@ async function load(filePath, cache, referrer) {
|
|
|
270
298
|
return res.config;
|
|
271
299
|
}
|
|
272
300
|
class CircularReferenceError extends ReferenceError {
|
|
273
|
-
|
|
274
|
-
super(...arguments);
|
|
275
|
-
this.name = 'CircularReferenceError';
|
|
276
|
-
}
|
|
301
|
+
name = 'CircularReferenceError';
|
|
277
302
|
}
|
package/lib/cosmiconfig.js
CHANGED
|
@@ -5,19 +5,38 @@ import { jsonc } from 'jsonc';
|
|
|
5
5
|
import { ConfigLoadError } from './config-load-error.js';
|
|
6
6
|
import { log } from './debug.js';
|
|
7
7
|
const searchLog = log.extend('search');
|
|
8
|
+
const jsoncLoader = (path, content) => {
|
|
9
|
+
try {
|
|
10
|
+
return jsonc.parse(content);
|
|
11
|
+
}
|
|
12
|
+
catch (error) {
|
|
13
|
+
if (error instanceof Error && error.name === 'JSONError') {
|
|
14
|
+
return defaultLoaders['noExt'](path, content);
|
|
15
|
+
}
|
|
16
|
+
throw error;
|
|
17
|
+
}
|
|
18
|
+
};
|
|
8
19
|
const explorer = cosmiconfig('markuplint', {
|
|
20
|
+
searchPlaces: [
|
|
21
|
+
'package.json',
|
|
22
|
+
'.markuplintrc',
|
|
23
|
+
'.markuplintrc.json',
|
|
24
|
+
'.markuplintrc.jsonc',
|
|
25
|
+
'.markuplintrc.yaml',
|
|
26
|
+
'.markuplintrc.yml',
|
|
27
|
+
'.markuplintrc.js',
|
|
28
|
+
'.markuplintrc.ts',
|
|
29
|
+
'.markuplintrc.cjs',
|
|
30
|
+
'.markuplintrc.mjs',
|
|
31
|
+
'markuplint.config.js',
|
|
32
|
+
'markuplint.config.ts',
|
|
33
|
+
'markuplint.config.cjs',
|
|
34
|
+
'markuplint.config.mjs',
|
|
35
|
+
'markuplint.config.jsonc',
|
|
36
|
+
],
|
|
9
37
|
loaders: {
|
|
10
|
-
noExt:
|
|
11
|
-
|
|
12
|
-
return jsonc.parse(content);
|
|
13
|
-
}
|
|
14
|
-
catch (error) {
|
|
15
|
-
if (error instanceof Error && error.name === 'JSONError') {
|
|
16
|
-
return defaultLoaders['noExt'](path, content);
|
|
17
|
-
}
|
|
18
|
-
throw error;
|
|
19
|
-
}
|
|
20
|
-
}),
|
|
38
|
+
noExt: jsoncLoader,
|
|
39
|
+
'.jsonc': jsoncLoader,
|
|
21
40
|
},
|
|
22
41
|
searchStrategy: 'project',
|
|
23
42
|
});
|
package/lib/ml-file/ml-file.js
CHANGED
|
@@ -1,51 +1,39 @@
|
|
|
1
|
-
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
2
|
-
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
3
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
4
|
-
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");
|
|
5
|
-
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
6
|
-
};
|
|
7
|
-
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
8
|
-
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
9
|
-
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");
|
|
10
|
-
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
11
|
-
};
|
|
12
|
-
var _MLFile_basename, _MLFile_code, _MLFile_dirname, _MLFile_stat, _MLFile_type;
|
|
13
1
|
import { promises as fs } from 'node:fs';
|
|
14
2
|
import path from 'node:path';
|
|
15
3
|
import ignore from 'ignore';
|
|
16
4
|
import { minimatch } from 'minimatch';
|
|
17
5
|
export class MLFile {
|
|
6
|
+
#basename;
|
|
7
|
+
#code;
|
|
8
|
+
#dirname;
|
|
9
|
+
/**
|
|
10
|
+
* - `Stats`: Exists
|
|
11
|
+
* - `null`: Not exists
|
|
12
|
+
* - `undefined`: Doesn't read yet
|
|
13
|
+
*/
|
|
14
|
+
#stat = undefined;
|
|
15
|
+
#type;
|
|
18
16
|
constructor(target) {
|
|
19
|
-
_MLFile_basename.set(this, void 0);
|
|
20
|
-
_MLFile_code.set(this, void 0);
|
|
21
|
-
_MLFile_dirname.set(this, void 0);
|
|
22
|
-
/**
|
|
23
|
-
* - `Stats`: Exists
|
|
24
|
-
* - `null`: Not exists
|
|
25
|
-
* - `undefined`: Doesn't read yet
|
|
26
|
-
*/
|
|
27
|
-
_MLFile_stat.set(this, undefined);
|
|
28
|
-
_MLFile_type.set(this, void 0);
|
|
29
17
|
if (typeof target === 'string') {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
18
|
+
this.#basename = path.basename(target);
|
|
19
|
+
this.#dirname = path.dirname(target);
|
|
20
|
+
this.#code = null;
|
|
21
|
+
this.#type = 'file-base';
|
|
34
22
|
return;
|
|
35
23
|
}
|
|
36
24
|
if (!target.workspace && target.name && path.isAbsolute(target.name)) {
|
|
37
|
-
|
|
38
|
-
|
|
25
|
+
this.#basename = path.basename(target.name);
|
|
26
|
+
this.#dirname = path.dirname(target.name);
|
|
39
27
|
}
|
|
40
28
|
else {
|
|
41
|
-
|
|
42
|
-
|
|
29
|
+
this.#basename = target.name ?? '<AnonymousFile>';
|
|
30
|
+
this.#dirname = target.workspace ?? process.cwd();
|
|
43
31
|
}
|
|
44
|
-
|
|
45
|
-
|
|
32
|
+
this.#code = target.sourceCode;
|
|
33
|
+
this.#type = 'code-base';
|
|
46
34
|
}
|
|
47
35
|
get dirname() {
|
|
48
|
-
return
|
|
36
|
+
return this.#dirname;
|
|
49
37
|
}
|
|
50
38
|
/**
|
|
51
39
|
* Normalized `MLFile.dirname`
|
|
@@ -60,16 +48,16 @@ export class MLFile {
|
|
|
60
48
|
return pathNormalize(this.path);
|
|
61
49
|
}
|
|
62
50
|
get path() {
|
|
63
|
-
return path.resolve(
|
|
51
|
+
return path.resolve(this.#dirname, this.#basename);
|
|
64
52
|
}
|
|
65
53
|
async dirExists() {
|
|
66
|
-
return !!(await stat(
|
|
54
|
+
return !!(await stat(this.#dirname));
|
|
67
55
|
}
|
|
68
56
|
async getCode() {
|
|
69
|
-
if (
|
|
70
|
-
return
|
|
57
|
+
if (this.#code != null) {
|
|
58
|
+
return this.#code;
|
|
71
59
|
}
|
|
72
|
-
if (
|
|
60
|
+
if (this.#type === 'file-base' && (await this.isExist())) {
|
|
73
61
|
return await this._fetch();
|
|
74
62
|
}
|
|
75
63
|
return '';
|
|
@@ -83,14 +71,14 @@ export class MLFile {
|
|
|
83
71
|
return ignored;
|
|
84
72
|
}
|
|
85
73
|
async isExist() {
|
|
86
|
-
if (
|
|
74
|
+
if (this.#type === 'code-base') {
|
|
87
75
|
return true;
|
|
88
76
|
}
|
|
89
77
|
const stat = await this._stat();
|
|
90
78
|
return !!stat;
|
|
91
79
|
}
|
|
92
80
|
async isFile() {
|
|
93
|
-
if (
|
|
81
|
+
if (this.#type === 'code-base') {
|
|
94
82
|
return true;
|
|
95
83
|
}
|
|
96
84
|
const stat = await this._stat();
|
|
@@ -100,25 +88,24 @@ export class MLFile {
|
|
|
100
88
|
return minimatch(this.nPath, pathNormalize(globPath));
|
|
101
89
|
}
|
|
102
90
|
setCode(code) {
|
|
103
|
-
if (
|
|
91
|
+
if (this.#type === 'file-base') {
|
|
104
92
|
throw new Error(`This file object is readonly (File-base: ${this.path})`);
|
|
105
93
|
}
|
|
106
|
-
|
|
94
|
+
this.#code = code;
|
|
107
95
|
}
|
|
108
96
|
async _fetch() {
|
|
109
97
|
const code = await fs.readFile(this.path, { encoding: 'utf8' });
|
|
110
|
-
|
|
98
|
+
this.#code = code;
|
|
111
99
|
return code;
|
|
112
100
|
}
|
|
113
101
|
async _stat() {
|
|
114
|
-
if (
|
|
115
|
-
return
|
|
102
|
+
if (this.#stat) {
|
|
103
|
+
return this.#stat;
|
|
116
104
|
}
|
|
117
|
-
|
|
118
|
-
return
|
|
105
|
+
this.#stat = await stat(this.path);
|
|
106
|
+
return this.#stat;
|
|
119
107
|
}
|
|
120
108
|
}
|
|
121
|
-
_MLFile_basename = new WeakMap(), _MLFile_code = new WeakMap(), _MLFile_dirname = new WeakMap(), _MLFile_stat = new WeakMap(), _MLFile_type = new WeakMap();
|
|
122
109
|
async function stat(filePath) {
|
|
123
110
|
try {
|
|
124
111
|
return await fs.stat(filePath);
|
package/lib/resolve-files.d.ts
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
1
|
import type { MLFile } from './ml-file/index.js';
|
|
2
2
|
import type { Target } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Resolves a list of targets (file globs or inline source code objects) into
|
|
5
|
+
* an array of {@link MLFile} instances.
|
|
6
|
+
*
|
|
7
|
+
* @param targetList - An array of file path globs or inline source code targets
|
|
8
|
+
* @param ignoreGlob - An optional glob pattern for files to exclude
|
|
9
|
+
* @returns An array of resolved MLFile instances
|
|
10
|
+
*/
|
|
3
11
|
export declare function resolveFiles(targetList: readonly Readonly<Target>[], ignoreGlob?: string): Promise<MLFile[]>;
|
package/lib/resolve-files.js
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
import { getAnonymousFile, getFiles } from './ml-file/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Resolves a list of targets (file globs or inline source code objects) into
|
|
4
|
+
* an array of {@link MLFile} instances.
|
|
5
|
+
*
|
|
6
|
+
* @param targetList - An array of file path globs or inline source code targets
|
|
7
|
+
* @param ignoreGlob - An optional glob pattern for files to exclude
|
|
8
|
+
* @returns An array of resolved MLFile instances
|
|
9
|
+
*/
|
|
2
10
|
export async function resolveFiles(targetList, ignoreGlob) {
|
|
3
11
|
const res = [];
|
|
4
12
|
for (const target of targetList) {
|
package/lib/resolve-parser.d.ts
CHANGED
|
@@ -1,9 +1,20 @@
|
|
|
1
1
|
import type { MLFile } from './ml-file/index.js';
|
|
2
|
-
import type {
|
|
2
|
+
import type { MLParser, ParserOptions } from '@markuplint/ml-ast';
|
|
3
3
|
import type { ParserConfig } from '@markuplint/ml-config';
|
|
4
|
+
/**
|
|
5
|
+
* Resolves the appropriate parser for a given file based on the parser configuration.
|
|
6
|
+
*
|
|
7
|
+
* Matches the file's basename against patterns in the parser config to find
|
|
8
|
+
* the correct parser module. Falls back to the HTML parser if no pattern matches.
|
|
9
|
+
*
|
|
10
|
+
* @param file - The file to find a parser for
|
|
11
|
+
* @param parserConfig - A mapping of file extension patterns to parser module names
|
|
12
|
+
* @param parserOptions - Parser options to pass through
|
|
13
|
+
* @returns The resolved parser, its module name, parser options, and whether a pattern matched
|
|
14
|
+
*/
|
|
4
15
|
export declare function resolveParser(file: Readonly<MLFile>, parserConfig?: ParserConfig, parserOptions?: ParserOptions): Promise<{
|
|
5
16
|
parserModName: string;
|
|
6
|
-
parser: MLParser
|
|
17
|
+
parser: MLParser;
|
|
7
18
|
parserOptions: ParserOptions;
|
|
8
19
|
matched: boolean;
|
|
9
20
|
}>;
|
package/lib/resolve-parser.js
CHANGED
|
@@ -2,6 +2,17 @@ import path from 'node:path';
|
|
|
2
2
|
import { generalImport } from './general-import.js';
|
|
3
3
|
import { toRegexp } from './utils.js';
|
|
4
4
|
const parsers = new Map();
|
|
5
|
+
/**
|
|
6
|
+
* Resolves the appropriate parser for a given file based on the parser configuration.
|
|
7
|
+
*
|
|
8
|
+
* Matches the file's basename against patterns in the parser config to find
|
|
9
|
+
* the correct parser module. Falls back to the HTML parser if no pattern matches.
|
|
10
|
+
*
|
|
11
|
+
* @param file - The file to find a parser for
|
|
12
|
+
* @param parserConfig - A mapping of file extension patterns to parser module names
|
|
13
|
+
* @param parserOptions - Parser options to pass through
|
|
14
|
+
* @returns The resolved parser, its module name, parser options, and whether a pattern matched
|
|
15
|
+
*/
|
|
5
16
|
export async function resolveParser(file, parserConfig, parserOptions) {
|
|
6
17
|
parserConfig = {
|
|
7
18
|
...parserConfig,
|
|
@@ -39,9 +50,5 @@ async function importParser(parserModName) {
|
|
|
39
50
|
if (!parserMod) {
|
|
40
51
|
throw new Error(`Parser module "${parserModName}" is not found.`);
|
|
41
52
|
}
|
|
42
|
-
// TODO: To be dropped in v5
|
|
43
|
-
if (!('parser' in parserMod)) {
|
|
44
|
-
return parserMod;
|
|
45
|
-
}
|
|
46
53
|
return parserMod.parser;
|
|
47
54
|
}
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
import type { OptimizedConfig, Pretender } from '@markuplint/ml-config';
|
|
2
2
|
type PretendersConfig = OptimizedConfig['pretenders'];
|
|
3
|
+
/**
|
|
4
|
+
* Resolves pretender definitions from files, imported modules, and inline data
|
|
5
|
+
* in the configuration.
|
|
6
|
+
*
|
|
7
|
+
* @param config - The pretenders configuration section from the optimized config
|
|
8
|
+
* @returns An array of all resolved pretender definitions
|
|
9
|
+
*/
|
|
3
10
|
export declare function resolvePretenders(config: PretendersConfig): Promise<Pretender[]>;
|
|
4
11
|
export {};
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
import { generalImport } from './general-import.js';
|
|
2
|
+
/**
|
|
3
|
+
* Resolves pretender definitions from files, imported modules, and inline data
|
|
4
|
+
* in the configuration.
|
|
5
|
+
*
|
|
6
|
+
* @param config - The pretenders configuration section from the optimized config
|
|
7
|
+
* @returns An array of all resolved pretender definitions
|
|
8
|
+
*/
|
|
2
9
|
export async function resolvePretenders(config) {
|
|
3
10
|
if (!config) {
|
|
4
11
|
return [];
|
package/lib/resolve-rules.d.ts
CHANGED
|
@@ -1,4 +1,16 @@
|
|
|
1
1
|
import type { AnyMLRule, Ruleset, Plugin } from '@markuplint/ml-core';
|
|
2
|
+
/**
|
|
3
|
+
* Resolves all rules from preset rules, plugins, and auto-loaded rules into
|
|
4
|
+
* a flat array of {@link MLRule} instances.
|
|
5
|
+
*
|
|
6
|
+
* @param plugins - The resolved plugins that may provide custom rules
|
|
7
|
+
* @param ruleset - The current ruleset (used for auto-loading)
|
|
8
|
+
* @param importPreset - Whether to import the built-in preset rules from `@markuplint/rules`
|
|
9
|
+
* @param autoLoad - Whether to auto-load rules referenced in the ruleset
|
|
10
|
+
* @returns An array of all resolved MLRule instances
|
|
11
|
+
*
|
|
12
|
+
* @deprecated The `autoLoad` parameter is deprecated
|
|
13
|
+
*/
|
|
2
14
|
export declare function resolveRules(plugins: readonly Plugin[], ruleset: Ruleset, importPreset: boolean,
|
|
3
15
|
/**
|
|
4
16
|
* @deprecated
|
package/lib/resolve-rules.js
CHANGED
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
import { MLRule } from '@markuplint/ml-core';
|
|
2
2
|
import { autoLoadRules } from './auto-load-rules.js';
|
|
3
3
|
let cachedPresetRules = null;
|
|
4
|
+
/**
|
|
5
|
+
* Resolves all rules from preset rules, plugins, and auto-loaded rules into
|
|
6
|
+
* a flat array of {@link MLRule} instances.
|
|
7
|
+
*
|
|
8
|
+
* @param plugins - The resolved plugins that may provide custom rules
|
|
9
|
+
* @param ruleset - The current ruleset (used for auto-loading)
|
|
10
|
+
* @param importPreset - Whether to import the built-in preset rules from `@markuplint/rules`
|
|
11
|
+
* @param autoLoad - Whether to auto-load rules referenced in the ruleset
|
|
12
|
+
* @returns An array of all resolved MLRule instances
|
|
13
|
+
*
|
|
14
|
+
* @deprecated The `autoLoad` parameter is deprecated
|
|
15
|
+
*/
|
|
4
16
|
export async function resolveRules(plugins, ruleset, importPreset,
|
|
5
17
|
/**
|
|
6
18
|
* @deprecated
|
package/lib/resolve-specs.d.ts
CHANGED
|
@@ -26,9 +26,9 @@ import type { ExtendedSpec, MLMLSpec } from '@markuplint/ml-spec';
|
|
|
26
26
|
* }
|
|
27
27
|
* ```
|
|
28
28
|
*
|
|
29
|
-
* @param filePath The
|
|
30
|
-
* @param specConfig The `
|
|
31
|
-
* @returns
|
|
29
|
+
* @param filePath - The path of the file being linted, used for pattern matching
|
|
30
|
+
* @param specConfig - The `specs` property from the config, mapping file patterns to spec module names
|
|
31
|
+
* @returns An object containing the base HTML spec and any matched extended specs as a schemas tuple
|
|
32
32
|
*/
|
|
33
33
|
export declare function resolveSpecs(filePath: string, specConfig?: SpecConfig): Promise<{
|
|
34
34
|
schemas: readonly [MLMLSpec, ...ExtendedSpec[]];
|
package/lib/resolve-specs.js
CHANGED
|
@@ -28,9 +28,9 @@ const caches = new Map();
|
|
|
28
28
|
* }
|
|
29
29
|
* ```
|
|
30
30
|
*
|
|
31
|
-
* @param filePath The
|
|
32
|
-
* @param specConfig The `
|
|
33
|
-
* @returns
|
|
31
|
+
* @param filePath - The path of the file being linted, used for pattern matching
|
|
32
|
+
* @param specConfig - The `specs` property from the config, mapping file patterns to spec module names
|
|
33
|
+
* @returns An object containing the base HTML spec and any matched extended specs as a schemas tuple
|
|
34
34
|
*/
|
|
35
35
|
export async function resolveSpecs(filePath, specConfig) {
|
|
36
36
|
const htmlSpec = await importSpecs('@markuplint/html-spec');
|
package/lib/types.d.ts
CHANGED
|
@@ -1,11 +1,22 @@
|
|
|
1
1
|
import type { OptimizedConfig } from '@markuplint/ml-config';
|
|
2
2
|
import type { Plugin } from '@markuplint/ml-core';
|
|
3
|
+
/**
|
|
4
|
+
* A fully resolved configuration set including merged config, plugins, source file paths,
|
|
5
|
+
* and any errors encountered during resolution.
|
|
6
|
+
*/
|
|
3
7
|
export interface ConfigSet {
|
|
8
|
+
/** The merged and optimized configuration */
|
|
4
9
|
readonly config: OptimizedConfig;
|
|
10
|
+
/** The resolved plugins */
|
|
5
11
|
readonly plugins: readonly Plugin[];
|
|
12
|
+
/** The set of config file paths that contributed to this configuration */
|
|
6
13
|
readonly files: ReadonlySet<string>;
|
|
14
|
+
/** Errors encountered during config loading or resolution */
|
|
7
15
|
readonly errs: readonly Readonly<Error>[];
|
|
8
16
|
}
|
|
17
|
+
/**
|
|
18
|
+
* A lint target: either a file path/glob string, or an inline source code object.
|
|
19
|
+
*/
|
|
9
20
|
export type Target = string | {
|
|
10
21
|
/**
|
|
11
22
|
* Target source code of evaluation
|
package/package.json
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@markuplint/file-resolver",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "5.0.0-alpha.0",
|
|
4
4
|
"description": "The file resolver of markuplint",
|
|
5
5
|
"repository": "git@github.com:markuplint/markuplint.git",
|
|
6
6
|
"author": "Yusuke Hirao <yusukehirao@me.com>",
|
|
7
7
|
"license": "MIT",
|
|
8
|
+
"engines": {
|
|
9
|
+
"node": ">=22"
|
|
10
|
+
},
|
|
8
11
|
"type": "module",
|
|
9
12
|
"exports": {
|
|
10
13
|
".": {
|
|
@@ -27,21 +30,21 @@
|
|
|
27
30
|
"@types/node": "24.5.1"
|
|
28
31
|
},
|
|
29
32
|
"dependencies": {
|
|
30
|
-
"@markuplint/html-parser": "
|
|
31
|
-
"@markuplint/ml-ast": "
|
|
32
|
-
"@markuplint/ml-config": "
|
|
33
|
-
"@markuplint/ml-core": "
|
|
34
|
-
"@markuplint/ml-spec": "
|
|
35
|
-
"@markuplint/parser-utils": "
|
|
36
|
-
"@markuplint/selector": "
|
|
37
|
-
"@markuplint/shared": "
|
|
33
|
+
"@markuplint/html-parser": "5.0.0-alpha.0",
|
|
34
|
+
"@markuplint/ml-ast": "5.0.0-alpha.0",
|
|
35
|
+
"@markuplint/ml-config": "5.0.0-alpha.0",
|
|
36
|
+
"@markuplint/ml-core": "5.0.0-alpha.0",
|
|
37
|
+
"@markuplint/ml-spec": "5.0.0-alpha.0",
|
|
38
|
+
"@markuplint/parser-utils": "5.0.0-alpha.0",
|
|
39
|
+
"@markuplint/selector": "5.0.0-alpha.0",
|
|
40
|
+
"@markuplint/shared": "5.0.0-alpha.0",
|
|
38
41
|
"cosmiconfig": "9.0.0",
|
|
39
42
|
"debug": "4.4.3",
|
|
40
|
-
"glob": "
|
|
43
|
+
"glob": "13.0.6",
|
|
41
44
|
"ignore": "7.0.5",
|
|
42
45
|
"import-meta-resolve": "4.2.0",
|
|
43
46
|
"jsonc": "2.0.0",
|
|
44
|
-
"minimatch": "10.
|
|
47
|
+
"minimatch": "10.2.2"
|
|
45
48
|
},
|
|
46
|
-
"gitHead": "
|
|
49
|
+
"gitHead": "13dcfc84ec83d87360c720e253383b60767e1b56"
|
|
47
50
|
}
|