@markuplint/file-resolver 4.9.18 → 5.0.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,38 @@
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
+ # [5.0.0-alpha.1](https://github.com/markuplint/markuplint/compare/v5.0.0-alpha.0...v5.0.0-alpha.1) (2026-02-22)
7
+
8
+ ### Bug Fixes
9
+
10
+ - **file-resolver:** fix matches() normalization asymmetry and harden tests ([4651df9](https://github.com/markuplint/markuplint/commit/4651df9679427755a3848937b3b8f5546cb04371))
11
+ - **file-resolver:** fix Windows path normalization for non-C: drives ([f31a942](https://github.com/markuplint/markuplint/commit/f31a9427fc417f444df26ad1a931d4df5957f29f)), closes [#1806](https://github.com/markuplint/markuplint/issues/1806)
12
+ - **file-resolver:** skip platform-specific fromFileURL tests per OS ([4c05356](https://github.com/markuplint/markuplint/commit/4c0535657a748d63f7832efeb3d33b43e5b8e081))
13
+
14
+ # [5.0.0-alpha.0](https://github.com/markuplint/markuplint/compare/v4.14.1...v5.0.0-alpha.0) (2026-02-20)
15
+
16
+ ### Bug Fixes
17
+
18
+ - **file-resolver:** use "options" instead of deprecated "option" in test fixtures ([98a53f2](https://github.com/markuplint/markuplint/commit/98a53f27c4a6e640f20e2c74421c1cdeba3e7db5))
19
+
20
+ - refactor(file-resolver)!: drop MLMarkupLanguageParser support ([3272ee7](https://github.com/markuplint/markuplint/commit/3272ee72a7c4fb3105fbecd41ec4ba5eff030092))
21
+
22
+ ### Features
23
+
24
+ - **file-resolver:** add .jsonc config file support via cosmiconfig ([755848d](https://github.com/markuplint/markuplint/commit/755848dbec75105e3cdf9de6becb84546b66deec))
25
+
26
+ ### BREAKING CHANGES
27
+
28
+ - Remove MLMarkupLanguageParser compatibility from
29
+ resolve-parser. Parser modules must now export MLParserModule
30
+ with a parser property.
31
+
32
+ * Remove MLMarkupLanguageParser import and union types
33
+ * Remove deprecated 'parser' in parserMod check
34
+ * Update test mock to use Parser class instance
35
+
36
+ Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
37
+
6
38
  ## [4.9.18](https://github.com/markuplint/markuplint/compare/@markuplint/file-resolver@4.9.17...@markuplint/file-resolver@4.9.18) (2026-02-10)
7
39
 
8
40
  **Note:** Version bump only for package @markuplint/file-resolver
@@ -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
  }
@@ -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';
@@ -29,12 +23,10 @@ const KEY_SEPARATOR = '__ML_CONFIG_MERGE__';
29
23
  * Configuration files are searched via cosmiconfig and cached by file path.
30
24
  */
31
25
  export class ConfigProvider {
32
- constructor() {
33
- _ConfigProvider_cache.set(this, new Map());
34
- _ConfigProvider_held.set(this, new Set());
35
- _ConfigProvider_recursiveLoadKeyAndDepth.set(this, new Map());
36
- _ConfigProvider_store.set(this, new Map());
37
- }
26
+ #cache = new Map();
27
+ #held = new Set();
28
+ #recursiveLoadKeyAndDepth = new Map();
29
+ #store = new Map();
38
30
  /**
39
31
  * Recursively loads a configuration and all its `extends` dependencies.
40
32
  *
@@ -47,15 +39,15 @@ export class ConfigProvider {
47
39
  async recursiveLoad(key, cache, referrer, depth = 1) {
48
40
  const stack = new Set();
49
41
  const errs = [];
50
- const ancestorDepth = __classPrivateFieldGet(this, _ConfigProvider_recursiveLoadKeyAndDepth, "f").get(key);
42
+ const ancestorDepth = this.#recursiveLoadKeyAndDepth.get(key);
51
43
  if (ancestorDepth != null && ancestorDepth < depth) {
52
44
  return {
53
45
  stack,
54
46
  errs: [new CircularReferenceError(`Circular reference detected: ${key}`)],
55
47
  };
56
48
  }
57
- __classPrivateFieldGet(this, _ConfigProvider_recursiveLoadKeyAndDepth, "f").set(key, depth);
58
- let config = __classPrivateFieldGet(this, _ConfigProvider_store, "f").get(key);
49
+ this.#recursiveLoadKeyAndDepth.set(key, depth);
50
+ let config = this.#store.get(key);
59
51
  if (!config) {
60
52
  config = await this._load(key, cache, referrer);
61
53
  }
@@ -93,18 +85,18 @@ export class ConfigProvider {
93
85
  */
94
86
  async resolve(targetFile, names, cache = true) {
95
87
  if (!cache) {
96
- __classPrivateFieldGet(this, _ConfigProvider_store, "f").clear();
97
- __classPrivateFieldGet(this, _ConfigProvider_cache, "f").clear();
88
+ this.#store.clear();
89
+ this.#cache.clear();
98
90
  cacheClear();
99
91
  }
100
92
  const keys = names.filter(nonNullableFilter);
101
93
  const key = keys.join(KEY_SEPARATOR);
102
- const currentConfig = __classPrivateFieldGet(this, _ConfigProvider_cache, "f").get(key);
94
+ const currentConfig = this.#cache.get(key);
103
95
  if (currentConfig) {
104
96
  return currentConfig;
105
97
  }
106
98
  let configSet = await this._mergeConfigs(keys, cache, targetFile.path);
107
- const filePath = [...configSet.files].reverse()[0];
99
+ const filePath = [...configSet.files].toReversed()[0];
108
100
  if (!filePath) {
109
101
  throw new ConfigParserError('Config file not found', {
110
102
  filePath: targetFile.path,
@@ -114,8 +106,8 @@ export class ConfigProvider {
114
106
  configSet.errs.push(...errors);
115
107
  const { plugins, errors: pluginErrors } = await resolvePlugins(configSet.config.plugins);
116
108
  configSet.errs.push(...pluginErrors);
117
- if (__classPrivateFieldGet(this, _ConfigProvider_held, "f").size > 0) {
118
- const extendHelds = [...__classPrivateFieldGet(this, _ConfigProvider_held, "f").values()];
109
+ if (this.#held.size > 0) {
110
+ const extendHelds = [...this.#held.values()];
119
111
  for (const held of extendHelds) {
120
112
  const [, prefix, namespace, name] = held.match(/^([a-z]+:)([^/]+)(?:\/(.+))?$/) ?? [];
121
113
  switch (prefix) {
@@ -130,7 +122,7 @@ export class ConfigProvider {
130
122
  }
131
123
  }
132
124
  configSet = await this._mergeConfigs([...keys, ...extendHelds], cache, targetFile.path);
133
- __classPrivateFieldGet(this, _ConfigProvider_held, "f").clear();
125
+ this.#held.clear();
134
126
  }
135
127
  // Resolves `overrides`
136
128
  if (configSet.config.overrides) {
@@ -157,7 +149,7 @@ export class ConfigProvider {
157
149
  ...configSet,
158
150
  plugins,
159
151
  };
160
- __classPrivateFieldGet(this, _ConfigProvider_cache, "f").set(key, result);
152
+ this.#cache.set(key, result);
161
153
  return result;
162
154
  }
163
155
  /**
@@ -180,7 +172,7 @@ export class ConfigProvider {
180
172
  }
181
173
  const { filePath, config } = res;
182
174
  const pathResolvedConfig = await this._pathResolve(config, filePath);
183
- __classPrivateFieldGet(this, _ConfigProvider_store, "f").set(filePath, pathResolvedConfig);
175
+ this.#store.set(filePath, pathResolvedConfig);
184
176
  cpLog('Store key: %s', filePath);
185
177
  return filePath;
186
178
  }
@@ -193,11 +185,11 @@ export class ConfigProvider {
193
185
  */
194
186
  set(config, key) {
195
187
  key = key ?? uuid();
196
- __classPrivateFieldGet(this, _ConfigProvider_store, "f").set(key, config);
188
+ this.#store.set(key, config);
197
189
  return key;
198
190
  }
199
191
  async _load(filePath, cache, referrer) {
200
- const entity = __classPrivateFieldGet(this, _ConfigProvider_store, "f").get(filePath);
192
+ const entity = this.#store.get(filePath);
201
193
  if (entity) {
202
194
  return entity;
203
195
  }
@@ -205,11 +197,11 @@ export class ConfigProvider {
205
197
  const [, name] = filePath.match(/^markuplint:(.+)$/i) ?? [];
206
198
  const config = await getPreset(name ?? filePath);
207
199
  const pathResolvedConfig = await this._pathResolve(config, filePath);
208
- __classPrivateFieldGet(this, _ConfigProvider_store, "f").set(filePath, pathResolvedConfig);
200
+ this.#store.set(filePath, pathResolvedConfig);
209
201
  return pathResolvedConfig;
210
202
  }
211
203
  if (isPluginModuleName(filePath)) {
212
- __classPrivateFieldGet(this, _ConfigProvider_held, "f").add(filePath);
204
+ this.#held.add(filePath);
213
205
  return;
214
206
  }
215
207
  if (!(await moduleExists(filePath)) && !path.isAbsolute(filePath)) {
@@ -220,21 +212,21 @@ export class ConfigProvider {
220
212
  return config;
221
213
  }
222
214
  const pathResolvedConfig = await this._pathResolve(config, filePath);
223
- __classPrivateFieldGet(this, _ConfigProvider_store, "f").set(filePath, pathResolvedConfig);
215
+ this.#store.set(filePath, pathResolvedConfig);
224
216
  return pathResolvedConfig;
225
217
  }
226
218
  async _mergeConfigs(keys, cache, referrer) {
227
219
  const resolvedKeys = new Set();
228
220
  const errs = [];
229
221
  for (const key of keys) {
230
- __classPrivateFieldGet(this, _ConfigProvider_recursiveLoadKeyAndDepth, "f").clear();
222
+ this.#recursiveLoadKeyAndDepth.clear();
231
223
  const keySet = await this.recursiveLoad(key, cache, referrer);
232
224
  for (const k of keySet.stack) {
233
225
  resolvedKeys.add(k);
234
226
  }
235
227
  errs.push(...keySet.errs);
236
228
  }
237
- const configs = [...resolvedKeys].map(name => __classPrivateFieldGet(this, _ConfigProvider_store, "f").get(name)).filter(nonNullableFilter);
229
+ const configs = [...resolvedKeys].map(name => this.#store.get(name)).filter(nonNullableFilter);
238
230
  let resultConfig = {};
239
231
  for (const config of configs) {
240
232
  if (config instanceof ConfigLoadError) {
@@ -289,7 +281,6 @@ export class ConfigProvider {
289
281
  return errors;
290
282
  }
291
283
  }
292
- _ConfigProvider_cache = new WeakMap(), _ConfigProvider_held = new WeakMap(), _ConfigProvider_recursiveLoadKeyAndDepth = new WeakMap(), _ConfigProvider_store = new WeakMap();
293
284
  async function load(filePath, cache, referrer) {
294
285
  if (!fileExists(filePath) && (await moduleExists(filePath))) {
295
286
  const config = (await generalImport(filePath)) ?? new ConfigLoadError('Module is not found', filePath, referrer);
@@ -307,8 +298,5 @@ async function load(filePath, cache, referrer) {
307
298
  return res.config;
308
299
  }
309
300
  class CircularReferenceError extends ReferenceError {
310
- constructor() {
311
- super(...arguments);
312
- this.name = 'CircularReferenceError';
313
- }
301
+ name = 'CircularReferenceError';
314
302
  }
@@ -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: ((path, content) => {
11
- try {
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
  });
@@ -1,6 +1,6 @@
1
1
  import { readFile } from 'node:fs/promises';
2
- import path from 'node:path';
3
2
  import { log } from './debug.js';
3
+ import { fromFileURL } from './path-utils.js';
4
4
  const fLog = log.extend('force-import-json-in-module');
5
5
  export async function forceImportJsonInModule(modPath) {
6
6
  const error = await import(modPath).catch(error => error);
@@ -18,11 +18,7 @@ export async function forceImportJsonInModule(modPath) {
18
18
  if (!absPath) {
19
19
  return error;
20
20
  }
21
- const normalizePath = absPath
22
- .replace(/^file:\/\//, '')
23
- .replaceAll('/', path.sep)
24
- // Windows
25
- .replace(/^[/\\](?=[a-z]:)/i, ''); // only remove a leading slash
21
+ const normalizePath = absPath.startsWith('file:') ? fromFileURL(absPath) : absPath;
26
22
  fLog('Find JSON file path: %s', normalizePath);
27
23
  const fileContent = await readFile(normalizePath, { encoding: 'utf8' });
28
24
  return JSON.parse(fileContent);
@@ -4,6 +4,7 @@ import path from 'node:path';
4
4
  import { pathToFileURL } from 'node:url';
5
5
  import { resolve } from 'import-meta-resolve';
6
6
  import { log } from './debug.js';
7
+ import { fromFileURL } from './path-utils.js';
7
8
  const gLog = log.extend('general-import');
8
9
  const gLogSuccess = gLog.extend('success');
9
10
  const gLogError = gLog.extend('error');
@@ -53,7 +54,8 @@ export async function generalImport(name) {
53
54
  ?.groups ?? {};
54
55
  if (filePath && packageName) {
55
56
  const modFile = resolve(packageName, import.meta.url);
56
- const modPath = path.dirname(modFile);
57
+ const modNativePath = modFile.startsWith('file:') ? fromFileURL(modFile) : modFile;
58
+ const modPath = path.dirname(modNativePath);
57
59
  const candidate = path.join(modPath, filePath);
58
60
  gLog('Try import absolute path: "%s"', candidate);
59
61
  const result = await generalImport(candidate);
@@ -1,5 +1,6 @@
1
1
  import { glob } from 'glob';
2
2
  import { minimatch } from 'minimatch';
3
+ import { normalizeForGlob } from '../path-utils.js';
3
4
  import { getFile } from './get-file.js';
4
5
  /**
5
6
  * Get files
@@ -9,7 +10,7 @@ import { getFile } from './get-file.js';
9
10
  * @param filePathOrGlob
10
11
  */
11
12
  export async function getFiles(filePathOrGlob, ignoreGlob) {
12
- const fileList = await glob(filePathOrGlob, {}).catch(() => []);
13
- const filtered = fileList.filter(fileName => !minimatch(fileName, ignoreGlob ?? ''));
13
+ const fileList = await glob(normalizeForGlob(filePathOrGlob), {}).catch(() => []);
14
+ const filtered = fileList.filter(fileName => !minimatch(fileName, normalizeForGlob(ignoreGlob ?? '')));
14
15
  return filtered.map(fileName => getFile(fileName));
15
16
  }
@@ -1,124 +1,112 @@
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';
5
+ import { normalizeForIgnore } from '../path-utils.js';
17
6
  export class MLFile {
7
+ #basename;
8
+ #code;
9
+ #dirname;
10
+ /**
11
+ * - `Stats`: Exists
12
+ * - `null`: Not exists
13
+ * - `undefined`: Doesn't read yet
14
+ */
15
+ #stat = undefined;
16
+ #type;
18
17
  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
18
  if (typeof target === 'string') {
30
- __classPrivateFieldSet(this, _MLFile_basename, path.basename(target), "f");
31
- __classPrivateFieldSet(this, _MLFile_dirname, path.dirname(target), "f");
32
- __classPrivateFieldSet(this, _MLFile_code, null, "f");
33
- __classPrivateFieldSet(this, _MLFile_type, 'file-base', "f");
19
+ this.#basename = path.basename(target);
20
+ this.#dirname = path.dirname(target);
21
+ this.#code = null;
22
+ this.#type = 'file-base';
34
23
  return;
35
24
  }
36
25
  if (!target.workspace && target.name && path.isAbsolute(target.name)) {
37
- __classPrivateFieldSet(this, _MLFile_basename, path.basename(target.name), "f");
38
- __classPrivateFieldSet(this, _MLFile_dirname, path.dirname(target.name), "f");
26
+ this.#basename = path.basename(target.name);
27
+ this.#dirname = path.dirname(target.name);
39
28
  }
40
29
  else {
41
- __classPrivateFieldSet(this, _MLFile_basename, target.name ?? '<AnonymousFile>', "f");
42
- __classPrivateFieldSet(this, _MLFile_dirname, target.workspace ?? process.cwd(), "f");
30
+ this.#basename = target.name ?? '<AnonymousFile>';
31
+ this.#dirname = target.workspace ?? process.cwd();
43
32
  }
44
- __classPrivateFieldSet(this, _MLFile_code, target.sourceCode, "f");
45
- __classPrivateFieldSet(this, _MLFile_type, 'code-base', "f");
33
+ this.#code = target.sourceCode;
34
+ this.#type = 'code-base';
46
35
  }
47
36
  get dirname() {
48
- return __classPrivateFieldGet(this, _MLFile_dirname, "f");
37
+ return this.#dirname;
49
38
  }
50
39
  /**
51
40
  * Normalized `MLFile.dirname`
52
41
  */
53
42
  get nDirname() {
54
- return pathNormalize(this.dirname);
43
+ return normalizeForIgnore(this.dirname);
55
44
  }
56
45
  /**
57
46
  * Normalized `MLFile.path`
58
47
  */
59
48
  get nPath() {
60
- return pathNormalize(this.path);
49
+ return normalizeForIgnore(this.path);
61
50
  }
62
51
  get path() {
63
- return path.resolve(__classPrivateFieldGet(this, _MLFile_dirname, "f"), __classPrivateFieldGet(this, _MLFile_basename, "f"));
52
+ return path.resolve(this.#dirname, this.#basename);
64
53
  }
65
54
  async dirExists() {
66
- return !!(await stat(__classPrivateFieldGet(this, _MLFile_dirname, "f")));
55
+ return !!(await stat(this.#dirname));
67
56
  }
68
57
  async getCode() {
69
- if (__classPrivateFieldGet(this, _MLFile_code, "f") != null) {
70
- return __classPrivateFieldGet(this, _MLFile_code, "f");
58
+ if (this.#code != null) {
59
+ return this.#code;
71
60
  }
72
- if (__classPrivateFieldGet(this, _MLFile_type, "f") === 'file-base' && (await this.isExist())) {
61
+ if (this.#type === 'file-base' && (await this.isExist())) {
73
62
  return await this._fetch();
74
63
  }
75
64
  return '';
76
65
  }
77
66
  ignored(globPath) {
78
67
  globPath = typeof globPath === 'string' ? [globPath] : globPath;
79
- const normalizedPaths = globPath.map(p => pathNormalize(p, true));
68
+ const normalizedPaths = globPath.map(p => normalizeForIgnore(p, true));
80
69
  // @ts-ignore
81
70
  const ig = ignore().add(normalizedPaths);
82
- const ignored = ig.ignores(pathNormalize(this.nPath, true));
71
+ const ignored = ig.ignores(normalizeForIgnore(this.path, true));
83
72
  return ignored;
84
73
  }
85
74
  async isExist() {
86
- if (__classPrivateFieldGet(this, _MLFile_type, "f") === 'code-base') {
75
+ if (this.#type === 'code-base') {
87
76
  return true;
88
77
  }
89
78
  const stat = await this._stat();
90
79
  return !!stat;
91
80
  }
92
81
  async isFile() {
93
- if (__classPrivateFieldGet(this, _MLFile_type, "f") === 'code-base') {
82
+ if (this.#type === 'code-base') {
94
83
  return true;
95
84
  }
96
85
  const stat = await this._stat();
97
86
  return !!stat && stat.isFile();
98
87
  }
99
88
  matches(globPath) {
100
- return minimatch(this.nPath, pathNormalize(globPath));
89
+ return minimatch(normalizeForIgnore(this.path), normalizeForIgnore(globPath));
101
90
  }
102
91
  setCode(code) {
103
- if (__classPrivateFieldGet(this, _MLFile_type, "f") === 'file-base') {
92
+ if (this.#type === 'file-base') {
104
93
  throw new Error(`This file object is readonly (File-base: ${this.path})`);
105
94
  }
106
- __classPrivateFieldSet(this, _MLFile_code, code, "f");
95
+ this.#code = code;
107
96
  }
108
97
  async _fetch() {
109
98
  const code = await fs.readFile(this.path, { encoding: 'utf8' });
110
- __classPrivateFieldSet(this, _MLFile_code, code, "f");
99
+ this.#code = code;
111
100
  return code;
112
101
  }
113
102
  async _stat() {
114
- if (__classPrivateFieldGet(this, _MLFile_stat, "f")) {
115
- return __classPrivateFieldGet(this, _MLFile_stat, "f");
103
+ if (this.#stat) {
104
+ return this.#stat;
116
105
  }
117
- __classPrivateFieldSet(this, _MLFile_stat, await stat(this.path), "f");
118
- return __classPrivateFieldGet(this, _MLFile_stat, "f");
106
+ this.#stat = await stat(this.path);
107
+ return this.#stat;
119
108
  }
120
109
  }
121
- _MLFile_basename = new WeakMap(), _MLFile_code = new WeakMap(), _MLFile_dirname = new WeakMap(), _MLFile_stat = new WeakMap(), _MLFile_type = new WeakMap();
122
110
  async function stat(filePath) {
123
111
  try {
124
112
  return await fs.stat(filePath);
@@ -134,22 +122,3 @@ async function stat(filePath) {
134
122
  throw error;
135
123
  }
136
124
  }
137
- function pathNormalize(filePath, relative = false) {
138
- const hasBang = filePath.startsWith('!');
139
- if (hasBang) {
140
- filePath = filePath.slice(1);
141
- }
142
- // Remove the local disk scheme of Windows OS
143
- if (path.isAbsolute(filePath)) {
144
- filePath = filePath.replace(/^[a-z]+:/i, '');
145
- if (relative) {
146
- filePath = path.relative(path.sep, filePath);
147
- }
148
- }
149
- // Replace the separator of Windows OS
150
- filePath = filePath.split(path.sep).join('/');
151
- if (hasBang) {
152
- filePath = `!${filePath}`;
153
- }
154
- return filePath;
155
- }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Convert OS-native separators to forward slashes.
3
+ * Identity function on POSIX.
4
+ */
5
+ export declare function toSlash(filePath: string): string;
6
+ /**
7
+ * Convert a `file://` URL to a native file path using Node.js built-in.
8
+ * Correctly handles URL encoding, UNC paths, and all drive letters.
9
+ */
10
+ export declare function fromFileURL(fileUrl: string): string;
11
+ /**
12
+ * Normalize a path for the `ignore` library (gitignore-style matching).
13
+ * Removes drive letters, converts to forward slashes, and optionally makes relative.
14
+ */
15
+ export declare function normalizeForIgnore(filePath: string, relative?: boolean): string;
16
+ /**
17
+ * Normalize a path for glob libraries (forward slashes required).
18
+ */
19
+ export declare function normalizeForGlob(filePath: string): string;
@@ -0,0 +1,46 @@
1
+ import path from 'node:path';
2
+ import { fileURLToPath } from 'node:url';
3
+ /**
4
+ * Convert OS-native separators to forward slashes.
5
+ * Identity function on POSIX.
6
+ */
7
+ export function toSlash(filePath) {
8
+ return filePath.replaceAll('\\', '/');
9
+ }
10
+ /**
11
+ * Convert a `file://` URL to a native file path using Node.js built-in.
12
+ * Correctly handles URL encoding, UNC paths, and all drive letters.
13
+ */
14
+ export function fromFileURL(fileUrl) {
15
+ return fileURLToPath(fileUrl);
16
+ }
17
+ /**
18
+ * Normalize a path for the `ignore` library (gitignore-style matching).
19
+ * Removes drive letters, converts to forward slashes, and optionally makes relative.
20
+ */
21
+ export function normalizeForIgnore(filePath, relative = false) {
22
+ const hasBang = filePath.startsWith('!');
23
+ if (hasBang) {
24
+ filePath = filePath.slice(1);
25
+ }
26
+ // Remove the local disk scheme of Windows OS (e.g. "C:", "P:")
27
+ if (path.isAbsolute(filePath) || /^[a-z]:/i.test(filePath)) {
28
+ filePath = filePath.replace(/^[a-z]:/i, '');
29
+ }
30
+ // Convert backslashes to forward slashes
31
+ filePath = toSlash(filePath);
32
+ if (relative) {
33
+ // Strip leading slashes to make the path relative
34
+ filePath = filePath.replace(/^\/+/, '');
35
+ }
36
+ if (hasBang) {
37
+ filePath = `!${filePath}`;
38
+ }
39
+ return filePath;
40
+ }
41
+ /**
42
+ * Normalize a path for glob libraries (forward slashes required).
43
+ */
44
+ export function normalizeForGlob(filePath) {
45
+ return toSlash(filePath);
46
+ }
@@ -1,5 +1,5 @@
1
1
  import type { MLFile } from './ml-file/index.js';
2
- import type { MLMarkupLanguageParser, MLParser, ParserOptions } from '@markuplint/ml-ast';
2
+ import type { MLParser, ParserOptions } from '@markuplint/ml-ast';
3
3
  import type { ParserConfig } from '@markuplint/ml-config';
4
4
  /**
5
5
  * Resolves the appropriate parser for a given file based on the parser configuration.
@@ -14,7 +14,7 @@ import type { ParserConfig } from '@markuplint/ml-config';
14
14
  */
15
15
  export declare function resolveParser(file: Readonly<MLFile>, parserConfig?: ParserConfig, parserOptions?: ParserOptions): Promise<{
16
16
  parserModName: string;
17
- parser: MLParser | MLMarkupLanguageParser;
17
+ parser: MLParser;
18
18
  parserOptions: ParserOptions;
19
19
  matched: boolean;
20
20
  }>;
@@ -50,9 +50,5 @@ async function importParser(parserModName) {
50
50
  if (!parserMod) {
51
51
  throw new Error(`Parser module "${parserModName}" is not found.`);
52
52
  }
53
- // TODO: To be dropped in v5
54
- if (!('parser' in parserMod)) {
55
- return parserMod;
56
- }
57
53
  return parserMod.parser;
58
54
  }
package/package.json CHANGED
@@ -1,10 +1,13 @@
1
1
  {
2
2
  "name": "@markuplint/file-resolver",
3
- "version": "4.9.18",
3
+ "version": "5.0.0-alpha.1",
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": "4.6.23",
31
- "@markuplint/ml-ast": "4.4.11",
32
- "@markuplint/ml-config": "4.8.15",
33
- "@markuplint/ml-core": "4.13.3",
34
- "@markuplint/ml-spec": "4.10.2",
35
- "@markuplint/parser-utils": "4.8.11",
36
- "@markuplint/selector": "4.7.8",
37
- "@markuplint/shared": "4.4.13",
33
+ "@markuplint/html-parser": "5.0.0-alpha.1",
34
+ "@markuplint/ml-ast": "5.0.0-alpha.1",
35
+ "@markuplint/ml-config": "5.0.0-alpha.1",
36
+ "@markuplint/ml-core": "5.0.0-alpha.1",
37
+ "@markuplint/ml-spec": "5.0.0-alpha.1",
38
+ "@markuplint/parser-utils": "5.0.0-alpha.1",
39
+ "@markuplint/selector": "5.0.0-alpha.1",
40
+ "@markuplint/shared": "5.0.0-alpha.1",
38
41
  "cosmiconfig": "9.0.0",
39
42
  "debug": "4.4.3",
40
- "glob": "13.0.1",
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.1.2"
47
+ "minimatch": "10.2.2"
45
48
  },
46
- "gitHead": "193ee7c1262bbed95424e38efdf1a8e56ff049f4"
49
+ "gitHead": "78a295e73a097a1ce09c777c06fa21ab68136387"
47
50
  }