@markuplint/file-resolver 5.0.0-rc.0 → 5.0.0-rc.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 +7 -0
- package/lib/config-provider.js +1 -2
- package/lib/cosmiconfig.d.ts +1 -1
- package/lib/cosmiconfig.js +1 -1
- package/lib/general-import.js +113 -5
- package/package.json +11 -11
- package/lib/config-load-error.d.ts +0 -6
- package/lib/config-load-error.js +0 -10
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,13 @@
|
|
|
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-rc.1](https://github.com/markuplint/markuplint/compare/v5.0.0-rc.0...v5.0.0-rc.1) (2026-03-27)
|
|
7
|
+
|
|
8
|
+
### Bug Fixes
|
|
9
|
+
|
|
10
|
+
- **file-resolver:** handle ERR_PACKAGE_PATH_NOT_EXPORTED in generalImport ([d563d60](https://github.com/markuplint/markuplint/commit/d563d6000c4c38be54c17ea015b0f26c80c5229c)), closes [#3516](https://github.com/markuplint/markuplint/issues/3516)
|
|
11
|
+
- **file-resolver:** resolve package subpaths before import to avoid runtime-specific errors ([cb2b641](https://github.com/markuplint/markuplint/commit/cb2b641e25cd9de487d395b6f219dd8b60ecd306))
|
|
12
|
+
|
|
6
13
|
# [5.0.0-rc.0](https://github.com/markuplint/markuplint/compare/v5.0.0-alpha.3...v5.0.0-rc.0) (2026-03-12)
|
|
7
14
|
|
|
8
15
|
### Bug Fixes
|
package/lib/config-provider.js
CHANGED
|
@@ -2,8 +2,7 @@ import path from 'node:path';
|
|
|
2
2
|
import { mergeConfig } from '@markuplint/ml-config';
|
|
3
3
|
import { ConfigParserError } from '@markuplint/parser-utils';
|
|
4
4
|
import { InvalidSelectorError, createSelector } from '@markuplint/selector';
|
|
5
|
-
import { nonNullableFilter, toNoEmptyStringArrayFromStringOrArray } from '@markuplint/shared';
|
|
6
|
-
import { ConfigLoadError } from './config-load-error.js';
|
|
5
|
+
import { nonNullableFilter, toNoEmptyStringArrayFromStringOrArray, ConfigLoadError } from '@markuplint/shared';
|
|
7
6
|
import { load as loadConfig, search } from './cosmiconfig.js';
|
|
8
7
|
import { log } from './debug.js';
|
|
9
8
|
import { generalImport } from './general-import.js';
|
package/lib/cosmiconfig.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { LoaderSync } from 'cosmiconfig';
|
|
2
|
-
import { ConfigLoadError } from '
|
|
2
|
+
import { ConfigLoadError } from '@markuplint/shared';
|
|
3
3
|
type CosmiConfig = ReturnType<LoaderSync>;
|
|
4
4
|
export declare function search<T = CosmiConfig>(filePath: string, cacheClear: boolean): Promise<{
|
|
5
5
|
filePath: string;
|
package/lib/cosmiconfig.js
CHANGED
|
@@ -2,7 +2,7 @@ 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 { ConfigLoadError } from '
|
|
5
|
+
import { ConfigLoadError } from '@markuplint/shared';
|
|
6
6
|
import { log } from './debug.js';
|
|
7
7
|
const searchLog = log.extend('search');
|
|
8
8
|
const jsoncLoader = (path, content) => {
|
package/lib/general-import.js
CHANGED
|
@@ -2,6 +2,7 @@ import fs from 'node:fs/promises';
|
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import { pathToFileURL } from 'node:url';
|
|
5
|
+
import { isFatalError } from '@markuplint/shared';
|
|
5
6
|
import { resolve } from 'import-meta-resolve';
|
|
6
7
|
import { log } from './debug.js';
|
|
7
8
|
import { fromFileURL } from './path-utils.js';
|
|
@@ -14,6 +15,17 @@ export async function generalImport(name) {
|
|
|
14
15
|
if (cache.has(name)) {
|
|
15
16
|
return cache.get(name);
|
|
16
17
|
}
|
|
18
|
+
// When the specifier is a package subpath (e.g., "@scope/pkg/file.json"),
|
|
19
|
+
// the package's exports map may not include it. Instead of relying on
|
|
20
|
+
// error codes (which differ between Node.js and vitest/vite), resolve
|
|
21
|
+
// the package directory upfront and convert to an absolute path.
|
|
22
|
+
const resolved = resolvePackageSubpath(name);
|
|
23
|
+
if (resolved) {
|
|
24
|
+
gLog('Resolved package subpath "%s" to "%s"', name, resolved);
|
|
25
|
+
const result = await generalImport(resolved);
|
|
26
|
+
cache.set(name, result);
|
|
27
|
+
return result;
|
|
28
|
+
}
|
|
17
29
|
try {
|
|
18
30
|
// Convert absolute paths to file:// URL format
|
|
19
31
|
let importPath = name;
|
|
@@ -64,14 +76,110 @@ export async function generalImport(name) {
|
|
|
64
76
|
}
|
|
65
77
|
}
|
|
66
78
|
if (path.isAbsolute(name) && name.endsWith('.json')) {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
79
|
+
try {
|
|
80
|
+
const file = await fs.readFile(name, 'utf8');
|
|
81
|
+
const json = JSON.parse(file);
|
|
82
|
+
gLogSuccess('Success by readFile("%s") and JSON.parse: %O', name, json);
|
|
83
|
+
cache.set(name, json);
|
|
84
|
+
return json;
|
|
85
|
+
}
|
|
86
|
+
catch (readError) {
|
|
87
|
+
gLogError('Error in readFile("%s"): %O', name, readError);
|
|
88
|
+
}
|
|
72
89
|
}
|
|
73
90
|
gLogError('Error in `import()`: %O', error);
|
|
74
91
|
cache.set(name, null);
|
|
75
92
|
return null;
|
|
76
93
|
}
|
|
77
94
|
}
|
|
95
|
+
/**
|
|
96
|
+
* Detects a bare package subpath specifier (e.g., `@scope/pkg/file.json`)
|
|
97
|
+
* and resolves it to an absolute file path when the package's exports map
|
|
98
|
+
* does not include the subpath.
|
|
99
|
+
*
|
|
100
|
+
* Returns the absolute path if resolution succeeds, or `null` if:
|
|
101
|
+
* - The specifier is not a package subpath (absolute path, relative path, no subpath)
|
|
102
|
+
* - The package's exports map already includes the subpath (no bypass needed)
|
|
103
|
+
* - The package cannot be found at all
|
|
104
|
+
*/
|
|
105
|
+
function resolvePackageSubpath(name) {
|
|
106
|
+
if (path.isAbsolute(name) || name.startsWith('.')) {
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
const packageName = name.startsWith('@') ? name.split('/').slice(0, 2).join('/') : name.split('/')[0];
|
|
110
|
+
if (!packageName) {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
const subpath = name.slice(packageName.length + 1);
|
|
114
|
+
if (!subpath) {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
const pkgDir = resolvePackageDir(packageName);
|
|
118
|
+
if (!pkgDir) {
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
try {
|
|
122
|
+
const pkgJson = require(path.join(pkgDir, 'package.json'));
|
|
123
|
+
if (!pkgJson.exports) {
|
|
124
|
+
return null; // No exports map — import() should work fine
|
|
125
|
+
}
|
|
126
|
+
// If the exports map explicitly includes this subpath, let import() handle it
|
|
127
|
+
const exportKey = `./${subpath}`;
|
|
128
|
+
if (typeof pkgJson.exports === 'object' && !Array.isArray(pkgJson.exports) && exportKey in pkgJson.exports) {
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
// Subpath not in exports — resolve to absolute path to bypass the restriction
|
|
132
|
+
return path.join(pkgDir, subpath);
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
if (isFatalError(error)) {
|
|
136
|
+
throw error;
|
|
137
|
+
}
|
|
138
|
+
return null;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Resolves the root directory of a package by name.
|
|
143
|
+
*/
|
|
144
|
+
function resolvePackageDir(packageName) {
|
|
145
|
+
// Try CJS require.resolve first — it can often resolve package.json
|
|
146
|
+
// even when the ESM exports map doesn't include it
|
|
147
|
+
try {
|
|
148
|
+
return path.dirname(require.resolve(`${packageName}/package.json`));
|
|
149
|
+
}
|
|
150
|
+
catch (error) {
|
|
151
|
+
if (isFatalError(error)) {
|
|
152
|
+
throw error;
|
|
153
|
+
}
|
|
154
|
+
// Fall through
|
|
155
|
+
}
|
|
156
|
+
// Fall back to import-meta-resolve to find the main entry, then walk up
|
|
157
|
+
try {
|
|
158
|
+
const mainUrl = resolve(packageName, import.meta.url);
|
|
159
|
+
const mainPath = mainUrl.startsWith('file:') ? fromFileURL(mainUrl) : mainUrl;
|
|
160
|
+
let dir = path.dirname(mainPath);
|
|
161
|
+
for (let i = 0; i < 10; i++) {
|
|
162
|
+
try {
|
|
163
|
+
require(path.join(dir, 'package.json'));
|
|
164
|
+
return dir;
|
|
165
|
+
}
|
|
166
|
+
catch (innerError) {
|
|
167
|
+
if (isFatalError(innerError)) {
|
|
168
|
+
throw innerError;
|
|
169
|
+
}
|
|
170
|
+
const parent = path.dirname(dir);
|
|
171
|
+
if (parent === dir) {
|
|
172
|
+
break; // Reached filesystem root
|
|
173
|
+
}
|
|
174
|
+
dir = parent;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
catch (error) {
|
|
179
|
+
if (isFatalError(error)) {
|
|
180
|
+
throw error;
|
|
181
|
+
}
|
|
182
|
+
// Package not found at all
|
|
183
|
+
}
|
|
184
|
+
return null;
|
|
185
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@markuplint/file-resolver",
|
|
3
|
-
"version": "5.0.0-rc.
|
|
3
|
+
"version": "5.0.0-rc.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,15 +30,15 @@
|
|
|
30
30
|
"@types/node": "24.5.1"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@markuplint/html-parser": "5.0.0-rc.
|
|
34
|
-
"@markuplint/ml-ast": "5.0.0-rc.
|
|
35
|
-
"@markuplint/ml-config": "5.0.0-rc.
|
|
36
|
-
"@markuplint/ml-core": "5.0.0-rc.
|
|
37
|
-
"@markuplint/ml-spec": "5.0.0-rc.
|
|
38
|
-
"@markuplint/parser-utils": "5.0.0-rc.
|
|
39
|
-
"@markuplint/pretenders": "5.0.0-rc.
|
|
40
|
-
"@markuplint/selector": "5.0.0-rc.
|
|
41
|
-
"@markuplint/shared": "5.0.0-rc.
|
|
33
|
+
"@markuplint/html-parser": "5.0.0-rc.1",
|
|
34
|
+
"@markuplint/ml-ast": "5.0.0-rc.1",
|
|
35
|
+
"@markuplint/ml-config": "5.0.0-rc.1",
|
|
36
|
+
"@markuplint/ml-core": "5.0.0-rc.1",
|
|
37
|
+
"@markuplint/ml-spec": "5.0.0-rc.1",
|
|
38
|
+
"@markuplint/parser-utils": "5.0.0-rc.1",
|
|
39
|
+
"@markuplint/pretenders": "5.0.0-rc.1",
|
|
40
|
+
"@markuplint/selector": "5.0.0-rc.1",
|
|
41
|
+
"@markuplint/shared": "5.0.0-rc.1",
|
|
42
42
|
"cosmiconfig": "9.0.1",
|
|
43
43
|
"debug": "4.4.3",
|
|
44
44
|
"glob": "13.0.6",
|
|
@@ -47,5 +47,5 @@
|
|
|
47
47
|
"jsonc": "2.0.0",
|
|
48
48
|
"minimatch": "10.2.4"
|
|
49
49
|
},
|
|
50
|
-
"gitHead": "
|
|
50
|
+
"gitHead": "0d6b4324d9a7d6b9e1ba57d4a57e45d36975cba9"
|
|
51
51
|
}
|
package/lib/config-load-error.js
DELETED