@markuplint/file-resolver 5.0.0-alpha.0 → 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,14 @@
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
+
6
14
  # [5.0.0-alpha.0](https://github.com/markuplint/markuplint/compare/v4.14.1...v5.0.0-alpha.0) (2026-02-20)
7
15
 
8
16
  ### Bug Fixes
@@ -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
  }
@@ -2,6 +2,7 @@ import { promises as fs } from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import ignore from 'ignore';
4
4
  import { minimatch } from 'minimatch';
5
+ import { normalizeForIgnore } from '../path-utils.js';
5
6
  export class MLFile {
6
7
  #basename;
7
8
  #code;
@@ -39,13 +40,13 @@ export class MLFile {
39
40
  * Normalized `MLFile.dirname`
40
41
  */
41
42
  get nDirname() {
42
- return pathNormalize(this.dirname);
43
+ return normalizeForIgnore(this.dirname);
43
44
  }
44
45
  /**
45
46
  * Normalized `MLFile.path`
46
47
  */
47
48
  get nPath() {
48
- return pathNormalize(this.path);
49
+ return normalizeForIgnore(this.path);
49
50
  }
50
51
  get path() {
51
52
  return path.resolve(this.#dirname, this.#basename);
@@ -64,10 +65,10 @@ export class MLFile {
64
65
  }
65
66
  ignored(globPath) {
66
67
  globPath = typeof globPath === 'string' ? [globPath] : globPath;
67
- const normalizedPaths = globPath.map(p => pathNormalize(p, true));
68
+ const normalizedPaths = globPath.map(p => normalizeForIgnore(p, true));
68
69
  // @ts-ignore
69
70
  const ig = ignore().add(normalizedPaths);
70
- const ignored = ig.ignores(pathNormalize(this.nPath, true));
71
+ const ignored = ig.ignores(normalizeForIgnore(this.path, true));
71
72
  return ignored;
72
73
  }
73
74
  async isExist() {
@@ -85,7 +86,7 @@ export class MLFile {
85
86
  return !!stat && stat.isFile();
86
87
  }
87
88
  matches(globPath) {
88
- return minimatch(this.nPath, pathNormalize(globPath));
89
+ return minimatch(normalizeForIgnore(this.path), normalizeForIgnore(globPath));
89
90
  }
90
91
  setCode(code) {
91
92
  if (this.#type === 'file-base') {
@@ -121,22 +122,3 @@ async function stat(filePath) {
121
122
  throw error;
122
123
  }
123
124
  }
124
- function pathNormalize(filePath, relative = false) {
125
- const hasBang = filePath.startsWith('!');
126
- if (hasBang) {
127
- filePath = filePath.slice(1);
128
- }
129
- // Remove the local disk scheme of Windows OS
130
- if (path.isAbsolute(filePath)) {
131
- filePath = filePath.replace(/^[a-z]+:/i, '');
132
- if (relative) {
133
- filePath = path.relative(path.sep, filePath);
134
- }
135
- }
136
- // Replace the separator of Windows OS
137
- filePath = filePath.split(path.sep).join('/');
138
- if (hasBang) {
139
- filePath = `!${filePath}`;
140
- }
141
- return filePath;
142
- }
@@ -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
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@markuplint/file-resolver",
3
- "version": "5.0.0-alpha.0",
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>",
@@ -30,14 +30,14 @@
30
30
  "@types/node": "24.5.1"
31
31
  },
32
32
  "dependencies": {
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",
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",
41
41
  "cosmiconfig": "9.0.0",
42
42
  "debug": "4.4.3",
43
43
  "glob": "13.0.6",
@@ -46,5 +46,5 @@
46
46
  "jsonc": "2.0.0",
47
47
  "minimatch": "10.2.2"
48
48
  },
49
- "gitHead": "13dcfc84ec83d87360c720e253383b60767e1b56"
49
+ "gitHead": "78a295e73a097a1ce09c777c06fa21ab68136387"
50
50
  }