@typecad/cuttlefish 0.1.0-alpha.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 (377) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +79 -0
  3. package/dist/api/board-types.d.ts +1 -0
  4. package/dist/api/board-types.js +4 -0
  5. package/dist/api/capabilities.d.ts +12 -0
  6. package/dist/api/capabilities.js +1 -0
  7. package/dist/api/config.d.ts +120 -0
  8. package/dist/api/config.js +7 -0
  9. package/dist/api/index.d.ts +5 -0
  10. package/dist/api/index.js +2 -0
  11. package/dist/api/schema/builder.d.ts +14 -0
  12. package/dist/api/schema/builder.js +15 -0
  13. package/dist/api/schema/index.d.ts +2 -0
  14. package/dist/api/schema/index.js +1 -0
  15. package/dist/api/schema/types.d.ts +257 -0
  16. package/dist/api/schema/types.js +4 -0
  17. package/dist/api/shared/async-runtime-static.d.ts +8 -0
  18. package/dist/api/shared/async-runtime-static.js +201 -0
  19. package/dist/api/shared/async-types.d.ts +42 -0
  20. package/dist/api/shared/async-types.js +8 -0
  21. package/dist/api/shared/board-resolver.d.ts +12 -0
  22. package/dist/api/shared/board-resolver.js +7 -0
  23. package/dist/api/shared/cpp-type-ir.d.ts +229 -0
  24. package/dist/api/shared/cpp-type-ir.js +672 -0
  25. package/dist/api/shared/display-adapter.d.ts +12 -0
  26. package/dist/api/shared/display-adapter.js +145 -0
  27. package/dist/api/shared/display-adapters/eink-mono.d.ts +2 -0
  28. package/dist/api/shared/display-adapters/eink-mono.js +53 -0
  29. package/dist/api/shared/display-adapters/sdl.d.ts +2 -0
  30. package/dist/api/shared/display-adapters/sdl.js +452 -0
  31. package/dist/api/shared/display-adapters/ssd1309.d.ts +2 -0
  32. package/dist/api/shared/display-adapters/ssd1309.js +136 -0
  33. package/dist/api/shared/display-adapters/st7796.d.ts +2 -0
  34. package/dist/api/shared/display-adapters/st7796.js +132 -0
  35. package/dist/api/shared/display-capabilities.d.ts +45 -0
  36. package/dist/api/shared/display-capabilities.js +54 -0
  37. package/dist/api/shared/display-op-ir.d.ts +62 -0
  38. package/dist/api/shared/display-op-ir.js +19 -0
  39. package/dist/api/shared/display-profile.d.ts +265 -0
  40. package/dist/api/shared/display-profile.js +338 -0
  41. package/dist/api/shared/graphics-strategy.d.ts +29 -0
  42. package/dist/api/shared/graphics-strategy.js +11 -0
  43. package/dist/api/shared/hal-op-ir.d.ts +393 -0
  44. package/dist/api/shared/hal-op-ir.js +18 -0
  45. package/dist/api/shared/index.d.ts +26 -0
  46. package/dist/api/shared/index.js +21 -0
  47. package/dist/api/shared/ir-core.d.ts +363 -0
  48. package/dist/api/shared/ir-core.js +8 -0
  49. package/dist/api/shared/ir-declarations.d.ts +201 -0
  50. package/dist/api/shared/ir-declarations.js +26 -0
  51. package/dist/api/shared/ir.d.ts +85 -0
  52. package/dist/api/shared/ir.js +13 -0
  53. package/dist/api/shared/platform-strategy.d.ts +402 -0
  54. package/dist/api/shared/platform-strategy.js +14 -0
  55. package/dist/api/shared/polyfill-helper-registry.d.ts +11 -0
  56. package/dist/api/shared/polyfill-helper-registry.js +86 -0
  57. package/dist/api/shared/polyfill-types.d.ts +33 -0
  58. package/dist/api/shared/polyfill-types.js +76 -0
  59. package/dist/api/shared/promise-runtime.d.ts +10 -0
  60. package/dist/api/shared/promise-runtime.js +244 -0
  61. package/dist/api/shared/snprintf-types.d.ts +29 -0
  62. package/dist/api/shared/snprintf-types.js +8 -0
  63. package/dist/api/shared/string-method-registry.d.ts +53 -0
  64. package/dist/api/shared/string-method-registry.js +168 -0
  65. package/dist/api/shared/toolchain-types.d.ts +63 -0
  66. package/dist/api/shared/toolchain-types.js +71 -0
  67. package/dist/api/shared/types.d.ts +50 -0
  68. package/dist/api/shared/types.js +6 -0
  69. package/dist/ast/parse.d.ts +2 -0
  70. package/dist/ast/parse.js +4 -0
  71. package/dist/cache.d.ts +26 -0
  72. package/dist/cache.js +95 -0
  73. package/dist/cli-utils.d.ts +37 -0
  74. package/dist/cli-utils.js +250 -0
  75. package/dist/cli.d.ts +2 -0
  76. package/dist/cli.js +635 -0
  77. package/dist/config-loader.d.ts +78 -0
  78. package/dist/config-loader.js +508 -0
  79. package/dist/config-schema.d.ts +233 -0
  80. package/dist/config-schema.js +83 -0
  81. package/dist/create/board-checklist.d.ts +2 -0
  82. package/dist/create/board-checklist.js +52 -0
  83. package/dist/create/board-codegen.d.ts +9 -0
  84. package/dist/create/board-codegen.js +238 -0
  85. package/dist/create/board-generators.d.ts +12 -0
  86. package/dist/create/board-generators.js +652 -0
  87. package/dist/create/board-spec.d.ts +640 -0
  88. package/dist/create/board-spec.js +215 -0
  89. package/dist/create/eslint-rules-template.d.ts +2 -0
  90. package/dist/create/eslint-rules-template.js +672 -0
  91. package/dist/create/index.d.ts +11 -0
  92. package/dist/create/index.js +8 -0
  93. package/dist/create/init-scaffold.d.ts +27 -0
  94. package/dist/create/init-scaffold.js +150 -0
  95. package/dist/create/init-templates.d.ts +24 -0
  96. package/dist/create/init-templates.js +379 -0
  97. package/dist/create/init-wizard.d.ts +8 -0
  98. package/dist/create/init-wizard.js +168 -0
  99. package/dist/debug/breakpoint-loader.d.ts +24 -0
  100. package/dist/debug/breakpoint-loader.js +137 -0
  101. package/dist/debug/index.d.ts +3 -0
  102. package/dist/debug/index.js +7 -0
  103. package/dist/debug/preprocessor.d.ts +9 -0
  104. package/dist/debug/preprocessor.js +214 -0
  105. package/dist/debug/types.d.ts +32 -0
  106. package/dist/debug/types.js +6 -0
  107. package/dist/diagnostics/diagnostics-report.d.ts +51 -0
  108. package/dist/diagnostics/diagnostics-report.js +333 -0
  109. package/dist/diagnostics/json-schema.d.ts +138 -0
  110. package/dist/diagnostics/json-schema.js +27 -0
  111. package/dist/diagnostics/md-writer.d.ts +10 -0
  112. package/dist/diagnostics/md-writer.js +336 -0
  113. package/dist/diagnostics/mermaid-builder.d.ts +65 -0
  114. package/dist/diagnostics/mermaid-builder.js +681 -0
  115. package/dist/emit/cpp-emitter.d.ts +6 -0
  116. package/dist/emit/cpp-emitter.js +48 -0
  117. package/dist/emit/emitters/class-emitter.d.ts +2 -0
  118. package/dist/emit/emitters/class-emitter.js +523 -0
  119. package/dist/emit/emitters/emitter-context.d.ts +174 -0
  120. package/dist/emit/emitters/emitter-context.js +1 -0
  121. package/dist/emit/emitters/entrypoint-synthesizer.d.ts +2 -0
  122. package/dist/emit/emitters/entrypoint-synthesizer.js +175 -0
  123. package/dist/emit/emitters/function-emitter-impl.d.ts +19 -0
  124. package/dist/emit/emitters/function-emitter-impl.js +428 -0
  125. package/dist/emit/emitters/line-appender.d.ts +15 -0
  126. package/dist/emit/emitters/line-appender.js +344 -0
  127. package/dist/emit/emitters/namespace-emitter.d.ts +2 -0
  128. package/dist/emit/emitters/namespace-emitter.js +324 -0
  129. package/dist/emit/emitters/output-finalizer.d.ts +4 -0
  130. package/dist/emit/emitters/output-finalizer.js +267 -0
  131. package/dist/emit/emitters/setup.d.ts +3 -0
  132. package/dist/emit/emitters/setup.js +851 -0
  133. package/dist/emit/emitters/top-level-prep.d.ts +2 -0
  134. package/dist/emit/emitters/top-level-prep.js +706 -0
  135. package/dist/emit/emitters/type-decl-emitter.d.ts +2 -0
  136. package/dist/emit/emitters/type-decl-emitter.js +251 -0
  137. package/dist/emit/emitters/ui-emitter.d.ts +21 -0
  138. package/dist/emit/emitters/ui-emitter.js +564 -0
  139. package/dist/emit/enum-emitter.d.ts +1 -0
  140. package/dist/emit/enum-emitter.js +1 -0
  141. package/dist/emit/expression-renderer.d.ts +283 -0
  142. package/dist/emit/expression-renderer.js +1477 -0
  143. package/dist/emit/native-helpers-emitter.d.ts +11 -0
  144. package/dist/emit/native-helpers-emitter.js +34 -0
  145. package/dist/emit/route-hal-op.d.ts +5 -0
  146. package/dist/emit/route-hal-op.js +13 -0
  147. package/dist/emit/snprintf-helpers.d.ts +17 -0
  148. package/dist/emit/snprintf-helpers.js +462 -0
  149. package/dist/emit/statement-renderer.d.ts +189 -0
  150. package/dist/emit/statement-renderer.js +948 -0
  151. package/dist/emit/utils/async-state-machine.d.ts +42 -0
  152. package/dist/emit/utils/async-state-machine.js +343 -0
  153. package/dist/emit/utils/comment-helpers.d.ts +20 -0
  154. package/dist/emit/utils/comment-helpers.js +31 -0
  155. package/dist/emit/utils/cpp-helpers.d.ts +47 -0
  156. package/dist/emit/utils/cpp-helpers.js +85 -0
  157. package/dist/emit/utils/include-resolver.d.ts +58 -0
  158. package/dist/emit/utils/include-resolver.js +155 -0
  159. package/dist/emit/utils/index.d.ts +8 -0
  160. package/dist/emit/utils/index.js +12 -0
  161. package/dist/emit/utils/type-inference.d.ts +159 -0
  162. package/dist/emit/utils/type-inference.js +559 -0
  163. package/dist/eslint-check.d.ts +10 -0
  164. package/dist/eslint-check.js +102 -0
  165. package/dist/framework-package.d.ts +10 -0
  166. package/dist/framework-package.js +58 -0
  167. package/dist/framework-registry.d.ts +39 -0
  168. package/dist/framework-registry.js +30 -0
  169. package/dist/incremental-cache.d.ts +116 -0
  170. package/dist/incremental-cache.js +335 -0
  171. package/dist/ir/adc-range-validation.d.ts +11 -0
  172. package/dist/ir/adc-range-validation.js +250 -0
  173. package/dist/ir/ast-node-utils.d.ts +8 -0
  174. package/dist/ir/ast-node-utils.js +32 -0
  175. package/dist/ir/board-pin-utils.d.ts +13 -0
  176. package/dist/ir/board-pin-utils.js +65 -0
  177. package/dist/ir/board-resolver.d.ts +36 -0
  178. package/dist/ir/board-resolver.js +437 -0
  179. package/dist/ir/build-ir-state.d.ts +167 -0
  180. package/dist/ir/build-ir-state.js +306 -0
  181. package/dist/ir/build-ir.d.ts +2 -0
  182. package/dist/ir/build-ir.js +808 -0
  183. package/dist/ir/call-graph.d.ts +35 -0
  184. package/dist/ir/call-graph.js +194 -0
  185. package/dist/ir/compile-time-only.d.ts +2 -0
  186. package/dist/ir/compile-time-only.js +16 -0
  187. package/dist/ir/declaration-builders.d.ts +9 -0
  188. package/dist/ir/declaration-builders.js +608 -0
  189. package/dist/ir/entry-points.d.ts +30 -0
  190. package/dist/ir/entry-points.js +191 -0
  191. package/dist/ir/expression-to-ir.d.ts +5 -0
  192. package/dist/ir/expression-to-ir.js +2249 -0
  193. package/dist/ir/extract-property-chain.d.ts +10 -0
  194. package/dist/ir/extract-property-chain.js +30 -0
  195. package/dist/ir/feature-prescan.d.ts +3 -0
  196. package/dist/ir/feature-prescan.js +43 -0
  197. package/dist/ir/feature-registry.d.ts +30 -0
  198. package/dist/ir/feature-registry.js +816 -0
  199. package/dist/ir/filter.d.ts +7 -0
  200. package/dist/ir/filter.js +85 -0
  201. package/dist/ir/function-builder.d.ts +19 -0
  202. package/dist/ir/function-builder.js +670 -0
  203. package/dist/ir/hal/hal-emitter.d.ts +62 -0
  204. package/dist/ir/hal/hal-emitter.js +735 -0
  205. package/dist/ir/hal/hal-parser.d.ts +61 -0
  206. package/dist/ir/hal/hal-parser.js +473 -0
  207. package/dist/ir/hal/hal-plugins.d.ts +34 -0
  208. package/dist/ir/hal/hal-plugins.js +706 -0
  209. package/dist/ir/hal-resolver.d.ts +5 -0
  210. package/dist/ir/hal-resolver.js +5 -0
  211. package/dist/ir/heap-analysis.d.ts +8 -0
  212. package/dist/ir/heap-analysis.js +350 -0
  213. package/dist/ir/heap-array-validation.d.ts +24 -0
  214. package/dist/ir/heap-array-validation.js +29 -0
  215. package/dist/ir/identifier-collector.d.ts +18 -0
  216. package/dist/ir/identifier-collector.js +439 -0
  217. package/dist/ir/interrupt-analysis.d.ts +39 -0
  218. package/dist/ir/interrupt-analysis.js +467 -0
  219. package/dist/ir/memory-budget-validation.d.ts +10 -0
  220. package/dist/ir/memory-budget-validation.js +86 -0
  221. package/dist/ir/namespace-builder.d.ts +7 -0
  222. package/dist/ir/namespace-builder.js +152 -0
  223. package/dist/ir/ownership-analysis.d.ts +9 -0
  224. package/dist/ir/ownership-analysis.js +1207 -0
  225. package/dist/ir/peripheral-ownership.d.ts +3 -0
  226. package/dist/ir/peripheral-ownership.js +152 -0
  227. package/dist/ir/peripheral-symbols.d.ts +7 -0
  228. package/dist/ir/peripheral-symbols.js +57 -0
  229. package/dist/ir/peripheral-usage.d.ts +54 -0
  230. package/dist/ir/peripheral-usage.js +691 -0
  231. package/dist/ir/peripheral-validation.d.ts +8 -0
  232. package/dist/ir/peripheral-validation.js +77 -0
  233. package/dist/ir/pin-alias-conflict.d.ts +4 -0
  234. package/dist/ir/pin-alias-conflict.js +34 -0
  235. package/dist/ir/pin-capability-validation.d.ts +6 -0
  236. package/dist/ir/pin-capability-validation.js +413 -0
  237. package/dist/ir/pin-mode-validation.d.ts +10 -0
  238. package/dist/ir/pin-mode-validation.js +301 -0
  239. package/dist/ir/pin-safety.d.ts +11 -0
  240. package/dist/ir/pin-safety.js +58 -0
  241. package/dist/ir/program-analysis.d.ts +44 -0
  242. package/dist/ir/program-analysis.js +589 -0
  243. package/dist/ir/pulldown-validation.d.ts +7 -0
  244. package/dist/ir/pulldown-validation.js +49 -0
  245. package/dist/ir/pwm-timer-sharing.d.ts +4 -0
  246. package/dist/ir/pwm-timer-sharing.js +93 -0
  247. package/dist/ir/reachability.d.ts +68 -0
  248. package/dist/ir/reachability.js +224 -0
  249. package/dist/ir/register-decorators.d.ts +6 -0
  250. package/dist/ir/register-decorators.js +49 -0
  251. package/dist/ir/render-expr.d.ts +4 -0
  252. package/dist/ir/render-expr.js +127 -0
  253. package/dist/ir/resource-analysis.d.ts +19 -0
  254. package/dist/ir/resource-analysis.js +153 -0
  255. package/dist/ir/statement-to-ir.d.ts +23 -0
  256. package/dist/ir/statement-to-ir.js +349 -0
  257. package/dist/ir/symbol-table.d.ts +68 -0
  258. package/dist/ir/symbol-table.js +179 -0
  259. package/dist/ir/symbol-types.d.ts +56 -0
  260. package/dist/ir/symbol-types.js +107 -0
  261. package/dist/ir/timer0-pwm-timing-conflict.d.ts +4 -0
  262. package/dist/ir/timer0-pwm-timing-conflict.js +71 -0
  263. package/dist/ir/timing-validation.d.ts +7 -0
  264. package/dist/ir/timing-validation.js +95 -0
  265. package/dist/ir/transformers/array-methods.d.ts +8 -0
  266. package/dist/ir/transformers/array-methods.js +493 -0
  267. package/dist/ir/transformers/call-statement.d.ts +5 -0
  268. package/dist/ir/transformers/call-statement.js +758 -0
  269. package/dist/ir/transformers/callback-context-registry.d.ts +20 -0
  270. package/dist/ir/transformers/callback-context-registry.js +55 -0
  271. package/dist/ir/transformers/canvas-lowering.d.ts +42 -0
  272. package/dist/ir/transformers/canvas-lowering.js +249 -0
  273. package/dist/ir/transformers/control-flow.d.ts +8 -0
  274. package/dist/ir/transformers/control-flow.js +508 -0
  275. package/dist/ir/transformers/expressions.d.ts +7 -0
  276. package/dist/ir/transformers/expressions.js +406 -0
  277. package/dist/ir/transformers/hal-call-resolver.d.ts +29 -0
  278. package/dist/ir/transformers/hal-call-resolver.js +274 -0
  279. package/dist/ir/transformers/hal-emit-helpers.d.ts +14 -0
  280. package/dist/ir/transformers/hal-emit-helpers.js +63 -0
  281. package/dist/ir/transformers/namespace-methods.d.ts +20 -0
  282. package/dist/ir/transformers/namespace-methods.js +110 -0
  283. package/dist/ir/transformers/register-assignment.d.ts +5 -0
  284. package/dist/ir/transformers/register-assignment.js +56 -0
  285. package/dist/ir/transformers/ui-call-resolver.d.ts +149 -0
  286. package/dist/ir/transformers/ui-call-resolver.js +1136 -0
  287. package/dist/ir/transformers/ui-callback-lowering.d.ts +62 -0
  288. package/dist/ir/transformers/ui-callback-lowering.js +623 -0
  289. package/dist/ir/transformers/ui-mount.d.ts +20 -0
  290. package/dist/ir/transformers/ui-mount.js +35 -0
  291. package/dist/ir/transformers/ui-reactive.d.ts +68 -0
  292. package/dist/ir/transformers/ui-reactive.js +129 -0
  293. package/dist/ir/transformers/variables.d.ts +14 -0
  294. package/dist/ir/transformers/variables.js +1045 -0
  295. package/dist/ir/try-catch-validation.d.ts +26 -0
  296. package/dist/ir/try-catch-validation.js +128 -0
  297. package/dist/ir/type-resolution.d.ts +31 -0
  298. package/dist/ir/type-resolution.js +977 -0
  299. package/dist/ir/ui-element-auto-wire.d.ts +50 -0
  300. package/dist/ir/ui-element-auto-wire.js +218 -0
  301. package/dist/ir/unit-suspicion-validation.d.ts +12 -0
  302. package/dist/ir/unit-suspicion-validation.js +296 -0
  303. package/dist/ir/utils/walk-ir.d.ts +6 -0
  304. package/dist/ir/utils/walk-ir.js +162 -0
  305. package/dist/ir/validation-orchestrator.d.ts +4 -0
  306. package/dist/ir/validation-orchestrator.js +45 -0
  307. package/dist/libdef/base-class-resolver.d.ts +23 -0
  308. package/dist/libdef/base-class-resolver.js +50 -0
  309. package/dist/libdef/cpp-to-decl.d.ts +34 -0
  310. package/dist/libdef/cpp-to-decl.js +584 -0
  311. package/dist/libdef/header-parser.d.ts +53 -0
  312. package/dist/libdef/header-parser.js +269 -0
  313. package/dist/libdef/registry.d.ts +10 -0
  314. package/dist/libdef/registry.js +142 -0
  315. package/dist/mapping/peripheral-names.d.ts +23 -0
  316. package/dist/mapping/peripheral-names.js +63 -0
  317. package/dist/mapping/source-map.d.ts +46 -0
  318. package/dist/mapping/source-map.js +207 -0
  319. package/dist/orchestrator/dts-generator.d.ts +17 -0
  320. package/dist/orchestrator/dts-generator.js +83 -0
  321. package/dist/orchestrator/graph-builder.d.ts +18 -0
  322. package/dist/orchestrator/graph-builder.js +218 -0
  323. package/dist/orchestrator/semantic-facts-verifier.d.ts +38 -0
  324. package/dist/orchestrator/semantic-facts-verifier.js +121 -0
  325. package/dist/orchestrator/semantic-facts.d.ts +141 -0
  326. package/dist/orchestrator/semantic-facts.js +652 -0
  327. package/dist/orchestrator/type-checker.d.ts +39 -0
  328. package/dist/orchestrator/type-checker.js +760 -0
  329. package/dist/platform/async-runtime.d.ts +8 -0
  330. package/dist/platform/async-runtime.js +26 -0
  331. package/dist/platform/generic-strategy.d.ts +101 -0
  332. package/dist/platform/generic-strategy.js +289 -0
  333. package/dist/platform/index.d.ts +4 -0
  334. package/dist/platform/index.js +9 -0
  335. package/dist/platform/registry.d.ts +21 -0
  336. package/dist/platform/registry.js +42 -0
  337. package/dist/platform/toolchain.d.ts +4 -0
  338. package/dist/platform/toolchain.js +43 -0
  339. package/dist/preview/client.d.ts +1 -0
  340. package/dist/preview/client.js +137 -0
  341. package/dist/preview/server.d.ts +5 -0
  342. package/dist/preview/server.js +199 -0
  343. package/dist/profiler/index.d.ts +28 -0
  344. package/dist/profiler/index.js +28 -0
  345. package/dist/profiler/profiler.d.ts +95 -0
  346. package/dist/profiler/profiler.js +338 -0
  347. package/dist/profiler/types.d.ts +87 -0
  348. package/dist/profiler/types.js +12 -0
  349. package/dist/stores/display-profile-store.d.ts +16 -0
  350. package/dist/stores/display-profile-store.js +48 -0
  351. package/dist/stores/theme-store.d.ts +13 -0
  352. package/dist/stores/theme-store.js +30 -0
  353. package/dist/testing.d.ts +45 -0
  354. package/dist/testing.js +47 -0
  355. package/dist/transpile/resolution.d.ts +29 -0
  356. package/dist/transpile/resolution.js +282 -0
  357. package/dist/transpile.d.ts +3 -0
  358. package/dist/transpile.js +722 -0
  359. package/dist/types.d.ts +217 -0
  360. package/dist/types.js +1 -0
  361. package/dist/ui/ui-bridge.d.ts +4 -0
  362. package/dist/ui/ui-bridge.js +34 -0
  363. package/dist/ui-hook.d.ts +84 -0
  364. package/dist/ui-hook.js +45 -0
  365. package/dist/utils/cli.d.ts +3 -0
  366. package/dist/utils/cli.js +463 -0
  367. package/dist/utils/fs.d.ts +4 -0
  368. package/dist/utils/fs.js +23 -0
  369. package/dist/utils/logger.d.ts +2 -0
  370. package/dist/utils/logger.js +8 -0
  371. package/dist/utils/strings.d.ts +35 -0
  372. package/dist/utils/strings.js +142 -0
  373. package/dist/utils/ui.d.ts +66 -0
  374. package/dist/utils/ui.js +149 -0
  375. package/dist/watch.d.ts +35 -0
  376. package/dist/watch.js +111 -0
  377. package/package.json +127 -0
@@ -0,0 +1,722 @@
1
+ // See TRANSPILER_GUIDE.md for architecture details.
2
+ import path from "node:path";
3
+ import fs from "node:fs";
4
+ import { createRequire } from "node:module";
5
+ import ts from "typescript";
6
+ import { buildProgramIR } from "./ir/build-ir.js";
7
+ import { classDeclarationToIR } from "./ir/declaration-builders.js";
8
+ import { clickHandlers } from "./ir/transformers/ui-call-resolver.js";
9
+ import { requireUIHook } from "./ui-hook.js";
10
+ import { loadUIEngine } from "./ui/ui-bridge.js";
11
+ import { setDisplayProfile, resetDisplayProfile } from "./stores/display-profile-store.js";
12
+ import { setThemeCss, resetThemeCss, setThemeClass } from "./stores/theme-store.js";
13
+ import { emitCpp, registerAllEnumNames } from "./emit/cpp-emitter.js";
14
+ import { readText } from "./utils/fs.js";
15
+ import { debug as logDebug, info } from "./utils/logger.js";
16
+ import { loadLibraryDefinitions, generateLibdefStubs } from "./libdef/registry.js";
17
+ import { buildCallGraph } from "./ir/call-graph.js";
18
+ import { clearCaches, } from "./cache.js";
19
+ import { detectEntryPoints, detectExportedEntryPoints } from "./ir/entry-points.js";
20
+ import { analyzeReachability } from "./ir/reachability.js";
21
+ import { filterProgramIR } from "./ir/filter.js";
22
+ import { setActiveStrategy } from "./ir/hal-resolver.js";
23
+ import { CompilationContext, contextStorage } from "./ir/build-ir-state.js";
24
+ import { buildSymbolTable, mergeSymbolTable, resolveInheritance, createSymbolTable } from "./ir/symbol-table.js";
25
+ import { loadBreakpoints, preprocess as debugPreprocess } from "./debug/index.js";
26
+ import { collectTranspileGraph } from "./orchestrator/graph-builder.js";
27
+ import { typeCheckFiles } from "./orchestrator/type-checker.js";
28
+ import { runSemanticGates } from "./orchestrator/type-checker.js";
29
+ import { autoGenerateMissingDecls } from "./orchestrator/dts-generator.js";
30
+ import { runEslintCheck, printEslintErrors } from "./eslint-check.js";
31
+ import { initProfiler, getProfiler } from "./profiler/index.js";
32
+ import { buildDiagnosticsReport, writeDiagnosticsReport } from "./diagnostics/diagnostics-report.js";
33
+ import { resolveImport, } from "./transpile/resolution.js";
34
+ const require = createRequire(import.meta.url);
35
+ let expectPreprocess;
36
+ function loadExpectPreprocessor() {
37
+ if (expectPreprocess) {
38
+ return expectPreprocess;
39
+ }
40
+ try {
41
+ const mod = require("@typecad/expect/preprocessor");
42
+ expectPreprocess = mod?.preprocess;
43
+ return expectPreprocess;
44
+ }
45
+ catch {
46
+ return undefined;
47
+ }
48
+ }
49
+ function cleanOutput(_entryDir, outDir) {
50
+ // NOTE: incremental transpilation is disabled (see incremental-cache.ts).
51
+ // Only the output directory is cleaned; do not delete .cuttlefish-cache.json
52
+ // here so a future incremental implementation can read prior metadata.
53
+ try {
54
+ if (fs.existsSync(outDir))
55
+ fs.rmSync(outDir, { recursive: true, force: true });
56
+ }
57
+ catch (e) {
58
+ if (process.env.CUTTLEFISH_DEBUG)
59
+ console.error("[transpile] Failed to clean output dir:", e);
60
+ }
61
+ }
62
+ /**
63
+ * Auto-generates .d.ts files for C++ modules that are missing declarations.
64
+ * Also tries to generate declarations for framework libraries.
65
+ * Returns list of generated files.
66
+ */
67
+ /**
68
+ * Apply tree-shaking to program IR if enabled
69
+ */
70
+ function applyTreeShaking(programIR, target, treeShakingOptions) {
71
+ // Default to enabled - tree-shaking removes unreachable code
72
+ const enabled = treeShakingOptions?.enabled !== false;
73
+ if (!enabled) {
74
+ return { programIR, removedSymbols: [] };
75
+ }
76
+ const profiler = getProfiler();
77
+ // Build call graph
78
+ profiler.startTimer("tree-shake:call-graph");
79
+ const callGraph = buildCallGraph(programIR);
80
+ profiler.endTimer("tree-shake:call-graph");
81
+ // Detect entry points
82
+ profiler.startTimer("tree-shake:entry-points");
83
+ const treeShakeStrategy = resolveStrategy(target);
84
+ const entryPoints = detectEntryPoints(programIR, {
85
+ customEntryPoints: treeShakingOptions?.entryPoints ?? [],
86
+ }, treeShakeStrategy.requiresLoopFunction()
87
+ ? [treeShakeStrategy.entrypointFunctionName(), "loop"]
88
+ : [treeShakeStrategy.entrypointFunctionName()]);
89
+ profiler.endTimer("tree-shake:entry-points");
90
+ // Analyze reachability
91
+ profiler.startTimer("tree-shake:reachability");
92
+ const reachability = analyzeReachability(programIR, callGraph, {
93
+ target,
94
+ entryPointConfig: {
95
+ customEntryPoints: treeShakingOptions?.entryPoints ?? [],
96
+ },
97
+ keepUnusedEnums: treeShakingOptions?.keepUnusedEnums,
98
+ keepUnusedClasses: treeShakingOptions?.keepUnusedClasses,
99
+ keepUnusedTypeAliases: treeShakingOptions?.keepUnusedTypeAliases,
100
+ keepUnusedVariables: treeShakingOptions?.keepUnusedVariables,
101
+ reportUnused: treeShakingOptions?.reportUnused,
102
+ });
103
+ profiler.endTimer("tree-shake:reachability");
104
+ // Filter program IR
105
+ profiler.startTimer("tree-shake:filter");
106
+ const result = filterProgramIR(programIR, reachability, {
107
+ enabled: true,
108
+ keepUnusedEnums: treeShakingOptions?.keepUnusedEnums,
109
+ keepUnusedClasses: treeShakingOptions?.keepUnusedClasses,
110
+ keepUnusedTypeAliases: treeShakingOptions?.keepUnusedTypeAliases,
111
+ keepUnusedVariables: treeShakingOptions?.keepUnusedVariables,
112
+ reportUnused: treeShakingOptions?.reportUnused,
113
+ });
114
+ profiler.endTimer("tree-shake:filter");
115
+ // Collect removed symbols for the report
116
+ const removedSymbols = [];
117
+ for (const fn of programIR.functions) {
118
+ if (!reachability.reachableFunctions.has(fn.originalName))
119
+ removedSymbols.push(fn.originalName);
120
+ }
121
+ for (const cls of programIR.classes) {
122
+ if (!reachability.reachableClasses.has(cls.name))
123
+ removedSymbols.push(cls.name);
124
+ }
125
+ for (const e of programIR.enums) {
126
+ if (!reachability.reachableEnums.has(e.name))
127
+ removedSymbols.push(e.name);
128
+ }
129
+ return { programIR: result, removedSymbols };
130
+ }
131
+ import { isStringEnum } from "./api/shared/index.js";
132
+ import { resolveStrategy } from "./platform/registry.js";
133
+ import { loadFrameworkPackage } from "./framework-package.js";
134
+ import { getLoadedFramework, hasLoadedFramework } from "./framework-registry.js";
135
+ function formatFatalDiagnostics(entries) {
136
+ const errors = entries.filter(({ diagnostic }) => diagnostic.severity === "error");
137
+ const lines = [
138
+ `Transpilation aborted because ${errors.length} unsupported pattern${errors.length === 1 ? "" : "s"} were found.`,
139
+ ];
140
+ for (const { filePath, diagnostic } of errors) {
141
+ const locationBase = filePath
142
+ ? path.relative(process.cwd(), filePath) || filePath
143
+ : diagnostic.source ?? "user code";
144
+ const position = diagnostic.line != null
145
+ ? `(${diagnostic.line}:${diagnostic.column ?? 1})`
146
+ : "";
147
+ const code = diagnostic.code ? `[${diagnostic.code}] ` : "";
148
+ lines.push(`ERROR: ${locationBase}${position}: ${code}${diagnostic.message}`);
149
+ if (diagnostic.sourceLine) {
150
+ lines.push(` ${diagnostic.sourceLine}`);
151
+ if (diagnostic.column != null && diagnostic.column > 0) {
152
+ lines.push(` ${" ".repeat(Math.max(0, diagnostic.column - 1))}^`);
153
+ }
154
+ }
155
+ if (diagnostic.hint) {
156
+ lines.push(` hint: ${diagnostic.hint}`);
157
+ }
158
+ }
159
+ return lines.join("\n");
160
+ }
161
+ function throwIfFatalDiagnostics(entries) {
162
+ if (entries.some(({ diagnostic }) => diagnostic.severity === "error")) {
163
+ throw new Error(formatFatalDiagnostics(entries));
164
+ }
165
+ }
166
+ /**
167
+ * Load the platform strategy from the configured framework package.
168
+ *
169
+ * Uses the unified loading path (CP6): loads the framework package via
170
+ * loadFrameworkPackage(), which populates the LoadedFramework registry,
171
+ * then extracts the strategy.
172
+ *
173
+ * Returns undefined if no framework package is configured or the package
174
+ * doesn't export a FrameworkStrategy, allowing emitCpp to fall back to
175
+ * target-based resolution via the platform registry.
176
+ */
177
+ function loadPlatformStrategy(frameworkPackage, _boardPackage, fromDir, debug) {
178
+ if (!frameworkPackage) {
179
+ if (debug) {
180
+ logDebug(`No framework package configured, will use target-based resolution`, true);
181
+ }
182
+ return undefined;
183
+ }
184
+ try {
185
+ loadFrameworkPackage(frameworkPackage, fromDir);
186
+ if (hasLoadedFramework()) {
187
+ const { strategy } = getLoadedFramework();
188
+ if (debug) {
189
+ logDebug(`Loaded FrameworkStrategy from ${frameworkPackage}`, true);
190
+ }
191
+ return strategy;
192
+ }
193
+ }
194
+ catch (e) {
195
+ if (debug) {
196
+ logDebug(`Failed to load strategy from ${frameworkPackage}: ${e instanceof Error ? e.message : String(e)}`, true);
197
+ }
198
+ }
199
+ if (debug) {
200
+ logDebug(`No framework strategy loaded, will use target-based resolution`, true);
201
+ }
202
+ return undefined;
203
+ }
204
+ export async function transpileFile(options) {
205
+ // Initialize profiler (disabled by default - internal use only)
206
+ const profiler = initProfiler({
207
+ enabled: options.diagnostics === true,
208
+ trackMemory: false,
209
+ threshold: 0, // Ensure even fast phases are recorded for the report
210
+ });
211
+ profiler.startSession();
212
+ profiler.startTimer("setup:caches");
213
+ // Clear session caches at the start of each transpilation
214
+ clearCaches();
215
+ // Load the UI engine from @typecad/ui (if installed). Sets up the hook so
216
+ // requireUIHook() works throughout this transpile run. Gracefully no-ops
217
+ // when @typecad/ui is absent (pure TS→C++ transpile).
218
+ await loadUIEngine();
219
+ const entryFile = path.resolve(options.inputFile);
220
+ const entryDir = path.dirname(entryFile);
221
+ const sourceDir = entryDir;
222
+ const sketchBaseName = path.basename(entryFile).replace(/\.[^.]+$/, "");
223
+ const outBaseDir = options.outDir ?? sourceDir;
224
+ profiler.endTimer("setup:caches");
225
+ profiler.startTimer("setup:load-strategy");
226
+ // Load platform strategy from framework or board package, or use target-based resolution
227
+ const boardStrategy = loadPlatformStrategy(options.frameworkPackage, options.boardPackage, entryDir, options.debug);
228
+ profiler.endTimer("setup:load-strategy");
229
+ // Use the framework-loaded strategy for output path computation if available,
230
+ // otherwise fall back to target-based resolution. This ensures a single
231
+ // consistent strategy drives both the output directory and emission.
232
+ const strategy = boardStrategy ?? resolveStrategy(options.target);
233
+ setActiveStrategy(strategy);
234
+ // Load display profile from config (if present) into the profile store.
235
+ resetDisplayProfile();
236
+ resetThemeCss();
237
+ const configDisplay = options.display;
238
+ if (configDisplay) {
239
+ const { resolveDisplayProfile } = await import("./api/shared/display-profile.js");
240
+ try {
241
+ // Load built-in profiles from the framework package via its exported path
242
+ const registry = new Map();
243
+ if (options.frameworkPackage) {
244
+ const profileMod = await import(options.frameworkPackage + "/displays/ili9341-spi").catch(() => null);
245
+ if (profileMod?.BUILT_IN_PROFILES) {
246
+ for (const [k, v] of Object.entries(profileMod.BUILT_IN_PROFILES)) {
247
+ registry.set(k, v);
248
+ }
249
+ }
250
+ }
251
+ const resolved = resolveDisplayProfile(configDisplay, registry);
252
+ const buildTarget = options.platformContext?.frameworkData?.buildTarget;
253
+ setDisplayProfile(resolved.profile, { cs: resolved.cs, dc: resolved.dc, rst: resolved.rst, bus: resolved.bus, address: resolved.address, reset: resolved.reset, buildTarget });
254
+ }
255
+ catch {
256
+ // Fall back to default profile — not fatal
257
+ }
258
+ // Apply theme CSS override if specified.
259
+ if (configDisplay.themeCss) {
260
+ setThemeCss(configDisplay.themeCss);
261
+ }
262
+ if (configDisplay.themeClass) {
263
+ setThemeClass(configDisplay.themeClass);
264
+ }
265
+ }
266
+ const outDir = path.join(outBaseDir, strategy.outputSubdirectory(sketchBaseName));
267
+ // Start fresh: clean the output directory. (Incremental builds are disabled —
268
+ // see incremental-cache.ts — so we always transpile the full graph.)
269
+ cleanOutput(entryDir, outDir);
270
+ profiler.startTimer("graph:collect");
271
+ const graphResult = collectTranspileGraph(entryFile, options.boardPackage);
272
+ profiler.endTimer("graph:collect");
273
+ const transpileFiles = graphResult.files;
274
+ // ── Type-check all files before transpiling ────────────────────────────────
275
+ // Skip type-checking if explicitly disabled
276
+ let typeCheckProgram;
277
+ if (options.skipTypeCheck !== true && transpileFiles.length > 0) {
278
+ profiler.startTimer("typecheck:full");
279
+ let typeCheckResult = typeCheckFiles(transpileFiles, options.boardPackage, entryFile);
280
+ // If type-checking failed, try to auto-generate missing .d.ts files from C++ sources
281
+ if (!typeCheckResult.success) {
282
+ profiler.startTimer("typecheck:autogen-decls");
283
+ const generatedDecls = autoGenerateMissingDecls(transpileFiles, typeCheckResult.errors);
284
+ profiler.endTimer("typecheck:autogen-decls");
285
+ // If we generated any declaration files, retry type-checking
286
+ if (generatedDecls.length > 0) {
287
+ profiler.startTimer("typecheck:retry");
288
+ typeCheckResult = typeCheckFiles(transpileFiles, options.boardPackage, entryFile);
289
+ profiler.endTimer("typecheck:retry");
290
+ }
291
+ }
292
+ profiler.endTimer("typecheck:full");
293
+ if (!typeCheckResult.success) {
294
+ // Report all type errors and throw to stop transpilation
295
+ const errorMessages = typeCheckResult.errors.map(e => `ERROR: ${e}`).join("\n");
296
+ throw new Error(`TypeScript type-checking failed:\n${errorMessages}\n\nTranspilation aborted due to TypeScript errors.`);
297
+ }
298
+ // Capture the program for the semantic-gates pass (Phase 3) so we don't
299
+ // rebuild it.
300
+ typeCheckProgram = typeCheckResult.program;
301
+ }
302
+ // ── ESLint gate ──────────────────────────────────────────────────────────
303
+ // ESLint catches what the type-checker cannot (idiom violations, banned
304
+ // globals, explicit `any`, etc.). Errors abort the build, mirroring the
305
+ // type-check behavior above. Skipped alongside type-checking when disabled.
306
+ if (options.skipLint !== true && options.skipTypeCheck !== true && transpileFiles.length > 0) {
307
+ profiler.startTimer("lint:eslint");
308
+ // The eslint config lives at the project root (next to cuttlefish.config.ts),
309
+ // not under src/. Fall back to entryDir for ad-hoc API/test callers that pass
310
+ // a bare input file without a configured project.
311
+ const eslintRoot = options.projectRoot ?? entryDir;
312
+ const eslintErrors = await runEslintCheck(eslintRoot);
313
+ profiler.endTimer("lint:eslint");
314
+ if (eslintErrors.length > 0) {
315
+ // Abort with a formatted message. The structured-diagnostic channel is
316
+ // not populated here because a thrown error discards the output anyway;
317
+ // printEslintErrors gives the user file/line/column/caret directly.
318
+ printEslintErrors(eslintErrors);
319
+ throw new Error(`ESLint reported ${eslintErrors.length} error${eslintErrors.length === 1 ? "" : "s"} — transpilation aborted.`);
320
+ }
321
+ }
322
+ const npmPackages = graphResult.npmPackages;
323
+ const definitions = loadLibraryDefinitions(sourceDir);
324
+ let entryOutputs;
325
+ const diagnostics = [];
326
+ const allRemovedSymbols = [];
327
+ // themeCss is only honored on the .ui.html disk-read path (loadUIModule).
328
+ // For a .ui single-file entry the inline <style> is the sole CSS source; the
329
+ // standalone file is silently ignored. Warn so authors don't maintain a dead
330
+ // stylesheet.
331
+ if (configDisplay?.themeCss) {
332
+ const entryExt = path.extname(options.inputFile).toLowerCase();
333
+ if (entryExt === ".ui") {
334
+ diagnostics.push({
335
+ severity: "warning",
336
+ code: "themeCss-ui-entry-ignored",
337
+ message: `display.themeCss is ignored for .ui single-file entries; the inline <style> in ${path.basename(options.inputFile)} is the sole CSS source.`,
338
+ hint: `Move the standalone CSS into the .ui file's <style> block, or change the entry to a .ts file that imports a .ui.html module.`,
339
+ source: path.basename(options.inputFile),
340
+ });
341
+ }
342
+ }
343
+ // ── Parser-level warnings (unknown CSS properties / HTML tags) ────────────
344
+ // The graph build above already loaded all .ui.html modules; surface their
345
+ // parser warnings (unknown CSS properties, unknown HTML tags) here so the
346
+ // author sees typos and unsupported features instead of silent drops.
347
+ for (const mod of requireUIHook().allUIModules()) {
348
+ for (const d of mod.diagnostics) {
349
+ diagnostics.push({ ...d, source: d.source ?? path.basename(mod.htmlPath) });
350
+ }
351
+ }
352
+ // ── Semantic gates (Phase 3) ──────────────────────────────────────────────
353
+ // Type-resolved checks that the syntactic feature-prescan cannot express:
354
+ // heterogeneous array literals and cross-file new-on-interface. These reuse
355
+ // the program built by typeCheckFiles. Surfaced as structured diagnostics
356
+ // (severity "error"); if any are present, the build aborts below.
357
+ if (typeCheckProgram) {
358
+ profiler.startTimer("typecheck:semantic-gates");
359
+ const semanticDiagnostics = runSemanticGates(typeCheckProgram, transpileFiles);
360
+ profiler.endTimer("typecheck:semantic-gates");
361
+ if (semanticDiagnostics.length > 0) {
362
+ diagnostics.push(...semanticDiagnostics);
363
+ throwIfFatalDiagnostics(semanticDiagnostics.map((diagnostic) => ({ diagnostic })));
364
+ }
365
+ }
366
+ // Load breakpoints if debug mode is enabled
367
+ const breakpoints = options.debug ? loadBreakpoints(sourceDir) : undefined;
368
+ // ── Determine files to transpile ─────────────────────────────────────────
369
+ // Incremental builds are disabled, so every file in the graph is processed.
370
+ // See incremental-cache.ts for why a partial rebuild cannot be sound without
371
+ // rehydrating cached IR/metadata for the entire graph.
372
+ const filesToProcess = transpileFiles;
373
+ // ── Phase 0: Pre-scan all files for class declarations to build a
374
+ // cross-module type registry. This is needed so that property access
375
+ // on imported class instances (e.g. player.weaponName) resolve to the
376
+ // correct C++ type during IR building, rather than falling back to "auto".
377
+ const prebuiltClassMap = new Map();
378
+ const classSources = [];
379
+ for (const filePath of transpileFiles) {
380
+ try {
381
+ const sourceText = await fs.promises.readFile(filePath, "utf8");
382
+ const source = ts.createSourceFile(filePath, sourceText, ts.ScriptTarget.Latest, true);
383
+ classSources.push({
384
+ filePath,
385
+ sourceText,
386
+ declarations: source.statements.filter(ts.isClassDeclaration),
387
+ });
388
+ }
389
+ catch {
390
+ // File read/parse errors will be caught during actual IR building
391
+ }
392
+ }
393
+ // Class annotations are reference types even when the referenced class is
394
+ // declared in another file or later in the graph. Register every class name
395
+ // before building any signatures so fields such as `player: Player` become
396
+ // `Player*` consistently in the cross-module registry.
397
+ const prebuildContext = new CompilationContext();
398
+ prebuildContext.activeStrategy = strategy;
399
+ for (const { declarations } of classSources) {
400
+ for (const declaration of declarations) {
401
+ if (!declaration.name)
402
+ continue;
403
+ prebuildContext.topLevelClassNames.add(declaration.name.text);
404
+ prebuildContext.classTypeNames.add(declaration.name.text);
405
+ }
406
+ }
407
+ contextStorage.run(prebuildContext, () => {
408
+ for (const { filePath, sourceText, declarations } of classSources) {
409
+ for (const declaration of declarations) {
410
+ const classIR = classDeclarationToIR(declaration, filePath, sourceText, [], new Map(), new Map(), [], new Map());
411
+ if (classIR && !prebuiltClassMap.has(classIR.name)) {
412
+ prebuiltClassMap.set(classIR.name, classIR);
413
+ prebuildContext.topLevelClasses.set(classIR.name, classIR);
414
+ }
415
+ }
416
+ }
417
+ });
418
+ const buildRawIR = async (filePath) => {
419
+ const fileBasename = path.basename(filePath);
420
+ profiler.startTimer(`ir:build:${fileBasename}`);
421
+ let sourceText = await fs.promises.readFile(filePath, "utf8");
422
+ const fileExtension = path.extname(filePath).toLowerCase();
423
+ // .ui single-file component: extract the <script> as the TS source, with an
424
+ // injected `import { screen }` so the script can reference the in-file
425
+ // template (registered as a UI module by the graph builder).
426
+ if (fileExtension === ".ui") {
427
+ const parts = requireUIHook().splitUiFile(sourceText);
428
+ const baseName = path.basename(filePath, ".ui");
429
+ sourceText = `import { screen } from './${baseName}.ui.html';\n` + parts.script;
430
+ }
431
+ // If the file imports @typecad/expect, run the AST preprocessor
432
+ // to rewrite describe/it/expect/done calls into Serial protocol statements.
433
+ if (sourceText.includes("@typecad/expect")) {
434
+ const preprocess = loadExpectPreprocessor();
435
+ if (!preprocess) {
436
+ throw new Error("The @typecad/expect package is required to transpile files that import @typecad/expect. " +
437
+ "Install @typecad/expect or remove the import.");
438
+ }
439
+ sourceText = preprocess(sourceText, filePath);
440
+ }
441
+ if (options.debug && breakpoints) {
442
+ const instrumented = debugPreprocess({
443
+ fileName: filePath,
444
+ breakpoints,
445
+ source: sourceText,
446
+ });
447
+ sourceText = instrumented;
448
+ }
449
+ profiler.startTimer(`ir:build-ir:${fileBasename}`);
450
+ const programIR = buildProgramIR(filePath, sourceText, options.boardPackage, prebuiltClassMap);
451
+ profiler.endTimer(`ir:build-ir:${fileBasename}`);
452
+ const npmPackage = npmPackages.get(filePath);
453
+ profiler.endTimer(`ir:build:${fileBasename}`);
454
+ return { filePath, programIR, npmPackage };
455
+ };
456
+ profiler.startTimer("ir:build-all");
457
+ profiler.captureMemorySnapshot("ir:pre-build");
458
+ const rawIRArray = await Promise.all(filesToProcess.map(buildRawIR));
459
+ profiler.captureMemorySnapshot("ir:post-build");
460
+ profiler.endTimer("ir:build-all");
461
+ // ── UI mount-time warnings (scroll memory budget, etc.) ─────────────────
462
+ for (const mod of requireUIHook().allUIModules()) {
463
+ for (const d of mod.mountDiagnostics) {
464
+ diagnostics.push({ ...d, source: d.source ?? path.basename(mod.htmlPath) });
465
+ }
466
+ }
467
+ throwIfFatalDiagnostics(rawIRArray.flatMap(({ filePath, programIR }) => programIR.diagnostics.map((diagnostic) => ({ filePath, diagnostic }))));
468
+ // ── Phase B: Compute cross-module import map ─────────────────────────────
469
+ // For each file, determine which of its symbols are imported by other files
470
+ // in the project. Those symbols become additional entry points for tree-shaking
471
+ // so they aren't eliminated as "unused" when they're only consumed externally.
472
+ profiler.startTimer("ir:cross-module-imports");
473
+ const symbolExportedTo = new Map(); // symbol → Set<filePath that defines it>
474
+ const fileDefinedSymbols = new Map(); // filePath → Set<symbol names>
475
+ for (const { filePath, programIR } of rawIRArray) {
476
+ const defined = new Set();
477
+ for (const fn of programIR.functions)
478
+ defined.add(fn.originalName);
479
+ for (const cls of programIR.classes)
480
+ defined.add(cls.name);
481
+ for (const e of programIR.enums)
482
+ defined.add(e.name);
483
+ for (const ta of programIR.typeAliases)
484
+ defined.add(ta.name);
485
+ fileDefinedSymbols.set(filePath, defined);
486
+ }
487
+ // For each file, look at its imports and record which symbols it imports
488
+ // from other files in the project.
489
+ const crossModuleImports = new Map(); // filePath → symbols imported by OTHER files
490
+ for (const { filePath, programIR } of rawIRArray) {
491
+ for (const imp of programIR.imports) {
492
+ // Resolve the import to find which file it comes from
493
+ const resolved = resolveImport(filePath, imp.moduleSpecifier, options.boardPackage);
494
+ if (!resolved)
495
+ continue;
496
+ const targetFile = resolved.sourcePath;
497
+ // Only track imports from files in our transpile graph
498
+ if (!fileDefinedSymbols.has(targetFile))
499
+ continue;
500
+ for (const symbol of imp.namedImports) {
501
+ if (!crossModuleImports.has(targetFile)) {
502
+ crossModuleImports.set(targetFile, new Set());
503
+ }
504
+ crossModuleImports.get(targetFile).add(symbol);
505
+ }
506
+ // Handle default imports: import X from "./module.js"
507
+ if (imp.defaultImportName) {
508
+ // Find the target module's default export name
509
+ const targetIR = rawIRArray.find(r => r.filePath === targetFile);
510
+ if (targetIR?.programIR.defaultExportName) {
511
+ if (!crossModuleImports.has(targetFile)) {
512
+ crossModuleImports.set(targetFile, new Set());
513
+ }
514
+ crossModuleImports.get(targetFile).add(targetIR.programIR.defaultExportName);
515
+ }
516
+ }
517
+ }
518
+ }
519
+ profiler.endTimer("ir:cross-module-imports");
520
+ // ── Phase C: Tree-shake with cross-module awareness + compute polyfills ──
521
+ const preBuiltArray = [];
522
+ for (const { filePath, programIR: rawIR, npmPackage } of rawIRArray) {
523
+ const fileBasename = path.basename(filePath);
524
+ // Detect symbols that other files import from this one
525
+ const importedByOthers = crossModuleImports.get(filePath) ?? new Set();
526
+ const exportedEntryPoints = detectExportedEntryPoints(rawIR, importedByOthers);
527
+ // Functions referenced by on:* HTML attributes (on:click="saveSettings") are
528
+ // named-ref handlers — the click-handler table references them, but the TS
529
+ // call-graph doesn't (the reference is in HTML). Keep them as tree-shake
530
+ // entry points so they aren't stripped before the table is emitted.
531
+ const onAttrEntryPoints = clickHandlers()
532
+ .filter(h => h.isNamedRef)
533
+ .map(h => h.fnName);
534
+ profiler.startTimer(`tree-shake:${fileBasename}`);
535
+ let shakingResult;
536
+ if (filePath === entryFile) {
537
+ shakingResult = applyTreeShaking(rawIR, options.target, {
538
+ ...options.treeShaking,
539
+ keepUnusedVariables: options.treeShaking?.keepUnusedVariables ?? false,
540
+ // Merge exported entry points so cross-module imports aren't shaken out,
541
+ // plus on:* HTML-attribute handler refs (named fns not called in TS).
542
+ entryPoints: [
543
+ ...(options.treeShaking?.entryPoints ?? []),
544
+ ...exportedEntryPoints,
545
+ ...onAttrEntryPoints,
546
+ ],
547
+ });
548
+ }
549
+ else {
550
+ shakingResult = applyTreeShaking(rawIR, options.target, {
551
+ enabled: options.treeShaking?.enabled ?? true,
552
+ keepUnusedEnums: true,
553
+ keepUnusedClasses: options.treeShaking?.keepUnusedClasses,
554
+ keepUnusedTypeAliases: options.treeShaking?.keepUnusedTypeAliases,
555
+ reportUnused: options.treeShaking?.reportUnused,
556
+ keepUnusedVariables: true,
557
+ // Merge exported entry points so cross-module imports aren't shaken out,
558
+ // plus on:* HTML-attribute handler refs (named fns not called in TS).
559
+ entryPoints: [
560
+ ...(options.treeShaking?.entryPoints ?? []),
561
+ ...exportedEntryPoints,
562
+ ...onAttrEntryPoints,
563
+ ],
564
+ });
565
+ }
566
+ const programIR = shakingResult.programIR;
567
+ allRemovedSymbols.push(...shakingResult.removedSymbols);
568
+ profiler.endTimer(`tree-shake:${fileBasename}`);
569
+ preBuiltArray.push({ filePath, programIR, npmPackage });
570
+ }
571
+ const preBuilt = new Map();
572
+ for (const item of preBuiltArray) {
573
+ preBuilt.set(item.filePath, item);
574
+ }
575
+ // Collect ALL enum IRs from ALL files and register them before emitting.
576
+ // This makes the property-access renderer and struct field type inference
577
+ // aware of every enum type (including its member values for AVR range checks)
578
+ // regardless of which file it's defined in or what order files are emitted.
579
+ const allEnumIRs = [];
580
+ const allEnumNames = new Set();
581
+ const allStringEnumNames = new Set();
582
+ // Cross-file symbol/type aggregation. This used to be ~90 lines of hand-rolled
583
+ // loops building allClassFieldTypes / allClassAccessors /
584
+ // allFunctionReturnTypes / allVariableTypes / allClassNames in two passes
585
+ // (own fields, then extendsClass inheritance). It is now a single
586
+ // SymbolTable built per file, merged across files, with inheritance resolved
587
+ // once — see ir/symbol-table.ts. The projected maps below keep the exact
588
+ // names/shapes the emit phase consumes, so this is a behavior-neutral swap.
589
+ const crossModuleTable = createSymbolTable();
590
+ for (const { programIR } of preBuilt.values()) {
591
+ for (const e of programIR.enums) {
592
+ allEnumIRs.push(e);
593
+ allEnumNames.add(e.name);
594
+ if (isStringEnum(e))
595
+ allStringEnumNames.add(e.name);
596
+ }
597
+ for (const ns of programIR.namespaces) {
598
+ for (const e of ns.enums) {
599
+ allEnumIRs.push(e);
600
+ allEnumNames.add(e.name);
601
+ if (isStringEnum(e))
602
+ allStringEnumNames.add(e.name);
603
+ }
604
+ }
605
+ mergeSymbolTable(crossModuleTable, buildSymbolTable(programIR));
606
+ }
607
+ resolveInheritance(crossModuleTable);
608
+ const allClassNames = crossModuleTable.classNames;
609
+ const allClassFieldTypes = crossModuleTable.classFieldTypes;
610
+ const allClassAccessors = crossModuleTable.classAccessors;
611
+ const allFunctionReturnTypes = crossModuleTable.functionReturnTypes;
612
+ const allVariableTypes = crossModuleTable.variableTypes;
613
+ profiler.startTimer("emit:register-enums");
614
+ registerAllEnumNames(allEnumIRs);
615
+ profiler.endTimer("emit:register-enums");
616
+ // ── Pass 2: emit (only for files that needed retranspilation) ─────────────
617
+ profiler.startTimer("emit:all");
618
+ profiler.captureMemorySnapshot("emit:pre");
619
+ for (const [filePath, { programIR, npmPackage }] of preBuilt) {
620
+ const fileBasename = path.basename(filePath);
621
+ profiler.startTimer(`emit:file:${fileBasename}`);
622
+ const emitOptions = {
623
+ outDir,
624
+ emitMode: options.emitMode,
625
+ target: options.target,
626
+ libdefs: definitions,
627
+ emitMaps: options.emitMaps,
628
+ platformContext: options.platformContext,
629
+ npmPackage,
630
+ npmPackages,
631
+ isEntryFile: filePath === entryFile,
632
+ nativeModules: graphResult.nativeModules,
633
+ crossModuleClasses: allClassNames,
634
+ crossModuleClassFieldTypes: allClassFieldTypes,
635
+ crossModuleClassAccessors: allClassAccessors,
636
+ crossModuleFunctionReturnTypes: allFunctionReturnTypes,
637
+ crossModuleEnumNames: allEnumNames,
638
+ crossModuleStringEnumNames: allStringEnumNames,
639
+ crossModuleVariableTypes: allVariableTypes,
640
+ };
641
+ // Pass the already-resolved strategy (framework-loaded or target-based)
642
+ emitOptions.strategy = strategy;
643
+ const emitted = emitCpp(programIR, emitOptions);
644
+ diagnostics.push(...emitted.diagnostics);
645
+ throwIfFatalDiagnostics(emitted.diagnostics.map((diagnostic) => ({ filePath, diagnostic })));
646
+ if (filePath === entryFile) {
647
+ entryOutputs = emitted;
648
+ }
649
+ profiler.endTimer(`emit:file:${fileBasename}`);
650
+ }
651
+ profiler.captureMemorySnapshot("emit:post");
652
+ profiler.endTimer("emit:all");
653
+ // The entry file is always emitted above (incremental builds are disabled,
654
+ // so every graph file is processed). Guard against the impossible case where
655
+ // emit somehow produced no entry output.
656
+ if (!entryOutputs) {
657
+ throw new Error(`Unable to transpile entry file '${entryFile}'.`);
658
+ }
659
+ // ── Copy native C++ modules to output ─────────────────────────────────────
660
+ profiler.startTimer("post:native-modules");
661
+ const nativeModuleOutputs = [];
662
+ for (const [moduleSpecifier, nativeModule] of graphResult.nativeModules) {
663
+ // Skip copying — Arduino's library system provides both .h and .cpp.
664
+ // Copying either causes conflicts: the .cpp merges into the .ino (duplicate
665
+ // definitions), and the .h shadows the library's own header (link failures).
666
+ // The gen-decls .d.ts files are sufficient for TypeScript type-checking.
667
+ info(`Native module (library-managed): ${moduleSpecifier}`);
668
+ }
669
+ profiler.endTimer("post:native-modules");
670
+ profiler.startTimer("post:flatten");
671
+ if (hasLoadedFramework()) {
672
+ const { toolchain } = getLoadedFramework();
673
+ if (toolchain?.prepare) {
674
+ try {
675
+ toolchain.prepare(path.dirname(entryOutputs.sourcePath), entryOutputs.sourcePath);
676
+ }
677
+ catch (e) {
678
+ if (process.env.CUTTLEFISH_DEBUG)
679
+ console.error("[transpile] Toolchain prepare failed:", e);
680
+ }
681
+ }
682
+ }
683
+ profiler.endTimer("post:flatten");
684
+ // Profiler session ends (profiling disabled - no report generation)
685
+ // ── Generate diagnostics report if enabled ──────────────────────────────
686
+ if (options.diagnostics) {
687
+ try {
688
+ const entryPreBuilt = preBuilt.get(entryFile);
689
+ const report = buildDiagnosticsReport({
690
+ entryFile,
691
+ program: entryPreBuilt?.programIR ?? null,
692
+ diagnostics,
693
+ asyncTaskNames: entryOutputs.asyncTaskNames ?? [],
694
+ usesTimers: entryOutputs.usesTimers ?? false,
695
+ target: options.target,
696
+ boardPackage: options.boardPackage,
697
+ frameworkPackage: options.frameworkPackage,
698
+ outDir,
699
+ outputFile: entryOutputs.sourcePath,
700
+ preBuilt,
701
+ profiler,
702
+ removedSymbols: allRemovedSymbols,
703
+ });
704
+ writeDiagnosticsReport(report, entryPreBuilt?.programIR ?? null, outDir);
705
+ }
706
+ catch (e) {
707
+ // Diagnostics report generation is best-effort; don't fail the build
708
+ if (options.debug) {
709
+ logDebug(`Diagnostics report generation failed: ${e instanceof Error ? e.message : String(e)}`, true);
710
+ }
711
+ }
712
+ }
713
+ return {
714
+ ...entryOutputs,
715
+ diagnostics,
716
+ };
717
+ }
718
+ export function generateLibraryDefinitions(options) {
719
+ const sourceText = readText(options.inputFile);
720
+ const programIR = buildProgramIR(options.inputFile, sourceText);
721
+ return generateLibdefStubs(options.inputFile, programIR.imports, options.outDir);
722
+ }