@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
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { HALOpIR, ProgramIR, RuntimePolyfillIR, Diagnostic } from "./api/shared/index.js";
|
|
2
|
+
/** Context handed to transformIR. */
|
|
3
|
+
export interface SafetyTransformContext {
|
|
4
|
+
/** True iff the entry file imports `safe` from @typecad/safety. The pass
|
|
5
|
+
* uses this as a fast no-op guard. */
|
|
6
|
+
readonly safetyInUse: boolean;
|
|
7
|
+
/** Build target (e.g. "esp32", "avr"). Strategies still resolve the
|
|
8
|
+
* underlying pinMode/digitalRead symbols; the safety package is target-
|
|
9
|
+
* agnostic. */
|
|
10
|
+
readonly target: string;
|
|
11
|
+
}
|
|
12
|
+
/** Capabilities cuttlefish core needs from the safety engine.
|
|
13
|
+
*
|
|
14
|
+
* Part A uses transformIR + resolveSafetyOp + resolveSemanticCall.
|
|
15
|
+
* Part B (ISO 26262 Part 6 rules) adds analyzeIR; Part C (sidecar) will
|
|
16
|
+
* add collectSafetyMetadata. Both reuse this same seam with no new core
|
|
17
|
+
* machinery. */
|
|
18
|
+
export interface TranspilerSafetyHook {
|
|
19
|
+
/** Post-build IR transform. Must return a (possibly new) ProgramIR. Part A
|
|
20
|
+
* uses this to inject safety.record_pin_mode companions after every
|
|
21
|
+
* gpio.pin_mode / safety.pin_mode op. Called only when hasSafetyHook(). */
|
|
22
|
+
transformIR(program: ProgramIR, ctx: SafetyTransformContext): ProgramIR;
|
|
23
|
+
/** Resolve a safety.* HAL op to C++ code or expression. Called from the
|
|
24
|
+
* extended routeHALOp() for any op whose `operation` starts with "safety.".
|
|
25
|
+
* NEVER delegated to a per-target strategy. Returns undefined if the op
|
|
26
|
+
* is not recognized (caller emits an unhandled-op warning). */
|
|
27
|
+
resolveSafetyOp(op: HALOpIR): {
|
|
28
|
+
code?: string;
|
|
29
|
+
expression?: string;
|
|
30
|
+
} | undefined;
|
|
31
|
+
/** Lower a safe.* TS call to a HAL op. Called from an extension point in
|
|
32
|
+
* tryResolveSemanticCall (hal-plugins.ts) when the callee resolves to the
|
|
33
|
+
* @typecad/safety package. Returns undefined if not a safety call. */
|
|
34
|
+
resolveSemanticCall?(callee: string, args: readonly unknown[]): HALOpIR | undefined;
|
|
35
|
+
/** Provide safety runtime polyfills (mode table + voter). Called from
|
|
36
|
+
* buildEmitterContext so the polyfills participate in tree-shaking.
|
|
37
|
+
* Returns [] when safety is not in use. */
|
|
38
|
+
buildPolyfills?(): RuntimePolyfillIR[];
|
|
39
|
+
/** Part B: read-only ISO 26262 Part 6 software-rule analysis. Called
|
|
40
|
+
* after transformIR completes, on the final transformed IR. Returns
|
|
41
|
+
* diagnostics that flow into the build's diagnostic list. Diagnostics
|
|
42
|
+
* with severity "error" abort the build (via throwIfFatalDiagnostics);
|
|
43
|
+
* "warning"/"info" are logged but the build succeeds. */
|
|
44
|
+
analyzeIR?(program: ProgramIR, ctx: SafetyTransformContext): Diagnostic[];
|
|
45
|
+
/** Part C: collect safety metadata for sidecar artifact generation.
|
|
46
|
+
* Called after analyzeIR completes. Returns structured metadata about
|
|
47
|
+
* each safety-critical function (ASIL level, mechanisms, rule results)
|
|
48
|
+
* that the caller writes to a sidecar JSON file. */
|
|
49
|
+
collectSafetyMetadata?(program: ProgramIR, ctx: SafetyTransformContext): SafetyMetadata[];
|
|
50
|
+
}
|
|
51
|
+
/** Set the safety hook. Called by transpile.ts after dynamically loading
|
|
52
|
+
* @typecad/safety/engine. Passing null clears it. */
|
|
53
|
+
export declare function setSafetyHook(hook: TranspilerSafetyHook | null): void;
|
|
54
|
+
/** Get the current safety hook, or null if @typecad/safety is not loaded. */
|
|
55
|
+
export declare function getSafetyHook(): TranspilerSafetyHook | null;
|
|
56
|
+
/** Returns true if the safety hook is registered (i.e. @typecad/safety loaded). */
|
|
57
|
+
export declare function hasSafetyHook(): boolean;
|
|
58
|
+
/** Get the safety hook, throwing if it's not set. Use only inside
|
|
59
|
+
* `if (hasSafetyHook())` guards. */
|
|
60
|
+
export declare function requireSafetyHook(): TranspilerSafetyHook;
|
|
61
|
+
export type { Diagnostic };
|
|
62
|
+
/** Metadata about one safety-critical function, collected for Part C. */
|
|
63
|
+
export interface SafetyFunctionMetadata {
|
|
64
|
+
name: string;
|
|
65
|
+
asilLevel: string;
|
|
66
|
+
source?: {
|
|
67
|
+
tsFile: string;
|
|
68
|
+
tsLine: number;
|
|
69
|
+
};
|
|
70
|
+
mechanisms: string[];
|
|
71
|
+
rules: Record<string, "pass" | {
|
|
72
|
+
severity: string;
|
|
73
|
+
message: string;
|
|
74
|
+
}>;
|
|
75
|
+
}
|
|
76
|
+
/** Result of Part C's metadata collection. */
|
|
77
|
+
export interface SafetyMetadata {
|
|
78
|
+
functions: SafetyFunctionMetadata[];
|
|
79
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Safety hook — the contract cuttlefish core uses to talk to the optional
|
|
3
|
+
// @typecad/safety package. Modeled on ui-hook.ts.
|
|
4
|
+
//
|
|
5
|
+
// @typecad/safety is an optional peer dependency. When it is installed and
|
|
6
|
+
// imported by a sketch, transpile.ts calls loadSafetyEngine() which dynamic-
|
|
7
|
+
// imports @typecad/safety/engine and calls registerSafetyEngine() to push an
|
|
8
|
+
// implementation of this interface into the registry. When absent, every
|
|
9
|
+
// hasSafetyHook() guard returns false and cuttlefish behaves as before.
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// ── Module-level hook state ────────────────────────────────────────────────
|
|
12
|
+
let safetyHook = null;
|
|
13
|
+
/** Set the safety hook. Called by transpile.ts after dynamically loading
|
|
14
|
+
* @typecad/safety/engine. Passing null clears it. */
|
|
15
|
+
export function setSafetyHook(hook) {
|
|
16
|
+
safetyHook = hook;
|
|
17
|
+
}
|
|
18
|
+
/** Get the current safety hook, or null if @typecad/safety is not loaded. */
|
|
19
|
+
export function getSafetyHook() {
|
|
20
|
+
return safetyHook;
|
|
21
|
+
}
|
|
22
|
+
/** Returns true if the safety hook is registered (i.e. @typecad/safety loaded). */
|
|
23
|
+
export function hasSafetyHook() {
|
|
24
|
+
return safetyHook !== null;
|
|
25
|
+
}
|
|
26
|
+
/** Get the safety hook, throwing if it's not set. Use only inside
|
|
27
|
+
* `if (hasSafetyHook())` guards. */
|
|
28
|
+
export function requireSafetyHook() {
|
|
29
|
+
if (!safetyHook) {
|
|
30
|
+
throw new Error("Safety hook is not registered. This code path requires @typecad/safety " +
|
|
31
|
+
"to be installed and loaded. This should not happen — the hook is set " +
|
|
32
|
+
"at the start of transpileFile() when safety is detected.");
|
|
33
|
+
}
|
|
34
|
+
return safetyHook;
|
|
35
|
+
}
|
package/dist/testing.d.ts
CHANGED
|
@@ -12,10 +12,14 @@ export { inferSnprintfArg, createEmissionScopeState, escapeCppStringLiteral } fr
|
|
|
12
12
|
export { setLoadedFramework } from "./framework-registry.js";
|
|
13
13
|
export { registerPlatformStrategy, resolveStrategy, clearAllProfileCaches } from "./platform/registry.js";
|
|
14
14
|
export type { EmitMode, GeneratedOutputs, TargetProfile, PlatformContext } from "./types.js";
|
|
15
|
+
export { ComplianceContext, runSelfCheck, renderRegistryJson } from "./emit/compliance/index.js";
|
|
16
|
+
export type { ComplianceMode, SelfCheckFinding, Deviation } from "./emit/compliance/index.js";
|
|
15
17
|
export { findConfigFile, parseConfigFile, loadCuttlefishConfig, generateVirtualTypeDeclaration, } from "./config-loader.js";
|
|
16
18
|
export { transpileFile } from "./transpile.js";
|
|
17
19
|
export { resetUIEngine, __simulateUIAbsentForTest } from "./ui/ui-bridge.js";
|
|
18
|
-
export {
|
|
20
|
+
export { resetSafetyEngine, __simulateSafetyAbsentForTest } from "./safety/safety-bridge.js";
|
|
21
|
+
export { setSafetyHook, hasSafetyHook } from "./safety-hook.js";
|
|
22
|
+
export { scaffoldProject, normalizeProjectName, KNOWN_BOARDS, generateProjectPackageJson, generateProjectTsconfig, generateProjectConfig, generateProjectEnvDts, generateStarterSketch, generateStarterTest, generateStarterSim, generateGitignore, generateEslintConfig, runInitWizard, } from "./create/index.js";
|
|
19
23
|
export type { InitProjectOptions } from "./create/index.js";
|
|
20
24
|
export { scaffoldBoardPackages, parseBoardSpec, safeParseBoardSpec, stripJsonc, } from "./create/index.js";
|
|
21
25
|
export type { BoardSpec, ScaffoldBoardResult } from "./create/index.js";
|
|
@@ -30,6 +34,8 @@ export { LINT_RULES, ESLINT_OPT_OUT_KINDS, kindRegistryEntries, } from "./ir/fea
|
|
|
30
34
|
export type { LintRule, FeatureEntry, FeatureStatus, DiagnosticMatch } from "./ir/feature-registry.js";
|
|
31
35
|
export { runEslintCheck } from "./eslint-check.js";
|
|
32
36
|
export type { ESLintError } from "./eslint-check.js";
|
|
37
|
+
export { checkLintCache, recordLintSuccess, invalidateLint, computeLintFingerprint, resolveEslintConfigPath, } from "./lint-cache.js";
|
|
38
|
+
export type { LintFingerprint, GateCacheResult } from "./lint-cache.js";
|
|
33
39
|
export { runSemanticGates } from "./orchestrator/type-checker.js";
|
|
34
40
|
export { canonicalize, buildSemanticFacts, } from "./orchestrator/semantic-facts.js";
|
|
35
41
|
export type { CanonicalType, SemanticFacts, FactStore, ValueCategory, Lifetime, Nullable, SemanticOrigin, AnalysisResult, BindingResolver, } from "./orchestrator/semantic-facts.js";
|
|
@@ -40,7 +46,7 @@ export { parseCommandLine } from "./utils/cli.js";
|
|
|
40
46
|
export { parseHeader, stripPreprocessorBlocks } from "./libdef/header-parser.js";
|
|
41
47
|
export { BaseClassResolver, buildClassIndex } from "./libdef/base-class-resolver.js";
|
|
42
48
|
export type { ResolveResult } from "./libdef/base-class-resolver.js";
|
|
43
|
-
export { generateDecl, generateDeclsForDirectory } from "./libdef/cpp-to-decl.js";
|
|
49
|
+
export { generateDecl, generateDeclsForDirectory, generateComponentDeclsForProject } from "./libdef/cpp-to-decl.js";
|
|
44
50
|
export { renderExprAsText } from "./ir/render-expr.js";
|
|
45
51
|
export { contextStorage, CompilationContext } from "./ir/build-ir-state.js";
|
|
46
52
|
export type { ExpressionIR } from "./api/index.js";
|
package/dist/testing.js
CHANGED
|
@@ -11,14 +11,19 @@ export { analyzePeripheralUsage, createEmptyPeripheralUsage } from "./ir/periphe
|
|
|
11
11
|
export { inferSnprintfArg, createEmissionScopeState, escapeCppStringLiteral } from "./emit/snprintf-helpers.js";
|
|
12
12
|
export { setLoadedFramework } from "./framework-registry.js";
|
|
13
13
|
export { registerPlatformStrategy, resolveStrategy, clearAllProfileCaches } from "./platform/registry.js";
|
|
14
|
+
// ── AUTOSAR compliance module (test-visible) ───────────────────────────────
|
|
15
|
+
export { ComplianceContext, runSelfCheck, renderRegistryJson } from "./emit/compliance/index.js";
|
|
14
16
|
// ── Config loader ───────────────────────────────────────────────────────────
|
|
15
17
|
export { findConfigFile, parseConfigFile, loadCuttlefishConfig, generateVirtualTypeDeclaration, } from "./config-loader.js";
|
|
16
18
|
// ── Transpiler internal API ─────────────────────────────────────────────────
|
|
17
19
|
export { transpileFile } from "./transpile.js";
|
|
18
20
|
// ── UI bridge (optional @typecad/ui) ─────────────────────────────────────────
|
|
19
21
|
export { resetUIEngine, __simulateUIAbsentForTest } from "./ui/ui-bridge.js";
|
|
22
|
+
// ── Safety bridge (optional @typecad/safety) ────────────────────────────────
|
|
23
|
+
export { resetSafetyEngine, __simulateSafetyAbsentForTest } from "./safety/safety-bridge.js";
|
|
24
|
+
export { setSafetyHook, hasSafetyHook } from "./safety-hook.js";
|
|
20
25
|
// ── Project scaffolding ──────────────────────────────────────────────────────
|
|
21
|
-
export { scaffoldProject, normalizeProjectName, KNOWN_BOARDS, generateProjectPackageJson, generateProjectTsconfig, generateProjectConfig, generateProjectEnvDts, generateStarterSketch, generateGitignore, generateEslintConfig, runInitWizard, } from "./create/index.js";
|
|
26
|
+
export { scaffoldProject, normalizeProjectName, KNOWN_BOARDS, generateProjectPackageJson, generateProjectTsconfig, generateProjectConfig, generateProjectEnvDts, generateStarterSketch, generateStarterTest, generateStarterSim, generateGitignore, generateEslintConfig, runInitWizard, } from "./create/index.js";
|
|
22
27
|
// ── Board codegen (`cuttlefish board add`) ───────────────────────────────────
|
|
23
28
|
export { scaffoldBoardPackages, parseBoardSpec, safeParseBoardSpec, stripJsonc, } from "./create/index.js";
|
|
24
29
|
export { BoardGenerators } from "./create/index.js";
|
|
@@ -34,6 +39,8 @@ export { prescanUnsupportedFeatures } from "./ir/feature-prescan.js";
|
|
|
34
39
|
export { LINT_RULES, ESLINT_OPT_OUT_KINDS, kindRegistryEntries, } from "./ir/feature-registry.js";
|
|
35
40
|
// ── ESLint gate ──────────────────────────────────────────────────────────────
|
|
36
41
|
export { runEslintCheck } from "./eslint-check.js";
|
|
42
|
+
// ── ESLint gate cache ────────────────────────────────────────────────────────
|
|
43
|
+
export { checkLintCache, recordLintSuccess, invalidateLint, computeLintFingerprint, resolveEslintConfigPath, } from "./lint-cache.js";
|
|
37
44
|
// ── Semantic gates (TypeChecker-based) ──────────────────────────────────────
|
|
38
45
|
export { runSemanticGates } from "./orchestrator/type-checker.js";
|
|
39
46
|
export { canonicalize, buildSemanticFacts, } from "./orchestrator/semantic-facts.js";
|
|
@@ -43,7 +50,7 @@ export { discoverWatchDirs, isRelevantChange } from "./watch.js";
|
|
|
43
50
|
export { parseCommandLine } from "./utils/cli.js";
|
|
44
51
|
export { parseHeader, stripPreprocessorBlocks } from "./libdef/header-parser.js";
|
|
45
52
|
export { BaseClassResolver, buildClassIndex } from "./libdef/base-class-resolver.js";
|
|
46
|
-
export { generateDecl, generateDeclsForDirectory } from "./libdef/cpp-to-decl.js";
|
|
53
|
+
export { generateDecl, generateDeclsForDirectory, generateComponentDeclsForProject } from "./libdef/cpp-to-decl.js";
|
|
47
54
|
// ── IR rendering internals (for fail-closed regression tests) ───────────────
|
|
48
55
|
export { renderExprAsText } from "./ir/render-expr.js";
|
|
49
56
|
export { contextStorage, CompilationContext } from "./ir/build-ir-state.js";
|
package/dist/transpile.d.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
1
|
import { GenerateLibdefOptions, GeneratedOutputs, TranspileOptions } from "./types.js";
|
|
2
|
+
import { loadFrameworkPackage } from "./framework-package.js";
|
|
3
|
+
export { loadFrameworkPackage };
|
|
4
|
+
export { getLoadedFramework, hasLoadedFramework } from "./framework-registry.js";
|
|
2
5
|
export declare function transpileFile(options: TranspileOptions): Promise<GeneratedOutputs>;
|
|
3
6
|
export declare function generateLibraryDefinitions(options: GenerateLibdefOptions): string[];
|
package/dist/transpile.js
CHANGED
|
@@ -3,15 +3,33 @@ import path from "node:path";
|
|
|
3
3
|
import fs from "node:fs";
|
|
4
4
|
import { createRequire } from "node:module";
|
|
5
5
|
import ts from "typescript";
|
|
6
|
+
// Read once at module load. Used to populate the AUTOSAR deviation sidecar's
|
|
7
|
+
// `toolVersion` field. Falls back to "unknown" if the read fails (e.g. tests
|
|
8
|
+
// that import the module from an unexpected location).
|
|
9
|
+
const CUTTLEFISH_VERSION = (() => {
|
|
10
|
+
try {
|
|
11
|
+
const require_ = createRequire(import.meta.url);
|
|
12
|
+
// transpile.ts lives in src/, compiled to dist/transpile.js. The package
|
|
13
|
+
// root is one level up from dist/, so from dist/ the path is ../package.json.
|
|
14
|
+
// From src/ (TypeScript source used by tests) it's also ../package.json.
|
|
15
|
+
const pkg = require_("../package.json");
|
|
16
|
+
return pkg.version ?? "unknown";
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return "unknown";
|
|
20
|
+
}
|
|
21
|
+
})();
|
|
6
22
|
import { buildProgramIR } from "./ir/build-ir.js";
|
|
7
23
|
import { classDeclarationToIR } from "./ir/declaration-builders.js";
|
|
8
24
|
import { clickHandlers } from "./ir/transformers/ui-call-resolver.js";
|
|
9
25
|
import { requireUIHook, hasUIHook } from "./ui-hook.js";
|
|
10
26
|
import { loadUIEngine } from "./ui/ui-bridge.js";
|
|
27
|
+
import { hasSafetyHook, requireSafetyHook } from "./safety-hook.js";
|
|
28
|
+
import { loadSafetyEngine } from "./safety/safety-bridge.js";
|
|
11
29
|
import { setDisplayProfile, resetDisplayProfile } from "./stores/display-profile-store.js";
|
|
12
30
|
import { setThemeCss, resetThemeCss, setThemeClass } from "./stores/theme-store.js";
|
|
13
31
|
import { emitCpp, registerAllEnumNames } from "./emit/cpp-emitter.js";
|
|
14
|
-
import { readText } from "./utils/fs.js";
|
|
32
|
+
import { readText, writeText } from "./utils/fs.js";
|
|
15
33
|
import { debug as logDebug, info } from "./utils/logger.js";
|
|
16
34
|
import { loadLibraryDefinitions, generateLibdefStubs } from "./libdef/registry.js";
|
|
17
35
|
import { buildCallGraph } from "./ir/call-graph.js";
|
|
@@ -19,7 +37,7 @@ import { clearCaches, } from "./cache.js";
|
|
|
19
37
|
import { detectEntryPoints, detectExportedEntryPoints } from "./ir/entry-points.js";
|
|
20
38
|
import { analyzeReachability } from "./ir/reachability.js";
|
|
21
39
|
import { filterProgramIR } from "./ir/filter.js";
|
|
22
|
-
import { setActiveStrategy } from "./ir/hal-resolver.js";
|
|
40
|
+
import { setActiveStrategy, loadHALModules } from "./ir/hal-resolver.js";
|
|
23
41
|
import { CompilationContext, contextStorage } from "./ir/build-ir-state.js";
|
|
24
42
|
import { buildSymbolTable, mergeSymbolTable, resolveInheritance, createSymbolTable } from "./ir/symbol-table.js";
|
|
25
43
|
import { loadBreakpoints, preprocess as debugPreprocess } from "./debug/index.js";
|
|
@@ -28,6 +46,7 @@ import { typeCheckFiles } from "./orchestrator/type-checker.js";
|
|
|
28
46
|
import { runSemanticGates } from "./orchestrator/type-checker.js";
|
|
29
47
|
import { autoGenerateMissingDecls } from "./orchestrator/dts-generator.js";
|
|
30
48
|
import { runEslintCheck, printEslintErrors } from "./eslint-check.js";
|
|
49
|
+
import { checkLintCache, recordLintSuccess } from "./lint-cache.js";
|
|
31
50
|
import { initProfiler, getProfiler } from "./profiler/index.js";
|
|
32
51
|
import { buildDiagnosticsReport, writeDiagnosticsReport } from "./diagnostics/diagnostics-report.js";
|
|
33
52
|
import { resolveImport, } from "./transpile/resolution.js";
|
|
@@ -47,23 +66,30 @@ function loadExpectPreprocessor() {
|
|
|
47
66
|
}
|
|
48
67
|
}
|
|
49
68
|
function cleanOutput(_entryDir, outDir) {
|
|
50
|
-
//
|
|
51
|
-
//
|
|
52
|
-
//
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
}
|
|
57
|
-
catch (e) {
|
|
58
|
-
if (process.env.CUTTLEFISH_DEBUG)
|
|
59
|
-
console.error("[transpile] Failed to clean output dir:", e);
|
|
60
|
-
}
|
|
69
|
+
// Preserved for incremental-build support: writeText now skips writing when
|
|
70
|
+
// content is identical, so keeping the existing output dir intact lets
|
|
71
|
+
// downstream build tools (idf.py/ninja, arduino-cli) reuse their build
|
|
72
|
+
// caches. Stale files from removed source modules are harmless — they're
|
|
73
|
+
// not referenced by the current entry file and won't be compiled.
|
|
74
|
+
// The output dir is still created (via writeText → ensureDir) on first run.
|
|
61
75
|
}
|
|
62
76
|
/**
|
|
63
77
|
* Auto-generates .d.ts files for C++ modules that are missing declarations.
|
|
64
78
|
* Also tries to generate declarations for framework libraries.
|
|
65
79
|
* Returns list of generated files.
|
|
66
80
|
*/
|
|
81
|
+
/**
|
|
82
|
+
* True iff the program imports `@typecad/safety` anywhere. The safety
|
|
83
|
+
* transform pass uses this as its fast no-op guard — when no file imports
|
|
84
|
+
* safety, the pass returns the program unchanged.
|
|
85
|
+
*/
|
|
86
|
+
function entryImportsSafety(program) {
|
|
87
|
+
for (const imp of program.imports) {
|
|
88
|
+
if (imp.moduleSpecifier === "@typecad/safety")
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
67
93
|
/**
|
|
68
94
|
* Apply tree-shaking to program IR if enabled
|
|
69
95
|
*/
|
|
@@ -131,6 +157,8 @@ function applyTreeShaking(programIR, target, treeShakingOptions) {
|
|
|
131
157
|
import { isStringEnum } from "./api/shared/index.js";
|
|
132
158
|
import { resolveStrategy } from "./platform/registry.js";
|
|
133
159
|
import { loadFrameworkPackage } from "./framework-package.js";
|
|
160
|
+
export { loadFrameworkPackage };
|
|
161
|
+
export { getLoadedFramework, hasLoadedFramework } from "./framework-registry.js";
|
|
134
162
|
import { getLoadedFramework, hasLoadedFramework } from "./framework-registry.js";
|
|
135
163
|
function formatFatalDiagnostics(entries) {
|
|
136
164
|
const errors = entries.filter(({ diagnostic }) => diagnostic.severity === "error");
|
|
@@ -138,8 +166,13 @@ function formatFatalDiagnostics(entries) {
|
|
|
138
166
|
`Transpilation aborted because ${errors.length} unsupported pattern${errors.length === 1 ? "" : "s"} were found.`,
|
|
139
167
|
];
|
|
140
168
|
for (const { filePath, diagnostic } of errors) {
|
|
141
|
-
|
|
142
|
-
|
|
169
|
+
// Prefer diagnostic.filePath (set explicitly by the emitter, e.g. AUTOSAR
|
|
170
|
+
// diagnostics that map a C++ line back to TS) over the wrapper's filePath
|
|
171
|
+
// (the TS source file being processed, which may not be the right pointer
|
|
172
|
+
// for emit-stage diagnostics).
|
|
173
|
+
const resolvedFile = diagnostic.filePath ?? filePath;
|
|
174
|
+
const locationBase = resolvedFile
|
|
175
|
+
? path.relative(process.cwd(), resolvedFile) || resolvedFile
|
|
143
176
|
: diagnostic.source ?? "user code";
|
|
144
177
|
const position = diagnostic.line != null
|
|
145
178
|
? `(${diagnostic.line}:${diagnostic.column ?? 1})`
|
|
@@ -216,6 +249,10 @@ export async function transpileFile(options) {
|
|
|
216
249
|
// requireUIHook() works throughout this transpile run. Gracefully no-ops
|
|
217
250
|
// when @typecad/ui is absent (pure TS→C++ transpile).
|
|
218
251
|
await loadUIEngine();
|
|
252
|
+
// Load the safety engine from @typecad/safety (if installed). Sets up the
|
|
253
|
+
// hook so requireSafetyHook() works throughout this transpile run.
|
|
254
|
+
// Gracefully no-ops when @typecad/safety is absent.
|
|
255
|
+
await loadSafetyEngine();
|
|
219
256
|
const entryFile = path.resolve(options.inputFile);
|
|
220
257
|
const entryDir = path.dirname(entryFile);
|
|
221
258
|
const sourceDir = entryDir;
|
|
@@ -237,24 +274,29 @@ export async function transpileFile(options) {
|
|
|
237
274
|
const configDisplay = options.display;
|
|
238
275
|
if (configDisplay) {
|
|
239
276
|
const { resolveDisplayProfile } = await import("./api/shared/display-profile.js");
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
277
|
+
// Build the named-profile registry. Prefer the strategy's hook (per-framework
|
|
278
|
+
// canonical profiles — Arduino returns its BUILT_IN_PROFILES, Zephyr returns
|
|
279
|
+
// its DT-binding profiles). Fall back to the legacy dynamic import of the
|
|
280
|
+
// framework's displays/ili9341-spi module when the strategy provides none.
|
|
281
|
+
// The registry maps a config `profile` name (e.g. "st7796-zephyr") to its
|
|
282
|
+
// DisplayProfile; an empty registry makes resolveDisplayProfile fall back to
|
|
283
|
+
// the bare default driver (config.driver ?? "ili9341") when no profile name
|
|
284
|
+
// is set, or throw "Unknown display profile" when one is.
|
|
285
|
+
let registry = new Map();
|
|
286
|
+
if (strategy.getProfileRegistry) {
|
|
287
|
+
registry = strategy.getProfileRegistry();
|
|
288
|
+
}
|
|
289
|
+
else if (options.frameworkPackage) {
|
|
290
|
+
const profileMod = await import(options.frameworkPackage + "/displays/ili9341-spi").catch(() => null);
|
|
291
|
+
if (profileMod?.BUILT_IN_PROFILES) {
|
|
292
|
+
for (const [k, v] of Object.entries(profileMod.BUILT_IN_PROFILES)) {
|
|
293
|
+
registry.set(k, v);
|
|
249
294
|
}
|
|
250
295
|
}
|
|
251
|
-
const resolved = resolveDisplayProfile(configDisplay, registry);
|
|
252
|
-
const buildTarget = options.platformContext?.frameworkData?.buildTarget;
|
|
253
|
-
setDisplayProfile(resolved.profile, { cs: resolved.cs, dc: resolved.dc, rst: resolved.rst, bus: resolved.bus, address: resolved.address, reset: resolved.reset, buildTarget });
|
|
254
|
-
}
|
|
255
|
-
catch {
|
|
256
|
-
// Fall back to default profile — not fatal
|
|
257
296
|
}
|
|
297
|
+
const resolved = resolveDisplayProfile(configDisplay, registry);
|
|
298
|
+
const buildTarget = options.platformContext?.frameworkData?.buildTarget;
|
|
299
|
+
setDisplayProfile(resolved.profile, { cs: resolved.cs, dc: resolved.dc, rst: resolved.rst, bus: resolved.bus, address: resolved.address, reset: resolved.reset, buildTarget });
|
|
258
300
|
// Apply theme CSS override if specified.
|
|
259
301
|
if (configDisplay.themeCss) {
|
|
260
302
|
setThemeCss(configDisplay.themeCss);
|
|
@@ -301,22 +343,43 @@ export async function transpileFile(options) {
|
|
|
301
343
|
}
|
|
302
344
|
// ── ESLint gate ──────────────────────────────────────────────────────────
|
|
303
345
|
// ESLint catches what the type-checker cannot (idiom violations, banned
|
|
304
|
-
// globals, explicit `any`, etc.).
|
|
305
|
-
//
|
|
346
|
+
// globals, explicit `any`, etc.). It is a mandatory correctness gate: it
|
|
347
|
+
// excludes non-AOT code patterns the transpiler cannot accept, so it cannot
|
|
348
|
+
// be dropped. Errors abort the build, mirroring the type-check behavior
|
|
349
|
+
// above. Skipped alongside type-checking when disabled.
|
|
350
|
+
//
|
|
351
|
+
// Because the lint result is a whole-program boolean that depends only on
|
|
352
|
+
// the source files, the eslint config, and the eslint/transpiler versions,
|
|
353
|
+
// it is cacheable. On a cache hit we skip the ~3s ESLint run entirely; on a
|
|
354
|
+
// miss we run ESLint and, only if clean, persist the result. See lint-cache.ts
|
|
355
|
+
// for the soundness contract.
|
|
306
356
|
if (options.skipLint !== true && options.skipTypeCheck !== true && transpileFiles.length > 0) {
|
|
307
|
-
profiler.startTimer("lint:eslint");
|
|
308
357
|
// The eslint config lives at the project root (next to cuttlefish.config.ts),
|
|
309
358
|
// not under src/. Fall back to entryDir for ad-hoc API/test callers that pass
|
|
310
359
|
// a bare input file without a configured project.
|
|
311
360
|
const eslintRoot = options.projectRoot ?? entryDir;
|
|
312
|
-
const
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
361
|
+
const lintCache = checkLintCache(eslintRoot, sourceDir, { force: options.force });
|
|
362
|
+
if (lintCache.hit) {
|
|
363
|
+
// Cache hit: previous clean run still applies, skip ESLint entirely.
|
|
364
|
+
profiler.startTimer("lint:eslint:cached");
|
|
365
|
+
profiler.endTimer("lint:eslint:cached");
|
|
366
|
+
}
|
|
367
|
+
else {
|
|
368
|
+
profiler.startTimer("lint:eslint");
|
|
369
|
+
const eslintErrors = await runEslintCheck(eslintRoot);
|
|
370
|
+
profiler.endTimer("lint:eslint");
|
|
371
|
+
if (eslintErrors.length > 0) {
|
|
372
|
+
// Abort with a formatted message. The structured-diagnostic channel is
|
|
373
|
+
// not populated here because a thrown error discards the output anyway;
|
|
374
|
+
// printEslintErrors gives the user file/line/column/caret directly.
|
|
375
|
+
// Do NOT persist a cache entry for a failing run.
|
|
376
|
+
printEslintErrors(eslintErrors);
|
|
377
|
+
throw new Error(`ESLint reported ${eslintErrors.length} error${eslintErrors.length === 1 ? "" : "s"} — transpilation aborted.`);
|
|
378
|
+
}
|
|
379
|
+
// Clean run: record the fingerprint so subsequent unchanged builds skip.
|
|
380
|
+
if (lintCache.fingerprint) {
|
|
381
|
+
recordLintSuccess(eslintRoot, lintCache.fingerprint);
|
|
382
|
+
}
|
|
320
383
|
}
|
|
321
384
|
}
|
|
322
385
|
const npmPackages = graphResult.npmPackages;
|
|
@@ -336,7 +399,7 @@ export async function transpileFile(options) {
|
|
|
336
399
|
code: "themeCss-ui-entry-ignored",
|
|
337
400
|
message: `display.themeCss is ignored for .ui single-file entries; the inline <style> in ${path.basename(options.inputFile)} is the sole CSS source.`,
|
|
338
401
|
hint: `Move the standalone CSS into the .ui file's <style> block, or change the entry to a .ts file that imports a .ui.html module.`,
|
|
339
|
-
|
|
402
|
+
filePath: path.basename(options.inputFile),
|
|
340
403
|
});
|
|
341
404
|
}
|
|
342
405
|
}
|
|
@@ -348,7 +411,7 @@ export async function transpileFile(options) {
|
|
|
348
411
|
if (hasUIHook()) {
|
|
349
412
|
for (const mod of requireUIHook().allUIModules()) {
|
|
350
413
|
for (const d of mod.diagnostics) {
|
|
351
|
-
diagnostics.push({ ...d,
|
|
414
|
+
diagnostics.push({ ...d, filePath: d.filePath ?? path.basename(mod.htmlPath) });
|
|
352
415
|
}
|
|
353
416
|
}
|
|
354
417
|
}
|
|
@@ -441,7 +504,13 @@ export async function transpileFile(options) {
|
|
|
441
504
|
}
|
|
442
505
|
sourceText = preprocess(sourceText, filePath);
|
|
443
506
|
}
|
|
444
|
-
|
|
507
|
+
// Printf instrumentation runs only in printf debug mode. In gdb mode
|
|
508
|
+
// (e.g. ESP32-S3), --debug emits #line markers at the emit stage and
|
|
509
|
+
// uses VS Code native breakpoints; the printf preprocessor is skipped
|
|
510
|
+
// so the source reaches IR/emit unmodified.
|
|
511
|
+
const buildTarget = options.platformContext?.frameworkData?.buildTarget;
|
|
512
|
+
const debugMode = strategy.debugMode?.(buildTarget) ?? 'printf';
|
|
513
|
+
if (options.debug && breakpoints && debugMode === 'printf') {
|
|
445
514
|
const instrumented = debugPreprocess({
|
|
446
515
|
fileName: filePath,
|
|
447
516
|
breakpoints,
|
|
@@ -458,6 +527,15 @@ export async function transpileFile(options) {
|
|
|
458
527
|
};
|
|
459
528
|
profiler.startTimer("ir:build-all");
|
|
460
529
|
profiler.captureMemorySnapshot("ir:pre-build");
|
|
530
|
+
// Load + parse the @typecad/hal source files ONCE for this transpile run.
|
|
531
|
+
// buildProgramIR used to force-reload HAL per graph file (O(files) re-reads
|
|
532
|
+
// and re-parses of all 28 HAL modules); warming it here makes the per-file
|
|
533
|
+
// loadHALModules() call inside buildProgramIR a cheap no-op. A fresh run of
|
|
534
|
+
// transpileFile always reaches this point, so edits to @typecad/hal source
|
|
535
|
+
// are picked up on the next build.
|
|
536
|
+
profiler.startTimer("ir:load-hal");
|
|
537
|
+
loadHALModules(true);
|
|
538
|
+
profiler.endTimer("ir:load-hal");
|
|
461
539
|
const rawIRArray = await Promise.all(filesToProcess.map(buildRawIR));
|
|
462
540
|
profiler.captureMemorySnapshot("ir:post-build");
|
|
463
541
|
profiler.endTimer("ir:build-all");
|
|
@@ -466,7 +544,7 @@ export async function transpileFile(options) {
|
|
|
466
544
|
if (hasUIHook()) {
|
|
467
545
|
for (const mod of requireUIHook().allUIModules()) {
|
|
468
546
|
for (const d of mod.mountDiagnostics) {
|
|
469
|
-
diagnostics.push({ ...d,
|
|
547
|
+
diagnostics.push({ ...d, filePath: d.filePath ?? path.basename(mod.htmlPath) });
|
|
470
548
|
}
|
|
471
549
|
}
|
|
472
550
|
}
|
|
@@ -619,6 +697,60 @@ export async function transpileFile(options) {
|
|
|
619
697
|
profiler.startTimer("emit:register-enums");
|
|
620
698
|
registerAllEnumNames(allEnumIRs);
|
|
621
699
|
profiler.endTimer("emit:register-enums");
|
|
700
|
+
// ── Phase D: safety transform pass ──────────────────────────────────────
|
|
701
|
+
// When @typecad/safety is in use, run its post-build IR transform to inject
|
|
702
|
+
// safety ops (e.g. companion safety.record_pin_mode after every
|
|
703
|
+
// gpio.pin_mode). Produces new IR per file; no-op when safety is off or
|
|
704
|
+
// absent.
|
|
705
|
+
if (hasSafetyHook()) {
|
|
706
|
+
profiler.startTimer("safety:transform");
|
|
707
|
+
const hook = requireSafetyHook();
|
|
708
|
+
for (const [filePath, item] of preBuilt) {
|
|
709
|
+
const newIR = hook.transformIR(item.programIR, {
|
|
710
|
+
safetyInUse: entryImportsSafety(item.programIR),
|
|
711
|
+
target: options.target,
|
|
712
|
+
});
|
|
713
|
+
preBuilt.set(filePath, { ...item, programIR: newIR });
|
|
714
|
+
}
|
|
715
|
+
profiler.endTimer("safety:transform");
|
|
716
|
+
// Part B: ISO 26262 Part 6 read-only IR analysis. Runs after transformIR
|
|
717
|
+
// so it sees the final IR (including injected safety ops). Returned
|
|
718
|
+
// diagnostics flow into the build's diagnostic list; error-severity
|
|
719
|
+
// diagnostics abort via throwIfFatalDiagnostics.
|
|
720
|
+
if (hook.analyzeIR) {
|
|
721
|
+
profiler.startTimer("safety:analyze");
|
|
722
|
+
for (const [, item] of preBuilt) {
|
|
723
|
+
const irDiags = hook.analyzeIR(item.programIR, {
|
|
724
|
+
safetyInUse: entryImportsSafety(item.programIR),
|
|
725
|
+
target: options.target,
|
|
726
|
+
});
|
|
727
|
+
if (irDiags.length > 0) {
|
|
728
|
+
diagnostics.push(...irDiags);
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
profiler.endTimer("safety:analyze");
|
|
732
|
+
}
|
|
733
|
+
// Part C: collect safety metadata and write sidecar artifact.
|
|
734
|
+
if (hook.collectSafetyMetadata) {
|
|
735
|
+
profiler.startTimer("safety:collect");
|
|
736
|
+
for (const [filePath, item] of preBuilt) {
|
|
737
|
+
if (!entryImportsSafety(item.programIR))
|
|
738
|
+
continue;
|
|
739
|
+
const metadata = hook.collectSafetyMetadata(item.programIR, {
|
|
740
|
+
safetyInUse: true,
|
|
741
|
+
target: options.target,
|
|
742
|
+
});
|
|
743
|
+
if (metadata.length > 0 && metadata[0].functions.length > 0) {
|
|
744
|
+
const { renderSafetySidecar } = await import("./safety/sidecar-bridge.js");
|
|
745
|
+
const toolVersion = CUTTLEFISH_VERSION;
|
|
746
|
+
const sidecarPath = path.join(outDir, path.basename(filePath).replace(/\.\w+$/, ".safety-sidecar.json"));
|
|
747
|
+
const sidecarJson = await renderSafetySidecar(metadata[0], toolVersion);
|
|
748
|
+
writeText(sidecarPath, sidecarJson);
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
profiler.endTimer("safety:collect");
|
|
752
|
+
}
|
|
753
|
+
}
|
|
622
754
|
// ── Pass 2: emit (only for files that needed retranspilation) ─────────────
|
|
623
755
|
profiler.startTimer("emit:all");
|
|
624
756
|
profiler.captureMemorySnapshot("emit:pre");
|
|
@@ -643,6 +775,9 @@ export async function transpileFile(options) {
|
|
|
643
775
|
crossModuleEnumNames: allEnumNames,
|
|
644
776
|
crossModuleStringEnumNames: allStringEnumNames,
|
|
645
777
|
crossModuleVariableTypes: allVariableTypes,
|
|
778
|
+
autosar: options.autosar,
|
|
779
|
+
toolVersion: CUTTLEFISH_VERSION,
|
|
780
|
+
autosarArxml: options.autosarArxml,
|
|
646
781
|
};
|
|
647
782
|
// Pass the already-resolved strategy (framework-loaded or target-based)
|
|
648
783
|
emitOptions.strategy = strategy;
|
|
@@ -689,6 +824,7 @@ export async function transpileFile(options) {
|
|
|
689
824
|
profiler.endTimer("post:flatten");
|
|
690
825
|
// Profiler session ends (profiling disabled - no report generation)
|
|
691
826
|
// ── Generate diagnostics report if enabled ──────────────────────────────
|
|
827
|
+
let diagnosticsReportPath;
|
|
692
828
|
if (options.diagnostics) {
|
|
693
829
|
try {
|
|
694
830
|
const entryPreBuilt = preBuilt.get(entryFile);
|
|
@@ -708,17 +844,23 @@ export async function transpileFile(options) {
|
|
|
708
844
|
removedSymbols: allRemovedSymbols,
|
|
709
845
|
});
|
|
710
846
|
writeDiagnosticsReport(report, entryPreBuilt?.programIR ?? null, outDir);
|
|
847
|
+
diagnosticsReportPath = path.join(outDir, "diagnostics.md");
|
|
711
848
|
}
|
|
712
849
|
catch (e) {
|
|
713
|
-
// Diagnostics report generation is best-effort; don't fail the build
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
850
|
+
// Diagnostics report generation is best-effort; don't fail the build,
|
|
851
|
+
// but surface the failure so the user knows --diagnostics didn't work.
|
|
852
|
+
diagnostics.push({
|
|
853
|
+
severity: "warning",
|
|
854
|
+
message: `Diagnostics report generation failed: ${e instanceof Error ? e.message : String(e)}`,
|
|
855
|
+
code: "diagnostics-report-failed",
|
|
856
|
+
source: "transpile",
|
|
857
|
+
});
|
|
717
858
|
}
|
|
718
859
|
}
|
|
719
860
|
return {
|
|
720
861
|
...entryOutputs,
|
|
721
862
|
diagnostics,
|
|
863
|
+
diagnosticsReportPath,
|
|
722
864
|
};
|
|
723
865
|
}
|
|
724
866
|
export function generateLibraryDefinitions(options) {
|