@jackuait/blok 0.3.1-beta.3 → 0.3.1-beta.7
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 +5 -2
- package/codemod/README.md +4 -4
- package/codemod/migrate-editorjs-to-blok.js +224 -25
- package/codemod/package.json +2 -3
- package/codemod/test.js +218 -4
- package/dist/{blok-DVGmVJt8.mjs → blok-BCT64ySF.mjs} +33 -4
- package/dist/blok.mjs +1 -1
- package/dist/blok.umd.js +2 -2
- package/dist/{index-6boN1DB4.mjs → index-4mwy4fr7.mjs} +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -44,10 +44,13 @@ Run the codemod to automatically update your codebase:
|
|
|
44
44
|
|
|
45
45
|
```bash
|
|
46
46
|
# Preview changes (recommended first)
|
|
47
|
-
npx
|
|
47
|
+
npx migrate-from-editorjs ./src --dry-run
|
|
48
48
|
|
|
49
49
|
# Apply changes
|
|
50
|
-
npx
|
|
50
|
+
npx migrate-from-editorjs ./src
|
|
51
|
+
|
|
52
|
+
# Process the entire project
|
|
53
|
+
npx migrate-from-editorjs .
|
|
51
54
|
```
|
|
52
55
|
|
|
53
56
|
The codemod handles:
|
package/codemod/README.md
CHANGED
|
@@ -8,16 +8,16 @@ Automatically migrate your codebase from EditorJS to Blok.
|
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
10
|
# Dry run (preview changes without modifying files)
|
|
11
|
-
npx
|
|
11
|
+
npx @jackuait/migrate-from-editorjs ./src --dry-run
|
|
12
12
|
|
|
13
13
|
# Apply changes
|
|
14
|
-
npx
|
|
14
|
+
npx @jackuait/migrate-from-editorjs ./src
|
|
15
15
|
|
|
16
16
|
# Process entire project
|
|
17
|
-
npx
|
|
17
|
+
npx @jackuait/migrate-from-editorjs .
|
|
18
18
|
|
|
19
19
|
# Verbose output
|
|
20
|
-
npx
|
|
20
|
+
npx @jackuait/migrate-from-editorjs ./src --verbose
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
## What It Does
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* It transforms imports, class names, selectors, data attributes, and text references.
|
|
8
8
|
*
|
|
9
9
|
* Usage:
|
|
10
|
-
* npx
|
|
10
|
+
* npx migrate-from-editorjs [path] [options]
|
|
11
11
|
*
|
|
12
12
|
* Options:
|
|
13
13
|
* --dry-run Show changes without modifying files
|
|
@@ -15,9 +15,9 @@
|
|
|
15
15
|
* --help Show help
|
|
16
16
|
*
|
|
17
17
|
* Examples:
|
|
18
|
-
* npx
|
|
19
|
-
* npx
|
|
20
|
-
* npx
|
|
18
|
+
* npx migrate-from-editorjs ./src
|
|
19
|
+
* npx migrate-from-editorjs ./src --dry-run
|
|
20
|
+
* npx migrate-from-editorjs .
|
|
21
21
|
*/
|
|
22
22
|
|
|
23
23
|
const fs = require('fs');
|
|
@@ -71,23 +71,99 @@ const CLASS_NAME_TRANSFORMS = [
|
|
|
71
71
|
];
|
|
72
72
|
|
|
73
73
|
// CSS class transformations
|
|
74
|
+
// Handles both with dot (.ce-block) and without dot (ce-block) patterns
|
|
74
75
|
const CSS_CLASS_TRANSFORMS = [
|
|
75
|
-
// Editor wrapper classes
|
|
76
|
-
{ pattern: /\.codex-
|
|
77
|
-
{ pattern: /\.codex-editor--narrow/g, replacement: '
|
|
78
|
-
{ pattern: /\.codex-editor--rtl/g, replacement: '
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
{ pattern:
|
|
82
|
-
{ pattern:
|
|
83
|
-
{ pattern:
|
|
84
|
-
{ pattern:
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
{ pattern: /\.ce-
|
|
88
|
-
{ pattern: /\.ce-
|
|
89
|
-
{ pattern: /\.ce-
|
|
90
|
-
{ pattern: /\.ce-
|
|
76
|
+
// Editor wrapper classes (codex-editor)
|
|
77
|
+
{ pattern: /\.codex-editor__redactor(?![\w-])/g, replacement: '[data-blok-redactor]' },
|
|
78
|
+
{ pattern: /\.codex-editor--narrow(?![\w-])/g, replacement: '[data-blok-narrow="true"]' },
|
|
79
|
+
{ pattern: /\.codex-editor--rtl(?![\w-])/g, replacement: '[data-blok-rtl="true"]' },
|
|
80
|
+
{ pattern: /\.codex-editor(?![\w-])/g, replacement: '[data-blok-editor]' },
|
|
81
|
+
// Without dot prefix (for string literals, classList operations)
|
|
82
|
+
{ pattern: /(['"`])codex-editor__redactor(['"`])/g, replacement: '$1data-blok-redactor$2' },
|
|
83
|
+
{ pattern: /(['"`])codex-editor--narrow(['"`])/g, replacement: '$1data-blok-narrow$2' },
|
|
84
|
+
{ pattern: /(['"`])codex-editor--rtl(['"`])/g, replacement: '$1data-blok-rtl$2' },
|
|
85
|
+
{ pattern: /(['"`])codex-editor(['"`])/g, replacement: '$1data-blok-editor$2' },
|
|
86
|
+
|
|
87
|
+
// Block classes (ce-block)
|
|
88
|
+
{ pattern: /\.ce-block--selected(?![\w-])/g, replacement: '[data-blok-selected="true"]' },
|
|
89
|
+
{ pattern: /\.ce-block--stretched(?![\w-])/g, replacement: '[data-blok-stretched="true"]' },
|
|
90
|
+
{ pattern: /\.ce-block--focused(?![\w-])/g, replacement: '[data-blok-focused="true"]' },
|
|
91
|
+
{ pattern: /\.ce-block__content(?![\w-])/g, replacement: '[data-blok-element-content]' },
|
|
92
|
+
{ pattern: /\.ce-block(?![\w-])/g, replacement: '[data-blok-element]' },
|
|
93
|
+
// Without dot prefix
|
|
94
|
+
{ pattern: /(['"`])ce-block--selected(['"`])/g, replacement: '$1data-blok-selected$2' },
|
|
95
|
+
{ pattern: /(['"`])ce-block--stretched(['"`])/g, replacement: '$1data-blok-stretched$2' },
|
|
96
|
+
{ pattern: /(['"`])ce-block--focused(['"`])/g, replacement: '$1data-blok-focused$2' },
|
|
97
|
+
{ pattern: /(['"`])ce-block__content(['"`])/g, replacement: '$1data-blok-element-content$2' },
|
|
98
|
+
{ pattern: /(['"`])ce-block(['"`])/g, replacement: '$1data-blok-element$2' },
|
|
99
|
+
|
|
100
|
+
// Toolbar classes (ce-toolbar)
|
|
101
|
+
{ pattern: /\.ce-toolbar__plus(?![\w-])/g, replacement: '[data-blok-testid="plus-button"]' },
|
|
102
|
+
{ pattern: /\.ce-toolbar__settings-btn(?![\w-])/g, replacement: '[data-blok-settings-toggler]' },
|
|
103
|
+
{ pattern: /\.ce-toolbar__actions(?![\w-])/g, replacement: '[data-blok-testid="toolbar-actions"]' },
|
|
104
|
+
{ pattern: /\.ce-toolbar(?![\w-])/g, replacement: '[data-blok-toolbar]' },
|
|
105
|
+
// Without dot prefix
|
|
106
|
+
{ pattern: /(['"`])ce-toolbar__plus(['"`])/g, replacement: '$1data-blok-testid="plus-button"$2' },
|
|
107
|
+
{ pattern: /(['"`])ce-toolbar__settings-btn(['"`])/g, replacement: '$1data-blok-settings-toggler$2' },
|
|
108
|
+
{ pattern: /(['"`])ce-toolbar__actions(['"`])/g, replacement: '$1data-blok-testid="toolbar-actions"$2' },
|
|
109
|
+
{ pattern: /(['"`])ce-toolbar(['"`])/g, replacement: '$1data-blok-toolbar$2' },
|
|
110
|
+
|
|
111
|
+
// Inline toolbar classes (ce-inline-toolbar, ce-inline-tool)
|
|
112
|
+
{ pattern: /\.ce-inline-tool--link(?![\w-])/g, replacement: '[data-blok-testid="inline-tool-link"]' },
|
|
113
|
+
{ pattern: /\.ce-inline-tool--bold(?![\w-])/g, replacement: '[data-blok-testid="inline-tool-bold"]' },
|
|
114
|
+
{ pattern: /\.ce-inline-tool--italic(?![\w-])/g, replacement: '[data-blok-testid="inline-tool-italic"]' },
|
|
115
|
+
{ pattern: /\.ce-inline-tool(?![\w-])/g, replacement: '[data-blok-testid="inline-tool"]' },
|
|
116
|
+
{ pattern: /\.ce-inline-toolbar(?![\w-])/g, replacement: '[data-blok-testid="inline-toolbar"]' },
|
|
117
|
+
// Without dot prefix
|
|
118
|
+
{ pattern: /(['"`])ce-inline-tool--link(['"`])/g, replacement: '$1data-blok-testid="inline-tool-link"$2' },
|
|
119
|
+
{ pattern: /(['"`])ce-inline-tool--bold(['"`])/g, replacement: '$1data-blok-testid="inline-tool-bold"$2' },
|
|
120
|
+
{ pattern: /(['"`])ce-inline-tool--italic(['"`])/g, replacement: '$1data-blok-testid="inline-tool-italic"$2' },
|
|
121
|
+
{ pattern: /(['"`])ce-inline-tool(['"`])/g, replacement: '$1data-blok-testid="inline-tool"$2' },
|
|
122
|
+
{ pattern: /(['"`])ce-inline-toolbar(['"`])/g, replacement: '$1data-blok-testid="inline-toolbar"$2' },
|
|
123
|
+
|
|
124
|
+
// Popover classes (ce-popover)
|
|
125
|
+
{ pattern: /\.ce-popover--opened(?![\w-])/g, replacement: '[data-blok-popover][data-blok-opened="true"]' },
|
|
126
|
+
{ pattern: /\.ce-popover__container(?![\w-])/g, replacement: '[data-blok-popover-container]' },
|
|
127
|
+
{ pattern: /\.ce-popover-item--focused(?![\w-])/g, replacement: '[data-blok-focused="true"]' },
|
|
128
|
+
{ pattern: /\.ce-popover-item(?![\w-])/g, replacement: '[data-blok-testid="popover-item"]' },
|
|
129
|
+
{ pattern: /\.ce-popover(?![\w-])/g, replacement: '[data-blok-popover]' },
|
|
130
|
+
// Without dot prefix
|
|
131
|
+
{ pattern: /(['"`])ce-popover--opened(['"`])/g, replacement: '$1data-blok-popover$2' },
|
|
132
|
+
{ pattern: /(['"`])ce-popover__container(['"`])/g, replacement: '$1data-blok-popover-container$2' },
|
|
133
|
+
{ pattern: /(['"`])ce-popover-item--focused(['"`])/g, replacement: '$1data-blok-focused$2' },
|
|
134
|
+
{ pattern: /(['"`])ce-popover-item(['"`])/g, replacement: '$1data-blok-testid="popover-item"$2' },
|
|
135
|
+
{ pattern: /(['"`])ce-popover(['"`])/g, replacement: '$1data-blok-popover$2' },
|
|
136
|
+
|
|
137
|
+
// Tool-specific classes (ce-paragraph, ce-header)
|
|
138
|
+
{ pattern: /\.ce-paragraph(?![\w-])/g, replacement: '[data-blok-tool="paragraph"]' },
|
|
139
|
+
{ pattern: /\.ce-header(?![\w-])/g, replacement: '[data-blok-tool="header"]' },
|
|
140
|
+
// Without dot prefix
|
|
141
|
+
{ pattern: /(['"`])ce-paragraph(['"`])/g, replacement: '$1data-blok-tool="paragraph"$2' },
|
|
142
|
+
{ pattern: /(['"`])ce-header(['"`])/g, replacement: '$1data-blok-tool="header"$2' },
|
|
143
|
+
|
|
144
|
+
// Conversion toolbar
|
|
145
|
+
{ pattern: /\.ce-conversion-toolbar(?![\w-])/g, replacement: '[data-blok-testid="conversion-toolbar"]' },
|
|
146
|
+
{ pattern: /\.ce-conversion-tool(?![\w-])/g, replacement: '[data-blok-testid="conversion-tool"]' },
|
|
147
|
+
{ pattern: /(['"`])ce-conversion-toolbar(['"`])/g, replacement: '$1data-blok-testid="conversion-toolbar"$2' },
|
|
148
|
+
{ pattern: /(['"`])ce-conversion-tool(['"`])/g, replacement: '$1data-blok-testid="conversion-tool"$2' },
|
|
149
|
+
|
|
150
|
+
// Settings and tune classes
|
|
151
|
+
{ pattern: /\.ce-settings(?![\w-])/g, replacement: '[data-blok-testid="block-settings"]' },
|
|
152
|
+
{ pattern: /\.ce-tune(?![\w-])/g, replacement: '[data-blok-testid="block-tune"]' },
|
|
153
|
+
{ pattern: /(['"`])ce-settings(['"`])/g, replacement: '$1data-blok-testid="block-settings"$2' },
|
|
154
|
+
{ pattern: /(['"`])ce-tune(['"`])/g, replacement: '$1data-blok-testid="block-tune"$2' },
|
|
155
|
+
|
|
156
|
+
// Stub block
|
|
157
|
+
{ pattern: /\.ce-stub(?![\w-])/g, replacement: '[data-blok-stub]' },
|
|
158
|
+
{ pattern: /(['"`])ce-stub(['"`])/g, replacement: '$1data-blok-stub$2' },
|
|
159
|
+
|
|
160
|
+
// Drag and drop
|
|
161
|
+
{ pattern: /\.ce-drag-handle(?![\w-])/g, replacement: '[data-blok-drag-handle]' },
|
|
162
|
+
{ pattern: /(['"`])ce-drag-handle(['"`])/g, replacement: '$1data-blok-drag-handle$2' },
|
|
163
|
+
|
|
164
|
+
// Additional state classes
|
|
165
|
+
{ pattern: /\.ce-ragged-right(?![\w-])/g, replacement: '[data-blok-ragged-right="true"]' },
|
|
166
|
+
{ pattern: /(['"`])ce-ragged-right(['"`])/g, replacement: '$1data-blok-ragged-right$2' },
|
|
91
167
|
];
|
|
92
168
|
|
|
93
169
|
// Data attribute transformations
|
|
@@ -119,6 +195,9 @@ const HOLDER_TRANSFORMS = [
|
|
|
119
195
|
{ pattern: /getElementById\s*\(\s*['"]editorjs['"]\s*\)/g, replacement: "getElementById('blok')" },
|
|
120
196
|
];
|
|
121
197
|
|
|
198
|
+
// Bundled tools - add new tools here as they are bundled with Blok
|
|
199
|
+
const BUNDLED_TOOLS = ['Header', 'Paragraph'];
|
|
200
|
+
|
|
122
201
|
// Tool configuration transformations
|
|
123
202
|
const TOOL_CONFIG_TRANSFORMS = [
|
|
124
203
|
// Handle class property syntax
|
|
@@ -196,6 +275,108 @@ function applyTransforms(content, transforms, fileName) {
|
|
|
196
275
|
return { result, changes };
|
|
197
276
|
}
|
|
198
277
|
|
|
278
|
+
/**
|
|
279
|
+
* Ensures that Blok is properly imported when bundled tools (Blok.Header, Blok.Paragraph, etc.) are used.
|
|
280
|
+
* This function checks if the content uses any Blok.* tool references and ensures there's a proper import.
|
|
281
|
+
*
|
|
282
|
+
* Handles the following scenarios:
|
|
283
|
+
* 1. No existing @jackuait/blok import -> adds `import Blok from '@jackuait/blok'`
|
|
284
|
+
* 2. Named imports only (e.g., `import { BlokConfig } from '@jackuait/blok'`) -> adds Blok default import
|
|
285
|
+
* 3. Default import with different name -> adds Blok to named imports
|
|
286
|
+
* 4. Already has Blok default import -> no changes needed
|
|
287
|
+
*/
|
|
288
|
+
function ensureBlokImport(content) {
|
|
289
|
+
// Check if content uses any Blok.* tool (e.g., Blok.Header, Blok.Paragraph)
|
|
290
|
+
const blokToolPattern = new RegExp(`Blok\\.(${BUNDLED_TOOLS.join('|')})`, 'g');
|
|
291
|
+
const usesBlokTools = blokToolPattern.test(content);
|
|
292
|
+
|
|
293
|
+
if (!usesBlokTools) {
|
|
294
|
+
return { result: content, changed: false };
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Check if Blok is already available as a default import
|
|
298
|
+
// Matches: import Blok from '@jackuait/blok' or import Blok, { ... } from '@jackuait/blok'
|
|
299
|
+
const hasBlokDefaultImport = /import\s+Blok\s*(?:,\s*\{[^}]*\}\s*)?from\s*['"]@jackuait\/blok['"]/.test(content);
|
|
300
|
+
|
|
301
|
+
if (hasBlokDefaultImport) {
|
|
302
|
+
return { result: content, changed: false };
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
// Check for existing @jackuait/blok import patterns
|
|
306
|
+
const namedOnlyImportPattern = /import\s*\{([^}]+)\}\s*from\s*['"]@jackuait\/blok['"];?/;
|
|
307
|
+
const defaultWithNamedPattern = /import\s+(\w+)\s*,\s*\{([^}]+)\}\s*from\s*['"]@jackuait\/blok['"];?/;
|
|
308
|
+
const defaultOnlyPattern = /import\s+(\w+)\s+from\s*['"]@jackuait\/blok['"];?/;
|
|
309
|
+
|
|
310
|
+
let result = content;
|
|
311
|
+
|
|
312
|
+
// Case 1: Named imports only -> add Blok default import
|
|
313
|
+
// e.g., `import { BlokConfig } from '@jackuait/blok'` -> `import Blok, { BlokConfig } from '@jackuait/blok'`
|
|
314
|
+
const namedOnlyMatch = content.match(namedOnlyImportPattern);
|
|
315
|
+
if (namedOnlyMatch) {
|
|
316
|
+
const namedImports = namedOnlyMatch[1];
|
|
317
|
+
result = content.replace(
|
|
318
|
+
namedOnlyImportPattern,
|
|
319
|
+
`import Blok, {${namedImports}} from '@jackuait/blok';`
|
|
320
|
+
);
|
|
321
|
+
return { result, changed: true };
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Case 2: Default import with different name + named imports -> add Blok to named imports
|
|
325
|
+
// e.g., `import Editor, { BlokConfig } from '@jackuait/blok'` -> `import Editor, { Blok, BlokConfig } from '@jackuait/blok'`
|
|
326
|
+
const defaultWithNamedMatch = content.match(defaultWithNamedPattern);
|
|
327
|
+
if (defaultWithNamedMatch) {
|
|
328
|
+
const defaultName = defaultWithNamedMatch[1];
|
|
329
|
+
const namedImports = defaultWithNamedMatch[2];
|
|
330
|
+
// Check if Blok is already in named imports
|
|
331
|
+
if (!/\bBlok\b/.test(namedImports)) {
|
|
332
|
+
result = content.replace(
|
|
333
|
+
defaultWithNamedPattern,
|
|
334
|
+
`import ${defaultName}, { Blok, ${namedImports.trim()} } from '@jackuait/blok';`
|
|
335
|
+
);
|
|
336
|
+
return { result, changed: true };
|
|
337
|
+
}
|
|
338
|
+
return { result: content, changed: false };
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// Case 3: Default import only with different name -> add Blok to named imports
|
|
342
|
+
// e.g., `import Editor from '@jackuait/blok'` -> `import Editor, { Blok } from '@jackuait/blok'`
|
|
343
|
+
const defaultOnlyMatch = content.match(defaultOnlyPattern);
|
|
344
|
+
if (defaultOnlyMatch) {
|
|
345
|
+
const defaultName = defaultOnlyMatch[1];
|
|
346
|
+
if (defaultName !== 'Blok') {
|
|
347
|
+
result = content.replace(
|
|
348
|
+
defaultOnlyPattern,
|
|
349
|
+
`import ${defaultName}, { Blok } from '@jackuait/blok';`
|
|
350
|
+
);
|
|
351
|
+
return { result, changed: true };
|
|
352
|
+
}
|
|
353
|
+
return { result: content, changed: false };
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// Case 4: No @jackuait/blok import at all -> add new import at the top (after any existing imports)
|
|
357
|
+
// Find the last import statement to insert after it
|
|
358
|
+
const importStatements = content.match(/^import\s+.+from\s+['"][^'"]+['"];?\s*$/gm);
|
|
359
|
+
if (importStatements && importStatements.length > 0) {
|
|
360
|
+
const lastImport = importStatements[importStatements.length - 1];
|
|
361
|
+
const lastImportIndex = content.lastIndexOf(lastImport);
|
|
362
|
+
const insertPosition = lastImportIndex + lastImport.length;
|
|
363
|
+
result =
|
|
364
|
+
content.slice(0, insertPosition) +
|
|
365
|
+
"\nimport Blok from '@jackuait/blok';" +
|
|
366
|
+
content.slice(insertPosition);
|
|
367
|
+
} else {
|
|
368
|
+
// No imports found, add at the very beginning (after shebang if present)
|
|
369
|
+
const shebangMatch = content.match(/^#!.*\n/);
|
|
370
|
+
if (shebangMatch) {
|
|
371
|
+
result = shebangMatch[0] + "import Blok from '@jackuait/blok';\n" + content.slice(shebangMatch[0].length);
|
|
372
|
+
} else {
|
|
373
|
+
result = "import Blok from '@jackuait/blok';\n" + content;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
return { result, changed: true };
|
|
378
|
+
}
|
|
379
|
+
|
|
199
380
|
function transformFile(filePath, dryRun = false) {
|
|
200
381
|
const content = fs.readFileSync(filePath, 'utf8');
|
|
201
382
|
let transformed = content;
|
|
@@ -262,6 +443,15 @@ function transformFile(filePath, dryRun = false) {
|
|
|
262
443
|
allChanges.push(...changes.map((c) => ({ ...c, category: 'tool-config' })));
|
|
263
444
|
}
|
|
264
445
|
|
|
446
|
+
// Ensure Blok is imported if bundled tools are used (JS/TS only)
|
|
447
|
+
if (isJsFile) {
|
|
448
|
+
const { result, changed } = ensureBlokImport(transformed);
|
|
449
|
+
if (changed) {
|
|
450
|
+
transformed = result;
|
|
451
|
+
allChanges.push({ category: 'imports', pattern: 'ensureBlokImport', count: 1, note: 'Added Blok import for bundled tools' });
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
265
455
|
// Apply text transforms (JS/TS/HTML) - replace "EditorJS" with "Blok"
|
|
266
456
|
if (isJsFile || isHtmlFile) {
|
|
267
457
|
const { result, changes } = applyTransforms(transformed, TEXT_TRANSFORMS, filePath);
|
|
@@ -339,7 +529,7 @@ function printHelp() {
|
|
|
339
529
|
EditorJS to Blok Codemod
|
|
340
530
|
|
|
341
531
|
Usage:
|
|
342
|
-
npx
|
|
532
|
+
npx migrate-from-editorjs [path] [options]
|
|
343
533
|
|
|
344
534
|
Arguments:
|
|
345
535
|
path Directory or file to transform (default: current directory)
|
|
@@ -350,19 +540,26 @@ Options:
|
|
|
350
540
|
--help Show this help message
|
|
351
541
|
|
|
352
542
|
Examples:
|
|
353
|
-
npx
|
|
354
|
-
npx
|
|
355
|
-
npx
|
|
543
|
+
npx migrate-from-editorjs ./src
|
|
544
|
+
npx migrate-from-editorjs ./src --dry-run
|
|
545
|
+
npx migrate-from-editorjs . --verbose
|
|
356
546
|
|
|
357
547
|
What this codemod does:
|
|
358
548
|
• Transforms EditorJS imports to Blok imports
|
|
359
549
|
• Updates type names (EditorConfig → BlokConfig)
|
|
360
550
|
• Replaces 'new EditorJS()' with 'new Blok()'
|
|
361
|
-
• Converts CSS selectors
|
|
551
|
+
• Converts CSS class selectors to data attributes:
|
|
552
|
+
- .codex-editor* → [data-blok-editor], [data-blok-redactor], etc.
|
|
553
|
+
- .ce-block* → [data-blok-element], [data-blok-selected], etc.
|
|
554
|
+
- .ce-toolbar* → [data-blok-toolbar], [data-blok-settings-toggler], etc.
|
|
555
|
+
- .ce-inline-toolbar, .ce-inline-tool* → [data-blok-testid="inline-*"]
|
|
556
|
+
- .ce-popover* → [data-blok-popover], [data-blok-popover-container], etc.
|
|
557
|
+
- .ce-paragraph, .ce-header → [data-blok-tool="paragraph|header"]
|
|
362
558
|
• Updates data attributes (data-id → data-blok-id)
|
|
363
559
|
• Changes default holder from 'editorjs' to 'blok'
|
|
364
560
|
• Updates package.json dependencies
|
|
365
561
|
• Converts bundled tool imports (Header, Paragraph)
|
|
562
|
+
• Ensures Blok is imported when using bundled tools (Blok.Header, etc.)
|
|
366
563
|
|
|
367
564
|
Note: After running, you may need to manually:
|
|
368
565
|
• Update any custom tool implementations
|
|
@@ -484,6 +681,8 @@ module.exports = {
|
|
|
484
681
|
transformFile,
|
|
485
682
|
updatePackageJson,
|
|
486
683
|
applyTransforms,
|
|
684
|
+
ensureBlokImport,
|
|
685
|
+
BUNDLED_TOOLS,
|
|
487
686
|
IMPORT_TRANSFORMS,
|
|
488
687
|
TYPE_TRANSFORMS,
|
|
489
688
|
CLASS_NAME_TRANSFORMS,
|
package/codemod/package.json
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
{
|
|
2
|
-
"name": "@jackuait/
|
|
2
|
+
"name": "@jackuait/migrate-from-editorjs",
|
|
3
3
|
"version": "1.0.0",
|
|
4
4
|
"description": "Codemod to migrate from EditorJS to Blok",
|
|
5
5
|
"main": "migrate-editorjs-to-blok.js",
|
|
6
6
|
"bin": {
|
|
7
|
-
"
|
|
8
|
-
"migrate-editorjs-to-blok": "./migrate-editorjs-to-blok.js"
|
|
7
|
+
"migrate-from-editorjs": "./migrate-editorjs-to-blok.js"
|
|
9
8
|
},
|
|
10
9
|
"keywords": [
|
|
11
10
|
"blok",
|
package/codemod/test.js
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
const {
|
|
6
6
|
applyTransforms,
|
|
7
|
+
ensureBlokImport,
|
|
8
|
+
BUNDLED_TOOLS,
|
|
7
9
|
IMPORT_TRANSFORMS,
|
|
8
10
|
TYPE_TRANSFORMS,
|
|
9
11
|
CLASS_NAME_TRANSFORMS,
|
|
@@ -113,19 +115,25 @@ console.log('\n🎨 CSS Class Transformations\n');
|
|
|
113
115
|
test('transforms .codex-editor class', () => {
|
|
114
116
|
const input = `.codex-editor { color: red; }`;
|
|
115
117
|
const { result } = applyTransforms(input, CSS_CLASS_TRANSFORMS);
|
|
116
|
-
assertEqual(result,
|
|
118
|
+
assertEqual(result, `[data-blok-editor] { color: red; }`);
|
|
117
119
|
});
|
|
118
120
|
|
|
119
121
|
test('transforms .codex-editor--narrow modifier', () => {
|
|
120
122
|
const input = `.codex-editor--narrow { width: 100%; }`;
|
|
121
123
|
const { result } = applyTransforms(input, CSS_CLASS_TRANSFORMS);
|
|
122
|
-
assertEqual(result,
|
|
124
|
+
assertEqual(result, `[data-blok-narrow="true"] { width: 100%; }`);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
test('transforms .codex-editor__redactor class', () => {
|
|
128
|
+
const input = `.codex-editor__redactor { padding: 20px; }`;
|
|
129
|
+
const { result } = applyTransforms(input, CSS_CLASS_TRANSFORMS);
|
|
130
|
+
assertEqual(result, `[data-blok-redactor] { padding: 20px; }`);
|
|
123
131
|
});
|
|
124
132
|
|
|
125
133
|
test('transforms .ce-block class', () => {
|
|
126
134
|
const input = `.ce-block { margin: 10px; }`;
|
|
127
135
|
const { result } = applyTransforms(input, CSS_CLASS_TRANSFORMS);
|
|
128
|
-
assertEqual(result, `[data-blok-
|
|
136
|
+
assertEqual(result, `[data-blok-element] { margin: 10px; }`);
|
|
129
137
|
});
|
|
130
138
|
|
|
131
139
|
test('transforms .ce-block--selected class', () => {
|
|
@@ -137,7 +145,7 @@ test('transforms .ce-block--selected class', () => {
|
|
|
137
145
|
test('transforms .ce-toolbar class', () => {
|
|
138
146
|
const input = `document.querySelector('.ce-toolbar')`;
|
|
139
147
|
const { result } = applyTransforms(input, CSS_CLASS_TRANSFORMS);
|
|
140
|
-
assertEqual(result, `document.querySelector('[data-blok-
|
|
148
|
+
assertEqual(result, `document.querySelector('[data-blok-toolbar]')`);
|
|
141
149
|
});
|
|
142
150
|
|
|
143
151
|
test('transforms .ce-inline-toolbar class', () => {
|
|
@@ -146,6 +154,66 @@ test('transforms .ce-inline-toolbar class', () => {
|
|
|
146
154
|
assertEqual(result, `[data-blok-testid="inline-toolbar"] { display: flex; }`);
|
|
147
155
|
});
|
|
148
156
|
|
|
157
|
+
test('transforms .ce-paragraph class', () => {
|
|
158
|
+
const input = `.ce-paragraph { line-height: 1.6; }`;
|
|
159
|
+
const { result } = applyTransforms(input, CSS_CLASS_TRANSFORMS);
|
|
160
|
+
assertEqual(result, `[data-blok-tool="paragraph"] { line-height: 1.6; }`);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
test('transforms .ce-header class', () => {
|
|
164
|
+
const input = `.ce-header { font-weight: bold; }`;
|
|
165
|
+
const { result } = applyTransforms(input, CSS_CLASS_TRANSFORMS);
|
|
166
|
+
assertEqual(result, `[data-blok-tool="header"] { font-weight: bold; }`);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
test('transforms .ce-inline-tool--link class', () => {
|
|
170
|
+
const input = `.ce-inline-tool--link { color: blue; }`;
|
|
171
|
+
const { result } = applyTransforms(input, CSS_CLASS_TRANSFORMS);
|
|
172
|
+
assertEqual(result, `[data-blok-testid="inline-tool-link"] { color: blue; }`);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
test('transforms .ce-inline-tool--bold class', () => {
|
|
176
|
+
const input = `.ce-inline-tool--bold { font-weight: bold; }`;
|
|
177
|
+
const { result } = applyTransforms(input, CSS_CLASS_TRANSFORMS);
|
|
178
|
+
assertEqual(result, `[data-blok-testid="inline-tool-bold"] { font-weight: bold; }`);
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
test('transforms .ce-inline-tool--italic class', () => {
|
|
182
|
+
const input = `.ce-inline-tool--italic { font-style: italic; }`;
|
|
183
|
+
const { result } = applyTransforms(input, CSS_CLASS_TRANSFORMS);
|
|
184
|
+
assertEqual(result, `[data-blok-testid="inline-tool-italic"] { font-style: italic; }`);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
test('transforms .ce-popover class', () => {
|
|
188
|
+
const input = `.ce-popover { position: absolute; }`;
|
|
189
|
+
const { result } = applyTransforms(input, CSS_CLASS_TRANSFORMS);
|
|
190
|
+
assertEqual(result, `[data-blok-popover] { position: absolute; }`);
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
test('transforms .ce-popover--opened class', () => {
|
|
194
|
+
const input = `.ce-popover--opened { display: block; }`;
|
|
195
|
+
const { result } = applyTransforms(input, CSS_CLASS_TRANSFORMS);
|
|
196
|
+
assertEqual(result, `[data-blok-popover][data-blok-opened="true"] { display: block; }`);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
test('transforms .ce-popover__container class', () => {
|
|
200
|
+
const input = `.ce-popover__container { overflow: hidden; }`;
|
|
201
|
+
const { result } = applyTransforms(input, CSS_CLASS_TRANSFORMS);
|
|
202
|
+
assertEqual(result, `[data-blok-popover-container] { overflow: hidden; }`);
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
test('transforms class names without dot prefix (string literals)', () => {
|
|
206
|
+
const input = `element.classList.add('ce-block');`;
|
|
207
|
+
const { result } = applyTransforms(input, CSS_CLASS_TRANSFORMS);
|
|
208
|
+
assertEqual(result, `element.classList.add('data-blok-element');`);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
test('transforms codex-editor in string literals', () => {
|
|
212
|
+
const input = `const wrapper = document.querySelector("codex-editor");`;
|
|
213
|
+
const { result } = applyTransforms(input, CSS_CLASS_TRANSFORMS);
|
|
214
|
+
assertEqual(result, `const wrapper = document.querySelector("data-blok-editor");`);
|
|
215
|
+
});
|
|
216
|
+
|
|
149
217
|
// ============================================================================
|
|
150
218
|
// Data Attribute Tests
|
|
151
219
|
// ============================================================================
|
|
@@ -291,6 +359,152 @@ test('does not transform unrelated EditorJS-like strings', () => {
|
|
|
291
359
|
assertEqual(result, input);
|
|
292
360
|
});
|
|
293
361
|
|
|
362
|
+
// ============================================================================
|
|
363
|
+
// Ensure Blok Import Tests
|
|
364
|
+
// ============================================================================
|
|
365
|
+
|
|
366
|
+
console.log('\n📥 Ensure Blok Import\n');
|
|
367
|
+
|
|
368
|
+
test('adds Blok import when using Blok.Header with no existing import', () => {
|
|
369
|
+
const input = `const editor = new Blok({
|
|
370
|
+
tools: { header: Blok.Header }
|
|
371
|
+
});`;
|
|
372
|
+
const { result, changed } = ensureBlokImport(input);
|
|
373
|
+
assertEqual(changed, true, 'Should indicate change');
|
|
374
|
+
assertEqual(result.includes("import Blok from '@jackuait/blok';"), true, 'Should add Blok import');
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
test('adds Blok import after existing imports', () => {
|
|
378
|
+
const input = `import React from 'react';
|
|
379
|
+
import { useState } from 'react';
|
|
380
|
+
|
|
381
|
+
const editor = new Blok({
|
|
382
|
+
tools: { header: Blok.Header }
|
|
383
|
+
});`;
|
|
384
|
+
const { result, changed } = ensureBlokImport(input);
|
|
385
|
+
assertEqual(changed, true, 'Should indicate change');
|
|
386
|
+
// Check that Blok import is added after existing imports
|
|
387
|
+
const blokImportIndex = result.indexOf("import Blok from '@jackuait/blok';");
|
|
388
|
+
const lastReactImportIndex = result.indexOf("import { useState } from 'react';");
|
|
389
|
+
assertEqual(blokImportIndex > lastReactImportIndex, true, 'Blok import should be after existing imports');
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
test('adds Blok to named-only import from @jackuait/blok', () => {
|
|
393
|
+
const input = `import { BlokConfig } from '@jackuait/blok';
|
|
394
|
+
|
|
395
|
+
const config: BlokConfig = {
|
|
396
|
+
tools: { header: Blok.Header }
|
|
397
|
+
};`;
|
|
398
|
+
const { result, changed } = ensureBlokImport(input);
|
|
399
|
+
assertEqual(changed, true, 'Should indicate change');
|
|
400
|
+
assertEqual(result.includes("import Blok, { BlokConfig } from '@jackuait/blok';"), true, 'Should add Blok default import');
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
test('adds Blok to named imports when default import has different name', () => {
|
|
404
|
+
const input = `import Editor, { BlokConfig } from '@jackuait/blok';
|
|
405
|
+
|
|
406
|
+
const config: BlokConfig = {
|
|
407
|
+
tools: { header: Blok.Header }
|
|
408
|
+
};`;
|
|
409
|
+
const { result, changed } = ensureBlokImport(input);
|
|
410
|
+
assertEqual(changed, true, 'Should indicate change');
|
|
411
|
+
assertEqual(result.includes("import Editor, { Blok, BlokConfig } from '@jackuait/blok';"), true, 'Should add Blok to named imports');
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
test('adds Blok as named import when default import has different name (no existing named imports)', () => {
|
|
415
|
+
const input = `import Editor from '@jackuait/blok';
|
|
416
|
+
|
|
417
|
+
const editor = new Editor({
|
|
418
|
+
tools: { header: Blok.Header }
|
|
419
|
+
});`;
|
|
420
|
+
const { result, changed } = ensureBlokImport(input);
|
|
421
|
+
assertEqual(changed, true, 'Should indicate change');
|
|
422
|
+
assertEqual(result.includes("import Editor, { Blok } from '@jackuait/blok';"), true, 'Should add Blok as named import');
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
test('does not modify when Blok is already default imported', () => {
|
|
426
|
+
const input = `import Blok from '@jackuait/blok';
|
|
427
|
+
|
|
428
|
+
const editor = new Blok({
|
|
429
|
+
tools: { header: Blok.Header }
|
|
430
|
+
});`;
|
|
431
|
+
const { result, changed } = ensureBlokImport(input);
|
|
432
|
+
assertEqual(changed, false, 'Should not indicate change');
|
|
433
|
+
assertEqual(result, input, 'Content should be unchanged');
|
|
434
|
+
});
|
|
435
|
+
|
|
436
|
+
test('does not modify when Blok is already default imported with named imports', () => {
|
|
437
|
+
const input = `import Blok, { BlokConfig } from '@jackuait/blok';
|
|
438
|
+
|
|
439
|
+
const editor = new Blok({
|
|
440
|
+
tools: { header: Blok.Header }
|
|
441
|
+
});`;
|
|
442
|
+
const { result, changed } = ensureBlokImport(input);
|
|
443
|
+
assertEqual(changed, false, 'Should not indicate change');
|
|
444
|
+
assertEqual(result, input, 'Content should be unchanged');
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
test('does not modify when no Blok tools are used', () => {
|
|
448
|
+
const input = `import { BlokConfig } from '@jackuait/blok';
|
|
449
|
+
|
|
450
|
+
const config: BlokConfig = {};`;
|
|
451
|
+
const { result, changed } = ensureBlokImport(input);
|
|
452
|
+
assertEqual(changed, false, 'Should not indicate change when no Blok.* tools used');
|
|
453
|
+
assertEqual(result, input, 'Content should be unchanged');
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
test('detects Blok.Paragraph usage', () => {
|
|
457
|
+
const input = `const editor = new Blok({
|
|
458
|
+
tools: { paragraph: Blok.Paragraph }
|
|
459
|
+
});`;
|
|
460
|
+
const { result, changed } = ensureBlokImport(input);
|
|
461
|
+
assertEqual(changed, true, 'Should detect Blok.Paragraph');
|
|
462
|
+
assertEqual(result.includes("import Blok from '@jackuait/blok';"), true, 'Should add Blok import');
|
|
463
|
+
});
|
|
464
|
+
|
|
465
|
+
test('handles multiple Blok tools usage', () => {
|
|
466
|
+
const input = `const editor = new Blok({
|
|
467
|
+
tools: {
|
|
468
|
+
header: Blok.Header,
|
|
469
|
+
paragraph: Blok.Paragraph
|
|
470
|
+
}
|
|
471
|
+
});`;
|
|
472
|
+
const { result, changed } = ensureBlokImport(input);
|
|
473
|
+
assertEqual(changed, true, 'Should detect multiple Blok tools');
|
|
474
|
+
assertEqual(result.includes("import Blok from '@jackuait/blok';"), true, 'Should add Blok import');
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
test('full migration adds Blok import for bundled tools', () => {
|
|
478
|
+
// This simulates a complete migration from EditorJS with bundled tools
|
|
479
|
+
const input = `import EditorJS from '@editorjs/editorjs';
|
|
480
|
+
import Header from '@editorjs/header';
|
|
481
|
+
import Paragraph from '@editorjs/paragraph';
|
|
482
|
+
|
|
483
|
+
const editor = new EditorJS({
|
|
484
|
+
holder: 'editorjs',
|
|
485
|
+
tools: {
|
|
486
|
+
header: {
|
|
487
|
+
class: Header,
|
|
488
|
+
},
|
|
489
|
+
paragraph: {
|
|
490
|
+
class: Paragraph,
|
|
491
|
+
},
|
|
492
|
+
},
|
|
493
|
+
});`;
|
|
494
|
+
|
|
495
|
+
let result = input;
|
|
496
|
+
result = applyTransforms(result, IMPORT_TRANSFORMS).result;
|
|
497
|
+
result = applyTransforms(result, CLASS_NAME_TRANSFORMS).result;
|
|
498
|
+
result = applyTransforms(result, HOLDER_TRANSFORMS).result;
|
|
499
|
+
result = applyTransforms(result, TOOL_CONFIG_TRANSFORMS).result;
|
|
500
|
+
result = ensureBlokImport(result).result;
|
|
501
|
+
|
|
502
|
+
// After transformation, should have Blok import (since original EditorJS import becomes @jackuait/blok)
|
|
503
|
+
assertEqual(result.includes("from '@jackuait/blok'"), true, 'Should have @jackuait/blok import');
|
|
504
|
+
assertEqual(result.includes('class: Blok.Header'), true, 'Should use Blok.Header');
|
|
505
|
+
assertEqual(result.includes('class: Blok.Paragraph'), true, 'Should use Blok.Paragraph');
|
|
506
|
+
});
|
|
507
|
+
|
|
294
508
|
// ============================================================================
|
|
295
509
|
// Summary
|
|
296
510
|
// ============================================================================
|