@wix/zero-config-implementation 1.82.0 → 1.84.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/{index-C-TJl-9-.js → index-BHTXuyLh.js} +1 -1
- package/dist/{index-tAvBvLbA.js → index-Byvq-358.js} +23686 -23720
- package/dist/index.d.ts +4 -12
- package/dist/index.js +1 -1
- package/package.json +2 -2
- package/src/converters/data-item-builder.ts +4 -1
- package/src/converters/to-editor-component.ts +11 -5
- package/src/index.css-imports-fatal.test.ts +33 -0
- package/src/index.ts +31 -77
- package/src/information-extractors/css/parse.ts +110 -106
- package/src/information-extractors/ts/components.ts +10 -0
- package/src/information-extractors/ts/types.ts +3 -0
- package/src/manifest-pipeline.css-match-fatal.test.ts +41 -0
- package/src/manifest-pipeline.ts +88 -130
- package/src/module-loader.ts +0 -4
- package/src/extraction-types.ts +0 -8
package/dist/index.d.ts
CHANGED
|
@@ -857,14 +857,12 @@ export declare function extractComponentManifest(componentPath: string, compiled
|
|
|
857
857
|
*
|
|
858
858
|
* @param componentPath - Path to the TypeScript source file
|
|
859
859
|
* @param compiledEntryPath - Path to the built JS entry file of the component's package (e.g. dist/index.js)
|
|
860
|
-
* @param options.onError - Called for each non-fatal extraction error as it occurs
|
|
861
860
|
* @errors
|
|
862
861
|
* - {@link NotFoundError} — Source file does not exist (phase: `compile`)
|
|
863
|
-
* - {@link ParseError} — TypeScript config or component types could not be parsed (phase: `compile` | `extract`)
|
|
864
|
-
* - {@link IoError} —
|
|
862
|
+
* - {@link ParseError} — TypeScript config or component types could not be parsed (phase: `compile` | `css` | `extract`)
|
|
863
|
+
* - {@link IoError} — Module failed to load, export access threw, render failed, or CSS selector matching failed
|
|
865
864
|
*/
|
|
866
865
|
export declare interface ExtractComponentManifestOptions extends RunExtractorsOptions {
|
|
867
|
-
onError?: (error: ExtractionError) => void;
|
|
868
866
|
}
|
|
869
867
|
|
|
870
868
|
export declare function extractComponentManifestResult(componentPath: string, compiledEntryPath: string, options?: ExtractComponentManifestOptions): ResultAsync<ManifestResult, InstanceType<typeof NotFoundError> | InstanceType<typeof ParseError> | InstanceType<typeof IoError>>;
|
|
@@ -909,13 +907,6 @@ declare type ExtractErrTypes<T extends readonly Result<unknown, unknown>[]> = {
|
|
|
909
907
|
[idx in keyof T]: T[idx] extends Result<unknown, infer E> ? E : never;
|
|
910
908
|
};
|
|
911
909
|
|
|
912
|
-
/** A non-fatal issue encountered during component extraction. */
|
|
913
|
-
export declare interface ExtractionError {
|
|
914
|
-
componentName: string;
|
|
915
|
-
phase: 'render' | 'coupling' | 'css' | 'loader' | 'conversion';
|
|
916
|
-
error: InstanceType<typeof BaseError>;
|
|
917
|
-
}
|
|
918
|
-
|
|
919
910
|
export declare interface ExtractionResult {
|
|
920
911
|
html: string;
|
|
921
912
|
store: ExtractorStore;
|
|
@@ -1590,7 +1581,6 @@ CauseArg extends Cause,
|
|
|
1590
1581
|
|
|
1591
1582
|
export declare interface ManifestResult {
|
|
1592
1583
|
component: EditorReactComponent;
|
|
1593
|
-
errors: ExtractionError[];
|
|
1594
1584
|
}
|
|
1595
1585
|
|
|
1596
1586
|
declare type MemberListOf<T> = ((T extends unknown ? (t: T) => T : never) extends infer U ? (U extends unknown ? (u: U) => unknown : never) extends (v: infer V) => unknown ? V : never : never) extends (_: unknown) => infer W ? [...MemberListOf<Exclude<T, W>>, W] : [];
|
|
@@ -2395,6 +2385,8 @@ export declare interface PropInfo {
|
|
|
2395
2385
|
resolvedType: ResolvedType;
|
|
2396
2386
|
description?: string;
|
|
2397
2387
|
deprecated?: boolean;
|
|
2388
|
+
min?: string;
|
|
2389
|
+
max?: string;
|
|
2398
2390
|
isStateTrigger?: boolean;
|
|
2399
2391
|
}
|
|
2400
2392
|
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { B as t, D as e, E as o, I as n, N as c, P as l, R as i, a as p, V as E, b as m, c as x, d as f, e as d, f as u, h as C, i as I, j as k, k as y, l as A, m as D, n as P, o as R, p as h, q as B, r as M, s as T, t as b, w } from "./index-
|
|
1
|
+
import { B as t, D as e, E as o, I as n, N as c, P as l, R as i, a as p, V as E, b as m, c as x, d as f, e as d, f as u, h as C, i as I, j as k, k as y, l as A, m as D, n as P, o as R, p as h, q as B, r as M, s as T, t as b, w } from "./index-Byvq-358.js";
|
|
2
2
|
import "react";
|
|
3
3
|
export {
|
|
4
4
|
t as BaseError,
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"registry": "https://registry.npmjs.org/",
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
7
|
-
"version": "1.
|
|
7
|
+
"version": "1.84.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",
|
|
@@ -82,5 +82,5 @@
|
|
|
82
82
|
]
|
|
83
83
|
}
|
|
84
84
|
},
|
|
85
|
-
"falconPackageHash": "
|
|
85
|
+
"falconPackageHash": "69093241c1274c1e1c73c1f04a0b7b1d9f478ab11a8829b00f8c8cc8"
|
|
86
86
|
}
|
|
@@ -169,7 +169,10 @@ function handlePrimitiveType(
|
|
|
169
169
|
}
|
|
170
170
|
} else if (typeValue.includes('number')) {
|
|
171
171
|
dataItem.dataType = DATA_TYPE.number
|
|
172
|
-
dataItem.number =
|
|
172
|
+
dataItem.number =
|
|
173
|
+
propInfo.min != null || propInfo.max != null
|
|
174
|
+
? { ...(propInfo.min != null && { min: propInfo.min }), ...(propInfo.max != null && { max: propInfo.max }) }
|
|
175
|
+
: {}
|
|
173
176
|
} else if (typeValue.includes('boolean')) {
|
|
174
177
|
dataItem.dataType = DATA_TYPE.booleanValue
|
|
175
178
|
} else {
|
|
@@ -11,6 +11,7 @@ import type {
|
|
|
11
11
|
} from '@wix/react-component-schema'
|
|
12
12
|
import { CSS_PROPERTIES, ELEMENTS } from '@wix/react-component-schema'
|
|
13
13
|
import { camelCase } from 'case-anything'
|
|
14
|
+
import { IoError } from '../errors'
|
|
14
15
|
import { buildRefElementManifestKey } from '../extensions/ref-elements/path-utils'
|
|
15
16
|
import type { RefElementMatch } from '../extensions/ref-elements/types'
|
|
16
17
|
import type { ComponentInfoWithCss } from '../index'
|
|
@@ -66,8 +67,14 @@ function buildEditorElement(
|
|
|
66
67
|
nearestCommonAncestorCustomProps: Map<string, Record<string, CssCustomPropertyItem>>,
|
|
67
68
|
): EditorElement {
|
|
68
69
|
const rootElement = component.elements[0]
|
|
69
|
-
|
|
70
|
-
|
|
70
|
+
if (!rootElement) {
|
|
71
|
+
throw new IoError(
|
|
72
|
+
`Component "${component.componentName}" render produced no root element — null or text-only render`,
|
|
73
|
+
{ props: { phase: 'render' } },
|
|
74
|
+
)
|
|
75
|
+
}
|
|
76
|
+
const childElements = rootElement.children
|
|
77
|
+
const rootCustomProps = nearestCommonAncestorCustomProps.get(rootElement.traceId) ?? {}
|
|
71
78
|
|
|
72
79
|
const customClassTriggers = collectCustomClassTriggers(component)
|
|
73
80
|
|
|
@@ -149,9 +156,8 @@ function buildData(
|
|
|
149
156
|
const defaultValue = prop.defaultValue?.kind !== 'unresolved' ? prop.defaultValue?.value : undefined
|
|
150
157
|
|
|
151
158
|
const result = buildDataItem(prop, defaultValue, propUsages, prop.propPath)
|
|
152
|
-
if (result.
|
|
153
|
-
|
|
154
|
-
}
|
|
159
|
+
if (result.isErr()) throw result.error
|
|
160
|
+
data[name] = result.value
|
|
155
161
|
}
|
|
156
162
|
|
|
157
163
|
return data
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import path from 'node:path'
|
|
2
|
+
import { beforeEach, expect, it, vi } from 'vitest'
|
|
3
|
+
import { extractComponentManifestResult } from './index'
|
|
4
|
+
|
|
5
|
+
vi.mock('./information-extractors/ts', async (importOriginal) => {
|
|
6
|
+
const original = await importOriginal<typeof import('./information-extractors/ts')>()
|
|
7
|
+
return {
|
|
8
|
+
...original,
|
|
9
|
+
extractCssImports: vi.fn(),
|
|
10
|
+
}
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
const componentPath = path.resolve(
|
|
14
|
+
import.meta.dirname,
|
|
15
|
+
'../../../packages/example-components/src/components/FunctionComponent/component.tsx',
|
|
16
|
+
)
|
|
17
|
+
const compiledEntryPath = path.resolve(import.meta.dirname, '../../../packages/example-components/dist/index.js')
|
|
18
|
+
|
|
19
|
+
it('throws ParseError when extractCssImports throws', async () => {
|
|
20
|
+
const { extractCssImports } = await import('./information-extractors/ts')
|
|
21
|
+
vi.mocked(extractCssImports).mockImplementation(() => {
|
|
22
|
+
throw new Error('simulated extractCssImports failure')
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
const result = await extractComponentManifestResult(componentPath, compiledEntryPath)
|
|
26
|
+
|
|
27
|
+
expect(result.isErr()).toBe(true)
|
|
28
|
+
if (result.isErr()) {
|
|
29
|
+
expect(result.error.name).toBe('ParseError')
|
|
30
|
+
expect(result.error.message).toContain('Failed to extract CSS import paths from')
|
|
31
|
+
expect(result.error.message).toContain('simulated extractCssImports failure')
|
|
32
|
+
}
|
|
33
|
+
})
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { WixServicesWrapper } from '@wix/builder-services-wrapper'
|
|
2
2
|
import type { EditorReactComponent } from '@wix/react-component-schema'
|
|
3
|
-
import { Result, ResultAsync
|
|
3
|
+
import { Result, ResultAsync } from 'neverthrow'
|
|
4
4
|
import React, { type ComponentType } from 'react'
|
|
5
5
|
|
|
6
6
|
import { toEditorReactComponent } from './converters'
|
|
@@ -9,13 +9,11 @@ import { buildContextProviderModules } from './extensions/context-providers/cont
|
|
|
9
9
|
import { buildContextAwareWrapper, loadMockProviders } from './extensions/context-providers/mock-provider'
|
|
10
10
|
import { buildRefElementContext } from './extensions/ref-elements/context'
|
|
11
11
|
import type { RefElementContext } from './extensions/ref-elements/types'
|
|
12
|
-
import type { ExtractionError } from './extraction-types'
|
|
13
12
|
import type { RunExtractorsOptions } from './information-extractors/react'
|
|
14
13
|
import { extractCssImports, extractDefaultComponentInfo } from './information-extractors/ts'
|
|
15
14
|
import type { ComponentInfo } from './information-extractors/ts/types'
|
|
16
15
|
import { processComponent } from './manifest-pipeline'
|
|
17
16
|
import { findComponent, findDefaultComponent, loadModuleForExtraction } from './module-loader'
|
|
18
|
-
import type { LoadModuleFailure } from './module-loader'
|
|
19
17
|
import { compileTsFile } from './ts-compiler'
|
|
20
18
|
|
|
21
19
|
const defaultWrapper = (Component: ComponentType<unknown>): ComponentType<unknown> => {
|
|
@@ -30,7 +28,6 @@ const defaultWrapper = (Component: ComponentType<unknown>): ComponentType<unknow
|
|
|
30
28
|
|
|
31
29
|
export interface ManifestResult {
|
|
32
30
|
component: EditorReactComponent
|
|
33
|
-
errors: ExtractionError[]
|
|
34
31
|
}
|
|
35
32
|
|
|
36
33
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
@@ -42,15 +39,12 @@ export interface ManifestResult {
|
|
|
42
39
|
*
|
|
43
40
|
* @param componentPath - Path to the TypeScript source file
|
|
44
41
|
* @param compiledEntryPath - Path to the built JS entry file of the component's package (e.g. dist/index.js)
|
|
45
|
-
* @param options.onError - Called for each non-fatal extraction error as it occurs
|
|
46
42
|
* @errors
|
|
47
43
|
* - {@link NotFoundError} — Source file does not exist (phase: `compile`)
|
|
48
|
-
* - {@link ParseError} — TypeScript config or component types could not be parsed (phase: `compile` | `extract`)
|
|
49
|
-
* - {@link IoError} —
|
|
44
|
+
* - {@link ParseError} — TypeScript config or component types could not be parsed (phase: `compile` | `css` | `extract`)
|
|
45
|
+
* - {@link IoError} — Module failed to load, export access threw, render failed, or CSS selector matching failed
|
|
50
46
|
*/
|
|
51
|
-
export interface ExtractComponentManifestOptions extends RunExtractorsOptions {
|
|
52
|
-
onError?: (error: ExtractionError) => void
|
|
53
|
-
}
|
|
47
|
+
export interface ExtractComponentManifestOptions extends RunExtractorsOptions {}
|
|
54
48
|
|
|
55
49
|
export function extractComponentManifestResult(
|
|
56
50
|
componentPath: string,
|
|
@@ -60,12 +54,6 @@ export function extractComponentManifestResult(
|
|
|
60
54
|
ManifestResult,
|
|
61
55
|
InstanceType<typeof NotFoundError> | InstanceType<typeof ParseError> | InstanceType<typeof IoError>
|
|
62
56
|
> {
|
|
63
|
-
const errors: ExtractionError[] = []
|
|
64
|
-
const report = (error: ExtractionError): void => {
|
|
65
|
-
errors.push(error)
|
|
66
|
-
options?.onError?.(error)
|
|
67
|
-
}
|
|
68
|
-
|
|
69
57
|
// Step 1: Compile TypeScript (fatal)
|
|
70
58
|
return compileTsFile(componentPath)
|
|
71
59
|
.andThen((program) => {
|
|
@@ -137,68 +125,42 @@ export function extractComponentManifestResult(
|
|
|
137
125
|
wrapper,
|
|
138
126
|
}
|
|
139
127
|
|
|
140
|
-
// Step 5: Load the compiled package module (
|
|
141
|
-
// so componentName is known when reporting failures
|
|
128
|
+
// Step 5: Load the compiled package module (fatal)
|
|
142
129
|
return loadModuleForExtraction(compiledEntryPath, refElementContext.runtimeModules)
|
|
143
|
-
.
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
failure,
|
|
154
|
-
}),
|
|
155
|
-
)
|
|
156
|
-
.andThen(({ cleanup, failure, loadComponent }) => {
|
|
157
|
-
if (failure) {
|
|
158
|
-
if (failure.esmError) {
|
|
159
|
-
report({
|
|
160
|
-
componentName,
|
|
161
|
-
phase: 'loader',
|
|
162
|
-
error: new IoError('ESM import failed', { cause: failure.esmError, props: { phase: 'loader' } }),
|
|
163
|
-
})
|
|
164
|
-
}
|
|
165
|
-
report({
|
|
166
|
-
componentName,
|
|
167
|
-
phase: 'loader',
|
|
168
|
-
error: new IoError('CJS require failed', { cause: failure.cjsError, props: { phase: 'loader' } }),
|
|
169
|
-
})
|
|
170
|
-
}
|
|
130
|
+
.mapErr((failure) => {
|
|
131
|
+
const esmDetail = failure.esmError ? `ESM: ${failure.esmError.message}` : 'ESM: not attempted'
|
|
132
|
+
return new IoError(
|
|
133
|
+
`Failed to load compiled bundle at "${compiledEntryPath}". ${esmDetail}. CJS: ${failure.cjsError.message}. Ensure the package is built before extraction.`,
|
|
134
|
+
{ cause: failure.cjsError, props: { phase: 'loader' } },
|
|
135
|
+
)
|
|
136
|
+
})
|
|
137
|
+
.andThen(({ cleanup, moduleExports }) => {
|
|
138
|
+
const loadComponent = (name: string): ComponentType<unknown> | null =>
|
|
139
|
+
findComponent(moduleExports, name) ?? findDefaultComponent(moduleExports)
|
|
171
140
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
`Failed to extract CSS imports: ${thrown instanceof Error ? thrown.message : String(thrown)}`,
|
|
184
|
-
{ cause: thrown instanceof Error ? thrown : undefined, props: { phase: 'css' } },
|
|
185
|
-
),
|
|
186
|
-
})
|
|
141
|
+
const getCssImportPaths = (): string[] => {
|
|
142
|
+
const cssResult = Result.fromThrowable(
|
|
143
|
+
extractCssImports,
|
|
144
|
+
(thrown) =>
|
|
145
|
+
new ParseError(
|
|
146
|
+
`Failed to extract CSS import paths from "${componentPath}": ${thrown instanceof Error ? thrown.message : String(thrown)}`,
|
|
147
|
+
{ cause: thrown instanceof Error ? thrown : undefined, props: { phase: 'css' } },
|
|
148
|
+
),
|
|
149
|
+
)(program)
|
|
150
|
+
if (cssResult.isErr()) throw cssResult.error
|
|
151
|
+
return cssResult.value
|
|
187
152
|
}
|
|
188
153
|
|
|
189
|
-
// Step 7:
|
|
154
|
+
// Step 6+7: CSS extraction and component processing — both under the cleanup boundary
|
|
190
155
|
return ResultAsync.fromPromise(
|
|
191
156
|
processComponentWithCleanup(
|
|
192
157
|
componentInfo,
|
|
193
158
|
loadComponent,
|
|
194
|
-
|
|
195
|
-
!!failure,
|
|
196
|
-
report,
|
|
159
|
+
getCssImportPaths,
|
|
197
160
|
optionsWithDefaults,
|
|
198
161
|
compiledEntryPath,
|
|
199
162
|
refElementContext,
|
|
200
163
|
cleanup,
|
|
201
|
-
errors,
|
|
202
164
|
),
|
|
203
165
|
normalizeComponentExtractionCleanupFailure,
|
|
204
166
|
)
|
|
@@ -224,22 +186,18 @@ function normalizeComponentExtractionCleanupFailure(thrown: unknown): InstanceTy
|
|
|
224
186
|
async function processComponentWithCleanup(
|
|
225
187
|
componentInfo: ComponentInfo,
|
|
226
188
|
loadComponent: (name: string) => ComponentType<unknown> | null,
|
|
227
|
-
|
|
228
|
-
didModuleLoadFail: boolean,
|
|
229
|
-
report: (error: ExtractionError) => void,
|
|
189
|
+
getCssImportPaths: () => string[],
|
|
230
190
|
options: ExtractComponentManifestOptions,
|
|
231
191
|
compiledEntryPath: string,
|
|
232
192
|
refElementContext: RefElementContext,
|
|
233
193
|
cleanup: () => Promise<void>,
|
|
234
|
-
errors: ExtractionError[],
|
|
235
194
|
): Promise<ManifestResult> {
|
|
236
195
|
try {
|
|
196
|
+
const cssImportPaths = getCssImportPaths()
|
|
237
197
|
const processResult = processComponent(
|
|
238
198
|
componentInfo,
|
|
239
199
|
loadComponent,
|
|
240
200
|
cssImportPaths,
|
|
241
|
-
didModuleLoadFail,
|
|
242
|
-
report,
|
|
243
201
|
options,
|
|
244
202
|
compiledEntryPath,
|
|
245
203
|
refElementContext.eligiblePaths,
|
|
@@ -248,10 +206,7 @@ async function processComponentWithCleanup(
|
|
|
248
206
|
throw processResult.error
|
|
249
207
|
}
|
|
250
208
|
|
|
251
|
-
return {
|
|
252
|
-
component: toEditorReactComponent(processResult.component),
|
|
253
|
-
errors,
|
|
254
|
-
}
|
|
209
|
+
return { component: toEditorReactComponent(processResult.component) }
|
|
255
210
|
} finally {
|
|
256
211
|
await cleanup()
|
|
257
212
|
}
|
|
@@ -292,7 +247,6 @@ export async function extractComponentManifest(
|
|
|
292
247
|
|
|
293
248
|
// ── Tier 1: High-Level API ──────────────────────────────────────────────────
|
|
294
249
|
// extractComponentManifest() is exported above as a named function declaration.
|
|
295
|
-
export type { ExtractionError } from './extraction-types'
|
|
296
250
|
export type { EditorReactComponent } from '@wix/react-component-schema'
|
|
297
251
|
export type { ComponentInfoWithCss, ExtractedCssInfo, ProcessComponentResult } from './manifest-pipeline'
|
|
298
252
|
|
|
@@ -125,56 +125,58 @@ function parseAllProperties(
|
|
|
125
125
|
const propertiesMap = new Map<string, CSSProperty[]>()
|
|
126
126
|
const varUsagesByProperty = new Map<string, Set<string>>()
|
|
127
127
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
128
|
+
const ast = parse(cssString, {
|
|
129
|
+
parseCustomProperty: true,
|
|
130
|
+
parseAtrulePrelude: false,
|
|
131
|
+
onParseError: (error) => {
|
|
132
|
+
throw error
|
|
133
|
+
},
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
const valueAliases = parseValueAliases(ast)
|
|
137
|
+
|
|
138
|
+
walk(ast, {
|
|
139
|
+
visit: 'Rule',
|
|
140
|
+
enter(this: WalkContext, rule: Rule) {
|
|
141
|
+
try {
|
|
142
|
+
const properties: CSSProperty[] = []
|
|
143
|
+
for (const child of rule.block.children) {
|
|
144
|
+
if (child.type !== 'Declaration') continue
|
|
145
|
+
const extracted = extractProperty(child, varUsagesByProperty, valueAliases)
|
|
146
|
+
if (extracted) {
|
|
147
|
+
properties.push(extracted)
|
|
144
148
|
}
|
|
149
|
+
}
|
|
145
150
|
|
|
146
|
-
|
|
151
|
+
if (properties.length === 0) return
|
|
147
152
|
|
|
148
|
-
|
|
153
|
+
const selectorStrings: string[] = []
|
|
149
154
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
155
|
+
if (isInsideKeyframes(this)) {
|
|
156
|
+
const keyframeName = this.atrule?.prelude ? generate(this.atrule.prelude).trim() : ''
|
|
157
|
+
if (!keyframeName) return
|
|
153
158
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
}
|
|
159
|
+
const keyframeSelector = generate(rule.prelude).trim()
|
|
160
|
+
selectorStrings.push(`@keyframes ${keyframeName} ${keyframeSelector}`)
|
|
161
|
+
} else {
|
|
162
|
+
if (rule.prelude.type !== 'SelectorList') return
|
|
163
|
+
for (const selectorNode of rule.prelude.children) {
|
|
164
|
+
if (selectorNode.type !== 'Selector') continue
|
|
165
|
+
const selectorString = generate(selectorNode)
|
|
166
|
+
parsedSelectors.set(selectorString, selectorNode)
|
|
167
|
+
selectorStrings.push(selectorString)
|
|
164
168
|
}
|
|
169
|
+
}
|
|
165
170
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
}
|
|
170
|
-
} catch {
|
|
171
|
-
// Skip rules that can't be processed
|
|
171
|
+
for (const selectorString of selectorStrings) {
|
|
172
|
+
const existing = propertiesMap.get(selectorString) ?? []
|
|
173
|
+
propertiesMap.set(selectorString, [...existing, ...properties])
|
|
172
174
|
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
}
|
|
175
|
+
} catch {
|
|
176
|
+
// Skip rules that can't be processed
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
})
|
|
178
180
|
|
|
179
181
|
return { propertiesMap, varUsagesByProperty }
|
|
180
182
|
}
|
|
@@ -506,33 +508,34 @@ function extractAtruleDescriptors(block: CssNode): Map<string, string> {
|
|
|
506
508
|
function parseRegisteredProperties(cssString: string): Map<string, RegisteredCustomProperty> {
|
|
507
509
|
const registered = new Map<string, RegisteredCustomProperty>()
|
|
508
510
|
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
511
|
+
const ast = parse(cssString, {
|
|
512
|
+
parseAtrulePrelude: false,
|
|
513
|
+
onParseError: (error) => {
|
|
514
|
+
throw error
|
|
515
|
+
},
|
|
516
|
+
})
|
|
517
|
+
|
|
518
|
+
walk(ast, {
|
|
519
|
+
visit: 'Atrule',
|
|
520
|
+
enter(atrule: Atrule) {
|
|
521
|
+
if (atrule.name !== 'property' || !atrule.prelude || !atrule.block) return
|
|
522
|
+
|
|
523
|
+
const varName = generate(atrule.prelude).trim()
|
|
524
|
+
if (!varName.startsWith('--')) return
|
|
525
|
+
|
|
526
|
+
const descriptors = extractAtruleDescriptors(atrule.block)
|
|
527
|
+
const resolved = cssPropertyTypeFromSyntax(descriptors.get('syntax') ?? '*')
|
|
528
|
+
if (!resolved) return
|
|
529
|
+
const { cssPropertyType, enumOptions } = resolved
|
|
530
|
+
const initialValue = descriptors.get('initial-value')
|
|
531
|
+
|
|
532
|
+
registered.set(varName, {
|
|
533
|
+
cssPropertyType,
|
|
534
|
+
...(enumOptions && { enumOptions }),
|
|
535
|
+
...(initialValue !== undefined && { defaultValue: initialValue }),
|
|
536
|
+
})
|
|
537
|
+
},
|
|
538
|
+
})
|
|
536
539
|
|
|
537
540
|
return registered
|
|
538
541
|
}
|
|
@@ -662,47 +665,48 @@ function analyzeStateSelector(selector: Selector): AnalyzedCompound[] {
|
|
|
662
665
|
function parseStateClasses(cssString: string): StateClassRule[] {
|
|
663
666
|
const results: StateClassRule[] = []
|
|
664
667
|
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
668
|
+
const ast = parse(cssString, {
|
|
669
|
+
parseAtrulePrelude: false,
|
|
670
|
+
onParseError: (error) => {
|
|
671
|
+
throw error
|
|
672
|
+
},
|
|
673
|
+
})
|
|
674
|
+
|
|
675
|
+
walk(ast, {
|
|
676
|
+
visit: 'Rule',
|
|
677
|
+
enter(rule: Rule) {
|
|
678
|
+
if (rule.prelude.type !== 'SelectorList') return
|
|
679
|
+
|
|
680
|
+
const pseudoByBase = new Map<string, NativePseudoClass>()
|
|
681
|
+
const modifiersByBase = new Map<string, Set<string>>()
|
|
682
|
+
|
|
683
|
+
for (const selectorNode of rule.prelude.children) {
|
|
684
|
+
if (selectorNode.type !== 'Selector') continue
|
|
685
|
+
for (const analyzed of analyzeStateSelector(selectorNode)) {
|
|
686
|
+
if (analyzed.pseudoClass && !pseudoByBase.has(analyzed.base)) {
|
|
687
|
+
pseudoByBase.set(analyzed.base, analyzed.pseudoClass)
|
|
688
|
+
}
|
|
689
|
+
if (analyzed.modifiers.length > 0) {
|
|
690
|
+
const modifiers = modifiersByBase.get(analyzed.base) ?? new Set<string>()
|
|
691
|
+
for (const modifier of analyzed.modifiers) modifiers.add(modifier)
|
|
692
|
+
modifiersByBase.set(analyzed.base, modifiers)
|
|
687
693
|
}
|
|
688
694
|
}
|
|
695
|
+
}
|
|
689
696
|
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
}
|
|
697
|
+
for (const [baseSelector, modifiers] of modifiersByBase) {
|
|
698
|
+
const basePseudo = pseudoByBase.get(baseSelector)
|
|
699
|
+
for (const modifier of modifiers) {
|
|
700
|
+
// A modifier is the editor-trigger class for a NATIVE state only when its
|
|
701
|
+
// own state name matches the base's pseudo (`x--hover` ↔ `:hover`). A custom
|
|
702
|
+
// modifier sharing a base with a native pseudo (`x--selected` next to `:hover`)
|
|
703
|
+
// stays custom — it must not inherit the unrelated pseudo.
|
|
704
|
+
const pseudoClass = basePseudo && stateNameFromModifier(modifier) === basePseudo ? basePseudo : undefined
|
|
705
|
+
results.push({ baseSelector, modifier, pseudoClass })
|
|
700
706
|
}
|
|
701
|
-
}
|
|
702
|
-
}
|
|
703
|
-
}
|
|
704
|
-
console.error('CSS state-class parsing error:', error)
|
|
705
|
-
}
|
|
707
|
+
}
|
|
708
|
+
},
|
|
709
|
+
})
|
|
706
710
|
|
|
707
711
|
return results
|
|
708
712
|
}
|
|
@@ -252,6 +252,8 @@ function convertComponentDoc(doc: ComponentDoc, program: ts.Program, checker: ts
|
|
|
252
252
|
resolvedType,
|
|
253
253
|
description: propItem.description || undefined,
|
|
254
254
|
deprecated: tags?.deprecated !== undefined ? true : undefined,
|
|
255
|
+
min: parseNumericTag(tags?.min, propName, 'min'),
|
|
256
|
+
max: parseNumericTag(tags?.max, propName, 'max'),
|
|
255
257
|
isStateTrigger: stateTrigger ? true : undefined,
|
|
256
258
|
}
|
|
257
259
|
}
|
|
@@ -335,6 +337,14 @@ function findPropsType(
|
|
|
335
337
|
// Default value conversion
|
|
336
338
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
337
339
|
|
|
340
|
+
function parseNumericTag(value: string | undefined, propName: string, tag: string): string | undefined {
|
|
341
|
+
if (value !== undefined && Number.isNaN(Number(value))) {
|
|
342
|
+
console.warn(`@${tag} on prop "${propName}" is not a valid number ("${value}") — ignoring`)
|
|
343
|
+
return undefined
|
|
344
|
+
}
|
|
345
|
+
return value
|
|
346
|
+
}
|
|
347
|
+
|
|
338
348
|
function convertDefaultValue(rdtDefault: { value: unknown } | null): DefaultValue | undefined {
|
|
339
349
|
if (!rdtDefault || rdtDefault.value == null) return undefined
|
|
340
350
|
|
|
@@ -51,6 +51,9 @@ export interface PropInfo {
|
|
|
51
51
|
description?: string
|
|
52
52
|
// Whether the prop is marked as deprecated via @deprecated JSDoc tag
|
|
53
53
|
deprecated?: boolean
|
|
54
|
+
// Numeric range constraints extracted from @min / @max JSDoc tags (decimal strings)
|
|
55
|
+
min?: string
|
|
56
|
+
max?: string
|
|
54
57
|
// True when the prop is wrapped in the `ElementState<>` marker type from
|
|
55
58
|
// @wix/react-component-utils, declaring it as the trigger for a custom design
|
|
56
59
|
// state. `type` and `resolvedType` reflect the unwrapped inner type, so the prop
|