@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,345 @@
1
+ import { Command, Flags } from '@oclif/core';
2
+ import { Logger } from '../../utils/logger.js';
3
+ import {
4
+ mkDependencyHash,
5
+ generateEntries,
6
+ createWebpackConfig,
7
+ type BuildDependency,
8
+ } from '@pie-element/bundler-shared';
9
+ import webpack from 'webpack';
10
+ import { execSync } from 'node:child_process';
11
+ import { existsSync, readFileSync, rmSync, symlinkSync, mkdirSync, writeFileSync } from 'node:fs';
12
+ import { tmpdir } from 'node:os';
13
+ import { join } from 'node:path';
14
+
15
+ export default class TestBundler extends Command {
16
+ static override description = 'Test the PIE bundler service with local workspace packages';
17
+
18
+ static override examples = [
19
+ 'bun run cli <%= command.id %>',
20
+ 'bun cli <%= command.id %>',
21
+ '<%= config.bin %> <%= command.id %>',
22
+ 'bun cli <%= command.id %> --element multiple-choice',
23
+ 'bun cli <%= command.id %> --clean',
24
+ ];
25
+
26
+ static override flags = {
27
+ element: Flags.string({
28
+ char: 'e',
29
+ description: 'Test with specific element (default: multiple-choice)',
30
+ default: 'multiple-choice',
31
+ }),
32
+ clean: Flags.boolean({
33
+ description: 'Clean test workspace before running',
34
+ default: false,
35
+ }),
36
+ verbose: Flags.boolean({
37
+ char: 'v',
38
+ description: 'Show detailed output including webpack logs',
39
+ default: false,
40
+ }),
41
+ 'keep-workspace': Flags.boolean({
42
+ description: 'Keep workspace directory after test (for debugging)',
43
+ default: false,
44
+ }),
45
+ };
46
+
47
+ private logger!: Logger;
48
+
49
+ public async run(): Promise<void> {
50
+ const { flags } = await this.parse(TestBundler);
51
+ this.logger = new Logger(flags.verbose);
52
+
53
+ this.logger.section('๐Ÿš€ Testing PIE Bundler with Workspace Packages');
54
+
55
+ // Setup paths
56
+ const workspaceRoot = join(tmpdir(), 'pie-bundler-workspace-test');
57
+ const cacheDir = join(workspaceRoot, 'cache');
58
+ const outputDir = join(workspaceRoot, 'output');
59
+
60
+ if (flags.clean && existsSync(workspaceRoot)) {
61
+ this.logger.info('Cleaning old workspace...');
62
+ rmSync(workspaceRoot, { recursive: true, force: true });
63
+ }
64
+
65
+ mkdirSync(outputDir, { recursive: true });
66
+ mkdirSync(cacheDir, { recursive: true });
67
+
68
+ // Build local packages first
69
+ this.log('\n๐Ÿ“ฆ Building local packages');
70
+ await this.buildLocalPackages(flags.element);
71
+
72
+ // Create workspace with local packages
73
+ this.log('\n๐Ÿ”— Setting up test workspace');
74
+ const workspaceDir = await this.setupWorkspace(workspaceRoot, flags.element);
75
+
76
+ // Test the bundler - use workspaceDir instead of installing from NPM
77
+ this.log('\nโš™๏ธ Running bundler test');
78
+
79
+ try {
80
+ this.logger.info(`Building bundle for @pie-element/${flags.element}...`);
81
+
82
+ // Call bundler directly without download step - workspace already has packages
83
+ const result = await this.buildFromWorkspace(workspaceDir, outputDir, flags.element);
84
+
85
+ if (flags.verbose) {
86
+ this.log('\nBuild Result:');
87
+ this.log(JSON.stringify(result, null, 2));
88
+ }
89
+
90
+ if (!result.success) {
91
+ this.logger.error('Build failed');
92
+ if (result.errors) {
93
+ this.log('\nErrors:');
94
+ for (const err of result.errors) {
95
+ this.log(` - ${err}`);
96
+ }
97
+ }
98
+ this.error('Bundler test failed', { exit: 1 });
99
+ }
100
+
101
+ // Verify results
102
+ this.log('\nโœ“ Verifying bundles');
103
+ await this.verifyBundles(outputDir, result.hash);
104
+
105
+ // Summary
106
+ this.log(`\n${'='.repeat(60)}`);
107
+ this.log('โœ… BUNDLER TEST PASSED');
108
+ this.log('='.repeat(60));
109
+ this.log(`Element: @pie-element/${flags.element}`);
110
+ this.log(`Hash: ${result.hash}`);
111
+ this.log(`Duration: ${result.duration}ms`);
112
+ this.log(`Cached: ${result.cached ? 'Yes' : 'No'}`);
113
+ this.log(`Bundle dir: ${join(outputDir, result.hash)}`);
114
+
115
+ if (!flags['keep-workspace']) {
116
+ this.logger.info('\nCleaning up workspace...');
117
+ rmSync(workspaceRoot, { recursive: true, force: true });
118
+ } else {
119
+ this.log(`Workspace: ${workspaceRoot}`);
120
+ }
121
+
122
+ this.log('='.repeat(60));
123
+ this.log('');
124
+ } catch (error: any) {
125
+ this.logger.error(`Test failed: ${error.message}`);
126
+ if (flags.verbose) {
127
+ this.log(error.stack);
128
+ }
129
+ this.error('Bundler test failed', { exit: 1 });
130
+ }
131
+ }
132
+
133
+ private async buildLocalPackages(element: string): Promise<void> {
134
+ const packages = ['packages/shared/mathquill', `packages/elements-react/${element}`];
135
+
136
+ for (const pkg of packages) {
137
+ if (!existsSync(pkg)) {
138
+ this.logger.warn(`Package not found: ${pkg}`);
139
+ continue;
140
+ }
141
+
142
+ this.logger.info(`Building ${pkg}...`);
143
+ try {
144
+ execSync('bun run build', {
145
+ cwd: pkg,
146
+ stdio: this.logger.isVerbose() ? 'inherit' : 'pipe',
147
+ });
148
+ } catch (error: any) {
149
+ this.logger.warn(`Build failed for ${pkg}: ${error.message}`);
150
+ }
151
+ }
152
+ }
153
+
154
+ private async setupWorkspace(workspaceRoot: string, element: string): Promise<string> {
155
+ const workspaceDir = join(workspaceRoot, 'workspace');
156
+ const packagesDir = join(workspaceDir, 'packages');
157
+
158
+ mkdirSync(packagesDir, { recursive: true });
159
+
160
+ // Create workspace package.json with bundler dependencies
161
+ writeFileSync(
162
+ join(workspaceDir, 'package.json'),
163
+ JSON.stringify(
164
+ {
165
+ name: 'pie-bundler-test-workspace',
166
+ private: true,
167
+ workspaces: [
168
+ 'packages/*',
169
+ 'packages/*/controller',
170
+ 'packages/*/configure',
171
+ 'packages/*/author',
172
+ ],
173
+ dependencies: {
174
+ 'esbuild-loader': '^4.4.2',
175
+ 'css-loader': '^7.1.2',
176
+ 'style-loader': '^4.0.0',
177
+ 'url-loader': '^4.1.1',
178
+ },
179
+ },
180
+ null,
181
+ 2
182
+ )
183
+ );
184
+
185
+ // Symlink local packages
186
+ const localPackages = {
187
+ 'shared-mathquill': 'packages/shared/mathquill',
188
+ [element]: `packages/elements-react/${element}`,
189
+ };
190
+
191
+ for (const [name, path] of Object.entries(localPackages)) {
192
+ if (!existsSync(path)) {
193
+ this.logger.warn(`Package not found: ${path}`);
194
+ continue;
195
+ }
196
+
197
+ const linkPath = join(packagesDir, name);
198
+ if (existsSync(linkPath)) {
199
+ rmSync(linkPath, { recursive: true, force: true });
200
+ }
201
+
202
+ this.logger.info(`Linking ${name} โ†’ ${path}`);
203
+ symlinkSync(join(process.cwd(), path), linkPath, 'dir');
204
+ }
205
+
206
+ // Install dependencies
207
+ this.logger.info('Installing workspace dependencies...');
208
+ try {
209
+ execSync('bun install', {
210
+ cwd: workspaceDir,
211
+ stdio: this.logger.isVerbose() ? 'inherit' : 'pipe',
212
+ });
213
+ } catch (error: any) {
214
+ this.logger.warn(`Workspace install had issues: ${error.message}`);
215
+ }
216
+
217
+ // Create symlinks in node_modules for @pie-element packages
218
+ const nodeModulesDir = join(workspaceDir, 'node_modules');
219
+ const pieElementDir = join(nodeModulesDir, '@pie-element');
220
+ mkdirSync(pieElementDir, { recursive: true });
221
+
222
+ // Link shared-mathquill
223
+ const mathquillLink = join(pieElementDir, 'shared-mathquill');
224
+ if (existsSync(mathquillLink)) {
225
+ rmSync(mathquillLink, { recursive: true, force: true });
226
+ }
227
+ symlinkSync(join(packagesDir, 'shared-mathquill'), mathquillLink, 'dir');
228
+ this.logger.info('Linked @pie-element/shared-mathquill in node_modules');
229
+
230
+ // Link element package
231
+ const elementLink = join(pieElementDir, element);
232
+ if (existsSync(elementLink)) {
233
+ rmSync(elementLink, { recursive: true, force: true });
234
+ }
235
+ symlinkSync(join(packagesDir, element), elementLink, 'dir');
236
+ this.logger.info(`Linked @pie-element/${element} in node_modules`);
237
+
238
+ return workspaceDir;
239
+ }
240
+
241
+ private async buildFromWorkspace(
242
+ workspaceDir: string,
243
+ outputDir: string,
244
+ element: string
245
+ ): Promise<any> {
246
+ const dependencies: BuildDependency[] = [{ name: `@pie-element/${element}`, version: '0.1.0' }];
247
+ const hash = mkDependencyHash(dependencies);
248
+ const startTime = Date.now();
249
+
250
+ this.logger.info(`Bundle hash: ${hash}`);
251
+
252
+ // Generate entry files
253
+ const entryDir = join(workspaceDir, 'entries');
254
+ mkdirSync(entryDir, { recursive: true });
255
+
256
+ const requestedBundles: Array<'player' | 'client-player' | 'editor'> = [
257
+ 'player',
258
+ 'client-player',
259
+ 'editor',
260
+ ];
261
+ const entries = generateEntries(dependencies, workspaceDir, requestedBundles);
262
+
263
+ writeFileSync(join(entryDir, 'player.js'), entries.player || '');
264
+ writeFileSync(join(entryDir, 'client-player.js'), entries['client-player'] || '');
265
+ writeFileSync(join(entryDir, 'editor.js'), entries.editor || '');
266
+
267
+ // Create webpack config
268
+ const bundleOutputPath = join(outputDir, hash);
269
+ mkdirSync(bundleOutputPath, { recursive: true });
270
+
271
+ const webpackConfig = createWebpackConfig({
272
+ context: entryDir,
273
+ entry: {
274
+ player: join(entryDir, 'player.js'),
275
+ 'client-player': join(entryDir, 'client-player.js'),
276
+ editor: join(entryDir, 'editor.js'),
277
+ },
278
+ outputPath: bundleOutputPath,
279
+ workspaceDir,
280
+ elements: dependencies.map((d) => d.name.split('/')[1]),
281
+ });
282
+
283
+ // Add repo's node_modules to webpack resolve paths for workspace testing
284
+ if (!webpackConfig.resolve) webpackConfig.resolve = {};
285
+ if (!webpackConfig.resolve.modules) webpackConfig.resolve.modules = [];
286
+ webpackConfig.resolve.modules.push(join(process.cwd(), 'node_modules'));
287
+
288
+ // Run webpack
289
+ return new Promise((resolve, reject) => {
290
+ webpack(webpackConfig, (err: any, stats: any) => {
291
+ if (err) {
292
+ reject(err);
293
+ return;
294
+ }
295
+
296
+ if (stats?.hasErrors()) {
297
+ const errors = stats.toString({ colors: false, errorDetails: true });
298
+ reject(new Error(errors));
299
+ return;
300
+ }
301
+
302
+ resolve({
303
+ success: true,
304
+ hash,
305
+ bundles: {
306
+ player: `/bundles/${hash}/player.js`,
307
+ clientPlayer: `/bundles/${hash}/client-player.js`,
308
+ editor: `/bundles/${hash}/editor.js`,
309
+ },
310
+ duration: Date.now() - startTime,
311
+ cached: false,
312
+ });
313
+ });
314
+ });
315
+ }
316
+
317
+ private async verifyBundles(outputDir: string, hash: string): Promise<void> {
318
+ const bundleDir = join(outputDir, hash);
319
+ const files = ['player.js', 'client-player.js', 'editor.js'];
320
+
321
+ let allOk = true;
322
+
323
+ for (const file of files) {
324
+ const filePath = join(bundleDir, file);
325
+ if (!existsSync(filePath)) {
326
+ this.logger.error(`โŒ ${file} not found`);
327
+ allOk = false;
328
+ continue;
329
+ }
330
+
331
+ const content = readFileSync(filePath, 'utf-8');
332
+ const size = (content.length / 1024).toFixed(1);
333
+ this.logger.success(`โœ… ${file} (${size} KB)`);
334
+
335
+ // Verify IIFE format for player bundle
336
+ if (file === 'player.js' && !content.includes('window.pie')) {
337
+ this.logger.warn(`โš ๏ธ ${file} doesn't contain window.pie`);
338
+ }
339
+ }
340
+
341
+ if (!allOk) {
342
+ this.error('Bundle verification failed', { exit: 1 });
343
+ }
344
+ }
345
+ }
@@ -0,0 +1,269 @@
1
+ import { Command, Flags } from '@oclif/core';
2
+ import { Logger } from '../../utils/logger.js';
3
+ import { MigrationOrchestrator } from '../../lib/mathquill/migration-orchestrator.js';
4
+ import { existsSync } from 'node:fs';
5
+ import { join } from 'node:path';
6
+
7
+ export default class MathQuillMigrate extends Command {
8
+ static override description = `Migrate MathQuill from PIE fork to Desmos fork with patches
9
+
10
+ Automates the migration from PIE's 2015 MathQuill fork to the modern Desmos fork
11
+ with Khan Academy patches, Learnosity features, and PIE extensions.
12
+
13
+ This command:
14
+ 1. Clones Desmos, Khan, and Learnosity forks to temp directory
15
+ 2. Extracts features from each fork:
16
+ - Khan: Mobile keyboard fixes, i18n ARIA strings
17
+ - Learnosity: Recurring decimals, nless/ngtr symbols, empty() method
18
+ - PIE: Matrices (pmatrix, bmatrix, vmatrix), lrnexponent
19
+ 3. Generates TypeScript extension modules with source attribution
20
+ 4. Creates unified extension loader
21
+ 5. Updates package.json to use Desmos fork
22
+ 6. Runs automated tests
23
+
24
+ The migration is repeatable - you can re-run it when forks update.
25
+
26
+ After migration, review changes and test thoroughly before committing.
27
+ `;
28
+
29
+ static override examples = [
30
+ {
31
+ description: 'Show what would be migrated (dry run)',
32
+ command: '<%= config.bin %> <%= command.id %> --dry-run',
33
+ },
34
+ {
35
+ description: 'Run full migration',
36
+ command: '<%= config.bin %> <%= command.id %>',
37
+ },
38
+ {
39
+ description: 'Verbose output with detailed progress',
40
+ command: '<%= config.bin %> <%= command.id %> --verbose',
41
+ },
42
+ {
43
+ description: 'Force migration (skip confirmations)',
44
+ command: '<%= config.bin %> <%= command.id %> --force',
45
+ },
46
+ {
47
+ description: 'Skip tests',
48
+ command: '<%= config.bin %> <%= command.id %> --skip-tests',
49
+ },
50
+ ];
51
+
52
+ static override flags = {
53
+ 'dry-run': Flags.boolean({
54
+ description: 'Show what would be done without making changes',
55
+ default: false,
56
+ }),
57
+ force: Flags.boolean({
58
+ description: 'Skip confirmation prompts and continue on non-fatal errors',
59
+ default: false,
60
+ }),
61
+ verbose: Flags.boolean({
62
+ char: 'v',
63
+ description: 'Show detailed progress output',
64
+ default: false,
65
+ }),
66
+ 'skip-tests': Flags.boolean({
67
+ description: 'Skip running tests after migration',
68
+ default: false,
69
+ }),
70
+ 'temp-dir': Flags.string({
71
+ description: 'Temporary directory for cloning forks',
72
+ default: '/tmp/mathquill-migration',
73
+ }),
74
+ clean: Flags.boolean({
75
+ description: 'Clean temporary directory before starting',
76
+ default: false,
77
+ }),
78
+ };
79
+
80
+ async run(): Promise<void> {
81
+ const { flags } = await this.parse(MathQuillMigrate);
82
+ const logger = new Logger(flags.verbose);
83
+
84
+ // Validate workspace
85
+ const workspaceRoot = process.cwd();
86
+ const mathquillPath = join(workspaceRoot, 'packages/shared/mathquill');
87
+
88
+ if (!existsSync(mathquillPath)) {
89
+ logger.error('MathQuill package not found at: packages/shared/mathquill');
90
+ logger.info('This command must be run from the workspace root');
91
+ this.exit(1);
92
+ }
93
+
94
+ // Show banner
95
+ logger.section('๐Ÿš€ MathQuill Migration');
96
+ logger.info(' Migrating from PIE fork to Desmos fork + patches\n');
97
+
98
+ if (flags['dry-run']) {
99
+ logger.warn('๐Ÿƒ DRY RUN MODE - No changes will be made\n');
100
+ }
101
+
102
+ // Create orchestrator
103
+ const orchestrator = new MigrationOrchestrator({
104
+ workspaceRoot,
105
+ mathquillPath,
106
+ tempDir: flags['temp-dir'],
107
+ dryRun: flags['dry-run'],
108
+ force: flags.force,
109
+ skipTests: flags['skip-tests'],
110
+ clean: flags.clean,
111
+ logger,
112
+ });
113
+
114
+ // Show configuration
115
+ if (flags.verbose) {
116
+ logger.section('Configuration');
117
+ logger.info(` Workspace: ${workspaceRoot}`);
118
+ logger.info(` MathQuill: ${mathquillPath}`);
119
+ logger.info(` Temp Dir: ${flags['temp-dir']}`);
120
+ logger.info(` Dry Run: ${flags['dry-run']}`);
121
+ logger.info(` Force: ${flags.force}`);
122
+ logger.info(` Skip Tests: ${flags['skip-tests']}\n`);
123
+ }
124
+
125
+ // Confirm if not in dry-run or force mode
126
+ if (!flags['dry-run'] && !flags.force) {
127
+ logger.warn('โš ๏ธ This will modify packages/shared/mathquill');
128
+ logger.info(' A git commit is recommended before proceeding.\n');
129
+
130
+ // In a real implementation, you'd use inquirer or similar
131
+ // For now, we'll just proceed
132
+ logger.info(' Proceeding with migration...\n');
133
+ }
134
+
135
+ try {
136
+ // Run migration
137
+ const result = await orchestrator.migrate();
138
+
139
+ if (flags['dry-run']) {
140
+ this.showDryRunSummary(result, logger);
141
+ } else {
142
+ this.showSuccessSummary(result, logger);
143
+ }
144
+
145
+ if (!result.success) {
146
+ this.exit(1);
147
+ }
148
+ } catch (error) {
149
+ logger.error('\nโŒ Migration failed');
150
+
151
+ if (error instanceof Error) {
152
+ logger.error(` ${error.message}`);
153
+ if (flags.verbose && error.stack) {
154
+ logger.debug(`\n${error.stack}`);
155
+ }
156
+ }
157
+
158
+ logger.info('\n๐Ÿ’ก Troubleshooting:');
159
+ logger.info(' 1. Check internet connectivity for git clones');
160
+ logger.info(' 2. Ensure temp directory is writable');
161
+ logger.info(' 3. Run with --verbose for detailed output');
162
+ logger.info(' 4. Try --clean to remove cached forks');
163
+ logger.info(' 5. Check docs/MATHQUILL_MIGRATION_CLI_PLAN.md');
164
+
165
+ this.exit(1);
166
+ }
167
+ }
168
+
169
+ private showDryRunSummary(result: any, logger: Logger): void {
170
+ logger.section('๐Ÿ“‹ Dry Run Summary');
171
+ logger.info('Would perform the following actions:\n');
172
+
173
+ logger.info('๐Ÿ“‚ Prepare environment:');
174
+ logger.info(` โ€ข Create temp directory: ${result.tempDir}`);
175
+ logger.info(' โ€ข Create extension directories\n');
176
+
177
+ logger.info('๐Ÿ”„ Clone forks:');
178
+ logger.info(' โ€ข Desmos fork (desmosinc/mathquill)');
179
+ logger.info(' โ€ข Khan fork (Khan/mathquill v1.0.3)');
180
+ logger.info(' โ€ข Learnosity fork (Learnosity/mathquill)\n');
181
+
182
+ logger.info('๐Ÿ“ฆ Extract features:');
183
+ logger.info(' Khan Academy:');
184
+ logger.info(' โ€ข mobile-keyboard.ts (Android keyboard fixes)');
185
+ logger.info(' โ€ข i18n-aria.ts (Internationalized ARIA strings)');
186
+ logger.info(' Learnosity:');
187
+ logger.info(' โ€ข recurring-decimal.ts (Recurring decimal dot)');
188
+ logger.info(' โ€ข not-symbols.ts (nless, ngtr symbols)');
189
+ logger.info(' โ€ข empty-method.ts (Check if field is empty)');
190
+ logger.info(' PIE:');
191
+ logger.info(' โ€ข matrices.ts (pmatrix, bmatrix, vmatrix)');
192
+ logger.info(' โ€ข lrn-exponent.ts (LRN exponent notation)\n');
193
+
194
+ logger.info('๐Ÿ”จ Generate:');
195
+ logger.info(' โ€ข Extension loader (src/extensions/index.ts)\n');
196
+
197
+ logger.info('๐Ÿ“ Update:');
198
+ logger.info(' โ€ข Install: mathquill@github:desmosinc/mathquill');
199
+ logger.info(' โ€ข Update: package.json with migration metadata\n');
200
+
201
+ logger.info('๐Ÿงช Test:');
202
+ logger.info(' โ€ข Run unit tests');
203
+ logger.info(' โ€ข Run integration tests\n');
204
+
205
+ logger.success('Dry run complete!');
206
+ logger.info('\nRun without --dry-run to execute migration.');
207
+ }
208
+
209
+ private showSuccessSummary(result: any, logger: Logger): void {
210
+ logger.section('โœ… Migration Complete!');
211
+
212
+ logger.info('\n๐Ÿ“Š Summary:');
213
+ logger.info(` Base: Desmos fork (${result.desmos?.commit || 'latest'})`);
214
+ logger.info(` + Khan: ${result.khan?.featuresCount || 0} patches applied`);
215
+ logger.info(` + Learnosity: ${result.learnosity?.featuresCount || 0} features added`);
216
+ logger.info(` + PIE: ${result.pie?.featuresCount || 0} extensions ported`);
217
+
218
+ if (result.files && result.files.length > 0) {
219
+ logger.info('\n๐Ÿ“‚ Generated Files:');
220
+ logger.info(' packages/shared/mathquill/src/extensions/');
221
+ logger.info(' โ”œโ”€โ”€ khan/');
222
+ logger.info(' โ”‚ โ”œโ”€โ”€ mobile-keyboard.ts');
223
+ logger.info(' โ”‚ โ””โ”€โ”€ i18n-aria.ts');
224
+ logger.info(' โ”œโ”€โ”€ learnosity/');
225
+ logger.info(' โ”‚ โ”œโ”€โ”€ recurring-decimal.ts');
226
+ logger.info(' โ”‚ โ”œโ”€โ”€ not-symbols.ts');
227
+ logger.info(' โ”‚ โ””โ”€โ”€ empty-method.ts');
228
+ logger.info(' โ”œโ”€โ”€ pie/');
229
+ logger.info(' โ”‚ โ”œโ”€โ”€ matrices.ts');
230
+ logger.info(' โ”‚ โ””โ”€โ”€ lrn-exponent.ts');
231
+ logger.info(' โ””โ”€โ”€ index.ts (loader)');
232
+ }
233
+
234
+ if (result.tests) {
235
+ logger.info('\n๐Ÿงช Tests:');
236
+ if (result.tests.unit?.passed) {
237
+ logger.success(` Unit tests: ${result.tests.unit.passed} passed`);
238
+ }
239
+ if (result.tests.integration?.passed) {
240
+ logger.success(` Integration tests: ${result.tests.integration.passed} passed`);
241
+ }
242
+ if (result.tests.warnings && result.tests.warnings.length > 0) {
243
+ logger.warn(` Warnings: ${result.tests.warnings.length}`);
244
+ }
245
+ }
246
+
247
+ logger.info('\n๐Ÿ“ Next Steps:');
248
+ logger.info(' 1. Review changes:');
249
+ logger.info(' git diff packages/shared/mathquill');
250
+ logger.info(' 2. Build package:');
251
+ logger.info(' cd packages/shared/mathquill && bun run build');
252
+ logger.info(' 3. Test in demo:');
253
+ logger.info(' bun run dev:demo');
254
+ logger.info(' 4. Verify migration:');
255
+ logger.info(' pie mathquill:status');
256
+ logger.info(' 5. Run full test suite:');
257
+ logger.info(' bun test packages/lib-react/math-input');
258
+ logger.info(' bun test packages/elements-react/math-inline');
259
+ logger.info(' 6. Commit changes:');
260
+ logger.info(' git add packages/shared/mathquill');
261
+ logger.info(' git commit -m "Migrate MathQuill to Desmos fork + patches"');
262
+
263
+ logger.info('\n๐Ÿ’ก Tips:');
264
+ logger.info(' โ€ข Review docs/MATHQUILL_MIGRATION_PLAN.md for details');
265
+ logger.info(' โ€ข Check docs/MATHQUILL_FORK_ANALYSIS.md for fork comparison');
266
+ logger.info(' โ€ข Run "pie mathquill:status" to verify migration state');
267
+ logger.info(' โ€ข Keep /tmp/mathquill-migration for re-running migration\n');
268
+ }
269
+ }