@wix/zero-config-implementation 1.13.0 → 1.14.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.
@@ -5,5 +5,10 @@ export declare function extractAllComponentInfo(program: ts.Program, filePath: s
5
5
  * Extracts component info for only the default-exported component in a file.
6
6
  * Returns `undefined` if the file has no default export or if the default export
7
7
  * is not a recognized React component.
8
+ *
9
+ * Handles the re-export case where the entry file only re-exports the component
10
+ * from another file (e.g. `export { Foo as default } from './Foo'`). In that
11
+ * situation react-docgen-typescript finds no component in the entry file, so we
12
+ * follow the re-export to the defining file and extract from there instead.
8
13
  */
9
14
  export declare function extractDefaultComponentInfo(program: ts.Program, filePath: string): ComponentInfo | undefined;
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "registry": "https://registry.npmjs.org/",
5
5
  "access": "public"
6
6
  },
7
- "version": "1.13.0",
7
+ "version": "1.14.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",
@@ -74,5 +74,5 @@
74
74
  ]
75
75
  }
76
76
  },
77
- "falconPackageHash": "415f1144395df7902b157f7d63d64cd53a60a5f69c460b61c217594b"
77
+ "falconPackageHash": "1365e0b6dc9053e4dd5346884137f7cc61e12eed845e32e0e86a6122"
78
78
  }
@@ -60,13 +60,25 @@ export function extractAllComponentInfo(program: ts.Program, filePath: string):
60
60
  * Extracts component info for only the default-exported component in a file.
61
61
  * Returns `undefined` if the file has no default export or if the default export
62
62
  * is not a recognized React component.
63
+ *
64
+ * Handles the re-export case where the entry file only re-exports the component
65
+ * from another file (e.g. `export { Foo as default } from './Foo'`). In that
66
+ * situation react-docgen-typescript finds no component in the entry file, so we
67
+ * follow the re-export to the defining file and extract from there instead.
63
68
  */
64
69
  export function extractDefaultComponentInfo(program: ts.Program, filePath: string): ComponentInfo | undefined {
65
70
  const defaultName = findDefaultExportName(program, filePath)
66
71
  if (!defaultName) return undefined
67
72
 
68
- const all = extractAllComponentInfo(program, filePath)
69
- return all.find((c) => c.componentName === defaultName)
73
+ const found = extractAllComponentInfo(program, filePath).find((c) => c.componentName === defaultName)
74
+ if (found) return found
75
+
76
+ const definingFilePath = resolveDefaultReExportFilePath(program, filePath)
77
+ if (definingFilePath) {
78
+ return extractAllComponentInfo(program, definingFilePath).find((c) => c.componentName === defaultName)
79
+ }
80
+
81
+ return undefined
70
82
  }
71
83
 
72
84
  // ─────────────────────────────────────────────────────────────────────────────
@@ -75,7 +87,7 @@ export function extractDefaultComponentInfo(program: ts.Program, filePath: strin
75
87
 
76
88
  /**
77
89
  * Resolves the name of the default-exported symbol in a file using the
78
- * TypeScript type checker.
90
+ * TypeScript type checker, with an AST fallback for cross-module re-exports.
79
91
  *
80
92
  * Returns `undefined` for anonymous default exports (e.g. `export default () => ...`)
81
93
  * where no stable name can be determined.
@@ -91,17 +103,83 @@ function findDefaultExportName(program: ts.Program, filePath: string): string |
91
103
  const defaultSymbol = checker.getExportsOfModule(moduleSymbol).find((symbol) => symbol.getName() === 'default')
92
104
  if (!defaultSymbol) return undefined
93
105
 
94
- // For `export default Foo` the symbol is an alias — resolve it to get 'Foo'
95
106
  if (defaultSymbol.getFlags() & ts.SymbolFlags.Alias) {
96
- const aliased = checker.getAliasedSymbol(defaultSymbol)
97
- const name = aliased.getName()
98
- // 'default' means the alias resolved back to an anonymous default export
99
- return name !== 'default' ? name : undefined
107
+ const aliasedSymbol = checker.getAliasedSymbol(defaultSymbol)
108
+ const name = aliasedSymbol.getName()
109
+ // Cross-module re-exports (e.g. `export { Foo as default } from './Foo'`) produce a
110
+ // transient alias with no declarations. Fall back to reading the name from the AST.
111
+ const hasDeclarations = (aliasedSymbol.getDeclarations()?.length ?? 0) > 0
112
+ if (name !== 'default' && hasDeclarations) return name
113
+ }
114
+
115
+ return findDefaultReExportNameFromAST(sourceFile)
116
+ }
117
+
118
+ /**
119
+ * Scans the AST for a named re-export that aliases a symbol to `default`
120
+ * (i.e. `export { Foo as default } from './other'`) and returns the original
121
+ * symbol name (`Foo`).
122
+ */
123
+ function findDefaultReExportNameFromAST(sourceFile: ts.SourceFile): string | undefined {
124
+ const specifier = findDefaultReExportSpecifier(sourceFile)
125
+ // propertyName is the original name before aliasing; when absent, the specifier
126
+ // name is both the local and exported name (which here would be 'default').
127
+ return specifier?.propertyName?.text
128
+ }
129
+
130
+ /**
131
+ * Resolves the absolute file path of the module targeted by a default re-export.
132
+ *
133
+ * For `export { Foo as default } from './other'`, returns the resolved path of
134
+ * `'./other'`. Uses `ts.resolveModuleName` because the type checker's alias chain
135
+ * ends at a transient symbol with no declarations for cross-module re-exports.
136
+ *
137
+ * Returns `undefined` when the file has no default re-export or the module
138
+ * specifier cannot be resolved.
139
+ */
140
+ function resolveDefaultReExportFilePath(program: ts.Program, filePath: string): string | undefined {
141
+ const sourceFile = program.getSourceFile(filePath)
142
+ if (!sourceFile) return undefined
143
+
144
+ const exportDeclaration = findDefaultReExportSpecifier(sourceFile)?.parent?.parent
145
+ if (!exportDeclaration || !ts.isExportDeclaration(exportDeclaration)) return undefined
146
+ if (!exportDeclaration.moduleSpecifier || !ts.isStringLiteral(exportDeclaration.moduleSpecifier)) return undefined
147
+
148
+ const resolveResult = ts.resolveModuleName(
149
+ exportDeclaration.moduleSpecifier.text,
150
+ filePath,
151
+ program.getCompilerOptions(),
152
+ ts.sys,
153
+ )
154
+
155
+ const resolvedPath = resolveResult.resolvedModule?.resolvedFileName
156
+ if (resolvedPath && resolvedPath !== filePath) {
157
+ return resolvedPath
100
158
  }
101
159
 
102
160
  return undefined
103
161
  }
104
162
 
163
+ /**
164
+ * Finds the `ExportSpecifier` node for the specifier that aliases something
165
+ * to `default` in a re-export declaration (e.g. the `Foo as default` part of
166
+ * `export { Foo as default } from './other'`).
167
+ */
168
+ function findDefaultReExportSpecifier(sourceFile: ts.SourceFile): ts.ExportSpecifier | undefined {
169
+ for (const statement of sourceFile.statements) {
170
+ if (!ts.isExportDeclaration(statement)) continue
171
+ if (!statement.moduleSpecifier) continue
172
+ if (!statement.exportClause || !ts.isNamedExports(statement.exportClause)) continue
173
+
174
+ for (const specifier of statement.exportClause.elements) {
175
+ if (specifier.name.text === 'default') {
176
+ return specifier
177
+ }
178
+ }
179
+ }
180
+ return undefined
181
+ }
182
+
105
183
  // ─────────────────────────────────────────────────────────────────────────────
106
184
  // ComponentDoc → ComponentInfo conversion
107
185
  // ─────────────────────────────────────────────────────────────────────────────
@@ -30,12 +30,5 @@ export function compileTsFile(
30
30
  cause: error as Error,
31
31
  props: { phase: 'compile' },
32
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
- })
33
+ ).map(({ result }) => ts.createProgram([filePath], result.options))
41
34
  }