@markuplint/file-resolver 4.0.0-alpha.2 → 4.0.0-dev.28
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/lib/auto-load-rules.js +6 -6
- package/lib/config-provider.d.ts +4 -1
- package/lib/config-provider.js +101 -81
- package/lib/cosmiconfig.d.ts +9 -3
- package/lib/cosmiconfig.js +19 -12
- package/lib/debug.d.ts +3 -0
- package/lib/debug.js +2 -0
- package/lib/ml-file/ml-file.js +16 -6
- package/lib/resolve-parser.js +1 -0
- package/lib/resolve-plugins.js +3 -3
- package/lib/resolve-rules.js +10 -10
- package/lib/resolve-specs.js +3 -1
- package/lib/utils.js +1 -1
- package/package.json +13 -13
package/lib/auto-load-rules.js
CHANGED
|
@@ -20,8 +20,8 @@ export async function autoLoadRules(ruleset) {
|
|
|
20
20
|
seed = null;
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
|
-
catch (
|
|
24
|
-
errors.push(
|
|
23
|
+
catch (error) {
|
|
24
|
+
errors.push(error);
|
|
25
25
|
}
|
|
26
26
|
if (seed) {
|
|
27
27
|
const rule = new MLRule({
|
|
@@ -39,8 +39,8 @@ export async function autoLoadRules(ruleset) {
|
|
|
39
39
|
seed = null;
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
|
-
catch (
|
|
43
|
-
errors.push(
|
|
42
|
+
catch (error) {
|
|
43
|
+
errors.push(error);
|
|
44
44
|
}
|
|
45
45
|
if (seed) {
|
|
46
46
|
const rule = new MLRule({
|
|
@@ -54,8 +54,8 @@ export async function autoLoadRules(ruleset) {
|
|
|
54
54
|
}
|
|
55
55
|
return {
|
|
56
56
|
// Clone
|
|
57
|
-
rules: rules
|
|
57
|
+
rules: [...rules],
|
|
58
58
|
// Clone
|
|
59
|
-
errors: errors
|
|
59
|
+
errors: [...errors],
|
|
60
60
|
};
|
|
61
61
|
}
|
package/lib/config-provider.d.ts
CHANGED
|
@@ -4,12 +4,15 @@ import type { Config } from '@markuplint/ml-config';
|
|
|
4
4
|
import type { Nullable } from '@markuplint/shared';
|
|
5
5
|
export declare class ConfigProvider {
|
|
6
6
|
#private;
|
|
7
|
+
recursiveLoad(key: string, cache: boolean, referrer: string, depth?: number): Promise<{
|
|
8
|
+
stack: Set<string>;
|
|
9
|
+
errs: Error[];
|
|
10
|
+
}>;
|
|
7
11
|
resolve(targetFile: Readonly<MLFile>, names: readonly Nullable<string>[], cache?: boolean): Promise<ConfigSet>;
|
|
8
12
|
search(targetFile: Readonly<MLFile>): Promise<string | null>;
|
|
9
13
|
set(config: Config, key?: string): string;
|
|
10
14
|
private _load;
|
|
11
15
|
private _mergeConfigs;
|
|
12
16
|
private _pathResolve;
|
|
13
|
-
private _recursiveLoad;
|
|
14
17
|
private _validateConfig;
|
|
15
18
|
}
|
package/lib/config-provider.js
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
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
|
+
};
|
|
1
6
|
var _ConfigProvider_cache, _ConfigProvider_held, _ConfigProvider_recursiveLoadKeyAndDepth, _ConfigProvider_store;
|
|
2
|
-
import { __classPrivateFieldGet } from "tslib";
|
|
3
7
|
import { createRequire } from 'node:module';
|
|
4
8
|
import path from 'node:path';
|
|
5
9
|
import { mergeConfig } from '@markuplint/ml-config';
|
|
@@ -7,9 +11,11 @@ import { getPreset } from '@markuplint/ml-core';
|
|
|
7
11
|
import { ConfigParserError } from '@markuplint/parser-utils';
|
|
8
12
|
import { InvalidSelectorError, createSelector } from '@markuplint/selector';
|
|
9
13
|
import { nonNullableFilter, toNoEmptyStringArrayFromStringOrArray } from '@markuplint/shared';
|
|
10
|
-
import { load as loadConfig, search } from './cosmiconfig.js';
|
|
14
|
+
import { ConfigLoadError, load as loadConfig, search } from './cosmiconfig.js';
|
|
15
|
+
import { log } from './debug.js';
|
|
11
16
|
import { cacheClear, resolvePlugins } from './resolve-plugins.js';
|
|
12
17
|
import { fileExists, uuid } from './utils.js';
|
|
18
|
+
const cpLog = log.extend('config-provider');
|
|
13
19
|
const require = createRequire(import.meta.url);
|
|
14
20
|
const KEY_SEPARATOR = '__ML_CONFIG_MERGE__';
|
|
15
21
|
export class ConfigProvider {
|
|
@@ -19,6 +25,44 @@ export class ConfigProvider {
|
|
|
19
25
|
_ConfigProvider_recursiveLoadKeyAndDepth.set(this, new Map());
|
|
20
26
|
_ConfigProvider_store.set(this, new Map());
|
|
21
27
|
}
|
|
28
|
+
async recursiveLoad(key, cache, referrer, depth = 1) {
|
|
29
|
+
const stack = new Set();
|
|
30
|
+
const errs = [];
|
|
31
|
+
const ancestorDepth = __classPrivateFieldGet(this, _ConfigProvider_recursiveLoadKeyAndDepth, "f").get(key);
|
|
32
|
+
if (ancestorDepth != null && ancestorDepth < depth) {
|
|
33
|
+
return {
|
|
34
|
+
stack,
|
|
35
|
+
errs: [new CircularReferenceError(`Circular reference detected: ${key}`)],
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
__classPrivateFieldGet(this, _ConfigProvider_recursiveLoadKeyAndDepth, "f").set(key, depth);
|
|
39
|
+
let config = __classPrivateFieldGet(this, _ConfigProvider_store, "f").get(key);
|
|
40
|
+
if (!config) {
|
|
41
|
+
config = await this._load(key, cache, referrer);
|
|
42
|
+
}
|
|
43
|
+
if (!config) {
|
|
44
|
+
return { stack, errs: [] };
|
|
45
|
+
}
|
|
46
|
+
if (config instanceof ConfigLoadError) {
|
|
47
|
+
stack.add(config.filePath);
|
|
48
|
+
return {
|
|
49
|
+
stack,
|
|
50
|
+
errs: [config],
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
const depKeys = config.extends === null ? null : toNoEmptyStringArrayFromStringOrArray(config.extends);
|
|
54
|
+
if (depKeys) {
|
|
55
|
+
for (const depKey of depKeys) {
|
|
56
|
+
const keys = await this.recursiveLoad(depKey, cache, key, depth + 1);
|
|
57
|
+
for (const key of keys.stack) {
|
|
58
|
+
stack.add(key);
|
|
59
|
+
}
|
|
60
|
+
errs.push(...keys.errs);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
stack.add(key);
|
|
64
|
+
return { stack, errs };
|
|
65
|
+
}
|
|
22
66
|
async resolve(targetFile, names, cache = true) {
|
|
23
67
|
if (!cache) {
|
|
24
68
|
__classPrivateFieldGet(this, _ConfigProvider_store, "f").clear();
|
|
@@ -31,8 +75,8 @@ export class ConfigProvider {
|
|
|
31
75
|
if (currentConfig) {
|
|
32
76
|
return currentConfig;
|
|
33
77
|
}
|
|
34
|
-
let configSet = await this._mergeConfigs(keys, cache);
|
|
35
|
-
const filePath =
|
|
78
|
+
let configSet = await this._mergeConfigs(keys, cache, targetFile.path);
|
|
79
|
+
const filePath = [...configSet.files].reverse()[0];
|
|
36
80
|
if (!filePath) {
|
|
37
81
|
throw new ConfigParserError('Config file not found', {
|
|
38
82
|
filePath: targetFile.path,
|
|
@@ -42,7 +86,7 @@ export class ConfigProvider {
|
|
|
42
86
|
configSet.errs.push(...errors);
|
|
43
87
|
const plugins = await resolvePlugins(configSet.config.plugins);
|
|
44
88
|
if (__classPrivateFieldGet(this, _ConfigProvider_held, "f").size > 0) {
|
|
45
|
-
const extendHelds =
|
|
89
|
+
const extendHelds = [...__classPrivateFieldGet(this, _ConfigProvider_held, "f").values()];
|
|
46
90
|
for (const held of extendHelds) {
|
|
47
91
|
const [, prefix, namespace, name] = held.match(/^([a-z]+:)([^/]+)(?:\/(.+))?$/) ?? [];
|
|
48
92
|
switch (prefix) {
|
|
@@ -56,7 +100,7 @@ export class ConfigProvider {
|
|
|
56
100
|
}
|
|
57
101
|
}
|
|
58
102
|
}
|
|
59
|
-
configSet = await this._mergeConfigs([...keys, ...extendHelds], cache);
|
|
103
|
+
configSet = await this._mergeConfigs([...keys, ...extendHelds], cache, targetFile.path);
|
|
60
104
|
__classPrivateFieldGet(this, _ConfigProvider_held, "f").clear();
|
|
61
105
|
}
|
|
62
106
|
// Resolves `overrides`
|
|
@@ -80,16 +124,21 @@ export class ConfigProvider {
|
|
|
80
124
|
return result;
|
|
81
125
|
}
|
|
82
126
|
async search(targetFile) {
|
|
83
|
-
|
|
127
|
+
const isExists = await targetFile.dirExists();
|
|
128
|
+
cpLog('search: %s', targetFile.path);
|
|
129
|
+
cpLog('isExists: %s', isExists);
|
|
130
|
+
if (!isExists) {
|
|
84
131
|
return null;
|
|
85
132
|
}
|
|
86
133
|
const res = await search(targetFile.path, false);
|
|
134
|
+
cpLog('searched config: %O', res);
|
|
87
135
|
if (!res) {
|
|
88
136
|
return null;
|
|
89
137
|
}
|
|
90
138
|
const { filePath, config } = res;
|
|
91
139
|
const pathResolvedConfig = await this._pathResolve(config, filePath);
|
|
92
140
|
__classPrivateFieldGet(this, _ConfigProvider_store, "f").set(filePath, pathResolvedConfig);
|
|
141
|
+
cpLog('Store key: %s', filePath);
|
|
93
142
|
return filePath;
|
|
94
143
|
}
|
|
95
144
|
set(config, key) {
|
|
@@ -97,7 +146,7 @@ export class ConfigProvider {
|
|
|
97
146
|
__classPrivateFieldGet(this, _ConfigProvider_store, "f").set(key, config);
|
|
98
147
|
return key;
|
|
99
148
|
}
|
|
100
|
-
async _load(filePath, cache) {
|
|
149
|
+
async _load(filePath, cache, referrer) {
|
|
101
150
|
const entity = __classPrivateFieldGet(this, _ConfigProvider_store, "f").get(filePath);
|
|
102
151
|
if (entity) {
|
|
103
152
|
return entity;
|
|
@@ -111,37 +160,37 @@ export class ConfigProvider {
|
|
|
111
160
|
}
|
|
112
161
|
if (isPlugin(filePath)) {
|
|
113
162
|
__classPrivateFieldGet(this, _ConfigProvider_held, "f").add(filePath);
|
|
114
|
-
return
|
|
163
|
+
return;
|
|
115
164
|
}
|
|
116
165
|
if (!(await moduleExists(filePath)) && !path.isAbsolute(filePath)) {
|
|
117
166
|
throw new TypeError(`${filePath} is not an absolute path`);
|
|
118
167
|
}
|
|
119
|
-
const config = await load(filePath, cache);
|
|
120
|
-
if (
|
|
121
|
-
return
|
|
168
|
+
const config = await load(filePath, cache, referrer);
|
|
169
|
+
if (config instanceof ConfigLoadError) {
|
|
170
|
+
return config;
|
|
122
171
|
}
|
|
123
172
|
const pathResolvedConfig = await this._pathResolve(config, filePath);
|
|
124
173
|
__classPrivateFieldGet(this, _ConfigProvider_store, "f").set(filePath, pathResolvedConfig);
|
|
125
174
|
return pathResolvedConfig;
|
|
126
175
|
}
|
|
127
|
-
async _mergeConfigs(keys, cache) {
|
|
176
|
+
async _mergeConfigs(keys, cache, referrer) {
|
|
128
177
|
const resolvedKeys = new Set();
|
|
129
178
|
const errs = [];
|
|
130
179
|
for (const key of keys) {
|
|
131
180
|
__classPrivateFieldGet(this, _ConfigProvider_recursiveLoadKeyAndDepth, "f").clear();
|
|
132
|
-
const keySet = await this.
|
|
181
|
+
const keySet = await this.recursiveLoad(key, cache, referrer);
|
|
133
182
|
for (const k of keySet.stack) {
|
|
134
183
|
resolvedKeys.add(k);
|
|
135
184
|
}
|
|
136
|
-
|
|
137
|
-
errs.push(...keySet.errs);
|
|
138
|
-
}
|
|
185
|
+
errs.push(...keySet.errs);
|
|
139
186
|
}
|
|
140
|
-
const configs =
|
|
141
|
-
.map(name => __classPrivateFieldGet(this, _ConfigProvider_store, "f").get(name))
|
|
142
|
-
.filter(nonNullableFilter);
|
|
187
|
+
const configs = [...resolvedKeys].map(name => __classPrivateFieldGet(this, _ConfigProvider_store, "f").get(name)).filter(nonNullableFilter);
|
|
143
188
|
let resultConfig = {};
|
|
144
189
|
for (const config of configs) {
|
|
190
|
+
if (config instanceof ConfigLoadError) {
|
|
191
|
+
errs.push(config);
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
145
194
|
resultConfig = mergeConfig(resultConfig, config);
|
|
146
195
|
}
|
|
147
196
|
return {
|
|
@@ -162,69 +211,42 @@ export class ConfigProvider {
|
|
|
162
211
|
overrides: await pathResolve(dir, config.overrides, undefined, true),
|
|
163
212
|
};
|
|
164
213
|
}
|
|
165
|
-
async _recursiveLoad(key, cache, depth = 1) {
|
|
166
|
-
const stack = new Set();
|
|
167
|
-
const errs = [];
|
|
168
|
-
const ancestorDepth = __classPrivateFieldGet(this, _ConfigProvider_recursiveLoadKeyAndDepth, "f").get(key);
|
|
169
|
-
if (ancestorDepth != null && ancestorDepth < depth) {
|
|
170
|
-
return {
|
|
171
|
-
stack,
|
|
172
|
-
errs: [new CircularReferenceError(`Circular reference detected: ${key}`)],
|
|
173
|
-
};
|
|
174
|
-
}
|
|
175
|
-
__classPrivateFieldGet(this, _ConfigProvider_recursiveLoadKeyAndDepth, "f").set(key, depth);
|
|
176
|
-
let config = __classPrivateFieldGet(this, _ConfigProvider_store, "f").get(key) ?? null;
|
|
177
|
-
if (!config) {
|
|
178
|
-
config = await this._load(key, cache);
|
|
179
|
-
}
|
|
180
|
-
if (!config) {
|
|
181
|
-
return { stack, errs: null };
|
|
182
|
-
}
|
|
183
|
-
const depKeys = config.extends !== null ? toNoEmptyStringArrayFromStringOrArray(config.extends) : null;
|
|
184
|
-
if (depKeys) {
|
|
185
|
-
for (const depKey of depKeys) {
|
|
186
|
-
const keys = await this._recursiveLoad(depKey, cache, depth + 1);
|
|
187
|
-
for (const key of keys.stack) {
|
|
188
|
-
stack.add(key);
|
|
189
|
-
}
|
|
190
|
-
if (keys.errs) {
|
|
191
|
-
errs.push(...keys.errs);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
stack.add(key);
|
|
196
|
-
return { stack, errs };
|
|
197
|
-
}
|
|
198
214
|
_validateConfig(config, filePath) {
|
|
199
215
|
const errors = [];
|
|
200
|
-
config.nodeRules
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
216
|
+
if (config.nodeRules)
|
|
217
|
+
for (const rule of config.nodeRules) {
|
|
218
|
+
if (rule.selector) {
|
|
219
|
+
try {
|
|
220
|
+
createSelector(rule.selector);
|
|
221
|
+
}
|
|
222
|
+
catch (error) {
|
|
223
|
+
if (error instanceof InvalidSelectorError) {
|
|
224
|
+
errors.push(new ConfigParserError(error.message, {
|
|
225
|
+
filePath,
|
|
226
|
+
raw: rule.selector,
|
|
227
|
+
}));
|
|
228
|
+
}
|
|
211
229
|
}
|
|
212
230
|
}
|
|
213
231
|
}
|
|
214
|
-
});
|
|
215
232
|
return errors;
|
|
216
233
|
}
|
|
217
234
|
}
|
|
218
235
|
_ConfigProvider_cache = new WeakMap(), _ConfigProvider_held = new WeakMap(), _ConfigProvider_recursiveLoadKeyAndDepth = new WeakMap(), _ConfigProvider_store = new WeakMap();
|
|
219
|
-
async function load(filePath, cache) {
|
|
236
|
+
async function load(filePath, cache, referrer) {
|
|
220
237
|
if (!fileExists(filePath) && (await moduleExists(filePath))) {
|
|
221
238
|
const mod = await import(filePath);
|
|
222
|
-
const config = mod?.default ??
|
|
239
|
+
const config = mod?.default ?? new ConfigLoadError('Module is not found', filePath, referrer);
|
|
223
240
|
return config;
|
|
224
241
|
}
|
|
225
|
-
const res = await loadConfig(filePath, !cache)
|
|
226
|
-
|
|
227
|
-
|
|
242
|
+
const res = await loadConfig(filePath, !cache, referrer).catch((error) => {
|
|
243
|
+
if (error instanceof ConfigLoadError) {
|
|
244
|
+
return error;
|
|
245
|
+
}
|
|
246
|
+
throw error;
|
|
247
|
+
});
|
|
248
|
+
if (res instanceof ConfigLoadError) {
|
|
249
|
+
return res;
|
|
228
250
|
}
|
|
229
251
|
return res.config;
|
|
230
252
|
}
|
|
@@ -279,33 +301,31 @@ async function moduleExists(name) {
|
|
|
279
301
|
try {
|
|
280
302
|
await import(name);
|
|
281
303
|
}
|
|
282
|
-
catch (
|
|
283
|
-
if (
|
|
284
|
-
|
|
285
|
-
return true;
|
|
286
|
-
}
|
|
304
|
+
catch (error) {
|
|
305
|
+
if (error instanceof Error && /^parse failure/i.test(error.message)) {
|
|
306
|
+
return true;
|
|
287
307
|
}
|
|
288
308
|
try {
|
|
289
309
|
require.resolve(name);
|
|
290
310
|
}
|
|
291
|
-
catch (
|
|
311
|
+
catch (error) {
|
|
292
312
|
if (
|
|
293
313
|
// @ts-ignore
|
|
294
|
-
'code' in
|
|
314
|
+
'code' in error &&
|
|
295
315
|
// @ts-ignore
|
|
296
|
-
|
|
316
|
+
error.code === 'ERR_PACKAGE_PATH_NOT_EXPORTED') {
|
|
297
317
|
// Even if there are issues with the fields,
|
|
298
318
|
// assume that the module exists and return true.
|
|
299
319
|
return true;
|
|
300
320
|
}
|
|
301
321
|
if (
|
|
302
322
|
// @ts-ignore
|
|
303
|
-
'code' in
|
|
323
|
+
'code' in error &&
|
|
304
324
|
// @ts-ignore
|
|
305
|
-
|
|
325
|
+
error.code === 'MODULE_NOT_FOUND') {
|
|
306
326
|
return false;
|
|
307
327
|
}
|
|
308
|
-
throw
|
|
328
|
+
throw error;
|
|
309
329
|
}
|
|
310
330
|
}
|
|
311
331
|
return true;
|
package/lib/cosmiconfig.d.ts
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
import type { LoaderSync } from 'cosmiconfig';
|
|
2
2
|
type CosmiConfig = ReturnType<LoaderSync>;
|
|
3
|
-
export declare function search<T = CosmiConfig>(
|
|
3
|
+
export declare function search<T = CosmiConfig>(filePath: string, cacheClear: boolean): Promise<{
|
|
4
4
|
filePath: string;
|
|
5
5
|
config: T;
|
|
6
6
|
} | null>;
|
|
7
|
-
export declare function load<T = CosmiConfig>(filePath: string, cacheClear: boolean): Promise<{
|
|
7
|
+
export declare function load<T = CosmiConfig>(filePath: string, cacheClear: boolean, referrer: string): Promise<ConfigLoadError | {
|
|
8
8
|
filePath: string;
|
|
9
9
|
config: T;
|
|
10
|
-
}
|
|
10
|
+
}>;
|
|
11
|
+
export declare class ConfigLoadError extends Error {
|
|
12
|
+
filePath: string;
|
|
13
|
+
name: string;
|
|
14
|
+
referrer: string;
|
|
15
|
+
constructor(message: string, filePath: string, referrer: string);
|
|
16
|
+
}
|
|
11
17
|
export {};
|
package/lib/cosmiconfig.js
CHANGED
|
@@ -2,6 +2,8 @@ import path from 'node:path';
|
|
|
2
2
|
import { ConfigParserError } from '@markuplint/parser-utils';
|
|
3
3
|
import { cosmiconfig, defaultLoaders } from 'cosmiconfig';
|
|
4
4
|
import { jsonc } from 'jsonc';
|
|
5
|
+
import { log } from './debug.js';
|
|
6
|
+
const searchLog = log.extend('search');
|
|
5
7
|
const explorer = cosmiconfig('markuplint', {
|
|
6
8
|
loaders: {
|
|
7
9
|
noExt: ((path, content) => {
|
|
@@ -17,12 +19,14 @@ const explorer = cosmiconfig('markuplint', {
|
|
|
17
19
|
}),
|
|
18
20
|
},
|
|
19
21
|
});
|
|
20
|
-
export async function search(
|
|
22
|
+
export async function search(filePath, cacheClear) {
|
|
21
23
|
if (cacheClear) {
|
|
22
24
|
explorer.clearCaches();
|
|
23
25
|
}
|
|
24
|
-
dir = path.dirname(
|
|
25
|
-
|
|
26
|
+
const dir = path.dirname(filePath);
|
|
27
|
+
searchLog('Search dir: %s', dir);
|
|
28
|
+
const result = await explorer.search(dir).catch(cacheConfigError(dir, filePath));
|
|
29
|
+
searchLog('Search result: %O', result);
|
|
26
30
|
if (!result || result.isEmpty) {
|
|
27
31
|
return null;
|
|
28
32
|
}
|
|
@@ -31,36 +35,39 @@ export async function search(dir, cacheClear) {
|
|
|
31
35
|
config: result.config,
|
|
32
36
|
};
|
|
33
37
|
}
|
|
34
|
-
export async function load(filePath, cacheClear) {
|
|
38
|
+
export async function load(filePath, cacheClear, referrer) {
|
|
35
39
|
if (cacheClear) {
|
|
36
40
|
explorer.clearCaches();
|
|
37
41
|
}
|
|
38
|
-
const result = await explorer.load(filePath).catch(cacheConfigError(filePath));
|
|
42
|
+
const result = await explorer.load(filePath).catch(cacheConfigError(filePath, referrer));
|
|
39
43
|
if (!result || result.isEmpty) {
|
|
40
|
-
return
|
|
44
|
+
return new ConfigLoadError('Config file is empty', filePath, referrer);
|
|
41
45
|
}
|
|
42
46
|
return {
|
|
43
47
|
filePath: result.filepath,
|
|
44
48
|
config: result.config,
|
|
45
49
|
};
|
|
46
50
|
}
|
|
47
|
-
class ConfigLoadError extends Error {
|
|
48
|
-
constructor(message, filePath) {
|
|
49
|
-
super(message + ` in ${
|
|
51
|
+
export class ConfigLoadError extends Error {
|
|
52
|
+
constructor(message, filePath, referrer) {
|
|
53
|
+
super(message + ` in ${referrer}`);
|
|
50
54
|
this.name = 'ConfigLoadError';
|
|
55
|
+
this.filePath = filePath;
|
|
56
|
+
this.referrer = referrer;
|
|
51
57
|
}
|
|
52
58
|
}
|
|
53
|
-
function cacheConfigError(fileOrDirPath) {
|
|
59
|
+
function cacheConfigError(fileOrDirPath, referrer) {
|
|
54
60
|
return (reason) => {
|
|
55
61
|
if (reason instanceof Error) {
|
|
56
62
|
switch (reason.name) {
|
|
57
|
-
case 'YAMLException':
|
|
63
|
+
case 'YAMLException': {
|
|
58
64
|
throw new ConfigParserError(reason.message, {
|
|
59
65
|
// @ts-ignore
|
|
60
66
|
filePath: reason.filepath ?? fileOrDirPath,
|
|
61
67
|
});
|
|
68
|
+
}
|
|
62
69
|
}
|
|
63
|
-
throw new ConfigLoadError(reason.message, fileOrDirPath);
|
|
70
|
+
throw new ConfigLoadError(reason.message, fileOrDirPath, referrer);
|
|
64
71
|
}
|
|
65
72
|
throw reason;
|
|
66
73
|
};
|
package/lib/debug.d.ts
ADDED
package/lib/debug.js
ADDED
package/lib/ml-file/ml-file.js
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
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
|
+
};
|
|
1
12
|
var _MLFile_basename, _MLFile_code, _MLFile_dirname, _MLFile_stat, _MLFile_type;
|
|
2
|
-
import { __classPrivateFieldGet, __classPrivateFieldSet } from "tslib";
|
|
3
13
|
import { promises as fs } from 'node:fs';
|
|
4
14
|
import path from 'node:path';
|
|
5
15
|
import ignore from 'ignore';
|
|
@@ -96,7 +106,7 @@ export class MLFile {
|
|
|
96
106
|
__classPrivateFieldSet(this, _MLFile_code, code, "f");
|
|
97
107
|
}
|
|
98
108
|
async _fetch() {
|
|
99
|
-
const code = await fs.readFile(this.path, { encoding: '
|
|
109
|
+
const code = await fs.readFile(this.path, { encoding: 'utf8' });
|
|
100
110
|
__classPrivateFieldSet(this, _MLFile_code, code, "f");
|
|
101
111
|
return code;
|
|
102
112
|
}
|
|
@@ -113,15 +123,15 @@ async function stat(filePath) {
|
|
|
113
123
|
try {
|
|
114
124
|
return await fs.stat(filePath);
|
|
115
125
|
}
|
|
116
|
-
catch (
|
|
126
|
+
catch (error) {
|
|
117
127
|
if (
|
|
118
128
|
// @ts-ignore
|
|
119
|
-
'code' in
|
|
129
|
+
'code' in error &&
|
|
120
130
|
// @ts-ignore
|
|
121
|
-
|
|
131
|
+
error.code === 'ENOENT') {
|
|
122
132
|
return null;
|
|
123
133
|
}
|
|
124
|
-
throw
|
|
134
|
+
throw error;
|
|
125
135
|
}
|
|
126
136
|
}
|
|
127
137
|
function pathNormalize(filePath, relative = false) {
|
package/lib/resolve-parser.js
CHANGED
|
@@ -10,6 +10,7 @@ export async function resolveParser(file, parserConfig, parserOptions) {
|
|
|
10
10
|
let parserModName = '@markuplint/html-parser';
|
|
11
11
|
let matched = false;
|
|
12
12
|
for (const pattern of Object.keys(parserConfig)) {
|
|
13
|
+
// eslint-disable-next-line unicorn/prefer-regexp-test
|
|
13
14
|
if (path.basename(file.path).match(toRegexp(pattern))) {
|
|
14
15
|
const modName = parserConfig[pattern];
|
|
15
16
|
if (!modName) {
|
package/lib/resolve-plugins.js
CHANGED
|
@@ -5,7 +5,7 @@ export async function resolvePlugins(pluginPaths) {
|
|
|
5
5
|
}
|
|
6
6
|
const plugins = await Promise.all(pluginPaths.map(p => importPlugin(p)));
|
|
7
7
|
// Clone
|
|
8
|
-
return plugins
|
|
8
|
+
return [...plugins];
|
|
9
9
|
}
|
|
10
10
|
export function cacheClear() {
|
|
11
11
|
cache.clear();
|
|
@@ -32,7 +32,7 @@ async function importPlugin(pluginPath) {
|
|
|
32
32
|
name = config.name
|
|
33
33
|
.toLowerCase()
|
|
34
34
|
.replace(/^(?:markuplint-rule-|@markuplint\/rule-)/i, '')
|
|
35
|
-
.
|
|
35
|
+
.replaceAll(/\s+|\/|\\|\./g, '-');
|
|
36
36
|
// eslint-disable-next-line no-console
|
|
37
37
|
console.info(`The plugin name became "${name}"`);
|
|
38
38
|
}
|
|
@@ -48,7 +48,7 @@ function getPluginConfig(pluginPath) {
|
|
|
48
48
|
return pluginPath;
|
|
49
49
|
}
|
|
50
50
|
async function failSafeImport(name) {
|
|
51
|
-
const res = await import(name).catch(
|
|
51
|
+
const res = await import(name).catch(error => error);
|
|
52
52
|
if ('code' in res && res === 'MODULE_NOT_FOUND') {
|
|
53
53
|
return null;
|
|
54
54
|
}
|
package/lib/resolve-rules.js
CHANGED
|
@@ -7,30 +7,30 @@ export async function resolveRules(plugins, ruleset, importPreset,
|
|
|
7
7
|
*/
|
|
8
8
|
autoLoad) {
|
|
9
9
|
const rules = importPreset ? await importPresetRules() : [];
|
|
10
|
-
|
|
10
|
+
for (const plugin of plugins) {
|
|
11
11
|
if (!plugin.rules) {
|
|
12
|
-
|
|
12
|
+
continue;
|
|
13
13
|
}
|
|
14
|
-
Object.entries(plugin.rules)
|
|
14
|
+
for (const [name, seed] of Object.entries(plugin.rules)) {
|
|
15
15
|
const rule = new MLRule({
|
|
16
16
|
name: `${plugin.name}/${name}`,
|
|
17
17
|
...seed,
|
|
18
18
|
});
|
|
19
19
|
rules.push(rule);
|
|
20
|
-
}
|
|
21
|
-
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
22
|
if (autoLoad) {
|
|
23
23
|
const { rules: additionalRules } = await autoLoadRules(ruleset);
|
|
24
|
-
|
|
24
|
+
for (const rule of additionalRules) {
|
|
25
25
|
rules.push(rule);
|
|
26
|
-
}
|
|
26
|
+
}
|
|
27
27
|
}
|
|
28
28
|
// Clone
|
|
29
|
-
return rules
|
|
29
|
+
return [...rules];
|
|
30
30
|
}
|
|
31
31
|
async function importPresetRules() {
|
|
32
32
|
if (cachedPresetRules) {
|
|
33
|
-
return cachedPresetRules
|
|
33
|
+
return [...cachedPresetRules];
|
|
34
34
|
}
|
|
35
35
|
const modName = '@markuplint/rules';
|
|
36
36
|
const mod = await import(modName);
|
|
@@ -44,5 +44,5 @@ async function importPresetRules() {
|
|
|
44
44
|
});
|
|
45
45
|
cachedPresetRules = ruleList;
|
|
46
46
|
// Clone
|
|
47
|
-
return ruleList
|
|
47
|
+
return [...ruleList];
|
|
48
48
|
}
|
package/lib/resolve-specs.js
CHANGED
|
@@ -41,6 +41,7 @@ export async function resolveSpecs(filePath, specConfig) {
|
|
|
41
41
|
}
|
|
42
42
|
else {
|
|
43
43
|
for (const pattern of Object.keys(specConfig)) {
|
|
44
|
+
// eslint-disable-next-line unicorn/prefer-regexp-test
|
|
44
45
|
if (path.basename(filePath).match(toRegexp(pattern))) {
|
|
45
46
|
const specModName = specConfig[pattern];
|
|
46
47
|
if (!specModName) {
|
|
@@ -65,7 +66,8 @@ async function importSpecs(specModName) {
|
|
|
65
66
|
return spec;
|
|
66
67
|
}
|
|
67
68
|
}
|
|
68
|
-
const
|
|
69
|
+
const mod = await import(specModName);
|
|
70
|
+
const spec = mod.default;
|
|
69
71
|
// @ts-ignore
|
|
70
72
|
caches.set(specModName, spec);
|
|
71
73
|
return spec;
|
package/lib/utils.js
CHANGED
|
@@ -9,7 +9,7 @@ export function fileExists(filePath) {
|
|
|
9
9
|
return fs.existsSync(filePath);
|
|
10
10
|
}
|
|
11
11
|
export function toRegexp(pattern) {
|
|
12
|
-
const matched = pattern.match(/^\/(.+)\/([
|
|
12
|
+
const matched = pattern.match(/^\/(.+)\/([gi]*)$/i);
|
|
13
13
|
if (matched && matched[1]) {
|
|
14
14
|
return new RegExp(matched[1], matched[2]);
|
|
15
15
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@markuplint/file-resolver",
|
|
3
|
-
"version": "4.0.0-
|
|
3
|
+
"version": "4.0.0-dev.28+0131de5e",
|
|
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>",
|
|
@@ -24,23 +24,23 @@
|
|
|
24
24
|
"clean": "tsc --build --clean"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
|
-
"@types/node": "20.8.
|
|
27
|
+
"@types/node": "20.8.7"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@markuplint/html-parser": "4.0.0-
|
|
31
|
-
"@markuplint/ml-ast": "4.0.0-
|
|
32
|
-
"@markuplint/ml-config": "4.0.0-
|
|
33
|
-
"@markuplint/ml-core": "4.0.0-
|
|
34
|
-
"@markuplint/ml-spec": "4.0.0-
|
|
35
|
-
"@markuplint/parser-utils": "4.0.0-
|
|
36
|
-
"@markuplint/selector": "4.0.0-
|
|
37
|
-
"@markuplint/shared": "4.0.0-
|
|
30
|
+
"@markuplint/html-parser": "4.0.0-dev.28+0131de5e",
|
|
31
|
+
"@markuplint/ml-ast": "4.0.0-dev.28+0131de5e",
|
|
32
|
+
"@markuplint/ml-config": "4.0.0-dev.28+0131de5e",
|
|
33
|
+
"@markuplint/ml-core": "4.0.0-dev.28+0131de5e",
|
|
34
|
+
"@markuplint/ml-spec": "4.0.0-dev.28+0131de5e",
|
|
35
|
+
"@markuplint/parser-utils": "4.0.0-dev.28+0131de5e",
|
|
36
|
+
"@markuplint/selector": "4.0.0-dev.28+0131de5e",
|
|
37
|
+
"@markuplint/shared": "4.0.0-dev.28+0131de5e",
|
|
38
38
|
"cosmiconfig": "^8.3.6",
|
|
39
|
+
"debug": "^4.3.4",
|
|
39
40
|
"glob": "^10.3.6",
|
|
40
41
|
"ignore": "^5.2.4",
|
|
41
42
|
"jsonc": "^2.0.0",
|
|
42
|
-
"minimatch": "^9.0.3"
|
|
43
|
-
"tslib": "^2.6.2"
|
|
43
|
+
"minimatch": "^9.0.3"
|
|
44
44
|
},
|
|
45
|
-
"gitHead": "
|
|
45
|
+
"gitHead": "0131de5ea9dd6d3fd5472d7b414b66644c758881"
|
|
46
46
|
}
|