@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,145 @@
1
+ import { Command, Args } from '@oclif/core';
2
+ import { existsSync } from 'node:fs';
3
+ import { readFile, writeFile } from 'node:fs/promises';
4
+ import {
5
+ getCurrentCommit,
6
+ getCommitsSince,
7
+ getCommitDetails,
8
+ getAuthorName,
9
+ } from '../../utils/git.js';
10
+ import { Logger } from '../../utils/logger.js';
11
+ import { DEFAULT_PATHS, HISTORY_FILE } from '../../lib/upstream/sync-constants.js';
12
+
13
+ interface UpstreamState {
14
+ lastCheck: string;
15
+ upstreamCommits: {
16
+ pieElements: string;
17
+ pieLib: string;
18
+ };
19
+ checkedBy: string;
20
+ }
21
+
22
+ export default class Track extends Command {
23
+ static override description = 'Track upstream repository changes';
24
+
25
+ static override examples = [
26
+ '<%= config.bin %> <%= command.id %> show',
27
+ '<%= config.bin %> <%= command.id %> record',
28
+ '<%= config.bin %> <%= command.id %> compare <commit-sha>',
29
+ ];
30
+
31
+ static override args = {
32
+ action: Args.string({
33
+ description: 'Action to perform',
34
+ required: true,
35
+ options: ['show', 'record', 'compare'],
36
+ }),
37
+ commit: Args.string({
38
+ description: 'Commit SHA for compare action',
39
+ required: false,
40
+ }),
41
+ };
42
+
43
+ private logger = new Logger();
44
+
45
+ public async run(): Promise<void> {
46
+ const { args } = await this.parse(Track);
47
+
48
+ switch (args.action) {
49
+ case 'show':
50
+ await this.showCommits();
51
+ break;
52
+ case 'record':
53
+ await this.recordState();
54
+ break;
55
+ case 'compare':
56
+ if (!args.commit) {
57
+ this.error('Commit SHA required for compare action');
58
+ }
59
+ await this.compareCommit(args.commit);
60
+ break;
61
+ default:
62
+ this.error(`Unknown action: ${args.action}`);
63
+ }
64
+ }
65
+
66
+ private async showCommits(): Promise<void> {
67
+ this.logger.section('📊 Upstream Repository Status');
68
+
69
+ const pieElements = DEFAULT_PATHS.PIE_ELEMENTS;
70
+ const pieLib = DEFAULT_PATHS.PIE_LIB;
71
+
72
+ if (!existsSync(HISTORY_FILE)) {
73
+ this.logger.warn('No history file found. Run "record" first to establish baseline.');
74
+ return;
75
+ }
76
+
77
+ const history: UpstreamState = JSON.parse(await readFile(HISTORY_FILE, 'utf-8'));
78
+
79
+ this.logger.info(`\nLast checked: ${history.lastCheck}`);
80
+ this.logger.info(`Checked by: ${history.checkedBy}\n`);
81
+
82
+ // Show new commits for pie-elements
83
+ this.logger.section('📦 pie-elements');
84
+ const elementsCommits = getCommitsSince(history.upstreamCommits.pieElements, pieElements);
85
+ if (elementsCommits.length === 0) {
86
+ this.logger.info(' No new commits');
87
+ } else {
88
+ this.logger.info(` ${elementsCommits.length} new commits:`);
89
+ for (const commit of elementsCommits.slice(0, 10)) {
90
+ this.logger.info(` ${commit}`);
91
+ }
92
+ if (elementsCommits.length > 10) {
93
+ this.logger.info(` ... and ${elementsCommits.length - 10} more`);
94
+ }
95
+ }
96
+
97
+ // Show new commits for pie-lib
98
+ this.logger.section('\n📚 pie-lib');
99
+ const libCommits = getCommitsSince(history.upstreamCommits.pieLib, pieLib);
100
+ if (libCommits.length === 0) {
101
+ this.logger.info(' No new commits');
102
+ } else {
103
+ this.logger.info(` ${libCommits.length} new commits:`);
104
+ for (const commit of libCommits.slice(0, 10)) {
105
+ this.logger.info(` ${commit}`);
106
+ }
107
+ if (libCommits.length > 10) {
108
+ this.logger.info(` ... and ${libCommits.length - 10} more`);
109
+ }
110
+ }
111
+ }
112
+
113
+ private async recordState(): Promise<void> {
114
+ const pieElements = DEFAULT_PATHS.PIE_ELEMENTS;
115
+ const pieLib = DEFAULT_PATHS.PIE_LIB;
116
+
117
+ const state: UpstreamState = {
118
+ lastCheck: new Date().toISOString(),
119
+ upstreamCommits: {
120
+ pieElements: getCurrentCommit(pieElements),
121
+ pieLib: getCurrentCommit(pieLib),
122
+ },
123
+ checkedBy: getAuthorName(pieElements),
124
+ };
125
+
126
+ await writeFile(HISTORY_FILE, `${JSON.stringify(state, null, 2)}\n`, 'utf-8');
127
+
128
+ this.logger.success('✅ Upstream state recorded');
129
+ this.logger.info(` pie-elements: ${state.upstreamCommits.pieElements.substring(0, 8)}`);
130
+ this.logger.info(` pie-lib: ${state.upstreamCommits.pieLib.substring(0, 8)}`);
131
+ }
132
+
133
+ private async compareCommit(commit: string): Promise<void> {
134
+ const pieElements = DEFAULT_PATHS.PIE_ELEMENTS;
135
+
136
+ this.logger.section(`📋 Commit Details: ${commit.substring(0, 8)}`);
137
+
138
+ try {
139
+ const details = getCommitDetails(commit, pieElements);
140
+ this.log(details);
141
+ } catch (error) {
142
+ this.error(`Failed to get commit details: ${error}`);
143
+ }
144
+ }
145
+ }
@@ -0,0 +1,110 @@
1
+ import { Command, Flags } from '@oclif/core';
2
+ import { resolve } from 'node:path';
3
+ import AnalyzeEsm from './analyze-esm.js';
4
+ import Sync from './sync.js';
5
+ import { assertReposExist } from '../../lib/upstream/repo-utils.js';
6
+
7
+ export default class Update extends Command {
8
+ static override description =
9
+ 'Update from upstream: analyze ESM compatibility and sync compatible packages';
10
+
11
+ static override examples = [
12
+ '<%= config.bin %> <%= command.id %>',
13
+ '<%= config.bin %> <%= command.id %> --dry-run',
14
+ '<%= config.bin %> <%= command.id %> --element=multiple-choice',
15
+ '<%= config.bin %> <%= command.id %> --verbose',
16
+ ];
17
+
18
+ static override flags = {
19
+ 'dry-run': Flags.boolean({
20
+ description: 'Show what would be synced without making changes',
21
+ default: false,
22
+ }),
23
+ verbose: Flags.boolean({
24
+ char: 'v',
25
+ description: 'Show detailed output',
26
+ default: false,
27
+ }),
28
+ element: Flags.string({
29
+ description: 'Update only specified element',
30
+ }),
31
+ 'skip-build': Flags.boolean({
32
+ description: 'Skip building after sync',
33
+ default: false,
34
+ }),
35
+ 'include-dev-deps': Flags.boolean({
36
+ description: 'Include dev dependencies in ESM analysis',
37
+ default: false,
38
+ }),
39
+ validate: Flags.boolean({
40
+ description: 'Run runtime validation during ESM analysis',
41
+ default: false,
42
+ }),
43
+ };
44
+
45
+ async run(): Promise<void> {
46
+ const { flags } = await this.parse(Update);
47
+
48
+ // Verify pie-elements and pie-lib repos exist
49
+ const pieElementsPath = resolve(process.cwd(), '../pie-elements');
50
+ const pieLibPath = resolve(process.cwd(), '../pie-lib');
51
+
52
+ try {
53
+ assertReposExist([
54
+ {
55
+ label: 'pie-elements repository',
56
+ path: pieElementsPath,
57
+ extraMessage: 'Expected upstream repository as a sibling directory.',
58
+ },
59
+ {
60
+ label: 'pie-lib repository',
61
+ path: pieLibPath,
62
+ extraMessage: 'Expected upstream repository as a sibling directory.',
63
+ },
64
+ ]);
65
+ } catch (error) {
66
+ this.error(error instanceof Error ? error.message : String(error));
67
+ }
68
+
69
+ if (flags.verbose) {
70
+ this.log('');
71
+ this.log('╔═══════════════════════════════════════════════════════╗');
72
+ this.log('║ Updating from Upstream Repositories ║');
73
+ this.log('╚═══════════════════════════════════════════════════════╝');
74
+ this.log('');
75
+ this.log('📊 Step 1/2: Analyzing ESM compatibility...');
76
+ this.log('');
77
+ }
78
+
79
+ const analyzeFlags = [
80
+ '--output=.compatibility/report.json',
81
+ flags.verbose ? '--verbose' : '',
82
+ flags['include-dev-deps'] ? '--include-dev-deps' : '',
83
+ flags.validate ? '--validate' : '',
84
+ ].filter(Boolean);
85
+
86
+ await AnalyzeEsm.run(analyzeFlags);
87
+
88
+ if (flags.verbose) {
89
+ this.log('');
90
+ this.log('─'.repeat(60));
91
+ this.log('');
92
+ this.log('🔄 Step 2/2: Syncing compatible packages...');
93
+ this.log('');
94
+ }
95
+
96
+ const syncFlags = [
97
+ flags['dry-run'] ? '--dry-run' : '',
98
+ flags.verbose ? '--verbose' : '',
99
+ flags.element ? `--element=${flags.element}` : '',
100
+ flags['skip-build'] ? '--skip-build' : '',
101
+ ].filter(Boolean);
102
+
103
+ await Sync.run(syncFlags);
104
+
105
+ if (!flags.verbose) {
106
+ this.log('');
107
+ this.log('✅ Update complete!');
108
+ }
109
+ }
110
+ }
@@ -0,0 +1,166 @@
1
+ import { Command, Flags } from '@oclif/core';
2
+ import { Logger } from '../../utils/logger.js';
3
+ import { existsSync } from 'node:fs';
4
+ import { readdir, readFile } from 'node:fs/promises';
5
+ import { join } from 'node:path';
6
+ import { EXCLUDED_UPSTREAM_ELEMENTS } from '../../lib/upstream/sync-constants.js';
7
+
8
+ type CheckResult = {
9
+ element: string;
10
+ ok: boolean;
11
+ errors: string[];
12
+ warnings: string[];
13
+ };
14
+
15
+ const ELEMENTS_REACT_DIR = 'packages/elements-react';
16
+
17
+ function hasAnyControllerSource(elementDir: string): boolean {
18
+ const base = join(elementDir, 'src', 'controller', 'index');
19
+ return (
20
+ existsSync(base + '.ts') ||
21
+ existsSync(base + '.tsx') ||
22
+ existsSync(base + '.js') ||
23
+ existsSync(base + '.jsx')
24
+ );
25
+ }
26
+
27
+ export default class VerifyControllers extends Command {
28
+ static override description =
29
+ 'Verify that element packages will publish controller modules correctly (exports + dist artifacts)';
30
+
31
+ static override examples = [
32
+ 'bun run cli <%= command.id %>',
33
+ 'bun cli <%= command.id %>',
34
+ '<%= config.bin %> <%= command.id %>',
35
+ ];
36
+
37
+ static override flags = {
38
+ verbose: Flags.boolean({
39
+ char: 'v',
40
+ description: 'Show detailed output',
41
+ default: false,
42
+ }),
43
+ element: Flags.string({
44
+ description: 'Check only specified element',
45
+ }),
46
+ };
47
+
48
+ private logger = new Logger();
49
+
50
+ public async run(): Promise<void> {
51
+ const { flags } = await this.parse(VerifyControllers);
52
+ this.logger = new Logger(flags.verbose);
53
+
54
+ this.logger.section('🧩 Verifying published controller modules');
55
+
56
+ const items = await readdir(ELEMENTS_REACT_DIR, { withFileTypes: true });
57
+ const excludedElements = new Set<string>(EXCLUDED_UPSTREAM_ELEMENTS as readonly string[]);
58
+ let elements = items
59
+ .filter((d) => d.isDirectory())
60
+ .map((d) => d.name)
61
+ .filter((name) => !excludedElements.has(name));
62
+
63
+ if (flags.element) {
64
+ if (!elements.includes(flags.element)) {
65
+ this.error(`Element '${flags.element}' not found in ${ELEMENTS_REACT_DIR}`);
66
+ }
67
+ elements = [flags.element];
68
+ }
69
+
70
+ const results: CheckResult[] = [];
71
+ for (const element of elements.sort()) {
72
+ const elementDir = join(ELEMENTS_REACT_DIR, element);
73
+ const pkgPath = join(elementDir, 'package.json');
74
+ if (!existsSync(pkgPath)) {
75
+ results.push({
76
+ element,
77
+ ok: false,
78
+ errors: ['Missing package.json'],
79
+ warnings: [],
80
+ });
81
+ continue;
82
+ }
83
+
84
+ const hasController = hasAnyControllerSource(elementDir);
85
+ if (!hasController) {
86
+ // Nothing to publish for controllers; skip.
87
+ results.push({ element, ok: true, errors: [], warnings: [] });
88
+ continue;
89
+ }
90
+
91
+ const pkgRaw = await readFile(pkgPath, 'utf-8');
92
+ const pkg = JSON.parse(pkgRaw) as any;
93
+ const exportsObj = pkg?.exports as any;
94
+ const controllerExport = exportsObj?.['./controller'];
95
+
96
+ const errors: string[] = [];
97
+ const warnings: string[] = [];
98
+ if (!controllerExport) {
99
+ errors.push('Missing exports["./controller"] in package.json');
100
+ } else {
101
+ const jsPath = controllerExport?.default as string | undefined;
102
+ const dtsPath = controllerExport?.types as string | undefined;
103
+
104
+ if (!jsPath || typeof jsPath !== 'string') {
105
+ errors.push('exports["./controller"].default is missing');
106
+ } else {
107
+ const abs = join(elementDir, jsPath.replace(/^\.\//, ''));
108
+ if (!existsSync(abs)) {
109
+ errors.push(`Controller JS artifact missing: ${jsPath}`);
110
+ }
111
+ }
112
+
113
+ // Types are nice-to-have for TS consumers, but not required for runtime/controller loading.
114
+ // We'll warn (not fail) if types are missing.
115
+ if (!dtsPath || typeof dtsPath !== 'string') {
116
+ warnings.push('exports["./controller"].types is missing');
117
+ } else {
118
+ const abs = join(elementDir, dtsPath.replace(/^\.\//, ''));
119
+ if (!existsSync(abs)) {
120
+ warnings.push(`Controller d.ts artifact missing: ${dtsPath}`);
121
+ }
122
+ }
123
+ }
124
+
125
+ results.push({ element, ok: errors.length === 0, errors, warnings });
126
+ }
127
+
128
+ const failed = results.filter((r) => !r.ok);
129
+ const warned = results.filter((r) => r.warnings.length > 0);
130
+ const checked = results.filter((r) =>
131
+ hasAnyControllerSource(join(ELEMENTS_REACT_DIR, r.element))
132
+ );
133
+
134
+ this.log(`\n${'='.repeat(60)}`);
135
+ this.log('📊 CONTROLLER PUBLISH VERIFICATION REPORT');
136
+ this.log('='.repeat(60));
137
+ this.log(`Elements scanned: ${results.length}`);
138
+ this.log(`Elements with controllers: ${checked.length}`);
139
+ this.log(`Passed: ${results.length - failed.length} ✅`);
140
+ this.log(`Failed: ${failed.length} ❌`);
141
+ this.log(`Warnings: ${warned.length} ⚠️`);
142
+
143
+ if (failed.length > 0) {
144
+ this.log('\n❌ FAILURES:');
145
+ for (const f of failed) {
146
+ this.log(` - ${f.element}`);
147
+ for (const e of f.errors) this.log(` • ${e}`);
148
+ }
149
+ this.log('\n💡 Fixes:');
150
+ this.log(' - Ensure package.json exports include "./controller"');
151
+ this.log(' - Ensure build outputs exist under dist/controller/');
152
+ this.log(` - Rebuild: cd ${ELEMENTS_REACT_DIR}/<element> && bun run build`);
153
+ this.error('Some controller publish checks failed', { exit: 1 });
154
+ }
155
+
156
+ if (warned.length > 0) {
157
+ this.log('\n⚠️ WARNINGS (non-blocking):');
158
+ for (const w of warned) {
159
+ this.log(` - ${w.element}`);
160
+ for (const msg of w.warnings) this.log(` • ${msg}`);
161
+ }
162
+ }
163
+
164
+ this.log(`\n${'='.repeat(60)}\n`);
165
+ }
166
+ }
@@ -0,0 +1,171 @@
1
+ import { Command, Flags } from '@oclif/core';
2
+ import { Logger } from '../../utils/logger.js';
3
+ import { execSync } from 'node:child_process';
4
+ import { existsSync } from 'node:fs';
5
+ import { readdir } from 'node:fs/promises';
6
+ import { join } from 'node:path';
7
+
8
+ interface BuildResult {
9
+ element: string;
10
+ success: boolean;
11
+ error?: string;
12
+ duration?: number;
13
+ }
14
+
15
+ const ELEMENTS_REACT_DIR = 'packages/elements-react';
16
+
17
+ export default class ReactBuild extends Command {
18
+ static override description = 'Verify React element builds';
19
+
20
+ static override examples = ['<%= config.bin %> <%= command.id %>'];
21
+
22
+ static override flags = {
23
+ verbose: Flags.boolean({
24
+ char: 'v',
25
+ description: 'Show detailed build output',
26
+ default: false,
27
+ }),
28
+ element: Flags.string({
29
+ description: 'Build only specified element',
30
+ }),
31
+ };
32
+
33
+ private logger = new Logger();
34
+
35
+ public async run(): Promise<void> {
36
+ const { flags } = await this.parse(ReactBuild);
37
+
38
+ this.logger = new Logger(flags.verbose);
39
+
40
+ this.logger.section('🔨 Verifying React element builds');
41
+
42
+ // Get all element directories
43
+ const items = await readdir(ELEMENTS_REACT_DIR, { withFileTypes: true });
44
+ let elements = items.filter((item) => item.isDirectory()).map((item) => item.name);
45
+
46
+ // Filter to specific element if requested
47
+ if (flags.element) {
48
+ if (!elements.includes(flags.element)) {
49
+ this.error(`Element '${flags.element}' not found in ${ELEMENTS_REACT_DIR}`);
50
+ }
51
+ elements = [flags.element];
52
+ }
53
+
54
+ if (elements.length === 0) {
55
+ this.logger.warn(`⚠️ No React elements found in ${ELEMENTS_REACT_DIR}/`);
56
+ return;
57
+ }
58
+
59
+ this.logger.info(`Found ${elements.length} element(s) to verify\n`);
60
+
61
+ const results: BuildResult[] = [];
62
+
63
+ // Build each element sequentially
64
+ for (const element of elements) {
65
+ process.stdout.write(` Building ${element}... `);
66
+
67
+ const result = await this.buildElement(element, flags.verbose);
68
+ results.push(result);
69
+
70
+ if (result.success) {
71
+ console.log(`✅ (${result.duration}ms)`);
72
+ } else {
73
+ console.log('❌');
74
+ }
75
+ }
76
+
77
+ // Generate report
78
+ this.log(`\n${'='.repeat(60)}`);
79
+ this.log('📊 BUILD VERIFICATION REPORT');
80
+ this.log('='.repeat(60));
81
+
82
+ const successful = results.filter((r) => r.success);
83
+ const failed = results.filter((r) => !r.success);
84
+
85
+ this.log(`Total: ${results.length}`);
86
+ this.log(`Passed: ${successful.length} ✅`);
87
+ this.log(`Failed: ${failed.length} ❌`);
88
+
89
+ if (successful.length > 0) {
90
+ this.log('\n✅ SUCCESSFUL BUILDS:');
91
+ successful.forEach((r) => {
92
+ this.log(` - ${r.element} (${r.duration}ms)`);
93
+ });
94
+ }
95
+
96
+ if (failed.length > 0) {
97
+ this.log('\n❌ FAILED BUILDS:');
98
+ failed.forEach((r) => {
99
+ this.log(`\n Element: ${r.element}`);
100
+ if (r.error) {
101
+ // Extract first few lines of error for summary
102
+ const errorLines = r.error.split('\n').slice(0, 10);
103
+ errorLines.forEach((line) => {
104
+ if (line.trim()) {
105
+ this.log(` ${line}`);
106
+ }
107
+ });
108
+ if (r.error.split('\n').length > 10) {
109
+ this.log(' ... (truncated)');
110
+ }
111
+ }
112
+ });
113
+
114
+ this.log('\n💡 To see full error for a specific element:');
115
+ this.log(' cd packages/elements-react/<element-name>');
116
+ this.log(' bun run build');
117
+ }
118
+
119
+ this.log(`\n${'='.repeat(60)}\n`);
120
+
121
+ // Exit with error code if any builds failed
122
+ if (failed.length > 0) {
123
+ this.error('Some builds failed', { exit: 1 });
124
+ }
125
+ }
126
+
127
+ private async buildElement(element: string, verbose: boolean): Promise<BuildResult> {
128
+ const elementDir = join(ELEMENTS_REACT_DIR, element);
129
+ const packageJsonPath = join(elementDir, 'package.json');
130
+
131
+ // Skip if no package.json
132
+ if (!existsSync(packageJsonPath)) {
133
+ return {
134
+ element,
135
+ success: false,
136
+ error: 'No package.json found',
137
+ };
138
+ }
139
+
140
+ const startTime = Date.now();
141
+
142
+ try {
143
+ execSync('bun run build', {
144
+ cwd: elementDir,
145
+ stdio: verbose ? 'inherit' : 'pipe',
146
+ encoding: 'utf-8',
147
+ });
148
+
149
+ const duration = Date.now() - startTime;
150
+
151
+ return {
152
+ element,
153
+ success: true,
154
+ duration,
155
+ };
156
+ } catch (error: unknown) {
157
+ const duration = Date.now() - startTime;
158
+
159
+ // Extract error message from stderr
160
+ const err = error as { stderr?: Buffer; message?: string };
161
+ const stderr = err.stderr?.toString() || err.message || 'Unknown error';
162
+
163
+ return {
164
+ element,
165
+ success: false,
166
+ error: stderr,
167
+ duration,
168
+ };
169
+ }
170
+ }
171
+ }
package/src/index.ts ADDED
@@ -0,0 +1 @@
1
+ export { run } from '@oclif/core';
@@ -0,0 +1,111 @@
1
+ import { writeFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import type { Logger } from '../../utils/logger.js';
4
+
5
+ /**
6
+ * Extension Loader Generator
7
+ *
8
+ * Generates the main extension loader that initializes MathQuill
9
+ * with all patches and extensions in the correct order.
10
+ */
11
+
12
+ export function generateExtensionLoader(mathquillPath: string, logger: Logger): void {
13
+ const loaderPath = join(mathquillPath, 'src/extensions/index.ts');
14
+
15
+ const content = `/**
16
+ * MathQuill Extensions Loader
17
+ *
18
+ * Loads all extensions in the correct order:
19
+ * 1. Desmos base (via npm package)
20
+ * 2. Khan Academy patches
21
+ * 3. Learnosity features
22
+ * 4. PIE features
23
+ *
24
+ * Generated by: pie mathquill:migrate
25
+ * Date: ${new Date().toISOString().split('T')[0]}
26
+ *
27
+ * DO NOT EDIT MANUALLY - Regenerate with: pie mathquill:migrate
28
+ */
29
+
30
+ import MathQuill from 'mathquill';
31
+ import type { MathQuillInterface, MathFieldInterface } from 'mathquill';
32
+
33
+ // Import Desmos MathQuill CSS
34
+ import 'mathquill/build/mathquill.css';
35
+
36
+ // Khan Academy patches
37
+ import {
38
+ applyMobileKeyboardFixes,
39
+ applyI18nAriaSupport,
40
+ } from './khan/index.js';
41
+
42
+ // Learnosity features
43
+ import {
44
+ addRecurringDecimal,
45
+ addNotSymbols,
46
+ applyEmptyMethod,
47
+ } from './learnosity/index.js';
48
+
49
+ // PIE features
50
+ import {
51
+ addMatrixCommands,
52
+ addLRNExponent,
53
+ } from './pie/index.js';
54
+
55
+ // Import PIE matrix styles
56
+ import './pie/styles.css';
57
+
58
+ /**
59
+ * Initialize MathQuill with all extensions
60
+ *
61
+ * This creates a MathQuill instance (v3 interface) with:
62
+ * - Desmos base (TypeScript, modern)
63
+ * - Khan Academy mobile and accessibility patches
64
+ * - Learnosity educational features
65
+ * - PIE custom extensions
66
+ *
67
+ * @returns Initialized MathQuill interface
68
+ */
69
+ export function initializeMathQuill(): MathQuillInterface {
70
+ // Get MathQuill v3 interface (no jQuery in public API)
71
+ const MQ = MathQuill.getInterface(3);
72
+
73
+ // Apply Khan Academy patches
74
+ applyMobileKeyboardFixes(MQ);
75
+ applyI18nAriaSupport(MQ);
76
+
77
+ // Add Learnosity features
78
+ addRecurringDecimal(MQ);
79
+ addNotSymbols(MQ);
80
+ applyEmptyMethod(MQ);
81
+
82
+ // Add PIE features
83
+ addMatrixCommands(MQ);
84
+ addLRNExponent(MQ);
85
+
86
+ return MQ;
87
+ }
88
+
89
+ // Create and export initialized instance
90
+ const MQ = initializeMathQuill();
91
+
92
+ // Default export: initialized MathQuill
93
+ export default MQ;
94
+
95
+ // Named exports
96
+ export const getInterface = MathQuill.getInterface;
97
+ export const MathField = MQ.MathField;
98
+ export const StaticMath = MQ.StaticMath;
99
+
100
+ // Re-export types
101
+ export type {
102
+ MathQuillInterface,
103
+ MathFieldInterface,
104
+ StaticMathInterface,
105
+ MathFieldConfig,
106
+ } from 'mathquill';
107
+ `;
108
+
109
+ writeFileSync(loaderPath, content);
110
+ logger.success(` ✓ Generated extension loader`);
111
+ }