@regmisatyam/retex 0.1.0 → 0.2.0
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 +189 -37
- package/dist/index-DJEasLJc.d.ts +492 -0
- package/dist/index-DXTDYNCI.d.cts +492 -0
- package/dist/index.cjs +1198 -591
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +196 -358
- package/dist/index.d.ts +196 -358
- package/dist/index.js +1166 -592
- package/dist/index.js.map +1 -1
- package/dist/library.cjs +649 -0
- package/dist/library.cjs.map +1 -0
- package/dist/library.d.cts +2 -0
- package/dist/library.d.ts +2 -0
- package/dist/library.js +625 -0
- package/dist/library.js.map +1 -0
- package/dist/{react-v8gyKEAs.d.cts → react-B3tN1iWa.d.cts} +74 -38
- package/dist/{react-v8gyKEAs.d.ts → react-B3tN1iWa.d.ts} +74 -38
- package/dist/react.cjs +113 -74
- package/dist/react.cjs.map +1 -1
- package/dist/react.d.cts +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/react.js +113 -74
- package/dist/react.js.map +1 -1
- package/package.json +7 -1
package/README.md
CHANGED
|
@@ -11,6 +11,11 @@ LaTeX without the toolchain. You write a small, readable markup language; ReTeX
|
|
|
11
11
|
tokenizes, parses, validates, and renders it to **HTML**, **React**, **JSON**, or
|
|
12
12
|
**PDF** — all from a single, dependency-free TypeScript library.
|
|
13
13
|
|
|
14
|
+
ReTeX is **blank by default** — a plain, unstyled document, "just simple
|
|
15
|
+
editing". Styling is opt-in: import a **library** with `\usepackage{...}`
|
|
16
|
+
(LaTeX-style) for colors, fonts, shapes, or a full themed look. Nothing is
|
|
17
|
+
imposed; you add exactly what you want.
|
|
18
|
+
|
|
14
19
|
```latex
|
|
15
20
|
\name{Ada Lovelace}
|
|
16
21
|
\title{Software Engineer}
|
|
@@ -27,10 +32,11 @@ tokenizes, parses, validates, and renders it to **HTML**, **React**, **JSON**, o
|
|
|
27
32
|
|
|
28
33
|
## Preview
|
|
29
34
|
|
|
30
|
-
The three [examples](examples/) rendered to HTML
|
|
31
|
-
|
|
35
|
+
The three [examples](examples/) rendered to HTML. Each opts into a theme pack
|
|
36
|
+
with a single `\usepackage{...}` line (`modern`, `compact`, `classic`).
|
|
37
|
+
Regenerate with `npx tsx examples/build.ts`.
|
|
32
38
|
|
|
33
|
-
| Single column (`
|
|
39
|
+
| Single column (`modern`) | Two column (`compact`) | Academic CV (`classic`) |
|
|
34
40
|
| --- | --- | --- |
|
|
35
41
|
| [](examples/software-engineer.retex) | [](examples/two-column.retex) | [](examples/research-paper.retex) |
|
|
36
42
|
|
|
@@ -44,18 +50,29 @@ set of commands (`\name`, `\job`, `\education`, `\skills`, `\section`, …), str
|
|
|
44
50
|
theming, and four production-ready renderers.
|
|
45
51
|
|
|
46
52
|
ReTeX **is not** a full LaTeX implementation. There is no math mode, no macro
|
|
47
|
-
programming, no
|
|
48
|
-
|
|
49
|
-
|
|
53
|
+
programming, no `\def`. The syntax is *LaTeX-inspired* — familiar braces and
|
|
54
|
+
backslash commands — but the language is deliberately small, safe, and
|
|
55
|
+
predictable. (Notably, `%` is a literal percent sign, not a comment;
|
|
50
56
|
see [Syntax](docs/SYNTAX.md).)
|
|
51
57
|
|
|
58
|
+
It does, however, borrow LaTeX's best idea: **packages**. The core is blank, and
|
|
59
|
+
you `\usepackage{...}` exactly the styling you need — `colors`, `fonts`,
|
|
60
|
+
`shapes`, or a complete theme pack like `modern`. See [Libraries](#libraries).
|
|
61
|
+
|
|
52
62
|
---
|
|
53
63
|
|
|
54
64
|
## Features
|
|
55
65
|
|
|
56
|
-
- **
|
|
57
|
-
|
|
58
|
-
|
|
66
|
+
- **Blank by default** — the core ships structure + basic emphasis; everything
|
|
67
|
+
decorative is opt-in. A document with no imports renders as a clean,
|
|
68
|
+
browser-default page.
|
|
69
|
+
- **Core typography** — `\textbf`, `\textit`, `\emph`, `\underline`, `\sout`,
|
|
70
|
+
`\bfseries`, `\itshape`.
|
|
71
|
+
- **Libraries (`\usepackage`)** — opt into styling, LaTeX-style: `colors`
|
|
72
|
+
(`\textcolor`, `\themecolor`), `fonts` (`\fontfamily`, `\fontsize`,
|
|
73
|
+
`\small`…`\Huge`, `\ttfamily`), `shapes` (`\hrule`, `\dashrule`, …, `\vspace`,
|
|
74
|
+
`\hspace`), and theme packs `modern` / `classic` / `compact`. Activate from the
|
|
75
|
+
document **or** from code (`engine.use(...)`).
|
|
59
76
|
- **Hyperlinks** — `\href{url}{label}` and `\url{url}`, with URL sanitization.
|
|
60
77
|
- **Sections** — `\section` and `\subsection`.
|
|
61
78
|
- **Résumé components** — `\name`, `\title`, `\email`, `\phone`, `\location`,
|
|
@@ -64,8 +81,8 @@ see [Syntax](docs/SYNTAX.md).)
|
|
|
64
81
|
environment.
|
|
65
82
|
- **Icons** — inline SVG icon set (`\icon{github}`, `\icon{linkedin}`, …),
|
|
66
83
|
extensible at runtime.
|
|
67
|
-
- **Theming** —
|
|
68
|
-
fully data-driven, deep-mergeable custom theme model.
|
|
84
|
+
- **Theming** — a blank default plus opt-in presets (`modern`, `classic`,
|
|
85
|
+
`compact`) and a fully data-driven, deep-mergeable custom theme model.
|
|
69
86
|
- **Plugins** — register new commands, environments, icons, renderers, or theme
|
|
70
87
|
patches on a per-engine basis. Two example plugins ship in the box
|
|
71
88
|
(`badgePlugin`, `ratingPlugin`).
|
|
@@ -73,6 +90,9 @@ see [Syntax](docs/SYNTAX.md).)
|
|
|
73
90
|
React element tree, a structured JSON AST, and print-ready HTML/PDF.
|
|
74
91
|
- **Editor tooling** — completion, hover docs, semantic tokens, diagnostics,
|
|
75
92
|
formatting, and AST inspection via `EditorService`.
|
|
93
|
+
- **Two-way preview** — opt-in `sourceMap` HTML stamps elements with source
|
|
94
|
+
offsets so a live editor can sync both ways (click preview ↔ highlight code),
|
|
95
|
+
Overleaf-style. See [`examples/playground.html`](examples/playground.html).
|
|
76
96
|
- **Incremental parsing** — block-level caching for fast live preview.
|
|
77
97
|
- **Security** — every text node and attribute is escaped; every URL is vetted.
|
|
78
98
|
No `eval`, no `Function`, no code execution of source.
|
|
@@ -84,7 +104,7 @@ see [Syntax](docs/SYNTAX.md).)
|
|
|
84
104
|
## Install
|
|
85
105
|
|
|
86
106
|
```bash
|
|
87
|
-
npm
|
|
107
|
+
npm i @regmisatyam/retex
|
|
88
108
|
```
|
|
89
109
|
|
|
90
110
|
React is an **optional peer dependency** — install it only if you use the React
|
|
@@ -98,22 +118,26 @@ PDF export lazily imports `puppeteer` if present; otherwise you can supply your
|
|
|
98
118
|
own headless-browser launcher (Playwright, a pooled Chromium, etc.) or print the
|
|
99
119
|
generated HTML to PDF in the browser.
|
|
100
120
|
|
|
101
|
-
The package exposes
|
|
121
|
+
The package exposes three entry points:
|
|
102
122
|
|
|
103
|
-
| Import path
|
|
104
|
-
|
|
|
105
|
-
|
|
|
106
|
-
|
|
|
123
|
+
| Import path | Contents |
|
|
124
|
+
| ---------------------------- | ----------------------------------------------------- |
|
|
125
|
+
| `@regmisatyam/retex` | The engine, pipeline, AST utilities, all renderers\*, theming, libraries\*\*, plugins, security, icons, editor, incremental compiler. |
|
|
126
|
+
| `@regmisatyam/retex/react` | The React renderer (`ReactRenderer`, `renderReact`). |
|
|
127
|
+
| `@regmisatyam/retex/library` | Just the libraries (`colorsLibrary`, `fontsLibrary`, …) and helpers. |
|
|
107
128
|
|
|
108
|
-
\* The React renderer is also re-exported from
|
|
109
|
-
|
|
129
|
+
\* The React renderer is also re-exported from `@regmisatyam/retex`, but lives behind
|
|
130
|
+
`@regmisatyam/retex/react` so the core bundle never imports React.
|
|
131
|
+
|
|
132
|
+
\*\* The libraries are also re-exported from `@regmisatyam/retex`; the
|
|
133
|
+
`/library` subpath just lets you import them on their own.
|
|
110
134
|
|
|
111
135
|
---
|
|
112
136
|
|
|
113
137
|
## Quick start
|
|
114
138
|
|
|
115
139
|
```ts
|
|
116
|
-
import { ReTeXEngine } from "retex";
|
|
140
|
+
import { ReTeXEngine } from "@regmisatyam/retex";
|
|
117
141
|
|
|
118
142
|
const engine = new ReTeXEngine();
|
|
119
143
|
|
|
@@ -141,11 +165,30 @@ const css = engine.styles();
|
|
|
141
165
|
`engine.toHtml` accepts either a source string or a parsed `DocumentNode`, so you
|
|
142
166
|
can parse once and render to several targets.
|
|
143
167
|
|
|
168
|
+
That document renders as a clean, **unstyled** page. To add styling, import a
|
|
169
|
+
library — from the source:
|
|
170
|
+
|
|
171
|
+
```latex
|
|
172
|
+
\usepackage{modern} %% a full themed look + the color/font/shape commands
|
|
173
|
+
\textcolor{#7c3aed}{Now in color}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
…or from code:
|
|
177
|
+
|
|
178
|
+
```ts
|
|
179
|
+
import { ReTeXEngine, colorsLibrary, fontsLibrary } from "@regmisatyam/retex";
|
|
180
|
+
const engine = new ReTeXEngine().use(colorsLibrary).use(fontsLibrary);
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
See [Libraries](#libraries) for the full catalog.
|
|
184
|
+
|
|
144
185
|
---
|
|
145
186
|
|
|
146
187
|
## A realistic résumé
|
|
147
188
|
|
|
148
189
|
```latex
|
|
190
|
+
\usepackage{modern} %% styled look + color/font/shape commands (e.g. \hspace below)
|
|
191
|
+
|
|
149
192
|
%% --- header ---
|
|
150
193
|
\name{Grace Hopper}
|
|
151
194
|
\title{Distinguished Engineer \& Compiler Pioneer}
|
|
@@ -252,7 +295,7 @@ engine.styles(); // the theme's CSS (for the fragmen
|
|
|
252
295
|
Or use the stage functions directly:
|
|
253
296
|
|
|
254
297
|
```ts
|
|
255
|
-
import { tokenize, parse, validate, renderHtml } from "retex";
|
|
298
|
+
import { tokenize, parse, validate, renderHtml } from "@regmisatyam/retex";
|
|
256
299
|
|
|
257
300
|
const { ast } = parse(tokenize(source).tokens);
|
|
258
301
|
const diagnostics = validate(ast);
|
|
@@ -266,7 +309,7 @@ factory yourself, so it works with React, Preact, or any compatible runtime.
|
|
|
266
309
|
|
|
267
310
|
```tsx
|
|
268
311
|
import React from "react";
|
|
269
|
-
import { ReTeXEngine } from "retex";
|
|
312
|
+
import { ReTeXEngine } from "@regmisatyam/retex";
|
|
270
313
|
|
|
271
314
|
function Resume({ source }: { source: string }) {
|
|
272
315
|
const engine = React.useMemo(() => new ReTeXEngine(), []);
|
|
@@ -287,8 +330,8 @@ Standalone, via the dedicated subpath:
|
|
|
287
330
|
|
|
288
331
|
```tsx
|
|
289
332
|
import React from "react";
|
|
290
|
-
import { renderReact } from "retex/react";
|
|
291
|
-
import { parse, tokenize } from "retex";
|
|
333
|
+
import { renderReact } from "@regmisatyam/retex/react";
|
|
334
|
+
import { parse, tokenize } from "@regmisatyam/retex";
|
|
292
335
|
|
|
293
336
|
const { ast } = parse(tokenize(source).tokens);
|
|
294
337
|
const element = renderReact(ast, {
|
|
@@ -321,18 +364,60 @@ const pdf2 = await engine.toPdf(source, {
|
|
|
321
364
|
|
|
322
365
|
---
|
|
323
366
|
|
|
367
|
+
## Libraries
|
|
368
|
+
|
|
369
|
+
ReTeX is blank by default; **libraries** add styling, opt-in. Activate them two
|
|
370
|
+
ways — and you can mix both.
|
|
371
|
+
|
|
372
|
+
**From the document** (LaTeX-style, in the preamble):
|
|
373
|
+
|
|
374
|
+
```latex
|
|
375
|
+
\usepackage{colors} %% \textcolor, \themecolor
|
|
376
|
+
\usepackage{fonts, shapes} %% comma-separate to import several at once
|
|
377
|
+
\textcolor{#2563eb}{Now available} — \hrule
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
`\use{...}` is an alias. If you use a gated command without its library, ReTeX
|
|
381
|
+
tells you exactly which `\usepackage{...}` to add.
|
|
382
|
+
|
|
383
|
+
**From code** (great for app integrations):
|
|
384
|
+
|
|
385
|
+
```ts
|
|
386
|
+
import { ReTeXEngine, colorsLibrary, shapesLibrary } from "@regmisatyam/retex";
|
|
387
|
+
|
|
388
|
+
const engine = new ReTeXEngine({ plugins: [colorsLibrary, shapesLibrary] });
|
|
389
|
+
// or: new ReTeXEngine().use(colorsLibrary).use(shapesLibrary)
|
|
390
|
+
engine.toHtml("\\textcolor{#2563eb}{Hi} \\hrule");
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
### Built-in libraries
|
|
394
|
+
|
|
395
|
+
| Library | `\usepackage{…}` | Provides |
|
|
396
|
+
| -------------------------------- | ---------------- | ------------------------------------------------------------------------- |
|
|
397
|
+
| `colorsLibrary` | `colors` | `\textcolor`, `\themecolor`, plus a `palettes` catalog |
|
|
398
|
+
| `fontsLibrary` | `fonts` | `\fontfamily`, `\fontsize`, `\small`…`\Huge`, `\normalsize`, `\ttfamily`, plus `fontStacks` / `fontTheme` |
|
|
399
|
+
| `shapesLibrary` | `shapes` | `\hrule`, `\divider`, `\dashrule`, `\dotrule`, `\thickrule`, `\doublerule`, `\vspace`, `\hspace` |
|
|
400
|
+
| `modern` / `classic` / `compact` | same name | A complete styled look **plus** all three toolkits above |
|
|
401
|
+
|
|
402
|
+
Theme packs are the quickest path to a polished document: `\usepackage{modern}`
|
|
403
|
+
gives you the full look *and* every styling command. Bring your own library with
|
|
404
|
+
`engine.provideLibrary(myLib)` (addressable by `\usepackage`) or `engine.use`.
|
|
405
|
+
|
|
406
|
+
---
|
|
407
|
+
|
|
324
408
|
## Theming
|
|
325
409
|
|
|
326
|
-
Themes are plain data, deep-merged over the default
|
|
327
|
-
|
|
410
|
+
Themes are plain data, deep-merged over the **blank** default — so every field is
|
|
411
|
+
optional, and what you don't set stays unstyled. Pass a full `Theme` or a partial
|
|
412
|
+
patch.
|
|
328
413
|
|
|
329
414
|
```ts
|
|
330
|
-
import { ReTeXEngine, modernTheme } from "retex";
|
|
415
|
+
import { ReTeXEngine, modernTheme } from "@regmisatyam/retex";
|
|
331
416
|
|
|
332
417
|
// Use a preset:
|
|
333
418
|
const a = new ReTeXEngine({ theme: modernTheme });
|
|
334
419
|
|
|
335
|
-
// Or a partial override (deep-merged over
|
|
420
|
+
// Or a partial override (deep-merged over blank):
|
|
336
421
|
const b = new ReTeXEngine({
|
|
337
422
|
theme: {
|
|
338
423
|
name: "brand",
|
|
@@ -342,14 +427,16 @@ const b = new ReTeXEngine({
|
|
|
342
427
|
},
|
|
343
428
|
});
|
|
344
429
|
|
|
345
|
-
// Swap at runtime:
|
|
430
|
+
// Swap at runtime (an anonymous patch merges over the current theme):
|
|
346
431
|
b.setTheme({ colors: { primary: "#059669" } });
|
|
347
432
|
```
|
|
348
433
|
|
|
349
|
-
Presets: `
|
|
350
|
-
available by name via `getTheme("modern")` or the `themes`
|
|
351
|
-
color is exposed as a CSS variable (`--retex-color-<token>`),
|
|
352
|
-
`\themecolor{token}{…}`.
|
|
434
|
+
Presets: `blankTheme` (the default), `modernTheme`, `classicTheme`,
|
|
435
|
+
`compactTheme` (also available by name via `getTheme("modern")` or the `themes`
|
|
436
|
+
map). Every theme color is exposed as a CSS variable (`--retex-color-<token>`),
|
|
437
|
+
usable from `\themecolor{token}{…}`. A blank theme still renders a readable
|
|
438
|
+
document — the structural CSS is always emitted; only decoration is left to a
|
|
439
|
+
theme/library.
|
|
353
440
|
|
|
354
441
|
---
|
|
355
442
|
|
|
@@ -360,7 +447,7 @@ theme patches. The simplest case — a custom inline command with a render
|
|
|
360
447
|
function and no custom AST node:
|
|
361
448
|
|
|
362
449
|
```ts
|
|
363
|
-
import { ReTeXEngine } from "retex";
|
|
450
|
+
import { ReTeXEngine } from "@regmisatyam/retex";
|
|
364
451
|
|
|
365
452
|
const engine = new ReTeXEngine();
|
|
366
453
|
|
|
@@ -384,7 +471,7 @@ engine.toHtml("\\badge{Open to work}");
|
|
|
384
471
|
Or package it as a reusable plugin and install with `.use()`:
|
|
385
472
|
|
|
386
473
|
```ts
|
|
387
|
-
import { ReTeXEngine, badgePlugin, ratingPlugin } from "retex";
|
|
474
|
+
import { ReTeXEngine, badgePlugin, ratingPlugin } from "@regmisatyam/retex";
|
|
388
475
|
|
|
389
476
|
const engine = new ReTeXEngine({ plugins: [badgePlugin, ratingPlugin] });
|
|
390
477
|
engine.toHtml("\\badge{New} \\rating{4}");
|
|
@@ -402,7 +489,7 @@ override. See **[docs/API.md](docs/API.md)** for the full `ReTeXPlugin` contract
|
|
|
402
489
|
All methods are pure and synchronous.
|
|
403
490
|
|
|
404
491
|
```ts
|
|
405
|
-
import { EditorService } from "retex";
|
|
492
|
+
import { EditorService } from "@regmisatyam/retex";
|
|
406
493
|
|
|
407
494
|
const editor = new EditorService();
|
|
408
495
|
|
|
@@ -419,6 +506,70 @@ optional quick-fixes, so editors can wire up code actions and doc links.
|
|
|
419
506
|
|
|
420
507
|
---
|
|
421
508
|
|
|
509
|
+
## Two-way preview (source mapping)
|
|
510
|
+
|
|
511
|
+
Like Overleaf's SyncTeX, ReTeX can keep an editor and its live preview in sync
|
|
512
|
+
in **both directions** — click an element in the preview to jump to the source
|
|
513
|
+
that produced it, and move the caret in the source to highlight the matching
|
|
514
|
+
element in the preview.
|
|
515
|
+
|
|
516
|
+
Turn it on with the `sourceMap` render option. The HTML renderer then stamps
|
|
517
|
+
every meaningful element with `data-rtx-pos="start:end"` (zero-based UTF-16
|
|
518
|
+
offsets into the source) and a `data-rtx-type` (the originating node):
|
|
519
|
+
|
|
520
|
+
```ts
|
|
521
|
+
const engine = new ReTeXEngine();
|
|
522
|
+
engine.toHtml(source, { sourceMap: true });
|
|
523
|
+
// …<section data-rtx-pos="20:40" data-rtx-type="section">…
|
|
524
|
+
// <p class="retex-para" data-rtx-pos="101:130" data-rtx-type="para">
|
|
525
|
+
// Built <strong data-rtx-pos="110:128" data-rtx-type="bold">compilers</strong>.
|
|
526
|
+
// </p>…
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
`sourceMap` defaults to **off**, so exported/production HTML stays clean and
|
|
530
|
+
unannotated — you only pay for the attributes when driving a live editor.
|
|
531
|
+
|
|
532
|
+
A few tiny, **editor-agnostic** helpers turn those attributes into sync (they
|
|
533
|
+
work with the real DOM or any object exposing `getAttribute`, so the core never
|
|
534
|
+
touches `document`):
|
|
535
|
+
|
|
536
|
+
```ts
|
|
537
|
+
import {
|
|
538
|
+
closestSourcePos, // preview → code: nearest annotated ancestor's span
|
|
539
|
+
pickElementForOffset, // code → preview: tightest element under the caret
|
|
540
|
+
SOURCE_POS_ATTR,
|
|
541
|
+
} from "@regmisatyam/retex";
|
|
542
|
+
|
|
543
|
+
// Reverse sync — click the preview, select the source it came from:
|
|
544
|
+
preview.addEventListener("click", (e) => {
|
|
545
|
+
const span = closestSourcePos(e.target);
|
|
546
|
+
if (span) {
|
|
547
|
+
textarea.focus();
|
|
548
|
+
textarea.setSelectionRange(span.start, span.end);
|
|
549
|
+
}
|
|
550
|
+
});
|
|
551
|
+
|
|
552
|
+
// Forward sync — caret moves, highlight the matching preview element:
|
|
553
|
+
function onCaretMove() {
|
|
554
|
+
const el = pickElementForOffset(
|
|
555
|
+
preview.querySelectorAll(`[${SOURCE_POS_ATTR}]`),
|
|
556
|
+
textarea.selectionStart,
|
|
557
|
+
);
|
|
558
|
+
el?.classList.add("active");
|
|
559
|
+
}
|
|
560
|
+
```
|
|
561
|
+
|
|
562
|
+
### Try it
|
|
563
|
+
|
|
564
|
+
A complete, dependency-free playground ships in [`examples/playground.html`](examples/playground.html)
|
|
565
|
+
— a split editor/preview with both sync directions wired up:
|
|
566
|
+
|
|
567
|
+
```bash
|
|
568
|
+
npm run playground # builds the library, serves it, and opens the demo
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
---
|
|
572
|
+
|
|
422
573
|
## Security
|
|
423
574
|
|
|
424
575
|
ReTeX renders untrusted markup, so safety is built into every layer:
|
|
@@ -446,7 +597,7 @@ paragraph re-parses only that block while the rest are served from cache and
|
|
|
446
597
|
cheaply re-positioned.
|
|
447
598
|
|
|
448
599
|
```ts
|
|
449
|
-
import { IncrementalCompiler } from "retex";
|
|
600
|
+
import { IncrementalCompiler } from "@regmisatyam/retex";
|
|
450
601
|
|
|
451
602
|
const inc = new IncrementalCompiler();
|
|
452
603
|
const { ast, diagnostics, stats } = inc.compile(source);
|
|
@@ -459,6 +610,7 @@ const { ast, diagnostics, stats } = inc.compile(source);
|
|
|
459
610
|
|
|
460
611
|
```bash
|
|
461
612
|
npm run build # bundle with tsup (ESM + CJS + d.ts)
|
|
613
|
+
npm run playground # build + serve the two-way preview demo
|
|
462
614
|
npm run typecheck # tsc --noEmit
|
|
463
615
|
npm test # run the test suite (vitest)
|
|
464
616
|
npm run test:watch # watch mode
|