@rsdoctor/core 1.5.11 → 1.5.12

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.
@@ -52,28 +52,103 @@ function normalizePathForCompare(filePath) {
52
52
  const cleanPath = splitIndex === 1 / 0 ? filePath : filePath.slice(0, splitIndex);
53
53
  return external_path_default().normalize(cleanPath);
54
54
  }
55
- function extractEsmEntry(pkgJson, pkgRoot) {
56
- const { exports: exportsField, module: moduleField } = pkgJson;
57
- if (null !== exportsField && 'object' == typeof exportsField) {
58
- const exports1 = exportsField;
59
- const dotEntry = exports1['.'];
60
- if (null !== dotEntry && 'object' == typeof dotEntry) {
61
- const dot = dotEntry;
62
- const importEntry = dot['import'];
63
- if ('string' == typeof importEntry) return external_path_default().resolve(pkgRoot, importEntry);
64
- if (null !== importEntry && 'object' == typeof importEntry) {
65
- const nested = importEntry['default'];
66
- if ('string' == typeof nested) return external_path_default().resolve(pkgRoot, nested);
67
- }
68
- }
69
- const topImport = exports1['import'];
70
- if ('string' == typeof topImport) return external_path_default().resolve(pkgRoot, topImport);
71
- if (null !== topImport && 'object' == typeof topImport) {
72
- const nested = topImport['default'];
73
- if ('string' == typeof nested) return external_path_default().resolve(pkgRoot, nested);
74
- }
55
+ function isDefinitelyEsmFile(filePath) {
56
+ return '.mjs' === external_path_default().extname(normalizePathForCompare(filePath));
57
+ }
58
+ function isDeclaredEsmEntry(filePath, esmEntries, toRealPath) {
59
+ const realPath = toRealPath(filePath);
60
+ return esmEntries.some((esmEntry)=>realPath === toRealPath(esmEntry));
61
+ }
62
+ function isRecord(value) {
63
+ return null !== value && 'object' == typeof value && !Array.isArray(value);
64
+ }
65
+ function substituteExportPattern(target, wildcard) {
66
+ return target.replace(/\*/g, wildcard);
67
+ }
68
+ function toPackagePath(pkgRoot, target, wildcard) {
69
+ return external_path_default().resolve(pkgRoot, substituteExportPattern(target, wildcard));
70
+ }
71
+ function collectStringTargets(value, wildcard) {
72
+ if ('string' == typeof value) return [
73
+ substituteExportPattern(value, wildcard)
74
+ ];
75
+ if (Array.isArray(value)) return value.flatMap((item)=>collectStringTargets(item, wildcard));
76
+ if (!isRecord(value)) return [];
77
+ return Object.values(value).flatMap((child)=>collectStringTargets(child, wildcard));
78
+ }
79
+ function collectEsmTargets(exportEntry, pkgRoot, wildcard) {
80
+ const entries = new Set();
81
+ const addTargets = (value)=>{
82
+ for (const target of collectStringTargets(value, wildcard))entries.add(toPackagePath(pkgRoot, target, ''));
83
+ };
84
+ const visit = (value)=>{
85
+ if ('string' == typeof value || Array.isArray(value)) return void addTargets(value);
86
+ if (!isRecord(value)) return;
87
+ addTargets(value['import']);
88
+ addTargets(value['module']);
89
+ for (const child of Object.values(value))if (isRecord(child) || Array.isArray(child)) visit(child);
90
+ };
91
+ visit(exportEntry);
92
+ return entries;
93
+ }
94
+ function hasSubpathExportKeys(exports1) {
95
+ return Object.keys(exports1).some((key)=>'.' === key || key.startsWith('./'));
96
+ }
97
+ function matchPatternExportKey(patternKey, exportKey) {
98
+ const wildcardIndex = patternKey.indexOf('*');
99
+ if (wildcardIndex < 0) return null;
100
+ const prefix = patternKey.slice(0, wildcardIndex);
101
+ const suffix = patternKey.slice(wildcardIndex + 1);
102
+ if (!exportKey.startsWith(prefix) || !exportKey.endsWith(suffix)) return null;
103
+ return exportKey.slice(prefix.length, exportKey.length - suffix.length);
104
+ }
105
+ function isPatternExportKey(key) {
106
+ return key.indexOf('*') === key.lastIndexOf('*') && key.includes('*');
107
+ }
108
+ function patternKeyCompare(keyA, keyB) {
109
+ const baseLengthA = keyA.includes('*') ? keyA.indexOf('*') + 1 : keyA.length;
110
+ const baseLengthB = keyB.includes('*') ? keyB.indexOf('*') + 1 : keyB.length;
111
+ if (baseLengthA > baseLengthB) return -1;
112
+ if (baseLengthB > baseLengthA) return 1;
113
+ if (!keyA.includes('*')) return 1;
114
+ if (!keyB.includes('*')) return -1;
115
+ if (keyA.length > keyB.length) return -1;
116
+ if (keyB.length > keyA.length) return 1;
117
+ return 0;
118
+ }
119
+ function resolveExportEntry(exports1, exportKey) {
120
+ if (Object.prototype.hasOwnProperty.call(exports1, exportKey)) return {
121
+ value: exports1[exportKey],
122
+ wildcard: ''
123
+ };
124
+ const patternEntries = Object.entries(exports1).filter(([key])=>isPatternExportKey(key)).sort(([keyA], [keyB])=>patternKeyCompare(keyA, keyB));
125
+ for (const [key, value] of patternEntries){
126
+ const wildcard = matchPatternExportKey(key, exportKey);
127
+ if (null !== wildcard) return {
128
+ value,
129
+ wildcard
130
+ };
75
131
  }
76
- if ('string' == typeof moduleField) return external_path_default().resolve(pkgRoot, moduleField);
132
+ return null;
133
+ }
134
+ function resolveExportEntryForKey(exportsField, exportKey) {
135
+ if (!isRecord(exportsField)) return null;
136
+ if (!hasSubpathExportKeys(exportsField)) return '.' === exportKey ? {
137
+ value: exportsField,
138
+ wildcard: ''
139
+ } : null;
140
+ return resolveExportEntry(exportsField, exportKey);
141
+ }
142
+ function extractEsmEntries(pkgJson, pkgRoot, exportKey) {
143
+ const { exports: exportsField, module: moduleField } = pkgJson;
144
+ const resolvedExport = resolveExportEntryForKey(exportsField, exportKey);
145
+ const entries = resolvedExport ? collectEsmTargets(resolvedExport.value, pkgRoot, resolvedExport.wildcard) : new Set();
146
+ if ('.' === exportKey && 'string' == typeof moduleField) entries.add(external_path_default().resolve(pkgRoot, moduleField));
147
+ return entries;
148
+ }
149
+ function getExportKeyFromRequest(packageName, request) {
150
+ if (request === packageName) return '.';
151
+ if (request.startsWith(`${packageName}/`)) return `.${request.slice(packageName.length)}`;
77
152
  return null;
78
153
  }
79
154
  const rule = (0, external_rule_cjs_namespaceObject.defineRule)(()=>({
@@ -121,10 +196,15 @@ const rule = (0, external_rule_cjs_namespaceObject.defineRule)(()=>({
121
196
  const pkg = packageGraph.getPackageByModule(dep.dependency);
122
197
  if (!pkg?.root) continue;
123
198
  const pkgJson = readPkgJson(pkg.root);
124
- const esmEntry = !ruleConfig.ignore.some((p)=>pkg.name.includes(p)) && pkgJson && extractEsmEntry(pkgJson, pkg.root);
125
- if (!esmEntry) continue;
199
+ if (ruleConfig.ignore.some((p)=>pkg.name.includes(p)) || !pkgJson) continue;
200
+ const exportKey = getExportKeyFromRequest(pkg.name, dep.request);
201
+ if (!exportKey) continue;
202
+ const esmEntries = extractEsmEntries(pkgJson, pkg.root, exportKey);
203
+ if (0 === esmEntries.size) continue;
204
+ const esmEntryList = Array.from(esmEntries);
126
205
  const resolvedModuleRealPath = toRealPath(dep.dependency.path);
127
- if (resolvedModuleRealPath === toRealPath(esmEntry)) continue;
206
+ if (isDefinitelyEsmFile(resolvedModuleRealPath)) continue;
207
+ if (isDeclaredEsmEntry(resolvedModuleRealPath, esmEntryList, toRealPath)) continue;
128
208
  const groupKey = `${pkg.name}::${resolvedModuleRealPath}`;
129
209
  const issuer = {
130
210
  id: dep.module.id,
@@ -135,7 +215,7 @@ const rule = (0, external_rule_cjs_namespaceObject.defineRule)(()=>({
135
215
  else groups.set(groupKey, {
136
216
  packageName: pkg.name,
137
217
  packageVersion: pkg.version,
138
- esmEntry,
218
+ esmEntry: esmEntryList[0],
139
219
  resolvedModule: {
140
220
  id: dep.dependency.id,
141
221
  path: dep.dependency.path,
@@ -14,28 +14,103 @@ function normalizePathForCompare(filePath) {
14
14
  const cleanPath = splitIndex === 1 / 0 ? filePath : filePath.slice(0, splitIndex);
15
15
  return path.normalize(cleanPath);
16
16
  }
17
- function extractEsmEntry(pkgJson, pkgRoot) {
18
- const { exports: exportsField, module: moduleField } = pkgJson;
19
- if (null !== exportsField && 'object' == typeof exportsField) {
20
- const exports = exportsField;
21
- const dotEntry = exports['.'];
22
- if (null !== dotEntry && 'object' == typeof dotEntry) {
23
- const dot = dotEntry;
24
- const importEntry = dot['import'];
25
- if ('string' == typeof importEntry) return path.resolve(pkgRoot, importEntry);
26
- if (null !== importEntry && 'object' == typeof importEntry) {
27
- const nested = importEntry['default'];
28
- if ('string' == typeof nested) return path.resolve(pkgRoot, nested);
29
- }
30
- }
31
- const topImport = exports['import'];
32
- if ('string' == typeof topImport) return path.resolve(pkgRoot, topImport);
33
- if (null !== topImport && 'object' == typeof topImport) {
34
- const nested = topImport['default'];
35
- if ('string' == typeof nested) return path.resolve(pkgRoot, nested);
36
- }
17
+ function isDefinitelyEsmFile(filePath) {
18
+ return '.mjs' === path.extname(normalizePathForCompare(filePath));
19
+ }
20
+ function isDeclaredEsmEntry(filePath, esmEntries, toRealPath) {
21
+ const realPath = toRealPath(filePath);
22
+ return esmEntries.some((esmEntry)=>realPath === toRealPath(esmEntry));
23
+ }
24
+ function isRecord(value) {
25
+ return null !== value && 'object' == typeof value && !Array.isArray(value);
26
+ }
27
+ function substituteExportPattern(target, wildcard) {
28
+ return target.replace(/\*/g, wildcard);
29
+ }
30
+ function toPackagePath(pkgRoot, target, wildcard) {
31
+ return path.resolve(pkgRoot, substituteExportPattern(target, wildcard));
32
+ }
33
+ function collectStringTargets(value, wildcard) {
34
+ if ('string' == typeof value) return [
35
+ substituteExportPattern(value, wildcard)
36
+ ];
37
+ if (Array.isArray(value)) return value.flatMap((item)=>collectStringTargets(item, wildcard));
38
+ if (!isRecord(value)) return [];
39
+ return Object.values(value).flatMap((child)=>collectStringTargets(child, wildcard));
40
+ }
41
+ function collectEsmTargets(exportEntry, pkgRoot, wildcard) {
42
+ const entries = new Set();
43
+ const addTargets = (value)=>{
44
+ for (const target of collectStringTargets(value, wildcard))entries.add(toPackagePath(pkgRoot, target, ''));
45
+ };
46
+ const visit = (value)=>{
47
+ if ('string' == typeof value || Array.isArray(value)) return void addTargets(value);
48
+ if (!isRecord(value)) return;
49
+ addTargets(value['import']);
50
+ addTargets(value['module']);
51
+ for (const child of Object.values(value))if (isRecord(child) || Array.isArray(child)) visit(child);
52
+ };
53
+ visit(exportEntry);
54
+ return entries;
55
+ }
56
+ function hasSubpathExportKeys(exports) {
57
+ return Object.keys(exports).some((key)=>'.' === key || key.startsWith('./'));
58
+ }
59
+ function matchPatternExportKey(patternKey, exportKey) {
60
+ const wildcardIndex = patternKey.indexOf('*');
61
+ if (wildcardIndex < 0) return null;
62
+ const prefix = patternKey.slice(0, wildcardIndex);
63
+ const suffix = patternKey.slice(wildcardIndex + 1);
64
+ if (!exportKey.startsWith(prefix) || !exportKey.endsWith(suffix)) return null;
65
+ return exportKey.slice(prefix.length, exportKey.length - suffix.length);
66
+ }
67
+ function isPatternExportKey(key) {
68
+ return key.indexOf('*') === key.lastIndexOf('*') && key.includes('*');
69
+ }
70
+ function patternKeyCompare(keyA, keyB) {
71
+ const baseLengthA = keyA.includes('*') ? keyA.indexOf('*') + 1 : keyA.length;
72
+ const baseLengthB = keyB.includes('*') ? keyB.indexOf('*') + 1 : keyB.length;
73
+ if (baseLengthA > baseLengthB) return -1;
74
+ if (baseLengthB > baseLengthA) return 1;
75
+ if (!keyA.includes('*')) return 1;
76
+ if (!keyB.includes('*')) return -1;
77
+ if (keyA.length > keyB.length) return -1;
78
+ if (keyB.length > keyA.length) return 1;
79
+ return 0;
80
+ }
81
+ function resolveExportEntry(exports, exportKey) {
82
+ if (Object.prototype.hasOwnProperty.call(exports, exportKey)) return {
83
+ value: exports[exportKey],
84
+ wildcard: ''
85
+ };
86
+ const patternEntries = Object.entries(exports).filter(([key])=>isPatternExportKey(key)).sort(([keyA], [keyB])=>patternKeyCompare(keyA, keyB));
87
+ for (const [key, value] of patternEntries){
88
+ const wildcard = matchPatternExportKey(key, exportKey);
89
+ if (null !== wildcard) return {
90
+ value,
91
+ wildcard
92
+ };
37
93
  }
38
- if ('string' == typeof moduleField) return path.resolve(pkgRoot, moduleField);
94
+ return null;
95
+ }
96
+ function resolveExportEntryForKey(exportsField, exportKey) {
97
+ if (!isRecord(exportsField)) return null;
98
+ if (!hasSubpathExportKeys(exportsField)) return '.' === exportKey ? {
99
+ value: exportsField,
100
+ wildcard: ''
101
+ } : null;
102
+ return resolveExportEntry(exportsField, exportKey);
103
+ }
104
+ function extractEsmEntries(pkgJson, pkgRoot, exportKey) {
105
+ const { exports: exportsField, module: moduleField } = pkgJson;
106
+ const resolvedExport = resolveExportEntryForKey(exportsField, exportKey);
107
+ const entries = resolvedExport ? collectEsmTargets(resolvedExport.value, pkgRoot, resolvedExport.wildcard) : new Set();
108
+ if ('.' === exportKey && 'string' == typeof moduleField) entries.add(path.resolve(pkgRoot, moduleField));
109
+ return entries;
110
+ }
111
+ function getExportKeyFromRequest(packageName, request) {
112
+ if (request === packageName) return '.';
113
+ if (request.startsWith(`${packageName}/`)) return `.${request.slice(packageName.length)}`;
39
114
  return null;
40
115
  }
41
116
  const rule = defineRule(()=>({
@@ -83,10 +158,15 @@ const rule = defineRule(()=>({
83
158
  const pkg = packageGraph.getPackageByModule(dep.dependency);
84
159
  if (!pkg?.root) continue;
85
160
  const pkgJson = readPkgJson(pkg.root);
86
- const esmEntry = !ruleConfig.ignore.some((p)=>pkg.name.includes(p)) && pkgJson && extractEsmEntry(pkgJson, pkg.root);
87
- if (!esmEntry) continue;
161
+ if (ruleConfig.ignore.some((p)=>pkg.name.includes(p)) || !pkgJson) continue;
162
+ const exportKey = getExportKeyFromRequest(pkg.name, dep.request);
163
+ if (!exportKey) continue;
164
+ const esmEntries = extractEsmEntries(pkgJson, pkg.root, exportKey);
165
+ if (0 === esmEntries.size) continue;
166
+ const esmEntryList = Array.from(esmEntries);
88
167
  const resolvedModuleRealPath = toRealPath(dep.dependency.path);
89
- if (resolvedModuleRealPath === toRealPath(esmEntry)) continue;
168
+ if (isDefinitelyEsmFile(resolvedModuleRealPath)) continue;
169
+ if (isDeclaredEsmEntry(resolvedModuleRealPath, esmEntryList, toRealPath)) continue;
90
170
  const groupKey = `${pkg.name}::${resolvedModuleRealPath}`;
91
171
  const issuer = {
92
172
  id: dep.module.id,
@@ -97,7 +177,7 @@ const rule = defineRule(()=>({
97
177
  else groups.set(groupKey, {
98
178
  packageName: pkg.name,
99
179
  packageVersion: pkg.version,
100
- esmEntry,
180
+ esmEntry: esmEntryList[0],
101
181
  resolvedModule: {
102
182
  id: dep.dependency.id,
103
183
  path: dep.dependency.path,
@@ -1 +1,2 @@
1
- export declare const rules: (import("node_modules/@rsdoctor/types/dist/linter").RuleData<import("./duplicate-package").Config, "duplicate-package"> | import("node_modules/@rsdoctor/types/dist/linter").RuleData<import("./default-import-check").Config, "default-import-check"> | import("node_modules/@rsdoctor/types/dist/linter").RuleData<import("./loader-performance-optimization").Config, "loader-performance-optimization"> | import("node_modules/@rsdoctor/types/dist/linter").RuleData<import("./ecma-version-check").Config, "ecma-version-check"> | import("node_modules/@rsdoctor/types/dist/linter").RuleData<import("./cross-chunks-package").Config, "cross-chunks-package"> | import("node_modules/@rsdoctor/types/dist/linter").RuleData<import("./module-mixed-chunks").Config, "module-mixed-chunks"> | import("node_modules/@rsdoctor/types/dist/linter").RuleData<import("./side-effects-only-imports").Config, "tree-shaking-side-effects-only"> | import("node_modules/@rsdoctor/types/dist/linter").RuleData<import("./cjs-require").Config, "cjs-require"> | import("node_modules/@rsdoctor/types/dist/linter").RuleData<import("./esm-resolved-to-cjs").Config, "esm-resolved-to-cjs">)[];
1
+ import type { Linter } from '@rsdoctor/types';
2
+ export declare const rules: Linter.RuleData[];
@@ -1 +1,2 @@
1
- export declare const rules: (import("node_modules/@rsdoctor/types/dist/linter").RuleData<import("./duplicate-package/index.js").Config, "duplicate-package"> | import("node_modules/@rsdoctor/types/dist/linter").RuleData<import("./default-import-check/index.js").Config, "default-import-check"> | import("node_modules/@rsdoctor/types/dist/linter").RuleData<import("./loader-performance-optimization/index.js").Config, "loader-performance-optimization"> | import("node_modules/@rsdoctor/types/dist/linter").RuleData<import("./ecma-version-check/index.js").Config, "ecma-version-check"> | import("node_modules/@rsdoctor/types/dist/linter").RuleData<import("./cross-chunks-package/index.js").Config, "cross-chunks-package"> | import("node_modules/@rsdoctor/types/dist/linter").RuleData<import("./module-mixed-chunks/index.js").Config, "module-mixed-chunks"> | import("node_modules/@rsdoctor/types/dist/linter").RuleData<import("./side-effects-only-imports/index.js").Config, "tree-shaking-side-effects-only"> | import("node_modules/@rsdoctor/types/dist/linter").RuleData<import("./cjs-require/index.js").Config, "cjs-require"> | import("node_modules/@rsdoctor/types/dist/linter").RuleData<import("./esm-resolved-to-cjs/index.js").Config, "esm-resolved-to-cjs">)[];
1
+ import type { Linter } from '@rsdoctor/types';
2
+ export declare const rules: Linter.RuleData[];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rsdoctor/core",
3
- "version": "1.5.11",
3
+ "version": "1.5.12",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/web-infra-dev/rsdoctor",
@@ -90,19 +90,19 @@
90
90
  "@rsbuild/plugin-check-syntax": "1.6.1",
91
91
  "@rspack/resolver": "0.2.8",
92
92
  "es-toolkit": "^1.45.1",
93
- "filesize": "^10.1.6",
93
+ "filesize": "^11.0.17",
94
94
  "fs-extra": "^11.1.1",
95
95
  "semver": "^7.7.4",
96
96
  "source-map": "^0.7.6",
97
- "@rsdoctor/sdk": "1.5.11",
98
- "@rsdoctor/utils": "1.5.11",
99
- "@rsdoctor/graph": "1.5.11",
100
- "@rsdoctor/types": "1.5.11"
97
+ "@rsdoctor/graph": "1.5.12",
98
+ "@rsdoctor/types": "1.5.12",
99
+ "@rsdoctor/sdk": "1.5.12",
100
+ "@rsdoctor/utils": "1.5.12"
101
101
  },
102
102
  "devDependencies": {
103
- "@rspack/core": "2.0.2",
103
+ "@rspack/core": "2.0.3",
104
104
  "@types/fs-extra": "^11.0.4",
105
- "@types/node": "^22.8.1",
105
+ "@types/node": "^24.12.3",
106
106
  "@types/node-fetch": "^2.6.13",
107
107
  "@types/semver": "^7.7.1",
108
108
  "@types/tapable": "2.3.0",
@@ -111,7 +111,7 @@
111
111
  "string-loader": "0.0.1",
112
112
  "ts-loader": "^9.5.7",
113
113
  "tslib": "2.8.1",
114
- "typescript": "^5.9.2",
114
+ "typescript": "^6.0.3",
115
115
  "webpack": "^5.105.4",
116
116
  "@scripts/test-helper": "0.1.1"
117
117
  },