@wix/zero-config-implementation 1.6.0 → 1.8.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.
@@ -1,6 +1,6 @@
1
1
  export { ExtractorStore } from './store';
2
2
  export { runExtractors } from './runner';
3
- export type { ExtractionResult } from './runner';
3
+ export type { ExtractionResult, RunExtractorsOptions } from './runner';
4
4
  export { buildElementTree } from './tree-builder';
5
5
  export type { ExtractedElement } from './tree-builder';
6
6
  export type { ReactExtractor, RenderContext, CreateElementEvent, RenderCompleteEvent } from './types';
@@ -8,6 +8,10 @@ export interface ExtractionResult {
8
8
  store: ExtractorStore;
9
9
  elements: ExtractedElement[];
10
10
  }
11
+ export interface RunExtractorsOptions {
12
+ /** Optional HOC to wrap the component before rendering (e.g. a context provider). */
13
+ wrapper?: (component: ComponentType<unknown>) => ComponentType<unknown>;
14
+ }
11
15
  /**
12
16
  * Runs extractors through the full lifecycle and returns extraction results.
13
17
  *
@@ -16,4 +20,4 @@ export interface ExtractionResult {
16
20
  * @param extractors - Array of extractors to run
17
21
  * @returns Extraction results including HTML, store, and element tree
18
22
  */
19
- export declare function runExtractors(componentInfo: ComponentInfo, component: ComponentType<unknown>, extractors: ReactExtractor[]): ExtractionResult;
23
+ export declare function runExtractors(componentInfo: ComponentInfo, component: ComponentType<unknown>, extractors: ReactExtractor[], options?: RunExtractorsOptions): ExtractionResult;
@@ -4,7 +4,7 @@
4
4
  * Core infrastructure and pluggable extractors for the React information extraction system.
5
5
  */
6
6
  export { ExtractorStore, runExtractors, buildElementTree, } from './core';
7
- export type { ExtractionResult, ExtractedElement, ReactExtractor, RenderContext, CreateElementEvent, RenderCompleteEvent, } from './core';
7
+ export type { ExtractionResult, RunExtractorsOptions, ExtractedElement, ReactExtractor, RenderContext, CreateElementEvent, RenderCompleteEvent, } from './core';
8
8
  export { createPropTrackerExtractor } from './prop-tracker';
9
9
  export type { PropTrackerData, PropTrackerExtractorState } from './prop-tracker';
10
10
  export { createCssPropertiesExtractor } from './css-properties';
@@ -4,6 +4,6 @@
4
4
  * API: runExtractors() with pluggable extractors
5
5
  */
6
6
  export { ExtractorStore, runExtractors, buildElementTree, createPropTrackerExtractor, createCssPropertiesExtractor, } from './extractors';
7
- export type { ExtractionResult, ExtractedElement, ReactExtractor, RenderContext, CreateElementEvent, RenderCompleteEvent, PropTrackerData, PropTrackerExtractorState, CssPropertiesData, } from './extractors';
7
+ export type { ExtractionResult, RunExtractorsOptions, ExtractedElement, ReactExtractor, RenderContext, CreateElementEvent, RenderCompleteEvent, PropTrackerData, PropTrackerExtractorState, CssPropertiesData, } from './extractors';
8
8
  export type { CoupledComponentInfo, CoupledProp, TrackingStores, DOMBinding, PropReadInfo, PropWriteInfo, PropSpyMeta, } from './types';
9
9
  export type { PropSpyContext } from './utils/prop-spy';
@@ -1,5 +1,5 @@
1
1
  import { ComponentInfo } from './information-extractors/ts';
2
- import { CoupledComponentInfo } from './information-extractors/react';
2
+ import { RunExtractorsOptions, CoupledComponentInfo } from './information-extractors/react';
3
3
  import { CSSParserAPI } from './information-extractors/css';
4
4
  import { ComponentType } from 'react';
5
5
  /** A non-fatal issue encountered during component processing. */
@@ -30,4 +30,4 @@ export interface ProcessComponentResult {
30
30
  * encountered during processing. Always returns a component, falling back
31
31
  * to minimal info (without DOM coupling) when rendering fails.
32
32
  */
33
- export declare function processComponent(componentInfo: ComponentInfo, loadComponent: (componentName: string) => ComponentType<unknown> | null, cssImportPaths: string[], loaderHasError?: boolean): ProcessComponentResult;
33
+ export declare function processComponent(componentInfo: ComponentInfo, loadComponent: (componentName: string) => ComponentType<unknown> | null, cssImportPaths: string[], loaderHasError?: boolean, options?: RunExtractorsOptions): ProcessComponentResult;
@@ -0,0 +1,20 @@
1
+ import { ResultAsync } from 'neverthrow';
2
+ import { ComponentType } from 'react';
3
+ import { IoError } from './errors';
4
+ type IoErrorInstance = InstanceType<typeof IoError>;
5
+ /**
6
+ * Attempts to load a module, first via ESM `import()`, then via CJS `require`.
7
+ *
8
+ * ESM `import()` is preferred because it participates in the ESM loader hook
9
+ * pipeline (registered via `module.register()` in the CLI). This is required
10
+ * for JSX interception to work — the loader hook redirects `react/jsx-runtime`
11
+ * to the interceptable version. CJS `require()` bypasses ESM hooks even when
12
+ * loading ESM modules (Node 22+), so it's only used as a fallback.
13
+ *
14
+ * @param entryPath - Absolute path to the module entry point.
15
+ * @returns A `ResultAsync` containing the module exports on success.
16
+ * @errors {IoError} When both ESM import and CJS require fail.
17
+ */
18
+ export declare function loadModule(entryPath: string): ResultAsync<Record<string, unknown>, IoErrorInstance>;
19
+ export declare function findComponent(moduleExports: Record<string, unknown>, name: string): ComponentType<unknown> | null;
20
+ export {};
package/dist/schema.d.ts CHANGED
@@ -77,6 +77,7 @@ export declare const DATA: {
77
77
  readonly edgeAnchorLinks: "edgeAnchorLinks";
78
78
  readonly loginToWixLink: "loginToWixLink";
79
79
  };
80
+ WIX_TYPE_TO_DATA_TYPE: Record<string, "number" | "function" | "UNKNOWN_DataType" | "text" | "textEnum" | "booleanValue" | "a11y" | "link" | "image" | "video" | "vectorArt" | "audio" | "schema" | "localDate" | "localTime" | "localDateTime" | "webUrl" | "email" | "phone" | "hostname" | "regex" | "guid" | "richText" | "container" | "arrayItems" | "direction" | "menuItems" | "data" | "onClick" | "onChange" | "onKeyPress" | "onKeyUp" | "onSubmit">;
80
81
  };
81
82
  export declare const MEDIA: {
82
83
  VIDEO_CATEGORY: {
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "registry": "https://registry.npmjs.org/",
5
5
  "access": "public"
6
6
  },
7
- "version": "1.6.0",
7
+ "version": "1.8.0",
8
8
  "description": "Core library for extracting component manifests from JS and CSS files",
9
9
  "type": "module",
10
10
  "main": "dist/index.js",
@@ -50,14 +50,11 @@
50
50
  "modern-errors-cli": "^6.0.0",
51
51
  "neverthrow": "^8.2.0",
52
52
  "parse5": "^7.1.2",
53
- "pkg-entry-points": "^1.1.1",
54
53
  "react-docgen-typescript": "^2.4.0",
55
- "read-package-up": "^11.0.0",
56
54
  "tsconfck": "^3.1.6",
57
55
  "typedoc": "^0.28.16",
58
56
  "typedoc-plugin-markdown": "^4.9.0",
59
57
  "vite": "^7.3.1",
60
- "vite-node": "^5.3.0",
61
58
  "vite-plugin-dts": "^4.5.4",
62
59
  "vitest": "^4.0.17"
63
60
  },
@@ -77,5 +74,5 @@
77
74
  ]
78
75
  }
79
76
  },
80
- "falconPackageHash": "bff28693b4f361afc6884e6bd88e888aaa2338365192170e935df129"
77
+ "falconPackageHash": "8ca973dc7cc82c6e8d0a333de6717b866e33337ee211bd335f3fa800"
81
78
  }
@@ -12,7 +12,6 @@
12
12
  * resolved type metadata is inconsistent (kind === 'array' but no elementType).
13
13
  */
14
14
 
15
- import { camelCase } from 'case-anything'
16
15
  import type { Result } from 'neverthrow'
17
16
  import { err, ok } from 'neverthrow'
18
17
  import { ParseError } from '../errors'
@@ -21,7 +20,7 @@ import type { DataItem } from '../schema'
21
20
  import { DATA, MEDIA } from '../schema'
22
21
  import { formatDisplayName } from './utils'
23
22
 
24
- const { DATA_TYPE } = DATA
23
+ const { DATA_TYPE, WIX_TYPE_TO_DATA_TYPE } = DATA
25
24
 
26
25
  type ParseErrorInstance = InstanceType<typeof ParseError>
27
26
 
@@ -279,10 +278,10 @@ function handleSemanticType(dataItem: DataItem, resolvedType: ResolvedType): voi
279
278
  return
280
279
  }
281
280
 
282
- // Wix public-schemas (Builder) types - map directly to DATA_TYPE via camelCase conversion
281
+ // Wix public-schemas (Builder) types - map directly to DATA_TYPE via explicit lookup
283
282
  if (source === '@wix/public-schemas') {
284
- const dataTypeKey = camelCase(semanticValue) as keyof typeof DATA_TYPE
285
- if (dataTypeKey in DATA_TYPE) {
283
+ const dataTypeKey = WIX_TYPE_TO_DATA_TYPE[semanticValue]
284
+ if (dataTypeKey) {
286
285
  dataItem.dataType = DATA_TYPE[dataTypeKey]
287
286
  applyDataToBuilderType(dataItem, dataTypeKey)
288
287
  } else {
@@ -0,0 +1,16 @@
1
+ import { describe, expect, it } from 'vitest'
2
+ import { formatDisplayName } from './utils'
3
+
4
+ describe('formatDisplayName', () => {
5
+ it('maps a11y to Accessibility', () => {
6
+ expect(formatDisplayName('a11y')).toBe('Accessibility')
7
+ })
8
+
9
+ it('formats kebab-case', () => {
10
+ expect(formatDisplayName('input-field-weight')).toBe('Input Field Weight')
11
+ })
12
+
13
+ it('formats camelCase', () => {
14
+ expect(formatDisplayName('camelCaseExample')).toBe('Camel Case Example')
15
+ })
16
+ })
@@ -4,6 +4,10 @@
4
4
 
5
5
  import { capitalCase } from 'case-anything'
6
6
 
7
+ const DISPLAY_NAME_OVERRIDES = {
8
+ a11y: 'Accessibility',
9
+ } as const
10
+
7
11
  /**
8
12
  * Formats any string format to Title Case display name
9
13
  * Handles: kebab-case, camelCase, PascalCase, snake_case, SCREAMING_SNAKE_CASE, and mixed formats
@@ -15,7 +19,11 @@ import { capitalCase } from 'case-anything'
15
19
  * "snake_case_example" -> "Snake Case Example"
16
20
  * "SCREAMING_SNAKE_CASE" -> "Screaming Snake Case"
17
21
  * "mixed-format_example" -> "Mixed Format Example"
22
+ * "a11y" -> "Accessibility"
18
23
  */
19
24
  export function formatDisplayName(input: string): string {
25
+ if (input in DISPLAY_NAME_OVERRIDES) {
26
+ return DISPLAY_NAME_OVERRIDES[input as keyof typeof DISPLAY_NAME_OVERRIDES]
27
+ }
20
28
  return capitalCase(input, { keepSpecialCharacters: false })
21
29
  }
package/src/index.ts CHANGED
@@ -11,8 +11,9 @@ import { compileTsFile } from './ts-compiler'
11
11
  import { type NotFoundError, ParseError } from './errors'
12
12
 
13
13
  // Component loader helpers
14
- import { findComponent, loadModule } from './component-loader'
14
+ import { findComponent, loadModule } from './module-loader'
15
15
 
16
+ import type { RunExtractorsOptions } from './information-extractors/react'
16
17
  // Pipeline orchestration
17
18
  import { processComponent } from './manifest-pipeline'
18
19
  export type {
@@ -36,6 +37,8 @@ export interface ManifestResult {
36
37
  errors: ExtractionError[]
37
38
  }
38
39
 
40
+ export type { RunExtractorsOptions } from './information-extractors/react'
41
+
39
42
  export interface ExtractionError {
40
43
  componentName: string
41
44
  phase: 'render' | 'coupling' | 'css' | 'loader' | 'conversion'
@@ -59,6 +62,7 @@ export interface ExtractionError {
59
62
  export function extractComponentManifest(
60
63
  componentPath: string,
61
64
  compiledEntryPath: string,
65
+ options?: RunExtractorsOptions,
62
66
  ): ResultAsync<ManifestResult, InstanceType<typeof NotFoundError> | InstanceType<typeof ParseError>> {
63
67
  // Step 1: Load the compiled package module (non-fatal)
64
68
  return loadModule(compiledEntryPath)
@@ -104,7 +108,7 @@ export function extractComponentManifest(
104
108
  // Surface loader error as a non-fatal error
105
109
  if (loaderError) {
106
110
  errors.push({
107
- componentName: compiledEntryPath,
111
+ componentName: componentInfo.componentName,
108
112
  phase: 'loader',
109
113
  error: loaderError,
110
114
  })
@@ -126,7 +130,7 @@ export function extractComponentManifest(
126
130
  }
127
131
 
128
132
  // Step 5: Process the default-exported component (non-fatal)
129
- const processResult = processComponent(componentInfo, loadComponent, cssImportPaths, !!loaderError)
133
+ const processResult = processComponent(componentInfo, loadComponent, cssImportPaths, !!loaderError, options)
130
134
  errors.push(...processResult.warnings)
131
135
  const component = toEditorReactComponent(processResult.component)
132
136
 
@@ -216,8 +220,8 @@ export {
216
220
  withDefectBoundary,
217
221
  } from './errors'
218
222
 
219
- /** Component loader */
220
- export { createComponentLoader } from './component-loader'
223
+ /** Module loader primitives */
224
+ export { loadModule, findComponent } from './module-loader'
221
225
 
222
226
  // ── Tier 3: Low-Level Renderer ──────────────────────────────────────────────
223
227
 
@@ -1,6 +1,6 @@
1
1
  export { ExtractorStore } from './store'
2
2
  export { runExtractors } from './runner'
3
- export type { ExtractionResult } from './runner'
3
+ export type { ExtractionResult, RunExtractorsOptions } from './runner'
4
4
  export { buildElementTree } from './tree-builder'
5
5
  export type { ExtractedElement } from './tree-builder'
6
6
  export type { ReactExtractor, RenderContext, CreateElementEvent, RenderCompleteEvent } from './types'
@@ -27,6 +27,11 @@ export interface ExtractionResult {
27
27
  elements: ExtractedElement[]
28
28
  }
29
29
 
30
+ export interface RunExtractorsOptions {
31
+ /** Optional HOC to wrap the component before rendering (e.g. a context provider). */
32
+ wrapper?: (component: ComponentType<unknown>) => ComponentType<unknown>
33
+ }
34
+
30
35
  // ─────────────────────────────────────────────────────────────────────────────
31
36
  // Runner
32
37
  // ─────────────────────────────────────────────────────────────────────────────
@@ -43,6 +48,7 @@ export function runExtractors(
43
48
  componentInfo: ComponentInfo,
44
49
  component: ComponentType<unknown>,
45
50
  extractors: ReactExtractor[],
51
+ options?: RunExtractorsOptions,
46
52
  ): ExtractionResult {
47
53
  // Reset mock counter for reproducible results
48
54
  resetMockCounter()
@@ -74,8 +80,11 @@ export function runExtractors(
74
80
  },
75
81
  ]
76
82
 
83
+ // Apply optional HOC wrapper before rendering
84
+ const renderComponent = options?.wrapper ? options.wrapper(context.component) : context.component
85
+
77
86
  // Phase 2: Render with element creation interception
78
- const html = renderWithExtractors(context.component, context.props, listeners, store)
87
+ const html = renderWithExtractors(renderComponent, context.props, listeners, store)
79
88
 
80
89
  // Phase 3: renderComplete - extractors can post-process
81
90
  for (const ext of extractors) {
@@ -12,6 +12,7 @@ export {
12
12
  } from './core'
13
13
  export type {
14
14
  ExtractionResult,
15
+ RunExtractorsOptions,
15
16
  ExtractedElement,
16
17
  ReactExtractor,
17
18
  RenderContext,
@@ -21,6 +21,7 @@ export {
21
21
  export type {
22
22
  // Core types
23
23
  ExtractionResult,
24
+ RunExtractorsOptions,
24
25
  ExtractedElement,
25
26
  ReactExtractor,
26
27
  RenderContext,
@@ -158,7 +158,19 @@ function findPropsType(
158
158
  if (!moduleSymbol) return undefined
159
159
 
160
160
  const exports = checker.getExportsOfModule(moduleSymbol)
161
- const componentSymbol = exports.find((s) => s.getName() === componentName)
161
+ let componentSymbol = exports.find((s) => s.getName() === componentName)
162
+
163
+ // If not found by name, check whether the default export resolves to the component
164
+ if (!componentSymbol) {
165
+ const defaultSymbol = exports.find((s) => s.getName() === 'default')
166
+ if (defaultSymbol && (defaultSymbol.getFlags() & ts.SymbolFlags.Alias) !== 0) {
167
+ const resolved = checker.getAliasedSymbol(defaultSymbol)
168
+ if (resolved.getName() === componentName) {
169
+ componentSymbol = resolved
170
+ }
171
+ }
172
+ }
173
+
162
174
  if (!componentSymbol) return undefined
163
175
 
164
176
  const componentType = checker.getTypeOfSymbol(componentSymbol)
@@ -1,13 +1,12 @@
1
1
  import * as fs from 'node:fs'
2
2
  import * as path from 'node:path'
3
- import { camelCase } from 'case-anything'
4
3
  import ts from 'typescript'
5
4
  import { DATA } from '../../../schema'
6
5
  import type { PropInfo, ResolvedType } from '../types'
7
6
 
8
7
  const MAX_RESOLVE_DEPTH = 30
9
8
 
10
- const { DATA_TYPE } = DATA
9
+ const { WIX_TYPE_TO_DATA_TYPE } = DATA
11
10
 
12
11
  /**
13
12
  * Cache for package name lookups
@@ -50,8 +49,7 @@ const VALID_REACT_SEMANTIC_TYPES = new Set(['ReactNode', 'ReactElement'])
50
49
  * Checks if a Wix type name maps to a valid DATA_TYPE key
51
50
  */
52
51
  function isValidWixSemanticType(symbolName: string): boolean {
53
- const camelCaseKey = camelCase(symbolName)
54
- return camelCaseKey in DATA_TYPE
52
+ return symbolName in WIX_TYPE_TO_DATA_TYPE
55
53
  }
56
54
 
57
55
  /**
@@ -6,6 +6,7 @@ import type { PropInfo } from './information-extractors/ts/types'
6
6
  import {
7
7
  type ExtractedElement,
8
8
  type PropTrackerData,
9
+ type RunExtractorsOptions,
9
10
  createCssPropertiesExtractor,
10
11
  createPropTrackerExtractor,
11
12
  runExtractors,
@@ -62,6 +63,7 @@ export function processComponent(
62
63
  loadComponent: (componentName: string) => ComponentType<unknown> | null,
63
64
  cssImportPaths: string[],
64
65
  loaderHasError?: boolean,
66
+ options?: RunExtractorsOptions,
65
67
  ): ProcessComponentResult {
66
68
  const warnings: ExtractionWarning[] = []
67
69
 
@@ -97,7 +99,7 @@ export function processComponent(
97
99
  const { extractor: propTracker, state } = createPropTrackerExtractor()
98
100
  const cssExtractor = createCssPropertiesExtractor()
99
101
 
100
- const result = runExtractors(componentInfo, Component, [propTracker, cssExtractor])
102
+ const result = runExtractors(componentInfo, Component, [propTracker, cssExtractor], options)
101
103
  html = result.html
102
104
  extractedElements = result.elements
103
105
 
@@ -0,0 +1,76 @@
1
+ import { createRequire } from 'node:module'
2
+ import { pascalCase } from 'case-anything'
3
+ import { ResultAsync, errAsync, okAsync } from 'neverthrow'
4
+ import type { ComponentType } from 'react'
5
+ import { IoError } from './errors'
6
+
7
+ type IoErrorInstance = InstanceType<typeof IoError>
8
+
9
+ /**
10
+ * Attempts to load a module, first via ESM `import()`, then via CJS `require`.
11
+ *
12
+ * ESM `import()` is preferred because it participates in the ESM loader hook
13
+ * pipeline (registered via `module.register()` in the CLI). This is required
14
+ * for JSX interception to work — the loader hook redirects `react/jsx-runtime`
15
+ * to the interceptable version. CJS `require()` bypasses ESM hooks even when
16
+ * loading ESM modules (Node 22+), so it's only used as a fallback.
17
+ *
18
+ * @param entryPath - Absolute path to the module entry point.
19
+ * @returns A `ResultAsync` containing the module exports on success.
20
+ * @errors {IoError} When both ESM import and CJS require fail.
21
+ */
22
+ export function loadModule(entryPath: string): ResultAsync<Record<string, unknown>, IoErrorInstance> {
23
+ return ResultAsync.fromPromise(import(entryPath) as Promise<Record<string, unknown>>, () => null).orElse(() => {
24
+ try {
25
+ const require = createRequire(import.meta.url)
26
+ const exports = require(entryPath)
27
+ return okAsync(exports as Record<string, unknown>)
28
+ } catch (requireErr) {
29
+ const cause = requireErr instanceof Error ? requireErr : new Error(String(requireErr))
30
+ return errAsync(
31
+ new IoError(`Failed to load ${entryPath}: ${cause.message}`, {
32
+ cause,
33
+ props: { phase: 'load' },
34
+ }),
35
+ )
36
+ }
37
+ })
38
+ }
39
+
40
+ function isPascalCase(name: string): boolean {
41
+ return name.length > 0 && pascalCase(name) === name
42
+ }
43
+
44
+ function isComponent(value: unknown): value is ComponentType<unknown> {
45
+ if (typeof value === 'function') return isPascalCase(value.name)
46
+ // React.memo() and React.forwardRef() return objects, not functions
47
+ if (typeof value === 'object' && value !== null && '$$typeof' in value) return true
48
+ return false
49
+ }
50
+
51
+ export function findComponent(moduleExports: Record<string, unknown>, name: string): ComponentType<unknown> | null {
52
+ // Direct named export
53
+ const direct = moduleExports[name]
54
+ if ((typeof direct === 'function' && isPascalCase(name)) || isComponent(direct)) {
55
+ return direct as ComponentType<unknown>
56
+ }
57
+
58
+ // Check default export
59
+ const defaultExport = moduleExports.default
60
+ if (defaultExport) {
61
+ // default is the component itself (matches by function name)
62
+ if (typeof defaultExport === 'function' && defaultExport.name === name) {
63
+ return defaultExport as ComponentType<unknown>
64
+ }
65
+
66
+ // CJS interop: default is an object with named exports
67
+ if (typeof defaultExport === 'object' && defaultExport !== null) {
68
+ const nested = (defaultExport as Record<string, unknown>)[name]
69
+ if (isComponent(nested)) {
70
+ return nested as ComponentType<unknown>
71
+ }
72
+ }
73
+ }
74
+
75
+ return null
76
+ }
package/src/schema.ts CHANGED
@@ -116,7 +116,39 @@ const ELEMENT_TYPE = {
116
116
  refElement: 'refElement',
117
117
  } as const
118
118
 
119
- export const DATA = { DATA_TYPE, A11Y_ATTRIBUTES, LINK_TYPE }
119
+ /**
120
+ * Maps @wix/public-schemas type names (PascalCase) to their DATA_TYPE keys.
121
+ * Derived from the exports of @wix/public-schemas.
122
+ */
123
+ const WIX_TYPE_TO_DATA_TYPE: Record<string, keyof typeof DATA_TYPE> = {
124
+ Link: 'link',
125
+ Image: 'image',
126
+ Video: 'video',
127
+ VectorArt: 'vectorArt',
128
+ A11y: 'a11y',
129
+ Audio: 'audio',
130
+ MenuItems: 'menuItems',
131
+ Schema: 'schema',
132
+ Text: 'text',
133
+ TextEnum: 'textEnum',
134
+ NumberType: 'number',
135
+ BooleanValue: 'booleanValue',
136
+ LocalDate: 'localDate',
137
+ LocalTime: 'localTime',
138
+ LocalDateTime: 'localDateTime',
139
+ WebUrl: 'webUrl',
140
+ Email: 'email',
141
+ Phone: 'phone',
142
+ Hostname: 'hostname',
143
+ Regex: 'regex',
144
+ Guid: 'guid',
145
+ RichText: 'richText',
146
+ Container: 'container',
147
+ ArrayItems: 'arrayItems',
148
+ Direction: 'direction',
149
+ }
150
+
151
+ export const DATA = { DATA_TYPE, A11Y_ATTRIBUTES, LINK_TYPE, WIX_TYPE_TO_DATA_TYPE }
120
152
  export const MEDIA = { VIDEO_CATEGORY, VECTOR_ART_CATEGORY, IMAGE_CATEGORY }
121
153
  export const ELEMENTS = { ELEMENT_TYPE }
122
154
 
package/vite.config.ts CHANGED
@@ -25,6 +25,12 @@ export default defineConfig({
25
25
  if (id.startsWith('node:') || builtinModules.includes(id) || ['typescript', 'lightningcss'].includes(id)) {
26
26
  return true
27
27
  }
28
+ // Externalize React so the host project's React instance is used at runtime.
29
+ // Bundling React creates a second instance that causes hook failures when
30
+ // user components resolve a different React from their own node_modules.
31
+ if (id === 'react' || id === 'react-dom' || id.startsWith('react/') || id.startsWith('react-dom/')) {
32
+ return true
33
+ }
28
34
  return false
29
35
  },
30
36
  },
@@ -1,42 +0,0 @@
1
- import { ResultAsync } from 'neverthrow';
2
- import { ComponentType } from 'react';
3
- import { IoError, NotFoundError } from './errors';
4
- type NotFoundErrorInstance = InstanceType<typeof NotFoundError>;
5
- type IoErrorInstance = InstanceType<typeof IoError>;
6
- export type LoaderError = NotFoundErrorInstance | IoErrorInstance;
7
- export interface ComponentLoaderResult {
8
- loadComponent: (name: string) => ComponentType<unknown> | null;
9
- packageName: string;
10
- entryPath: string | undefined;
11
- exportNames: string[];
12
- }
13
- /**
14
- * Creates a component loader that dynamically loads components from the
15
- * user's package. Finds the nearest package.json, resolves its entry point,
16
- * and loads the module.
17
- *
18
- * The returned `loadComponent` is synchronous (required by processComponent).
19
- * ESM modules are loaded via async `import()` during creation, so this
20
- * function itself is async.
21
- *
22
- * @returns A `ResultAsync` containing a `ComponentLoaderResult` on success.
23
- * @errors {NotFoundError} When no package.json is found or the package has no resolvable entry points.
24
- * @errors {IoError} When module loading fails (CJS require or ESM import).
25
- */
26
- export declare function createComponentLoader(componentPath: string): ResultAsync<ComponentLoaderResult, LoaderError>;
27
- /**
28
- * Attempts to load a module, first via ESM `import()`, then via CJS `require`.
29
- *
30
- * ESM `import()` is preferred because it participates in the ESM loader hook
31
- * pipeline (registered via `module.register()` in the CLI). This is required
32
- * for JSX interception to work — the loader hook redirects `react/jsx-runtime`
33
- * to the interceptable version. CJS `require()` bypasses ESM hooks even when
34
- * loading ESM modules (Node 22+), so it's only used as a fallback.
35
- *
36
- * @param entryPath - Absolute path to the module entry point.
37
- * @returns A `ResultAsync` containing the module exports on success.
38
- * @errors {IoError} When both ESM import and CJS require fail.
39
- */
40
- export declare function loadModule(entryPath: string): ResultAsync<Record<string, unknown>, IoErrorInstance>;
41
- export declare function findComponent(moduleExports: Record<string, unknown>, name: string): ComponentType<unknown> | null;
42
- export {};