@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.
Files changed (90) hide show
  1. package/dist/api/shared/display-adapter.d.ts +2 -1
  2. package/dist/api/shared/display-adapter.js +8 -1
  3. package/dist/api/shared/display-adapters/sdl.js +9 -2
  4. package/dist/api/shared/display-profile.d.ts +20 -3
  5. package/dist/api/shared/display-profile.js +21 -6
  6. package/dist/api/shared/framework-manifest-registry.d.ts +9 -0
  7. package/dist/api/shared/framework-manifest-registry.js +25 -0
  8. package/dist/api/shared/framework-manifest.d.ts +462 -0
  9. package/dist/api/shared/framework-manifest.js +149 -0
  10. package/dist/api/shared/glcdfont.d.ts +12 -0
  11. package/dist/api/shared/glcdfont.js +124 -0
  12. package/dist/api/shared/graphics-strategy.d.ts +28 -0
  13. package/dist/api/shared/hal-op-ir.d.ts +418 -1
  14. package/dist/api/shared/hal-op-ir.js +95 -1
  15. package/dist/api/shared/index.d.ts +11 -1
  16. package/dist/api/shared/index.js +14 -0
  17. package/dist/api/shared/native-display-op-resolver.d.ts +10 -0
  18. package/dist/api/shared/native-display-op-resolver.js +64 -0
  19. package/dist/api/shared/platform-strategy.d.ts +9 -0
  20. package/dist/api/shared/promise-runtime.js +78 -0
  21. package/dist/api/shared/validate-framework-manifest.d.ts +28 -0
  22. package/dist/api/shared/validate-framework-manifest.js +417 -0
  23. package/dist/cli.js +109 -4
  24. package/dist/config-loader.js +20 -1
  25. package/dist/config-schema.d.ts +36 -36
  26. package/dist/create/board-spec.d.ts +4 -4
  27. package/dist/create/init-scaffold.d.ts +3 -0
  28. package/dist/create/init-scaffold.js +48 -0
  29. package/dist/create/init-templates.d.ts +2 -0
  30. package/dist/create/init-templates.js +28 -8
  31. package/dist/create/init-wizard.js +20 -1
  32. package/dist/emit/cpp-emitter.js +4 -3
  33. package/dist/emit/emitters/emitter-context.d.ts +5 -0
  34. package/dist/emit/emitters/function-emitter-impl.js +69 -59
  35. package/dist/emit/emitters/output-finalizer.d.ts +6 -0
  36. package/dist/emit/emitters/output-finalizer.js +21 -11
  37. package/dist/emit/emitters/setup.js +40 -0
  38. package/dist/emit/emitters/top-level-prep.js +2 -0
  39. package/dist/emit/emitters/ui-emitter.js +23 -8
  40. package/dist/emit/expression-renderer.js +10 -1
  41. package/dist/emit/snprintf-helpers.js +8 -0
  42. package/dist/emit/statement-renderer.js +13 -0
  43. package/dist/emit/utils/async-state-machine.js +185 -116
  44. package/dist/emit/utils/hal-op-cpp-type.d.ts +6 -0
  45. package/dist/emit/utils/hal-op-cpp-type.js +40 -0
  46. package/dist/ir/build-ir.js +5 -1
  47. package/dist/ir/expression-to-ir.js +14 -0
  48. package/dist/ir/feature-registry.js +7 -25
  49. package/dist/ir/hal/hal-emitter.d.ts +5 -2
  50. package/dist/ir/hal/hal-emitter.js +40 -12
  51. package/dist/ir/hal/hal-parser.d.ts +6 -0
  52. package/dist/ir/hal/hal-parser.js +74 -0
  53. package/dist/ir/hal/hal-plugins.js +561 -0
  54. package/dist/ir/identifier-collector.js +18 -0
  55. package/dist/ir/network-validation.d.ts +4 -0
  56. package/dist/ir/network-validation.js +184 -0
  57. package/dist/ir/ownership-analysis.js +14 -1
  58. package/dist/ir/program-analysis.d.ts +23 -0
  59. package/dist/ir/program-analysis.js +81 -2
  60. package/dist/ir/timing-validation.d.ts +6 -1
  61. package/dist/ir/timing-validation.js +50 -12
  62. package/dist/ir/transformers/expressions.js +58 -0
  63. package/dist/ir/transformers/hal-emit-helpers.js +1 -1
  64. package/dist/ir/transformers/variables.js +41 -3
  65. package/dist/ir/validation-orchestrator.js +3 -1
  66. package/dist/libdef/c-to-decl.d.ts +27 -0
  67. package/dist/libdef/c-to-decl.js +397 -0
  68. package/dist/libdef/component-decls.d.ts +2 -0
  69. package/dist/libdef/component-decls.js +6 -0
  70. package/dist/libdef/component-discovery.d.ts +43 -0
  71. package/dist/libdef/component-discovery.js +83 -0
  72. package/dist/libdef/cpp-to-decl.d.ts +9 -0
  73. package/dist/libdef/cpp-to-decl.js +72 -0
  74. package/dist/libdef/idf-discovery.d.ts +7 -0
  75. package/dist/libdef/idf-discovery.js +59 -0
  76. package/dist/libdef/registry.js +5 -2
  77. package/dist/lint-cache.d.ts +59 -0
  78. package/dist/lint-cache.js +257 -0
  79. package/dist/orchestrator/graph-builder.js +6 -2
  80. package/dist/stores/display-profile-store.d.ts +1 -0
  81. package/dist/stores/display-profile-store.js +1 -0
  82. package/dist/testing.d.ts +3 -1
  83. package/dist/testing.js +3 -1
  84. package/dist/transpile.d.ts +3 -0
  85. package/dist/transpile.js +72 -27
  86. package/dist/ui-hook.d.ts +17 -1
  87. package/dist/utils/cli.js +27 -1
  88. package/dist/utils/fs.d.ts +13 -0
  89. package/dist/utils/fs.js +50 -0
  90. 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
- : `@typecad/framework-${framework}`;
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: { buildTarget: configBuildTarget },
450
+ frameworkData: {
451
+ buildTarget: configBuildTarget,
452
+ ...(fcPsram ? { psram: fcPsram } : {}),
453
+ },
349
454
  };
350
455
  }
351
456
  if (config.outputOutDir && !options.outDir) {
@@ -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)
@@ -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 resolvedToolchain = options.toolchainType ?? 'arduino-cli';
154
- const buildTargetLine = buildTarget ? `\n // Framework data\n frameworkData: {\n buildTarget: '${buildTarget}',\n },` : '';
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}',${buildTargetLine}
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
- parserOptions: {
512
- // Type-aware parsing so @typescript-eslint/no-explicit-any resolves
513
- // imported bindings to their real types instead of defaulting to any.
514
- project: "./tsconfig.json",
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) {
@@ -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, shims)
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