@typecad/cuttlefish 0.1.0-alpha.2 → 1.0.0-alpha.11
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/README.md +10 -10
- package/dist/api/board-types.d.ts +1 -1
- package/dist/api/config.d.ts +38 -2
- package/dist/api/index.d.ts +1 -1
- package/dist/api/schema/types.d.ts +15 -0
- package/dist/api/shared/async-runtime-static.d.ts +12 -1
- package/dist/api/shared/async-runtime-static.js +69 -24
- package/dist/api/shared/async-symbol-detector.d.ts +11 -0
- package/dist/api/shared/async-symbol-detector.js +140 -0
- package/dist/api/shared/async-types.d.ts +24 -0
- package/dist/api/shared/coop-scheduler.d.ts +60 -0
- package/dist/api/shared/coop-scheduler.js +149 -0
- package/dist/api/shared/display-adapter.d.ts +4 -3
- package/dist/api/shared/display-adapter.js +15 -118
- package/dist/api/shared/display-adapters/sdl.js +38 -31
- package/dist/api/shared/display-profile.d.ts +47 -25
- package/dist/api/shared/display-profile.js +31 -164
- package/dist/api/shared/framework-manifest-registry.d.ts +9 -0
- package/dist/api/shared/framework-manifest-registry.js +27 -0
- package/dist/api/shared/framework-manifest.d.ts +595 -0
- package/dist/api/shared/framework-manifest.js +193 -0
- package/dist/api/shared/glcdfont.d.ts +12 -0
- package/dist/api/shared/glcdfont.js +124 -0
- package/dist/api/shared/graphics-strategy.d.ts +37 -0
- package/dist/api/shared/hal-op-ir.d.ts +764 -1
- package/dist/api/shared/hal-op-ir.js +129 -1
- package/dist/api/shared/index.d.ts +22 -2
- package/dist/api/shared/index.js +26 -1
- package/dist/api/shared/ir-core.d.ts +4 -0
- package/dist/api/shared/ir-declarations.d.ts +6 -0
- package/dist/api/shared/native-display-op-resolver.d.ts +10 -0
- package/dist/api/shared/native-display-op-resolver.js +64 -0
- package/dist/api/shared/platform-strategy.d.ts +119 -2
- package/dist/api/shared/polyfill-helper-registry.js +18 -1
- package/dist/api/shared/promise-runtime.d.ts +1 -1
- package/dist/api/shared/promise-runtime.js +95 -13
- package/dist/api/shared/spdx-licenses.d.ts +139 -0
- package/dist/api/shared/spdx-licenses.js +385 -0
- package/dist/api/shared/toolchain-types.d.ts +17 -0
- package/dist/api/shared/types.d.ts +8 -0
- package/dist/api/shared/validate-framework-manifest.d.ts +28 -0
- package/dist/api/shared/validate-framework-manifest.js +494 -0
- package/dist/api/shared/worker-runtime-polyfill.d.ts +22 -0
- package/dist/api/shared/worker-runtime-polyfill.js +34 -0
- package/dist/api/shared/worker-runtime.d.ts +69 -0
- package/dist/api/shared/worker-runtime.js +163 -0
- package/dist/cli-utils.d.ts +1 -0
- package/dist/cli-utils.js +3 -1
- package/dist/cli.js +249 -32
- package/dist/config-loader.d.ts +7 -2
- package/dist/config-loader.js +23 -3
- package/dist/config-schema.d.ts +112 -70
- package/dist/config-schema.js +14 -0
- package/dist/contract/board-generator.d.ts +17 -0
- package/dist/contract/board-generator.js +57 -0
- package/dist/contract/contract-parser.d.ts +217 -0
- package/dist/contract/contract-parser.js +224 -0
- package/dist/contract/index.d.ts +22 -0
- package/dist/contract/index.js +68 -0
- package/dist/create/board-codegen.js +4 -4
- package/dist/create/board-generators.js +4 -5
- package/dist/create/board-spec.d.ts +72 -75
- package/dist/create/board-spec.js +0 -1
- package/dist/create/framework-catalog.d.ts +64 -0
- package/dist/create/framework-catalog.js +136 -0
- package/dist/create/index.d.ts +5 -1
- package/dist/create/index.js +3 -1
- package/dist/create/init-scaffold.d.ts +12 -3
- package/dist/create/init-scaffold.js +49 -14
- package/dist/create/init-templates.d.ts +4 -0
- package/dist/create/init-templates.js +271 -17
- package/dist/create/init-wizard.js +41 -27
- package/dist/create/install-deps.d.ts +32 -0
- package/dist/create/install-deps.js +46 -0
- package/dist/debug/preprocessor.js +178 -39
- package/dist/debug/types.d.ts +23 -0
- package/dist/diagnostics/mermaid-builder.d.ts +1 -1
- package/dist/diagnostics/mermaid-builder.js +34 -24
- package/dist/emit/compliance/arxml-writer.d.ts +11 -0
- package/dist/emit/compliance/arxml-writer.js +34 -0
- package/dist/emit/compliance/compliance-context.d.ts +57 -0
- package/dist/emit/compliance/compliance-context.js +113 -0
- package/dist/emit/compliance/deviation-ledger.d.ts +27 -0
- package/dist/emit/compliance/deviation-ledger.js +47 -0
- package/dist/emit/compliance/deviation-writer.d.ts +30 -0
- package/dist/emit/compliance/deviation-writer.js +37 -0
- package/dist/emit/compliance/index.d.ts +7 -0
- package/dist/emit/compliance/index.js +6 -0
- package/dist/emit/compliance/rule-engine.d.ts +13 -0
- package/dist/emit/compliance/rule-engine.js +101 -0
- package/dist/emit/compliance/rules.d.ts +16 -0
- package/dist/emit/compliance/rules.js +191 -0
- package/dist/emit/compliance/types.d.ts +59 -0
- package/dist/emit/compliance/types.js +8 -0
- package/dist/emit/cpp-emitter.js +4 -3
- package/dist/emit/emitters/class-emitter.js +6 -1
- package/dist/emit/emitters/emitter-context.d.ts +28 -1
- package/dist/emit/emitters/function-emitter-impl.js +153 -53
- package/dist/emit/emitters/line-appender.js +19 -0
- package/dist/emit/emitters/line-marker.d.ts +38 -0
- package/dist/emit/emitters/line-marker.js +39 -0
- package/dist/emit/emitters/output-finalizer.d.ts +6 -0
- package/dist/emit/emitters/output-finalizer.js +101 -12
- package/dist/emit/emitters/setup.d.ts +17 -0
- package/dist/emit/emitters/setup.js +372 -30
- package/dist/emit/emitters/top-level-prep.js +8 -0
- package/dist/emit/emitters/type-decl-emitter.js +28 -3
- package/dist/emit/emitters/ui-emitter.js +33 -9
- package/dist/emit/expression-renderer.d.ts +1 -1
- package/dist/emit/expression-renderer.js +91 -6
- package/dist/emit/route-hal-op.js +18 -5
- package/dist/emit/snprintf-helpers.js +15 -4
- package/dist/emit/statement-renderer.d.ts +10 -0
- package/dist/emit/statement-renderer.js +34 -5
- package/dist/emit/utils/async-state-machine.js +221 -125
- package/dist/emit/utils/hal-op-cpp-type.d.ts +6 -0
- package/dist/emit/utils/hal-op-cpp-type.js +40 -0
- package/dist/framework-package.js +2 -0
- package/dist/framework-registry.d.ts +17 -0
- package/dist/install/framework-catalog.d.ts +53 -0
- package/dist/install/framework-catalog.js +107 -0
- package/dist/install/handle-install.d.ts +35 -0
- package/dist/install/handle-install.js +177 -0
- package/dist/install/index.d.ts +4 -0
- package/dist/install/index.js +3 -0
- package/dist/ir/adc-range-validation.js +40 -26
- package/dist/ir/build-ir-state.d.ts +1 -0
- package/dist/ir/build-ir-state.js +14 -0
- package/dist/ir/build-ir.js +9 -5
- package/dist/ir/call-graph.js +16 -0
- package/dist/ir/expression-to-ir.js +207 -6
- package/dist/ir/feature-registry.js +7 -25
- package/dist/ir/function-builder.js +22 -0
- package/dist/ir/hal/hal-emitter.d.ts +5 -2
- package/dist/ir/hal/hal-emitter.js +50 -18
- package/dist/ir/hal/hal-parser.d.ts +6 -0
- package/dist/ir/hal/hal-parser.js +81 -0
- package/dist/ir/hal/hal-plugins.js +733 -1
- package/dist/ir/identifier-collector.js +35 -0
- package/dist/ir/interrupt-analysis.d.ts +5 -1
- package/dist/ir/interrupt-analysis.js +16 -19
- package/dist/ir/memory-budget-validation.js +1 -0
- package/dist/ir/network-validation.d.ts +4 -0
- package/dist/ir/network-validation.js +184 -0
- package/dist/ir/ownership-analysis.js +39 -1
- package/dist/ir/peripheral-ownership.js +5 -0
- package/dist/ir/peripheral-validation.d.ts +1 -1
- package/dist/ir/peripheral-validation.js +6 -3
- package/dist/ir/pin-alias-conflict.d.ts +1 -1
- package/dist/ir/pin-alias-conflict.js +2 -1
- package/dist/ir/pin-capability-validation.js +71 -32
- package/dist/ir/pin-mode-validation.d.ts +2 -2
- package/dist/ir/pin-mode-validation.js +54 -18
- package/dist/ir/pin-safety.d.ts +1 -1
- package/dist/ir/pin-safety.js +2 -1
- package/dist/ir/program-analysis.d.ts +81 -0
- package/dist/ir/program-analysis.js +429 -0
- package/dist/ir/pulldown-validation.d.ts +1 -1
- package/dist/ir/pulldown-validation.js +2 -1
- package/dist/ir/pwm-timer-sharing.d.ts +1 -1
- package/dist/ir/pwm-timer-sharing.js +2 -1
- package/dist/ir/render-expr.js +11 -0
- package/dist/ir/resource-analysis.js +2 -0
- package/dist/ir/timer0-pwm-timing-conflict.d.ts +1 -1
- package/dist/ir/timer0-pwm-timing-conflict.js +2 -1
- package/dist/ir/timing-validation.d.ts +6 -1
- package/dist/ir/timing-validation.js +61 -13
- package/dist/ir/transformers/call-statement.js +105 -0
- package/dist/ir/transformers/expressions.js +62 -0
- package/dist/ir/transformers/hal-call-resolver.js +21 -0
- package/dist/ir/transformers/hal-emit-helpers.js +1 -1
- package/dist/ir/transformers/namespace-methods.js +17 -12
- package/dist/ir/transformers/ui-reactive.js +2 -2
- package/dist/ir/transformers/variables.js +137 -19
- package/dist/ir/try-catch-validation.js +2 -0
- package/dist/ir/type-resolution.js +20 -2
- package/dist/ir/ui-element-auto-wire.js +7 -5
- package/dist/ir/unit-suspicion-validation.js +9 -7
- package/dist/ir/utils/map-statements.d.ts +4 -0
- package/dist/ir/utils/map-statements.js +79 -0
- package/dist/ir/validation-orchestrator.js +15 -8
- package/dist/ir/worker-analysis.d.ts +10 -0
- package/dist/ir/worker-analysis.js +261 -0
- package/dist/libdef/c-to-decl.d.ts +27 -0
- package/dist/libdef/c-to-decl.js +397 -0
- package/dist/libdef/component-decls.d.ts +2 -0
- package/dist/libdef/component-decls.js +6 -0
- package/dist/libdef/component-discovery.d.ts +43 -0
- package/dist/libdef/component-discovery.js +83 -0
- package/dist/libdef/cpp-to-decl.d.ts +9 -0
- package/dist/libdef/cpp-to-decl.js +72 -0
- package/dist/libdef/registry.js +5 -2
- package/dist/lint-cache.d.ts +59 -0
- package/dist/lint-cache.js +257 -0
- package/dist/orchestrator/graph-builder.js +14 -9
- package/dist/platform/coop-scheduler-runtime.d.ts +19 -0
- package/dist/platform/coop-scheduler-runtime.js +41 -0
- package/dist/platform/generic-strategy.d.ts +15 -3
- package/dist/platform/generic-strategy.js +49 -4
- package/dist/safety/safety-bridge.d.ts +11 -0
- package/dist/safety/safety-bridge.js +48 -0
- package/dist/safety/sidecar-bridge.d.ts +5 -0
- package/dist/safety/sidecar-bridge.js +22 -0
- package/dist/safety-hook.d.ts +79 -0
- package/dist/safety-hook.js +35 -0
- package/dist/testing.d.ts +8 -2
- package/dist/testing.js +9 -2
- package/dist/transpile.d.ts +3 -0
- package/dist/transpile.js +191 -49
- package/dist/types.d.ts +22 -3
- package/dist/ui-hook.d.ts +17 -3
- package/dist/utils/cli.js +123 -2
- package/dist/utils/fs.d.ts +13 -0
- package/dist/utils/fs.js +50 -0
- package/dist/utils/ui.js +3 -1
- package/package.json +23 -3
- package/dist/api/shared/display-adapters/eink-mono.d.ts +0 -2
- package/dist/api/shared/display-adapters/eink-mono.js +0 -53
- package/dist/api/shared/display-adapters/ssd1309.d.ts +0 -2
- package/dist/api/shared/display-adapters/ssd1309.js +0 -136
- package/dist/api/shared/display-adapters/st7796.d.ts +0 -2
- package/dist/api/shared/display-adapters/st7796.js +0 -132
- package/dist/ir/heap-array-validation.d.ts +0 -24
- package/dist/ir/heap-array-validation.js +0 -29
package/dist/cli.js
CHANGED
|
@@ -2,18 +2,21 @@
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import fs from "node:fs";
|
|
4
4
|
import { parseCommandLine, printHelp } from "./utils/cli.js";
|
|
5
|
-
import { scaffoldProject, printInitNextSteps, KNOWN_TARGETS } from "./create/index.js";
|
|
5
|
+
import { scaffoldProject, printInitNextSteps, KNOWN_TARGETS, frameworksForTarget, frameworkCatalogEntry, FRAMEWORK_CATALOG, frameworkTargetProfile } from "./create/index.js";
|
|
6
6
|
import { runInitWizard } from "./create/index.js";
|
|
7
|
+
import { installProjectDependencies } from "./create/install-deps.js";
|
|
7
8
|
import { generateLibraryDefinitions, transpileFile } from "./transpile.js";
|
|
8
|
-
import { generateDecl, generateDeclsForDirectory } from "./libdef/cpp-to-decl.js";
|
|
9
|
+
import { generateDecl, generateDeclsForDirectory, generateComponentDeclsForProject } from "./libdef/cpp-to-decl.js";
|
|
9
10
|
import { mapCppLocationToTs, readSourceMap, resolveMapPath } from "./mapping/source-map.js";
|
|
10
11
|
import { compileSource, uploadFirmware, monitorDevice } from "./platform/toolchain.js";
|
|
11
12
|
import { resolveStrategy } from "./platform/registry.js";
|
|
12
13
|
import { loadFrameworkPackage } from "./framework-package.js";
|
|
13
14
|
import { getLoadedFramework, hasLoadedFramework } from "./framework-registry.js";
|
|
14
15
|
import { loadCuttlefishConfig, generateVirtualTypeDeclaration } from "./config-loader.js";
|
|
16
|
+
import { generateContractBoard } from "./contract/index.js";
|
|
15
17
|
import { requireUIHook, hasUIHook } from "./ui-hook.js";
|
|
16
18
|
import { loadUIEngine } from "./ui/ui-bridge.js";
|
|
19
|
+
import { loadSafetyEngine } from "./safety/safety-bridge.js";
|
|
17
20
|
import { runWatch, discoverWatchDirs } from "./watch.js";
|
|
18
21
|
import { runExpectTests, assertTypeScriptInput, printDiagnostics, printMappedCompileErrors } from "./cli-utils.js";
|
|
19
22
|
import { runPreviewServer } from "./preview/server.js";
|
|
@@ -31,6 +34,27 @@ function displayConfigForTranspile(config) {
|
|
|
31
34
|
}
|
|
32
35
|
return display;
|
|
33
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* Apply the PSRAM config to the Arduino build: append the `PSRAM={opi|quad}`
|
|
39
|
+
* menu option to the FQBN (so the Arduino core's psramFound()/ps_malloc work
|
|
40
|
+
* at runtime — they're gated on the board menu option, not just the define)
|
|
41
|
+
* and add `-DBOARD_HAS_PSRAM` to the defines (so the framework's PSRAM canvas
|
|
42
|
+
* allocator is compiled in). No-op when psram is unset or the target isn't an
|
|
43
|
+
* Arduino FQBN (the PSRAM= option is Arduino-core-specific).
|
|
44
|
+
*/
|
|
45
|
+
function applyPsramToArduinoBuild(buildTarget, defines, psram) {
|
|
46
|
+
if (!psram || !buildTarget || !buildTarget.includes(':')) {
|
|
47
|
+
return { buildTarget, defines: defines ?? {} };
|
|
48
|
+
}
|
|
49
|
+
// Arduino FQBN config options follow the board:option as `key=value` pairs.
|
|
50
|
+
// Append PSRAM= if not already present (don't clobber an explicit override).
|
|
51
|
+
const psramOpt = `PSRAM=${psram}`;
|
|
52
|
+
const adjustedTarget = buildTarget.includes('PSRAM=') ? buildTarget : `${buildTarget}:${psramOpt}`;
|
|
53
|
+
const adjustedDefines = { ...(defines ?? {}) };
|
|
54
|
+
if (!('BOARD_HAS_PSRAM' in adjustedDefines))
|
|
55
|
+
adjustedDefines.BOARD_HAS_PSRAM = '';
|
|
56
|
+
return { buildTarget: adjustedTarget, defines: adjustedDefines };
|
|
57
|
+
}
|
|
34
58
|
async function handleCreate(options) {
|
|
35
59
|
const targetId = options.target ?? options.board;
|
|
36
60
|
const hasTarget = !!targetId;
|
|
@@ -40,15 +64,35 @@ async function handleCreate(options) {
|
|
|
40
64
|
const available = KNOWN_TARGETS.map(t => ` - ${t.id} (${t.displayName})`).join("\n");
|
|
41
65
|
throw new Error(`Unknown target '${targetId}'. Available targets:\n${available}`);
|
|
42
66
|
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
67
|
+
// Resolve the framework. --framework wins; otherwise narrow via the catalog
|
|
68
|
+
// for the chosen board and auto-pick when exactly one is compatible.
|
|
69
|
+
let frameworkId;
|
|
70
|
+
let frameworkPackage;
|
|
71
|
+
if (options.framework) {
|
|
72
|
+
const requested = frameworkCatalogEntry(options.framework);
|
|
73
|
+
if (!requested) {
|
|
74
|
+
const available = FRAMEWORK_CATALOG.filter(f => f.installable).map(f => f.id).join(", ");
|
|
75
|
+
throw new Error(`Unknown framework '${options.framework}'. Available: ${available}`);
|
|
76
|
+
}
|
|
77
|
+
frameworkId = requested.id;
|
|
78
|
+
frameworkPackage = requested.packageName;
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
const compatible = frameworksForTarget(target).filter(f => f.installable);
|
|
82
|
+
if (compatible.length === 1) {
|
|
83
|
+
frameworkId = compatible[0].id;
|
|
84
|
+
frameworkPackage = compatible[0].packageName;
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
const list = compatible.map(f => f.id).join(", ");
|
|
88
|
+
throw new Error(`Target '${target.id}' is compatible with multiple frameworks (${list}). ` +
|
|
89
|
+
`Pass --framework <id> to choose one.`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
51
92
|
const projectName = options.projectName || 'my-project';
|
|
93
|
+
// Framework-specific build target + toolchain (Zephyr board id + 'west' vs
|
|
94
|
+
// the Arduino FQBN + 'arduino-cli').
|
|
95
|
+
const profile = frameworkTargetProfile(target, frameworkId);
|
|
52
96
|
const result = scaffoldProject({
|
|
53
97
|
projectName,
|
|
54
98
|
targetId: target.id,
|
|
@@ -57,18 +101,17 @@ async function handleCreate(options) {
|
|
|
57
101
|
architecture: target.architecture,
|
|
58
102
|
boardPackage: target.boardPackage,
|
|
59
103
|
frameworkPackage,
|
|
60
|
-
framework,
|
|
61
|
-
buildTarget: target.buildTarget,
|
|
104
|
+
framework: frameworkId,
|
|
105
|
+
buildTarget: profile.buildTarget ?? target.buildTarget,
|
|
106
|
+
...(profile.toolchainType ? { toolchainType: profile.toolchainType } : {}),
|
|
62
107
|
mcu: target.mcu,
|
|
63
108
|
baudRate: target.isNative ? undefined : (options.baud ?? 9600),
|
|
64
109
|
includeSketch: !options.noSketch,
|
|
110
|
+
...(target.frameworkData
|
|
111
|
+
? { frameworkData: target.frameworkData }
|
|
112
|
+
: {}),
|
|
65
113
|
}, options.outDir);
|
|
66
|
-
|
|
67
|
-
for (const file of result.createdFiles) {
|
|
68
|
-
const relative = path.relative(process.cwd(), file);
|
|
69
|
-
console.log(` ${chalk.dim(relative || file)}`);
|
|
70
|
-
}
|
|
71
|
-
printInitNextSteps(result.options, result.outDir);
|
|
114
|
+
finalizeCreate(result, options);
|
|
72
115
|
}
|
|
73
116
|
else {
|
|
74
117
|
const wizardResult = await runInitWizard({
|
|
@@ -83,13 +126,37 @@ async function handleCreate(options) {
|
|
|
83
126
|
return;
|
|
84
127
|
}
|
|
85
128
|
const result = scaffoldProject(wizardResult, options.outDir);
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
129
|
+
finalizeCreate(result, options);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Shared tail of `cuttlefish create`: list the created files, install the new
|
|
134
|
+
* project's dependencies (so it's ready to build with no extra step — skipped
|
|
135
|
+
* via --no-install), and print the next-steps. A failed install is non-fatal:
|
|
136
|
+
* the scaffold itself is valid, so we warn and point at the manual command
|
|
137
|
+
* rather than undoing anything.
|
|
138
|
+
*/
|
|
139
|
+
function finalizeCreate(result, options) {
|
|
140
|
+
console.log(`\n${chalk.green("✓")} Created project files:`);
|
|
141
|
+
for (const file of result.createdFiles) {
|
|
142
|
+
const relative = path.relative(process.cwd(), file);
|
|
143
|
+
console.log(` ${chalk.dim(relative || file)}`);
|
|
144
|
+
}
|
|
145
|
+
let installed = false;
|
|
146
|
+
if (!options.noInstall) {
|
|
147
|
+
try {
|
|
148
|
+
const { pm } = installProjectDependencies({ projectDir: result.outDir });
|
|
149
|
+
installed = true;
|
|
150
|
+
console.log(`\n${chalk.green("✓")} Installed dependencies via ${pm}.`);
|
|
151
|
+
}
|
|
152
|
+
catch (err) {
|
|
153
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
154
|
+
const relativeDir = path.relative(process.cwd(), result.outDir) || '.';
|
|
155
|
+
console.log(`\n${chalk.yellow("!")} Could not install dependencies automatically: ${message}`);
|
|
156
|
+
console.log(chalk.dim(` Run '${chalk.white("npm install")}' manually in ${relativeDir} when ready.`));
|
|
90
157
|
}
|
|
91
|
-
printInitNextSteps(result.options, result.outDir);
|
|
92
158
|
}
|
|
159
|
+
printInitNextSteps(result.options, result.outDir, { installed });
|
|
93
160
|
}
|
|
94
161
|
async function handleBoardAdd(options) {
|
|
95
162
|
const { scaffoldBoardPackages, parseBoardSpec, generateFrameworkChecklist } = await import("./create/index.js");
|
|
@@ -154,6 +221,38 @@ async function main() {
|
|
|
154
221
|
}
|
|
155
222
|
return;
|
|
156
223
|
}
|
|
224
|
+
if (options.command === "doctor" || options.command === "licenses") {
|
|
225
|
+
// These commands run standalone (often before a build), so the framework
|
|
226
|
+
// isn't loaded yet. Load it from the config's framework field so the
|
|
227
|
+
// framework-supplied doctor/licenses handlers are available.
|
|
228
|
+
if (!hasLoadedFramework()) {
|
|
229
|
+
const config = loadCuttlefishConfig(process.cwd());
|
|
230
|
+
if (config?.framework) {
|
|
231
|
+
try {
|
|
232
|
+
loadFrameworkPackage(config.framework, process.cwd());
|
|
233
|
+
}
|
|
234
|
+
catch {
|
|
235
|
+
// Framework package not resolvable — fall through to the
|
|
236
|
+
// no-support message below rather than crashing the command.
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
const fw = hasLoadedFramework() ? getLoadedFramework() : undefined;
|
|
241
|
+
if (options.command === "doctor") {
|
|
242
|
+
if (!fw?.doctor) {
|
|
243
|
+
ui.printInfo("This framework provides no doctor support.");
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
fw.doctor();
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
if (!fw?.licenses) {
|
|
250
|
+
ui.printInfo("This framework provides no licenses support.");
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
fw.licenses(options.strict ?? false, options.all ?? false);
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
157
256
|
// ── Handle build command — entry point comes from config ──────────
|
|
158
257
|
if (options.command === "build") {
|
|
159
258
|
const buildConfig = loadCuttlefishConfig(process.cwd());
|
|
@@ -184,6 +283,37 @@ async function main() {
|
|
|
184
283
|
// it intentionally has no inputFile (it scans scanDir instead), so the
|
|
185
284
|
// generic "Missing input file path" check would otherwise block it.
|
|
186
285
|
if (options.command === "gen-decls") {
|
|
286
|
+
// Check for --components flag (ESP-IDF components: managed + local).
|
|
287
|
+
// Runs before scanDir/single-file branches; components mode has no
|
|
288
|
+
// inputFile by design (it scans managed_components/ + components/).
|
|
289
|
+
const componentsDir = options.componentsDir;
|
|
290
|
+
if (componentsDir) {
|
|
291
|
+
ui.printHeader();
|
|
292
|
+
ui.printStep(`Generating component declarations for ${componentsDir}...`);
|
|
293
|
+
const config = loadCuttlefishConfig(componentsDir);
|
|
294
|
+
const frameworkConfig = (config?.frameworkConfig ?? {});
|
|
295
|
+
const componentsNode = frameworkConfig?.components ?? {};
|
|
296
|
+
const managedSpecs = Object.keys(componentsNode.managed ?? {});
|
|
297
|
+
// idf.py stores managed deps as <namespace>__<name> (slashes → __).
|
|
298
|
+
const managedNames = managedSpecs.map((spec) => spec.replace("/", "__"));
|
|
299
|
+
const localPaths = (componentsNode.local ?? []).map((p) => path.isAbsolute(p) ? p : path.resolve(componentsDir, p));
|
|
300
|
+
const builtinNames = componentsNode.builtin ?? [];
|
|
301
|
+
const created = generateComponentDeclsForProject(componentsDir, {
|
|
302
|
+
managed: managedNames,
|
|
303
|
+
local: localPaths,
|
|
304
|
+
builtin: [],
|
|
305
|
+
idfRoot: undefined,
|
|
306
|
+
});
|
|
307
|
+
if (created.length === 0) {
|
|
308
|
+
ui.printInfo("No component declaration files created.");
|
|
309
|
+
}
|
|
310
|
+
else {
|
|
311
|
+
ui.printSuccess(`Created ${created.length} declaration file(s):`);
|
|
312
|
+
for (const f of created)
|
|
313
|
+
ui.printFileCreated(f);
|
|
314
|
+
}
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
187
317
|
// Check for --all flag (scan directory)
|
|
188
318
|
const scanDir = options.scanDir;
|
|
189
319
|
if (scanDir) {
|
|
@@ -255,8 +385,56 @@ async function main() {
|
|
|
255
385
|
// ── Load cuttlefish.config.ts (config wins over CLI flags) ──────────
|
|
256
386
|
const inputDir = path.dirname(path.resolve(options.inputFile));
|
|
257
387
|
const config = loadCuttlefishConfig(inputDir);
|
|
388
|
+
// ── Contract-based board narrowing ──────────────────────────────────
|
|
389
|
+
// If the config names a TypeCAD contract (*.contract.json from typecad.net),
|
|
390
|
+
// generate the narrowed `.cuttlefish/board.ts` BEFORE env.d.ts is written
|
|
391
|
+
// (it emits `export * from './board.js'` for this case) and before the
|
|
392
|
+
// board package is resolved downstream. The generated board re-exports
|
|
393
|
+
// only the pins/peripherals the PCB actually wires.
|
|
394
|
+
if (config?.contract) {
|
|
395
|
+
await generateContractBoard(config);
|
|
396
|
+
}
|
|
397
|
+
// ── Pre-transpile: generate component .d.ts stubs ───────────────────
|
|
398
|
+
// The transpile type-check needs the .d.ts imports in main.ts to resolve.
|
|
399
|
+
// For ESP-IDF builtin/managed/local components, those .d.ts files are
|
|
400
|
+
// generated by gen-decls — so we must run it BEFORE type-checking, not
|
|
401
|
+
// only as part of --compile. The cache is written under the transpile
|
|
402
|
+
// output dir (the same path --compile uses), so user imports resolve
|
|
403
|
+
// identically in both flows.
|
|
404
|
+
if (config?.frameworkConfig) {
|
|
405
|
+
const fc = config.frameworkConfig;
|
|
406
|
+
const componentsNode = fc?.components ?? {};
|
|
407
|
+
const managedSpecs = Object.keys(componentsNode.managed ?? {});
|
|
408
|
+
const localPaths = (componentsNode.local ?? []).map((p) => path.isAbsolute(p) ? p : path.resolve(inputDir, p));
|
|
409
|
+
const builtinNames = componentsNode.builtin ?? [];
|
|
410
|
+
if (managedSpecs.length > 0 || localPaths.length > 0 || builtinNames.length > 0) {
|
|
411
|
+
// Resolve the transpile output dir — the cache lives under it so the
|
|
412
|
+
// framework's compile path and this pre-transpile pass agree on location.
|
|
413
|
+
// output.outDir in config is relative to the project root (inputDir);
|
|
414
|
+
// if absent, the transpile output goes alongside the entry file.
|
|
415
|
+
const outBase = config.outputOutDir
|
|
416
|
+
? path.resolve(inputDir, config.outputOutDir)
|
|
417
|
+
: inputDir;
|
|
418
|
+
try {
|
|
419
|
+
generateComponentDeclsForProject(outBase, {
|
|
420
|
+
managed: managedSpecs.map((s) => s.replace('/', '__')),
|
|
421
|
+
local: localPaths,
|
|
422
|
+
builtin: [],
|
|
423
|
+
idfRoot: undefined,
|
|
424
|
+
});
|
|
425
|
+
}
|
|
426
|
+
catch {
|
|
427
|
+
// Non-fatal: if gen-decls fails (e.g. component not yet fetched),
|
|
428
|
+
// the type-checker will surface the missing-import errors with
|
|
429
|
+
// clearer context than crashing here.
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
}
|
|
258
433
|
// Load the UI engine (if @typecad/ui is installed) before any UI work.
|
|
259
434
|
await loadUIEngine();
|
|
435
|
+
// Load the safety engine (if @typecad/safety is installed) before any
|
|
436
|
+
// safety work.
|
|
437
|
+
await loadSafetyEngine();
|
|
260
438
|
let effectivePlatformContext = options.platformContext;
|
|
261
439
|
let effectiveTarget = options.target;
|
|
262
440
|
let effectiveOutDir = options.outDir;
|
|
@@ -277,9 +455,16 @@ async function main() {
|
|
|
277
455
|
}
|
|
278
456
|
let configBuildTarget = config.buildTarget;
|
|
279
457
|
if (configBuildTarget) {
|
|
458
|
+
// Reconstruct frameworkData from buildTarget so the toolchain sees the
|
|
459
|
+
// full frameworkData, not just buildTarget.
|
|
280
460
|
effectivePlatformContext = {
|
|
281
461
|
architecture: configBuildTarget.split(":")?.[1]?.toLowerCase(),
|
|
282
|
-
frameworkData: {
|
|
462
|
+
frameworkData: {
|
|
463
|
+
buildTarget: configBuildTarget,
|
|
464
|
+
// Thread the PSRAM type through to the framework so it can emit the
|
|
465
|
+
// PSRAM-enabling Kconfig (Zephyr) / define + FQBN option (Arduino).
|
|
466
|
+
...(config.psram ? { psram: config.psram } : {}),
|
|
467
|
+
},
|
|
283
468
|
};
|
|
284
469
|
}
|
|
285
470
|
if (config.outputOutDir && !options.outDir) {
|
|
@@ -358,15 +543,21 @@ async function main() {
|
|
|
358
543
|
diagnostics: options.diagnostics,
|
|
359
544
|
display: displayConfigForTranspile(config),
|
|
360
545
|
projectRoot: options.projectRoot,
|
|
546
|
+
autosar: options.autosar,
|
|
547
|
+
autosarArxml: options.autosarArxml,
|
|
361
548
|
});
|
|
362
549
|
printDiagnostics(result.diagnostics);
|
|
363
550
|
ui.printTasks(result.asyncTaskNames ?? [], result.usesTimers ?? false);
|
|
551
|
+
if (result.diagnosticsReportPath) {
|
|
552
|
+
ui.printInfo(`Diagnostics report: ${result.diagnosticsReportPath}`);
|
|
553
|
+
}
|
|
364
554
|
if (result.diagnostics.length === 0) {
|
|
365
555
|
ui.printSuccess();
|
|
366
556
|
initialBuildOk = true;
|
|
367
557
|
// Initial compile + upload if flags are set
|
|
368
558
|
if (options.compile) {
|
|
369
|
-
const
|
|
559
|
+
const baseBuildTarget = effectivePlatformContext?.frameworkData?.buildTarget ?? options.platformContext?.frameworkData?.buildTarget;
|
|
560
|
+
const { buildTarget, defines: psramDefines } = applyPsramToArduinoBuild(baseBuildTarget, config?.outputDefines, config?.psram);
|
|
370
561
|
const watchOpts = {
|
|
371
562
|
outputDir: path.dirname(result.sourcePath),
|
|
372
563
|
sourcePath: result.sourcePath,
|
|
@@ -375,8 +566,12 @@ async function main() {
|
|
|
375
566
|
baud: options.baud ?? config?.console?.baudRate,
|
|
376
567
|
optimize: config?.outputOptimize,
|
|
377
568
|
extraFlags: config?.outputExtraFlags,
|
|
378
|
-
defines:
|
|
569
|
+
defines: psramDefines,
|
|
570
|
+
psram: config?.psram,
|
|
379
571
|
frameworkConfig: config?.frameworkConfig,
|
|
572
|
+
zephyrConfig: config?.zephyrConfig,
|
|
573
|
+
display: displayConfigForTranspile(config),
|
|
574
|
+
debug: options.debug,
|
|
380
575
|
};
|
|
381
576
|
ui.printCompiling(buildTarget ?? "native");
|
|
382
577
|
const compileResult = compileSource(watchOpts);
|
|
@@ -448,16 +643,22 @@ async function main() {
|
|
|
448
643
|
diagnostics: options.diagnostics,
|
|
449
644
|
display: displayConfigForTranspile(config),
|
|
450
645
|
projectRoot: options.projectRoot,
|
|
646
|
+
autosar: options.autosar,
|
|
647
|
+
autosarArxml: options.autosarArxml,
|
|
451
648
|
});
|
|
452
649
|
printDiagnostics(rebuildResult.diagnostics);
|
|
453
650
|
ui.printTasks(rebuildResult.asyncTaskNames ?? [], rebuildResult.usesTimers ?? false);
|
|
651
|
+
if (rebuildResult.diagnosticsReportPath) {
|
|
652
|
+
ui.printInfo(`Diagnostics report: ${rebuildResult.diagnosticsReportPath}`);
|
|
653
|
+
}
|
|
454
654
|
if (rebuildResult.diagnostics.length > 0) {
|
|
455
655
|
// Errors shown via printDiagnostics — skip compile/upload
|
|
456
656
|
}
|
|
457
657
|
else {
|
|
458
658
|
ui.printSuccess();
|
|
459
659
|
if (options.compile) {
|
|
460
|
-
const
|
|
660
|
+
const baseBuildTarget = effectivePlatformContext?.frameworkData?.buildTarget ?? options.platformContext?.frameworkData?.buildTarget;
|
|
661
|
+
const { buildTarget, defines: psramDefines } = applyPsramToArduinoBuild(baseBuildTarget, config?.outputDefines, config?.psram);
|
|
461
662
|
const rebuildOpts = {
|
|
462
663
|
outputDir: path.dirname(rebuildResult.sourcePath),
|
|
463
664
|
sourcePath: rebuildResult.sourcePath,
|
|
@@ -466,8 +667,12 @@ async function main() {
|
|
|
466
667
|
baud: options.baud ?? config?.console?.baudRate,
|
|
467
668
|
optimize: config?.outputOptimize,
|
|
468
669
|
extraFlags: config?.outputExtraFlags,
|
|
469
|
-
defines:
|
|
670
|
+
defines: psramDefines,
|
|
671
|
+
psram: config?.psram,
|
|
470
672
|
frameworkConfig: config?.frameworkConfig,
|
|
673
|
+
zephyrConfig: config?.zephyrConfig,
|
|
674
|
+
display: displayConfigForTranspile(config),
|
|
675
|
+
debug: options.debug,
|
|
471
676
|
};
|
|
472
677
|
ui.printCompiling(buildTarget ?? "native");
|
|
473
678
|
const compileResult = compileSource(rebuildOpts);
|
|
@@ -532,9 +737,14 @@ async function main() {
|
|
|
532
737
|
diagnostics: options.diagnostics,
|
|
533
738
|
display: displayConfigForTranspile(config) ?? options.display,
|
|
534
739
|
projectRoot: options.projectRoot,
|
|
740
|
+
autosar: options.autosar,
|
|
741
|
+
autosarArxml: options.autosarArxml,
|
|
535
742
|
});
|
|
536
743
|
printDiagnostics(result.diagnostics);
|
|
537
744
|
ui.printTasks(result.asyncTaskNames ?? [], result.usesTimers ?? false);
|
|
745
|
+
if (result.diagnosticsReportPath) {
|
|
746
|
+
ui.printInfo(`Diagnostics report: ${result.diagnosticsReportPath}`);
|
|
747
|
+
}
|
|
538
748
|
}
|
|
539
749
|
if (hasFatalDiagnostics(result)) {
|
|
540
750
|
ui.printError("Transpilation failed. Fix the transpiler diagnostics above before compiling.");
|
|
@@ -555,7 +765,8 @@ async function main() {
|
|
|
555
765
|
return;
|
|
556
766
|
}
|
|
557
767
|
// --compile (delegates to the active framework's toolchain)
|
|
558
|
-
const
|
|
768
|
+
const baseBuildTarget = effectivePlatformContext?.frameworkData?.buildTarget ?? options.platformContext?.frameworkData?.buildTarget;
|
|
769
|
+
const { buildTarget, defines: psramDefines } = applyPsramToArduinoBuild(baseBuildTarget, config?.outputDefines, config?.psram);
|
|
559
770
|
const toolchainOpts = {
|
|
560
771
|
outputDir: path.dirname(result.sourcePath),
|
|
561
772
|
sourcePath: result.sourcePath,
|
|
@@ -564,8 +775,12 @@ async function main() {
|
|
|
564
775
|
baud: options.baud ?? config?.console?.baudRate,
|
|
565
776
|
optimize: config?.outputOptimize,
|
|
566
777
|
extraFlags: config?.outputExtraFlags,
|
|
567
|
-
defines:
|
|
778
|
+
defines: psramDefines,
|
|
779
|
+
psram: config?.psram,
|
|
568
780
|
frameworkConfig: config?.frameworkConfig,
|
|
781
|
+
zephyrConfig: config?.zephyrConfig,
|
|
782
|
+
display: displayConfigForTranspile(config),
|
|
783
|
+
debug: options.debug,
|
|
569
784
|
};
|
|
570
785
|
ui.printCompiling(buildTarget ?? "native");
|
|
571
786
|
let compileResult;
|
|
@@ -616,8 +831,10 @@ async function main() {
|
|
|
616
831
|
return;
|
|
617
832
|
}
|
|
618
833
|
if (options.monitor) {
|
|
619
|
-
// --monitor (blocks until Ctrl+C)
|
|
620
|
-
|
|
834
|
+
// --monitor (blocks until Ctrl+C). Show the resolved baud (config ?? --baud)
|
|
835
|
+
// in the banner — toolchainOpts.baud is built the same way, so what the
|
|
836
|
+
// user sees is what the framework monitor opens the port at.
|
|
837
|
+
ui.printMonitoring(port, toolchainOpts.baud ?? 115200);
|
|
621
838
|
monitorDevice(toolchainOpts);
|
|
622
839
|
return;
|
|
623
840
|
}
|
package/dist/config-loader.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ export interface ResolvedCuttlefishConfig {
|
|
|
13
13
|
contract?: string;
|
|
14
14
|
/** Build target identifier (e.g. FQBN for Arduino CLI). */
|
|
15
15
|
buildTarget?: string;
|
|
16
|
-
/** Output framework (e.g. 'arduino'
|
|
16
|
+
/** Output framework (e.g. 'arduino'). */
|
|
17
17
|
outputFramework?: string;
|
|
18
18
|
/** Optimization level. */
|
|
19
19
|
outputOptimize?: string;
|
|
@@ -21,9 +21,12 @@ export interface ResolvedCuttlefishConfig {
|
|
|
21
21
|
outputOutDir?: string;
|
|
22
22
|
/**
|
|
23
23
|
* Framework package for code generation strategy.
|
|
24
|
-
* Can be '@typecad/framework-arduino', '@typecad/framework-
|
|
24
|
+
* Can be '@typecad/framework-arduino', '@typecad/framework-zephyr',
|
|
25
|
+
* '@typecad/framework-native', or a custom path.
|
|
25
26
|
*/
|
|
26
27
|
framework?: string;
|
|
28
|
+
/** ESP32 PSRAM type ('opi' | 'quad') when the target board has PSRAM. */
|
|
29
|
+
psram?: 'opi' | 'quad';
|
|
27
30
|
/** Entry point TypeScript file (relative to config file directory). */
|
|
28
31
|
entry?: string;
|
|
29
32
|
/** Path to the config file that was loaded. */
|
|
@@ -39,6 +42,8 @@ export interface ResolvedCuttlefishConfig {
|
|
|
39
42
|
outputDefines?: Record<string, string>;
|
|
40
43
|
/** Framework-specific config (e.g. `native` section). */
|
|
41
44
|
frameworkConfig?: Record<string, unknown>;
|
|
45
|
+
/** Zephyr-specific config (e.g. `zephyr` section). */
|
|
46
|
+
zephyrConfig?: Record<string, unknown>;
|
|
42
47
|
/** Display profile config. */
|
|
43
48
|
display?: import("./api/shared/display-profile.js").DisplayConfig;
|
|
44
49
|
}
|
package/dist/config-loader.js
CHANGED
|
@@ -297,6 +297,9 @@ export function parseConfigFile(configPath) {
|
|
|
297
297
|
const framework = flat.get("framework");
|
|
298
298
|
if (typeof framework === "string")
|
|
299
299
|
resolved.framework = framework;
|
|
300
|
+
const psram = flat.get("psram");
|
|
301
|
+
if (psram === "opi" || psram === "quad")
|
|
302
|
+
resolved.psram = psram;
|
|
300
303
|
const outputFramework = flat.get("output.framework");
|
|
301
304
|
if (typeof outputFramework === "string")
|
|
302
305
|
resolved.outputFramework = outputFramework;
|
|
@@ -323,8 +326,20 @@ export function parseConfigFile(configPath) {
|
|
|
323
326
|
if (outputDefines)
|
|
324
327
|
resolved.outputDefines = outputDefines;
|
|
325
328
|
const nativeSection = extractFrameworkSection(configObject, "native");
|
|
326
|
-
if (nativeSection)
|
|
329
|
+
if (nativeSection) {
|
|
327
330
|
resolved.frameworkConfig = nativeSection;
|
|
331
|
+
}
|
|
332
|
+
// Parse zephyr-specific config section.
|
|
333
|
+
const zephyrKconfig = extractStringRecord(configObject, ["zephyr", "kconfig"]);
|
|
334
|
+
const zephyrCmakeArgs = extractStringArray(configObject, ["zephyr", "cmakeArgs"]);
|
|
335
|
+
const zephyrRunner = flat.get("zephyr.runner");
|
|
336
|
+
if (zephyrKconfig || zephyrCmakeArgs || typeof zephyrRunner === "string") {
|
|
337
|
+
resolved.zephyrConfig = {
|
|
338
|
+
...(zephyrKconfig ? { kconfig: zephyrKconfig } : {}),
|
|
339
|
+
...(zephyrCmakeArgs ? { cmakeArgs: zephyrCmakeArgs } : {}),
|
|
340
|
+
...(typeof zephyrRunner === "string" ? { runner: zephyrRunner } : {}),
|
|
341
|
+
};
|
|
342
|
+
}
|
|
328
343
|
// Parse display profile config (nested object with profile name, wiring, touch)
|
|
329
344
|
const displaySection = extractFrameworkSection(configObject, "display");
|
|
330
345
|
if (displaySection)
|
|
@@ -344,6 +359,8 @@ export function parseConfigFile(configPath) {
|
|
|
344
359
|
structuredForValidation.entry = resolved.entry;
|
|
345
360
|
if (resolved.framework)
|
|
346
361
|
structuredForValidation.framework = resolved.framework;
|
|
362
|
+
if (resolved.psram)
|
|
363
|
+
structuredForValidation.psram = resolved.psram;
|
|
347
364
|
if (resolved.outputFramework || resolved.outputOptimize || resolved.outputOutDir || resolved.outputExtraFlags || resolved.outputDefines) {
|
|
348
365
|
structuredForValidation.output = {
|
|
349
366
|
...(resolved.outputFramework ? { framework: resolved.outputFramework } : {}),
|
|
@@ -355,6 +372,8 @@ export function parseConfigFile(configPath) {
|
|
|
355
372
|
}
|
|
356
373
|
if (resolved.console)
|
|
357
374
|
structuredForValidation.console = resolved.console;
|
|
375
|
+
if (resolved.zephyrConfig)
|
|
376
|
+
structuredForValidation.zephyr = resolved.zephyrConfig;
|
|
358
377
|
if (resolved.buildTarget) {
|
|
359
378
|
structuredForValidation.frameworkData = { buildTarget: resolved.buildTarget };
|
|
360
379
|
}
|
|
@@ -478,8 +497,9 @@ export function generateVirtualTypeDeclaration(config, platformDeclarations) {
|
|
|
478
497
|
// `declare module` in cuttlefish-env.d.ts is treated as module augmentation
|
|
479
498
|
// (because that file has `export {}`), which can't define a new module.
|
|
480
499
|
//
|
|
481
|
-
// For contract-based configs, board.ts is generated by the
|
|
482
|
-
//
|
|
500
|
+
// For contract-based configs, the narrowed board.ts is generated by the
|
|
501
|
+
// contract reader (`contract/board-generator.ts`, invoked from cli.ts before
|
|
502
|
+
// this function runs), so don't overwrite it with the generic re-export here.
|
|
483
503
|
if (!config.contract && (config.board || config.mcu)) {
|
|
484
504
|
const reExportSource = config.board ?? config.mcu;
|
|
485
505
|
const boardTsPath = path.join(cuttlefishDir, "board.ts");
|