@pie-element/cli 0.1.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 (255) hide show
  1. package/.compatibility/report.json +2491 -0
  2. package/.turbo/turbo-build.log +1 -0
  3. package/CHANGELOG.md +8 -0
  4. package/README.md +240 -0
  5. package/REFACTORING.md +131 -0
  6. package/bin/dev.js +5 -0
  7. package/bin/run.js +5 -0
  8. package/dist/commands/dev/demo/index.d.ts +33 -0
  9. package/dist/commands/dev/demo/index.d.ts.map +1 -0
  10. package/dist/commands/dev/demo/index.js +274 -0
  11. package/dist/commands/dev/demo/index.js.map +1 -0
  12. package/dist/commands/dev/test-bundler.d.ts +18 -0
  13. package/dist/commands/dev/test-bundler.d.ts.map +1 -0
  14. package/dist/commands/dev/test-bundler.js +284 -0
  15. package/dist/commands/dev/test-bundler.js.map +1 -0
  16. package/dist/commands/mathquill/migrate.d.ts +20 -0
  17. package/dist/commands/mathquill/migrate.d.ts.map +1 -0
  18. package/dist/commands/mathquill/migrate.js +240 -0
  19. package/dist/commands/mathquill/migrate.js.map +1 -0
  20. package/dist/commands/mathquill/status.d.ts +20 -0
  21. package/dist/commands/mathquill/status.d.ts.map +1 -0
  22. package/dist/commands/mathquill/status.js +231 -0
  23. package/dist/commands/mathquill/status.js.map +1 -0
  24. package/dist/commands/packages/create-controllers.d.ts +13 -0
  25. package/dist/commands/packages/create-controllers.d.ts.map +1 -0
  26. package/dist/commands/packages/create-controllers.js +103 -0
  27. package/dist/commands/packages/create-controllers.js.map +1 -0
  28. package/dist/commands/packages/create-lib-react.d.ts +17 -0
  29. package/dist/commands/packages/create-lib-react.d.ts.map +1 -0
  30. package/dist/commands/packages/create-lib-react.js +228 -0
  31. package/dist/commands/packages/create-lib-react.js.map +1 -0
  32. package/dist/commands/packages/create-react-elements.d.ts +16 -0
  33. package/dist/commands/packages/create-react-elements.d.ts.map +1 -0
  34. package/dist/commands/packages/create-react-elements.js +259 -0
  35. package/dist/commands/packages/create-react-elements.js.map +1 -0
  36. package/dist/commands/packages/enable-publishing.d.ts +11 -0
  37. package/dist/commands/packages/enable-publishing.d.ts.map +1 -0
  38. package/dist/commands/packages/enable-publishing.js +117 -0
  39. package/dist/commands/packages/enable-publishing.js.map +1 -0
  40. package/dist/commands/packages/init-synced-elements.d.ts +16 -0
  41. package/dist/commands/packages/init-synced-elements.d.ts.map +1 -0
  42. package/dist/commands/packages/init-synced-elements.js +204 -0
  43. package/dist/commands/packages/init-synced-elements.js.map +1 -0
  44. package/dist/commands/upstream/analyze-esm.d.ts +59 -0
  45. package/dist/commands/upstream/analyze-esm.d.ts.map +1 -0
  46. package/dist/commands/upstream/analyze-esm.js +1481 -0
  47. package/dist/commands/upstream/analyze-esm.js.map +1 -0
  48. package/dist/commands/upstream/check.d.ts +15 -0
  49. package/dist/commands/upstream/check.d.ts.map +1 -0
  50. package/dist/commands/upstream/check.js +165 -0
  51. package/dist/commands/upstream/check.js.map +1 -0
  52. package/dist/commands/upstream/deps.d.ts +15 -0
  53. package/dist/commands/upstream/deps.d.ts.map +1 -0
  54. package/dist/commands/upstream/deps.js +174 -0
  55. package/dist/commands/upstream/deps.js.map +1 -0
  56. package/dist/commands/upstream/sync-mathquill.d.ts +12 -0
  57. package/dist/commands/upstream/sync-mathquill.d.ts.map +1 -0
  58. package/dist/commands/upstream/sync-mathquill.js +97 -0
  59. package/dist/commands/upstream/sync-mathquill.js.map +1 -0
  60. package/dist/commands/upstream/sync.d.ts +25 -0
  61. package/dist/commands/upstream/sync.d.ts.map +1 -0
  62. package/dist/commands/upstream/sync.js +552 -0
  63. package/dist/commands/upstream/sync.js.map +1 -0
  64. package/dist/commands/upstream/track.d.ts +15 -0
  65. package/dist/commands/upstream/track.d.ts.map +1 -0
  66. package/dist/commands/upstream/track.js +115 -0
  67. package/dist/commands/upstream/track.js.map +1 -0
  68. package/dist/commands/upstream/update.d.ts +15 -0
  69. package/dist/commands/upstream/update.d.ts.map +1 -0
  70. package/dist/commands/upstream/update.js +98 -0
  71. package/dist/commands/upstream/update.js.map +1 -0
  72. package/dist/commands/verify/controllers.d.ts +12 -0
  73. package/dist/commands/verify/controllers.d.ts.map +1 -0
  74. package/dist/commands/verify/controllers.js +138 -0
  75. package/dist/commands/verify/controllers.js.map +1 -0
  76. package/dist/commands/verify/react-build.d.ts +13 -0
  77. package/dist/commands/verify/react-build.d.ts.map +1 -0
  78. package/dist/commands/verify/react-build.js +135 -0
  79. package/dist/commands/verify/react-build.js.map +1 -0
  80. package/dist/index.d.ts +2 -0
  81. package/dist/index.d.ts.map +1 -0
  82. package/dist/index.js +2 -0
  83. package/dist/index.js.map +1 -0
  84. package/dist/lib/mathquill/extension-generator.d.ts +9 -0
  85. package/dist/lib/mathquill/extension-generator.d.ts.map +1 -0
  86. package/dist/lib/mathquill/extension-generator.js +107 -0
  87. package/dist/lib/mathquill/extension-generator.js.map +1 -0
  88. package/dist/lib/mathquill/khan-extractor.d.ts +16 -0
  89. package/dist/lib/mathquill/khan-extractor.d.ts.map +1 -0
  90. package/dist/lib/mathquill/khan-extractor.js +148 -0
  91. package/dist/lib/mathquill/khan-extractor.js.map +1 -0
  92. package/dist/lib/mathquill/learnosity-extractor.d.ts +15 -0
  93. package/dist/lib/mathquill/learnosity-extractor.d.ts.map +1 -0
  94. package/dist/lib/mathquill/learnosity-extractor.js +170 -0
  95. package/dist/lib/mathquill/learnosity-extractor.js.map +1 -0
  96. package/dist/lib/mathquill/migration-orchestrator.d.ts +77 -0
  97. package/dist/lib/mathquill/migration-orchestrator.d.ts.map +1 -0
  98. package/dist/lib/mathquill/migration-orchestrator.js +391 -0
  99. package/dist/lib/mathquill/migration-orchestrator.js.map +1 -0
  100. package/dist/lib/mathquill/package-updater.d.ts +9 -0
  101. package/dist/lib/mathquill/package-updater.d.ts.map +1 -0
  102. package/dist/lib/mathquill/package-updater.js +103 -0
  103. package/dist/lib/mathquill/package-updater.js.map +1 -0
  104. package/dist/lib/mathquill/pie-extractor.d.ts +14 -0
  105. package/dist/lib/mathquill/pie-extractor.d.ts.map +1 -0
  106. package/dist/lib/mathquill/pie-extractor.js +167 -0
  107. package/dist/lib/mathquill/pie-extractor.js.map +1 -0
  108. package/dist/lib/upstream/repo-utils.d.ts +9 -0
  109. package/dist/lib/upstream/repo-utils.d.ts.map +1 -0
  110. package/dist/lib/upstream/repo-utils.js +10 -0
  111. package/dist/lib/upstream/repo-utils.js.map +1 -0
  112. package/dist/lib/upstream/sync-compatibility.d.ts +29 -0
  113. package/dist/lib/upstream/sync-compatibility.d.ts.map +1 -0
  114. package/dist/lib/upstream/sync-compatibility.js +70 -0
  115. package/dist/lib/upstream/sync-compatibility.js.map +1 -0
  116. package/dist/lib/upstream/sync-constants.d.ts +50 -0
  117. package/dist/lib/upstream/sync-constants.d.ts.map +1 -0
  118. package/dist/lib/upstream/sync-constants.js +66 -0
  119. package/dist/lib/upstream/sync-constants.js.map +1 -0
  120. package/dist/lib/upstream/sync-controllers-strategy.d.ts +18 -0
  121. package/dist/lib/upstream/sync-controllers-strategy.d.ts.map +1 -0
  122. package/dist/lib/upstream/sync-controllers-strategy.js +243 -0
  123. package/dist/lib/upstream/sync-controllers-strategy.js.map +1 -0
  124. package/dist/lib/upstream/sync-demo-metadata.d.ts +15 -0
  125. package/dist/lib/upstream/sync-demo-metadata.d.ts.map +1 -0
  126. package/dist/lib/upstream/sync-demo-metadata.js +165 -0
  127. package/dist/lib/upstream/sync-demo-metadata.js.map +1 -0
  128. package/dist/lib/upstream/sync-dev-exports.d.ts +13 -0
  129. package/dist/lib/upstream/sync-dev-exports.d.ts.map +1 -0
  130. package/dist/lib/upstream/sync-dev-exports.js +115 -0
  131. package/dist/lib/upstream/sync-dev-exports.js.map +1 -0
  132. package/dist/lib/upstream/sync-externals.d.ts +29 -0
  133. package/dist/lib/upstream/sync-externals.d.ts.map +1 -0
  134. package/dist/lib/upstream/sync-externals.js +164 -0
  135. package/dist/lib/upstream/sync-externals.js.map +1 -0
  136. package/dist/lib/upstream/sync-filesystem.d.ts +21 -0
  137. package/dist/lib/upstream/sync-filesystem.d.ts.map +1 -0
  138. package/dist/lib/upstream/sync-filesystem.js +64 -0
  139. package/dist/lib/upstream/sync-filesystem.js.map +1 -0
  140. package/dist/lib/upstream/sync-imports.d.ts +284 -0
  141. package/dist/lib/upstream/sync-imports.d.ts.map +1 -0
  142. package/dist/lib/upstream/sync-imports.js +816 -0
  143. package/dist/lib/upstream/sync-imports.js.map +1 -0
  144. package/dist/lib/upstream/sync-mathquill.d.ts +19 -0
  145. package/dist/lib/upstream/sync-mathquill.d.ts.map +1 -0
  146. package/dist/lib/upstream/sync-mathquill.js +160 -0
  147. package/dist/lib/upstream/sync-mathquill.js.map +1 -0
  148. package/dist/lib/upstream/sync-package-json.d.ts +28 -0
  149. package/dist/lib/upstream/sync-package-json.d.ts.map +1 -0
  150. package/dist/lib/upstream/sync-package-json.js +66 -0
  151. package/dist/lib/upstream/sync-package-json.js.map +1 -0
  152. package/dist/lib/upstream/sync-package-manager.d.ts +50 -0
  153. package/dist/lib/upstream/sync-package-manager.d.ts.map +1 -0
  154. package/dist/lib/upstream/sync-package-manager.js +570 -0
  155. package/dist/lib/upstream/sync-package-manager.js.map +1 -0
  156. package/dist/lib/upstream/sync-pielib-strategy.d.ts +26 -0
  157. package/dist/lib/upstream/sync-pielib-strategy.d.ts.map +1 -0
  158. package/dist/lib/upstream/sync-pielib-strategy.js +345 -0
  159. package/dist/lib/upstream/sync-pielib-strategy.js.map +1 -0
  160. package/dist/lib/upstream/sync-react-strategy.d.ts +54 -0
  161. package/dist/lib/upstream/sync-react-strategy.d.ts.map +1 -0
  162. package/dist/lib/upstream/sync-react-strategy.js +733 -0
  163. package/dist/lib/upstream/sync-react-strategy.js.map +1 -0
  164. package/dist/lib/upstream/sync-strategy.d.ts +49 -0
  165. package/dist/lib/upstream/sync-strategy.d.ts.map +1 -0
  166. package/dist/lib/upstream/sync-strategy.js +2 -0
  167. package/dist/lib/upstream/sync-strategy.js.map +1 -0
  168. package/dist/lib/upstream/sync-summary.d.ts +20 -0
  169. package/dist/lib/upstream/sync-summary.d.ts.map +1 -0
  170. package/dist/lib/upstream/sync-summary.js +157 -0
  171. package/dist/lib/upstream/sync-summary.js.map +1 -0
  172. package/dist/lib/upstream/sync-transforms.d.ts +60 -0
  173. package/dist/lib/upstream/sync-transforms.d.ts.map +1 -0
  174. package/dist/lib/upstream/sync-transforms.js +97 -0
  175. package/dist/lib/upstream/sync-transforms.js.map +1 -0
  176. package/dist/lib/upstream/sync-vite-config.d.ts +13 -0
  177. package/dist/lib/upstream/sync-vite-config.d.ts.map +1 -0
  178. package/dist/lib/upstream/sync-vite-config.js +231 -0
  179. package/dist/lib/upstream/sync-vite-config.js.map +1 -0
  180. package/dist/utils/compatibility.d.ts +129 -0
  181. package/dist/utils/compatibility.d.ts.map +1 -0
  182. package/dist/utils/compatibility.js +44 -0
  183. package/dist/utils/compatibility.js.map +1 -0
  184. package/dist/utils/conversion.d.ts +25 -0
  185. package/dist/utils/conversion.d.ts.map +1 -0
  186. package/dist/utils/conversion.js +123 -0
  187. package/dist/utils/conversion.js.map +1 -0
  188. package/dist/utils/git.d.ts +7 -0
  189. package/dist/utils/git.d.ts.map +1 -0
  190. package/dist/utils/git.js +63 -0
  191. package/dist/utils/git.js.map +1 -0
  192. package/dist/utils/logger.d.ts +29 -0
  193. package/dist/utils/logger.d.ts.map +1 -0
  194. package/dist/utils/logger.js +63 -0
  195. package/dist/utils/logger.js.map +1 -0
  196. package/dist/utils/package-json.d.ts +12 -0
  197. package/dist/utils/package-json.d.ts.map +1 -0
  198. package/dist/utils/package-json.js +29 -0
  199. package/dist/utils/package-json.js.map +1 -0
  200. package/package.json +59 -0
  201. package/packages/cli/bin/dev.js +5 -0
  202. package/packages/cli/bin/run.js +5 -0
  203. package/src/commands/dev/demo/index.ts +313 -0
  204. package/src/commands/dev/test-bundler.ts +345 -0
  205. package/src/commands/mathquill/migrate.ts +269 -0
  206. package/src/commands/mathquill/status.ts +267 -0
  207. package/src/commands/packages/create-controllers.ts +127 -0
  208. package/src/commands/packages/create-lib-react.ts +279 -0
  209. package/src/commands/packages/create-react-elements.ts +306 -0
  210. package/src/commands/packages/enable-publishing.ts +161 -0
  211. package/src/commands/packages/init-synced-elements.ts +259 -0
  212. package/src/commands/upstream/README.md +444 -0
  213. package/src/commands/upstream/analyze-esm.ts +1973 -0
  214. package/src/commands/upstream/check.ts +228 -0
  215. package/src/commands/upstream/deps.ts +222 -0
  216. package/src/commands/upstream/sync-mathquill.ts +108 -0
  217. package/src/commands/upstream/sync.ts +687 -0
  218. package/src/commands/upstream/track.ts +145 -0
  219. package/src/commands/upstream/update.ts +110 -0
  220. package/src/commands/verify/controllers.ts +166 -0
  221. package/src/commands/verify/react-build.ts +171 -0
  222. package/src/index.ts +1 -0
  223. package/src/lib/mathquill/extension-generator.ts +111 -0
  224. package/src/lib/mathquill/khan-extractor.ts +175 -0
  225. package/src/lib/mathquill/learnosity-extractor.ts +198 -0
  226. package/src/lib/mathquill/migration-orchestrator.ts +513 -0
  227. package/src/lib/mathquill/package-updater.ts +120 -0
  228. package/src/lib/mathquill/pie-extractor.ts +194 -0
  229. package/src/lib/upstream/repo-utils.ts +18 -0
  230. package/src/lib/upstream/sync-compatibility.ts +89 -0
  231. package/src/lib/upstream/sync-constants.ts +77 -0
  232. package/src/lib/upstream/sync-controllers-strategy.ts +291 -0
  233. package/src/lib/upstream/sync-demo-metadata.ts +199 -0
  234. package/src/lib/upstream/sync-dev-exports.ts +151 -0
  235. package/src/lib/upstream/sync-externals.ts +159 -0
  236. package/src/lib/upstream/sync-filesystem.ts +87 -0
  237. package/src/lib/upstream/sync-imports.ts +992 -0
  238. package/src/lib/upstream/sync-mathquill.ts +194 -0
  239. package/src/lib/upstream/sync-package-json.ts +86 -0
  240. package/src/lib/upstream/sync-package-manager.ts +694 -0
  241. package/src/lib/upstream/sync-pielib-strategy.ts +441 -0
  242. package/src/lib/upstream/sync-react-strategy.ts +945 -0
  243. package/src/lib/upstream/sync-strategy.ts +61 -0
  244. package/src/lib/upstream/sync-summary.ts +194 -0
  245. package/src/lib/upstream/sync-transforms.ts +154 -0
  246. package/src/lib/upstream/sync-vite-config.ts +243 -0
  247. package/src/types/es-module-lexer.d.ts +4 -0
  248. package/src/utils/compatibility.ts +182 -0
  249. package/src/utils/conversion.ts +153 -0
  250. package/src/utils/git.ts +63 -0
  251. package/src/utils/logger.ts +69 -0
  252. package/src/utils/package-json.ts +43 -0
  253. package/tests/sync-package-manager.test.ts +112 -0
  254. package/tests/transform-assign-props.test.ts +101 -0
  255. package/tsconfig.json +15 -0
@@ -0,0 +1,694 @@
1
+ /**
2
+ * Centralized package.json management for upstream sync operations
3
+ *
4
+ * This module consolidates all package.json generation and management logic
5
+ * to eliminate the massive duplication between controllers-strategy and react-strategy.
6
+ */
7
+
8
+ import { existsSync } from 'node:fs';
9
+ import { readFile, writeFile } from 'node:fs/promises';
10
+ import { dirname, join } from 'node:path';
11
+ import { loadPackageJson, type PackageJson } from '../../utils/package-json.js';
12
+ import type { SyncConfig } from './sync-strategy.js';
13
+ import { existsAny } from './sync-filesystem.js';
14
+ import { applyPackageJsonTransforms } from './sync-transforms.js';
15
+ import { BUILD_TOOLS, REACT, PACKAGE_DEFAULTS, SCRIPTS, WORKSPACE } from './sync-constants.js';
16
+
17
+ interface EntryPointMap {
18
+ hasIndex: boolean;
19
+ hasDelivery: boolean;
20
+ hasAuthor: boolean;
21
+ hasController: boolean;
22
+ hasConfigure: boolean;
23
+ hasPrint: boolean;
24
+ hasTypes: boolean;
25
+ }
26
+
27
+ /**
28
+ * Detect available entry points in an element package
29
+ */
30
+ export function detectEntryPoints(elementDir: string): EntryPointMap {
31
+ return {
32
+ hasIndex: existsAny([
33
+ join(elementDir, 'src/index.ts'),
34
+ join(elementDir, 'src/index.tsx'),
35
+ join(elementDir, 'src/index.js'),
36
+ join(elementDir, 'src/index.jsx'),
37
+ ]),
38
+ hasDelivery: existsAny([
39
+ join(elementDir, 'src/delivery/index.ts'),
40
+ join(elementDir, 'src/delivery/index.tsx'),
41
+ join(elementDir, 'src/delivery/index.js'),
42
+ join(elementDir, 'src/delivery/index.jsx'),
43
+ ]),
44
+ hasAuthor: existsAny([
45
+ join(elementDir, 'src/author/index.ts'),
46
+ join(elementDir, 'src/author/index.tsx'),
47
+ join(elementDir, 'src/author/index.js'),
48
+ join(elementDir, 'src/author/index.jsx'),
49
+ ]),
50
+ hasController: existsAny([
51
+ join(elementDir, 'src/controller/index.ts'),
52
+ join(elementDir, 'src/controller/index.tsx'),
53
+ join(elementDir, 'src/controller/index.js'),
54
+ join(elementDir, 'src/controller/index.jsx'),
55
+ ]),
56
+ hasConfigure: existsAny([
57
+ join(elementDir, 'src/configure/index.ts'),
58
+ join(elementDir, 'src/configure/index.tsx'),
59
+ join(elementDir, 'src/configure/index.js'),
60
+ join(elementDir, 'src/configure/index.jsx'),
61
+ ]),
62
+ hasPrint: existsAny([
63
+ join(elementDir, 'src/print/index.ts'),
64
+ join(elementDir, 'src/print/index.tsx'),
65
+ join(elementDir, 'src/print/index.js'),
66
+ join(elementDir, 'src/print/index.jsx'),
67
+ ]),
68
+ hasTypes: existsAny([
69
+ join(elementDir, 'src/types/index.ts'),
70
+ join(elementDir, 'src/types/index.tsx'),
71
+ join(elementDir, 'src/types/index.js'),
72
+ join(elementDir, 'src/types/index.jsx'),
73
+ ]),
74
+ };
75
+ }
76
+
77
+ /**
78
+ * Generate exports object based on available entry points
79
+ */
80
+ export function generateExportsObject(entryPoints: EntryPointMap): Record<string, unknown> {
81
+ const exports: Record<string, unknown> = {
82
+ '.': {
83
+ types: './dist/index.d.ts',
84
+ default: './dist/index.js',
85
+ },
86
+ };
87
+
88
+ if (entryPoints.hasDelivery) {
89
+ exports['./delivery'] = {
90
+ types: './dist/delivery/index.d.ts',
91
+ default: './dist/delivery/index.js',
92
+ };
93
+ }
94
+
95
+ if (entryPoints.hasAuthor) {
96
+ exports['./author'] = {
97
+ types: './dist/author/index.d.ts',
98
+ default: './dist/author/index.js',
99
+ };
100
+ }
101
+
102
+ if (entryPoints.hasController) {
103
+ exports['./controller'] = {
104
+ types: './dist/controller/index.d.ts',
105
+ default: './dist/controller/index.js',
106
+ };
107
+ }
108
+
109
+ if (entryPoints.hasConfigure) {
110
+ exports['./configure'] = {
111
+ types: './dist/configure/index.d.ts',
112
+ default: './dist/configure/index.js',
113
+ };
114
+ }
115
+
116
+ if (entryPoints.hasPrint) {
117
+ exports['./print'] = {
118
+ types: './dist/print/index.d.ts',
119
+ default: './dist/print/index.js',
120
+ };
121
+ }
122
+
123
+ if (entryPoints.hasTypes) {
124
+ exports['./types'] = {
125
+ types: './dist/types/index.d.ts',
126
+ default: './dist/types/index.js',
127
+ };
128
+ }
129
+
130
+ return exports;
131
+ }
132
+
133
+ /**
134
+ * Extract imports from source files to determine runtime dependencies
135
+ */
136
+ export async function extractImportsFromSources(elementDir: string): Promise<Set<string>> {
137
+ const imports = new Set<string>();
138
+ const srcDir = join(elementDir, 'src');
139
+
140
+ if (!existsSync(srcDir)) {
141
+ return imports;
142
+ }
143
+
144
+ // Recursively find all .ts, .tsx, .js, .jsx files
145
+ const { readdir: readdirRecursive } = await import('node:fs/promises');
146
+
147
+ async function scanDirectory(dir: string): Promise<void> {
148
+ try {
149
+ const entries = await readdirRecursive(dir, { withFileTypes: true });
150
+
151
+ for (const entry of entries) {
152
+ const fullPath = join(dir, entry.name);
153
+
154
+ if (entry.isDirectory()) {
155
+ await scanDirectory(fullPath);
156
+ } else if (entry.isFile() && /\.(ts|tsx|js|jsx)$/.test(entry.name)) {
157
+ try {
158
+ const content = await readFile(fullPath, 'utf-8');
159
+ // Match import statements: import ... from 'package' or import('package')
160
+ const importRegex = /import\s+(?:[\w{},\s*]+\s+from\s+)?['"]([^'"]+)['"]/g;
161
+ const dynamicImportRegex = /import\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
162
+
163
+ let match: RegExpExecArray | null;
164
+
165
+ match = importRegex.exec(content);
166
+ while (match !== null) {
167
+ const importPath = match[1];
168
+ // Only track package imports (not relative imports)
169
+ if (!importPath.startsWith('.') && !importPath.startsWith('/')) {
170
+ imports.add(importPath);
171
+ }
172
+ match = importRegex.exec(content);
173
+ }
174
+
175
+ match = dynamicImportRegex.exec(content);
176
+ while (match !== null) {
177
+ const importPath = match[1];
178
+ if (!importPath.startsWith('.') && !importPath.startsWith('/')) {
179
+ imports.add(importPath);
180
+ }
181
+ match = dynamicImportRegex.exec(content);
182
+ }
183
+ } catch (err) {
184
+ // Skip files that can't be read
185
+ }
186
+ }
187
+ }
188
+ } catch (err) {
189
+ // Skip directories that can't be read
190
+ }
191
+ }
192
+
193
+ await scanDirectory(srcDir);
194
+ return imports;
195
+ }
196
+
197
+ async function findInstalledPackageJson(
198
+ packageName: string,
199
+ fromDir: string
200
+ ): Promise<PackageJson | null> {
201
+ try {
202
+ const { createRequire } = await import('node:module');
203
+ const req = createRequire(join(fromDir, 'package.json'));
204
+ const resolvedEntry = req.resolve(packageName);
205
+
206
+ let currentDir = dirname(resolvedEntry);
207
+ while (true) {
208
+ const candidate = join(currentDir, 'package.json');
209
+ if (existsSync(candidate)) {
210
+ const pkg = await loadPackageJson(candidate).catch(() => null);
211
+ if (pkg && pkg.name === packageName) {
212
+ return pkg;
213
+ }
214
+ }
215
+
216
+ const parent = dirname(currentDir);
217
+ if (parent === currentDir) {
218
+ break;
219
+ }
220
+ currentDir = parent;
221
+ }
222
+ } catch {
223
+ // Package not resolvable from this location.
224
+ }
225
+ return null;
226
+ }
227
+
228
+ async function addTransitivePeerDependencies(
229
+ deps: Record<string, string>,
230
+ fromDir: string
231
+ ): Promise<void> {
232
+ const depNames = Object.keys(deps);
233
+
234
+ for (const depName of depNames) {
235
+ if (
236
+ depName.startsWith(WORKSPACE.PIE_LIB_PREFIX) ||
237
+ depName.startsWith(WORKSPACE.PIE_ELEMENT_PREFIX) ||
238
+ depName.startsWith(WORKSPACE.PIE_FRAMEWORK_PREFIX)
239
+ ) {
240
+ continue;
241
+ }
242
+
243
+ const installedPkg = await findInstalledPackageJson(depName, fromDir);
244
+ const peerDeps = (installedPkg?.peerDependencies as Record<string, string> | undefined) ?? {};
245
+
246
+ for (const [peerName, peerVersion] of Object.entries(peerDeps)) {
247
+ if (deps[peerName]) {
248
+ continue;
249
+ }
250
+
251
+ if (
252
+ peerName.startsWith(WORKSPACE.PIE_LIB_PREFIX) ||
253
+ peerName.startsWith(WORKSPACE.PIE_ELEMENT_PREFIX) ||
254
+ peerName.startsWith(WORKSPACE.PIE_FRAMEWORK_PREFIX)
255
+ ) {
256
+ deps[peerName] = WORKSPACE.VERSION;
257
+ } else {
258
+ deps[peerName] = peerVersion;
259
+ }
260
+ }
261
+ }
262
+ }
263
+
264
+ async function inferPeerVersionFromDeclaredDeps(
265
+ targetDep: string,
266
+ deps: Record<string, string>,
267
+ fromDir: string
268
+ ): Promise<string | null> {
269
+ for (const depName of Object.keys(deps)) {
270
+ if (
271
+ depName.startsWith(WORKSPACE.PIE_LIB_PREFIX) ||
272
+ depName.startsWith(WORKSPACE.PIE_ELEMENT_PREFIX) ||
273
+ depName.startsWith(WORKSPACE.PIE_FRAMEWORK_PREFIX)
274
+ ) {
275
+ continue;
276
+ }
277
+
278
+ const installedPkg = await findInstalledPackageJson(depName, fromDir);
279
+ const peerDeps = (installedPkg?.peerDependencies as Record<string, string> | undefined) ?? {};
280
+ if (peerDeps[targetDep]) {
281
+ return peerDeps[targetDep];
282
+ }
283
+ }
284
+
285
+ return null;
286
+ }
287
+
288
+ /**
289
+ * Extract and normalize dependencies from upstream package.json
290
+ */
291
+ export function extractUpstreamDependencies(
292
+ upstreamPkg: PackageJson | null
293
+ ): Record<string, string> {
294
+ if (!upstreamPkg) return {};
295
+
296
+ const upstreamDeps = (upstreamPkg.dependencies as Record<string, string> | undefined) ?? {};
297
+ const expectedDeps: Record<string, string> = {};
298
+
299
+ for (const [name, version] of Object.entries(upstreamDeps)) {
300
+ if (name.startsWith(WORKSPACE.PIE_LIB_PREFIX)) {
301
+ expectedDeps[name] = WORKSPACE.VERSION;
302
+ } else if (name !== 'react' && name !== 'react-dom') {
303
+ expectedDeps[name] = version;
304
+ }
305
+ }
306
+
307
+ return expectedDeps;
308
+ }
309
+
310
+ /**
311
+ * Ensure devDependencies include all required build tools
312
+ */
313
+ export function ensureBuildToolDependencies(pkg: PackageJson): void {
314
+ if (!pkg.devDependencies || typeof pkg.devDependencies !== 'object') {
315
+ pkg.devDependencies = {};
316
+ }
317
+
318
+ const devDeps = pkg.devDependencies as Record<string, string>;
319
+
320
+ if (!devDeps.vite) devDeps.vite = BUILD_TOOLS.VITE;
321
+ if (!devDeps.typescript) devDeps.typescript = BUILD_TOOLS.TYPESCRIPT;
322
+ if (!devDeps['@vitejs/plugin-react'])
323
+ devDeps['@vitejs/plugin-react'] = BUILD_TOOLS.VITE_REACT_PLUGIN;
324
+ if (!devDeps['@types/react']) devDeps['@types/react'] = REACT.TYPES_VERSION;
325
+ if (!devDeps['@types/react-dom']) devDeps['@types/react-dom'] = REACT.TYPES_VERSION;
326
+ }
327
+
328
+ /**
329
+ * Check if a @pie-framework or @pie-element/shared- package exists in the workspace
330
+ *
331
+ * Supports both legacy @pie-framework naming and new @pie-element/shared- naming:
332
+ * - @pie-framework/mathquill → packages/shared/mathquill (legacy, should migrate)
333
+ * - @pie-element/shared-mathquill → packages/shared/mathquill (new convention)
334
+ */
335
+ function isPieFrameworkWorkspacePackage(packageName: string, config: SyncConfig): boolean {
336
+ let pkgName: string;
337
+
338
+ if (packageName.startsWith(WORKSPACE.PIE_FRAMEWORK_PREFIX)) {
339
+ // Legacy @pie-framework/* packages
340
+ pkgName = packageName.replace(WORKSPACE.PIE_FRAMEWORK_PREFIX, '');
341
+ } else if (packageName.startsWith(WORKSPACE.PIE_ELEMENT_PREFIX + 'shared-')) {
342
+ // New @pie-element/shared-* packages
343
+ pkgName = packageName.replace(WORKSPACE.PIE_ELEMENT_PREFIX + 'shared-', '');
344
+ } else {
345
+ return false;
346
+ }
347
+
348
+ const sharedPackagePath = join(config.pieElementsNg, 'packages/shared', pkgName, 'package.json');
349
+ return existsSync(sharedPackagePath);
350
+ }
351
+
352
+ /**
353
+ * Generate or update element package.json
354
+ *
355
+ * This function consolidates the ~400 lines of duplicated package.json management
356
+ * logic from controllers-strategy and react-strategy into a single, maintainable function.
357
+ */
358
+ export async function ensureElementPackageJson(
359
+ elementName: string,
360
+ elementDir: string,
361
+ config: SyncConfig
362
+ ): Promise<boolean> {
363
+ if (!existsSync(elementDir)) {
364
+ return false;
365
+ }
366
+
367
+ const pkgPath = join(elementDir, 'package.json');
368
+ const upstreamPkgPath = join(config.pieElements, 'packages', elementName, 'package.json');
369
+
370
+ // Load existing package.json
371
+ let pkg: PackageJson | null = null;
372
+ if (existsSync(pkgPath)) {
373
+ pkg = await loadPackageJson(pkgPath).catch(() => null);
374
+ }
375
+
376
+ // Load upstream package.json for dependency info
377
+ const upstreamPkg = existsSync(upstreamPkgPath)
378
+ ? await loadPackageJson(upstreamPkgPath).catch(() => null)
379
+ : null;
380
+
381
+ // Extract and normalize upstream dependencies
382
+ const expectedDeps = extractUpstreamDependencies(upstreamPkg);
383
+
384
+ // Scan source files for actual imports to catch dependencies from skipped directories
385
+ const importedPackages = await extractImportsFromSources(elementDir);
386
+
387
+ // Add workspace dependencies found in imports
388
+ const currentPackageName = `${WORKSPACE.PIE_ELEMENT_PREFIX}${elementName}`;
389
+ for (const imported of importedPackages) {
390
+ // Skip self-dependencies (package importing from itself)
391
+ if (imported === currentPackageName) {
392
+ continue;
393
+ }
394
+
395
+ if (imported.startsWith(WORKSPACE.PIE_LIB_PREFIX)) {
396
+ expectedDeps[imported] = WORKSPACE.VERSION;
397
+ } else if (imported.startsWith(WORKSPACE.PIE_ELEMENT_PREFIX)) {
398
+ expectedDeps[imported] = WORKSPACE.VERSION;
399
+ } else if (imported.startsWith(WORKSPACE.PIE_FRAMEWORK_PREFIX)) {
400
+ // Only add as workspace dependency if it actually exists in packages/shared/
401
+ if (isPieFrameworkWorkspacePackage(imported, config)) {
402
+ expectedDeps[imported] = WORKSPACE.VERSION;
403
+ } else {
404
+ // External @pie-framework package - add with latest version if not already in deps
405
+ if (!expectedDeps[imported]) {
406
+ expectedDeps[imported] = '^1.0.0'; // Use latest 1.x version as default
407
+ }
408
+ }
409
+ }
410
+ }
411
+ await addTransitivePeerDependencies(expectedDeps, elementDir);
412
+
413
+ // Create minimal package.json if missing
414
+ if (!pkg) {
415
+ pkg = {
416
+ name: `${WORKSPACE.PIE_ELEMENT_PREFIX}${elementName}`,
417
+ private: true,
418
+ version: '0.1.0',
419
+ description:
420
+ (upstreamPkg?.description as string | undefined) ??
421
+ `React implementation of ${elementName} element synced from pie-elements`,
422
+ dependencies: expectedDeps,
423
+ peerDependencies: {
424
+ react: REACT.VERSION,
425
+ 'react-dom': REACT.VERSION,
426
+ },
427
+ };
428
+ }
429
+
430
+ // Update dependencies to match upstream
431
+ if (Object.keys(expectedDeps).length > 0) {
432
+ pkg.dependencies = expectedDeps;
433
+ }
434
+
435
+ // Preserve pie metadata (if present upstream or locally)
436
+ const pieMetadata = ((upstreamPkg as PackageJson | null | undefined)?.pie ??
437
+ (pkg as PackageJson | null | undefined)?.pie ??
438
+ undefined) as { capabilities?: string[] } | undefined;
439
+
440
+ // Apply all standard transformations
441
+ pkg = applyPackageJsonTransforms(pkg);
442
+
443
+ if (pieMetadata) {
444
+ pkg.pie = pieMetadata;
445
+ }
446
+
447
+ // Detect available entry points
448
+ const entryPoints = detectEntryPoints(elementDir);
449
+
450
+ // Generate exports based on entry points
451
+ pkg.exports = generateExportsObject(entryPoints);
452
+
453
+ // Warn when metadata/structure disagree for core capabilities
454
+ const metadataCapabilities = Array.isArray(pieMetadata?.capabilities)
455
+ ? new Set(pieMetadata.capabilities)
456
+ : null;
457
+
458
+ if (!entryPoints.hasDelivery || !entryPoints.hasController) {
459
+ console.warn(
460
+ `[upstream:update] ${elementName} missing required entry points:` +
461
+ `${entryPoints.hasDelivery ? '' : ' delivery'}` +
462
+ `${entryPoints.hasController ? '' : ' controller'}`
463
+ );
464
+ }
465
+
466
+ if (metadataCapabilities) {
467
+ if (metadataCapabilities.has('author') && !entryPoints.hasAuthor) {
468
+ console.warn(
469
+ `[upstream:update] ${elementName} metadata includes author but no src/author entry`
470
+ );
471
+ }
472
+ if (metadataCapabilities.has('print') && !entryPoints.hasPrint) {
473
+ console.warn(
474
+ `[upstream:update] ${elementName} metadata includes print but no src/print entry`
475
+ );
476
+ }
477
+ } else {
478
+ if (entryPoints.hasAuthor) {
479
+ console.warn(
480
+ `[upstream:update] ${elementName} has src/author but no pie.capabilities metadata`
481
+ );
482
+ }
483
+ if (entryPoints.hasPrint) {
484
+ console.warn(
485
+ `[upstream:update] ${elementName} has src/print but no pie.capabilities metadata`
486
+ );
487
+ }
488
+ }
489
+
490
+ // Set core package.json fields
491
+ pkg.name = `${WORKSPACE.PIE_ELEMENT_PREFIX}${elementName}`;
492
+ pkg.type = PACKAGE_DEFAULTS.TYPE;
493
+ pkg.main = './dist/index.js';
494
+ pkg.types = './dist/index.d.ts';
495
+
496
+ // Ensure files array
497
+ const files = Array.isArray(pkg.files) ? (pkg.files as unknown[]) : [];
498
+ const normalizedFiles = new Set<string>(files.filter((v): v is string => typeof v === 'string'));
499
+ normalizedFiles.add('dist');
500
+ normalizedFiles.add('src');
501
+ pkg.files = Array.from(normalizedFiles).sort();
502
+
503
+ // Set sideEffects
504
+ if (typeof pkg.sideEffects === 'undefined') {
505
+ pkg.sideEffects = PACKAGE_DEFAULTS.SIDE_EFFECTS;
506
+ }
507
+
508
+ // Ensure devDependencies
509
+ ensureBuildToolDependencies(pkg);
510
+
511
+ // Ensure build scripts
512
+ if (!pkg.scripts || typeof pkg.scripts !== 'object') {
513
+ pkg.scripts = {};
514
+ }
515
+
516
+ const scripts = pkg.scripts as Record<string, string>;
517
+ const hasIifeEntry =
518
+ existsSync(join(elementDir, 'src/index.iife.ts')) ||
519
+ existsSync(join(elementDir, 'vite.config.iife.ts'));
520
+
521
+ scripts.build = hasIifeEntry ? SCRIPTS.BUILD_WITH_IIFE : SCRIPTS.BUILD;
522
+ scripts.dev = SCRIPTS.DEV;
523
+ scripts.demo = SCRIPTS.DEMO;
524
+ scripts.test = SCRIPTS.TEST;
525
+
526
+ // Check if content changed
527
+ const nextContent = `${JSON.stringify(pkg, null, 2)}\n`;
528
+ const currentContent = existsSync(pkgPath)
529
+ ? await readFile(pkgPath, 'utf-8').catch(() => null)
530
+ : null;
531
+
532
+ if (currentContent === nextContent) {
533
+ return false;
534
+ }
535
+
536
+ // Write updated package.json
537
+ await writeFile(pkgPath, nextContent, 'utf-8');
538
+ return true;
539
+ }
540
+
541
+ /**
542
+ * Generate or update pie-lib package.json
543
+ */
544
+ export async function ensurePieLibPackageJson(
545
+ pkgName: string,
546
+ pkgDir: string,
547
+ config: SyncConfig
548
+ ): Promise<boolean> {
549
+ if (!existsSync(pkgDir)) {
550
+ return false;
551
+ }
552
+
553
+ const pkgPath = join(pkgDir, 'package.json');
554
+ const upstreamPkgPath = join(config.pieLib, 'packages', pkgName, 'package.json');
555
+
556
+ let pkg: PackageJson | null = null;
557
+ if (existsSync(pkgPath)) {
558
+ pkg = await loadPackageJson(pkgPath).catch(() => null);
559
+ }
560
+
561
+ const upstreamPkg = existsSync(upstreamPkgPath)
562
+ ? await loadPackageJson(upstreamPkgPath).catch(() => null)
563
+ : null;
564
+
565
+ // Extract upstream dependencies
566
+ const upstreamDeps = (upstreamPkg?.dependencies as Record<string, string> | undefined) ?? {};
567
+ const expectedDeps: Record<string, string> = {};
568
+
569
+ for (const [name, version] of Object.entries(upstreamDeps)) {
570
+ // Transform @pie-framework/mathquill to @pie-element/shared-mathquill
571
+ if (name === '@pie-framework/mathquill') {
572
+ expectedDeps['@pie-element/shared-mathquill'] = WORKSPACE.VERSION;
573
+ } else if (name.startsWith(WORKSPACE.PIE_LIB_PREFIX)) {
574
+ expectedDeps[name] = WORKSPACE.VERSION;
575
+ } else {
576
+ expectedDeps[name] = version;
577
+ }
578
+ }
579
+
580
+ const importedPackages = await extractImportsFromSources(pkgDir);
581
+ for (const importedPkg of importedPackages) {
582
+ if (importedPkg === '@pie-framework/mathquill') {
583
+ expectedDeps['@pie-element/shared-mathquill'] = WORKSPACE.VERSION;
584
+ continue;
585
+ }
586
+ if (importedPkg.startsWith(WORKSPACE.PIE_LIB_PREFIX)) {
587
+ expectedDeps[importedPkg] = WORKSPACE.VERSION;
588
+ continue;
589
+ }
590
+ if (importedPkg.startsWith(WORKSPACE.PIE_ELEMENT_PREFIX)) {
591
+ expectedDeps[importedPkg] = WORKSPACE.VERSION;
592
+ continue;
593
+ }
594
+
595
+ // Keep pie-lib package.json aligned with actual runtime imports from src/.
596
+ // Upstream package.json can miss some third-party dependencies.
597
+ if (!expectedDeps[importedPkg]) {
598
+ const installedPkg = await findInstalledPackageJson(importedPkg, pkgDir);
599
+ if (installedPkg?.version) {
600
+ expectedDeps[importedPkg] = `^${installedPkg.version}`;
601
+ } else {
602
+ const inferredPeerVersion = await inferPeerVersionFromDeclaredDeps(
603
+ importedPkg,
604
+ expectedDeps,
605
+ pkgDir
606
+ );
607
+ if (inferredPeerVersion) {
608
+ expectedDeps[importedPkg] = inferredPeerVersion;
609
+ }
610
+ }
611
+ }
612
+ }
613
+
614
+ // graphing imports @dnd-kit/core directly in source but upstream metadata can omit it.
615
+ if (pkgName === 'graphing' && !expectedDeps['@dnd-kit/core']) {
616
+ expectedDeps['@dnd-kit/core'] = '^6.3.0';
617
+ }
618
+
619
+ // Create minimal package.json if missing
620
+ if (!pkg) {
621
+ pkg = {
622
+ name: `${WORKSPACE.PIE_LIB_PREFIX}${pkgName}`,
623
+ private: true,
624
+ version: '0.1.0',
625
+ description:
626
+ (upstreamPkg?.description as string | undefined) ??
627
+ `React implementation of @pie-lib/${pkgName} synced from pie-lib`,
628
+ dependencies: expectedDeps,
629
+ };
630
+ }
631
+
632
+ // Update dependencies
633
+ if (Object.keys(expectedDeps).length > 0) {
634
+ pkg.dependencies = expectedDeps;
635
+ }
636
+
637
+ // Special handling for math-rendering: reference MathJax adapter package
638
+ if (pkgName === 'math-rendering') {
639
+ pkg.dependencies = {
640
+ [`${WORKSPACE.PIE_ELEMENT_PREFIX}shared-math-rendering-mathjax`]: WORKSPACE.VERSION,
641
+ };
642
+ }
643
+
644
+ // Generate exports
645
+ const exportsObj: Record<string, unknown> = {
646
+ ...(typeof pkg.exports === 'object' && pkg.exports
647
+ ? (pkg.exports as Record<string, unknown>)
648
+ : {}),
649
+ };
650
+
651
+ exportsObj['.'] = {
652
+ types: './dist/index.d.ts',
653
+ default: './dist/index.js',
654
+ };
655
+
656
+ pkg.name = `${WORKSPACE.PIE_LIB_PREFIX}${pkgName}`;
657
+ pkg.type = PACKAGE_DEFAULTS.TYPE;
658
+ pkg.main = './dist/index.js';
659
+ pkg.types = './dist/index.d.ts';
660
+ pkg.exports = exportsObj;
661
+
662
+ // Ensure files array
663
+ const files = Array.isArray(pkg.files) ? (pkg.files as unknown[]) : [];
664
+ const normalizedFiles = new Set<string>(files.filter((v): v is string => typeof v === 'string'));
665
+ normalizedFiles.add('dist');
666
+ normalizedFiles.add('src');
667
+ pkg.files = Array.from(normalizedFiles).sort();
668
+
669
+ if (typeof pkg.sideEffects === 'undefined') {
670
+ pkg.sideEffects = PACKAGE_DEFAULTS.SIDE_EFFECTS;
671
+ }
672
+
673
+ // Ensure build scripts
674
+ if (!pkg.scripts || typeof pkg.scripts !== 'object') {
675
+ pkg.scripts = {};
676
+ }
677
+
678
+ const scripts = pkg.scripts as Record<string, string>;
679
+ scripts.build = SCRIPTS.BUILD;
680
+ scripts.dev = SCRIPTS.DEV;
681
+ scripts.test = SCRIPTS.TEST;
682
+
683
+ const nextContent = `${JSON.stringify(pkg, null, 2)}\n`;
684
+ const currentContent = existsSync(pkgPath)
685
+ ? await readFile(pkgPath, 'utf-8').catch(() => null)
686
+ : null;
687
+
688
+ if (currentContent === nextContent) {
689
+ return false;
690
+ }
691
+
692
+ await writeFile(pkgPath, nextContent, 'utf-8');
693
+ return true;
694
+ }