@boba-cli/dsl 0.1.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +443 -0
- package/dist/index.cjs +528 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +738 -0
- package/dist/index.d.ts +738 -0
- package/dist/index.js +473 -0
- package/dist/index.js.map +1 -0
- package/package.json +56 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,473 @@
|
|
|
1
|
+
import { Program, batch, KeyMsg, quit } from '@boba-cli/tea';
|
|
2
|
+
import { newBinding, matches } from '@boba-cli/key';
|
|
3
|
+
import { Style } from '@boba-cli/chapstick';
|
|
4
|
+
export { Style } from '@boba-cli/chapstick';
|
|
5
|
+
import { line, SpinnerModel } from '@boba-cli/spinner';
|
|
6
|
+
export { dot, ellipsis, line, meter, miniDot, moon, points, pulse } from '@boba-cli/spinner';
|
|
7
|
+
import { EchoMode, TextInputModel } from '@boba-cli/textinput';
|
|
8
|
+
export { CursorMode, EchoMode } from '@boba-cli/textinput';
|
|
9
|
+
|
|
10
|
+
// src/app-builder.ts
|
|
11
|
+
function render(node) {
|
|
12
|
+
if (typeof node === "string") {
|
|
13
|
+
return node;
|
|
14
|
+
}
|
|
15
|
+
if (isTextNode(node)) {
|
|
16
|
+
return renderTextNode(node);
|
|
17
|
+
}
|
|
18
|
+
if (isLayoutNode(node)) {
|
|
19
|
+
return renderLayoutNode(node);
|
|
20
|
+
}
|
|
21
|
+
if (isComponentView(node)) {
|
|
22
|
+
return node.view;
|
|
23
|
+
}
|
|
24
|
+
return "";
|
|
25
|
+
}
|
|
26
|
+
function isTextNode(node) {
|
|
27
|
+
return typeof node === "object" && "_type" in node && node._type === "text";
|
|
28
|
+
}
|
|
29
|
+
function isLayoutNode(node) {
|
|
30
|
+
return typeof node === "object" && "_type" in node && (node._type === "vstack" || node._type === "hstack");
|
|
31
|
+
}
|
|
32
|
+
function isComponentView(node) {
|
|
33
|
+
return typeof node === "object" && "_type" in node && node._type === "component";
|
|
34
|
+
}
|
|
35
|
+
function renderTextNode(node) {
|
|
36
|
+
let style = new Style();
|
|
37
|
+
if (node._bold) {
|
|
38
|
+
style = style.bold(true);
|
|
39
|
+
}
|
|
40
|
+
if (node._dim && !node._foreground) {
|
|
41
|
+
style = style.foreground("#888888");
|
|
42
|
+
}
|
|
43
|
+
if (node._italic) {
|
|
44
|
+
style = style.italic(true);
|
|
45
|
+
}
|
|
46
|
+
if (node._foreground) {
|
|
47
|
+
style = style.foreground(node._foreground);
|
|
48
|
+
}
|
|
49
|
+
if (node._background) {
|
|
50
|
+
style = style.background(node._background);
|
|
51
|
+
}
|
|
52
|
+
return style.render(node.content);
|
|
53
|
+
}
|
|
54
|
+
function renderLayoutNode(node) {
|
|
55
|
+
const renderedChildren = node.children.map((child) => render(child)).filter((s) => s.length > 0);
|
|
56
|
+
if (node._type === "vstack") {
|
|
57
|
+
const separator2 = node.spacing > 0 ? "\n".repeat(node.spacing + 1) : "\n";
|
|
58
|
+
return renderedChildren.join(separator2);
|
|
59
|
+
}
|
|
60
|
+
const separator = node.spacing > 0 ? " ".repeat(node.spacing) : "";
|
|
61
|
+
return renderedChildren.join(separator);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// src/view/nodes.ts
|
|
65
|
+
function text(content) {
|
|
66
|
+
return createTextNode(content, false, false, false, void 0, void 0);
|
|
67
|
+
}
|
|
68
|
+
function createTextNode(content, bold, dim, italic, foreground, background) {
|
|
69
|
+
return {
|
|
70
|
+
_type: "text",
|
|
71
|
+
content,
|
|
72
|
+
_bold: bold,
|
|
73
|
+
_dim: dim,
|
|
74
|
+
_italic: italic,
|
|
75
|
+
_foreground: foreground,
|
|
76
|
+
_background: background,
|
|
77
|
+
bold() {
|
|
78
|
+
return createTextNode(content, true, dim, italic, foreground, background);
|
|
79
|
+
},
|
|
80
|
+
dim() {
|
|
81
|
+
return createTextNode(content, bold, true, italic, foreground, background);
|
|
82
|
+
},
|
|
83
|
+
italic() {
|
|
84
|
+
return createTextNode(content, bold, dim, true, foreground, background);
|
|
85
|
+
},
|
|
86
|
+
foreground(color) {
|
|
87
|
+
return createTextNode(content, bold, dim, italic, color, background);
|
|
88
|
+
},
|
|
89
|
+
background(color) {
|
|
90
|
+
return createTextNode(content, bold, dim, italic, foreground, color);
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
function vstack(...children) {
|
|
95
|
+
return {
|
|
96
|
+
_type: "vstack",
|
|
97
|
+
children,
|
|
98
|
+
spacing: 0
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
function hstack(...children) {
|
|
102
|
+
return {
|
|
103
|
+
_type: "hstack",
|
|
104
|
+
children,
|
|
105
|
+
spacing: 0
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
function spacer(height = 1) {
|
|
109
|
+
return "\n".repeat(height);
|
|
110
|
+
}
|
|
111
|
+
function divider(char = "\u2500", width = 40) {
|
|
112
|
+
return char.repeat(width);
|
|
113
|
+
}
|
|
114
|
+
function when(condition, node) {
|
|
115
|
+
return condition ? node : "";
|
|
116
|
+
}
|
|
117
|
+
function choose(condition, ifTrue, ifFalse) {
|
|
118
|
+
return condition ? ifTrue : ifFalse;
|
|
119
|
+
}
|
|
120
|
+
function map(items, render2) {
|
|
121
|
+
return items.map(render2);
|
|
122
|
+
}
|
|
123
|
+
function componentView(view) {
|
|
124
|
+
return {
|
|
125
|
+
_type: "component",
|
|
126
|
+
view
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// src/app-builder.ts
|
|
131
|
+
var AppBuilder = class _AppBuilder {
|
|
132
|
+
#initialState;
|
|
133
|
+
#components;
|
|
134
|
+
#keyHandlers;
|
|
135
|
+
#viewFn;
|
|
136
|
+
constructor(initialState, components, keyHandlers, viewFn) {
|
|
137
|
+
this.#initialState = initialState;
|
|
138
|
+
this.#components = components;
|
|
139
|
+
this.#keyHandlers = keyHandlers;
|
|
140
|
+
this.#viewFn = viewFn;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Create a new AppBuilder instance.
|
|
144
|
+
* @internal
|
|
145
|
+
*/
|
|
146
|
+
static create() {
|
|
147
|
+
return new _AppBuilder(void 0, [], [], void 0);
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Set the initial application state.
|
|
151
|
+
*
|
|
152
|
+
* @remarks
|
|
153
|
+
* This should typically be called early in the builder chain. If called after
|
|
154
|
+
* registering key handlers or a view function, those will be preserved but
|
|
155
|
+
* their type information will be updated to reflect the new state type.
|
|
156
|
+
*
|
|
157
|
+
* @example
|
|
158
|
+
* ```typescript
|
|
159
|
+
* createApp()
|
|
160
|
+
* .state({ count: 0, name: 'World' })
|
|
161
|
+
* ```
|
|
162
|
+
*
|
|
163
|
+
* @typeParam S - The application state type
|
|
164
|
+
* @param initial - The initial state object
|
|
165
|
+
* @returns A new {@link AppBuilder} with the state type parameter set
|
|
166
|
+
*
|
|
167
|
+
* @public
|
|
168
|
+
*/
|
|
169
|
+
state(initial) {
|
|
170
|
+
return new _AppBuilder(
|
|
171
|
+
initial,
|
|
172
|
+
this.#components,
|
|
173
|
+
this.#keyHandlers,
|
|
174
|
+
this.#viewFn
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Register a component with a unique key.
|
|
179
|
+
*
|
|
180
|
+
* @remarks
|
|
181
|
+
* Components are TEA models wrapped in a {@link ComponentBuilder} that
|
|
182
|
+
* manages their lifecycle. The component's rendered view is available in
|
|
183
|
+
* the view function via `components[key]`.
|
|
184
|
+
*
|
|
185
|
+
* @example
|
|
186
|
+
* ```typescript
|
|
187
|
+
* createApp()
|
|
188
|
+
* .component('loading', spinner())
|
|
189
|
+
* .component('input', textInput())
|
|
190
|
+
* ```
|
|
191
|
+
*
|
|
192
|
+
* @typeParam K - The component key (string literal type)
|
|
193
|
+
* @typeParam M - The component model type
|
|
194
|
+
* @param key - Unique identifier for this component
|
|
195
|
+
* @param builder - Component builder implementing init/update/view
|
|
196
|
+
* @returns A new {@link AppBuilder} with the component registered
|
|
197
|
+
*
|
|
198
|
+
* @public
|
|
199
|
+
*/
|
|
200
|
+
component(key, builder) {
|
|
201
|
+
const newComponents = [...this.#components, { key, builder }];
|
|
202
|
+
return new _AppBuilder(
|
|
203
|
+
this.#initialState,
|
|
204
|
+
newComponents,
|
|
205
|
+
this.#keyHandlers,
|
|
206
|
+
this.#viewFn
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Register a key handler.
|
|
211
|
+
*
|
|
212
|
+
* @remarks
|
|
213
|
+
* Key handlers receive an {@link EventContext} with the current state and
|
|
214
|
+
* components. Multiple keys can be bound to the same handler by passing an
|
|
215
|
+
* array of key strings. Key strings support modifiers like 'ctrl+c', 'alt+enter'.
|
|
216
|
+
*
|
|
217
|
+
* @example
|
|
218
|
+
* ```typescript
|
|
219
|
+
* createApp()
|
|
220
|
+
* .onKey('q', ({ quit }) => quit())
|
|
221
|
+
* .onKey(['up', 'k'], ({ state, update }) => update({ index: state.index - 1 }))
|
|
222
|
+
* .onKey('ctrl+c', ({ quit }) => quit())
|
|
223
|
+
* ```
|
|
224
|
+
*
|
|
225
|
+
* @param keys - Single key string or array of key strings
|
|
226
|
+
* @param handler - Function to call when any of the keys are pressed
|
|
227
|
+
* @returns A new {@link AppBuilder} with the key handler registered
|
|
228
|
+
*
|
|
229
|
+
* @public
|
|
230
|
+
*/
|
|
231
|
+
onKey(keys, handler) {
|
|
232
|
+
const keyArray = Array.isArray(keys) ? keys : [keys];
|
|
233
|
+
const newHandlers = [...this.#keyHandlers, { keys: keyArray, handler }];
|
|
234
|
+
return new _AppBuilder(this.#initialState, this.#components, newHandlers, this.#viewFn);
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Set the view function.
|
|
238
|
+
*
|
|
239
|
+
* @remarks
|
|
240
|
+
* The view function is called on every render cycle and receives the current
|
|
241
|
+
* state and component views. It should return a {@link ViewNode} tree
|
|
242
|
+
* describing the UI to display.
|
|
243
|
+
*
|
|
244
|
+
* @example
|
|
245
|
+
* ```typescript
|
|
246
|
+
* createApp()
|
|
247
|
+
* .view(({ state, components }) => vstack(
|
|
248
|
+
* text('Hello ' + state.name),
|
|
249
|
+
* components.spinner
|
|
250
|
+
* ))
|
|
251
|
+
* ```
|
|
252
|
+
*
|
|
253
|
+
* @param fn - Function that returns a {@link ViewNode} tree
|
|
254
|
+
* @returns A new {@link AppBuilder} with the view function set
|
|
255
|
+
*
|
|
256
|
+
* @public
|
|
257
|
+
*/
|
|
258
|
+
view(fn) {
|
|
259
|
+
return new _AppBuilder(this.#initialState, this.#components, this.#keyHandlers, fn);
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Build the application.
|
|
263
|
+
*
|
|
264
|
+
* @remarks
|
|
265
|
+
* Finalizes the builder chain and creates an {@link App} instance ready
|
|
266
|
+
* to run. This method must be called after setting a view function via
|
|
267
|
+
* {@link AppBuilder.view}.
|
|
268
|
+
*
|
|
269
|
+
* @throws Error if no view function has been set
|
|
270
|
+
*
|
|
271
|
+
* @returns The built {@link App} ready to run
|
|
272
|
+
*
|
|
273
|
+
* @public
|
|
274
|
+
*/
|
|
275
|
+
build() {
|
|
276
|
+
if (this.#viewFn === void 0) {
|
|
277
|
+
throw new Error("AppBuilder: view() must be called before build()");
|
|
278
|
+
}
|
|
279
|
+
const initialState = this.#initialState;
|
|
280
|
+
const components = this.#components;
|
|
281
|
+
const keyHandlers = this.#keyHandlers;
|
|
282
|
+
const viewFn = this.#viewFn;
|
|
283
|
+
const model = new GeneratedModel(initialState, components, keyHandlers, viewFn);
|
|
284
|
+
return {
|
|
285
|
+
async run() {
|
|
286
|
+
const program = new Program(model);
|
|
287
|
+
const result = await program.run();
|
|
288
|
+
return { state: result.model.getUserState() };
|
|
289
|
+
},
|
|
290
|
+
getModel() {
|
|
291
|
+
return model;
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
function createApp() {
|
|
297
|
+
return AppBuilder.create();
|
|
298
|
+
}
|
|
299
|
+
var GeneratedModel = class _GeneratedModel {
|
|
300
|
+
#userState;
|
|
301
|
+
#componentModels;
|
|
302
|
+
#componentBuilders;
|
|
303
|
+
#keyHandlers;
|
|
304
|
+
#viewFn;
|
|
305
|
+
constructor(userState, components, keyHandlers, viewFn, componentModels) {
|
|
306
|
+
this.#userState = userState;
|
|
307
|
+
this.#keyHandlers = keyHandlers;
|
|
308
|
+
this.#viewFn = viewFn;
|
|
309
|
+
this.#componentBuilders = /* @__PURE__ */ new Map();
|
|
310
|
+
for (const { key, builder } of components) {
|
|
311
|
+
this.#componentBuilders.set(key, builder);
|
|
312
|
+
}
|
|
313
|
+
this.#componentModels = componentModels ?? /* @__PURE__ */ new Map();
|
|
314
|
+
}
|
|
315
|
+
getUserState() {
|
|
316
|
+
return this.#userState;
|
|
317
|
+
}
|
|
318
|
+
init() {
|
|
319
|
+
const cmds = [];
|
|
320
|
+
for (const [key, builder] of this.#componentBuilders) {
|
|
321
|
+
const [model, cmd] = builder.init();
|
|
322
|
+
this.#componentModels.set(key, model);
|
|
323
|
+
if (cmd) {
|
|
324
|
+
cmds.push(cmd);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
return cmds.length > 0 ? batch(...cmds) : null;
|
|
328
|
+
}
|
|
329
|
+
update(msg) {
|
|
330
|
+
if (msg instanceof KeyMsg) {
|
|
331
|
+
for (const { keys, handler } of this.#keyHandlers) {
|
|
332
|
+
const binding = newBinding({ keys });
|
|
333
|
+
if (matches(msg, binding)) {
|
|
334
|
+
let nextUserState = this.#userState;
|
|
335
|
+
let shouldQuit = false;
|
|
336
|
+
const ctx = {
|
|
337
|
+
state: this.#userState,
|
|
338
|
+
components: this.#buildComponentViews(),
|
|
339
|
+
update: (patch) => {
|
|
340
|
+
nextUserState = { ...nextUserState, ...patch };
|
|
341
|
+
},
|
|
342
|
+
setState: (newState) => {
|
|
343
|
+
nextUserState = newState;
|
|
344
|
+
},
|
|
345
|
+
quit: () => {
|
|
346
|
+
shouldQuit = true;
|
|
347
|
+
}
|
|
348
|
+
};
|
|
349
|
+
handler(ctx);
|
|
350
|
+
if (shouldQuit) {
|
|
351
|
+
return [this, quit()];
|
|
352
|
+
}
|
|
353
|
+
if (nextUserState !== this.#userState) {
|
|
354
|
+
return [this.#withUserState(nextUserState), null];
|
|
355
|
+
}
|
|
356
|
+
return [this, null];
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
const cmds = [];
|
|
361
|
+
let anyComponentChanged = false;
|
|
362
|
+
const newComponentModels = new Map(this.#componentModels);
|
|
363
|
+
for (const [key, builder] of this.#componentBuilders) {
|
|
364
|
+
const currentModel = this.#componentModels.get(key);
|
|
365
|
+
if (currentModel === void 0) {
|
|
366
|
+
continue;
|
|
367
|
+
}
|
|
368
|
+
const [nextModel, cmd] = builder.update(currentModel, msg);
|
|
369
|
+
if (nextModel !== currentModel) {
|
|
370
|
+
newComponentModels.set(key, nextModel);
|
|
371
|
+
anyComponentChanged = true;
|
|
372
|
+
}
|
|
373
|
+
if (cmd) {
|
|
374
|
+
cmds.push(cmd);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
if (anyComponentChanged) {
|
|
378
|
+
const next = new _GeneratedModel(
|
|
379
|
+
this.#userState,
|
|
380
|
+
Array.from(this.#componentBuilders.entries()).map(([key, builder]) => ({
|
|
381
|
+
key,
|
|
382
|
+
builder
|
|
383
|
+
})),
|
|
384
|
+
this.#keyHandlers,
|
|
385
|
+
this.#viewFn,
|
|
386
|
+
newComponentModels
|
|
387
|
+
);
|
|
388
|
+
return [next, cmds.length > 0 ? batch(...cmds) : null];
|
|
389
|
+
}
|
|
390
|
+
return [this, cmds.length > 0 ? batch(...cmds) : null];
|
|
391
|
+
}
|
|
392
|
+
view() {
|
|
393
|
+
if (!this.#viewFn) {
|
|
394
|
+
return "";
|
|
395
|
+
}
|
|
396
|
+
const componentViews = this.#buildComponentViews();
|
|
397
|
+
const node = this.#viewFn({
|
|
398
|
+
state: this.#userState,
|
|
399
|
+
components: componentViews
|
|
400
|
+
});
|
|
401
|
+
return render(node);
|
|
402
|
+
}
|
|
403
|
+
#buildComponentViews() {
|
|
404
|
+
const views = {};
|
|
405
|
+
for (const [key, builder] of this.#componentBuilders) {
|
|
406
|
+
const model = this.#componentModels.get(key);
|
|
407
|
+
if (model !== void 0) {
|
|
408
|
+
views[key] = componentView(builder.view(model));
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
return views;
|
|
412
|
+
}
|
|
413
|
+
#withUserState(newUserState) {
|
|
414
|
+
return new _GeneratedModel(
|
|
415
|
+
newUserState,
|
|
416
|
+
Array.from(this.#componentBuilders.entries()).map(([key, builder]) => ({
|
|
417
|
+
key,
|
|
418
|
+
builder
|
|
419
|
+
})),
|
|
420
|
+
this.#keyHandlers,
|
|
421
|
+
this.#viewFn,
|
|
422
|
+
this.#componentModels
|
|
423
|
+
);
|
|
424
|
+
}
|
|
425
|
+
};
|
|
426
|
+
function spinner(options = {}) {
|
|
427
|
+
const spinnerOpts = {
|
|
428
|
+
spinner: options.spinner ?? line,
|
|
429
|
+
style: options.style ?? new Style()
|
|
430
|
+
};
|
|
431
|
+
return {
|
|
432
|
+
init() {
|
|
433
|
+
const model = new SpinnerModel(spinnerOpts);
|
|
434
|
+
return [model, model.tick()];
|
|
435
|
+
},
|
|
436
|
+
update(model, msg) {
|
|
437
|
+
return model.update(msg);
|
|
438
|
+
},
|
|
439
|
+
view(model) {
|
|
440
|
+
return model.view();
|
|
441
|
+
}
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
function textInput(options = {}) {
|
|
445
|
+
const inputOpts = {
|
|
446
|
+
placeholder: options.placeholder ?? "",
|
|
447
|
+
width: options.width,
|
|
448
|
+
echoMode: options.echoMode ?? EchoMode.Normal,
|
|
449
|
+
charLimit: options.charLimit ?? 0,
|
|
450
|
+
prompt: options.prompt ?? "",
|
|
451
|
+
promptStyle: options.promptStyle ?? new Style(),
|
|
452
|
+
textStyle: options.textStyle ?? new Style(),
|
|
453
|
+
placeholderStyle: options.placeholderStyle ?? new Style(),
|
|
454
|
+
validate: options.validate
|
|
455
|
+
};
|
|
456
|
+
return {
|
|
457
|
+
init() {
|
|
458
|
+
const model = TextInputModel.new(inputOpts);
|
|
459
|
+
const [focused, cmd] = model.focus();
|
|
460
|
+
return [focused, cmd];
|
|
461
|
+
},
|
|
462
|
+
update(model, msg) {
|
|
463
|
+
return model.update(msg);
|
|
464
|
+
},
|
|
465
|
+
view(model) {
|
|
466
|
+
return model.view();
|
|
467
|
+
}
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
export { AppBuilder, choose, createApp, divider, hstack, map, render, spacer, spinner, text, textInput, vstack, when };
|
|
472
|
+
//# sourceMappingURL=index.js.map
|
|
473
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/view/renderer.ts","../src/view/nodes.ts","../src/app-builder.ts","../src/components/spinner.ts","../src/components/textinput.ts"],"names":["separator","render","teaQuit","Style"],"mappings":"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","file":"index.js","sourcesContent":["import { Style } from '@boba-cli/chapstick'\nimport type { ViewNode, TextNode, LayoutNode, ComponentView } from '../types.js'\n\n/**\n * Render a view node tree to a string.\n *\n * @remarks\n * Recursively renders a {@link ViewNode} tree into a terminal-ready string.\n * Handles text styling, layout stacking, and component views. This function\n * is typically called internally by the DSL, but can be used directly for\n * testing or custom rendering.\n *\n * @example\n * ```typescript\n * const tree = vstack(\n * text('Hello').bold(),\n * text('World').foreground('#ff79c6')\n * )\n * const output = render(tree)\n * console.log(output)\n * ```\n *\n * @param node - The view node tree to render\n * @returns A string ready to display in the terminal\n *\n * @public\n */\nexport function render(node: ViewNode): string {\n if (typeof node === 'string') {\n return node\n }\n\n if (isTextNode(node)) {\n return renderTextNode(node)\n }\n\n if (isLayoutNode(node)) {\n return renderLayoutNode(node)\n }\n\n if (isComponentView(node)) {\n return node.view\n }\n\n // Should not reach here, but TypeScript doesn't know that\n return ''\n}\n\nfunction isTextNode(node: ViewNode): node is TextNode {\n return typeof node === 'object' && '_type' in node && node._type === 'text'\n}\n\nfunction isLayoutNode(node: ViewNode): node is LayoutNode {\n return (\n typeof node === 'object' &&\n '_type' in node &&\n (node._type === 'vstack' || node._type === 'hstack')\n )\n}\n\nfunction isComponentView(node: ViewNode): node is ComponentView {\n return typeof node === 'object' && '_type' in node && node._type === 'component'\n}\n\nfunction renderTextNode(node: TextNode): string {\n let style = new Style()\n\n if (node._bold) {\n style = style.bold(true)\n }\n // Note: dim is rendered as a darker color when foreground is set\n // If dim and no foreground, use a gray color\n if (node._dim && !node._foreground) {\n style = style.foreground('#888888')\n }\n if (node._italic) {\n style = style.italic(true)\n }\n if (node._foreground) {\n style = style.foreground(node._foreground)\n }\n if (node._background) {\n style = style.background(node._background)\n }\n\n return style.render(node.content)\n}\n\nfunction renderLayoutNode(node: LayoutNode): string {\n const renderedChildren = node.children\n .map((child) => render(child))\n .filter((s) => s.length > 0)\n\n if (node._type === 'vstack') {\n const separator = node.spacing > 0 ? '\\n'.repeat(node.spacing + 1) : '\\n'\n return renderedChildren.join(separator)\n }\n\n // hstack\n const separator = node.spacing > 0 ? ' '.repeat(node.spacing) : ''\n return renderedChildren.join(separator)\n}\n","import type { TextNode, LayoutNode, ViewNode, ComponentView } from '../types.js'\n\n/**\n * Create a text node with chainable style methods.\n *\n * @remarks\n * Text nodes support fluent styling via methods like `bold()`,\n * `dim()`, `italic()`, `foreground()`, and `background()`.\n *\n * @example\n * ```typescript\n * text('Hello').bold().foreground('#ff79c6')\n * text('Warning').dim()\n * ```\n *\n * @param content - The text content to display\n * @returns A new {@link TextNode}\n *\n * @public\n */\nexport function text(content: string): TextNode {\n return createTextNode(content, false, false, false, undefined, undefined)\n}\n\nfunction createTextNode(\n content: string,\n bold: boolean,\n dim: boolean,\n italic: boolean,\n foreground: string | undefined,\n background: string | undefined,\n): TextNode {\n return {\n _type: 'text',\n content,\n _bold: bold,\n _dim: dim,\n _italic: italic,\n _foreground: foreground,\n _background: background,\n bold() {\n return createTextNode(content, true, dim, italic, foreground, background)\n },\n dim() {\n return createTextNode(content, bold, true, italic, foreground, background)\n },\n italic() {\n return createTextNode(content, bold, dim, true, foreground, background)\n },\n foreground(color: string) {\n return createTextNode(content, bold, dim, italic, color, background)\n },\n background(color: string) {\n return createTextNode(content, bold, dim, italic, foreground, color)\n },\n }\n}\n\n/**\n * Create a vertical stack layout.\n *\n * @remarks\n * Arranges child views vertically with newlines between them. Children are\n * rendered in order from top to bottom.\n *\n * @example\n * ```typescript\n * vstack(\n * text('Line 1'),\n * text('Line 2'),\n * text('Line 3')\n * )\n * ```\n *\n * @param children - View nodes to stack vertically\n * @returns A new {@link LayoutNode} with vertical stacking\n *\n * @public\n */\nexport function vstack(...children: ViewNode[]): LayoutNode {\n return {\n _type: 'vstack',\n children,\n spacing: 0,\n }\n}\n\n/**\n * Create a horizontal stack layout.\n *\n * @remarks\n * Arranges child views horizontally on the same line. Children are\n * rendered in order from left to right.\n *\n * @example\n * ```typescript\n * hstack(\n * text('Left'),\n * text(' | '),\n * text('Right')\n * )\n * ```\n *\n * @param children - View nodes to stack horizontally\n * @returns A new {@link LayoutNode} with horizontal stacking\n *\n * @public\n */\nexport function hstack(...children: ViewNode[]): LayoutNode {\n return {\n _type: 'hstack',\n children,\n spacing: 0,\n }\n}\n\n/**\n * Create empty vertical space.\n *\n * @remarks\n * Useful for adding vertical spacing between sections of your UI.\n *\n * @example\n * ```typescript\n * vstack(\n * text('Header'),\n * spacer(2),\n * text('Content')\n * )\n * ```\n *\n * @param height - Number of blank lines to insert (default: 1)\n * @returns A string containing the specified number of newlines\n *\n * @public\n */\nexport function spacer(height = 1): string {\n return '\\n'.repeat(height)\n}\n\n/**\n * Create a divider line.\n *\n * @remarks\n * Renders a horizontal line using a repeated character.\n *\n * @example\n * ```typescript\n * vstack(\n * text('Section 1'),\n * divider(),\n * text('Section 2'),\n * divider('=', 50)\n * )\n * ```\n *\n * @param char - Character to repeat (default: '─')\n * @param width - Number of times to repeat the character (default: 40)\n * @returns A string containing the divider line\n *\n * @public\n */\nexport function divider(char = '─', width = 40): string {\n return char.repeat(width)\n}\n\n/**\n * Conditionally render a node.\n *\n * @remarks\n * Returns the node if the condition is true, otherwise returns an empty string.\n *\n * @example\n * ```typescript\n * vstack(\n * text('Always visible'),\n * when(state.showHelp, text('Help text'))\n * )\n * ```\n *\n * @param condition - Boolean condition to test\n * @param node - View node to render if condition is true\n * @returns The node if condition is true, empty string otherwise\n *\n * @public\n */\nexport function when(condition: boolean, node: ViewNode): ViewNode {\n return condition ? node : ''\n}\n\n/**\n * Choose between two nodes based on condition.\n *\n * @remarks\n * Returns one node if the condition is true, another if false.\n *\n * @example\n * ```typescript\n * vstack(\n * choose(\n * state.isLoading,\n * text('Loading...').dim(),\n * text('Ready!').bold()\n * )\n * )\n * ```\n *\n * @param condition - Boolean condition to test\n * @param ifTrue - View node to render if condition is true\n * @param ifFalse - View node to render if condition is false\n * @returns Either ifTrue or ifFalse depending on condition\n *\n * @public\n */\nexport function choose(condition: boolean, ifTrue: ViewNode, ifFalse: ViewNode): ViewNode {\n return condition ? ifTrue : ifFalse\n}\n\n/**\n * Map items to view nodes.\n *\n * @remarks\n * Transforms an array of items into an array of view nodes. The render\n * function receives each item and its index.\n *\n * @example\n * ```typescript\n * vstack(\n * ...map(state.items, (item, index) =>\n * text(`${index + 1}. ${item.name}`)\n * )\n * )\n * ```\n *\n * @typeParam T - The type of items in the array\n * @param items - Array of items to map\n * @param render - Function to transform each item into a view node\n * @returns Array of view nodes\n *\n * @public\n */\nexport function map<T>(items: T[], render: (item: T, index: number) => ViewNode): ViewNode[] {\n return items.map(render)\n}\n\n/**\n * Create a component view wrapper.\n * @internal\n */\nexport function componentView(view: string): ComponentView {\n return {\n _type: 'component',\n view,\n }\n}\n","import {\n Program,\n KeyMsg,\n quit as teaQuit,\n batch,\n type Cmd,\n type Model,\n type Msg,\n} from '@boba-cli/tea'\nimport { newBinding, matches } from '@boba-cli/key'\nimport type {\n App,\n ComponentBuilder,\n EventContext,\n KeyHandler,\n ViewFunction,\n ComponentView,\n} from './types.js'\nimport { render } from './view/renderer.js'\nimport { componentView } from './view/nodes.js'\n\n/**\n * Internal key handler registration.\n * @internal\n */\ninterface KeyHandlerEntry<State, Components extends Record<string, unknown>> {\n keys: string[]\n handler: KeyHandler<State, Components>\n}\n\n/**\n * Internal component registration.\n * @internal\n */\ninterface ComponentEntry {\n key: string\n builder: ComponentBuilder<unknown>\n}\n\n/**\n * Builder for creating declarative CLI applications.\n *\n * @example\n * ```typescript\n * const app = createApp()\n * .state({ count: 0 })\n * .component('spinner', spinner())\n * .onKey('q', ({ quit }) => quit())\n * .view(({ state, components }) => vstack(\n * text('Count: ' + state.count),\n * components.spinner\n * ))\n * .build()\n *\n * await app.run()\n * ```\n *\n * @public\n */\nexport class AppBuilder<\n State = undefined,\n Components extends Record<string, unknown> = Record<string, never>,\n> {\n readonly #initialState: State | undefined\n readonly #components: ComponentEntry[]\n readonly #keyHandlers: KeyHandlerEntry<State, Components>[]\n readonly #viewFn: ViewFunction<State, Components> | undefined\n\n private constructor(\n initialState: State | undefined,\n components: ComponentEntry[],\n keyHandlers: KeyHandlerEntry<State, Components>[],\n viewFn: ViewFunction<State, Components> | undefined,\n ) {\n this.#initialState = initialState\n this.#components = components\n this.#keyHandlers = keyHandlers\n this.#viewFn = viewFn\n }\n\n /**\n * Create a new AppBuilder instance.\n * @internal\n */\n static create(): AppBuilder<undefined, Record<string, never>> {\n return new AppBuilder(undefined, [], [], undefined)\n }\n\n /**\n * Set the initial application state.\n *\n * @remarks\n * This should typically be called early in the builder chain. If called after\n * registering key handlers or a view function, those will be preserved but\n * their type information will be updated to reflect the new state type.\n *\n * @example\n * ```typescript\n * createApp()\n * .state({ count: 0, name: 'World' })\n * ```\n *\n * @typeParam S - The application state type\n * @param initial - The initial state object\n * @returns A new {@link AppBuilder} with the state type parameter set\n *\n * @public\n */\n state<S>(initial: S): AppBuilder<S, Components> {\n // Preserve existing handlers and view function with updated type\n // This is safe because the handlers/view will receive the new state type\n return new AppBuilder(\n initial,\n this.#components,\n this.#keyHandlers as unknown as KeyHandlerEntry<S, Components>[],\n this.#viewFn as unknown as ViewFunction<S, Components> | undefined,\n )\n }\n\n /**\n * Register a component with a unique key.\n *\n * @remarks\n * Components are TEA models wrapped in a {@link ComponentBuilder} that\n * manages their lifecycle. The component's rendered view is available in\n * the view function via `components[key]`.\n *\n * @example\n * ```typescript\n * createApp()\n * .component('loading', spinner())\n * .component('input', textInput())\n * ```\n *\n * @typeParam K - The component key (string literal type)\n * @typeParam M - The component model type\n * @param key - Unique identifier for this component\n * @param builder - Component builder implementing init/update/view\n * @returns A new {@link AppBuilder} with the component registered\n *\n * @public\n */\n component<K extends string, M>(\n key: K,\n builder: ComponentBuilder<M>,\n ): AppBuilder<State, Components & Record<K, M>> {\n const newComponents = [...this.#components, { key, builder: builder as ComponentBuilder<unknown> }]\n return new AppBuilder(\n this.#initialState,\n newComponents,\n this.#keyHandlers as KeyHandlerEntry<State, Components & Record<K, M>>[],\n this.#viewFn as ViewFunction<State, Components & Record<K, M>> | undefined,\n )\n }\n\n /**\n * Register a key handler.\n *\n * @remarks\n * Key handlers receive an {@link EventContext} with the current state and\n * components. Multiple keys can be bound to the same handler by passing an\n * array of key strings. Key strings support modifiers like 'ctrl+c', 'alt+enter'.\n *\n * @example\n * ```typescript\n * createApp()\n * .onKey('q', ({ quit }) => quit())\n * .onKey(['up', 'k'], ({ state, update }) => update({ index: state.index - 1 }))\n * .onKey('ctrl+c', ({ quit }) => quit())\n * ```\n *\n * @param keys - Single key string or array of key strings\n * @param handler - Function to call when any of the keys are pressed\n * @returns A new {@link AppBuilder} with the key handler registered\n *\n * @public\n */\n onKey(\n keys: string | string[],\n handler: KeyHandler<State, Components>,\n ): AppBuilder<State, Components> {\n const keyArray = Array.isArray(keys) ? keys : [keys]\n const newHandlers = [...this.#keyHandlers, { keys: keyArray, handler }]\n return new AppBuilder(this.#initialState, this.#components, newHandlers, this.#viewFn)\n }\n\n /**\n * Set the view function.\n *\n * @remarks\n * The view function is called on every render cycle and receives the current\n * state and component views. It should return a {@link ViewNode} tree\n * describing the UI to display.\n *\n * @example\n * ```typescript\n * createApp()\n * .view(({ state, components }) => vstack(\n * text('Hello ' + state.name),\n * components.spinner\n * ))\n * ```\n *\n * @param fn - Function that returns a {@link ViewNode} tree\n * @returns A new {@link AppBuilder} with the view function set\n *\n * @public\n */\n view(fn: ViewFunction<State, Components>): AppBuilder<State, Components> {\n return new AppBuilder(this.#initialState, this.#components, this.#keyHandlers, fn)\n }\n\n /**\n * Build the application.\n *\n * @remarks\n * Finalizes the builder chain and creates an {@link App} instance ready\n * to run. This method must be called after setting a view function via\n * {@link AppBuilder.view}.\n *\n * @throws Error if no view function has been set\n *\n * @returns The built {@link App} ready to run\n *\n * @public\n */\n build(): App<State, Components> {\n if (this.#viewFn === undefined) {\n throw new Error('AppBuilder: view() must be called before build()')\n }\n\n const initialState = this.#initialState as State\n const components = this.#components\n const keyHandlers = this.#keyHandlers\n const viewFn = this.#viewFn\n\n // Create the generated model\n const model = new GeneratedModel(initialState, components, keyHandlers, viewFn)\n\n return {\n async run() {\n const program = new Program(model)\n const result = await program.run()\n return { state: result.model.getUserState() }\n },\n getModel() {\n return model\n },\n }\n }\n}\n\n/**\n * Create a new application builder.\n *\n * @example\n * ```typescript\n * const app = createApp()\n * .state({ count: 0 })\n * .onKey('q', ({ quit }) => quit())\n * .view(({ state }) => text('Count: ' + state.count))\n * .build()\n * ```\n *\n * @public\n */\nexport function createApp(): AppBuilder<undefined, Record<string, never>> {\n return AppBuilder.create()\n}\n\n/**\n * Generated TEA model from the builder configuration.\n * @internal\n */\nclass GeneratedModel<State, Components extends Record<string, unknown>>\n implements Model<Msg, GeneratedModel<State, Components>>\n{\n readonly #userState: State\n readonly #componentModels: Map<string, unknown>\n readonly #componentBuilders: Map<string, ComponentBuilder<unknown>>\n readonly #keyHandlers: KeyHandlerEntry<State, Components>[]\n readonly #viewFn: ViewFunction<State, Components> | undefined\n\n constructor(\n userState: State,\n components: ComponentEntry[],\n keyHandlers: KeyHandlerEntry<State, Components>[],\n viewFn: ViewFunction<State, Components> | undefined,\n componentModels?: Map<string, unknown>,\n ) {\n this.#userState = userState\n this.#keyHandlers = keyHandlers\n this.#viewFn = viewFn\n\n // Build component builders map\n this.#componentBuilders = new Map()\n for (const { key, builder } of components) {\n this.#componentBuilders.set(key, builder)\n }\n\n // Use provided component models or empty map (init will populate)\n this.#componentModels = componentModels ?? new Map<string, unknown>()\n }\n\n getUserState(): State {\n return this.#userState\n }\n\n init(): Cmd<Msg> {\n const cmds: Cmd<Msg>[] = []\n\n // Initialize all components\n for (const [key, builder] of this.#componentBuilders) {\n const [model, cmd] = builder.init()\n this.#componentModels.set(key, model)\n if (cmd) {\n cmds.push(cmd)\n }\n }\n\n return cmds.length > 0 ? batch(...cmds) : null\n }\n\n update(msg: Msg): [GeneratedModel<State, Components>, Cmd<Msg>] {\n // Check key handlers first\n if (msg instanceof KeyMsg) {\n for (const { keys, handler } of this.#keyHandlers) {\n const binding = newBinding({ keys })\n if (matches(msg, binding)) {\n // Create event context and call handler\n let nextUserState = this.#userState\n let shouldQuit = false\n\n const ctx: EventContext<State, Components> = {\n state: this.#userState,\n components: this.#buildComponentViews(),\n update: (patch) => {\n nextUserState = { ...nextUserState, ...patch }\n },\n setState: (newState) => {\n nextUserState = newState\n },\n quit: () => {\n shouldQuit = true\n },\n }\n\n handler(ctx)\n\n if (shouldQuit) {\n return [this, teaQuit()]\n }\n\n if (nextUserState !== this.#userState) {\n return [this.#withUserState(nextUserState), null]\n }\n\n return [this, null]\n }\n }\n }\n\n // Route message to all components\n const cmds: Cmd<Msg>[] = []\n let anyComponentChanged = false\n const newComponentModels = new Map(this.#componentModels)\n\n for (const [key, builder] of this.#componentBuilders) {\n const currentModel = this.#componentModels.get(key)\n if (currentModel === undefined) {\n continue\n }\n\n const [nextModel, cmd] = builder.update(currentModel, msg)\n\n if (nextModel !== currentModel) {\n newComponentModels.set(key, nextModel)\n anyComponentChanged = true\n }\n\n if (cmd) {\n cmds.push(cmd)\n }\n }\n\n if (anyComponentChanged) {\n const next = new GeneratedModel(\n this.#userState,\n Array.from(this.#componentBuilders.entries()).map(([key, builder]) => ({\n key,\n builder,\n })),\n this.#keyHandlers,\n this.#viewFn,\n newComponentModels,\n )\n return [next, cmds.length > 0 ? batch(...cmds) : null]\n }\n\n return [this, cmds.length > 0 ? batch(...cmds) : null]\n }\n\n view(): string {\n if (!this.#viewFn) {\n return ''\n }\n\n const componentViews = this.#buildComponentViews()\n const node = this.#viewFn({\n state: this.#userState,\n components: componentViews,\n })\n\n return render(node)\n }\n\n #buildComponentViews(): { [K in keyof Components]: ComponentView } {\n const views: Record<string, ComponentView> = {}\n\n for (const [key, builder] of this.#componentBuilders) {\n const model = this.#componentModels.get(key)\n if (model !== undefined) {\n views[key] = componentView(builder.view(model))\n }\n }\n\n return views as { [K in keyof Components]: ComponentView }\n }\n\n #withUserState(newUserState: State): GeneratedModel<State, Components> {\n return new GeneratedModel(\n newUserState,\n Array.from(this.#componentBuilders.entries()).map(([key, builder]) => ({\n key,\n builder,\n })),\n this.#keyHandlers,\n this.#viewFn,\n this.#componentModels,\n )\n }\n}\n","import { type Cmd, type Msg } from '@boba-cli/tea'\nimport { Style } from '@boba-cli/chapstick'\nimport { SpinnerModel, type Spinner, line, type SpinnerOptions } from '@boba-cli/spinner'\nimport type { ComponentBuilder } from '../types.js'\n\n/**\n * Options for the spinner component builder.\n *\n * @remarks\n * Configure the spinner animation and styling when creating a spinner component.\n *\n * @public\n */\nexport interface SpinnerBuilderOptions {\n /**\n * Spinner animation to use (default: `line`).\n *\n * @remarks\n * Available spinners include `line`, `dot`, `miniDot`,\n * `pulse`, `points`, `moon`, `meter`, and `ellipsis`.\n */\n spinner?: Spinner\n /**\n * Style for rendering the spinner.\n *\n * @remarks\n * Uses `Style` from `@boba-cli/chapstick` to apply terminal colors and formatting.\n */\n style?: Style\n}\n\n/**\n * Create a spinner component builder.\n *\n * @remarks\n * Creates a {@link ComponentBuilder} wrapping the `@boba-cli/spinner` package.\n * The spinner automatically animates and can be styled with custom colors.\n *\n * @example\n * Basic usage:\n * ```typescript\n * const app = createApp()\n * .component('loading', spinner())\n * .view(({ components }) => components.loading)\n * .build()\n * ```\n *\n * @example\n * With custom styling:\n * ```typescript\n * const app = createApp()\n * .component('loading', spinner({\n * style: new Style().foreground('#50fa7b')\n * }))\n * .view(({ components }) => hstack(\n * components.loading,\n * text('Loading...')\n * ))\n * .build()\n * ```\n *\n * @param options - Configuration options for the spinner\n * @returns A {@link ComponentBuilder} ready to use with {@link AppBuilder.component}\n *\n * @public\n */\nexport function spinner(options: SpinnerBuilderOptions = {}): ComponentBuilder<SpinnerModel> {\n const spinnerOpts: SpinnerOptions = {\n spinner: options.spinner ?? line,\n style: options.style ?? new Style(),\n }\n\n return {\n init(): [SpinnerModel, Cmd<Msg>] {\n const model = new SpinnerModel(spinnerOpts)\n return [model, model.tick() as Cmd<Msg>]\n },\n\n update(model: SpinnerModel, msg: Msg): [SpinnerModel, Cmd<Msg>] {\n return model.update(msg)\n },\n\n view(model: SpinnerModel): string {\n return model.view()\n },\n }\n}\n","import { type Cmd, type Msg } from '@boba-cli/tea'\nimport { Style } from '@boba-cli/chapstick'\nimport {\n TextInputModel,\n EchoMode,\n type TextInputOptions,\n type ValidateFunc,\n} from '@boba-cli/textinput'\nimport type { ComponentBuilder } from '../types.js'\n\n/**\n * Options for the textInput component builder.\n * @public\n */\nexport interface TextInputBuilderOptions {\n /** Placeholder text shown when input is empty. */\n placeholder?: string\n /** Width constraint for the input field. */\n width?: number\n /** Echo mode for input display (Normal, Password, or None). */\n echoMode?: EchoMode\n /** Character limit for input. */\n charLimit?: number\n /** Prompt string shown before the input. */\n prompt?: string\n /** Style for the prompt. */\n promptStyle?: Style\n /** Style for the input text. */\n textStyle?: Style\n /** Style for the placeholder text. */\n placeholderStyle?: Style\n /** Validation function. */\n validate?: ValidateFunc\n}\n\n/**\n * Create a textInput component builder.\n *\n * @example\n * ```typescript\n * const app = createApp()\n * .component('nameInput', textInput({\n * placeholder: 'Enter your name...',\n * width: 40,\n * validate: (value) => value.length < 3 ? new Error('Too short') : null\n * }))\n * .view(({ components }) => components.nameInput)\n * .build()\n * ```\n *\n * @public\n */\nexport function textInput(\n options: TextInputBuilderOptions = {},\n): ComponentBuilder<TextInputModel> {\n const inputOpts: TextInputOptions = {\n placeholder: options.placeholder ?? '',\n width: options.width,\n echoMode: options.echoMode ?? EchoMode.Normal,\n charLimit: options.charLimit ?? 0,\n prompt: options.prompt ?? '',\n promptStyle: options.promptStyle ?? new Style(),\n textStyle: options.textStyle ?? new Style(),\n placeholderStyle: options.placeholderStyle ?? new Style(),\n validate: options.validate,\n }\n\n return {\n init(): [TextInputModel, Cmd<Msg>] {\n const model = TextInputModel.new(inputOpts)\n const [focused, cmd] = model.focus()\n return [focused, cmd]\n },\n\n update(model: TextInputModel, msg: Msg): [TextInputModel, Cmd<Msg>] {\n return model.update(msg)\n },\n\n view(model: TextInputModel): string {\n return model.view()\n },\n }\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@boba-cli/dsl",
|
|
3
|
+
"description": "Declarative DSL for building CLI applications with minimal ceremony",
|
|
4
|
+
"version": "0.1.0-alpha.1",
|
|
5
|
+
"dependencies": {
|
|
6
|
+
"@boba-cli/chapstick": "0.1.0-alpha.2",
|
|
7
|
+
"@boba-cli/key": "0.1.0-alpha.1",
|
|
8
|
+
"@boba-cli/spinner": "0.1.0-alpha.2",
|
|
9
|
+
"@boba-cli/tea": "0.1.0-alpha.1",
|
|
10
|
+
"@boba-cli/textinput": "0.1.0-alpha.2"
|
|
11
|
+
},
|
|
12
|
+
"devDependencies": {
|
|
13
|
+
"tsd": "^0.31.2",
|
|
14
|
+
"typescript": "5.8.2",
|
|
15
|
+
"vitest": "^4.0.16"
|
|
16
|
+
},
|
|
17
|
+
"engines": {
|
|
18
|
+
"node": ">=20.0.0"
|
|
19
|
+
},
|
|
20
|
+
"exports": {
|
|
21
|
+
".": {
|
|
22
|
+
"import": {
|
|
23
|
+
"types": "./dist/index.d.ts",
|
|
24
|
+
"default": "./dist/index.js"
|
|
25
|
+
},
|
|
26
|
+
"require": {
|
|
27
|
+
"types": "./dist/index.d.cts",
|
|
28
|
+
"default": "./dist/index.cjs"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"./package.json": "./package.json"
|
|
32
|
+
},
|
|
33
|
+
"files": [
|
|
34
|
+
"dist"
|
|
35
|
+
],
|
|
36
|
+
"main": "./dist/index.cjs",
|
|
37
|
+
"module": "./dist/index.js",
|
|
38
|
+
"sideEffects": false,
|
|
39
|
+
"tsd": {
|
|
40
|
+
"directory": "test-d"
|
|
41
|
+
},
|
|
42
|
+
"type": "module",
|
|
43
|
+
"types": "./dist/index.d.ts",
|
|
44
|
+
"scripts": {
|
|
45
|
+
"build": "tsup",
|
|
46
|
+
"check": "pnpm run check:lint-ts && pnpm run test",
|
|
47
|
+
"check:api-report": "pnpm run generate:api-report",
|
|
48
|
+
"check:eslint": "pnpm run check:lint-ts",
|
|
49
|
+
"check:lint-ts": "eslint \"{src,test}/**/*.{ts,tsx}\"",
|
|
50
|
+
"fix:lint-ts": "eslint \"{src,test}/**/*.{ts,tsx}\" --fix",
|
|
51
|
+
"generate:api-report": "api-extractor run --local",
|
|
52
|
+
"test": "pnpm run test:ts && pnpm run test:types",
|
|
53
|
+
"test:ts": "vitest run --passWithNoTests",
|
|
54
|
+
"test:types": "tsd"
|
|
55
|
+
}
|
|
56
|
+
}
|