@pyreon/mcp 0.12.10 → 0.12.11
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/lib/analysis/index.js.html +1 -1
- package/lib/index.js +177 -20
- package/lib/index.js.map +1 -1
- package/package.json +2 -2
- package/src/api-reference.ts +191 -19
package/lib/index.js
CHANGED
|
@@ -12750,7 +12750,7 @@ var StdioServerTransport = class {
|
|
|
12750
12750
|
|
|
12751
12751
|
//#endregion
|
|
12752
12752
|
//#region package.json
|
|
12753
|
-
var version = "0.12.
|
|
12753
|
+
var version = "0.12.11";
|
|
12754
12754
|
|
|
12755
12755
|
//#endregion
|
|
12756
12756
|
//#region src/api-reference.ts
|
|
@@ -13307,7 +13307,10 @@ theme.remove() // delete from storage`,
|
|
|
13307
13307
|
},
|
|
13308
13308
|
"i18n/createI18n": {
|
|
13309
13309
|
signature: "createI18n(options: { locale: string, messages: Record<string, Record<string, string>>, loader?, fallbackLocale?, pluralRules? }): I18nInstance",
|
|
13310
|
-
example:
|
|
13310
|
+
example: `// Full entry — includes JSX components (Trans, I18nProvider, useI18n)
|
|
13311
|
+
import { createI18n, useI18n } from '@pyreon/i18n'
|
|
13312
|
+
|
|
13313
|
+
const i18n = createI18n({
|
|
13311
13314
|
locale: 'en',
|
|
13312
13315
|
messages: { en: { greeting: 'Hello, {{name}}!' } },
|
|
13313
13316
|
loader: (locale, ns) => import(\`./locales/\${locale}/\${ns}.json\`),
|
|
@@ -13315,8 +13318,18 @@ theme.remove() // delete from storage`,
|
|
|
13315
13318
|
|
|
13316
13319
|
const { t, locale } = useI18n()
|
|
13317
13320
|
t('greeting', { name: 'World' }) // "Hello, World!"
|
|
13318
|
-
locale.set('fr') // switch reactively
|
|
13319
|
-
|
|
13321
|
+
locale.set('fr') // switch reactively
|
|
13322
|
+
|
|
13323
|
+
// Backend / non-JSX entry — @pyreon/i18n/core
|
|
13324
|
+
// Zero JSX dependencies, transitively only @pyreon/reactivity.
|
|
13325
|
+
// Use this on backends, edge workers, non-Pyreon frontends.
|
|
13326
|
+
import { createI18n } from '@pyreon/i18n/core'
|
|
13327
|
+
const backendI18n = createI18n({ locale: 'en', messages: { en: { hello: 'Hi' } } })
|
|
13328
|
+
backendI18n.t('hello')`,
|
|
13329
|
+
notes: "Interpolation with {{name}}, pluralization with _one/_other suffixes. Namespace lazy loading. <Trans> component for rich JSX interpolation. TWO ENTRY POINTS: `@pyreon/i18n` (full, with JSX components) vs `@pyreon/i18n/core` (framework-agnostic, zero JSX deps — use for backends and non-Pyreon consumers). Both return identical I18nInstance objects.",
|
|
13330
|
+
mistakes: `- Using \`@pyreon/i18n\` (the main entry) on a backend without a JSX-aware tsconfig — the bun condition resolves to source which transitively includes the Trans JSX component. Use \`@pyreon/i18n/core\` instead.
|
|
13331
|
+
- Reading the README example and importing from \`@pyreon/i18n\` in a non-Pyreon project — that path works for Pyreon UIs but the README now documents \`/core\` as the backend recommendation.
|
|
13332
|
+
- Trying to use \`<Trans>\` from \`@pyreon/i18n/core\` — it's intentionally not exported there. Import it from the main \`@pyreon/i18n\` entry instead.`
|
|
13320
13333
|
},
|
|
13321
13334
|
"document/createDocument": {
|
|
13322
13335
|
signature: "createDocument(props?: DocumentProps): DocumentBuilder",
|
|
@@ -13332,37 +13345,113 @@ await doc.toNotion() // Notion blocks`,
|
|
|
13332
13345
|
notes: "14+ output formats. JSX primitives: Document, Page, Heading, Text, Table, Image, List, Code, etc. Heavy renderers lazy-loaded."
|
|
13333
13346
|
},
|
|
13334
13347
|
"flow/createFlow": {
|
|
13335
|
-
signature: "createFlow
|
|
13336
|
-
example:
|
|
13348
|
+
signature: "createFlow<TData = Record<string, unknown>>(config: FlowConfig<TData>): FlowInstance<TData>",
|
|
13349
|
+
example: `// Generic over node data shape — typed consumers get strong narrowing
|
|
13350
|
+
interface WorkflowData {
|
|
13351
|
+
kind: 'trigger' | 'filter' | 'transform' | 'notify'
|
|
13352
|
+
label: string
|
|
13353
|
+
}
|
|
13354
|
+
|
|
13355
|
+
const flow = createFlow<WorkflowData>({
|
|
13337
13356
|
nodes: [
|
|
13338
|
-
{ id: '1', position: { x: 0, y: 0 }, data: { label: 'Start' } },
|
|
13339
|
-
{ id: '2', position: { x: 200, y: 100 }, data: { label: 'End' } },
|
|
13357
|
+
{ id: '1', type: 'custom', position: { x: 0, y: 0 }, data: { kind: 'trigger', label: 'Start' } },
|
|
13358
|
+
{ id: '2', type: 'custom', position: { x: 200, y: 100 }, data: { kind: 'notify', label: 'End' } },
|
|
13340
13359
|
],
|
|
13341
|
-
edges: [{ id: 'e1', source: '1', target: '2' }],
|
|
13360
|
+
edges: [{ id: 'e1', source: '1', target: '2', animated: true }],
|
|
13342
13361
|
})
|
|
13343
13362
|
|
|
13344
|
-
|
|
13345
|
-
|
|
13363
|
+
// node.data.kind narrows to the typed union, not unknown
|
|
13364
|
+
const trigger = flow.findNodes((n) => n.data.kind === 'trigger')
|
|
13365
|
+
|
|
13366
|
+
flow.addNode({ id: '3', type: 'custom', position: { x: 100, y: 200 }, data: { kind: 'transform', label: 'New' } })
|
|
13367
|
+
await flow.layout('layered', { direction: 'RIGHT', nodeSpacing: 50, layerSpacing: 100 }) // auto-layout via lazy-loaded elkjs
|
|
13368
|
+
// LayoutOptions applicability: direction/layerSpacing/edgeRouting apply to layered/tree only;
|
|
13369
|
+
// force/stress/radial/box/rectpacking silently ignore them. nodeSpacing applies to all algorithms.
|
|
13370
|
+
const json = flow.toJSON(); flow.fromJSON(json) // round-trip serialization
|
|
13371
|
+
|
|
13372
|
+
// Custom node renderer — every prop except id is a REACTIVE ACCESSOR
|
|
13373
|
+
function CustomNode(props: NodeComponentProps<WorkflowData>) {
|
|
13374
|
+
return (
|
|
13375
|
+
<div
|
|
13376
|
+
class={() => (props.selected() ? 'selected' : '')}
|
|
13377
|
+
style={() => \`cursor: \${props.dragging() ? 'grabbing' : 'grab'}\`}
|
|
13378
|
+
>
|
|
13379
|
+
{() => props.data().label}
|
|
13380
|
+
</div>
|
|
13381
|
+
)
|
|
13382
|
+
}
|
|
13346
13383
|
|
|
13347
|
-
<Flow instance={flow}
|
|
13348
|
-
|
|
13384
|
+
<Flow instance={flow} nodeTypes={{ custom: CustomNode }}>
|
|
13385
|
+
<Background variant="dots" />
|
|
13386
|
+
<Controls />
|
|
13387
|
+
<MiniMap />
|
|
13388
|
+
</Flow>`,
|
|
13389
|
+
notes: "Signal-native nodes/edges. Generic over node data shape: createFlow<TData> returns FlowInstance<TData> so node.data.kind narrows correctly. Defaults to Record<string, unknown> if no generic supplied. NodeComponentProps has THREE reactive accessors — data: () => TData, selected: () => boolean, dragging: () => boolean — read inside reactive scopes so the node patches in place when ANY underlying state changes. Each node mounts EXACTLY ONCE across the lifetime of the graph regardless of how many drags, selection clicks, or updateNode mutations happen. Internally <Flow> uses <For> keyed by node.id plus per-node accessors that read live state from instance.nodes() — so a 60fps drag in a 1000-node graph is O(1) instead of O(N) per frame. Auto-layout via elkjs (lazy-loaded, ~1.4MB chunk only on first .layout() call). Pan/zoom via pointer events + CSS transforms. No D3. JSX components are NOT generic at the call site (<Flow<MyData> /> is invalid JSX) — FlowProps.instance is typed as FlowInstance<any> so typed consumers can pass FlowInstance<MyData> without casting.",
|
|
13390
|
+
mistakes: `- Forgetting to declare @pyreon/runtime-dom in consumer app deps — flow's JSX emits _tpl() which needs runtime-dom imports
|
|
13391
|
+
- Reading props.data, props.selected, or props.dragging as plain values — they're ALL accessors, call them: props.data().kind, props.selected(), props.dragging()
|
|
13392
|
+
- Calling props.data() OUTSIDE a reactive scope — captures the value once at component setup, defeating reactivity. Read it inside JSX expression thunks, effect, or computed: {() => props.data().label}
|
|
13393
|
+
- Adding [key: string]: unknown index signature to your node data interface — no longer needed now that createFlow is generic. Just pass createFlow<MyData>(...)
|
|
13394
|
+
- Using direction: 'row' on flow's containing layout — Pyreon Element accepts 'inline'|'rows'|'reverseInline'|'reverseRows', not 'row'
|
|
13395
|
+
- Setting LayoutOptions.direction (or layerSpacing, or edgeRouting) on a force/stress/radial/box/rectpacking layout and expecting a directional result — these options are namespaced under ELK's layered/tree pipelines and silently ignored by the geometric algorithms. Switch the algorithm to 'layered' or 'tree' if you need a directional layout.
|
|
13396
|
+
- Missing the <Flow nodeTypes={{ key: Component }}> registration — node.type strings dispatch to that map`
|
|
13349
13397
|
},
|
|
13350
13398
|
"code/createEditor": {
|
|
13351
|
-
signature: "createEditor(config: { value?: string, language?:
|
|
13399
|
+
signature: "createEditor(config: { value?: string, language?: EditorLanguage, theme?: EditorTheme, onChange?: (val: string) => void, minimap?: boolean, lineNumbers?: boolean, ... }): EditorInstance",
|
|
13352
13400
|
example: `const editor = createEditor({
|
|
13353
13401
|
value: '// hello',
|
|
13354
13402
|
language: 'typescript',
|
|
13355
13403
|
theme: 'dark',
|
|
13356
13404
|
minimap: true,
|
|
13405
|
+
onChange: (next) => console.log('user edit:', next),
|
|
13357
13406
|
})
|
|
13358
13407
|
|
|
13359
|
-
editor.value()
|
|
13408
|
+
editor.value() // reactive Signal<string>, read inside JSX/effects
|
|
13409
|
+
editor.value.set('new') // write back into CodeMirror
|
|
13410
|
+
editor.cursor() // computed { line, col }
|
|
13411
|
+
editor.lineCount() // computed
|
|
13360
13412
|
editor.goToLine(42)
|
|
13361
13413
|
editor.insert('new code')
|
|
13414
|
+
editor.setDiagnostics([{ from: 0, to: 5, severity: 'error', message: '...' }])
|
|
13415
|
+
|
|
13416
|
+
<CodeEditor instance={editor} style="height: 400px" />
|
|
13417
|
+
<DiffEditor original="old" modified="new" language="typescript" />`,
|
|
13418
|
+
notes: "Built on CodeMirror 6 (~250KB vs Monaco's ~2.5MB). 19 languages via lazy-loaded grammars (declared as optionalDependencies). Two-way binding: editor.value is a writable Signal — pass onChange for editor → external, set editor.value for external → editor. For external↔editor binding with built-in loop prevention, use the higher-level `bindEditorToSignal({ editor, signal, serialize, parse })` helper instead of hand-rolling the flag pattern. <CodeEditor> auto-mounts and cleans up on unmount.",
|
|
13419
|
+
mistakes: `- Forgetting to declare @pyreon/runtime-dom in consumer app deps — <CodeEditor> JSX emits _tpl() which needs runtime-dom imports
|
|
13420
|
+
- Hand-rolling the applyingFromExternal/applyingFromEditor flag pattern for two-way binding — use the bindEditorToSignal helper instead, it handles the loop prevention correctly and is tested
|
|
13421
|
+
- Calling editor methods before mount — they no-op safely but changes don't persist
|
|
13422
|
+
- Setting both vim: true and emacs: true — emacs wins`
|
|
13423
|
+
},
|
|
13424
|
+
"code/bindEditorToSignal": {
|
|
13425
|
+
signature: "bindEditorToSignal<T>(options: { editor: EditorInstance, signal: SignalLike<T>, serialize: (val: T) => string, parse: (text: string) => T | null, onParseError?: (err: Error) => void }): { dispose: () => void }",
|
|
13426
|
+
example: `import { bindEditorToSignal, createEditor } from '@pyreon/code'
|
|
13427
|
+
import { signal } from '@pyreon/reactivity'
|
|
13428
|
+
|
|
13429
|
+
interface Doc { name: string; count: number }
|
|
13430
|
+
const data = signal<Doc>({ name: 'Alice', count: 1 })
|
|
13431
|
+
|
|
13432
|
+
const editor = createEditor({
|
|
13433
|
+
value: JSON.stringify(data(), null, 2),
|
|
13434
|
+
language: 'json',
|
|
13435
|
+
})
|
|
13362
13436
|
|
|
13363
|
-
|
|
13364
|
-
|
|
13365
|
-
|
|
13437
|
+
const binding = bindEditorToSignal({
|
|
13438
|
+
editor,
|
|
13439
|
+
signal: data, // accepts Signal<T> or any SignalLike<T>
|
|
13440
|
+
serialize: (val) => JSON.stringify(val, null, 2),
|
|
13441
|
+
parse: (text) => {
|
|
13442
|
+
try { return JSON.parse(text) } catch { return null }
|
|
13443
|
+
},
|
|
13444
|
+
onParseError: (err) => console.warn(err.message),
|
|
13445
|
+
})
|
|
13446
|
+
|
|
13447
|
+
// Later, on unmount:
|
|
13448
|
+
binding.dispose()`,
|
|
13449
|
+
notes: "Replaces the recurring loop-prevention flag-pair boilerplate (applyingFromExternal / applyingFromEditor) that consumers had to hand-roll for two-way external↔editor binding. The helper manages both directions, breaks the format-on-input race via internal flags, catches parse errors, and returns a disposable. Accepts any SignalLike<T> (Pyreon Signal, custom store wrapper, etc.). The editor itself ALSO has internal CM↔signal loop guards — this helper adds the SECOND layer for the external↔editor boundary.",
|
|
13450
|
+
mistakes: `- Forgetting to call binding.dispose() on unmount — leaks both effects until the editor instance is GC'd
|
|
13451
|
+
- Non-deterministic serialize() — if serialize(parse(text)) returns a string structurally different from the input text, the helper dispatches redundant editor writes that fight the user's typing. JSON.stringify with consistent indentation is fine; pretty-printing that varies on every call is not
|
|
13452
|
+
- Throwing in parse() without an onParseError handler — the helper catches and silently no-ops if no handler is provided. Pass onParseError to surface parse errors in your UI
|
|
13453
|
+
- Returning a non-null value from parse() for malformed input — the helper writes whatever you return, including partial / corrupted state. Return null on parse failure, or throw with an error message
|
|
13454
|
+
- Using bindEditorToSignal AND a manual editor.value.set() loop in the same component — defeats the loop prevention. Pick one binding strategy per editor instance`
|
|
13366
13455
|
},
|
|
13367
13456
|
"hotkeys/useHotkey": {
|
|
13368
13457
|
signature: "useHotkey(shortcut: string, handler: (e: KeyboardEvent) => void, options?: HotkeyOptions): void",
|
|
@@ -13423,7 +13512,7 @@ console.log(result.totalErrors, result.totalWarnings)
|
|
|
13423
13512
|
|
|
13424
13513
|
// With config file auto-loading + rule overrides
|
|
13425
13514
|
lint({ paths: ["."], ruleOverrides: { "pyreon/no-classname": "off" } })`,
|
|
13426
|
-
notes: "Programmatic API.
|
|
13515
|
+
notes: "Programmatic API. 58 rules across 12 categories. Auto-loads .pyreonlintrc.json. Presets: recommended, strict, app, lib. Uses oxc-parser with AST caching."
|
|
13427
13516
|
},
|
|
13428
13517
|
"lint/lintFile": {
|
|
13429
13518
|
signature: "lintFile(filePath: string, sourceText: string, rules: Rule[], config: LintConfig, cache?: AstCache): LintFileResult",
|
|
@@ -13439,10 +13528,26 @@ const result = lintFile("app.tsx", source, allRules, config, cache)`,
|
|
|
13439
13528
|
example: `pyreon-lint --preset strict --quiet # CI mode
|
|
13440
13529
|
pyreon-lint --fix # auto-fix
|
|
13441
13530
|
pyreon-lint --watch src/ # watch mode
|
|
13442
|
-
pyreon-lint --list # list all
|
|
13531
|
+
pyreon-lint --list # list all 58 rules
|
|
13443
13532
|
pyreon-lint --format json # machine-readable`,
|
|
13444
13533
|
notes: "CLI entry. Config: .pyreonlintrc.json, package.json 'pyreonlint' field. Ignore: .pyreonlintignore + .gitignore. Watch: fs.watch recursive with 100ms debounce."
|
|
13445
13534
|
},
|
|
13535
|
+
"lint/no-process-dev-gate": {
|
|
13536
|
+
signature: "rule: pyreon/no-process-dev-gate (architecture, error, auto-fixable)",
|
|
13537
|
+
example: `// ❌ Wrong — dead code in real Vite browser bundles
|
|
13538
|
+
const __DEV__ = typeof process !== 'undefined' && process.env.NODE_ENV !== 'production'
|
|
13539
|
+
if (__DEV__) console.warn('hello')
|
|
13540
|
+
|
|
13541
|
+
// ✅ Correct — Vite literal-replaces import.meta.env.DEV at build time
|
|
13542
|
+
// @ts-ignore — provided by Vite/Rolldown at build time
|
|
13543
|
+
const __DEV__ = import.meta.env?.DEV === true
|
|
13544
|
+
if (__DEV__) console.warn('hello')`,
|
|
13545
|
+
notes: "The `typeof process !== 'undefined' && process.env.NODE_ENV !== 'production'` pattern works in vitest (Node, `process` is defined) but is silently dead code in real Vite browser bundles because Vite does NOT polyfill `process` for the client. Every `console.warn` gated on the broken constant never fires for real users in dev mode — unit tests pass while users get nothing. Use `import.meta.env.DEV` instead — Vite/Rolldown literal-replace it at build time, prod tree-shakes the warning to zero bytes, and vitest sets it to `true` automatically. Server-only packages (`zero`, `core/server`, `core/runtime-server`, `vite-plugin`, `cli`, `lint`, `mcp`, `storybook`, `typescript`) and test files are exempt. Reference implementation: `packages/fundamentals/flow/src/layout.ts:warnIgnoredOptions`. The rule has an auto-fix that replaces the broken expression with `import.meta.env?.DEV === true`.",
|
|
13546
|
+
mistakes: `- Copying the \`typeof process !== 'undefined' && process.env.NODE_ENV !== 'production'\` pattern from existing codebases — it works in Node but is dead in browser bundles
|
|
13547
|
+
- Trying to test with \`delete globalThis.process\` — vitest's own \`import.meta.env\` depends on \`process\`, so deleting it breaks the FIXED gate too (not because the gate is wrong, but because vitest can't resolve it)
|
|
13548
|
+
- Adding \`process: { env: { ... } }\` polyfills to vite.config.ts as a workaround — fix the source instead
|
|
13549
|
+
- Using the rule for server-only packages — they're correctly exempt because Node always has \`process\``
|
|
13550
|
+
},
|
|
13446
13551
|
"ui-core/PyreonUI": {
|
|
13447
13552
|
signature: "PyreonUI(props: { theme?: Theme; mode?: 'light' | 'dark' | 'system'; inversed?: boolean; children: VNodeChild }): VNodeChild",
|
|
13448
13553
|
example: `import { PyreonUI } from "@pyreon/ui-core"
|
|
@@ -13629,6 +13734,58 @@ export const Primary: StoryObj<typeof meta> = {
|
|
|
13629
13734
|
args: { variant: 'primary', label: 'Click me' },
|
|
13630
13735
|
}`,
|
|
13631
13736
|
notes: "Storybook renderer for Pyreon components. Re-exports h, Fragment, signal, computed, effect, mount for story convenience."
|
|
13737
|
+
},
|
|
13738
|
+
"document-primitives/extractDocNode": {
|
|
13739
|
+
signature: "extractDocNode(templateFn: () => VNode, options?: ExtractOptions): DocNode",
|
|
13740
|
+
example: `import {
|
|
13741
|
+
DocDocument, DocPage, DocHeading, DocText,
|
|
13742
|
+
extractDocNode,
|
|
13743
|
+
} from '@pyreon/document-primitives'
|
|
13744
|
+
import { download } from '@pyreon/document'
|
|
13745
|
+
|
|
13746
|
+
interface Resume { name: string; headline: string }
|
|
13747
|
+
|
|
13748
|
+
function ResumeTemplate(props: { resume: () => Resume }) {
|
|
13749
|
+
return (
|
|
13750
|
+
// title and author accept reactive accessors — extractDocNode
|
|
13751
|
+
// resolves them at extraction time, so each export click reads
|
|
13752
|
+
// the LIVE value from the underlying signal
|
|
13753
|
+
<DocDocument
|
|
13754
|
+
title={() => \`\${props.resume().name} — Resume\`}
|
|
13755
|
+
author={() => props.resume().name}
|
|
13756
|
+
>
|
|
13757
|
+
<DocPage>
|
|
13758
|
+
<DocHeading level="h1">{() => props.resume().name}</DocHeading>
|
|
13759
|
+
<DocText>{() => props.resume().headline}</DocText>
|
|
13760
|
+
</DocPage>
|
|
13761
|
+
</DocDocument>
|
|
13762
|
+
)
|
|
13763
|
+
}
|
|
13764
|
+
|
|
13765
|
+
// One-step extraction. The two-step createDocumentExport(...).getDocNode()
|
|
13766
|
+
// form is still exported for callers that want to pass the helper
|
|
13767
|
+
// object around, but extractDocNode is the recommended form.
|
|
13768
|
+
const tree = extractDocNode(() => <ResumeTemplate resume={store.resume} />)
|
|
13769
|
+
await download(tree, 'resume.pdf')
|
|
13770
|
+
await download(tree, 'resume.docx')
|
|
13771
|
+
await download(tree, 'resume.html')
|
|
13772
|
+
await download(tree, 'resume.md')`,
|
|
13773
|
+
notes: "18 primitives: DocDocument, DocPage, DocSection, DocRow, DocColumn, DocHeading, DocText, DocLink, DocImage, DocTable, DocList, DocListItem, DocCode, DocDivider, DocSpacer, DocButton, DocQuote, DocPageBreak. Same component tree renders in browser AND exports — primitives carry _documentType statics that extractDocumentTree (from @pyreon/connector-document) walks to produce a DocNode for @pyreon/document's render() to consume. DocDocument's title/author/subject accept either a string OR a `() => string` accessor; function values are stored in _documentProps and resolved at extraction time so reactive metadata works without `const initial = get()` workarounds. PR #197 also fixed a latent bug in extractDocumentTree: it now CALLS rocketstyle component functions to read post-attrs _documentProps, where before it only looked at the JSX vnode's props directly — every primitive's metadata was silently dropped during export until that fix landed.",
|
|
13774
|
+
mistakes: `- Calling props.title() at the top of a template body to "fix" reactivity — components run ONCE at mount, so this captures the initial value forever. Pass the accessor through to DocDocument as-is: <DocDocument title={() => get().name}>
|
|
13775
|
+
- DocRow direction: layout props (direction, gap) go in .attrs() not .theme(). Element accepts 'inline' | 'rows' | 'reverseInline' | 'reverseRows' — 'row' is NOT valid
|
|
13776
|
+
- For text children reactivity, pass a signal accessor and read inside body: <DocText>{() => store.field()}</DocText>
|
|
13777
|
+
- Don't declare runtime-filled fields (tag, _documentProps) in the rocketstyle .attrs<P>() generic — they leak as required JSX props
|
|
13778
|
+
- Using createDocumentExport(...).getDocNode() in new code — prefer extractDocNode(fn) which is one call instead of two. createDocumentExport is kept for backward compat`
|
|
13779
|
+
},
|
|
13780
|
+
"document-primitives/createDocumentExport": {
|
|
13781
|
+
signature: "createDocumentExport(templateFn: () => VNode): { getDocNode(): DocNode }",
|
|
13782
|
+
example: `// Two-step form (kept for backward compat). New code should
|
|
13783
|
+
// prefer the one-step extractDocNode helper.
|
|
13784
|
+
import { createDocumentExport } from '@pyreon/document-primitives'
|
|
13785
|
+
|
|
13786
|
+
const helper = createDocumentExport(() => <Resume name="Aisha" />)
|
|
13787
|
+
const tree = helper.getDocNode()`,
|
|
13788
|
+
notes: "Wrapper around extractDocNode. The wrapper-object form is kept for callers that want to pass the helper around (e.g. to wrapper components that take a DocumentExport instance). New code should use extractDocNode(templateFn) which is one call instead of two."
|
|
13632
13789
|
}
|
|
13633
13790
|
};
|
|
13634
13791
|
|