@jackuait/blok 0.3.1-beta.0 → 0.3.1-beta.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/README.md CHANGED
@@ -44,10 +44,10 @@ Run the codemod to automatically update your codebase:
44
44
 
45
45
  ```bash
46
46
  # Preview changes (recommended first)
47
- npx @jackuait/blok-codemod ./src --dry-run
47
+ npx blok-codemod ./src --dry-run
48
48
 
49
49
  # Apply changes
50
- npx @jackuait/blok-codemod ./src
50
+ npx blok-codemod ./src
51
51
  ```
52
52
 
53
53
  The codemod handles:
@@ -0,0 +1,177 @@
1
+ # EditorJS to Blok Codemod
2
+
3
+ Automatically migrate your codebase from EditorJS to Blok.
4
+
5
+ ## Installation & Usage
6
+
7
+ ### Using npx (recommended)
8
+
9
+ ```bash
10
+ # Dry run (preview changes without modifying files)
11
+ npx blok-codemod ./src --dry-run
12
+
13
+ # Apply changes
14
+ npx blok-codemod ./src
15
+
16
+ # Process entire project
17
+ npx blok-codemod .
18
+
19
+ # Verbose output
20
+ npx blok-codemod ./src --verbose
21
+ ```
22
+
23
+ ## What It Does
24
+
25
+ ### Import Transformations
26
+
27
+ ```diff
28
+ - import EditorJS from '@editorjs/editorjs';
29
+ + import Blok from '@jackuait/blok';
30
+
31
+ - import Header from '@editorjs/header';
32
+ - import Paragraph from '@editorjs/paragraph';
33
+ + // Header is now bundled with Blok: use Blok.Header
34
+ + // Paragraph is now bundled with Blok: use Blok.Paragraph
35
+ ```
36
+
37
+ ### Type Transformations
38
+
39
+ ```diff
40
+ - import type { EditorConfig } from '@editorjs/editorjs';
41
+ + import type { BlokConfig } from '@jackuait/blok';
42
+ ```
43
+
44
+ ### Class Name Transformations
45
+
46
+ ```diff
47
+ - const editor = new EditorJS({ ... });
48
+ + const editor = new Blok({ ... });
49
+ ```
50
+
51
+ ### CSS Selector Transformations
52
+
53
+ ```diff
54
+ - .codex-editor { }
55
+ + .blok-editor { }
56
+
57
+ - .ce-block { }
58
+ + [data-blok-testid="block-wrapper"] { }
59
+
60
+ - .ce-block--selected { }
61
+ + [data-blok-selected="true"] { }
62
+
63
+ - .ce-toolbar { }
64
+ + [data-blok-testid="toolbar"] { }
65
+ ```
66
+
67
+ ### Data Attribute Transformations
68
+
69
+ ```diff
70
+ - document.querySelector('[data-id="abc123"]');
71
+ + document.querySelector('[data-blok-id="abc123"]');
72
+
73
+ - document.querySelector('[data-item-name="bold"]');
74
+ + document.querySelector('[data-blok-item-name="bold"]');
75
+ ```
76
+
77
+ ### Default Holder Transformation
78
+
79
+ ```diff
80
+ - <div id="editorjs"></div>
81
+ + <div id="blok"></div>
82
+
83
+ - holder: 'editorjs'
84
+ + holder: 'blok'
85
+ ```
86
+
87
+ ### Tool Configuration Transformations
88
+
89
+ ```diff
90
+ tools: {
91
+ - header: Header,
92
+ - paragraph: Paragraph,
93
+ + header: Blok.Header,
94
+ + paragraph: Blok.Paragraph,
95
+ }
96
+ ```
97
+
98
+ ### package.json Updates
99
+
100
+ ```diff
101
+ {
102
+ "dependencies": {
103
+ - "@editorjs/editorjs": "^2.28.0",
104
+ - "@editorjs/header": "^2.8.0",
105
+ - "@editorjs/paragraph": "^2.11.0",
106
+ + "@jackuait/blok": "latest"
107
+ }
108
+ }
109
+ ```
110
+
111
+ ## Options
112
+
113
+ | Option | Description |
114
+ |--------|-------------|
115
+ | `--dry-run` | Preview changes without modifying files |
116
+ | `--verbose` | Show detailed output for each file |
117
+ | `--help` | Show help message |
118
+
119
+ ## Supported File Types
120
+
121
+ - JavaScript: `.js`, `.jsx`
122
+ - TypeScript: `.ts`, `.tsx`
123
+ - Vue: `.vue`
124
+ - Svelte: `.svelte`
125
+ - HTML: `.html`
126
+ - CSS: `.css`, `.scss`, `.less`
127
+
128
+ ## After Migration
129
+
130
+ 1. **Install dependencies**: Run `npm install` or `yarn` to update your dependencies
131
+
132
+ 2. **Review changes**: The codemod handles most common patterns, but review the changes for:
133
+ - Custom tool implementations
134
+ - Complex selector patterns
135
+ - Dynamic string construction
136
+
137
+ 3. **Update custom tools**: If you have custom tools, ensure they follow Blok's API:
138
+ - Lifecycle hooks: `rendered()`, `updated()`, `removed()`, `moved()`
139
+ - Use `data-blok-*` attributes
140
+
141
+ 4. **Test thoroughly**: Run your test suite and manually verify the editor works correctly
142
+
143
+ 5. **Check MIGRATION.md**: See the full [migration guide](../MIGRATION.md) for manual updates
144
+
145
+ ## Programmatic Usage
146
+
147
+ ```javascript
148
+ // If you've cloned the blok repository
149
+ const {
150
+ transformFile,
151
+ updatePackageJson,
152
+ applyTransforms,
153
+ } = require('./codemod/migrate-editorjs-to-blok');
154
+
155
+ // Transform a single file
156
+ const result = transformFile('/path/to/file.ts', false);
157
+ console.log(result.changes);
158
+
159
+ // Update package.json
160
+ const pkgResult = updatePackageJson('/path/to/package.json', false);
161
+ console.log(pkgResult.changes);
162
+ ```
163
+
164
+ ## Known Limitations
165
+
166
+ - Does not handle dynamic imports with variable paths
167
+ - Complex nested selectors may need manual adjustment
168
+ - Custom EditorJS plugins need manual migration
169
+ - String templates with EditorJS references need manual review
170
+
171
+ ## Contributing
172
+
173
+ Found a pattern that should be transformed? Open an issue or PR on the [Blok repository](https://github.com/jackuait/blok).
174
+
175
+ ## License
176
+
177
+ Apache-2.0
@@ -0,0 +1,480 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * EditorJS to Blok Codemod
5
+ *
6
+ * This script automates the migration from EditorJS to Blok.
7
+ * It transforms imports, class names, selectors, and data attributes.
8
+ *
9
+ * Usage:
10
+ * npx blok-codemod [path] [options]
11
+ *
12
+ * Options:
13
+ * --dry-run Show changes without modifying files
14
+ * --verbose Show detailed output
15
+ * --help Show help
16
+ *
17
+ * Examples:
18
+ * npx blok-codemod ./src
19
+ * npx blok-codemod ./src --dry-run
20
+ * npx blok-codemod .
21
+ */
22
+
23
+ const fs = require('fs');
24
+ const path = require('path');
25
+
26
+ // ============================================================================
27
+ // Configuration
28
+ // ============================================================================
29
+
30
+ const FILE_EXTENSIONS = ['.js', '.jsx', '.ts', '.tsx', '.vue', '.svelte', '.html', '.css', '.scss', '.less'];
31
+
32
+ // Import transformations
33
+ const IMPORT_TRANSFORMS = [
34
+ // Main EditorJS import
35
+ {
36
+ pattern: /from\s+['"]@editorjs\/editorjs['"]/g,
37
+ replacement: "from '@jackuait/blok'",
38
+ },
39
+ {
40
+ pattern: /require\s*\(\s*['"]@editorjs\/editorjs['"]\s*\)/g,
41
+ replacement: "require('@jackuait/blok')",
42
+ },
43
+ // Header tool (now bundled)
44
+ {
45
+ pattern: /import\s+(\w+)\s+from\s+['"]@editorjs\/header['"];?\n?/g,
46
+ replacement: '// Header is now bundled with Blok: use Blok.Header\n',
47
+ note: 'Header tool is now bundled',
48
+ },
49
+ // Paragraph tool (now bundled)
50
+ {
51
+ pattern: /import\s+(\w+)\s+from\s+['"]@editorjs\/paragraph['"];?\n?/g,
52
+ replacement: '// Paragraph is now bundled with Blok: use Blok.Paragraph\n',
53
+ note: 'Paragraph tool is now bundled',
54
+ },
55
+ ];
56
+
57
+ // Type import transformations (order matters - more specific patterns first)
58
+ const TYPE_TRANSFORMS = [
59
+ { pattern: /EditorJS\.EditorConfig/g, replacement: 'BlokConfig' },
60
+ { pattern: /EditorConfig/g, replacement: 'BlokConfig' },
61
+ ];
62
+
63
+ // Class name transformations
64
+ const CLASS_NAME_TRANSFORMS = [
65
+ // Constructor
66
+ { pattern: /new\s+EditorJS\s*\(/g, replacement: 'new Blok(' },
67
+ // Type annotations
68
+ { pattern: /:\s*EditorJS(?![a-zA-Z])/g, replacement: ': Blok' },
69
+ // Generic type parameters
70
+ { pattern: /<EditorJS>/g, replacement: '<Blok>' },
71
+ ];
72
+
73
+ // CSS class transformations
74
+ const CSS_CLASS_TRANSFORMS = [
75
+ // Editor wrapper classes
76
+ { pattern: /\.codex-editor(?![\w-])/g, replacement: '.blok-editor' },
77
+ { pattern: /\.codex-editor--narrow/g, replacement: '.blok-editor--narrow' },
78
+ { pattern: /\.codex-editor--rtl/g, replacement: '.blok-editor--rtl' },
79
+ // CE prefix classes (commonly used)
80
+ { pattern: /\.ce-block(?![\w-])/g, replacement: '[data-blok-testid="block-wrapper"]' },
81
+ { pattern: /\.ce-block--selected/g, replacement: '[data-blok-selected="true"]' },
82
+ { pattern: /\.ce-block--stretched/g, replacement: '[data-blok-stretched="true"]' },
83
+ { pattern: /\.ce-block__content/g, replacement: '[data-blok-testid="block-content"]' },
84
+ { pattern: /\.ce-toolbar(?![\w-])/g, replacement: '[data-blok-testid="toolbar"]' },
85
+ { pattern: /\.ce-toolbar__plus/g, replacement: '[data-blok-testid="plus-button"]' },
86
+ { pattern: /\.ce-toolbar__settings-btn/g, replacement: '[data-blok-testid="settings-toggler"]' },
87
+ { pattern: /\.ce-toolbar__actions/g, replacement: '[data-blok-testid="toolbar-actions"]' },
88
+ { pattern: /\.ce-inline-toolbar/g, replacement: '[data-blok-testid="inline-toolbar"]' },
89
+ { pattern: /\.ce-popover(?![\w-])/g, replacement: '[data-blok-testid="popover-container"]' },
90
+ { pattern: /\.ce-popover-item/g, replacement: '[data-blok-testid="popover-item"]' },
91
+ ];
92
+
93
+ // Data attribute transformations
94
+ const DATA_ATTRIBUTE_TRANSFORMS = [
95
+ // Core data attributes
96
+ { pattern: /data-id(?=["'\]=\s\]])/g, replacement: 'data-blok-id' },
97
+ { pattern: /data-item-name/g, replacement: 'data-blok-item-name' },
98
+ { pattern: /data-empty/g, replacement: 'data-blok-empty' },
99
+ // Cypress/test selectors
100
+ { pattern: /\[data-cy=["']?editorjs["']?\]/g, replacement: '[data-blok-testid="blok-editor"]' },
101
+ { pattern: /data-cy=["']?editorjs["']?/g, replacement: 'data-blok-testid="blok-editor"' },
102
+ ];
103
+
104
+ // Selector transformations for JavaScript/TypeScript query strings
105
+ const SELECTOR_TRANSFORMS = [
106
+ // querySelector patterns
107
+ { pattern: /\[data-id=/g, replacement: '[data-blok-id=' },
108
+ { pattern: /\[data-item-name=/g, replacement: '[data-blok-item-name=' },
109
+ { pattern: /\[data-empty\]/g, replacement: '[data-blok-empty]' },
110
+ { pattern: /\[data-empty=/g, replacement: '[data-blok-empty=' },
111
+ ];
112
+
113
+ // Default holder transformation
114
+ const HOLDER_TRANSFORMS = [
115
+ // HTML id attribute
116
+ { pattern: /id=["']editorjs["']/g, replacement: 'id="blok"' },
117
+ // JavaScript string literals for holder
118
+ { pattern: /holder:\s*['"]editorjs['"]/g, replacement: "holder: 'blok'" },
119
+ { pattern: /getElementById\s*\(\s*['"]editorjs['"]\s*\)/g, replacement: "getElementById('blok')" },
120
+ ];
121
+
122
+ // Tool configuration transformations
123
+ const TOOL_CONFIG_TRANSFORMS = [
124
+ // Handle class property syntax
125
+ { pattern: /class:\s*Header(?!Config)/g, replacement: 'class: Blok.Header' },
126
+ { pattern: /class:\s*Paragraph(?!Config)/g, replacement: 'class: Blok.Paragraph' },
127
+ // Handle standalone tool references in tools config (e.g., `paragraph: Paragraph`)
128
+ { pattern: /(\bheader\s*:\s*)Header(?!Config)(?=\s*[,}\n])/g, replacement: '$1Blok.Header' },
129
+ { pattern: /(\bparagraph\s*:\s*)Paragraph(?!Config)(?=\s*[,}\n])/g, replacement: '$1Blok.Paragraph' },
130
+ ];
131
+
132
+ // ============================================================================
133
+ // Utility Functions
134
+ // ============================================================================
135
+
136
+ function log(message, verbose = false) {
137
+ if (verbose || !global.isVerbose) {
138
+ console.log(message);
139
+ }
140
+ }
141
+
142
+ function logVerbose(message) {
143
+ if (global.isVerbose) {
144
+ console.log(` ${message}`);
145
+ }
146
+ }
147
+
148
+ function getAllFiles(dirPath, arrayOfFiles = []) {
149
+ const files = fs.readdirSync(dirPath);
150
+
151
+ files.forEach((file) => {
152
+ const fullPath = path.join(dirPath, file);
153
+
154
+ // Skip node_modules, dist, and hidden directories
155
+ if (file === 'node_modules' || file === 'dist' || file === 'build' || file.startsWith('.')) {
156
+ return;
157
+ }
158
+
159
+ if (fs.statSync(fullPath).isDirectory()) {
160
+ getAllFiles(fullPath, arrayOfFiles);
161
+ } else {
162
+ const ext = path.extname(file).toLowerCase();
163
+ if (FILE_EXTENSIONS.includes(ext)) {
164
+ arrayOfFiles.push(fullPath);
165
+ }
166
+ }
167
+ });
168
+
169
+ return arrayOfFiles;
170
+ }
171
+
172
+ function applyTransforms(content, transforms, fileName) {
173
+ let result = content;
174
+ const changes = [];
175
+
176
+ transforms.forEach(({ pattern, replacement, note }) => {
177
+ const matches = result.match(pattern);
178
+ if (matches) {
179
+ changes.push({
180
+ pattern: pattern.toString(),
181
+ count: matches.length,
182
+ note,
183
+ });
184
+ result = result.replace(pattern, replacement);
185
+ }
186
+ });
187
+
188
+ return { result, changes };
189
+ }
190
+
191
+ function transformFile(filePath, dryRun = false) {
192
+ const content = fs.readFileSync(filePath, 'utf8');
193
+ let transformed = content;
194
+ const allChanges = [];
195
+
196
+ const ext = path.extname(filePath).toLowerCase();
197
+ const isStyleFile = ['.css', '.scss', '.less'].includes(ext);
198
+ const isHtmlFile = ext === '.html';
199
+ const isJsFile = ['.js', '.jsx', '.ts', '.tsx', '.vue', '.svelte'].includes(ext);
200
+
201
+ // Apply import transforms (JS/TS only)
202
+ if (isJsFile) {
203
+ const { result, changes } = applyTransforms(transformed, IMPORT_TRANSFORMS, filePath);
204
+ transformed = result;
205
+ allChanges.push(...changes.map((c) => ({ ...c, category: 'imports' })));
206
+ }
207
+
208
+ // Apply type transforms (JS/TS only)
209
+ if (isJsFile) {
210
+ const { result, changes } = applyTransforms(transformed, TYPE_TRANSFORMS, filePath);
211
+ transformed = result;
212
+ allChanges.push(...changes.map((c) => ({ ...c, category: 'types' })));
213
+ }
214
+
215
+ // Apply class name transforms (JS/TS only)
216
+ if (isJsFile) {
217
+ const { result, changes } = applyTransforms(transformed, CLASS_NAME_TRANSFORMS, filePath);
218
+ transformed = result;
219
+ allChanges.push(...changes.map((c) => ({ ...c, category: 'class-names' })));
220
+ }
221
+
222
+ // Apply CSS class transforms (all files)
223
+ {
224
+ const { result, changes } = applyTransforms(transformed, CSS_CLASS_TRANSFORMS, filePath);
225
+ transformed = result;
226
+ allChanges.push(...changes.map((c) => ({ ...c, category: 'css-classes' })));
227
+ }
228
+
229
+ // Apply data attribute transforms (JS/TS/HTML)
230
+ if (isJsFile || isHtmlFile) {
231
+ const { result, changes } = applyTransforms(transformed, DATA_ATTRIBUTE_TRANSFORMS, filePath);
232
+ transformed = result;
233
+ allChanges.push(...changes.map((c) => ({ ...c, category: 'data-attributes' })));
234
+ }
235
+
236
+ // Apply selector transforms (JS/TS)
237
+ if (isJsFile) {
238
+ const { result, changes } = applyTransforms(transformed, SELECTOR_TRANSFORMS, filePath);
239
+ transformed = result;
240
+ allChanges.push(...changes.map((c) => ({ ...c, category: 'selectors' })));
241
+ }
242
+
243
+ // Apply holder transforms (JS/TS/HTML)
244
+ if (isJsFile || isHtmlFile) {
245
+ const { result, changes } = applyTransforms(transformed, HOLDER_TRANSFORMS, filePath);
246
+ transformed = result;
247
+ allChanges.push(...changes.map((c) => ({ ...c, category: 'holder' })));
248
+ }
249
+
250
+ // Apply tool config transforms (JS/TS only)
251
+ if (isJsFile) {
252
+ const { result, changes } = applyTransforms(transformed, TOOL_CONFIG_TRANSFORMS, filePath);
253
+ transformed = result;
254
+ allChanges.push(...changes.map((c) => ({ ...c, category: 'tool-config' })));
255
+ }
256
+
257
+ const hasChanges = transformed !== content;
258
+
259
+ if (hasChanges && !dryRun) {
260
+ fs.writeFileSync(filePath, transformed, 'utf8');
261
+ }
262
+
263
+ return {
264
+ filePath,
265
+ hasChanges,
266
+ changes: allChanges,
267
+ };
268
+ }
269
+
270
+ function updatePackageJson(packageJsonPath, dryRun = false) {
271
+ if (!fs.existsSync(packageJsonPath)) {
272
+ return { hasChanges: false, changes: [] };
273
+ }
274
+
275
+ const content = fs.readFileSync(packageJsonPath, 'utf8');
276
+ const pkg = JSON.parse(content);
277
+ const changes = [];
278
+
279
+ // Track dependencies to remove
280
+ const depsToRemove = ['@editorjs/editorjs', '@editorjs/header', '@editorjs/paragraph'];
281
+ const devDepsToRemove = [...depsToRemove];
282
+
283
+ // Check and update dependencies
284
+ if (pkg.dependencies) {
285
+ depsToRemove.forEach((dep) => {
286
+ if (pkg.dependencies[dep]) {
287
+ changes.push({ action: 'remove', type: 'dependencies', package: dep });
288
+ delete pkg.dependencies[dep];
289
+ }
290
+ });
291
+
292
+ // Add @jackuait/blok if not present
293
+ if (!pkg.dependencies['@jackuait/blok']) {
294
+ changes.push({ action: 'add', type: 'dependencies', package: '@jackuait/blok' });
295
+ pkg.dependencies['@jackuait/blok'] = 'latest';
296
+ }
297
+ }
298
+
299
+ // Check and update devDependencies
300
+ if (pkg.devDependencies) {
301
+ devDepsToRemove.forEach((dep) => {
302
+ if (pkg.devDependencies[dep]) {
303
+ changes.push({ action: 'remove', type: 'devDependencies', package: dep });
304
+ delete pkg.devDependencies[dep];
305
+ }
306
+ });
307
+ }
308
+
309
+ const hasChanges = changes.length > 0;
310
+
311
+ if (hasChanges && !dryRun) {
312
+ fs.writeFileSync(packageJsonPath, JSON.stringify(pkg, null, 2) + '\n', 'utf8');
313
+ }
314
+
315
+ return { hasChanges, changes };
316
+ }
317
+
318
+ // ============================================================================
319
+ // Main Execution
320
+ // ============================================================================
321
+
322
+ function printHelp() {
323
+ console.log(`
324
+ EditorJS to Blok Codemod
325
+
326
+ Usage:
327
+ npx blok-codemod [path] [options]
328
+
329
+ Arguments:
330
+ path Directory or file to transform (default: current directory)
331
+
332
+ Options:
333
+ --dry-run Show changes without modifying files
334
+ --verbose Show detailed output for each file
335
+ --help Show this help message
336
+
337
+ Examples:
338
+ npx blok-codemod ./src
339
+ npx blok-codemod ./src --dry-run
340
+ npx blok-codemod . --verbose
341
+
342
+ What this codemod does:
343
+ • Transforms EditorJS imports to Blok imports
344
+ • Updates type names (EditorConfig → BlokConfig)
345
+ • Replaces 'new EditorJS()' with 'new Blok()'
346
+ • Converts CSS selectors (.ce-* → [data-blok-*])
347
+ • Updates data attributes (data-id → data-blok-id)
348
+ • Changes default holder from 'editorjs' to 'blok'
349
+ • Updates package.json dependencies
350
+ • Converts bundled tool imports (Header, Paragraph)
351
+
352
+ Note: After running, you may need to manually:
353
+ • Update any custom tool implementations
354
+ • Review and test the changes
355
+ • Run 'npm install' or 'yarn' to update dependencies
356
+ `);
357
+ }
358
+
359
+ function main() {
360
+ const args = process.argv.slice(2);
361
+
362
+ // Parse arguments
363
+ const dryRun = args.includes('--dry-run');
364
+ const verbose = args.includes('--verbose');
365
+ const help = args.includes('--help') || args.includes('-h');
366
+
367
+ global.isVerbose = verbose;
368
+
369
+ if (help) {
370
+ printHelp();
371
+ process.exit(0);
372
+ }
373
+
374
+ // Get target path
375
+ const targetPath = args.find((arg) => !arg.startsWith('--')) || '.';
376
+ const absolutePath = path.resolve(targetPath);
377
+
378
+ if (!fs.existsSync(absolutePath)) {
379
+ console.error(`Error: Path does not exist: ${absolutePath}`);
380
+ process.exit(1);
381
+ }
382
+
383
+ console.log('\n🔄 EditorJS to Blok Migration\n');
384
+ console.log(`📁 Target: ${absolutePath}`);
385
+ console.log(`🔍 Mode: ${dryRun ? 'Dry run (no changes will be made)' : 'Live'}\n`);
386
+
387
+ const stats = {
388
+ filesScanned: 0,
389
+ filesModified: 0,
390
+ totalChanges: 0,
391
+ };
392
+
393
+ // Get all files to process
394
+ let files = [];
395
+ if (fs.statSync(absolutePath).isDirectory()) {
396
+ files = getAllFiles(absolutePath);
397
+ } else {
398
+ files = [absolutePath];
399
+ }
400
+
401
+ stats.filesScanned = files.length;
402
+
403
+ console.log(`📝 Scanning ${files.length} files...\n`);
404
+
405
+ // Process each file
406
+ files.forEach((filePath) => {
407
+ const result = transformFile(filePath, dryRun);
408
+
409
+ if (result.hasChanges) {
410
+ stats.filesModified++;
411
+ stats.totalChanges += result.changes.length;
412
+
413
+ const relativePath = path.relative(absolutePath, filePath);
414
+ console.log(`✏️ ${relativePath}`);
415
+
416
+ if (verbose) {
417
+ result.changes.forEach((change) => {
418
+ console.log(` - [${change.category}] ${change.count} occurrence(s)`);
419
+ if (change.note) {
420
+ console.log(` Note: ${change.note}`);
421
+ }
422
+ });
423
+ }
424
+ }
425
+ });
426
+
427
+ // Process package.json
428
+ const packageJsonPath = path.join(
429
+ fs.statSync(absolutePath).isDirectory() ? absolutePath : path.dirname(absolutePath),
430
+ 'package.json'
431
+ );
432
+
433
+ const pkgResult = updatePackageJson(packageJsonPath, dryRun);
434
+ if (pkgResult.hasChanges) {
435
+ console.log(`\n📦 package.json updates:`);
436
+ pkgResult.changes.forEach((change) => {
437
+ const symbol = change.action === 'add' ? '+' : '-';
438
+ console.log(` ${symbol} ${change.package} (${change.type})`);
439
+ });
440
+ }
441
+
442
+ // Print summary
443
+ console.log('\n' + '─'.repeat(50));
444
+ console.log('\n📊 Summary:\n');
445
+ console.log(` Files scanned: ${stats.filesScanned}`);
446
+ console.log(` Files modified: ${stats.filesModified}`);
447
+ console.log(` Total changes: ${stats.totalChanges}`);
448
+
449
+ if (dryRun) {
450
+ console.log('\n⚠️ Dry run complete. No files were modified.');
451
+ console.log(' Run without --dry-run to apply changes.\n');
452
+ } else {
453
+ console.log('\n✅ Migration complete!\n');
454
+ console.log('📋 Next steps:');
455
+ console.log(' 1. Run `npm install` or `yarn` to update dependencies');
456
+ console.log(' 2. Review the changes in your codebase');
457
+ console.log(' 3. Test your application thoroughly');
458
+ console.log(' 4. Check MIGRATION.md for any manual updates needed\n');
459
+ }
460
+ }
461
+
462
+ // Run if called directly
463
+ if (require.main === module) {
464
+ main();
465
+ }
466
+
467
+ // Export for testing
468
+ module.exports = {
469
+ transformFile,
470
+ updatePackageJson,
471
+ applyTransforms,
472
+ IMPORT_TRANSFORMS,
473
+ TYPE_TRANSFORMS,
474
+ CLASS_NAME_TRANSFORMS,
475
+ CSS_CLASS_TRANSFORMS,
476
+ DATA_ATTRIBUTE_TRANSFORMS,
477
+ SELECTOR_TRANSFORMS,
478
+ HOLDER_TRANSFORMS,
479
+ TOOL_CONFIG_TRANSFORMS,
480
+ };
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "@jackuait/blok-codemod",
3
+ "version": "1.0.0",
4
+ "description": "Codemod to migrate from EditorJS to Blok",
5
+ "main": "migrate-editorjs-to-blok.js",
6
+ "bin": {
7
+ "blok-codemod": "./migrate-editorjs-to-blok.js",
8
+ "migrate-editorjs-to-blok": "./migrate-editorjs-to-blok.js"
9
+ },
10
+ "keywords": [
11
+ "blok",
12
+ "editorjs",
13
+ "codemod",
14
+ "migration",
15
+ "editor",
16
+ "rich-text"
17
+ ],
18
+ "author": "Jack Uait",
19
+ "license": "Apache-2.0",
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "https://github.com/jackuait/blok.git",
23
+ "directory": "codemod"
24
+ },
25
+ "engines": {
26
+ "node": ">=14.0.0"
27
+ },
28
+ "files": [
29
+ "migrate-editorjs-to-blok.js",
30
+ "README.md"
31
+ ]
32
+ }