@typecad/cuttlefish 1.0.0-alpha.13 → 1.0.0-alpha.15
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 +82 -79
- package/assets/editor-extensions/typecad-debug/README.md +141 -0
- package/assets/editor-extensions/typecad-debug/out/extension.js +347 -0
- package/assets/editor-extensions/typecad-debug/out/extension.js.map +1 -0
- package/assets/editor-extensions/typecad-debug/package.json +76 -0
- package/assets/editor-extensions/typecad-ui/LICENSE +27 -0
- package/assets/editor-extensions/typecad-ui/README.md +52 -0
- package/assets/editor-extensions/typecad-ui/file-icon-dark.png +0 -0
- package/assets/editor-extensions/typecad-ui/file-icon-light.png +0 -0
- package/assets/editor-extensions/typecad-ui/icon.png +0 -0
- package/assets/editor-extensions/typecad-ui/language-configuration.json +43 -0
- package/assets/editor-extensions/typecad-ui/package.json +54 -0
- package/assets/editor-extensions/typecad-ui/snippets/typecad-ui.json +80 -0
- package/assets/editor-extensions/typecad-ui/syntaxes/markdown-ui.json +45 -0
- package/assets/editor-extensions/typecad-ui/syntaxes/typecad-ui.tmLanguage.json +1269 -0
- package/dist/api/config.d.ts +30 -27
- package/dist/api/schema/types.d.ts +20 -3
- package/dist/api/shared/async-runtime-static.d.ts +1 -1
- package/dist/api/shared/async-runtime-static.js +5 -5
- package/dist/api/shared/async-types.d.ts +3 -2
- package/dist/api/shared/board-catalog.d.ts +2 -0
- package/dist/api/shared/board-catalog.js +6 -0
- package/dist/api/shared/board-resolver.d.ts +4 -7
- package/dist/api/shared/board-resolver.js +2 -1
- package/dist/api/shared/coop-scheduler.d.ts +1 -1
- package/dist/api/shared/coop-scheduler.js +1 -1
- package/dist/api/shared/cpp-type-ir.d.ts +1 -1
- package/dist/api/shared/cpp-type-ir.js +1 -1
- package/dist/api/shared/display-profile.d.ts +1 -1
- package/dist/api/shared/display-profile.js +3 -5
- package/dist/api/shared/framework-manifest-registry.d.ts +1 -1
- package/dist/api/shared/framework-manifest-registry.js +0 -1
- package/dist/api/shared/framework-manifest.d.ts +65 -507
- package/dist/api/shared/framework-manifest.js +6 -11
- package/dist/api/shared/hal-op-ir.d.ts +294 -685
- package/dist/api/shared/hal-op-ir.js +31 -65
- package/dist/api/shared/index.d.ts +0 -4
- package/dist/api/shared/index.js +1 -7
- package/dist/api/shared/ir-core.d.ts +6 -0
- package/dist/api/shared/ir-declarations.d.ts +4 -0
- package/dist/api/shared/ir.d.ts +1 -3
- package/dist/api/shared/platform-strategy.d.ts +14 -37
- package/dist/api/shared/polyfill-helper-registry.js +0 -5
- package/dist/api/shared/polyfill-types.js +0 -27
- package/dist/api/shared/promise-runtime.js +3 -3
- package/dist/api/shared/spdx-licenses.d.ts +12 -10
- package/dist/api/shared/spdx-licenses.js +1 -1
- package/dist/api/shared/toolchain-types.d.ts +1 -1
- package/dist/api/shared/toolchain-types.js +3 -3
- package/dist/api/shared/types.d.ts +0 -3
- package/dist/api/shared/validate-framework-manifest.js +13 -33
- package/dist/board-catalog/dts-reader.d.ts +220 -0
- package/dist/board-catalog/dts-reader.js +1052 -0
- package/dist/board-catalog/index.d.ts +11 -0
- package/dist/board-catalog/index.js +12 -0
- package/dist/board-catalog/pinconfig.d.ts +48 -0
- package/dist/board-catalog/pinconfig.js +263 -0
- package/dist/board-catalog/sdk.d.ts +82 -0
- package/dist/board-catalog/sdk.js +180 -0
- package/dist/board-catalog/store.d.ts +196 -0
- package/dist/board-catalog/store.js +363 -0
- package/dist/board-catalog/types.d.ts +233 -0
- package/dist/board-catalog/types.js +7 -0
- package/dist/board-catalog/walker.d.ts +112 -0
- package/dist/board-catalog/walker.js +1101 -0
- package/dist/cli-utils.d.ts +2 -2
- package/dist/cli-utils.js +5 -5
- package/dist/cli.js +227 -244
- package/dist/config-loader.d.ts +14 -19
- package/dist/config-loader.js +242 -78
- package/dist/config-schema.d.ts +19 -227
- package/dist/config-schema.js +9 -8
- package/dist/contract/board-generator.d.ts +4 -4
- package/dist/contract/board-generator.js +16 -5
- package/dist/contract/contract-parser.d.ts +36 -127
- package/dist/contract/contract-parser.js +76 -0
- package/dist/contract/index.d.ts +1 -1
- package/dist/contract/index.js +70 -21
- package/dist/create/active-board-catalog.d.ts +9 -0
- package/dist/create/active-board-catalog.js +94 -0
- package/dist/create/board-catalog.generated.d.ts +96 -0
- package/dist/create/board-catalog.generated.js +128737 -0
- package/dist/create/board-search.d.ts +23 -0
- package/dist/create/board-search.js +88 -0
- package/dist/create/editor-integration.d.ts +32 -0
- package/dist/create/editor-integration.js +257 -0
- package/dist/create/eslint-rules-template.d.ts +2 -2
- package/dist/create/framework-catalog.d.ts +48 -8
- package/dist/create/framework-catalog.js +58 -44
- package/dist/create/index.d.ts +8 -13
- package/dist/create/index.js +5 -9
- package/dist/create/mcu-target.d.ts +39 -0
- package/dist/create/mcu-target.js +80 -0
- package/dist/create/pack-targets.d.ts +19 -0
- package/dist/create/pack-targets.js +56 -0
- package/dist/create/{init-scaffold.d.ts → scaffold.d.ts} +8 -11
- package/dist/create/{init-scaffold.js → scaffold.js} +28 -87
- package/dist/create/templates.d.ts +51 -0
- package/dist/create/{init-templates.js → templates.js} +135 -116
- package/dist/create/wizard.d.ts +19 -0
- package/dist/create/wizard.js +314 -0
- package/dist/debug/preprocessor.d.ts +5 -0
- package/dist/debug/preprocessor.js +31 -7
- package/dist/diagnostics/diagnostics-report.d.ts +1 -1
- package/dist/diagnostics/diagnostics-report.js +3 -11
- package/dist/diagnostics/json-schema.d.ts +1 -1
- package/dist/diagnostics/md-writer.js +2 -2
- package/dist/diagnostics/mermaid-builder.d.ts +1 -1
- package/dist/diagnostics/mermaid-builder.js +1 -1
- package/dist/emit/cpp-emitter.js +4 -1
- package/dist/emit/emitters/class-emitter.js +32 -4
- package/dist/emit/emitters/emitter-context.d.ts +11 -0
- package/dist/emit/emitters/entrypoint-synthesizer.js +1 -1
- package/dist/emit/emitters/function-emitter-impl.js +33 -17
- package/dist/emit/emitters/output-finalizer.d.ts +7 -0
- package/dist/emit/emitters/output-finalizer.js +31 -23
- package/dist/emit/emitters/setup.js +147 -63
- package/dist/emit/emitters/top-level-prep.js +1 -1
- package/dist/emit/emitters/type-decl-emitter.js +13 -8
- package/dist/emit/emitters/ui-emitter.js +1 -1
- package/dist/emit/expression-renderer.d.ts +3 -3
- package/dist/emit/expression-renderer.js +17 -7
- package/dist/emit/route-hal-op.js +17 -48
- package/dist/emit/snprintf-helpers.d.ts +1 -3
- package/dist/emit/snprintf-helpers.js +1 -39
- package/dist/emit/statement-renderer.d.ts +0 -4
- package/dist/emit/statement-renderer.js +54 -45
- package/dist/emit/utils/async-state-machine.d.ts +16 -9
- package/dist/emit/utils/async-state-machine.js +56 -54
- package/dist/emit/utils/comment-helpers.d.ts +0 -8
- package/dist/emit/utils/comment-helpers.js +1 -1
- package/dist/emit/utils/cpp-helpers.d.ts +2 -2
- package/dist/emit/utils/cpp-helpers.js +2 -2
- package/dist/emit/utils/hal-op-cpp-type.js +4 -6
- package/dist/emit/utils/index.d.ts +3 -3
- package/dist/emit/utils/index.js +3 -3
- package/dist/emit/utils/type-inference.d.ts +1 -29
- package/dist/emit/utils/type-inference.js +0 -75
- package/dist/framework-package.d.ts +3 -0
- package/dist/framework-package.js +23 -6
- package/dist/framework-registry.d.ts +5 -5
- package/dist/frameworks/native/graphics/terminal-preview.d.ts +5 -0
- package/dist/frameworks/native/graphics/terminal-preview.js +33 -0
- package/dist/frameworks/native/index.d.ts +3 -0
- package/dist/frameworks/native/index.js +15 -0
- package/dist/frameworks/native/native-compile.d.ts +17 -0
- package/dist/frameworks/native/native-compile.js +170 -0
- package/dist/frameworks/native/native-config.d.ts +41 -0
- package/dist/frameworks/native/native-config.js +7 -0
- package/dist/frameworks/native/strategy.d.ts +97 -0
- package/dist/frameworks/native/strategy.js +664 -0
- package/dist/ir/board-resolver.d.ts +18 -23
- package/dist/ir/board-resolver.js +43 -401
- package/dist/ir/build-ir-state.d.ts +3 -0
- package/dist/ir/build-ir-state.js +42 -0
- package/dist/ir/build-ir.d.ts +1 -1
- package/dist/ir/build-ir.js +64 -45
- package/dist/ir/declaration-builders.js +1 -0
- package/dist/ir/expression-to-ir.js +17 -10
- package/dist/ir/hal/hal-emitter.d.ts +0 -2
- package/dist/ir/hal/hal-emitter.js +92 -41
- package/dist/ir/hal/hal-parser.d.ts +5 -5
- package/dist/ir/hal/hal-parser.js +128 -36
- package/dist/ir/hal/hal-plugins.d.ts +0 -1
- package/dist/ir/hal/hal-plugins.js +425 -716
- package/dist/ir/heap-analysis.js +2 -41
- package/dist/ir/identifier-collector.js +32 -20
- package/dist/ir/network-validation.js +7 -11
- package/dist/ir/ownership-analysis.js +75 -13
- package/dist/ir/peripheral-usage.d.ts +8 -2
- package/dist/ir/peripheral-usage.js +78 -149
- package/dist/ir/peripheral-validation.js +23 -3
- package/dist/ir/pin-capability-validation.d.ts +12 -0
- package/dist/ir/pin-capability-validation.js +61 -97
- package/dist/ir/pin-mode-validation.js +5 -5
- package/dist/ir/program-analysis.d.ts +8 -15
- package/dist/ir/program-analysis.js +140 -323
- package/dist/ir/pulldown-validation.js +11 -4
- package/dist/ir/resource-analysis.js +15 -3
- package/dist/ir/statement-to-ir.js +0 -6
- package/dist/ir/timing-validation.js +1 -1
- package/dist/ir/transformers/array-methods.js +11 -5
- package/dist/ir/transformers/call-statement.js +20 -87
- package/dist/ir/transformers/callback-context-registry.d.ts +1 -1
- package/dist/ir/transformers/callback-context-registry.js +8 -16
- package/dist/ir/transformers/control-flow.js +0 -13
- package/dist/ir/transformers/expressions.js +10 -9
- package/dist/ir/transformers/hal-call-resolver.d.ts +2 -2
- package/dist/ir/transformers/hal-call-resolver.js +24 -10
- package/dist/ir/transformers/hal-emit-helpers.d.ts +2 -2
- package/dist/ir/transformers/hal-emit-helpers.js +2 -2
- package/dist/ir/transformers/namespace-methods.js +1 -1
- package/dist/ir/transformers/ui-callback-lowering.d.ts +0 -2
- package/dist/ir/transformers/ui-callback-lowering.js +0 -24
- package/dist/ir/transformers/variables.js +338 -42
- package/dist/ir/try-catch-validation.js +4 -3
- package/dist/ir/type-resolution.js +15 -4
- package/dist/ir/validation-orchestrator.js +0 -9
- package/dist/libdef/cpp-to-decl.d.ts +0 -9
- package/dist/libdef/cpp-to-decl.js +0 -72
- package/dist/libdef/registry.d.ts +0 -1
- package/dist/libdef/registry.js +1 -24
- package/dist/library/catalog.d.ts +35 -0
- package/dist/library/catalog.js +67 -0
- package/dist/library/cli.d.ts +2 -0
- package/dist/library/cli.js +168 -0
- package/dist/library/init.d.ts +25 -0
- package/dist/library/init.js +397 -0
- package/dist/library/install.d.ts +9 -0
- package/dist/library/install.js +80 -0
- package/dist/library/registry-search.d.ts +34 -0
- package/dist/library/registry-search.js +50 -0
- package/dist/library/validate.d.ts +17 -0
- package/dist/library/validate.js +175 -0
- package/dist/library-packages.d.ts +95 -0
- package/dist/library-packages.js +275 -0
- package/dist/mapping/peripheral-names.js +9 -4
- package/dist/mapping/source-map.d.ts +3 -4
- package/dist/mapping/source-map.js +12 -18
- package/dist/orchestrator/graph-builder.d.ts +3 -3
- package/dist/orchestrator/graph-builder.js +29 -12
- package/dist/orchestrator/type-checker.d.ts +3 -3
- package/dist/orchestrator/type-checker.js +3 -3
- package/dist/platform/async-runtime.js +1 -1
- package/dist/platform/coop-scheduler-runtime.js +2 -2
- package/dist/platform/generic-debug-codegen.d.ts +36 -0
- package/dist/platform/generic-debug-codegen.js +185 -0
- package/dist/platform/generic-strategy.d.ts +3 -4
- package/dist/platform/generic-strategy.js +5 -80
- package/dist/platform/index.js +1 -1
- package/dist/platform/registry.d.ts +2 -2
- package/dist/platform/registry.js +2 -2
- package/dist/preview/client.js +17 -3
- package/dist/safety/asil-decorators.d.ts +32 -0
- package/dist/safety/asil-decorators.js +32 -0
- package/dist/safety/authoring.d.ts +85 -0
- package/dist/safety/authoring.js +61 -0
- package/dist/safety/engine.d.ts +3 -0
- package/dist/safety/engine.js +83 -0
- package/dist/safety/hal/ops.d.ts +48 -0
- package/dist/safety/hal/ops.js +46 -0
- package/dist/safety/iso26262/analyze.d.ts +13 -0
- package/dist/safety/iso26262/analyze.js +29 -0
- package/dist/safety/iso26262/asil.d.ts +10 -0
- package/dist/safety/iso26262/asil.js +22 -0
- package/dist/safety/iso26262/collect.d.ts +14 -0
- package/dist/safety/iso26262/collect.js +137 -0
- package/dist/safety/iso26262/goto-checker.d.ts +10 -0
- package/dist/safety/iso26262/goto-checker.js +72 -0
- package/dist/safety/iso26262/heap-checker.d.ts +7 -0
- package/dist/safety/iso26262/heap-checker.js +57 -0
- package/dist/safety/iso26262/init-checker.d.ts +13 -0
- package/dist/safety/iso26262/init-checker.js +127 -0
- package/dist/safety/iso26262/loop-checker.d.ts +7 -0
- package/dist/safety/iso26262/loop-checker.js +86 -0
- package/dist/safety/iso26262/recursion-checker.d.ts +9 -0
- package/dist/safety/iso26262/recursion-checker.js +134 -0
- package/dist/safety/iso26262/sidecar-writer.d.ts +25 -0
- package/dist/safety/iso26262/sidecar-writer.js +13 -0
- package/dist/safety/passes/pinMode-intercept.d.ts +3 -0
- package/dist/safety/passes/pinMode-intercept.js +60 -0
- package/dist/safety/runtime/mode-table.d.ts +5 -0
- package/dist/safety/runtime/mode-table.js +55 -0
- package/dist/safety/runtime/polyfills.d.ts +6 -0
- package/dist/safety/runtime/polyfills.js +12 -0
- package/dist/safety/runtime/result-struct.d.ts +11 -0
- package/dist/safety/runtime/result-struct.js +98 -0
- package/dist/safety/runtime/safe-int.d.ts +33 -0
- package/dist/safety/runtime/safe-int.js +170 -0
- package/dist/safety/runtime/safe-traits.d.ts +4 -0
- package/dist/safety/runtime/safe-traits.js +95 -0
- package/dist/safety/runtime/safe-variable.d.ts +39 -0
- package/dist/safety/runtime/safe-variable.js +226 -0
- package/dist/safety/runtime/vote.d.ts +8 -0
- package/dist/safety/runtime/vote.js +92 -0
- package/dist/safety/runtime/write-verify.d.ts +8 -0
- package/dist/safety/runtime/write-verify.js +71 -0
- package/dist/safety/safe-int-types.d.ts +45 -0
- package/dist/safety/safe-int-types.js +25 -0
- package/dist/safety/safe-variable-types.d.ts +29 -0
- package/dist/safety/safe-variable-types.js +19 -0
- package/dist/safety/safety-bridge.d.ts +5 -6
- package/dist/safety/safety-bridge.js +15 -28
- package/dist/safety/sidecar-bridge.d.ts +1 -3
- package/dist/safety/sidecar-bridge.js +3 -20
- package/dist/safety/specifiers.d.ts +6 -0
- package/dist/safety/specifiers.js +14 -0
- package/dist/safety-hook.d.ts +8 -7
- package/dist/safety-hook.js +12 -13
- package/dist/testing.d.ts +5 -6
- package/dist/testing.js +5 -5
- package/dist/transpile/resolution.d.ts +1 -1
- package/dist/transpile/resolution.js +18 -9
- package/dist/transpile/test-pins.d.ts +18 -0
- package/dist/transpile/test-pins.js +142 -0
- package/dist/transpile.d.ts +1 -2
- package/dist/transpile.js +90 -29
- package/dist/types.d.ts +45 -26
- package/dist/ui-hook.d.ts +1 -1
- package/dist/utils/cli.d.ts +2 -2
- package/dist/utils/cli.js +160 -157
- package/dist/utils/fs.d.ts +3 -4
- package/dist/utils/fs.js +4 -5
- package/dist/utils/strings.d.ts +0 -1
- package/dist/utils/strings.js +0 -8
- package/dist/utils/ui.d.ts +5 -2
- package/dist/utils/ui.js +13 -3
- package/package.json +158 -149
- package/dist/add-preset.d.ts +0 -4
- package/dist/add-preset.js +0 -74
- package/dist/api/shared/async-symbol-detector.d.ts +0 -11
- package/dist/api/shared/async-symbol-detector.js +0 -140
- package/dist/api/shared/native-display-op-resolver.d.ts +0 -10
- package/dist/api/shared/native-display-op-resolver.js +0 -64
- package/dist/create/board-checklist.d.ts +0 -2
- package/dist/create/board-checklist.js +0 -52
- package/dist/create/board-codegen.d.ts +0 -9
- package/dist/create/board-codegen.js +0 -238
- package/dist/create/board-generators.d.ts +0 -12
- package/dist/create/board-generators.js +0 -651
- package/dist/create/board-spec.d.ts +0 -637
- package/dist/create/board-spec.js +0 -214
- package/dist/create/init-templates.d.ts +0 -28
- package/dist/create/init-wizard.d.ts +0 -8
- package/dist/create/init-wizard.js +0 -182
- package/dist/install/framework-catalog.d.ts +0 -53
- package/dist/install/framework-catalog.js +0 -107
- package/dist/install/handle-install.d.ts +0 -35
- package/dist/install/handle-install.js +0 -177
- package/dist/install/index.d.ts +0 -4
- package/dist/install/index.js +0 -3
- package/dist/ir/pin-state-tracking.d.ts +0 -58
- package/dist/ir/pin-state-tracking.js +0 -182
- package/dist/ir/pwm-timer-sharing.d.ts +0 -4
- package/dist/ir/pwm-timer-sharing.js +0 -94
- package/dist/ir/timer0-pwm-timing-conflict.d.ts +0 -4
- package/dist/ir/timer0-pwm-timing-conflict.js +0 -72
- package/dist/libdef/component-decls.d.ts +0 -2
- package/dist/libdef/component-decls.js +0 -6
- package/dist/libdef/component-discovery.d.ts +0 -43
- package/dist/libdef/component-discovery.js +0 -83
- package/dist/theme-tokens.d.ts +0 -22
- package/dist/theme-tokens.js +0 -172
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// `cuttlefish library validate` — the standalone library-package validator.
|
|
3
|
+
//
|
|
4
|
+
// Before this, manifest errors only surfaced at import time inside a
|
|
5
|
+
// transpile. This runs the same checks (and more) on a package directory:
|
|
6
|
+
// manifest schema, file existence, include/gateToken consistency, npm
|
|
7
|
+
// keyword consistency, and an AUTOSAR --strict pass over the shim bytes —
|
|
8
|
+
// the same self-check the generated application runs over them.
|
|
9
|
+
// ---------------------------------------------------------------------------
|
|
10
|
+
import fs from "node:fs";
|
|
11
|
+
import path from "node:path";
|
|
12
|
+
import { ComplianceContext, runSelfCheck } from "../emit/compliance/index.js";
|
|
13
|
+
import { LIBRARY_MARKER_KEYWORD, libraryCategory } from "./catalog.js";
|
|
14
|
+
function readJson(filePath) {
|
|
15
|
+
return JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
16
|
+
}
|
|
17
|
+
export function validateLibraryPackage(dir) {
|
|
18
|
+
const errors = [];
|
|
19
|
+
const warnings = [];
|
|
20
|
+
const root = path.resolve(dir);
|
|
21
|
+
const fail = (message) => {
|
|
22
|
+
errors.push(message);
|
|
23
|
+
};
|
|
24
|
+
// ── Manifest ───────────────────────────────────────────────────────────
|
|
25
|
+
const manifestPath = path.join(root, "cuttlefish.library.json");
|
|
26
|
+
let manifest;
|
|
27
|
+
if (!fs.existsSync(manifestPath)) {
|
|
28
|
+
fail("cuttlefish.library.json not found — is this a library package directory?");
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
try {
|
|
32
|
+
manifest = readJson(manifestPath);
|
|
33
|
+
}
|
|
34
|
+
catch (e) {
|
|
35
|
+
fail(`cuttlefish.library.json is not valid JSON: ${e instanceof Error ? e.message : String(e)}`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
if (manifest) {
|
|
39
|
+
for (const field of ["id", "module", "framework", "include", "gateToken"]) {
|
|
40
|
+
if (typeof manifest[field] !== "string" || manifest[field].length === 0) {
|
|
41
|
+
fail(`Manifest field '${field}' must be a non-empty string.`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (!Array.isArray(manifest.shims) || manifest.shims.length === 0) {
|
|
45
|
+
fail("Manifest 'shims' must be a non-empty array.");
|
|
46
|
+
}
|
|
47
|
+
const include = typeof manifest.include === "string" ? manifest.include : "";
|
|
48
|
+
const gateToken = typeof manifest.gateToken === "string" ? manifest.gateToken : "";
|
|
49
|
+
if (include.length > 0 && gateToken.length > 0 && !include.includes(gateToken)) {
|
|
50
|
+
fail(`The gate token '${gateToken}' does not appear in the include '${include}' — ` +
|
|
51
|
+
`the include line is the only guaranteed emission, so contributions would never gate on.`);
|
|
52
|
+
}
|
|
53
|
+
// ── Shim files ───────────────────────────────────────────────────────
|
|
54
|
+
if (Array.isArray(manifest.shims)) {
|
|
55
|
+
for (const entry of manifest.shims) {
|
|
56
|
+
if (typeof entry !== "object" ||
|
|
57
|
+
entry === null ||
|
|
58
|
+
typeof entry.path !== "string" ||
|
|
59
|
+
typeof entry.outName !== "string") {
|
|
60
|
+
fail("Every 'shims' entry needs string 'path' and 'outName' fields.");
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
const shimPath = path.join(root, entry.path);
|
|
64
|
+
if (!fs.existsSync(shimPath)) {
|
|
65
|
+
fail(`Shim file listed in the manifest is missing: ${entry.path}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// ── Overlay fragment ─────────────────────────────────────────────────
|
|
70
|
+
if (typeof manifest.overlay === "string" && manifest.overlay.length > 0) {
|
|
71
|
+
const overlayPath = path.join(root, manifest.overlay);
|
|
72
|
+
if (!fs.existsSync(overlayPath)) {
|
|
73
|
+
fail(`Overlay fragment listed in the manifest is missing: ${manifest.overlay}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// ── package.json consistency ───────────────────────────────────────────
|
|
78
|
+
const pkgJsonPath = path.join(root, "package.json");
|
|
79
|
+
let pkgName;
|
|
80
|
+
let pkgKeywords = [];
|
|
81
|
+
if (!fs.existsSync(pkgJsonPath)) {
|
|
82
|
+
warnings.push("No package.json — nothing published, so keyword checks are skipped.");
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
try {
|
|
86
|
+
const pkg = readJson(pkgJsonPath);
|
|
87
|
+
if (typeof pkg.name === "string") {
|
|
88
|
+
pkgName = pkg.name;
|
|
89
|
+
}
|
|
90
|
+
if (Array.isArray(pkg.keywords)) {
|
|
91
|
+
pkgKeywords = pkg.keywords.filter((k) => typeof k === "string");
|
|
92
|
+
}
|
|
93
|
+
if (Array.isArray(pkg.files) && !pkg.files.includes("cuttlefish.library.json")) {
|
|
94
|
+
fail("package.json 'files' must include 'cuttlefish.library.json' or the manifest will not ship.");
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
catch (e) {
|
|
98
|
+
fail(`package.json is not valid JSON: ${e instanceof Error ? e.message : String(e)}`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
if (pkgKeywords.length > 0 && !pkgKeywords.map((k) => k.toLowerCase()).includes(LIBRARY_MARKER_KEYWORD)) {
|
|
102
|
+
fail(`package.json keywords must include the marker '${LIBRARY_MARKER_KEYWORD}' — it is what library search scans for.`);
|
|
103
|
+
}
|
|
104
|
+
const categoryKeywords = pkgKeywords.filter((k) => k.startsWith("cuttlefish-") && k !== LIBRARY_MARKER_KEYWORD);
|
|
105
|
+
if (pkgKeywords.length > 0 && categoryKeywords.length === 0) {
|
|
106
|
+
warnings.push(`No category keyword in package.json — add one of ${LIBRARY_MARKER_KEYWORD}'s siblings (e.g. cuttlefish-led) so the library is browsable by category.`);
|
|
107
|
+
}
|
|
108
|
+
for (const kw of categoryKeywords) {
|
|
109
|
+
const id = kw.slice("cuttlefish-".length);
|
|
110
|
+
if (!libraryCategory(id)) {
|
|
111
|
+
warnings.push(`Keyword '${kw}' is not a known category keyword (cuttlefish-<id>).`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
if (categoryKeywords.length > 1) {
|
|
115
|
+
warnings.push(`Multiple category keywords (${categoryKeywords.join(", ")}) — search results show the first taxonomy match only.`);
|
|
116
|
+
}
|
|
117
|
+
if (manifest && pkgName !== undefined && typeof manifest.module === "string" && manifest.module !== pkgName) {
|
|
118
|
+
fail(`Manifest 'module' (${manifest.module}) must equal the package.json name (${pkgName}) — ` +
|
|
119
|
+
`the import specifier users write resolves against the package name.`);
|
|
120
|
+
}
|
|
121
|
+
// ── AUTOSAR strict over the shim bytes ─────────────────────────────────
|
|
122
|
+
const autosarFindings = [];
|
|
123
|
+
if (manifest && Array.isArray(manifest.shims)) {
|
|
124
|
+
const headerLines = [];
|
|
125
|
+
const sourceLines = [];
|
|
126
|
+
let anyReadable = false;
|
|
127
|
+
for (const entry of manifest.shims) {
|
|
128
|
+
if (typeof entry !== "object" || entry === null)
|
|
129
|
+
continue;
|
|
130
|
+
const rel = entry.path;
|
|
131
|
+
if (typeof rel !== "string")
|
|
132
|
+
continue;
|
|
133
|
+
const abs = path.join(root, rel);
|
|
134
|
+
if (!fs.existsSync(abs))
|
|
135
|
+
continue;
|
|
136
|
+
anyReadable = true;
|
|
137
|
+
const content = fs.readFileSync(abs, "utf8").split("\n");
|
|
138
|
+
if (abs.endsWith(".h") || abs.endsWith(".hpp")) {
|
|
139
|
+
headerLines.push(...content, "");
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
sourceLines.push(...content, "");
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
if (anyReadable) {
|
|
146
|
+
const ctx = new ComplianceContext("strict");
|
|
147
|
+
autosarFindings.push(...runSelfCheck(ctx, sourceLines, headerLines));
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
for (const finding of autosarFindings) {
|
|
151
|
+
fail(`AUTOSAR ${finding.ruleId} (${finding.file} line ${finding.line}): ${finding.snippet}`);
|
|
152
|
+
}
|
|
153
|
+
return {
|
|
154
|
+
valid: errors.length === 0,
|
|
155
|
+
dir: root,
|
|
156
|
+
errors,
|
|
157
|
+
warnings,
|
|
158
|
+
autosarFindings,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
/** Render the human-readable report. Returns the process exit code (0/1). */
|
|
162
|
+
export function printValidationReport(report) {
|
|
163
|
+
for (const warning of report.warnings) {
|
|
164
|
+
console.log(` ! ${warning}`);
|
|
165
|
+
}
|
|
166
|
+
for (const error of report.errors) {
|
|
167
|
+
console.log(` x ${error}`);
|
|
168
|
+
}
|
|
169
|
+
if (report.valid) {
|
|
170
|
+
console.log(` Valid library package${report.warnings.length > 0 ? ` (${report.warnings.length} warning${report.warnings.length === 1 ? "" : "s"})` : ""}.`);
|
|
171
|
+
return 0;
|
|
172
|
+
}
|
|
173
|
+
console.log(` ${report.errors.length} error${report.errors.length === 1 ? "" : "s"}.`);
|
|
174
|
+
return 1;
|
|
175
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import type { LibraryDefinition } from "./types.js";
|
|
2
|
+
/** A native source file a library contributes to the emitted sources. */
|
|
3
|
+
export interface CuttlefishLibraryShim {
|
|
4
|
+
/** Path of the shim file, relative to the library package root. */
|
|
5
|
+
path: string;
|
|
6
|
+
/** File name the shim is written as, in the emitted source directory. */
|
|
7
|
+
outName: string;
|
|
8
|
+
}
|
|
9
|
+
/** The parsed `cuttlefish.library.json` manifest. */
|
|
10
|
+
export interface CuttlefishLibraryManifest {
|
|
11
|
+
/** Stable library id (diagnostics / sidecar records). */
|
|
12
|
+
id: string;
|
|
13
|
+
/** The import specifier this library resolves (e.g. '@typecad/zephyr-esp32s3-rgb'). */
|
|
14
|
+
module: string;
|
|
15
|
+
/** The framework (strategy id) this library requires, e.g. 'zephyr'. */
|
|
16
|
+
framework: string;
|
|
17
|
+
/** Build-target prefixes this library supports (matched against buildTarget). */
|
|
18
|
+
targets?: string[];
|
|
19
|
+
/** C++ include directive content for the import, e.g. '"__tc_rgbled.h"'. */
|
|
20
|
+
include: string;
|
|
21
|
+
/** Token whose presence in emitted sources gates the library's artifacts. */
|
|
22
|
+
gateToken: string;
|
|
23
|
+
/** Native shim files written into the emitted source directory. */
|
|
24
|
+
shims: CuttlefishLibraryShim[];
|
|
25
|
+
/** CONFIG_ lines contributed to the framework's prj.conf. */
|
|
26
|
+
kconfig?: string[];
|
|
27
|
+
/** Path of a devicetree overlay fragment, relative to the package root. */
|
|
28
|
+
overlay?: string;
|
|
29
|
+
}
|
|
30
|
+
/** A registered library: manifest + resolved package root. */
|
|
31
|
+
export interface RegisteredCuttlefishLibrary {
|
|
32
|
+
manifest: CuttlefishLibraryManifest;
|
|
33
|
+
packageRoot: string;
|
|
34
|
+
}
|
|
35
|
+
/** One record of the `libraries.json` sidecar (build-time contribution set). */
|
|
36
|
+
export interface LibrarySidecarEntry {
|
|
37
|
+
id: string;
|
|
38
|
+
module: string;
|
|
39
|
+
framework: string;
|
|
40
|
+
gateToken: string;
|
|
41
|
+
kconfig: string[];
|
|
42
|
+
/** Absolute path of the overlay fragment, or null. */
|
|
43
|
+
overlay: string | null;
|
|
44
|
+
/** Shim file names written into the emitted source directory. */
|
|
45
|
+
shims: string[];
|
|
46
|
+
}
|
|
47
|
+
/** Sidecar file name — written next to the emitted sources. */
|
|
48
|
+
export declare const LIBRARY_SIDECAR_NAME = "libraries.json";
|
|
49
|
+
/** Clear all registrations. Called once per transpilation, before graph build. */
|
|
50
|
+
export declare function resetCuttlefishLibraries(): void;
|
|
51
|
+
/** All libraries registered during this transpilation (import-driven). */
|
|
52
|
+
export declare function getRegisteredCuttlefishLibraries(): RegisteredCuttlefishLibrary[];
|
|
53
|
+
/**
|
|
54
|
+
* Whether the specifier resolves to a library registered in this
|
|
55
|
+
* transpilation. Library packages are exempt from the @typecad/* "SDK
|
|
56
|
+
* imports are type-level only" include skip — their import IS the include
|
|
57
|
+
* of the shim header.
|
|
58
|
+
*/
|
|
59
|
+
export declare function isRegisteredCuttlefishLibrary(moduleSpecifier: string): boolean;
|
|
60
|
+
/**
|
|
61
|
+
* Resolve the package root for a bare npm specifier from the importing file,
|
|
62
|
+
* then register the package as a cuttlefish library if it ships a
|
|
63
|
+
* `cuttlefish.library.json`. Returns true when the specifier resolved to a
|
|
64
|
+
* registered library (the import must then be skipped by the graph builder).
|
|
65
|
+
*/
|
|
66
|
+
export declare function registerCuttlefishLibraryFromSpecifier(fromFile: string, moduleSpecifier: string): boolean;
|
|
67
|
+
/** Libdef entries for the registry: import → shim include. */
|
|
68
|
+
export declare function cuttlefishLibraryLibdefs(): LibraryDefinition[];
|
|
69
|
+
/**
|
|
70
|
+
* Validate registered libraries against the loaded framework (and, when
|
|
71
|
+
* known, the build target). Throws with an actionable message when a library
|
|
72
|
+
* cannot work in this project — far earlier and clearer than the native
|
|
73
|
+
* compiler would.
|
|
74
|
+
*/
|
|
75
|
+
export declare function validateCuttlefishLibraries(frameworkId: string, buildTarget?: string): void;
|
|
76
|
+
/**
|
|
77
|
+
* Emit the library artifacts for the libraries actually used in this
|
|
78
|
+
* transpilation, and write the `libraries.json` sidecar:
|
|
79
|
+
*
|
|
80
|
+
* 1. scan the emitted sources for each library's gate token;
|
|
81
|
+
* 2. write (write-if-changed) the shims of every gated library into the
|
|
82
|
+
* emitted source directory — the framework scaffold's CMake/program
|
|
83
|
+
* regen picks the sources up automatically;
|
|
84
|
+
* 3. delete shim files recorded in the previous sidecar that are no longer
|
|
85
|
+
* used, so removing an import removes its native code;
|
|
86
|
+
* 4. persist the sidecar (even when empty — that clears stale state).
|
|
87
|
+
*/
|
|
88
|
+
export declare function writeCuttlefishLibraryArtifacts(outDir: string, srcDir: string): void;
|
|
89
|
+
/**
|
|
90
|
+
* Read the `libraries.json` sidecar for build-time consumers (framework
|
|
91
|
+
* toolchains). Tolerant of a missing or corrupt file (returns []).
|
|
92
|
+
*/
|
|
93
|
+
export declare function readCuttlefishLibrarySidecar(outDir: string): LibrarySidecarEntry[];
|
|
94
|
+
/** Re-exported for transpile.ts libdef injection convenience. */
|
|
95
|
+
export declare function libraryDefinitionKey(module: string): string;
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Cuttlefish library packages
|
|
3
|
+
//
|
|
4
|
+
// A library package is an npm package whose root carries a
|
|
5
|
+
// `cuttlefish.library.json` manifest. It contributes native artifacts to the
|
|
6
|
+
// generated firmware application:
|
|
7
|
+
//
|
|
8
|
+
// - an include mapping (the import emits `#include` of the shim header)
|
|
9
|
+
// - shim files (native C++ written into the emitted source directory)
|
|
10
|
+
// - framework build contributions (Kconfig lines, a devicetree overlay
|
|
11
|
+
// fragment) consumed by the framework toolchain via the sidecar
|
|
12
|
+
//
|
|
13
|
+
// Discovery is import-driven: when the transpile graph builder resolves an
|
|
14
|
+
// import whose package ships the manifest, the package is registered at that
|
|
15
|
+
// moment and its TypeScript is NOT transpiled — the package's own types are
|
|
16
|
+
// the compile-time contract, and the native shim is the implementation.
|
|
17
|
+
// Nothing scans node_modules; an installed-but-never-imported library
|
|
18
|
+
// contributes nothing.
|
|
19
|
+
//
|
|
20
|
+
// The transpiler writes a `libraries.json` sidecar next to the emitted
|
|
21
|
+
// sources (the board-constants.json convention) recording the libraries that
|
|
22
|
+
// were actually used, so build-time consumers (framework toolchains) can
|
|
23
|
+
// re-derive their contributions idempotently from disk.
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
import fs from "node:fs";
|
|
26
|
+
import path from "node:path";
|
|
27
|
+
import { createRequire } from "node:module";
|
|
28
|
+
import { toModuleKey } from "./utils/strings.js";
|
|
29
|
+
/** Sidecar file name — written next to the emitted sources. */
|
|
30
|
+
export const LIBRARY_SIDECAR_NAME = "libraries.json";
|
|
31
|
+
const registered = new Map();
|
|
32
|
+
/** Specifiers known NOT to be library packages (avoids repeated resolve attempts). */
|
|
33
|
+
const knownNonLibraries = new Set();
|
|
34
|
+
/** Clear all registrations. Called once per transpilation, before graph build. */
|
|
35
|
+
export function resetCuttlefishLibraries() {
|
|
36
|
+
registered.clear();
|
|
37
|
+
knownNonLibraries.clear();
|
|
38
|
+
}
|
|
39
|
+
/** All libraries registered during this transpilation (import-driven). */
|
|
40
|
+
export function getRegisteredCuttlefishLibraries() {
|
|
41
|
+
return [...registered.values()];
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Whether the specifier resolves to a library registered in this
|
|
45
|
+
* transpilation. Library packages are exempt from the @typecad/* "SDK
|
|
46
|
+
* imports are type-level only" include skip — their import IS the include
|
|
47
|
+
* of the shim header.
|
|
48
|
+
*/
|
|
49
|
+
export function isRegisteredCuttlefishLibrary(moduleSpecifier) {
|
|
50
|
+
return registered.has(moduleSpecifier.toLowerCase());
|
|
51
|
+
}
|
|
52
|
+
function parseManifest(packageRoot) {
|
|
53
|
+
const manifestPath = path.join(packageRoot, "cuttlefish.library.json");
|
|
54
|
+
let raw;
|
|
55
|
+
try {
|
|
56
|
+
raw = JSON.parse(fs.readFileSync(manifestPath, "utf8"));
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
if (typeof raw !== "object" || raw === null)
|
|
62
|
+
return undefined;
|
|
63
|
+
const m = raw;
|
|
64
|
+
if (typeof m.id !== "string" ||
|
|
65
|
+
typeof m.module !== "string" ||
|
|
66
|
+
typeof m.framework !== "string" ||
|
|
67
|
+
typeof m.include !== "string" ||
|
|
68
|
+
typeof m.gateToken !== "string" ||
|
|
69
|
+
!Array.isArray(m.shims) ||
|
|
70
|
+
m.shims.length === 0) {
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
for (const shim of m.shims) {
|
|
74
|
+
if (typeof shim?.path !== "string" || typeof shim?.outName !== "string") {
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
id: m.id,
|
|
80
|
+
module: m.module,
|
|
81
|
+
framework: m.framework,
|
|
82
|
+
targets: m.targets,
|
|
83
|
+
include: m.include,
|
|
84
|
+
gateToken: m.gateToken,
|
|
85
|
+
shims: m.shims,
|
|
86
|
+
kconfig: Array.isArray(m.kconfig) ? m.kconfig.filter((k) => typeof k === "string") : [],
|
|
87
|
+
overlay: typeof m.overlay === "string" ? m.overlay : undefined,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Resolve the package root for a bare npm specifier from the importing file,
|
|
92
|
+
* then register the package as a cuttlefish library if it ships a
|
|
93
|
+
* `cuttlefish.library.json`. Returns true when the specifier resolved to a
|
|
94
|
+
* registered library (the import must then be skipped by the graph builder).
|
|
95
|
+
*/
|
|
96
|
+
export function registerCuttlefishLibraryFromSpecifier(fromFile, moduleSpecifier) {
|
|
97
|
+
// Only bare npm specifiers ('pkg' or '@scope/pkg'[/subpath]) — relative
|
|
98
|
+
// imports and node builtins cannot be library packages.
|
|
99
|
+
if (moduleSpecifier.startsWith(".") || moduleSpecifier.startsWith("node:")) {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
const cacheKey = moduleSpecifier.toLowerCase();
|
|
103
|
+
if (knownNonLibraries.has(cacheKey))
|
|
104
|
+
return false;
|
|
105
|
+
if (registered.has(cacheKey))
|
|
106
|
+
return true;
|
|
107
|
+
let packageRoot;
|
|
108
|
+
try {
|
|
109
|
+
const req = createRequire(path.resolve(fromFile));
|
|
110
|
+
// Resolve the package entry, then walk up to the nearest package.json —
|
|
111
|
+
// the package root. (Resolving the manifest subpath directly would be
|
|
112
|
+
// blocked by exports encapsulation; the entry point always resolves.)
|
|
113
|
+
const entry = req.resolve(moduleSpecifier);
|
|
114
|
+
let dir = path.dirname(entry);
|
|
115
|
+
while (dir !== path.dirname(dir)) {
|
|
116
|
+
if (fs.existsSync(path.join(dir, "package.json")))
|
|
117
|
+
break;
|
|
118
|
+
dir = path.dirname(dir);
|
|
119
|
+
}
|
|
120
|
+
if (dir !== path.dirname(dir))
|
|
121
|
+
packageRoot = dir;
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
packageRoot = undefined;
|
|
125
|
+
}
|
|
126
|
+
if (packageRoot === undefined) {
|
|
127
|
+
knownNonLibraries.add(cacheKey);
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
const manifest = parseManifest(packageRoot);
|
|
131
|
+
if (manifest === undefined) {
|
|
132
|
+
knownNonLibraries.add(cacheKey);
|
|
133
|
+
return false;
|
|
134
|
+
}
|
|
135
|
+
registered.set(cacheKey, { manifest, packageRoot });
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
/** Libdef entries for the registry: import → shim include. */
|
|
139
|
+
export function cuttlefishLibraryLibdefs() {
|
|
140
|
+
return getRegisteredCuttlefishLibraries().map(({ manifest }) => ({
|
|
141
|
+
module: manifest.module,
|
|
142
|
+
include: manifest.include,
|
|
143
|
+
}));
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Validate registered libraries against the loaded framework (and, when
|
|
147
|
+
* known, the build target). Throws with an actionable message when a library
|
|
148
|
+
* cannot work in this project — far earlier and clearer than the native
|
|
149
|
+
* compiler would.
|
|
150
|
+
*/
|
|
151
|
+
export function validateCuttlefishLibraries(frameworkId, buildTarget) {
|
|
152
|
+
for (const { manifest } of getRegisteredCuttlefishLibraries()) {
|
|
153
|
+
if (manifest.framework !== frameworkId) {
|
|
154
|
+
throw new Error(`Library ${manifest.module} requires the '${manifest.framework}' framework, ` +
|
|
155
|
+
`but this project uses '${frameworkId}'. ` +
|
|
156
|
+
`Install the matching framework or a '${frameworkId}' counterpart of this library.`);
|
|
157
|
+
}
|
|
158
|
+
if (buildTarget !== undefined &&
|
|
159
|
+
manifest.targets !== undefined &&
|
|
160
|
+
manifest.targets.length > 0 &&
|
|
161
|
+
!manifest.targets.some((t) => buildTarget.toLowerCase().startsWith(t.toLowerCase()))) {
|
|
162
|
+
throw new Error(`Library ${manifest.module} supports board targets ` +
|
|
163
|
+
`[${manifest.targets.join(", ")}], but this project builds for '${buildTarget}'.`);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
function writeIfChanged(filePath, content) {
|
|
168
|
+
try {
|
|
169
|
+
if (fs.existsSync(filePath) && fs.readFileSync(filePath, "utf8") === content)
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
/* fall through and write */
|
|
174
|
+
}
|
|
175
|
+
fs.writeFileSync(filePath, content);
|
|
176
|
+
}
|
|
177
|
+
function readEmittedTokens(srcDir) {
|
|
178
|
+
let out = "";
|
|
179
|
+
let names = [];
|
|
180
|
+
try {
|
|
181
|
+
names = fs.readdirSync(srcDir);
|
|
182
|
+
}
|
|
183
|
+
catch {
|
|
184
|
+
return "";
|
|
185
|
+
}
|
|
186
|
+
for (const name of names) {
|
|
187
|
+
if (!/\.(cpp|cc|c|h|hpp|ino)$/.test(name))
|
|
188
|
+
continue;
|
|
189
|
+
try {
|
|
190
|
+
out += fs.readFileSync(path.join(srcDir, name), "utf8");
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
/* best-effort */
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return out;
|
|
197
|
+
}
|
|
198
|
+
function readSidecar(sidecarPath) {
|
|
199
|
+
try {
|
|
200
|
+
const raw = JSON.parse(fs.readFileSync(sidecarPath, "utf8"));
|
|
201
|
+
if (!Array.isArray(raw))
|
|
202
|
+
return [];
|
|
203
|
+
return raw.filter((e) => typeof e === "object" && e !== null && typeof e.module === "string");
|
|
204
|
+
}
|
|
205
|
+
catch {
|
|
206
|
+
return [];
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Emit the library artifacts for the libraries actually used in this
|
|
211
|
+
* transpilation, and write the `libraries.json` sidecar:
|
|
212
|
+
*
|
|
213
|
+
* 1. scan the emitted sources for each library's gate token;
|
|
214
|
+
* 2. write (write-if-changed) the shims of every gated library into the
|
|
215
|
+
* emitted source directory — the framework scaffold's CMake/program
|
|
216
|
+
* regen picks the sources up automatically;
|
|
217
|
+
* 3. delete shim files recorded in the previous sidecar that are no longer
|
|
218
|
+
* used, so removing an import removes its native code;
|
|
219
|
+
* 4. persist the sidecar (even when empty — that clears stale state).
|
|
220
|
+
*/
|
|
221
|
+
export function writeCuttlefishLibraryArtifacts(outDir, srcDir) {
|
|
222
|
+
const sidecarPath = path.join(outDir, LIBRARY_SIDECAR_NAME);
|
|
223
|
+
const previous = readSidecar(sidecarPath);
|
|
224
|
+
const previousShims = new Set(previous.flatMap((e) => e.shims));
|
|
225
|
+
const emitted = readEmittedTokens(srcDir);
|
|
226
|
+
const usedShims = new Set();
|
|
227
|
+
const entries = [];
|
|
228
|
+
for (const { manifest, packageRoot } of getRegisteredCuttlefishLibraries()) {
|
|
229
|
+
if (!emitted.includes(manifest.gateToken))
|
|
230
|
+
continue;
|
|
231
|
+
for (const shim of manifest.shims) {
|
|
232
|
+
let content;
|
|
233
|
+
try {
|
|
234
|
+
content = fs.readFileSync(path.join(packageRoot, shim.path), "utf8");
|
|
235
|
+
}
|
|
236
|
+
catch {
|
|
237
|
+
continue; // broken library packaging — the missing header surfaces at compile
|
|
238
|
+
}
|
|
239
|
+
writeIfChanged(path.join(srcDir, shim.outName), content);
|
|
240
|
+
usedShims.add(shim.outName);
|
|
241
|
+
}
|
|
242
|
+
entries.push({
|
|
243
|
+
id: manifest.id,
|
|
244
|
+
module: manifest.module,
|
|
245
|
+
framework: manifest.framework,
|
|
246
|
+
gateToken: manifest.gateToken,
|
|
247
|
+
kconfig: manifest.kconfig ?? [],
|
|
248
|
+
overlay: manifest.overlay ? path.join(packageRoot, manifest.overlay) : null,
|
|
249
|
+
shims: manifest.shims.map((s) => s.outName).filter((n) => usedShims.has(n)),
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
// Remove shims from a previous build whose library is no longer used.
|
|
253
|
+
for (const name of previousShims) {
|
|
254
|
+
if (usedShims.has(name))
|
|
255
|
+
continue;
|
|
256
|
+
try {
|
|
257
|
+
fs.rmSync(path.join(srcDir, name), { force: true });
|
|
258
|
+
}
|
|
259
|
+
catch {
|
|
260
|
+
/* best-effort */
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
writeIfChanged(sidecarPath, JSON.stringify(entries, null, 2) + "\n");
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Read the `libraries.json` sidecar for build-time consumers (framework
|
|
267
|
+
* toolchains). Tolerant of a missing or corrupt file (returns []).
|
|
268
|
+
*/
|
|
269
|
+
export function readCuttlefishLibrarySidecar(outDir) {
|
|
270
|
+
return readSidecar(path.join(outDir, LIBRARY_SIDECAR_NAME));
|
|
271
|
+
}
|
|
272
|
+
/** Re-exported for transpile.ts libdef injection convenience. */
|
|
273
|
+
export function libraryDefinitionKey(module) {
|
|
274
|
+
return toModuleKey(module);
|
|
275
|
+
}
|
|
@@ -20,14 +20,19 @@ export function mapPeripheralName(name, strategy) {
|
|
|
20
20
|
if (mapped)
|
|
21
21
|
return mapped;
|
|
22
22
|
}
|
|
23
|
-
// Default fallbacks if strategy doesn't provide a mapping
|
|
23
|
+
// Default fallbacks if strategy doesn't provide a mapping: the canonical
|
|
24
|
+
// TypeCAD controller names. (The Arduino object names — Serial/Wire/SPI —
|
|
25
|
+
// went with framework-arduino; every live consumer only parses the
|
|
26
|
+
// trailing controller digit, which the canonical names carry too.)
|
|
24
27
|
const canonical = name.toUpperCase();
|
|
25
28
|
if (canonical === "UART0")
|
|
26
|
-
return "
|
|
29
|
+
return "UART0";
|
|
27
30
|
if (canonical === "I2C0")
|
|
28
|
-
return "
|
|
31
|
+
return "I2C0";
|
|
29
32
|
if (canonical === "SPI0")
|
|
30
|
-
return "
|
|
33
|
+
return "SPI0";
|
|
34
|
+
if (canonical === "USB0")
|
|
35
|
+
return "USB0";
|
|
31
36
|
return undefined;
|
|
32
37
|
}
|
|
33
38
|
// ---------------------------------------------------------------------------
|
|
@@ -3,12 +3,11 @@ export declare function writeSourceMap(map: GeneratedSourceMap): string;
|
|
|
3
3
|
export declare function readSourceMap(mapPath: string): GeneratedSourceMap;
|
|
4
4
|
export declare function makeGeneratedMap(generatedFilePath: string, sourceFilePath: string, entries: SourceMapEntry[]): GeneratedSourceMap;
|
|
5
5
|
export declare function mapCppLocationToTs(map: GeneratedSourceMap, cppLine: number, cppColumn: number, message?: string, cppFilePath?: string): MappedDiagnostic;
|
|
6
|
-
export declare function resolveMapPath(mapFileOrGeneratedFile: string): string;
|
|
7
6
|
/**
|
|
8
|
-
* Resolve source map path for a
|
|
9
|
-
* When a
|
|
7
|
+
* Resolve source map path for a generated program
|
|
8
|
+
* When a program is flattened, the source map needs to account for merged files
|
|
10
9
|
*/
|
|
11
|
-
export declare function
|
|
10
|
+
export declare function resolveSourceMapForProgram(programPath: string, originalSourceMapPath?: string): string | undefined;
|
|
12
11
|
/**
|
|
13
12
|
* Index of every per-file source map in a build directory, keyed by the
|
|
14
13
|
* normalized generated file path (the .cpp/.h each map covers).
|
|
@@ -80,42 +80,36 @@ export function mapCppLocationToTs(map, cppLine, cppColumn, message, cppFilePath
|
|
|
80
80
|
symbolName: best?.symbolName,
|
|
81
81
|
};
|
|
82
82
|
}
|
|
83
|
-
export function resolveMapPath(mapFileOrGeneratedFile) {
|
|
84
|
-
if (mapFileOrGeneratedFile.endsWith(".thcppmap.json")) {
|
|
85
|
-
return path.resolve(process.cwd(), mapFileOrGeneratedFile);
|
|
86
|
-
}
|
|
87
|
-
return toSourceMapPath(path.resolve(process.cwd(), mapFileOrGeneratedFile));
|
|
88
|
-
}
|
|
89
83
|
/**
|
|
90
|
-
* Resolve source map path for a
|
|
91
|
-
* When a
|
|
84
|
+
* Resolve source map path for a generated program
|
|
85
|
+
* When a program is flattened, the source map needs to account for merged files
|
|
92
86
|
*/
|
|
93
|
-
export function
|
|
94
|
-
const
|
|
95
|
-
const
|
|
87
|
+
export function resolveSourceMapForProgram(programPath, originalSourceMapPath) {
|
|
88
|
+
const programDir = path.dirname(programPath);
|
|
89
|
+
const programName = path.basename(programPath, path.extname(programPath));
|
|
96
90
|
// First try the original source map path
|
|
97
91
|
if (originalSourceMapPath && fs.existsSync(originalSourceMapPath)) {
|
|
98
92
|
return originalSourceMapPath;
|
|
99
93
|
}
|
|
100
|
-
// Try to find source maps for the
|
|
94
|
+
// Try to find source maps for the program directory
|
|
101
95
|
const possibleMaps = [
|
|
102
|
-
path.join(
|
|
103
|
-
path.join(
|
|
104
|
-
path.join(
|
|
96
|
+
path.join(programDir, `${programName}.thcppmap.json`),
|
|
97
|
+
path.join(programDir, `${programName}.cpp.thcppmap.json`),
|
|
98
|
+
path.join(programDir, "example.thcppmap.json"), // Common case for example programs
|
|
105
99
|
];
|
|
106
100
|
for (const mapPath of possibleMaps) {
|
|
107
101
|
if (fs.existsSync(mapPath)) {
|
|
108
102
|
return mapPath;
|
|
109
103
|
}
|
|
110
104
|
}
|
|
111
|
-
// Look for any .thcppmap.json files in the
|
|
105
|
+
// Look for any .thcppmap.json files in the program directory
|
|
112
106
|
try {
|
|
113
|
-
const files = fs.readdirSync(
|
|
107
|
+
const files = fs.readdirSync(programDir);
|
|
114
108
|
const mapFiles = files.filter(f => f.endsWith('.thcppmap.json'));
|
|
115
109
|
if (mapFiles.length > 0) {
|
|
116
110
|
// Return the most recent one
|
|
117
111
|
const latest = mapFiles.sort().pop();
|
|
118
|
-
return path.join(
|
|
112
|
+
return path.join(programDir, latest);
|
|
119
113
|
}
|
|
120
114
|
}
|
|
121
115
|
catch {
|
|
@@ -12,10 +12,10 @@ export declare function topologicalSortFiles(files: string[], dependencies: Map<
|
|
|
12
12
|
* Also detects native C++ modules (.d.ts + .cpp pairs).
|
|
13
13
|
* Files are returned in dependency order (dependencies before dependents).
|
|
14
14
|
*
|
|
15
|
-
* @param
|
|
16
|
-
*
|
|
15
|
+
* @param boardTarget When provided, `@typecad/board` imports fall back to
|
|
16
|
+
* this package specifier (legacy; generated boards win).
|
|
17
17
|
*/
|
|
18
|
-
export declare function collectTranspileGraph(entryFile: string,
|
|
18
|
+
export declare function collectTranspileGraph(entryFile: string, boardTarget?: string, imageDecodeOpts?: {
|
|
19
19
|
maxW?: number;
|
|
20
20
|
maxH?: number;
|
|
21
21
|
}): Promise<TranspileGraphResult>;
|