@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,584 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* C++ to TypeScript Declaration Generator
|
|
3
|
+
*
|
|
4
|
+
* Parses simple C++ class definitions and generates .d.ts declaration files
|
|
5
|
+
* for use with TypeCAD's native module support.
|
|
6
|
+
*/
|
|
7
|
+
import fs from "node:fs";
|
|
8
|
+
import path from "node:path";
|
|
9
|
+
import { parseHeader } from "./header-parser.js";
|
|
10
|
+
import { buildClassIndex, BaseClassResolver } from "./base-class-resolver.js";
|
|
11
|
+
/**
|
|
12
|
+
* Maps C++ types to TypeScript types
|
|
13
|
+
*/
|
|
14
|
+
function mapCppTypeToTs(cppType) {
|
|
15
|
+
const trimmed = cppType.trim();
|
|
16
|
+
// Remove const qualifier
|
|
17
|
+
const withoutConst = trimmed.replace(/^const\s+/, "");
|
|
18
|
+
// Basic type mappings
|
|
19
|
+
const typeMap = {
|
|
20
|
+
"int": "number",
|
|
21
|
+
"unsigned int": "number",
|
|
22
|
+
"uint8_t": "number",
|
|
23
|
+
"uint16_t": "number",
|
|
24
|
+
"uint32_t": "number",
|
|
25
|
+
"int8_t": "number",
|
|
26
|
+
"int16_t": "number",
|
|
27
|
+
"int32_t": "number",
|
|
28
|
+
"float": "number",
|
|
29
|
+
"double": "number",
|
|
30
|
+
"bool": "boolean",
|
|
31
|
+
"void": "void",
|
|
32
|
+
"char": "string",
|
|
33
|
+
"char*": "string",
|
|
34
|
+
"const char*": "string",
|
|
35
|
+
"std::string": "string",
|
|
36
|
+
"String": "string",
|
|
37
|
+
};
|
|
38
|
+
// Check direct mapping
|
|
39
|
+
if (typeMap[withoutConst]) {
|
|
40
|
+
return typeMap[withoutConst];
|
|
41
|
+
}
|
|
42
|
+
// Handle pointers (treat as number for simplicity)
|
|
43
|
+
if (withoutConst.endsWith("*")) {
|
|
44
|
+
return "number";
|
|
45
|
+
}
|
|
46
|
+
// Handle references
|
|
47
|
+
if (withoutConst.endsWith("&")) {
|
|
48
|
+
const baseType = withoutConst.slice(0, -1).trim();
|
|
49
|
+
return mapCppTypeToTs(baseType);
|
|
50
|
+
}
|
|
51
|
+
// Default to any for unknown types
|
|
52
|
+
return "any";
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Extracts the parameter list from a function signature
|
|
56
|
+
*/
|
|
57
|
+
function parseParameters(paramString) {
|
|
58
|
+
if (!paramString.trim()) {
|
|
59
|
+
return [];
|
|
60
|
+
}
|
|
61
|
+
const params = [];
|
|
62
|
+
const parts = paramString.split(",");
|
|
63
|
+
for (const part of parts) {
|
|
64
|
+
const trimmed = part.trim();
|
|
65
|
+
if (!trimmed)
|
|
66
|
+
continue;
|
|
67
|
+
// Handle default values (remove them for the declaration)
|
|
68
|
+
const withoutDefault = trimmed.split("=")[0].trim();
|
|
69
|
+
// Split into tokens to find type and name
|
|
70
|
+
const tokens = withoutDefault.split(/\s+/);
|
|
71
|
+
if (tokens.length >= 2) {
|
|
72
|
+
// C++ lets pointer/reference markers attach to either the type
|
|
73
|
+
// (`char* label`) or the name (`char *label`), and arrays appear as a
|
|
74
|
+
// trailing `[]` on the name. Peel leading `*`/`&` and trailing `[]`
|
|
75
|
+
// off the name token back onto the type, so the emitted name is a
|
|
76
|
+
// valid TS identifier and the type still routes through
|
|
77
|
+
// mapCppTypeToTs (which maps pointers/refs appropriately).
|
|
78
|
+
let name = tokens[tokens.length - 1];
|
|
79
|
+
const typeParts = tokens.slice(0, -1);
|
|
80
|
+
const prefix = [];
|
|
81
|
+
while (/^[*&]/.test(name)) {
|
|
82
|
+
prefix.push(name[0]);
|
|
83
|
+
name = name.slice(1);
|
|
84
|
+
}
|
|
85
|
+
let arrayMarker = "";
|
|
86
|
+
if (/\[\]$/.test(name)) {
|
|
87
|
+
arrayMarker = "*";
|
|
88
|
+
name = name.replace(/\[\]$/, "");
|
|
89
|
+
}
|
|
90
|
+
if (prefix.length > 0 || arrayMarker) {
|
|
91
|
+
typeParts.push(prefix.join("") + arrayMarker);
|
|
92
|
+
}
|
|
93
|
+
const type = typeParts.join(" ").trim();
|
|
94
|
+
params.push({ type: mapCppTypeToTs(type), name });
|
|
95
|
+
}
|
|
96
|
+
else if (tokens.length === 1) {
|
|
97
|
+
// Just a type (unnamed parameter)
|
|
98
|
+
params.push({ type: mapCppTypeToTs(tokens[0]), name: "" });
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return params;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Parses a C++ class definition
|
|
105
|
+
*/
|
|
106
|
+
function parseCppClass(content) {
|
|
107
|
+
const result = {
|
|
108
|
+
classes: [],
|
|
109
|
+
constants: [],
|
|
110
|
+
};
|
|
111
|
+
// Parse top-level constants: const int NAME = value;
|
|
112
|
+
const constRegex = /const\s+(\w+)\s+(\w+)\s*=\s*([^;]+);/g;
|
|
113
|
+
let constMatch;
|
|
114
|
+
while ((constMatch = constRegex.exec(content)) !== null) {
|
|
115
|
+
result.constants.push({
|
|
116
|
+
type: mapCppTypeToTs(constMatch[1]),
|
|
117
|
+
name: constMatch[2],
|
|
118
|
+
value: constMatch[3].trim(),
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
// Parse methods with scope resolution (ClassName::methodName) - for .cpp implementation files
|
|
122
|
+
// This handles cases where the class is defined in a header but implemented in .cpp
|
|
123
|
+
const scopeResolutionRegex = /(?:^|\n)\s*(?:(\w+(?:\s*[*&])?)\s+)?(\w+)::(\w+)\s*\(([^)]*)\)\s*(?:const\s*)?(?:\{|;)/g;
|
|
124
|
+
let scopeMatch;
|
|
125
|
+
const classesFromImpl = new Map();
|
|
126
|
+
while ((scopeMatch = scopeResolutionRegex.exec(content)) !== null) {
|
|
127
|
+
const returnType = scopeMatch[1]?.trim();
|
|
128
|
+
const className = scopeMatch[2];
|
|
129
|
+
const methodName = scopeMatch[3];
|
|
130
|
+
const params = scopeMatch[4];
|
|
131
|
+
// Skip if this looks like a namespace (e.g., std::something)
|
|
132
|
+
if (!returnType && className.toLowerCase() === className) {
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
if (!classesFromImpl.has(className)) {
|
|
136
|
+
classesFromImpl.set(className, {
|
|
137
|
+
name: className,
|
|
138
|
+
methods: [],
|
|
139
|
+
constructors: [],
|
|
140
|
+
source: "cpp",
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
const cppClass = classesFromImpl.get(className);
|
|
144
|
+
// Check if this is a constructor (method name matches class name)
|
|
145
|
+
if (methodName === className) {
|
|
146
|
+
cppClass.constructors.push({
|
|
147
|
+
parameters: parseParameters(params),
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
else if (returnType) {
|
|
151
|
+
cppClass.methods.push({
|
|
152
|
+
returnType: mapCppTypeToTs(returnType),
|
|
153
|
+
name: methodName,
|
|
154
|
+
parameters: parseParameters(params),
|
|
155
|
+
isPublic: true, // Assume public for implementation files
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
// Add inferred classes to result
|
|
160
|
+
for (const cppClass of classesFromImpl.values()) {
|
|
161
|
+
result.classes.push(cppClass);
|
|
162
|
+
}
|
|
163
|
+
// Parse class definitions - handle nested braces properly
|
|
164
|
+
// Find class keyword and then find matching closing brace
|
|
165
|
+
const classStartRegex = /\bclass\s+(\w+)\s*\{/g;
|
|
166
|
+
let classStartMatch;
|
|
167
|
+
while ((classStartMatch = classStartRegex.exec(content)) !== null) {
|
|
168
|
+
const className = classStartMatch[1];
|
|
169
|
+
const startIndex = classStartMatch.index + classStartMatch[0].length;
|
|
170
|
+
// Find matching closing brace
|
|
171
|
+
let braceCount = 1;
|
|
172
|
+
let endIndex = startIndex;
|
|
173
|
+
for (let i = startIndex; i < content.length && braceCount > 0; i++) {
|
|
174
|
+
if (content[i] === '{')
|
|
175
|
+
braceCount++;
|
|
176
|
+
if (content[i] === '}')
|
|
177
|
+
braceCount--;
|
|
178
|
+
endIndex = i;
|
|
179
|
+
}
|
|
180
|
+
const classBody = content.slice(startIndex, endIndex);
|
|
181
|
+
const cppClass = {
|
|
182
|
+
name: className,
|
|
183
|
+
methods: [],
|
|
184
|
+
constructors: [],
|
|
185
|
+
source: "cpp",
|
|
186
|
+
};
|
|
187
|
+
// Track current access level
|
|
188
|
+
let isPublic = false;
|
|
189
|
+
// Parse declarations within the class body
|
|
190
|
+
// Match constructors and methods: type name(params) or name(params)
|
|
191
|
+
// Need to handle multi-line declarations
|
|
192
|
+
// First, normalize whitespace
|
|
193
|
+
const normalizedBody = classBody.replace(/\s+/g, ' ');
|
|
194
|
+
// Split by method/constructor definitions (look for patterns ending with { or ;)
|
|
195
|
+
// Pattern: type name(params) { body } or type name(params);
|
|
196
|
+
const methodRegex = /(\w+(?:\s*[*&])?)\s+(\w+)\s*\(([^)]*)\)\s*(?:const\s*)?(?:\{|;)/g;
|
|
197
|
+
let methodMatch;
|
|
198
|
+
// Also look for access specifiers
|
|
199
|
+
const accessRegex = /(public|private|protected)\s*:/g;
|
|
200
|
+
// Process the class body by scanning for declarations
|
|
201
|
+
let lastIndex = 0;
|
|
202
|
+
const segments = [];
|
|
203
|
+
let currentIsPublic = false;
|
|
204
|
+
// Split by access specifiers
|
|
205
|
+
const bodyWithAccess = normalizedBody.split(/(public:|private:|protected:)/);
|
|
206
|
+
for (let i = 0; i < bodyWithAccess.length; i++) {
|
|
207
|
+
const segment = bodyWithAccess[i].trim();
|
|
208
|
+
if (segment === 'public:') {
|
|
209
|
+
currentIsPublic = true;
|
|
210
|
+
}
|
|
211
|
+
else if (segment === 'private:' || segment === 'protected:') {
|
|
212
|
+
currentIsPublic = false;
|
|
213
|
+
}
|
|
214
|
+
else if (segment) {
|
|
215
|
+
segments.push({ text: segment, isPublic: currentIsPublic });
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
// Process each segment for methods and constructors
|
|
219
|
+
for (const segment of segments) {
|
|
220
|
+
// Match methods with return type: type name(params)
|
|
221
|
+
const methodMatches = segment.text.matchAll(/(\w+(?:\s*[*&])?)\s+(\w+)\s*\(([^)]*)\)\s*(?:const\s*)?(?:\{|;)/g);
|
|
222
|
+
for (const match of methodMatches) {
|
|
223
|
+
const returnType = match[1].trim();
|
|
224
|
+
const name = match[2].trim();
|
|
225
|
+
const params = match[3];
|
|
226
|
+
// Check if this is a constructor (name matches class name)
|
|
227
|
+
if (name === className) {
|
|
228
|
+
cppClass.constructors.push({
|
|
229
|
+
parameters: parseParameters(params),
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
cppClass.methods.push({
|
|
234
|
+
returnType: mapCppTypeToTs(returnType),
|
|
235
|
+
name,
|
|
236
|
+
parameters: parseParameters(params),
|
|
237
|
+
isPublic: segment.isPublic,
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
// Also match constructors without return type: ClassName(params)
|
|
242
|
+
const ctorRegex = new RegExp(`\\b(${className})\\s*\\(([^)]*)\\)\\s*(?:\\{|;)`, 'g');
|
|
243
|
+
const ctorMatches = segment.text.matchAll(ctorRegex);
|
|
244
|
+
for (const match of ctorMatches) {
|
|
245
|
+
const params = match[2];
|
|
246
|
+
// Check if we already have this constructor (avoid duplicates)
|
|
247
|
+
const existingCtor = cppClass.constructors.find(c => c.parameters.length === parseParameters(params).length);
|
|
248
|
+
if (!existingCtor) {
|
|
249
|
+
cppClass.constructors.push({
|
|
250
|
+
parameters: parseParameters(params),
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
result.classes.push(cppClass);
|
|
256
|
+
}
|
|
257
|
+
return result;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Generates TypeScript declaration content from parsed C++
|
|
261
|
+
*/
|
|
262
|
+
function generateDeclaration(parsed, moduleDocstring) {
|
|
263
|
+
const lines = [];
|
|
264
|
+
// Add docstring if provided
|
|
265
|
+
if (moduleDocstring) {
|
|
266
|
+
lines.push(`/** ${moduleDocstring} */`);
|
|
267
|
+
}
|
|
268
|
+
// Export constants
|
|
269
|
+
for (const constant of parsed.constants) {
|
|
270
|
+
lines.push(`export declare const ${constant.name}: ${constant.type};`);
|
|
271
|
+
}
|
|
272
|
+
if (parsed.constants.length > 0 && parsed.classes.length > 0) {
|
|
273
|
+
lines.push("");
|
|
274
|
+
}
|
|
275
|
+
// Export classes
|
|
276
|
+
for (const cppClass of parsed.classes) {
|
|
277
|
+
const extendsClause = cppClass.baseClass ? ` extends ${cppClass.baseClass}` : "";
|
|
278
|
+
lines.push(`export declare class ${cppClass.name}${extendsClause} {`);
|
|
279
|
+
// Add constructors
|
|
280
|
+
for (const ctor of cppClass.constructors) {
|
|
281
|
+
const params = ctor.parameters
|
|
282
|
+
.filter(p => p.name)
|
|
283
|
+
.map(p => `${p.name}: ${p.type}`)
|
|
284
|
+
.join(", ");
|
|
285
|
+
lines.push(` constructor(${params});`);
|
|
286
|
+
}
|
|
287
|
+
// Add public methods
|
|
288
|
+
const publicMethods = cppClass.methods.filter(m => m.isPublic);
|
|
289
|
+
for (const method of publicMethods) {
|
|
290
|
+
const params = method.parameters
|
|
291
|
+
.filter(p => p.name)
|
|
292
|
+
.map(p => `${p.name}: ${p.type}`)
|
|
293
|
+
.join(", ");
|
|
294
|
+
lines.push(` ${method.name}(${params}): ${method.returnType};`);
|
|
295
|
+
}
|
|
296
|
+
lines.push("}");
|
|
297
|
+
}
|
|
298
|
+
return lines.join("\n") + "\n";
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* Backward-compat wrapper — delegates to generateDecl so legacy callers
|
|
302
|
+
* (e.g. cli.ts) keep resolving while the richer .h/.cpp merge lives in
|
|
303
|
+
* generateDecl.
|
|
304
|
+
*/
|
|
305
|
+
export function generateDeclFromCpp(cppFilePath, outputPath) {
|
|
306
|
+
return generateDecl(cppFilePath, outputPath);
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Scans a directory for .h and .cpp files and generates .d.ts files.
|
|
310
|
+
*
|
|
311
|
+
* Two passes:
|
|
312
|
+
* 1. Build a class-name → header-path index from all .h under dir.
|
|
313
|
+
* 2. For each .h (and .cpp with no sibling .h), parse + merge + emit with
|
|
314
|
+
* cross-file base resolution.
|
|
315
|
+
*/
|
|
316
|
+
export function generateDeclsForDirectory(dir, recursive = true) {
|
|
317
|
+
const created = [];
|
|
318
|
+
if (!fs.existsSync(dir)) {
|
|
319
|
+
return created;
|
|
320
|
+
}
|
|
321
|
+
const index = buildClassIndex(dir);
|
|
322
|
+
const resolver = new BaseClassResolver(index);
|
|
323
|
+
const walk = (d) => {
|
|
324
|
+
const entries = fs.readdirSync(d, { withFileTypes: true });
|
|
325
|
+
for (const entry of entries) {
|
|
326
|
+
const full = path.join(d, entry.name);
|
|
327
|
+
if (entry.isDirectory() && recursive) {
|
|
328
|
+
walk(full);
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
331
|
+
if (!entry.isFile())
|
|
332
|
+
continue;
|
|
333
|
+
const lower = entry.name.toLowerCase();
|
|
334
|
+
if (lower.endsWith(".h")) {
|
|
335
|
+
const r = generateDeclWithResolver(full, resolver);
|
|
336
|
+
if (r)
|
|
337
|
+
created.push(r);
|
|
338
|
+
}
|
|
339
|
+
else if (lower.endsWith(".cpp")) {
|
|
340
|
+
// Skip .cpp files that have a sibling .h — the .h drives emission.
|
|
341
|
+
const cppBase = path.basename(full, ".cpp");
|
|
342
|
+
const siblingH = path.join(path.dirname(full), cppBase + ".h");
|
|
343
|
+
if (!fs.existsSync(siblingH)) {
|
|
344
|
+
const r = generateDeclWithResolver(full, resolver);
|
|
345
|
+
if (r)
|
|
346
|
+
created.push(r);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
};
|
|
351
|
+
walk(dir);
|
|
352
|
+
return created;
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Generates a .d.ts from a .h OR .cpp file.
|
|
356
|
+
*
|
|
357
|
+
* - For .h: parses the header; if a sibling .cpp exists, merges its
|
|
358
|
+
* impl-only methods (ClassName::method) into the header's classes.
|
|
359
|
+
* - For .cpp: if a sibling .h exists, parses the header and merges; else
|
|
360
|
+
* behaves as the legacy flattened path.
|
|
361
|
+
*
|
|
362
|
+
* Does NOT resolve cross-file base classes — bases from other headers are
|
|
363
|
+
* left as bare `extends Foo` (no import). Cross-file resolution is added in
|
|
364
|
+
* a later step for the directory-scan path.
|
|
365
|
+
*/
|
|
366
|
+
export function generateDecl(filePath, outputPath) {
|
|
367
|
+
if (!fs.existsSync(filePath)) {
|
|
368
|
+
return null;
|
|
369
|
+
}
|
|
370
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
371
|
+
const dir = path.dirname(filePath);
|
|
372
|
+
const baseNoExt = path.basename(filePath, ext);
|
|
373
|
+
let headerPath;
|
|
374
|
+
let cppPath;
|
|
375
|
+
if (ext === ".h") {
|
|
376
|
+
headerPath = filePath;
|
|
377
|
+
const siblingCpp = path.join(dir, baseNoExt + ".cpp");
|
|
378
|
+
cppPath = fs.existsSync(siblingCpp) ? siblingCpp : undefined;
|
|
379
|
+
}
|
|
380
|
+
else if (ext === ".cpp") {
|
|
381
|
+
cppPath = filePath;
|
|
382
|
+
const siblingH = path.join(dir, baseNoExt + ".h");
|
|
383
|
+
headerPath = fs.existsSync(siblingH) ? siblingH : undefined;
|
|
384
|
+
}
|
|
385
|
+
else {
|
|
386
|
+
return null;
|
|
387
|
+
}
|
|
388
|
+
let headerClasses = [];
|
|
389
|
+
if (headerPath) {
|
|
390
|
+
headerClasses = parseHeader(fs.readFileSync(headerPath, "utf8"));
|
|
391
|
+
}
|
|
392
|
+
let cppResult = { classes: [], constants: [] };
|
|
393
|
+
if (cppPath) {
|
|
394
|
+
cppResult = parseCppClass(fs.readFileSync(cppPath, "utf8"));
|
|
395
|
+
}
|
|
396
|
+
const merged = [];
|
|
397
|
+
const byName = new Map();
|
|
398
|
+
for (const h of headerClasses) {
|
|
399
|
+
const cls = {
|
|
400
|
+
name: h.name,
|
|
401
|
+
methods: [...h.methods],
|
|
402
|
+
constructors: [...h.constructors],
|
|
403
|
+
baseClass: h.baseClass,
|
|
404
|
+
source: "header",
|
|
405
|
+
};
|
|
406
|
+
byName.set(h.name, cls);
|
|
407
|
+
merged.push(cls);
|
|
408
|
+
}
|
|
409
|
+
for (const c of cppResult.classes) {
|
|
410
|
+
const existing = byName.get(c.name);
|
|
411
|
+
if (existing) {
|
|
412
|
+
for (const m of c.methods) {
|
|
413
|
+
if (!existing.methods.some(em => em.name === m.name)) {
|
|
414
|
+
existing.methods.push(m);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
else if (!headerPath) {
|
|
419
|
+
// No sibling header: keep .cpp-inferred classes (legacy flattened path).
|
|
420
|
+
// When a header IS present, drop .cpp-only classes — they belong to
|
|
421
|
+
// other headers and would otherwise create false "same-file" bases
|
|
422
|
+
// (e.g. Adafruit_SPITFT inferred from Adafruit_ILI9341.cpp's
|
|
423
|
+
// Adafruit_SPITFT::readcommand8 scope resolution).
|
|
424
|
+
merged.push({ ...c, source: "cpp" });
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
if (merged.length === 0 && cppResult.constants.length === 0) {
|
|
428
|
+
return null;
|
|
429
|
+
}
|
|
430
|
+
// Single-file mode: no resolver → all cross-file bases are "external" →
|
|
431
|
+
// emitted as empty stubs so the .d.ts still compiles standalone.
|
|
432
|
+
const declaration = generateDeclarationWithResolver({ classes: merged, constants: cppResult.constants }, new BaseClassResolver(new Map()), filePath);
|
|
433
|
+
const outPath = outputPath || path.join(dir, baseNoExt + ".d.ts");
|
|
434
|
+
if (!fs.existsSync(outPath) || fs.readFileSync(outPath, "utf8") !== declaration) {
|
|
435
|
+
fs.writeFileSync(outPath, declaration, "utf8");
|
|
436
|
+
}
|
|
437
|
+
return outPath;
|
|
438
|
+
}
|
|
439
|
+
/**
|
|
440
|
+
* Like generateDeclaration, but resolves base classes against `resolver` and
|
|
441
|
+
* the emitting file path (for computing relative import paths).
|
|
442
|
+
*
|
|
443
|
+
* Emission order: imports → constants → stubs → classes.
|
|
444
|
+
*/
|
|
445
|
+
function generateDeclarationWithResolver(parsed, resolver, emittingFilePath) {
|
|
446
|
+
const lines = [];
|
|
447
|
+
const importingFrom = new Map(); // baseName → POSIX import path
|
|
448
|
+
const stubs = new Set(); // unresolved base names
|
|
449
|
+
const classNames = new Set(parsed.classes.map(c => c.name));
|
|
450
|
+
// Classify each base.
|
|
451
|
+
for (const cls of parsed.classes) {
|
|
452
|
+
if (!cls.baseClass)
|
|
453
|
+
continue;
|
|
454
|
+
if (classNames.has(cls.baseClass))
|
|
455
|
+
continue; // same-file base
|
|
456
|
+
const r = resolver.resolve(cls.baseClass);
|
|
457
|
+
if (r.kind === "found") {
|
|
458
|
+
const targetDts = r.headerPath.replace(/\.h$/i, ".d.ts");
|
|
459
|
+
const rel = path.relative(path.dirname(emittingFilePath), path.dirname(targetDts));
|
|
460
|
+
const targetBase = path.basename(targetDts, ".d.ts");
|
|
461
|
+
const relPosix = (rel || ".").replace(/\\/g, "/");
|
|
462
|
+
const importPath = relPosix === "." ? `./${targetBase}` : `${relPosix}/${targetBase}`;
|
|
463
|
+
importingFrom.set(cls.baseClass, importPath);
|
|
464
|
+
}
|
|
465
|
+
else {
|
|
466
|
+
stubs.add(cls.baseClass);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
// Imports.
|
|
470
|
+
for (const [baseName, importPath] of importingFrom) {
|
|
471
|
+
lines.push(`import type { ${baseName} } from "${importPath}";`);
|
|
472
|
+
}
|
|
473
|
+
if (importingFrom.size > 0)
|
|
474
|
+
lines.push("");
|
|
475
|
+
// Constants.
|
|
476
|
+
for (const constant of parsed.constants) {
|
|
477
|
+
lines.push(`export declare const ${constant.name}: ${constant.type};`);
|
|
478
|
+
}
|
|
479
|
+
if (parsed.constants.length > 0 && parsed.classes.length > 0)
|
|
480
|
+
lines.push("");
|
|
481
|
+
// Stubs for unresolved bases.
|
|
482
|
+
for (const stub of stubs) {
|
|
483
|
+
lines.push(`declare class ${stub} {}`);
|
|
484
|
+
}
|
|
485
|
+
if (stubs.size > 0)
|
|
486
|
+
lines.push("");
|
|
487
|
+
// Classes.
|
|
488
|
+
for (const cppClass of parsed.classes) {
|
|
489
|
+
const extendsClause = cppClass.baseClass ? ` extends ${cppClass.baseClass}` : "";
|
|
490
|
+
lines.push(`export declare class ${cppClass.name}${extendsClause} {`);
|
|
491
|
+
for (const ctor of cppClass.constructors) {
|
|
492
|
+
const params = ctor.parameters
|
|
493
|
+
.filter(p => p.name)
|
|
494
|
+
.map(p => `${p.name}: ${p.type}`)
|
|
495
|
+
.join(", ");
|
|
496
|
+
lines.push(` constructor(${params});`);
|
|
497
|
+
}
|
|
498
|
+
const publicMethods = cppClass.methods.filter(m => m.isPublic);
|
|
499
|
+
for (const method of publicMethods) {
|
|
500
|
+
const params = method.parameters
|
|
501
|
+
.filter(p => p.name)
|
|
502
|
+
.map(p => `${p.name}: ${p.type}`)
|
|
503
|
+
.join(", ");
|
|
504
|
+
lines.push(` ${method.name}(${params}): ${method.returnType};`);
|
|
505
|
+
}
|
|
506
|
+
lines.push("}");
|
|
507
|
+
}
|
|
508
|
+
return lines.join("\n") + "\n";
|
|
509
|
+
}
|
|
510
|
+
/**
|
|
511
|
+
* Like generateDecl, but resolves base classes against `resolver`: cross-file
|
|
512
|
+
* bases become `import type`; unresolved bases become empty stubs.
|
|
513
|
+
*/
|
|
514
|
+
function generateDeclWithResolver(filePath, resolver) {
|
|
515
|
+
if (!fs.existsSync(filePath)) {
|
|
516
|
+
return null;
|
|
517
|
+
}
|
|
518
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
519
|
+
const dir = path.dirname(filePath);
|
|
520
|
+
const baseNoExt = path.basename(filePath, ext);
|
|
521
|
+
let headerPath;
|
|
522
|
+
let cppPath;
|
|
523
|
+
if (ext === ".h") {
|
|
524
|
+
headerPath = filePath;
|
|
525
|
+
const siblingCpp = path.join(dir, baseNoExt + ".cpp");
|
|
526
|
+
cppPath = fs.existsSync(siblingCpp) ? siblingCpp : undefined;
|
|
527
|
+
}
|
|
528
|
+
else if (ext === ".cpp") {
|
|
529
|
+
cppPath = filePath;
|
|
530
|
+
const siblingH = path.join(dir, baseNoExt + ".h");
|
|
531
|
+
headerPath = fs.existsSync(siblingH) ? siblingH : undefined;
|
|
532
|
+
}
|
|
533
|
+
else {
|
|
534
|
+
return null;
|
|
535
|
+
}
|
|
536
|
+
let headerClasses = [];
|
|
537
|
+
if (headerPath) {
|
|
538
|
+
headerClasses = parseHeader(fs.readFileSync(headerPath, "utf8"));
|
|
539
|
+
}
|
|
540
|
+
let cppResult = { classes: [], constants: [] };
|
|
541
|
+
if (cppPath) {
|
|
542
|
+
cppResult = parseCppClass(fs.readFileSync(cppPath, "utf8"));
|
|
543
|
+
}
|
|
544
|
+
const merged = [];
|
|
545
|
+
const byName = new Map();
|
|
546
|
+
for (const h of headerClasses) {
|
|
547
|
+
const cls = {
|
|
548
|
+
name: h.name,
|
|
549
|
+
methods: [...h.methods],
|
|
550
|
+
constructors: [...h.constructors],
|
|
551
|
+
baseClass: h.baseClass,
|
|
552
|
+
source: "header",
|
|
553
|
+
};
|
|
554
|
+
byName.set(h.name, cls);
|
|
555
|
+
merged.push(cls);
|
|
556
|
+
}
|
|
557
|
+
for (const c of cppResult.classes) {
|
|
558
|
+
const existing = byName.get(c.name);
|
|
559
|
+
if (existing) {
|
|
560
|
+
for (const m of c.methods) {
|
|
561
|
+
if (!existing.methods.some(em => em.name === m.name)) {
|
|
562
|
+
existing.methods.push(m);
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
else if (!headerPath) {
|
|
567
|
+
// No sibling header: keep .cpp-inferred classes (legacy flattened path).
|
|
568
|
+
// When a header IS present, drop .cpp-only classes — they belong to
|
|
569
|
+
// other headers and would otherwise create false "same-file" bases
|
|
570
|
+
// (e.g. Adafruit_SPITFT inferred from Adafruit_ILI9341.cpp's
|
|
571
|
+
// Adafruit_SPITFT::readcommand8 scope resolution).
|
|
572
|
+
merged.push({ ...c, source: "cpp" });
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
if (merged.length === 0 && cppResult.constants.length === 0) {
|
|
576
|
+
return null;
|
|
577
|
+
}
|
|
578
|
+
const declaration = generateDeclarationWithResolver({ classes: merged, constants: cppResult.constants }, resolver, filePath);
|
|
579
|
+
const outPath = path.join(dir, baseNoExt + ".d.ts");
|
|
580
|
+
if (!fs.existsSync(outPath) || fs.readFileSync(outPath, "utf8") !== declaration) {
|
|
581
|
+
fs.writeFileSync(outPath, declaration, "utf8");
|
|
582
|
+
}
|
|
583
|
+
return outPath;
|
|
584
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Header parser for gen-decls.
|
|
3
|
+
*
|
|
4
|
+
* Parses C++ class declarations from .h files, capturing inheritance
|
|
5
|
+
* (`class X : public Y`) and stripping #if/#ifdef/#ifndef preprocessor
|
|
6
|
+
* blocks before parsing. See
|
|
7
|
+
* docs/superpowers/specs/2026-06-20-gen-decls-inheritance-design.md.
|
|
8
|
+
*/
|
|
9
|
+
/** A single method/constructor signature. */
|
|
10
|
+
export interface ClassMethod {
|
|
11
|
+
name: string;
|
|
12
|
+
returnType: string;
|
|
13
|
+
parameters: {
|
|
14
|
+
type: string;
|
|
15
|
+
name: string;
|
|
16
|
+
}[];
|
|
17
|
+
isPublic: boolean;
|
|
18
|
+
}
|
|
19
|
+
/** A parsed C++ class with optional base class. */
|
|
20
|
+
export interface ParsedClass {
|
|
21
|
+
name: string;
|
|
22
|
+
methods: ClassMethod[];
|
|
23
|
+
constructors: {
|
|
24
|
+
parameters: {
|
|
25
|
+
type: string;
|
|
26
|
+
name: string;
|
|
27
|
+
}[];
|
|
28
|
+
}[];
|
|
29
|
+
baseClass?: string;
|
|
30
|
+
source: "header" | "cpp";
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Normalizes C++ preprocessor directives so the class parser can see the
|
|
34
|
+
* declarations. Semantics:
|
|
35
|
+
*
|
|
36
|
+
* - Include guards (`#ifndef X` immediately followed by `#define X`, and
|
|
37
|
+
* `#pragma once`): directives dropped, all guarded content kept.
|
|
38
|
+
* - `#if` / `#ifdef` / `#ifndef` ... `#else` / `#elif` ... `#endif`: the
|
|
39
|
+
* FIRST branch's content is kept; `#else` / `#elif` branch content is
|
|
40
|
+
* dropped; all directives themselves are dropped. This preserves classes
|
|
41
|
+
* that real-world headers gate behind target conditionals (e.g.
|
|
42
|
+
* Adafruit_SPITFT wraps its whole class in `#if !defined(__AVR_ATtiny85__)`),
|
|
43
|
+
* at the cost of possibly emitting declarations from a branch that isn't
|
|
44
|
+
* active on the user's target. That's acceptable for .d.ts generation,
|
|
45
|
+
* where a slightly wider API surface beats no API surface.
|
|
46
|
+
*/
|
|
47
|
+
export declare function stripPreprocessorBlocks(content: string): string;
|
|
48
|
+
/**
|
|
49
|
+
* Parses a header source string into ParsedClass[]. Strips #if blocks first,
|
|
50
|
+
* then finds `class Name [: access Base] { ... }` blocks and extracts public
|
|
51
|
+
* methods + constructors.
|
|
52
|
+
*/
|
|
53
|
+
export declare function parseHeader(content: string): ParsedClass[];
|