@bpmn-io/codemods 0.1.0 → 0.2.0
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/bin/codemods.js +0 -0
- package/esm/README.md +14 -6
- package/esm/lib/cli.js +61 -7
- package/esm/lib/migrate.js +4 -3
- package/esm/lib/resolve.js +16 -2
- package/esm/lib/transform.js +3 -2
- package/package.json +17 -3
package/bin/codemods.js
CHANGED
|
File without changes
|
package/esm/README.md
CHANGED
|
@@ -23,11 +23,19 @@ npx @bpmn-io/codemods esm <dir|file>
|
|
|
23
23
|
|
|
24
24
|
# preview without writing
|
|
25
25
|
npx @bpmn-io/codemods esm --dry-run src
|
|
26
|
+
|
|
27
|
+
# only rewrite imports of specific packages
|
|
28
|
+
npx @bpmn-io/codemods esm src --package diagram-js --package bpmn-js
|
|
26
29
|
```
|
|
27
30
|
|
|
28
31
|
Run it from your project root so that `node_modules` (the installed,
|
|
29
32
|
already-migrated package) is reachable for resolution.
|
|
30
33
|
|
|
34
|
+
When one or more `--package` (`-p`) options are given, only imports of those
|
|
35
|
+
packages are rewritten — relative imports and imports of other packages are
|
|
36
|
+
left untouched (and not reported as unresolved). This is useful for migrating
|
|
37
|
+
one dependency at a time, e.g. when only `diagram-js` has gone "ESM only".
|
|
38
|
+
|
|
31
39
|
The command exits with code `1` if any import could not be resolved, so it can
|
|
32
40
|
be used as a CI guard.
|
|
33
41
|
|
|
@@ -36,17 +44,17 @@ be used as a CI guard.
|
|
|
36
44
|
For every `.js`, `.jsx`, `.mjs`, `.cjs`, `.ts` and `.tsx` file below the target
|
|
37
45
|
(ignoring `node_modules` and `.git`):
|
|
38
46
|
|
|
39
|
-
1.
|
|
40
|
-
`import('...')` call with a string-literal argument
|
|
41
|
-
|
|
42
|
-
|
|
47
|
+
1. Collect static `import` / `export ... from` source, and every dynamic
|
|
48
|
+
`import('...')` call with a string-literal argument - filter by (optional) `package` filters
|
|
49
|
+
2. Matching imports are checked against ES module resolution.
|
|
50
|
+
3. If the specifier does not resolve as written but does once an extension is
|
|
43
51
|
appended, the extension is added.
|
|
44
52
|
- Relative and bare (`node_modules`) specifiers are both handled.
|
|
45
53
|
- TypeScript / JSX sources are written with a `.js` specifier (NodeNext
|
|
46
54
|
convention), even though the file on disk is `.ts`/`.tsx`.
|
|
47
|
-
|
|
55
|
+
4. Specifiers that already carry an extension, and bare package roots
|
|
48
56
|
(e.g. `diagram-js`, `react`), are left untouched.
|
|
49
|
-
|
|
57
|
+
5. Anything that still cannot be resolved (e.g. a directory import that would
|
|
50
58
|
need `/index.js`, or a genuinely missing file) is **reported** for manual
|
|
51
59
|
review — never silently changed.
|
|
52
60
|
|
package/esm/lib/cli.js
CHANGED
|
@@ -9,8 +9,10 @@ native ES module resolution (e.g. when migrating to a package that ships
|
|
|
9
9
|
"ESM only", such as diagram-js).
|
|
10
10
|
|
|
11
11
|
Options:
|
|
12
|
-
-
|
|
13
|
-
|
|
12
|
+
-p, --package <name> Only rewrite imports of this package (repeatable);
|
|
13
|
+
when given, relative and other packages are left alone
|
|
14
|
+
-d, --dry-run Report changes without writing files
|
|
15
|
+
-h, --help Show this help
|
|
14
16
|
|
|
15
17
|
Exit code is 1 when any import could not be resolved.
|
|
16
18
|
`;
|
|
@@ -21,23 +23,75 @@ Exit code is 1 when any import could not be resolved.
|
|
|
21
23
|
* @param {string[]} argv - arguments after the mod name
|
|
22
24
|
*/
|
|
23
25
|
export function run(argv) {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
+
let options;
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
options = parseArgs(argv);
|
|
30
|
+
} catch (error) {
|
|
31
|
+
process.stderr.write(`${error.message}\n\n${HELP}`);
|
|
32
|
+
process.exitCode = 1;
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const { help, dryRun, packages, target } = options;
|
|
26
37
|
|
|
27
38
|
if (help) {
|
|
28
39
|
process.stdout.write(HELP);
|
|
29
40
|
return;
|
|
30
41
|
}
|
|
31
42
|
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
const report = migrate(target, { dryRun });
|
|
43
|
+
const report = migrate(target, { dryRun, packages });
|
|
35
44
|
|
|
36
45
|
print(report, dryRun);
|
|
37
46
|
|
|
38
47
|
process.exitCode = report.unresolved.length || report.errors.length ? 1 : 0;
|
|
39
48
|
}
|
|
40
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Parse the `esm` mod CLI arguments.
|
|
52
|
+
*
|
|
53
|
+
* @param {string[]} argv
|
|
54
|
+
*
|
|
55
|
+
* @return {{ help: boolean, dryRun: boolean, packages: string[], target: string }}
|
|
56
|
+
*
|
|
57
|
+
* @throws {Error} on invalid usage, e.g. a `--package` without a name
|
|
58
|
+
*/
|
|
59
|
+
export function parseArgs(argv) {
|
|
60
|
+
const options = { help: false, dryRun: false, packages: [], target: null };
|
|
61
|
+
|
|
62
|
+
for (let i = 0; i < argv.length; i++) {
|
|
63
|
+
const arg = argv[i];
|
|
64
|
+
|
|
65
|
+
if (arg === '--help' || arg === '-h') {
|
|
66
|
+
options.help = true;
|
|
67
|
+
} else if (arg === '--dry-run' || arg === '-d') {
|
|
68
|
+
options.dryRun = true;
|
|
69
|
+
} else if (arg === '--package' || arg === '-p') {
|
|
70
|
+
options.packages.push(requirePackage(arg, argv[++i]));
|
|
71
|
+
} else if (arg.startsWith('--package=')) {
|
|
72
|
+
options.packages.push(requirePackage('--package', arg.slice('--package='.length)));
|
|
73
|
+
} else if (!arg.startsWith('-') && options.target === null) {
|
|
74
|
+
options.target = arg;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
options.target = options.target || process.cwd();
|
|
79
|
+
|
|
80
|
+
return options;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Guard against a `--package` option that is missing its value or accidentally
|
|
85
|
+
* swallowed a following flag (e.g. `-p -d`).
|
|
86
|
+
*/
|
|
87
|
+
function requirePackage(flag, value) {
|
|
88
|
+
if (!value || value.startsWith('-')) {
|
|
89
|
+
throw new Error(`Option ${flag} requires a package name, got: ${value ?? '(none)'}`);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return value;
|
|
93
|
+
}
|
|
94
|
+
|
|
41
95
|
function print(report, dryRun) {
|
|
42
96
|
const rel = (file) => path.relative(process.cwd(), file) || file;
|
|
43
97
|
|
package/esm/lib/migrate.js
CHANGED
|
@@ -22,12 +22,13 @@ const IGNORED_DIRECTORIES = new Set([ 'node_modules', '.git' ]);
|
|
|
22
22
|
* under native ES module resolution.
|
|
23
23
|
*
|
|
24
24
|
* @param {string} target - directory or file to migrate
|
|
25
|
-
* @param {{ dryRun?: boolean }} [options]
|
|
25
|
+
* @param {{ dryRun?: boolean, packages?: string[] }} [options] - when
|
|
26
|
+
* `packages` is non-empty, only imports of those packages are rewritten
|
|
26
27
|
*
|
|
27
28
|
* @return {Report}
|
|
28
29
|
*/
|
|
29
30
|
export function migrate(target, options = {}) {
|
|
30
|
-
const { dryRun = false } = options;
|
|
31
|
+
const { dryRun = false, packages = [] } = options;
|
|
31
32
|
|
|
32
33
|
const report = {
|
|
33
34
|
filesScanned: 0,
|
|
@@ -42,7 +43,7 @@ export function migrate(target, options = {}) {
|
|
|
42
43
|
report.filesScanned++;
|
|
43
44
|
|
|
44
45
|
const code = fs.readFileSync(file, 'utf8');
|
|
45
|
-
const result = transform(code, file);
|
|
46
|
+
const result = transform(code, file, { packages });
|
|
46
47
|
|
|
47
48
|
if (result.error) {
|
|
48
49
|
report.errors.push({ file, message: result.error.message });
|
package/esm/lib/resolve.js
CHANGED
|
@@ -47,14 +47,21 @@ const BARE_CANDIDATES = [
|
|
|
47
47
|
*
|
|
48
48
|
* @param {string} specifier - the raw import source, e.g. `diagram-js/lib/util/Elements`
|
|
49
49
|
* @param {string} fromFile - absolute path of the importing file
|
|
50
|
+
* @param {{ packages?: string[] }} [options] - if `packages` is non-empty, only
|
|
51
|
+
* imports of those packages are considered; everything else is skipped
|
|
50
52
|
*
|
|
51
53
|
* @return {{ status: ('skip'|'rewrite'|'unresolved'), specifier: string }}
|
|
52
|
-
* `skip` if already resolvable
|
|
54
|
+
* `skip` if already resolvable or filtered out by `options.packages`, `rewrite` with the new specifier if an
|
|
53
55
|
* extension was appended, `unresolved` if no `.js`-style extension resolves.
|
|
54
56
|
*/
|
|
55
|
-
export function resolveImport(specifier, fromFile) {
|
|
57
|
+
export function resolveImport(specifier, fromFile, options = {}) {
|
|
56
58
|
const bare = isBare(specifier);
|
|
57
59
|
|
|
60
|
+
// when restricted to specific packages, only their (bare) imports apply
|
|
61
|
+
if (options.packages && options.packages.length && !targetsPackage(specifier, bare, options.packages)) {
|
|
62
|
+
return { status: 'skip', specifier };
|
|
63
|
+
}
|
|
64
|
+
|
|
58
65
|
// bare package roots (e.g. 'react', 'diagram-js') resolve via main/exports
|
|
59
66
|
if (bare && isBareRoot(specifier)) {
|
|
60
67
|
return { status: 'skip', specifier };
|
|
@@ -153,6 +160,13 @@ function isBareRoot(specifier) {
|
|
|
153
160
|
return parseBare(specifier).subpath === '';
|
|
154
161
|
}
|
|
155
162
|
|
|
163
|
+
/**
|
|
164
|
+
* Whether the specifier imports from one of the given packages.
|
|
165
|
+
*/
|
|
166
|
+
function targetsPackage(specifier, bare, packages) {
|
|
167
|
+
return bare && packages.includes(parseBare(specifier).pkg);
|
|
168
|
+
}
|
|
169
|
+
|
|
156
170
|
function specifierExtension(specifier) {
|
|
157
171
|
const segment = specifier.split('/').pop();
|
|
158
172
|
const dot = segment.lastIndexOf('.');
|
package/esm/lib/transform.js
CHANGED
|
@@ -13,6 +13,7 @@ import { resolveImport } from './resolve.js';
|
|
|
13
13
|
*
|
|
14
14
|
* @param {string} code - the file contents
|
|
15
15
|
* @param {string} filename - absolute path of the file (used for resolution)
|
|
16
|
+
* @param {{ packages?: string[] }} [options] - passed through to the resolver
|
|
16
17
|
*
|
|
17
18
|
* @return {{
|
|
18
19
|
* code: string,
|
|
@@ -22,7 +23,7 @@ import { resolveImport } from './resolve.js';
|
|
|
22
23
|
* error: (Error|null)
|
|
23
24
|
* }}
|
|
24
25
|
*/
|
|
25
|
-
export function transform(code, filename) {
|
|
26
|
+
export function transform(code, filename, options = {}) {
|
|
26
27
|
let ast;
|
|
27
28
|
|
|
28
29
|
try {
|
|
@@ -40,7 +41,7 @@ export function transform(code, filename) {
|
|
|
40
41
|
const unresolved = [];
|
|
41
42
|
|
|
42
43
|
for (const source of importSources(ast)) {
|
|
43
|
-
const result = resolveImport(source.value, filename);
|
|
44
|
+
const result = resolveImport(source.value, filename, options);
|
|
44
45
|
const line = source.loc.start.line;
|
|
45
46
|
|
|
46
47
|
if (result.status === 'rewrite') {
|
package/package.json
CHANGED
|
@@ -1,11 +1,25 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bpmn-io/codemods",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "A collection of codemods for the bpmn.io ecosystem.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"codemods": "bin/codemods.js"
|
|
8
8
|
},
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "https://github.com/bpmn-io/diagram-js"
|
|
12
|
+
},
|
|
13
|
+
"author": {
|
|
14
|
+
"name": "Nico Rehwaldt",
|
|
15
|
+
"url": "https://github.com/nikku"
|
|
16
|
+
},
|
|
17
|
+
"contributors": [
|
|
18
|
+
{
|
|
19
|
+
"name": "bpmn.io contributors",
|
|
20
|
+
"url": "https://github.com/bpmn-io"
|
|
21
|
+
}
|
|
22
|
+
],
|
|
9
23
|
"scripts": {
|
|
10
24
|
"all": "npm run test",
|
|
11
25
|
"test": "mocha"
|
|
@@ -26,7 +40,7 @@
|
|
|
26
40
|
"@babel/parser": "^7.24.0"
|
|
27
41
|
},
|
|
28
42
|
"devDependencies": {
|
|
29
|
-
"chai": "^
|
|
30
|
-
"mocha": "^
|
|
43
|
+
"chai": "^6.2.2",
|
|
44
|
+
"mocha": "^11.7.6"
|
|
31
45
|
}
|
|
32
46
|
}
|