@opentui/solid 0.1.87 → 0.1.88

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -52,7 +52,9 @@ export function getComponentCatalogue(): {
52
52
  export function extend(objects: any): void;
53
53
  export var effect: typeof createRenderEffect;
54
54
  export var createTextNode: any;
55
+ export function createSolidSlotRegistry(renderer: any, context: any, options?: {}): import("@opentui/core").SlotRegistry<any, object, any>;
55
56
  export function createSlotNode(): SlotRenderable;
57
+ export function createSlot(registry: any, options?: {}): (props: any) => any;
56
58
  export var createElement: any;
57
59
  export function createDynamic(component: any, props: any): import("solid-js").Accessor<any>;
58
60
  declare var createComponent2: typeof createComponent;
@@ -95,6 +97,7 @@ export class SlotRenderable extends SlotBaseRenderable {
95
97
  destroyed: boolean;
96
98
  getSlotChild(parent: any): any;
97
99
  }
100
+ export function Slot(props: any): import("solid-js").Accessor<any>;
98
101
  export var RendererContext: import("solid-js").Context<any>;
99
102
  export function Portal(props: any): SlotRenderable;
100
103
  export class LinkRenderable extends SpanRenderable {
package/index.d.ts CHANGED
@@ -11,8 +11,9 @@ export declare const testRender: (node: () => JSX.Element, renderConfig?: TestRe
11
11
  captureSpans: () => import("@opentui/core").CapturedFrame;
12
12
  resize: (width: number, height: number) => void;
13
13
  }>;
14
- export * from "./src/reconciler";
15
- export * from "./src/elements";
16
- export * from "./src/time-to-first-draw";
17
- export * from "./src/types/elements";
14
+ export * from "./src/reconciler.js";
15
+ export * from "./src/elements/index.js";
16
+ export * from "./src/time-to-first-draw.js";
17
+ export * from "./src/plugins/slot.js";
18
+ export * from "./src/types/elements.js";
18
19
  export { type JSX };
package/index.js CHANGED
@@ -945,6 +945,143 @@ var TimeToFirstDraw = (props) => {
945
945
  return _el$;
946
946
  })();
947
947
  };
948
+ // src/plugins/slot.tsx
949
+ import { createSlotRegistry } from "@opentui/core";
950
+ import { children, createMemo as createMemo3, createSignal as createSignal2, ErrorBoundary, For, onCleanup as onCleanup3, splitProps as splitProps2 } from "solid-js";
951
+ function createSolidSlotRegistry(renderer, context, options = {}) {
952
+ return createSlotRegistry(renderer, "solid:slot-registry", context, options);
953
+ }
954
+ function createSlot(registry, options = {}) {
955
+ return function BoundSlot(props) {
956
+ return createComponent2(Slot, mergeProps3(props, {
957
+ registry,
958
+ get pluginFailurePlaceholder() {
959
+ return options.pluginFailurePlaceholder;
960
+ }
961
+ }));
962
+ };
963
+ }
964
+ function Slot(props) {
965
+ const [local, slotProps] = splitProps2(props, ["registry", "name", "mode", "children", "pluginFailurePlaceholder"]);
966
+ const registry = () => local.registry;
967
+ const pluginFailurePlaceholder = () => local.pluginFailurePlaceholder;
968
+ const [version, setVersion] = createSignal2(0);
969
+ const unsubscribe = registry().subscribe(() => {
970
+ setVersion((current) => current + 1);
971
+ });
972
+ onCleanup3(unsubscribe);
973
+ const entries = createMemo3((previousEntries = []) => {
974
+ version();
975
+ const resolvedEntries = registry().resolveEntries(local.name);
976
+ const previousById = new Map(previousEntries.map((entry) => [entry.id, entry]));
977
+ return resolvedEntries.map((entry) => {
978
+ const previousEntry = previousById.get(entry.id);
979
+ if (previousEntry && previousEntry.renderer === entry.renderer) {
980
+ return previousEntry;
981
+ }
982
+ return entry;
983
+ });
984
+ });
985
+ const entryIds = createMemo3(() => entries().map((entry) => entry.id));
986
+ const entriesById = createMemo3(() => new Map(entries().map((entry) => [entry.id, entry])));
987
+ const fallbackChildren = children(() => local.children);
988
+ const slotName = () => String(local.name);
989
+ const renderPluginFailurePlaceholder = (failure, fallbackValue) => {
990
+ if (!pluginFailurePlaceholder()) {
991
+ return fallbackValue;
992
+ }
993
+ try {
994
+ return pluginFailurePlaceholder()(failure);
995
+ } catch (error) {
996
+ registry().reportPluginError({
997
+ pluginId: failure.pluginId,
998
+ slot: failure.slot ?? slotName(),
999
+ phase: "error_placeholder",
1000
+ source: "solid",
1001
+ error
1002
+ });
1003
+ return fallbackValue;
1004
+ }
1005
+ };
1006
+ const renderEntry = (entry, fallbackOnError) => {
1007
+ const fallbackValue = fallbackOnError ?? null;
1008
+ let initialRender;
1009
+ try {
1010
+ initialRender = entry.renderer(registry().context, slotProps);
1011
+ } catch (error) {
1012
+ const failure = registry().reportPluginError({
1013
+ pluginId: entry.id,
1014
+ slot: slotName(),
1015
+ phase: "render",
1016
+ source: "solid",
1017
+ error
1018
+ });
1019
+ return renderPluginFailurePlaceholder(failure, fallbackValue);
1020
+ }
1021
+ const resolvedInitialRender = children(() => initialRender);
1022
+ const hasInitialOutput = resolvedInitialRender.toArray().some((node) => node !== null && node !== undefined && node !== false);
1023
+ if (!hasInitialOutput) {
1024
+ return fallbackValue;
1025
+ }
1026
+ return createComponent2(ErrorBoundary, {
1027
+ fallback: (error) => {
1028
+ const failure = registry().reportPluginError({
1029
+ pluginId: entry.id,
1030
+ slot: slotName(),
1031
+ phase: "render",
1032
+ source: "solid",
1033
+ error
1034
+ });
1035
+ return renderPluginFailurePlaceholder(failure, fallbackValue);
1036
+ },
1037
+ get children() {
1038
+ return resolvedInitialRender();
1039
+ }
1040
+ });
1041
+ };
1042
+ const AppendEntry = (appendProps) => {
1043
+ const entry = createMemo3(() => entriesById().get(appendProps.entryId));
1044
+ return memo2(() => {
1045
+ const resolvedEntry = entry();
1046
+ if (!resolvedEntry) {
1047
+ return null;
1048
+ }
1049
+ return renderEntry(resolvedEntry);
1050
+ });
1051
+ };
1052
+ const appendView = [memo2(fallbackChildren), createComponent2(For, {
1053
+ get each() {
1054
+ return entryIds();
1055
+ },
1056
+ children: (entryId) => createComponent2(AppendEntry, {
1057
+ entryId
1058
+ })
1059
+ })];
1060
+ return memo2(() => {
1061
+ const resolvedEntries = entries();
1062
+ const mode = local.mode ?? "append";
1063
+ const fallback = fallbackChildren();
1064
+ if (resolvedEntries.length === 0) {
1065
+ return fallback;
1066
+ }
1067
+ if (mode === "single_winner") {
1068
+ const winner = resolvedEntries[0];
1069
+ if (!winner) {
1070
+ return fallback;
1071
+ }
1072
+ return renderEntry(winner, fallback);
1073
+ }
1074
+ if (mode === "replace") {
1075
+ const renderedEntries = resolvedEntries.map((entry) => renderEntry(entry));
1076
+ const hasPluginOutput = renderedEntries.some((entry) => entry !== null && entry !== undefined && entry !== false);
1077
+ if (!hasPluginOutput) {
1078
+ return fallback;
1079
+ }
1080
+ return renderedEntries;
1081
+ }
1082
+ return appendView;
1083
+ });
1084
+ }
948
1085
 
949
1086
  // index.ts
950
1087
  var mountSolidRoot = (renderer, node) => {
@@ -1037,7 +1174,9 @@ export {
1037
1174
  extend,
1038
1175
  effect,
1039
1176
  createTextNode,
1177
+ createSolidSlotRegistry,
1040
1178
  createSlotNode,
1179
+ createSlot,
1041
1180
  createElement,
1042
1181
  createDynamic,
1043
1182
  createComponent2 as createComponent,
@@ -1048,6 +1187,7 @@ export {
1048
1187
  TimeToFirstDraw,
1049
1188
  TextSlotRenderable,
1050
1189
  SlotRenderable,
1190
+ Slot,
1051
1191
  RendererContext,
1052
1192
  Portal,
1053
1193
  LinkRenderable,
package/jsx-runtime.d.ts CHANGED
@@ -14,7 +14,7 @@ import type {
14
14
  TabSelectProps,
15
15
  TextareaProps,
16
16
  TextProps,
17
- } from "./src/types/elements"
17
+ } from "./src/types/elements.js"
18
18
  import type { DomNode } from "./dist"
19
19
 
20
20
  declare namespace JSX {
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "main": "index.js",
5
5
  "types": "index.d.ts",
6
6
  "type": "module",
7
- "version": "0.1.87",
7
+ "version": "0.1.88",
8
8
  "description": "SolidJS renderer for OpenTUI",
9
9
  "license": "MIT",
10
10
  "repository": {
@@ -25,15 +25,19 @@
25
25
  "types": "./scripts/solid-plugin.d.ts",
26
26
  "import": "./scripts/solid-plugin.ts"
27
27
  },
28
+ "./runtime-plugin-support": {
29
+ "types": "./scripts/runtime-plugin-support.d.ts",
30
+ "import": "./scripts/runtime-plugin-support.ts"
31
+ },
28
32
  "./jsx-runtime": "./jsx-runtime.d.ts",
29
33
  "./jsx-dev-runtime": "./jsx-runtime.d.ts"
30
34
  },
31
35
  "dependencies": {
32
36
  "@babel/core": "7.28.0",
33
37
  "@babel/preset-typescript": "7.27.1",
34
- "@opentui/core": "0.1.87",
38
+ "@opentui/core": "0.1.88",
35
39
  "babel-plugin-module-resolver": "5.0.2",
36
- "babel-preset-solid": "1.9.9",
40
+ "babel-preset-solid": "1.9.10",
37
41
  "entities": "7.0.1",
38
42
  "s-js": "^0.4.9"
39
43
  },
@@ -41,9 +45,10 @@
41
45
  "@types/babel__core": "7.20.5",
42
46
  "@types/bun": "latest",
43
47
  "@types/node": "^24.0.0",
48
+ "solid-js": "1.9.11",
44
49
  "typescript": "^5"
45
50
  },
46
51
  "peerDependencies": {
47
- "solid-js": "1.9.9"
52
+ "solid-js": "1.9.11"
48
53
  }
49
54
  }
@@ -1,4 +1,4 @@
1
- import solidTransformPlugin from "./solid-plugin"
1
+ import solidTransformPlugin from "./solid-plugin.js"
2
2
  import { plugin, type BunPlugin } from "bun"
3
3
 
4
4
  plugin(solidTransformPlugin)
@@ -0,0 +1 @@
1
+ export declare function ensureRuntimePluginSupport(): boolean;
@@ -0,0 +1,61 @@
1
+ import { plugin as registerBunPlugin } from "bun"
2
+ import * as coreRuntime from "@opentui/core"
3
+ import {
4
+ createRuntimePlugin,
5
+ isCoreRuntimeModuleSpecifier,
6
+ runtimeModuleIdForSpecifier,
7
+ type RuntimeModuleEntry,
8
+ } from "@opentui/core/runtime-plugin"
9
+ import * as solidJsRuntime from "solid-js"
10
+ import * as solidJsStoreRuntime from "solid-js/store"
11
+ import * as solidRuntime from "../index"
12
+ import { createSolidTransformPlugin } from "./solid-plugin"
13
+
14
+ const runtimePluginSupportInstalledKey = "__opentuiSolidRuntimePluginSupportInstalled__"
15
+
16
+ type RuntimePluginSupportState = typeof globalThis & {
17
+ [runtimePluginSupportInstalledKey]?: boolean
18
+ }
19
+
20
+ const additionalRuntimeModules: Record<string, RuntimeModuleEntry> = {
21
+ "@opentui/solid": solidRuntime as Record<string, unknown>,
22
+ "solid-js": solidJsRuntime as Record<string, unknown>,
23
+ "solid-js/store": solidJsStoreRuntime as Record<string, unknown>,
24
+ }
25
+
26
+ const resolveRuntimeSpecifier = (specifier: string): string | null => {
27
+ if (!isCoreRuntimeModuleSpecifier(specifier) && !additionalRuntimeModules[specifier]) {
28
+ return null
29
+ }
30
+
31
+ return runtimeModuleIdForSpecifier(specifier)
32
+ }
33
+
34
+ export function ensureRuntimePluginSupport(): boolean {
35
+ const state = globalThis as RuntimePluginSupportState
36
+
37
+ if (state[runtimePluginSupportInstalledKey]) {
38
+ return false
39
+ }
40
+
41
+ registerBunPlugin(
42
+ createSolidTransformPlugin({
43
+ moduleName: runtimeModuleIdForSpecifier("@opentui/solid"),
44
+ resolvePath(specifier) {
45
+ return resolveRuntimeSpecifier(specifier)
46
+ },
47
+ }),
48
+ )
49
+
50
+ registerBunPlugin(
51
+ createRuntimePlugin({
52
+ core: coreRuntime as Record<string, unknown>,
53
+ additional: additionalRuntimeModules,
54
+ }),
55
+ )
56
+
57
+ state[runtimePluginSupportInstalledKey] = true
58
+ return true
59
+ }
60
+
61
+ ensureRuntimePluginSupport()
@@ -1,3 +1,9 @@
1
1
  import { type BunPlugin } from "bun";
2
+ export type ResolveImportPath = (specifier: string) => string | null;
3
+ export interface CreateSolidTransformPluginOptions {
4
+ moduleName?: string;
5
+ resolvePath?: ResolveImportPath;
6
+ }
7
+ export declare function createSolidTransformPlugin(input?: CreateSolidTransformPluginOptions): BunPlugin;
2
8
  declare const solidTransformPlugin: BunPlugin;
3
9
  export default solidTransformPlugin;
@@ -3,49 +3,82 @@ import { readFile } from "node:fs/promises"
3
3
  // @ts-expect-error - Types not important.
4
4
  import ts from "@babel/preset-typescript"
5
5
  // @ts-expect-error - Types not important.
6
+ import moduleResolver from "babel-plugin-module-resolver"
7
+ // @ts-expect-error - Types not important.
6
8
  import solid from "babel-preset-solid"
7
9
  import { type BunPlugin } from "bun"
8
10
 
9
- const solidTransformPlugin: BunPlugin = {
10
- name: "bun-plugin-solid",
11
- setup: (build) => {
12
- build.onLoad({ filter: /\/node_modules\/solid-js\/dist\/server\.js$/ }, async (args) => {
13
- const path = args.path.replace("server.js", "solid.js")
14
- const code = await readFile(path, "utf8")
15
- return { contents: code, loader: "js" }
16
- })
17
- build.onLoad({ filter: /\/node_modules\/solid-js\/store\/dist\/server\.js$/ }, async (args) => {
18
- const path = args.path.replace("server.js", "store.js")
19
- const code = await readFile(path, "utf8")
20
- return { contents: code, loader: "js" }
21
- })
22
- build.onLoad({ filter: /\.(js|ts)x$/ }, async (args) => {
23
- const code = await readFile(args.path, "utf8")
24
- const transforms = await transformAsync(code, {
25
- filename: args.path,
26
- // env: {
27
- // development: {
28
- // plugins: [["solid-refresh/babel", { "bundler": "esm" }]],
29
- // },
30
- // },
31
- // plugins: [["solid-refresh/babel", { bundler: "esm" }]],
32
- presets: [
33
- [
34
- solid,
35
- {
36
- moduleName: "@opentui/solid",
37
- generate: "universal",
38
- },
11
+ export type ResolveImportPath = (specifier: string) => string | null
12
+
13
+ export interface CreateSolidTransformPluginOptions {
14
+ moduleName?: string
15
+ resolvePath?: ResolveImportPath
16
+ }
17
+
18
+ export function createSolidTransformPlugin(input: CreateSolidTransformPluginOptions = {}): BunPlugin {
19
+ const moduleName = input.moduleName ?? "@opentui/solid"
20
+ const resolvePath = input.resolvePath
21
+
22
+ return {
23
+ name: "bun-plugin-solid",
24
+ setup: (build) => {
25
+ build.onLoad({ filter: /[\/\\]node_modules[\/\\]solid-js[\/\\]dist[\/\\]server\.js$/ }, async (args) => {
26
+ const path = args.path.replace("server.js", "solid.js")
27
+ const file = Bun.file(path)
28
+ const code = await file.text()
29
+ return { contents: code, loader: "js" }
30
+ })
31
+
32
+ build.onLoad(
33
+ { filter: /[\/\\]node_modules[\/\\]solid-js[\/\\]store[\/\\]dist[\/\\]server\.js$/ },
34
+ async (args) => {
35
+ const path = args.path.replace("server.js", "store.js")
36
+ const file = Bun.file(path)
37
+ const code = await file.text()
38
+ return { contents: code, loader: "js" }
39
+ },
40
+ )
41
+
42
+ build.onLoad({ filter: /\.(js|ts)x$/ }, async (args) => {
43
+ const file = Bun.file(args.path)
44
+ const code = await file.text()
45
+ const plugins = resolvePath
46
+ ? [
47
+ [
48
+ moduleResolver,
49
+ {
50
+ resolvePath(specifier: string) {
51
+ return resolvePath(specifier) ?? specifier
52
+ },
53
+ },
54
+ ],
55
+ ]
56
+ : []
57
+
58
+ const transforms = await transformAsync(code, {
59
+ filename: args.path,
60
+ plugins,
61
+ presets: [
62
+ [
63
+ solid,
64
+ {
65
+ moduleName,
66
+ generate: "universal",
67
+ },
68
+ ],
69
+ [ts],
39
70
  ],
40
- [ts],
41
- ],
71
+ })
72
+
73
+ return {
74
+ contents: transforms?.code ?? "",
75
+ loader: "js",
76
+ }
42
77
  })
43
- return {
44
- contents: transforms?.code ?? "",
45
- loader: "js",
46
- }
47
- })
48
- },
78
+ },
79
+ }
49
80
  }
50
81
 
82
+ const solidTransformPlugin = createSolidTransformPlugin()
83
+
51
84
  export default solidTransformPlugin
@@ -1,4 +1,4 @@
1
- import { type DomNode } from "../reconciler";
1
+ import { type DomNode } from "../reconciler.js";
2
2
  import type { JSX } from "../../jsx-runtime";
3
3
  import type { ValidComponent, ComponentProps } from "solid-js";
4
4
  /**
@@ -1,8 +1,8 @@
1
1
  import { ASCIIFontRenderable, BoxRenderable, CodeRenderable, DiffRenderable, InputRenderable, LineNumberRenderable, MarkdownRenderable, ScrollBoxRenderable, SelectRenderable, TabSelectRenderable, TextareaRenderable, TextNodeRenderable, TextRenderable, type RenderContext, type TextNodeOptions } from "@opentui/core";
2
- import type { RenderableConstructor } from "../types/elements";
3
- export * from "./hooks";
4
- export * from "./extras";
5
- export * from "./slot";
2
+ import type { RenderableConstructor } from "../types/elements.js";
3
+ export * from "./hooks.js";
4
+ export * from "./extras.js";
5
+ export * from "./slot.js";
6
6
  declare class SpanRenderable extends TextNodeRenderable {
7
7
  private readonly _ctx;
8
8
  constructor(_ctx: RenderContext | null, options: TextNodeOptions);
@@ -69,4 +69,4 @@ export declare const componentCatalogue: ComponentCatalogue;
69
69
  */
70
70
  export declare function extend<T extends ComponentCatalogue>(objects: T): void;
71
71
  export declare function getComponentCatalogue(): ComponentCatalogue;
72
- export type { ExtendedComponentProps, ExtendedIntrinsicElements, RenderableConstructor } from "../types/elements";
72
+ export type { ExtendedComponentProps, ExtendedIntrinsicElements, RenderableConstructor } from "../types/elements.js";
@@ -0,0 +1,25 @@
1
+ import { SlotRegistry, type CliRenderer, type Plugin, type PluginContext, type PluginErrorEvent, type SlotMode, type SlotRegistryOptions } from "@opentui/core";
2
+ import { type JSX } from "solid-js";
3
+ export type { SlotMode };
4
+ type SlotMap = Record<string, object>;
5
+ export type SolidPlugin<TSlots extends SlotMap, TContext extends PluginContext = PluginContext> = Plugin<JSX.Element, TSlots, TContext>;
6
+ export type SolidSlotProps<TSlots extends SlotMap, K extends keyof TSlots, TContext extends PluginContext = PluginContext> = {
7
+ registry: SlotRegistry<JSX.Element, TSlots, TContext>;
8
+ name: K;
9
+ mode?: SlotMode;
10
+ children?: JSX.Element;
11
+ pluginFailurePlaceholder?: (failure: PluginErrorEvent) => JSX.Element;
12
+ } & TSlots[K];
13
+ export type SolidBoundSlotProps<TSlots extends SlotMap, K extends keyof TSlots> = {
14
+ name: K;
15
+ mode?: SlotMode;
16
+ children?: JSX.Element;
17
+ } & TSlots[K];
18
+ export type SolidRegistrySlotComponent<TSlots extends SlotMap, TContext extends PluginContext = PluginContext> = <K extends keyof TSlots>(props: SolidSlotProps<TSlots, K, TContext>) => JSX.Element;
19
+ export type SolidSlotComponent<TSlots extends SlotMap> = <K extends keyof TSlots>(props: SolidBoundSlotProps<TSlots, K>) => JSX.Element;
20
+ export interface SolidSlotOptions {
21
+ pluginFailurePlaceholder?: (failure: PluginErrorEvent) => JSX.Element;
22
+ }
23
+ export declare function createSolidSlotRegistry<TSlots extends SlotMap, TContext extends PluginContext = PluginContext>(renderer: CliRenderer, context: TContext, options?: SlotRegistryOptions): SlotRegistry<JSX.Element, TSlots, TContext>;
24
+ export declare function createSlot<TSlots extends SlotMap, TContext extends PluginContext = PluginContext>(registry: SlotRegistry<JSX.Element, TSlots, TContext>, options?: SolidSlotOptions): SolidSlotComponent<TSlots>;
25
+ export declare function Slot<TSlots extends SlotMap, TContext extends PluginContext = PluginContext, K extends keyof TSlots = keyof TSlots>(props: SolidSlotProps<TSlots, K, TContext>): JSX.Element;
@@ -1,5 +1,5 @@
1
1
  import { BaseRenderable } from "@opentui/core";
2
- import { SlotRenderable } from "./elements";
2
+ import { SlotRenderable } from "./elements/index.js";
3
3
  export type DomNode = BaseRenderable;
4
4
  export declare function createSlotNode(): SlotRenderable;
5
5
  export declare const _render: (code: () => BaseRenderable, node: BaseRenderable) => () => void, effect: <T>(fn: (prev?: T) => T, init?: T) => void, memo: <T>(fn: () => T, equal: boolean) => () => T, createComponent: <T>(Comp: (props: T) => BaseRenderable, props: T) => BaseRenderable, createElement: (tag: string) => BaseRenderable, createTextNode: (value: string) => BaseRenderable, insertNode: (parent: BaseRenderable, node: BaseRenderable, anchor?: BaseRenderable | undefined) => void, insert: <T>(parent: any, accessor: T | (() => T), marker?: any | null, initial?: any) => BaseRenderable, spread: <T>(node: any, accessor: (() => T) | T, skipChildren?: boolean) => void, setProp: <T>(node: BaseRenderable, name: string, value: T, prev?: T | undefined) => T, mergeProps: (...sources: unknown[]) => unknown, use: <A, T>(fn: (element: BaseRenderable, arg: A) => T, element: BaseRenderable, arg: A) => T;