@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.
Files changed (78) hide show
  1. package/README.md +72 -0
  2. package/dist/component-loader.d.ts +42 -0
  3. package/dist/component-renderer.d.ts +31 -0
  4. package/dist/converters/data-item-builder.d.ts +15 -0
  5. package/dist/converters/index.d.ts +1 -0
  6. package/dist/converters/to-editor-component.d.ts +3 -0
  7. package/dist/converters/utils.d.ts +16 -0
  8. package/dist/errors.d.ts +230 -0
  9. package/dist/index.d.ts +42 -0
  10. package/dist/index.js +51978 -0
  11. package/dist/information-extractors/css/index.d.ts +3 -0
  12. package/dist/information-extractors/css/parse.d.ts +7 -0
  13. package/dist/information-extractors/css/selector-matcher.d.ts +3 -0
  14. package/dist/information-extractors/css/types.d.ts +49 -0
  15. package/dist/information-extractors/react/extractors/core/index.d.ts +6 -0
  16. package/dist/information-extractors/react/extractors/core/runner.d.ts +19 -0
  17. package/dist/information-extractors/react/extractors/core/store.d.ts +17 -0
  18. package/dist/information-extractors/react/extractors/core/tree-builder.d.ts +15 -0
  19. package/dist/information-extractors/react/extractors/core/types.d.ts +40 -0
  20. package/dist/information-extractors/react/extractors/css-properties.d.ts +20 -0
  21. package/dist/information-extractors/react/extractors/index.d.ts +11 -0
  22. package/dist/information-extractors/react/extractors/prop-tracker.d.ts +24 -0
  23. package/dist/information-extractors/react/index.d.ts +9 -0
  24. package/dist/information-extractors/react/types.d.ts +51 -0
  25. package/dist/information-extractors/react/utils/mock-generator.d.ts +9 -0
  26. package/dist/information-extractors/react/utils/prop-spy.d.ts +10 -0
  27. package/dist/information-extractors/ts/components.d.ts +9 -0
  28. package/dist/information-extractors/ts/css-imports.d.ts +2 -0
  29. package/dist/information-extractors/ts/index.d.ts +3 -0
  30. package/dist/information-extractors/ts/types.d.ts +47 -0
  31. package/dist/information-extractors/ts/utils/semantic-type-resolver.d.ts +3 -0
  32. package/dist/jsx-runtime-interceptor.d.ts +42 -0
  33. package/dist/jsx-runtime-interceptor.js +63 -0
  34. package/dist/jsx-runtime-loader.d.ts +23 -0
  35. package/dist/jsx-runtime-loader.js +7 -0
  36. package/dist/manifest-pipeline.d.ts +33 -0
  37. package/dist/schema.d.ts +167 -0
  38. package/dist/ts-compiler.d.ts +13 -0
  39. package/package.json +81 -0
  40. package/src/component-loader.test.ts +277 -0
  41. package/src/component-loader.ts +256 -0
  42. package/src/component-renderer.ts +192 -0
  43. package/src/converters/data-item-builder.ts +354 -0
  44. package/src/converters/index.ts +1 -0
  45. package/src/converters/to-editor-component.ts +167 -0
  46. package/src/converters/utils.ts +21 -0
  47. package/src/errors.ts +103 -0
  48. package/src/index.ts +223 -0
  49. package/src/information-extractors/css/README.md +3 -0
  50. package/src/information-extractors/css/index.ts +3 -0
  51. package/src/information-extractors/css/parse.ts +450 -0
  52. package/src/information-extractors/css/selector-matcher.ts +88 -0
  53. package/src/information-extractors/css/types.ts +56 -0
  54. package/src/information-extractors/react/extractors/core/index.ts +6 -0
  55. package/src/information-extractors/react/extractors/core/runner.ts +89 -0
  56. package/src/information-extractors/react/extractors/core/store.ts +36 -0
  57. package/src/information-extractors/react/extractors/core/tree-builder.ts +273 -0
  58. package/src/information-extractors/react/extractors/core/types.ts +48 -0
  59. package/src/information-extractors/react/extractors/css-properties.ts +214 -0
  60. package/src/information-extractors/react/extractors/index.ts +27 -0
  61. package/src/information-extractors/react/extractors/prop-tracker.ts +132 -0
  62. package/src/information-extractors/react/index.ts +53 -0
  63. package/src/information-extractors/react/types.ts +70 -0
  64. package/src/information-extractors/react/utils/mock-generator.ts +331 -0
  65. package/src/information-extractors/react/utils/prop-spy.ts +168 -0
  66. package/src/information-extractors/ts/components.ts +300 -0
  67. package/src/information-extractors/ts/css-imports.ts +26 -0
  68. package/src/information-extractors/ts/index.ts +3 -0
  69. package/src/information-extractors/ts/types.ts +56 -0
  70. package/src/information-extractors/ts/utils/semantic-type-resolver.ts +377 -0
  71. package/src/jsx-runtime-interceptor.ts +146 -0
  72. package/src/jsx-runtime-loader.ts +38 -0
  73. package/src/manifest-pipeline.ts +362 -0
  74. package/src/schema.ts +174 -0
  75. package/src/ts-compiler.ts +41 -0
  76. package/tsconfig.json +17 -0
  77. package/typedoc.json +18 -0
  78. package/vite.config.ts +45 -0
@@ -0,0 +1,300 @@
1
+ import { type ComponentDoc, type PropItem, withCompilerOptions } from 'react-docgen-typescript'
2
+ import ts from 'typescript'
3
+ import type { ComponentInfo, DefaultValue, PropInfo } from './types'
4
+ import { resolveType } from './utils/semantic-type-resolver'
5
+
6
+ // ─────────────────────────────────────────────────────────────────────────────
7
+ // Shared parser options
8
+ // ─────────────────────────────────────────────────────────────────────────────
9
+
10
+ const sharedPropFilter = (prop: PropItem) => {
11
+ // Exclude props inherited from node_modules (e.g. HTMLAttributes)
12
+ if (prop.declarations && prop.declarations.length > 0) {
13
+ return !prop.declarations.every((d) => d.fileName.includes('node_modules'))
14
+ }
15
+ return true
16
+ }
17
+
18
+ const sharedParserOptions = {
19
+ shouldRemoveUndefinedFromOptional: true,
20
+ shouldIncludePropTagMap: true,
21
+ skipChildrenPropWithoutDoc: true,
22
+ propFilter: sharedPropFilter,
23
+ }
24
+
25
+ // ─────────────────────────────────────────────────────────────────────────────
26
+ // Main API
27
+ // ─────────────────────────────────────────────────────────────────────────────
28
+
29
+ export function extractAllComponentInfo(program: ts.Program, filePath: string): ComponentInfo[] {
30
+ const checker = program.getTypeChecker()
31
+ const options = program.getCompilerOptions()
32
+
33
+ const parser = withCompilerOptions(options, sharedParserOptions)
34
+
35
+ const docs = parser.parseWithProgramProvider([filePath], () => program)
36
+
37
+ return docs.map((doc) => convertComponentDoc(doc, program, checker))
38
+ }
39
+
40
+ /**
41
+ * Extracts component info for only the default-exported component in a file.
42
+ * Returns `undefined` if the file has no default export or if the default export
43
+ * is not a recognized React component.
44
+ */
45
+ export function extractDefaultComponentInfo(program: ts.Program, filePath: string): ComponentInfo | undefined {
46
+ const defaultName = findDefaultExportName(program, filePath)
47
+ if (!defaultName) return undefined
48
+
49
+ const all = extractAllComponentInfo(program, filePath)
50
+ return all.find((c) => c.componentName === defaultName)
51
+ }
52
+
53
+ // ─────────────────────────────────────────────────────────────────────────────
54
+ // Default export resolution
55
+ // ─────────────────────────────────────────────────────────────────────────────
56
+
57
+ /**
58
+ * Resolves the name of the default-exported symbol in a file using the
59
+ * TypeScript type checker.
60
+ *
61
+ * Returns `undefined` for anonymous default exports (e.g. `export default () => ...`)
62
+ * where no stable name can be determined.
63
+ */
64
+ function findDefaultExportName(program: ts.Program, filePath: string): string | undefined {
65
+ const checker = program.getTypeChecker()
66
+ const sourceFile = program.getSourceFile(filePath)
67
+ if (!sourceFile) return undefined
68
+
69
+ const moduleSymbol = checker.getSymbolAtLocation(sourceFile)
70
+ if (!moduleSymbol) return undefined
71
+
72
+ const defaultSymbol = checker.getExportsOfModule(moduleSymbol).find((s) => s.getName() === 'default')
73
+ if (!defaultSymbol) return undefined
74
+
75
+ // For `export default Foo` the symbol is an alias — resolve it to get 'Foo'
76
+ if (defaultSymbol.getFlags() & ts.SymbolFlags.Alias) {
77
+ const aliased = checker.getAliasedSymbol(defaultSymbol)
78
+ const name = aliased.getName()
79
+ // 'default' means the alias resolved back to an anonymous default export
80
+ return name !== 'default' ? name : undefined
81
+ }
82
+
83
+ return undefined
84
+ }
85
+
86
+ // ─────────────────────────────────────────────────────────────────────────────
87
+ // ComponentDoc → ComponentInfo conversion
88
+ // ─────────────────────────────────────────────────────────────────────────────
89
+
90
+ function convertComponentDoc(doc: ComponentDoc, program: ts.Program, checker: ts.TypeChecker): ComponentInfo {
91
+ const propsType = findPropsType(doc.displayName, doc.filePath, program, checker)
92
+ const props: Record<string, PropInfo> = {}
93
+
94
+ for (const [propName, propItem] of Object.entries(doc.props)) {
95
+ // Skip props marked with @ignore or @private
96
+ const tags = propItem.tags as Record<string, string> | undefined
97
+ if (tags?.ignore !== undefined || tags?.private !== undefined) {
98
+ continue
99
+ }
100
+
101
+ // Look up the ts.Symbol from the props type for deep resolution
102
+ const propSymbol = propsType?.getProperty(propName)
103
+ const propType = propSymbol ? checker.getTypeOfSymbol(propSymbol) : undefined
104
+
105
+ // Resolve the type deeply
106
+ let declaredTypeInfo: DeclaredTypeInfo | undefined
107
+ if (propSymbol) {
108
+ const decl = propSymbol.getDeclarations()?.[0]
109
+ if (decl) {
110
+ declaredTypeInfo = getDeclaredTypeInfo(propSymbol, decl.getSourceFile(), checker)
111
+ }
112
+ }
113
+
114
+ const typeString = declaredTypeInfo?.name ?? propItem.type.name
115
+ const resolvedType = propType
116
+ ? resolveType(propType, checker, 0, typeString, declaredTypeInfo?.symbol)
117
+ : { kind: 'primitive' as const, value: propItem.type.name }
118
+
119
+ // Convert default value
120
+ let defaultValue: DefaultValue | undefined
121
+ if (propItem.defaultValue != null) {
122
+ defaultValue = convertDefaultValue(propItem.defaultValue)
123
+ }
124
+
125
+ // If no runtime default, check @default JSDoc tag
126
+ if (!defaultValue && tags?.default) {
127
+ defaultValue = parseDefaultValueFromString(tags.default)
128
+ }
129
+
130
+ props[propName] = {
131
+ name: propName,
132
+ type: typeString,
133
+ required: propItem.required,
134
+ defaultValue,
135
+ resolvedType,
136
+ description: propItem.description || undefined,
137
+ deprecated: tags?.deprecated !== undefined ? true : undefined,
138
+ }
139
+ }
140
+
141
+ return { componentName: doc.displayName, props }
142
+ }
143
+
144
+ // ─────────────────────────────────────────────────────────────────────────────
145
+ // Find the props ts.Type for a component (needed for resolveType)
146
+ // ─────────────────────────────────────────────────────────────────────────────
147
+
148
+ function findPropsType(
149
+ componentName: string,
150
+ filePath: string,
151
+ program: ts.Program,
152
+ checker: ts.TypeChecker,
153
+ ): ts.Type | undefined {
154
+ const sourceFile = program.getSourceFile(filePath)
155
+ if (!sourceFile) return undefined
156
+
157
+ const moduleSymbol = checker.getSymbolAtLocation(sourceFile)
158
+ if (!moduleSymbol) return undefined
159
+
160
+ const exports = checker.getExportsOfModule(moduleSymbol)
161
+ const componentSymbol = exports.find((s) => s.getName() === componentName)
162
+ if (!componentSymbol) return undefined
163
+
164
+ const componentType = checker.getTypeOfSymbol(componentSymbol)
165
+
166
+ // Function component: call signatures → first parameter is props
167
+ const callSigs = componentType.getCallSignatures()
168
+ if (callSigs.length > 0) {
169
+ const params = callSigs[0].getParameters()
170
+ if (params.length > 0) {
171
+ return checker.getTypeOfSymbol(params[0])
172
+ }
173
+ }
174
+
175
+ // Class component: construct signatures → first type argument is props
176
+ const constructSigs = componentType.getConstructSignatures()
177
+ if (constructSigs.length > 0) {
178
+ const params = constructSigs[0].getParameters()
179
+ if (params.length > 0) {
180
+ const propsParam = checker.getTypeOfSymbol(params[0])
181
+ return propsParam
182
+ }
183
+ }
184
+
185
+ // memo / forwardRef: check for the underlying call signature
186
+ // The type of memo(...) or forwardRef(...) wraps the inner component
187
+ const properties = componentType.getProperties()
188
+ for (const prop of properties) {
189
+ if (prop.getName() === 'type' || prop.getName() === 'render') {
190
+ const innerType = checker.getTypeOfSymbol(prop)
191
+ const innerSigs = innerType.getCallSignatures()
192
+ if (innerSigs.length > 0) {
193
+ const params = innerSigs[0].getParameters()
194
+ if (params.length > 0) {
195
+ return checker.getTypeOfSymbol(params[0])
196
+ }
197
+ }
198
+ }
199
+ }
200
+
201
+ return undefined
202
+ }
203
+
204
+ // ─────────────────────────────────────────────────────────────────────────────
205
+ // Default value conversion
206
+ // ─────────────────────────────────────────────────────────────────────────────
207
+
208
+ function convertDefaultValue(rdtDefault: { value: unknown } | null): DefaultValue | undefined {
209
+ if (!rdtDefault || rdtDefault.value == null) return undefined
210
+
211
+ const val = rdtDefault.value
212
+
213
+ // Already a primitive — react-docgen-typescript sometimes returns non-string values
214
+ if (typeof val === 'boolean') return { kind: 'boolean', value: val }
215
+ if (typeof val === 'number') return { kind: 'number', value: val }
216
+ if (typeof val !== 'string') return { kind: 'unresolved', value: String(val) }
217
+
218
+ // react-docgen-typescript strips quotes from string literal defaults,
219
+ // so 'ltr' becomes "ltr". Try parsing as a well-known literal first
220
+ // (true/false/null/number), otherwise treat as a string value.
221
+ return parseDefaultValueFromString(val, /* fallbackToString */ true)
222
+ }
223
+
224
+ /**
225
+ * Parses a string value (typically from JSDoc @default or react-docgen-typescript) into a DefaultValue.
226
+ * Attempts to parse as JSON-like literals, falls back to unresolved (or string if fallbackToString is true).
227
+ *
228
+ * @param fallbackToString — when true, unquoted non-literal values become `{ kind: 'string' }`
229
+ * instead of `{ kind: 'unresolved' }`. Used for react-docgen-typescript defaults which are
230
+ * always literals but arrive without surrounding quotes.
231
+ */
232
+ function parseDefaultValueFromString(text: string, fallbackToString = false): DefaultValue {
233
+ const trimmed = text.trim()
234
+
235
+ // Boolean literals
236
+ if (trimmed === 'true') {
237
+ return { kind: 'boolean', value: true }
238
+ }
239
+ if (trimmed === 'false') {
240
+ return { kind: 'boolean', value: false }
241
+ }
242
+
243
+ // Null literal
244
+ if (trimmed === 'null') {
245
+ return { kind: 'null', value: null }
246
+ }
247
+
248
+ // Number literals (including negative)
249
+ const num = Number(trimmed)
250
+ if (!Number.isNaN(num) && trimmed !== '') {
251
+ return { kind: 'number', value: num }
252
+ }
253
+
254
+ // String literals: 'hello' or "hello"
255
+ if ((trimmed.startsWith("'") && trimmed.endsWith("'")) || (trimmed.startsWith('"') && trimmed.endsWith('"'))) {
256
+ return { kind: 'string', value: trimmed.slice(1, -1) }
257
+ }
258
+
259
+ // react-docgen-typescript strips quotes from string defaults, so treat as string
260
+ if (fallbackToString) {
261
+ return { kind: 'string', value: trimmed }
262
+ }
263
+
264
+ // Everything else is unresolved (e.g. JSDoc @default with a reference)
265
+ return { kind: 'unresolved', value: trimmed }
266
+ }
267
+
268
+ // ─────────────────────────────────────────────────────────────────────────────
269
+ // Helpers
270
+ // ─────────────────────────────────────────────────────────────────────────────
271
+
272
+ interface DeclaredTypeInfo {
273
+ name: string
274
+ symbol?: ts.Symbol
275
+ }
276
+
277
+ function getDeclaredTypeInfo(
278
+ prop: ts.Symbol,
279
+ sourceFile: ts.SourceFile,
280
+ checker: ts.TypeChecker,
281
+ ): DeclaredTypeInfo | undefined {
282
+ const declaration = prop.getDeclarations()?.[0]
283
+ if (!declaration) return undefined
284
+
285
+ if (
286
+ (ts.isPropertySignature(declaration) || ts.isPropertyDeclaration(declaration) || ts.isParameter(declaration)) &&
287
+ declaration.type &&
288
+ ts.isTypeReferenceNode(declaration.type)
289
+ ) {
290
+ const typeName = declaration.type.typeName
291
+ const identifier = ts.isIdentifier(typeName) ? typeName : typeName.right
292
+
293
+ return {
294
+ name: identifier.getText(sourceFile),
295
+ symbol: checker.getSymbolAtLocation(identifier),
296
+ }
297
+ }
298
+
299
+ return undefined
300
+ }
@@ -0,0 +1,26 @@
1
+ import * as path from 'node:path'
2
+ import ts, { type Program } from 'typescript'
3
+
4
+ export function extractCssImports(program: Program): string[] {
5
+ const cssFiles = new Set<string>()
6
+
7
+ for (const sourceFile of program.getSourceFiles()) {
8
+ // Skip external libraries
9
+ if (sourceFile.isDeclarationFile || sourceFile.fileName.includes('node_modules')) continue
10
+
11
+ ts.forEachChild(sourceFile, (node) => {
12
+ // Look for: import './style.css' or import x from './style.css'
13
+ if (ts.isImportDeclaration(node) && ts.isStringLiteral(node.moduleSpecifier)) {
14
+ const importPath = node.moduleSpecifier.text
15
+
16
+ if (importPath.endsWith('.css')) {
17
+ // Resolve relative path to an absolute disk path
18
+ const fullPath = path.resolve(path.dirname(sourceFile.fileName), importPath)
19
+ cssFiles.add(fullPath)
20
+ }
21
+ }
22
+ })
23
+ }
24
+
25
+ return Array.from(cssFiles)
26
+ }
@@ -0,0 +1,3 @@
1
+ export { extractAllComponentInfo, extractDefaultComponentInfo } from './components'
2
+ export { extractCssImports } from './css-imports'
3
+ export type { ComponentInfo } from './types'
@@ -0,0 +1,56 @@
1
+ /**
2
+ * API to extract component props with full TypeScript type resolution
3
+ */
4
+
5
+ export type ResolvedKind =
6
+ | 'primitive'
7
+ | 'semantic'
8
+ | 'array'
9
+ | 'object'
10
+ | 'union'
11
+ | 'intersection'
12
+ | 'enum'
13
+ | 'literal'
14
+ | 'function'
15
+
16
+ export interface ResolvedType {
17
+ kind: ResolvedKind
18
+ value?: unknown
19
+ /** For semantic types, the package the symbol came from */
20
+ source?: string
21
+ properties?: Record<string, PropInfo>
22
+ elementType?: ResolvedType
23
+ types?: ResolvedType[] // For unions and intersections
24
+ }
25
+
26
+ /**
27
+ * Resolved default value with its type.
28
+ * For literals (string, number, boolean, null), the value is the actual JavaScript value.
29
+ * For unresolvable values (references, expressions), we store the raw source text.
30
+ */
31
+ export type DefaultValue =
32
+ | { kind: 'string'; value: string }
33
+ | { kind: 'number'; value: number }
34
+ | { kind: 'boolean'; value: boolean }
35
+ | { kind: 'null'; value: null }
36
+ | { kind: 'unresolved'; value: string } // For references like MY_CONST or complex expressions
37
+
38
+ export interface PropInfo {
39
+ name: string
40
+ // The unresolved type. For example "string", "User".
41
+ type: string
42
+ required: boolean
43
+ // The resolved default value of the prop
44
+ defaultValue?: DefaultValue
45
+ // Describes the resolved type of the prop, recursively down to the primitive type.
46
+ resolvedType: ResolvedType
47
+ // Description extracted from JSDoc comment
48
+ description?: string
49
+ // Whether the prop is marked as deprecated via @deprecated JSDoc tag
50
+ deprecated?: boolean
51
+ }
52
+
53
+ export interface ComponentInfo {
54
+ componentName: string
55
+ props: Record<string, PropInfo>
56
+ }