@procore/text-editor 0.0.1 → 0.0.3
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/CHANGELOG.md +19 -0
- package/codemod/__fixtures__/hammer.config.mjs +7 -0
- package/codemod/__fixtures__/procore.config.js +7 -0
- package/codemod/__fixtures__/procore.config.ternary.js +18 -0
- package/codemod/text-editor-migrate.js +383 -3
- package/codemod/text-editor-migrate.test.js +86 -0
- package/dist/{src/TextEditor → TextEditor}/TextEditor.d.ts +1 -1
- package/dist/TextEditor/TextEditor.js +2 -12
- package/dist/TextEditor/TextEditor.js.map +1 -1
- package/dist/TextEditor/TextEditor.styles.js +1 -1
- package/dist/{src/TextEditor → TextEditor}/TextEditor.types.d.ts +0 -38
- package/dist/TextEditor/TextEditor.types.js.map +1 -1
- package/dist/TextEditor/TextEditorProvider.js +1 -1
- package/dist/TextEditor/TextEditorProvider.js.map +1 -1
- package/dist/TextEditor/plugins/TabSpacesPlugin/TabSpacesPlugin.js +2 -0
- package/dist/TextEditor/plugins/TabSpacesPlugin/TabSpacesPlugin.js.map +1 -1
- package/dist/TextEditor/useTabAsNavigation.js +2 -1
- package/dist/TextEditor/useTabAsNavigation.js.map +1 -1
- package/dist/TextEditor/utils/config.js +5 -2
- package/dist/TextEditor/utils/config.js.map +1 -1
- package/dist/TextEditorOutput/TextEditorOutput.styles.js +1 -1
- package/dist/_typedoc/TextEditor/TextEditor.types.json +16 -68
- package/dist/_typedoc/TextEditor/TextEditorProvider.types.json +2 -2
- package/dist/_typedoc/TextEditorOutput/TextEditorOutput.types.json +3 -3
- package/dist/_typedoc/deprecations.json +1 -1
- package/package.json +12 -16
- package/dist/codemod/__fixtures__/src/components/ComplexEditor.d.ts +0 -2
- package/dist/codemod/__fixtures__/src/components/FormWithRichText.d.ts +0 -2
- package/dist/codemod/__fixtures__/src/components/MultilineFormRichText.d.ts +0 -2
- package/dist/codemod/__fixtures__/src/components/NoTextEditor.d.ts +0 -2
- package/dist/codemod/__fixtures__/src/components/ReadOnlyRichText.d.ts +0 -2
- package/dist/codemod/__fixtures__/src/components/SimpleEditor.d.ts +0 -2
- package/dist/codemod/__fixtures__/src/components/TypeImportEditor.d.ts +0 -2
- /package/dist/{src/TextEditor → TextEditor}/EditorError.d.ts +0 -0
- /package/dist/{src/TextEditor → TextEditor}/StickyToolbar/index.d.ts +0 -0
- /package/dist/{src/TextEditor → TextEditor}/StickyToolbar/useStickyToolbar.d.ts +0 -0
- /package/dist/{src/TextEditor → TextEditor}/StickyToolbar/useStickyToolbar.types.d.ts +0 -0
- /package/dist/{src/TextEditor → TextEditor}/TextEditor.styles.d.ts +0 -0
- /package/dist/{src/TextEditor → TextEditor}/TextEditorProvider.d.ts +0 -0
- /package/dist/{src/TextEditor → TextEditor}/TextEditorProvider.types.d.ts +0 -0
- /package/dist/{src/TextEditor → TextEditor}/index.d.ts +0 -0
- /package/dist/{src/TextEditor → TextEditor}/license_key.d.ts +0 -0
- /package/dist/{src/TextEditor → TextEditor}/plugins/CutPlugin/CutCommand.d.ts +0 -0
- /package/dist/{src/TextEditor → TextEditor}/plugins/CutPlugin/CutPlugin.d.ts +0 -0
- /package/dist/{src/TextEditor → TextEditor}/plugins/CutPlugin/index.d.ts +0 -0
- /package/dist/{src/TextEditor → TextEditor}/plugins/IndentPaddingToMarginPlugin/IndentPaddingToMarginPlugin.d.ts +0 -0
- /package/dist/{src/TextEditor → TextEditor}/plugins/IndentPaddingToMarginPlugin/index.d.ts +0 -0
- /package/dist/{src/TextEditor → TextEditor}/plugins/PasteAsTextPlugin/PasteAsTextCommand.d.ts +0 -0
- /package/dist/{src/TextEditor → TextEditor}/plugins/PasteAsTextPlugin/PasteAsTextPlugin.d.ts +0 -0
- /package/dist/{src/TextEditor → TextEditor}/plugins/PasteAsTextPlugin/index.d.ts +0 -0
- /package/dist/{src/TextEditor → TextEditor}/plugins/PastePlugin/PasteCommand.d.ts +0 -0
- /package/dist/{src/TextEditor → TextEditor}/plugins/PastePlugin/PastePlugin.d.ts +0 -0
- /package/dist/{src/TextEditor → TextEditor}/plugins/PastePlugin/index.d.ts +0 -0
- /package/dist/{src/TextEditor → TextEditor}/plugins/TabSpacesPlugin/TabSpacesPlugin.d.ts +0 -0
- /package/dist/{src/TextEditor → TextEditor}/plugins/TabSpacesPlugin/index.d.ts +0 -0
- /package/dist/{src/TextEditor → TextEditor}/textEditorTheming/icons.d.ts +0 -0
- /package/dist/{src/TextEditor → TextEditor}/textEditorTheming/index.d.ts +0 -0
- /package/dist/{src/TextEditor → TextEditor}/textEditorTheming/textEditorTheming.styles.d.ts +0 -0
- /package/dist/{src/TextEditor → TextEditor}/useCKEditorCss.d.ts +0 -0
- /package/dist/{src/TextEditor → TextEditor}/useTabAsNavigation.d.ts +0 -0
- /package/dist/{src/TextEditor → TextEditor}/utils/config.d.ts +0 -0
- /package/dist/{src/TextEditor → TextEditor}/utils/index.d.ts +0 -0
- /package/dist/{src/TextEditor → TextEditor}/utils/locale.d.ts +0 -0
- /package/dist/{src/TextEditor → TextEditor}/utils/plugins.d.ts +0 -0
- /package/dist/{src/TextEditorOutput → TextEditorOutput}/TextEditorOutput.d.ts +0 -0
- /package/dist/{src/TextEditorOutput → TextEditorOutput}/TextEditorOutput.styles.d.ts +0 -0
- /package/dist/{src/TextEditorOutput → TextEditorOutput}/TextEditorOutput.types.d.ts +0 -0
- /package/dist/{src/TextEditorOutput → TextEditorOutput}/TextEditorOutput.utils.d.ts +0 -0
- /package/dist/{src/TextEditorOutput → TextEditorOutput}/index.d.ts +0 -0
- /package/dist/{src/_storyHelpers → _storyHelpers}/constants.d.ts +0 -0
- /package/dist/{src/_utils → _utils}/propsTypedoc.d.ts +0 -0
- /package/dist/{src/index.d.ts → index.d.ts} +0 -0
- /package/dist/{src/stories → stories}/util.d.ts +0 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# @procore/text-editor
|
|
2
|
+
|
|
3
|
+
## 0.0.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 4656bce: Default provider TAB key as navigation to remove keyboard trap
|
|
8
|
+
|
|
9
|
+
## 0.0.2
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 7d01b54: Fix some types and remove legacy tinymce no-ops and focus.
|
|
14
|
+
|
|
15
|
+
## 0.0.1
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- f95beb2: Initial release of a separate TextEditor package, extracted from core-react.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
const { coreReactJestConfig } = require('@procore/core-react/jestConfig')
|
|
2
|
+
|
|
3
|
+
module.exports = ({ env }) => ({
|
|
4
|
+
app: {
|
|
5
|
+
webpackOverride(config) {
|
|
6
|
+
return config
|
|
7
|
+
},
|
|
8
|
+
moduleFederation: {
|
|
9
|
+
shared:
|
|
10
|
+
process.env.NODE_ENV === 'production'
|
|
11
|
+
? { react: deps.react, 'react-dom': deps['react-dom'] }
|
|
12
|
+
: {},
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
jestOverride: (defaultConfig) => {
|
|
16
|
+
return coreReactJestConfig(defaultConfig)
|
|
17
|
+
},
|
|
18
|
+
})
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* 1. Updates imports from @procore/core-react to @procore/text-editor for TextEditor components
|
|
9
9
|
* 2. Updates Form.RichText usage to include textEditorComponent prop
|
|
10
10
|
* 3. Updates Jest configuration to use textEditorJestConfig
|
|
11
|
+
* 4. Adds ckeditor5 singleton to module federation shared config
|
|
11
12
|
*/
|
|
12
13
|
|
|
13
14
|
const fs = require('fs')
|
|
@@ -28,9 +29,43 @@ const stats = {
|
|
|
28
29
|
importsUpdated: 0,
|
|
29
30
|
formRichTextUpdated: 0,
|
|
30
31
|
jestConfigUpdated: 0,
|
|
32
|
+
moduleFederationUpdated: 0,
|
|
31
33
|
errors: [],
|
|
32
34
|
}
|
|
33
35
|
|
|
36
|
+
/**
|
|
37
|
+
* Check if ckeditor5 is in package.json dependencies
|
|
38
|
+
*/
|
|
39
|
+
function checkForCkeditor5(targetPath) {
|
|
40
|
+
let currentDir = targetPath
|
|
41
|
+
|
|
42
|
+
// If targetPath is a file, start from its directory
|
|
43
|
+
if (fs.existsSync(targetPath) && fs.statSync(targetPath).isFile()) {
|
|
44
|
+
currentDir = path.dirname(targetPath)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Walk up the directory tree to find package.json
|
|
48
|
+
while (currentDir !== path.dirname(currentDir)) {
|
|
49
|
+
const packageJsonPath = path.join(currentDir, 'package.json')
|
|
50
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
51
|
+
try {
|
|
52
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'))
|
|
53
|
+
const deps = {
|
|
54
|
+
...packageJson.dependencies,
|
|
55
|
+
...packageJson.devDependencies,
|
|
56
|
+
...packageJson.peerDependencies,
|
|
57
|
+
}
|
|
58
|
+
return !!deps['ckeditor5']
|
|
59
|
+
} catch (error) {
|
|
60
|
+
// Ignore parse errors, continue searching
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
currentDir = path.dirname(currentDir)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return false
|
|
67
|
+
}
|
|
68
|
+
|
|
34
69
|
// TextEditor-related exports that should be migrated
|
|
35
70
|
const TEXT_EDITOR_EXPORTS = [
|
|
36
71
|
'TextEditor',
|
|
@@ -254,6 +289,312 @@ function transformFormRichText(fileContent) {
|
|
|
254
289
|
return { content: newContent, modified }
|
|
255
290
|
}
|
|
256
291
|
|
|
292
|
+
/**
|
|
293
|
+
* Add ckeditor5 singleton configuration to a shared object
|
|
294
|
+
* Returns the modified content and whether it was modified
|
|
295
|
+
*/
|
|
296
|
+
function addSingletonToSharedObject(content, sharedMatch) {
|
|
297
|
+
const insertPosition = sharedMatch.index + sharedMatch[0].length
|
|
298
|
+
|
|
299
|
+
// Detect indentation by looking at the content after `shared: {` or similar
|
|
300
|
+
// Find the first property inside shared to determine proper indentation
|
|
301
|
+
const afterShared = content.substring(insertPosition)
|
|
302
|
+
const firstPropertyMatch = afterShared.match(/\n(\s+)\w+\s*:/)
|
|
303
|
+
|
|
304
|
+
let propertyIndent = ''
|
|
305
|
+
let indentUnit = ' ' // default to 2 spaces
|
|
306
|
+
|
|
307
|
+
if (firstPropertyMatch) {
|
|
308
|
+
// Use the same indentation as the first property inside shared
|
|
309
|
+
propertyIndent = firstPropertyMatch[1]
|
|
310
|
+
// Detect indent unit from the property indent
|
|
311
|
+
const sharedLineMatch = content
|
|
312
|
+
.substring(0, sharedMatch.index)
|
|
313
|
+
.match(/\n(\s+)(?:shared|\?)/)
|
|
314
|
+
if (sharedLineMatch) {
|
|
315
|
+
const sharedIndent = sharedLineMatch[1]
|
|
316
|
+
// indentUnit is the difference between property indent and shared indent
|
|
317
|
+
if (propertyIndent.length > sharedIndent.length) {
|
|
318
|
+
indentUnit = propertyIndent.substring(sharedIndent.length)
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
} else {
|
|
322
|
+
// Fallback: detect from the 'shared' line indentation
|
|
323
|
+
const beforeMatch = content.substring(0, sharedMatch.index)
|
|
324
|
+
const lastNewlineIndex = beforeMatch.lastIndexOf('\n')
|
|
325
|
+
const lineStart =
|
|
326
|
+
lastNewlineIndex >= 0
|
|
327
|
+
? beforeMatch.substring(lastNewlineIndex + 1)
|
|
328
|
+
: beforeMatch
|
|
329
|
+
const baseIndentMatch = lineStart.match(/^(\s*)/)
|
|
330
|
+
const baseIndent = baseIndentMatch ? baseIndentMatch[1] : ''
|
|
331
|
+
|
|
332
|
+
// Determine if we're using tabs or spaces
|
|
333
|
+
const useTabs = baseIndent.includes('\t')
|
|
334
|
+
indentUnit = useTabs ? '\t' : ' '
|
|
335
|
+
propertyIndent = baseIndent + indentUnit
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// Build the ckeditor5 configuration
|
|
339
|
+
const ckeditor5Config = `
|
|
340
|
+
${propertyIndent}ckeditor5: {
|
|
341
|
+
${propertyIndent}${indentUnit}requiredVersion: deps['ckeditor5'],
|
|
342
|
+
${propertyIndent}${indentUnit}singleton: true,
|
|
343
|
+
${propertyIndent}},`
|
|
344
|
+
|
|
345
|
+
// Insert the ckeditor5 configuration after the opening brace
|
|
346
|
+
return (
|
|
347
|
+
content.substring(0, insertPosition) +
|
|
348
|
+
ckeditor5Config +
|
|
349
|
+
content.substring(insertPosition)
|
|
350
|
+
)
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Add deps import/require statement to the file if not already present
|
|
355
|
+
*/
|
|
356
|
+
function addDepsImport(content, filePath) {
|
|
357
|
+
// Check if deps variable is already declared
|
|
358
|
+
if (
|
|
359
|
+
content.includes("require('./package.json')") ||
|
|
360
|
+
content.includes("require('./package.json').dependencies") ||
|
|
361
|
+
content.includes("from './package.json'")
|
|
362
|
+
) {
|
|
363
|
+
return content
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// Determine if using CommonJS or ES modules
|
|
367
|
+
const isESModule =
|
|
368
|
+
content.includes('export default') ||
|
|
369
|
+
content.includes('export {') ||
|
|
370
|
+
filePath.endsWith('.mjs')
|
|
371
|
+
|
|
372
|
+
if (isESModule) {
|
|
373
|
+
// For ES modules, add import at the top
|
|
374
|
+
const importStatement =
|
|
375
|
+
"import deps from './package.json' with { type: 'json' }\n"
|
|
376
|
+
|
|
377
|
+
// Find the first import or export statement
|
|
378
|
+
const firstImportMatch = content.match(/^(import|export)/m)
|
|
379
|
+
if (firstImportMatch) {
|
|
380
|
+
const insertPos = content.indexOf(firstImportMatch[0])
|
|
381
|
+
return (
|
|
382
|
+
content.substring(0, insertPos) +
|
|
383
|
+
importStatement +
|
|
384
|
+
content.substring(insertPos)
|
|
385
|
+
)
|
|
386
|
+
} else {
|
|
387
|
+
return importStatement + content
|
|
388
|
+
}
|
|
389
|
+
} else {
|
|
390
|
+
// For CommonJS, add require at the top
|
|
391
|
+
const requireStatement =
|
|
392
|
+
"const deps = require('./package.json').dependencies\n"
|
|
393
|
+
|
|
394
|
+
// Find the first require or const statement
|
|
395
|
+
const firstRequireMatch = content.match(/^(const|let|var|require)/m)
|
|
396
|
+
if (firstRequireMatch) {
|
|
397
|
+
const insertPos = content.indexOf(firstRequireMatch[0])
|
|
398
|
+
return (
|
|
399
|
+
content.substring(0, insertPos) +
|
|
400
|
+
requireStatement +
|
|
401
|
+
content.substring(insertPos)
|
|
402
|
+
)
|
|
403
|
+
} else {
|
|
404
|
+
return requireStatement + content
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Find the matching closing brace for an opening brace at the given position
|
|
411
|
+
*/
|
|
412
|
+
function findMatchingBrace(content, openBracePos) {
|
|
413
|
+
let depth = 1
|
|
414
|
+
let pos = openBracePos + 1
|
|
415
|
+
|
|
416
|
+
while (pos < content.length && depth > 0) {
|
|
417
|
+
const char = content[pos]
|
|
418
|
+
if (char === '{') {
|
|
419
|
+
depth++
|
|
420
|
+
} else if (char === '}') {
|
|
421
|
+
depth--
|
|
422
|
+
}
|
|
423
|
+
pos++
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
return depth === 0 ? pos - 1 : -1
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Check if an object block contains a 'react' key
|
|
431
|
+
*/
|
|
432
|
+
function objectContainsReact(content, openBracePos) {
|
|
433
|
+
const closeBracePos = findMatchingBrace(content, openBracePos)
|
|
434
|
+
if (closeBracePos === -1) return false
|
|
435
|
+
|
|
436
|
+
const objectContent = content.substring(openBracePos, closeBracePos + 1)
|
|
437
|
+
// Check for react as a key (handles react:, 'react':, "react":)
|
|
438
|
+
return /(?:^|[{,\s])react\s*:|['"]react['"]\s*:/.test(objectContent)
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* Transform module federation with ternary conditional pattern
|
|
443
|
+
* Handles patterns like:
|
|
444
|
+
* shared: process.env.NODE_ENV === 'production'
|
|
445
|
+
* ? { react: deps.react, 'react-dom': deps['react-dom'] }
|
|
446
|
+
* : {},
|
|
447
|
+
* Only adds ckeditor5 to branches that contain 'react'
|
|
448
|
+
*/
|
|
449
|
+
function transformModuleFederationTernary(fileContent, filePath) {
|
|
450
|
+
let newContent = fileContent
|
|
451
|
+
|
|
452
|
+
// Pattern to match ternary conditional for shared - find the opening brace of the consequent
|
|
453
|
+
// Matches: shared: <condition> ? {
|
|
454
|
+
const ternaryRegex =
|
|
455
|
+
/shared\s*:\s*[\w.]+\s*===?\s*['"][^'"]+['"]\s*\?\s*(\{)/g
|
|
456
|
+
|
|
457
|
+
let match
|
|
458
|
+
let offset = 0
|
|
459
|
+
|
|
460
|
+
// Find all ternary patterns and process the consequent (true branch)
|
|
461
|
+
while ((match = ternaryRegex.exec(fileContent)) !== null) {
|
|
462
|
+
const adjustedIndex = match.index + offset
|
|
463
|
+
const bracePos = adjustedIndex + match[0].length - 1 // Position of the opening brace
|
|
464
|
+
|
|
465
|
+
// Only add ckeditor5 if this branch contains 'react'
|
|
466
|
+
if (!objectContainsReact(newContent, bracePos)) {
|
|
467
|
+
continue
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
const adjustedMatch = {
|
|
471
|
+
...match,
|
|
472
|
+
index: bracePos,
|
|
473
|
+
0: '{',
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
const beforeLength = newContent.length
|
|
477
|
+
newContent = addSingletonToSharedObject(newContent, adjustedMatch)
|
|
478
|
+
offset += newContent.length - beforeLength
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// Now find and process the alternate (false branch) - the : { ... } part
|
|
482
|
+
// We need to find the consequent's closing brace first, then find the alternate
|
|
483
|
+
const ternaryRegex2 = /shared\s*:\s*[\w.]+\s*===?\s*['"][^'"]+['"]\s*\?\s*\{/g
|
|
484
|
+
|
|
485
|
+
let match2
|
|
486
|
+
while ((match2 = ternaryRegex2.exec(newContent)) !== null) {
|
|
487
|
+
// Find the opening brace of the consequent
|
|
488
|
+
const consequentOpenBrace = match2.index + match2[0].length - 1
|
|
489
|
+
|
|
490
|
+
// Find the matching closing brace
|
|
491
|
+
const consequentCloseBrace = findMatchingBrace(
|
|
492
|
+
newContent,
|
|
493
|
+
consequentOpenBrace
|
|
494
|
+
)
|
|
495
|
+
|
|
496
|
+
if (consequentCloseBrace === -1) continue
|
|
497
|
+
|
|
498
|
+
// Find the alternate branch - look for : { after the closing brace
|
|
499
|
+
const afterConsequent = newContent.substring(consequentCloseBrace + 1)
|
|
500
|
+
const alternateMatch = afterConsequent.match(/^\s*:\s*(\{)/)
|
|
501
|
+
|
|
502
|
+
if (alternateMatch) {
|
|
503
|
+
const alternateBracePos =
|
|
504
|
+
consequentCloseBrace +
|
|
505
|
+
1 +
|
|
506
|
+
alternateMatch.index +
|
|
507
|
+
alternateMatch[0].length -
|
|
508
|
+
1
|
|
509
|
+
|
|
510
|
+
// Only add ckeditor5 if this branch contains 'react'
|
|
511
|
+
if (!objectContainsReact(newContent, alternateBracePos)) {
|
|
512
|
+
continue
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
const adjustedMatch = {
|
|
516
|
+
index: alternateBracePos,
|
|
517
|
+
0: '{',
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
newContent = addSingletonToSharedObject(newContent, adjustedMatch)
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// Add deps import if needed only if we made changes
|
|
525
|
+
if (newContent !== fileContent) {
|
|
526
|
+
newContent = addDepsImport(newContent, filePath)
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
const modified = newContent !== fileContent
|
|
530
|
+
if (modified) {
|
|
531
|
+
stats.moduleFederationUpdated++
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
return { content: newContent, modified }
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
/**
|
|
538
|
+
* Transform module federation configuration to add ckeditor5 singleton
|
|
539
|
+
* Handles both hammer.config.* and procore.config.* files
|
|
540
|
+
* Supports both direct object pattern and ternary conditional pattern
|
|
541
|
+
*/
|
|
542
|
+
function transformModuleFederation(fileContent, filePath) {
|
|
543
|
+
let modified = false
|
|
544
|
+
let newContent = fileContent
|
|
545
|
+
const fileName = path.basename(filePath)
|
|
546
|
+
|
|
547
|
+
// Only process hammer.config.* or procore.config.* files
|
|
548
|
+
if (
|
|
549
|
+
!fileName.startsWith('hammer.config') &&
|
|
550
|
+
!fileName.startsWith('procore.config')
|
|
551
|
+
) {
|
|
552
|
+
return { content: newContent, modified }
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
// Check if moduleFederation.shared exists in the file
|
|
556
|
+
if (!newContent.includes('moduleFederation')) {
|
|
557
|
+
return { content: newContent, modified }
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
// Check if shared key exists
|
|
561
|
+
if (!newContent.includes('shared')) {
|
|
562
|
+
return { content: newContent, modified }
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
// Check if ckeditor5 is already configured
|
|
566
|
+
if (newContent.includes('ckeditor5')) {
|
|
567
|
+
return { content: newContent, modified }
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
// Check if this is a ternary conditional pattern
|
|
571
|
+
const ternaryPattern = /shared\s*:\s*[\w.]+\s*===?\s*['"][^'"]+['"]\s*\?/
|
|
572
|
+
if (ternaryPattern.test(newContent)) {
|
|
573
|
+
return transformModuleFederationTernary(newContent, filePath)
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
// Pattern to match shared object in moduleFederation
|
|
577
|
+
// Handles both `shared: {` and `shared:{` patterns
|
|
578
|
+
const sharedObjectRegex = /(shared\s*:\s*\{)/g
|
|
579
|
+
|
|
580
|
+
if (sharedObjectRegex.test(newContent)) {
|
|
581
|
+
// Reset regex lastIndex
|
|
582
|
+
sharedObjectRegex.lastIndex = 0
|
|
583
|
+
|
|
584
|
+
// Find the match and its position
|
|
585
|
+
const match = sharedObjectRegex.exec(newContent)
|
|
586
|
+
if (match) {
|
|
587
|
+
newContent = addSingletonToSharedObject(newContent, match)
|
|
588
|
+
newContent = addDepsImport(newContent, filePath)
|
|
589
|
+
|
|
590
|
+
modified = true
|
|
591
|
+
stats.moduleFederationUpdated++
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
return { content: newContent, modified }
|
|
596
|
+
}
|
|
597
|
+
|
|
257
598
|
/**
|
|
258
599
|
* Transform Jest configuration files
|
|
259
600
|
*/
|
|
@@ -315,7 +656,8 @@ function processFile(filePath) {
|
|
|
315
656
|
// Config file detection
|
|
316
657
|
const isConfigFile =
|
|
317
658
|
/^jest\.(config|setup)\.(js|ts|cjs|mjs)$/.test(fileName) ||
|
|
318
|
-
/\.config\.(js|ts|cjs|mjs)$/.test(fileName)
|
|
659
|
+
/\.config\.(js|ts|cjs|mjs)$/.test(fileName) ||
|
|
660
|
+
/\.config\..*\.(js|ts|cjs|mjs)$/.test(fileName) // Last one for tests like "procore.config.ternay.js"
|
|
319
661
|
|
|
320
662
|
// Process Jest configuration
|
|
321
663
|
if (isConfigFile && content.includes('coreReactJestConfig')) {
|
|
@@ -326,6 +668,15 @@ function processFile(filePath) {
|
|
|
326
668
|
}
|
|
327
669
|
}
|
|
328
670
|
|
|
671
|
+
// Process module federation configuration for hammer.config.* and procore.config.* files
|
|
672
|
+
if (isConfigFile) {
|
|
673
|
+
const modFedResult = transformModuleFederation(newContent, filePath)
|
|
674
|
+
if (modFedResult.modified) {
|
|
675
|
+
newContent = modFedResult.content
|
|
676
|
+
wasModified = true
|
|
677
|
+
}
|
|
678
|
+
}
|
|
679
|
+
|
|
329
680
|
// For regular JS/TS files, process imports and Form.RichText
|
|
330
681
|
if (['.js', '.jsx', '.ts', '.tsx'].includes(ext) && !isConfigFile) {
|
|
331
682
|
// Transform imports
|
|
@@ -420,6 +771,7 @@ ${colors.bold}Transformations:${colors.reset}
|
|
|
420
771
|
• Updates imports from @procore/core-react to @procore/text-editor
|
|
421
772
|
• Adds textEditorComponent prop to Form.RichText
|
|
422
773
|
• Updates Jest configuration to use textEditorJestConfig
|
|
774
|
+
• Adds ckeditor5 singleton to module federation shared config
|
|
423
775
|
|
|
424
776
|
${colors.bold}Examples:${colors.reset}
|
|
425
777
|
|
|
@@ -433,6 +785,24 @@ ${colors.bold}Examples:${colors.reset}
|
|
|
433
785
|
// Default to current directory if no path provided
|
|
434
786
|
const targetPath = path.resolve(args[0] || '.')
|
|
435
787
|
|
|
788
|
+
// Check if ckeditor5 is installed before running
|
|
789
|
+
if (!checkForCkeditor5(targetPath)) {
|
|
790
|
+
console.log(
|
|
791
|
+
`\n${colors.info}ℹ${colors.reset} ckeditor5 not found in package.json\n`
|
|
792
|
+
)
|
|
793
|
+
console.log(`Are you sure you need a text-editor?\n`)
|
|
794
|
+
console.log(
|
|
795
|
+
` To use TextEditor components, you need to install ckeditor5 first:\n`
|
|
796
|
+
)
|
|
797
|
+
console.log(` ${colors.info}For libraries:${colors.reset}`)
|
|
798
|
+
console.log(` yarn add ckeditor5@^47 --peer --dev\n`)
|
|
799
|
+
console.log(` ${colors.info}For apps:${colors.reset}`)
|
|
800
|
+
console.log(` yarn add ckeditor5@^47\n`)
|
|
801
|
+
console.log(` After installing, re-run this codemod.\n`)
|
|
802
|
+
console.log(` See README_MIGRATION.md for more details.\n`)
|
|
803
|
+
return
|
|
804
|
+
}
|
|
805
|
+
|
|
436
806
|
console.log(
|
|
437
807
|
`\n${colors.bold}Starting TextEditor migration...${colors.reset}\n`
|
|
438
808
|
)
|
|
@@ -488,6 +858,9 @@ ${colors.bold}Examples:${colors.reset}
|
|
|
488
858
|
console.log(
|
|
489
859
|
`${colors.success}✓${colors.reset} Jest configs updated: ${stats.jestConfigUpdated}`
|
|
490
860
|
)
|
|
861
|
+
console.log(
|
|
862
|
+
`${colors.success}✓${colors.reset} Module federation configs updated: ${stats.moduleFederationUpdated}`
|
|
863
|
+
)
|
|
491
864
|
|
|
492
865
|
if (stats.errors.length > 0) {
|
|
493
866
|
console.log(`\n${colors.error}Errors encountered:${colors.reset}`)
|
|
@@ -500,8 +873,15 @@ ${colors.bold}Examples:${colors.reset}
|
|
|
500
873
|
|
|
501
874
|
if (stats.filesProcessed > 0) {
|
|
502
875
|
console.log(`${colors.bold}Next steps:${colors.reset}\n`)
|
|
503
|
-
console.log(
|
|
504
|
-
|
|
876
|
+
console.log(
|
|
877
|
+
'1. Review the changes made by the codemod. May need to be linted.\n'
|
|
878
|
+
)
|
|
879
|
+
console.log(
|
|
880
|
+
'2. Review previous implementation for additional props and migrate to CKEditor, https://ckeditor.com/docs/ckeditor5/latest/features/index.html\n'
|
|
881
|
+
)
|
|
882
|
+
console.log(
|
|
883
|
+
'3. Run your tests to ensure everything works correctly and test a deploy preview.\n'
|
|
884
|
+
)
|
|
505
885
|
}
|
|
506
886
|
}
|
|
507
887
|
|
|
@@ -169,6 +169,92 @@ describe('text-editor-migrate codemod', () => {
|
|
|
169
169
|
})
|
|
170
170
|
})
|
|
171
171
|
|
|
172
|
+
describe('module federation transformations', () => {
|
|
173
|
+
it('should add ckeditor5 to moduleFederation.shared in hammer.config.mjs', () => {
|
|
174
|
+
execFileSync('node', [codemodPath, testDir], { stdio: 'ignore' })
|
|
175
|
+
|
|
176
|
+
const content = readFile('hammer.config.mjs')
|
|
177
|
+
|
|
178
|
+
expect(content).toContain('ckeditor5:')
|
|
179
|
+
expect(content).toContain("requiredVersion: deps['ckeditor5']")
|
|
180
|
+
expect(content).toContain('singleton: true')
|
|
181
|
+
expect(content).toContain("import deps from './package.json'")
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
it('should add ckeditor5 to moduleFederation.shared in procore.config.js', () => {
|
|
185
|
+
execFileSync('node', [codemodPath, testDir], { stdio: 'ignore' })
|
|
186
|
+
|
|
187
|
+
const content = readFile('procore.config.js')
|
|
188
|
+
|
|
189
|
+
expect(content).toContain('ckeditor5:')
|
|
190
|
+
expect(content).toContain("requiredVersion: deps['ckeditor5']")
|
|
191
|
+
expect(content).toContain('singleton: true')
|
|
192
|
+
expect(content).toContain(
|
|
193
|
+
"const deps = require('./package.json').dependencies"
|
|
194
|
+
)
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
it('should not add ckeditor5 if already present', () => {
|
|
198
|
+
// First run adds ckeditor5
|
|
199
|
+
execFileSync('node', [codemodPath, testDir], { stdio: 'ignore' })
|
|
200
|
+
const firstRun = readFile('hammer.config.mjs')
|
|
201
|
+
|
|
202
|
+
// Second run should not duplicate
|
|
203
|
+
execFileSync('node', [codemodPath, testDir], { stdio: 'ignore' })
|
|
204
|
+
const secondRun = readFile('hammer.config.mjs')
|
|
205
|
+
|
|
206
|
+
expect(firstRun).toBe(secondRun)
|
|
207
|
+
// Count occurrences of ckeditor5 - should only appear once
|
|
208
|
+
const matches = secondRun.match(/ckeditor5:/g)
|
|
209
|
+
expect(matches).toHaveLength(1)
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
it('should add ckeditor5 only to ternary branches that contain react', () => {
|
|
213
|
+
execFileSync('node', [codemodPath, testDir], { stdio: 'ignore' })
|
|
214
|
+
|
|
215
|
+
const content = readFile('procore.config.ternary.js')
|
|
216
|
+
|
|
217
|
+
// Should have ckeditor5 only in the branch with react (production branch)
|
|
218
|
+
const ckeditor5Matches = content.match(/ckeditor5:/g)
|
|
219
|
+
expect(ckeditor5Matches).toHaveLength(1)
|
|
220
|
+
|
|
221
|
+
// Should have deps import
|
|
222
|
+
expect(content).toContain(
|
|
223
|
+
"const deps = require('./package.json').dependencies"
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
// Should have singleton: true only once
|
|
227
|
+
const singletonMatches = content.match(/singleton:\s*true/g)
|
|
228
|
+
expect(singletonMatches).toHaveLength(1)
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
it('should not modify config files without moduleFederation.shared', () => {
|
|
232
|
+
// Create a config file without moduleFederation
|
|
233
|
+
const noModFedConfig = `
|
|
234
|
+
const { coreReactJestConfig } = require('@procore/core-react/jestConfig')
|
|
235
|
+
|
|
236
|
+
module.exports = {
|
|
237
|
+
app: {
|
|
238
|
+
webpackOverride(config) {
|
|
239
|
+
return config
|
|
240
|
+
},
|
|
241
|
+
},
|
|
242
|
+
}
|
|
243
|
+
`
|
|
244
|
+
fs.writeFileSync(
|
|
245
|
+
path.join(testDir, 'no-mod-fed.config.js'),
|
|
246
|
+
noModFedConfig
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
execFileSync('node', [codemodPath, testDir], { stdio: 'ignore' })
|
|
250
|
+
|
|
251
|
+
const content = readFile('no-mod-fed.config.js')
|
|
252
|
+
|
|
253
|
+
expect(content).not.toContain('ckeditor5')
|
|
254
|
+
expect(content).not.toContain("require('./package.json')")
|
|
255
|
+
})
|
|
256
|
+
})
|
|
257
|
+
|
|
172
258
|
describe('edge cases', () => {
|
|
173
259
|
it('should not modify files without TextEditor usage', () => {
|
|
174
260
|
execFileSync('node', [codemodPath, testDir], { stdio: 'ignore' })
|
|
@@ -41,4 +41,4 @@ import type { TextEditorProps } from './TextEditor.types';
|
|
|
41
41
|
* }),
|
|
42
42
|
* })
|
|
43
43
|
*/
|
|
44
|
-
export declare function TextEditor({ disabled, error, value, initialValue, onChange, onInit, onBlur, onKeyDown, config: externalConfig, plugins: stringPlugins, locale: propLocale, onDirty,
|
|
44
|
+
export declare function TextEditor({ disabled, error, value, initialValue, onChange, onInit, onBlur, onKeyDown, config: externalConfig, plugins: stringPlugins, locale: propLocale, onDirty, ...restProps }: TextEditorProps): React.JSX.Element | null;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
2
|
-
var _excluded = ["disabled", "error", "value", "initialValue", "onChange", "onInit", "onBlur", "onKeyDown", "config", "plugins", "locale", "onDirty"
|
|
2
|
+
var _excluded = ["disabled", "error", "value", "initialValue", "onChange", "onInit", "onBlur", "onKeyDown", "config", "plugins", "locale", "onDirty"];
|
|
3
3
|
function _extends() { return _extends = Object.assign ? Object.assign.bind() : function (n) { for (var e = 1; e < arguments.length; e++) { var t = arguments[e]; for (var r in t) ({}).hasOwnProperty.call(t, r) && (n[r] = t[r]); } return n; }, _extends.apply(null, arguments); }
|
|
4
4
|
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
5
5
|
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
@@ -17,7 +17,7 @@ function _objectWithoutPropertiesLoose(r, e) { if (null == r) return {}; var t =
|
|
|
17
17
|
import { CKEditor } from '@ckeditor/ckeditor5-react';
|
|
18
18
|
import { ClassicEditor } from 'ckeditor5';
|
|
19
19
|
import React from 'react';
|
|
20
|
-
import {
|
|
20
|
+
import { useI18nContext, useZIndexContext } from '@procore/core-react';
|
|
21
21
|
import { useStickyToolbar } from './StickyToolbar';
|
|
22
22
|
import { GlobalEditorStyles, StyledTextEditor } from './TextEditor.styles';
|
|
23
23
|
import { TextEditorContext } from './TextEditorProvider';
|
|
@@ -80,10 +80,6 @@ export function TextEditor(_ref) {
|
|
|
80
80
|
stringPlugins = _ref.plugins,
|
|
81
81
|
propLocale = _ref.locale,
|
|
82
82
|
onDirty = _ref.onDirty,
|
|
83
|
-
_tinyMCE = _ref.tinyMCE,
|
|
84
|
-
_init = _ref.init,
|
|
85
|
-
_onEditorChange = _ref.onEditorChange,
|
|
86
|
-
_onFocusOut = _ref.onFocusOut,
|
|
87
83
|
restProps = _objectWithoutProperties(_ref, _excluded);
|
|
88
84
|
var _useCKEditorCss = useCKEditorCss(),
|
|
89
85
|
cssLoading = _useCKEditorCss.isLoading;
|
|
@@ -135,12 +131,6 @@ export function TextEditor(_ref) {
|
|
|
135
131
|
var finalConfig = _objectSpread(_objectSpread({}, configWithPlugins), externalResult !== null && externalResult !== void 0 ? externalResult : {});
|
|
136
132
|
return finalConfig;
|
|
137
133
|
}, [externalConfig, locale, stringPlugins]);
|
|
138
|
-
var focusScope = UNSAFE_useOverridableFocusScope();
|
|
139
|
-
React.useEffect(function () {
|
|
140
|
-
focusScope.setProps({
|
|
141
|
-
contain: false
|
|
142
|
-
});
|
|
143
|
-
}, [focusScope]);
|
|
144
134
|
var bindKeyDownHandler = React.useCallback(function (editor) {
|
|
145
135
|
if (!onKeyDown) {
|
|
146
136
|
return;
|