@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,397 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// `cuttlefish library init` — scaffold a new cuttlefish library package.
|
|
3
|
+
//
|
|
4
|
+
// Generates everything a library author needs to reach a valid, publishable
|
|
5
|
+
// package: the typed API skeleton, the cuttlefish.library.json manifest,
|
|
6
|
+
// AUTOSAR C++14-compliant shim stubs, (for Zephyr) an overlay fragment, a
|
|
7
|
+
// README, and a starter compliance test. The scaffold is deliberately tiny —
|
|
8
|
+
// it must pass `cuttlefish library validate` the moment it lands.
|
|
9
|
+
//
|
|
10
|
+
// Prompts follow the create-wizard's readline pattern (no prompt dependency);
|
|
11
|
+
// flags override prompts, and --yes takes defaults for anything unanswered.
|
|
12
|
+
// ---------------------------------------------------------------------------
|
|
13
|
+
import fs from "node:fs";
|
|
14
|
+
import path from "node:path";
|
|
15
|
+
import readline from "node:readline/promises";
|
|
16
|
+
import { stdin as input, stdout as output } from "node:process";
|
|
17
|
+
import chalk from "chalk";
|
|
18
|
+
import { LIBRARY_CATEGORIES, libraryCategory, requiredLibraryKeywords } from "./catalog.js";
|
|
19
|
+
import { FRAMEWORK_CATALOG } from "../create/framework-catalog.js";
|
|
20
|
+
// ── Naming derivations ──────────────────────────────────────────────────────
|
|
21
|
+
/** Slug of a package basename: lowercase, runs of non-alphanumerics → '-'. */
|
|
22
|
+
export function libraryIdFromPackageName(packageName) {
|
|
23
|
+
const base = packageName.includes("/") ? packageName.split("/").pop() : packageName;
|
|
24
|
+
const slug = base.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
25
|
+
if (slug.length === 0) {
|
|
26
|
+
throw new Error(`Cannot derive a library id from '${packageName}'.`);
|
|
27
|
+
}
|
|
28
|
+
return slug;
|
|
29
|
+
}
|
|
30
|
+
/** '__tc_my_lib' for id 'my-lib' — a valid C++ identifier prefix. */
|
|
31
|
+
export function tokenBaseFromId(id) {
|
|
32
|
+
return id.replace(/-/g, "_");
|
|
33
|
+
}
|
|
34
|
+
function pascalCase(id) {
|
|
35
|
+
return id.split("-").filter(Boolean).map((s) => s.charAt(0).toUpperCase() + s.slice(1)).join("");
|
|
36
|
+
}
|
|
37
|
+
function camelCase(id) {
|
|
38
|
+
const pascal = pascalCase(id);
|
|
39
|
+
return pascal.charAt(0).toLowerCase() + pascal.slice(1);
|
|
40
|
+
}
|
|
41
|
+
function isValidNpmName(name) {
|
|
42
|
+
// Conservative check: optional @scope/ + url-safe segments.
|
|
43
|
+
return /^(?:@[a-z0-9-~][a-z0-9-._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/i.test(name);
|
|
44
|
+
}
|
|
45
|
+
async function promptText(rl, prompt, defaultValue, validate) {
|
|
46
|
+
for (;;) {
|
|
47
|
+
const suffix = defaultValue ? ` (${defaultValue})` : "";
|
|
48
|
+
const answer = await rl.question(`${chalk.cyan("?")} ${prompt}${suffix}: `);
|
|
49
|
+
const value = (answer.trim() || (defaultValue ?? "")).trim();
|
|
50
|
+
if (validate) {
|
|
51
|
+
const error = validate(value);
|
|
52
|
+
if (error) {
|
|
53
|
+
console.log(` ${chalk.red("✗")} ${error}`);
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return value;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
async function promptSelect(rl, prompt, options) {
|
|
61
|
+
console.log(`${chalk.cyan("?")} ${prompt}:`);
|
|
62
|
+
for (let i = 0; i < options.length; i++) {
|
|
63
|
+
console.log(` ${chalk.dim(`${i + 1})`)} ${options[i].label}`);
|
|
64
|
+
}
|
|
65
|
+
for (;;) {
|
|
66
|
+
const answer = await rl.question(` Enter number (1-${options.length}): `);
|
|
67
|
+
const idx = parseInt(answer.trim(), 10) - 1;
|
|
68
|
+
if (idx >= 0 && idx < options.length) {
|
|
69
|
+
return options[idx].value;
|
|
70
|
+
}
|
|
71
|
+
console.log(` ${chalk.red("✗")} Please enter a number between 1 and ${options.length}.`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
function renderPackageJson(m) {
|
|
75
|
+
return JSON.stringify({
|
|
76
|
+
name: m.packageName,
|
|
77
|
+
version: "1.0.0",
|
|
78
|
+
description: `${m.category.label} library for cuttlefish (${m.framework} framework)`,
|
|
79
|
+
type: "module",
|
|
80
|
+
main: "./dist/index.js",
|
|
81
|
+
types: "./dist/index.d.ts",
|
|
82
|
+
files: ["dist", "src", "shims", "cuttlefish.library.json"],
|
|
83
|
+
scripts: {
|
|
84
|
+
build: "tsc",
|
|
85
|
+
prepublishOnly: "npm run build",
|
|
86
|
+
},
|
|
87
|
+
license: "MIT",
|
|
88
|
+
keywords: [
|
|
89
|
+
"cuttlefish",
|
|
90
|
+
...requiredLibraryKeywords(m.category.id),
|
|
91
|
+
m.framework,
|
|
92
|
+
...m.id.split("-"),
|
|
93
|
+
],
|
|
94
|
+
engines: { node: ">=18" },
|
|
95
|
+
sideEffects: false,
|
|
96
|
+
exports: {
|
|
97
|
+
".": { types: "./dist/index.d.ts", default: "./dist/index.js" },
|
|
98
|
+
"./package.json": "./package.json",
|
|
99
|
+
},
|
|
100
|
+
devDependencies: {
|
|
101
|
+
"@typecad/cuttlefish": "*",
|
|
102
|
+
vitest: "^4.0.0",
|
|
103
|
+
},
|
|
104
|
+
}, null, 2) + "\n";
|
|
105
|
+
}
|
|
106
|
+
function renderTsconfig() {
|
|
107
|
+
return JSON.stringify({
|
|
108
|
+
compilerOptions: {
|
|
109
|
+
composite: true,
|
|
110
|
+
target: "ES2021",
|
|
111
|
+
module: "Node16",
|
|
112
|
+
moduleResolution: "Node16",
|
|
113
|
+
strict: true,
|
|
114
|
+
esModuleInterop: true,
|
|
115
|
+
skipLibCheck: true,
|
|
116
|
+
forceConsistentCasingInFileNames: true,
|
|
117
|
+
declaration: true,
|
|
118
|
+
declarationMap: false,
|
|
119
|
+
sourceMap: false,
|
|
120
|
+
rootDir: "src",
|
|
121
|
+
outDir: "dist",
|
|
122
|
+
},
|
|
123
|
+
include: ["src/**/*.ts"],
|
|
124
|
+
}, null, 2) + "\n";
|
|
125
|
+
}
|
|
126
|
+
function renderApi(m) {
|
|
127
|
+
return `// ---------------------------------------------------------------------------
|
|
128
|
+
// ${m.packageName} — cuttlefish library package (${m.category.label.toLowerCase()}).
|
|
129
|
+
//
|
|
130
|
+
// This file is the typed API contract: it is never executed. The cuttlefish
|
|
131
|
+
// transpiler lowers the import to the shim header (shims/__tc_${m.tokenBase}.h),
|
|
132
|
+
// and calls on the exported instance render verbatim into the generated C++.
|
|
133
|
+
// Two rules keep that contract intact:
|
|
134
|
+
//
|
|
135
|
+
// 1. Method names are load-bearing. The C++ class in the shim must define
|
|
136
|
+
// exactly these names — 'rgbLed'-style call sites appear in the C++ as-is.
|
|
137
|
+
// 2. Export a singleton instance, not a constructor. A bare 'new' in user
|
|
138
|
+
// code renders as a C++ heap allocation, which the AUTOSAR C++14 rules
|
|
139
|
+
// the shims follow forbid.
|
|
140
|
+
// ---------------------------------------------------------------------------
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* ${m.category.description} (${m.category.examples}).
|
|
144
|
+
*/
|
|
145
|
+
export class ${m.className} {
|
|
146
|
+
/**
|
|
147
|
+
* Prepare the hardware. Call once from setup(). The stub in the shim is a
|
|
148
|
+
* no-op — replace it with your initialization code.
|
|
149
|
+
*/
|
|
150
|
+
begin(): this {
|
|
151
|
+
return this;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Add your API here. Every method needs a matching member on the C++ class.
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** The instance to use — a preconstructed singleton (see rule 2 above). */
|
|
158
|
+
export const ${m.instanceName}: ${m.className} = new ${m.className}();
|
|
159
|
+
`;
|
|
160
|
+
}
|
|
161
|
+
function renderManifest(m) {
|
|
162
|
+
const manifest = {
|
|
163
|
+
id: m.id,
|
|
164
|
+
module: m.packageName,
|
|
165
|
+
framework: m.framework,
|
|
166
|
+
include: `"__tc_${m.tokenBase}.h"`,
|
|
167
|
+
gateToken: `__tc_${m.tokenBase}`,
|
|
168
|
+
shims: [
|
|
169
|
+
{ path: `shims/__tc_${m.tokenBase}.h`, outName: `__tc_${m.tokenBase}.h` },
|
|
170
|
+
{ path: `shims/__tc_${m.tokenBase}.cpp`, outName: `__tc_${m.tokenBase}.cpp` },
|
|
171
|
+
],
|
|
172
|
+
};
|
|
173
|
+
if (m.targets.length > 0) {
|
|
174
|
+
manifest.targets = m.targets;
|
|
175
|
+
}
|
|
176
|
+
if (m.isZephyr) {
|
|
177
|
+
manifest.kconfig = ["# TODO: the CONFIG_ lines your library needs, e.g. CONFIG_LED_STRIP=y"];
|
|
178
|
+
manifest.overlay = `shims/${m.id}.overlay`;
|
|
179
|
+
}
|
|
180
|
+
return JSON.stringify(manifest, null, 2) + "\n";
|
|
181
|
+
}
|
|
182
|
+
function renderShimHeader(m) {
|
|
183
|
+
return `// ---------------------------------------------------------------------------
|
|
184
|
+
// __tc_${m.tokenBase}.h — ${m.packageName} shim.
|
|
185
|
+
//
|
|
186
|
+
// The implementation behind the TypeScript API in src/index.ts. Calls on the
|
|
187
|
+
// exported instance render verbatim, so the class name, the instance name,
|
|
188
|
+
// and the method names must match src/index.ts exactly.
|
|
189
|
+
//
|
|
190
|
+
// AUTOSAR C++14 by construction: fixed-width integers, static_cast only (no
|
|
191
|
+
// C-style casts), no heap, 'final' on the leaf class. The generated
|
|
192
|
+
// application's compliance check runs over these bytes.
|
|
193
|
+
// ---------------------------------------------------------------------------
|
|
194
|
+
|
|
195
|
+
#ifndef TC_${m.tokenBase.toUpperCase()}_H_
|
|
196
|
+
#define TC_${m.tokenBase.toUpperCase()}_H_
|
|
197
|
+
|
|
198
|
+
#include <cstdint>
|
|
199
|
+
${m.isZephyr ? "#include <zephyr/kernel.h>\n" : ""}class ${m.className} final
|
|
200
|
+
{
|
|
201
|
+
public:
|
|
202
|
+
${m.className}& begin();
|
|
203
|
+
|
|
204
|
+
private:
|
|
205
|
+
bool initialized_ = false;
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
extern ${m.className} ${m.instanceName};
|
|
209
|
+
|
|
210
|
+
#endif // TC_${m.tokenBase.toUpperCase()}_H_
|
|
211
|
+
`;
|
|
212
|
+
}
|
|
213
|
+
function renderShimSource(m) {
|
|
214
|
+
return `// ---------------------------------------------------------------------------
|
|
215
|
+
// __tc_${m.tokenBase}.cpp — ${m.packageName} shim implementation.
|
|
216
|
+
// See __tc_${m.tokenBase}.h for the contract. AUTOSAR C++14 compliant.
|
|
217
|
+
// ---------------------------------------------------------------------------
|
|
218
|
+
|
|
219
|
+
#include "__tc_${m.tokenBase}.h"
|
|
220
|
+
|
|
221
|
+
${m.className}& ${m.className}::begin()
|
|
222
|
+
{
|
|
223
|
+
initialized_ = true;
|
|
224
|
+
return *this;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
${m.className} ${m.instanceName};
|
|
228
|
+
`;
|
|
229
|
+
}
|
|
230
|
+
function renderOverlay(m) {
|
|
231
|
+
return `/*
|
|
232
|
+
* ${m.id} — devicetree overlay fragment (${m.category.label.toLowerCase()}).
|
|
233
|
+
*
|
|
234
|
+
* Appended to the generated boards/<board>.overlay when this library's import
|
|
235
|
+
* is used. Describe the hardware here: controllers, pins, binding properties.
|
|
236
|
+
* When upstream Zephyr ships a sample overlay for your part, adapt it and
|
|
237
|
+
* credit it in a comment.
|
|
238
|
+
*/
|
|
239
|
+
|
|
240
|
+
/ {
|
|
241
|
+
\t/* aliases { your-alias = &your_node; }; */
|
|
242
|
+
};
|
|
243
|
+
`;
|
|
244
|
+
}
|
|
245
|
+
function renderReadme(m) {
|
|
246
|
+
return `# ${m.packageName}
|
|
247
|
+
|
|
248
|
+
A cuttlefish library package — ${m.category.label.toLowerCase()} (${m.category.description}),
|
|
249
|
+
for the **${m.framework}** framework${m.targets.length > 0 ? ` (targets: ${m.targets.join(", ")})` : ""}.
|
|
250
|
+
|
|
251
|
+
## Use it
|
|
252
|
+
|
|
253
|
+
\`\`\`ts
|
|
254
|
+
import { ${m.instanceName} } from '${m.packageName}';
|
|
255
|
+
|
|
256
|
+
${m.instanceName}.begin();
|
|
257
|
+
\`\`\`
|
|
258
|
+
|
|
259
|
+
## Carry the hardware facts
|
|
260
|
+
|
|
261
|
+
This library owns the facts so projects don't have to. Document, in this
|
|
262
|
+
README, everything a user would otherwise hunt through datasheets for:
|
|
263
|
+
|
|
264
|
+
- which board revisions and targets are supported (and which pins they use),
|
|
265
|
+
- which peripherals the library borrows (say so — a user may want them),
|
|
266
|
+
- wiring requirements (I2C address straps, level shifting, pull-ups).
|
|
267
|
+
|
|
268
|
+
## Authoring checklist
|
|
269
|
+
|
|
270
|
+
- Method names in src/index.ts match the C++ shim names exactly; calls render verbatim.
|
|
271
|
+
- Export a singleton instance, not a constructor.
|
|
272
|
+
- Shims pass \`cuttlefish library validate\` (AUTOSAR C++14, strict).
|
|
273
|
+
- \`cuttlefish.library.json\` declares framework${m.isZephyr ? ", kconfig, overlay" : ""} and the shim files.
|
|
274
|
+
- package.json keywords include the marker (\`cuttlefish-library\`) and category (\`cuttlefish-${m.category.id}\`) keywords.
|
|
275
|
+
|
|
276
|
+
Validate with \`cuttlefish library validate .\` from this directory.
|
|
277
|
+
`;
|
|
278
|
+
}
|
|
279
|
+
function renderStarterTest(m) {
|
|
280
|
+
return `import { describe, it, expect } from "vitest";
|
|
281
|
+
import { readFileSync } from "node:fs";
|
|
282
|
+
import { join, dirname } from "node:path";
|
|
283
|
+
import { fileURLToPath } from "node:url";
|
|
284
|
+
import { ComplianceContext, runSelfCheck } from "@typecad/cuttlefish/compliance";
|
|
285
|
+
|
|
286
|
+
// The shipped shim bytes must pass --autosar=strict, exactly like framework
|
|
287
|
+
// shims — the generated application's compliance check runs over them.
|
|
288
|
+
|
|
289
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
290
|
+
|
|
291
|
+
describe("${m.packageName} shims under --autosar=strict", () => {
|
|
292
|
+
it("__tc_${m.tokenBase}.h/.cpp produce no unrecorded AUTOSAR violations", () => {
|
|
293
|
+
const header = readFileSync(join(here, "..", "shims", "__tc_${m.tokenBase}.h"), "utf8").split("\\n");
|
|
294
|
+
const source = readFileSync(join(here, "..", "shims", "__tc_${m.tokenBase}.cpp"), "utf8").split("\\n");
|
|
295
|
+
const ctx = new ComplianceContext("strict");
|
|
296
|
+
const findings = runSelfCheck(ctx, source, header);
|
|
297
|
+
expect(findings).toEqual([]);
|
|
298
|
+
});
|
|
299
|
+
});
|
|
300
|
+
`;
|
|
301
|
+
}
|
|
302
|
+
// ── Scaffolding ─────────────────────────────────────────────────────────────
|
|
303
|
+
function writeIfChanged(filePath, content) {
|
|
304
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
305
|
+
fs.writeFileSync(filePath, content);
|
|
306
|
+
}
|
|
307
|
+
export async function runLibraryInit(options) {
|
|
308
|
+
const interactive = options.yes !== true;
|
|
309
|
+
let packageName = options.name;
|
|
310
|
+
let framework = options.framework;
|
|
311
|
+
let categoryId = options.category;
|
|
312
|
+
let targetsRaw = options.targets;
|
|
313
|
+
const missing = packageName === undefined || framework === undefined || categoryId === undefined;
|
|
314
|
+
if (interactive && missing) {
|
|
315
|
+
const rl = readline.createInterface({ input, output });
|
|
316
|
+
try {
|
|
317
|
+
if (packageName === undefined) {
|
|
318
|
+
packageName = await promptText(rl, "Package name (npm name, e.g. @acme/my-lib)", "my-lib", (v) => isValidNpmName(v) ? null : "Not a valid npm package name.");
|
|
319
|
+
}
|
|
320
|
+
if (framework === undefined) {
|
|
321
|
+
framework = await promptSelect(rl, "Framework", FRAMEWORK_CATALOG.map((f) => ({ label: f.label, value: f.id })));
|
|
322
|
+
}
|
|
323
|
+
if (categoryId === undefined) {
|
|
324
|
+
categoryId = await promptSelect(rl, "Category", LIBRARY_CATEGORIES.map((c) => ({ label: `${c.label} — ${c.description}`, value: c.id })));
|
|
325
|
+
}
|
|
326
|
+
if (targetsRaw === undefined) {
|
|
327
|
+
targetsRaw = await promptText(rl, "Board targets (comma-separated build-target prefixes, empty for any)", "");
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
finally {
|
|
331
|
+
rl.close();
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
packageName = (packageName ?? "my-lib").trim();
|
|
335
|
+
framework = (framework ?? "zephyr").trim();
|
|
336
|
+
categoryId = (categoryId ?? "utility").trim();
|
|
337
|
+
if (!isValidNpmName(packageName)) {
|
|
338
|
+
throw new Error(`'${packageName}' is not a valid npm package name.`);
|
|
339
|
+
}
|
|
340
|
+
const frameworkEntry = FRAMEWORK_CATALOG.find((f) => f.id === framework);
|
|
341
|
+
if (!frameworkEntry) {
|
|
342
|
+
throw new Error(`Unknown framework '${framework}'. Known: ${FRAMEWORK_CATALOG.map((f) => f.id).join(", ")}.`);
|
|
343
|
+
}
|
|
344
|
+
const category = libraryCategory(categoryId);
|
|
345
|
+
if (!category) {
|
|
346
|
+
throw new Error(`Unknown category '${categoryId}'. Known: ${LIBRARY_CATEGORIES.map((c) => c.id).join(", ")}.`);
|
|
347
|
+
}
|
|
348
|
+
const id = libraryIdFromPackageName(packageName);
|
|
349
|
+
const targets = (targetsRaw ?? "")
|
|
350
|
+
.split(",")
|
|
351
|
+
.map((t) => t.trim())
|
|
352
|
+
.filter((t) => t.length > 0);
|
|
353
|
+
const model = {
|
|
354
|
+
packageName,
|
|
355
|
+
id,
|
|
356
|
+
className: pascalCase(id),
|
|
357
|
+
instanceName: camelCase(id),
|
|
358
|
+
tokenBase: tokenBaseFromId(id),
|
|
359
|
+
framework,
|
|
360
|
+
category,
|
|
361
|
+
targets,
|
|
362
|
+
isZephyr: framework === "zephyr",
|
|
363
|
+
};
|
|
364
|
+
const packageDir = path.resolve(options.dir ?? path.join(process.cwd(), id));
|
|
365
|
+
if (fs.existsSync(packageDir) && fs.readdirSync(packageDir).length > 0) {
|
|
366
|
+
throw new Error(`Directory ${packageDir} exists and is not empty.`);
|
|
367
|
+
}
|
|
368
|
+
writeIfChanged(path.join(packageDir, "package.json"), renderPackageJson(model));
|
|
369
|
+
writeIfChanged(path.join(packageDir, "tsconfig.json"), renderTsconfig());
|
|
370
|
+
writeIfChanged(path.join(packageDir, ".gitignore"), "node_modules/\ndist/\n");
|
|
371
|
+
writeIfChanged(path.join(packageDir, "src", "index.ts"), renderApi(model));
|
|
372
|
+
writeIfChanged(path.join(packageDir, "cuttlefish.library.json"), renderManifest(model));
|
|
373
|
+
writeIfChanged(path.join(packageDir, "shims", `__tc_${model.tokenBase}.h`), renderShimHeader(model));
|
|
374
|
+
writeIfChanged(path.join(packageDir, "shims", `__tc_${model.tokenBase}.cpp`), renderShimSource(model));
|
|
375
|
+
if (model.isZephyr) {
|
|
376
|
+
writeIfChanged(path.join(packageDir, "shims", `${id}.overlay`), renderOverlay(model));
|
|
377
|
+
}
|
|
378
|
+
writeIfChanged(path.join(packageDir, "README.md"), renderReadme(model));
|
|
379
|
+
writeIfChanged(path.join(packageDir, "tests", "library.test.ts"), renderStarterTest(model));
|
|
380
|
+
console.log(chalk.green("+") + ` Scaffolded ${chalk.white.bold(packageName)} in ${packageDir}`);
|
|
381
|
+
console.log();
|
|
382
|
+
console.log(` Framework: ${frameworkEntry.label}`);
|
|
383
|
+
console.log(` Category: ${category.label} (keyword: cuttlefish-${category.id})`);
|
|
384
|
+
console.log(` Library id: ${id} (shim __tc_${model.tokenBase}.h, gate token __tc_${model.tokenBase})`);
|
|
385
|
+
console.log();
|
|
386
|
+
console.log(" Next steps:");
|
|
387
|
+
console.log(` 1. Replace the stubs in shims/ with your implementation.`);
|
|
388
|
+
console.log(` 2. Fill in cuttlefish.library.json${model.isZephyr ? " (kconfig + overlay)" : ""} as you go.`);
|
|
389
|
+
console.log(` 3. Validate: npx cuttlefish library validate ${packageDir}`);
|
|
390
|
+
console.log(` 4. Use it in a project: npm install <path-or-published-name>, then`);
|
|
391
|
+
console.log(` import { ${model.instanceName} } from '${packageName}';`);
|
|
392
|
+
if (model.isZephyr) {
|
|
393
|
+
console.log();
|
|
394
|
+
console.log(chalk.dim(" Note: for Zephyr, document the peripherals your overlay borrows in the README."));
|
|
395
|
+
}
|
|
396
|
+
return { packageDir, packageName, id };
|
|
397
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Walk up from `fromDir` to the nearest package.json. */
|
|
2
|
+
export declare function findProjectPackageJson(fromDir?: string): string | null;
|
|
3
|
+
/** Installed version spec for a dependency name, or undefined. */
|
|
4
|
+
export declare function installedDependencyVersion(packageJsonPath: string, name: string): string | undefined;
|
|
5
|
+
/**
|
|
6
|
+
* Install npm packages into the project owning `cwd`. Returns the directory
|
|
7
|
+
* the install ran in. Throws with npm's stderr on failure.
|
|
8
|
+
*/
|
|
9
|
+
export declare function npmInstall(packageNames: string[], cwd: string): void;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Library installation — `cuttlefish library install <pkg...>`.
|
|
3
|
+
//
|
|
4
|
+
// Finds the nearest project package.json (walking up from the cwd) and runs
|
|
5
|
+
// npm install against it. The Windows spawn handling mirrors the typecad
|
|
6
|
+
// binary's package command: npm.cmd must go through cmd.exe, and a resolved
|
|
7
|
+
// .cmd/.bat cannot be exec'd directly.
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
import fs from "node:fs";
|
|
10
|
+
import path from "node:path";
|
|
11
|
+
import { execFileSync } from "node:child_process";
|
|
12
|
+
const IS_WIN = process.platform === "win32";
|
|
13
|
+
const IS_CMD_EXT = /\.(?:cmd|bat)$/i;
|
|
14
|
+
/** Walk up from `fromDir` to the nearest package.json. */
|
|
15
|
+
export function findProjectPackageJson(fromDir = process.cwd()) {
|
|
16
|
+
let dir = path.resolve(fromDir);
|
|
17
|
+
const root = path.parse(dir).root;
|
|
18
|
+
for (;;) {
|
|
19
|
+
const candidate = path.join(dir, "package.json");
|
|
20
|
+
if (fs.existsSync(candidate))
|
|
21
|
+
return candidate;
|
|
22
|
+
if (dir === root)
|
|
23
|
+
return null;
|
|
24
|
+
dir = path.dirname(dir);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/** Installed version spec for a dependency name, or undefined. */
|
|
28
|
+
export function installedDependencyVersion(packageJsonPath, name) {
|
|
29
|
+
try {
|
|
30
|
+
const pkg = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
|
|
31
|
+
for (const section of ["dependencies", "devDependencies", "optionalDependencies"]) {
|
|
32
|
+
const deps = pkg[section];
|
|
33
|
+
if (deps && typeof deps === "object" && typeof deps[name] === "string") {
|
|
34
|
+
return deps[name];
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
// unreadable manifest — treat as not installed
|
|
40
|
+
}
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
function resolveNpmExecutable() {
|
|
44
|
+
const isWin = IS_WIN;
|
|
45
|
+
const cmd = isWin ? "npm.cmd" : "npm";
|
|
46
|
+
for (const dir of (process.env.PATH ?? "").split(path.delimiter)) {
|
|
47
|
+
if (!dir)
|
|
48
|
+
continue;
|
|
49
|
+
const candidate = path.join(dir, cmd);
|
|
50
|
+
try {
|
|
51
|
+
if (fs.statSync(candidate).isFile())
|
|
52
|
+
return candidate;
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
// keep scanning
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Install npm packages into the project owning `cwd`. Returns the directory
|
|
62
|
+
* the install ran in. Throws with npm's stderr on failure.
|
|
63
|
+
*/
|
|
64
|
+
export function npmInstall(packageNames, cwd) {
|
|
65
|
+
const resolved = resolveNpmExecutable();
|
|
66
|
+
if (IS_WIN && (!resolved || IS_CMD_EXT.test(resolved))) {
|
|
67
|
+
execFileSync("cmd.exe", ["/d", "/s", "/c", "npm", "install", ...packageNames], {
|
|
68
|
+
cwd,
|
|
69
|
+
stdio: "pipe",
|
|
70
|
+
timeout: 300_000,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
execFileSync(resolved ?? "npm", ["install", ...packageNames], {
|
|
75
|
+
cwd,
|
|
76
|
+
stdio: "pipe",
|
|
77
|
+
timeout: 300_000,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/** Fields of an npm search result this module consumes. */
|
|
2
|
+
export interface NpmSearchPackage {
|
|
3
|
+
name: string;
|
|
4
|
+
version: string;
|
|
5
|
+
description?: string;
|
|
6
|
+
keywords?: string[];
|
|
7
|
+
links?: {
|
|
8
|
+
npm?: string;
|
|
9
|
+
};
|
|
10
|
+
publisher?: {
|
|
11
|
+
username?: string;
|
|
12
|
+
};
|
|
13
|
+
date?: string;
|
|
14
|
+
}
|
|
15
|
+
/** One shaped search result. */
|
|
16
|
+
export interface LibrarySearchResult {
|
|
17
|
+
name: string;
|
|
18
|
+
version: string;
|
|
19
|
+
description: string | null;
|
|
20
|
+
/** Category id derived from the package's keywords, or null. */
|
|
21
|
+
category: string | null;
|
|
22
|
+
npmUrl: string;
|
|
23
|
+
published: string | null;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Build the `text=` query for the registry search endpoint. Exported for tests.
|
|
27
|
+
*/
|
|
28
|
+
export declare function buildLibrarySearchQuery(text?: string, categoryId?: string): string;
|
|
29
|
+
/**
|
|
30
|
+
* Search npm for cuttlefish library packages. `size` caps results (registry
|
|
31
|
+
* max is 250). Throws on HTTP failure — the CLI surfaces the message.
|
|
32
|
+
*/
|
|
33
|
+
export declare function searchLibraryPackages(text?: string, categoryId?: string, size?: number): Promise<LibrarySearchResult[]>;
|
|
34
|
+
export declare function shapeResult(pkg: NpmSearchPackage): LibrarySearchResult;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// npm registry search for cuttlefish library packages — the discovery client
|
|
3
|
+
// behind `cuttlefish library search`.
|
|
4
|
+
//
|
|
5
|
+
// Ports the concept from the typecad binary's `typecad package` command: the
|
|
6
|
+
// npm registry's search endpoint is the catalog, keywords are the taxonomy.
|
|
7
|
+
// The marker keyword (cuttlefish-library) is always part of the query; a
|
|
8
|
+
// category narrows it; free text ANDs on top. The endpoint ANDs `keywords:`
|
|
9
|
+
// terms, so the composition needs no client-side filtering.
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
import { LIBRARY_MARKER_KEYWORD, categoryKeyword, categoryFromKeywords } from "./catalog.js";
|
|
12
|
+
const REGISTRY_SEARCH_ENDPOINT = "https://registry.npmjs.org/-/v1/search";
|
|
13
|
+
/**
|
|
14
|
+
* Build the `text=` query for the registry search endpoint. Exported for tests.
|
|
15
|
+
*/
|
|
16
|
+
export function buildLibrarySearchQuery(text, categoryId) {
|
|
17
|
+
const terms = [`keywords:${LIBRARY_MARKER_KEYWORD}`];
|
|
18
|
+
if (categoryId) {
|
|
19
|
+
terms.push(`keywords:${categoryKeyword(categoryId)}`);
|
|
20
|
+
}
|
|
21
|
+
const freeText = text?.trim();
|
|
22
|
+
if (freeText) {
|
|
23
|
+
// Free text terms are space-separated; the registry ANDs them.
|
|
24
|
+
terms.push(freeText);
|
|
25
|
+
}
|
|
26
|
+
return terms.join(" ");
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Search npm for cuttlefish library packages. `size` caps results (registry
|
|
30
|
+
* max is 250). Throws on HTTP failure — the CLI surfaces the message.
|
|
31
|
+
*/
|
|
32
|
+
export async function searchLibraryPackages(text, categoryId, size = 250) {
|
|
33
|
+
const url = `${REGISTRY_SEARCH_ENDPOINT}?text=${encodeURIComponent(buildLibrarySearchQuery(text, categoryId))}&size=${size}`;
|
|
34
|
+
const res = await fetch(url);
|
|
35
|
+
if (!res.ok) {
|
|
36
|
+
throw new Error(`npm registry returned ${res.status}: ${res.statusText}`);
|
|
37
|
+
}
|
|
38
|
+
const data = (await res.json());
|
|
39
|
+
return data.objects.map((o) => shapeResult(o.package));
|
|
40
|
+
}
|
|
41
|
+
export function shapeResult(pkg) {
|
|
42
|
+
return {
|
|
43
|
+
name: pkg.name,
|
|
44
|
+
version: pkg.version,
|
|
45
|
+
description: pkg.description ?? null,
|
|
46
|
+
category: categoryFromKeywords(pkg.keywords),
|
|
47
|
+
npmUrl: pkg.links?.npm ?? `https://www.npmjs.com/package/${pkg.name}`,
|
|
48
|
+
published: pkg.date ?? null,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type SelfCheckFinding } from "../emit/compliance/index.js";
|
|
2
|
+
export interface LibraryValidationFinding {
|
|
3
|
+
severity: "error" | "warning";
|
|
4
|
+
message: string;
|
|
5
|
+
}
|
|
6
|
+
export interface LibraryValidationReport {
|
|
7
|
+
valid: boolean;
|
|
8
|
+
/** The package directory that was validated. */
|
|
9
|
+
dir: string;
|
|
10
|
+
errors: string[];
|
|
11
|
+
warnings: string[];
|
|
12
|
+
/** Unrecorded AUTOSAR violations in the shim sources (strict mode). */
|
|
13
|
+
autosarFindings: SelfCheckFinding[];
|
|
14
|
+
}
|
|
15
|
+
export declare function validateLibraryPackage(dir: string): LibraryValidationReport;
|
|
16
|
+
/** Render the human-readable report. Returns the process exit code (0/1). */
|
|
17
|
+
export declare function printValidationReport(report: LibraryValidationReport): number;
|