@wix/zero-config-implementation 1.5.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 +72 -0
- package/dist/component-loader.d.ts +42 -0
- package/dist/component-renderer.d.ts +31 -0
- package/dist/converters/data-item-builder.d.ts +15 -0
- package/dist/converters/index.d.ts +1 -0
- package/dist/converters/to-editor-component.d.ts +3 -0
- package/dist/converters/utils.d.ts +16 -0
- package/dist/errors.d.ts +230 -0
- package/dist/index.d.ts +42 -0
- package/dist/index.js +51978 -0
- package/dist/information-extractors/css/index.d.ts +3 -0
- package/dist/information-extractors/css/parse.d.ts +7 -0
- package/dist/information-extractors/css/selector-matcher.d.ts +3 -0
- package/dist/information-extractors/css/types.d.ts +49 -0
- package/dist/information-extractors/react/extractors/core/index.d.ts +6 -0
- package/dist/information-extractors/react/extractors/core/runner.d.ts +19 -0
- package/dist/information-extractors/react/extractors/core/store.d.ts +17 -0
- package/dist/information-extractors/react/extractors/core/tree-builder.d.ts +15 -0
- package/dist/information-extractors/react/extractors/core/types.d.ts +40 -0
- package/dist/information-extractors/react/extractors/css-properties.d.ts +20 -0
- package/dist/information-extractors/react/extractors/index.d.ts +11 -0
- package/dist/information-extractors/react/extractors/prop-tracker.d.ts +24 -0
- package/dist/information-extractors/react/index.d.ts +9 -0
- package/dist/information-extractors/react/types.d.ts +51 -0
- package/dist/information-extractors/react/utils/mock-generator.d.ts +9 -0
- package/dist/information-extractors/react/utils/prop-spy.d.ts +10 -0
- package/dist/information-extractors/ts/components.d.ts +9 -0
- package/dist/information-extractors/ts/css-imports.d.ts +2 -0
- package/dist/information-extractors/ts/index.d.ts +3 -0
- package/dist/information-extractors/ts/types.d.ts +47 -0
- package/dist/information-extractors/ts/utils/semantic-type-resolver.d.ts +3 -0
- package/dist/jsx-runtime-interceptor.d.ts +42 -0
- package/dist/jsx-runtime-interceptor.js +63 -0
- package/dist/jsx-runtime-loader.d.ts +23 -0
- package/dist/jsx-runtime-loader.js +7 -0
- package/dist/manifest-pipeline.d.ts +33 -0
- package/dist/schema.d.ts +167 -0
- package/dist/ts-compiler.d.ts +13 -0
- package/package.json +81 -0
- package/src/component-loader.test.ts +277 -0
- package/src/component-loader.ts +256 -0
- package/src/component-renderer.ts +192 -0
- package/src/converters/data-item-builder.ts +354 -0
- package/src/converters/index.ts +1 -0
- package/src/converters/to-editor-component.ts +167 -0
- package/src/converters/utils.ts +21 -0
- package/src/errors.ts +103 -0
- package/src/index.ts +223 -0
- package/src/information-extractors/css/README.md +3 -0
- package/src/information-extractors/css/index.ts +3 -0
- package/src/information-extractors/css/parse.ts +450 -0
- package/src/information-extractors/css/selector-matcher.ts +88 -0
- package/src/information-extractors/css/types.ts +56 -0
- package/src/information-extractors/react/extractors/core/index.ts +6 -0
- package/src/information-extractors/react/extractors/core/runner.ts +89 -0
- package/src/information-extractors/react/extractors/core/store.ts +36 -0
- package/src/information-extractors/react/extractors/core/tree-builder.ts +273 -0
- package/src/information-extractors/react/extractors/core/types.ts +48 -0
- package/src/information-extractors/react/extractors/css-properties.ts +214 -0
- package/src/information-extractors/react/extractors/index.ts +27 -0
- package/src/information-extractors/react/extractors/prop-tracker.ts +132 -0
- package/src/information-extractors/react/index.ts +53 -0
- package/src/information-extractors/react/types.ts +70 -0
- package/src/information-extractors/react/utils/mock-generator.ts +331 -0
- package/src/information-extractors/react/utils/prop-spy.ts +168 -0
- package/src/information-extractors/ts/components.ts +300 -0
- package/src/information-extractors/ts/css-imports.ts +26 -0
- package/src/information-extractors/ts/index.ts +3 -0
- package/src/information-extractors/ts/types.ts +56 -0
- package/src/information-extractors/ts/utils/semantic-type-resolver.ts +377 -0
- package/src/jsx-runtime-interceptor.ts +146 -0
- package/src/jsx-runtime-loader.ts +38 -0
- package/src/manifest-pipeline.ts +362 -0
- package/src/schema.ts +174 -0
- package/src/ts-compiler.ts +41 -0
- package/tsconfig.json +17 -0
- package/typedoc.json +18 -0
- package/vite.config.ts +45 -0
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
import fs from 'node:fs'
|
|
2
|
+
|
|
3
|
+
import type { ComponentInfo } from './information-extractors/ts'
|
|
4
|
+
import type { PropInfo } from './information-extractors/ts/types'
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
type ExtractedElement,
|
|
8
|
+
type PropTrackerData,
|
|
9
|
+
createCssPropertiesExtractor,
|
|
10
|
+
createPropTrackerExtractor,
|
|
11
|
+
runExtractors,
|
|
12
|
+
} from './information-extractors/react'
|
|
13
|
+
import type { CoupledComponentInfo, CoupledProp, DOMBinding, TrackingStores } from './information-extractors/react'
|
|
14
|
+
|
|
15
|
+
import { matchCssSelectors, parseCss } from './information-extractors/css'
|
|
16
|
+
import type { CSSParserAPI } from './information-extractors/css'
|
|
17
|
+
|
|
18
|
+
import type { ComponentType } from 'react'
|
|
19
|
+
|
|
20
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
21
|
+
// Types
|
|
22
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
23
|
+
|
|
24
|
+
/** A non-fatal issue encountered during component processing. */
|
|
25
|
+
export interface ExtractionWarning {
|
|
26
|
+
componentName: string
|
|
27
|
+
phase: 'render' | 'coupling' | 'css' | 'loader' | 'conversion'
|
|
28
|
+
error: string
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface ExtractedCssInfo {
|
|
32
|
+
filePath: string
|
|
33
|
+
api: CSSParserAPI
|
|
34
|
+
properties: Map<string, string>
|
|
35
|
+
customProperties: Map<string, string>
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface ComponentInfoWithCss extends CoupledComponentInfo {
|
|
39
|
+
css: ExtractedCssInfo[]
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** The result of processing a single component through the manifest pipeline. */
|
|
43
|
+
export interface ProcessComponentResult {
|
|
44
|
+
component: ComponentInfoWithCss
|
|
45
|
+
warnings: ExtractionWarning[]
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
49
|
+
// Component Processing
|
|
50
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Processes a single component through the full manifest pipeline:
|
|
54
|
+
* loading, rendering with prop tracking, CSS extraction, and selector matching.
|
|
55
|
+
*
|
|
56
|
+
* Returns the enriched component info alongside any non-fatal warnings
|
|
57
|
+
* encountered during processing. Always returns a component, falling back
|
|
58
|
+
* to minimal info (without DOM coupling) when rendering fails.
|
|
59
|
+
*/
|
|
60
|
+
export function processComponent(
|
|
61
|
+
componentInfo: ComponentInfo,
|
|
62
|
+
loadComponent: (componentName: string) => ComponentType<unknown> | null,
|
|
63
|
+
cssImportPaths: string[],
|
|
64
|
+
loaderHasError?: boolean,
|
|
65
|
+
): ProcessComponentResult {
|
|
66
|
+
const warnings: ExtractionWarning[] = []
|
|
67
|
+
|
|
68
|
+
// Load the actual component
|
|
69
|
+
let Component: ComponentType<unknown> | null = null
|
|
70
|
+
try {
|
|
71
|
+
Component = loadComponent(componentInfo.componentName)
|
|
72
|
+
if (!Component && !loaderHasError) {
|
|
73
|
+
warnings.push({
|
|
74
|
+
componentName: componentInfo.componentName,
|
|
75
|
+
phase: 'loader',
|
|
76
|
+
error: `Component "${componentInfo.componentName}" not found in package exports`,
|
|
77
|
+
})
|
|
78
|
+
}
|
|
79
|
+
} catch (error) {
|
|
80
|
+
if (!loaderHasError) {
|
|
81
|
+
warnings.push({
|
|
82
|
+
componentName: componentInfo.componentName,
|
|
83
|
+
phase: 'loader',
|
|
84
|
+
error: `Failed to load "${componentInfo.componentName}": ${error instanceof Error ? error.message : String(error)}`,
|
|
85
|
+
})
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
let coupledInfo: CoupledComponentInfo | null = null
|
|
90
|
+
let enhancedInfo: CoupledComponentInfo
|
|
91
|
+
let html: string | undefined
|
|
92
|
+
let extractedElements: ExtractedElement[] = []
|
|
93
|
+
|
|
94
|
+
// Render component with mock props and track prop flow (can fail)
|
|
95
|
+
if (Component) {
|
|
96
|
+
try {
|
|
97
|
+
const { extractor: propTracker, state } = createPropTrackerExtractor()
|
|
98
|
+
const cssExtractor = createCssPropertiesExtractor()
|
|
99
|
+
|
|
100
|
+
const result = runExtractors(componentInfo, Component, [propTracker, cssExtractor])
|
|
101
|
+
html = result.html
|
|
102
|
+
extractedElements = result.elements
|
|
103
|
+
|
|
104
|
+
const { props: coupledProps, innerElementProps } = buildCoupledProps(componentInfo, state.stores)
|
|
105
|
+
coupledInfo = {
|
|
106
|
+
componentName: componentInfo.componentName,
|
|
107
|
+
props: coupledProps,
|
|
108
|
+
elements: convertElements(extractedElements),
|
|
109
|
+
innerElementProps: innerElementProps.size > 0 ? innerElementProps : undefined,
|
|
110
|
+
}
|
|
111
|
+
} catch (error) {
|
|
112
|
+
warnings.push({
|
|
113
|
+
componentName: componentInfo.componentName,
|
|
114
|
+
phase: 'render',
|
|
115
|
+
error: error instanceof Error ? error.message : String(error),
|
|
116
|
+
})
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// If rendering succeeded, use the coupled info directly
|
|
121
|
+
// (CSS properties are now embedded via css-properties-extractor)
|
|
122
|
+
if (coupledInfo) {
|
|
123
|
+
enhancedInfo = coupledInfo
|
|
124
|
+
} else {
|
|
125
|
+
// Fallback: create minimal info without DOM coupling
|
|
126
|
+
enhancedInfo = {
|
|
127
|
+
componentName: componentInfo.componentName,
|
|
128
|
+
props: Object.fromEntries(
|
|
129
|
+
Object.entries(componentInfo.props).map(([name, info]) => [name, { ...info, bindings: [], logicOnly: false }]),
|
|
130
|
+
),
|
|
131
|
+
elements: [],
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Read and parse CSS imports
|
|
136
|
+
const { cssInfos: css, warnings: cssWarnings } = extractCssInfo(cssImportPaths, componentInfo.componentName)
|
|
137
|
+
warnings.push(...cssWarnings)
|
|
138
|
+
|
|
139
|
+
// Match CSS selectors to elements
|
|
140
|
+
if (html && extractedElements.length > 0 && css.length > 0) {
|
|
141
|
+
try {
|
|
142
|
+
const enrichedElements = matchCssSelectors(html, extractedElements, css)
|
|
143
|
+
enhancedInfo = {
|
|
144
|
+
...enhancedInfo,
|
|
145
|
+
elements: convertElements(enrichedElements),
|
|
146
|
+
}
|
|
147
|
+
} catch (error) {
|
|
148
|
+
warnings.push({
|
|
149
|
+
componentName: componentInfo.componentName,
|
|
150
|
+
phase: 'css',
|
|
151
|
+
error: `CSS selector matching failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
152
|
+
})
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return {
|
|
157
|
+
component: {
|
|
158
|
+
...enhancedInfo,
|
|
159
|
+
css,
|
|
160
|
+
},
|
|
161
|
+
warnings,
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
166
|
+
// Prop Coupling Helpers
|
|
167
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
168
|
+
|
|
169
|
+
function buildCoupledProps(
|
|
170
|
+
componentInfo: ComponentInfo,
|
|
171
|
+
stores: TrackingStores,
|
|
172
|
+
): { props: Record<string, CoupledProp>; innerElementProps: Map<string, Record<string, CoupledProp>> } {
|
|
173
|
+
const result: Record<string, CoupledProp> = {}
|
|
174
|
+
|
|
175
|
+
for (const [name, info] of Object.entries(componentInfo.props)) {
|
|
176
|
+
const path = `props.${name}`
|
|
177
|
+
const wasRead = stores.reads.has(path)
|
|
178
|
+
const writeInfo = stores.writes.get(path)
|
|
179
|
+
|
|
180
|
+
result[name] = {
|
|
181
|
+
...info,
|
|
182
|
+
logicOnly: wasRead && !writeInfo,
|
|
183
|
+
bindings: writeInfo ? extractBindings(writeInfo) : [],
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const innerElementProps = processElementPropsWrites(componentInfo, stores)
|
|
188
|
+
|
|
189
|
+
return { props: result, innerElementProps }
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const ELEMENT_PROPS_PREFIX = 'props.elementProps.'
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Processes stores.writes entries with paths starting with "props.elementProps."
|
|
196
|
+
* to extract inner element prop bindings grouped by elementId (traceId).
|
|
197
|
+
*/
|
|
198
|
+
function processElementPropsWrites(
|
|
199
|
+
componentInfo: ComponentInfo,
|
|
200
|
+
stores: TrackingStores,
|
|
201
|
+
): Map<string, Record<string, CoupledProp>> {
|
|
202
|
+
const result = new Map<string, Record<string, CoupledProp>>()
|
|
203
|
+
|
|
204
|
+
const elementPropsInfo = componentInfo.props.elementProps
|
|
205
|
+
if (!elementPropsInfo) return result
|
|
206
|
+
|
|
207
|
+
for (const [path, writeInfo] of stores.writes) {
|
|
208
|
+
if (!path.startsWith(ELEMENT_PROPS_PREFIX)) continue
|
|
209
|
+
|
|
210
|
+
// Resolve the PropInfo for this leaf prop by walking the type tree
|
|
211
|
+
const relativePath = path.slice(ELEMENT_PROPS_PREFIX.length) // e.g. "navbar.items"
|
|
212
|
+
const propInfo = resolveInnerPropInfo(elementPropsInfo, relativePath)
|
|
213
|
+
if (!propInfo) continue
|
|
214
|
+
|
|
215
|
+
const bindings = extractBindings(writeInfo)
|
|
216
|
+
|
|
217
|
+
// Group by elementId from the bindings
|
|
218
|
+
for (const binding of bindings) {
|
|
219
|
+
const { elementId } = binding
|
|
220
|
+
let propsForElement = result.get(elementId)
|
|
221
|
+
if (!propsForElement) {
|
|
222
|
+
propsForElement = {}
|
|
223
|
+
result.set(elementId, propsForElement)
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const leafName = propInfo.name
|
|
227
|
+
if (!propsForElement[leafName]) {
|
|
228
|
+
propsForElement[leafName] = {
|
|
229
|
+
...propInfo,
|
|
230
|
+
logicOnly: false,
|
|
231
|
+
bindings: [],
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
propsForElement[leafName].bindings.push(binding)
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return result
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Walks the elementProps resolved type tree by path segments to find the leaf PropInfo.
|
|
243
|
+
* e.g., path "navbar.items" -> walk elementProps type -> navbar property -> items property
|
|
244
|
+
* Handles recursive nesting: "hamburgerMenu.elementProps.hamburgerCloseButton.label"
|
|
245
|
+
*/
|
|
246
|
+
function resolveInnerPropInfo(elementPropsPropInfo: PropInfo, relativePath: string): PropInfo | null {
|
|
247
|
+
const segments = relativePath.split('.')
|
|
248
|
+
let currentType = elementPropsPropInfo.resolvedType
|
|
249
|
+
|
|
250
|
+
for (let i = 0; i < segments.length; i++) {
|
|
251
|
+
const segment = segments[i]
|
|
252
|
+
if (!currentType.properties) return null
|
|
253
|
+
|
|
254
|
+
const propInfo = currentType.properties[segment]
|
|
255
|
+
if (!propInfo) return null
|
|
256
|
+
|
|
257
|
+
// If this is the last segment, return the PropInfo
|
|
258
|
+
if (i === segments.length - 1) {
|
|
259
|
+
return propInfo
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Otherwise, descend into this property's resolved type
|
|
263
|
+
currentType = propInfo.resolvedType
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
return null
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function extractBindings(writeInfo: TrackingStores['writes'] extends Map<string, infer V> ? V : never): DOMBinding[] {
|
|
270
|
+
const bindings: DOMBinding[] = []
|
|
271
|
+
|
|
272
|
+
for (const [key, attrInfo] of writeInfo.attributes) {
|
|
273
|
+
const elementId = key.split(':')[0]
|
|
274
|
+
const elementInfo = writeInfo.elements.get(elementId)
|
|
275
|
+
if (elementInfo) {
|
|
276
|
+
bindings.push({
|
|
277
|
+
element: elementInfo.tag,
|
|
278
|
+
attribute: attrInfo.attr,
|
|
279
|
+
concatenated: attrInfo.concatenated,
|
|
280
|
+
elementId: elementInfo.elementId,
|
|
281
|
+
})
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return bindings
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function convertElements(elements: ExtractedElement[]): CoupledComponentInfo['elements'] {
|
|
289
|
+
return elements.map((el) => {
|
|
290
|
+
// Get prop-tracker data for boundProps
|
|
291
|
+
const propTrackerData = el.extractorData.get('prop-tracker') as PropTrackerData | undefined
|
|
292
|
+
|
|
293
|
+
return {
|
|
294
|
+
traceId: el.traceId,
|
|
295
|
+
name: el.name,
|
|
296
|
+
tag: el.tag,
|
|
297
|
+
attributes: el.attributes,
|
|
298
|
+
extractorData: el.extractorData,
|
|
299
|
+
children: convertElements(el.children),
|
|
300
|
+
// Legacy fields
|
|
301
|
+
boundProps: propTrackerData?.boundProps ?? [],
|
|
302
|
+
role: propTrackerData?.role,
|
|
303
|
+
hasTextContent: el.hasTextContent,
|
|
304
|
+
}
|
|
305
|
+
})
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
309
|
+
// CSS Extraction
|
|
310
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Reads and parses CSS files, extracting standard and custom properties.
|
|
314
|
+
* Returns the parsed CSS info alongside any warnings from files that failed to parse.
|
|
315
|
+
*/
|
|
316
|
+
function extractCssInfo(
|
|
317
|
+
cssImportPaths: string[],
|
|
318
|
+
componentName: string,
|
|
319
|
+
): { cssInfos: ExtractedCssInfo[]; warnings: ExtractionWarning[] } {
|
|
320
|
+
const cssInfos: ExtractedCssInfo[] = []
|
|
321
|
+
const warnings: ExtractionWarning[] = []
|
|
322
|
+
|
|
323
|
+
for (const cssPath of cssImportPaths) {
|
|
324
|
+
try {
|
|
325
|
+
// Read CSS file
|
|
326
|
+
const cssContent = fs.readFileSync(cssPath, 'utf-8')
|
|
327
|
+
|
|
328
|
+
// Parse CSS
|
|
329
|
+
const api = parseCss(cssContent)
|
|
330
|
+
const allProps = api.getAllProperties()
|
|
331
|
+
|
|
332
|
+
// Extract regular properties and custom properties (CSS variables)
|
|
333
|
+
const properties = new Map<string, string>()
|
|
334
|
+
const customProperties = new Map<string, string>()
|
|
335
|
+
|
|
336
|
+
for (const [, props] of allProps) {
|
|
337
|
+
for (const prop of props) {
|
|
338
|
+
if (prop.name.startsWith('--')) {
|
|
339
|
+
customProperties.set(prop.name, prop.value)
|
|
340
|
+
} else {
|
|
341
|
+
properties.set(prop.name, prop.value)
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
cssInfos.push({
|
|
347
|
+
filePath: cssPath,
|
|
348
|
+
api,
|
|
349
|
+
properties,
|
|
350
|
+
customProperties,
|
|
351
|
+
})
|
|
352
|
+
} catch (error) {
|
|
353
|
+
warnings.push({
|
|
354
|
+
componentName,
|
|
355
|
+
phase: 'css',
|
|
356
|
+
error: `Failed to parse ${cssPath}: ${error instanceof Error ? error.message : String(error)}`,
|
|
357
|
+
})
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
return { cssInfos, warnings }
|
|
362
|
+
}
|
package/src/schema.ts
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
2
|
+
// Constants
|
|
3
|
+
// Extracted verbatim from @wix/component-protocol (chunk-U3HQJA7D.js).
|
|
4
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
5
|
+
|
|
6
|
+
const DATA_TYPE = {
|
|
7
|
+
UNKNOWN_DataType: 'UNKNOWN_DataType',
|
|
8
|
+
text: 'text',
|
|
9
|
+
textEnum: 'textEnum',
|
|
10
|
+
number: 'number',
|
|
11
|
+
booleanValue: 'booleanValue',
|
|
12
|
+
a11y: 'a11y',
|
|
13
|
+
link: 'link',
|
|
14
|
+
image: 'image',
|
|
15
|
+
video: 'video',
|
|
16
|
+
vectorArt: 'vectorArt',
|
|
17
|
+
audio: 'audio',
|
|
18
|
+
schema: 'schema',
|
|
19
|
+
localDate: 'localDate',
|
|
20
|
+
localTime: 'localTime',
|
|
21
|
+
localDateTime: 'localDateTime',
|
|
22
|
+
webUrl: 'webUrl',
|
|
23
|
+
email: 'email',
|
|
24
|
+
phone: 'phone',
|
|
25
|
+
hostname: 'hostname',
|
|
26
|
+
regex: 'regex',
|
|
27
|
+
guid: 'guid',
|
|
28
|
+
richText: 'richText',
|
|
29
|
+
container: 'container',
|
|
30
|
+
arrayItems: 'arrayItems',
|
|
31
|
+
direction: 'direction',
|
|
32
|
+
menuItems: 'menuItems',
|
|
33
|
+
data: 'data',
|
|
34
|
+
function: 'function',
|
|
35
|
+
onClick: 'onClick',
|
|
36
|
+
onChange: 'onChange',
|
|
37
|
+
onKeyPress: 'onKeyPress',
|
|
38
|
+
onKeyUp: 'onKeyUp',
|
|
39
|
+
onSubmit: 'onSubmit',
|
|
40
|
+
} as const
|
|
41
|
+
|
|
42
|
+
const A11Y_ATTRIBUTES = {
|
|
43
|
+
Unknown_AriaAttributes: 'Unknown_AriaAttributes',
|
|
44
|
+
tabIndex: 'tabIndex',
|
|
45
|
+
ariaLevel: 'ariaLevel',
|
|
46
|
+
ariaExpanded: 'ariaExpanded',
|
|
47
|
+
ariaDisabled: 'ariaDisabled',
|
|
48
|
+
ariaAtomic: 'ariaAtomic',
|
|
49
|
+
ariaHidden: 'ariaHidden',
|
|
50
|
+
ariaBusy: 'ariaBusy',
|
|
51
|
+
multiline: 'multiline',
|
|
52
|
+
ariaAutocomplete: 'ariaAutocomplete',
|
|
53
|
+
ariaPressed: 'ariaPressed',
|
|
54
|
+
ariaHaspopup: 'ariaHaspopup',
|
|
55
|
+
ariaRelevant: 'ariaRelevant',
|
|
56
|
+
role: 'role',
|
|
57
|
+
ariaLive: 'ariaLive',
|
|
58
|
+
ariaCurrent: 'ariaCurrent',
|
|
59
|
+
ariaLabel: 'ariaLabel',
|
|
60
|
+
ariaRoledescription: 'ariaRoledescription',
|
|
61
|
+
ariaDescribedby: 'ariaDescribedby',
|
|
62
|
+
ariaLabelledby: 'ariaLabelledby',
|
|
63
|
+
ariaErrormessage: 'ariaErrormessage',
|
|
64
|
+
ariaOwns: 'ariaOwns',
|
|
65
|
+
ariaControls: 'ariaControls',
|
|
66
|
+
tag: 'tag',
|
|
67
|
+
ariaMultiline: 'ariaMultiline',
|
|
68
|
+
ariaInvalid: 'ariaInvalid',
|
|
69
|
+
} as const
|
|
70
|
+
|
|
71
|
+
const LINK_TYPE = {
|
|
72
|
+
UNKNOWN_LinkType: 'UNKNOWN_LinkType',
|
|
73
|
+
externalLink: 'externalLink',
|
|
74
|
+
anchorLink: 'anchorLink',
|
|
75
|
+
emailLink: 'emailLink',
|
|
76
|
+
phoneLink: 'phoneLink',
|
|
77
|
+
dynamicPageLink: 'dynamicPageLink',
|
|
78
|
+
pageLink: 'pageLink',
|
|
79
|
+
whatsAppLink: 'whatsAppLink',
|
|
80
|
+
documentLink: 'documentLink',
|
|
81
|
+
popupLink: 'popupLink',
|
|
82
|
+
addressLink: 'addressLink',
|
|
83
|
+
edgeAnchorLinks: 'edgeAnchorLinks',
|
|
84
|
+
loginToWixLink: 'loginToWixLink',
|
|
85
|
+
} as const
|
|
86
|
+
|
|
87
|
+
const IMAGE_CATEGORY = {
|
|
88
|
+
UNKNOWN_CategoryName: 'UNKNOWN_CategoryName',
|
|
89
|
+
IMAGE: 'IMAGE',
|
|
90
|
+
IMAGE_BACKGROUND: 'IMAGE_BACKGROUND',
|
|
91
|
+
} as const
|
|
92
|
+
|
|
93
|
+
const VIDEO_CATEGORY = {
|
|
94
|
+
UNKNOWN_VideoCategoryTypes: 'UNKNOWN_VideoCategoryTypes',
|
|
95
|
+
VIDEO: 'VIDEO',
|
|
96
|
+
VIDEO_TRANSPARENT: 'VIDEO_TRANSPARENT',
|
|
97
|
+
VIDEO_OPAQUE: 'VIDEO_OPAQUE',
|
|
98
|
+
} as const
|
|
99
|
+
|
|
100
|
+
const VECTOR_ART_CATEGORY = {
|
|
101
|
+
UNKNOWN_VectorArtCategoryTypes: 'UNKNOWN_VectorArtCategoryTypes',
|
|
102
|
+
SHAPE_ALL: 'SHAPE_ALL',
|
|
103
|
+
SHAPE_BASIC: 'SHAPE_BASIC',
|
|
104
|
+
SHAPE_ART: 'SHAPE_ART',
|
|
105
|
+
ICON_SOCIAL: 'ICON_SOCIAL',
|
|
106
|
+
SHAPE_DIVIDERS: 'SHAPE_DIVIDERS',
|
|
107
|
+
SHAPE_LOCATION: 'SHAPE_LOCATION',
|
|
108
|
+
SHAPE_DOCUMENTS: 'SHAPE_DOCUMENTS',
|
|
109
|
+
SHAPE_SOCIAL: 'SHAPE_SOCIAL',
|
|
110
|
+
SHAPE_ARROWS: 'SHAPE_ARROWS',
|
|
111
|
+
} as const
|
|
112
|
+
|
|
113
|
+
const ELEMENT_TYPE = {
|
|
114
|
+
UNKNOWN_ElementType: 'UNKNOWN_ElementType',
|
|
115
|
+
inlineElement: 'inlineElement',
|
|
116
|
+
refElement: 'refElement',
|
|
117
|
+
} as const
|
|
118
|
+
|
|
119
|
+
export const DATA = { DATA_TYPE, A11Y_ATTRIBUTES, LINK_TYPE }
|
|
120
|
+
export const MEDIA = { VIDEO_CATEGORY, VECTOR_ART_CATEGORY, IMAGE_CATEGORY }
|
|
121
|
+
export const ELEMENTS = { ELEMENT_TYPE }
|
|
122
|
+
|
|
123
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
124
|
+
// Types
|
|
125
|
+
// Minimal stubs matching only the properties accessed in this codebase.
|
|
126
|
+
// ─────────────────────────────────────────────────────────────────────────────
|
|
127
|
+
|
|
128
|
+
export interface DataItem {
|
|
129
|
+
displayName?: string
|
|
130
|
+
defaultValue?: unknown
|
|
131
|
+
dataType?: string
|
|
132
|
+
text?: Record<string, unknown>
|
|
133
|
+
number?: Record<string, unknown>
|
|
134
|
+
booleanValue?: unknown
|
|
135
|
+
textEnum?: { options: Array<{ value: string; displayName?: string }> }
|
|
136
|
+
arrayItems?: { dataItem?: DataItem }
|
|
137
|
+
data?: { items?: Record<string, DataItem> }
|
|
138
|
+
function?: Record<string, unknown>
|
|
139
|
+
link?: { linkTypes?: string[] }
|
|
140
|
+
image?: { category?: string }
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export interface CssPropertyItem {
|
|
144
|
+
defaultValue?: string
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export interface CssCustomPropertyItem {
|
|
148
|
+
defaultValue?: string
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export interface EditorElement {
|
|
152
|
+
selector?: string
|
|
153
|
+
displayName?: string
|
|
154
|
+
data?: Record<string, DataItem>
|
|
155
|
+
elements?: Record<string, ElementItem>
|
|
156
|
+
cssProperties?: Record<string, CssPropertyItem>
|
|
157
|
+
cssCustomProperties?: Record<string, CssCustomPropertyItem>
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export interface ElementItem {
|
|
161
|
+
elementType?: string
|
|
162
|
+
inlineElement?: {
|
|
163
|
+
selector?: string
|
|
164
|
+
displayName?: string
|
|
165
|
+
data?: Record<string, DataItem>
|
|
166
|
+
cssProperties?: Record<string, CssPropertyItem>
|
|
167
|
+
cssCustomProperties?: Record<string, CssCustomPropertyItem>
|
|
168
|
+
elements?: Record<string, ElementItem>
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export interface EditorReactComponent {
|
|
173
|
+
editorElement?: EditorElement
|
|
174
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import * as fs from 'node:fs'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
import { ResultAsync, errAsync } from 'neverthrow'
|
|
4
|
+
import { parseNative } from 'tsconfck'
|
|
5
|
+
import ts from 'typescript'
|
|
6
|
+
import { NotFoundError, ParseError } from './errors'
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Compile a TypeScript file into a ts.Program.
|
|
10
|
+
*
|
|
11
|
+
* @param filePath - Path to the TypeScript source file
|
|
12
|
+
* @returns The compiled TypeScript program on success
|
|
13
|
+
* @errors
|
|
14
|
+
* - {@link NotFoundError} — Source file does not exist (phase: `compile`)
|
|
15
|
+
* - {@link ParseError} — TypeScript config could not be parsed (phase: `compile`)
|
|
16
|
+
*/
|
|
17
|
+
export function compileTsFile(
|
|
18
|
+
filePath: string,
|
|
19
|
+
): ResultAsync<ts.Program, InstanceType<typeof NotFoundError> | InstanceType<typeof ParseError>> {
|
|
20
|
+
const resolvedPath = path.resolve(filePath)
|
|
21
|
+
|
|
22
|
+
if (!fs.existsSync(resolvedPath)) {
|
|
23
|
+
return errAsync(new NotFoundError(`File not found: ${resolvedPath}`, { props: { phase: 'compile' } }))
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return ResultAsync.fromPromise(
|
|
27
|
+
parseNative(resolvedPath),
|
|
28
|
+
(error) =>
|
|
29
|
+
new ParseError(`Failed to parse TypeScript config for ${resolvedPath}`, {
|
|
30
|
+
cause: error as Error,
|
|
31
|
+
props: { phase: 'compile' },
|
|
32
|
+
}),
|
|
33
|
+
).map(({ tsconfig, tsconfigFile }) => {
|
|
34
|
+
// Parse the JSON config through TypeScript's API to handle inheritance,
|
|
35
|
+
// convert string values (like "ES2020") to enum values, and process extends
|
|
36
|
+
const configDir = path.dirname(tsconfigFile)
|
|
37
|
+
const parsedConfig = ts.parseJsonConfigFileContent(tsconfig, ts.sys, configDir)
|
|
38
|
+
|
|
39
|
+
return ts.createProgram([filePath], parsedConfig.options)
|
|
40
|
+
})
|
|
41
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "Bundler",
|
|
6
|
+
"lib": ["ES2020"],
|
|
7
|
+
"outDir": "./dist",
|
|
8
|
+
"rootDir": "./src",
|
|
9
|
+
"strict": true,
|
|
10
|
+
"esModuleInterop": true,
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"forceConsistentCasingInFileNames": true,
|
|
13
|
+
"declaration": true
|
|
14
|
+
},
|
|
15
|
+
"include": ["src/**/*"],
|
|
16
|
+
"exclude": ["node_modules", "dist"]
|
|
17
|
+
}
|
package/typedoc.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"entryPoints": [
|
|
3
|
+
"./src"
|
|
4
|
+
],
|
|
5
|
+
"entryPointStrategy": "expand",
|
|
6
|
+
"out": "docs-audit",
|
|
7
|
+
"plugin": [
|
|
8
|
+
"typedoc-plugin-markdown"
|
|
9
|
+
],
|
|
10
|
+
"readme": "none",
|
|
11
|
+
"githubPages": false,
|
|
12
|
+
"disableSources": true,
|
|
13
|
+
"cleanOutputDir": true,
|
|
14
|
+
"excludePrivate": false,
|
|
15
|
+
"excludeProtected": false,
|
|
16
|
+
"excludeInternal": false,
|
|
17
|
+
"excludeExternals": false
|
|
18
|
+
}
|
package/vite.config.ts
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { builtinModules } from 'node:module'
|
|
2
|
+
import { resolve } from 'node:path'
|
|
3
|
+
import { defineConfig } from 'vite'
|
|
4
|
+
import dts from 'vite-plugin-dts'
|
|
5
|
+
|
|
6
|
+
export default defineConfig({
|
|
7
|
+
plugins: [
|
|
8
|
+
dts({
|
|
9
|
+
include: ['src/**/*.ts'],
|
|
10
|
+
exclude: ['**/*.test.ts'],
|
|
11
|
+
}),
|
|
12
|
+
],
|
|
13
|
+
build: {
|
|
14
|
+
lib: {
|
|
15
|
+
entry: {
|
|
16
|
+
index: resolve(__dirname, 'src/index.ts'),
|
|
17
|
+
'jsx-runtime-interceptor': resolve(__dirname, 'src/jsx-runtime-interceptor.ts'),
|
|
18
|
+
'jsx-runtime-loader': resolve(__dirname, 'src/jsx-runtime-loader.ts'),
|
|
19
|
+
},
|
|
20
|
+
formats: ['es'],
|
|
21
|
+
},
|
|
22
|
+
rollupOptions: {
|
|
23
|
+
external: (id) => {
|
|
24
|
+
// Externalize Node.js built-ins (both node: prefixed and bare), typescript (peer dep), and lightningcss (native bindings)
|
|
25
|
+
if (id.startsWith('node:') || builtinModules.includes(id) || ['typescript', 'lightningcss'].includes(id)) {
|
|
26
|
+
return true
|
|
27
|
+
}
|
|
28
|
+
return false
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
outDir: 'dist',
|
|
32
|
+
},
|
|
33
|
+
resolve: {
|
|
34
|
+
// Ensure Node.js-specific export conditions are used when bundling for Node
|
|
35
|
+
conditions: ['node', 'import', 'module', 'default'],
|
|
36
|
+
alias: {
|
|
37
|
+
// Alias jsx-runtime to our interceptable version for tests
|
|
38
|
+
'react/jsx-runtime': resolve(__dirname, 'src/jsx-runtime-interceptor.ts'),
|
|
39
|
+
'react/jsx-dev-runtime': resolve(__dirname, 'src/jsx-runtime-interceptor.ts'),
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
test: {
|
|
43
|
+
globals: true,
|
|
44
|
+
},
|
|
45
|
+
})
|