@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.
- package/LICENSE +21 -0
- package/README.md +79 -0
- package/dist/api/board-types.d.ts +1 -0
- package/dist/api/board-types.js +4 -0
- package/dist/api/capabilities.d.ts +12 -0
- package/dist/api/capabilities.js +1 -0
- package/dist/api/config.d.ts +120 -0
- package/dist/api/config.js +7 -0
- package/dist/api/index.d.ts +5 -0
- package/dist/api/index.js +2 -0
- package/dist/api/schema/builder.d.ts +14 -0
- package/dist/api/schema/builder.js +15 -0
- package/dist/api/schema/index.d.ts +2 -0
- package/dist/api/schema/index.js +1 -0
- package/dist/api/schema/types.d.ts +257 -0
- package/dist/api/schema/types.js +4 -0
- package/dist/api/shared/async-runtime-static.d.ts +8 -0
- package/dist/api/shared/async-runtime-static.js +201 -0
- package/dist/api/shared/async-types.d.ts +42 -0
- package/dist/api/shared/async-types.js +8 -0
- package/dist/api/shared/board-resolver.d.ts +12 -0
- package/dist/api/shared/board-resolver.js +7 -0
- package/dist/api/shared/cpp-type-ir.d.ts +229 -0
- package/dist/api/shared/cpp-type-ir.js +672 -0
- package/dist/api/shared/display-adapter.d.ts +12 -0
- package/dist/api/shared/display-adapter.js +145 -0
- package/dist/api/shared/display-adapters/eink-mono.d.ts +2 -0
- package/dist/api/shared/display-adapters/eink-mono.js +53 -0
- package/dist/api/shared/display-adapters/sdl.d.ts +2 -0
- package/dist/api/shared/display-adapters/sdl.js +452 -0
- package/dist/api/shared/display-adapters/ssd1309.d.ts +2 -0
- package/dist/api/shared/display-adapters/ssd1309.js +136 -0
- package/dist/api/shared/display-adapters/st7796.d.ts +2 -0
- package/dist/api/shared/display-adapters/st7796.js +132 -0
- package/dist/api/shared/display-capabilities.d.ts +45 -0
- package/dist/api/shared/display-capabilities.js +54 -0
- package/dist/api/shared/display-op-ir.d.ts +62 -0
- package/dist/api/shared/display-op-ir.js +19 -0
- package/dist/api/shared/display-profile.d.ts +265 -0
- package/dist/api/shared/display-profile.js +338 -0
- package/dist/api/shared/graphics-strategy.d.ts +29 -0
- package/dist/api/shared/graphics-strategy.js +11 -0
- package/dist/api/shared/hal-op-ir.d.ts +393 -0
- package/dist/api/shared/hal-op-ir.js +18 -0
- package/dist/api/shared/index.d.ts +26 -0
- package/dist/api/shared/index.js +21 -0
- package/dist/api/shared/ir-core.d.ts +363 -0
- package/dist/api/shared/ir-core.js +8 -0
- package/dist/api/shared/ir-declarations.d.ts +201 -0
- package/dist/api/shared/ir-declarations.js +26 -0
- package/dist/api/shared/ir.d.ts +85 -0
- package/dist/api/shared/ir.js +13 -0
- package/dist/api/shared/platform-strategy.d.ts +402 -0
- package/dist/api/shared/platform-strategy.js +14 -0
- package/dist/api/shared/polyfill-helper-registry.d.ts +11 -0
- package/dist/api/shared/polyfill-helper-registry.js +86 -0
- package/dist/api/shared/polyfill-types.d.ts +33 -0
- package/dist/api/shared/polyfill-types.js +76 -0
- package/dist/api/shared/promise-runtime.d.ts +10 -0
- package/dist/api/shared/promise-runtime.js +244 -0
- package/dist/api/shared/snprintf-types.d.ts +29 -0
- package/dist/api/shared/snprintf-types.js +8 -0
- package/dist/api/shared/string-method-registry.d.ts +53 -0
- package/dist/api/shared/string-method-registry.js +168 -0
- package/dist/api/shared/toolchain-types.d.ts +63 -0
- package/dist/api/shared/toolchain-types.js +71 -0
- package/dist/api/shared/types.d.ts +50 -0
- package/dist/api/shared/types.js +6 -0
- package/dist/ast/parse.d.ts +2 -0
- package/dist/ast/parse.js +4 -0
- package/dist/cache.d.ts +26 -0
- package/dist/cache.js +95 -0
- package/dist/cli-utils.d.ts +37 -0
- package/dist/cli-utils.js +250 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +635 -0
- package/dist/config-loader.d.ts +78 -0
- package/dist/config-loader.js +508 -0
- package/dist/config-schema.d.ts +233 -0
- package/dist/config-schema.js +83 -0
- package/dist/create/board-checklist.d.ts +2 -0
- package/dist/create/board-checklist.js +52 -0
- package/dist/create/board-codegen.d.ts +9 -0
- package/dist/create/board-codegen.js +238 -0
- package/dist/create/board-generators.d.ts +12 -0
- package/dist/create/board-generators.js +652 -0
- package/dist/create/board-spec.d.ts +640 -0
- package/dist/create/board-spec.js +215 -0
- package/dist/create/eslint-rules-template.d.ts +2 -0
- package/dist/create/eslint-rules-template.js +672 -0
- package/dist/create/index.d.ts +11 -0
- package/dist/create/index.js +8 -0
- package/dist/create/init-scaffold.d.ts +27 -0
- package/dist/create/init-scaffold.js +150 -0
- package/dist/create/init-templates.d.ts +24 -0
- package/dist/create/init-templates.js +379 -0
- package/dist/create/init-wizard.d.ts +8 -0
- package/dist/create/init-wizard.js +168 -0
- package/dist/debug/breakpoint-loader.d.ts +24 -0
- package/dist/debug/breakpoint-loader.js +137 -0
- package/dist/debug/index.d.ts +3 -0
- package/dist/debug/index.js +7 -0
- package/dist/debug/preprocessor.d.ts +9 -0
- package/dist/debug/preprocessor.js +214 -0
- package/dist/debug/types.d.ts +32 -0
- package/dist/debug/types.js +6 -0
- package/dist/diagnostics/diagnostics-report.d.ts +51 -0
- package/dist/diagnostics/diagnostics-report.js +333 -0
- package/dist/diagnostics/json-schema.d.ts +138 -0
- package/dist/diagnostics/json-schema.js +27 -0
- package/dist/diagnostics/md-writer.d.ts +10 -0
- package/dist/diagnostics/md-writer.js +336 -0
- package/dist/diagnostics/mermaid-builder.d.ts +65 -0
- package/dist/diagnostics/mermaid-builder.js +681 -0
- package/dist/emit/cpp-emitter.d.ts +6 -0
- package/dist/emit/cpp-emitter.js +48 -0
- package/dist/emit/emitters/class-emitter.d.ts +2 -0
- package/dist/emit/emitters/class-emitter.js +523 -0
- package/dist/emit/emitters/emitter-context.d.ts +174 -0
- package/dist/emit/emitters/emitter-context.js +1 -0
- package/dist/emit/emitters/entrypoint-synthesizer.d.ts +2 -0
- package/dist/emit/emitters/entrypoint-synthesizer.js +175 -0
- package/dist/emit/emitters/function-emitter-impl.d.ts +19 -0
- package/dist/emit/emitters/function-emitter-impl.js +428 -0
- package/dist/emit/emitters/line-appender.d.ts +15 -0
- package/dist/emit/emitters/line-appender.js +344 -0
- package/dist/emit/emitters/namespace-emitter.d.ts +2 -0
- package/dist/emit/emitters/namespace-emitter.js +324 -0
- package/dist/emit/emitters/output-finalizer.d.ts +4 -0
- package/dist/emit/emitters/output-finalizer.js +267 -0
- package/dist/emit/emitters/setup.d.ts +3 -0
- package/dist/emit/emitters/setup.js +851 -0
- package/dist/emit/emitters/top-level-prep.d.ts +2 -0
- package/dist/emit/emitters/top-level-prep.js +706 -0
- package/dist/emit/emitters/type-decl-emitter.d.ts +2 -0
- package/dist/emit/emitters/type-decl-emitter.js +251 -0
- package/dist/emit/emitters/ui-emitter.d.ts +21 -0
- package/dist/emit/emitters/ui-emitter.js +564 -0
- package/dist/emit/enum-emitter.d.ts +1 -0
- package/dist/emit/enum-emitter.js +1 -0
- package/dist/emit/expression-renderer.d.ts +283 -0
- package/dist/emit/expression-renderer.js +1477 -0
- package/dist/emit/native-helpers-emitter.d.ts +11 -0
- package/dist/emit/native-helpers-emitter.js +34 -0
- package/dist/emit/route-hal-op.d.ts +5 -0
- package/dist/emit/route-hal-op.js +13 -0
- package/dist/emit/snprintf-helpers.d.ts +17 -0
- package/dist/emit/snprintf-helpers.js +462 -0
- package/dist/emit/statement-renderer.d.ts +189 -0
- package/dist/emit/statement-renderer.js +948 -0
- package/dist/emit/utils/async-state-machine.d.ts +42 -0
- package/dist/emit/utils/async-state-machine.js +343 -0
- package/dist/emit/utils/comment-helpers.d.ts +20 -0
- package/dist/emit/utils/comment-helpers.js +31 -0
- package/dist/emit/utils/cpp-helpers.d.ts +47 -0
- package/dist/emit/utils/cpp-helpers.js +85 -0
- package/dist/emit/utils/include-resolver.d.ts +58 -0
- package/dist/emit/utils/include-resolver.js +155 -0
- package/dist/emit/utils/index.d.ts +8 -0
- package/dist/emit/utils/index.js +12 -0
- package/dist/emit/utils/type-inference.d.ts +159 -0
- package/dist/emit/utils/type-inference.js +559 -0
- package/dist/eslint-check.d.ts +10 -0
- package/dist/eslint-check.js +102 -0
- package/dist/framework-package.d.ts +10 -0
- package/dist/framework-package.js +58 -0
- package/dist/framework-registry.d.ts +39 -0
- package/dist/framework-registry.js +30 -0
- package/dist/incremental-cache.d.ts +116 -0
- package/dist/incremental-cache.js +335 -0
- package/dist/ir/adc-range-validation.d.ts +11 -0
- package/dist/ir/adc-range-validation.js +250 -0
- package/dist/ir/ast-node-utils.d.ts +8 -0
- package/dist/ir/ast-node-utils.js +32 -0
- package/dist/ir/board-pin-utils.d.ts +13 -0
- package/dist/ir/board-pin-utils.js +65 -0
- package/dist/ir/board-resolver.d.ts +36 -0
- package/dist/ir/board-resolver.js +437 -0
- package/dist/ir/build-ir-state.d.ts +167 -0
- package/dist/ir/build-ir-state.js +306 -0
- package/dist/ir/build-ir.d.ts +2 -0
- package/dist/ir/build-ir.js +808 -0
- package/dist/ir/call-graph.d.ts +35 -0
- package/dist/ir/call-graph.js +194 -0
- package/dist/ir/compile-time-only.d.ts +2 -0
- package/dist/ir/compile-time-only.js +16 -0
- package/dist/ir/declaration-builders.d.ts +9 -0
- package/dist/ir/declaration-builders.js +608 -0
- package/dist/ir/entry-points.d.ts +30 -0
- package/dist/ir/entry-points.js +191 -0
- package/dist/ir/expression-to-ir.d.ts +5 -0
- package/dist/ir/expression-to-ir.js +2249 -0
- package/dist/ir/extract-property-chain.d.ts +10 -0
- package/dist/ir/extract-property-chain.js +30 -0
- package/dist/ir/feature-prescan.d.ts +3 -0
- package/dist/ir/feature-prescan.js +43 -0
- package/dist/ir/feature-registry.d.ts +30 -0
- package/dist/ir/feature-registry.js +816 -0
- package/dist/ir/filter.d.ts +7 -0
- package/dist/ir/filter.js +85 -0
- package/dist/ir/function-builder.d.ts +19 -0
- package/dist/ir/function-builder.js +670 -0
- package/dist/ir/hal/hal-emitter.d.ts +62 -0
- package/dist/ir/hal/hal-emitter.js +735 -0
- package/dist/ir/hal/hal-parser.d.ts +61 -0
- package/dist/ir/hal/hal-parser.js +473 -0
- package/dist/ir/hal/hal-plugins.d.ts +34 -0
- package/dist/ir/hal/hal-plugins.js +706 -0
- package/dist/ir/hal-resolver.d.ts +5 -0
- package/dist/ir/hal-resolver.js +5 -0
- package/dist/ir/heap-analysis.d.ts +8 -0
- package/dist/ir/heap-analysis.js +350 -0
- package/dist/ir/heap-array-validation.d.ts +24 -0
- package/dist/ir/heap-array-validation.js +29 -0
- package/dist/ir/identifier-collector.d.ts +18 -0
- package/dist/ir/identifier-collector.js +439 -0
- package/dist/ir/interrupt-analysis.d.ts +39 -0
- package/dist/ir/interrupt-analysis.js +467 -0
- package/dist/ir/memory-budget-validation.d.ts +10 -0
- package/dist/ir/memory-budget-validation.js +86 -0
- package/dist/ir/namespace-builder.d.ts +7 -0
- package/dist/ir/namespace-builder.js +152 -0
- package/dist/ir/ownership-analysis.d.ts +9 -0
- package/dist/ir/ownership-analysis.js +1207 -0
- package/dist/ir/peripheral-ownership.d.ts +3 -0
- package/dist/ir/peripheral-ownership.js +152 -0
- package/dist/ir/peripheral-symbols.d.ts +7 -0
- package/dist/ir/peripheral-symbols.js +57 -0
- package/dist/ir/peripheral-usage.d.ts +54 -0
- package/dist/ir/peripheral-usage.js +691 -0
- package/dist/ir/peripheral-validation.d.ts +8 -0
- package/dist/ir/peripheral-validation.js +77 -0
- package/dist/ir/pin-alias-conflict.d.ts +4 -0
- package/dist/ir/pin-alias-conflict.js +34 -0
- package/dist/ir/pin-capability-validation.d.ts +6 -0
- package/dist/ir/pin-capability-validation.js +413 -0
- package/dist/ir/pin-mode-validation.d.ts +10 -0
- package/dist/ir/pin-mode-validation.js +301 -0
- package/dist/ir/pin-safety.d.ts +11 -0
- package/dist/ir/pin-safety.js +58 -0
- package/dist/ir/program-analysis.d.ts +44 -0
- package/dist/ir/program-analysis.js +589 -0
- package/dist/ir/pulldown-validation.d.ts +7 -0
- package/dist/ir/pulldown-validation.js +49 -0
- package/dist/ir/pwm-timer-sharing.d.ts +4 -0
- package/dist/ir/pwm-timer-sharing.js +93 -0
- package/dist/ir/reachability.d.ts +68 -0
- package/dist/ir/reachability.js +224 -0
- package/dist/ir/register-decorators.d.ts +6 -0
- package/dist/ir/register-decorators.js +49 -0
- package/dist/ir/render-expr.d.ts +4 -0
- package/dist/ir/render-expr.js +127 -0
- package/dist/ir/resource-analysis.d.ts +19 -0
- package/dist/ir/resource-analysis.js +153 -0
- package/dist/ir/statement-to-ir.d.ts +23 -0
- package/dist/ir/statement-to-ir.js +349 -0
- package/dist/ir/symbol-table.d.ts +68 -0
- package/dist/ir/symbol-table.js +179 -0
- package/dist/ir/symbol-types.d.ts +56 -0
- package/dist/ir/symbol-types.js +107 -0
- package/dist/ir/timer0-pwm-timing-conflict.d.ts +4 -0
- package/dist/ir/timer0-pwm-timing-conflict.js +71 -0
- package/dist/ir/timing-validation.d.ts +7 -0
- package/dist/ir/timing-validation.js +95 -0
- package/dist/ir/transformers/array-methods.d.ts +8 -0
- package/dist/ir/transformers/array-methods.js +493 -0
- package/dist/ir/transformers/call-statement.d.ts +5 -0
- package/dist/ir/transformers/call-statement.js +758 -0
- package/dist/ir/transformers/callback-context-registry.d.ts +20 -0
- package/dist/ir/transformers/callback-context-registry.js +55 -0
- package/dist/ir/transformers/canvas-lowering.d.ts +42 -0
- package/dist/ir/transformers/canvas-lowering.js +249 -0
- package/dist/ir/transformers/control-flow.d.ts +8 -0
- package/dist/ir/transformers/control-flow.js +508 -0
- package/dist/ir/transformers/expressions.d.ts +7 -0
- package/dist/ir/transformers/expressions.js +406 -0
- package/dist/ir/transformers/hal-call-resolver.d.ts +29 -0
- package/dist/ir/transformers/hal-call-resolver.js +274 -0
- package/dist/ir/transformers/hal-emit-helpers.d.ts +14 -0
- package/dist/ir/transformers/hal-emit-helpers.js +63 -0
- package/dist/ir/transformers/namespace-methods.d.ts +20 -0
- package/dist/ir/transformers/namespace-methods.js +110 -0
- package/dist/ir/transformers/register-assignment.d.ts +5 -0
- package/dist/ir/transformers/register-assignment.js +56 -0
- package/dist/ir/transformers/ui-call-resolver.d.ts +149 -0
- package/dist/ir/transformers/ui-call-resolver.js +1136 -0
- package/dist/ir/transformers/ui-callback-lowering.d.ts +62 -0
- package/dist/ir/transformers/ui-callback-lowering.js +623 -0
- package/dist/ir/transformers/ui-mount.d.ts +20 -0
- package/dist/ir/transformers/ui-mount.js +35 -0
- package/dist/ir/transformers/ui-reactive.d.ts +68 -0
- package/dist/ir/transformers/ui-reactive.js +129 -0
- package/dist/ir/transformers/variables.d.ts +14 -0
- package/dist/ir/transformers/variables.js +1045 -0
- package/dist/ir/try-catch-validation.d.ts +26 -0
- package/dist/ir/try-catch-validation.js +128 -0
- package/dist/ir/type-resolution.d.ts +31 -0
- package/dist/ir/type-resolution.js +977 -0
- package/dist/ir/ui-element-auto-wire.d.ts +50 -0
- package/dist/ir/ui-element-auto-wire.js +218 -0
- package/dist/ir/unit-suspicion-validation.d.ts +12 -0
- package/dist/ir/unit-suspicion-validation.js +296 -0
- package/dist/ir/utils/walk-ir.d.ts +6 -0
- package/dist/ir/utils/walk-ir.js +162 -0
- package/dist/ir/validation-orchestrator.d.ts +4 -0
- package/dist/ir/validation-orchestrator.js +45 -0
- package/dist/libdef/base-class-resolver.d.ts +23 -0
- package/dist/libdef/base-class-resolver.js +50 -0
- package/dist/libdef/cpp-to-decl.d.ts +34 -0
- package/dist/libdef/cpp-to-decl.js +584 -0
- package/dist/libdef/header-parser.d.ts +53 -0
- package/dist/libdef/header-parser.js +269 -0
- package/dist/libdef/registry.d.ts +10 -0
- package/dist/libdef/registry.js +142 -0
- package/dist/mapping/peripheral-names.d.ts +23 -0
- package/dist/mapping/peripheral-names.js +63 -0
- package/dist/mapping/source-map.d.ts +46 -0
- package/dist/mapping/source-map.js +207 -0
- package/dist/orchestrator/dts-generator.d.ts +17 -0
- package/dist/orchestrator/dts-generator.js +83 -0
- package/dist/orchestrator/graph-builder.d.ts +18 -0
- package/dist/orchestrator/graph-builder.js +218 -0
- package/dist/orchestrator/semantic-facts-verifier.d.ts +38 -0
- package/dist/orchestrator/semantic-facts-verifier.js +121 -0
- package/dist/orchestrator/semantic-facts.d.ts +141 -0
- package/dist/orchestrator/semantic-facts.js +652 -0
- package/dist/orchestrator/type-checker.d.ts +39 -0
- package/dist/orchestrator/type-checker.js +760 -0
- package/dist/platform/async-runtime.d.ts +8 -0
- package/dist/platform/async-runtime.js +26 -0
- package/dist/platform/generic-strategy.d.ts +101 -0
- package/dist/platform/generic-strategy.js +289 -0
- package/dist/platform/index.d.ts +4 -0
- package/dist/platform/index.js +9 -0
- package/dist/platform/registry.d.ts +21 -0
- package/dist/platform/registry.js +42 -0
- package/dist/platform/toolchain.d.ts +4 -0
- package/dist/platform/toolchain.js +43 -0
- package/dist/preview/client.d.ts +1 -0
- package/dist/preview/client.js +137 -0
- package/dist/preview/server.d.ts +5 -0
- package/dist/preview/server.js +199 -0
- package/dist/profiler/index.d.ts +28 -0
- package/dist/profiler/index.js +28 -0
- package/dist/profiler/profiler.d.ts +95 -0
- package/dist/profiler/profiler.js +338 -0
- package/dist/profiler/types.d.ts +87 -0
- package/dist/profiler/types.js +12 -0
- package/dist/stores/display-profile-store.d.ts +16 -0
- package/dist/stores/display-profile-store.js +48 -0
- package/dist/stores/theme-store.d.ts +13 -0
- package/dist/stores/theme-store.js +30 -0
- package/dist/testing.d.ts +45 -0
- package/dist/testing.js +47 -0
- package/dist/transpile/resolution.d.ts +29 -0
- package/dist/transpile/resolution.js +282 -0
- package/dist/transpile.d.ts +3 -0
- package/dist/transpile.js +722 -0
- package/dist/types.d.ts +217 -0
- package/dist/types.js +1 -0
- package/dist/ui/ui-bridge.d.ts +4 -0
- package/dist/ui/ui-bridge.js +34 -0
- package/dist/ui-hook.d.ts +84 -0
- package/dist/ui-hook.js +45 -0
- package/dist/utils/cli.d.ts +3 -0
- package/dist/utils/cli.js +463 -0
- package/dist/utils/fs.d.ts +4 -0
- package/dist/utils/fs.js +23 -0
- package/dist/utils/logger.d.ts +2 -0
- package/dist/utils/logger.js +8 -0
- package/dist/utils/strings.d.ts +35 -0
- package/dist/utils/strings.js +142 -0
- package/dist/utils/ui.d.ts +66 -0
- package/dist/utils/ui.js +149 -0
- package/dist/watch.d.ts +35 -0
- package/dist/watch.js +111 -0
- 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
|
+
}
|