@jackuait/blok 0.4.1-beta.1 → 0.4.1-beta.12

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.
Files changed (403) hide show
  1. package/README.md +138 -17
  2. package/codemod/README.md +45 -7
  3. package/codemod/migrate-editorjs-to-blok.js +960 -92
  4. package/codemod/test.js +780 -77
  5. package/dist/blok.mjs +5 -2
  6. package/dist/chunks/blok-BU6NwVkN.mjs +13239 -0
  7. package/dist/chunks/i18next-CugVlwWp.mjs +1292 -0
  8. package/dist/chunks/i18next-loader-D8GzSwio.mjs +43 -0
  9. package/dist/{index-CEXLTV6f.mjs → chunks/index-C5e_WLFg.mjs} +2 -2
  10. package/dist/chunks/inline-tool-convert-CLUxkCe_.mjs +1990 -0
  11. package/dist/chunks/messages-0tDXLuyH.mjs +48 -0
  12. package/dist/chunks/messages-2_xedlYw.mjs +48 -0
  13. package/dist/chunks/messages-AHESHJm_.mjs +48 -0
  14. package/dist/chunks/messages-B5hdXZwA.mjs +48 -0
  15. package/dist/chunks/messages-B5jGUnOy.mjs +48 -0
  16. package/dist/chunks/messages-B5puUm7R.mjs +48 -0
  17. package/dist/chunks/messages-B66ZSDCJ.mjs +48 -0
  18. package/dist/chunks/messages-B9Oba7sq.mjs +48 -0
  19. package/dist/chunks/messages-BA0rcTCY.mjs +48 -0
  20. package/dist/chunks/messages-BBJgd5jG.mjs +48 -0
  21. package/dist/chunks/messages-BPqWKx5Z.mjs +48 -0
  22. package/dist/chunks/messages-Bdv-IkfG.mjs +48 -0
  23. package/dist/chunks/messages-BeUhMpsr.mjs +48 -0
  24. package/dist/chunks/messages-Bf6Y3_GI.mjs +48 -0
  25. package/dist/chunks/messages-BiExzWJv.mjs +48 -0
  26. package/dist/chunks/messages-BlpqL8vG.mjs +48 -0
  27. package/dist/chunks/messages-BmKCChWZ.mjs +48 -0
  28. package/dist/chunks/messages-Bn253WWC.mjs +48 -0
  29. package/dist/chunks/messages-BrJHUxQL.mjs +48 -0
  30. package/dist/chunks/messages-C5b7hr_E.mjs +48 -0
  31. package/dist/chunks/messages-C7I_AVH2.mjs +48 -0
  32. package/dist/chunks/messages-CJoBtXU6.mjs +48 -0
  33. package/dist/chunks/messages-CQj2JU2j.mjs +48 -0
  34. package/dist/chunks/messages-CUZ1x1QD.mjs +48 -0
  35. package/dist/chunks/messages-CUy1vn-b.mjs +48 -0
  36. package/dist/chunks/messages-CVeWVKsV.mjs +48 -0
  37. package/dist/chunks/messages-CXHd9SUK.mjs +48 -0
  38. package/dist/chunks/messages-CbMyJSzS.mjs +48 -0
  39. package/dist/chunks/messages-CbhuIWRJ.mjs +48 -0
  40. package/dist/chunks/messages-CeCjVKMW.mjs +48 -0
  41. package/dist/chunks/messages-Cj-t1bdy.mjs +48 -0
  42. package/dist/chunks/messages-CkFT2gle.mjs +48 -0
  43. package/dist/chunks/messages-Cm9aLHeX.mjs +48 -0
  44. package/dist/chunks/messages-CnvW8Slp.mjs +48 -0
  45. package/dist/chunks/messages-Cr-RJ7YB.mjs +48 -0
  46. package/dist/chunks/messages-CrsJ1TEJ.mjs +48 -0
  47. package/dist/chunks/messages-Cu08aLS3.mjs +48 -0
  48. package/dist/chunks/messages-CvaqJFN-.mjs +48 -0
  49. package/dist/chunks/messages-CyDU5lz9.mjs +48 -0
  50. package/dist/chunks/messages-CySyfkMU.mjs +48 -0
  51. package/dist/chunks/messages-Cyi2AMmz.mjs +48 -0
  52. package/dist/chunks/messages-D00OjS2n.mjs +48 -0
  53. package/dist/chunks/messages-DDLgIPDF.mjs +48 -0
  54. package/dist/chunks/messages-DMQIHGRj.mjs +48 -0
  55. package/dist/chunks/messages-DOlC_Tty.mjs +48 -0
  56. package/dist/chunks/messages-DV6shA9b.mjs +48 -0
  57. package/dist/chunks/messages-DY94ykcE.mjs +48 -0
  58. package/dist/chunks/messages-DbVquYKN.mjs +48 -0
  59. package/dist/chunks/messages-DcKOuncK.mjs +48 -0
  60. package/dist/chunks/messages-Dg92dXZ5.mjs +48 -0
  61. package/dist/chunks/messages-DnbbyJT3.mjs +48 -0
  62. package/dist/chunks/messages-DteYq0rv.mjs +48 -0
  63. package/dist/chunks/messages-GC2PhgV3.mjs +48 -0
  64. package/dist/chunks/messages-JGsXAReJ.mjs +48 -0
  65. package/dist/chunks/messages-JZUhXTuV.mjs +48 -0
  66. package/dist/chunks/messages-LvFKBBPa.mjs +48 -0
  67. package/dist/chunks/messages-NP1myMGI.mjs +48 -0
  68. package/dist/chunks/messages-Q4kc_ZtL.mjs +48 -0
  69. package/dist/chunks/messages-RvMHb2Ht.mjs +48 -0
  70. package/dist/chunks/messages-ftMcCEuO.mjs +48 -0
  71. package/dist/chunks/messages-o24dK6CU.mjs +48 -0
  72. package/dist/chunks/messages-pA5TvcAj.mjs +48 -0
  73. package/dist/chunks/messages-rRSHQDCX.mjs +48 -0
  74. package/dist/chunks/messages-srxrv8Yh.mjs +48 -0
  75. package/dist/chunks/messages-wdqp4610.mjs +48 -0
  76. package/dist/chunks/messages-zS1AXZ0y.mjs +48 -0
  77. package/dist/chunks/messages-zSzDzXej.mjs +48 -0
  78. package/dist/full.mjs +50 -0
  79. package/dist/locales.mjs +228 -0
  80. package/dist/messages-0tDXLuyH.mjs +48 -0
  81. package/dist/messages-2_xedlYw.mjs +48 -0
  82. package/dist/messages-AHESHJm_.mjs +48 -0
  83. package/dist/messages-B5hdXZwA.mjs +48 -0
  84. package/dist/messages-B5jGUnOy.mjs +48 -0
  85. package/dist/messages-B5puUm7R.mjs +48 -0
  86. package/dist/messages-B66ZSDCJ.mjs +48 -0
  87. package/dist/messages-B9Oba7sq.mjs +48 -0
  88. package/dist/messages-BA0rcTCY.mjs +48 -0
  89. package/dist/messages-BBJgd5jG.mjs +48 -0
  90. package/dist/messages-BPqWKx5Z.mjs +48 -0
  91. package/dist/messages-Bdv-IkfG.mjs +48 -0
  92. package/dist/messages-BeUhMpsr.mjs +48 -0
  93. package/dist/messages-Bf6Y3_GI.mjs +48 -0
  94. package/dist/messages-BiExzWJv.mjs +48 -0
  95. package/dist/messages-BlpqL8vG.mjs +48 -0
  96. package/dist/messages-BmKCChWZ.mjs +48 -0
  97. package/dist/messages-Bn253WWC.mjs +48 -0
  98. package/dist/messages-BrJHUxQL.mjs +48 -0
  99. package/dist/messages-C5b7hr_E.mjs +48 -0
  100. package/dist/messages-C7I_AVH2.mjs +48 -0
  101. package/dist/messages-CJoBtXU6.mjs +48 -0
  102. package/dist/messages-CQj2JU2j.mjs +48 -0
  103. package/dist/messages-CUZ1x1QD.mjs +48 -0
  104. package/dist/messages-CUy1vn-b.mjs +48 -0
  105. package/dist/messages-CVeWVKsV.mjs +48 -0
  106. package/dist/messages-CXHd9SUK.mjs +48 -0
  107. package/dist/messages-CbMyJSzS.mjs +48 -0
  108. package/dist/messages-CbhuIWRJ.mjs +48 -0
  109. package/dist/messages-CeCjVKMW.mjs +48 -0
  110. package/dist/messages-Cj-t1bdy.mjs +48 -0
  111. package/dist/messages-CkFT2gle.mjs +48 -0
  112. package/dist/messages-Cm9aLHeX.mjs +48 -0
  113. package/dist/messages-CnvW8Slp.mjs +48 -0
  114. package/dist/messages-Cr-RJ7YB.mjs +48 -0
  115. package/dist/messages-CrsJ1TEJ.mjs +48 -0
  116. package/dist/messages-Cu08aLS3.mjs +48 -0
  117. package/dist/messages-CvaqJFN-.mjs +48 -0
  118. package/dist/messages-CyDU5lz9.mjs +48 -0
  119. package/dist/messages-CySyfkMU.mjs +48 -0
  120. package/dist/messages-Cyi2AMmz.mjs +48 -0
  121. package/dist/messages-D00OjS2n.mjs +48 -0
  122. package/dist/messages-DDLgIPDF.mjs +48 -0
  123. package/dist/messages-DMQIHGRj.mjs +48 -0
  124. package/dist/messages-DOlC_Tty.mjs +48 -0
  125. package/dist/messages-DV6shA9b.mjs +48 -0
  126. package/dist/messages-DY94ykcE.mjs +48 -0
  127. package/dist/messages-DbVquYKN.mjs +48 -0
  128. package/dist/messages-DcKOuncK.mjs +48 -0
  129. package/dist/messages-Dg92dXZ5.mjs +48 -0
  130. package/dist/messages-DnbbyJT3.mjs +48 -0
  131. package/dist/messages-DteYq0rv.mjs +48 -0
  132. package/dist/messages-GC2PhgV3.mjs +48 -0
  133. package/dist/messages-JGsXAReJ.mjs +48 -0
  134. package/dist/messages-JZUhXTuV.mjs +48 -0
  135. package/dist/messages-LvFKBBPa.mjs +48 -0
  136. package/dist/messages-NP1myMGI.mjs +48 -0
  137. package/dist/messages-Q4kc_ZtL.mjs +48 -0
  138. package/dist/messages-RvMHb2Ht.mjs +48 -0
  139. package/dist/messages-ftMcCEuO.mjs +48 -0
  140. package/dist/messages-o24dK6CU.mjs +48 -0
  141. package/dist/messages-pA5TvcAj.mjs +48 -0
  142. package/dist/messages-rRSHQDCX.mjs +48 -0
  143. package/dist/messages-srxrv8Yh.mjs +48 -0
  144. package/dist/messages-wdqp4610.mjs +48 -0
  145. package/dist/messages-zS1AXZ0y.mjs +48 -0
  146. package/dist/messages-zSzDzXej.mjs +48 -0
  147. package/dist/tools.mjs +3126 -0
  148. package/dist/vendor.LICENSE.txt +26 -225
  149. package/package.json +63 -24
  150. package/src/blok.ts +267 -0
  151. package/src/components/__module.ts +139 -0
  152. package/src/components/block/api.ts +155 -0
  153. package/src/components/block/index.ts +1428 -0
  154. package/src/components/block-tunes/block-tune-delete.ts +51 -0
  155. package/src/components/blocks.ts +352 -0
  156. package/src/components/constants/data-attributes.ts +344 -0
  157. package/src/components/constants.ts +76 -0
  158. package/src/components/core.ts +392 -0
  159. package/src/components/dom.ts +773 -0
  160. package/src/components/domIterator.ts +189 -0
  161. package/src/components/errors/critical.ts +5 -0
  162. package/src/components/events/BlockChanged.ts +16 -0
  163. package/src/components/events/BlockHovered.ts +21 -0
  164. package/src/components/events/BlockSettingsClosed.ts +12 -0
  165. package/src/components/events/BlockSettingsOpened.ts +12 -0
  166. package/src/components/events/BlokMobileLayoutToggled.ts +15 -0
  167. package/src/components/events/FakeCursorAboutToBeToggled.ts +17 -0
  168. package/src/components/events/FakeCursorHaveBeenSet.ts +17 -0
  169. package/src/components/events/HistoryStateChanged.ts +19 -0
  170. package/src/components/events/RedactorDomChanged.ts +14 -0
  171. package/src/components/events/index.ts +46 -0
  172. package/src/components/flipper.ts +497 -0
  173. package/src/components/i18n/i18next-loader.ts +84 -0
  174. package/src/components/i18n/lightweight-i18n.ts +86 -0
  175. package/src/components/i18n/locales/TRANSLATION_GUIDELINES.md +113 -0
  176. package/src/components/i18n/locales/am/messages.json +45 -0
  177. package/src/components/i18n/locales/ar/messages.json +45 -0
  178. package/src/components/i18n/locales/az/messages.json +45 -0
  179. package/src/components/i18n/locales/bg/messages.json +45 -0
  180. package/src/components/i18n/locales/bn/messages.json +45 -0
  181. package/src/components/i18n/locales/bs/messages.json +45 -0
  182. package/src/components/i18n/locales/cs/messages.json +45 -0
  183. package/src/components/i18n/locales/da/messages.json +45 -0
  184. package/src/components/i18n/locales/de/messages.json +45 -0
  185. package/src/components/i18n/locales/dv/messages.json +45 -0
  186. package/src/components/i18n/locales/el/messages.json +45 -0
  187. package/src/components/i18n/locales/en/messages.json +45 -0
  188. package/src/components/i18n/locales/es/messages.json +45 -0
  189. package/src/components/i18n/locales/et/messages.json +45 -0
  190. package/src/components/i18n/locales/fa/messages.json +45 -0
  191. package/src/components/i18n/locales/fi/messages.json +45 -0
  192. package/src/components/i18n/locales/fil/messages.json +45 -0
  193. package/src/components/i18n/locales/fr/messages.json +45 -0
  194. package/src/components/i18n/locales/gu/messages.json +45 -0
  195. package/src/components/i18n/locales/he/messages.json +45 -0
  196. package/src/components/i18n/locales/hi/messages.json +45 -0
  197. package/src/components/i18n/locales/hr/messages.json +45 -0
  198. package/src/components/i18n/locales/hu/messages.json +45 -0
  199. package/src/components/i18n/locales/hy/messages.json +45 -0
  200. package/src/components/i18n/locales/id/messages.json +45 -0
  201. package/src/components/i18n/locales/index.ts +231 -0
  202. package/src/components/i18n/locales/it/messages.json +45 -0
  203. package/src/components/i18n/locales/ja/messages.json +45 -0
  204. package/src/components/i18n/locales/ka/messages.json +45 -0
  205. package/src/components/i18n/locales/km/messages.json +45 -0
  206. package/src/components/i18n/locales/kn/messages.json +45 -0
  207. package/src/components/i18n/locales/ko/messages.json +45 -0
  208. package/src/components/i18n/locales/ku/messages.json +45 -0
  209. package/src/components/i18n/locales/lo/messages.json +45 -0
  210. package/src/components/i18n/locales/lt/messages.json +45 -0
  211. package/src/components/i18n/locales/lv/messages.json +45 -0
  212. package/src/components/i18n/locales/mk/messages.json +45 -0
  213. package/src/components/i18n/locales/ml/messages.json +45 -0
  214. package/src/components/i18n/locales/mn/messages.json +45 -0
  215. package/src/components/i18n/locales/mr/messages.json +45 -0
  216. package/src/components/i18n/locales/ms/messages.json +45 -0
  217. package/src/components/i18n/locales/my/messages.json +45 -0
  218. package/src/components/i18n/locales/ne/messages.json +45 -0
  219. package/src/components/i18n/locales/nl/messages.json +45 -0
  220. package/src/components/i18n/locales/no/messages.json +45 -0
  221. package/src/components/i18n/locales/pa/messages.json +45 -0
  222. package/src/components/i18n/locales/pl/messages.json +45 -0
  223. package/src/components/i18n/locales/ps/messages.json +45 -0
  224. package/src/components/i18n/locales/pt/messages.json +45 -0
  225. package/src/components/i18n/locales/ro/messages.json +45 -0
  226. package/src/components/i18n/locales/ru/messages.json +45 -0
  227. package/src/components/i18n/locales/sd/messages.json +45 -0
  228. package/src/components/i18n/locales/si/messages.json +45 -0
  229. package/src/components/i18n/locales/sk/messages.json +45 -0
  230. package/src/components/i18n/locales/sl/messages.json +45 -0
  231. package/src/components/i18n/locales/sq/messages.json +45 -0
  232. package/src/components/i18n/locales/sr/messages.json +45 -0
  233. package/src/components/i18n/locales/sv/messages.json +45 -0
  234. package/src/components/i18n/locales/sw/messages.json +45 -0
  235. package/src/components/i18n/locales/ta/messages.json +45 -0
  236. package/src/components/i18n/locales/te/messages.json +45 -0
  237. package/src/components/i18n/locales/th/messages.json +45 -0
  238. package/src/components/i18n/locales/tr/messages.json +45 -0
  239. package/src/components/i18n/locales/ug/messages.json +45 -0
  240. package/src/components/i18n/locales/uk/messages.json +45 -0
  241. package/src/components/i18n/locales/ur/messages.json +45 -0
  242. package/src/components/i18n/locales/vi/messages.json +45 -0
  243. package/src/components/i18n/locales/yi/messages.json +45 -0
  244. package/src/components/i18n/locales/zh/messages.json +45 -0
  245. package/src/components/icons/index.ts +242 -0
  246. package/src/components/inline-tools/inline-tool-bold.ts +2213 -0
  247. package/src/components/inline-tools/inline-tool-convert.ts +142 -0
  248. package/src/components/inline-tools/inline-tool-italic.ts +500 -0
  249. package/src/components/inline-tools/inline-tool-link.ts +540 -0
  250. package/src/components/modules/api/blocks.ts +377 -0
  251. package/src/components/modules/api/caret.ts +125 -0
  252. package/src/components/modules/api/events.ts +51 -0
  253. package/src/components/modules/api/history.ts +73 -0
  254. package/src/components/modules/api/i18n.ts +35 -0
  255. package/src/components/modules/api/index.ts +39 -0
  256. package/src/components/modules/api/inlineToolbar.ts +33 -0
  257. package/src/components/modules/api/listeners.ts +56 -0
  258. package/src/components/modules/api/notifier.ts +46 -0
  259. package/src/components/modules/api/readonly.ts +39 -0
  260. package/src/components/modules/api/sanitizer.ts +30 -0
  261. package/src/components/modules/api/saver.ts +52 -0
  262. package/src/components/modules/api/selection.ts +48 -0
  263. package/src/components/modules/api/styles.ts +72 -0
  264. package/src/components/modules/api/toolbar.ts +79 -0
  265. package/src/components/modules/api/tools.ts +16 -0
  266. package/src/components/modules/api/tooltip.ts +67 -0
  267. package/src/components/modules/api/ui.ts +36 -0
  268. package/src/components/modules/blockEvents.ts +1591 -0
  269. package/src/components/modules/blockManager.ts +1356 -0
  270. package/src/components/modules/blockSelection.ts +708 -0
  271. package/src/components/modules/caret.ts +853 -0
  272. package/src/components/modules/crossBlockSelection.ts +329 -0
  273. package/src/components/modules/dragManager.ts +1204 -0
  274. package/src/components/modules/history.ts +1098 -0
  275. package/src/components/modules/i18n.ts +332 -0
  276. package/src/components/modules/index.ts +139 -0
  277. package/src/components/modules/modificationsObserver.ts +147 -0
  278. package/src/components/modules/paste.ts +1092 -0
  279. package/src/components/modules/readonly.ts +136 -0
  280. package/src/components/modules/rectangleSelection.ts +711 -0
  281. package/src/components/modules/renderer.ts +155 -0
  282. package/src/components/modules/saver.ts +283 -0
  283. package/src/components/modules/toolbar/blockSettings.ts +782 -0
  284. package/src/components/modules/toolbar/index.ts +1296 -0
  285. package/src/components/modules/toolbar/inline.ts +956 -0
  286. package/src/components/modules/tools.ts +625 -0
  287. package/src/components/modules/ui.ts +1283 -0
  288. package/src/components/polyfills.ts +113 -0
  289. package/src/components/selection.ts +1179 -0
  290. package/src/components/tools/base.ts +301 -0
  291. package/src/components/tools/block.ts +339 -0
  292. package/src/components/tools/collection.ts +67 -0
  293. package/src/components/tools/factory.ts +138 -0
  294. package/src/components/tools/inline.ts +71 -0
  295. package/src/components/tools/tune.ts +33 -0
  296. package/src/components/ui/toolbox.ts +610 -0
  297. package/src/components/utils/announcer.ts +205 -0
  298. package/src/components/utils/api.ts +20 -0
  299. package/src/components/utils/bem.ts +26 -0
  300. package/src/components/utils/blocks.ts +284 -0
  301. package/src/components/utils/caret.ts +1067 -0
  302. package/src/components/utils/data-model-transform.ts +382 -0
  303. package/src/components/utils/events.ts +117 -0
  304. package/src/components/utils/keyboard.ts +60 -0
  305. package/src/components/utils/listeners.ts +296 -0
  306. package/src/components/utils/mutations.ts +39 -0
  307. package/src/components/utils/notifier/draw.ts +190 -0
  308. package/src/components/utils/notifier/index.ts +66 -0
  309. package/src/components/utils/notifier/types.ts +1 -0
  310. package/src/components/utils/notifier.ts +77 -0
  311. package/src/components/utils/placeholder.ts +140 -0
  312. package/src/components/utils/popover/components/hint/hint.const.ts +10 -0
  313. package/src/components/utils/popover/components/hint/hint.ts +46 -0
  314. package/src/components/utils/popover/components/hint/index.ts +6 -0
  315. package/src/components/utils/popover/components/popover-header/index.ts +2 -0
  316. package/src/components/utils/popover/components/popover-header/popover-header.const.ts +8 -0
  317. package/src/components/utils/popover/components/popover-header/popover-header.ts +80 -0
  318. package/src/components/utils/popover/components/popover-header/popover-header.types.ts +14 -0
  319. package/src/components/utils/popover/components/popover-item/index.ts +13 -0
  320. package/src/components/utils/popover/components/popover-item/popover-item-default/popover-item-default.const.ts +50 -0
  321. package/src/components/utils/popover/components/popover-item/popover-item-default/popover-item-default.ts +680 -0
  322. package/src/components/utils/popover/components/popover-item/popover-item-html/popover-item-html.const.ts +14 -0
  323. package/src/components/utils/popover/components/popover-item/popover-item-html/popover-item-html.ts +136 -0
  324. package/src/components/utils/popover/components/popover-item/popover-item-separator/popover-item-separator.const.ts +20 -0
  325. package/src/components/utils/popover/components/popover-item/popover-item-separator/popover-item-separator.ts +117 -0
  326. package/src/components/utils/popover/components/popover-item/popover-item.ts +197 -0
  327. package/src/components/utils/popover/components/search-input/index.ts +2 -0
  328. package/src/components/utils/popover/components/search-input/search-input.const.ts +8 -0
  329. package/src/components/utils/popover/components/search-input/search-input.ts +178 -0
  330. package/src/components/utils/popover/components/search-input/search-input.types.ts +59 -0
  331. package/src/components/utils/popover/index.ts +13 -0
  332. package/src/components/utils/popover/popover-abstract.ts +457 -0
  333. package/src/components/utils/popover/popover-desktop.ts +682 -0
  334. package/src/components/utils/popover/popover-inline.ts +338 -0
  335. package/src/components/utils/popover/popover-mobile.ts +201 -0
  336. package/src/components/utils/popover/popover.const.ts +81 -0
  337. package/src/components/utils/popover/utils/popover-states-history.ts +72 -0
  338. package/src/components/utils/promise-queue.ts +43 -0
  339. package/src/components/utils/sanitizer.ts +537 -0
  340. package/src/components/utils/scroll-locker.ts +87 -0
  341. package/src/components/utils/shortcut.ts +231 -0
  342. package/src/components/utils/shortcuts.ts +113 -0
  343. package/src/components/utils/tools.ts +110 -0
  344. package/src/components/utils/tooltip.ts +591 -0
  345. package/src/components/utils/tw.ts +241 -0
  346. package/src/components/utils.ts +1081 -0
  347. package/src/env.d.ts +13 -0
  348. package/src/full.ts +69 -0
  349. package/src/locales.ts +51 -0
  350. package/src/stories/Block.stories.ts +498 -0
  351. package/src/stories/EditorModes.stories.ts +505 -0
  352. package/src/stories/Header.stories.ts +137 -0
  353. package/src/stories/InlineToolbar.stories.ts +498 -0
  354. package/src/stories/List.stories.ts +259 -0
  355. package/src/stories/Notifier.stories.ts +340 -0
  356. package/src/stories/Paragraph.stories.ts +112 -0
  357. package/src/stories/Placeholder.stories.ts +319 -0
  358. package/src/stories/Popover.stories.ts +759 -0
  359. package/src/stories/Selection.stories.ts +250 -0
  360. package/src/stories/StubBlock.stories.ts +156 -0
  361. package/src/stories/Toolbar.stories.ts +223 -0
  362. package/src/stories/Toolbox.stories.ts +166 -0
  363. package/src/stories/Tooltip.stories.ts +198 -0
  364. package/src/stories/helpers.ts +463 -0
  365. package/src/styles/main.css +126 -0
  366. package/src/tools/header/index.ts +647 -0
  367. package/src/tools/index.ts +45 -0
  368. package/src/tools/list/index.ts +1826 -0
  369. package/src/tools/paragraph/index.ts +412 -0
  370. package/src/tools/stub/index.ts +107 -0
  371. package/src/types-internal/blok-modules.d.ts +87 -0
  372. package/src/types-internal/html-janitor.d.ts +28 -0
  373. package/src/types-internal/module-config.d.ts +11 -0
  374. package/src/variants/all-locales.ts +155 -0
  375. package/src/variants/blok-maximum.ts +20 -0
  376. package/src/variants/blok-minimum.ts +243 -0
  377. package/types/api/blocks.d.ts +9 -1
  378. package/types/api/history.d.ts +7 -0
  379. package/types/api/i18n.d.ts +22 -3
  380. package/types/api/selection.d.ts +6 -0
  381. package/types/api/styles.d.ts +23 -10
  382. package/types/configs/blok-config.d.ts +29 -0
  383. package/types/configs/i18n-config.d.ts +52 -2
  384. package/types/configs/i18n-dictionary.d.ts +16 -90
  385. package/types/configs/sanitizer-config.d.ts +25 -1
  386. package/types/data-attributes.d.ts +170 -0
  387. package/types/data-formats/output-data.d.ts +15 -0
  388. package/types/full.d.ts +80 -0
  389. package/types/index.d.ts +30 -13
  390. package/types/locales.d.ts +59 -0
  391. package/types/tools/adapters/inline-tool-adapter.d.ts +10 -0
  392. package/types/tools/block-tool.d.ts +11 -2
  393. package/types/tools/header.d.ts +18 -0
  394. package/types/tools/index.d.ts +1 -0
  395. package/types/tools/list.d.ts +91 -0
  396. package/types/tools/paragraph.d.ts +71 -0
  397. package/types/tools/tool-settings.d.ts +99 -6
  398. package/types/tools/tool.d.ts +6 -0
  399. package/types/tools-entry.d.ts +49 -0
  400. package/types/utils/popover/popover-item.d.ts +24 -5
  401. package/types/utils/popover/popover.d.ts +13 -0
  402. package/dist/blok-C8XbyLHh.mjs +0 -25795
  403. package/dist/blok.umd.js +0 -181
@@ -29,28 +29,615 @@ const path = require('path');
29
29
 
30
30
  const FILE_EXTENSIONS = ['.js', '.jsx', '.ts', '.tsx', '.vue', '.svelte', '.html', '.css', '.scss', '.less'];
31
31
 
32
+ // ============================================================================
33
+ // i18n Transformation Utilities
34
+ // ============================================================================
35
+
36
+ /**
37
+ * EditorJS to Blok key mappings for keys that changed.
38
+ * These are applied after flattening to convert EditorJS keys to Blok equivalents.
39
+ *
40
+ * EditorJS keys reference: https://editorjs.io/i18n/
41
+ * Blok keys reference: src/components/i18n/locales/en/messages.json
42
+ */
43
+ const I18N_KEY_MAPPINGS = {
44
+ // UI keys that changed
45
+ 'ui.blockTunes.toggler.Click to tune': 'blockSettings.clickToOpenMenu',
46
+ 'ui.blockTunes.toggler.or drag to move': 'blockSettings.dragToMove',
47
+ 'ui.toolbar.toolbox.Add': 'toolbox.addBelow',
48
+ 'ui.inlineToolbar.converter.Convert to': 'popover.convertTo',
49
+ 'ui.popover.Filter': 'popover.search',
50
+
51
+ // Tool names that changed (EditorJS uses different casing/wording)
52
+ 'toolNames.Ordered List': 'toolNames.numberedList',
53
+ 'toolNames.Unordered List': 'toolNames.bulletedList',
54
+
55
+ // Tools messages that changed
56
+ 'tools.stub.The block can not be displayed correctly': 'tools.stub.blockCannotBeDisplayed',
57
+ 'tools.stub.The block can not be displayed correctly.': 'tools.stub.blockCannotBeDisplayed',
58
+
59
+ // Block tunes that changed in Blok
60
+ 'blockTunes.delete.Delete': 'blockSettings.delete',
61
+
62
+ // Block tunes that are removed in Blok (moveUp/moveDown replaced with drag)
63
+ // These are mapped to null to indicate they should be removed
64
+ 'blockTunes.moveUp.Move up': null,
65
+ 'blockTunes.moveDown.Move down': null,
66
+ };
67
+
68
+ /**
69
+ * Namespace mappings from old verbose prefixes to new simplified prefixes.
70
+ * Applied after camelCase normalization.
71
+ */
72
+ const NAMESPACE_MAPPINGS = {
73
+ 'ui.blockTunes.toggler': 'blockSettings',
74
+ 'ui.toolbar.toolbox': 'toolbox',
75
+ 'ui.popover': 'popover',
76
+ 'blockTunes': 'blockSettings',
77
+ };
78
+
79
+ /**
80
+ * Converts an EditorJS-style key (with English text) to Blok-style (camelCase).
81
+ * Blok uses camelCase for the final segment of translation keys.
82
+ * Also applies namespace simplification for known verbose prefixes.
83
+ * Example: 'ui.popover.Nothing found' → 'popover.nothingFound'
84
+ * Example: 'toolNames.Text' → 'toolNames.text'
85
+ * @param {string} key - The dot-notation key with English text
86
+ * @returns {string} The normalized key with camelCase final segment
87
+ */
88
+ function normalizeKey(key) {
89
+ const parts = key.split('.');
90
+ const lastPart = parts[parts.length - 1];
91
+
92
+ // Convert "Nothing found" → "nothingFound", "Text" → "text"
93
+ const words = lastPart.split(/\s+/);
94
+ const camelCase = words
95
+ .map((word, i) => {
96
+ if (i === 0) {
97
+ // First word is lowercase
98
+ return word.toLowerCase();
99
+ }
100
+ // Subsequent words have first letter capitalized
101
+ return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
102
+ })
103
+ .join('');
104
+
105
+ parts[parts.length - 1] = camelCase;
106
+ let normalizedKey = parts.join('.');
107
+
108
+ // Apply namespace mappings (longest prefix first for correct matching)
109
+ const sortedPrefixes = Object.keys(NAMESPACE_MAPPINGS).sort((a, b) => b.length - a.length);
110
+ for (const oldPrefix of sortedPrefixes) {
111
+ if (normalizedKey.startsWith(oldPrefix + '.')) {
112
+ normalizedKey = NAMESPACE_MAPPINGS[oldPrefix] + normalizedKey.slice(oldPrefix.length);
113
+ break;
114
+ }
115
+ }
116
+
117
+ return normalizedKey;
118
+ }
119
+
120
+ /**
121
+ * Flattens a nested i18n dictionary object to Blok's flat dot-notation format.
122
+ * @param {Object} obj - The nested dictionary object
123
+ * @param {string} prefix - Current key prefix (for recursion)
124
+ * @returns {Object} Flattened dictionary with dot-notation keys
125
+ */
126
+ function flattenI18nDictionary(obj, prefix = '') {
127
+ const result = {};
128
+
129
+ for (const [key, value] of Object.entries(obj)) {
130
+ const newKey = prefix ? `${prefix}.${key}` : key;
131
+
132
+ if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
133
+ Object.assign(result, flattenI18nDictionary(value, newKey));
134
+ } else {
135
+ // Check explicit mappings first, otherwise normalize to camelCase
136
+ let finalKey;
137
+ if (newKey in I18N_KEY_MAPPINGS) {
138
+ finalKey = I18N_KEY_MAPPINGS[newKey];
139
+ } else {
140
+ finalKey = normalizeKey(newKey);
141
+ }
142
+
143
+ // Skip keys that are mapped to null (removed in Blok)
144
+ if (finalKey === null) {
145
+ continue;
146
+ }
147
+
148
+ result[finalKey] = value;
149
+ }
150
+ }
151
+
152
+ return result;
153
+ }
154
+
155
+ /**
156
+ * Parses a JavaScript object literal string into an actual object.
157
+ * Handles simple object literals with string values.
158
+ * @param {string} str - The object literal string
159
+ * @returns {Object|null} Parsed object or null if parsing fails
160
+ */
161
+ function parseObjectLiteral(str) {
162
+ try {
163
+ // Convert single quotes to double quotes for JSON compatibility
164
+ // Handle unquoted keys by quoting them
165
+ let jsonStr = str
166
+ // Replace single quotes with double quotes
167
+ .replace(/'/g, '"')
168
+ // Handle unquoted keys (identifier followed by colon)
169
+ .replace(/(\s*)(\w+)(\s*:\s*)/g, '$1"$2"$3')
170
+ // Remove trailing commas before closing braces/brackets
171
+ .replace(/,(\s*[}\]])/g, '$1');
172
+
173
+ return JSON.parse(jsonStr);
174
+ } catch {
175
+ return null;
176
+ }
177
+ }
178
+
179
+ /**
180
+ * Converts a flattened dictionary object to a formatted JavaScript object string.
181
+ * @param {Object} obj - The flattened dictionary
182
+ * @param {string} indent - The indentation string
183
+ * @returns {string} Formatted object string
184
+ */
185
+ function objectToString(obj, indent = ' ') {
186
+ const entries = Object.entries(obj);
187
+ if (entries.length === 0) {
188
+ return '{}';
189
+ }
190
+
191
+ const lines = entries.map(([key, value]) => {
192
+ const escapedValue = typeof value === 'string' ? value.replace(/"/g, '\\"') : value;
193
+ return `${indent}"${key}": "${escapedValue}"`;
194
+ });
195
+
196
+ return '{\n' + lines.join(',\n') + '\n' + indent.slice(2) + '}';
197
+ }
198
+
199
+ /**
200
+ * Finds matching brace for nested object parsing.
201
+ * @param {string} str - The string to search
202
+ * @param {number} startIndex - Starting position (should be at opening brace)
203
+ * @returns {number} Index of matching closing brace, or -1 if not found
204
+ */
205
+ function findMatchingBrace(str, startIndex) {
206
+ let depth = 0;
207
+ let inString = false;
208
+ let stringChar = '';
209
+
210
+ for (let i = startIndex; i < str.length; i++) {
211
+ const char = str[i];
212
+ const prevChar = i > 0 ? str[i - 1] : '';
213
+
214
+ // Handle string detection
215
+ if ((char === '"' || char === "'" || char === '`') && prevChar !== '\\') {
216
+ if (!inString) {
217
+ inString = true;
218
+ stringChar = char;
219
+ } else if (char === stringChar) {
220
+ inString = false;
221
+ }
222
+ continue;
223
+ }
224
+
225
+ if (inString) continue;
226
+
227
+ if (char === '{') {
228
+ depth++;
229
+ } else if (char === '}') {
230
+ depth--;
231
+ if (depth === 0) {
232
+ return i;
233
+ }
234
+ }
235
+ }
236
+
237
+ return -1;
238
+ }
239
+
240
+ /**
241
+ * Transforms EditorJS nested i18n messages to Blok flat format.
242
+ * @param {string} content - The file content
243
+ * @returns {{result: string, changed: boolean}} Transformed content and change flag
244
+ */
245
+ function transformI18nConfig(content) {
246
+ // Pattern to find i18n: { messages: { ... } } or i18n: { messages: { ... }, ... }
247
+ const i18nStartPattern = /i18n\s*:\s*\{\s*messages\s*:\s*\{/g;
248
+ let match;
249
+ let result = content;
250
+ let changed = false;
251
+ let offset = 0;
252
+
253
+ // Reset lastIndex for the regex
254
+ i18nStartPattern.lastIndex = 0;
255
+
256
+ while ((match = i18nStartPattern.exec(content)) !== null) {
257
+ const messagesStart = match.index + match[0].length - 1; // Position of opening brace of messages
258
+ const messagesEnd = findMatchingBrace(content, messagesStart);
259
+
260
+ if (messagesEnd === -1) continue;
261
+
262
+ const messagesStr = content.substring(messagesStart, messagesEnd + 1);
263
+
264
+ // Skip if it contains functions or arrow functions (dynamic content)
265
+ if (messagesStr.includes('function') || messagesStr.includes('=>')) {
266
+ continue;
267
+ }
268
+
269
+ // Try to parse the messages object
270
+ const messagesObj = parseObjectLiteral(messagesStr);
271
+ if (!messagesObj) continue;
272
+
273
+ // Flatten the dictionary
274
+ const flattened = flattenI18nDictionary(messagesObj);
275
+
276
+ // Detect indentation from the original content
277
+ const lineStart = content.lastIndexOf('\n', match.index) + 1;
278
+ const lineContent = content.substring(lineStart, match.index);
279
+ const baseIndent = lineContent.match(/^\s*/)?.[0] || '';
280
+ const messagesIndent = baseIndent + ' ';
281
+
282
+ // Convert to string with proper formatting
283
+ const flattenedStr = objectToString(flattened, messagesIndent + ' ');
284
+
285
+ // Replace in result (accounting for previous replacements)
286
+ const adjustedStart = messagesStart + offset;
287
+ const adjustedEnd = messagesEnd + 1 + offset;
288
+ result = result.substring(0, adjustedStart) + flattenedStr + result.substring(adjustedEnd);
289
+
290
+ // Update offset for next iteration
291
+ offset += flattenedStr.length - (messagesEnd + 1 - messagesStart);
292
+ changed = true;
293
+ }
294
+
295
+ return { result, changed };
296
+ }
297
+
298
+ /**
299
+ * Removes i18n messages from config to use Blok's built-in library translations.
300
+ * Preserves other i18n properties like locale and direction.
301
+ * @param {string} content - The file content
302
+ * @returns {{result: string, changed: boolean}} Transformed content and change flag
303
+ */
304
+ function removeI18nMessages(content) {
305
+ // Pattern to find i18n config with messages property
306
+ const i18nStartPattern = /i18n\s*:\s*\{/g;
307
+ let match;
308
+ let result = content;
309
+ let changed = false;
310
+ let offset = 0;
311
+
312
+ // Reset lastIndex for the regex
313
+ i18nStartPattern.lastIndex = 0;
314
+
315
+ while ((match = i18nStartPattern.exec(content)) !== null) {
316
+ const i18nStart = match.index + match[0].length - 1; // Position of opening brace
317
+ const i18nEnd = findMatchingBrace(content, i18nStart);
318
+
319
+ if (i18nEnd === -1) continue;
320
+
321
+ const i18nContent = content.substring(i18nStart, i18nEnd + 1);
322
+
323
+ // Check if this i18n config has a messages property
324
+ if (!i18nContent.includes('messages')) {
325
+ continue;
326
+ }
327
+
328
+ // Find the messages property and remove it
329
+ // Handle: messages: { ... }, or messages: { ... } (with or without trailing comma)
330
+ const messagesPattern = /,?\s*messages\s*:\s*\{/;
331
+ const messagesMatch = i18nContent.match(messagesPattern);
332
+
333
+ if (!messagesMatch) continue;
334
+
335
+ const messagesStartInContent = i18nContent.indexOf(messagesMatch[0]);
336
+ const messagesObjStart = i18nContent.indexOf('{', messagesStartInContent);
337
+ const messagesObjEnd = findMatchingBrace(i18nContent, messagesObjStart);
338
+
339
+ if (messagesObjEnd === -1) continue;
340
+
341
+ // Determine what to remove (including trailing comma if present)
342
+ let removeStart = messagesStartInContent;
343
+ let removeEnd = messagesObjEnd + 1;
344
+
345
+ // Check for trailing comma after messages object
346
+ const afterMessages = i18nContent.substring(removeEnd);
347
+ const trailingCommaMatch = afterMessages.match(/^\s*,/);
348
+ if (trailingCommaMatch) {
349
+ removeEnd += trailingCommaMatch[0].length;
350
+ }
351
+
352
+ // If messages started with a comma (not first property), include it in removal
353
+ // Otherwise, we need to handle the case where it's the first property
354
+ const beforeMessages = i18nContent.substring(0, messagesStartInContent);
355
+ if (!messagesMatch[0].startsWith(',') && beforeMessages.trim() === '{') {
356
+ // messages is first property, remove trailing comma if any
357
+ }
358
+
359
+ // Build the new i18n content without messages
360
+ let newI18nContent = i18nContent.substring(0, removeStart) + i18nContent.substring(removeEnd);
361
+
362
+ // Clean up: remove leading comma if messages was removed and left one
363
+ newI18nContent = newI18nContent.replace(/\{\s*,/, '{');
364
+
365
+ // Clean up: remove trailing comma before closing brace
366
+ newI18nContent = newI18nContent.replace(/,\s*\}/, ' }');
367
+
368
+ // Clean up empty i18n config: { } -> remove or simplify
369
+ const isEmptyI18n = newI18nContent.replace(/\s/g, '') === '{}';
370
+
371
+ if (isEmptyI18n) {
372
+ // Replace with empty object or keep minimal
373
+ newI18nContent = '{}';
374
+ }
375
+
376
+ // Replace in result (accounting for previous replacements)
377
+ const adjustedI18nStart = i18nStart + offset;
378
+ const adjustedI18nEnd = i18nEnd + 1 + offset;
379
+ result = result.substring(0, adjustedI18nStart) + newI18nContent + result.substring(adjustedI18nEnd);
380
+
381
+ // Update offset for next iteration
382
+ offset += newI18nContent.length - (i18nEnd + 1 - i18nStart);
383
+ changed = true;
384
+ }
385
+
386
+ return { result, changed };
387
+ }
388
+
32
389
  // Import transformations
33
390
  const IMPORT_TRANSFORMS = [
34
- // Main EditorJS import
391
+ // ============================================================================
392
+ // Blok default import → named import transformations
393
+ // These handle cases where users incorrectly use default imports with Blok
394
+ // (Blok only exports named exports, not a default export)
395
+ // ============================================================================
396
+
397
+ // Combined default + named imports from @jackuait/blok: import Blok, { BlokConfig } from '@jackuait/blok'
398
+ // → import { Blok, BlokConfig } from '@jackuait/blok'
399
+ // IMPORTANT: Must come before regular default import patterns to avoid partial matching
400
+ {
401
+ pattern: /import\s+Blok\s*,\s*\{\s*([^}]+?)\s*\}\s*from\s+['"]@jackuait\/blok['"]/g,
402
+ replacement: "import { Blok, $1 } from '@jackuait/blok'",
403
+ note: 'Converted combined default + named import to named import',
404
+ },
405
+ // Aliased combined: import Editor, { BlokConfig } from '@jackuait/blok'
406
+ {
407
+ pattern: /import\s+(\w+)\s*,\s*\{\s*([^}]+?)\s*\}\s*from\s+['"]@jackuait\/blok['"]/g,
408
+ replacement: "import { Blok as $1, $2 } from '@jackuait/blok'",
409
+ note: 'Converted aliased combined default + named import to named import',
410
+ },
411
+ // Default import: import Blok from '@jackuait/blok' → import { Blok } from '@jackuait/blok'
412
+ {
413
+ pattern: /import\s+Blok\s+from\s+['"]@jackuait\/blok['"]/g,
414
+ replacement: "import { Blok } from '@jackuait/blok'",
415
+ note: 'Converted default import to named import',
416
+ },
417
+ // Aliased default import: import Editor from '@jackuait/blok' → import { Blok as Editor } from '@jackuait/blok'
418
+ // Must check it's not followed by /tools or /types to avoid matching subpath imports
419
+ {
420
+ pattern: /import\s+(\w+)\s+from\s+['"]@jackuait\/blok['"](?!\/)/g,
421
+ replacement: "import { Blok as $1 } from '@jackuait/blok'",
422
+ note: 'Converted aliased default import to named import',
423
+ },
424
+ // Type-only default import: import type Blok from '@jackuait/blok'
425
+ {
426
+ pattern: /import\s+type\s+Blok\s+from\s+['"]@jackuait\/blok['"]/g,
427
+ replacement: "import type { Blok } from '@jackuait/blok'",
428
+ note: 'Converted type-only default import to named import',
429
+ },
430
+ // Type-only aliased default import: import type Editor from '@jackuait/blok'
431
+ {
432
+ pattern: /import\s+type\s+(\w+)\s+from\s+['"]@jackuait\/blok['"](?!\/)/g,
433
+ replacement: "import type { Blok as $1 } from '@jackuait/blok'",
434
+ note: 'Converted type-only aliased default import to named import',
435
+ },
436
+ // Namespace import: import * as Blok from '@jackuait/blok' → import { Blok } from '@jackuait/blok'
437
+ {
438
+ pattern: /import\s+\*\s+as\s+Blok\s+from\s+['"]@jackuait\/blok['"]/g,
439
+ replacement: "import { Blok } from '@jackuait/blok'",
440
+ note: 'Converted namespace import to named import',
441
+ },
442
+ // Aliased namespace import: import * as Editor from '@jackuait/blok'
443
+ {
444
+ pattern: /import\s+\*\s+as\s+(\w+)\s+from\s+['"]@jackuait\/blok['"](?!\/)/g,
445
+ replacement: "import { Blok as $1 } from '@jackuait/blok'",
446
+ note: 'Converted namespace import to named import with alias',
447
+ },
448
+ // Dynamic import with destructuring: const { default: Editor } = await import('@jackuait/blok')
449
+ {
450
+ pattern: /\{\s*default\s*:\s*(\w+)\s*\}\s*=\s*await\s+import\s*\(\s*['"]@jackuait\/blok['"]\s*\)/g,
451
+ replacement: "{ Blok: $1 } = await import('@jackuait/blok')",
452
+ note: 'Converted destructured dynamic import',
453
+ },
454
+ // Re-export default as named: export { default as Editor } from '@jackuait/blok'
455
+ {
456
+ pattern: /export\s+\{\s*default\s+as\s+(\w+)\s*\}\s*from\s+['"]@jackuait\/blok['"]/g,
457
+ replacement: "export { Blok as $1 } from '@jackuait/blok'",
458
+ note: 'Converted default re-export to named export',
459
+ },
460
+ // Re-export default: export { default } from '@jackuait/blok'
461
+ {
462
+ pattern: /export\s+\{\s*default\s*\}\s*from\s+['"]@jackuait\/blok['"]/g,
463
+ replacement: "export { Blok } from '@jackuait/blok'",
464
+ note: 'Converted default re-export',
465
+ },
466
+ // Require with .default access: require('@jackuait/blok').default
467
+ {
468
+ pattern: /require\s*\(\s*['"]@jackuait\/blok['"]\s*\)\.default/g,
469
+ replacement: "require('@jackuait/blok').Blok",
470
+ note: 'Converted require().default to require().Blok',
471
+ },
472
+ // Destructured require with default: const { default: Editor } = require('@jackuait/blok')
473
+ {
474
+ pattern: /const\s+\{\s*default\s*:\s*(\w+)\s*\}\s*=\s*require\s*\(\s*['"]@jackuait\/blok['"]\s*\)/g,
475
+ replacement: "const { Blok: $1 } = require('@jackuait/blok')",
476
+ note: 'Converted destructured default require to named require',
477
+ },
478
+
479
+ // ============================================================================
480
+ // EditorJS → Blok transformations
481
+ // ============================================================================
482
+
483
+ // EditorJS subpath imports (e.g., @editorjs/editorjs/types -> @jackuait/blok/types)
484
+ {
485
+ pattern: /from\s+['"]@editorjs\/editorjs\/([^'"]+)['"]/g,
486
+ replacement: "from '@jackuait/blok/$1'",
487
+ },
488
+ {
489
+ pattern: /require\s*\(\s*['"]@editorjs\/editorjs\/([^'"]+)['"]\s*\)/g,
490
+ replacement: "require('@jackuait/blok/$1')",
491
+ },
492
+ // Combined default + named imports: import EditorJS, { EditorConfig } from '@editorjs/editorjs'
493
+ // → import { Blok, EditorConfig } from '@jackuait/blok' (EditorConfig → BlokConfig handled by TYPE_TRANSFORMS)
494
+ // IMPORTANT: Must come before regular default import patterns to avoid partial matching
495
+ {
496
+ pattern: /import\s+EditorJS\s*,\s*\{\s*([^}]+?)\s*\}\s*from\s+['"]@editorjs\/editorjs['"]/g,
497
+ replacement: "import { Blok, $1 } from '@jackuait/blok'",
498
+ note: 'Converted combined default + named import',
499
+ },
500
+ // Aliased combined: import Editor, { EditorConfig } from '@editorjs/editorjs'
501
+ {
502
+ pattern: /import\s+(\w+)\s*,\s*\{\s*([^}]+?)\s*\}\s*from\s+['"]@editorjs\/editorjs['"]/g,
503
+ replacement: "import { Blok as $1, $2 } from '@jackuait/blok'",
504
+ note: 'Converted aliased combined default + named import',
505
+ },
506
+ // Main EditorJS default import -> Blok named import
507
+ // e.g., import EditorJS from '@editorjs/editorjs' -> import { Blok } from '@jackuait/blok'
35
508
  {
36
- pattern: /from\s+['"]@editorjs\/editorjs['"]/g,
37
- replacement: "from '@jackuait/blok'",
509
+ pattern: /import\s+EditorJS\s+from\s+['"]@editorjs\/editorjs['"]/g,
510
+ replacement: "import { Blok } from '@jackuait/blok'",
511
+ note: 'Converted default import to named import',
512
+ },
513
+ // EditorJS default import with alias -> Blok named import with alias
514
+ // e.g., import Editor from '@editorjs/editorjs' -> import { Blok as Editor } from '@jackuait/blok'
515
+ {
516
+ pattern: /import\s+(\w+)\s+from\s+['"]@editorjs\/editorjs['"]/g,
517
+ replacement: "import { Blok as $1 } from '@jackuait/blok'",
518
+ note: 'Converted default import to named import with alias',
519
+ },
520
+ // Destructured require with default: const { default: EditorJS } = require(...)
521
+ // IMPORTANT: Must come before generic require pattern to avoid partial matching
522
+ {
523
+ pattern: /const\s+\{\s*default\s*:\s*(\w+)\s*\}\s*=\s*require\s*\(\s*['"]@editorjs\/editorjs['"]\s*\)/g,
524
+ replacement: "const { Blok: $1 } = require('@jackuait/blok')",
525
+ note: 'Converted destructured default require to named require',
38
526
  },
39
527
  {
40
528
  pattern: /require\s*\(\s*['"]@editorjs\/editorjs['"]\s*\)/g,
41
- replacement: "require('@jackuait/blok')",
529
+ replacement: "require('@jackuait/blok').Blok",
530
+ },
531
+ // Namespace import: import * as EditorJS from '@editorjs/editorjs'
532
+ // → convert to named import (namespace pattern no longer needed with named exports)
533
+ {
534
+ pattern: /import\s+\*\s+as\s+(\w+)\s+from\s+['"]@editorjs\/editorjs['"]/g,
535
+ replacement: "import { Blok as $1 } from '@jackuait/blok'",
536
+ note: 'Converted namespace import to named import (namespace no longer needed)',
537
+ },
538
+ // Dynamic import with .then chaining that expects default (must come before generic dynamic import)
539
+ {
540
+ pattern: /import\s*\(\s*['"]@editorjs\/editorjs['"]\s*\)\s*\.then\s*\(\s*(\w+)\s*=>\s*\1\.default\s*\)/g,
541
+ replacement: "import('@jackuait/blok').then($1 => $1.Blok)",
542
+ note: 'Converted dynamic import .then(m => m.default) pattern',
543
+ },
544
+ // Dynamic import with destructuring: const { default: Editor } = await import('@editorjs/editorjs')
545
+ // IMPORTANT: Must come before generic dynamic import to avoid partial matching
546
+ {
547
+ pattern: /\{\s*default\s*:\s*(\w+)\s*\}\s*=\s*await\s+import\s*\(\s*['"]@editorjs\/editorjs['"]\s*\)/g,
548
+ replacement: "{ Blok: $1 } = await import('@jackuait/blok')",
549
+ note: 'Converted destructured dynamic import',
550
+ },
551
+ // Dynamic import: import('@editorjs/editorjs')
552
+ {
553
+ pattern: /import\s*\(\s*['"]@editorjs\/editorjs['"]\s*\)/g,
554
+ replacement: "import('@jackuait/blok').then(m => ({ default: m.Blok }))",
555
+ note: 'Converted dynamic import (wraps named export as default for compatibility)',
556
+ },
557
+ // Type-only default import: import type EditorJS from '@editorjs/editorjs'
558
+ {
559
+ pattern: /import\s+type\s+(\w+)\s+from\s+['"]@editorjs\/editorjs['"]/g,
560
+ replacement: "import type { Blok as $1 } from '@jackuait/blok'",
561
+ note: 'Converted type-only default import to named import',
562
+ },
563
+ // Re-export default as named: export { default as Editor } from '@editorjs/editorjs'
564
+ {
565
+ pattern: /export\s+\{\s*default\s+as\s+(\w+)\s*\}\s*from\s+['"]@editorjs\/editorjs['"]/g,
566
+ replacement: "export { Blok as $1 } from '@jackuait/blok'",
567
+ note: 'Converted default re-export to named export',
568
+ },
569
+ // Re-export default: export { default } from '@editorjs/editorjs'
570
+ {
571
+ pattern: /export\s+\{\s*default\s*\}\s*from\s+['"]@editorjs\/editorjs['"]/g,
572
+ replacement: "export { Blok } from '@jackuait/blok'",
573
+ note: 'Converted default re-export',
574
+ },
575
+ // Header tool (now bundled) - import directly from @jackuait/blok
576
+ {
577
+ pattern: /import\s+Header\s+from\s+['"]@editorjs\/header['"];?\n?/g,
578
+ replacement: "import { Header } from '@jackuait/blok';\n",
579
+ note: 'Header tool is now bundled with Blok',
42
580
  },
43
- // Header tool (now bundled)
44
581
  {
45
582
  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',
583
+ replacement: "import { Header as $1 } from '@jackuait/blok';\n",
584
+ note: 'Header tool is now bundled with Blok (aliased)',
585
+ },
586
+ // Paragraph tool (now bundled) - import directly from @jackuait/blok
587
+ {
588
+ pattern: /import\s+Paragraph\s+from\s+['"]@editorjs\/paragraph['"];?\n?/g,
589
+ replacement: "import { Paragraph } from '@jackuait/blok';\n",
590
+ note: 'Paragraph tool is now bundled with Blok',
48
591
  },
49
- // Paragraph tool (now bundled)
50
592
  {
51
593
  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',
594
+ replacement: "import { Paragraph as $1 } from '@jackuait/blok';\n",
595
+ note: 'Paragraph tool is now bundled with Blok (aliased)',
596
+ },
597
+ // List tool (now bundled) - import directly from @jackuait/blok
598
+ {
599
+ pattern: /import\s+List\s+from\s+['"]@editorjs\/list['"];?\n?/g,
600
+ replacement: "import { List } from '@jackuait/blok';\n",
601
+ note: 'List tool is now bundled with Blok',
602
+ },
603
+ {
604
+ pattern: /import\s+(\w+)\s+from\s+['"]@editorjs\/list['"];?\n?/g,
605
+ replacement: "import { List as $1 } from '@jackuait/blok';\n",
606
+ note: 'List tool is now bundled with Blok (aliased)',
607
+ },
608
+ // Tool require statements (CommonJS)
609
+ // Header require: const Header = require('@editorjs/header')
610
+ {
611
+ pattern: /const\s+Header\s*=\s*require\s*\(\s*['"]@editorjs\/header['"]\s*\)/g,
612
+ replacement: "const { Header } = require('@jackuait/blok')",
613
+ note: 'Header tool is now bundled with Blok',
614
+ },
615
+ {
616
+ pattern: /const\s+(\w+)\s*=\s*require\s*\(\s*['"]@editorjs\/header['"]\s*\)/g,
617
+ replacement: "const { Header: $1 } = require('@jackuait/blok')",
618
+ note: 'Header tool is now bundled with Blok (aliased)',
619
+ },
620
+ // Paragraph require
621
+ {
622
+ pattern: /const\s+Paragraph\s*=\s*require\s*\(\s*['"]@editorjs\/paragraph['"]\s*\)/g,
623
+ replacement: "const { Paragraph } = require('@jackuait/blok')",
624
+ note: 'Paragraph tool is now bundled with Blok',
625
+ },
626
+ {
627
+ pattern: /const\s+(\w+)\s*=\s*require\s*\(\s*['"]@editorjs\/paragraph['"]\s*\)/g,
628
+ replacement: "const { Paragraph: $1 } = require('@jackuait/blok')",
629
+ note: 'Paragraph tool is now bundled with Blok (aliased)',
630
+ },
631
+ // List require
632
+ {
633
+ pattern: /const\s+List\s*=\s*require\s*\(\s*['"]@editorjs\/list['"]\s*\)/g,
634
+ replacement: "const { List } = require('@jackuait/blok')",
635
+ note: 'List tool is now bundled with Blok',
636
+ },
637
+ {
638
+ pattern: /const\s+(\w+)\s*=\s*require\s*\(\s*['"]@editorjs\/list['"]\s*\)/g,
639
+ replacement: "const { List: $1 } = require('@jackuait/blok')",
640
+ note: 'List tool is now bundled with Blok (aliased)',
54
641
  },
55
642
  ];
56
643
 
@@ -122,13 +709,13 @@ const CSS_CLASS_TRANSFORMS = [
122
709
  { pattern: /(['"`])ce-inline-toolbar(['"`])/g, replacement: '$1data-blok-testid="inline-toolbar"$2' },
123
710
 
124
711
  // Popover classes (ce-popover)
125
- { pattern: /\.ce-popover--opened(?![\w-])/g, replacement: '[data-blok-popover][data-blok-opened="true"]' },
712
+ { pattern: /\.ce-popover--opened(?![\w-])/g, replacement: '[data-blok-popover-opened="true"]' },
126
713
  { pattern: /\.ce-popover__container(?![\w-])/g, replacement: '[data-blok-popover-container]' },
127
714
  { pattern: /\.ce-popover-item--focused(?![\w-])/g, replacement: '[data-blok-focused="true"]' },
128
715
  { pattern: /\.ce-popover-item(?![\w-])/g, replacement: '[data-blok-testid="popover-item"]' },
129
716
  { pattern: /\.ce-popover(?![\w-])/g, replacement: '[data-blok-popover]' },
130
717
  // Without dot prefix
131
- { pattern: /(['"`])ce-popover--opened(['"`])/g, replacement: '$1data-blok-popover$2' },
718
+ { pattern: /(['"`])ce-popover--opened(['"`])/g, replacement: '$1data-blok-popover-opened$2' },
132
719
  { pattern: /(['"`])ce-popover__container(['"`])/g, replacement: '$1data-blok-popover-container$2' },
133
720
  { pattern: /(['"`])ce-popover-item--focused(['"`])/g, replacement: '$1data-blok-focused$2' },
134
721
  { pattern: /(['"`])ce-popover-item(['"`])/g, replacement: '$1data-blok-testid="popover-item"$2' },
@@ -166,17 +753,17 @@ const CSS_CLASS_TRANSFORMS = [
166
753
  { pattern: /(['"`])ce-ragged-right(['"`])/g, replacement: '$1data-blok-ragged-right$2' },
167
754
 
168
755
  // Popover item states and icons
169
- { pattern: /\.ce-popover-item--confirmation(?![\w-])/g, replacement: '[data-blok-confirmation="true"]' },
756
+ { pattern: /\.ce-popover-item--confirmation(?![\w-])/g, replacement: '[data-blok-popover-item-confirmation="true"]' },
170
757
  { pattern: /\.ce-popover-item__icon(?![\w-])/g, replacement: '[data-blok-testid="popover-item-icon"]' },
171
758
  { pattern: /\.ce-popover-item__icon--tool(?![\w-])/g, replacement: '[data-blok-testid="popover-item-icon-tool"]' },
172
- { pattern: /(['"`])ce-popover-item--confirmation(['"`])/g, replacement: '$1data-blok-confirmation$2' },
759
+ { pattern: /(['"`])ce-popover-item--confirmation(['"`])/g, replacement: '$1data-blok-popover-item-confirmation$2' },
173
760
  { pattern: /(['"`])ce-popover-item__icon(['"`])/g, replacement: '$1data-blok-testid="popover-item-icon"$2' },
174
761
  { pattern: /(['"`])ce-popover-item__icon--tool(['"`])/g, replacement: '$1data-blok-testid="popover-item-icon-tool"$2' },
175
762
 
176
763
  // Toolbox classes (ce-toolbox)
177
- { pattern: /\.ce-toolbox--opened(?![\w-])/g, replacement: '[data-blok-toolbox][data-blok-opened="true"]' },
764
+ { pattern: /\.ce-toolbox--opened(?![\w-])/g, replacement: '[data-blok-toolbox-opened="true"]' },
178
765
  { pattern: /\.ce-toolbox(?![\w-])/g, replacement: '[data-blok-toolbox]' },
179
- { pattern: /(['"`])ce-toolbox--opened(['"`])/g, replacement: '$1data-blok-toolbox$2' },
766
+ { pattern: /(['"`])ce-toolbox--opened(['"`])/g, replacement: '$1data-blok-toolbox-opened$2' },
180
767
  { pattern: /(['"`])ce-toolbox(['"`])/g, replacement: '$1data-blok-toolbox$2' },
181
768
 
182
769
  // CDX list classes (cdx-list)
@@ -240,19 +827,141 @@ const HOLDER_TRANSFORMS = [
240
827
  { pattern: /getElementById\s*\(\s*['"]editorjs['"]\s*\)/g, replacement: "getElementById('blok')" },
241
828
  ];
242
829
 
243
- // Bundled tools - add new tools here as they are bundled with Blok
244
- const BUNDLED_TOOLS = ['Header', 'Paragraph'];
830
+ // Bundled tools - these are now in a separate entry point (@jackuait/blok/tools)
831
+ const BUNDLED_TOOLS = ['Header', 'Paragraph', 'List'];
832
+
833
+ // Inline tools - also in the tools entry point
834
+ const INLINE_TOOLS = ['Bold', 'Italic', 'Link'];
835
+
836
+ // Internal tools - these are bundled with core and don't need to be imported
837
+ const INTERNAL_TOOLS = ['Convert', 'Delete'];
838
+
839
+ // All tools that should be imported from @jackuait/blok/tools
840
+ const ALL_TOOLS = [...BUNDLED_TOOLS, ...INLINE_TOOLS];
245
841
 
246
842
  // Tool configuration transformations
843
+ // Note: With named exports, tools are imported directly (e.g., { Header } from '@jackuait/blok/tools')
844
+ // so the tool class references don't need the Blok. prefix anymore
247
845
  const TOOL_CONFIG_TRANSFORMS = [
248
- // Handle class property syntax
249
- { pattern: /class:\s*Header(?!Config)/g, replacement: 'class: Blok.Header' },
250
- { pattern: /class:\s*Paragraph(?!Config)/g, replacement: 'class: Blok.Paragraph' },
251
- // Handle standalone tool references in tools config (e.g., `paragraph: Paragraph`)
252
- { pattern: /(\bheader\s*:\s*)Header(?!Config)(?=\s*[,}\n])/g, replacement: '$1Blok.Header' },
253
- { pattern: /(\bparagraph\s*:\s*)Paragraph(?!Config)(?=\s*[,}\n])/g, replacement: '$1Blok.Paragraph' },
846
+ // Convert old Blok.Header style to direct Header reference (for existing Blok users upgrading)
847
+ // Block tools
848
+ { pattern: /class:\s*Blok\.Header(?!Config)/g, replacement: 'class: Header' },
849
+ { pattern: /class:\s*Blok\.Paragraph(?!Config)/g, replacement: 'class: Paragraph' },
850
+ { pattern: /class:\s*Blok\.List(?!Config|Item)/g, replacement: 'class: List' },
851
+ { pattern: /(\bheader\s*:\s*)Blok\.Header(?!Config)(?=\s*[,}\n])/g, replacement: '$1Header' },
852
+ { pattern: /(\bparagraph\s*:\s*)Blok\.Paragraph(?!Config)(?=\s*[,}\n])/g, replacement: '$1Paragraph' },
853
+ { pattern: /(\blist\s*:\s*)Blok\.List(?!Config|Item)(?=\s*[,}\n])/g, replacement: '$1List' },
854
+ // Inline tools
855
+ { pattern: /class:\s*Blok\.Bold(?!Config)/g, replacement: 'class: Bold' },
856
+ { pattern: /class:\s*Blok\.Italic(?!Config)/g, replacement: 'class: Italic' },
857
+ { pattern: /class:\s*Blok\.Link(?!Config)/g, replacement: 'class: Link' },
858
+ { pattern: /(\bbold\s*:\s*)Blok\.Bold(?!Config)(?=\s*[,}\n])/g, replacement: '$1Bold' },
859
+ { pattern: /(\bitalic\s*:\s*)Blok\.Italic(?!Config)(?=\s*[,}\n])/g, replacement: '$1Italic' },
860
+ { pattern: /(\blink\s*:\s*)Blok\.Link(?!Config)(?=\s*[,}\n])/g, replacement: '$1Link' },
861
+ ];
862
+
863
+ // Transforms to remove internal tools configuration (Convert and Delete are now bundled)
864
+ const INTERNAL_TOOL_REMOVAL_TRANSFORMS = [
865
+ // Remove convertTo/convert configuration lines (with various formats)
866
+ { pattern: /,?\s*convert(?:To)?\s*:\s*\{\s*class\s*:\s*(?:Blok\.)?Convert\s*\}\s*,?/g, replacement: '' },
867
+ { pattern: /,?\s*convert(?:To)?\s*:\s*(?:Blok\.)?Convert\s*,?/g, replacement: '' },
868
+ // Remove delete configuration lines
869
+ { pattern: /,?\s*delete\s*:\s*\{\s*class\s*:\s*(?:Blok\.)?Delete(?:Tune)?\s*\}\s*,?/g, replacement: '' },
870
+ { pattern: /,?\s*delete\s*:\s*(?:Blok\.)?Delete(?:Tune)?\s*,?/g, replacement: '' },
871
+ ];
872
+
873
+ // ============================================================================
874
+ // Blok Modular Import Transformations (Strategy 5)
875
+ // ============================================================================
876
+
877
+ /**
878
+ * Transforms for migrating old Blok imports to the new modular structure.
879
+ * After Strategy 3/4, tools are in a separate entry point (@jackuait/blok/tools).
880
+ *
881
+ * Detects and transforms patterns like:
882
+ * import { Blok, Header, Paragraph } from '@jackuait/blok'
883
+ * To:
884
+ * import { Blok } from '@jackuait/blok';
885
+ * import { Header, Paragraph } from '@jackuait/blok/tools';
886
+ */
887
+ const BLOK_MODULAR_IMPORT_TRANSFORMS = [
888
+ // Transform tool imports from old @jackuait/blok to @jackuait/blok/tools
889
+ // These patterns match tools being imported from the main entry point
890
+ // The actual splitting is done in splitBlokImports() function below
254
891
  ];
255
892
 
893
+ /**
894
+ * Splits a combined @jackuait/blok import that contains both core exports and tools
895
+ * into separate imports from the correct entry points.
896
+ *
897
+ * @param {string} content - The file content
898
+ * @returns {{result: string, changed: boolean}} Transformed content and change flag
899
+ */
900
+ function splitBlokImports(content) {
901
+ // Pattern to find @jackuait/blok named imports
902
+ const blokImportPattern = /import\s*\{\s*([^}]+)\s*\}\s*from\s*['"]@jackuait\/blok['"];?/g;
903
+ let result = content;
904
+ let changed = false;
905
+
906
+ // Collect all matches first to avoid regex state issues
907
+ const matches = [];
908
+ let match;
909
+ while ((match = blokImportPattern.exec(content)) !== null) {
910
+ matches.push({
911
+ fullMatch: match[0],
912
+ imports: match[1],
913
+ index: match.index,
914
+ });
915
+ }
916
+
917
+ // Process matches in reverse order to preserve indices
918
+ for (let i = matches.length - 1; i >= 0; i--) {
919
+ const { fullMatch, imports } = matches[i];
920
+
921
+ // Parse the imports
922
+ const importList = imports.split(',').map(s => s.trim()).filter(s => s.length > 0);
923
+
924
+ // Separate core imports from tool imports
925
+ const coreImports = [];
926
+ const toolImports = [];
927
+
928
+ for (const imp of importList) {
929
+ // Handle aliased imports: "Header as MyHeader"
930
+ const importName = imp.split(/\s+as\s+/)[0].trim();
931
+
932
+ if (ALL_TOOLS.includes(importName)) {
933
+ toolImports.push(imp);
934
+ } else {
935
+ coreImports.push(imp);
936
+ }
937
+ }
938
+
939
+ // Only transform if there are tool imports mixed with core imports
940
+ // or if there are only tool imports (they should come from /tools)
941
+ if (toolImports.length > 0) {
942
+ let newImports = '';
943
+
944
+ if (coreImports.length > 0) {
945
+ newImports += `import { ${coreImports.join(', ')} } from '@jackuait/blok';\n`;
946
+ }
947
+
948
+ if (toolImports.length > 0) {
949
+ newImports += `import { ${toolImports.join(', ')} } from '@jackuait/blok/tools';`;
950
+ }
951
+
952
+ // Preserve trailing newline if original had one
953
+ if (!fullMatch.endsWith(';')) {
954
+ newImports = newImports.replace(/;$/, '');
955
+ }
956
+
957
+ result = result.replace(fullMatch, newImports);
958
+ changed = true;
959
+ }
960
+ }
961
+
962
+ return { result, changed };
963
+ }
964
+
256
965
  // Text transformations for "EditorJS" string references
257
966
  const TEXT_TRANSFORMS = [
258
967
  // Replace exact "EditorJS" text (preserves case-sensitive matching)
@@ -321,108 +1030,185 @@ function applyTransforms(content, transforms, fileName) {
321
1030
  }
322
1031
 
323
1032
  /**
324
- * Ensures that Blok is properly imported when bundled tools (Blok.Header, Blok.Paragraph, etc.) are used.
325
- * This function checks if the content uses any Blok.* tool references and ensures there's a proper import.
1033
+ * Ensures that bundled tools (Header, Paragraph, List) are properly imported when used.
1034
+ * Since Blok now uses named exports, tools are imported directly.
1035
+ *
1036
+ * @deprecated Use ensureToolsImport instead, which imports from @jackuait/blok/tools
326
1037
  *
327
1038
  * Handles the following scenarios:
328
- * 1. No existing @jackuait/blok import -> adds `import Blok from '@jackuait/blok'`
329
- * 2. Named imports only (e.g., `import { BlokConfig } from '@jackuait/blok'`) -> adds Blok default import
330
- * 3. Default import with different name -> adds Blok to named imports
331
- * 4. Already has Blok default import -> no changes needed
1039
+ * 1. Tool used without import -> adds `import { Header, Paragraph, List } from '@jackuait/blok'`
1040
+ * 2. Existing @jackuait/blok import without tools -> adds tools to named imports
332
1041
  */
333
1042
  function ensureBlokImport(content) {
334
- // Check if content uses any Blok.* tool (e.g., Blok.Header, Blok.Paragraph)
335
- const blokToolPattern = new RegExp(`Blok\\.(${BUNDLED_TOOLS.join('|')})`, 'g');
336
- const usesBlokTools = blokToolPattern.test(content);
1043
+ // Check which bundled tools are used in the content
1044
+ const usedTools = BUNDLED_TOOLS.filter(tool => {
1045
+ // Match tool usage in class: Tool or tool: Tool patterns, or direct Tool references
1046
+ const toolPattern = new RegExp(`\\b${tool}\\b`, 'g');
1047
+ return toolPattern.test(content);
1048
+ });
1049
+
1050
+ if (usedTools.length === 0) {
1051
+ return { result: content, changed: false };
1052
+ }
1053
+
1054
+ // Check for existing @jackuait/blok import
1055
+ const namedImportPattern = /import\s*\{([^}]+)\}\s*from\s*['"]@jackuait\/blok['"];?/;
1056
+ const namedMatch = content.match(namedImportPattern);
337
1057
 
338
- if (!usesBlokTools) {
1058
+ let result = content;
1059
+
1060
+ if (namedMatch) {
1061
+ // Check which tools are missing from the import
1062
+ const existingImports = namedMatch[1];
1063
+ const missingTools = usedTools.filter(tool => {
1064
+ const toolPattern = new RegExp(`\\b${tool}\\b`);
1065
+ return !toolPattern.test(existingImports);
1066
+ });
1067
+
1068
+ if (missingTools.length > 0) {
1069
+ // Add missing tools to existing import
1070
+ const newImports = `${missingTools.join(', ')}, ${existingImports.trim()}`;
1071
+ result = content.replace(
1072
+ namedImportPattern,
1073
+ `import { ${newImports} } from '@jackuait/blok';`
1074
+ );
1075
+ return { result, changed: true };
1076
+ }
339
1077
  return { result: content, changed: false };
340
1078
  }
341
1079
 
342
- // Check if Blok is already available as a default import
343
- // Matches: import Blok from '@jackuait/blok' or import Blok, { ... } from '@jackuait/blok'
344
- const hasBlokDefaultImport = /import\s+Blok\s*(?:,\s*\{[^}]*\}\s*)?from\s*['"]@jackuait\/blok['"]/.test(content);
1080
+ // No @jackuait/blok import at all -> add new import
1081
+ const importStatements = content.match(/^import\s+.+from\s+['"][^'"]+['"];?\s*$/gm);
1082
+ const newImport = `import { ${usedTools.join(', ')} } from '@jackuait/blok';`;
1083
+
1084
+ if (importStatements && importStatements.length > 0) {
1085
+ const lastImport = importStatements[importStatements.length - 1];
1086
+ const lastImportIndex = content.lastIndexOf(lastImport);
1087
+ const insertPosition = lastImportIndex + lastImport.length;
1088
+ result =
1089
+ content.slice(0, insertPosition) +
1090
+ '\n' + newImport +
1091
+ content.slice(insertPosition);
1092
+ } else {
1093
+ // No imports found, add at the very beginning (after shebang if present)
1094
+ const shebangMatch = content.match(/^#!.*\n/);
1095
+ if (shebangMatch) {
1096
+ result = shebangMatch[0] + newImport + '\n' + content.slice(shebangMatch[0].length);
1097
+ } else {
1098
+ result = newImport + '\n' + content;
1099
+ }
1100
+ }
1101
+
1102
+ return { result, changed: true };
1103
+ }
345
1104
 
346
- if (hasBlokDefaultImport) {
1105
+ /**
1106
+ * Ensures that tools are properly imported from @jackuait/blok/tools when used.
1107
+ * This is the modern approach after Strategy 3/4 where tools are in a separate entry point.
1108
+ *
1109
+ * Handles the following scenarios:
1110
+ * 1. Tool used without import -> adds `import { Header, ... } from '@jackuait/blok/tools'`
1111
+ * 2. Existing @jackuait/blok/tools import without tools -> adds tools to named imports
1112
+ */
1113
+ function ensureToolsImport(content) {
1114
+ // Check which tools are used in the content
1115
+ const usedTools = ALL_TOOLS.filter(tool => {
1116
+ // Match tool usage in class: Tool or tool: Tool patterns, or direct Tool references
1117
+ const toolPattern = new RegExp(`\\b${tool}\\b`, 'g');
1118
+ return toolPattern.test(content);
1119
+ });
1120
+
1121
+ if (usedTools.length === 0) {
347
1122
  return { result: content, changed: false };
348
1123
  }
349
1124
 
350
- // Check for existing @jackuait/blok import patterns
351
- const namedOnlyImportPattern = /import\s*\{([^}]+)\}\s*from\s*['"]@jackuait\/blok['"];?/;
352
- const defaultWithNamedPattern = /import\s+(\w+)\s*,\s*\{([^}]+)\}\s*from\s*['"]@jackuait\/blok['"];?/;
353
- const defaultOnlyPattern = /import\s+(\w+)\s+from\s*['"]@jackuait\/blok['"];?/;
1125
+ // Check for existing @jackuait/blok/tools import
1126
+ const toolsImportPattern = /import\s*\{([^}]+)\}\s*from\s*['"]@jackuait\/blok\/tools['"];?/;
1127
+ const toolsMatch = content.match(toolsImportPattern);
1128
+
1129
+ // Also check for @jackuait/blok import (legacy, will be split later)
1130
+ const mainImportPattern = /import\s*\{([^}]+)\}\s*from\s*['"]@jackuait\/blok['"];?/;
1131
+ const mainMatch = content.match(mainImportPattern);
354
1132
 
355
1133
  let result = content;
356
1134
 
357
- // Case 1: Named imports only -> add Blok default import
358
- // e.g., `import { BlokConfig } from '@jackuait/blok'` -> `import Blok, { BlokConfig } from '@jackuait/blok'`
359
- const namedOnlyMatch = content.match(namedOnlyImportPattern);
360
- if (namedOnlyMatch) {
361
- const namedImports = namedOnlyMatch[1];
362
- result = content.replace(
363
- namedOnlyImportPattern,
364
- `import Blok, {${namedImports}} from '@jackuait/blok';`
365
- );
366
- return { result, changed: true };
367
- }
368
-
369
- // Case 2: Default import with different name + named imports -> add Blok to named imports
370
- // e.g., `import Editor, { BlokConfig } from '@jackuait/blok'` -> `import Editor, { Blok, BlokConfig } from '@jackuait/blok'`
371
- const defaultWithNamedMatch = content.match(defaultWithNamedPattern);
372
- if (defaultWithNamedMatch) {
373
- const defaultName = defaultWithNamedMatch[1];
374
- const namedImports = defaultWithNamedMatch[2];
375
- // Check if Blok is already in named imports
376
- if (!/\bBlok\b/.test(namedImports)) {
1135
+ // Check if tools are already imported from /tools
1136
+ if (toolsMatch) {
1137
+ const existingToolsImports = toolsMatch[1];
1138
+ const missingTools = usedTools.filter(tool => {
1139
+ const toolPattern = new RegExp(`\\b${tool}\\b`);
1140
+ return !toolPattern.test(existingToolsImports);
1141
+ });
1142
+
1143
+ if (missingTools.length > 0) {
1144
+ // Add missing tools to existing /tools import
1145
+ const newImports = `${existingToolsImports.trim()}, ${missingTools.join(', ')}`;
377
1146
  result = content.replace(
378
- defaultWithNamedPattern,
379
- `import ${defaultName}, { Blok, ${namedImports.trim()} } from '@jackuait/blok';`
1147
+ toolsImportPattern,
1148
+ `import { ${newImports} } from '@jackuait/blok/tools';`
380
1149
  );
381
1150
  return { result, changed: true };
382
1151
  }
383
1152
  return { result: content, changed: false };
384
1153
  }
385
1154
 
386
- // Case 3: Default import only with different name -> add Blok to named imports
387
- // e.g., `import Editor from '@jackuait/blok'` -> `import Editor, { Blok } from '@jackuait/blok'`
388
- const defaultOnlyMatch = content.match(defaultOnlyPattern);
389
- if (defaultOnlyMatch) {
390
- const defaultName = defaultOnlyMatch[1];
391
- if (defaultName !== 'Blok') {
1155
+ // Check if tools are in the main import (will be split later by splitBlokImports)
1156
+ if (mainMatch) {
1157
+ const existingMainImports = mainMatch[1];
1158
+ const toolsInMain = usedTools.filter(tool => {
1159
+ const toolPattern = new RegExp(`\\b${tool}\\b`);
1160
+ return toolPattern.test(existingMainImports);
1161
+ });
1162
+
1163
+ // If all tools are already in main import, don't add duplicate
1164
+ // They'll be moved by splitBlokImports
1165
+ if (toolsInMain.length === usedTools.length) {
1166
+ return { result: content, changed: false };
1167
+ }
1168
+
1169
+ // Some tools missing from both imports - add them to main (will be split later)
1170
+ const missingTools = usedTools.filter(tool => {
1171
+ const toolPattern = new RegExp(`\\b${tool}\\b`);
1172
+ return !toolPattern.test(existingMainImports);
1173
+ });
1174
+
1175
+ if (missingTools.length > 0) {
1176
+ const newImports = `${missingTools.join(', ')}, ${existingMainImports.trim()}`;
392
1177
  result = content.replace(
393
- defaultOnlyPattern,
394
- `import ${defaultName}, { Blok } from '@jackuait/blok';`
1178
+ mainImportPattern,
1179
+ `import { ${newImports} } from '@jackuait/blok';`
395
1180
  );
396
1181
  return { result, changed: true };
397
1182
  }
398
1183
  return { result: content, changed: false };
399
1184
  }
400
1185
 
401
- // Case 4: No @jackuait/blok import at all -> add new import at the top (after any existing imports)
402
- // Find the last import statement to insert after it
1186
+ // No @jackuait/blok or @jackuait/blok/tools import -> add new /tools import
403
1187
  const importStatements = content.match(/^import\s+.+from\s+['"][^'"]+['"];?\s*$/gm);
1188
+ const newImport = `import { ${usedTools.join(', ')} } from '@jackuait/blok/tools';`;
1189
+
404
1190
  if (importStatements && importStatements.length > 0) {
405
1191
  const lastImport = importStatements[importStatements.length - 1];
406
1192
  const lastImportIndex = content.lastIndexOf(lastImport);
407
1193
  const insertPosition = lastImportIndex + lastImport.length;
408
1194
  result =
409
1195
  content.slice(0, insertPosition) +
410
- "\nimport Blok from '@jackuait/blok';" +
1196
+ '\n' + newImport +
411
1197
  content.slice(insertPosition);
412
1198
  } else {
413
1199
  // No imports found, add at the very beginning (after shebang if present)
414
1200
  const shebangMatch = content.match(/^#!.*\n/);
415
1201
  if (shebangMatch) {
416
- result = shebangMatch[0] + "import Blok from '@jackuait/blok';\n" + content.slice(shebangMatch[0].length);
1202
+ result = shebangMatch[0] + newImport + '\n' + content.slice(shebangMatch[0].length);
417
1203
  } else {
418
- result = "import Blok from '@jackuait/blok';\n" + content;
1204
+ result = newImport + '\n' + content;
419
1205
  }
420
1206
  }
421
1207
 
422
1208
  return { result, changed: true };
423
1209
  }
424
1210
 
425
- function transformFile(filePath, dryRun = false) {
1211
+ function transformFile(filePath, dryRun = false, useLibraryI18n = false) {
426
1212
  const content = fs.readFileSync(filePath, 'utf8');
427
1213
  let transformed = content;
428
1214
  const allChanges = [];
@@ -488,12 +1274,28 @@ function transformFile(filePath, dryRun = false) {
488
1274
  allChanges.push(...changes.map((c) => ({ ...c, category: 'tool-config' })));
489
1275
  }
490
1276
 
491
- // Ensure Blok is imported if bundled tools are used (JS/TS only)
1277
+ // Remove internal tools configuration (Convert and Delete are now bundled) (JS/TS only)
492
1278
  if (isJsFile) {
493
- const { result, changed } = ensureBlokImport(transformed);
1279
+ const { result, changes } = applyTransforms(transformed, INTERNAL_TOOL_REMOVAL_TRANSFORMS, filePath);
1280
+ transformed = result;
1281
+ allChanges.push(...changes.map((c) => ({ ...c, category: 'internal-tools-removal' })));
1282
+ }
1283
+
1284
+ // Ensure tools are imported if bundled tools are used (JS/TS only)
1285
+ if (isJsFile) {
1286
+ const { result, changed } = ensureToolsImport(transformed);
1287
+ if (changed) {
1288
+ transformed = result;
1289
+ allChanges.push({ category: 'imports', pattern: 'ensureToolsImport', count: 1, note: 'Added tools import from @jackuait/blok/tools' });
1290
+ }
1291
+ }
1292
+
1293
+ // Split combined @jackuait/blok imports into core and tools (JS/TS only)
1294
+ if (isJsFile) {
1295
+ const { result, changed } = splitBlokImports(transformed);
494
1296
  if (changed) {
495
1297
  transformed = result;
496
- allChanges.push({ category: 'imports', pattern: 'ensureBlokImport', count: 1, note: 'Added Blok import for bundled tools' });
1298
+ allChanges.push({ category: 'imports', pattern: 'splitBlokImports', count: 1, note: 'Split tools into separate @jackuait/blok/tools import' });
497
1299
  }
498
1300
  }
499
1301
 
@@ -504,6 +1306,25 @@ function transformFile(filePath, dryRun = false) {
504
1306
  allChanges.push(...changes.map((c) => ({ ...c, category: 'text' })));
505
1307
  }
506
1308
 
1309
+ // Apply i18n transforms (JS/TS only)
1310
+ if (isJsFile) {
1311
+ if (useLibraryI18n) {
1312
+ // Remove custom messages to use Blok's built-in translations
1313
+ const { result, changed } = removeI18nMessages(transformed);
1314
+ if (changed) {
1315
+ transformed = result;
1316
+ allChanges.push({ category: 'i18n', pattern: 'removeI18nMessages', count: 1, note: 'Removed custom i18n messages to use library translations' });
1317
+ }
1318
+ } else {
1319
+ // Flatten nested messages to dot-notation
1320
+ const { result, changed } = transformI18nConfig(transformed);
1321
+ if (changed) {
1322
+ transformed = result;
1323
+ allChanges.push({ category: 'i18n', pattern: 'flattenI18nMessages', count: 1, note: 'Flattened nested i18n messages to dot-notation' });
1324
+ }
1325
+ }
1326
+ }
1327
+
507
1328
  const hasChanges = transformed !== content;
508
1329
 
509
1330
  if (hasChanges && !dryRun) {
@@ -527,7 +1348,7 @@ function updatePackageJson(packageJsonPath, dryRun = false) {
527
1348
  const changes = [];
528
1349
 
529
1350
  // Track dependencies to remove
530
- const depsToRemove = ['@editorjs/editorjs', '@editorjs/header', '@editorjs/paragraph'];
1351
+ const depsToRemove = ['@editorjs/editorjs', '@editorjs/header', '@editorjs/paragraph', '@editorjs/list'];
531
1352
  const devDepsToRemove = [...depsToRemove];
532
1353
 
533
1354
  // Check and update dependencies
@@ -580,9 +1401,10 @@ Arguments:
580
1401
  path Directory or file to transform (default: current directory)
581
1402
 
582
1403
  Options:
583
- --dry-run Show changes without modifying files
584
- --verbose Show detailed output for each file
585
- --help Show this help message
1404
+ --dry-run Show changes without modifying files
1405
+ --verbose Show detailed output for each file
1406
+ --use-library-i18n Remove custom i18n messages and use Blok's built-in translations
1407
+ --help Show this help message
586
1408
 
587
1409
  Examples:
588
1410
  npx -p @jackuait/blok migrate-from-editorjs ./src
@@ -590,17 +1412,24 @@ Examples:
590
1412
  npx -p @jackuait/blok migrate-from-editorjs . --verbose
591
1413
 
592
1414
  What this codemod does:
593
- • Transforms EditorJS imports to Blok imports
1415
+
1416
+ EditorJS Migration:
1417
+ • Transforms EditorJS imports to Blok named imports:
1418
+ - import EditorJS from '@editorjs/editorjs' → import { Blok } from '@jackuait/blok'
1419
+ - import Editor from '@editorjs/editorjs' → import { Blok as Editor } from '@jackuait/blok'
1420
+ • Updates bundled tool imports (Header, Paragraph, List):
1421
+ - import Header from '@editorjs/header' → import { Header } from '@jackuait/blok/tools'
1422
+ - import List from '@editorjs/list' → import { List } from '@jackuait/blok/tools'
594
1423
  • Updates type names (EditorConfig → BlokConfig)
595
1424
  • Replaces 'new EditorJS()' with 'new Blok()'
596
1425
  • Converts CSS class selectors to data attributes:
597
1426
  - .codex-editor* → [data-blok-editor], [data-blok-redactor], etc.
598
1427
  - .ce-block* → [data-blok-element], [data-blok-selected], etc.
599
1428
  - .ce-toolbar* → [data-blok-toolbar], [data-blok-settings-toggler], etc.
600
- - .ce-toolbox* → [data-blok-toolbox], etc.
1429
+ - .ce-toolbox* → [data-blok-toolbox], [data-blok-toolbox-opened], etc.
601
1430
  - .ce-inline-toolbar, .ce-inline-tool* → [data-blok-testid="inline-*"]
602
- - .ce-popover* → [data-blok-popover], [data-blok-popover-container], etc.
603
- - .ce-popover-item* → [data-blok-testid="popover-item*"], [data-blok-confirmation], etc.
1431
+ - .ce-popover* → [data-blok-popover], [data-blok-popover-opened], [data-blok-popover-container], etc.
1432
+ - .ce-popover-item* → [data-blok-testid="popover-item*"], [data-blok-popover-item-confirmation], etc.
604
1433
  - .ce-paragraph, .ce-header → [data-blok-tool="paragraph|header"]
605
1434
  - .cdx-list* → [data-blok-list], [data-blok-list-item], etc.
606
1435
  - .cdx-button, .cdx-input, .cdx-loader → [data-blok-button], etc.
@@ -608,8 +1437,32 @@ What this codemod does:
608
1437
  • Updates data attributes (data-id → data-blok-id)
609
1438
  • Changes default holder from 'editorjs' to 'blok'
610
1439
  • Updates package.json dependencies
611
- Converts bundled tool imports (Header, Paragraph)
612
- Ensures Blok is imported when using bundled tools (Blok.Header, etc.)
1440
+ Transforms nested i18n messages to flat dot-notation format with camelCase keys:
1441
+ - { ui: { toolbar: { Add: "..." } } } → { "ui.toolbar.add": "..." }
1442
+ - { toolNames: { "Nothing found": "..." } } → { "toolNames.nothingFound": "..." }
1443
+ • Maps changed i18n keys to their Blok equivalents (camelCase):
1444
+ - "Click to tune" → "clickToOpenMenu"
1445
+ - "or drag to move" → "dragToMove"
1446
+ - "Add" (toolbar) → "clickToAddBelow"
1447
+ - "Filter" (popover) → "search"
1448
+ - "Ordered List" → "numberedList"
1449
+ - "Unordered List" → "bulletedList"
1450
+ • Removes obsolete keys (moveUp/moveDown replaced with drag)
1451
+
1452
+ Old Blok → Modular Blok Migration:
1453
+ • Splits combined imports into core and tools:
1454
+ - import { Blok, Header } from '@jackuait/blok'
1455
+ → import { Blok } from '@jackuait/blok';
1456
+ import { Header } from '@jackuait/blok/tools';
1457
+ • Transforms static property access to direct references:
1458
+ - Blok.Header → Header (with import added)
1459
+ - Blok.Paragraph → Paragraph
1460
+ - Blok.List → List
1461
+ - Blok.Bold, Blok.Italic, Blok.Link → Bold, Italic, Link
1462
+ • Removes internal tools configuration (Convert and Delete are now bundled with core):
1463
+ - convertTo: { class: Convert } → removed (automatically available)
1464
+ - delete: { class: Delete } → removed (automatically available)
1465
+ • Ensures tools are imported from @jackuait/blok/tools when used
613
1466
 
614
1467
  Note: After running, you may need to manually:
615
1468
  • Update any custom tool implementations
@@ -624,6 +1477,7 @@ function main() {
624
1477
  // Parse arguments
625
1478
  const dryRun = args.includes('--dry-run');
626
1479
  const verbose = args.includes('--verbose');
1480
+ const useLibraryI18n = args.includes('--use-library-i18n');
627
1481
  const help = args.includes('--help') || args.includes('-h');
628
1482
 
629
1483
  global.isVerbose = verbose;
@@ -666,7 +1520,7 @@ function main() {
666
1520
 
667
1521
  // Process each file
668
1522
  files.forEach((filePath) => {
669
- const result = transformFile(filePath, dryRun);
1523
+ const result = transformFile(filePath, dryRun, useLibraryI18n);
670
1524
 
671
1525
  if (result.hasChanges) {
672
1526
  stats.filesModified++;
@@ -732,7 +1586,20 @@ module.exports = {
732
1586
  updatePackageJson,
733
1587
  applyTransforms,
734
1588
  ensureBlokImport,
1589
+ ensureToolsImport,
1590
+ splitBlokImports,
1591
+ normalizeKey,
1592
+ flattenI18nDictionary,
1593
+ transformI18nConfig,
1594
+ removeI18nMessages,
1595
+ parseObjectLiteral,
1596
+ findMatchingBrace,
1597
+ objectToString,
1598
+ I18N_KEY_MAPPINGS,
735
1599
  BUNDLED_TOOLS,
1600
+ INLINE_TOOLS,
1601
+ INTERNAL_TOOLS,
1602
+ ALL_TOOLS,
736
1603
  IMPORT_TRANSFORMS,
737
1604
  TYPE_TRANSFORMS,
738
1605
  CLASS_NAME_TRANSFORMS,
@@ -741,5 +1608,6 @@ module.exports = {
741
1608
  SELECTOR_TRANSFORMS,
742
1609
  HOLDER_TRANSFORMS,
743
1610
  TOOL_CONFIG_TRANSFORMS,
1611
+ INTERNAL_TOOL_REMOVAL_TRANSFORMS,
744
1612
  TEXT_TRANSFORMS,
745
1613
  };