@wix/zero-config-implementation 1.25.0 → 1.27.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/dist/extraction-types.d.ts +7 -0
- package/dist/index.d.ts +12 -13
- package/dist/index.js +8159 -8139
- package/dist/information-extractors/css/types.d.ts +2 -0
- package/dist/manifest-pipeline.d.ts +3 -9
- package/dist/module-loader.d.ts +12 -5
- package/package.json +2 -2
- package/src/extraction-types.ts +8 -0
- package/src/index.ts +83 -101
- package/src/information-extractors/css/parse.ts +3 -2
- package/src/information-extractors/css/selector-matcher.ts +46 -21
- package/src/information-extractors/css/types.ts +2 -0
- package/src/manifest-pipeline.ts +53 -46
- package/src/module-loader.ts +24 -19
package/src/manifest-pipeline.ts
CHANGED
|
@@ -19,23 +19,19 @@ import { compileSass } from './information-extractors/css/sass-adapter'
|
|
|
19
19
|
|
|
20
20
|
import type { ComponentType } from 'react'
|
|
21
21
|
|
|
22
|
+
import { IoError, NotFoundError, ParseError } from './errors'
|
|
23
|
+
import type { ExtractionError } from './extraction-types'
|
|
24
|
+
|
|
22
25
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
23
26
|
// Types
|
|
24
27
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
25
28
|
|
|
26
|
-
/** A non-fatal issue encountered during component processing. */
|
|
27
|
-
export interface ExtractionWarning {
|
|
28
|
-
componentName: string
|
|
29
|
-
phase: 'render' | 'coupling' | 'css' | 'loader' | 'conversion'
|
|
30
|
-
error: string
|
|
31
|
-
stack?: string
|
|
32
|
-
}
|
|
33
|
-
|
|
34
29
|
export interface ExtractedCssInfo {
|
|
35
30
|
filePath: string
|
|
36
31
|
api: CSSParserAPI
|
|
37
32
|
properties: Map<string, string>
|
|
38
33
|
customProperties: Map<string, string>
|
|
34
|
+
isCssModule: boolean
|
|
39
35
|
}
|
|
40
36
|
|
|
41
37
|
export interface ComponentInfoWithCss extends CoupledComponentInfo {
|
|
@@ -46,7 +42,6 @@ export interface ComponentInfoWithCss extends CoupledComponentInfo {
|
|
|
46
42
|
/** The result of processing a single component through the manifest pipeline. */
|
|
47
43
|
export interface ProcessComponentResult {
|
|
48
44
|
component: ComponentInfoWithCss
|
|
49
|
-
warnings: ExtractionWarning[]
|
|
50
45
|
}
|
|
51
46
|
|
|
52
47
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -65,29 +60,34 @@ export function processComponent(
|
|
|
65
60
|
componentInfo: ComponentInfo,
|
|
66
61
|
loadComponent: (componentName: string) => ComponentType<unknown> | null,
|
|
67
62
|
cssImportPaths: string[],
|
|
68
|
-
loaderHasError
|
|
63
|
+
loaderHasError: boolean,
|
|
64
|
+
report: (error: ExtractionError) => void,
|
|
69
65
|
options?: RunExtractorsOptions,
|
|
70
66
|
): ProcessComponentResult {
|
|
71
|
-
const
|
|
67
|
+
const { componentName } = componentInfo
|
|
72
68
|
|
|
73
69
|
// Load the actual component
|
|
74
70
|
let Component: ComponentType<unknown> | null = null
|
|
75
71
|
try {
|
|
76
|
-
Component = loadComponent(
|
|
72
|
+
Component = loadComponent(componentName)
|
|
77
73
|
if (!Component && !loaderHasError) {
|
|
78
|
-
|
|
79
|
-
componentName
|
|
74
|
+
report({
|
|
75
|
+
componentName,
|
|
80
76
|
phase: 'loader',
|
|
81
|
-
error: `Component "${
|
|
77
|
+
error: new NotFoundError(`Component "${componentName}" not found in package exports`, {
|
|
78
|
+
props: { phase: 'loader' },
|
|
79
|
+
}),
|
|
82
80
|
})
|
|
83
81
|
}
|
|
84
|
-
} catch (
|
|
82
|
+
} catch (thrownError) {
|
|
85
83
|
if (!loaderHasError) {
|
|
86
|
-
|
|
87
|
-
componentName
|
|
84
|
+
report({
|
|
85
|
+
componentName,
|
|
88
86
|
phase: 'loader',
|
|
89
|
-
error:
|
|
90
|
-
|
|
87
|
+
error: new IoError(
|
|
88
|
+
`Failed to load "${componentName}": ${thrownError instanceof Error ? thrownError.message : String(thrownError)}`,
|
|
89
|
+
{ cause: thrownError instanceof Error ? thrownError : undefined, props: { phase: 'loader' } },
|
|
90
|
+
),
|
|
91
91
|
})
|
|
92
92
|
}
|
|
93
93
|
}
|
|
@@ -109,18 +109,20 @@ export function processComponent(
|
|
|
109
109
|
|
|
110
110
|
const { props: coupledProps, innerElementProps } = buildCoupledProps(componentInfo, state.stores)
|
|
111
111
|
coupledInfo = {
|
|
112
|
-
componentName
|
|
112
|
+
componentName,
|
|
113
113
|
props: coupledProps,
|
|
114
114
|
elements: convertElements(extractedElements),
|
|
115
115
|
innerElementProps: innerElementProps.size > 0 ? innerElementProps : undefined,
|
|
116
116
|
propUsages: state.stores.propUsages,
|
|
117
117
|
}
|
|
118
|
-
} catch (
|
|
119
|
-
|
|
120
|
-
componentName
|
|
118
|
+
} catch (thrownError) {
|
|
119
|
+
report({
|
|
120
|
+
componentName,
|
|
121
121
|
phase: 'render',
|
|
122
|
-
error:
|
|
123
|
-
|
|
122
|
+
error: new IoError(thrownError instanceof Error ? thrownError.message : String(thrownError), {
|
|
123
|
+
cause: thrownError instanceof Error ? thrownError : undefined,
|
|
124
|
+
props: { phase: 'render' },
|
|
125
|
+
}),
|
|
124
126
|
})
|
|
125
127
|
}
|
|
126
128
|
}
|
|
@@ -132,7 +134,7 @@ export function processComponent(
|
|
|
132
134
|
} else {
|
|
133
135
|
// Fallback: create minimal info without DOM coupling
|
|
134
136
|
enhancedInfo = {
|
|
135
|
-
componentName
|
|
137
|
+
componentName,
|
|
136
138
|
props: Object.fromEntries(
|
|
137
139
|
Object.entries(componentInfo.props).map(([name, info]) => [
|
|
138
140
|
name,
|
|
@@ -145,8 +147,7 @@ export function processComponent(
|
|
|
145
147
|
}
|
|
146
148
|
|
|
147
149
|
// Read and parse CSS imports
|
|
148
|
-
const
|
|
149
|
-
warnings.push(...cssWarnings)
|
|
150
|
+
const css = extractCssInfo(cssImportPaths, componentName, report)
|
|
150
151
|
|
|
151
152
|
// Match CSS selectors to elements
|
|
152
153
|
let varUsedByTraceId = new Map<string, Set<string>>()
|
|
@@ -158,12 +159,14 @@ export function processComponent(
|
|
|
158
159
|
elements: convertElements(matchResult.elements),
|
|
159
160
|
}
|
|
160
161
|
varUsedByTraceId = matchResult.varUsedByTraceId
|
|
161
|
-
} catch (
|
|
162
|
-
|
|
163
|
-
componentName
|
|
162
|
+
} catch (thrownError) {
|
|
163
|
+
report({
|
|
164
|
+
componentName,
|
|
164
165
|
phase: 'css',
|
|
165
|
-
error:
|
|
166
|
-
|
|
166
|
+
error: new IoError(
|
|
167
|
+
`CSS selector matching failed: ${thrownError instanceof Error ? thrownError.message : String(thrownError)}`,
|
|
168
|
+
{ cause: thrownError instanceof Error ? thrownError : undefined, props: { phase: 'css' } },
|
|
169
|
+
),
|
|
167
170
|
})
|
|
168
171
|
}
|
|
169
172
|
}
|
|
@@ -174,7 +177,6 @@ export function processComponent(
|
|
|
174
177
|
css,
|
|
175
178
|
varUsedByTraceId,
|
|
176
179
|
},
|
|
177
|
-
warnings,
|
|
178
180
|
}
|
|
179
181
|
}
|
|
180
182
|
|
|
@@ -326,14 +328,14 @@ function convertElements(elements: ExtractedElement[]): CoupledComponentInfo['el
|
|
|
326
328
|
|
|
327
329
|
/**
|
|
328
330
|
* Reads and parses CSS files, extracting standard and custom properties.
|
|
329
|
-
*
|
|
331
|
+
* Non-fatal parse failures are reported via `reportError`.
|
|
330
332
|
*/
|
|
331
333
|
function extractCssInfo(
|
|
332
334
|
cssImportPaths: string[],
|
|
333
335
|
componentName: string,
|
|
334
|
-
|
|
336
|
+
report: (error: ExtractionError) => void,
|
|
337
|
+
): ExtractedCssInfo[] {
|
|
335
338
|
const cssInfos: ExtractedCssInfo[] = []
|
|
336
|
-
const warnings: ExtractionWarning[] = []
|
|
337
339
|
|
|
338
340
|
for (const cssPath of cssImportPaths) {
|
|
339
341
|
try {
|
|
@@ -342,11 +344,13 @@ function extractCssInfo(
|
|
|
342
344
|
if (cssPath.endsWith('.scss') || cssPath.endsWith('.sass')) {
|
|
343
345
|
const compiledCssResult = compileSass(cssPath)
|
|
344
346
|
if (compiledCssResult.isErr()) {
|
|
345
|
-
|
|
347
|
+
report({
|
|
346
348
|
componentName,
|
|
347
349
|
phase: 'css',
|
|
348
|
-
error: `Failed to
|
|
349
|
-
|
|
350
|
+
error: new ParseError(`Failed to compile ${cssPath}`, {
|
|
351
|
+
cause: compiledCssResult.error,
|
|
352
|
+
props: { phase: 'css' },
|
|
353
|
+
}),
|
|
350
354
|
})
|
|
351
355
|
continue
|
|
352
356
|
}
|
|
@@ -378,16 +382,19 @@ function extractCssInfo(
|
|
|
378
382
|
api,
|
|
379
383
|
properties,
|
|
380
384
|
customProperties,
|
|
385
|
+
isCssModule: /\.module\.(css|scss|sass)$/.test(cssPath),
|
|
381
386
|
})
|
|
382
|
-
} catch (
|
|
383
|
-
|
|
387
|
+
} catch (thrownError) {
|
|
388
|
+
report({
|
|
384
389
|
componentName,
|
|
385
390
|
phase: 'css',
|
|
386
|
-
error:
|
|
387
|
-
|
|
391
|
+
error: new ParseError(
|
|
392
|
+
`Failed to parse ${cssPath}: ${thrownError instanceof Error ? thrownError.message : String(thrownError)}`,
|
|
393
|
+
{ cause: thrownError instanceof Error ? thrownError : undefined, props: { phase: 'css' } },
|
|
394
|
+
),
|
|
388
395
|
})
|
|
389
396
|
}
|
|
390
397
|
}
|
|
391
398
|
|
|
392
|
-
return
|
|
399
|
+
return cssInfos
|
|
393
400
|
}
|
package/src/module-loader.ts
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
import { createRequire } from 'node:module'
|
|
2
|
-
import { pascalCase } from 'case-anything'
|
|
3
2
|
import { ResultAsync, errAsync, okAsync } from 'neverthrow'
|
|
4
3
|
import type { ComponentType } from 'react'
|
|
5
|
-
import { IoError } from './errors'
|
|
6
4
|
|
|
7
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Structured failure from `loadModule` when both ESM import and CJS require fail.
|
|
7
|
+
* Carries each error separately so callers can report them independently.
|
|
8
|
+
*/
|
|
9
|
+
export interface LoadModuleFailure {
|
|
10
|
+
/** The ESM import error, or null when no ESM was attempted (e.g. empty path). */
|
|
11
|
+
esmError: Error | null
|
|
12
|
+
/** The CJS require error, or a generic error for the empty-path case. */
|
|
13
|
+
cjsError: Error
|
|
14
|
+
}
|
|
8
15
|
|
|
9
16
|
/**
|
|
10
17
|
* Attempts to load a module, first via ESM `import()`, then via CJS `require`.
|
|
@@ -17,32 +24,30 @@ type IoErrorInstance = InstanceType<typeof IoError>
|
|
|
17
24
|
*
|
|
18
25
|
* @param entryPath - Absolute path to the module entry point.
|
|
19
26
|
* @returns A `ResultAsync` containing the module exports on success.
|
|
20
|
-
* @errors {
|
|
27
|
+
* @errors {LoadModuleFailure} When both ESM import and CJS require fail.
|
|
21
28
|
*/
|
|
22
|
-
export function loadModule(entryPath: string): ResultAsync<Record<string, unknown>,
|
|
23
|
-
|
|
29
|
+
export function loadModule(entryPath: string): ResultAsync<Record<string, unknown>, LoadModuleFailure> {
|
|
30
|
+
if (!entryPath) {
|
|
31
|
+
return errAsync({ esmError: null, cjsError: new Error('No compiled entry path provided') })
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return ResultAsync.fromPromise(
|
|
35
|
+
import(entryPath) as Promise<Record<string, unknown>>,
|
|
36
|
+
(esmErr): Error => (esmErr instanceof Error ? esmErr : new Error(String(esmErr))),
|
|
37
|
+
).orElse((esmError) => {
|
|
24
38
|
try {
|
|
25
39
|
const require = createRequire(import.meta.url)
|
|
26
40
|
const exports = require(entryPath)
|
|
27
41
|
return okAsync(exports as Record<string, unknown>)
|
|
28
42
|
} catch (requireErr) {
|
|
29
|
-
const
|
|
30
|
-
return errAsync(
|
|
31
|
-
new IoError(`Failed to load ${entryPath}: ${cause.message}`, {
|
|
32
|
-
cause,
|
|
33
|
-
props: { phase: 'load' },
|
|
34
|
-
}),
|
|
35
|
-
)
|
|
43
|
+
const cjsError = requireErr instanceof Error ? requireErr : new Error(String(requireErr))
|
|
44
|
+
return errAsync({ esmError, cjsError })
|
|
36
45
|
}
|
|
37
46
|
})
|
|
38
47
|
}
|
|
39
48
|
|
|
40
|
-
function isPascalCase(name: string): boolean {
|
|
41
|
-
return name.length > 0 && pascalCase(name) === name
|
|
42
|
-
}
|
|
43
|
-
|
|
44
49
|
function isComponent(value: unknown): value is ComponentType<unknown> {
|
|
45
|
-
if (typeof value === 'function') return
|
|
50
|
+
if (typeof value === 'function') return true
|
|
46
51
|
// React.memo() and React.forwardRef() return objects, not functions
|
|
47
52
|
if (typeof value === 'object' && value !== null && '$$typeof' in value) return true
|
|
48
53
|
return false
|
|
@@ -51,7 +56,7 @@ function isComponent(value: unknown): value is ComponentType<unknown> {
|
|
|
51
56
|
export function findComponent(moduleExports: Record<string, unknown>, name: string): ComponentType<unknown> | null {
|
|
52
57
|
// Direct named export
|
|
53
58
|
const direct = moduleExports[name]
|
|
54
|
-
if (
|
|
59
|
+
if (isComponent(direct)) {
|
|
55
60
|
return direct as ComponentType<unknown>
|
|
56
61
|
}
|
|
57
62
|
|