@cratis/components.migrator 0.0.0 → 4.1.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.
@@ -0,0 +1,137 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import { readFileSync, writeFileSync, statSync, readdirSync } from 'node:fs';
5
+ import { join, extname } from 'node:path';
6
+ import { packageName as defaultPackageName } from './namespaceMap.js';
7
+ import { preflightCompatibility } from './compatibility.js';
8
+
9
+ const EXTENSIONS = new Set([
10
+ '.js',
11
+ '.jsx',
12
+ '.mjs',
13
+ '.cjs',
14
+ '.ts',
15
+ '.tsx',
16
+ '.mts',
17
+ '.cts',
18
+ ]);
19
+ const SKIP_DIRS = new Set(['node_modules', 'dist', '.git', '.yarn']);
20
+
21
+ /** Runs one independently invocable Components codemod CLI. */
22
+ export function runTransformCli(argv, { command, description, transform }) {
23
+ const args = [...argv];
24
+ let check = false;
25
+ let packageName = defaultPackageName;
26
+ const paths = [];
27
+
28
+ while (args.length > 0) {
29
+ const arg = args.shift();
30
+ if (arg === '--check') check = true;
31
+ else if (arg === '--package') {
32
+ const configuredPackage = args.shift();
33
+ if (!configuredPackage) {
34
+ console.error('--package requires a package name.');
35
+ return 1;
36
+ }
37
+ packageName = configuredPackage;
38
+ } else if (arg === '--help' || arg === '-h') {
39
+ printUsage(command, description);
40
+ return 0;
41
+ } else if (arg.startsWith('-')) {
42
+ console.error(`Unknown option: ${arg}`);
43
+ return 1;
44
+ } else paths.push(arg);
45
+ }
46
+
47
+ if (paths.length === 0) {
48
+ printUsage(command, description);
49
+ return 1;
50
+ }
51
+
52
+ try {
53
+ preflightCompatibility({ packageName });
54
+ } catch (error) {
55
+ console.error(
56
+ `Compatibility preflight failed: ${error instanceof Error ? error.message : String(error)}`,
57
+ );
58
+ return 1;
59
+ }
60
+
61
+ let files;
62
+ try {
63
+ files = paths.flatMap(collectFiles);
64
+ } catch (error) {
65
+ console.error(error instanceof Error ? error.message : String(error));
66
+ return 1;
67
+ }
68
+
69
+ let changedCount = 0;
70
+ let diagnosticCount = 0;
71
+ let errorCount = 0;
72
+ for (const file of files) {
73
+ try {
74
+ const original = readFileSync(file, 'utf8');
75
+ const result = transform(file, original, { packageName });
76
+ for (const diagnostic of result.diagnostics) {
77
+ diagnosticCount += 1;
78
+ console.warn(
79
+ `${diagnostic.file}:${diagnostic.line}:${diagnostic.column}: ${diagnostic.message}`,
80
+ );
81
+ }
82
+ if (result.changed) {
83
+ changedCount += 1;
84
+ if (check) console.log(`would rewrite ${file}`);
85
+ else {
86
+ writeFileSync(file, result.text, 'utf8');
87
+ console.log(`rewrote ${file}`);
88
+ }
89
+ }
90
+ } catch (error) {
91
+ errorCount += 1;
92
+ console.error(
93
+ `${file}: failed to process: ${error instanceof Error ? error.message : String(error)}`,
94
+ );
95
+ }
96
+ }
97
+
98
+ console.log(
99
+ `\n${files.length} file(s) scanned, ${changedCount} ${check ? 'would change' : 'changed'}, ${diagnosticCount} unsupported or ambiguous case(s) reported, ${errorCount} processing error(s).`,
100
+ );
101
+ if (diagnosticCount > 0 || errorCount > 0 || (check && changedCount > 0)) return 1;
102
+ return 0;
103
+ }
104
+
105
+ function printUsage(command, description) {
106
+ console.log(
107
+ [
108
+ `Usage: ${command} [--check] [--package <name>] <path...>`,
109
+ '',
110
+ description,
111
+ '',
112
+ 'Options:',
113
+ ' --check Report what would change without writing files; exits 1 if',
114
+ ' anything would change or manual review is required.',
115
+ ' --package <name> Components package name (default: @cratis/components).',
116
+ '',
117
+ '<path...> may be files or directories; directories are walked recursively for',
118
+ '.js/.jsx/.mjs/.cjs/.ts/.tsx/.mts/.cts files, skipping node_modules,',
119
+ 'dist, .git, and .yarn.',
120
+ ].join('\n'),
121
+ );
122
+ }
123
+
124
+ function collectFiles(inputPath) {
125
+ const stats = statSync(inputPath);
126
+ if (stats.isFile()) return EXTENSIONS.has(extname(inputPath)) ? [inputPath] : [];
127
+ if (!stats.isDirectory()) return [];
128
+ const files = [];
129
+ for (const entry of readdirSync(inputPath, { withFileTypes: true })) {
130
+ if (SKIP_DIRS.has(entry.name)) continue;
131
+ const entryPath = join(inputPath, entry.name);
132
+ if (entry.isDirectory()) files.push(...collectFiles(entryPath));
133
+ else if (entry.isFile() && EXTENSIONS.has(extname(entry.name)))
134
+ files.push(entryPath);
135
+ }
136
+ return files;
137
+ }
@@ -0,0 +1,355 @@
1
+ // Copyright (c) Cratis. All rights reserved.
2
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3
+
4
+ import ts from 'typescript';
5
+ import {
6
+ packageName as defaultPackageName,
7
+ namespaceSubpaths,
8
+ approvedRootSymbols,
9
+ removedRootSymbols,
10
+ } from './namespaceMap.js';
11
+
12
+ /**
13
+ * Rewrites static imports of the `@cratis/components` root barrel into subpath
14
+ * namespace imports, using the TypeScript compiler API for exact, position-based text
15
+ * edits rather than a full reprint — every untouched line of the file, including
16
+ * comments and formatting, is left byte-identical.
17
+ *
18
+ * What it does:
19
+ * - `import { Canvas } from '@cratis/components'` becomes
20
+ * `import * as Canvas from '@cratis/components/Canvas'`.
21
+ * - `import type { Canvas } from '@cratis/components'` becomes
22
+ * `import type * as Canvas from '@cratis/components/Canvas'`; a per-specifier
23
+ * `type` modifier (`import { type Canvas } from ...`) is honored the same way.
24
+ * - Aliases are preserved: `import { Canvas as C } from '@cratis/components'` becomes
25
+ * `import * as C from '@cratis/components/Canvas'`.
26
+ * - A mixed import naming both an approved setup symbol and a namespace is split: the
27
+ * setup symbol stays imported from the root, each namespace becomes its own subpath
28
+ * import.
29
+ * - An import naming several namespaces produces one subpath import per namespace, in
30
+ * their original left-to-right order.
31
+ * - A named re-export follows the same rules: `export { Canvas } from '@cratis/components'`
32
+ * becomes `export * as Canvas from '@cratis/components/Canvas'`, with the same alias,
33
+ * type-only, mixed-setup-symbol, and multiple-namespace handling as the import case.
34
+ * - Applying the codemod again is a no-op: it only ever matches an import or named
35
+ * re-export whose module specifier is exactly the configured package name, so an
36
+ * already-migrated subpath import or re-export is never revisited.
37
+ *
38
+ * What it deliberately refuses to guess, reporting a diagnostic instead of editing:
39
+ * - A namespace import of the whole package (`import * as Components from '...'`) —
40
+ * which subpath each later `Components.X` access belongs to cannot be determined
41
+ * from the import alone.
42
+ * - A default import (`import Components from '...'`) — the package has no default
43
+ * export.
44
+ * - A side-effect-only import (`import '...'`) — there is no binding to infer a
45
+ * subpath from.
46
+ * - A named import or named re-export of a symbol that is neither an approved setup
47
+ * symbol nor a known namespace — the whole statement is left untouched so a
48
+ * partial, silently incomplete migration is never produced.
49
+ * - A known removed Components 3 renderer-compatibility export — it has no Components 4
50
+ * subpath, so the statement is left untouched with typed-parts guidance.
51
+ * - A dynamic `import('...')` or CommonJS `require('...')` call anywhere in the file.
52
+ * - A wildcard re-export of the whole package (`export * from '@cratis/components'`)
53
+ * or a namespace re-export of the whole package (`export * as X from
54
+ * '@cratis/components'`) — the same ambiguity as a whole-package namespace import:
55
+ * which subpath each later access belongs to cannot be determined from the
56
+ * re-export alone.
57
+ *
58
+ * @param {string} fileName - Used only to select the TypeScript/TSX/JS/JSX grammar.
59
+ * @param {string} text - The source file's current contents.
60
+ * @param {{ packageName?: string }} [options]
61
+ * @returns {{ text: string, changed: boolean, diagnostics: Array<{ file: string, line: number, column: number, message: string }> }}
62
+ */
63
+ export function transformSource(fileName, text, options = {}) {
64
+ const packageName = options.packageName ?? defaultPackageName;
65
+ const sourceFile = ts.createSourceFile(
66
+ fileName,
67
+ text,
68
+ ts.ScriptTarget.Latest,
69
+ true,
70
+ scriptKindFor(fileName),
71
+ );
72
+
73
+ const diagnostics = [];
74
+ const edits = [];
75
+
76
+ const isRootSpecifier = (moduleSpecifier) =>
77
+ !!moduleSpecifier &&
78
+ ts.isStringLiteral(moduleSpecifier) &&
79
+ moduleSpecifier.text === packageName;
80
+
81
+ const report = (node, message) => {
82
+ const { line, character } = sourceFile.getLineAndCharacterOfPosition(
83
+ node.getStart(sourceFile),
84
+ );
85
+ diagnostics.push({
86
+ file: fileName,
87
+ line: line + 1,
88
+ column: character + 1,
89
+ message,
90
+ });
91
+ };
92
+
93
+ const handleImport = (node) => {
94
+ if (!isRootSpecifier(node.moduleSpecifier)) return;
95
+
96
+ const importClause = node.importClause;
97
+ if (!importClause) {
98
+ report(
99
+ node,
100
+ `Side-effect-only import of '${packageName}' cannot be auto-migrated: there is no named binding to infer a subpath from. Import the specific subpath directly, or remove the import if it has no effect.`,
101
+ );
102
+ return;
103
+ }
104
+ if (importClause.name) {
105
+ report(
106
+ node,
107
+ `Default import of '${packageName}' cannot be auto-migrated: the package has no default export. Replace it with the subpath import(s) it actually needs.`,
108
+ );
109
+ return;
110
+ }
111
+
112
+ const bindings = importClause.namedBindings;
113
+ if (!bindings || !ts.isNamedImports(bindings)) {
114
+ report(
115
+ node,
116
+ `Namespace import of '${packageName}' cannot be auto-migrated: which subpath each later member access belongs to cannot be inferred from the import alone. Replace it with the specific namespace subpath import(s) it needs.`,
117
+ );
118
+ return;
119
+ }
120
+
121
+ const declTypeOnly = importClause.isTypeOnly;
122
+ const kept = [];
123
+ const namespaced = [];
124
+ let hasUnknown = false;
125
+
126
+ for (const element of bindings.elements) {
127
+ const importedName = (element.propertyName ?? element.name).text;
128
+ const localName = element.name.text;
129
+
130
+ if (approvedRootSymbols.has(importedName)) {
131
+ kept.push({
132
+ name: importedName,
133
+ localName,
134
+ elementTypeOnly: element.isTypeOnly,
135
+ });
136
+ continue;
137
+ }
138
+ if (Object.hasOwn(namespaceSubpaths, importedName)) {
139
+ namespaced.push({
140
+ localName,
141
+ subpath: namespaceSubpaths[importedName],
142
+ typeOnly: declTypeOnly || element.isTypeOnly,
143
+ });
144
+ continue;
145
+ }
146
+ if (removedRootSymbols.has(importedName)) {
147
+ hasUnknown = true;
148
+ report(
149
+ element,
150
+ `'${importedName}' was a Components 3 PrimeReact compatibility export and has no Components 4 subpath. Leaving this import untouched — migrate renderer slots to typed Cratis parts and remove the compatibility export manually.`,
151
+ );
152
+ continue;
153
+ }
154
+
155
+ hasUnknown = true;
156
+ report(
157
+ element,
158
+ `'${importedName}' is not a recognized '${packageName}' root export (neither an approved setup symbol nor a known namespace). Leaving this import untouched — add '${importedName}' to the migration map, or migrate it by hand.`,
159
+ );
160
+ }
161
+
162
+ // Never guess: an unrecognized specifier means the whole statement is left as-is,
163
+ // even for the specifiers this codemod does recognize.
164
+ if (hasUnknown) return;
165
+ // Nothing to migrate — the import already only names approved root symbols.
166
+ if (namespaced.length === 0) return;
167
+
168
+ const replacement = buildSubpathReplacementText({
169
+ keyword: 'import',
170
+ packageName,
171
+ declTypeOnly,
172
+ kept,
173
+ namespaced,
174
+ });
175
+ edits.push({ start: node.getStart(sourceFile), end: node.getEnd(), replacement });
176
+ };
177
+
178
+ const handleExport = (node) => {
179
+ if (!isRootSpecifier(node.moduleSpecifier)) return;
180
+
181
+ if (!node.exportClause || !ts.isNamedExports(node.exportClause)) {
182
+ // A bare `export * from '@cratis/components'` or a namespace re-export
183
+ // (`export * as X from '@cratis/components'`) is exactly as ambiguous as a
184
+ // whole-package namespace import: there is no way to know, from the
185
+ // re-export alone, which subpath a later consumer's member access needs.
186
+ report(
187
+ node,
188
+ `A wildcard 're-export … from ${JSON.stringify(packageName)}' cannot be auto-migrated: which subpath each later member access belongs to cannot be inferred from the re-export alone. Replace it with explicit subpath re-export(s).`,
189
+ );
190
+ return;
191
+ }
192
+
193
+ const declTypeOnly = node.isTypeOnly;
194
+ const kept = [];
195
+ const namespaced = [];
196
+ let hasUnknown = false;
197
+
198
+ for (const element of node.exportClause.elements) {
199
+ const exportedName = (element.propertyName ?? element.name).text;
200
+ const localName = element.name.text;
201
+
202
+ if (approvedRootSymbols.has(exportedName)) {
203
+ kept.push({
204
+ name: exportedName,
205
+ localName,
206
+ elementTypeOnly: element.isTypeOnly,
207
+ });
208
+ continue;
209
+ }
210
+ if (Object.hasOwn(namespaceSubpaths, exportedName)) {
211
+ namespaced.push({
212
+ localName,
213
+ subpath: namespaceSubpaths[exportedName],
214
+ typeOnly: declTypeOnly || element.isTypeOnly,
215
+ });
216
+ continue;
217
+ }
218
+ if (removedRootSymbols.has(exportedName)) {
219
+ hasUnknown = true;
220
+ report(
221
+ element,
222
+ `'${exportedName}' was a Components 3 PrimeReact compatibility export and has no Components 4 subpath. Leaving this re-export untouched — migrate renderer slots to typed Cratis parts and remove the compatibility export manually.`,
223
+ );
224
+ continue;
225
+ }
226
+
227
+ hasUnknown = true;
228
+ report(
229
+ element,
230
+ `'${exportedName}' is not a recognized '${packageName}' root export (neither an approved setup symbol nor a known namespace). Leaving this re-export untouched — add '${exportedName}' to the migration map, or migrate it by hand.`,
231
+ );
232
+ }
233
+
234
+ // Never guess: an unrecognized specifier means the whole statement is left as-is,
235
+ // even for the specifiers this codemod does recognize.
236
+ if (hasUnknown) return;
237
+ // Nothing to migrate — the re-export already only names approved root symbols.
238
+ if (namespaced.length === 0) return;
239
+
240
+ const replacement = buildSubpathReplacementText({
241
+ keyword: 'export',
242
+ packageName,
243
+ declTypeOnly,
244
+ kept,
245
+ namespaced,
246
+ });
247
+ edits.push({ start: node.getStart(sourceFile), end: node.getEnd(), replacement });
248
+ };
249
+
250
+ const handleImportEquals = (node) => {
251
+ if (!ts.isExternalModuleReference(node.moduleReference)) return;
252
+ if (!isRootSpecifier(node.moduleReference.expression)) return;
253
+
254
+ report(
255
+ node,
256
+ `Import-assignment of '${packageName}' ('import ${node.name.text} = require(...)') cannot be auto-migrated: which subpath each later member access belongs to cannot be inferred from the import alone. Replace it with the specific namespace subpath import(s) it needs.`,
257
+ );
258
+ };
259
+
260
+ const checkCallExpression = (node) => {
261
+ const isDynamicImport = node.expression.kind === ts.SyntaxKind.ImportKeyword;
262
+ const isRequire =
263
+ ts.isIdentifier(node.expression) && node.expression.text === 'require';
264
+ if (!isDynamicImport && !isRequire) return;
265
+
266
+ const [arg] = node.arguments;
267
+ if (arg && ts.isStringLiteral(arg) && arg.text === packageName) {
268
+ const form = isDynamicImport
269
+ ? "dynamic 'import(...)'"
270
+ : "CommonJS 'require(...)'";
271
+ report(
272
+ node,
273
+ `A ${form} of '${packageName}' cannot be auto-migrated: this codemod only rewrites static 'import' declarations. Replace it with the specific subpath import(s) it needs.`,
274
+ );
275
+ }
276
+ };
277
+
278
+ const visit = (node) => {
279
+ if (ts.isImportDeclaration(node)) {
280
+ handleImport(node);
281
+ return;
282
+ }
283
+ if (ts.isExportDeclaration(node)) {
284
+ handleExport(node);
285
+ return;
286
+ }
287
+ if (ts.isImportEqualsDeclaration(node)) {
288
+ handleImportEquals(node);
289
+ return;
290
+ }
291
+ if (ts.isCallExpression(node)) {
292
+ checkCallExpression(node);
293
+ }
294
+ ts.forEachChild(node, visit);
295
+ };
296
+ visit(sourceFile);
297
+
298
+ if (edits.length === 0) {
299
+ return { text, changed: false, diagnostics };
300
+ }
301
+
302
+ edits.sort((a, b) => a.start - b.start);
303
+ let output = text;
304
+ for (let i = edits.length - 1; i >= 0; i--) {
305
+ const { start, end, replacement } = edits[i];
306
+ output = output.slice(0, start) + replacement + output.slice(end);
307
+ }
308
+
309
+ return { text: output, changed: true, diagnostics };
310
+ }
311
+
312
+ /**
313
+ * Builds replacement source text for both the import and named-re-export cases, which
314
+ * share an identical shape: an optional statement that keeps the approved root symbols
315
+ * (`{ name, localName, elementTypeOnly }` entries), followed by one `* as localName from
316
+ * '<package>/<subpath>'` statement per namespace (`{ localName, subpath, typeOnly }`
317
+ * entries). `keyword` is `'import'` or `'export'`.
318
+ */
319
+ function buildSubpathReplacementText({ keyword, packageName, declTypeOnly, kept, namespaced }) {
320
+ const lines = [];
321
+
322
+ if (kept.length > 0) {
323
+ const specifiers = kept
324
+ .map(({ name, localName, elementTypeOnly }) => {
325
+ const prefix = !declTypeOnly && elementTypeOnly ? 'type ' : '';
326
+ const alias = localName === name ? '' : ` as ${localName}`;
327
+ return `${prefix}${name}${alias}`;
328
+ })
329
+ .join(', ');
330
+ lines.push(
331
+ `${keyword} ${declTypeOnly ? 'type ' : ''}{ ${specifiers} } from '${packageName}';`,
332
+ );
333
+ }
334
+
335
+ for (const { localName, subpath, typeOnly } of namespaced) {
336
+ lines.push(
337
+ `${keyword} ${typeOnly ? 'type ' : ''}* as ${localName} from '${packageName}/${subpath}';`,
338
+ );
339
+ }
340
+
341
+ return lines.join('\n');
342
+ }
343
+
344
+ function scriptKindFor(fileName) {
345
+ if (fileName.endsWith('.tsx')) return ts.ScriptKind.TSX;
346
+ if (fileName.endsWith('.jsx')) return ts.ScriptKind.JSX;
347
+ if (fileName.endsWith('.mts') || fileName.endsWith('.cts')) return ts.ScriptKind.TS;
348
+ if (
349
+ fileName.endsWith('.js') ||
350
+ fileName.endsWith('.mjs') ||
351
+ fileName.endsWith('.cjs')
352
+ )
353
+ return ts.ScriptKind.JS;
354
+ return ts.ScriptKind.TS;
355
+ }
package/package.json CHANGED
@@ -1,13 +1,42 @@
1
1
  {
2
2
  "name": "@cratis/components.migrator",
3
- "version": "0.0.0",
4
- "description": "Reserved package record for the Cratis Components release workflow.",
3
+ "version": "4.1.0",
4
+ "description": "Migration tooling for updating @cratis/components consumers from Components 3 to Components 4.",
5
+ "author": "Cratis",
5
6
  "license": "MIT",
6
7
  "type": "module",
7
8
  "publishConfig": {
8
9
  "access": "public"
9
10
  },
11
+ "engines": {
12
+ "node": ">=20.0.0"
13
+ },
10
14
  "files": [
11
- "README.md"
12
- ]
15
+ "lib",
16
+ "scripts",
17
+ "README.md",
18
+ "LICENSE",
19
+ "compat-manifest.json"
20
+ ],
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "https://github.com/Cratis/Components"
24
+ },
25
+ "exports": {
26
+ "./package.json": "./package.json"
27
+ },
28
+ "bin": {
29
+ "cratis-components-button-variant-tone": "./scripts/button-variant-tone.js",
30
+ "cratis-components-change-handler": "./scripts/change-handler.js",
31
+ "cratis-components-remove-root-namespace-imports": "./scripts/remove-root-namespace-imports.js"
32
+ },
33
+ "scripts": {
34
+ "test": "yarn g:test",
35
+ "verify-package": "node ./test/verify-packed-package.mjs",
36
+ "ci": "yarn g:test"
37
+ },
38
+ "dependencies": {
39
+ "semver": "7.8.5",
40
+ "typescript": "^6.0.3"
41
+ }
13
42
  }
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env node
2
+ // Copyright (c) Cratis. All rights reserved.
3
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
4
+
5
+ import { transformButtonVariantTone } from '../lib/buttonVariantToneTransform.js';
6
+ import { runTransformCli } from '../lib/runTransformCli.js';
7
+
8
+ process.exitCode = runTransformCli(process.argv.slice(2), {
9
+ command: 'cratis-components-button-variant-tone',
10
+ description:
11
+ 'Migrates deprecated Button text/link/outlined/rounded/severity JSX props to variant/tone/shape. Uncertain cases are annotated and reported for manual review.',
12
+ transform: transformButtonVariantTone,
13
+ });
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env node
2
+ // Copyright (c) Cratis. All rights reserved.
3
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
4
+
5
+ import { transformChangeHandlers } from '../lib/changeHandlerTransform.js';
6
+ import { runTransformCli } from '../lib/runTransformCli.js';
7
+
8
+ process.exitCode = runTransformCli(process.argv.slice(2), {
9
+ command: 'cratis-components-change-handler',
10
+ description:
11
+ 'Migrates structurally-proven Components event-wrapper callbacks to semantic value callbacks. Ambiguous handlers are annotated and reported for manual review.',
12
+ transform: transformChangeHandlers,
13
+ });
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env node
2
+ // Copyright (c) Cratis. All rights reserved.
3
+ // Licensed under the MIT license. See LICENSE file in the project root for full license information.
4
+
5
+ import { runTransformCli } from '../lib/runTransformCli.js';
6
+ import { transformSource } from '../lib/transform.js';
7
+
8
+ process.exitCode = runTransformCli(process.argv.slice(2), {
9
+ command: 'cratis-components-remove-root-namespace-imports',
10
+ description:
11
+ 'Rewrites Components 3 root namespace imports and named re-exports to Components 4 explicit subpaths. Unsupported whole-package and unknown forms are reported without guessing.',
12
+ transform: (file, text, { packageName }) =>
13
+ transformSource(file, text, { packageName }),
14
+ });