@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.
- package/README.md +72 -0
- package/dist/component-loader.d.ts +42 -0
- package/dist/component-renderer.d.ts +31 -0
- package/dist/converters/data-item-builder.d.ts +15 -0
- package/dist/converters/index.d.ts +1 -0
- package/dist/converters/to-editor-component.d.ts +3 -0
- package/dist/converters/utils.d.ts +16 -0
- package/dist/errors.d.ts +230 -0
- package/dist/index.d.ts +42 -0
- package/dist/index.js +51978 -0
- package/dist/information-extractors/css/index.d.ts +3 -0
- package/dist/information-extractors/css/parse.d.ts +7 -0
- package/dist/information-extractors/css/selector-matcher.d.ts +3 -0
- package/dist/information-extractors/css/types.d.ts +49 -0
- package/dist/information-extractors/react/extractors/core/index.d.ts +6 -0
- package/dist/information-extractors/react/extractors/core/runner.d.ts +19 -0
- package/dist/information-extractors/react/extractors/core/store.d.ts +17 -0
- package/dist/information-extractors/react/extractors/core/tree-builder.d.ts +15 -0
- package/dist/information-extractors/react/extractors/core/types.d.ts +40 -0
- package/dist/information-extractors/react/extractors/css-properties.d.ts +20 -0
- package/dist/information-extractors/react/extractors/index.d.ts +11 -0
- package/dist/information-extractors/react/extractors/prop-tracker.d.ts +24 -0
- package/dist/information-extractors/react/index.d.ts +9 -0
- package/dist/information-extractors/react/types.d.ts +51 -0
- package/dist/information-extractors/react/utils/mock-generator.d.ts +9 -0
- package/dist/information-extractors/react/utils/prop-spy.d.ts +10 -0
- package/dist/information-extractors/ts/components.d.ts +9 -0
- package/dist/information-extractors/ts/css-imports.d.ts +2 -0
- package/dist/information-extractors/ts/index.d.ts +3 -0
- package/dist/information-extractors/ts/types.d.ts +47 -0
- package/dist/information-extractors/ts/utils/semantic-type-resolver.d.ts +3 -0
- package/dist/jsx-runtime-interceptor.d.ts +42 -0
- package/dist/jsx-runtime-interceptor.js +63 -0
- package/dist/jsx-runtime-loader.d.ts +23 -0
- package/dist/jsx-runtime-loader.js +7 -0
- package/dist/manifest-pipeline.d.ts +33 -0
- package/dist/schema.d.ts +167 -0
- package/dist/ts-compiler.d.ts +13 -0
- package/package.json +81 -0
- package/src/component-loader.test.ts +277 -0
- package/src/component-loader.ts +256 -0
- package/src/component-renderer.ts +192 -0
- package/src/converters/data-item-builder.ts +354 -0
- package/src/converters/index.ts +1 -0
- package/src/converters/to-editor-component.ts +167 -0
- package/src/converters/utils.ts +21 -0
- package/src/errors.ts +103 -0
- package/src/index.ts +223 -0
- package/src/information-extractors/css/README.md +3 -0
- package/src/information-extractors/css/index.ts +3 -0
- package/src/information-extractors/css/parse.ts +450 -0
- package/src/information-extractors/css/selector-matcher.ts +88 -0
- package/src/information-extractors/css/types.ts +56 -0
- package/src/information-extractors/react/extractors/core/index.ts +6 -0
- package/src/information-extractors/react/extractors/core/runner.ts +89 -0
- package/src/information-extractors/react/extractors/core/store.ts +36 -0
- package/src/information-extractors/react/extractors/core/tree-builder.ts +273 -0
- package/src/information-extractors/react/extractors/core/types.ts +48 -0
- package/src/information-extractors/react/extractors/css-properties.ts +214 -0
- package/src/information-extractors/react/extractors/index.ts +27 -0
- package/src/information-extractors/react/extractors/prop-tracker.ts +132 -0
- package/src/information-extractors/react/index.ts +53 -0
- package/src/information-extractors/react/types.ts +70 -0
- package/src/information-extractors/react/utils/mock-generator.ts +331 -0
- package/src/information-extractors/react/utils/prop-spy.ts +168 -0
- package/src/information-extractors/ts/components.ts +300 -0
- package/src/information-extractors/ts/css-imports.ts +26 -0
- package/src/information-extractors/ts/index.ts +3 -0
- package/src/information-extractors/ts/types.ts +56 -0
- package/src/information-extractors/ts/utils/semantic-type-resolver.ts +377 -0
- package/src/jsx-runtime-interceptor.ts +146 -0
- package/src/jsx-runtime-loader.ts +38 -0
- package/src/manifest-pipeline.ts +362 -0
- package/src/schema.ts +174 -0
- package/src/ts-compiler.ts +41 -0
- package/tsconfig.json +17 -0
- package/typedoc.json +18 -0
- package/vite.config.ts +45 -0
package/dist/schema.d.ts
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
export declare const DATA: {
|
|
2
|
+
DATA_TYPE: {
|
|
3
|
+
readonly UNKNOWN_DataType: "UNKNOWN_DataType";
|
|
4
|
+
readonly text: "text";
|
|
5
|
+
readonly textEnum: "textEnum";
|
|
6
|
+
readonly number: "number";
|
|
7
|
+
readonly booleanValue: "booleanValue";
|
|
8
|
+
readonly a11y: "a11y";
|
|
9
|
+
readonly link: "link";
|
|
10
|
+
readonly image: "image";
|
|
11
|
+
readonly video: "video";
|
|
12
|
+
readonly vectorArt: "vectorArt";
|
|
13
|
+
readonly audio: "audio";
|
|
14
|
+
readonly schema: "schema";
|
|
15
|
+
readonly localDate: "localDate";
|
|
16
|
+
readonly localTime: "localTime";
|
|
17
|
+
readonly localDateTime: "localDateTime";
|
|
18
|
+
readonly webUrl: "webUrl";
|
|
19
|
+
readonly email: "email";
|
|
20
|
+
readonly phone: "phone";
|
|
21
|
+
readonly hostname: "hostname";
|
|
22
|
+
readonly regex: "regex";
|
|
23
|
+
readonly guid: "guid";
|
|
24
|
+
readonly richText: "richText";
|
|
25
|
+
readonly container: "container";
|
|
26
|
+
readonly arrayItems: "arrayItems";
|
|
27
|
+
readonly direction: "direction";
|
|
28
|
+
readonly menuItems: "menuItems";
|
|
29
|
+
readonly data: "data";
|
|
30
|
+
readonly function: "function";
|
|
31
|
+
readonly onClick: "onClick";
|
|
32
|
+
readonly onChange: "onChange";
|
|
33
|
+
readonly onKeyPress: "onKeyPress";
|
|
34
|
+
readonly onKeyUp: "onKeyUp";
|
|
35
|
+
readonly onSubmit: "onSubmit";
|
|
36
|
+
};
|
|
37
|
+
A11Y_ATTRIBUTES: {
|
|
38
|
+
readonly Unknown_AriaAttributes: "Unknown_AriaAttributes";
|
|
39
|
+
readonly tabIndex: "tabIndex";
|
|
40
|
+
readonly ariaLevel: "ariaLevel";
|
|
41
|
+
readonly ariaExpanded: "ariaExpanded";
|
|
42
|
+
readonly ariaDisabled: "ariaDisabled";
|
|
43
|
+
readonly ariaAtomic: "ariaAtomic";
|
|
44
|
+
readonly ariaHidden: "ariaHidden";
|
|
45
|
+
readonly ariaBusy: "ariaBusy";
|
|
46
|
+
readonly multiline: "multiline";
|
|
47
|
+
readonly ariaAutocomplete: "ariaAutocomplete";
|
|
48
|
+
readonly ariaPressed: "ariaPressed";
|
|
49
|
+
readonly ariaHaspopup: "ariaHaspopup";
|
|
50
|
+
readonly ariaRelevant: "ariaRelevant";
|
|
51
|
+
readonly role: "role";
|
|
52
|
+
readonly ariaLive: "ariaLive";
|
|
53
|
+
readonly ariaCurrent: "ariaCurrent";
|
|
54
|
+
readonly ariaLabel: "ariaLabel";
|
|
55
|
+
readonly ariaRoledescription: "ariaRoledescription";
|
|
56
|
+
readonly ariaDescribedby: "ariaDescribedby";
|
|
57
|
+
readonly ariaLabelledby: "ariaLabelledby";
|
|
58
|
+
readonly ariaErrormessage: "ariaErrormessage";
|
|
59
|
+
readonly ariaOwns: "ariaOwns";
|
|
60
|
+
readonly ariaControls: "ariaControls";
|
|
61
|
+
readonly tag: "tag";
|
|
62
|
+
readonly ariaMultiline: "ariaMultiline";
|
|
63
|
+
readonly ariaInvalid: "ariaInvalid";
|
|
64
|
+
};
|
|
65
|
+
LINK_TYPE: {
|
|
66
|
+
readonly UNKNOWN_LinkType: "UNKNOWN_LinkType";
|
|
67
|
+
readonly externalLink: "externalLink";
|
|
68
|
+
readonly anchorLink: "anchorLink";
|
|
69
|
+
readonly emailLink: "emailLink";
|
|
70
|
+
readonly phoneLink: "phoneLink";
|
|
71
|
+
readonly dynamicPageLink: "dynamicPageLink";
|
|
72
|
+
readonly pageLink: "pageLink";
|
|
73
|
+
readonly whatsAppLink: "whatsAppLink";
|
|
74
|
+
readonly documentLink: "documentLink";
|
|
75
|
+
readonly popupLink: "popupLink";
|
|
76
|
+
readonly addressLink: "addressLink";
|
|
77
|
+
readonly edgeAnchorLinks: "edgeAnchorLinks";
|
|
78
|
+
readonly loginToWixLink: "loginToWixLink";
|
|
79
|
+
};
|
|
80
|
+
};
|
|
81
|
+
export declare const MEDIA: {
|
|
82
|
+
VIDEO_CATEGORY: {
|
|
83
|
+
readonly UNKNOWN_VideoCategoryTypes: "UNKNOWN_VideoCategoryTypes";
|
|
84
|
+
readonly VIDEO: "VIDEO";
|
|
85
|
+
readonly VIDEO_TRANSPARENT: "VIDEO_TRANSPARENT";
|
|
86
|
+
readonly VIDEO_OPAQUE: "VIDEO_OPAQUE";
|
|
87
|
+
};
|
|
88
|
+
VECTOR_ART_CATEGORY: {
|
|
89
|
+
readonly UNKNOWN_VectorArtCategoryTypes: "UNKNOWN_VectorArtCategoryTypes";
|
|
90
|
+
readonly SHAPE_ALL: "SHAPE_ALL";
|
|
91
|
+
readonly SHAPE_BASIC: "SHAPE_BASIC";
|
|
92
|
+
readonly SHAPE_ART: "SHAPE_ART";
|
|
93
|
+
readonly ICON_SOCIAL: "ICON_SOCIAL";
|
|
94
|
+
readonly SHAPE_DIVIDERS: "SHAPE_DIVIDERS";
|
|
95
|
+
readonly SHAPE_LOCATION: "SHAPE_LOCATION";
|
|
96
|
+
readonly SHAPE_DOCUMENTS: "SHAPE_DOCUMENTS";
|
|
97
|
+
readonly SHAPE_SOCIAL: "SHAPE_SOCIAL";
|
|
98
|
+
readonly SHAPE_ARROWS: "SHAPE_ARROWS";
|
|
99
|
+
};
|
|
100
|
+
IMAGE_CATEGORY: {
|
|
101
|
+
readonly UNKNOWN_CategoryName: "UNKNOWN_CategoryName";
|
|
102
|
+
readonly IMAGE: "IMAGE";
|
|
103
|
+
readonly IMAGE_BACKGROUND: "IMAGE_BACKGROUND";
|
|
104
|
+
};
|
|
105
|
+
};
|
|
106
|
+
export declare const ELEMENTS: {
|
|
107
|
+
ELEMENT_TYPE: {
|
|
108
|
+
readonly UNKNOWN_ElementType: "UNKNOWN_ElementType";
|
|
109
|
+
readonly inlineElement: "inlineElement";
|
|
110
|
+
readonly refElement: "refElement";
|
|
111
|
+
};
|
|
112
|
+
};
|
|
113
|
+
export interface DataItem {
|
|
114
|
+
displayName?: string;
|
|
115
|
+
defaultValue?: unknown;
|
|
116
|
+
dataType?: string;
|
|
117
|
+
text?: Record<string, unknown>;
|
|
118
|
+
number?: Record<string, unknown>;
|
|
119
|
+
booleanValue?: unknown;
|
|
120
|
+
textEnum?: {
|
|
121
|
+
options: Array<{
|
|
122
|
+
value: string;
|
|
123
|
+
displayName?: string;
|
|
124
|
+
}>;
|
|
125
|
+
};
|
|
126
|
+
arrayItems?: {
|
|
127
|
+
dataItem?: DataItem;
|
|
128
|
+
};
|
|
129
|
+
data?: {
|
|
130
|
+
items?: Record<string, DataItem>;
|
|
131
|
+
};
|
|
132
|
+
function?: Record<string, unknown>;
|
|
133
|
+
link?: {
|
|
134
|
+
linkTypes?: string[];
|
|
135
|
+
};
|
|
136
|
+
image?: {
|
|
137
|
+
category?: string;
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
export interface CssPropertyItem {
|
|
141
|
+
defaultValue?: string;
|
|
142
|
+
}
|
|
143
|
+
export interface CssCustomPropertyItem {
|
|
144
|
+
defaultValue?: string;
|
|
145
|
+
}
|
|
146
|
+
export interface EditorElement {
|
|
147
|
+
selector?: string;
|
|
148
|
+
displayName?: string;
|
|
149
|
+
data?: Record<string, DataItem>;
|
|
150
|
+
elements?: Record<string, ElementItem>;
|
|
151
|
+
cssProperties?: Record<string, CssPropertyItem>;
|
|
152
|
+
cssCustomProperties?: Record<string, CssCustomPropertyItem>;
|
|
153
|
+
}
|
|
154
|
+
export interface ElementItem {
|
|
155
|
+
elementType?: string;
|
|
156
|
+
inlineElement?: {
|
|
157
|
+
selector?: string;
|
|
158
|
+
displayName?: string;
|
|
159
|
+
data?: Record<string, DataItem>;
|
|
160
|
+
cssProperties?: Record<string, CssPropertyItem>;
|
|
161
|
+
cssCustomProperties?: Record<string, CssCustomPropertyItem>;
|
|
162
|
+
elements?: Record<string, ElementItem>;
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
export interface EditorReactComponent {
|
|
166
|
+
editorElement?: EditorElement;
|
|
167
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ResultAsync } from 'neverthrow';
|
|
2
|
+
import { default as ts } from 'typescript';
|
|
3
|
+
import { NotFoundError, ParseError } from './errors';
|
|
4
|
+
/**
|
|
5
|
+
* Compile a TypeScript file into a ts.Program.
|
|
6
|
+
*
|
|
7
|
+
* @param filePath - Path to the TypeScript source file
|
|
8
|
+
* @returns The compiled TypeScript program on success
|
|
9
|
+
* @errors
|
|
10
|
+
* - {@link NotFoundError} — Source file does not exist (phase: `compile`)
|
|
11
|
+
* - {@link ParseError} — TypeScript config could not be parsed (phase: `compile`)
|
|
12
|
+
*/
|
|
13
|
+
export declare function compileTsFile(filePath: string): ResultAsync<ts.Program, InstanceType<typeof NotFoundError> | InstanceType<typeof ParseError>>;
|
package/package.json
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@wix/zero-config-implementation",
|
|
3
|
+
"publishConfig": {
|
|
4
|
+
"registry": "https://registry.npmjs.org/",
|
|
5
|
+
"access": "public"
|
|
6
|
+
},
|
|
7
|
+
"version": "1.5.0",
|
|
8
|
+
"description": "Core library for extracting component manifests from JS and CSS files",
|
|
9
|
+
"type": "module",
|
|
10
|
+
"main": "dist/index.js",
|
|
11
|
+
"types": "dist/index.d.ts",
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"import": "./dist/index.js",
|
|
15
|
+
"types": "./dist/index.d.ts"
|
|
16
|
+
},
|
|
17
|
+
"./jsx-runtime-loader": {
|
|
18
|
+
"import": "./dist/jsx-runtime-loader.js"
|
|
19
|
+
},
|
|
20
|
+
"./jsx-runtime-interceptor": {
|
|
21
|
+
"import": "./dist/jsx-runtime-interceptor.js"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "vite build",
|
|
26
|
+
"lint": "biome check src/",
|
|
27
|
+
"test": "vitest run --passWithNoTests",
|
|
28
|
+
"test:watch": "vitest --passWithNoTests",
|
|
29
|
+
"docs": "typedoc"
|
|
30
|
+
},
|
|
31
|
+
"keywords": [],
|
|
32
|
+
"author": "",
|
|
33
|
+
"license": "ISC",
|
|
34
|
+
"peerDependencies": {
|
|
35
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
36
|
+
"react-dom": "^18.0.0 || ^19.0.0",
|
|
37
|
+
"typescript": "^5.0.0"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"lightningcss": "^1.31.1"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@faker-js/faker": "^10.2.0",
|
|
44
|
+
"@types/node": "^20.0.0",
|
|
45
|
+
"@types/react": "^18.3.1",
|
|
46
|
+
"@types/react-dom": "^18.3.1",
|
|
47
|
+
"case-anything": "^3.1.2",
|
|
48
|
+
"cheerio": "^1.2.0",
|
|
49
|
+
"modern-errors": "^7.1.4",
|
|
50
|
+
"modern-errors-cli": "^6.0.0",
|
|
51
|
+
"neverthrow": "^8.2.0",
|
|
52
|
+
"parse5": "^7.1.2",
|
|
53
|
+
"pkg-entry-points": "^1.1.1",
|
|
54
|
+
"react-docgen-typescript": "^2.4.0",
|
|
55
|
+
"read-package-up": "^11.0.0",
|
|
56
|
+
"tsconfck": "^3.1.6",
|
|
57
|
+
"typedoc": "^0.28.16",
|
|
58
|
+
"typedoc-plugin-markdown": "^4.9.0",
|
|
59
|
+
"vite": "^7.3.1",
|
|
60
|
+
"vite-node": "^5.3.0",
|
|
61
|
+
"vite-plugin-dts": "^4.5.4",
|
|
62
|
+
"vitest": "^4.0.17"
|
|
63
|
+
},
|
|
64
|
+
"wix": {
|
|
65
|
+
"artifact": {
|
|
66
|
+
"groupId": "com.wixpress.npm",
|
|
67
|
+
"artifactId": "zero-config-implementation",
|
|
68
|
+
"targets": {
|
|
69
|
+
"static": false,
|
|
70
|
+
"docker": false
|
|
71
|
+
}
|
|
72
|
+
},
|
|
73
|
+
"validations": {
|
|
74
|
+
"postBuild": [
|
|
75
|
+
"lint",
|
|
76
|
+
"test"
|
|
77
|
+
]
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
"falconPackageHash": "79168aff8f115865bd48bf546e449528bbdac723a1e22c1085e10a6b"
|
|
81
|
+
}
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import { join } from 'node:path'
|
|
2
|
+
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
3
|
+
|
|
4
|
+
const mockModuleRequire = vi.fn()
|
|
5
|
+
|
|
6
|
+
vi.mock('node:module', () => ({
|
|
7
|
+
createRequire: () => mockModuleRequire,
|
|
8
|
+
}))
|
|
9
|
+
|
|
10
|
+
vi.mock('read-package-up', () => ({
|
|
11
|
+
readPackageUpSync: vi.fn(),
|
|
12
|
+
}))
|
|
13
|
+
|
|
14
|
+
vi.mock('pkg-entry-points', () => ({
|
|
15
|
+
getPackageEntryPoints: vi.fn(),
|
|
16
|
+
}))
|
|
17
|
+
|
|
18
|
+
import { getPackageEntryPoints } from 'pkg-entry-points'
|
|
19
|
+
import type { NormalizedPackageJson } from 'read-package-up'
|
|
20
|
+
import { readPackageUpSync } from 'read-package-up'
|
|
21
|
+
import { createComponentLoader } from './component-loader'
|
|
22
|
+
|
|
23
|
+
const mockReadPackageUp = vi.mocked(readPackageUpSync)
|
|
24
|
+
const mockGetEntryPoints = vi.mocked(getPackageEntryPoints)
|
|
25
|
+
|
|
26
|
+
const fakePkgDir = '/fake/project'
|
|
27
|
+
const fakePkgJsonPath = join(fakePkgDir, 'package.json')
|
|
28
|
+
|
|
29
|
+
function mockPackage(name: string) {
|
|
30
|
+
mockReadPackageUp.mockReturnValue({
|
|
31
|
+
packageJson: { name } as NormalizedPackageJson,
|
|
32
|
+
path: fakePkgJsonPath,
|
|
33
|
+
})
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
beforeEach(() => {
|
|
37
|
+
vi.resetAllMocks()
|
|
38
|
+
mockModuleRequire.mockImplementation(() => {
|
|
39
|
+
throw new Error('Cannot find module')
|
|
40
|
+
})
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
describe('createComponentLoader', () => {
|
|
44
|
+
it('returns error when no package.json is found', async () => {
|
|
45
|
+
mockReadPackageUp.mockReturnValue(undefined)
|
|
46
|
+
|
|
47
|
+
const result = await createComponentLoader('/some/Component.tsx')
|
|
48
|
+
|
|
49
|
+
expect(result.isErr()).toBe(true)
|
|
50
|
+
if (result.isErr()) {
|
|
51
|
+
expect(result.error.message).toBe('No package.json found')
|
|
52
|
+
}
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
it('returns error when package has no resolvable entry points', async () => {
|
|
56
|
+
mockPackage('my-pkg')
|
|
57
|
+
mockGetEntryPoints.mockResolvedValue({})
|
|
58
|
+
|
|
59
|
+
const result = await createComponentLoader(join(fakePkgDir, 'src/Component.tsx'))
|
|
60
|
+
|
|
61
|
+
expect(result.isErr()).toBe(true)
|
|
62
|
+
if (result.isErr()) {
|
|
63
|
+
expect(result.error.message).toContain('no resolvable entry point')
|
|
64
|
+
expect(result.error.message).toContain('my-pkg')
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
it('returns error when only types exports exist', async () => {
|
|
69
|
+
mockPackage('types-only')
|
|
70
|
+
mockGetEntryPoints.mockResolvedValue({
|
|
71
|
+
'.': [[['types'], './dist/index.d.ts']],
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
const result = await createComponentLoader(join(fakePkgDir, 'src/Component.tsx'))
|
|
75
|
+
|
|
76
|
+
expect(result.isErr()).toBe(true)
|
|
77
|
+
if (result.isErr()) {
|
|
78
|
+
expect(result.error.message).toContain('no loadable exports')
|
|
79
|
+
}
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
it('returns error with path when single entry point fails to load', async () => {
|
|
83
|
+
mockPackage('bad-entry')
|
|
84
|
+
mockGetEntryPoints.mockResolvedValue({
|
|
85
|
+
'.': [[['import'], './dist/index.js']],
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
const result = await createComponentLoader(join(fakePkgDir, 'src/Component.tsx'))
|
|
89
|
+
|
|
90
|
+
expect(result.isErr()).toBe(true)
|
|
91
|
+
if (result.isErr()) {
|
|
92
|
+
expect(result.error.message).toContain('Failed to load')
|
|
93
|
+
expect(result.error.message).toContain(join(fakePkgDir, 'dist/index.js'))
|
|
94
|
+
}
|
|
95
|
+
})
|
|
96
|
+
|
|
97
|
+
it('returns error when multiple subpath exports all fail to load', async () => {
|
|
98
|
+
mockPackage('multi-fail')
|
|
99
|
+
mockGetEntryPoints.mockResolvedValue({
|
|
100
|
+
'./foo': [[['import'], './dist/foo.js']],
|
|
101
|
+
'./bar': [[['import'], './dist/bar.js']],
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
const result = await createComponentLoader(join(fakePkgDir, 'src/Component.tsx'))
|
|
105
|
+
|
|
106
|
+
expect(result.isErr()).toBe(true)
|
|
107
|
+
if (result.isErr()) {
|
|
108
|
+
expect(result.error.message).toContain('Failed to load')
|
|
109
|
+
}
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
it('skips types-only entries in multi-subpath exports', async () => {
|
|
113
|
+
mockPackage('mixed-types')
|
|
114
|
+
mockGetEntryPoints.mockResolvedValue({
|
|
115
|
+
'./types': [[['types'], './dist/types.d.ts']],
|
|
116
|
+
'./runtime': [[['import'], './dist/does-not-exist.js']],
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
const result = await createComponentLoader(join(fakePkgDir, 'src/Component.tsx'))
|
|
120
|
+
|
|
121
|
+
// ./types should be skipped (selectBestPath returns undefined), ./runtime fails to load
|
|
122
|
+
expect(result.isErr()).toBe(true)
|
|
123
|
+
if (result.isErr()) {
|
|
124
|
+
expect(result.error.message).toContain('Failed to load')
|
|
125
|
+
}
|
|
126
|
+
})
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
describe('selectBestPath (via createComponentLoader)', () => {
|
|
130
|
+
it('prefers import condition over node and default', async () => {
|
|
131
|
+
mockPackage('prio-test')
|
|
132
|
+
mockGetEntryPoints.mockResolvedValue({
|
|
133
|
+
'.': [
|
|
134
|
+
[['default'], './dist/default.js'],
|
|
135
|
+
[['node'], './dist/node.js'],
|
|
136
|
+
[['import'], './dist/import.js'],
|
|
137
|
+
],
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
const result = await createComponentLoader(join(fakePkgDir, 'src/Component.tsx'))
|
|
141
|
+
|
|
142
|
+
// Module load will fail since these are fake paths, but entryPath is in the error
|
|
143
|
+
expect(result.isErr()).toBe(true)
|
|
144
|
+
if (result.isErr()) {
|
|
145
|
+
expect(result.error.message).toContain(join(fakePkgDir, 'dist/import.js'))
|
|
146
|
+
}
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
it('falls back to node condition when import is not available', async () => {
|
|
150
|
+
mockPackage('node-fallback')
|
|
151
|
+
mockGetEntryPoints.mockResolvedValue({
|
|
152
|
+
'.': [
|
|
153
|
+
[['default'], './dist/default.js'],
|
|
154
|
+
[['node'], './dist/node.js'],
|
|
155
|
+
],
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
const result = await createComponentLoader(join(fakePkgDir, 'src/Component.tsx'))
|
|
159
|
+
|
|
160
|
+
expect(result.isErr()).toBe(true)
|
|
161
|
+
if (result.isErr()) {
|
|
162
|
+
expect(result.error.message).toContain(join(fakePkgDir, 'dist/node.js'))
|
|
163
|
+
}
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
it('falls back to default condition when import and node are not available', async () => {
|
|
167
|
+
mockPackage('default-fallback')
|
|
168
|
+
mockGetEntryPoints.mockResolvedValue({
|
|
169
|
+
'.': [[['default'], './dist/default.js']],
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
const result = await createComponentLoader(join(fakePkgDir, 'src/Component.tsx'))
|
|
173
|
+
|
|
174
|
+
expect(result.isErr()).toBe(true)
|
|
175
|
+
if (result.isErr()) {
|
|
176
|
+
expect(result.error.message).toContain(join(fakePkgDir, 'dist/default.js'))
|
|
177
|
+
}
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
it('skips entries that include types condition', async () => {
|
|
181
|
+
mockPackage('skip-types')
|
|
182
|
+
mockGetEntryPoints.mockResolvedValue({
|
|
183
|
+
'.': [
|
|
184
|
+
[['import', 'types'], './dist/index.d.ts'],
|
|
185
|
+
[['import'], './dist/index.js'],
|
|
186
|
+
],
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
const result = await createComponentLoader(join(fakePkgDir, 'src/Component.tsx'))
|
|
190
|
+
|
|
191
|
+
expect(result.isErr()).toBe(true)
|
|
192
|
+
if (result.isErr()) {
|
|
193
|
+
expect(result.error.message).toContain(join(fakePkgDir, 'dist/index.js'))
|
|
194
|
+
}
|
|
195
|
+
})
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
describe('error message quality', () => {
|
|
199
|
+
it('includes package name in error messages', async () => {
|
|
200
|
+
mockPackage('@scope/my-components')
|
|
201
|
+
mockGetEntryPoints.mockResolvedValue({})
|
|
202
|
+
|
|
203
|
+
const result = await createComponentLoader(join(fakePkgDir, 'src/Component.tsx'))
|
|
204
|
+
|
|
205
|
+
expect(result.isErr()).toBe(true)
|
|
206
|
+
if (result.isErr()) {
|
|
207
|
+
expect(result.error.message).toContain('@scope/my-components')
|
|
208
|
+
}
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
it('includes package.json path in no-entry-point error', async () => {
|
|
212
|
+
mockPackage('no-entry')
|
|
213
|
+
mockGetEntryPoints.mockResolvedValue({})
|
|
214
|
+
|
|
215
|
+
const result = await createComponentLoader(join(fakePkgDir, 'src/Component.tsx'))
|
|
216
|
+
|
|
217
|
+
expect(result.isErr()).toBe(true)
|
|
218
|
+
if (result.isErr()) {
|
|
219
|
+
expect(result.error.message).toContain(fakePkgJsonPath)
|
|
220
|
+
}
|
|
221
|
+
})
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
describe('component resolution behavior', () => {
|
|
225
|
+
it('fails today for direct named export when function name is minified', async () => {
|
|
226
|
+
const packageDir = '/fake/minified-export-test'
|
|
227
|
+
const packageJsonPath = join(packageDir, 'package.json')
|
|
228
|
+
|
|
229
|
+
mockReadPackageUp.mockReturnValue({
|
|
230
|
+
packageJson: { name: 'minified-export-test' } as NormalizedPackageJson,
|
|
231
|
+
path: packageJsonPath,
|
|
232
|
+
})
|
|
233
|
+
mockGetEntryPoints.mockResolvedValue({
|
|
234
|
+
'.': [[['import'], './dist/index.js']],
|
|
235
|
+
})
|
|
236
|
+
mockModuleRequire.mockImplementation(() => ({
|
|
237
|
+
CardX: function j() {
|
|
238
|
+
return null
|
|
239
|
+
},
|
|
240
|
+
}))
|
|
241
|
+
|
|
242
|
+
const result = await createComponentLoader(join(packageDir, 'src/CardX.tsx'))
|
|
243
|
+
|
|
244
|
+
expect(result.isOk()).toBe(true)
|
|
245
|
+
if (result.isOk()) {
|
|
246
|
+
const component = result.value.loadComponent('CardX')
|
|
247
|
+
// TDD red phase: this currently returns null because function.name === "j".
|
|
248
|
+
expect(component).not.toBeNull()
|
|
249
|
+
}
|
|
250
|
+
})
|
|
251
|
+
|
|
252
|
+
it('rejects direct function exports with non-PascalCase export keys', async () => {
|
|
253
|
+
const packageDir = '/fake/lowercase-export-test'
|
|
254
|
+
const packageJsonPath = join(packageDir, 'package.json')
|
|
255
|
+
|
|
256
|
+
mockReadPackageUp.mockReturnValue({
|
|
257
|
+
packageJson: { name: 'lowercase-export-test' } as NormalizedPackageJson,
|
|
258
|
+
path: packageJsonPath,
|
|
259
|
+
})
|
|
260
|
+
mockGetEntryPoints.mockResolvedValue({
|
|
261
|
+
'.': [[['import'], './dist/index.js']],
|
|
262
|
+
})
|
|
263
|
+
mockModuleRequire.mockImplementation(() => ({
|
|
264
|
+
helper: function j() {
|
|
265
|
+
return null
|
|
266
|
+
},
|
|
267
|
+
}))
|
|
268
|
+
|
|
269
|
+
const result = await createComponentLoader(join(packageDir, 'src/helper.ts'))
|
|
270
|
+
|
|
271
|
+
expect(result.isOk()).toBe(true)
|
|
272
|
+
if (result.isOk()) {
|
|
273
|
+
const component = result.value.loadComponent('helper')
|
|
274
|
+
expect(component).toBeNull()
|
|
275
|
+
}
|
|
276
|
+
})
|
|
277
|
+
})
|