@todayai-labs/tck-bundler
Compile a .tck.tsx widget source into a distributable .tckb bundle. CLI + programmatic.
The widget-side build tool. Takes a .tck.tsx source, runs esbuild + Tailwind v4 over it, and emits a .tckb envelope ready to ship to a host. CLI for one-shot builds; programmatic for higher-level pipelines.
tck-bundler calls @todayai-labs/tck-bundle-format's packTckb to construct the envelope — tck-bundler derives the manifest, compiles the JS, scopes the CSS; the format package owns the envelope bytes.
CLI
# Bundle a single widget
pnpm exec tck-bundle widgets/counter/widget.tck.tsx --out widgets/counter/dist/widget.tckb
# Bundle every widget in the workspace
pnpm exec tck-bundle --all
# Print the manifest JSON Schema (the authoring contract: fields,
# defaults, constraints) to stdout
pnpm exec tck-bundle schematck-bundle schema [--json] prints the manifest JSON Schema — the authoring contract's fields, their defaults, and their constraints — to stdout. It's the canonical reference for what a WidgetManifestInput accepts and how sizes / defaultSize / defaultState default. The schema is generated from the zod single source of truth (packages/tck/src/manifest/schema.ts); see @todayai-labs/tck.
Programmatic surface
import { bundleWidget, readWidgetSource, type BundleOptions } from '@todayai-labs/tck-bundler'
const result = await bundleWidget({
entry: 'widgets/counter/widget.tck.tsx',
outFile: 'widgets/counter/dist/widget.tckb',
// ... see BundleOptions
})| Export | What it does |
|---|---|
bundleWidget(options) | Compile + scope CSS + pack .tckb. Returns the BundleResult. |
BundleError | Thrown when a widget imports a bare specifier outside TCK_EXTERNAL_SPECIFIERS, exceeds maxSizeBytes, or fails manifest validation. |
readWidgetSource(path) | Read a .tck.tsx file as UTF-8 text. Useful for tests and dev tools that want the source before invoking esbuild. |
buildWidgetCss, scopeCss | The CSS scoping pipeline. Lifted into its own export for tooling that wants to lint or pre-process scoped CSS. |
Constraints
- Manifest authoring belongs to widget source. The entry directory and an existing
cwdbound the source files whosedefineManifest,defineSlots,TCK_ABI_VERSION, andTCK_MANIFEST_VERSIONimports are pinned into the bundle. Installed dependencies undernode_modulesmust not call or re-export those APIs. The bundler resolves symlinks before checking containment, so a link whose target escapes the authoring roots is not inspected as widget source. - Pinning preserves module semantics. Named and namespace imports, namespace aliases/destructuring, standalone version constants, and local re-exports all use build-time manifest helpers. Runtime hooks and components remain external.
- SDK checks follow bindings and the actual manifest. All JavaScript/TypeScript module extensions are scanned. Unknown exports fail before manifest evaluation; Feed restrictions use the normalized exported manifest. Comments and unrelated
type: 'feed'objects cannot select the mode. Namespace shadowing is resolved by the TypeScript binder; arbitrary aliases and dynamic property keys are outside the static check. - A missing
cwdis only a resolution base. An absolute entry still supplies its own authoring root, but a non-existent working directory cannot own source and is not added to the boundary. Derived source roots are validated and fail closed instead of widening the scan. - Externals whitelist enforced at build time.
BundleError: bundle imports "<spec>", which is not on the TCK externals whitelist— fail-fast at CI, not at load time. - Soft cap 256 KB on
widget.mjs. Configurable viamaxSizeBytes. Exceeding it is a build error, not a load error. - JSX: automatic transform throughout. Classic-transform output (banner-injected
import React) is internal — widget source itself usesreact-jsx.
Source
- Package:
packages/tck-bundler - Producer entry:
packages/tck-bundler/src/bundle.ts - CSS scoping:
packages/tck-bundler/src/css.ts