@typecad/cuttlefish 1.0.0-alpha.6 → 1.0.0-alpha.7
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/dist/api/shared/display-adapter.d.ts +2 -1
- package/dist/api/shared/display-adapter.js +8 -1
- package/dist/api/shared/display-adapters/sdl.js +9 -2
- package/dist/api/shared/display-profile.d.ts +20 -3
- package/dist/api/shared/display-profile.js +21 -6
- package/dist/api/shared/framework-manifest-registry.d.ts +9 -0
- package/dist/api/shared/framework-manifest-registry.js +25 -0
- package/dist/api/shared/framework-manifest.d.ts +462 -0
- package/dist/api/shared/framework-manifest.js +149 -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 +28 -0
- package/dist/api/shared/hal-op-ir.d.ts +418 -1
- package/dist/api/shared/hal-op-ir.js +95 -1
- package/dist/api/shared/index.d.ts +11 -1
- package/dist/api/shared/index.js +14 -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 +9 -0
- package/dist/api/shared/promise-runtime.js +78 -0
- package/dist/api/shared/validate-framework-manifest.d.ts +28 -0
- package/dist/api/shared/validate-framework-manifest.js +417 -0
- package/dist/cli.js +109 -4
- package/dist/config-loader.js +20 -1
- package/dist/config-schema.d.ts +36 -36
- package/dist/create/board-spec.d.ts +4 -4
- package/dist/create/init-scaffold.d.ts +3 -0
- package/dist/create/init-scaffold.js +48 -0
- package/dist/create/init-templates.d.ts +2 -0
- package/dist/create/init-templates.js +28 -8
- package/dist/create/init-wizard.js +20 -1
- package/dist/emit/cpp-emitter.js +4 -3
- package/dist/emit/emitters/emitter-context.d.ts +5 -0
- package/dist/emit/emitters/function-emitter-impl.js +69 -59
- package/dist/emit/emitters/output-finalizer.d.ts +6 -0
- package/dist/emit/emitters/output-finalizer.js +21 -11
- package/dist/emit/emitters/setup.js +40 -0
- package/dist/emit/emitters/top-level-prep.js +2 -0
- package/dist/emit/emitters/ui-emitter.js +23 -8
- package/dist/emit/expression-renderer.js +10 -1
- package/dist/emit/snprintf-helpers.js +8 -0
- package/dist/emit/statement-renderer.js +13 -0
- package/dist/emit/utils/async-state-machine.js +185 -116
- 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/ir/build-ir.js +5 -1
- package/dist/ir/expression-to-ir.js +14 -0
- package/dist/ir/feature-registry.js +7 -25
- package/dist/ir/hal/hal-emitter.d.ts +5 -2
- package/dist/ir/hal/hal-emitter.js +40 -12
- package/dist/ir/hal/hal-parser.d.ts +6 -0
- package/dist/ir/hal/hal-parser.js +74 -0
- package/dist/ir/hal/hal-plugins.js +561 -0
- package/dist/ir/identifier-collector.js +18 -0
- package/dist/ir/network-validation.d.ts +4 -0
- package/dist/ir/network-validation.js +184 -0
- package/dist/ir/ownership-analysis.js +14 -1
- package/dist/ir/program-analysis.d.ts +23 -0
- package/dist/ir/program-analysis.js +81 -2
- package/dist/ir/timing-validation.d.ts +6 -1
- package/dist/ir/timing-validation.js +50 -12
- package/dist/ir/transformers/expressions.js +58 -0
- package/dist/ir/transformers/hal-emit-helpers.js +1 -1
- package/dist/ir/transformers/variables.js +41 -3
- package/dist/ir/validation-orchestrator.js +3 -1
- 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/idf-discovery.d.ts +7 -0
- package/dist/libdef/idf-discovery.js +59 -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 +6 -2
- package/dist/stores/display-profile-store.d.ts +1 -0
- package/dist/stores/display-profile-store.js +1 -0
- package/dist/testing.d.ts +3 -1
- package/dist/testing.js +3 -1
- package/dist/transpile.d.ts +3 -0
- package/dist/transpile.js +72 -27
- package/dist/ui-hook.d.ts +17 -1
- package/dist/utils/cli.js +27 -1
- package/dist/utils/fs.d.ts +13 -0
- package/dist/utils/fs.js +50 -0
- package/package.json +8 -4
package/dist/cli.js
CHANGED
|
@@ -5,7 +5,8 @@ import { parseCommandLine, printHelp } from "./utils/cli.js";
|
|
|
5
5
|
import { scaffoldProject, printInitNextSteps, KNOWN_TARGETS } from "./create/index.js";
|
|
6
6
|
import { runInitWizard } from "./create/index.js";
|
|
7
7
|
import { generateLibraryDefinitions, transpileFile } from "./transpile.js";
|
|
8
|
-
import { generateDecl, generateDeclsForDirectory } from "./libdef/cpp-to-decl.js";
|
|
8
|
+
import { generateDecl, generateDeclsForDirectory, generateComponentDeclsForProject } from "./libdef/cpp-to-decl.js";
|
|
9
|
+
import { discoverIdfRootForGenDecls } from "./libdef/idf-discovery.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";
|
|
@@ -49,8 +50,14 @@ async function handleCreate(options) {
|
|
|
49
50
|
? '@typecad/framework-avr'
|
|
50
51
|
: framework === 'arduino'
|
|
51
52
|
? '@typecad/framework-arduino'
|
|
52
|
-
:
|
|
53
|
+
: framework === 'esp32'
|
|
54
|
+
? '@typecad/framework-esp32'
|
|
55
|
+
: `@typecad/framework-${framework}`;
|
|
53
56
|
const projectName = options.projectName || 'my-project';
|
|
57
|
+
const isEspIdf = frameworkPackage === '@typecad/framework-esp32' || framework === 'esp32';
|
|
58
|
+
const idfTarget = target.frameworkData?.target
|
|
59
|
+
?? target.architecture
|
|
60
|
+
?? 'esp32';
|
|
54
61
|
const result = scaffoldProject({
|
|
55
62
|
projectName,
|
|
56
63
|
targetId: target.id,
|
|
@@ -60,10 +67,18 @@ async function handleCreate(options) {
|
|
|
60
67
|
boardPackage: target.boardPackage,
|
|
61
68
|
frameworkPackage,
|
|
62
69
|
framework,
|
|
63
|
-
buildTarget: target.buildTarget,
|
|
70
|
+
buildTarget: isEspIdf ? idfTarget : target.buildTarget,
|
|
64
71
|
mcu: target.mcu,
|
|
65
72
|
baudRate: target.isNative ? undefined : (options.baud ?? 9600),
|
|
66
73
|
includeSketch: !options.noSketch,
|
|
74
|
+
...(isEspIdf
|
|
75
|
+
? {
|
|
76
|
+
toolchainType: 'idf',
|
|
77
|
+
frameworkData: target.frameworkData ?? { target: idfTarget, buildTarget: idfTarget },
|
|
78
|
+
}
|
|
79
|
+
: target.frameworkData
|
|
80
|
+
? { frameworkData: target.frameworkData }
|
|
81
|
+
: {}),
|
|
67
82
|
}, options.outDir);
|
|
68
83
|
console.log(`\n${chalk.green("✓")} Created project files:`);
|
|
69
84
|
for (const file of result.createdFiles) {
|
|
@@ -250,6 +265,47 @@ async function main() {
|
|
|
250
265
|
// it intentionally has no inputFile (it scans scanDir instead), so the
|
|
251
266
|
// generic "Missing input file path" check would otherwise block it.
|
|
252
267
|
if (options.command === "gen-decls") {
|
|
268
|
+
// Check for --components flag (ESP-IDF components: managed + local).
|
|
269
|
+
// Runs before scanDir/single-file branches; components mode has no
|
|
270
|
+
// inputFile by design (it scans managed_components/ + components/).
|
|
271
|
+
const componentsDir = options.componentsDir;
|
|
272
|
+
if (componentsDir) {
|
|
273
|
+
ui.printHeader();
|
|
274
|
+
ui.printStep(`Generating component declarations for ${componentsDir}...`);
|
|
275
|
+
const config = loadCuttlefishConfig(componentsDir);
|
|
276
|
+
const frameworkConfig = (config?.frameworkConfig ?? {});
|
|
277
|
+
const componentsNode = frameworkConfig?.components ?? {};
|
|
278
|
+
const managedSpecs = Object.keys(componentsNode.managed ?? {});
|
|
279
|
+
// idf.py stores managed deps as <namespace>__<name> (slashes → __).
|
|
280
|
+
const managedNames = managedSpecs.map((spec) => spec.replace("/", "__"));
|
|
281
|
+
const localPaths = (componentsNode.local ?? []).map((p) => path.isAbsolute(p) ? p : path.resolve(componentsDir, p));
|
|
282
|
+
const builtinNames = componentsNode.builtin ?? [];
|
|
283
|
+
// Built-in components resolve against an ESP-IDF install. The CLI
|
|
284
|
+
// uses minimal discovery (env var + well-known paths). If no install
|
|
285
|
+
// is found, builtins are skipped with a notice — the framework-esp32
|
|
286
|
+
// compile path performs richer discovery and is the primary entry
|
|
287
|
+
// point for builtin gen-decls.
|
|
288
|
+
const idfRoot = discoverIdfRootForGenDecls();
|
|
289
|
+
if (builtinNames.length > 0 && !idfRoot) {
|
|
290
|
+
ui.printInfo('components.builtin declared but no ESP-IDF install was found; skipping builtin headers. ' +
|
|
291
|
+
'Run via `cuttlefish build --compile` (which performs full IDF discovery) or set $IDF_PATH.');
|
|
292
|
+
}
|
|
293
|
+
const created = generateComponentDeclsForProject(componentsDir, {
|
|
294
|
+
managed: managedNames,
|
|
295
|
+
local: localPaths,
|
|
296
|
+
builtin: idfRoot ? builtinNames : [],
|
|
297
|
+
idfRoot,
|
|
298
|
+
});
|
|
299
|
+
if (created.length === 0) {
|
|
300
|
+
ui.printInfo("No component declaration files created.");
|
|
301
|
+
}
|
|
302
|
+
else {
|
|
303
|
+
ui.printSuccess(`Created ${created.length} declaration file(s):`);
|
|
304
|
+
for (const f of created)
|
|
305
|
+
ui.printFileCreated(f);
|
|
306
|
+
}
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
253
309
|
// Check for --all flag (scan directory)
|
|
254
310
|
const scanDir = options.scanDir;
|
|
255
311
|
if (scanDir) {
|
|
@@ -321,6 +377,47 @@ async function main() {
|
|
|
321
377
|
// ── Load cuttlefish.config.ts (config wins over CLI flags) ──────────
|
|
322
378
|
const inputDir = path.dirname(path.resolve(options.inputFile));
|
|
323
379
|
const config = loadCuttlefishConfig(inputDir);
|
|
380
|
+
// ── Pre-transpile: generate component .d.ts stubs ───────────────────
|
|
381
|
+
// The transpile type-check needs the .d.ts imports in main.ts to resolve.
|
|
382
|
+
// For ESP-IDF builtin/managed/local components, those .d.ts files are
|
|
383
|
+
// generated by gen-decls — so we must run it BEFORE type-checking, not
|
|
384
|
+
// only as part of --compile. The cache is written under the transpile
|
|
385
|
+
// output dir (the same path --compile uses), so user imports resolve
|
|
386
|
+
// identically in both flows.
|
|
387
|
+
if (config?.frameworkConfig) {
|
|
388
|
+
const fc = config.frameworkConfig;
|
|
389
|
+
const componentsNode = fc?.components ?? {};
|
|
390
|
+
const managedSpecs = Object.keys(componentsNode.managed ?? {});
|
|
391
|
+
const localPaths = (componentsNode.local ?? []).map((p) => path.isAbsolute(p) ? p : path.resolve(inputDir, p));
|
|
392
|
+
const builtinNames = componentsNode.builtin ?? [];
|
|
393
|
+
if (managedSpecs.length > 0 || localPaths.length > 0 || builtinNames.length > 0) {
|
|
394
|
+
// Resolve the transpile output dir — the cache lives under it so the
|
|
395
|
+
// framework's compile path and this pre-transpile pass agree on location.
|
|
396
|
+
// output.outDir in config is relative to the project root (inputDir);
|
|
397
|
+
// if absent, the transpile output goes alongside the entry file.
|
|
398
|
+
const outBase = config.outputOutDir
|
|
399
|
+
? path.resolve(inputDir, config.outputOutDir)
|
|
400
|
+
: inputDir;
|
|
401
|
+
// For builtins we need IDF_PATH. Use cuttlefish's minimal discovery
|
|
402
|
+
// (env var + well-known paths) — full discovery is done by
|
|
403
|
+
// framework-esp32 at --compile time. If no install is found,
|
|
404
|
+
// builtins are skipped; --compile will regenerate them.
|
|
405
|
+
const idfRoot = discoverIdfRootForGenDecls();
|
|
406
|
+
try {
|
|
407
|
+
generateComponentDeclsForProject(outBase, {
|
|
408
|
+
managed: managedSpecs.map((s) => s.replace('/', '__')),
|
|
409
|
+
local: localPaths,
|
|
410
|
+
builtin: idfRoot ? builtinNames : [],
|
|
411
|
+
idfRoot,
|
|
412
|
+
});
|
|
413
|
+
}
|
|
414
|
+
catch {
|
|
415
|
+
// Non-fatal: if gen-decls fails (e.g. component not yet fetched),
|
|
416
|
+
// the type-checker will surface the missing-import errors with
|
|
417
|
+
// clearer context than crashing here.
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
324
421
|
// Load the UI engine (if @typecad/ui is installed) before any UI work.
|
|
325
422
|
await loadUIEngine();
|
|
326
423
|
let effectivePlatformContext = options.platformContext;
|
|
@@ -343,9 +440,17 @@ async function main() {
|
|
|
343
440
|
}
|
|
344
441
|
let configBuildTarget = config.buildTarget;
|
|
345
442
|
if (configBuildTarget) {
|
|
443
|
+
// Reconstruct frameworkData from buildTarget, preserving other fields
|
|
444
|
+
// (psram, components, etc.) from the resolved frameworkConfig so the
|
|
445
|
+
// transpile-time diagnostics (e.g. scroll-canvas-memory PSRAM budget)
|
|
446
|
+
// and the toolchain both see the full frameworkData, not just buildTarget.
|
|
447
|
+
const fcPsram = config.frameworkConfig?.psram;
|
|
346
448
|
effectivePlatformContext = {
|
|
347
449
|
architecture: configBuildTarget.split(":")?.[1]?.toLowerCase(),
|
|
348
|
-
frameworkData: {
|
|
450
|
+
frameworkData: {
|
|
451
|
+
buildTarget: configBuildTarget,
|
|
452
|
+
...(fcPsram ? { psram: fcPsram } : {}),
|
|
453
|
+
},
|
|
349
454
|
};
|
|
350
455
|
}
|
|
351
456
|
if (config.outputOutDir && !options.outDir) {
|
package/dist/config-loader.js
CHANGED
|
@@ -9,6 +9,12 @@
|
|
|
9
9
|
import path from "node:path";
|
|
10
10
|
import fs from "node:fs";
|
|
11
11
|
import ts from "typescript";
|
|
12
|
+
/** True for the framework-esp32 package or any path that resolves to it.
|
|
13
|
+
* Used to decide whether to route frameworkData → frameworkConfig so the
|
|
14
|
+
* ESP-IDF toolchain can read components, target, etc. */
|
|
15
|
+
function isEspIdfFramework(framework) {
|
|
16
|
+
return framework === "@typecad/framework-esp32" || framework.endsWith("framework-esp32");
|
|
17
|
+
}
|
|
12
18
|
import { safeValidateConfig } from "./config-schema.js";
|
|
13
19
|
/** The filename we search for when walking up directories. */
|
|
14
20
|
const CONFIG_FILENAME = "cuttlefish.config.ts";
|
|
@@ -323,8 +329,21 @@ export function parseConfigFile(configPath) {
|
|
|
323
329
|
if (outputDefines)
|
|
324
330
|
resolved.outputDefines = outputDefines;
|
|
325
331
|
const nativeSection = extractFrameworkSection(configObject, "native");
|
|
326
|
-
if (nativeSection)
|
|
332
|
+
if (nativeSection) {
|
|
327
333
|
resolved.frameworkConfig = nativeSection;
|
|
334
|
+
}
|
|
335
|
+
else if (resolved.framework && isEspIdfFramework(resolved.framework)) {
|
|
336
|
+
// ESP-IDF: route frameworkData through frameworkConfig so the toolchain
|
|
337
|
+
// can read components, target, and other framework-specific fields.
|
|
338
|
+
// extractFrameworkSection walks the AST and preserves nested shapes
|
|
339
|
+
// (components.managed, components.local, etc.).
|
|
340
|
+
const frameworkData = configObject
|
|
341
|
+
? extractFrameworkSection(configObject, "frameworkData")
|
|
342
|
+
: undefined;
|
|
343
|
+
if (frameworkData) {
|
|
344
|
+
resolved.frameworkConfig = frameworkData;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
328
347
|
// Parse display profile config (nested object with profile name, wiring, touch)
|
|
329
348
|
const displaySection = extractFrameworkSection(configObject, "display");
|
|
330
349
|
if (displaySection)
|
package/dist/config-schema.d.ts
CHANGED
|
@@ -41,16 +41,16 @@ export declare const CuttlefishConfigSchema: z.ZodEffects<z.ZodObject<{
|
|
|
41
41
|
buildTarget: z.ZodOptional<z.ZodString>;
|
|
42
42
|
board: z.ZodOptional<z.ZodString>;
|
|
43
43
|
}, "strict", z.ZodTypeAny, {
|
|
44
|
-
buildTarget?: string | undefined;
|
|
45
|
-
port?: string | undefined;
|
|
46
44
|
board?: string | undefined;
|
|
45
|
+
port?: string | undefined;
|
|
46
|
+
buildTarget?: string | undefined;
|
|
47
47
|
baudRate?: number | undefined;
|
|
48
48
|
include?: string[] | undefined;
|
|
49
49
|
timeout?: number | undefined;
|
|
50
50
|
}, {
|
|
51
|
-
buildTarget?: string | undefined;
|
|
52
|
-
port?: string | undefined;
|
|
53
51
|
board?: string | undefined;
|
|
52
|
+
port?: string | undefined;
|
|
53
|
+
buildTarget?: string | undefined;
|
|
54
54
|
baudRate?: number | undefined;
|
|
55
55
|
include?: string[] | undefined;
|
|
56
56
|
timeout?: number | undefined;
|
|
@@ -83,6 +83,12 @@ export declare const CuttlefishConfigSchema: z.ZodEffects<z.ZodObject<{
|
|
|
83
83
|
} | undefined;
|
|
84
84
|
frameworkData?: Record<string, unknown> | undefined;
|
|
85
85
|
target?: string | undefined;
|
|
86
|
+
board?: string | undefined;
|
|
87
|
+
exclude?: string[] | undefined;
|
|
88
|
+
toolchain?: {
|
|
89
|
+
type?: string | undefined;
|
|
90
|
+
frameworkOptions?: Record<string, unknown> | undefined;
|
|
91
|
+
} | undefined;
|
|
86
92
|
framework?: string | undefined;
|
|
87
93
|
native?: Record<string, unknown> | undefined;
|
|
88
94
|
output?: {
|
|
@@ -92,20 +98,14 @@ export declare const CuttlefishConfigSchema: z.ZodEffects<z.ZodObject<{
|
|
|
92
98
|
defines?: Record<string, string> | undefined;
|
|
93
99
|
extraFlags?: string[] | undefined;
|
|
94
100
|
} | undefined;
|
|
95
|
-
board?: string | undefined;
|
|
96
101
|
mcu?: string | undefined;
|
|
97
102
|
include?: string[] | undefined;
|
|
98
103
|
entry?: string | undefined;
|
|
99
|
-
toolchain?: {
|
|
100
|
-
type?: string | undefined;
|
|
101
|
-
frameworkOptions?: Record<string, unknown> | undefined;
|
|
102
|
-
} | undefined;
|
|
103
104
|
contract?: string | undefined;
|
|
104
|
-
exclude?: string[] | undefined;
|
|
105
105
|
test?: {
|
|
106
|
-
buildTarget?: string | undefined;
|
|
107
|
-
port?: string | undefined;
|
|
108
106
|
board?: string | undefined;
|
|
107
|
+
port?: string | undefined;
|
|
108
|
+
buildTarget?: string | undefined;
|
|
109
109
|
baudRate?: number | undefined;
|
|
110
110
|
include?: string[] | undefined;
|
|
111
111
|
timeout?: number | undefined;
|
|
@@ -117,6 +117,12 @@ export declare const CuttlefishConfigSchema: z.ZodEffects<z.ZodObject<{
|
|
|
117
117
|
} | undefined;
|
|
118
118
|
frameworkData?: Record<string, unknown> | undefined;
|
|
119
119
|
target?: string | undefined;
|
|
120
|
+
board?: string | undefined;
|
|
121
|
+
exclude?: string[] | undefined;
|
|
122
|
+
toolchain?: {
|
|
123
|
+
type?: string | undefined;
|
|
124
|
+
frameworkOptions?: Record<string, unknown> | undefined;
|
|
125
|
+
} | undefined;
|
|
120
126
|
framework?: string | undefined;
|
|
121
127
|
native?: Record<string, unknown> | undefined;
|
|
122
128
|
output?: {
|
|
@@ -126,20 +132,14 @@ export declare const CuttlefishConfigSchema: z.ZodEffects<z.ZodObject<{
|
|
|
126
132
|
defines?: Record<string, string> | undefined;
|
|
127
133
|
extraFlags?: string[] | undefined;
|
|
128
134
|
} | undefined;
|
|
129
|
-
board?: string | undefined;
|
|
130
135
|
mcu?: string | undefined;
|
|
131
136
|
include?: string[] | undefined;
|
|
132
137
|
entry?: string | undefined;
|
|
133
|
-
toolchain?: {
|
|
134
|
-
type?: string | undefined;
|
|
135
|
-
frameworkOptions?: Record<string, unknown> | undefined;
|
|
136
|
-
} | undefined;
|
|
137
138
|
contract?: string | undefined;
|
|
138
|
-
exclude?: string[] | undefined;
|
|
139
139
|
test?: {
|
|
140
|
-
buildTarget?: string | undefined;
|
|
141
|
-
port?: string | undefined;
|
|
142
140
|
board?: string | undefined;
|
|
141
|
+
port?: string | undefined;
|
|
142
|
+
buildTarget?: string | undefined;
|
|
143
143
|
baudRate?: number | undefined;
|
|
144
144
|
include?: string[] | undefined;
|
|
145
145
|
timeout?: number | undefined;
|
|
@@ -151,6 +151,12 @@ export declare const CuttlefishConfigSchema: z.ZodEffects<z.ZodObject<{
|
|
|
151
151
|
} | undefined;
|
|
152
152
|
frameworkData?: Record<string, unknown> | undefined;
|
|
153
153
|
target?: string | undefined;
|
|
154
|
+
board?: string | undefined;
|
|
155
|
+
exclude?: string[] | undefined;
|
|
156
|
+
toolchain?: {
|
|
157
|
+
type?: string | undefined;
|
|
158
|
+
frameworkOptions?: Record<string, unknown> | undefined;
|
|
159
|
+
} | undefined;
|
|
154
160
|
framework?: string | undefined;
|
|
155
161
|
native?: Record<string, unknown> | undefined;
|
|
156
162
|
output?: {
|
|
@@ -160,20 +166,14 @@ export declare const CuttlefishConfigSchema: z.ZodEffects<z.ZodObject<{
|
|
|
160
166
|
defines?: Record<string, string> | undefined;
|
|
161
167
|
extraFlags?: string[] | undefined;
|
|
162
168
|
} | undefined;
|
|
163
|
-
board?: string | undefined;
|
|
164
169
|
mcu?: string | undefined;
|
|
165
170
|
include?: string[] | undefined;
|
|
166
171
|
entry?: string | undefined;
|
|
167
|
-
toolchain?: {
|
|
168
|
-
type?: string | undefined;
|
|
169
|
-
frameworkOptions?: Record<string, unknown> | undefined;
|
|
170
|
-
} | undefined;
|
|
171
172
|
contract?: string | undefined;
|
|
172
|
-
exclude?: string[] | undefined;
|
|
173
173
|
test?: {
|
|
174
|
-
buildTarget?: string | undefined;
|
|
175
|
-
port?: string | undefined;
|
|
176
174
|
board?: string | undefined;
|
|
175
|
+
port?: string | undefined;
|
|
176
|
+
buildTarget?: string | undefined;
|
|
177
177
|
baudRate?: number | undefined;
|
|
178
178
|
include?: string[] | undefined;
|
|
179
179
|
timeout?: number | undefined;
|
|
@@ -185,6 +185,12 @@ export declare const CuttlefishConfigSchema: z.ZodEffects<z.ZodObject<{
|
|
|
185
185
|
} | undefined;
|
|
186
186
|
frameworkData?: Record<string, unknown> | undefined;
|
|
187
187
|
target?: string | undefined;
|
|
188
|
+
board?: string | undefined;
|
|
189
|
+
exclude?: string[] | undefined;
|
|
190
|
+
toolchain?: {
|
|
191
|
+
type?: string | undefined;
|
|
192
|
+
frameworkOptions?: Record<string, unknown> | undefined;
|
|
193
|
+
} | undefined;
|
|
188
194
|
framework?: string | undefined;
|
|
189
195
|
native?: Record<string, unknown> | undefined;
|
|
190
196
|
output?: {
|
|
@@ -194,20 +200,14 @@ export declare const CuttlefishConfigSchema: z.ZodEffects<z.ZodObject<{
|
|
|
194
200
|
defines?: Record<string, string> | undefined;
|
|
195
201
|
extraFlags?: string[] | undefined;
|
|
196
202
|
} | undefined;
|
|
197
|
-
board?: string | undefined;
|
|
198
203
|
mcu?: string | undefined;
|
|
199
204
|
include?: string[] | undefined;
|
|
200
205
|
entry?: string | undefined;
|
|
201
|
-
toolchain?: {
|
|
202
|
-
type?: string | undefined;
|
|
203
|
-
frameworkOptions?: Record<string, unknown> | undefined;
|
|
204
|
-
} | undefined;
|
|
205
206
|
contract?: string | undefined;
|
|
206
|
-
exclude?: string[] | undefined;
|
|
207
207
|
test?: {
|
|
208
|
-
buildTarget?: string | undefined;
|
|
209
|
-
port?: string | undefined;
|
|
210
208
|
board?: string | undefined;
|
|
209
|
+
port?: string | undefined;
|
|
210
|
+
buildTarget?: string | undefined;
|
|
211
211
|
baudRate?: number | undefined;
|
|
212
212
|
include?: string[] | undefined;
|
|
213
213
|
timeout?: number | undefined;
|
|
@@ -40,6 +40,7 @@ export declare const BoardSpecSchema: z.ZodObject<{
|
|
|
40
40
|
}, "strict", z.ZodTypeAny, {
|
|
41
41
|
gpio: number;
|
|
42
42
|
capabilities: "FULL_GPIO" | "FULL_GPIO_ANALOG" | "FULL_GPIO_TOUCH" | "FULL_GPIO_ANALOG_TOUCH" | "FULL_GPIO_DAC" | "INPUT_ONLY";
|
|
43
|
+
notes?: string | undefined;
|
|
43
44
|
functions?: {
|
|
44
45
|
type: "i2c" | "spi" | "uart" | "adc" | "dac" | "pwm" | "touch" | "usb";
|
|
45
46
|
instance: number;
|
|
@@ -48,11 +49,11 @@ export declare const BoardSpecSchema: z.ZodObject<{
|
|
|
48
49
|
alt?: string[] | undefined;
|
|
49
50
|
warnings?: string[] | undefined;
|
|
50
51
|
unsafe?: boolean | undefined;
|
|
51
|
-
notes?: string | undefined;
|
|
52
52
|
onboardLed?: boolean | undefined;
|
|
53
53
|
}, {
|
|
54
54
|
gpio: number;
|
|
55
55
|
capabilities: "FULL_GPIO" | "FULL_GPIO_ANALOG" | "FULL_GPIO_TOUCH" | "FULL_GPIO_ANALOG_TOUCH" | "FULL_GPIO_DAC" | "INPUT_ONLY";
|
|
56
|
+
notes?: string | undefined;
|
|
56
57
|
functions?: {
|
|
57
58
|
type: "i2c" | "spi" | "uart" | "adc" | "dac" | "pwm" | "touch" | "usb";
|
|
58
59
|
instance: number;
|
|
@@ -61,7 +62,6 @@ export declare const BoardSpecSchema: z.ZodObject<{
|
|
|
61
62
|
alt?: string[] | undefined;
|
|
62
63
|
warnings?: string[] | undefined;
|
|
63
64
|
unsafe?: boolean | undefined;
|
|
64
|
-
notes?: string | undefined;
|
|
65
65
|
onboardLed?: boolean | undefined;
|
|
66
66
|
}>, "many">;
|
|
67
67
|
unsafe: z.ZodArray<z.ZodString, "many">;
|
|
@@ -436,6 +436,7 @@ export declare const BoardSpecSchema: z.ZodObject<{
|
|
|
436
436
|
pins: {
|
|
437
437
|
gpio: number;
|
|
438
438
|
capabilities: "FULL_GPIO" | "FULL_GPIO_ANALOG" | "FULL_GPIO_TOUCH" | "FULL_GPIO_ANALOG_TOUCH" | "FULL_GPIO_DAC" | "INPUT_ONLY";
|
|
439
|
+
notes?: string | undefined;
|
|
439
440
|
functions?: {
|
|
440
441
|
type: "i2c" | "spi" | "uart" | "adc" | "dac" | "pwm" | "touch" | "usb";
|
|
441
442
|
instance: number;
|
|
@@ -444,7 +445,6 @@ export declare const BoardSpecSchema: z.ZodObject<{
|
|
|
444
445
|
alt?: string[] | undefined;
|
|
445
446
|
warnings?: string[] | undefined;
|
|
446
447
|
unsafe?: boolean | undefined;
|
|
447
|
-
notes?: string | undefined;
|
|
448
448
|
onboardLed?: boolean | undefined;
|
|
449
449
|
}[];
|
|
450
450
|
analog: string[];
|
|
@@ -568,6 +568,7 @@ export declare const BoardSpecSchema: z.ZodObject<{
|
|
|
568
568
|
pins: {
|
|
569
569
|
gpio: number;
|
|
570
570
|
capabilities: "FULL_GPIO" | "FULL_GPIO_ANALOG" | "FULL_GPIO_TOUCH" | "FULL_GPIO_ANALOG_TOUCH" | "FULL_GPIO_DAC" | "INPUT_ONLY";
|
|
571
|
+
notes?: string | undefined;
|
|
571
572
|
functions?: {
|
|
572
573
|
type: "i2c" | "spi" | "uart" | "adc" | "dac" | "pwm" | "touch" | "usb";
|
|
573
574
|
instance: number;
|
|
@@ -576,7 +577,6 @@ export declare const BoardSpecSchema: z.ZodObject<{
|
|
|
576
577
|
alt?: string[] | undefined;
|
|
577
578
|
warnings?: string[] | undefined;
|
|
578
579
|
unsafe?: boolean | undefined;
|
|
579
|
-
notes?: string | undefined;
|
|
580
580
|
onboardLed?: boolean | undefined;
|
|
581
581
|
}[];
|
|
582
582
|
analog: string[];
|
|
@@ -10,6 +10,9 @@ export interface KnownTarget {
|
|
|
10
10
|
framework: string;
|
|
11
11
|
buildTarget?: string;
|
|
12
12
|
mcu?: string;
|
|
13
|
+
/** Framework-specific config (becomes frameworkData in cuttlefish.config.ts).
|
|
14
|
+
* Used by framework-esp32 to carry `target: 'esp32'|'esp32s3'|'esp32c3'|'esp32c6'`. */
|
|
15
|
+
frameworkData?: Record<string, unknown>;
|
|
13
16
|
}
|
|
14
17
|
export declare function registerKnownTarget(target: KnownTarget): void;
|
|
15
18
|
export declare const KNOWN_TARGETS: ReadonlyArray<KnownTarget>;
|
|
@@ -33,6 +33,18 @@ const _knownTargets = [
|
|
|
33
33
|
buildTarget: 'esp32:esp32:esp32',
|
|
34
34
|
mcu: 'esp32',
|
|
35
35
|
},
|
|
36
|
+
{
|
|
37
|
+
id: 'esp32-devkit-idf',
|
|
38
|
+
displayName: 'ESP32 DevKit (native ESP-IDF)',
|
|
39
|
+
isNative: false,
|
|
40
|
+
architecture: 'esp32',
|
|
41
|
+
boardPackage: '@typecad/board-esp32-devkit',
|
|
42
|
+
frameworkPackage: '@typecad/framework-esp32',
|
|
43
|
+
framework: 'esp32',
|
|
44
|
+
buildTarget: 'esp32:esp32:esp32',
|
|
45
|
+
mcu: 'esp32',
|
|
46
|
+
frameworkData: { target: 'esp32' },
|
|
47
|
+
},
|
|
36
48
|
{
|
|
37
49
|
id: 'esp32s3',
|
|
38
50
|
displayName: 'ESP32-S3',
|
|
@@ -44,6 +56,18 @@ const _knownTargets = [
|
|
|
44
56
|
buildTarget: 'esp32:esp32:esp32s3',
|
|
45
57
|
mcu: 'esp32s3',
|
|
46
58
|
},
|
|
59
|
+
{
|
|
60
|
+
id: 'esp32s3-idf',
|
|
61
|
+
displayName: 'ESP32-S3 (native ESP-IDF)',
|
|
62
|
+
isNative: false,
|
|
63
|
+
architecture: 'esp32s3',
|
|
64
|
+
boardPackage: '@typecad/board-esp32s3',
|
|
65
|
+
frameworkPackage: '@typecad/framework-esp32',
|
|
66
|
+
framework: 'esp32',
|
|
67
|
+
buildTarget: 'esp32:esp32:esp32s3',
|
|
68
|
+
mcu: 'esp32s3',
|
|
69
|
+
frameworkData: { target: 'esp32s3' },
|
|
70
|
+
},
|
|
47
71
|
{
|
|
48
72
|
id: 'esp32c3',
|
|
49
73
|
displayName: 'ESP32-C3',
|
|
@@ -55,6 +79,18 @@ const _knownTargets = [
|
|
|
55
79
|
buildTarget: 'esp32:esp32:esp32c3',
|
|
56
80
|
mcu: 'esp32c3',
|
|
57
81
|
},
|
|
82
|
+
{
|
|
83
|
+
id: 'esp32c3-idf',
|
|
84
|
+
displayName: 'ESP32-C3 (native ESP-IDF)',
|
|
85
|
+
isNative: false,
|
|
86
|
+
architecture: 'esp32c3',
|
|
87
|
+
boardPackage: '@typecad/board-esp32c3',
|
|
88
|
+
frameworkPackage: '@typecad/framework-esp32',
|
|
89
|
+
framework: 'esp32',
|
|
90
|
+
buildTarget: 'esp32:esp32:esp32c3',
|
|
91
|
+
mcu: 'esp32c3',
|
|
92
|
+
frameworkData: { target: 'esp32c3' },
|
|
93
|
+
},
|
|
58
94
|
{
|
|
59
95
|
id: 'esp32c6',
|
|
60
96
|
displayName: 'ESP32-C6',
|
|
@@ -66,6 +102,18 @@ const _knownTargets = [
|
|
|
66
102
|
buildTarget: 'esp32:esp32:esp32c6',
|
|
67
103
|
mcu: 'esp32c6',
|
|
68
104
|
},
|
|
105
|
+
{
|
|
106
|
+
id: 'esp32c6-idf',
|
|
107
|
+
displayName: 'ESP32-C6 (native ESP-IDF)',
|
|
108
|
+
isNative: false,
|
|
109
|
+
architecture: 'esp32c6',
|
|
110
|
+
boardPackage: '@typecad/board-esp32c6',
|
|
111
|
+
frameworkPackage: '@typecad/framework-esp32',
|
|
112
|
+
framework: 'esp32',
|
|
113
|
+
buildTarget: 'esp32:esp32:esp32c6',
|
|
114
|
+
mcu: 'esp32c6',
|
|
115
|
+
frameworkData: { target: 'esp32c6' },
|
|
116
|
+
},
|
|
69
117
|
{
|
|
70
118
|
id: 'rp2040',
|
|
71
119
|
displayName: 'RP2040 (Pico)',
|
|
@@ -13,6 +13,8 @@ export interface InitProjectOptions {
|
|
|
13
13
|
baudRate?: number;
|
|
14
14
|
includeSketch: boolean;
|
|
15
15
|
toolchainType?: string;
|
|
16
|
+
/** Extra frameworkData fields (e.g. `{ target: 'esp32s3' }` for framework-esp32). */
|
|
17
|
+
frameworkData?: Record<string, unknown>;
|
|
16
18
|
}
|
|
17
19
|
export declare function generateProjectPackageJson(options: InitProjectOptions): string;
|
|
18
20
|
export declare function generateProjectTsconfig(options: InitProjectOptions): string;
|
|
@@ -150,8 +150,26 @@ export default config;
|
|
|
150
150
|
`;
|
|
151
151
|
}
|
|
152
152
|
const buildTarget = options.buildTarget;
|
|
153
|
-
const
|
|
154
|
-
|
|
153
|
+
const isEspIdf = options.frameworkPackage === '@typecad/framework-esp32'
|
|
154
|
+
|| options.framework === 'esp32'
|
|
155
|
+
|| options.toolchainType === 'idf';
|
|
156
|
+
const resolvedToolchain = options.toolchainType
|
|
157
|
+
?? (isEspIdf ? 'idf' : 'arduino-cli');
|
|
158
|
+
// framework-esp32 wants IDF chip ids in frameworkData (target + buildTarget),
|
|
159
|
+
// not Arduino FQBNs. Known IDF targets already set frameworkData.target;
|
|
160
|
+
// wizard/CLI may still pass an FQBN in buildTarget — normalize to the last segment.
|
|
161
|
+
let frameworkDataBlock = '';
|
|
162
|
+
if (isEspIdf) {
|
|
163
|
+
const idfTarget = options.frameworkData?.target
|
|
164
|
+
?? options.frameworkData?.buildTarget
|
|
165
|
+
?? (buildTarget?.includes(':') ? buildTarget.split(':').filter(Boolean).pop() : buildTarget)
|
|
166
|
+
?? options.architecture
|
|
167
|
+
?? 'esp32';
|
|
168
|
+
frameworkDataBlock = `\n // Framework data (IDF chip target)\n frameworkData: {\n target: '${idfTarget}',\n buildTarget: '${idfTarget}',\n },`;
|
|
169
|
+
}
|
|
170
|
+
else if (buildTarget) {
|
|
171
|
+
frameworkDataBlock = `\n // Framework data\n frameworkData: {\n buildTarget: '${buildTarget}',\n },`;
|
|
172
|
+
}
|
|
155
173
|
const mcuLine = options.mcu
|
|
156
174
|
? `\n // MCU package — provides silicon-level pin definitions\n mcu: '${options.mcu.startsWith('@') ? options.mcu : `@typecad/mcu-${options.mcu}`}',\n`
|
|
157
175
|
: '';
|
|
@@ -181,7 +199,7 @@ const config: CuttlefishConfig = {
|
|
|
181
199
|
target: '${options.architecture}',${mcuLine}${boardLine}
|
|
182
200
|
|
|
183
201
|
// Framework package — controls code generation strategy
|
|
184
|
-
framework: '${options.frameworkPackage}',${
|
|
202
|
+
framework: '${options.frameworkPackage}',${frameworkDataBlock}
|
|
185
203
|
|
|
186
204
|
// Output / build options
|
|
187
205
|
output: {
|
|
@@ -508,11 +526,13 @@ export default [
|
|
|
508
526
|
files: ["src/**/*.ts"],
|
|
509
527
|
languageOptions: {
|
|
510
528
|
parser: tsparser,
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
529
|
+
// NOTE: do NOT set parserOptions.project here. None of the rules below
|
|
530
|
+
// (no-restricted-syntax, the cuttlefish/* AST rules, no-explicit-any,
|
|
531
|
+
// no-eval, ...) consume type information, so enabling type-aware linting
|
|
532
|
+
// only forces ESLint to build a full TS type-program per file — ~3.4s of
|
|
533
|
+
// pure overhead on small projects with zero change to what is detected.
|
|
534
|
+
// If a future rule needs types, scope project to that rule only via
|
|
535
|
+
// parserOptions on a dedicated config block, not globally.
|
|
516
536
|
},
|
|
517
537
|
plugins: {
|
|
518
538
|
"@typescript-eslint": tseslint,
|
|
@@ -94,6 +94,11 @@ export async function runInitWizard(partialOptions) {
|
|
|
94
94
|
if (target.architecture === 'avr') {
|
|
95
95
|
frameworkOptions.push({ label: "Bare-metal AVR (PORTB, etc.)", value: 'avr', pkg: '@typecad/framework-avr' });
|
|
96
96
|
}
|
|
97
|
+
// ESP32 family: offer the native ESP-IDF flavor alongside Arduino.
|
|
98
|
+
if (target.architecture === 'esp32' || target.architecture === 'esp32s3'
|
|
99
|
+
|| target.architecture === 'esp32c3' || target.architecture === 'esp32c6') {
|
|
100
|
+
frameworkOptions.push({ label: "ESP32 (native ESP-IDF)", value: 'esp32', pkg: '@typecad/framework-esp32' });
|
|
101
|
+
}
|
|
97
102
|
if (partialOptions?.framework) {
|
|
98
103
|
const match = frameworkOptions.find(f => f.value === partialOptions.framework);
|
|
99
104
|
framework = match?.value ?? partialOptions.framework;
|
|
@@ -141,6 +146,10 @@ export async function runInitWizard(partialOptions) {
|
|
|
141
146
|
else {
|
|
142
147
|
includeSketch = await promptConfirm(rl, "Create starter sketch?", true);
|
|
143
148
|
}
|
|
149
|
+
const isEspIdf = frameworkPackage === '@typecad/framework-esp32' || framework === 'esp32';
|
|
150
|
+
const idfTarget = target.frameworkData?.target
|
|
151
|
+
?? target.architecture
|
|
152
|
+
?? 'esp32';
|
|
144
153
|
return {
|
|
145
154
|
projectName,
|
|
146
155
|
targetId: target.id,
|
|
@@ -150,10 +159,20 @@ export async function runInitWizard(partialOptions) {
|
|
|
150
159
|
boardPackage: target.boardPackage,
|
|
151
160
|
frameworkPackage,
|
|
152
161
|
framework,
|
|
153
|
-
buildTarget: target.buildTarget,
|
|
162
|
+
buildTarget: isEspIdf ? idfTarget : target.buildTarget,
|
|
154
163
|
mcu: target.mcu,
|
|
155
164
|
baudRate,
|
|
156
165
|
includeSketch,
|
|
166
|
+
...(isEspIdf
|
|
167
|
+
? {
|
|
168
|
+
toolchainType: 'idf',
|
|
169
|
+
frameworkData: target.frameworkData?.target
|
|
170
|
+
? { ...target.frameworkData }
|
|
171
|
+
: { target: idfTarget, buildTarget: idfTarget },
|
|
172
|
+
}
|
|
173
|
+
: target.frameworkData
|
|
174
|
+
? { frameworkData: target.frameworkData }
|
|
175
|
+
: {}),
|
|
157
176
|
};
|
|
158
177
|
}
|
|
159
178
|
catch (err) {
|
package/dist/emit/cpp-emitter.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { buildEmitterContext } from "./emitters/setup.js";
|
|
2
|
-
import { emitPreamble } from "./emitters/output-finalizer.js";
|
|
2
|
+
import { emitPreamble, emitAsyncTaskClasses, finalizeOutput } from "./emitters/output-finalizer.js";
|
|
3
3
|
import { runTopLevelPreprocessing } from "./emitters/top-level-prep.js";
|
|
4
4
|
import { synthesizeEntrypoints } from "./emitters/entrypoint-synthesizer.js";
|
|
5
5
|
import { emitTypeDeclarations } from "./emitters/type-decl-emitter.js";
|
|
@@ -7,7 +7,6 @@ import { emitNamespaces } from "./emitters/namespace-emitter.js";
|
|
|
7
7
|
import { emitClasses } from "./emitters/class-emitter.js";
|
|
8
8
|
import { emitPostClassDeclarations, emitCallbackFunctions, emitFunctions, emitFunctionForwardDeclarations } from "./emitters/function-emitter-impl.js";
|
|
9
9
|
import { emitUIRuntime } from "./emitters/ui-emitter.js";
|
|
10
|
-
import { finalizeOutput } from "./emitters/output-finalizer.js";
|
|
11
10
|
const globalEnumNames = new Set();
|
|
12
11
|
const globalLargeEnumNames = new Set();
|
|
13
12
|
export function registerAllEnumNames(enums) {
|
|
@@ -21,7 +20,7 @@ export function registerAllEnumNames(enums) {
|
|
|
21
20
|
export function emitCpp(program, options) {
|
|
22
21
|
// 1. Build shared emitter context (strategy, renderers, analysis, etc.)
|
|
23
22
|
const ctx = buildEmitterContext(program, options);
|
|
24
|
-
// 2. Emit preamble (includes, polyfills, async task classes
|
|
23
|
+
// 2. Emit preamble (includes, polyfills, shims) — async task classes deferred
|
|
25
24
|
emitPreamble(ctx);
|
|
26
25
|
// 2.5. Emit UI runtime (header structs + static tables) — entry file only,
|
|
27
26
|
// when a UI is mounted. File-scope, must precede any function using it.
|
|
@@ -32,6 +31,8 @@ export function emitCpp(program, options) {
|
|
|
32
31
|
synthesizeEntrypoints(ctx);
|
|
33
32
|
// 5. Emit type declarations (enums, type aliases, interfaces, top-level constants)
|
|
34
33
|
emitTypeDeclarations(ctx);
|
|
34
|
+
// 5.5. Async state machines — after globals so WIFI_SSID etc. are in scope
|
|
35
|
+
emitAsyncTaskClasses(ctx);
|
|
35
36
|
// 6. Emit namespaces
|
|
36
37
|
emitNamespaces(ctx);
|
|
37
38
|
// 6.5. Emit function forward declarations (split mode — must precede class definitions)
|
|
@@ -56,6 +56,11 @@ export interface CallbackFunction {
|
|
|
56
56
|
params: string[];
|
|
57
57
|
statements: StatementIR[];
|
|
58
58
|
debounceMs?: number;
|
|
59
|
+
/**
|
|
60
|
+
* True for GPIO / hardware ISR callbacks only. WiFi event handlers, timers,
|
|
61
|
+
* and setInterval use the same hoist path but must NOT get IRAM_ATTR.
|
|
62
|
+
*/
|
|
63
|
+
isInterruptHandler?: boolean;
|
|
59
64
|
/**
|
|
60
65
|
* Return type of the synthesized free function. Defaults to "void" (the
|
|
61
66
|
* historical ISR/HAL-callback case). Populated from a hoisted lambda's
|