@wix/zero-config-implementation 1.78.0 → 1.79.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 (33) hide show
  1. package/README.md +4 -0
  2. package/dist/{index--6I20lGB.js → index-CoU1WRYf.js} +25036 -24768
  3. package/dist/{index-DAAmOM1c.js → index-DCJue8M7.js} +1 -1
  4. package/dist/index.js +1 -1
  5. package/package.json +2 -2
  6. package/src/converters/to-editor-component.ts +2 -2
  7. package/src/extensions/context-providers/context.test.ts +181 -0
  8. package/src/extensions/context-providers/context.ts +83 -0
  9. package/src/extensions/context-providers/mock-provider.test.ts +44 -0
  10. package/src/extensions/context-providers/mock-provider.ts +210 -0
  11. package/src/extensions/context-providers/types.ts +22 -0
  12. package/src/{ref-elements → extensions/ref-elements}/context.test.ts +4 -4
  13. package/src/extensions/ref-elements/context.ts +168 -0
  14. package/src/{ref-elements → extensions/ref-elements}/eligible-paths.ts +1 -1
  15. package/src/extensions/shared/ast-scanner.ts +85 -0
  16. package/src/extensions/shared/catalog-loader.ts +64 -0
  17. package/src/{ref-elements → extensions/shared}/module-resolution.test.ts +2 -2
  18. package/src/index.ts +108 -67
  19. package/src/information-extractors/react/extractors/prop-tracker.test.ts +2 -2
  20. package/src/information-extractors/react/extractors/prop-tracker.ts +3 -3
  21. package/src/information-extractors/react/utils/mock-generator.test.ts +107 -3
  22. package/src/information-extractors/react/utils/mock-generator.ts +104 -1
  23. package/src/manifest-pipeline.ts +4 -1
  24. package/src/module-loader.test.ts +1 -1
  25. package/src/module-loader.ts +1 -1
  26. package/src/react-runtime-loader.ts +28 -2
  27. package/src/ref-elements/context.ts +0 -280
  28. /package/src/{ref-elements → extensions/ref-elements}/component-tag.ts +0 -0
  29. /package/src/{ref-elements → extensions/ref-elements}/path-utils.test.ts +0 -0
  30. /package/src/{ref-elements → extensions/ref-elements}/path-utils.ts +0 -0
  31. /package/src/{ref-elements → extensions/ref-elements}/types.ts +0 -0
  32. /package/src/{ref-elements → extensions/shared}/module-resolution.ts +0 -0
  33. /package/src/{ref-elements/module-specifier.ts → extensions/shared/package-specifier.ts} +0 -0
@@ -1,7 +1,7 @@
1
- import { describe, expect, it } from 'vitest'
2
- import { createRefElementMarker, parseRefElementMarker } from '../../../ref-elements/path-utils'
1
+ import { beforeEach, describe, expect, it } from 'vitest'
2
+ import { createRefElementMarker, parseRefElementMarker } from '../../../extensions/ref-elements/path-utils'
3
3
  import type { ComponentInfo } from '../../ts/types'
4
- import { generateMockProps, resetMockCounter } from './mock-generator'
4
+ import { generateMockContextValues, generateMockProps, resetMockCounter } from './mock-generator'
5
5
 
6
6
  describe('generateMockProps', () => {
7
7
  it('injects a dedicated ref-element id marker into each eligible elementProps branch', () => {
@@ -129,3 +129,107 @@ describe('generateMockProps', () => {
129
129
  expect(parseRefElementMarker(ctaProps.id)).toBe('card.elementProps.cta')
130
130
  })
131
131
  })
132
+
133
+ describe('generateMockContextValues', () => {
134
+ beforeEach(() => {
135
+ resetMockCounter()
136
+ })
137
+
138
+ it('returns an empty object for an empty schema', () => {
139
+ expect(generateMockContextValues({ items: {} })).toEqual({})
140
+ })
141
+
142
+ it('maps text to a string', () => {
143
+ const result = generateMockContextValues({ items: { title: { dataType: 'text' } } })
144
+ expect(typeof result.title).toBe('string')
145
+ })
146
+
147
+ it('maps number to a number', () => {
148
+ const result = generateMockContextValues({ items: { count: { dataType: 'number' } } })
149
+ expect(typeof result.count).toBe('number')
150
+ })
151
+
152
+ it('maps boolean to a boolean', () => {
153
+ const result = generateMockContextValues({ items: { active: { dataType: 'boolean' } } })
154
+ expect(typeof result.active).toBe('boolean')
155
+ })
156
+
157
+ it('maps function to a function', () => {
158
+ const result = generateMockContextValues({ items: { onAction: { dataType: 'function' } } })
159
+ expect(typeof result.onAction).toBe('function')
160
+ })
161
+
162
+ it('maps richText to an HTML paragraph string', () => {
163
+ const result = generateMockContextValues({ items: { body: { dataType: 'richText' } } })
164
+ expect(result.body as string).toMatch(/^<p>/)
165
+ })
166
+
167
+ it('maps image to an image-shaped object', () => {
168
+ const result = generateMockContextValues({ items: { photo: { dataType: 'image' } } })
169
+ expect(result.photo).toMatchObject({
170
+ uri: expect.any(String),
171
+ url: expect.any(String),
172
+ width: expect.any(Number),
173
+ height: expect.any(Number),
174
+ })
175
+ })
176
+
177
+ it('maps link to a link-shaped object', () => {
178
+ const result = generateMockContextValues({ items: { destination: { dataType: 'link' } } })
179
+ expect(result.destination).toMatchObject({ href: expect.any(String), target: expect.any(String) })
180
+ })
181
+
182
+ it('maps data with a nested schema to a nested object', () => {
183
+ const result = generateMockContextValues({
184
+ items: {
185
+ event: {
186
+ dataType: 'data',
187
+ data: { items: { name: { dataType: 'text' } } },
188
+ },
189
+ },
190
+ })
191
+ expect(result.event).toMatchObject({ name: expect.any(String) })
192
+ })
193
+
194
+ it('maps data with an empty schema to a permissive mock that handles Object.keys safely', () => {
195
+ const result = generateMockContextValues({
196
+ items: { event: { dataType: 'data', data: { items: {} } } },
197
+ })
198
+ expect(() => Object.keys(result.event as object)).not.toThrow()
199
+ expect(Object.keys(result.event as object)).toHaveLength(0)
200
+ })
201
+
202
+ it('returns a permissive fallback for undeclared context fields so deep access chains do not crash', () => {
203
+ const result = generateMockContextValues({ items: {} })
204
+ const undeclared = (result as Record<string, unknown>).anything
205
+ expect(() => Object.keys(undeclared as object)).not.toThrow()
206
+ expect(Object.keys(undeclared as object)).toHaveLength(0)
207
+ const deeplyNested = (undeclared as Record<string, unknown>).deeply
208
+ expect(() => (deeplyNested as Record<string, unknown>)?.nested).not.toThrow()
209
+ })
210
+
211
+ it('maps arrayItems with a nested schema to a single-element array containing the element object', () => {
212
+ const result = generateMockContextValues({
213
+ items: {
214
+ tickets: {
215
+ dataType: 'arrayItems',
216
+ arrayItems: { item: { dataType: 'data', data: { items: { price: { dataType: 'number' } } } } },
217
+ },
218
+ },
219
+ })
220
+ expect(result.tickets).toHaveLength(1)
221
+ expect((result.tickets as Record<string, unknown>[])[0]).toMatchObject({ price: expect.any(Number) })
222
+ })
223
+
224
+ it('maps arrayItems with no item schema to an empty array', () => {
225
+ const result = generateMockContextValues({
226
+ items: { tickets: { dataType: 'arrayItems', arrayItems: {} } },
227
+ })
228
+ expect(result.tickets).toEqual([])
229
+ })
230
+
231
+ it('falls back to a string for unknown dataTypes', () => {
232
+ const result = generateMockContextValues({ items: { something: { dataType: 'unknown-type' } } })
233
+ expect(typeof result.something).toBe('string')
234
+ })
235
+ })
@@ -4,7 +4,11 @@
4
4
  */
5
5
 
6
6
  import { faker } from '@faker-js/faker'
7
- import { createRefElementMarker, extractElementPropsPathFromPropPath } from '../../../ref-elements/path-utils'
7
+ import type { ContextSchema } from '../../../extensions/context-providers/types'
8
+ import {
9
+ createRefElementMarker,
10
+ extractElementPropsPathFromPropPath,
11
+ } from '../../../extensions/ref-elements/path-utils'
8
12
  import type { ComponentInfo, DefaultValue, PropInfo, ResolvedType } from '../../ts/types'
9
13
 
10
14
  export const PRESETS_WRAPPER_CLASS_NAME = 'mock-presets-wrapper-probe'
@@ -484,3 +488,102 @@ function generateMockMenuItems(): unknown[] {
484
488
  link: generateMockLink(),
485
489
  }))
486
490
  }
491
+
492
+ // ─────────────────────────────────────────────────────────────────────────────
493
+ // Context Schema Mock Generator
494
+ // ─────────────────────────────────────────────────────────────────────────────
495
+
496
+ /**
497
+ * A recursive Proxy that safely handles any property access, function call, or string coercion.
498
+ * Used as a fallback for context fields not declared in the schema.
499
+ */
500
+ function buildPermissiveMock(): unknown {
501
+ const mock: unknown = new Proxy((() => {}) as (...args: unknown[]) => unknown, {
502
+ get(_target, prop) {
503
+ if (prop === 'then') return undefined // prevent thenable detection
504
+ if (prop === Symbol.toPrimitive) return (_hint: string) => 'mock'
505
+ if (prop === Symbol.iterator) return function* () {}
506
+ if (prop === Symbol.asyncIterator) return async function* () {}
507
+ if (typeof prop === 'symbol') return undefined
508
+ return mock
509
+ },
510
+ ownKeys() {
511
+ return []
512
+ },
513
+ getOwnPropertyDescriptor() {
514
+ return undefined
515
+ },
516
+ apply() {
517
+ return mock
518
+ },
519
+ })
520
+ return mock
521
+ }
522
+
523
+ /**
524
+ * Wraps schema-derived values in a Proxy so that any property NOT declared in the schema
525
+ * returns a permissive mock instead of undefined, preventing crashes on undeclared field access.
526
+ */
527
+ function buildSchemaSeededProxy(schemaValues: Record<string, unknown>): Record<string, unknown> {
528
+ return new Proxy(schemaValues, {
529
+ get(target, prop) {
530
+ if (prop === 'then') return undefined // prevent thenable/Promise detection
531
+ if (typeof prop === 'symbol') return Reflect.get(target, prop)
532
+ if (Object.prototype.hasOwnProperty.call(target, prop)) return target[prop as string]
533
+ return buildPermissiveMock()
534
+ },
535
+ })
536
+ }
537
+
538
+ /** Generate mock context values from a context extension schema. */
539
+ export function generateMockContextValues(schema: ContextSchema): Record<string, unknown> {
540
+ const result: Record<string, unknown> = {}
541
+ for (const [key, item] of Object.entries(schema.items)) {
542
+ result[key] = generateValueFromResolvedType(contextDataTypeToResolvedType(item), key, `context.${key}`)
543
+ }
544
+ return buildSchemaSeededProxy(result)
545
+ }
546
+
547
+ function contextDataTypeToResolvedType(item: ContextSchema['items'][string]): ResolvedType {
548
+ switch (item.dataType) {
549
+ case 'text':
550
+ return { kind: 'primitive', value: 'string' }
551
+ case 'richText':
552
+ return { kind: 'semantic', value: 'RichText' }
553
+ case 'image':
554
+ return { kind: 'semantic', value: 'Image' }
555
+ case 'video':
556
+ return { kind: 'semantic', value: 'Video' }
557
+ case 'vectorArt':
558
+ return { kind: 'semantic', value: 'VectorArt' }
559
+ case 'link':
560
+ return { kind: 'semantic', value: 'Link' }
561
+ case 'function':
562
+ return { kind: 'function' }
563
+ case 'number':
564
+ return { kind: 'primitive', value: 'number' }
565
+ case 'boolean':
566
+ return { kind: 'primitive', value: 'boolean' }
567
+ case 'arrayItems': {
568
+ const itemEntry = item.arrayItems?.item
569
+ if (itemEntry) {
570
+ const itemValue = generateValueFromResolvedType(
571
+ contextDataTypeToResolvedType(itemEntry),
572
+ 'item',
573
+ 'context.arrayItem',
574
+ )
575
+ return { kind: 'literal', value: [itemValue] }
576
+ }
577
+ return { kind: 'literal', value: [] }
578
+ }
579
+ case 'data': {
580
+ const nestedSchema = item.data
581
+ if (nestedSchema?.items && Object.keys(nestedSchema.items).length > 0) {
582
+ return { kind: 'literal', value: generateMockContextValues(nestedSchema) }
583
+ }
584
+ return { kind: 'literal', value: buildPermissiveMock() }
585
+ }
586
+ default:
587
+ return { kind: 'primitive', value: 'string' }
588
+ }
589
+ }
@@ -15,10 +15,13 @@ import {
15
15
  } from './information-extractors/react'
16
16
  import type { CoupledComponentInfo, CoupledProp, DOMBinding, TrackingStores } from './information-extractors/react'
17
17
 
18
+ import {
19
+ extractElementPropsPathFromPropPath,
20
+ extractParentElementPropsPath,
21
+ } from './extensions/ref-elements/path-utils'
18
22
  import { matchCssSelectors, parseCss } from './information-extractors/css'
19
23
  import type { CSSParserAPI } from './information-extractors/css'
20
24
  import { compileSass } from './information-extractors/css/sass-adapter'
21
- import { extractElementPropsPathFromPropPath, extractParentElementPropsPath } from './ref-elements/path-utils'
22
25
 
23
26
  import type { ComponentType } from 'react'
24
27
 
@@ -2,9 +2,9 @@ import { createRequire } from 'node:module'
2
2
  import path from 'node:path'
3
3
  import { pathToFileURL } from 'node:url'
4
4
  import { describe, expect, it } from 'vitest'
5
+ import { readRefElementComponentType } from './extensions/ref-elements/component-tag'
5
6
  import { loadCjsModule, loadModuleForExtraction } from './module-loader'
6
7
  import { readLoaderPortRefStateForTests, readRegisteredRefElementModuleUrlsForTests } from './react-runtime-loader'
7
- import { readRefElementComponentType } from './ref-elements/component-tag'
8
8
 
9
9
  const require = createRequire(import.meta.url)
10
10
 
@@ -4,8 +4,8 @@ import React from 'react'
4
4
  import type { ComponentType } from 'react'
5
5
  import ReactDOM from 'react-dom'
6
6
 
7
+ import type { RefElementModule } from './extensions/ref-elements/types'
7
8
  import { ensureReactRuntimeLoader, loadTaggedCjsModule, prepareRefElementEsmImport } from './react-runtime-loader'
8
- import type { RefElementModule } from './ref-elements/types'
9
9
 
10
10
  /**
11
11
  * Structured failure from `loadModule` when both ESM import and CJS require fail.
@@ -2,8 +2,8 @@ import { createHash } from 'node:crypto'
2
2
  import { createRequire, register } from 'node:module'
3
3
  import { fileURLToPath, pathToFileURL } from 'node:url'
4
4
  import { MessageChannel, MessagePort } from 'node:worker_threads'
5
- import { buildRefElementTaggerSource, tagRefElementComponentForCleanup } from './ref-elements/component-tag'
6
- import type { RefElementModule } from './ref-elements/types'
5
+ import { buildRefElementTaggerSource, tagRefElementComponentForCleanup } from './extensions/ref-elements/component-tag'
6
+ import type { RefElementModule } from './extensions/ref-elements/types'
7
7
 
8
8
  const JSX_INTERCEPTOR_STATE_KEY = 'zero-config:jsx-interceptor'
9
9
  const JSX_ORIGINALS_KEY = 'zero-config:jsx-originals'
@@ -68,6 +68,32 @@ interface RefElementRuntimeState {
68
68
  originalModuleLoad: InternalModuleApi['_load'] | null
69
69
  }
70
70
 
71
+ /**
72
+ * Returns the URL that the ESM loader will assign to a context provider module
73
+ * when it is imported transitively from a ref-entry-signed statics bundle.
74
+ *
75
+ * When runtimeModules is non-empty, the statics bundle entry URL carries a
76
+ * `?zero-config-ref-entry=HASH` signature, and `shouldPropagateRefElementEntrySignature`
77
+ * propagates that hash to every non-node_modules file: import. `captureHookContext`
78
+ * must import the provider at the same signed URL so both paths hit the same Node.js
79
+ * ESM cache entry (and therefore the same `createContext` return value).
80
+ *
81
+ * When runtimeModules is empty, no hash is ever appended, so the plain URL is correct.
82
+ */
83
+ export function computeSignedContextProviderUrl(resolvedModuleUrl: string, runtimeModules: RefElementModule[]): string {
84
+ if (runtimeModules.length === 0) {
85
+ return resolvedModuleUrl
86
+ }
87
+
88
+ const registeredModules = mergeRefElementModules(runtimeModules)
89
+ const signature = buildRefElementEntrySignature(registeredModules)
90
+ const signedUrl = new URL(resolvedModuleUrl)
91
+ if (!signedUrl.searchParams.has(REF_ELEMENT_ENTRY_SIGNATURE_QUERY_PARAM)) {
92
+ signedUrl.searchParams.set(REF_ELEMENT_ENTRY_SIGNATURE_QUERY_PARAM, signature)
93
+ }
94
+ return signedUrl.href
95
+ }
96
+
71
97
  export async function prepareRefElementEsmImport(
72
98
  entryPath: string,
73
99
  runtimeModules: RefElementModule[] = [],
@@ -1,280 +0,0 @@
1
- import ts from 'typescript'
2
- import type { ComponentInfo } from '../information-extractors/ts/types'
3
- import { extractEligibleRefElementPaths } from './eligible-paths'
4
- import { resolveModuleUrlFromEntryPath } from './module-resolution'
5
- import { extractBasePackageName, extractModuleLeafExportName } from './module-specifier'
6
- import type { RefElementContext, RefElementModule } from './types'
7
-
8
- interface RawRefElementExtensionEntry {
9
- options?: {
10
- type?: string
11
- resources?: {
12
- client?: {
13
- moduleSpecifier?: string
14
- }
15
- }
16
- }
17
- type?: string
18
- resources?: {
19
- client?: {
20
- moduleSpecifier?: string
21
- }
22
- }
23
- }
24
-
25
- interface NormalizedRefElementCatalogEntry {
26
- moduleSpecifier: string
27
- refComponentType: string
28
- }
29
-
30
- export async function buildRefElementContext(
31
- program: ts.Program,
32
- sourceFilePath: string,
33
- componentInfo: ComponentInfo,
34
- compiledEntryPath: string,
35
- ): Promise<RefElementContext> {
36
- const importedModuleSpecifiers = collectImportedRefElementModuleSpecifiers(program, sourceFilePath)
37
-
38
- return {
39
- eligiblePaths: extractEligibleRefElementPaths(componentInfo),
40
- runtimeModules: await resolveRuntimeModules(importedModuleSpecifiers, compiledEntryPath),
41
- }
42
- }
43
-
44
- async function resolveRuntimeModules(
45
- importedModuleSpecifiers: string[],
46
- compiledEntryPath: string,
47
- ): Promise<RefElementModule[]> {
48
- if (importedModuleSpecifiers.length === 0) {
49
- return []
50
- }
51
-
52
- const importedModuleSpecifierSet = new Set(importedModuleSpecifiers)
53
- const importedPackageNames = [
54
- ...new Set(importedModuleSpecifiers.filter(isPackageModuleSpecifier).map(extractBasePackageName)),
55
- ]
56
- const catalogEntries = (
57
- await Promise.all(importedPackageNames.map((packageName) => loadCatalogEntries(packageName, compiledEntryPath)))
58
- ).flat()
59
-
60
- const runtimeModulesBySpecifier = new Map<string, Set<string>>()
61
-
62
- for (const catalogEntry of catalogEntries) {
63
- if (!importedModuleSpecifierSet.has(catalogEntry.moduleSpecifier)) {
64
- continue
65
- }
66
-
67
- const refComponentTypes = runtimeModulesBySpecifier.get(catalogEntry.moduleSpecifier) ?? new Set<string>()
68
- refComponentTypes.add(catalogEntry.refComponentType)
69
- runtimeModulesBySpecifier.set(catalogEntry.moduleSpecifier, refComponentTypes)
70
- }
71
-
72
- const runtimeModules: RefElementModule[] = []
73
-
74
- for (const [moduleSpecifier, refComponentTypes] of runtimeModulesBySpecifier.entries()) {
75
- if (refComponentTypes.size !== 1) {
76
- continue
77
- }
78
-
79
- const refComponentType = [...refComponentTypes][0]
80
- if (!refComponentType) {
81
- continue
82
- }
83
-
84
- const resolvedModuleUrl = resolveModuleUrlFromEntryPath(compiledEntryPath, moduleSpecifier)
85
- if (!resolvedModuleUrl) {
86
- continue
87
- }
88
-
89
- runtimeModules.push({
90
- exportNames: buildRefElementExportNames(moduleSpecifier),
91
- moduleSpecifier,
92
- resolvedModuleUrl,
93
- refComponentType,
94
- })
95
- }
96
-
97
- return runtimeModules
98
- }
99
-
100
- function isPackageModuleSpecifier(moduleSpecifier: string): boolean {
101
- return !moduleSpecifier.startsWith('.') && !moduleSpecifier.startsWith('/') && !moduleSpecifier.startsWith('file:')
102
- }
103
-
104
- function collectImportedRefElementModuleSpecifiers(program: ts.Program, sourceFilePath: string): string[] {
105
- const entrySourceFile = findSourceFile(program, sourceFilePath)
106
- if (!entrySourceFile) {
107
- return []
108
- }
109
-
110
- const importedModuleSpecifiers = new Set<string>()
111
- const visitedSourceFiles = new Set<string>()
112
-
113
- function visitSourceFile(sourceFile: ts.SourceFile): void {
114
- if (visitedSourceFiles.has(sourceFile.fileName)) {
115
- return
116
- }
117
-
118
- visitedSourceFiles.add(sourceFile.fileName)
119
-
120
- for (const statement of sourceFile.statements) {
121
- const moduleSpecifier = readReferencedModuleSpecifier(statement)
122
- if (!moduleSpecifier) {
123
- continue
124
- }
125
-
126
- if (referencesRefElementModule(statement, moduleSpecifier)) {
127
- importedModuleSpecifiers.add(moduleSpecifier)
128
- }
129
-
130
- const importedSourceFile = resolveImportedSourceFile(program, sourceFile.fileName, moduleSpecifier)
131
- if (importedSourceFile) {
132
- visitSourceFile(importedSourceFile)
133
- }
134
- }
135
- }
136
-
137
- visitSourceFile(entrySourceFile)
138
- return [...importedModuleSpecifiers]
139
- }
140
-
141
- async function loadCatalogEntries(
142
- packageName: string,
143
- compiledEntryPath: string,
144
- ): Promise<NormalizedRefElementCatalogEntry[]> {
145
- const resolvedExtensionsUrl = resolveModuleUrlFromEntryPath(compiledEntryPath, `${packageName}/extensions`)
146
- if (!resolvedExtensionsUrl) {
147
- return []
148
- }
149
-
150
- const extensionModule = (await import(resolvedExtensionsUrl)) as Record<string, unknown>
151
-
152
- return extractRawExtensionEntries(extensionModule)
153
- .map(normalizeExtensionEntry)
154
- .flatMap((catalogEntry) => (catalogEntry ? [catalogEntry] : []))
155
- }
156
-
157
- function referencesRefElementModule(statement: ts.Statement, moduleSpecifier: string): boolean {
158
- const moduleLeafExportName = extractModuleLeafExportName(moduleSpecifier)
159
- if (!moduleLeafExportName) {
160
- return false
161
- }
162
-
163
- if (ts.isImportDeclaration(statement)) {
164
- return importsRefElementModule(statement, moduleLeafExportName)
165
- }
166
-
167
- if (ts.isExportDeclaration(statement)) {
168
- return reExportsRefElementModule(statement, moduleLeafExportName)
169
- }
170
-
171
- return false
172
- }
173
-
174
- function importsRefElementModule(importDeclaration: ts.ImportDeclaration, moduleLeafExportName: string): boolean {
175
- const importClause = importDeclaration.importClause
176
- if (!importClause) {
177
- return false
178
- }
179
-
180
- if (importClause.name) {
181
- return true
182
- }
183
-
184
- if (!importClause.namedBindings || !ts.isNamedImports(importClause.namedBindings)) {
185
- return false
186
- }
187
-
188
- return importClause.namedBindings.elements.some((importSpecifier) => {
189
- const importedName = importSpecifier.propertyName?.text ?? importSpecifier.name.text
190
- return importedName === moduleLeafExportName
191
- })
192
- }
193
-
194
- function reExportsRefElementModule(exportDeclaration: ts.ExportDeclaration, moduleLeafExportName: string): boolean {
195
- if (!exportDeclaration.exportClause || !ts.isNamedExports(exportDeclaration.exportClause)) {
196
- return false
197
- }
198
-
199
- return exportDeclaration.exportClause.elements.some((exportSpecifier) => {
200
- const exportedSourceName = exportSpecifier.propertyName?.text ?? exportSpecifier.name.text
201
- return exportedSourceName === 'default' || exportedSourceName === moduleLeafExportName
202
- })
203
- }
204
-
205
- function extractRawExtensionEntries(extensionModule: Record<string, unknown>): RawRefElementExtensionEntry[] {
206
- const defaultExport = extensionModule.default
207
- const extensionEntries = Array.isArray(defaultExport)
208
- ? defaultExport
209
- : ((defaultExport as { extensions?: unknown[] } | undefined)?.extensions ?? extensionModule.extensions)
210
-
211
- return Array.isArray(extensionEntries) ? (extensionEntries as RawRefElementExtensionEntry[]) : []
212
- }
213
-
214
- function normalizeExtensionEntry(rawEntry: RawRefElementExtensionEntry): NormalizedRefElementCatalogEntry | undefined {
215
- const normalizedEntry = rawEntry.options ?? rawEntry
216
- const moduleSpecifier = normalizedEntry.resources?.client?.moduleSpecifier
217
- const refComponentType = normalizedEntry.type
218
-
219
- if (!moduleSpecifier || !refComponentType) {
220
- return undefined
221
- }
222
-
223
- return {
224
- moduleSpecifier,
225
- refComponentType,
226
- }
227
- }
228
-
229
- function buildRefElementExportNames(moduleSpecifier: string): string[] {
230
- const exportNames = new Set<string>(['default'])
231
- if (extractBasePackageName(moduleSpecifier) === moduleSpecifier) {
232
- return [...exportNames]
233
- }
234
-
235
- const moduleLeafName = extractModuleLeafExportName(moduleSpecifier)
236
- if (moduleLeafName) {
237
- exportNames.add(moduleLeafName)
238
- }
239
-
240
- return [...exportNames]
241
- }
242
-
243
- function findSourceFile(program: ts.Program, sourceFilePath: string): ts.SourceFile | undefined {
244
- return (
245
- program.getSourceFile(sourceFilePath) ??
246
- program.getSourceFiles().find((sourceFile) => sourceFile.fileName === sourceFilePath)
247
- )
248
- }
249
-
250
- function resolveImportedSourceFile(
251
- program: ts.Program,
252
- importingFilePath: string,
253
- moduleSpecifier: string,
254
- ): ts.SourceFile | undefined {
255
- const resolvedModule = ts.resolveModuleName(moduleSpecifier, importingFilePath, program.getCompilerOptions(), ts.sys)
256
- const resolvedFileName = resolvedModule.resolvedModule?.resolvedFileName
257
-
258
- if (!resolvedFileName) {
259
- return undefined
260
- }
261
-
262
- const resolvedSourceFile = findSourceFile(program, resolvedFileName)
263
- if (!resolvedSourceFile || resolvedSourceFile.isDeclarationFile) {
264
- return undefined
265
- }
266
-
267
- return resolvedSourceFile
268
- }
269
-
270
- function readReferencedModuleSpecifier(statement: ts.Statement): string | undefined {
271
- if (ts.isImportDeclaration(statement) && ts.isStringLiteral(statement.moduleSpecifier)) {
272
- return statement.moduleSpecifier.text
273
- }
274
-
275
- if (ts.isExportDeclaration(statement) && statement.moduleSpecifier && ts.isStringLiteral(statement.moduleSpecifier)) {
276
- return statement.moduleSpecifier.text
277
- }
278
-
279
- return undefined
280
- }