@bpmn-io/codemods 0.2.1 → 0.3.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/esm/README.md +4 -4
- package/esm/lib/resolve.js +84 -3
- package/package.json +1 -1
package/esm/README.md
CHANGED
|
@@ -52,11 +52,12 @@ For every `.js`, `.jsx`, `.mjs`, `.cjs`, `.ts` and `.tsx` file below the target
|
|
|
52
52
|
- Relative and bare (`node_modules`) specifiers are both handled.
|
|
53
53
|
- TypeScript / JSX sources are written with a `.js` specifier (NodeNext
|
|
54
54
|
convention), even though the file on disk is `.ts`/`.tsx`.
|
|
55
|
+
- Relative directory imports are expanded to `<dir>/index.js` when an
|
|
56
|
+
`index` file exists in that directory (e.g. `./lib` → `./lib/index.js`).
|
|
55
57
|
4. Specifiers that already carry an extension, and bare package roots
|
|
56
58
|
(e.g. `diagram-js`, `react`), are left untouched.
|
|
57
|
-
5. Anything that still cannot be resolved (e.g. a
|
|
58
|
-
|
|
59
|
-
review — never silently changed.
|
|
59
|
+
5. Anything that still cannot be resolved (e.g. a genuinely missing file) is
|
|
60
|
+
**reported** for manual review — never silently changed.
|
|
60
61
|
|
|
61
62
|
Only the source string is edited; surrounding formatting is preserved exactly.
|
|
62
63
|
|
|
@@ -74,5 +75,4 @@ By design, the mod does **not**:
|
|
|
74
75
|
|
|
75
76
|
- rewrite `require()` calls,
|
|
76
77
|
- rewrite dynamic imports with a non-literal argument (`import(name)`),
|
|
77
|
-
- add `/index.js` for directory imports,
|
|
78
78
|
- guess at unresolvable imports — these are reported instead.
|
package/esm/lib/resolve.js
CHANGED
|
@@ -72,6 +72,11 @@ export function resolveImport(specifier, fromFile, options = {}) {
|
|
|
72
72
|
return { status: 'skip', specifier };
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
// bare subpath covered by package.json exports → already valid for ESM
|
|
76
|
+
if (bare && isSubpathExported(specifier, fromFile)) {
|
|
77
|
+
return { status: 'skip', specifier };
|
|
78
|
+
}
|
|
79
|
+
|
|
75
80
|
const rewritten = bare
|
|
76
81
|
? resolveBare(specifier, fromFile)
|
|
77
82
|
: resolveRelative(specifier, fromFile);
|
|
@@ -86,7 +91,19 @@ export function resolveImport(specifier, fromFile, options = {}) {
|
|
|
86
91
|
function resolveRelative(specifier, fromFile) {
|
|
87
92
|
const base = path.resolve(path.dirname(fromFile), specifier);
|
|
88
93
|
|
|
89
|
-
|
|
94
|
+
const direct = resolveCandidates(base, specifier, RELATIVE_CANDIDATES);
|
|
95
|
+
if (direct) return direct;
|
|
96
|
+
|
|
97
|
+
// directory import: specifier points to a directory, try <dir>/index.*
|
|
98
|
+
if (isDirectory(base)) {
|
|
99
|
+
return resolveCandidates(
|
|
100
|
+
path.join(base, 'index'),
|
|
101
|
+
specifier + '/index',
|
|
102
|
+
RELATIVE_CANDIDATES
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return null;
|
|
90
107
|
}
|
|
91
108
|
|
|
92
109
|
function resolveBare(specifier, fromFile) {
|
|
@@ -98,7 +115,20 @@ function resolveBare(specifier, fromFile) {
|
|
|
98
115
|
return null;
|
|
99
116
|
}
|
|
100
117
|
|
|
101
|
-
|
|
118
|
+
const basePath = path.join(pkgDir, subpath);
|
|
119
|
+
|
|
120
|
+
const direct = resolveCandidates(basePath, specifier, BARE_CANDIDATES);
|
|
121
|
+
if (direct) return direct;
|
|
122
|
+
|
|
123
|
+
if (isDirectory(basePath)) {
|
|
124
|
+
return resolveCandidates(
|
|
125
|
+
path.join(basePath, 'index'),
|
|
126
|
+
specifier + '/index',
|
|
127
|
+
BARE_CANDIDATES
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return null;
|
|
102
132
|
}
|
|
103
133
|
|
|
104
134
|
function resolveCandidates(basePath, specifier, candidates) {
|
|
@@ -113,7 +143,8 @@ function resolveCandidates(basePath, specifier, candidates) {
|
|
|
113
143
|
|
|
114
144
|
/**
|
|
115
145
|
* Locate a package directory by walking up the `node_modules` chain, starting
|
|
116
|
-
* from the importing file's directory.
|
|
146
|
+
* from the importing file's directory. Also handles self-referencing: if a
|
|
147
|
+
* directory's own `package.json` names the package, that directory is returned.
|
|
117
148
|
*/
|
|
118
149
|
function findPackageDir(pkg, fromDir) {
|
|
119
150
|
let dir = fromDir;
|
|
@@ -125,6 +156,10 @@ function findPackageDir(pkg, fromDir) {
|
|
|
125
156
|
return candidate;
|
|
126
157
|
}
|
|
127
158
|
|
|
159
|
+
if (readJson(path.join(dir, 'package.json'))?.name === pkg) {
|
|
160
|
+
return dir;
|
|
161
|
+
}
|
|
162
|
+
|
|
128
163
|
const parent = path.dirname(dir);
|
|
129
164
|
|
|
130
165
|
if (parent === dir) {
|
|
@@ -189,3 +224,49 @@ function isDirectory(p) {
|
|
|
189
224
|
return false;
|
|
190
225
|
}
|
|
191
226
|
}
|
|
227
|
+
|
|
228
|
+
function readJson(filePath) {
|
|
229
|
+
try {
|
|
230
|
+
return JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
231
|
+
} catch {
|
|
232
|
+
return null;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Returns true if the bare subpath import is declared in the package's
|
|
238
|
+
* `exports` field, meaning it is already valid for ESM and should not be
|
|
239
|
+
* rewritten.
|
|
240
|
+
*/
|
|
241
|
+
function isSubpathExported(specifier, fromFile) {
|
|
242
|
+
const { pkg, subpath } = parseBare(specifier);
|
|
243
|
+
if (!subpath) return false;
|
|
244
|
+
|
|
245
|
+
const pkgDir = findPackageDir(pkg, path.dirname(fromFile));
|
|
246
|
+
if (!pkgDir) return false;
|
|
247
|
+
|
|
248
|
+
const exports = readJson(path.join(pkgDir, 'package.json'))?.exports;
|
|
249
|
+
if (!exports || typeof exports !== 'object') return false;
|
|
250
|
+
|
|
251
|
+
const key = './' + subpath;
|
|
252
|
+
|
|
253
|
+
if (key in exports) return true;
|
|
254
|
+
|
|
255
|
+
for (const [ pattern, valueTemplate ] of Object.entries(exports)) {
|
|
256
|
+
if (!pattern.includes('*') || typeof valueTemplate !== 'string') {
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, '\\$&').replace(/\*/g, '(.*)');
|
|
261
|
+
const match = new RegExp('^' + escaped + '$').exec(key);
|
|
262
|
+
|
|
263
|
+
if (!match) continue;
|
|
264
|
+
|
|
265
|
+
// wildcard matched: only treat as already-valid if the resolved target
|
|
266
|
+
// is an actual file — a directory target still needs rewriting to index.js
|
|
267
|
+
const resolved = valueTemplate.replace(/\*/g, match[1]);
|
|
268
|
+
if (isFile(path.join(pkgDir, resolved))) return true;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
return false;
|
|
272
|
+
}
|