From 22a9a0dab3767aaf6959d119fd29582d4251ab06 Mon Sep 17 00:00:00 2001
From: Jordan Santell
Date: Thu, 9 Oct 2025 11:39:43 -0700
Subject: [PATCH] chore: Introduce CTHelpers in ts-transformers serving
imported symbols.
As a part of the "pretransform" step, we replace the CTS enable
directive with a wildcard import for "commontools" module that is
used when injecting new functionality via transformations.
Remove unused imports from various patterns that are now injected
by the transformers.
---
.../array-in-cell-ast-nocomponents.tsx | 12 +-
...y-in-cell-with-remove-ast-nocomponents.tsx | 1 -
.../array-in-cell-with-remove-editable.tsx | 12 +-
packages/patterns/charm-ref-in-cell.tsx | 1 -
packages/patterns/chatbot-list-view.tsx | 1 -
packages/patterns/chatbot-note-composed.tsx | 15 +-
packages/patterns/chatbot-outliner.tsx | 10 -
packages/patterns/chatbot.tsx | 5 -
packages/patterns/cheeseboard.tsx | 15 +-
packages/patterns/common-tools.tsx | 7 -
packages/patterns/counter-handlers.ts | 2 +-
packages/patterns/counter.tsx | 10 +-
packages/patterns/ct-list.tsx | 11 +-
packages/patterns/ct-render.tsx | 14 +-
packages/patterns/ct-select.tsx | 20 +-
packages/patterns/ct-tags.tsx | 20 +-
packages/patterns/default-app.tsx | 1 -
packages/patterns/dice.tsx | 11 +-
packages/patterns/fetch-data.tsx | 5 -
packages/patterns/instantiate-recipe.tsx | 1 -
packages/patterns/list-operations.tsx | 3 -
packages/patterns/nested-counter.tsx | 2 +-
packages/patterns/note.tsx | 1 -
packages/patterns/output_schema.tsx | 4 -
packages/runner/src/harness/engine.ts | 32 +--
packages/runner/src/harness/pretransform.ts | 60 +++++
packages/ts-transformers/src/core/context.ts | 10 +-
.../ts-transformers/src/core/ct-helpers.ts | 120 +++++++++
.../ts-transformers/src/core/cts-directive.ts | 17 --
packages/ts-transformers/src/core/imports.ts | 254 ------------------
packages/ts-transformers/src/core/mod.ts | 7 +-
packages/ts-transformers/src/ct-pipeline.ts | 2 +-
packages/ts-transformers/src/mod.ts | 5 +-
.../src/transformers/builtins/derive.ts | 39 +--
.../src/transformers/builtins/ifelse.ts | 13 +-
.../src/transformers/opaque-ref-jsx.ts | 18 +-
.../emitters/conditional-expression.ts | 4 +-
.../src/transformers/opaque-ref/helpers.ts | 2 +-
.../src/transformers/schema-generator.ts | 69 ++---
.../src/transformers/schema-injection.ts | 19 +-
.../builder-conditional.expected.tsx | 9 +-
.../ast-transform/counter-recipe.expected.tsx | 17 +-
.../derive-object-literal-input.expected.tsx | 25 +-
.../event-handler-no-derive.expected.tsx | 13 +-
.../handler-object-literal.expected.tsx | 9 +-
.../recipe-array-map.expected.tsx | 13 +-
.../schema-generation-builders.expected.tsx | 11 +-
...chema-generation-derive-alias.expected.tsx | 9 +-
...-generation-derive-inside-jsx.expected.tsx | 11 +-
...ation-derive-multiple-returns.expected.tsx | 11 +-
...ema-generation-derive-untyped.expected.tsx | 9 +-
.../schema-generation-derive.expected.tsx | 9 +-
...eneration-handler-both-inline.expected.tsx | 11 +-
...generation-handler-event-only.expected.tsx | 9 +-
...generation-handler-inside-jsx.expected.tsx | 9 +-
...ration-handler-no-annotations.expected.tsx | 9 +-
...ma-generation-lift-cell-array.expected.tsx | 11 +-
...ma-generation-lift-inside-jsx.expected.tsx | 9 +-
...a-generation-lift-no-generics.expected.tsx | 9 +-
...chema-generation-lift-untyped.expected.tsx | 9 +-
.../schema-generation-lift.expected.tsx | 9 +-
.../ast-transform/ternary_derive.expected.tsx | 9 +-
...array-cell-remove-intersection.expected.ts | 13 +-
.../complex-nested-types.expected.ts | 13 +-
.../date-and-map-types.expected.ts | 9 +-
.../preserve-explicit-schemas.expected.ts | 3 +-
.../handler-schema/simple-handler.expected.ts | 9 +-
...unsupported-intersection-index.expected.ts | 9 +-
.../complex-expressions.expected.tsx | 11 +-
.../complex-expressions.input.tsx | 2 +-
.../element-access-complex.expected.tsx | 42 +--
.../element-access-complex.input.tsx | 2 +-
.../element-access-simple.expected.tsx | 13 +-
.../element-access-simple.input.tsx | 2 +-
.../jsx-arithmetic-operations.expected.tsx | 29 +-
.../jsx-complex-mixed.expected.tsx | 27 +-
.../jsx-conditional-rendering.expected.tsx | 29 +-
.../jsx-function-calls.expected.tsx | 55 ++--
.../jsx-property-access.expected.tsx | 33 +--
.../jsx-string-operations.expected.tsx | 31 +--
.../method-chains.expected.tsx | 51 ++--
.../jsx-expressions/method-chains.input.tsx | 2 +-
.../no-double-derive.expected.tsx | 5 +-
.../no-transform-simple-ref.expected.tsx | 3 +-
.../opaque-ref-cell-map.expected.tsx | 18 +-
.../opaque-ref-cell-map.input.tsx | 1 -
.../opaque-ref-operations.expected.tsx | 11 +-
.../optional-chain-predicate.expected.tsx | 7 +-
.../optional-element-access.expected.tsx | 7 +-
.../parent-suppression-edge.expected.tsx | 28 +-
.../parent-suppression-edge.input.tsx | 2 +-
.../recipe-statements-vs-jsx.expected.tsx | 23 +-
.../recipe-with-cells.expected.tsx | 11 +-
.../nested-default-optional.expected.tsx | 13 +-
.../opaque-ref-map.expected.ts | 7 +-
.../recipe-with-types.expected.tsx | 13 +-
.../with-opaque-ref.expected.tsx | 11 +-
.../schema-transform/with-options.expected.ts | 7 +-
.../test/opaque-ref/map-callbacks.test.ts | 6 +-
.../test/opaque-ref/runtime-style.test.ts | 29 --
.../ts-transformers/test/transform.test.ts | 14 +-
packages/ts-transformers/test/utils.ts | 13 +-
102 files changed, 708 insertions(+), 1001 deletions(-)
create mode 100644 packages/runner/src/harness/pretransform.ts
create mode 100644 packages/ts-transformers/src/core/ct-helpers.ts
delete mode 100644 packages/ts-transformers/src/core/cts-directive.ts
delete mode 100644 packages/ts-transformers/src/core/imports.ts
diff --git a/packages/patterns/array-in-cell-ast-nocomponents.tsx b/packages/patterns/array-in-cell-ast-nocomponents.tsx
index 7c1a7a654..ab9f0e97d 100644
--- a/packages/patterns/array-in-cell-ast-nocomponents.tsx
+++ b/packages/patterns/array-in-cell-ast-nocomponents.tsx
@@ -1,15 +1,5 @@
///
-import {
- Cell,
- Default,
- derive,
- h,
- handler,
- NAME,
- recipe,
- Stream,
- UI,
-} from "commontools";
+import { Cell, Default, h, handler, NAME, recipe, UI } from "commontools";
interface Item {
text: Default;
diff --git a/packages/patterns/array-in-cell-with-remove-ast-nocomponents.tsx b/packages/patterns/array-in-cell-with-remove-ast-nocomponents.tsx
index 6c336bf7a..8e6419f30 100644
--- a/packages/patterns/array-in-cell-with-remove-ast-nocomponents.tsx
+++ b/packages/patterns/array-in-cell-with-remove-ast-nocomponents.tsx
@@ -2,7 +2,6 @@
import {
Cell,
Default,
- derive,
h,
handler,
NAME,
diff --git a/packages/patterns/array-in-cell-with-remove-editable.tsx b/packages/patterns/array-in-cell-with-remove-editable.tsx
index 4073f000f..d249239e6 100644
--- a/packages/patterns/array-in-cell-with-remove-editable.tsx
+++ b/packages/patterns/array-in-cell-with-remove-editable.tsx
@@ -1,15 +1,5 @@
///
-import {
- Cell,
- Default,
- derive,
- h,
- handler,
- NAME,
- recipe,
- Stream,
- UI,
-} from "commontools";
+import { Cell, Default, h, handler, NAME, recipe, UI } from "commontools";
interface Item {
text: Default;
diff --git a/packages/patterns/charm-ref-in-cell.tsx b/packages/patterns/charm-ref-in-cell.tsx
index 55ebe4d79..3b4c38600 100644
--- a/packages/patterns/charm-ref-in-cell.tsx
+++ b/packages/patterns/charm-ref-in-cell.tsx
@@ -2,7 +2,6 @@
import {
Cell,
cell,
- createCell,
Default,
derive,
h,
diff --git a/packages/patterns/chatbot-list-view.tsx b/packages/patterns/chatbot-list-view.tsx
index 42ccccbca..bc5e5f678 100644
--- a/packages/patterns/chatbot-list-view.tsx
+++ b/packages/patterns/chatbot-list-view.tsx
@@ -3,7 +3,6 @@ import {
Cell,
cell,
Default,
- derive,
h,
handler,
ID,
diff --git a/packages/patterns/chatbot-note-composed.tsx b/packages/patterns/chatbot-note-composed.tsx
index cfbdada5f..11480e872 100644
--- a/packages/patterns/chatbot-note-composed.tsx
+++ b/packages/patterns/chatbot-note-composed.tsx
@@ -4,31 +4,18 @@ import {
Cell,
cell,
Default,
- derive,
- fetchData,
- getRecipeEnvironment,
h,
handler,
- ID,
- ifElse,
- JSONSchema,
- lift,
- llm,
- llmDialog,
NAME,
navigateTo,
OpaqueRef,
recipe,
- str,
- Stream,
- toSchema,
- UI,
} from "commontools";
import Chat from "./chatbot.tsx";
import Note from "./note.tsx";
import { type BacklinksMap } from "./backlinks-index.tsx";
-import Tools, {
+import {
addListItem,
calculator,
ListItem,
diff --git a/packages/patterns/chatbot-outliner.tsx b/packages/patterns/chatbot-outliner.tsx
index a5c406408..5ddba89b2 100644
--- a/packages/patterns/chatbot-outliner.tsx
+++ b/packages/patterns/chatbot-outliner.tsx
@@ -2,25 +2,15 @@
import {
BuiltInLLMMessage,
Cell,
- cell,
Default,
- derive,
- fetchData,
- getRecipeEnvironment,
h,
handler,
- ID,
ifElse,
JSONSchema,
- lift,
- llm,
- llmDialog,
NAME,
navigateTo,
OpaqueRef,
recipe,
- str,
- Stream,
UI,
} from "commontools";
diff --git a/packages/patterns/chatbot.tsx b/packages/patterns/chatbot.tsx
index 6b7e2e9c8..39ae48a7d 100644
--- a/packages/patterns/chatbot.tsx
+++ b/packages/patterns/chatbot.tsx
@@ -9,14 +9,9 @@ import {
generateObject,
h,
handler,
- ifElse,
- JSONSchema,
- lift,
- llm,
llmDialog,
NAME,
recipe,
- str,
Stream,
UI,
} from "commontools";
diff --git a/packages/patterns/cheeseboard.tsx b/packages/patterns/cheeseboard.tsx
index e8bfd76ee..9ccc594a1 100644
--- a/packages/patterns/cheeseboard.tsx
+++ b/packages/patterns/cheeseboard.tsx
@@ -1,18 +1,5 @@
///
-import {
- Cell,
- cell,
- Default,
- derive,
- fetchData,
- h,
- handler,
- ifElse,
- lift,
- NAME,
- recipe,
- UI,
-} from "commontools";
+import { fetchData, h, lift, NAME, recipe, UI } from "commontools";
/**
* Fetch the Cheeseboard pizza schedule via Toolshed's web-read endpoint and
diff --git a/packages/patterns/common-tools.tsx b/packages/patterns/common-tools.tsx
index 55783fd07..ff3fe05b5 100644
--- a/packages/patterns/common-tools.tsx
+++ b/packages/patterns/common-tools.tsx
@@ -1,20 +1,13 @@
///
import {
- BuiltInLLMMessage,
BuiltInLLMTool,
Cell,
- cell,
- Default,
derive,
fetchData,
h,
handler,
ifElse,
- llmDialog,
- NAME,
recipe,
- Stream,
- UI,
} from "commontools";
///// COMMON TOOLS (get it?) ////
diff --git a/packages/patterns/counter-handlers.ts b/packages/patterns/counter-handlers.ts
index 522165404..08a9b53a6 100644
--- a/packages/patterns/counter-handlers.ts
+++ b/packages/patterns/counter-handlers.ts
@@ -1,5 +1,5 @@
///
-import { Cell, derive, handler } from "commontools";
+import { Cell, handler } from "commontools";
export const increment = handler<
unknown,
diff --git a/packages/patterns/counter.tsx b/packages/patterns/counter.tsx
index 99f3271ae..7e017ae84 100644
--- a/packages/patterns/counter.tsx
+++ b/packages/patterns/counter.tsx
@@ -1,12 +1,6 @@
///
-import { Default, derive, h, NAME, recipe, str, Stream, UI } from "commontools";
-import {
- decrement,
- getValue,
- increment,
- nth,
- previous,
-} from "./counter-handlers.ts";
+import { Default, h, NAME, recipe, str, Stream, UI } from "commontools";
+import { decrement, increment, nth, previous } from "./counter-handlers.ts";
interface RecipeState {
value: Default;
diff --git a/packages/patterns/ct-list.tsx b/packages/patterns/ct-list.tsx
index 7976688eb..9087ececb 100644
--- a/packages/patterns/ct-list.tsx
+++ b/packages/patterns/ct-list.tsx
@@ -1,14 +1,5 @@
///
-import {
- Cell,
- Default,
- h,
- handler,
- NAME,
- recipe,
- toSchema,
- UI,
-} from "commontools";
+import { Default, h, NAME, recipe, UI } from "commontools";
interface Item {
title: string;
diff --git a/packages/patterns/ct-render.tsx b/packages/patterns/ct-render.tsx
index ff3e9b16b..42e09c60f 100644
--- a/packages/patterns/ct-render.tsx
+++ b/packages/patterns/ct-render.tsx
@@ -1,17 +1,5 @@
///
-import {
- Cell,
- Default,
- derive,
- h,
- handler,
- NAME,
- Opaque,
- OpaqueRef,
- recipe,
- str,
- UI,
-} from "commontools";
+import { Cell, Default, h, handler, NAME, recipe, str, UI } from "commontools";
interface RecipeState {
value: Default;
diff --git a/packages/patterns/ct-select.tsx b/packages/patterns/ct-select.tsx
index 53d237468..31951efa8 100644
--- a/packages/patterns/ct-select.tsx
+++ b/packages/patterns/ct-select.tsx
@@ -1,24 +1,6 @@
///
-import {
- Cell,
- cell,
- compileAndRun,
- Default,
- derive,
- h,
- handler,
- ifElse,
- Mutable,
- NAME,
- navigateTo,
- Opaque,
- OpaqueRef,
- recipe,
- render,
- str,
- UI,
-} from "commontools";
+import { Default, h, NAME, OpaqueRef, recipe, UI } from "commontools";
type Input = {
selected: Default;
diff --git a/packages/patterns/ct-tags.tsx b/packages/patterns/ct-tags.tsx
index f8851341f..89b373647 100644
--- a/packages/patterns/ct-tags.tsx
+++ b/packages/patterns/ct-tags.tsx
@@ -1,24 +1,6 @@
///
-import {
- Cell,
- cell,
- compileAndRun,
- Default,
- derive,
- h,
- handler,
- ifElse,
- Mutable,
- NAME,
- navigateTo,
- Opaque,
- OpaqueRef,
- recipe,
- render,
- str,
- UI,
-} from "commontools";
+import { Cell, Default, h, handler, NAME, recipe, UI } from "commontools";
type Input = {
tags: string[];
diff --git a/packages/patterns/default-app.tsx b/packages/patterns/default-app.tsx
index 2abaf91fb..e5e30368f 100644
--- a/packages/patterns/default-app.tsx
+++ b/packages/patterns/default-app.tsx
@@ -7,7 +7,6 @@ import {
handler,
NAME,
navigateTo,
- Opaque,
OpaqueRef,
recipe,
str,
diff --git a/packages/patterns/dice.tsx b/packages/patterns/dice.tsx
index 9b03fc887..d1a242756 100644
--- a/packages/patterns/dice.tsx
+++ b/packages/patterns/dice.tsx
@@ -1,14 +1,5 @@
///
-import {
- Default,
- h,
- NAME,
- OpaqueRef,
- recipe,
- str,
- Stream,
- UI,
-} from "commontools";
+import { Default, h, NAME, recipe, Stream, UI } from "commontools";
import { getValue, roll } from "./dice-handlers.ts";
interface RecipeState {
diff --git a/packages/patterns/fetch-data.tsx b/packages/patterns/fetch-data.tsx
index 343c67f0c..64761060c 100644
--- a/packages/patterns/fetch-data.tsx
+++ b/packages/patterns/fetch-data.tsx
@@ -1,17 +1,12 @@
///
import {
- Cell,
- cell,
Default,
derive,
fetchData,
h,
- handler,
lift,
NAME,
recipe,
- schema,
- str,
UI,
} from "commontools";
diff --git a/packages/patterns/instantiate-recipe.tsx b/packages/patterns/instantiate-recipe.tsx
index e5f8e998c..9105a67ce 100644
--- a/packages/patterns/instantiate-recipe.tsx
+++ b/packages/patterns/instantiate-recipe.tsx
@@ -2,7 +2,6 @@
import {
Cell,
Default,
- derive,
h,
handler,
NAME,
diff --git a/packages/patterns/list-operations.tsx b/packages/patterns/list-operations.tsx
index 43bbc4d94..acce23425 100644
--- a/packages/patterns/list-operations.tsx
+++ b/packages/patterns/list-operations.tsx
@@ -8,10 +8,7 @@ import {
ID,
lift,
NAME,
- Opaque,
recipe,
- str,
- toSchema,
UI,
} from "commontools";
diff --git a/packages/patterns/nested-counter.tsx b/packages/patterns/nested-counter.tsx
index 9e7caaabe..90e6551fb 100644
--- a/packages/patterns/nested-counter.tsx
+++ b/packages/patterns/nested-counter.tsx
@@ -1,5 +1,5 @@
///
-import { Default, derive, h, NAME, recipe, str, UI } from "commontools";
+import { Default, h, NAME, recipe, str, UI } from "commontools";
import { decrement, increment, nth, previous } from "./counter-handlers.ts";
interface RecipeState {
diff --git a/packages/patterns/note.tsx b/packages/patterns/note.tsx
index a3379cb8c..345130af5 100644
--- a/packages/patterns/note.tsx
+++ b/packages/patterns/note.tsx
@@ -11,7 +11,6 @@ import {
navigateTo,
OpaqueRef,
recipe,
- toSchema,
UI,
} from "commontools";
import { type BacklinksMap } from "./backlinks-index.tsx";
diff --git a/packages/patterns/output_schema.tsx b/packages/patterns/output_schema.tsx
index 3e683a53b..07f5afa06 100644
--- a/packages/patterns/output_schema.tsx
+++ b/packages/patterns/output_schema.tsx
@@ -2,14 +2,10 @@
import {
Cell,
Default,
- derive,
h,
handler,
NAME,
- Opaque,
- OpaqueRef,
recipe,
- str,
UI,
VNode,
} from "commontools";
diff --git a/packages/runner/src/harness/engine.ts b/packages/runner/src/harness/engine.ts
index 7bbb8dc11..f8d97d604 100644
--- a/packages/runner/src/harness/engine.ts
+++ b/packages/runner/src/harness/engine.ts
@@ -22,6 +22,7 @@ import * as RuntimeModules from "./runtime-modules.ts";
import { IRuntime } from "../runtime.ts";
import * as merkleReference from "merkle-reference";
import { StaticCache } from "@commontools/static";
+import { pretransformProgram } from "./pretransform.ts";
const RUNTIME_ENGINE_CONSOLE_HOOK = "RUNTIME_ENGINE_CONSOLE_HOOK";
const INJECTED_SCRIPT =
@@ -147,7 +148,7 @@ export class Engine extends EventTarget implements Harness {
> {
const id = options.identifier ?? computeId(program);
const filename = options.filename ?? `${id}.js`;
- const mappedProgram = mapPrefixProgramFiles(program, id);
+ const mappedProgram = pretransformProgram(program, id);
const resolver = new EngineProgramResolver(
mappedProgram,
this.ctRuntime.staticCache,
@@ -250,32 +251,3 @@ function computeId(program: Program): string {
];
return merkleReference.refer(source).toString();
}
-
-// Adds `id` as a prefix to all files in the program.
-// Injects a new entry at root `/index.ts` to re-export
-// the entry contents because otherwise `typescript`
-// flattens the output, eliding the common prefix.
-function mapPrefixProgramFiles(program: RuntimeProgram, id: string): Program {
- const main = program.main;
- const exportNameds = `export * from "${prefix(main, id)}";`;
- const exportDefault = `export { default } from "${prefix(main, id)}";`;
- const hasDefault = !program.mainExport || program.mainExport === "default";
- const files = [
- ...program.files.map((source) => ({
- name: prefix(source.name, id),
- contents: source.contents,
- })),
- {
- name: `/index.ts`,
- contents: `${exportNameds}${hasDefault ? `\n${exportDefault}` : ""}`,
- },
- ];
- return {
- main: `/index.ts`,
- files,
- };
-}
-
-function prefix(filename: string, id: string): string {
- return `/${id}${filename}`;
-}
diff --git a/packages/runner/src/harness/pretransform.ts b/packages/runner/src/harness/pretransform.ts
new file mode 100644
index 000000000..39f3524d4
--- /dev/null
+++ b/packages/runner/src/harness/pretransform.ts
@@ -0,0 +1,60 @@
+import { transformCtDirective } from "@commontools/ts-transformers";
+import { RuntimeProgram } from "./types.ts";
+
+export function pretransformProgram(
+ program: RuntimeProgram,
+ id: string,
+): RuntimeProgram {
+ program = transformInjectHelperModule(program);
+ program = transformProgramWithPrefix(program, id);
+ return program;
+}
+
+// For each source file in the program, replace
+// a `/// ` directive line with an
+// internal import statement for use by the AST transformer
+// to provide access to helpers like `derive`, etc.
+export function transformInjectHelperModule(
+ program: RuntimeProgram,
+): RuntimeProgram {
+ return {
+ main: program.main,
+ files: program.files.map((source) => ({
+ name: source.name,
+ contents: transformCtDirective(source.contents),
+ })),
+ mainExport: program.mainExport,
+ };
+}
+
+// Adds `id` as a prefix to all files in the program.
+// Injects a new entry at root `/index.ts` to re-export
+// the entry contents because otherwise `typescript`
+// flattens the output, eliding the common prefix.
+export function transformProgramWithPrefix(
+ program: RuntimeProgram,
+ id: string,
+): RuntimeProgram {
+ const main = program.main;
+ const exportNameds = `export * from "${prefix(main, id)}";`;
+ const exportDefault = `export { default } from "${prefix(main, id)}";`;
+ const hasDefault = !program.mainExport || program.mainExport === "default";
+ const files = [
+ ...program.files.map((source) => ({
+ name: prefix(source.name, id),
+ contents: source.contents,
+ })),
+ {
+ name: `/index.ts`,
+ contents: `${exportNameds}${hasDefault ? `\n${exportDefault}` : ""}`,
+ },
+ ];
+ return {
+ main: `/index.ts`,
+ files,
+ };
+}
+
+function prefix(filename: string, id: string): string {
+ return `/${id}${filename}`;
+}
diff --git a/packages/ts-transformers/src/core/context.ts b/packages/ts-transformers/src/core/context.ts
index 4f3a3b7a2..6497485b1 100644
--- a/packages/ts-transformers/src/core/context.ts
+++ b/packages/ts-transformers/src/core/context.ts
@@ -1,10 +1,10 @@
import ts from "typescript";
-import { ImportRequirements } from "./imports.ts";
import {
DiagnosticInput,
TransformationDiagnostic,
TransformationOptions,
} from "./transformers.ts";
+import { CTHelpers } from "./ct-helpers.ts";
const DEFAULT_OPTIONS: TransformationOptions = {
mode: "transform",
@@ -16,7 +16,6 @@ export interface TransformationContextConfig {
sourceFile: ts.SourceFile;
tsContext: ts.TransformationContext;
options?: TransformationOptions;
- imports?: ImportRequirements;
}
export class TransformationContext {
@@ -25,7 +24,7 @@ export class TransformationContext {
readonly factory: ts.NodeFactory;
readonly sourceFile: ts.SourceFile;
readonly options: TransformationOptions;
- readonly imports: ImportRequirements;
+ readonly ctHelpers: CTHelpers;
readonly diagnostics: TransformationDiagnostic[] = [];
readonly tsContext: ts.TransformationContext;
#typeCache = new Map();
@@ -36,7 +35,10 @@ export class TransformationContext {
this.tsContext = config.tsContext;
this.factory = config.tsContext.factory;
this.sourceFile = config.sourceFile;
- this.imports = config.imports ?? new ImportRequirements();
+ this.ctHelpers = new CTHelpers({
+ factory: this.factory,
+ sourceFile: this.sourceFile,
+ });
this.options = {
...DEFAULT_OPTIONS,
...config.options,
diff --git a/packages/ts-transformers/src/core/ct-helpers.ts b/packages/ts-transformers/src/core/ct-helpers.ts
new file mode 100644
index 000000000..eff061d62
--- /dev/null
+++ b/packages/ts-transformers/src/core/ct-helpers.ts
@@ -0,0 +1,120 @@
+import ts from "typescript";
+import { TransformationContext } from "./mod.ts";
+
+export const CT_HELPERS_IDENTIFIER = "__ctHelpers";
+
+const CT_HELPERS_SPECIFIER = "commontools";
+
+const HELPERS_STMT =
+ `import * as ${CT_HELPERS_IDENTIFIER} from "${CT_HELPERS_SPECIFIER}";`;
+
+const HELPERS_USED_STMT = `${CT_HELPERS_IDENTIFIER}.NAME; // `;
+
+export class CTHelpers {
+ #sourceFile: ts.SourceFile;
+ #factory: ts.NodeFactory;
+ #helperIdent?: ts.Identifier;
+
+ constructor(params: Pick) {
+ this.#sourceFile = params.sourceFile;
+ this.#factory = params.factory;
+
+ for (const stmt of this.#sourceFile.statements) {
+ const symbol = getCTHelpersIdentifier(stmt);
+ if (symbol) {
+ this.#helperIdent = symbol;
+ break;
+ }
+ }
+ }
+
+ sourceHasHelpers(): boolean {
+ return !!this.#helperIdent;
+ }
+
+ // Returns an PropertyAccessExpression of the requested
+ // helper name e.g. `(__ctHelpers.derive)`.
+ getHelperExpr(
+ name: string,
+ ): ts.PropertyAccessExpression {
+ if (!this.sourceHasHelpers()) {
+ throw new Error("Source file does not contain helpers.");
+ }
+ return this.#factory.createPropertyAccessExpression(
+ this.#helperIdent!,
+ name,
+ );
+ }
+
+ // Returns an QualifiedName of the requested
+ // helper name e.g. `__ctHelpers.JSONSchema`.
+ getHelperQualified(
+ name: string,
+ ): ts.QualifiedName {
+ if (!this.sourceHasHelpers()) {
+ throw new Error("Source file does not contain helpers.");
+ }
+ return this.#factory.createQualifiedName(
+ this.#helperIdent!,
+ name,
+ );
+ }
+}
+
+// Replace a `/// ` directive line with an
+// internal import statement for use by the AST transformer
+// to provide access to helpers like `derive`, etc.
+// This operates on strings, and to be used outside of
+// the TypeScript transformer pipeline, since symbol binding
+// occurs before transformers run.
+//
+// We must also inject usage of the module before the AST transformer
+// pipeline, otherwise the binding fails, and the helper module
+// is not available in the compiled JS.
+//
+// Source maps are derived from this transformation.
+// Take care in maintaining source lines from its input.
+//
+// This injected statement enables subsequent transformations.
+export function transformCtDirective(
+ source: string,
+): string {
+ // Throw when this symbol name is already in use
+ // for some reason.
+ if (source.indexOf(CT_HELPERS_IDENTIFIER) !== -1) {
+ throw new Error(
+ `Source cannot contain reserved '${CT_HELPERS_IDENTIFIER}' symbol.`,
+ );
+ }
+ const lines = source.split("\n");
+ if (!lines[0] || !isCTSEnabled(lines[0])) {
+ return source;
+ }
+ return [
+ HELPERS_STMT,
+ ...lines.slice(1),
+ HELPERS_USED_STMT,
+ ].join("\n");
+}
+
+function isCTSEnabled(line: string) {
+ return /^\/\/\/\s* /m.test(line);
+}
+
+function getCTHelpersIdentifier(
+ statement: ts.Statement,
+): ts.Identifier | undefined {
+ if (!ts.isImportDeclaration(statement)) return;
+ const { importClause, moduleSpecifier } = statement;
+
+ // Check specifier is "commontools"
+ if (!ts.isStringLiteral(moduleSpecifier)) return;
+ if (moduleSpecifier.text !== CT_HELPERS_SPECIFIER) return;
+
+ // Check it is a namespace import `* as __ctHelpers`
+ if (!importClause || !ts.isImportClause(importClause)) return;
+ const { namedBindings } = importClause;
+ if (!namedBindings || !ts.isNamespaceImport(namedBindings)) return;
+ if (namedBindings.name.getText() !== CT_HELPERS_IDENTIFIER) return;
+ return namedBindings.name;
+}
diff --git a/packages/ts-transformers/src/core/cts-directive.ts b/packages/ts-transformers/src/core/cts-directive.ts
deleted file mode 100644
index 85192397a..000000000
--- a/packages/ts-transformers/src/core/cts-directive.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import ts from "typescript";
-
-/**
- * Returns true when the source file starts with the CommonTools
- * triple-slash directive `/// `.
- */
-export function hasCtsEnableDirective(sourceFile: ts.SourceFile): boolean {
- const text = sourceFile.getFullText();
- const ranges = ts.getLeadingCommentRanges(text, 0) ?? [];
- for (const range of ranges) {
- const commentText = text.slice(range.pos, range.end);
- if (/^\/\/\/\s* /m.test(commentText)) {
- return true;
- }
- }
- return false;
-}
diff --git a/packages/ts-transformers/src/core/imports.ts b/packages/ts-transformers/src/core/imports.ts
deleted file mode 100644
index 84e45cb3c..000000000
--- a/packages/ts-transformers/src/core/imports.ts
+++ /dev/null
@@ -1,254 +0,0 @@
-import ts from "typescript";
-import { TransformationContext } from "./mod.ts";
-
-export interface ImportRequest {
- readonly name: string;
- readonly module: string;
- readonly typeOnly?: boolean;
-}
-
-interface ModuleRequirements {
- readonly required: Set;
- readonly forbidden: Set;
- readonly module: string;
- readonly typeOnly: boolean;
-}
-
-// Accumulates requirements in the form of required
-// or forbidden module imports. Once accumulated,
-// these requirements can be applied to a SourceFile.
-export class ImportRequirements {
- #map = new Map();
-
- // Marks the import as required, importing the
- // symbol during render if not already imported.
- require(request: ImportRequest): void {
- const reqs = this.#get(request);
- reqs.forbidden.delete(request.name);
- reqs.required.add(request.name);
- }
-
- // Marks the import for removal when rendered,
- // if imported by the source code.
- forbid(request: ImportRequest): void {
- const reqs = this.#get(request);
- reqs.required.delete(request.name);
- reqs.forbidden.add(request.name);
- }
-
- getIdentifier(
- context: Pick,
- request: ImportRequest,
- ): ts.Identifier {
- this.require(request);
- const identifier = findImportedIdentifier(
- context.sourceFile,
- request.module,
- request.name,
- );
- if (identifier) return identifier;
- // This will create a raw identifier that is not bound
- // to an import, and most likely will fail when compiling
- // to JS.
- return context.factory.createIdentifier(`${request.name}`);
- }
-
- // Returns the requirements scoped by the request,
- // lazily creating and storing as needed.
- #get(request: ImportRequest): ModuleRequirements {
- const key = `${request.module}|${request.typeOnly ? "t" : "v"}`;
- const existing = this.#map.get(key);
- if (existing) {
- return existing;
- }
- const reqs = {
- module: request.module,
- typeOnly: request.typeOnly ?? false,
- required: new Set(),
- forbidden: new Set(),
- };
- this.#map.set(key, reqs);
- return reqs;
- }
-
- // Applies all requirements to the provided SourceFile,
- // returning the mapped source.
- apply(sourceFile: ts.SourceFile, factory: ts.NodeFactory): ts.SourceFile {
- let source = sourceFile;
- for (const record of this.#map.values()) {
- source = applyToSource(source, factory, record);
- }
- return source;
- }
-}
-
-function applyToSource(
- sourceFile: ts.SourceFile,
- factory: ts.NodeFactory,
- record: ModuleRequirements,
-): ts.SourceFile {
- let existing: ts.ImportDeclaration | undefined;
- let existingIndex = -1;
-
- sourceFile.statements.forEach((statement, index) => {
- if (!ts.isImportDeclaration(statement)) {
- return;
- }
- if (!ts.isStringLiteral(statement.moduleSpecifier)) {
- return;
- }
- if (statement.moduleSpecifier.text !== record.module) {
- return;
- }
- const clause = statement.importClause;
- if (!clause || !clause.namedBindings) {
- return;
- }
- if (!ts.isNamedImports(clause.namedBindings)) {
- return;
- }
- if (record.typeOnly && !clause.isTypeOnly) {
- return;
- }
- existing = statement;
- existingIndex = index;
- });
-
- if (existing) {
- const newStmt = applyToImport(existing, factory, record);
- if (newStmt) {
- const statements = [...sourceFile.statements];
- statements[existingIndex] = newStmt;
- return factory.updateSourceFile(sourceFile, statements);
- } else {
- return sourceFile;
- }
- }
- return insertImport(sourceFile, factory, record);
-}
-
-// Transforms the given import statement with
-// the module requirements. Returns `undefined`
-// if no transformation could be applied.
-function applyToImport(
- existing: ts.ImportDeclaration,
- factory: ts.NodeFactory,
- record: ModuleRequirements,
-): ts.ImportDeclaration | undefined {
- const clause = existing.importClause;
- if (!clause || !clause.namedBindings) {
- return undefined;
- }
-
- const named = clause.namedBindings;
- if (!ts.isNamedImports(named)) {
- return undefined;
- }
-
- const filteredExisting = named.elements.filter((element) =>
- !record.forbidden.has(element.name.text)
- );
- const didRemove = filteredExisting.length !== named.elements.length;
- const existingNames = new Set(
- filteredExisting.map((element) => element.name.text),
- );
- const nextElements = [...filteredExisting];
-
- for (const name of record.required) {
- if (existingNames.has(name)) continue;
- nextElements.push(
- factory.createImportSpecifier(
- false,
- undefined,
- factory.createIdentifier(name),
- ),
- );
- }
-
- if (!didRemove && nextElements.length === named.elements.length) {
- return undefined;
- }
-
- return factory.updateImportDeclaration(
- existing,
- existing.modifiers,
- factory.updateImportClause(
- clause,
- record.typeOnly || clause.isTypeOnly,
- clause.name,
- factory.createNamedImports(nextElements),
- ),
- existing.moduleSpecifier,
- existing.assertClause,
- );
-}
-
-function insertImport(
- sourceFile: ts.SourceFile,
- factory: ts.NodeFactory,
- record: ModuleRequirements,
-): ts.SourceFile {
- const elements = Array.from(record.required).map((name) =>
- factory.createImportSpecifier(
- false,
- undefined,
- factory.createIdentifier(name),
- )
- );
-
- const clause = factory.createImportClause(
- record.typeOnly,
- undefined,
- factory.createNamedImports(elements),
- );
-
- const declaration = factory.createImportDeclaration(
- undefined,
- clause,
- factory.createStringLiteral(record.module),
- undefined,
- );
-
- const statements = [...sourceFile.statements];
- let insertIndex = 0;
- for (let i = 0; i < statements.length; i++) {
- const statement = statements[i];
- if (!statement) {
- break;
- }
- if (ts.isImportDeclaration(statement)) {
- insertIndex = i + 1;
- continue;
- }
- break;
- }
- statements.splice(insertIndex, 0, declaration);
- return factory.updateSourceFile(sourceFile, statements);
-}
-
-function findImportedIdentifier(
- sourceFile: ts.SourceFile,
- moduleName: string,
- importName: string,
-): ts.Identifier | undefined {
- for (const statement of sourceFile.statements) {
- if (ts.isImportDeclaration(statement)) {
- const moduleSpecifier = statement.moduleSpecifier;
- if (
- ts.isStringLiteral(moduleSpecifier) &&
- moduleSpecifier.text === moduleName
- ) {
- const namedBindings = statement.importClause?.namedBindings;
- if (namedBindings && ts.isNamedImports(namedBindings)) {
- for (const element of namedBindings.elements) {
- const name = element.propertyName?.text || element.name.text;
- if (name === importName) {
- return element.name; // This is the local identifier
- }
- }
- }
- }
- }
- }
- return undefined;
-}
diff --git a/packages/ts-transformers/src/core/mod.ts b/packages/ts-transformers/src/core/mod.ts
index 25f1ca262..8511d4c0d 100644
--- a/packages/ts-transformers/src/core/mod.ts
+++ b/packages/ts-transformers/src/core/mod.ts
@@ -1,4 +1,3 @@
-export { type ImportRequest, ImportRequirements } from "./imports.ts";
export { TransformationContext } from "./context.ts";
export type {
DiagnosticInput,
@@ -9,4 +8,8 @@ export type {
} from "./transformers.ts";
export { Pipeline, Transformer } from "./transformers.ts";
export * from "./common-tools-symbols.ts";
-export { hasCtsEnableDirective } from "./cts-directive.ts";
+export {
+ CT_HELPERS_IDENTIFIER,
+ CTHelpers,
+ transformCtDirective,
+} from "./ct-helpers.ts";
diff --git a/packages/ts-transformers/src/ct-pipeline.ts b/packages/ts-transformers/src/ct-pipeline.ts
index 0047d0980..35cc39232 100644
--- a/packages/ts-transformers/src/ct-pipeline.ts
+++ b/packages/ts-transformers/src/ct-pipeline.ts
@@ -3,7 +3,7 @@ import {
SchemaGeneratorTransformer,
SchemaInjectionTransformer,
} from "./transformers/mod.ts";
-import { Pipeline, TransformationOptions, TypeRegistry } from "./core/mod.ts";
+import { Pipeline, TransformationOptions } from "./core/mod.ts";
export class CommonToolsTransformerPipeline extends Pipeline {
constructor(options: TransformationOptions = {}) {
diff --git a/packages/ts-transformers/src/mod.ts b/packages/ts-transformers/src/mod.ts
index 361a26d21..ea2ff6296 100644
--- a/packages/ts-transformers/src/mod.ts
+++ b/packages/ts-transformers/src/mod.ts
@@ -1,13 +1,10 @@
-export { ImportRequirements as ImportManager } from "./core/imports.ts";
-export type { ImportRequest } from "./core/imports.ts";
-
export type {
TransformationContext,
TransformationDiagnostic,
TransformationOptions,
TransformMode,
} from "./core/mod.ts";
-export { Pipeline, Transformer } from "./core/mod.ts";
+export { Pipeline, transformCtDirective, Transformer } from "./core/mod.ts";
export {
OpaqueRefJSXTransformer,
diff --git a/packages/ts-transformers/src/transformers/builtins/derive.ts b/packages/ts-transformers/src/transformers/builtins/derive.ts
index ab6153425..21c02974a 100644
--- a/packages/ts-transformers/src/transformers/builtins/derive.ts
+++ b/packages/ts-transformers/src/transformers/builtins/derive.ts
@@ -1,31 +1,5 @@
import ts from "typescript";
-import {
- containsOpaqueRef,
- isOpaqueRefType,
- isSimpleOpaqueRefAccess,
-} from "../opaque-ref/opaque-ref.ts";
-import {
- createDataFlowAnalyzer,
- type DataFlowAnalysis,
- dedupeExpressions,
-} from "../../ast/mod.ts";
-import { ImportRequirements } from "../../core/mod.ts";
-
-function replaceOpaqueRefWithParam(
- expression: ts.Expression,
- opaqueRef: ts.Expression,
- paramName: string,
- factory: ts.NodeFactory,
- context: ts.TransformationContext,
-): ts.Expression {
- const visit = (node: ts.Node): ts.Node => {
- if (node === opaqueRef) {
- return factory.createIdentifier(paramName);
- }
- return ts.visitEachChild(node, visit, context);
- };
- return visit(expression) as ts.Expression;
-}
+import { CTHelpers } from "../../core/ct-helpers.ts";
function replaceOpaqueRefsWithParams(
expression: ts.Expression,
@@ -58,7 +32,7 @@ export interface DeriveCallOptions {
readonly factory: ts.NodeFactory;
readonly sourceFile: ts.SourceFile;
readonly tsContext: ts.TransformationContext;
- readonly imports: ImportRequirements;
+ readonly ctHelpers: CTHelpers;
}
function createPropertyName(
@@ -166,7 +140,7 @@ export function createDeriveCall(
): ts.Expression | undefined {
if (refs.length === 0) return undefined;
- const { factory, tsContext, imports, sourceFile } = options;
+ const { factory, tsContext, ctHelpers, sourceFile } = options;
const { entries, refToParamName } = planDeriveEntries(refs);
if (entries.length === 0) return undefined;
@@ -186,17 +160,14 @@ export function createDeriveCall(
lambdaBody,
);
- const deriveIdentifier = imports.getIdentifier({ factory, sourceFile }, {
- module: "commontools",
- name: "derive",
- });
+ const deriveExpr = ctHelpers.getHelperExpr("derive");
const deriveArgs = [
...createDeriveArgs(factory, entries),
arrowFunction,
];
return factory.createCallExpression(
- deriveIdentifier,
+ deriveExpr,
undefined,
deriveArgs,
);
diff --git a/packages/ts-transformers/src/transformers/builtins/ifelse.ts b/packages/ts-transformers/src/transformers/builtins/ifelse.ts
index ca9f7ea3e..86ed5de14 100644
--- a/packages/ts-transformers/src/transformers/builtins/ifelse.ts
+++ b/packages/ts-transformers/src/transformers/builtins/ifelse.ts
@@ -1,10 +1,10 @@
import ts from "typescript";
-import { ImportRequirements } from "../../core/mod.ts";
+import { CTHelpers } from "../../core/mod.ts";
export interface IfElseParams {
expression: ts.ConditionalExpression;
factory: ts.NodeFactory;
- imports: ImportRequirements;
+ ctHelpers: CTHelpers;
sourceFile: ts.SourceFile;
overrides?: IfElseOverrides;
}
@@ -16,11 +16,8 @@ export interface IfElseOverrides {
}
export function createIfElseCall(params: IfElseParams): ts.CallExpression {
- const { factory, imports, overrides, expression, sourceFile } = params;
- const ifElseIdentifier = imports.getIdentifier({ factory, sourceFile }, {
- module: "commontools",
- name: "ifElse",
- });
+ const { factory, ctHelpers, overrides, expression } = params;
+ const ifElseExpr = ctHelpers.getHelperExpr("ifElse");
let predicate = overrides?.predicate ?? expression.condition;
let whenTrue = overrides?.whenTrue ?? expression.whenTrue;
@@ -34,7 +31,7 @@ export function createIfElseCall(params: IfElseParams): ts.CallExpression {
}
return factory.createCallExpression(
- ifElseIdentifier,
+ ifElseExpr,
undefined,
[predicate, whenTrue, whenFalse],
);
diff --git a/packages/ts-transformers/src/transformers/opaque-ref-jsx.ts b/packages/ts-transformers/src/transformers/opaque-ref-jsx.ts
index bb2a097e5..d79959090 100644
--- a/packages/ts-transformers/src/transformers/opaque-ref-jsx.ts
+++ b/packages/ts-transformers/src/transformers/opaque-ref-jsx.ts
@@ -1,29 +1,19 @@
import ts from "typescript";
-import {
- hasCtsEnableDirective,
- TransformationContext,
- Transformer,
-} from "../core/mod.ts";
+import { TransformationContext, Transformer } from "../core/mod.ts";
import {
createDataFlowAnalyzer,
detectCallKind,
isEventHandlerJsxAttribute,
} from "../ast/mod.ts";
-import { OpaqueRefHelperName, rewriteExpression } from "./opaque-ref/mod.ts";
+import { rewriteExpression } from "./opaque-ref/mod.ts";
export class OpaqueRefJSXTransformer extends Transformer {
override filter(context: TransformationContext): boolean {
- return hasCtsEnableDirective(context.sourceFile);
+ return context.ctHelpers.sourceHasHelpers();
}
transform(context: TransformationContext): ts.SourceFile {
- const { tsContext: transformation } = context;
-
- const out = transform(context);
- return context.imports.apply(
- out,
- transformation.factory,
- );
+ return transform(context);
}
}
diff --git a/packages/ts-transformers/src/transformers/opaque-ref/emitters/conditional-expression.ts b/packages/ts-transformers/src/transformers/opaque-ref/emitters/conditional-expression.ts
index 77ab91042..46b7c50a2 100644
--- a/packages/ts-transformers/src/transformers/opaque-ref/emitters/conditional-expression.ts
+++ b/packages/ts-transformers/src/transformers/opaque-ref/emitters/conditional-expression.ts
@@ -1,6 +1,6 @@
import ts from "typescript";
-import type { Emitter, OpaqueRefHelperName } from "../types.ts";
+import type { Emitter } from "../types.ts";
import { createIfElseCall } from "../../builtins/ifelse.ts";
import { selectDataFlowsWithin } from "../../../ast/mod.ts";
import { isSimpleOpaqueRefAccess } from "../opaque-ref.ts";
@@ -109,7 +109,7 @@ export const emitConditionalExpression: Emitter = ({
return createIfElseCall({
expression,
factory: context.factory,
- imports: context.imports,
+ ctHelpers: context.ctHelpers,
sourceFile: context.sourceFile,
overrides: {
predicate,
diff --git a/packages/ts-transformers/src/transformers/opaque-ref/helpers.ts b/packages/ts-transformers/src/transformers/opaque-ref/helpers.ts
index 1c1ed2a28..544312d0e 100644
--- a/packages/ts-transformers/src/transformers/opaque-ref/helpers.ts
+++ b/packages/ts-transformers/src/transformers/opaque-ref/helpers.ts
@@ -285,7 +285,7 @@ export function createDeriveCallForExpression(
factory: context.factory,
sourceFile: context.sourceFile,
tsContext: context.tsContext,
- imports: context.imports,
+ ctHelpers: context.ctHelpers,
});
return deriveCall;
diff --git a/packages/ts-transformers/src/transformers/schema-generator.ts b/packages/ts-transformers/src/transformers/schema-generator.ts
index 67f0b32b7..96fa06ea5 100644
--- a/packages/ts-transformers/src/transformers/schema-generator.ts
+++ b/packages/ts-transformers/src/transformers/schema-generator.ts
@@ -1,6 +1,6 @@
import ts from "typescript";
import {
- hasCtsEnableDirective,
+ CT_HELPERS_IDENTIFIER,
TransformationContext,
Transformer,
} from "../core/mod.ts";
@@ -10,7 +10,7 @@ let generateSchema: ReturnType | undefined;
export class SchemaGeneratorTransformer extends Transformer {
override filter(context: TransformationContext): boolean {
- return hasCtsEnableDirective(context.sourceFile);
+ return context.ctHelpers.sourceHasHelpers();
}
transform(context: TransformationContext): ts.SourceFile {
@@ -19,17 +19,8 @@ export class SchemaGeneratorTransformer extends Transformer {
const { logger, typeRegistry } = context.options;
const visit: ts.Visitor = (node) => {
- if (
- ts.isCallExpression(node) &&
- ts.isIdentifier(node.expression) &&
- node.expression.text === "toSchema" &&
- node.typeArguments &&
- node.typeArguments.length === 1
- ) {
- const typeArg = node.typeArguments[0];
- if (!typeArg) {
- return ts.visitEachChild(node, visit, transformation);
- }
+ if (isToSchemaNode(node)) {
+ const typeArg = node.typeArguments[0]!;
// First check if we have a registered Type for this node
// (from schema-injection when synthetic TypeNodes were created)
@@ -73,16 +64,12 @@ export class SchemaGeneratorTransformer extends Transformer {
),
);
- const jsonSchemaIdentifier = context.imports.getIdentifier(
- context,
- { module: "commontools", name: "JSONSchema" },
+ const jsonSchemaName = context.ctHelpers.getHelperQualified(
+ "JSONSchema",
);
const satisfiesExpression = context.factory.createSatisfiesExpression(
constAssertion,
- context.factory.createTypeReferenceNode(
- jsonSchemaIdentifier,
- undefined,
- ),
+ context.factory.createTypeReferenceNode(jsonSchemaName),
);
return satisfiesExpression;
@@ -91,17 +78,7 @@ export class SchemaGeneratorTransformer extends Transformer {
return ts.visitEachChild(node, visit, transformation);
};
- const result = ts.visitNode(sourceFile, visit) as ts.SourceFile;
-
- context.imports.forbid({
- module: "commontools",
- name: "toSchema",
- });
-
- return context.imports.apply(
- result,
- transformation.factory,
- );
+ return ts.visitNode(sourceFile, visit) as ts.SourceFile;
}
}
@@ -172,3 +149,33 @@ function evaluateExpression(
if (constantValue !== undefined) return constantValue;
return undefined;
}
+
+// Helper type extending CallExpression with
+// truthy typeArguments.
+interface ToSchemaNode extends ts.CallExpression {
+ typeArguments: ts.NodeArray;
+}
+function isToSchemaNode(node: ts.Node): node is ToSchemaNode {
+ if (!ts.isCallExpression(node)) return false;
+ const { typeArguments, expression } = node;
+ if (!typeArguments || typeArguments.length !== 1) return false;
+
+ // Raw identity expression `toSchema()`
+ if (
+ ts.isIdentifier(expression) &&
+ expression.text === "toSchema" &&
+ typeArguments &&
+ typeArguments.length === 1
+ ) {
+ return true;
+ }
+ // Raw property access expression `__ctHelpers.toSchema()`
+ if (
+ ts.isPropertyAccessExpression(expression) &&
+ expression.expression.getText() === CT_HELPERS_IDENTIFIER &&
+ expression.name.text === "toSchema"
+ ) {
+ return true;
+ }
+ return false;
+}
diff --git a/packages/ts-transformers/src/transformers/schema-injection.ts b/packages/ts-transformers/src/transformers/schema-injection.ts
index 386cb44d6..5816576dc 100644
--- a/packages/ts-transformers/src/transformers/schema-injection.ts
+++ b/packages/ts-transformers/src/transformers/schema-injection.ts
@@ -8,7 +8,6 @@ import {
typeToSchemaTypeNode,
} from "../ast/mod.ts";
import {
- hasCtsEnableDirective,
TransformationContext,
Transformer,
type TypeRegistry,
@@ -104,25 +103,22 @@ function collectFunctionSchemaTypeNodes(
}
function createToSchemaCall(
- { imports, factory, sourceFile }: Pick<
+ { ctHelpers, factory }: Pick<
TransformationContext,
- "imports" | "factory" | "sourceFile"
+ "ctHelpers" | "factory"
>,
typeNode: ts.TypeNode,
): ts.CallExpression {
- const identifier = imports.getIdentifier({ factory, sourceFile }, {
- module: "commontools",
- name: "toSchema",
- });
+ const expr = ctHelpers.getHelperExpr("toSchema");
return factory.createCallExpression(
- identifier,
+ expr,
[typeNode],
[],
);
}
function prependSchemaArguments(
- context: Pick,
+ context: Pick,
node: ts.CallExpression,
argumentTypeNode: ts.TypeNode,
argumentType: ts.Type | undefined,
@@ -159,10 +155,11 @@ function prependSchemaArguments(
export class SchemaInjectionTransformer extends Transformer {
override filter(context: TransformationContext): boolean {
- return hasCtsEnableDirective(context.sourceFile);
+ return context.ctHelpers.sourceHasHelpers();
}
+
transform(context: TransformationContext): ts.SourceFile {
- const { sourceFile, tsContext: transformation, checker, imports } = context;
+ const { sourceFile, tsContext: transformation, checker } = context;
const typeRegistry = context.options.typeRegistry;
const visit = (node: ts.Node): ts.Node => {
diff --git a/packages/ts-transformers/test/fixtures/ast-transform/builder-conditional.expected.tsx b/packages/ts-transformers/test/fixtures/ast-transform/builder-conditional.expected.tsx
index 32b8860fa..4d5864974 100644
--- a/packages/ts-transformers/test/fixtures/ast-transform/builder-conditional.expected.tsx
+++ b/packages/ts-transformers/test/fixtures/ast-transform/builder-conditional.expected.tsx
@@ -1,5 +1,5 @@
-///
-import { Default, h, NAME, recipe, UI, derive, ifElse, JSONSchema } from "commontools";
+import * as __ctHelpers from "commontools";
+import { Default, h, NAME, recipe, UI } from "commontools";
interface RecipeState {
count: Default;
label: Default;
@@ -17,11 +17,12 @@ export default recipe({
}
},
required: ["count", "label"]
-} as const satisfies JSONSchema, (state) => {
+} as const satisfies __ctHelpers.JSONSchema, (state) => {
return {
[NAME]: state.label,
[UI]: (
- {ifElse(derive({ state, state_count: state.count }, ({ state: state, state_count: _v2 }) => state && _v2 > 0), Positive
, Non-positive
)}
+ {__ctHelpers.ifElse(__ctHelpers.derive({ state, state_count: state.count }, ({ state: state, state_count: _v2 }) => state && _v2 > 0), Positive
, Non-positive
)}
),
};
});
+__ctHelpers.NAME; //
diff --git a/packages/ts-transformers/test/fixtures/ast-transform/counter-recipe.expected.tsx b/packages/ts-transformers/test/fixtures/ast-transform/counter-recipe.expected.tsx
index 31383fe77..135e7f9f2 100644
--- a/packages/ts-transformers/test/fixtures/ast-transform/counter-recipe.expected.tsx
+++ b/packages/ts-transformers/test/fixtures/ast-transform/counter-recipe.expected.tsx
@@ -1,12 +1,12 @@
-///
-import { Cell, Default, h, handler, NAME, recipe, str, UI, derive, ifElse, JSONSchema } from "commontools";
+import * as __ctHelpers from "commontools";
+import { Cell, Default, h, handler, NAME, recipe, str, UI } from "commontools";
interface CounterState {
value: Cell;
}
interface RecipeState {
value: Default;
}
-const increment = handler(true as const satisfies JSONSchema, {
+const increment = handler(true as const satisfies __ctHelpers.JSONSchema, {
type: "object",
properties: {
value: {
@@ -15,10 +15,10 @@ const increment = handler(true as const satisfies JSONSchema, {
}
},
required: ["value"]
-} as const satisfies JSONSchema, (e, state) => {
+} as const satisfies __ctHelpers.JSONSchema, (e, state) => {
state.value.set(state.value.get() + 1);
});
-const decrement = handler(true as const satisfies JSONSchema, {
+const decrement = handler(true as const satisfies __ctHelpers.JSONSchema, {
type: "object",
properties: {
value: {
@@ -27,7 +27,7 @@ const decrement = handler(true as const satisfies JSONSchema, {
}
},
required: ["value"]
-} as const satisfies JSONSchema, (_, state: {
+} as const satisfies __ctHelpers.JSONSchema, (_, state: {
value: Cell;
}) => {
state.value.set(state.value.get() - 1);
@@ -41,16 +41,17 @@ export default recipe({
}
},
required: ["value"]
-} as const satisfies JSONSchema, (state) => {
+} as const satisfies __ctHelpers.JSONSchema, (state) => {
return {
[NAME]: str `Simple counter: ${state.value}`,
[UI]: (
-
- next number: {ifElse(state.value, derive(state.value, _v1 => _v1 + 1), "unknown")}
+ next number: {__ctHelpers.ifElse(state.value, __ctHelpers.derive(state.value, _v1 => _v1 + 1), "unknown")}
+
),
value: state.value,
};
});
+__ctHelpers.NAME; //
diff --git a/packages/ts-transformers/test/fixtures/ast-transform/derive-object-literal-input.expected.tsx b/packages/ts-transformers/test/fixtures/ast-transform/derive-object-literal-input.expected.tsx
index edc1341f5..9db62853a 100644
--- a/packages/ts-transformers/test/fixtures/ast-transform/derive-object-literal-input.expected.tsx
+++ b/packages/ts-transformers/test/fixtures/ast-transform/derive-object-literal-input.expected.tsx
@@ -1,29 +1,29 @@
-///
-import { cell, derive, lift, JSONSchema } from "commontools";
+import * as __ctHelpers from "commontools";
+import { cell, derive, lift } from "commontools";
const stage = cell("initial");
const attemptCount = cell(0);
const acceptedCount = cell(0);
const rejectedCount = cell(0);
const normalizedStage = lift({
type: "string"
-} as const satisfies JSONSchema, {
+} as const satisfies __ctHelpers.JSONSchema, {
type: "string"
-} as const satisfies JSONSchema, (value: string) => value)(stage);
+} as const satisfies __ctHelpers.JSONSchema, (value: string) => value)(stage);
const attempts = lift({
type: "number"
-} as const satisfies JSONSchema, {
+} as const satisfies __ctHelpers.JSONSchema, {
type: "number"
-} as const satisfies JSONSchema, (count: number) => count)(attemptCount);
+} as const satisfies __ctHelpers.JSONSchema, (count: number) => count)(attemptCount);
const accepted = lift({
type: "number"
-} as const satisfies JSONSchema, {
+} as const satisfies __ctHelpers.JSONSchema, {
type: "number"
-} as const satisfies JSONSchema, (count: number) => count)(acceptedCount);
+} as const satisfies __ctHelpers.JSONSchema, (count: number) => count)(acceptedCount);
const rejected = lift({
type: "number"
-} as const satisfies JSONSchema, {
+} as const satisfies __ctHelpers.JSONSchema, {
type: "number"
-} as const satisfies JSONSchema, (count: number) => count)(rejectedCount);
+} as const satisfies __ctHelpers.JSONSchema, (count: number) => count)(rejectedCount);
const summary = derive({
type: "object",
properties: {
@@ -45,12 +45,13 @@ const summary = derive({
}
},
required: ["stage", "attempts", "accepted", "rejected"]
-} as const satisfies JSONSchema, {
+} as const satisfies __ctHelpers.JSONSchema, {
type: "string"
-} as const satisfies JSONSchema, {
+} as const satisfies __ctHelpers.JSONSchema, {
stage: normalizedStage,
attempts: attempts,
accepted: accepted,
rejected: rejected,
}, (snapshot) => `stage:${snapshot.stage} attempts:${snapshot.attempts}` +
` accepted:${snapshot.accepted} rejected:${snapshot.rejected}`);
+__ctHelpers.NAME; //
diff --git a/packages/ts-transformers/test/fixtures/ast-transform/event-handler-no-derive.expected.tsx b/packages/ts-transformers/test/fixtures/ast-transform/event-handler-no-derive.expected.tsx
index bd0796a26..25696ac38 100644
--- a/packages/ts-transformers/test/fixtures/ast-transform/event-handler-no-derive.expected.tsx
+++ b/packages/ts-transformers/test/fixtures/ast-transform/event-handler-no-derive.expected.tsx
@@ -1,5 +1,5 @@
-///
-import { Cell, Default, h, handler, recipe, UI, derive, JSONSchema } from "commontools";
+import * as __ctHelpers from "commontools";
+import { Cell, Default, h, handler, recipe, UI } from "commontools";
declare global {
namespace JSX {
interface IntrinsicElements {
@@ -7,7 +7,7 @@ declare global {
}
}
}
-const handleClick = handler(true as const satisfies JSONSchema, {
+const handleClick = handler(true as const satisfies __ctHelpers.JSONSchema, {
type: "object",
properties: {
count: {
@@ -16,7 +16,7 @@ const handleClick = handler(true as const satisfies JSONSchema, {
}
},
required: ["count"]
-} as const satisfies JSONSchema, (_, { count }) => {
+} as const satisfies __ctHelpers.JSONSchema, (_, { count }) => {
count.set(count.get() + 1);
});
export default recipe({
@@ -28,11 +28,11 @@ export default recipe({
}
},
required: ["count"]
-} as const satisfies JSONSchema, ({ count }) => {
+} as const satisfies __ctHelpers.JSONSchema, ({ count }) => {
return {
[UI]: (
{/* Regular JSX expression - should be wrapped in derive */}
-
Count: {derive(count, count => count + 1)}
+
Count: {__ctHelpers.derive(count, count => count + 1)}
{/* Event handler with OpaqueRef - should NOT be wrapped in derive */}
@@ -47,3 +47,4 @@ export default recipe({
count,
};
});
+__ctHelpers.NAME; //
diff --git a/packages/ts-transformers/test/fixtures/ast-transform/handler-object-literal.expected.tsx b/packages/ts-transformers/test/fixtures/ast-transform/handler-object-literal.expected.tsx
index c017384d2..12824e042 100644
--- a/packages/ts-transformers/test/fixtures/ast-transform/handler-object-literal.expected.tsx
+++ b/packages/ts-transformers/test/fixtures/ast-transform/handler-object-literal.expected.tsx
@@ -1,10 +1,10 @@
-///
-import { Cell, handler, recipe, JSONSchema } from "commontools";
+import * as __ctHelpers from "commontools";
+import { Cell, handler, recipe } from "commontools";
interface State {
value: Cell;
name?: Cell;
}
-const myHandler = handler(true as const satisfies JSONSchema, {
+const myHandler = handler(true as const satisfies __ctHelpers.JSONSchema, {
type: "object",
properties: {
value: {
@@ -17,7 +17,7 @@ const myHandler = handler(true as const satisfies JSONSchema, {
}
},
required: ["value"]
-} as const satisfies JSONSchema, (_, state: State) => {
+} as const satisfies __ctHelpers.JSONSchema, (_, state: State) => {
state.value.set(state.value.get() + 1);
});
export default recipe({ type: "object", properties: { value: { type: "number" }, name: { type: "string" } } }, (state) => {
@@ -30,3 +30,4 @@ export default recipe({ type: "object", properties: { value: { type: "number" },
onClick3: myHandler(state),
};
});
+__ctHelpers.NAME; //
diff --git a/packages/ts-transformers/test/fixtures/ast-transform/recipe-array-map.expected.tsx b/packages/ts-transformers/test/fixtures/ast-transform/recipe-array-map.expected.tsx
index c386b6477..48671121f 100644
--- a/packages/ts-transformers/test/fixtures/ast-transform/recipe-array-map.expected.tsx
+++ b/packages/ts-transformers/test/fixtures/ast-transform/recipe-array-map.expected.tsx
@@ -1,6 +1,6 @@
-///
-import { Cell, derive, h, handler, NAME, recipe, str, UI, JSONSchema } from "commontools";
-const adder = handler(true as const satisfies JSONSchema, {
+import * as __ctHelpers from "commontools";
+import { Cell, derive, h, handler, NAME, recipe, str, UI } from "commontools";
+const adder = handler(true as const satisfies __ctHelpers.JSONSchema, {
type: "object",
properties: {
values: {
@@ -12,7 +12,7 @@ const adder = handler(true as const satisfies JSONSchema, {
}
},
required: ["values"]
-} as const satisfies JSONSchema, (_, state: {
+} as const satisfies __ctHelpers.JSONSchema, (_, state: {
values: Cell;
}) => {
state.values.push(Math.random().toString(36).substring(2, 15));
@@ -28,11 +28,11 @@ export default recipe({
}
},
required: ["values"]
-} as const satisfies JSONSchema, ({ values }) => {
+} as const satisfies __ctHelpers.JSONSchema, ({ values }) => {
derive({
type: "array",
items: true
- } as const satisfies JSONSchema, true as const satisfies JSONSchema, values, (values) => {
+ } as const satisfies __ctHelpers.JSONSchema, true as const satisfies __ctHelpers.JSONSchema, values, (values) => {
console.log("values#", values?.length);
});
return {
@@ -48,3 +48,4 @@ export default recipe({
values,
};
});
+__ctHelpers.NAME; //
diff --git a/packages/ts-transformers/test/fixtures/ast-transform/schema-generation-builders.expected.tsx b/packages/ts-transformers/test/fixtures/ast-transform/schema-generation-builders.expected.tsx
index 418411de5..3faa5a183 100644
--- a/packages/ts-transformers/test/fixtures/ast-transform/schema-generation-builders.expected.tsx
+++ b/packages/ts-transformers/test/fixtures/ast-transform/schema-generation-builders.expected.tsx
@@ -1,5 +1,5 @@
-///
-import { handler, recipe, UI, JSONSchema } from "commontools";
+import * as __ctHelpers from "commontools";
+import { handler, recipe, UI } from "commontools";
type TodoState = {
items: string[];
};
@@ -14,7 +14,7 @@ const addTodo = handler({
}
},
required: ["add"]
-} as const satisfies JSONSchema, {
+} as const satisfies __ctHelpers.JSONSchema, {
type: "object",
properties: {
items: {
@@ -25,7 +25,7 @@ const addTodo = handler({
}
},
required: ["items"]
-} as const satisfies JSONSchema, (event, state) => {
+} as const satisfies __ctHelpers.JSONSchema, (event, state) => {
state.items.push(event.add);
});
export default recipe({
@@ -39,7 +39,7 @@ export default recipe({
}
},
required: ["items"]
-} as const satisfies JSONSchema, (state) => {
+} as const satisfies __ctHelpers.JSONSchema, (state) => {
return {
[UI]: (
@@ -51,3 +51,4 @@ export default recipe({
),
};
});
+__ctHelpers.NAME; //
diff --git a/packages/ts-transformers/test/fixtures/ast-transform/schema-generation-derive-alias.expected.tsx b/packages/ts-transformers/test/fixtures/ast-transform/schema-generation-derive-alias.expected.tsx
index 62319af70..3ff30f9c8 100644
--- a/packages/ts-transformers/test/fixtures/ast-transform/schema-generation-derive-alias.expected.tsx
+++ b/packages/ts-transformers/test/fixtures/ast-transform/schema-generation-derive-alias.expected.tsx
@@ -1,5 +1,5 @@
-///
-import { derive as deriveAlias, JSONSchema } from "commontools";
+import * as __ctHelpers from "commontools";
+import { derive as deriveAlias } from "commontools";
type AliasInput = {
text: string;
};
@@ -15,7 +15,7 @@ export const textLength = deriveAlias({
}
},
required: ["text"]
-} as const satisfies JSONSchema, {
+} as const satisfies __ctHelpers.JSONSchema, {
type: "object",
properties: {
length: {
@@ -23,6 +23,7 @@ export const textLength = deriveAlias({
}
},
required: ["length"]
-} as const satisfies JSONSchema, state, (value) => ({
+} as const satisfies __ctHelpers.JSONSchema, state, (value) => ({
length: value.text.length,
}));
+__ctHelpers.NAME; //
diff --git a/packages/ts-transformers/test/fixtures/ast-transform/schema-generation-derive-inside-jsx.expected.tsx b/packages/ts-transformers/test/fixtures/ast-transform/schema-generation-derive-inside-jsx.expected.tsx
index 1ba446c0a..c4f71326b 100644
--- a/packages/ts-transformers/test/fixtures/ast-transform/schema-generation-derive-inside-jsx.expected.tsx
+++ b/packages/ts-transformers/test/fixtures/ast-transform/schema-generation-derive-inside-jsx.expected.tsx
@@ -1,10 +1,11 @@
-///
-import { derive, h, JSONSchema } from "commontools";
+import * as __ctHelpers from "commontools";
+import { derive, h } from "commontools";
declare const value: number;
export const result = (
{derive({
type: "number"
-} as const satisfies JSONSchema, {
+} as const satisfies __ctHelpers.JSONSchema, {
type: "number"
-} as const satisfies JSONSchema, value, (v) => v * 2)}
-
);
\ No newline at end of file
+} as const satisfies __ctHelpers.JSONSchema, value, (v) => v * 2)}
+ );
+__ctHelpers.NAME; //
diff --git a/packages/ts-transformers/test/fixtures/ast-transform/schema-generation-derive-multiple-returns.expected.tsx b/packages/ts-transformers/test/fixtures/ast-transform/schema-generation-derive-multiple-returns.expected.tsx
index d9741173a..6e7b111e9 100644
--- a/packages/ts-transformers/test/fixtures/ast-transform/schema-generation-derive-multiple-returns.expected.tsx
+++ b/packages/ts-transformers/test/fixtures/ast-transform/schema-generation-derive-multiple-returns.expected.tsx
@@ -1,14 +1,15 @@
-///
-import { derive, JSONSchema } from "commontools";
+import * as __ctHelpers from "commontools";
+import { derive } from "commontools";
declare const flag: boolean;
// Function with multiple return statements - should infer string | number
export const multiReturn = derive({
type: "boolean"
-} as const satisfies JSONSchema, {
+} as const satisfies __ctHelpers.JSONSchema, {
enum: ["hello", 42]
-} as const satisfies JSONSchema, flag, (value) => {
+} as const satisfies __ctHelpers.JSONSchema, flag, (value) => {
if (value) {
return "hello";
}
return 42;
-});
\ No newline at end of file
+});
+__ctHelpers.NAME; //
diff --git a/packages/ts-transformers/test/fixtures/ast-transform/schema-generation-derive-untyped.expected.tsx b/packages/ts-transformers/test/fixtures/ast-transform/schema-generation-derive-untyped.expected.tsx
index 94131ed77..0c9209970 100644
--- a/packages/ts-transformers/test/fixtures/ast-transform/schema-generation-derive-untyped.expected.tsx
+++ b/packages/ts-transformers/test/fixtures/ast-transform/schema-generation-derive-untyped.expected.tsx
@@ -1,8 +1,9 @@
-///
-import { derive, JSONSchema } from "commontools";
+import * as __ctHelpers from "commontools";
+import { derive } from "commontools";
declare const total: number;
export const doubled = derive({
type: "number"
-} as const satisfies JSONSchema, {
+} as const satisfies __ctHelpers.JSONSchema, {
type: "number"
-} as const satisfies JSONSchema, total, (value) => value * 2);
+} as const satisfies __ctHelpers.JSONSchema, total, (value) => value * 2);
+__ctHelpers.NAME; //
diff --git a/packages/ts-transformers/test/fixtures/ast-transform/schema-generation-derive.expected.tsx b/packages/ts-transformers/test/fixtures/ast-transform/schema-generation-derive.expected.tsx
index ddf1598cd..c496d2d24 100644
--- a/packages/ts-transformers/test/fixtures/ast-transform/schema-generation-derive.expected.tsx
+++ b/packages/ts-transformers/test/fixtures/ast-transform/schema-generation-derive.expected.tsx
@@ -1,5 +1,5 @@
-///
-import { derive, JSONSchema } from "commontools";
+import * as __ctHelpers from "commontools";
+import { derive } from "commontools";
type DeriveInput = {
count: number;
};
@@ -15,7 +15,7 @@ export const doubledValue = derive({
}
},
required: ["count"]
-} as const satisfies JSONSchema, {
+} as const satisfies __ctHelpers.JSONSchema, {
type: "object",
properties: {
doubled: {
@@ -23,6 +23,7 @@ export const doubledValue = derive({
}
},
required: ["doubled"]
-} as const satisfies JSONSchema, source, (input) => ({
+} as const satisfies __ctHelpers.JSONSchema, source, (input) => ({
doubled: input.count * 2,
}));
+__ctHelpers.NAME; //
diff --git a/packages/ts-transformers/test/fixtures/ast-transform/schema-generation-handler-both-inline.expected.tsx b/packages/ts-transformers/test/fixtures/ast-transform/schema-generation-handler-both-inline.expected.tsx
index caf90bb30..4bef75da6 100644
--- a/packages/ts-transformers/test/fixtures/ast-transform/schema-generation-handler-both-inline.expected.tsx
+++ b/packages/ts-transformers/test/fixtures/ast-transform/schema-generation-handler-both-inline.expected.tsx
@@ -1,5 +1,5 @@
-///
-import { handler, JSONSchema } from "commontools";
+import * as __ctHelpers from "commontools";
+import { handler } from "commontools";
interface IncrementEvent {
amount: number;
}
@@ -15,7 +15,7 @@ export const incrementer = handler({
}
},
required: ["amount"]
-} as const satisfies JSONSchema, {
+} as const satisfies __ctHelpers.JSONSchema, {
type: "object",
properties: {
count: {
@@ -23,6 +23,7 @@ export const incrementer = handler({
}
},
required: ["count"]
-} as const satisfies JSONSchema, (event: IncrementEvent, state: CounterState) => {
+} as const satisfies __ctHelpers.JSONSchema, (event: IncrementEvent, state: CounterState) => {
state.count += event.amount;
-});
\ No newline at end of file
+});
+__ctHelpers.NAME; //
diff --git a/packages/ts-transformers/test/fixtures/ast-transform/schema-generation-handler-event-only.expected.tsx b/packages/ts-transformers/test/fixtures/ast-transform/schema-generation-handler-event-only.expected.tsx
index aff078af6..eeaa64c80 100644
--- a/packages/ts-transformers/test/fixtures/ast-transform/schema-generation-handler-event-only.expected.tsx
+++ b/packages/ts-transformers/test/fixtures/ast-transform/schema-generation-handler-event-only.expected.tsx
@@ -1,5 +1,5 @@
-///
-import { handler, JSONSchema } from "commontools";
+import * as __ctHelpers from "commontools";
+import { handler } from "commontools";
interface IncrementEvent {
amount: number;
}
@@ -12,6 +12,7 @@ export const incrementer = handler({
}
},
required: ["amount"]
-} as const satisfies JSONSchema, true as const satisfies JSONSchema, (event: IncrementEvent, state) => {
+} as const satisfies __ctHelpers.JSONSchema, true as const satisfies __ctHelpers.JSONSchema, (event: IncrementEvent, state) => {
console.log("increment by", event.amount);
-});
\ No newline at end of file
+});
+__ctHelpers.NAME; //
diff --git a/packages/ts-transformers/test/fixtures/ast-transform/schema-generation-handler-inside-jsx.expected.tsx b/packages/ts-transformers/test/fixtures/ast-transform/schema-generation-handler-inside-jsx.expected.tsx
index 865689f65..43c65c9f4 100644
--- a/packages/ts-transformers/test/fixtures/ast-transform/schema-generation-handler-inside-jsx.expected.tsx
+++ b/packages/ts-transformers/test/fixtures/ast-transform/schema-generation-handler-inside-jsx.expected.tsx
@@ -1,5 +1,5 @@
-///
-import { handler, h, JSONSchema } from "commontools";
+import * as __ctHelpers from "commontools";
+import { handler, h } from "commontools";
interface ClickEvent {
x: number;
y: number;
@@ -23,7 +23,7 @@ export const result = (
}
},
required: ["x", "y"]
- } as const satisfies JSONSchema, {
+ } as const satisfies __ctHelpers.JSONSchema, {
type: "object",
properties: {
clicks: {
@@ -43,8 +43,9 @@ export const result = (
}
},
required: ["clicks", "lastPosition"]
- } as const satisfies JSONSchema, (event: ClickEvent, state: AppState) => ({
+ } as const satisfies __ctHelpers.JSONSchema, (event: ClickEvent, state: AppState) => ({
clicks: state.clicks + 1,
lastPosition: { x: event.x, y: event.y },
}))}
);
+__ctHelpers.NAME; //
diff --git a/packages/ts-transformers/test/fixtures/ast-transform/schema-generation-handler-no-annotations.expected.tsx b/packages/ts-transformers/test/fixtures/ast-transform/schema-generation-handler-no-annotations.expected.tsx
index afaaa72f1..139a4b7cc 100644
--- a/packages/ts-transformers/test/fixtures/ast-transform/schema-generation-handler-no-annotations.expected.tsx
+++ b/packages/ts-transformers/test/fixtures/ast-transform/schema-generation-handler-no-annotations.expected.tsx
@@ -1,6 +1,7 @@
-///
-import { handler, JSONSchema } from "commontools";
+import * as __ctHelpers from "commontools";
+import { handler } from "commontools";
// No type annotations at all - should generate unknown schemas
-export const genericHandler = handler(true as const satisfies JSONSchema, true as const satisfies JSONSchema, (event, state) => {
+export const genericHandler = handler(true as const satisfies __ctHelpers.JSONSchema, true as const satisfies __ctHelpers.JSONSchema, (event, state) => {
console.log("event:", event, "state:", state);
-});
\ No newline at end of file
+});
+__ctHelpers.NAME; //
diff --git a/packages/ts-transformers/test/fixtures/ast-transform/schema-generation-lift-cell-array.expected.tsx b/packages/ts-transformers/test/fixtures/ast-transform/schema-generation-lift-cell-array.expected.tsx
index f865367b2..382d7446f 100644
--- a/packages/ts-transformers/test/fixtures/ast-transform/schema-generation-lift-cell-array.expected.tsx
+++ b/packages/ts-transformers/test/fixtures/ast-transform/schema-generation-lift-cell-array.expected.tsx
@@ -1,5 +1,5 @@
-///
-import { lift, Cell, JSONSchema } from "commontools";
+import * as __ctHelpers from "commontools";
+import { lift, Cell } from "commontools";
interface CharmEntry {
id: string;
name: string;
@@ -33,7 +33,7 @@ const logCharmsList = lift({
required: ["id", "name"]
}
}
-} as const satisfies JSONSchema, {
+} as const satisfies __ctHelpers.JSONSchema, {
$schema: "https://json-schema.org/draft/2020-12/schema",
type: "array",
items: {
@@ -54,8 +54,9 @@ const logCharmsList = lift({
required: ["id", "name"]
}
}
-} as const satisfies JSONSchema, ({ charmsList }) => {
+} as const satisfies __ctHelpers.JSONSchema, ({ charmsList }) => {
console.log("logCharmsList: ", charmsList.get());
return charmsList;
});
-export default logCharmsList;
\ No newline at end of file
+export default logCharmsList;
+__ctHelpers.NAME; //
diff --git a/packages/ts-transformers/test/fixtures/ast-transform/schema-generation-lift-inside-jsx.expected.tsx b/packages/ts-transformers/test/fixtures/ast-transform/schema-generation-lift-inside-jsx.expected.tsx
index 2a6b0a404..0e89bbe8d 100644
--- a/packages/ts-transformers/test/fixtures/ast-transform/schema-generation-lift-inside-jsx.expected.tsx
+++ b/packages/ts-transformers/test/fixtures/ast-transform/schema-generation-lift-inside-jsx.expected.tsx
@@ -1,5 +1,5 @@
-///
-import { lift, h, JSONSchema } from "commontools";
+import * as __ctHelpers from "commontools";
+import { lift, h } from "commontools";
interface Person {
name: string;
age: number;
@@ -21,7 +21,7 @@ export const result = (
}
},
required: ["name", "age"]
- } as const satisfies JSONSchema, {
+ } as const satisfies __ctHelpers.JSONSchema, {
type: "object",
properties: {
name: {
@@ -32,8 +32,9 @@ export const result = (
}
},
required: ["name", "birthYear"]
- } as const satisfies JSONSchema, (person: Person): PersonWithYear => ({
+ } as const satisfies __ctHelpers.JSONSchema, (person: Person): PersonWithYear => ({
name: person.name,
birthYear: currentYear - person.age,
}))}
);
+__ctHelpers.NAME; //
diff --git a/packages/ts-transformers/test/fixtures/ast-transform/schema-generation-lift-no-generics.expected.tsx b/packages/ts-transformers/test/fixtures/ast-transform/schema-generation-lift-no-generics.expected.tsx
index ce98538cf..d950a5500 100644
--- a/packages/ts-transformers/test/fixtures/ast-transform/schema-generation-lift-no-generics.expected.tsx
+++ b/packages/ts-transformers/test/fixtures/ast-transform/schema-generation-lift-no-generics.expected.tsx
@@ -1,5 +1,5 @@
-///
-import { lift, JSONSchema } from "commontools";
+import * as __ctHelpers from "commontools";
+import { lift } from "commontools";
type LiftArgs = {
value: number;
};
@@ -14,7 +14,7 @@ export const doubleValue = lift({
}
},
required: ["value"]
-} as const satisfies JSONSchema, {
+} as const satisfies __ctHelpers.JSONSchema, {
type: "object",
properties: {
doubled: {
@@ -22,6 +22,7 @@ export const doubleValue = lift({
}
},
required: ["doubled"]
-} as const satisfies JSONSchema, (args: LiftArgs): LiftResult => ({
+} as const satisfies __ctHelpers.JSONSchema, (args: LiftArgs): LiftResult => ({
doubled: args.value * 2,
}));
+__ctHelpers.NAME; //
diff --git a/packages/ts-transformers/test/fixtures/ast-transform/schema-generation-lift-untyped.expected.tsx b/packages/ts-transformers/test/fixtures/ast-transform/schema-generation-lift-untyped.expected.tsx
index c1ba73291..fc1c6acdc 100644
--- a/packages/ts-transformers/test/fixtures/ast-transform/schema-generation-lift-untyped.expected.tsx
+++ b/packages/ts-transformers/test/fixtures/ast-transform/schema-generation-lift-untyped.expected.tsx
@@ -1,5 +1,6 @@
-///
-import { lift, JSONSchema } from "commontools";
-export const doubleValue = lift(true as const satisfies JSONSchema, {
+import * as __ctHelpers from "commontools";
+import { lift } from "commontools";
+export const doubleValue = lift(true as const satisfies __ctHelpers.JSONSchema, {
type: "number"
-} as const satisfies JSONSchema, (value) => value * 2);
+} as const satisfies __ctHelpers.JSONSchema, (value) => value * 2);
+__ctHelpers.NAME; //
diff --git a/packages/ts-transformers/test/fixtures/ast-transform/schema-generation-lift.expected.tsx b/packages/ts-transformers/test/fixtures/ast-transform/schema-generation-lift.expected.tsx
index 46411f0bf..4e3c1521b 100644
--- a/packages/ts-transformers/test/fixtures/ast-transform/schema-generation-lift.expected.tsx
+++ b/packages/ts-transformers/test/fixtures/ast-transform/schema-generation-lift.expected.tsx
@@ -1,5 +1,5 @@
-///
-import { lift, JSONSchema } from "commontools";
+import * as __ctHelpers from "commontools";
+import { lift } from "commontools";
type LiftArgs = {
value: number;
};
@@ -14,7 +14,7 @@ export const doubleValue = lift({
}
},
required: ["value"]
-} as const satisfies JSONSchema, {
+} as const satisfies __ctHelpers.JSONSchema, {
type: "object",
properties: {
doubled: {
@@ -22,6 +22,7 @@ export const doubleValue = lift({
}
},
required: ["doubled"]
-} as const satisfies JSONSchema, ({ value }) => ({
+} as const satisfies __ctHelpers.JSONSchema, ({ value }) => ({
doubled: value * 2,
}));
+__ctHelpers.NAME; //
diff --git a/packages/ts-transformers/test/fixtures/ast-transform/ternary_derive.expected.tsx b/packages/ts-transformers/test/fixtures/ast-transform/ternary_derive.expected.tsx
index 8fbb795c9..7aae15bd5 100644
--- a/packages/ts-transformers/test/fixtures/ast-transform/ternary_derive.expected.tsx
+++ b/packages/ts-transformers/test/fixtures/ast-transform/ternary_derive.expected.tsx
@@ -1,5 +1,5 @@
-///
-import { Cell, Default, derive, h, handler, NAME, Opaque, OpaqueRef, recipe, str, UI, ifElse, JSONSchema } from "commontools";
+import * as __ctHelpers from "commontools";
+import { Cell, Default, derive, h, handler, NAME, Opaque, OpaqueRef, recipe, str, UI, } from "commontools";
interface RecipeState {
value: Default;
}
@@ -12,11 +12,12 @@ export default recipe({
}
},
required: ["value"]
-} as const satisfies JSONSchema, (state) => {
+} as const satisfies __ctHelpers.JSONSchema, (state) => {
return {
[NAME]: "test ternary with derive",
[UI]: (
- {ifElse(derive(state.value, _v1 => _v1 + 1), derive(state.value, _v1 => _v1 + 2), "undefined")}
+ {__ctHelpers.ifElse(__ctHelpers.derive(state.value, _v1 => _v1 + 1), __ctHelpers.derive(state.value, _v1 => _v1 + 2), "undefined")}
),
};
});
+__ctHelpers.NAME; //
diff --git a/packages/ts-transformers/test/fixtures/handler-schema/array-cell-remove-intersection.expected.ts b/packages/ts-transformers/test/fixtures/handler-schema/array-cell-remove-intersection.expected.ts
index af31b6871..2a7ef8f06 100644
--- a/packages/ts-transformers/test/fixtures/handler-schema/array-cell-remove-intersection.expected.ts
+++ b/packages/ts-transformers/test/fixtures/handler-schema/array-cell-remove-intersection.expected.ts
@@ -1,12 +1,12 @@
-///
-import { handler, Cell, JSONSchema } from "commontools";
+import * as __ctHelpers from "commontools";
+import { handler, Cell } from "commontools";
interface Item {
text: string;
}
interface ListState {
items: Cell- ;
}
-const removeItem = handler(true as const satisfies JSONSchema, {
+const removeItem = handler(true as const satisfies __ctHelpers.JSONSchema, {
$schema: "https://json-schema.org/draft/2020-12/schema",
type: "object",
properties: {
@@ -33,7 +33,7 @@ const removeItem = handler(true as const satisfies JSONSchema, {
required: ["text"]
}
}
-} as const satisfies JSONSchema, (_, { items, index }) => {
+} as const satisfies __ctHelpers.JSONSchema, (_, { items, index }) => {
const next = items.get().slice();
if (index >= 0 && index < next.length)
next.splice(index, 1);
@@ -43,7 +43,7 @@ const removeItem = handler(true as const satisfies JSONSchema, {
type ListStateWithIndex = ListState & {
index: number;
};
-const removeItemAlias = handler(true as const satisfies JSONSchema, {
+const removeItemAlias = handler(true as const satisfies __ctHelpers.JSONSchema, {
$schema: "https://json-schema.org/draft/2020-12/schema",
type: "object",
properties: {
@@ -70,10 +70,11 @@ const removeItemAlias = handler(true as const satisfies JSONSchema, {
required: ["text"]
}
}
-} as const satisfies JSONSchema, (_, { items, index }) => {
+} as const satisfies __ctHelpers.JSONSchema, (_, { items, index }) => {
const next = items.get().slice();
if (index >= 0 && index < next.length)
next.splice(index, 1);
items.set(next);
});
export { removeItem, removeItemAlias };
+__ctHelpers.NAME; //
diff --git a/packages/ts-transformers/test/fixtures/handler-schema/complex-nested-types.expected.ts b/packages/ts-transformers/test/fixtures/handler-schema/complex-nested-types.expected.ts
index 446c75ec2..f8a46bb92 100644
--- a/packages/ts-transformers/test/fixtures/handler-schema/complex-nested-types.expected.ts
+++ b/packages/ts-transformers/test/fixtures/handler-schema/complex-nested-types.expected.ts
@@ -1,5 +1,5 @@
-///
-import { handler, Cell, recipe, JSONSchema } from "commontools";
+import * as __ctHelpers from "commontools";
+import { handler, Cell, recipe } from "commontools";
// Updated 2025-09-03: String literal unions now generate correct JSON Schema
// (enum instead of array) due to schema-generator UnionFormatter improvements
interface UserEvent {
@@ -42,7 +42,7 @@ const userHandler = handler({
}
},
required: ["user", "action"]
-} as const satisfies JSONSchema, {
+} as const satisfies __ctHelpers.JSONSchema, {
type: "object",
properties: {
users: {
@@ -74,7 +74,7 @@ const userHandler = handler({
}
},
required: ["users", "lastAction", "count"]
-} as const satisfies JSONSchema, (event, state) => {
+} as const satisfies __ctHelpers.JSONSchema, (event, state) => {
if (event.action === "create") {
state.users.push({
id: Date.now().toString(),
@@ -102,7 +102,7 @@ const updateTags = handler({
}
},
required: ["detail"]
-} as const satisfies JSONSchema, {
+} as const satisfies __ctHelpers.JSONSchema, {
type: "object",
properties: {
tags: {
@@ -114,10 +114,11 @@ const updateTags = handler({
}
},
required: ["tags"]
-} as const satisfies JSONSchema, ({ detail }, state) => {
+} as const satisfies __ctHelpers.JSONSchema, ({ detail }, state) => {
state.tags.set(detail?.tags ?? []);
});
export { userHandler };
export default recipe("complex-nested-types test", () => {
return { userHandler };
});
+__ctHelpers.NAME; //
diff --git a/packages/ts-transformers/test/fixtures/handler-schema/date-and-map-types.expected.ts b/packages/ts-transformers/test/fixtures/handler-schema/date-and-map-types.expected.ts
index d85114c98..9a97e6e9a 100644
--- a/packages/ts-transformers/test/fixtures/handler-schema/date-and-map-types.expected.ts
+++ b/packages/ts-transformers/test/fixtures/handler-schema/date-and-map-types.expected.ts
@@ -1,5 +1,5 @@
-///
-import { handler, JSONSchema } from "commontools";
+import * as __ctHelpers from "commontools";
+import { handler } from "commontools";
interface TimedEvent {
timestamp: Date;
data: Map;
@@ -32,7 +32,7 @@ const timedHandler = handler({
required: ["size"]
}
}
-} as const satisfies JSONSchema, {
+} as const satisfies __ctHelpers.JSONSchema, {
$schema: "https://json-schema.org/draft/2020-12/schema",
type: "object",
properties: {
@@ -56,10 +56,11 @@ const timedHandler = handler({
required: ["size"]
}
}
-} as const satisfies JSONSchema, (event, state) => {
+} as const satisfies __ctHelpers.JSONSchema, (event, state) => {
state.lastUpdate = event.timestamp;
event.data.forEach((value, key) => {
state.history.set(key, new Date());
});
});
export { timedHandler };
+__ctHelpers.NAME; //
diff --git a/packages/ts-transformers/test/fixtures/handler-schema/preserve-explicit-schemas.expected.ts b/packages/ts-transformers/test/fixtures/handler-schema/preserve-explicit-schemas.expected.ts
index 68120dd47..bd71b0165 100644
--- a/packages/ts-transformers/test/fixtures/handler-schema/preserve-explicit-schemas.expected.ts
+++ b/packages/ts-transformers/test/fixtures/handler-schema/preserve-explicit-schemas.expected.ts
@@ -1,4 +1,4 @@
-///
+import * as __ctHelpers from "commontools";
import { handler } from "commontools";
const eventSchema = {
type: "object",
@@ -16,3 +16,4 @@ const logHandler = handler(eventSchema, stateSchema, (event, state) => {
state.log.push(event.message);
});
export { logHandler };
+__ctHelpers.NAME; //
diff --git a/packages/ts-transformers/test/fixtures/handler-schema/simple-handler.expected.ts b/packages/ts-transformers/test/fixtures/handler-schema/simple-handler.expected.ts
index 29a326c2d..2cc088b8b 100644
--- a/packages/ts-transformers/test/fixtures/handler-schema/simple-handler.expected.ts
+++ b/packages/ts-transformers/test/fixtures/handler-schema/simple-handler.expected.ts
@@ -1,5 +1,5 @@
-///
-import { handler, JSONSchema } from "commontools";
+import * as __ctHelpers from "commontools";
+import { handler } from "commontools";
interface CounterEvent {
increment: number;
}
@@ -14,7 +14,7 @@ const myHandler = handler({
}
},
required: ["increment"]
-} as const satisfies JSONSchema, {
+} as const satisfies __ctHelpers.JSONSchema, {
type: "object",
properties: {
value: {
@@ -22,7 +22,8 @@ const myHandler = handler({
}
},
required: ["value"]
-} as const satisfies JSONSchema, (event, state) => {
+} as const satisfies __ctHelpers.JSONSchema, (event, state) => {
state.value = state.value + event.increment;
});
export { myHandler };
+__ctHelpers.NAME; //
diff --git a/packages/ts-transformers/test/fixtures/handler-schema/unsupported-intersection-index.expected.ts b/packages/ts-transformers/test/fixtures/handler-schema/unsupported-intersection-index.expected.ts
index 3df71c58a..4fde3bbd8 100644
--- a/packages/ts-transformers/test/fixtures/handler-schema/unsupported-intersection-index.expected.ts
+++ b/packages/ts-transformers/test/fixtures/handler-schema/unsupported-intersection-index.expected.ts
@@ -1,5 +1,5 @@
-///
-import { handler, Cell, JSONSchema } from "commontools";
+import * as __ctHelpers from "commontools";
+import { handler, Cell } from "commontools";
interface Item {
text: string;
}
@@ -10,12 +10,13 @@ interface ListState {
type Indexed = {
[k: string]: unknown;
};
-const removeItem = handler(true as const satisfies JSONSchema, {
+const removeItem = handler(true as const satisfies __ctHelpers.JSONSchema, {
type: "object",
additionalProperties: true,
$comment: "Unsupported intersection pattern: index signature on constituent"
-} as const satisfies JSONSchema, (_, { items }) => {
+} as const satisfies __ctHelpers.JSONSchema, (_, { items }) => {
// noop
items.get();
});
export { removeItem };
+__ctHelpers.NAME; //
diff --git a/packages/ts-transformers/test/fixtures/jsx-expressions/complex-expressions.expected.tsx b/packages/ts-transformers/test/fixtures/jsx-expressions/complex-expressions.expected.tsx
index b03dcac07..2f59bc9b9 100644
--- a/packages/ts-transformers/test/fixtures/jsx-expressions/complex-expressions.expected.tsx
+++ b/packages/ts-transformers/test/fixtures/jsx-expressions/complex-expressions.expected.tsx
@@ -1,5 +1,5 @@
-///
-import { cell, derive, h, recipe, UI, JSONSchema } from "commontools";
+import * as __ctHelpers from "commontools";
+import { cell, h, recipe, UI } from "commontools";
interface Problem {
price: number;
discount: number;
@@ -19,12 +19,13 @@ export default recipe({
}
},
required: ["price", "discount", "tax"]
-} as const satisfies JSONSchema, ({ price, discount, tax }) => {
+} as const satisfies __ctHelpers.JSONSchema, ({ price, discount, tax }) => {
return {
[UI]: (
Price: {price}
-
Discount: {derive({ price, discount }, ({ price: price, discount: discount }) => price - discount)}
-
With tax: {derive({ price, discount, tax }, ({ price: price, discount: discount, tax: tax }) => (price - discount) * (1 + tax))}
+
Discount: {__ctHelpers.derive({ price, discount }, ({ price: price, discount: discount }) => price - discount)}
+
With tax: {__ctHelpers.derive({ price, discount, tax }, ({ price: price, discount: discount, tax: tax }) => (price - discount) * (1 + tax))}
)
};
});
+__ctHelpers.NAME; //
diff --git a/packages/ts-transformers/test/fixtures/jsx-expressions/complex-expressions.input.tsx b/packages/ts-transformers/test/fixtures/jsx-expressions/complex-expressions.input.tsx
index 708f596c4..f29e6f3f6 100644
--- a/packages/ts-transformers/test/fixtures/jsx-expressions/complex-expressions.input.tsx
+++ b/packages/ts-transformers/test/fixtures/jsx-expressions/complex-expressions.input.tsx
@@ -1,5 +1,5 @@
///
-import { cell, derive, h, recipe, UI } from "commontools";
+import { cell, h, recipe, UI } from "commontools";
interface Problem {
price: number;
diff --git a/packages/ts-transformers/test/fixtures/jsx-expressions/element-access-complex.expected.tsx b/packages/ts-transformers/test/fixtures/jsx-expressions/element-access-complex.expected.tsx
index b9b51089b..791089a67 100644
--- a/packages/ts-transformers/test/fixtures/jsx-expressions/element-access-complex.expected.tsx
+++ b/packages/ts-transformers/test/fixtures/jsx-expressions/element-access-complex.expected.tsx
@@ -1,5 +1,5 @@
-///
-import { h, recipe, UI, derive, ifElse, JSONSchema } from "commontools";
+import * as __ctHelpers from "commontools";
+import { h, recipe, UI, ifElse } from "commontools";
interface State {
matrix: number[][];
row: number;
@@ -106,67 +106,67 @@ export default recipe({
}
},
required: ["matrix", "row", "col", "items", "arr", "a", "b", "indices", "nested", "users", "selectedUser", "selectedScore"]
-} as const satisfies JSONSchema, (state) => {
+} as const satisfies __ctHelpers.JSONSchema, (state) => {
return {
[UI]: (
Nested Element Access
{/* Double indexing into matrix */}
-
Matrix value: {derive({ state_matrix: state.matrix, state_row: state.row, state_col: state.col }, ({ state_matrix: _v1, state_row: _v2, state_col: _v3 }) => _v1[_v2][_v3])}
+
Matrix value: {__ctHelpers.derive({ state_matrix: state.matrix, state_row: state.row, state_col: state.col }, ({ state_matrix: _v1, state_row: _v2, state_col: _v3 }) => _v1[_v2][_v3])}
{/* Triple nested access */}
-
Deep nested: {derive({ state_nested_arrays: state.nested.arrays, state_nested_index: state.nested.index, state_row: state.row }, ({ state_nested_arrays: _v1, state_nested_index: _v2, state_row: _v3 }) => _v1[_v2][_v3])}
+
Deep nested: {__ctHelpers.derive({ state_nested_arrays: state.nested.arrays, state_nested_index: state.nested.index, state_row: state.row }, ({ state_nested_arrays: _v1, state_nested_index: _v2, state_row: _v3 }) => _v1[_v2][_v3])}
Multiple References to Same Array
{/* Same array accessed multiple times with different indices */}
-
First and last: {state.items[0]} and {derive({ state_items: state.items, state_items_length: state.items.length }, ({ state_items: _v1, state_items_length: _v2 }) => _v1[_v2 - 1])}
+
First and last: {state.items[0]} and {__ctHelpers.derive({ state_items: state.items, state_items_length: state.items.length }, ({ state_items: _v1, state_items_length: _v2 }) => _v1[_v2 - 1])}
{/* Array used in computation and access */}
-
Sum of ends: {derive({ state_arr: state.arr, state_arr_length: state.arr.length }, ({ state_arr: _v1, state_arr_length: _v2 }) => _v1[0] + _v1[_v2 - 1])}
+
Sum of ends: {__ctHelpers.derive({ state_arr: state.arr, state_arr_length: state.arr.length }, ({ state_arr: _v1, state_arr_length: _v2 }) => _v1[0] + _v1[_v2 - 1])}
Computed Indices
{/* Index from multiple state values */}
-
Computed index: {derive({ state_arr: state.arr, state_a: state.a, state_b: state.b }, ({ state_arr: _v1, state_a: _v2, state_b: _v3 }) => _v1[_v2 + _v3])}
+
Computed index: {__ctHelpers.derive({ state_arr: state.arr, state_a: state.a, state_b: state.b }, ({ state_arr: _v1, state_a: _v2, state_b: _v3 }) => _v1[_v2 + _v3])}
{/* Index from computation involving array */}
-
Modulo index: {derive({ state_items: state.items, state_row: state.row, state_items_length: state.items.length }, ({ state_items: _v1, state_row: _v2, state_items_length: _v3 }) => _v1[_v2 % _v3])}
+
Modulo index: {__ctHelpers.derive({ state_items: state.items, state_row: state.row, state_items_length: state.items.length }, ({ state_items: _v1, state_row: _v2, state_items_length: _v3 }) => _v1[_v2 % _v3])}
{/* Complex index expression */}
-
Complex: {derive({ state_arr: state.arr, state_a: state.a, state_arr_length: state.arr.length }, ({ state_arr: _v1, state_a: _v2, state_arr_length: _v3 }) => _v1[Math.min(_v2 * 2, _v3 - 1)])}
+
Complex: {__ctHelpers.derive({ state_arr: state.arr, state_a: state.a, state_arr_length: state.arr.length }, ({ state_arr: _v1, state_a: _v2, state_arr_length: _v3 }) => _v1[Math.min(_v2 * 2, _v3 - 1)])}
Chained Element Access
{/* Element access returning array, then accessing that */}
-
User score: {derive({ state_users: state.users, state_selectedUser: state.selectedUser, state_selectedScore: state.selectedScore }, ({ state_users: _v1, state_selectedUser: _v2, state_selectedScore: _v3 }) => _v1[_v2].scores[_v3])}
+
User score: {__ctHelpers.derive({ state_users: state.users, state_selectedUser: state.selectedUser, state_selectedScore: state.selectedScore }, ({ state_users: _v1, state_selectedUser: _v2, state_selectedScore: _v3 }) => _v1[_v2].scores[_v3])}
{/* Using one array element as index for another */}
-
Indirect: {derive({ state_items: state.items, state_indices: state.indices }, ({ state_items: _v1, state_indices: _v2 }) => _v1[_v2[0]])}
+
Indirect: {__ctHelpers.derive({ state_items: state.items, state_indices: state.indices }, ({ state_items: _v1, state_indices: _v2 }) => _v1[_v2[0]])}
{/* Array element used as index for same array */}
-
Self reference: {derive(state.arr, _v1 => _v1[_v1[0]])}
+
Self reference: {__ctHelpers.derive(state.arr, _v1 => _v1[_v1[0]])}
Mixed Property and Element Access
{/* Property access followed by element access with computed index */}
-
Mixed: {derive({ state_nested_arrays: state.nested.arrays, state_nested_index: state.nested.index }, ({ state_nested_arrays: _v1, state_nested_index: _v2 }) => _v1[_v2].length)}
+
Mixed: {__ctHelpers.derive({ state_nested_arrays: state.nested.arrays, state_nested_index: state.nested.index }, ({ state_nested_arrays: _v1, state_nested_index: _v2 }) => _v1[_v2].length)}
{/* Element access followed by property access */}
-
User name length: {derive({ state_users: state.users, state_selectedUser: state.selectedUser }, ({ state_users: _v1, state_selectedUser: _v2 }) => _v1[_v2].name.length)}
+
User name length: {__ctHelpers.derive({ state_users: state.users, state_selectedUser: state.selectedUser }, ({ state_users: _v1, state_selectedUser: _v2 }) => _v1[_v2].name.length)}
Element Access in Conditions
{/* Element access in ternary */}
-
Conditional: {ifElse(derive({ state_arr: state.arr, state_a: state.a }, ({ state_arr: _v1, state_a: _v2 }) => _v1[_v2] > 10), derive({ state_items: state.items, state_b: state.b }, ({ state_items: _v1, state_b: _v2 }) => _v1[_v2]), state.items[0])}
+
Conditional: {__ctHelpers.ifElse(__ctHelpers.derive({ state_arr: state.arr, state_a: state.a }, ({ state_arr: _v1, state_a: _v2 }) => _v1[_v2] > 10), __ctHelpers.derive({ state_items: state.items, state_b: state.b }, ({ state_items: _v1, state_b: _v2 }) => _v1[_v2]), state.items[0])}
{/* Element access in boolean expression */}
-
Has value: {ifElse(derive({ state_matrix: state.matrix, state_row: state.row, state_col: state.col }, ({ state_matrix: _v1, state_row: _v2, state_col: _v3 }) => _v1[_v2][_v3] > 0), "positive", "non-positive")}
+
Has value: {ifElse(__ctHelpers.derive({ state_matrix: state.matrix, state_row: state.row, state_col: state.col }, ({ state_matrix: _v1, state_row: _v2, state_col: _v3 }) => _v1[_v2][_v3] > 0), "positive", "non-positive")}
Element Access with Operators
{/* Element access with arithmetic */}
-
Product: {derive({ state_arr: state.arr, state_a: state.a, state_b: state.b }, ({ state_arr: _v1, state_a: _v2, state_b: _v3 }) => _v1[_v2] * _v1[_v3])}
+
Product: {__ctHelpers.derive({ state_arr: state.arr, state_a: state.a, state_b: state.b }, ({ state_arr: _v1, state_a: _v2, state_b: _v3 }) => _v1[_v2] * _v1[_v3])}
{/* Element access with string concatenation */}
-
Concat: {derive({ state_items: state.items, state_indices: state.indices }, ({ state_items: _v1, state_indices: _v2 }) => _v1[0] + " - " + _v1[_v2[0]])}
+
Concat: {__ctHelpers.derive({ state_items: state.items, state_indices: state.indices }, ({ state_items: _v1, state_indices: _v2 }) => _v1[0] + " - " + _v1[_v2[0]])}
{/* Multiple element accesses in single expression */}
-
Sum: {derive(state.arr, _v1 => _v1[0] + _v1[1] + _v1[2])}
+
Sum: {__ctHelpers.derive(state.arr, _v1 => _v1[0] + _v1[1] + _v1[2])}
),
};
});
-
+__ctHelpers.NAME; //
diff --git a/packages/ts-transformers/test/fixtures/jsx-expressions/element-access-complex.input.tsx b/packages/ts-transformers/test/fixtures/jsx-expressions/element-access-complex.input.tsx
index f5f3b0a35..29b658813 100644
--- a/packages/ts-transformers/test/fixtures/jsx-expressions/element-access-complex.input.tsx
+++ b/packages/ts-transformers/test/fixtures/jsx-expressions/element-access-complex.input.tsx
@@ -1,5 +1,5 @@
///
-import { h, recipe, UI, derive, ifElse, JSONSchema } from "commontools";
+import { h, recipe, UI, ifElse } from "commontools";
interface State {
matrix: number[][];
diff --git a/packages/ts-transformers/test/fixtures/jsx-expressions/element-access-simple.expected.tsx b/packages/ts-transformers/test/fixtures/jsx-expressions/element-access-simple.expected.tsx
index f31801200..0ebbc9454 100644
--- a/packages/ts-transformers/test/fixtures/jsx-expressions/element-access-simple.expected.tsx
+++ b/packages/ts-transformers/test/fixtures/jsx-expressions/element-access-simple.expected.tsx
@@ -1,5 +1,5 @@
-///
-import { h, recipe, UI, derive, ifElse, JSONSchema } from "commontools";
+import * as __ctHelpers from "commontools";
+import { h, recipe, UI } from "commontools";
interface State {
items: string[];
index: number;
@@ -36,18 +36,19 @@ export default recipe({
}
},
required: ["items", "index", "matrix", "row", "col"]
-} as const satisfies JSONSchema, (state) => {
+} as const satisfies __ctHelpers.JSONSchema, (state) => {
return {
[UI]: (
Dynamic Element Access
{/* Basic dynamic index */}
-
Item: {derive({ state_items: state.items, state_index: state.index }, ({ state_items: _v1, state_index: _v2 }) => _v1[_v2])}
+
Item: {__ctHelpers.derive({ state_items: state.items, state_index: state.index }, ({ state_items: _v1, state_index: _v2 }) => _v1[_v2])}
{/* Computed index */}
-
Last: {derive({ state_items: state.items, state_items_length: state.items.length }, ({ state_items: _v1, state_items_length: _v2 }) => _v1[_v2 - 1])}
+
Last: {__ctHelpers.derive({ state_items: state.items, state_items_length: state.items.length }, ({ state_items: _v1, state_items_length: _v2 }) => _v1[_v2 - 1])}
{/* Double indexing */}
-
Matrix: {derive({ state_matrix: state.matrix, state_row: state.row, state_col: state.col }, ({ state_matrix: _v1, state_row: _v2, state_col: _v3 }) => _v1[_v2][_v3])}
+
Matrix: {__ctHelpers.derive({ state_matrix: state.matrix, state_row: state.row, state_col: state.col }, ({ state_matrix: _v1, state_row: _v2, state_col: _v3 }) => _v1[_v2][_v3])}
),
};
});
+__ctHelpers.NAME; //
diff --git a/packages/ts-transformers/test/fixtures/jsx-expressions/element-access-simple.input.tsx b/packages/ts-transformers/test/fixtures/jsx-expressions/element-access-simple.input.tsx
index 14c23b21a..23efcbf5c 100644
--- a/packages/ts-transformers/test/fixtures/jsx-expressions/element-access-simple.input.tsx
+++ b/packages/ts-transformers/test/fixtures/jsx-expressions/element-access-simple.input.tsx
@@ -1,5 +1,5 @@
///
-import { h, recipe, UI, derive, ifElse, JSONSchema } from "commontools";
+import { h, recipe, UI } from "commontools";
interface State {
items: string[];
diff --git a/packages/ts-transformers/test/fixtures/jsx-expressions/jsx-arithmetic-operations.expected.tsx b/packages/ts-transformers/test/fixtures/jsx-expressions/jsx-arithmetic-operations.expected.tsx
index e6639be7f..de60ec183 100644
--- a/packages/ts-transformers/test/fixtures/jsx-expressions/jsx-arithmetic-operations.expected.tsx
+++ b/packages/ts-transformers/test/fixtures/jsx-expressions/jsx-arithmetic-operations.expected.tsx
@@ -1,5 +1,5 @@
-///
-import { h, recipe, UI, derive, JSONSchema } from "commontools";
+import * as __ctHelpers from "commontools";
+import { h, recipe, UI } from "commontools";
interface State {
count: number;
price: number;
@@ -23,25 +23,26 @@ export default recipe({
}
},
required: ["count", "price", "discount", "quantity"]
-} as const satisfies JSONSchema, (state) => {
+} as const satisfies __ctHelpers.JSONSchema, (state) => {
return {
[UI]: (
Basic Arithmetic
-
Count + 1: {derive(state.count, _v1 => _v1 + 1)}
-
Count - 1: {derive(state.count, _v1 => _v1 - 1)}
-
Count * 2: {derive(state.count, _v1 => _v1 * 2)}
-
Price / 2: {derive(state.price, _v1 => _v1 / 2)}
-
Count % 3: {derive(state.count, _v1 => _v1 % 3)}
+
Count + 1: {__ctHelpers.derive(state.count, _v1 => _v1 + 1)}
+
Count - 1: {__ctHelpers.derive(state.count, _v1 => _v1 - 1)}
+
Count * 2: {__ctHelpers.derive(state.count, _v1 => _v1 * 2)}
+
Price / 2: {__ctHelpers.derive(state.price, _v1 => _v1 / 2)}
+
Count % 3: {__ctHelpers.derive(state.count, _v1 => _v1 % 3)}
Complex Expressions
-
Discounted Price: {derive({ state_price: state.price, state_discount: state.discount }, ({ state_price: _v1, state_discount: _v2 }) => _v1 - (_v1 * _v2))}
-
Total: {derive({ state_price: state.price, state_quantity: state.quantity }, ({ state_price: _v1, state_quantity: _v2 }) => _v1 * _v2)}
-
With Tax (8%): {derive({ state_price: state.price, state_quantity: state.quantity }, ({ state_price: _v1, state_quantity: _v2 }) => (_v1 * _v2) * 1.08)}
-
Complex: {derive({ state_count: state.count, state_quantity: state.quantity, state_price: state.price, state_discount: state.discount }, ({ state_count: _v1, state_quantity: _v2, state_price: _v3, state_discount: _v4 }) => (_v1 + _v2) * _v3 - (_v3 * _v4))}
+
Discounted Price: {__ctHelpers.derive({ state_price: state.price, state_discount: state.discount }, ({ state_price: _v1, state_discount: _v2 }) => _v1 - (_v1 * _v2))}
+
Total: {__ctHelpers.derive({ state_price: state.price, state_quantity: state.quantity }, ({ state_price: _v1, state_quantity: _v2 }) => _v1 * _v2)}
+
With Tax (8%): {__ctHelpers.derive({ state_price: state.price, state_quantity: state.quantity }, ({ state_price: _v1, state_quantity: _v2 }) => (_v1 * _v2) * 1.08)}
+
Complex: {__ctHelpers.derive({ state_count: state.count, state_quantity: state.quantity, state_price: state.price, state_discount: state.discount }, ({ state_count: _v1, state_quantity: _v2, state_price: _v3, state_discount: _v4 }) => (_v1 + _v2) * _v3 - (_v3 * _v4))}
Multiple Same Ref
-
Count³: {derive(state.count, _v1 => _v1 * _v1 * _v1)}
-
Price Range: ${derive(state.price, _v1 => _v1 - 10)} - ${derive(state.price, _v1 => _v1 + 10)}
+
Count³: {__ctHelpers.derive(state.count, _v1 => _v1 * _v1 * _v1)}
+
Price Range: ${__ctHelpers.derive(state.price, _v1 => _v1 - 10)} - ${__ctHelpers.derive(state.price, _v1 => _v1 + 10)}
),
};
});
+__ctHelpers.NAME; //
diff --git a/packages/ts-transformers/test/fixtures/jsx-expressions/jsx-complex-mixed.expected.tsx b/packages/ts-transformers/test/fixtures/jsx-expressions/jsx-complex-mixed.expected.tsx
index 5aa4c70df..a78130b6c 100644
--- a/packages/ts-transformers/test/fixtures/jsx-expressions/jsx-complex-mixed.expected.tsx
+++ b/packages/ts-transformers/test/fixtures/jsx-expressions/jsx-complex-mixed.expected.tsx
@@ -1,5 +1,5 @@
-///
-import { h, recipe, UI, derive, ifElse, JSONSchema } from "commontools";
+import * as __ctHelpers from "commontools";
+import { h, recipe, UI } from "commontools";
interface Item {
id: number;
name: string;
@@ -53,40 +53,41 @@ export default recipe({
required: ["id", "name", "price", "active"]
}
}
-} as const satisfies JSONSchema, (state) => {
+} as const satisfies __ctHelpers.JSONSchema, (state) => {
return {
[UI]: (
Array Operations
Total items: {state.items.length}
-
Filtered count: {derive({ state_items: state.items, state_filter: state.filter }, ({ state_items: _v1, state_filter: _v2 }) => _v1.filter(i => i.name.includes(_v2)).length)}
+
Filtered count: {__ctHelpers.derive({ state_items: state.items, state_filter: state.filter }, ({ state_items: _v1, state_filter: _v2 }) => _v1.filter(i => i.name.includes(_v2)).length)}
Array with Complex Expressions
{state.items.map(item => (
{item.name}
- Original: ${item.price}
- - Discounted: ${derive({ item_price: item.price, state_discount: state.discount }, ({ item_price: _v1, state_discount: _v2 }) => (_v1 * (1 - _v2)).toFixed(2))}
- - With tax: ${derive({ item_price: item.price, state_discount: state.discount, state_taxRate: state.taxRate }, ({ item_price: _v1, state_discount: _v2, state_taxRate: _v3 }) => (_v1 * (1 - _v2) * (1 + _v3)).toFixed(2))}
+ - Discounted: ${__ctHelpers.derive({ item_price: item.price, state_discount: state.discount }, ({ item_price: _v1, state_discount: _v2 }) => (_v1 * (1 - _v2)).toFixed(2))}
+ - With tax: ${__ctHelpers.derive({ item_price: item.price, state_discount: state.discount, state_taxRate: state.taxRate }, ({ item_price: _v1, state_discount: _v2, state_taxRate: _v3 }) => (_v1 * (1 - _v2) * (1 + _v3)).toFixed(2))}
))}
Array Methods
Item count: {state.items.length}
-
Active items: {derive(state.items, _v1 => _v1.filter(i => i.active).length)}
+
Active items: {__ctHelpers.derive(state.items, _v1 => _v1.filter(i => i.active).length)}
Simple Operations
-
Discount percent: {derive(state.discount, _v1 => _v1 * 100)}%
-
Tax percent: {derive(state.taxRate, _v1 => _v1 * 100)}%
+
Discount percent: {__ctHelpers.derive(state.discount, _v1 => _v1 * 100)}%
+
Tax percent: {__ctHelpers.derive(state.taxRate, _v1 => _v1 * 100)}%
Array Predicates
-
All active: {ifElse(derive(state.items, _v1 => _v1.every(i => i.active)), "Yes", "No")}
-
Any active: {ifElse(derive(state.items, _v1 => _v1.some(i => i.active)), "Yes", "No")}
-
Has expensive (gt 100): {ifElse(derive(state.items, _v1 => _v1.some(i => i.price > 100)), "Yes", "No")}
+
All active: {__ctHelpers.ifElse(__ctHelpers.derive(state.items, _v1 => _v1.every(i => i.active)), "Yes", "No")}
+
Any active: {__ctHelpers.ifElse(__ctHelpers.derive(state.items, _v1 => _v1.some(i => i.active)), "Yes", "No")}
+
Has expensive (gt 100): {__ctHelpers.ifElse(__ctHelpers.derive(state.items, _v1 => _v1.some(i => i.price > 100)), "Yes", "No")}
Object Operations
-
_v1 > 0)} data-discount={state.discount}>
+
_v1 > 0)} data-discount={state.discount}>
Object attributes
),
};
});
+__ctHelpers.NAME; //
diff --git a/packages/ts-transformers/test/fixtures/jsx-expressions/jsx-conditional-rendering.expected.tsx b/packages/ts-transformers/test/fixtures/jsx-expressions/jsx-conditional-rendering.expected.tsx
index 28ddfb3ad..a0adf5907 100644
--- a/packages/ts-transformers/test/fixtures/jsx-expressions/jsx-conditional-rendering.expected.tsx
+++ b/packages/ts-transformers/test/fixtures/jsx-expressions/jsx-conditional-rendering.expected.tsx
@@ -1,5 +1,5 @@
-///
-import { h, recipe, UI, ifElse, derive, JSONSchema } from "commontools";
+import * as __ctHelpers from "commontools";
+import { h, recipe, UI, ifElse } from "commontools";
interface State {
isActive: boolean;
count: number;
@@ -31,33 +31,34 @@ export default recipe({
}
},
required: ["isActive", "count", "userType", "score", "hasPermission", "isPremium"]
-} as const satisfies JSONSchema, (state) => {
+} as const satisfies __ctHelpers.JSONSchema, (state) => {
return {
[UI]: (
Basic Ternary
-
{ifElse(state.isActive, "Active", "Inactive")}
-
{ifElse(state.hasPermission, "Authorized", "Denied")}
+
{__ctHelpers.ifElse(state.isActive, "Active", "Inactive")}
+
{__ctHelpers.ifElse(state.hasPermission, "Authorized", "Denied")}
Ternary with Comparisons
-
{ifElse(derive(state.count, _v1 => _v1 > 10), "High", "Low")}
-
{ifElse(derive(state.score, _v1 => _v1 >= 90), "A", derive(state.score, _v1 => _v1 >= 80 ? "B" : "C"))}
-
{ifElse(derive(state.count, _v1 => _v1 === 0), "Empty", derive(state.count, _v1 => _v1 === 1 ? "Single" : "Multiple"))}
+
{__ctHelpers.ifElse(__ctHelpers.derive(state.count, _v1 => _v1 > 10), "High", "Low")}
+
{__ctHelpers.ifElse(__ctHelpers.derive(state.score, _v1 => _v1 >= 90), "A", __ctHelpers.derive(state.score, _v1 => _v1 >= 80 ? "B" : "C"))}
+
{__ctHelpers.ifElse(__ctHelpers.derive(state.count, _v1 => _v1 === 0), "Empty", __ctHelpers.derive(state.count, _v1 => _v1 === 1 ? "Single" : "Multiple"))}
Nested Ternary
-
{ifElse(state.isActive, derive(state.isPremium, _v1 => (_v1 ? "Premium Active" : "Regular Active")), "Inactive")}
-
{ifElse(derive(state.userType, _v1 => _v1 === "admin"), "Admin", derive(state.userType, _v1 => _v1 === "user" ? "User" : "Guest"))}
+
{__ctHelpers.ifElse(state.isActive, __ctHelpers.derive(state.isPremium, _v1 => (_v1 ? "Premium Active" : "Regular Active")), "Inactive")}
+
{__ctHelpers.ifElse(__ctHelpers.derive(state.userType, _v1 => _v1 === "admin"), "Admin", __ctHelpers.derive(state.userType, _v1 => _v1 === "user" ? "User" : "Guest"))}
Complex Conditions
-
{ifElse(derive({ state_isActive: state.isActive, state_hasPermission: state.hasPermission }, ({ state_isActive: _v1, state_hasPermission: _v2 }) => _v1 && _v2), "Full Access", "Limited Access")}
-
{ifElse(derive(state.count, _v1 => _v1 > 0 && _v1 < 10), "In Range", "Out of Range")}
-
{ifElse(derive({ state_isPremium: state.isPremium, state_score: state.score }, ({ state_isPremium: _v1, state_score: _v2 }) => _v1 || _v2 > 100), "Premium Features", "Basic Features")}
+
{__ctHelpers.ifElse(__ctHelpers.derive({ state_isActive: state.isActive, state_hasPermission: state.hasPermission }, ({ state_isActive: _v1, state_hasPermission: _v2 }) => _v1 && _v2), "Full Access", "Limited Access")}
+
{__ctHelpers.ifElse(__ctHelpers.derive(state.count, _v1 => _v1 > 0 && _v1 < 10), "In Range", "Out of Range")}
+
{__ctHelpers.ifElse(__ctHelpers.derive({ state_isPremium: state.isPremium, state_score: state.score }, ({ state_isPremium: _v1, state_score: _v2 }) => _v1 || _v2 > 100), "Premium Features", "Basic Features")}
IfElse Component
{ifElse(state.isActive,
User is active with {state.count} items
,
User is inactive
)}
- {ifElse(derive(state.count, _v1 => _v1 > 5),
+ {ifElse(__ctHelpers.derive(state.count, _v1 => _v1 > 5),
Many items: {state.count}
, Few items: {state.count}
)}
),
};
});
+__ctHelpers.NAME; //
diff --git a/packages/ts-transformers/test/fixtures/jsx-expressions/jsx-function-calls.expected.tsx b/packages/ts-transformers/test/fixtures/jsx-expressions/jsx-function-calls.expected.tsx
index 4f804c453..a69f1883c 100644
--- a/packages/ts-transformers/test/fixtures/jsx-expressions/jsx-function-calls.expected.tsx
+++ b/packages/ts-transformers/test/fixtures/jsx-expressions/jsx-function-calls.expected.tsx
@@ -1,5 +1,5 @@
-///
-import { h, recipe, UI, derive, ifElse, JSONSchema } from "commontools";
+import * as __ctHelpers from "commontools";
+import { h, recipe, UI } from "commontools";
interface State {
a: number;
b: number;
@@ -38,44 +38,45 @@ export default recipe({
}
},
required: ["a", "b", "price", "text", "values", "name", "float"]
-} as const satisfies JSONSchema, (state) => {
+} as const satisfies __ctHelpers.JSONSchema, (state) => {
return {
[UI]: (
Math Functions
-
Max: {derive({ state_a: state.a, state_b: state.b }, ({ state_a: _v1, state_b: _v2 }) => Math.max(_v1, _v2))}
-
Min: {derive(state.a, _v1 => Math.min(_v1, 10))}
-
Abs: {derive({ state_a: state.a, state_b: state.b }, ({ state_a: _v1, state_b: _v2 }) => Math.abs(_v1 - _v2))}
-
Round: {derive(state.price, _v1 => Math.round(_v1))}
-
Floor: {derive(state.price, _v1 => Math.floor(_v1))}
-
Ceiling: {derive(state.price, _v1 => Math.ceil(_v1))}
-
Square root: {derive(state.a, _v1 => Math.sqrt(_v1))}
+
Max: {__ctHelpers.derive({ state_a: state.a, state_b: state.b }, ({ state_a: _v1, state_b: _v2 }) => Math.max(_v1, _v2))}
+
Min: {__ctHelpers.derive(state.a, _v1 => Math.min(_v1, 10))}
+
Abs: {__ctHelpers.derive({ state_a: state.a, state_b: state.b }, ({ state_a: _v1, state_b: _v2 }) => Math.abs(_v1 - _v2))}
+
Round: {__ctHelpers.derive(state.price, _v1 => Math.round(_v1))}
+
Floor: {__ctHelpers.derive(state.price, _v1 => Math.floor(_v1))}
+
Ceiling: {__ctHelpers.derive(state.price, _v1 => Math.ceil(_v1))}
+
Square root: {__ctHelpers.derive(state.a, _v1 => Math.sqrt(_v1))}
String Methods as Function Calls
-
Uppercase: {derive(state.name, _v1 => _v1.toUpperCase())}
-
Lowercase: {derive(state.name, _v1 => _v1.toLowerCase())}
-
Substring: {derive(state.text, _v1 => _v1.substring(0, 5))}
-
Replace: {derive(state.text, _v1 => _v1.replace("old", "new"))}
-
Includes: {ifElse(derive(state.text, _v1 => _v1.includes("test")), "Yes", "No")}
-
Starts with: {ifElse(derive(state.name, _v1 => _v1.startsWith("A")), "Yes", "No")}
+
Uppercase: {__ctHelpers.derive(state.name, _v1 => _v1.toUpperCase())}
+
Lowercase: {__ctHelpers.derive(state.name, _v1 => _v1.toLowerCase())}
+
Substring: {__ctHelpers.derive(state.text, _v1 => _v1.substring(0, 5))}
+
Replace: {__ctHelpers.derive(state.text, _v1 => _v1.replace("old", "new"))}
+
Includes: {__ctHelpers.ifElse(__ctHelpers.derive(state.text, _v1 => _v1.includes("test")), "Yes", "No")}
+
Starts with: {__ctHelpers.ifElse(__ctHelpers.derive(state.name, _v1 => _v1.startsWith("A")), "Yes", "No")}
Number Methods
-
To Fixed: {derive(state.price, _v1 => _v1.toFixed(2))}
-
To Precision: {derive(state.price, _v1 => _v1.toPrecision(4))}
+
To Fixed: {__ctHelpers.derive(state.price, _v1 => _v1.toFixed(2))}
+
To Precision: {__ctHelpers.derive(state.price, _v1 => _v1.toPrecision(4))}
Parse Functions
-
Parse Int: {derive(state.float, _v1 => parseInt(_v1))}
-
Parse Float: {derive(state.float, _v1 => parseFloat(_v1))}
+
Parse Int: {__ctHelpers.derive(state.float, _v1 => parseInt(_v1))}
+
Parse Float: {__ctHelpers.derive(state.float, _v1 => parseFloat(_v1))}
Array Method Calls
-
Sum: {derive(state.values, _v1 => _v1.reduce((a, b) => a + b, 0))}
-
Max value: {derive(state.values, _v1 => Math.max(..._v1))}
-
Joined: {derive(state.values, _v1 => _v1.join(", "))}
+
Sum: {__ctHelpers.derive(state.values, _v1 => _v1.reduce((a, b) => a + b, 0))}
+
Max value: {__ctHelpers.derive(state.values, _v1 => Math.max(..._v1))}
+
Joined: {__ctHelpers.derive(state.values, _v1 => _v1.join(", "))}
Complex Function Calls
-
Multiple args: {derive(state.a, _v1 => Math.pow(_v1, 2))}
-
Nested calls: {derive(state.a, _v1 => Math.round(Math.sqrt(_v1)))}
-
Chained calls: {derive(state.name, _v1 => _v1.trim().toUpperCase())}
-
With expressions: {derive({ state_a: state.a, state_b: state.b }, ({ state_a: _v1, state_b: _v2 }) => Math.max(_v1 + 1, _v2 * 2))}
+
Multiple args: {__ctHelpers.derive(state.a, _v1 => Math.pow(_v1, 2))}
+
Nested calls: {__ctHelpers.derive(state.a, _v1 => Math.round(Math.sqrt(_v1)))}
+
Chained calls: {__ctHelpers.derive(state.name, _v1 => _v1.trim().toUpperCase())}
+
With expressions: {__ctHelpers.derive({ state_a: state.a, state_b: state.b }, ({ state_a: _v1, state_b: _v2 }) => Math.max(_v1 + 1, _v2 * 2))}
),
};
});
+__ctHelpers.NAME; //
diff --git a/packages/ts-transformers/test/fixtures/jsx-expressions/jsx-property-access.expected.tsx b/packages/ts-transformers/test/fixtures/jsx-expressions/jsx-property-access.expected.tsx
index 6f561fa8b..1c6a59b66 100644
--- a/packages/ts-transformers/test/fixtures/jsx-expressions/jsx-property-access.expected.tsx
+++ b/packages/ts-transformers/test/fixtures/jsx-expressions/jsx-property-access.expected.tsx
@@ -1,5 +1,5 @@
-///
-import { h, recipe, UI, ifElse, derive, JSONSchema } from "commontools";
+import * as __ctHelpers from "commontools";
+import { h, recipe, UI } from "commontools";
interface User {
name: string;
age: number;
@@ -132,50 +132,51 @@ export default recipe({
required: ["name", "age", "active", "profile"]
}
}
-} as const satisfies JSONSchema, (state) => {
+} as const satisfies __ctHelpers.JSONSchema, (state) => {
return {
[UI]: (
Basic Property Access
{state.user.name}
Age: {state.user.age}
-
Active: {ifElse(state.user.active, "Yes", "No")}
+
Active: {__ctHelpers.ifElse(state.user.active, "Yes", "No")}
Nested Property Access
Bio: {state.user.profile.bio}
Location: {state.user.profile.location}
Theme: {state.user.profile.settings.theme}
-
Notifications: {ifElse(state.user.profile.settings.notifications, "On", "Off")}
+
Notifications: {__ctHelpers.ifElse(state.user.profile.settings.notifications, "On", "Off")}
Property Access with Operations
-
Age + 1: {derive(state.user.age, _v1 => _v1 + 1)}
+
Age + 1: {__ctHelpers.derive(state.user.age, _v1 => _v1 + 1)}
Name length: {state.user.name.length}
-
Uppercase name: {derive(state.user.name, _v1 => _v1.toUpperCase())}
-
Location includes city: {ifElse(derive(state.user.profile.location, _v1 => _v1.includes("City")), "Yes", "No")}
+
Uppercase name: {__ctHelpers.derive(state.user.name, _v1 => _v1.toUpperCase())}
+
Location includes city: {__ctHelpers.ifElse(__ctHelpers.derive(state.user.profile.location, _v1 => _v1.includes("City")), "Yes", "No")}
Array Element Access
-
Item at index: {derive({ state_items: state.items, state_index: state.index }, ({ state_items: _v1, state_index: _v2 }) => _v1[_v2])}
+
Item at index: {__ctHelpers.derive({ state_items: state.items, state_index: state.index }, ({ state_items: _v1, state_index: _v2 }) => _v1[_v2])}
First item: {state.items[0]}
-
Last item: {derive({ state_items: state.items, state_items_length: state.items.length }, ({ state_items: _v1, state_items_length: _v2 }) => _v1[_v2 - 1])}
-
Number at index: {derive({ state_numbers: state.numbers, state_index: state.index }, ({ state_numbers: _v1, state_index: _v2 }) => _v1[_v2])}
+
Last item: {__ctHelpers.derive({ state_items: state.items, state_items_length: state.items.length }, ({ state_items: _v1, state_items_length: _v2 }) => _v1[_v2 - 1])}
+
Number at index: {__ctHelpers.derive({ state_numbers: state.numbers, state_index: state.index }, ({ state_numbers: _v1, state_index: _v2 }) => _v1[_v2])}
Config Access with Styles
_v1 + "px")
+ fontSize: __ctHelpers.derive(state.config.theme.fontSize, _v1 => _v1 + "px")
}}>
Styled text
Theme-aware box
Complex Property Chains
-
{derive({ state_user_name: state.user.name, state_user_profile_location: state.user.profile.location }, ({ state_user_name: _v1, state_user_profile_location: _v2 }) => _v1 + " from " + _v2)}
-
Font size + 2: {derive(state.config.theme.fontSize, _v1 => _v1 + 2)}px
-
Has beta and dark mode: {ifElse(derive({ state_config_features_beta: state.config.features.beta, state_config_features_darkMode: state.config.features.darkMode }, ({ state_config_features_beta: _v1, state_config_features_darkMode: _v2 }) => _v1 && _v2), "Yes", "No")}
+
{__ctHelpers.derive({ state_user_name: state.user.name, state_user_profile_location: state.user.profile.location }, ({ state_user_name: _v1, state_user_profile_location: _v2 }) => _v1 + " from " + _v2)}
+
Font size + 2: {__ctHelpers.derive(state.config.theme.fontSize, _v1 => _v1 + 2)}px
+
Has beta and dark mode: {__ctHelpers.ifElse(__ctHelpers.derive({ state_config_features_beta: state.config.features.beta, state_config_features_darkMode: state.config.features.darkMode }, ({ state_config_features_beta: _v1, state_config_features_darkMode: _v2 }) => _v1 && _v2), "Yes", "No")}
),
};
});
+__ctHelpers.NAME; //
diff --git a/packages/ts-transformers/test/fixtures/jsx-expressions/jsx-string-operations.expected.tsx b/packages/ts-transformers/test/fixtures/jsx-expressions/jsx-string-operations.expected.tsx
index 178b01a32..3c6cc9f8a 100644
--- a/packages/ts-transformers/test/fixtures/jsx-expressions/jsx-string-operations.expected.tsx
+++ b/packages/ts-transformers/test/fixtures/jsx-expressions/jsx-string-operations.expected.tsx
@@ -1,5 +1,5 @@
-///
-import { h, recipe, UI, derive, JSONSchema } from "commontools";
+import * as __ctHelpers from "commontools";
+import { h, recipe, UI } from "commontools";
interface State {
firstName: string;
lastName: string;
@@ -27,29 +27,30 @@ export default recipe({
}
},
required: ["firstName", "lastName", "title", "message", "count"]
-} as const satisfies JSONSchema, (state) => {
+} as const satisfies __ctHelpers.JSONSchema, (state) => {
return {
[UI]: (
String Concatenation
-
{derive({ state_title: state.title, state_firstName: state.firstName, state_lastName: state.lastName }, ({ state_title: _v1, state_firstName: _v2, state_lastName: _v3 }) => _v1 + ": " + _v2 + " " + _v3)}
-
{derive({ state_firstName: state.firstName, state_lastName: state.lastName }, ({ state_firstName: _v1, state_lastName: _v2 }) => _v1 + _v2)}
-
{derive(state.firstName, _v1 => "Hello, " + _v1 + "!")}
+
{__ctHelpers.derive({ state_title: state.title, state_firstName: state.firstName, state_lastName: state.lastName }, ({ state_title: _v1, state_firstName: _v2, state_lastName: _v3 }) => _v1 + ": " + _v2 + " " + _v3)}
+
{__ctHelpers.derive({ state_firstName: state.firstName, state_lastName: state.lastName }, ({ state_firstName: _v1, state_lastName: _v2 }) => _v1 + _v2)}
+
{__ctHelpers.derive(state.firstName, _v1 => "Hello, " + _v1 + "!")}
Template Literals
-
{derive(state.firstName, _v1 => `Welcome, ${_v1}!`)}
-
{derive({ state_firstName: state.firstName, state_lastName: state.lastName }, ({ state_firstName: _v1, state_lastName: _v2 }) => `Full name: ${_v1} ${_v2}`)}
-
{derive({ state_title: state.title, state_firstName: state.firstName, state_lastName: state.lastName }, ({ state_title: _v1, state_firstName: _v2, state_lastName: _v3 }) => `${_v1}: ${_v2} ${_v3}`)}
+
{__ctHelpers.derive(state.firstName, _v1 => `Welcome, ${_v1}!`)}
+
{__ctHelpers.derive({ state_firstName: state.firstName, state_lastName: state.lastName }, ({ state_firstName: _v1, state_lastName: _v2 }) => `Full name: ${_v1} ${_v2}`)}
+
{__ctHelpers.derive({ state_title: state.title, state_firstName: state.firstName, state_lastName: state.lastName }, ({ state_title: _v1, state_firstName: _v2, state_lastName: _v3 }) => `${_v1}: ${_v2} ${_v3}`)}
String Methods
-
Uppercase: {derive(state.firstName, _v1 => _v1.toUpperCase())}
-
Lowercase: {derive(state.title, _v1 => _v1.toLowerCase())}
+
Uppercase: {__ctHelpers.derive(state.firstName, _v1 => _v1.toUpperCase())}
+
Lowercase: {__ctHelpers.derive(state.title, _v1 => _v1.toLowerCase())}
Length: {state.message.length}
-
Substring: {derive(state.message, _v1 => _v1.substring(0, 5))}
+
Substring: {__ctHelpers.derive(state.message, _v1 => _v1.substring(0, 5))}
Mixed String and Number
-
{derive({ state_firstName: state.firstName, state_count: state.count }, ({ state_firstName: _v1, state_count: _v2 }) => _v1 + " has " + _v2 + " items")}
-
{derive({ state_firstName: state.firstName, state_count: state.count }, ({ state_firstName: _v1, state_count: _v2 }) => `${_v1} has ${_v2} items`)}
-
Count as string: {derive(state.count, _v1 => "Count: " + _v1)}
+
{__ctHelpers.derive({ state_firstName: state.firstName, state_count: state.count }, ({ state_firstName: _v1, state_count: _v2 }) => _v1 + " has " + _v2 + " items")}
+
{__ctHelpers.derive({ state_firstName: state.firstName, state_count: state.count }, ({ state_firstName: _v1, state_count: _v2 }) => `${_v1} has ${_v2} items`)}
+
Count as string: {__ctHelpers.derive(state.count, _v1 => "Count: " + _v1)}
),
};
});
+__ctHelpers.NAME; //
diff --git a/packages/ts-transformers/test/fixtures/jsx-expressions/method-chains.expected.tsx b/packages/ts-transformers/test/fixtures/jsx-expressions/method-chains.expected.tsx
index 8d5672328..58100dd53 100644
--- a/packages/ts-transformers/test/fixtures/jsx-expressions/method-chains.expected.tsx
+++ b/packages/ts-transformers/test/fixtures/jsx-expressions/method-chains.expected.tsx
@@ -1,5 +1,5 @@
-///
-import { h, recipe, UI, derive, ifElse, JSONSchema } from "commontools";
+import * as __ctHelpers from "commontools";
+import { h, recipe, UI } from "commontools";
interface State {
text: string;
searchTerm: string;
@@ -102,85 +102,86 @@ export default recipe({
}
},
required: ["text", "searchTerm", "items", "start", "end", "threshold", "factor", "names", "prefix", "prices", "discount", "taxRate", "users", "minAge", "words", "separator"]
-} as const satisfies JSONSchema, (state) => {
+} as const satisfies __ctHelpers.JSONSchema, (state) => {
return {
[UI]: (
Chained String Methods
{/* Simple chain */}
-
Trimmed lower: {derive(state.text, _v1 => _v1.trim().toLowerCase())}
+
Trimmed lower: {__ctHelpers.derive(state.text, _v1 => _v1.trim().toLowerCase())}
{/* Chain with reactive argument */}
-
Contains search: {derive({ state_text: state.text, state_searchTerm: state.searchTerm }, ({ state_text: _v1, state_searchTerm: _v2 }) => _v1.toLowerCase().includes(_v2.toLowerCase()))}
+
Contains search: {__ctHelpers.derive({ state_text: state.text, state_searchTerm: state.searchTerm }, ({ state_text: _v1, state_searchTerm: _v2 }) => _v1.toLowerCase().includes(_v2.toLowerCase()))}
{/* Longer chain */}
-
Processed: {derive(state.text, _v1 => _v1.trim().toLowerCase().replace("old", "new").toUpperCase())}
+
Processed: {__ctHelpers.derive(state.text, _v1 => _v1.trim().toLowerCase().replace("old", "new").toUpperCase())}
Array Method Chains
{/* Filter then length */}
-
Count above threshold: {derive({ state_items: state.items, state_threshold: state.threshold }, ({ state_items: _v1, state_threshold: _v2 }) => _v1.filter(x => x > _v2).length)}
+
Count above threshold: {__ctHelpers.derive({ state_items: state.items, state_threshold: state.threshold }, ({ state_items: _v1, state_threshold: _v2 }) => _v1.filter(x => x > _v2).length)}
{/* Filter then map */}
- {derive({ state_items: state.items, state_threshold: state.threshold }, ({ state_items: _v1, state_threshold: _v2 }) => _v1.filter(x => x > _v2)).map(x => (Value: {derive({ x, state_factor: state.factor }, ({ x: x, state_factor: _v2 }) => x * _v2)} ))}
+ {__ctHelpers.derive({ state_items: state.items, state_threshold: state.threshold }, ({ state_items: _v1, state_threshold: _v2 }) => _v1.filter(x => x > _v2)).map(x => (Value: {__ctHelpers.derive({ x, state_factor: state.factor }, ({ x: x, state_factor: _v2 }) => x * _v2)} ))}
{/* Multiple filters */}
-
Double filter count: {derive({ state_items: state.items, state_start: state.start, state_end: state.end }, ({ state_items: _v1, state_start: _v2, state_end: _v3 }) => _v1.filter(x => x > _v2).filter(x => x < _v3).length)}
+
Double filter count: {__ctHelpers.derive({ state_items: state.items, state_start: state.start, state_end: state.end }, ({ state_items: _v1, state_start: _v2, state_end: _v3 }) => _v1.filter(x => x > _v2).filter(x => x < _v3).length)}
Methods with Reactive Arguments
{/* Slice with reactive indices */}
-
Sliced items: {derive({ state_items: state.items, state_start: state.start, state_end: state.end }, ({ state_items: _v1, state_start: _v2, state_end: _v3 }) => _v1.slice(_v2, _v3).join(", "))}
+
Sliced items: {__ctHelpers.derive({ state_items: state.items, state_start: state.start, state_end: state.end }, ({ state_items: _v1, state_start: _v2, state_end: _v3 }) => _v1.slice(_v2, _v3).join(", "))}
{/* String methods with reactive args */}
-
Starts with: {derive({ state_names: state.names, state_prefix: state.prefix }, ({ state_names: _v1, state_prefix: _v2 }) => _v1.filter(n => n.startsWith(_v2)).join(", "))}
+
Starts with: {__ctHelpers.derive({ state_names: state.names, state_prefix: state.prefix }, ({ state_names: _v1, state_prefix: _v2 }) => _v1.filter(n => n.startsWith(_v2)).join(", "))}
{/* Array find with reactive predicate */}
-
First match: {derive({ state_names: state.names, state_searchTerm: state.searchTerm }, ({ state_names: _v1, state_searchTerm: _v2 }) => _v1.find(n => n.includes(_v2)))}
+
First match: {__ctHelpers.derive({ state_names: state.names, state_searchTerm: state.searchTerm }, ({ state_names: _v1, state_searchTerm: _v2 }) => _v1.find(n => n.includes(_v2)))}
Complex Method Combinations
{/* Map with chained operations inside */}
- {state.names.map(name => ({derive(name, name => name.trim().toLowerCase().replace(" ", "-"))} ))}
+ {state.names.map(name => ({__ctHelpers.derive(name, name => name.trim().toLowerCase().replace(" ", "-"))} ))}
{/* Reduce with reactive accumulator */}
-
Total with discount: {derive({ state_prices: state.prices, state_discount: state.discount }, ({ state_prices: _v1, state_discount: _v2 }) => _v1.reduce((sum, price) => sum + price * (1 - _v2), 0))}
+
Total with discount: {__ctHelpers.derive({ state_prices: state.prices, state_discount: state.discount }, ({ state_prices: _v1, state_discount: _v2 }) => _v1.reduce((sum, price) => sum + price * (1 - _v2), 0))}
{/* Method result used in computation */}
-
Average * factor: {derive({ state_items: state.items, state_items_length: state.items.length, state_factor: state.factor }, ({ state_items: _v1, state_items_length: _v2, state_factor: _v3 }) => (_v1.reduce((a, b) => a + b, 0) / _v2) * _v3)}
+
Average * factor: {__ctHelpers.derive({ state_items: state.items, state_items_length: state.items.length, state_factor: state.factor }, ({ state_items: _v1, state_items_length: _v2, state_factor: _v3 }) => (_v1.reduce((a, b) => a + b, 0) / _v2) * _v3)}
Methods on Computed Values
{/* Method on binary expression result */}
-
Formatted price: {derive({ state_prices: state.prices, state_discount: state.discount }, ({ state_prices: _v1, state_discount: _v2 }) => (_v1[0] * (1 - _v2)).toFixed(2))}
+
Formatted price: {__ctHelpers.derive({ state_prices: state.prices, state_discount: state.discount }, ({ state_prices: _v1, state_discount: _v2 }) => (_v1[0] * (1 - _v2)).toFixed(2))}
{/* Method on conditional result */}
-
Conditional trim: {derive({ state_text: state.text, state_text_length: state.text.length, state_prefix: state.prefix }, ({ state_text: _v1, state_text_length: _v2, state_prefix: _v3 }) => (_v2 > 10 ? _v1 : _v3).trim())}
+
Conditional trim: {__ctHelpers.derive({ state_text: state.text, state_text_length: state.text.length, state_prefix: state.prefix }, ({ state_text: _v1, state_text_length: _v2, state_prefix: _v3 }) => (_v2 > 10 ? _v1 : _v3).trim())}
{/* Method chain on computed value */}
-
Complex: {derive({ state_text: state.text, state_prefix: state.prefix }, ({ state_text: _v1, state_prefix: _v2 }) => (_v1 + " " + _v2).trim().toLowerCase().split(" ").join("-"))}
+
Complex: {__ctHelpers.derive({ state_text: state.text, state_prefix: state.prefix }, ({ state_text: _v1, state_prefix: _v2 }) => (_v1 + " " + _v2).trim().toLowerCase().split(" ").join("-"))}
Array Methods with Complex Predicates
{/* Filter with multiple conditions */}
-
Active adults: {derive({ state_users: state.users, state_minAge: state.minAge }, ({ state_users: _v1, state_minAge: _v2 }) => _v1.filter(u => u.age >= _v2 && u.active).length)}
+
Active adults: {__ctHelpers.derive({ state_users: state.users, state_minAge: state.minAge }, ({ state_users: _v1, state_minAge: _v2 }) => _v1.filter(u => u.age >= _v2 && u.active).length)}
{/* Map with conditional logic */}
- {state.users.map(u => ({ifElse(u.active, derive(u.name, _v1 => _v1.toUpperCase()), derive(u.name, _v1 => _v1.toLowerCase()))} ))}
+ {state.users.map(u => ({__ctHelpers.ifElse(u.active, __ctHelpers.derive(u.name, _v1 => _v1.toUpperCase()), __ctHelpers.derive(u.name, _v1 => _v1.toLowerCase()))} ))}
{/* Some/every with reactive predicates */}
-
Has adults: {ifElse(derive({ state_users: state.users, state_minAge: state.minAge }, ({ state_users: _v1, state_minAge: _v2 }) => _v1.some(u => u.age >= _v2)), "Yes", "No")}
-
All active: {ifElse(derive(state.users, _v1 => _v1.every(u => u.active)), "Yes", "No")}
+
Has adults: {__ctHelpers.ifElse(__ctHelpers.derive({ state_users: state.users, state_minAge: state.minAge }, ({ state_users: _v1, state_minAge: _v2 }) => _v1.some(u => u.age >= _v2)), "Yes", "No")}
+
All active: {__ctHelpers.ifElse(__ctHelpers.derive(state.users, _v1 => _v1.every(u => u.active)), "Yes", "No")}
Method Calls in Expressions
{/* Method result in arithmetic */}
-
Length sum: {derive({ state_text: state.text, state_prefix: state.prefix }, ({ state_text: _v1, state_prefix: _v2 }) => _v1.trim().length + _v2.trim().length)}
+
Length sum: {__ctHelpers.derive({ state_text: state.text, state_prefix: state.prefix }, ({ state_text: _v1, state_prefix: _v2 }) => _v1.trim().length + _v2.trim().length)}
{/* Method result in comparison */}
-
Is long: {ifElse(derive({ state_text: state.text, state_threshold: state.threshold }, ({ state_text: _v1, state_threshold: _v2 }) => _v1.trim().length > _v2), "Yes", "No")}
+
Is long: {__ctHelpers.ifElse(__ctHelpers.derive({ state_text: state.text, state_threshold: state.threshold }, ({ state_text: _v1, state_threshold: _v2 }) => _v1.trim().length > _v2), "Yes", "No")}
{/* Multiple method results combined */}
-
Joined: {derive({ state_words: state.words, state_separator: state.separator }, ({ state_words: _v1, state_separator: _v2 }) => _v1.join(_v2).toUpperCase())}
+
Joined: {__ctHelpers.derive({ state_words: state.words, state_separator: state.separator }, ({ state_words: _v1, state_separator: _v2 }) => _v1.join(_v2).toUpperCase())}
),
};
});
+__ctHelpers.NAME; //
diff --git a/packages/ts-transformers/test/fixtures/jsx-expressions/method-chains.input.tsx b/packages/ts-transformers/test/fixtures/jsx-expressions/method-chains.input.tsx
index 711045632..fd3271a0f 100644
--- a/packages/ts-transformers/test/fixtures/jsx-expressions/method-chains.input.tsx
+++ b/packages/ts-transformers/test/fixtures/jsx-expressions/method-chains.input.tsx
@@ -1,5 +1,5 @@
///
-import { h, recipe, UI, derive, ifElse, JSONSchema } from "commontools";
+import { h, recipe, UI } from "commontools";
interface State {
text: string;
diff --git a/packages/ts-transformers/test/fixtures/jsx-expressions/no-double-derive.expected.tsx b/packages/ts-transformers/test/fixtures/jsx-expressions/no-double-derive.expected.tsx
index 96b036003..dcc682f22 100644
--- a/packages/ts-transformers/test/fixtures/jsx-expressions/no-double-derive.expected.tsx
+++ b/packages/ts-transformers/test/fixtures/jsx-expressions/no-double-derive.expected.tsx
@@ -1,4 +1,4 @@
-///
+import * as __ctHelpers from "commontools";
import { derive, h } from "commontools";
// Test case: User-written derive calls should not be double-wrapped
// This tests that derive(index, (i) => i + 1) doesn't become derive(index, index => derive(index, (i) => i + 1))
@@ -19,4 +19,5 @@ export default function TestComponent({ items, cellRef }) {
{/* Simple property access - should NOT be transformed */}
Direct access: {cellRef.value}
);
-}
\ No newline at end of file
+}
+__ctHelpers.NAME; //
diff --git a/packages/ts-transformers/test/fixtures/jsx-expressions/no-transform-simple-ref.expected.tsx b/packages/ts-transformers/test/fixtures/jsx-expressions/no-transform-simple-ref.expected.tsx
index e83e3b0eb..3ed5bb828 100644
--- a/packages/ts-transformers/test/fixtures/jsx-expressions/no-transform-simple-ref.expected.tsx
+++ b/packages/ts-transformers/test/fixtures/jsx-expressions/no-transform-simple-ref.expected.tsx
@@ -1,4 +1,4 @@
-///
+import * as __ctHelpers from "commontools";
import { recipe, NAME, OpaqueRef, h } from "commontools";
const count: OpaqueRef = {} as any;
const element = {count}
;
@@ -7,3 +7,4 @@ export default recipe("test", (state) => {
[NAME]: "test",
};
});
+__ctHelpers.NAME; //
diff --git a/packages/ts-transformers/test/fixtures/jsx-expressions/opaque-ref-cell-map.expected.tsx b/packages/ts-transformers/test/fixtures/jsx-expressions/opaque-ref-cell-map.expected.tsx
index 71e4bc3ba..fe15ac2a4 100644
--- a/packages/ts-transformers/test/fixtures/jsx-expressions/opaque-ref-cell-map.expected.tsx
+++ b/packages/ts-transformers/test/fixtures/jsx-expressions/opaque-ref-cell-map.expected.tsx
@@ -1,5 +1,5 @@
-///
-import { Cell, cell, createCell, derive, h, handler, ifElse, lift, NAME, navigateTo, recipe, UI, JSONSchema } from "commontools";
+import * as __ctHelpers from "commontools";
+import { Cell, cell, createCell, h, handler, ifElse, lift, NAME, navigateTo, recipe, UI, } from "commontools";
// the simple charm (to which we'll store references within a cell)
const SimpleRecipe = recipe("Simple Recipe", () => ({
[NAME]: "Some Simple Recipe",
@@ -57,7 +57,7 @@ const addCharmAndNavigate = lift({
return undefined;
});
// Create a new SimpleRecipe and add it to the array
-const createSimpleRecipe = handler(true as const satisfies JSONSchema, {
+const createSimpleRecipe = handler(true as const satisfies __ctHelpers.JSONSchema, {
type: "object",
properties: {
cellRef: {
@@ -67,7 +67,7 @@ const createSimpleRecipe = handler(true as const satisfies JSONSchema, {
}
},
required: ["cellRef"]
-} as const satisfies JSONSchema, (_, { cellRef }) => {
+} as const satisfies __ctHelpers.JSONSchema, (_, { cellRef }) => {
// Create isInitialized cell for this charm addition
const isInitialized = cell(false);
// Create the charm
@@ -76,13 +76,13 @@ const createSimpleRecipe = handler(true as const satisfies JSONSchema, {
return addCharmAndNavigate({ charm, cellRef, isInitialized });
});
// Handler to navigate to a specific charm from the list
-const goToCharm = handler(true as const satisfies JSONSchema, {
+const goToCharm = handler(true as const satisfies __ctHelpers.JSONSchema, {
type: "object",
properties: {
charm: true
},
required: ["charm"]
-} as const satisfies JSONSchema, (_, { charm }) => {
+} as const satisfies __ctHelpers.JSONSchema, (_, { charm }) => {
console.log("goToCharm clicked");
return navigateTo(charm);
});
@@ -100,9 +100,9 @@ export default recipe("Charms Launcher", () => {
{ifElse(!cellRef?.length, No charms created yet
,
{cellRef.map((charm: any, index: number) => (
- Go to Charm {derive(index, index => index + 1)}
+ Go to Charm {__ctHelpers.derive(index, index => index + 1)}
- Charm {derive(index, index => index + 1)}: {derive(charm, charm => charm[NAME] || "Unnamed")}
+ Charm {__ctHelpers.derive(index, index => index + 1)}: {__ctHelpers.derive(charm, charm => charm[NAME] || "Unnamed")}
))}
)}
@@ -113,4 +113,4 @@ export default recipe("Charms Launcher", () => {
cellRef,
};
});
-
+__ctHelpers.NAME; //
diff --git a/packages/ts-transformers/test/fixtures/jsx-expressions/opaque-ref-cell-map.input.tsx b/packages/ts-transformers/test/fixtures/jsx-expressions/opaque-ref-cell-map.input.tsx
index 3f35a33ef..bbc2caabb 100644
--- a/packages/ts-transformers/test/fixtures/jsx-expressions/opaque-ref-cell-map.input.tsx
+++ b/packages/ts-transformers/test/fixtures/jsx-expressions/opaque-ref-cell-map.input.tsx
@@ -3,7 +3,6 @@ import {
Cell,
cell,
createCell,
- derive,
h,
handler,
ifElse,
diff --git a/packages/ts-transformers/test/fixtures/jsx-expressions/opaque-ref-operations.expected.tsx b/packages/ts-transformers/test/fixtures/jsx-expressions/opaque-ref-operations.expected.tsx
index 2959e6869..3c7f65890 100644
--- a/packages/ts-transformers/test/fixtures/jsx-expressions/opaque-ref-operations.expected.tsx
+++ b/packages/ts-transformers/test/fixtures/jsx-expressions/opaque-ref-operations.expected.tsx
@@ -1,14 +1,15 @@
-///
-import { cell, h, recipe, UI, derive } from "commontools";
+import * as __ctHelpers from "commontools";
+import { cell, h, recipe, UI } from "commontools";
export default recipe("OpaqueRefOperations", (state) => {
const count = cell(10);
const price = cell(10);
return {
[UI]: (
Count: {count}
-
Next: {derive(count, count => count + 1)}
-
Double: {derive(count, count => count * 2)}
-
Total: {derive(price, price => price * 1.1)}
+
Next: {__ctHelpers.derive(count, count => count + 1)}
+
Double: {__ctHelpers.derive(count, count => count * 2)}
+
Total: {__ctHelpers.derive(price, price => price * 1.1)}
)
};
});
+__ctHelpers.NAME; //
diff --git a/packages/ts-transformers/test/fixtures/jsx-expressions/optional-chain-predicate.expected.tsx b/packages/ts-transformers/test/fixtures/jsx-expressions/optional-chain-predicate.expected.tsx
index a736ec69a..b11bcc68f 100644
--- a/packages/ts-transformers/test/fixtures/jsx-expressions/optional-chain-predicate.expected.tsx
+++ b/packages/ts-transformers/test/fixtures/jsx-expressions/optional-chain-predicate.expected.tsx
@@ -1,11 +1,12 @@
-///
-import { cell, h, recipe, NAME, UI, derive } from "commontools";
+import * as __ctHelpers from "commontools";
+import { cell, h, recipe, NAME, UI } from "commontools";
export default recipe("Optional Chain Predicate", () => {
const items = cell([]);
return {
[NAME]: "Optional chain predicate",
[UI]: (
- {derive(items, items => !items?.length && No items )}
+ {__ctHelpers.derive(items, items => !items?.length && No items )}
),
};
});
+__ctHelpers.NAME; //
diff --git a/packages/ts-transformers/test/fixtures/jsx-expressions/optional-element-access.expected.tsx b/packages/ts-transformers/test/fixtures/jsx-expressions/optional-element-access.expected.tsx
index 6c093b5ab..5b94052fd 100644
--- a/packages/ts-transformers/test/fixtures/jsx-expressions/optional-element-access.expected.tsx
+++ b/packages/ts-transformers/test/fixtures/jsx-expressions/optional-element-access.expected.tsx
@@ -1,11 +1,12 @@
-///
-import { cell, h, recipe, NAME, UI, derive } from "commontools";
+import * as __ctHelpers from "commontools";
+import { cell, h, recipe, NAME, UI } from "commontools";
export default recipe("Optional Element Access", () => {
const list = cell(undefined);
return {
[NAME]: "Optional element access",
[UI]: (
- {derive(list, list => !list?.[0] && No first entry )}
+ {__ctHelpers.derive(list, list => !list?.[0] && No first entry )}
),
};
});
+__ctHelpers.NAME; //
diff --git a/packages/ts-transformers/test/fixtures/jsx-expressions/parent-suppression-edge.expected.tsx b/packages/ts-transformers/test/fixtures/jsx-expressions/parent-suppression-edge.expected.tsx
index 121aca65d..84f65ddec 100644
--- a/packages/ts-transformers/test/fixtures/jsx-expressions/parent-suppression-edge.expected.tsx
+++ b/packages/ts-transformers/test/fixtures/jsx-expressions/parent-suppression-edge.expected.tsx
@@ -1,5 +1,5 @@
-///
-import { h, recipe, UI, derive, ifElse, JSONSchema } from "commontools";
+import * as __ctHelpers from "commontools";
+import { h, recipe, UI } from "commontools";
interface State {
user: {
name: string;
@@ -314,7 +314,7 @@ export default recipe({
}
},
required: ["user", "config", "data", "deeply", "arrays"]
-} as const satisfies JSONSchema, (state) => {
+} as const satisfies __ctHelpers.JSONSchema, (state) => {
return {
[UI]: (
Same Base, Different Properties
@@ -322,10 +322,10 @@ export default recipe({
User info: {state.user.name} (age: {state.user.age}, email: {state.user.email})
{/* String concatenation with multiple property accesses */}
-
Full profile: {derive({ state_user_name: state.user.name, state_user_profile_location: state.user.profile.location, state_user_profile_bio: state.user.profile.bio }, ({ state_user_name: _v1, state_user_profile_location: _v2, state_user_profile_bio: _v3 }) => _v1 + " from " + _v2 + " - " + _v3)}
+
Full profile: {__ctHelpers.derive({ state_user_name: state.user.name, state_user_profile_location: state.user.profile.location, state_user_profile_bio: state.user.profile.bio }, ({ state_user_name: _v1, state_user_profile_location: _v2, state_user_profile_bio: _v3 }) => _v1 + " from " + _v2 + " - " + _v3)}
{/* Arithmetic with multiple properties from same base */}
-
Age calculation: {derive(state.user.age, _v1 => _v1 * 12)} months, or {derive(state.user.age, _v1 => _v1 * 365)} days
+
Age calculation: {__ctHelpers.derive(state.user.age, _v1 => _v1 * 12)} months, or {__ctHelpers.derive(state.user.age, _v1 => _v1 * 365)} days
Deeply Nested Property Chains
{/* Multiple references to deeply nested object */}
@@ -335,7 +335,7 @@ export default recipe({
Typography: Headings in {state.config.theme.fonts.heading}, body in {state.config.theme.fonts.body}, code in {state.config.theme.fonts.mono}
{/* Mixed depth accesses */}
-
Config summary: Dark mode {ifElse(state.config.features.darkMode, "enabled", "disabled")} with {state.config.theme.colors.primary} primary color
+
Config summary: Dark mode {__ctHelpers.ifElse(state.config.features.darkMode, "enabled", "disabled")} with {state.config.theme.colors.primary} primary color
Very Deep Nesting with Multiple References
{/* Accessing different properties at same deep level */}
@@ -356,17 +356,17 @@ export default recipe({
Complex Expressions with Shared Bases
{/* Conditional with multiple property accesses */}
-
Status: {ifElse(state.user.settings.notifications, derive({ state_user_name: state.user.name, state_user_settings_theme: state.user.settings.theme }, ({ state_user_name: _v1, state_user_settings_theme: _v2 }) => _v1 + " has notifications on with " + _v2 + " theme"), derive(state.user.name, _v1 => _v1 + " has notifications off"))}
+
Status: {__ctHelpers.ifElse(state.user.settings.notifications, __ctHelpers.derive({ state_user_name: state.user.name, state_user_settings_theme: state.user.settings.theme }, ({ state_user_name: _v1, state_user_settings_theme: _v2 }) => _v1 + " has notifications on with " + _v2 + " theme"), __ctHelpers.derive(state.user.name, _v1 => _v1 + " has notifications off"))}
{/* Computed expression with shared base */}
-
Spacing calc: {derive({ state_config_theme_spacing_small: state.config.theme.spacing.small, state_config_theme_spacing_medium: state.config.theme.spacing.medium, state_config_theme_spacing_large: state.config.theme.spacing.large }, ({ state_config_theme_spacing_small: _v1, state_config_theme_spacing_medium: _v2, state_config_theme_spacing_large: _v3 }) => _v1 + _v2 + _v3)} total
+
Spacing calc: {__ctHelpers.derive({ state_config_theme_spacing_small: state.config.theme.spacing.small, state_config_theme_spacing_medium: state.config.theme.spacing.medium, state_config_theme_spacing_large: state.config.theme.spacing.large }, ({ state_config_theme_spacing_small: _v1, state_config_theme_spacing_medium: _v2, state_config_theme_spacing_large: _v3 }) => _v1 + _v2 + _v3)} total
{/* Boolean expressions with multiple properties */}
-
Features: {ifElse(derive({ state_config_features_darkMode: state.config.features.darkMode, state_config_features_animations: state.config.features.animations }, ({ state_config_features_darkMode: _v1, state_config_features_animations: _v2 }) => _v1 && _v2), "Full features", "Limited features")}
+
Features: {__ctHelpers.ifElse(__ctHelpers.derive({ state_config_features_darkMode: state.config.features.darkMode, state_config_features_animations: state.config.features.animations }, ({ state_config_features_darkMode: _v1, state_config_features_animations: _v2 }) => _v1 && _v2), "Full features", "Limited features")}
Method Calls on Shared Bases
{/* Multiple method calls on properties from same base */}
-
Formatted: {derive(state.user.name, _v1 => _v1.toUpperCase())} - {derive(state.user.email, _v1 => _v1.toLowerCase())}
+
Formatted: {__ctHelpers.derive(state.user.name, _v1 => _v1.toUpperCase())} - {__ctHelpers.derive(state.user.email, _v1 => _v1.toLowerCase())}
{/* Property access and method calls mixed */}
Profile length: {state.user.profile.bio.length} chars in bio, {state.user.profile.location.length} chars in location
@@ -379,16 +379,16 @@ export default recipe({
Data summary: {state.data.items.length} items with average {state.data.totals.average}
{/* Multiple levels of the same chain */}
-
Nested refs: {state.config.theme.colors.primary} in {state.config.theme.fonts.body} with {ifElse(state.config.features.animations, "animations", "no animations")}
+
Nested refs: {state.config.theme.colors.primary} in {state.config.theme.fonts.body} with {__ctHelpers.ifElse(state.config.features.animations, "animations", "no animations")}
Extreme Parent Suppression Test
{/* Using every level of a deep chain */}
All levels:
- Root: {ifElse(state.deeply, "exists", "missing")},
- Nested: {ifElse(state.deeply.nested, "exists", "missing")},
+ Root: {__ctHelpers.ifElse(state.deeply, "exists", "missing")},
+ Nested: {__ctHelpers.ifElse(state.deeply.nested, "exists", "missing")},
Value: {state.deeply.nested.structure.with.many.levels.value}
),
};
});
-
+__ctHelpers.NAME; //
diff --git a/packages/ts-transformers/test/fixtures/jsx-expressions/parent-suppression-edge.input.tsx b/packages/ts-transformers/test/fixtures/jsx-expressions/parent-suppression-edge.input.tsx
index 95bf627c4..2b566d7c7 100644
--- a/packages/ts-transformers/test/fixtures/jsx-expressions/parent-suppression-edge.input.tsx
+++ b/packages/ts-transformers/test/fixtures/jsx-expressions/parent-suppression-edge.input.tsx
@@ -1,5 +1,5 @@
///
-import { h, recipe, UI, derive, ifElse, JSONSchema } from "commontools";
+import { h, recipe, UI } from "commontools";
interface State {
user: {
diff --git a/packages/ts-transformers/test/fixtures/jsx-expressions/recipe-statements-vs-jsx.expected.tsx b/packages/ts-transformers/test/fixtures/jsx-expressions/recipe-statements-vs-jsx.expected.tsx
index c9458d6bf..6a5a0138c 100644
--- a/packages/ts-transformers/test/fixtures/jsx-expressions/recipe-statements-vs-jsx.expected.tsx
+++ b/packages/ts-transformers/test/fixtures/jsx-expressions/recipe-statements-vs-jsx.expected.tsx
@@ -1,9 +1,9 @@
-///
-import { recipe, UI, NAME, str, handler, h, Cell, derive, ifElse, JSONSchema } from "commontools";
+import * as __ctHelpers from "commontools";
+import { recipe, UI, NAME, str, handler, h, Cell } from "commontools";
interface RecipeState {
value: number;
}
-const increment = handler(true as const satisfies JSONSchema, {
+const increment = handler(true as const satisfies __ctHelpers.JSONSchema, {
type: "object",
properties: {
value: {
@@ -12,12 +12,12 @@ const increment = handler(true as const satisfies JSONSchema, {
}
},
required: ["value"]
-} as const satisfies JSONSchema, (e, state: {
+} as const satisfies __ctHelpers.JSONSchema, (e, state: {
value: Cell;
}) => {
state.value.set(state.value.get() + 1);
});
-const decrement = handler(true as const satisfies JSONSchema, {
+const decrement = handler(true as const satisfies __ctHelpers.JSONSchema, {
type: "object",
properties: {
value: {
@@ -26,7 +26,7 @@ const decrement = handler(true as const satisfies JSONSchema, {
}
},
required: ["value"]
-} as const satisfies JSONSchema, (e, state: {
+} as const satisfies __ctHelpers.JSONSchema, (e, state: {
value: Cell;
}) => {
state.value.set(state.value.get() - 1);
@@ -39,7 +39,7 @@ export default recipe({
}
},
required: ["value"]
-} as const satisfies JSONSchema, (state) => {
+} as const satisfies __ctHelpers.JSONSchema, (state) => {
// These should NOT be transformed (statement context)
const next = state.value + 1;
const previous = state.value - 1;
@@ -58,13 +58,13 @@ export default recipe({
{/* These SHOULD be transformed (JSX expression context) */}
Current: {state.value}
- Next number: {derive(state.value, _v1 => _v1 + 1)}
+ Next number: {__ctHelpers.derive(state.value, _v1 => _v1 + 1)}
- Previous: {derive(state.value, _v1 => _v1 - 1)}
+ Previous: {__ctHelpers.derive(state.value, _v1 => _v1 - 1)}
- Doubled: {derive(state.value, _v1 => _v1 * 2)}
+ Doubled: {__ctHelpers.derive(state.value, _v1 => _v1 * 2)}
- Status: {ifElse(derive(state.value, _v1 => _v1 > 10), "High", "Low")}
+ Status: {__ctHelpers.ifElse(__ctHelpers.derive(state.value, _v1 => _v1 > 10), "High", "Low")}
+
),
@@ -78,3 +78,4 @@ export default recipe({
}
};
});
+__ctHelpers.NAME; //
diff --git a/packages/ts-transformers/test/fixtures/jsx-expressions/recipe-with-cells.expected.tsx b/packages/ts-transformers/test/fixtures/jsx-expressions/recipe-with-cells.expected.tsx
index c7c9415eb..0008e7948 100644
--- a/packages/ts-transformers/test/fixtures/jsx-expressions/recipe-with-cells.expected.tsx
+++ b/packages/ts-transformers/test/fixtures/jsx-expressions/recipe-with-cells.expected.tsx
@@ -1,5 +1,5 @@
-///
-import { h, recipe, UI, derive, JSONSchema } from "commontools";
+import * as __ctHelpers from "commontools";
+import { h, recipe, UI } from "commontools";
export default recipe({
type: "object",
properties: {
@@ -8,13 +8,14 @@ export default recipe({
}
},
required: ["value"]
-} as const satisfies JSONSchema, (cell) => {
+} as const satisfies __ctHelpers.JSONSchema, (cell) => {
return {
[UI]: (
Current value: {cell.value}
-
Next value: {derive(cell.value, _v1 => _v1 + 1)}
-
Double: {derive(cell.value, _v1 => _v1 * 2)}
+
Next value: {__ctHelpers.derive(cell.value, _v1 => _v1 + 1)}
+
Double: {__ctHelpers.derive(cell.value, _v1 => _v1 * 2)}
),
value: cell.value,
};
});
+__ctHelpers.NAME; //
diff --git a/packages/ts-transformers/test/fixtures/schema-transform/nested-default-optional.expected.tsx b/packages/ts-transformers/test/fixtures/schema-transform/nested-default-optional.expected.tsx
index 37d48bc58..61f3b6042 100644
--- a/packages/ts-transformers/test/fixtures/schema-transform/nested-default-optional.expected.tsx
+++ b/packages/ts-transformers/test/fixtures/schema-transform/nested-default-optional.expected.tsx
@@ -1,5 +1,5 @@
-///
-import { type Cell, Default, handler, recipe, JSONSchema } from "commontools";
+import * as __ctHelpers from "commontools";
+import { type Cell, Default, handler, recipe } from "commontools";
interface OptionalBranch {
counter?: number;
label?: string;
@@ -14,7 +14,7 @@ interface NestedOptionalArgs {
// deno-lint-ignore ban-types
state: Default;
}
-const increment = handler(true as const satisfies JSONSchema, {
+const increment = handler(true as const satisfies __ctHelpers.JSONSchema, {
$schema: "https://json-schema.org/draft/2020-12/schema",
type: "object",
properties: {
@@ -53,7 +53,7 @@ const increment = handler(true as const satisfies JSONSchema, {
}
}
}
-} as const satisfies JSONSchema, (_, context: {
+} as const satisfies __ctHelpers.JSONSchema, (_, context: {
state: Cell;
}) => {
const current = context.state.get() ?? {};
@@ -100,9 +100,10 @@ export default recipe({
}
}
}
-} as const satisfies JSONSchema, ({ state }) => {
+} as const satisfies __ctHelpers.JSONSchema, ({ state }) => {
return {
state,
increment: increment({ state }),
};
-});
\ No newline at end of file
+});
+__ctHelpers.NAME; //
diff --git a/packages/ts-transformers/test/fixtures/schema-transform/opaque-ref-map.expected.ts b/packages/ts-transformers/test/fixtures/schema-transform/opaque-ref-map.expected.ts
index 1b4bb3db1..384a95c33 100644
--- a/packages/ts-transformers/test/fixtures/schema-transform/opaque-ref-map.expected.ts
+++ b/packages/ts-transformers/test/fixtures/schema-transform/opaque-ref-map.expected.ts
@@ -1,5 +1,5 @@
-///
-import { OpaqueRef, recipe, JSONSchema } from "commontools";
+import * as __ctHelpers from "commontools";
+import { OpaqueRef, recipe } from "commontools";
interface TodoItem {
title: string;
done: boolean;
@@ -30,7 +30,7 @@ export default recipe({
required: ["title", "done"]
}
}
-} as const satisfies JSONSchema, ({ items }) => {
+} as const satisfies __ctHelpers.JSONSchema, ({ items }) => {
// This should NOT be transformed to items.get().map()
// because OpaqueRef has its own map method
const mapped = items.map((item) => item.title);
@@ -42,3 +42,4 @@ export default recipe({
}));
return { mapped, filtered };
});
+__ctHelpers.NAME; //
diff --git a/packages/ts-transformers/test/fixtures/schema-transform/recipe-with-types.expected.tsx b/packages/ts-transformers/test/fixtures/schema-transform/recipe-with-types.expected.tsx
index 26ba2a874..361108128 100644
--- a/packages/ts-transformers/test/fixtures/schema-transform/recipe-with-types.expected.tsx
+++ b/packages/ts-transformers/test/fixtures/schema-transform/recipe-with-types.expected.tsx
@@ -1,5 +1,5 @@
-///
-import { recipe, h, UI, NAME, Cell, Default, handler, JSONSchema } from "commontools";
+import * as __ctHelpers from "commontools";
+import { recipe, h, UI, NAME, toSchema, Cell, Default, handler } from "commontools";
interface Item {
text: Default;
}
@@ -45,7 +45,7 @@ const inputSchema = {
required: ["text"]
}
}
-} as const satisfies JSONSchema;
+} as const satisfies __ctHelpers.JSONSchema;
const outputSchema = {
$schema: "https://json-schema.org/draft/2020-12/schema",
type: "object",
@@ -78,7 +78,7 @@ const outputSchema = {
required: ["text"]
}
}
-} as const satisfies JSONSchema;
+} as const satisfies __ctHelpers.JSONSchema;
// Handler that logs the message event
const addItem = handler({
type: "object",
@@ -94,7 +94,7 @@ const addItem = handler({
}
},
required: ["detail"]
-} as const satisfies JSONSchema, {
+} as const satisfies __ctHelpers.JSONSchema, {
$schema: "https://json-schema.org/draft/2020-12/schema",
type: "object",
properties: {
@@ -119,7 +119,7 @@ const addItem = handler({
required: ["text"]
}
}
-} as const satisfies JSONSchema, (event: InputEventType, { items }: {
+} as const satisfies __ctHelpers.JSONSchema, (event: InputEventType, { items }: {
items: Cell- ;
}) => {
items.push({ text: event.detail.message });
@@ -142,3 +142,4 @@ export default recipe(inputSchema, outputSchema, ({ title, items }) => {
items_count
};
});
+__ctHelpers.NAME; //
diff --git a/packages/ts-transformers/test/fixtures/schema-transform/with-opaque-ref.expected.tsx b/packages/ts-transformers/test/fixtures/schema-transform/with-opaque-ref.expected.tsx
index 28e7527ab..48629eda3 100644
--- a/packages/ts-transformers/test/fixtures/schema-transform/with-opaque-ref.expected.tsx
+++ b/packages/ts-transformers/test/fixtures/schema-transform/with-opaque-ref.expected.tsx
@@ -1,5 +1,5 @@
-///
-import { Cell, derive, h, recipe, UI, JSONSchema } from "commontools";
+import * as __ctHelpers from "commontools";
+import { Cell, derive, h, recipe, toSchema, UI } from "commontools";
interface State {
value: Cell;
}
@@ -15,11 +15,11 @@ const model = {
default: {
value: 0
}
-} as const satisfies JSONSchema;
+} as const satisfies __ctHelpers.JSONSchema;
export default recipe(model, model, (cell) => {
- const doubled = derive(true as const satisfies JSONSchema, {
+ const doubled = derive(true as const satisfies __ctHelpers.JSONSchema, {
type: "number"
- } as const satisfies JSONSchema, cell.value, (v) => v * 2);
+ } as const satisfies __ctHelpers.JSONSchema, cell.value, (v) => v * 2);
return {
[UI]: (
Value: {cell.value}
@@ -28,3 +28,4 @@ export default recipe(model, model, (cell) => {
value: cell.value,
};
});
+__ctHelpers.NAME; //
diff --git a/packages/ts-transformers/test/fixtures/schema-transform/with-options.expected.ts b/packages/ts-transformers/test/fixtures/schema-transform/with-options.expected.ts
index 60866e9d1..b0eecd8fb 100644
--- a/packages/ts-transformers/test/fixtures/schema-transform/with-options.expected.ts
+++ b/packages/ts-transformers/test/fixtures/schema-transform/with-options.expected.ts
@@ -1,5 +1,5 @@
-///
-import { JSONSchema } from "commontools";
+import * as __ctHelpers from "commontools";
+import { toSchema } from "commontools";
interface Config {
value: number;
}
@@ -15,5 +15,6 @@ const configSchema = {
value: 42
},
description: "Configuration schema"
-} as const satisfies JSONSchema;
+} as const satisfies __ctHelpers.JSONSchema;
export { configSchema };
+__ctHelpers.NAME; //
diff --git a/packages/ts-transformers/test/opaque-ref/map-callbacks.test.ts b/packages/ts-transformers/test/opaque-ref/map-callbacks.test.ts
index 821eda709..0c0a2ea21 100644
--- a/packages/ts-transformers/test/opaque-ref/map-callbacks.test.ts
+++ b/packages/ts-transformers/test/opaque-ref/map-callbacks.test.ts
@@ -46,15 +46,15 @@ describe("OpaqueRef map callbacks", () => {
assertStringIncludes(
output,
- "derive(index, index => index + 1)",
+ "__ctHelpers.derive(index, index => index + 1)",
);
assertStringIncludes(
output,
- 'derive(charm, charm => charm[NAME] || "Unnamed")',
+ '__ctHelpers.derive(charm, charm => charm[NAME] || "Unnamed")',
);
assertStringIncludes(
output,
- "ifElse(derive(state.charms.length, _v1 => !_v1)",
+ "ifElse(__ctHelpers.derive(state.charms.length, _v1 => !_v1)",
);
});
});
diff --git a/packages/ts-transformers/test/opaque-ref/runtime-style.test.ts b/packages/ts-transformers/test/opaque-ref/runtime-style.test.ts
index b25ce86a1..fbb8a8eba 100644
--- a/packages/ts-transformers/test/opaque-ref/runtime-style.test.ts
+++ b/packages/ts-transformers/test/opaque-ref/runtime-style.test.ts
@@ -10,35 +10,6 @@ const commontools = await staticCache.getText("types/commontools.d.ts");
describe("OpaqueRef transformer (runtime-style API)", () => {
const types = { "commontools.d.ts": commontools };
- describe("import management", () => {
- it("adds derive import when needed", async () => {
- const source = `///
-import { OpaqueRef, h } from "commontools";
-const count: OpaqueRef = {} as any;
-const el = {count + 1}
;
-`;
- const transformed = await transformSource(source, { types });
- expect(transformed).toContain(
- 'import { OpaqueRef, h, derive } from "commontools"',
- );
- });
-
- it("does not duplicate existing imports", async () => {
- const source = `///
-import { OpaqueRef, derive, ifElse, h } from "commontools";
-const count: OpaqueRef = {} as any;
-const isActive: OpaqueRef = {} as any;
-const el = {count + 1} {isActive ? 1 : 0}
;
-`;
- const transformed = await transformSource(source, { types });
- const importMatches = transformed.match(/import.*from "commontools"/g);
- expect(importMatches).toHaveLength(1);
- expect(transformed).toContain(
- 'import { OpaqueRef, derive, ifElse, h } from "commontools"',
- );
- });
- });
-
describe("error mode", () => {
it("reports errors instead of transforming", async () => {
const source = `///
diff --git a/packages/ts-transformers/test/transform.test.ts b/packages/ts-transformers/test/transform.test.ts
index 466f2acc3..2cf4777b4 100644
--- a/packages/ts-transformers/test/transform.test.ts
+++ b/packages/ts-transformers/test/transform.test.ts
@@ -22,23 +22,15 @@ describe("CommonToolsTransformerPipeline", () => {
"/main.ts": fixture,
});
assert(
- /toSchema/.test(disabled["/main.ts"]!),
- "no replacements without ",
- );
- assert(
- !/JSONSchema/.test(disabled["/main.ts"]!),
+ !/import \* as __ctHelpers/.test(disabled["/main.ts"]!),
"no replacements without ",
);
const enabled = await transformFiles({
"/main.ts": `/// \n` + fixture,
});
assert(
- !/toSchema/.test(enabled["/main.ts"]!),
- "replacements with ",
- );
- assert(
- /JSONSchema/.test(enabled["/main.ts"]!),
- "replacements with ",
+ /import \* as __ctHelpers/.test(enabled["/main.ts"]!),
+ "no replacements without ",
);
});
});
diff --git a/packages/ts-transformers/test/utils.ts b/packages/ts-transformers/test/utils.ts
index 870e986e2..cfb24d230 100644
--- a/packages/ts-transformers/test/utils.ts
+++ b/packages/ts-transformers/test/utils.ts
@@ -1,7 +1,10 @@
import ts from "typescript";
import { join } from "@std/path";
import { StaticCache } from "@commontools/static";
-import { CommonToolsTransformerPipeline } from "../src/mod.ts";
+import {
+ CommonToolsTransformerPipeline,
+ transformCtDirective,
+} from "../src/mod.ts";
import { assert } from "@std/assert";
const ENV_TYPE_ENTRIES = ["es2023", "dom", "jsx"] as const;
@@ -28,7 +31,7 @@ export async function transformSource(
}
export async function transformFiles(
- files: Record,
+ inFiles: Record,
options: TransformOptions = {},
): Promise> {
const {
@@ -40,6 +43,12 @@ export async function transformFiles(
envTypesCache = await loadEnvironmentTypes();
}
+ // Pretransform
+ const files = Object.entries(inFiles).reduce((files, [key, value]) => {
+ files[key] = transformCtDirective(value);
+ return files;
+ }, {} as Record);
+
const compilerOptions: ts.CompilerOptions = {
target: ts.ScriptTarget.ES2020,
module: ts.ModuleKind.CommonJS,