next-live 0.1.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/CHANGELOG.md +33 -0
- package/LICENSE +21 -0
- package/README.md +142 -0
- package/dist/editor.cjs +247 -0
- package/dist/editor.cjs.map +1 -0
- package/dist/editor.d.cts +80 -0
- package/dist/editor.d.ts +80 -0
- package/dist/editor.js +225 -0
- package/dist/editor.js.map +1 -0
- package/dist/index.cjs +1265 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +580 -0
- package/dist/index.d.ts +580 -0
- package/dist/index.js +1192 -0
- package/dist/index.js.map +1 -0
- package/dist/server.cjs +287 -0
- package/dist/server.cjs.map +1 -0
- package/dist/server.d.cts +142 -0
- package/dist/server.d.ts +142 -0
- package/dist/server.js +282 -0
- package/dist/server.js.map +1 -0
- package/dist/shared.js +30 -0
- package/dist/shared.js.map +1 -0
- package/docs/01-getting-started.md +244 -0
- package/docs/02-module-registry.md +487 -0
- package/docs/03-sharing-your-app-libraries.md +206 -0
- package/docs/04-scaling.md +234 -0
- package/docs/05-security.md +204 -0
- package/docs/06-api-reference.md +337 -0
- package/docs/07-troubleshooting.md +289 -0
- package/docs/08-integration-guide.md +340 -0
- package/docs/09-non-ui-snippets.md +124 -0
- package/docs/10-validating-in-ci.md +192 -0
- package/docs/README.md +76 -0
- package/package.json +105 -0
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,580 @@
|
|
|
1
|
+
import { ComponentType, ReactElement, ReactNode, ElementType, Component, ErrorInfo, Context } from 'react';
|
|
2
|
+
import * as sucrase from 'sucrase';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A value registered under an import specifier.
|
|
6
|
+
*
|
|
7
|
+
* The idiomatic value is a real module namespace - `import * as ui from
|
|
8
|
+
* 'my-ui-kit'` - but any object, function, or primitive works. See
|
|
9
|
+
* {@link normalizeModule} for how each shape maps onto `default` and named
|
|
10
|
+
* imports.
|
|
11
|
+
*/
|
|
12
|
+
type ModuleValue = unknown;
|
|
13
|
+
/**
|
|
14
|
+
* A lazily-loaded module, awaited before user code is evaluated.
|
|
15
|
+
*
|
|
16
|
+
* Receives the specifier that was imported. Exact-match entries can ignore it;
|
|
17
|
+
* prefix entries (a key ending in `/`) need it, since one loader serves the
|
|
18
|
+
* whole subtree.
|
|
19
|
+
*/
|
|
20
|
+
type ModuleLoader = (specifier: string) => ModuleValue | Promise<ModuleValue>;
|
|
21
|
+
/**
|
|
22
|
+
* Maps import specifiers to values:
|
|
23
|
+
*
|
|
24
|
+
* ```ts
|
|
25
|
+
* { 'react': React, '@app/ui': ui, '@app/store': { useAppStore } }
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
interface ModuleRegistry {
|
|
29
|
+
[specifier: string]: ModuleValue | ModuleLoader;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Free variables injected directly into the snippet's scope, the way
|
|
33
|
+
* `react-live` does it. Prefer `modules` + real `import` statements; this
|
|
34
|
+
* exists for compatibility and for values that read better unqualified.
|
|
35
|
+
*/
|
|
36
|
+
interface LiveScope {
|
|
37
|
+
[identifier: string]: unknown;
|
|
38
|
+
}
|
|
39
|
+
/** A module record shaped so Sucrase's interop helpers become no-ops. */
|
|
40
|
+
interface NormalizedModule {
|
|
41
|
+
readonly __esModule: true;
|
|
42
|
+
readonly default: unknown;
|
|
43
|
+
readonly [name: string]: unknown;
|
|
44
|
+
}
|
|
45
|
+
/** What the snippet ultimately produced. */
|
|
46
|
+
type LiveRenderable = {
|
|
47
|
+
kind: 'component';
|
|
48
|
+
component: ComponentType<Record<string, unknown>>;
|
|
49
|
+
} | {
|
|
50
|
+
kind: 'element';
|
|
51
|
+
element: ReactElement;
|
|
52
|
+
};
|
|
53
|
+
/** How the renderable was found - surfaced in error messages. */
|
|
54
|
+
type ExtractionSource = 'render()' | 'export default' | 'module.exports' | 'named export' | 'declaration';
|
|
55
|
+
interface TranspileOptions {
|
|
56
|
+
/**
|
|
57
|
+
* Logical filename. Drives the `sourceURL` so stack frames and DevTools
|
|
58
|
+
* agree on one name. Use a stable per-app identity, e.g. `app-42.tsx`.
|
|
59
|
+
*/
|
|
60
|
+
filePath?: string;
|
|
61
|
+
/** `false` selects `react/jsx-dev-runtime`, giving richer component stacks. */
|
|
62
|
+
production?: boolean;
|
|
63
|
+
jsxRuntime?: 'automatic' | 'classic';
|
|
64
|
+
jsxImportSource?: string;
|
|
65
|
+
}
|
|
66
|
+
interface TransformResult {
|
|
67
|
+
code: string;
|
|
68
|
+
/** Lines inserted above the user's line 1. Added back during error mapping. */
|
|
69
|
+
linePrefixOffset: number;
|
|
70
|
+
/** True when the snippet was compiled as a bare expression. */
|
|
71
|
+
expression: boolean;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Replaces the built-in Sucrase pass - for server-precompiled output, or a
|
|
75
|
+
* different transpiler entirely.
|
|
76
|
+
*/
|
|
77
|
+
type TransformFn = (source: string, options: Required<TranspileOptions>) => TransformResult | Promise<TransformResult>;
|
|
78
|
+
interface CompileOptions extends TranspileOptions {
|
|
79
|
+
modules?: ModuleRegistry;
|
|
80
|
+
scope?: LiveScope;
|
|
81
|
+
transform?: TransformFn;
|
|
82
|
+
/**
|
|
83
|
+
* Resolve `pkg/Sub` against a registered `pkg` by walking the remaining
|
|
84
|
+
* segments as property accesses. Correct for barrel-shaped packages, wrong
|
|
85
|
+
* for those whose subpaths are not re-exported - so it is opt-in rather than
|
|
86
|
+
* a silent guess. Default false.
|
|
87
|
+
*/
|
|
88
|
+
resolveSubpaths?: boolean;
|
|
89
|
+
/** Aborts a compile whose result is no longer wanted. */
|
|
90
|
+
signal?: AbortSignal;
|
|
91
|
+
}
|
|
92
|
+
interface CompileResult {
|
|
93
|
+
renderable: LiveRenderable;
|
|
94
|
+
via: ExtractionSource;
|
|
95
|
+
/** The transpiled JavaScript, for debugging and for the server cache. */
|
|
96
|
+
code: string;
|
|
97
|
+
/** Every module specifier the snippet imported, sorted. */
|
|
98
|
+
imports: readonly string[];
|
|
99
|
+
}
|
|
100
|
+
interface CompileModuleResult {
|
|
101
|
+
/** Everything the snippet exported. */
|
|
102
|
+
exports: Record<string, unknown>;
|
|
103
|
+
/** The transpiled JavaScript. */
|
|
104
|
+
code: string;
|
|
105
|
+
/** Every module specifier the snippet imported, sorted. */
|
|
106
|
+
imports: readonly string[];
|
|
107
|
+
}
|
|
108
|
+
interface CompileSuccessInfo {
|
|
109
|
+
compileId: number;
|
|
110
|
+
imports: readonly string[];
|
|
111
|
+
via?: ExtractionSource;
|
|
112
|
+
durationMs: number;
|
|
113
|
+
}
|
|
114
|
+
interface UseLiveRunnerOptions extends CompileOptions {
|
|
115
|
+
code: string;
|
|
116
|
+
/**
|
|
117
|
+
* Called when the code is edited from inside, by `<LiveEditor>`, or via
|
|
118
|
+
* `setCode`. Not called when the `code` prop changes from outside, which
|
|
119
|
+
* would otherwise echo your own updates back at you.
|
|
120
|
+
*
|
|
121
|
+
* This is what a control panel needs to persist an author's edits.
|
|
122
|
+
*/
|
|
123
|
+
onCodeChange?: (code: string) => void;
|
|
124
|
+
/** Called after every successful compile. Not called on failure or abort. */
|
|
125
|
+
onCompileSuccess?: (info: CompileSuccessInfo) => void;
|
|
126
|
+
/** Milliseconds to wait after a change before recompiling. Default 150. */
|
|
127
|
+
debounce?: number;
|
|
128
|
+
/**
|
|
129
|
+
* Keep the last working component mounted when a recompile fails, instead
|
|
130
|
+
* of blanking the preview on every half-typed keystroke. Default true.
|
|
131
|
+
*/
|
|
132
|
+
keepLastGood?: boolean;
|
|
133
|
+
/** Renders per second before the loop breaker trips. Default 1000. */
|
|
134
|
+
maxRendersPerSecond?: number;
|
|
135
|
+
}
|
|
136
|
+
interface LiveRunnerState {
|
|
137
|
+
code: string;
|
|
138
|
+
setCode: (code: string) => void;
|
|
139
|
+
/** Null until the first successful compile - including during SSR. */
|
|
140
|
+
Component: ComponentType<Record<string, unknown>> | null;
|
|
141
|
+
element: ReactElement | null;
|
|
142
|
+
error: Error | null;
|
|
143
|
+
isCompiling: boolean;
|
|
144
|
+
/** Changes on every successful compile; use as a remount `key`. */
|
|
145
|
+
compileId: number;
|
|
146
|
+
}
|
|
147
|
+
interface LiveContextValue extends LiveRunnerState {
|
|
148
|
+
/** Props forwarded into the rendered component. */
|
|
149
|
+
props: Record<string, unknown>;
|
|
150
|
+
language: string;
|
|
151
|
+
/** Called by the error boundary when rendering the snippet throws. */
|
|
152
|
+
reportRuntimeError: (error: Error) => void;
|
|
153
|
+
fallback: ReactNode;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
interface LiveProviderProps extends Omit<UseLiveRunnerOptions, 'code'> {
|
|
157
|
+
/**
|
|
158
|
+
* The snippet to run. Treated as controlled: when it changes - because a new
|
|
159
|
+
* app was fetched from an API, say, the preview follows.
|
|
160
|
+
*/
|
|
161
|
+
code: string;
|
|
162
|
+
/** Props forwarded into the rendered component. Passed by reference. */
|
|
163
|
+
props?: Record<string, unknown>;
|
|
164
|
+
/** Language hint for `<LiveEditor>` highlighting. Default 'tsx'. */
|
|
165
|
+
language?: string;
|
|
166
|
+
/** Notified on every compile and runtime error. */
|
|
167
|
+
onError?: (error: Error) => void;
|
|
168
|
+
/** Rendered by `<LivePreview>` until the first compile finishes. */
|
|
169
|
+
fallback?: ReactNode;
|
|
170
|
+
children?: ReactNode;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Provides a compiled snippet to `<LiveEditor>`, `<LivePreview>`, and
|
|
174
|
+
* `<LiveError>`.
|
|
175
|
+
*
|
|
176
|
+
* Safe to render from a Server Component: nothing is compiled during the
|
|
177
|
+
* server pass, and the first client render matches the server output exactly.
|
|
178
|
+
*/
|
|
179
|
+
declare function LiveProvider(props: LiveProviderProps): ReactNode;
|
|
180
|
+
|
|
181
|
+
interface LivePreviewProps {
|
|
182
|
+
/** Element to wrap the preview in. Default 'div'. */
|
|
183
|
+
as?: ElementType;
|
|
184
|
+
className?: string;
|
|
185
|
+
style?: React.CSSProperties;
|
|
186
|
+
/**
|
|
187
|
+
* Props handed to the compiled component, merged over the provider's.
|
|
188
|
+
* Passed by reference, so live objects - a map view, a store, the active
|
|
189
|
+
* user, arrive intact rather than serialized.
|
|
190
|
+
*/
|
|
191
|
+
props?: Record<string, unknown>;
|
|
192
|
+
/** Shown until the first compile finishes. Overrides the provider's. */
|
|
193
|
+
fallback?: ReactNode;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Renders the compiled component.
|
|
197
|
+
*
|
|
198
|
+
* Until the first compile lands this renders `fallback` - on the server and on
|
|
199
|
+
* the client's first pass alike, which is precisely why hydration cannot
|
|
200
|
+
* mismatch. Give it a skeleton of roughly the right size to avoid layout shift.
|
|
201
|
+
*/
|
|
202
|
+
declare function LivePreview(props: LivePreviewProps): ReactNode;
|
|
203
|
+
|
|
204
|
+
interface LiveErrorProps {
|
|
205
|
+
as?: ElementType;
|
|
206
|
+
className?: string;
|
|
207
|
+
style?: React.CSSProperties;
|
|
208
|
+
/** Replaces the default rendering entirely. */
|
|
209
|
+
children?: (error: Error) => ReactNode;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Displays the current compile or runtime error, and renders nothing when the
|
|
213
|
+
* snippet is healthy.
|
|
214
|
+
*/
|
|
215
|
+
declare function LiveError$1(props: LiveErrorProps): ReactNode;
|
|
216
|
+
|
|
217
|
+
interface LiveErrorBoundaryProps {
|
|
218
|
+
children: ReactNode;
|
|
219
|
+
onError: (error: Error) => void;
|
|
220
|
+
/**
|
|
221
|
+
* Changing this resets the boundary. Wire it to the compile id so fixing a
|
|
222
|
+
* broken snippet recovers on its own, with no manual retry.
|
|
223
|
+
*/
|
|
224
|
+
resetKey: unknown;
|
|
225
|
+
fallback?: ReactNode;
|
|
226
|
+
}
|
|
227
|
+
interface State {
|
|
228
|
+
error: Error | null;
|
|
229
|
+
resetKey: unknown;
|
|
230
|
+
/** True after resetKey changes — allows one render attempt before falling back again. */
|
|
231
|
+
recovering: boolean;
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Contains failures in evaluated code so a bad snippet cannot take down the
|
|
235
|
+
* host application.
|
|
236
|
+
*
|
|
237
|
+
* React recovers from an error by client-rendering the whole nearest boundary,
|
|
238
|
+
* so this one is kept as tight around the preview as possible - anything else
|
|
239
|
+
* on the page is unaffected.
|
|
240
|
+
*/
|
|
241
|
+
declare class LiveErrorBoundary extends Component<LiveErrorBoundaryProps, State> {
|
|
242
|
+
private mounted;
|
|
243
|
+
constructor(props: LiveErrorBoundaryProps);
|
|
244
|
+
static getDerivedStateFromError(error: Error): Partial<State>;
|
|
245
|
+
static getDerivedStateFromProps(props: LiveErrorBoundaryProps, state: State): Partial<State> | null;
|
|
246
|
+
componentDidCatch(error: Error, info: ErrorInfo): void;
|
|
247
|
+
componentDidUpdate(_prevProps: LiveErrorBoundaryProps, prevState: State): void;
|
|
248
|
+
componentWillUnmount(): void;
|
|
249
|
+
render(): ReactNode;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* The headless engine behind `<LiveProvider>` - compiles a snippet and hands
|
|
254
|
+
* back a component, for hosts building their own UI.
|
|
255
|
+
*
|
|
256
|
+
* Compilation never runs during render or on the server. The first client
|
|
257
|
+
* render produces exactly what the server produced (no component, no error),
|
|
258
|
+
* so there is nothing for React to find mismatched during hydration; the
|
|
259
|
+
* compile starts afterwards, in an effect.
|
|
260
|
+
*
|
|
261
|
+
* For snippets that are not components, use {@link useLiveModule}.
|
|
262
|
+
*/
|
|
263
|
+
declare function useLiveRunner(options: UseLiveRunnerOptions): LiveRunnerState;
|
|
264
|
+
|
|
265
|
+
interface UseLiveModuleOptions extends Omit<UseLiveRunnerOptions, 'maxRendersPerSecond'> {
|
|
266
|
+
}
|
|
267
|
+
interface LiveModuleState<T extends Record<string, unknown> = Record<string, unknown>> {
|
|
268
|
+
code: string;
|
|
269
|
+
setCode: (code: string) => void;
|
|
270
|
+
/** Everything the snippet exported. Null until the first successful run. */
|
|
271
|
+
exports: T | null;
|
|
272
|
+
/** Shorthand for `exports?.default`. */
|
|
273
|
+
value: unknown;
|
|
274
|
+
error: Error | null;
|
|
275
|
+
isCompiling: boolean;
|
|
276
|
+
/** Increments on every successful run. */
|
|
277
|
+
compileId: number;
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Runs a snippet and returns its exports, without requiring a React component.
|
|
281
|
+
*
|
|
282
|
+
* Use it for stored code that is not UI - a validator, a data transformer, a
|
|
283
|
+
* calculated field, a config builder:
|
|
284
|
+
*
|
|
285
|
+
* ```ts
|
|
286
|
+
* const { exports } = useLiveModule({ code: rule.source, modules });
|
|
287
|
+
* const isValid = exports?.validate?.(input);
|
|
288
|
+
* ```
|
|
289
|
+
*
|
|
290
|
+
* Shares its scheduling with `useLiveRunner`, so the SSR, debounce, and
|
|
291
|
+
* keep-last-good behaviour is identical: nothing runs during the server pass,
|
|
292
|
+
* and a failed recompile leaves the previous exports in place.
|
|
293
|
+
*
|
|
294
|
+
* The same caveat applies as everywhere else in this library, the snippet runs
|
|
295
|
+
* with your page's full authority, so its author must be someone you trust.
|
|
296
|
+
*/
|
|
297
|
+
declare function useLiveModule<T extends Record<string, unknown> = Record<string, unknown>>(options: UseLiveModuleOptions): LiveModuleState<T>;
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Reads the surrounding `<LiveProvider>`. Use it to build custom editors,
|
|
301
|
+
* toolbars, or status indicators that stay in sync with the preview.
|
|
302
|
+
*/
|
|
303
|
+
declare function useLiveContext(): LiveContextValue;
|
|
304
|
+
|
|
305
|
+
declare const LiveContext: Context<LiveContextValue | null>;
|
|
306
|
+
|
|
307
|
+
/** Base class for every error next-live raises. */
|
|
308
|
+
declare class LiveError extends Error {
|
|
309
|
+
constructor(message: string, options?: {
|
|
310
|
+
cause?: unknown;
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
/** The snippet could not be parsed or transpiled. */
|
|
314
|
+
declare class LiveCompileError extends LiveError {
|
|
315
|
+
readonly line: number | undefined;
|
|
316
|
+
readonly column: number | undefined;
|
|
317
|
+
constructor(message: string, position?: {
|
|
318
|
+
line?: number;
|
|
319
|
+
column?: number;
|
|
320
|
+
}, cause?: unknown);
|
|
321
|
+
}
|
|
322
|
+
/** The snippet threw while being evaluated or rendered. */
|
|
323
|
+
declare class LiveRuntimeError extends LiveError {
|
|
324
|
+
}
|
|
325
|
+
/** A render loop was detected and stopped. */
|
|
326
|
+
declare class RenderLoopError extends LiveRuntimeError {
|
|
327
|
+
}
|
|
328
|
+
/** `import` referenced a specifier that is not in the registry. */
|
|
329
|
+
declare class ModuleNotFoundError extends LiveError {
|
|
330
|
+
readonly specifier: string;
|
|
331
|
+
readonly available: readonly string[];
|
|
332
|
+
constructor(specifier: string, available: readonly string[]);
|
|
333
|
+
}
|
|
334
|
+
/** The snippet compiled and ran but produced nothing renderable. */
|
|
335
|
+
declare class NoComponentError extends LiveError {
|
|
336
|
+
}
|
|
337
|
+
/** Sucrase could not be loaded (usually a chunk-load failure). */
|
|
338
|
+
declare class TranspilerLoadError extends LiveError {
|
|
339
|
+
constructor(cause: unknown);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
interface CompileInput extends CompileOptions {
|
|
343
|
+
code: string;
|
|
344
|
+
/** Reports each render of the compiled component to the loop breaker. */
|
|
345
|
+
onRender?: () => void;
|
|
346
|
+
}
|
|
347
|
+
/**
|
|
348
|
+
* Compiles and evaluates a snippet, returning something renderable.
|
|
349
|
+
*
|
|
350
|
+
* The stages run in a fixed order for one reason: Sucrase emits synchronous
|
|
351
|
+
* `require()` calls, so every module - including any registered as an async
|
|
352
|
+
* loader, must be resolved *before* evaluation begins.
|
|
353
|
+
*/
|
|
354
|
+
declare function compile(input: CompileInput): Promise<CompileResult>;
|
|
355
|
+
/**
|
|
356
|
+
* Compiles and runs a snippet, returning its exports rather than a component.
|
|
357
|
+
*
|
|
358
|
+
* Use this for stored code that is not UI, a validator, a data transformer, a
|
|
359
|
+
* calculated field. {@link compile} is the right call when you need something
|
|
360
|
+
* to render; this one makes no such demand and will happily return
|
|
361
|
+
* `{ validate, schema }`.
|
|
362
|
+
*/
|
|
363
|
+
declare function compileModule(input: CompileInput): Promise<CompileModuleResult>;
|
|
364
|
+
|
|
365
|
+
type SucraseModule = typeof sucrase;
|
|
366
|
+
/**
|
|
367
|
+
* Starts fetching the transpiler chunk ahead of time. Worth calling on idle or
|
|
368
|
+
* on route prefetch so the first compile is not gated on a network round trip.
|
|
369
|
+
*/
|
|
370
|
+
declare function preloadTranspiler(): void;
|
|
371
|
+
/** Replaces the loaded transpiler. Intended for tests and custom backends. */
|
|
372
|
+
declare function setTranspiler(module: SucraseModule | null): void;
|
|
373
|
+
/**
|
|
374
|
+
* Transpiles a snippet to CommonJS that `new Function` can evaluate.
|
|
375
|
+
*
|
|
376
|
+
* Three authoring styles are supported, resolved without ever asking the user
|
|
377
|
+
* which one they used:
|
|
378
|
+
*
|
|
379
|
+
* 1. A real module - `export default function App() {}`.
|
|
380
|
+
* 2. A bare expression, `<div/>` or `() => <div/>`.
|
|
381
|
+
* 3. Bare statements with no export, `function App() {}`, or `render(<App/>)`.
|
|
382
|
+
*
|
|
383
|
+
* Styles 1 and 3 are compiled as-is; the component is recovered after
|
|
384
|
+
* evaluation (see `evaluate.ts`). Style 2 is not a valid module body on its
|
|
385
|
+
* own, so it is wrapped in `export default (...)`.
|
|
386
|
+
*/
|
|
387
|
+
declare function transpile(source: string, options?: TranspileOptions, transform?: TransformFn): Promise<TransformResult>;
|
|
388
|
+
/**
|
|
389
|
+
* Wraps an already-compiled result as a `transform` function, so the client
|
|
390
|
+
* skips loading Sucrase entirely:
|
|
391
|
+
*
|
|
392
|
+
* ```tsx
|
|
393
|
+
* <LiveProvider code={source} transform={precompiledTransform(compiled)} />
|
|
394
|
+
* ```
|
|
395
|
+
*
|
|
396
|
+
* Lives here rather than next-live/server on purpose. It is a pure closure over
|
|
397
|
+
* a value and needs no transpiler - but importing it from the server entry
|
|
398
|
+
* would pull Sucrase statically into the page bundle, which is the exact cost
|
|
399
|
+
* precompiling exists to avoid.
|
|
400
|
+
*/
|
|
401
|
+
declare function precompiledTransform(result: TransformResult): () => TransformResult;
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Modules every snippet can import without the host registering anything.
|
|
405
|
+
*
|
|
406
|
+
* The two JSX runtimes are not optional: with `jsxRuntime: 'automatic'`
|
|
407
|
+
* Sucrase compiles every JSX element into a `react/jsx-runtime` (or
|
|
408
|
+
* `react/jsx-dev-runtime`) call, so without these entries *every* snippet
|
|
409
|
+
* fails on its first tag.
|
|
410
|
+
*
|
|
411
|
+
* These are static imports so evaluated code shares the host's single React
|
|
412
|
+
* instance - hooks and context work across the boundary, which is the whole
|
|
413
|
+
* point of evaluating in the host realm. Host `modules` merge over these, so
|
|
414
|
+
* a React shim can still be substituted.
|
|
415
|
+
*
|
|
416
|
+
* `react-dom` is deliberately absent: snippets rarely need it, and a host that
|
|
417
|
+
* does can register it explicitly.
|
|
418
|
+
*/
|
|
419
|
+
declare const builtinModules: ModuleRegistry;
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Wraps a function so the registry treats it as a lazy loader instead of as
|
|
423
|
+
* the module value. Without this a registered component function would be
|
|
424
|
+
* indistinguishable from a loader.
|
|
425
|
+
*
|
|
426
|
+
* ```ts
|
|
427
|
+
* { 'heavy-chart': defineLoader(() => import('heavy-chart')) }
|
|
428
|
+
* ```
|
|
429
|
+
*/
|
|
430
|
+
declare function defineLoader(load: ModuleLoader): ModuleLoader;
|
|
431
|
+
/**
|
|
432
|
+
* Builds an explicit module record, for the shape that cannot be inferred.
|
|
433
|
+
*
|
|
434
|
+
* {@link normalizeModule} unwraps any registered object that has its own
|
|
435
|
+
* `default` key, because that is almost always a module wrapper. When it is
|
|
436
|
+
* not - a config object that happens to contain the word `default`, say so:
|
|
437
|
+
*
|
|
438
|
+
* ```ts
|
|
439
|
+
* { './theme': defineModule({ default: { default: 'dark', light: '#fff' } }) }
|
|
440
|
+
* // import theme from './theme' → the whole object
|
|
441
|
+
* ```
|
|
442
|
+
*
|
|
443
|
+
* `default` and `exports.default` address the same slot, because ESM makes no
|
|
444
|
+
* distinction between a default export and a named export called `default`.
|
|
445
|
+
* A top-level `default` wins if both are given.
|
|
446
|
+
*/
|
|
447
|
+
declare function defineModule(shape: {
|
|
448
|
+
default?: unknown;
|
|
449
|
+
exports?: Record<string, unknown>;
|
|
450
|
+
}): NormalizedModule;
|
|
451
|
+
/**
|
|
452
|
+
* Converts a registered value into a record Sucrase's interop helpers accept.
|
|
453
|
+
*
|
|
454
|
+
* The trick that makes this total: both helpers Sucrase emits are identity
|
|
455
|
+
* functions when the required value carries `__esModule === true` -
|
|
456
|
+
* `_interopRequireDefault` is literally `obj && obj.__esModule ? obj : {default: obj}`.
|
|
457
|
+
* By always returning such a record we neutralise both helpers, so this
|
|
458
|
+
* function becomes the single source of truth for what `default` and each
|
|
459
|
+
* named import resolve to.
|
|
460
|
+
*
|
|
461
|
+
* Named exports are exposed as *getters* over the original value rather than
|
|
462
|
+
* copied. That preserves ES module live bindings, and - more importantly in
|
|
463
|
+
* practice, avoids eagerly invoking the lazy namespace getters that packages
|
|
464
|
+
* like icon sets and large UI barrels use, which a naive spread would trigger
|
|
465
|
+
* on every single compile.
|
|
466
|
+
*/
|
|
467
|
+
declare function normalizeModule(value: ModuleValue): NormalizedModule;
|
|
468
|
+
/** The resolved, synchronously-readable module table handed to a snippet. */
|
|
469
|
+
interface ResolvedModules {
|
|
470
|
+
get(specifier: string): NormalizedModule | undefined;
|
|
471
|
+
readonly keys: readonly string[];
|
|
472
|
+
}
|
|
473
|
+
interface ResolveOptions {
|
|
474
|
+
registry: ModuleRegistry;
|
|
475
|
+
/** Specifiers found in the compiled output. */
|
|
476
|
+
specifiers: Iterable<string>;
|
|
477
|
+
/**
|
|
478
|
+
* Resolve `@scope/pkg/Sub` against a registered `@scope/pkg` by walking the
|
|
479
|
+
* remaining segments as property accesses. Correct for barrel-shaped
|
|
480
|
+
* packages, wrong for those whose subpaths are not re-exported - so it is
|
|
481
|
+
* opt-in rather than a silent guess. Default false.
|
|
482
|
+
*/
|
|
483
|
+
resolveSubpaths?: boolean;
|
|
484
|
+
signal?: AbortSignal;
|
|
485
|
+
}
|
|
486
|
+
/**
|
|
487
|
+
* Resolves every specifier a snippet needs *before* evaluation, because
|
|
488
|
+
* Sucrase emits synchronous `require()` calls that cannot await anything.
|
|
489
|
+
*/
|
|
490
|
+
declare function resolveModules(options: ResolveOptions): Promise<ResolvedModules>;
|
|
491
|
+
/**
|
|
492
|
+
* The synchronous `require` shim handed to compiled snippets. Sucrase emits
|
|
493
|
+
* `var _react = require('react')` at module top level, so this must never
|
|
494
|
+
* return a promise.
|
|
495
|
+
*/
|
|
496
|
+
declare function createRequire(resolved: ResolvedModules): (specifier: string) => NormalizedModule;
|
|
497
|
+
|
|
498
|
+
/**
|
|
499
|
+
* Merges registry groups into one, later groups winning.
|
|
500
|
+
*
|
|
501
|
+
* A large SDK surface is best kept as several small files - one per domain -
|
|
502
|
+
* rather than a single object that grows without bound. This composes them.
|
|
503
|
+
*
|
|
504
|
+
* ```ts
|
|
505
|
+
* export const liveModules = createRegistry(vendorModules, storeModules, uiModules);
|
|
506
|
+
* ```
|
|
507
|
+
*
|
|
508
|
+
* A key defined by two groups is almost always a mistake rather than an
|
|
509
|
+
* intentional override, and it fails silently otherwise, so it warns in
|
|
510
|
+
* development.
|
|
511
|
+
*/
|
|
512
|
+
declare function createRegistry(...groups: ModuleRegistry[]): ModuleRegistry;
|
|
513
|
+
/** The lazy shape `import.meta.glob()` returns: path → thunk. */
|
|
514
|
+
type GlobResult = Record<string, () => Promise<unknown>>;
|
|
515
|
+
/**
|
|
516
|
+
* Builds a registry from a directory of files, so the registry stops needing
|
|
517
|
+
* hand-maintenance as the codebase grows.
|
|
518
|
+
*
|
|
519
|
+
* Pass the *result* of `import.meta.glob` - the call has to stay in your own
|
|
520
|
+
* code, because bundlers resolve the pattern statically at the call site.
|
|
521
|
+
* (`import.meta.glob` requires Turbopack; under webpack, build an equivalent
|
|
522
|
+
* `{ path: () => import(path) }` object yourself.)
|
|
523
|
+
*
|
|
524
|
+
* ```ts
|
|
525
|
+
* export const storeModules = registryFromGlob(
|
|
526
|
+
* import.meta.glob('../stores/*.ts'),
|
|
527
|
+
* (path) => `@app/store/${path.split('/').pop()!.replace(/\.tsx?$/, '')}`,
|
|
528
|
+
* );
|
|
529
|
+
* // → { '@app/store/cart': <loader>, '@app/store/user': <loader>, … }
|
|
530
|
+
* ```
|
|
531
|
+
*
|
|
532
|
+
* Every entry is a loader, so none of these files is fetched until a snippet
|
|
533
|
+
* imports it.
|
|
534
|
+
*
|
|
535
|
+
* @param glob Result of `import.meta.glob(pattern)`, left lazy.
|
|
536
|
+
* @param toSpecifier Maps a file path to the specifier snippet authors write.
|
|
537
|
+
* Return `null` to leave a file out of the registry.
|
|
538
|
+
*/
|
|
539
|
+
declare function registryFromGlob(glob: GlobResult, toSpecifier: (path: string) => string | null): ModuleRegistry;
|
|
540
|
+
|
|
541
|
+
interface RenderBudgetOptions {
|
|
542
|
+
/** Renders allowed inside one window before the breaker trips. Default 1000. */
|
|
543
|
+
maxRenders?: number;
|
|
544
|
+
/** Length of the rolling window in milliseconds. Default 1000. */
|
|
545
|
+
windowMs?: number;
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
548
|
+
* A circuit breaker for runaway re-renders.
|
|
549
|
+
*
|
|
550
|
+
* This is the one class of hang that *can* be caught in the host realm. A
|
|
551
|
+
* synchronous `while (true)` blocks the main thread and nothing - no timer, no
|
|
552
|
+
* AbortController, will ever run again. But the common real failure in
|
|
553
|
+
* authored code is not that: it is `setState` during render, or an effect with
|
|
554
|
+
* a bad dependency array, which React executes as a rapid *sequence* of
|
|
555
|
+
* renders. Between them the breaker gets to run, so it can stop the loop.
|
|
556
|
+
*
|
|
557
|
+
* The throw happens during render so the nearest error boundary catches it and
|
|
558
|
+
* the host application stays alive.
|
|
559
|
+
*/
|
|
560
|
+
declare function createRenderBudget(options?: RenderBudgetOptions): () => void;
|
|
561
|
+
|
|
562
|
+
interface PositionedError extends Error {
|
|
563
|
+
line?: number;
|
|
564
|
+
column?: number;
|
|
565
|
+
}
|
|
566
|
+
/**
|
|
567
|
+
* Reads a 1-based line/column from a compile or runtime error, if present.
|
|
568
|
+
*
|
|
569
|
+
* Accepts a nullish value and returns null for it. The state this is meant to
|
|
570
|
+
* be fed from - `LiveRunnerState.error` and `LiveContextValue.error` - is
|
|
571
|
+
* `Error | null`, so `errorPosition(error)` is the natural thing to write in a
|
|
572
|
+
* custom editor. TypeScript rejects that, but a JavaScript host would have hit
|
|
573
|
+
* a TypeError instead of the "no position" answer the name promises.
|
|
574
|
+
*/
|
|
575
|
+
declare function errorPosition(error: Error | null | undefined): {
|
|
576
|
+
line: number;
|
|
577
|
+
column?: number;
|
|
578
|
+
} | null;
|
|
579
|
+
|
|
580
|
+
export { type CompileInput, type CompileModuleResult, type CompileOptions, type CompileResult, type CompileSuccessInfo, type ExtractionSource, type GlobResult, LiveCompileError, LiveContext, type LiveContextValue, LiveError$1 as LiveError, LiveError as LiveErrorBase, LiveErrorBoundary, type LiveErrorBoundaryProps, type LiveErrorProps, type LiveModuleState, LivePreview, type LivePreviewProps, LiveProvider, type LiveProviderProps, type LiveRenderable, type LiveRunnerState, LiveRuntimeError, type LiveScope, type ModuleLoader, ModuleNotFoundError, type ModuleRegistry, type ModuleValue, NoComponentError, type NormalizedModule, type PositionedError, type RenderBudgetOptions, RenderLoopError, type TransformFn, type TransformResult, type TranspileOptions, TranspilerLoadError, type UseLiveModuleOptions, type UseLiveRunnerOptions, builtinModules, compile, compileModule, createRegistry, createRenderBudget, createRequire, defineLoader, defineModule, errorPosition, normalizeModule, precompiledTransform, preloadTranspiler, registryFromGlob, resolveModules, setTranspiler, transpile, useLiveContext, useLiveModule, useLiveRunner };
|