@pie-element/cli 0.1.1 → 0.1.2-next.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (281) hide show
  1. package/README.md +48 -10
  2. package/dist/commands/dev/demo/index.d.ts +1 -1
  3. package/dist/commands/dev/demo/index.js +21 -0
  4. package/dist/commands/dev/demo/index.js.map +1 -1
  5. package/dist/commands/dev/test-bundler.d.ts +0 -1
  6. package/dist/commands/dev/test-bundler.js +2 -10
  7. package/dist/commands/dev/test-bundler.js.map +1 -1
  8. package/dist/commands/docs/generate.d.ts +16 -0
  9. package/dist/commands/docs/generate.js +150 -0
  10. package/dist/commands/docs/generate.js.map +1 -0
  11. package/dist/commands/docs/verify.d.ts +12 -0
  12. package/dist/commands/docs/verify.js +40 -0
  13. package/dist/commands/docs/verify.js.map +1 -0
  14. package/dist/commands/packages/create-controllers.d.ts +0 -1
  15. package/dist/commands/packages/create-controllers.js.map +1 -1
  16. package/dist/commands/packages/create-lib-react.d.ts +0 -1
  17. package/dist/commands/packages/create-lib-react.js +1 -3
  18. package/dist/commands/packages/create-lib-react.js.map +1 -1
  19. package/dist/commands/packages/create-react-elements.d.ts +0 -1
  20. package/dist/commands/packages/create-react-elements.js +2 -3
  21. package/dist/commands/packages/create-react-elements.js.map +1 -1
  22. package/dist/commands/packages/enable-publishing.d.ts +0 -1
  23. package/dist/commands/packages/enable-publishing.js +26 -23
  24. package/dist/commands/packages/enable-publishing.js.map +1 -1
  25. package/dist/commands/packages/init-synced-elements.d.ts +0 -1
  26. package/dist/commands/packages/init-synced-elements.js +1 -1
  27. package/dist/commands/packages/init-synced-elements.js.map +1 -1
  28. package/dist/commands/upstream/analyze-esm.d.ts +0 -1
  29. package/dist/commands/upstream/analyze-esm.js +5 -1
  30. package/dist/commands/upstream/analyze-esm.js.map +1 -1
  31. package/dist/commands/upstream/check.d.ts +0 -1
  32. package/dist/commands/upstream/check.js.map +1 -1
  33. package/dist/commands/upstream/deps.d.ts +0 -1
  34. package/dist/commands/upstream/deps.js.map +1 -1
  35. package/dist/commands/upstream/sync.d.ts +11 -2
  36. package/dist/commands/upstream/sync.js +364 -25
  37. package/dist/commands/upstream/sync.js.map +1 -1
  38. package/dist/commands/upstream/track.d.ts +0 -1
  39. package/dist/commands/upstream/track.js.map +1 -1
  40. package/dist/commands/upstream/update.d.ts +0 -1
  41. package/dist/commands/upstream/update.js +37 -7
  42. package/dist/commands/upstream/update.js.map +1 -1
  43. package/dist/commands/verify/controllers.d.ts +0 -1
  44. package/dist/commands/verify/controllers.js +213 -59
  45. package/dist/commands/verify/controllers.js.map +1 -1
  46. package/dist/commands/verify/dependency-integrity.d.ts +18 -0
  47. package/dist/commands/verify/dependency-integrity.js +147 -0
  48. package/dist/commands/verify/dependency-integrity.js.map +1 -0
  49. package/dist/commands/verify/react-build.d.ts +0 -1
  50. package/dist/commands/verify/react-build.js.map +1 -1
  51. package/dist/index.d.ts +0 -1
  52. package/dist/index.js.map +1 -1
  53. package/dist/lib/docs/contract-validator.d.ts +12 -0
  54. package/dist/lib/docs/contract-validator.js +38 -0
  55. package/dist/lib/docs/contract-validator.js.map +1 -0
  56. package/dist/lib/docs/contracts.d.ts +11 -0
  57. package/dist/lib/docs/contracts.js +241 -0
  58. package/dist/lib/docs/contracts.js.map +1 -0
  59. package/dist/lib/docs/discovery.d.ts +10 -0
  60. package/dist/lib/docs/discovery.js +91 -0
  61. package/dist/lib/docs/discovery.js.map +1 -0
  62. package/dist/lib/docs/generator.d.ts +14 -0
  63. package/dist/lib/docs/generator.js +450 -0
  64. package/dist/lib/docs/generator.js.map +1 -0
  65. package/dist/lib/docs/source-extractor.d.ts +2 -0
  66. package/dist/lib/docs/source-extractor.js +209 -0
  67. package/dist/lib/docs/source-extractor.js.map +1 -0
  68. package/dist/lib/docs/types.d.ts +77 -0
  69. package/dist/lib/docs/types.js +2 -0
  70. package/dist/lib/docs/types.js.map +1 -0
  71. package/dist/lib/upstream/repo-utils.d.ts +0 -1
  72. package/dist/lib/upstream/repo-utils.js.map +1 -1
  73. package/dist/lib/upstream/sync-compatibility.d.ts +0 -1
  74. package/dist/lib/upstream/sync-compatibility.js.map +1 -1
  75. package/dist/lib/upstream/sync-constants.d.ts +35 -7
  76. package/dist/lib/upstream/sync-constants.js +40 -11
  77. package/dist/lib/upstream/sync-constants.js.map +1 -1
  78. package/dist/lib/upstream/sync-controllers-strategy.d.ts +0 -1
  79. package/dist/lib/upstream/sync-controllers-strategy.js +14 -4
  80. package/dist/lib/upstream/sync-controllers-strategy.js.map +1 -1
  81. package/dist/lib/upstream/sync-demo-metadata.d.ts +0 -1
  82. package/dist/lib/upstream/sync-demo-metadata.js +1 -1
  83. package/dist/lib/upstream/sync-demo-metadata.js.map +1 -1
  84. package/dist/lib/upstream/sync-entry-discovery.d.ts +3 -0
  85. package/dist/lib/upstream/sync-entry-discovery.js +23 -0
  86. package/dist/lib/upstream/sync-entry-discovery.js.map +1 -0
  87. package/dist/lib/upstream/sync-externals.d.ts +7 -7
  88. package/dist/lib/upstream/sync-externals.js +51 -35
  89. package/dist/lib/upstream/sync-externals.js.map +1 -1
  90. package/dist/lib/upstream/sync-filesystem.d.ts +0 -1
  91. package/dist/lib/upstream/sync-filesystem.js.map +1 -1
  92. package/dist/lib/upstream/sync-imports.d.ts +82 -62
  93. package/dist/lib/upstream/sync-imports.js +658 -185
  94. package/dist/lib/upstream/sync-imports.js.map +1 -1
  95. package/dist/lib/upstream/sync-package-json.d.ts +0 -1
  96. package/dist/lib/upstream/sync-package-json.js +7 -0
  97. package/dist/lib/upstream/sync-package-json.js.map +1 -1
  98. package/dist/lib/upstream/sync-package-manager.d.ts +14 -4
  99. package/dist/lib/upstream/sync-package-manager.js +408 -72
  100. package/dist/lib/upstream/sync-package-manager.js.map +1 -1
  101. package/dist/lib/upstream/sync-pielib-strategy.d.ts +2 -1
  102. package/dist/lib/upstream/sync-pielib-strategy.js +228 -110
  103. package/dist/lib/upstream/sync-pielib-strategy.js.map +1 -1
  104. package/dist/lib/upstream/sync-presets.d.ts +53 -0
  105. package/dist/lib/upstream/sync-presets.js +203 -0
  106. package/dist/lib/upstream/sync-presets.js.map +1 -0
  107. package/dist/lib/upstream/sync-react-strategy.d.ts +6 -1
  108. package/dist/lib/upstream/sync-react-strategy.js +342 -134
  109. package/dist/lib/upstream/sync-react-strategy.js.map +1 -1
  110. package/dist/lib/upstream/sync-source-edit.d.ts +14 -0
  111. package/dist/lib/upstream/sync-source-edit.js +89 -0
  112. package/dist/lib/upstream/sync-source-edit.js.map +1 -0
  113. package/dist/lib/upstream/sync-source-tree.d.ts +22 -0
  114. package/dist/lib/upstream/sync-source-tree.js +99 -0
  115. package/dist/lib/upstream/sync-source-tree.js.map +1 -0
  116. package/dist/lib/upstream/sync-strategy.d.ts +1 -1
  117. package/dist/lib/upstream/sync-strategy.js.map +1 -1
  118. package/dist/lib/upstream/sync-summary.d.ts +0 -1
  119. package/dist/lib/upstream/sync-summary.js.map +1 -1
  120. package/dist/lib/upstream/sync-transforms.d.ts +13 -11
  121. package/dist/lib/upstream/sync-transforms.js +23 -17
  122. package/dist/lib/upstream/sync-transforms.js.map +1 -1
  123. package/dist/lib/upstream/sync-vite-config.d.ts +13 -1
  124. package/dist/lib/upstream/sync-vite-config.js +65 -46
  125. package/dist/lib/upstream/sync-vite-config.js.map +1 -1
  126. package/dist/utils/compatibility.d.ts +13 -1
  127. package/dist/utils/compatibility.js +143 -2
  128. package/dist/utils/compatibility.js.map +1 -1
  129. package/dist/utils/conversion.d.ts +0 -1
  130. package/dist/utils/conversion.js.map +1 -1
  131. package/dist/utils/dependency-integrity.d.ts +37 -0
  132. package/dist/utils/dependency-integrity.js +557 -0
  133. package/dist/utils/dependency-integrity.js.map +1 -0
  134. package/dist/utils/git.d.ts +0 -1
  135. package/dist/utils/git.js.map +1 -1
  136. package/dist/utils/logger.d.ts +0 -1
  137. package/dist/utils/logger.js.map +1 -1
  138. package/dist/utils/package-json.d.ts +0 -1
  139. package/dist/utils/package-json.js.map +1 -1
  140. package/package.json +24 -12
  141. package/.compatibility/report.json +0 -2491
  142. package/.turbo/turbo-build.log +0 -1
  143. package/CHANGELOG.md +0 -8
  144. package/REFACTORING.md +0 -131
  145. package/bin/dev.js +0 -5
  146. package/dist/commands/dev/demo/index.d.ts.map +0 -1
  147. package/dist/commands/dev/test-bundler.d.ts.map +0 -1
  148. package/dist/commands/mathquill/migrate.d.ts +0 -20
  149. package/dist/commands/mathquill/migrate.d.ts.map +0 -1
  150. package/dist/commands/mathquill/migrate.js +0 -240
  151. package/dist/commands/mathquill/migrate.js.map +0 -1
  152. package/dist/commands/mathquill/status.d.ts +0 -20
  153. package/dist/commands/mathquill/status.d.ts.map +0 -1
  154. package/dist/commands/mathquill/status.js +0 -231
  155. package/dist/commands/mathquill/status.js.map +0 -1
  156. package/dist/commands/packages/create-controllers.d.ts.map +0 -1
  157. package/dist/commands/packages/create-lib-react.d.ts.map +0 -1
  158. package/dist/commands/packages/create-react-elements.d.ts.map +0 -1
  159. package/dist/commands/packages/enable-publishing.d.ts.map +0 -1
  160. package/dist/commands/packages/init-synced-elements.d.ts.map +0 -1
  161. package/dist/commands/upstream/analyze-esm.d.ts.map +0 -1
  162. package/dist/commands/upstream/check.d.ts.map +0 -1
  163. package/dist/commands/upstream/deps.d.ts.map +0 -1
  164. package/dist/commands/upstream/sync-mathquill.d.ts +0 -12
  165. package/dist/commands/upstream/sync-mathquill.d.ts.map +0 -1
  166. package/dist/commands/upstream/sync-mathquill.js +0 -97
  167. package/dist/commands/upstream/sync-mathquill.js.map +0 -1
  168. package/dist/commands/upstream/sync.d.ts.map +0 -1
  169. package/dist/commands/upstream/track.d.ts.map +0 -1
  170. package/dist/commands/upstream/update.d.ts.map +0 -1
  171. package/dist/commands/verify/controllers.d.ts.map +0 -1
  172. package/dist/commands/verify/react-build.d.ts.map +0 -1
  173. package/dist/index.d.ts.map +0 -1
  174. package/dist/lib/mathquill/extension-generator.d.ts +0 -9
  175. package/dist/lib/mathquill/extension-generator.d.ts.map +0 -1
  176. package/dist/lib/mathquill/extension-generator.js +0 -107
  177. package/dist/lib/mathquill/extension-generator.js.map +0 -1
  178. package/dist/lib/mathquill/khan-extractor.d.ts +0 -16
  179. package/dist/lib/mathquill/khan-extractor.d.ts.map +0 -1
  180. package/dist/lib/mathquill/khan-extractor.js +0 -148
  181. package/dist/lib/mathquill/khan-extractor.js.map +0 -1
  182. package/dist/lib/mathquill/learnosity-extractor.d.ts +0 -15
  183. package/dist/lib/mathquill/learnosity-extractor.d.ts.map +0 -1
  184. package/dist/lib/mathquill/learnosity-extractor.js +0 -170
  185. package/dist/lib/mathquill/learnosity-extractor.js.map +0 -1
  186. package/dist/lib/mathquill/migration-orchestrator.d.ts +0 -77
  187. package/dist/lib/mathquill/migration-orchestrator.d.ts.map +0 -1
  188. package/dist/lib/mathquill/migration-orchestrator.js +0 -391
  189. package/dist/lib/mathquill/migration-orchestrator.js.map +0 -1
  190. package/dist/lib/mathquill/package-updater.d.ts +0 -9
  191. package/dist/lib/mathquill/package-updater.d.ts.map +0 -1
  192. package/dist/lib/mathquill/package-updater.js +0 -103
  193. package/dist/lib/mathquill/package-updater.js.map +0 -1
  194. package/dist/lib/mathquill/pie-extractor.d.ts +0 -14
  195. package/dist/lib/mathquill/pie-extractor.d.ts.map +0 -1
  196. package/dist/lib/mathquill/pie-extractor.js +0 -167
  197. package/dist/lib/mathquill/pie-extractor.js.map +0 -1
  198. package/dist/lib/upstream/repo-utils.d.ts.map +0 -1
  199. package/dist/lib/upstream/sync-compatibility.d.ts.map +0 -1
  200. package/dist/lib/upstream/sync-constants.d.ts.map +0 -1
  201. package/dist/lib/upstream/sync-controllers-strategy.d.ts.map +0 -1
  202. package/dist/lib/upstream/sync-demo-metadata.d.ts.map +0 -1
  203. package/dist/lib/upstream/sync-dev-exports.d.ts +0 -13
  204. package/dist/lib/upstream/sync-dev-exports.d.ts.map +0 -1
  205. package/dist/lib/upstream/sync-dev-exports.js +0 -115
  206. package/dist/lib/upstream/sync-dev-exports.js.map +0 -1
  207. package/dist/lib/upstream/sync-externals.d.ts.map +0 -1
  208. package/dist/lib/upstream/sync-filesystem.d.ts.map +0 -1
  209. package/dist/lib/upstream/sync-imports.d.ts.map +0 -1
  210. package/dist/lib/upstream/sync-mathquill.d.ts +0 -19
  211. package/dist/lib/upstream/sync-mathquill.d.ts.map +0 -1
  212. package/dist/lib/upstream/sync-mathquill.js +0 -160
  213. package/dist/lib/upstream/sync-mathquill.js.map +0 -1
  214. package/dist/lib/upstream/sync-package-json.d.ts.map +0 -1
  215. package/dist/lib/upstream/sync-package-manager.d.ts.map +0 -1
  216. package/dist/lib/upstream/sync-pielib-strategy.d.ts.map +0 -1
  217. package/dist/lib/upstream/sync-react-strategy.d.ts.map +0 -1
  218. package/dist/lib/upstream/sync-strategy.d.ts.map +0 -1
  219. package/dist/lib/upstream/sync-summary.d.ts.map +0 -1
  220. package/dist/lib/upstream/sync-transforms.d.ts.map +0 -1
  221. package/dist/lib/upstream/sync-vite-config.d.ts.map +0 -1
  222. package/dist/utils/compatibility.d.ts.map +0 -1
  223. package/dist/utils/conversion.d.ts.map +0 -1
  224. package/dist/utils/git.d.ts.map +0 -1
  225. package/dist/utils/logger.d.ts.map +0 -1
  226. package/dist/utils/package-json.d.ts.map +0 -1
  227. package/packages/cli/bin/dev.js +0 -5
  228. package/packages/cli/bin/run.js +0 -5
  229. package/src/commands/dev/demo/index.ts +0 -313
  230. package/src/commands/dev/test-bundler.ts +0 -345
  231. package/src/commands/mathquill/migrate.ts +0 -269
  232. package/src/commands/mathquill/status.ts +0 -267
  233. package/src/commands/packages/create-controllers.ts +0 -127
  234. package/src/commands/packages/create-lib-react.ts +0 -279
  235. package/src/commands/packages/create-react-elements.ts +0 -306
  236. package/src/commands/packages/enable-publishing.ts +0 -161
  237. package/src/commands/packages/init-synced-elements.ts +0 -259
  238. package/src/commands/upstream/README.md +0 -444
  239. package/src/commands/upstream/analyze-esm.ts +0 -1973
  240. package/src/commands/upstream/check.ts +0 -228
  241. package/src/commands/upstream/deps.ts +0 -222
  242. package/src/commands/upstream/sync-mathquill.ts +0 -108
  243. package/src/commands/upstream/sync.ts +0 -687
  244. package/src/commands/upstream/track.ts +0 -145
  245. package/src/commands/upstream/update.ts +0 -110
  246. package/src/commands/verify/controllers.ts +0 -166
  247. package/src/commands/verify/react-build.ts +0 -171
  248. package/src/index.ts +0 -1
  249. package/src/lib/mathquill/extension-generator.ts +0 -111
  250. package/src/lib/mathquill/khan-extractor.ts +0 -175
  251. package/src/lib/mathquill/learnosity-extractor.ts +0 -198
  252. package/src/lib/mathquill/migration-orchestrator.ts +0 -513
  253. package/src/lib/mathquill/package-updater.ts +0 -120
  254. package/src/lib/mathquill/pie-extractor.ts +0 -194
  255. package/src/lib/upstream/repo-utils.ts +0 -18
  256. package/src/lib/upstream/sync-compatibility.ts +0 -89
  257. package/src/lib/upstream/sync-constants.ts +0 -77
  258. package/src/lib/upstream/sync-controllers-strategy.ts +0 -291
  259. package/src/lib/upstream/sync-demo-metadata.ts +0 -199
  260. package/src/lib/upstream/sync-dev-exports.ts +0 -151
  261. package/src/lib/upstream/sync-externals.ts +0 -159
  262. package/src/lib/upstream/sync-filesystem.ts +0 -87
  263. package/src/lib/upstream/sync-imports.ts +0 -992
  264. package/src/lib/upstream/sync-mathquill.ts +0 -194
  265. package/src/lib/upstream/sync-package-json.ts +0 -86
  266. package/src/lib/upstream/sync-package-manager.ts +0 -694
  267. package/src/lib/upstream/sync-pielib-strategy.ts +0 -441
  268. package/src/lib/upstream/sync-react-strategy.ts +0 -945
  269. package/src/lib/upstream/sync-strategy.ts +0 -61
  270. package/src/lib/upstream/sync-summary.ts +0 -194
  271. package/src/lib/upstream/sync-transforms.ts +0 -154
  272. package/src/lib/upstream/sync-vite-config.ts +0 -243
  273. package/src/types/es-module-lexer.d.ts +0 -4
  274. package/src/utils/compatibility.ts +0 -182
  275. package/src/utils/conversion.ts +0 -153
  276. package/src/utils/git.ts +0 -63
  277. package/src/utils/logger.ts +0 -69
  278. package/src/utils/package-json.ts +0 -43
  279. package/tests/sync-package-manager.test.ts +0 -112
  280. package/tests/transform-assign-props.test.ts +0 -101
  281. package/tsconfig.json +0 -15
@@ -32,7 +32,21 @@
32
32
  * - Student-facing UI only needs minimal fallback configuration
33
33
  * - Inline empty defaults object to avoid the dependency
34
34
  */
35
+ import { existsSync } from 'node:fs';
35
36
  import { readFile, writeFile } from 'node:fs/promises';
37
+ import { dirname, join } from 'node:path';
38
+ import ts from 'typescript';
39
+ import { PRESET_IDS, shouldTransformConfigUiMathjsSource, shouldTransformReactInputAutosizeSource, } from './sync-presets.js';
40
+ import { applySourceEdits, rewriteModuleSpecifiers } from './sync-source-edit.js';
41
+ const SOURCE_PATH_PRESET_RULES = {
42
+ [PRESET_IDS.transformTextSelectTokenTypesReexport]: (sourcePath) => sourcePath.includes('text-select') && sourcePath.includes('token-select/index'),
43
+ [PRESET_IDS.transformChartingUtilsTypeFix]: (sourcePath) => sourcePath.includes('charting') && sourcePath.includes('utils'),
44
+ [PRESET_IDS.transformTranslatorIndexTypeFix]: (sourcePath) => sourcePath.includes('translator') && sourcePath.includes('/src/index'),
45
+ [PRESET_IDS.transformRenderUiInlineMenuExport]: (sourcePath) => sourcePath.includes('render-ui/src/index.js'),
46
+ };
47
+ function sourcePathMatchesPreset(sourcePath, presetId) {
48
+ return !!sourcePath && SOURCE_PATH_PRESET_RULES[presetId](sourcePath);
49
+ }
36
50
  /**
37
51
  * Fix import statements in a file to handle default export conversions
38
52
  *
@@ -62,6 +76,67 @@ export async function fixImportsInFile(filePath, defaultExportFiles) {
62
76
  }
63
77
  return modified;
64
78
  }
79
+ /**
80
+ * Rewrite relative ESM specifiers to explicit `.js` extensions for NodeNext compatibility.
81
+ *
82
+ * This is applied after sync so TypeScript source uses runtime-valid ESM specifiers:
83
+ * - `./foo` -> `./foo.js`
84
+ * - `./bar` (directory with index file) -> `./bar/index.js`
85
+ */
86
+ export async function rewriteRelativeSpecifiersForNodeEsm(filePath) {
87
+ let content = await readFile(filePath, 'utf-8');
88
+ let modified = false;
89
+ const rewriteSpecifier = (specifier) => {
90
+ if (!specifier.startsWith('./') && !specifier.startsWith('../')) {
91
+ return specifier;
92
+ }
93
+ // Keep explicit extensions as-is (css/json/svg/js/etc.).
94
+ if (/\.[a-z0-9]+$/i.test(specifier)) {
95
+ return specifier;
96
+ }
97
+ const basePath = join(dirname(filePath), specifier);
98
+ const moduleFileExtensions = ['.ts', '.tsx', '.mts', '.cts', '.js', '.jsx', '.mjs', '.cjs'];
99
+ const indexFileExtensions = moduleFileExtensions.map((ext) => `/index${ext}`);
100
+ const hasModuleFile = moduleFileExtensions.some((ext) => existsSync(`${basePath}${ext}`));
101
+ if (hasModuleFile) {
102
+ return `${specifier}.js`;
103
+ }
104
+ const hasIndexFile = indexFileExtensions.some((suffix) => existsSync(`${basePath}${suffix}`));
105
+ if (hasIndexFile) {
106
+ return `${specifier.replace(/\/$/, '')}/index.js`;
107
+ }
108
+ // Do not guess unresolved paths.
109
+ return specifier;
110
+ };
111
+ content = content.replace(/(from\s+)(['"])(\.\.?\/[^'"]+)\2/g, (match, prefix, quote, specifier) => {
112
+ const rewritten = rewriteSpecifier(specifier);
113
+ if (rewritten !== specifier) {
114
+ modified = true;
115
+ return `${prefix}${quote}${rewritten}${quote}`;
116
+ }
117
+ return match;
118
+ });
119
+ content = content.replace(/(import\(\s*)(['"])(\.\.?\/[^'"]+)\2(\s*\))/g, (match, start, quote, specifier, end) => {
120
+ const rewritten = rewriteSpecifier(specifier);
121
+ if (rewritten !== specifier) {
122
+ modified = true;
123
+ return `${start}${quote}${rewritten}${quote}${end}`;
124
+ }
125
+ return match;
126
+ });
127
+ content = content.replace(/\bimport\s+(['"])(\.\.?\/[^'"]+)\1/g, (match, quote, specifier) => {
128
+ const rewritten = rewriteSpecifier(specifier);
129
+ if (rewritten !== specifier) {
130
+ modified = true;
131
+ return `import ${quote}${rewritten}${quote}`;
132
+ }
133
+ return match;
134
+ });
135
+ if (modified) {
136
+ await writeFile(filePath, content, 'utf-8');
137
+ }
138
+ return modified;
139
+ }
65
140
  /**
66
141
  * Check if code contains JSX syntax
67
142
  */
@@ -97,7 +172,7 @@ const ${importName} = {
97
172
  */
98
173
  export function reexportTokenTypes(code, filePath) {
99
174
  // Only apply to text-select token-select/index file
100
- if (!filePath.includes('text-select') || !filePath.includes('token-select/index')) {
175
+ if (!sourcePathMatchesPreset(filePath, PRESET_IDS.transformTextSelectTokenTypesReexport)) {
101
176
  return code;
102
177
  }
103
178
  // Check if file imports TokenTypes from ./token
@@ -202,47 +277,273 @@ export function convertModuleExportsToEsm(content) {
202
277
  const esmExports = exports.join('\n\n');
203
278
  return content.replace(fullMatch, esmExports);
204
279
  }
280
+ const VENDORED_LODASH_PACKAGE = '@pie-element/shared-lodash';
281
+ function lodashMemberFromPath(path) {
282
+ return path.replace(/\.js$/i, '').split('/').pop() ?? path;
283
+ }
205
284
  /**
206
- * Transform lodash imports to lodash-es for ESM compatibility
285
+ * Transform lodash and lodash-es imports to the vendored shared lodash package.
207
286
  *
208
- * Handles all lodash import patterns:
209
- * - Individual module imports: import isEmpty from 'lodash/isEmpty'
210
- * - Named imports: import { isEmpty, isEqual } from 'lodash'
211
- * - Default import: import _ from 'lodash'
212
- * - Namespace import: import * as _ from 'lodash'
287
+ * The vendored package is intentionally a narrow ESM implementation of the
288
+ * lodash helpers used by synced PIE sources, avoiding CDN/runtime lodash
289
+ * resolution while keeping import shapes tree-shakeable.
290
+ */
291
+ export function transformLodashToVendoredLodash(content) {
292
+ const sourceFile = ts.createSourceFile('source.tsx', content, ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX);
293
+ const edits = [];
294
+ const dynamicDeepImportMembers = new Set();
295
+ const visit = (node) => {
296
+ if (ts.isImportDeclaration(node) && ts.isStringLiteral(node.moduleSpecifier)) {
297
+ const path = lodashDeepImportPath(node.moduleSpecifier.text);
298
+ if (path) {
299
+ const member = lodashMemberFromPath(path);
300
+ edits.push({
301
+ start: node.getStart(sourceFile),
302
+ end: node.getEnd(),
303
+ text: lodashImportReplacement(node, sourceFile, member),
304
+ });
305
+ }
306
+ }
307
+ else if (ts.isExportDeclaration(node) &&
308
+ node.moduleSpecifier &&
309
+ ts.isStringLiteral(node.moduleSpecifier)) {
310
+ const path = lodashDeepImportPath(node.moduleSpecifier.text);
311
+ if (path) {
312
+ const member = lodashMemberFromPath(path);
313
+ edits.push({
314
+ start: node.getStart(sourceFile),
315
+ end: node.getEnd(),
316
+ text: lodashExportReplacement(node, sourceFile, member),
317
+ });
318
+ }
319
+ }
320
+ else if (isDynamicStringImport(node)) {
321
+ const path = lodashDeepImportPath(node.arguments[0].text);
322
+ if (path) {
323
+ const member = lodashMemberFromPath(path);
324
+ dynamicDeepImportMembers.add(member);
325
+ edits.push({
326
+ start: node.getStart(sourceFile),
327
+ end: node.getEnd(),
328
+ text: `Promise.resolve({ default: ${member}, ${member} })`,
329
+ });
330
+ }
331
+ }
332
+ ts.forEachChild(node, visit);
333
+ };
334
+ visit(sourceFile);
335
+ let transformed = applySourceEdits(content, edits);
336
+ // Root imports keep their local import shape.
337
+ transformed = rewriteModuleSpecifiers(transformed, (specifier) => specifier === 'lodash' || specifier === 'lodash-es' ? VENDORED_LODASH_PACKAGE : undefined);
338
+ if (dynamicDeepImportMembers.size > 0) {
339
+ const imports = [...dynamicDeepImportMembers].sort().join(', ');
340
+ transformed = `import { ${imports} } from '${VENDORED_LODASH_PACKAGE}';\n${transformed}`;
341
+ }
342
+ return transformed;
343
+ }
344
+ function lodashDeepImportPath(specifier) {
345
+ const match = specifier.match(/^(?:lodash|lodash-es)\/(.+)$/);
346
+ return match?.[1] ?? null;
347
+ }
348
+ function isDynamicStringImport(node) {
349
+ return (ts.isCallExpression(node) &&
350
+ node.expression.kind === ts.SyntaxKind.ImportKeyword &&
351
+ node.arguments.length === 1 &&
352
+ ts.isStringLiteral(node.arguments[0]));
353
+ }
354
+ function quoteLike(specifier, originalText) {
355
+ const delimiter = originalText.startsWith('"') ? '"' : "'";
356
+ const escaped = specifier.replace(/\\/g, '\\\\').replaceAll(delimiter, `\\${delimiter}`);
357
+ return `${delimiter}${escaped}${delimiter}`;
358
+ }
359
+ function lodashImportReplacement(node, sourceFile, member) {
360
+ const packageSpecifier = quoteLike(VENDORED_LODASH_PACKAGE, node.moduleSpecifier.getText(sourceFile));
361
+ const importClause = node.importClause;
362
+ if (!importClause) {
363
+ return `import ${packageSpecifier};`;
364
+ }
365
+ if (importClause.name) {
366
+ return `import { ${formatLodashBinding(member, importClause.name.text)} } from ${packageSpecifier};`;
367
+ }
368
+ const namedBindings = importClause.namedBindings;
369
+ if (namedBindings && ts.isNamespaceImport(namedBindings)) {
370
+ return `import { ${formatLodashBinding(member, namedBindings.name.text)} } from ${packageSpecifier};`;
371
+ }
372
+ if (namedBindings && ts.isNamedImports(namedBindings)) {
373
+ const bindings = namedBindings.elements
374
+ .map((specifier) => lodashImportSpecifierBinding(member, specifier))
375
+ .join(', ');
376
+ const typeOnly = importClause.isTypeOnly ? 'type ' : '';
377
+ return `import ${typeOnly}{ ${bindings} } from ${packageSpecifier};`;
378
+ }
379
+ return `import ${packageSpecifier};`;
380
+ }
381
+ function lodashExportReplacement(node, sourceFile, member) {
382
+ const packageSpecifier = quoteLike(VENDORED_LODASH_PACKAGE, node.moduleSpecifier?.getText(sourceFile) ?? `'${VENDORED_LODASH_PACKAGE}'`);
383
+ const exportClause = node.exportClause;
384
+ if (!exportClause) {
385
+ return `export { ${member} } from ${packageSpecifier};`;
386
+ }
387
+ if (ts.isNamespaceExport(exportClause)) {
388
+ return `export { ${formatLodashBinding(member, exportClause.name.text)} } from ${packageSpecifier};`;
389
+ }
390
+ const bindings = exportClause.elements
391
+ .map((specifier) => lodashExportSpecifierBinding(member, specifier))
392
+ .join(', ');
393
+ const typeOnly = node.isTypeOnly ? 'type ' : '';
394
+ return `export ${typeOnly}{ ${bindings} } from ${packageSpecifier};`;
395
+ }
396
+ function lodashImportSpecifierBinding(member, specifier) {
397
+ const imported = specifier.propertyName?.text ?? specifier.name.text;
398
+ const local = specifier.name.text;
399
+ if (imported === 'default') {
400
+ return formatLodashBinding(member, local);
401
+ }
402
+ return imported === local ? imported : `${imported} as ${local}`;
403
+ }
404
+ function lodashExportSpecifierBinding(member, specifier) {
405
+ const exported = specifier.name.text;
406
+ const local = specifier.propertyName?.text ?? exported;
407
+ if (local === 'default') {
408
+ return formatLodashBinding(member, exported);
409
+ }
410
+ return local === exported ? exported : `${local} as ${exported}`;
411
+ }
412
+ function formatLodashBinding(imported, local) {
413
+ return imported === local ? imported : `${imported} as ${local}`;
414
+ }
415
+ export const transformLodashToLodashEs = transformLodashToVendoredLodash;
416
+ /**
417
+ * Transform classnames imports to clsx.
213
418
  *
214
- * Converts them to use lodash-es which is a pure ESM package.
419
+ * clsx preserves the call shape used by classnames while offering a browser ESM
420
+ * entrypoint, so synced source should prefer it over the CommonJS-era package.
215
421
  */
216
- export function transformLodashToLodashEs(content) {
217
- let transformed = content;
218
- // Pattern 1: Individual module imports
219
- // import isEmpty from 'lodash/isEmpty' → import { isEmpty } from 'lodash-es'
220
- // import cloneDeep from 'lodash/cloneDeep' → import { cloneDeep } from 'lodash-es'
221
- transformed = transformed.replace(/import\s+(\w+)\s+from\s+['"]lodash\/(\w+)['"]/g, "import { $1 } from 'lodash-es'");
222
- // Pattern 2: Named imports, default imports, and namespace imports from main module
223
- // import { isEmpty, isEqual } from 'lodash' → import { isEmpty, isEqual } from 'lodash-es'
224
- // import _ from 'lodash' → import _ from 'lodash-es'
225
- // import * as _ from 'lodash' → import * as _ from 'lodash-es'
226
- transformed = transformed.replace(/from\s+['"]lodash['"]/g, "from 'lodash-es'");
422
+ export function transformClassnamesToClsx(content) {
423
+ return rewriteModuleSpecifiers(content, (specifier) => specifier === 'classnames' ? 'clsx' : undefined);
424
+ }
425
+ /**
426
+ * Replace react-input-autosize with the local ESM autosize input component.
427
+ *
428
+ * The upstream dependency is CommonJS-era and only used by graph label inputs.
429
+ * Synced packages generate a tiny local component instead of carrying the
430
+ * dependency into browser ESM output.
431
+ */
432
+ export function transformReactInputAutosizeToLocal(content, sourcePath) {
433
+ if (!shouldTransformReactInputAutosizeSource(sourcePath)) {
434
+ return content;
435
+ }
436
+ const sourceFile = ts.createSourceFile('source.tsx', content, ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX);
437
+ const edits = [];
438
+ const visit = (node) => {
439
+ if (ts.isImportDeclaration(node) &&
440
+ ts.isStringLiteral(node.moduleSpecifier) &&
441
+ node.moduleSpecifier.text === 'react-input-autosize') {
442
+ const importName = node.importClause?.name?.text;
443
+ if (importName) {
444
+ edits.push({
445
+ start: node.getStart(sourceFile),
446
+ end: node.getEnd(),
447
+ text: importName === 'AutosizeInput'
448
+ ? "import { AutosizeInput } from './autosize-input.js';"
449
+ : `import { AutosizeInput as ${importName} } from './autosize-input.js';`,
450
+ });
451
+ }
452
+ }
453
+ else if (isAutosizeInputComponentAdapter(node, sourceFile)) {
454
+ edits.push({
455
+ start: node.getStart(sourceFile),
456
+ end: node.getEnd(),
457
+ text: '',
458
+ });
459
+ }
460
+ else if (isAutosizeInputComponentJsxTag(node)) {
461
+ edits.push({
462
+ start: node.tagName.getStart(sourceFile),
463
+ end: node.tagName.getEnd(),
464
+ text: 'AutosizeInput',
465
+ });
466
+ }
467
+ ts.forEachChild(node, visit);
468
+ };
469
+ visit(sourceFile);
470
+ return applySourceEdits(content, edits);
471
+ }
472
+ function isAutosizeInputComponentAdapter(node, sourceFile) {
473
+ if (!ts.isVariableStatement(node) || node.declarationList.declarations.length !== 1) {
474
+ return false;
475
+ }
476
+ const declaration = node.declarationList.declarations[0];
477
+ if (!ts.isIdentifier(declaration.name) || declaration.name.text !== 'AutosizeInputComponent') {
478
+ return false;
479
+ }
480
+ return (declaration.initializer?.getText(sourceFile).replace(/\s+/g, '') ===
481
+ 'AutosizeInput?.default??AutosizeInput');
482
+ }
483
+ function isAutosizeInputComponentJsxTag(node) {
484
+ return ((ts.isJsxOpeningElement(node) ||
485
+ ts.isJsxClosingElement(node) ||
486
+ ts.isJsxSelfClosingElement(node)) &&
487
+ ts.isIdentifier(node.tagName) &&
488
+ node.tagName.text === 'AutosizeInputComponent');
489
+ }
490
+ /**
491
+ * Replace config-ui's tiny mathjs usage with a local fraction-to-number helper.
492
+ *
493
+ * number-text-field-custom only uses `math.number(math.fraction(value))` to
494
+ * compare custom fraction values. Keep mathjs for number-line where the surface
495
+ * includes exact rational arithmetic, comparisons, snapping, and expression
496
+ * parsing.
497
+ */
498
+ export function transformConfigUiMathjsToLocalFraction(content, sourcePath) {
499
+ if (!shouldTransformConfigUiMathjsSource(sourcePath)) {
500
+ return content;
501
+ }
502
+ let transformed = content.replace(/math\.number\(\s*math\.fraction\(([^()]+?)\)\s*\)/g, 'fractionToNumber($1)');
503
+ if (transformed === content) {
504
+ return content;
505
+ }
506
+ if (!/\bmath\./.test(transformed)) {
507
+ transformed = transformed.replace(/import\s+\*\s+as\s+math\s+from\s+(['"])mathjs\1;?/, "import { fractionToNumber } from './fraction-to-number.js';");
508
+ }
509
+ else if (!transformed.includes("from './fraction-to-number.js'")) {
510
+ transformed = transformed.replace(/import\s+\*\s+as\s+math\s+from\s+(['"])mathjs\1;?/, (match) => `${match}\nimport { fractionToNumber } from './fraction-to-number.js';`);
511
+ }
227
512
  return transformed;
228
513
  }
229
514
  /**
230
- * Transform package.json dependencies from lodash to lodash-es
515
+ * Compatibility wrapper retained for older callers. Lodash imports now target
516
+ * the vendored shared package rather than lodash-es deep modules.
517
+ */
518
+ export function transformLodashEsDeepImportsToFullySpecified(content) {
519
+ return transformLodashToVendoredLodash(content);
520
+ }
521
+ /**
522
+ * Ensure known strict-ESM deep imports include explicit file extensions.
523
+ *
524
+ * Webpack's fully-specified ESM resolution requires `.js` on deep imports like:
525
+ * - react-konva/lib/ReactKonvaCore -> react-konva/lib/ReactKonvaCore.js
526
+ */
527
+ export function transformKnownDeepImportsToFullySpecified(content) {
528
+ return rewriteModuleSpecifiers(content, (specifier) => specifier === 'react-konva/lib/ReactKonvaCore' ? 'react-konva/lib/ReactKonvaCore.js' : undefined);
529
+ }
530
+ /**
531
+ * Transform package.json dependencies from lodash/lodash-es to the vendored package.
231
532
  *
232
- * Replaces lodash with lodash-es version 4.17.22 (latest ESM version)
233
- * Also removes @types/lodash as lodash-es includes built-in TypeScript types
533
+ * The browser/runtime dependency is a workspace package that vendors the small
534
+ * lodash helper surface needed by synced sources.
234
535
  */
235
536
  export function transformPackageJsonLodash(packageJson) {
236
537
  const transformed = { ...packageJson };
237
- // Replace lodash with lodash-es in dependencies
238
- if (transformed.dependencies?.lodash) {
239
- transformed.dependencies['lodash-es'] = '^4.17.22';
538
+ if (transformed.dependencies?.lodash || transformed.dependencies?.['lodash-es']) {
539
+ transformed.dependencies[VENDORED_LODASH_PACKAGE] = 'workspace:*';
240
540
  delete transformed.dependencies.lodash;
541
+ delete transformed.dependencies['lodash-es'];
241
542
  }
242
- // Replace lodash with lodash-es in devDependencies
243
- if (transformed.devDependencies?.lodash) {
244
- transformed.devDependencies['lodash-es'] = '^4.17.22';
543
+ if (transformed.devDependencies?.lodash || transformed.devDependencies?.['lodash-es']) {
544
+ transformed.devDependencies[VENDORED_LODASH_PACKAGE] = 'workspace:*';
245
545
  delete transformed.devDependencies.lodash;
546
+ delete transformed.devDependencies['lodash-es'];
246
547
  }
247
548
  // Remove @types/lodash if present (lodash-es has built-in types)
248
549
  if (transformed.dependencies?.['@types/lodash']) {
@@ -261,14 +562,74 @@ export function transformPackageJsonLodash(packageJson) {
261
562
  */
262
563
  export function transformPackageJsonRecharts(packageJson) {
263
564
  const transformed = { ...packageJson };
264
- // Upgrade recharts to 3.x in dependencies
565
+ // Keep recharts on the current ESM-capable major.
265
566
  if (transformed.dependencies?.recharts) {
266
- // Only upgrade if it's 2.x
267
- const version = transformed.dependencies.recharts;
268
- if (version.includes('2.')) {
269
- transformed.dependencies.recharts = '^3.7.0';
567
+ transformed.dependencies.recharts = '^3.8.1';
568
+ }
569
+ return transformed;
570
+ }
571
+ /**
572
+ * Apply browser-ESM dependency replacements and upgrades for synced packages.
573
+ *
574
+ * This is intentionally dependency policy, not build-output patching: upstream
575
+ * sync rewrites package manifests toward packages that can participate in the
576
+ * browser ESM graph directly.
577
+ */
578
+ export function transformPackageJsonBrowserEsmDependencies(packageJson, options = {}) {
579
+ const transformed = { ...packageJson };
580
+ const deps = transformed.dependencies;
581
+ if (!deps) {
582
+ return transformed;
583
+ }
584
+ if (deps.classnames) {
585
+ deps.clsx = '^2.1.1';
586
+ delete deps.classnames;
587
+ }
588
+ if (options.removeReactInputAutosize) {
589
+ delete deps['react-input-autosize'];
590
+ }
591
+ if (options.removeMathjs) {
592
+ delete deps.mathjs;
593
+ }
594
+ // Ranges forced onto synced packages after the copy.
595
+ //
596
+ // Only pin a dependency here when this repo has a reason upstream does not
597
+ // share, and state that reason. A pin silently overrides whatever upstream
598
+ // declares, so a pin that has outlived its reason quietly undoes upstream's
599
+ // decisions on every sync.
600
+ //
601
+ // mathjs is pinned to satisfy two constraints that pull in opposite
602
+ // directions:
603
+ // - This repo needs an `exports` map. mathjs 7.x (upstream's range) has
604
+ // none, so it resolves to its CJS `main` and reintroduces the runtime
605
+ // require leaks that "fix: stabilize browser ESM packaging" (481671b0)
606
+ // removed from the browser ESM bundles.
607
+ // - Upstream needs fraction.js 4. mathjs >= 14 depends on fraction.js ^5,
608
+ // which uses BigInt and breaks number-line bundling (PIE-926, PIE-1005).
609
+ // mathjs 13.2.3 is the newest release that has an `exports` map AND still
610
+ // depends on fraction.js ^4.3.7, so it satisfies both. Do not raise this to
611
+ // 14+ without resolving the BigInt problem first.
612
+ //
613
+ // react-draggable is deliberately NOT pinned: upstream moved the synced
614
+ // packages to ^4.7.1 and fixed the v4 drag regressions against that exact
615
+ // version, so a pin here would downgrade away from the code those fixes were
616
+ // written for.
617
+ const versionPins = {
618
+ '@types/react': '^18.2.0',
619
+ '@types/react-dom': '^18.2.0',
620
+ clsx: '^2.1.1',
621
+ mathjs: '^13.2.3',
622
+ 'react-is': '^18.3.1',
623
+ 'react-redux': '^9.3.0',
624
+ redux: '^5.0.1',
625
+ };
626
+ for (const [name, version] of Object.entries(versionPins)) {
627
+ if (deps[name]) {
628
+ deps[name] = version;
270
629
  }
271
630
  }
631
+ delete deps.react;
632
+ delete deps['react-dom'];
272
633
  return transformed;
273
634
  }
274
635
  /**
@@ -279,12 +640,15 @@ export function transformPackageJsonRecharts(packageJson) {
279
640
  * - @pie-framework/pie-configure-events → @pie-element/shared-configure-events
280
641
  */
281
642
  export function transformPieFrameworkEventImports(content) {
282
- let transformed = content;
283
- // Transform pie-player-events imports
284
- transformed = transformed.replace(/from\s+['"]@pie-framework\/pie-player-events['"]/g, "from '@pie-element/shared-player-events'");
285
- // Transform pie-configure-events imports
286
- transformed = transformed.replace(/from\s+['"]@pie-framework\/pie-configure-events['"]/g, "from '@pie-element/shared-configure-events'");
287
- return transformed;
643
+ return rewriteModuleSpecifiers(content, (specifier) => {
644
+ if (specifier === '@pie-framework/pie-player-events') {
645
+ return '@pie-element/shared-player-events';
646
+ }
647
+ if (specifier === '@pie-framework/pie-configure-events') {
648
+ return '@pie-element/shared-configure-events';
649
+ }
650
+ return undefined;
651
+ });
288
652
  }
289
653
  /**
290
654
  * Transform @pie-lib/controller-utils imports to internal @pie-element/shared-controller-utils
@@ -296,7 +660,7 @@ export function transformPieFrameworkEventImports(content) {
296
660
  * and no lodash/debug dependencies.
297
661
  */
298
662
  export function transformControllerUtilsImports(content) {
299
- return content.replace(/from\s+['"]@pie-lib\/controller-utils['"]/g, "from '@pie-element/shared-controller-utils'");
663
+ return rewriteModuleSpecifiers(content, (specifier) => specifier === '@pie-lib/controller-utils' ? '@pie-element/shared-controller-utils' : undefined);
300
664
  }
301
665
  /**
302
666
  * Transform @pie-lib shared package imports to internal @pie-element/shared-* packages
@@ -307,24 +671,29 @@ export function transformControllerUtilsImports(content) {
307
671
  * These packages have been moved to shared/ for better version control and consistency.
308
672
  */
309
673
  export function transformSharedPackageImports(content) {
310
- let transformed = content;
311
- // Transform math-rendering
312
- transformed = transformed.replace(/from\s+['"]@pie-lib\/math-rendering['"]/g, "from '@pie-element/shared-math-rendering-mathjax'");
313
- // Transform feedback
314
- transformed = transformed.replace(/from\s+['"]@pie-lib\/feedback['"]/g, "from '@pie-element/shared-feedback'");
315
- return transformed;
674
+ return rewriteModuleSpecifiers(content, (specifier) => {
675
+ if (specifier === '@pie-lib/math-rendering') {
676
+ return '@pie-element/shared-math-rendering-mathjax';
677
+ }
678
+ if (specifier === '@pie-lib/feedback') {
679
+ return '@pie-element/shared-feedback';
680
+ }
681
+ return undefined;
682
+ });
316
683
  }
317
684
  /**
318
- * Transform @pie-framework/mathquill imports to internal @pie-element/shared-mathquill
685
+ * Rewrite legacy configure subpath imports to author entrypoints.
319
686
  *
320
- * Handles:
321
- * - @pie-framework/mathquill → @pie-element/shared-mathquill
687
+ * Upstream sometimes imports configure elements via:
688
+ * - @pie-element/<element-name>/configure/lib
322
689
  *
323
- * Our internal mathquill package is a fork from the PIE org with matrix support,
324
- * accessibility features, and modernized ESM code.
690
+ * In this repo, configure code is synced into `src/author` and packages expose that via
691
+ * the `./author` export. Rewriting to `/author` preserves author-vs-delivery boundaries.
325
692
  */
326
- export function transformMathquillImports(content) {
327
- return content.replace(/from\s+['"]@pie-framework\/mathquill['"]/g, "from '@pie-element/shared-mathquill'");
693
+ export function transformLegacyConfigureLibImports(content) {
694
+ return rewriteModuleSpecifiers(content, (specifier) => /^@pie-element\/[^/]+\/configure\/lib$/.test(specifier)
695
+ ? specifier.replace(/\/configure\/lib$/, '/author')
696
+ : undefined);
328
697
  }
329
698
  /**
330
699
  * Transform package.json dependencies for PIE Framework event packages
@@ -396,24 +765,6 @@ export function transformPackageJsonSharedPackages(packageJson) {
396
765
  }
397
766
  return transformed;
398
767
  }
399
- /**
400
- * Transform package.json dependencies for @pie-framework/mathquill
401
- *
402
- * Replaces @pie-framework/mathquill with internal @pie-element/shared-mathquill
403
- */
404
- export function transformPackageJsonMathquill(packageJson) {
405
- const transformed = { ...packageJson };
406
- // Replace @pie-framework/mathquill with internal package
407
- if (transformed.dependencies?.['@pie-framework/mathquill']) {
408
- transformed.dependencies['@pie-element/shared-mathquill'] = 'workspace:*';
409
- delete transformed.dependencies['@pie-framework/mathquill'];
410
- }
411
- if (transformed.devDependencies?.['@pie-framework/mathquill']) {
412
- transformed.devDependencies['@pie-element/shared-mathquill'] = 'workspace:*';
413
- delete transformed.devDependencies['@pie-framework/mathquill'];
414
- }
415
- return transformed;
416
- }
417
768
  /**
418
769
  * Transform direct property assignment patterns to use assignProps utility
419
770
  *
@@ -502,50 +853,22 @@ export function transformConfigureUtilsImports(content, relativePath) {
502
853
  if (!configureRootFilePattern.test(relativePath)) {
503
854
  return content;
504
855
  }
505
- // Transform '../utils' to './utils' (only exact match, not '../utils/something')
506
- let transformed = content;
507
- transformed = transformed.replace(/from\s+['"]\.\.\/utils['"]/g, "from './utils'");
508
- return transformed;
856
+ // Transform '../utils' to './utils.js' (only exact match, not '../utils/something')
857
+ // Keep explicit runtime extension for Node ESM consistency.
858
+ return rewriteModuleSpecifiers(content, (specifier) => specifier === '../utils' ? './utils.js' : undefined);
509
859
  }
510
860
  /**
511
- * Transform SSR-unsafe require() calls to standard ESM imports
512
- *
513
- * Upstream pie-lib code uses this pattern for SSR compatibility with MathQuill:
514
- *
515
- * ```js
516
- * // - mathquill error window not defined
517
- * let EditableHtml;
518
- * let StyledEditableHTML;
519
- * if (typeof window !== 'undefined') {
520
- * EditableHtml = require('@pie-lib/editable-html-tip-tap')['default'];
521
- * StyledEditableHTML = styled(EditableHtml)(({ theme }) => ({
522
- * fontFamily: theme.typography.fontFamily,
523
- * }));
524
- * }
525
- * ```
526
- *
527
- * This doesn't work in browser ESM (require is not defined). Transform to:
861
+ * Transform legacy SSR-gated `require()` for pie-lib editors into static ESM imports.
528
862
  *
529
- * ```tsx
530
- * import EditableHtmlImport from '@pie-lib/editable-html-tip-tap';
531
- *
532
- * const EditableHtml = EditableHtmlImport;
533
- * const StyledEditableHTML = styled(EditableHtml)(({ theme }) => ({
534
- * fontFamily: theme.typography.fontFamily,
535
- * }));
536
- * ```
537
- *
538
- * We use direct imports instead of React.lazy because:
539
- * - Simpler code (no Suspense wrapper needed)
540
- * - No loading flicker
541
- * - Modern bundlers handle ESM imports correctly for SSR
542
- * - The component is always needed (no code-splitting benefit)
863
+ * Older upstream pie-lib used `typeof window` + `require()` so heavy editor code did not
864
+ * run under SSR. That breaks in browser ESM where `require` is undefined. When this pattern
865
+ * is still present in synced sources, rewrite it to a normal default import plus styled()
866
+ * wrapper. If upstream has already moved to ESM imports, this is a no-op.
543
867
  */
544
- export function transformSsrRequireToReactLazy(content) {
868
+ export function transformSsrRequireToEsmImport(content) {
545
869
  let transformed = content;
546
870
  // Pattern 1: SSR check with require() for editable-html-tip-tap with styled wrapper
547
871
  // Matches:
548
- // // - mathquill error window not defined
549
872
  // let EditableHtml;
550
873
  // let StyledEditableHTML;
551
874
  // if (typeof window !== 'undefined') {
@@ -558,7 +881,6 @@ export function transformSsrRequireToReactLazy(content) {
558
881
  const match = transformed.match(ssrRequirePattern);
559
882
  if (match) {
560
883
  const [fullMatch, componentVar, styledVar, importPath, styleParams] = match;
561
- // Generate direct import replacement (better than React.lazy - simpler, no Suspense needed)
562
884
  const importVarName = `${componentVar}Import`;
563
885
  const replacement = `import ${importVarName} from '${importPath}';
564
886
 
@@ -578,7 +900,6 @@ const ${styledVar} = styled(${componentVar})(${styleParams});`;
578
900
  if (simpleMatch && !match) {
579
901
  // Only apply if we didn't already match the styled pattern
580
902
  const [fullMatch, componentVar, importPath] = simpleMatch;
581
- // Generate direct import replacement (better than React.lazy - simpler, no Suspense needed)
582
903
  const importVarName = `${componentVar}Import`;
583
904
  const replacement = `import ${importVarName} from '${importPath}';
584
905
 
@@ -607,12 +928,11 @@ export function fixStyledComponentTypes(content) {
607
928
  // Match any identifier, not just those starting with "Styled"
608
929
  const constStyledRegex = /const (\w+) = styled\(/g;
609
930
  transformed = transformed.replace(constStyledRegex, 'const $1: any = styled(');
610
- // Pattern 3: Class methods/arrow functions that might return styled components
611
- // Add `: any` return type to methods that are missing type annotations
612
- // Match: methodName = () => { or methodName = (params) => {
613
- // But only at start of line (class methods), not property assignments like reader.onload =
614
- // And only if they don't already have a type annotation
615
- const methodRegex = /^(\s*)(\w+)\s*=\s*\([^)]*\)\s*=>\s*\{/gm;
931
+ // Pattern 3: Class field arrow functions that might return styled components
932
+ // Add `: any` return type to class fields missing type annotations.
933
+ // IMPORTANT: keep this line-scoped so we do not accidentally match multiline JSX
934
+ // assignments such as `foo = (<Component ref={(r) => { ... }} />)`.
935
+ const methodRegex = /^(\s*)(\w+)\s*=\s*\([^)\n]*\)\s*=>\s*\{/gm;
616
936
  transformed = transformed.replace(methodRegex, (match) => {
617
937
  // Check if already has type annotation (: type = )
618
938
  const hasTypeAnnotation = match.includes(':');
@@ -647,53 +967,27 @@ export function fixStyledComponentTypes(content) {
647
967
  * @returns Transformed content with relative imports
648
968
  */
649
969
  export function transformSelfReferentialImports(content, packageName, sourceFilePath) {
650
- let transformed = content;
651
970
  // Extract element name from package name (@pie-element/fraction-model → fraction-model)
652
971
  const elementName = packageName.replace('@pie-element/', '');
653
- // Pattern: import { X } from '@pie-element/element-name' or import X from '@pie-element/element-name'
654
- const selfImportPattern = new RegExp(`import\\s+(?:\\{\\s*([^}]+)\\s*\\}|([\\w]+))\\s+from\\s+['"]@pie-element/${elementName}['"]`, 'g');
655
- const matches = Array.from(transformed.matchAll(selfImportPattern));
656
- for (const match of matches) {
657
- const [fullMatch, namedImports, defaultImport] = match;
658
- // Determine source directory (configure/src → author, src → delivery)
659
- let targetPath = '';
660
- if (sourceFilePath.startsWith('configure/src/')) {
661
- // Configure files reference delivery components
662
- // configure/src/main.jsx → author/main.tsx
663
- // Relative path from author/ to delivery/
664
- targetPath = '../delivery';
665
- }
666
- else if (sourceFilePath.startsWith('src/')) {
667
- // Main src files shouldn't self-reference, but handle it anyway
668
- targetPath = '.';
669
- }
670
- else {
671
- // Unknown structure, skip
672
- continue;
673
- }
674
- // Strategy: Self-referential imports typically come from the package's main
675
- // index file which re-exports from various locations. Rather than trying to
676
- // guess individual file locations (which often fails for namespace exports
677
- // and re-exports), we keep the import pointing to the index (delivery folder)
678
- // which handles all re-exports correctly.
679
- //
680
- // Examples:
681
- // - import { NumberLineComponent, dataConverter, tickUtils } from '@pie-element/number-line'
682
- // → import { NumberLineComponent, dataConverter, tickUtils } from '../delivery'
683
- // - import FractionModel from '@pie-element/fraction-model'
684
- // → import FractionModel from '../delivery'
685
- if (namedImports) {
686
- // Named imports - keep them as a single import from the index
687
- const replacement = `import { ${namedImports} } from '${targetPath}'`;
688
- transformed = transformed.replace(fullMatch, replacement);
689
- }
690
- else if (defaultImport) {
691
- // Default import - reference main index
692
- const replacement = `import ${defaultImport} from '${targetPath}'`;
693
- transformed = transformed.replace(fullMatch, replacement);
694
- }
972
+ let targetPath = null;
973
+ if (sourceFilePath.startsWith('configure/src/')) {
974
+ // Configure files reference delivery components
975
+ // configure/src/main.jsx → author/main.tsx
976
+ // Relative path from author/ to delivery/
977
+ targetPath = '../delivery';
695
978
  }
696
- return transformed;
979
+ else if (sourceFilePath.startsWith('src/')) {
980
+ // Main src files shouldn't self-reference, but handle it anyway
981
+ targetPath = '.';
982
+ }
983
+ if (!targetPath) {
984
+ return content;
985
+ }
986
+ // Strategy: Self-referential imports typically come from the package's main
987
+ // index file which re-exports from various locations. Rather than trying to
988
+ // guess individual file locations (which often fails for namespace exports
989
+ // and re-exports), we keep the import pointing to the index.
990
+ return rewriteModuleSpecifiers(content, (specifier) => specifier === `@pie-element/${elementName}` ? `${targetPath}/index.js` : undefined);
697
991
  }
698
992
  /**
699
993
  * Fix type inference errors for exported functions with complex return types
@@ -706,14 +1000,14 @@ export function transformSelfReferentialImports(content, packageName, sourceFile
706
1000
  export function fixExportedFunctionTypes(content, sourcePath) {
707
1001
  let transformed = content;
708
1002
  // Only apply to specific files that are known to have issues
709
- if (sourcePath?.includes('charting') && sourcePath.includes('utils')) {
1003
+ if (sourcePathMatchesPreset(sourcePath, PRESET_IDS.transformChartingUtilsTypeFix)) {
710
1004
  // Fix: export const dataToXBand = (...) => { ... }
711
1005
  // Pattern: export const functionName = (params) => {
712
1006
  transformed = transformed.replace(/export const dataToXBand = \(/g, 'export const dataToXBand: any = (');
713
1007
  }
714
1008
  // Fix translator package default export type inference issue
715
1009
  // The spread operator `...i18next` causes TypeScript to require a reference to i18next types
716
- if (sourcePath?.includes('translator') && sourcePath?.includes('/src/index')) {
1010
+ if (sourcePathMatchesPreset(sourcePath, PRESET_IDS.transformTranslatorIndexTypeFix)) {
717
1011
  // Add type imports
718
1012
  if (!transformed.includes('type i18n')) {
719
1013
  transformed = transformed.replace(/import i18next from 'i18next';/, "import i18next, { type i18n, type TOptions } from 'i18next';");
@@ -756,21 +1050,40 @@ const translatorModule: TranslatorModule = `;
756
1050
  * @returns Transformed content with Menu imports replaced
757
1051
  */
758
1052
  export function transformMenuToInlineMenu(content) {
759
- let transformed = content;
760
- // Pattern 1: import Menu from '@mui/material/Menu'
761
- // → import { InlineMenu as Menu } from '@pie-lib/render-ui'
762
- if (/import\s+Menu\s+from\s+['"]@mui\/material\/Menu['"]/.test(transformed)) {
763
- transformed = transformed.replace(/import\s+Menu\s+from\s+['"]@mui\/material\/Menu['"]/g, "import { InlineMenu as Menu } from '@pie-lib/render-ui'");
764
- }
765
- // Pattern 2: import Menu, { MenuProps } from '@mui/material/Menu'
766
- // → import { InlineMenu as Menu } from '@pie-lib/render-ui'
767
- // import type { MenuProps } from '@mui/material/Menu'
768
- const namedImportMatch = transformed.match(/import\s+Menu\s*,\s*\{([^}]+)\}\s+from\s+['"]@mui\/material\/Menu['"]/);
769
- if (namedImportMatch) {
770
- const namedImports = namedImportMatch[1];
771
- transformed = transformed.replace(/import\s+Menu\s*,\s*\{([^}]+)\}\s+from\s+['"]@mui\/material\/Menu['"]/, `import { InlineMenu as Menu } from '@pie-lib/render-ui';\nimport type {${namedImports}} from '@mui/material/Menu'`);
1053
+ const sourceFile = ts.createSourceFile('source.tsx', content, ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX);
1054
+ const edits = [];
1055
+ const visit = (node) => {
1056
+ if (ts.isImportDeclaration(node) &&
1057
+ ts.isStringLiteral(node.moduleSpecifier) &&
1058
+ node.moduleSpecifier.text === '@mui/material/Menu') {
1059
+ const defaultName = node.importClause?.name?.text;
1060
+ if (!defaultName) {
1061
+ return;
1062
+ }
1063
+ const namedImports = menuNamedImportsText(node, sourceFile);
1064
+ const replacement = [
1065
+ `import { InlineMenu as ${defaultName} } from '@pie-lib/render-ui';`,
1066
+ namedImports ? `import type { ${namedImports} } from '@mui/material/Menu';` : null,
1067
+ ]
1068
+ .filter((line) => !!line)
1069
+ .join('\n');
1070
+ edits.push({
1071
+ start: node.getStart(sourceFile),
1072
+ end: node.getEnd(),
1073
+ text: replacement,
1074
+ });
1075
+ }
1076
+ ts.forEachChild(node, visit);
1077
+ };
1078
+ visit(sourceFile);
1079
+ return applySourceEdits(content, edits);
1080
+ }
1081
+ function menuNamedImportsText(node, sourceFile) {
1082
+ const namedBindings = node.importClause?.namedBindings;
1083
+ if (!namedBindings || !ts.isNamedImports(namedBindings)) {
1084
+ return null;
772
1085
  }
773
- return transformed;
1086
+ return namedBindings.elements.map((element) => element.getText(sourceFile)).join(', ');
774
1087
  }
775
1088
  /**
776
1089
  * Add InlineMenu export to render-ui index file
@@ -789,7 +1102,7 @@ export function transformMenuToInlineMenu(content) {
789
1102
  export function addInlineMenuExport(content, sourcePath) {
790
1103
  // Only apply to render-ui root index file (not subdirectories like collapsible/index.tsx)
791
1104
  // sourcePath format: pie-lib/packages/render-ui/src/index.js
792
- if (!sourcePath?.includes('render-ui/src/index.js')) {
1105
+ if (!sourcePathMatchesPreset(sourcePath, PRESET_IDS.transformRenderUiInlineMenuExport)) {
793
1106
  return content;
794
1107
  }
795
1108
  // Check if InlineMenu export already exists
@@ -801,16 +1114,176 @@ export function addInlineMenuExport(content, sourcePath) {
801
1114
  return content.trimEnd() + exportStatement;
802
1115
  }
803
1116
  /**
804
- * Transform MathQuill.getInterface(2) to getInterface(3)
1117
+ * Transform React component imports that may resolve as module objects in IIFE builds.
805
1118
  *
806
- * Interface version 2 requires jQuery, but pie-elements-ng uses interface version 3
807
- * which has a jQuery-free API. This transform updates calls to use version 3.
1119
+ * Some libraries export React components as objects (e.g. forwardRef) or wrapped modules,
1120
+ * which can trigger React invariant #130 in certain bundled interop paths.
808
1121
  *
809
- * @param content - Source code content
810
- * @returns Transformed content with getInterface(3) instead of getInterface(2)
1122
+ * This transform rewrites known-risk imports to pass through a small runtime unwrap helper:
1123
+ * - `@mdi/react` default import
1124
+ * - `react-konva` named imports
811
1125
  */
812
- export function transformMathQuillInterface(content) {
813
- // Replace MathQuill.getInterface(2) with MathQuill.getInterface(3)
814
- return content.replace(/MathQuill\.getInterface\(2\)/g, 'MathQuill.getInterface(3)');
1126
+ export function transformReactInteropComponentImports(content) {
1127
+ let transformed = content;
1128
+ let touched = false;
1129
+ // Handle default import from @mdi/react:
1130
+ // import Icon from '@mdi/react'
1131
+ // ->
1132
+ // import IconImport from '@mdi/react'
1133
+ // const Icon = unwrapReactInteropSymbol(IconImport, 'Icon');
1134
+ transformed = transformed.replace(/^import\s+([A-Za-z_$][\w$]*)\s+from\s+['"]@mdi\/react['"];?\s*$/m, (match, localName) => {
1135
+ if (localName.endsWith('Import')) {
1136
+ return match;
1137
+ }
1138
+ touched = true;
1139
+ return `import ${localName}Import from '@mdi/react';`;
1140
+ });
1141
+ const mdiMatch = transformed.match(/^import\s+([A-Za-z_$][\w$]*)Import\s+from\s+['"]@mdi\/react['"];?\s*$/m);
1142
+ if (mdiMatch) {
1143
+ const localName = mdiMatch[1];
1144
+ const declaration = `const ${localName} = unwrapReactInteropSymbol(${localName}Import, '${localName}');`;
1145
+ if (!transformed.includes(declaration)) {
1146
+ touched = true;
1147
+ transformed = transformed.replace(mdiMatch[0], `${mdiMatch[0]}\n${declaration}`);
1148
+ }
1149
+ }
1150
+ // Handle named imports from react-konva:
1151
+ // import { Stage, Layer } from 'react-konva'
1152
+ // ->
1153
+ // import { Stage as StageImport, Layer as LayerImport } from 'react-konva'
1154
+ // const Stage = unwrapReactInteropSymbol(StageImport, 'Stage');
1155
+ // const Layer = unwrapReactInteropSymbol(LayerImport, 'Layer');
1156
+ const konvaImportRegex = /^import\s+\{([^}]+)\}\s+from\s+['"]react-konva['"];?\s*$/m;
1157
+ const konvaMatch = transformed.match(konvaImportRegex);
1158
+ if (konvaMatch) {
1159
+ const rawSpec = konvaMatch[1]
1160
+ .split(',')
1161
+ .map((s) => s.trim())
1162
+ .filter(Boolean);
1163
+ const parsed = rawSpec
1164
+ .map((entry) => {
1165
+ const aliasMatch = entry.match(/^([A-Za-z_$][\w$]*)\s+as\s+([A-Za-z_$][\w$]*)$/);
1166
+ if (aliasMatch) {
1167
+ return { exported: aliasMatch[1], local: aliasMatch[2] };
1168
+ }
1169
+ const singleMatch = entry.match(/^([A-Za-z_$][\w$]*)$/);
1170
+ if (singleMatch) {
1171
+ return { exported: singleMatch[1], local: singleMatch[1] };
1172
+ }
1173
+ return null;
1174
+ })
1175
+ .filter((v) => !!v);
1176
+ if (parsed.length) {
1177
+ const rewrittenSpecs = parsed.map(({ exported, local }) => `${exported} as ${local}Import`);
1178
+ const declarations = parsed.map(({ exported, local }) => `const ${local} = unwrapReactInteropSymbol(${local}Import, '${exported}');`);
1179
+ transformed = transformed.replace(konvaImportRegex, `import { ${rewrittenSpecs.join(', ')} } from 'react-konva';`);
1180
+ for (const declaration of declarations) {
1181
+ if (!transformed.includes(declaration)) {
1182
+ transformed = transformed.replace(/^import\s+\{[^}]+\}\s+from\s+['"]react-konva['"];?\s*$/m, (line) => `${line}\n${declaration}`);
1183
+ }
1184
+ }
1185
+ touched = true;
1186
+ }
1187
+ }
1188
+ // Handle mixed named imports from @pie-lib/render-ui where React components
1189
+ // may resolve through nested/default interop in IIFE bundles.
1190
+ // Example:
1191
+ // import { Collapsible, color, PreviewPrompt } from '@pie-lib/render-ui'
1192
+ // ->
1193
+ // import { Collapsible as CollapsibleImport, color, PreviewPrompt as PreviewPromptImport } from '@pie-lib/render-ui';
1194
+ // const Collapsible = unwrapReactInteropSymbol(CollapsibleImport, 'Collapsible');
1195
+ // const PreviewPrompt = unwrapReactInteropSymbol(PreviewPromptImport, 'PreviewPrompt');
1196
+ const renderUiImportRegex = /^import\s+\{([^}]+)\}\s+from\s+['"]@pie-lib\/render-ui['"];?\s*$/m;
1197
+ const renderUiMatch = transformed.match(renderUiImportRegex);
1198
+ if (renderUiMatch) {
1199
+ const rawSpec = renderUiMatch[1]
1200
+ .split(',')
1201
+ .map((s) => s.trim())
1202
+ .filter(Boolean);
1203
+ const parsed = rawSpec
1204
+ .map((entry) => {
1205
+ const aliasMatch = entry.match(/^([A-Za-z_$][\w$]*)\s+as\s+([A-Za-z_$][\w$]*)$/);
1206
+ if (aliasMatch) {
1207
+ return { exported: aliasMatch[1], local: aliasMatch[2] };
1208
+ }
1209
+ const singleMatch = entry.match(/^([A-Za-z_$][\w$]*)$/);
1210
+ if (singleMatch) {
1211
+ return { exported: singleMatch[1], local: singleMatch[1] };
1212
+ }
1213
+ return null;
1214
+ })
1215
+ .filter((v) => !!v);
1216
+ // Heuristic: only wrap React component-like imports (PascalCase symbols).
1217
+ const componentLike = parsed.filter(({ exported, local }) => /^[A-Z]/.test(exported) && /^[A-Z]/.test(local) && !local.endsWith('Import'));
1218
+ if (componentLike.length) {
1219
+ const rewrittenSpecs = parsed.map(({ exported, local }) => {
1220
+ const shouldWrap = componentLike.some((entry) => entry.exported === exported && entry.local === local);
1221
+ return shouldWrap
1222
+ ? `${exported} as ${local}Import`
1223
+ : exported === local
1224
+ ? local
1225
+ : `${exported} as ${local}`;
1226
+ });
1227
+ const declarations = componentLike.map(({ exported, local }) => `const ${local} = unwrapReactInteropSymbol(${local}Import, '${exported}');`);
1228
+ transformed = transformed.replace(renderUiImportRegex, `import { ${rewrittenSpecs.join(', ')} } from '@pie-lib/render-ui';`);
1229
+ // Add a namespace import so wrapped component imports can fall back to module members
1230
+ // when named import interop resolves unexpectedly in IIFE bundles.
1231
+ if (!/^import\s+\*\s+as\s+RenderUiNamespace\s+from\s+['"]@pie-lib\/render-ui['"];?\s*$/m.test(transformed)) {
1232
+ transformed = transformed.replace(/^import\s+\{[^}]+\}\s+from\s+['"]@pie-lib\/render-ui['"];?\s*$/m, (line) => `${line}\nimport * as RenderUiNamespace from '@pie-lib/render-ui';`);
1233
+ }
1234
+ const renderUiInteropPrelude = `const renderUiNamespaceAny = RenderUiNamespace as any;
1235
+ const renderUiDefaultMaybe = renderUiNamespaceAny['default'];
1236
+ const renderUi =
1237
+ renderUiDefaultMaybe && typeof renderUiDefaultMaybe === 'object'
1238
+ ? renderUiDefaultMaybe
1239
+ : renderUiNamespaceAny;`;
1240
+ if (!transformed.includes('const renderUiNamespaceAny = RenderUiNamespace as any;')) {
1241
+ transformed = transformed.replace(/^import\s+\*\s+as\s+RenderUiNamespace\s+from\s+['"]@pie-lib\/render-ui['"];?\s*$/m, (line) => `${line}\n${renderUiInteropPrelude}`);
1242
+ }
1243
+ for (const declaration of declarations) {
1244
+ const withFallback = declaration.replace(/unwrapReactInteropSymbol\((\w+)Import, '(\w+)'\);$/, "unwrapReactInteropSymbol($1Import, '$2') || unwrapReactInteropSymbol(renderUi.$2, '$2');");
1245
+ if (!transformed.includes(declaration) && !transformed.includes(withFallback)) {
1246
+ transformed = transformed.replace(/^import\s+\{[^}]+\}\s+from\s+['"]@pie-lib\/render-ui['"];?\s*$/m, (line) => `${line}\n${withFallback}`);
1247
+ }
1248
+ }
1249
+ touched = true;
1250
+ }
1251
+ }
1252
+ if (!touched) {
1253
+ return transformed;
1254
+ }
1255
+ if (!transformed.includes('function isRenderableReactInteropType(')) {
1256
+ const helperBlock = `function isRenderableReactInteropType(value: any) {
1257
+ return (
1258
+ typeof value === 'function' ||
1259
+ (typeof value === 'object' && value !== null && typeof value.$$typeof === 'symbol')
1260
+ );
1261
+ }
1262
+
1263
+ function unwrapReactInteropSymbol(maybeSymbol: any, namedExport?: string) {
1264
+ if (!maybeSymbol) return maybeSymbol;
1265
+ if (isRenderableReactInteropType(maybeSymbol)) return maybeSymbol;
1266
+ if (isRenderableReactInteropType(maybeSymbol.default)) return maybeSymbol.default;
1267
+ if (namedExport && isRenderableReactInteropType(maybeSymbol[namedExport])) {
1268
+ return maybeSymbol[namedExport];
1269
+ }
1270
+ if (namedExport && isRenderableReactInteropType(maybeSymbol[namedExport]?.default)) {
1271
+ return maybeSymbol[namedExport].default;
1272
+ }
1273
+ return maybeSymbol;
1274
+ }
1275
+ `;
1276
+ // Capture top-of-file import blocks, including multiline imports.
1277
+ const importBlockMatch = transformed.match(/^(?:(?:\s*\/\/[^\n]*\n|\s*\/\*[\s\S]*?\*\/\s*\n|\s*\n)*)((?:import[\s\S]*?;\s*\n)+)/);
1278
+ if (importBlockMatch) {
1279
+ const insertAt = importBlockMatch[0].length;
1280
+ transformed =
1281
+ transformed.slice(0, insertAt) + '\n' + helperBlock + transformed.slice(insertAt);
1282
+ }
1283
+ else {
1284
+ transformed = `${helperBlock}\n${transformed}`;
1285
+ }
1286
+ }
1287
+ return transformed;
815
1288
  }
816
1289
  //# sourceMappingURL=sync-imports.js.map