react-code-canvas
Version:
A browser-based canvas for rendering React components at runtime
70 lines (69 loc) • 3.45 kB
TypeScript
type Scope = Record<string, any>;
/**
* Everything cheap enough to ship in the first chunk. React is a peer
* dependency (already on the page), the edit helpers are our own code, and
* react-helmet-async is ~6 KB -- not worth a round trip.
*/
export declare const baseScope: Scope;
/**
* `PascalCase` -> `kebab-case`. Must stay in step with the same function in
* scripts/generate-scope-maps.mjs, which is what decided the ICON_LOADERS keys.
*/
export declare function toKebab(name: string): string;
/**
* Every identifier-shaped token in the source.
*
* Deliberately a regex and not a parse: this runs before evaluation on every
* code change, and the only consequence of over-matching is loading a module
* that turns out to be unused. A word inside a string or comment can trigger a
* needless fetch; nothing breaks.
*
* Under-matching would break things, and cannot happen -- every name the code
* can reference appears literally in the source. Names assembled at runtime
* (`scope['Ac' + 'tivity']`) were never resolvable here anyway, because the
* scope is passed as `new Function` parameters.
*/
export declare function collectIdentifiers(code: string): Set<string>;
/** The dynamic import for an icon name, or undefined if it is not a lucide icon. */
export declare function iconLoaderFor(name: string): (() => Promise<any>) | undefined;
/**
* Copy a module namespace into the scope, skipping `default`.
*
* A namespace object always carries `default`, which is a reserved word and so
* cannot become a `new Function` parameter. The generated name lists exclude it
* for the same reason -- otherwise the `default` in every `export default`
* would look like a reference to recharts.
*/
export declare function assignNamespace(scope: Scope, mod: Record<string, unknown>): void;
/** True if the name is a lucide icon, without loading anything. */
export declare const isIconName: (name: string) => boolean;
/** True if the name comes from recharts. */
export declare const isRechartsName: (name: string) => boolean;
/** True if the name comes from motion/react. */
export declare const isMotionName: (name: string) => boolean;
/** True if the name is a Font Awesome icon from react-icons/fa. */
export declare const isFaName: (name: string) => boolean;
/**
* The names `resolveScope` would put in scope for this code, without loading
* anything. Mirrors resolveScope exactly, because it is what decides the
* `new Function` parameter list -- and therefore which top-level declarations
* in the code count as duplicates.
*/
export declare function scopeNamesFor(code: string, extra?: string[]): string[];
/**
* Build the scope the given code actually needs.
*
* The old behaviour was to spread every export of lucide-react (5670 names),
* recharts and motion into one object at module load. That forced ~300 KB
* (brotli) into the first chunk regardless of what the code used, and handed
* ~5800 parameters to `new Function` on every single evaluation.
*
* Here each group is fetched only when the code references it: icons one file
* at a time (~0.7 KB each), recharts and motion whole (they are not usefully
* splittable). Repeat calls are free -- the browser caches resolved modules, so
* an already-loaded icon resolves in a microtask.
*
* `extra` is merged last so a caller-supplied scope always wins.
*/
export declare function resolveScope(code: string, extra?: Scope): Promise<Scope>;
export {};