@justeattakeaway/pie-webc 0.0.0-snapshot-release-20240614124349 → 0.0.0-snapshot-release-20240619133108
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/package.json +194 -14
- package/src/componentService.js +182 -0
- package/test/componentService.spec.js +309 -0
- package/vite.config.js +12 -0
package/package.json
CHANGED
|
@@ -1,13 +1,194 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@justeattakeaway/pie-webc",
|
|
3
3
|
"description": "Component bundle containing all PIE web components",
|
|
4
|
-
"version": "0.0.0-snapshot-release-
|
|
4
|
+
"version": "0.0.0-snapshot-release-20240619133108",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
7
|
-
"
|
|
8
|
-
"react",
|
|
7
|
+
"**/*.js",
|
|
9
8
|
"**/*.d.ts"
|
|
10
9
|
],
|
|
10
|
+
"exports": {
|
|
11
|
+
"./components/assistive-text.js": {
|
|
12
|
+
"import": "./components/assistive-text.js",
|
|
13
|
+
"require": "./components/assistive-text.js",
|
|
14
|
+
"types": "./components/assistive-text.d.ts"
|
|
15
|
+
},
|
|
16
|
+
"./react/assistive-text.js": {
|
|
17
|
+
"import": "./react/assistive-text.js",
|
|
18
|
+
"require": "./react/assistive-text.js",
|
|
19
|
+
"types": "./react/assistive-text.d.ts"
|
|
20
|
+
},
|
|
21
|
+
"./components/button.js": {
|
|
22
|
+
"import": "./components/button.js",
|
|
23
|
+
"require": "./components/button.js",
|
|
24
|
+
"types": "./components/button.d.ts"
|
|
25
|
+
},
|
|
26
|
+
"./react/button.js": {
|
|
27
|
+
"import": "./react/button.js",
|
|
28
|
+
"require": "./react/button.js",
|
|
29
|
+
"types": "./react/button.d.ts"
|
|
30
|
+
},
|
|
31
|
+
"./components/card.js": {
|
|
32
|
+
"import": "./components/card.js",
|
|
33
|
+
"require": "./components/card.js",
|
|
34
|
+
"types": "./components/card.d.ts"
|
|
35
|
+
},
|
|
36
|
+
"./react/card.js": {
|
|
37
|
+
"import": "./react/card.js",
|
|
38
|
+
"require": "./react/card.js",
|
|
39
|
+
"types": "./react/card.d.ts"
|
|
40
|
+
},
|
|
41
|
+
"./components/checkbox.js": {
|
|
42
|
+
"import": "./components/checkbox.js",
|
|
43
|
+
"require": "./components/checkbox.js",
|
|
44
|
+
"types": "./components/checkbox.d.ts"
|
|
45
|
+
},
|
|
46
|
+
"./react/checkbox.js": {
|
|
47
|
+
"import": "./react/checkbox.js",
|
|
48
|
+
"require": "./react/checkbox.js",
|
|
49
|
+
"types": "./react/checkbox.d.ts"
|
|
50
|
+
},
|
|
51
|
+
"./components/chip.js": {
|
|
52
|
+
"import": "./components/chip.js",
|
|
53
|
+
"require": "./components/chip.js",
|
|
54
|
+
"types": "./components/chip.d.ts"
|
|
55
|
+
},
|
|
56
|
+
"./react/chip.js": {
|
|
57
|
+
"import": "./react/chip.js",
|
|
58
|
+
"require": "./react/chip.js",
|
|
59
|
+
"types": "./react/chip.d.ts"
|
|
60
|
+
},
|
|
61
|
+
"./components/cookie-banner.js": {
|
|
62
|
+
"import": "./components/cookie-banner.js",
|
|
63
|
+
"require": "./components/cookie-banner.js",
|
|
64
|
+
"types": "./components/cookie-banner.d.ts"
|
|
65
|
+
},
|
|
66
|
+
"./react/cookie-banner.js": {
|
|
67
|
+
"import": "./react/cookie-banner.js",
|
|
68
|
+
"require": "./react/cookie-banner.js",
|
|
69
|
+
"types": "./react/cookie-banner.d.ts"
|
|
70
|
+
},
|
|
71
|
+
"./components/divider.js": {
|
|
72
|
+
"import": "./components/divider.js",
|
|
73
|
+
"require": "./components/divider.js",
|
|
74
|
+
"types": "./components/divider.d.ts"
|
|
75
|
+
},
|
|
76
|
+
"./react/divider.js": {
|
|
77
|
+
"import": "./react/divider.js",
|
|
78
|
+
"require": "./react/divider.js",
|
|
79
|
+
"types": "./react/divider.d.ts"
|
|
80
|
+
},
|
|
81
|
+
"./components/form-label.js": {
|
|
82
|
+
"import": "./components/form-label.js",
|
|
83
|
+
"require": "./components/form-label.js",
|
|
84
|
+
"types": "./components/form-label.d.ts"
|
|
85
|
+
},
|
|
86
|
+
"./react/form-label.js": {
|
|
87
|
+
"import": "./react/form-label.js",
|
|
88
|
+
"require": "./react/form-label.js",
|
|
89
|
+
"types": "./react/form-label.d.ts"
|
|
90
|
+
},
|
|
91
|
+
"./components/icon-button.js": {
|
|
92
|
+
"import": "./components/icon-button.js",
|
|
93
|
+
"require": "./components/icon-button.js",
|
|
94
|
+
"types": "./components/icon-button.d.ts"
|
|
95
|
+
},
|
|
96
|
+
"./react/icon-button.js": {
|
|
97
|
+
"import": "./react/icon-button.js",
|
|
98
|
+
"require": "./react/icon-button.js",
|
|
99
|
+
"types": "./react/icon-button.d.ts"
|
|
100
|
+
},
|
|
101
|
+
"./components/link.js": {
|
|
102
|
+
"import": "./components/link.js",
|
|
103
|
+
"require": "./components/link.js",
|
|
104
|
+
"types": "./components/link.d.ts"
|
|
105
|
+
},
|
|
106
|
+
"./react/link.js": {
|
|
107
|
+
"import": "./react/link.js",
|
|
108
|
+
"require": "./react/link.js",
|
|
109
|
+
"types": "./react/link.d.ts"
|
|
110
|
+
},
|
|
111
|
+
"./components/modal.js": {
|
|
112
|
+
"import": "./components/modal.js",
|
|
113
|
+
"require": "./components/modal.js",
|
|
114
|
+
"types": "./components/modal.d.ts"
|
|
115
|
+
},
|
|
116
|
+
"./react/modal.js": {
|
|
117
|
+
"import": "./react/modal.js",
|
|
118
|
+
"require": "./react/modal.js",
|
|
119
|
+
"types": "./react/modal.d.ts"
|
|
120
|
+
},
|
|
121
|
+
"./components/notification.js": {
|
|
122
|
+
"import": "./components/notification.js",
|
|
123
|
+
"require": "./components/notification.js",
|
|
124
|
+
"types": "./components/notification.d.ts"
|
|
125
|
+
},
|
|
126
|
+
"./react/notification.js": {
|
|
127
|
+
"import": "./react/notification.js",
|
|
128
|
+
"require": "./react/notification.js",
|
|
129
|
+
"types": "./react/notification.d.ts"
|
|
130
|
+
},
|
|
131
|
+
"./components/spinner.js": {
|
|
132
|
+
"import": "./components/spinner.js",
|
|
133
|
+
"require": "./components/spinner.js",
|
|
134
|
+
"types": "./components/spinner.d.ts"
|
|
135
|
+
},
|
|
136
|
+
"./react/spinner.js": {
|
|
137
|
+
"import": "./react/spinner.js",
|
|
138
|
+
"require": "./react/spinner.js",
|
|
139
|
+
"types": "./react/spinner.d.ts"
|
|
140
|
+
},
|
|
141
|
+
"./components/switch.js": {
|
|
142
|
+
"import": "./components/switch.js",
|
|
143
|
+
"require": "./components/switch.js",
|
|
144
|
+
"types": "./components/switch.d.ts"
|
|
145
|
+
},
|
|
146
|
+
"./react/switch.js": {
|
|
147
|
+
"import": "./react/switch.js",
|
|
148
|
+
"require": "./react/switch.js",
|
|
149
|
+
"types": "./react/switch.d.ts"
|
|
150
|
+
},
|
|
151
|
+
"./components/tag.js": {
|
|
152
|
+
"import": "./components/tag.js",
|
|
153
|
+
"require": "./components/tag.js",
|
|
154
|
+
"types": "./components/tag.d.ts"
|
|
155
|
+
},
|
|
156
|
+
"./react/tag.js": {
|
|
157
|
+
"import": "./react/tag.js",
|
|
158
|
+
"require": "./react/tag.js",
|
|
159
|
+
"types": "./react/tag.d.ts"
|
|
160
|
+
},
|
|
161
|
+
"./components/text-input.js": {
|
|
162
|
+
"import": "./components/text-input.js",
|
|
163
|
+
"require": "./components/text-input.js",
|
|
164
|
+
"types": "./components/text-input.d.ts"
|
|
165
|
+
},
|
|
166
|
+
"./react/text-input.js": {
|
|
167
|
+
"import": "./react/text-input.js",
|
|
168
|
+
"require": "./react/text-input.js",
|
|
169
|
+
"types": "./react/text-input.d.ts"
|
|
170
|
+
},
|
|
171
|
+
"./components/textarea.js": {
|
|
172
|
+
"import": "./components/textarea.js",
|
|
173
|
+
"require": "./components/textarea.js",
|
|
174
|
+
"types": "./components/textarea.d.ts"
|
|
175
|
+
},
|
|
176
|
+
"./react/textarea.js": {
|
|
177
|
+
"import": "./react/textarea.js",
|
|
178
|
+
"require": "./react/textarea.js",
|
|
179
|
+
"types": "./react/textarea.d.ts"
|
|
180
|
+
},
|
|
181
|
+
"./components/toast.js": {
|
|
182
|
+
"import": "./components/toast.js",
|
|
183
|
+
"require": "./components/toast.js",
|
|
184
|
+
"types": "./components/toast.d.ts"
|
|
185
|
+
},
|
|
186
|
+
"./react/toast.js": {
|
|
187
|
+
"import": "./react/toast.js",
|
|
188
|
+
"require": "./react/toast.js",
|
|
189
|
+
"types": "./react/toast.d.ts"
|
|
190
|
+
}
|
|
191
|
+
},
|
|
11
192
|
"bin": {
|
|
12
193
|
"add-components": "./src/index.js"
|
|
13
194
|
},
|
|
@@ -27,29 +208,28 @@
|
|
|
27
208
|
},
|
|
28
209
|
"dependencies": {
|
|
29
210
|
"@justeattakeaway/pie-assistive-text": "0.4.1",
|
|
30
|
-
"@justeattakeaway/pie-button": "0.
|
|
211
|
+
"@justeattakeaway/pie-button": "0.0.0-snapshot-release-20240619133108",
|
|
31
212
|
"@justeattakeaway/pie-card": "0.19.5",
|
|
32
213
|
"@justeattakeaway/pie-checkbox": "0.5.0",
|
|
33
|
-
"@justeattakeaway/pie-chip": "0.
|
|
34
|
-
"@justeattakeaway/pie-cookie-banner": "0.
|
|
214
|
+
"@justeattakeaway/pie-chip": "0.0.0-snapshot-release-20240619133108",
|
|
215
|
+
"@justeattakeaway/pie-cookie-banner": "0.0.0-snapshot-release-20240619133108",
|
|
35
216
|
"@justeattakeaway/pie-divider": "0.13.5",
|
|
36
217
|
"@justeattakeaway/pie-form-label": "0.13.5",
|
|
37
|
-
"@justeattakeaway/pie-icon-button": "0.
|
|
218
|
+
"@justeattakeaway/pie-icon-button": "0.0.0-snapshot-release-20240619133108",
|
|
38
219
|
"@justeattakeaway/pie-link": "0.17.5",
|
|
39
|
-
"@justeattakeaway/pie-modal": "0.
|
|
40
|
-
"@justeattakeaway/pie-notification": "0.
|
|
41
|
-
"@justeattakeaway/pie-spinner": "0.
|
|
220
|
+
"@justeattakeaway/pie-modal": "0.0.0-snapshot-release-20240619133108",
|
|
221
|
+
"@justeattakeaway/pie-notification": "0.0.0-snapshot-release-20240619133108",
|
|
222
|
+
"@justeattakeaway/pie-spinner": "0.0.0-snapshot-release-20240619133108",
|
|
42
223
|
"@justeattakeaway/pie-switch": "0.29.8",
|
|
43
224
|
"@justeattakeaway/pie-tag": "0.9.6",
|
|
44
225
|
"@justeattakeaway/pie-text-input": "0.22.2",
|
|
45
|
-
"@justeattakeaway/pie-textarea": "0.
|
|
46
|
-
"@justeattakeaway/pie-toast": "0.
|
|
226
|
+
"@justeattakeaway/pie-textarea": "0.1.0",
|
|
227
|
+
"@justeattakeaway/pie-toast": "0.1.0"
|
|
47
228
|
},
|
|
48
229
|
"volta": {
|
|
49
230
|
"extends": "../../../package.json"
|
|
50
231
|
},
|
|
51
232
|
"sideEffects": [
|
|
52
|
-
"
|
|
53
|
-
"react/*.js"
|
|
233
|
+
"**/*.js"
|
|
54
234
|
]
|
|
55
235
|
}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
|
|
3
|
+
export class ComponentService {
|
|
4
|
+
constructor (fs, path) {
|
|
5
|
+
this.fs = fs;
|
|
6
|
+
this.path = path;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Helper function to get some frequently-used paths for the script.
|
|
11
|
+
* @param {string} workingDir - The current working directory.
|
|
12
|
+
* @returns {Object} - An object containing useful paths for the script.
|
|
13
|
+
*/
|
|
14
|
+
getPathShortcuts (workingDir) {
|
|
15
|
+
const componentsSourceDir = this.path.resolve(workingDir, 'packages/components');
|
|
16
|
+
const pieWebcDir = this.path.join(componentsSourceDir, 'pie-webc');
|
|
17
|
+
const componentsTargetDir = this.path.join(pieWebcDir, 'components');
|
|
18
|
+
const reactTargetDir = this.path.join(pieWebcDir, 'react');
|
|
19
|
+
const pieWebcPackageJsonPath = this.path.join(pieWebcDir, 'package.json');
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
componentsSourceDir,
|
|
23
|
+
componentsTargetDir,
|
|
24
|
+
reactTargetDir,
|
|
25
|
+
pieWebcPackageJsonPath,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Checks if a directory exists and creates it if it doesn't.
|
|
31
|
+
* @param {string} dir - The directory to create if it doesn't exist.
|
|
32
|
+
*/
|
|
33
|
+
ensureDirectoryExists (dir) {
|
|
34
|
+
if (!this.fs.existsSync(dir)) {
|
|
35
|
+
this.fs.mkdirSync(dir, { recursive: true });
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Reads and returns the package.json file at the given path,
|
|
41
|
+
* making sure that the `exports` and `dependencies` fields are present.
|
|
42
|
+
* @param {string} packageJsonPath - Path to the package.json file, including the file name.
|
|
43
|
+
* @returns - The prepared package.json object.
|
|
44
|
+
*/
|
|
45
|
+
readAndPreparePackageJson (packageJsonPath) {
|
|
46
|
+
const packageJsonData = this.fs.readFileSync(packageJsonPath, 'utf-8');
|
|
47
|
+
const packageJson = JSON.parse(packageJsonData);
|
|
48
|
+
packageJson.exports = packageJson.exports || {};
|
|
49
|
+
packageJson.dependencies = packageJson.dependencies || {};
|
|
50
|
+
|
|
51
|
+
return packageJson;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Verifies that the script is run from the root of the monorepo
|
|
56
|
+
* and that the package name matches the expected package name.
|
|
57
|
+
* @param {string} workingDir - The working directory from which the script is being run.
|
|
58
|
+
* @param {*} expectedPackageName - The expected package name for the directory the script should be run from.
|
|
59
|
+
*/
|
|
60
|
+
verifyRootDirectory (workingDir, expectedPackageName) {
|
|
61
|
+
const packageJsonPath = this.path.join(workingDir, 'package.json');
|
|
62
|
+
|
|
63
|
+
if (!this.fs.existsSync(packageJsonPath)) {
|
|
64
|
+
throw new Error(chalk.redBright('Please run this script from the root of the monorepo.'));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const packageJson = JSON.parse(this.fs.readFileSync(packageJsonPath, 'utf8'));
|
|
68
|
+
|
|
69
|
+
if (packageJson.name !== expectedPackageName) {
|
|
70
|
+
throw new Error(chalk.redBright('Incorrect package: Please run this script from the root of the monorepo.'));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Creates the exports for a component to be added to the pie-webc package.json.
|
|
76
|
+
* @param {string} componentName - The name of the component to create exports for, omitting the `'pie-'` prefix.
|
|
77
|
+
* @returns {Object} - An object containing the exports for the component.
|
|
78
|
+
*/
|
|
79
|
+
createPackageJsonExports (componentName) {
|
|
80
|
+
const exports = {
|
|
81
|
+
[`./components/${componentName}.js`]: {
|
|
82
|
+
import: `./components/${componentName}.js`,
|
|
83
|
+
require: `./components/${componentName}.js`,
|
|
84
|
+
types: `./components/${componentName}.d.ts`,
|
|
85
|
+
},
|
|
86
|
+
[`./react/${componentName}.js`]: {
|
|
87
|
+
import: `./react/${componentName}.js`,
|
|
88
|
+
require: `./react/${componentName}.js`,
|
|
89
|
+
types: `./react/${componentName}.d.ts`,
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
return exports;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Writes a `.js` and `.d.ts` file for the given component to the target directory.
|
|
98
|
+
* @param {*} componentName - The name of the component to write files for, omitting the `'pie-'` prefix.
|
|
99
|
+
* @param {*} target - An object containing the target directory and the export path.
|
|
100
|
+
* @param {*} target.dir - The target directory to write the files to.
|
|
101
|
+
* Either `'components'` or `'react'`.
|
|
102
|
+
* @param {*} target.exportPath - The export path for the component.
|
|
103
|
+
* For react components, this should be the path to the react.js file including the package name, e.g., `'@justeattakeaway/pie-button/dist/react.js'`.
|
|
104
|
+
* Otherwise, this should be the package name, e.g., `'@justeattakeaway/pie-button'`.
|
|
105
|
+
*/
|
|
106
|
+
writeFilesForComponent (componentName, target) {
|
|
107
|
+
const jsFilePath = this.path.join(target.dir, `${componentName}.js`);
|
|
108
|
+
const tsFilePath = this.path.join(target.dir, `${componentName}.d.ts`);
|
|
109
|
+
|
|
110
|
+
const fileContent = `export * from '${target.exportPath}';\n`;
|
|
111
|
+
this.fs.writeFileSync(jsFilePath, fileContent);
|
|
112
|
+
this.fs.writeFileSync(tsFilePath, fileContent);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Writes the package.json file to the given path.
|
|
117
|
+
* @param {string} path - The path to write the package.json file to.
|
|
118
|
+
* @param {Object} content - The content to write to the package.json file.
|
|
119
|
+
*/
|
|
120
|
+
writePackageJson (path, content) {
|
|
121
|
+
this.fs.writeFileSync(path, `${JSON.stringify(content, null, 2)}\n`);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Processes all components in the components directory, adding them to the pie-webc package.
|
|
126
|
+
* @param {*} workingDir - The working directory from which the script is run.
|
|
127
|
+
* @param {*} excludedFolders - An array of folder names to exclude from the processing.
|
|
128
|
+
* By default, any folder starting with 'pie-' will be processed, unless excluded.
|
|
129
|
+
* @param {*} packageJson - The package.json object to update with the new dependencies and exports.
|
|
130
|
+
* @returns - The updated package.json object.
|
|
131
|
+
*/
|
|
132
|
+
processComponents (workingDir, excludedFolders, packageJson) {
|
|
133
|
+
const newPackageJson = { ...packageJson };
|
|
134
|
+
const {
|
|
135
|
+
componentsSourceDir, componentsTargetDir, reactTargetDir,
|
|
136
|
+
} = this.getPathShortcuts(workingDir);
|
|
137
|
+
|
|
138
|
+
this.fs.readdirSync(componentsSourceDir).forEach((folder) => {
|
|
139
|
+
if (!folder.startsWith('pie-')) {
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
if (excludedFolders.includes(folder)) {
|
|
143
|
+
console.info(chalk.yellow(`Excluding: ${chalk.white(folder)}`));
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const fullFolderPath = this.path.join(componentsSourceDir, folder);
|
|
148
|
+
const componentName = folder.replace('pie-', '');
|
|
149
|
+
const packageName = `@justeattakeaway/${folder}`;
|
|
150
|
+
const componentPackageJsonPath = this.path.join(fullFolderPath, 'package.json');
|
|
151
|
+
const componentPackageJsonData = this.fs.readFileSync(componentPackageJsonPath, 'utf-8');
|
|
152
|
+
const componentPackageJson = JSON.parse(componentPackageJsonData);
|
|
153
|
+
|
|
154
|
+
// Add the component to dependencies
|
|
155
|
+
newPackageJson.dependencies[packageName] = componentPackageJson.version;
|
|
156
|
+
|
|
157
|
+
const targets = [
|
|
158
|
+
{
|
|
159
|
+
dir: componentsTargetDir,
|
|
160
|
+
exportPath: packageName,
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
dir: reactTargetDir,
|
|
164
|
+
exportPath: `${packageName}/dist/react.js`,
|
|
165
|
+
}
|
|
166
|
+
];
|
|
167
|
+
|
|
168
|
+
console.info(chalk.gray(`Adding: ${chalk.white(folder)}`));
|
|
169
|
+
|
|
170
|
+
targets.forEach((target) => {
|
|
171
|
+
this.writeFilesForComponent(componentName, target);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
newPackageJson.exports = {
|
|
175
|
+
...newPackageJson.exports,
|
|
176
|
+
...this.createPackageJsonExports(componentName),
|
|
177
|
+
};
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
return newPackageJson;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
import { ComponentService } from '../src/componentService';
|
|
2
|
+
|
|
3
|
+
describe('ComponentService', () => {
|
|
4
|
+
let fsMock;
|
|
5
|
+
let pathMock;
|
|
6
|
+
let componentService;
|
|
7
|
+
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
fsMock = {
|
|
10
|
+
existsSync: vi.fn(),
|
|
11
|
+
mkdirSync: vi.fn(),
|
|
12
|
+
readdirSync: vi.fn(),
|
|
13
|
+
readFileSync: vi.fn(),
|
|
14
|
+
writeFileSync: vi.fn(),
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
pathMock = {
|
|
18
|
+
join: vi.fn(),
|
|
19
|
+
resolve: vi.fn(),
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
pathMock.join.mockImplementation((...args) => args.join('/'));
|
|
23
|
+
pathMock.resolve.mockImplementation((...args) => args.join('/'));
|
|
24
|
+
|
|
25
|
+
// Suppress console output
|
|
26
|
+
vi.spyOn(console, 'info').mockImplementation(() => {}); // eslint-disable-line @typescript-eslint/no-empty-function
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
describe('ensureDirectoryExists', () => {
|
|
30
|
+
it('should create a directory if it does not exist', () => {
|
|
31
|
+
// Arrange
|
|
32
|
+
fsMock.existsSync.mockReturnValue(false);
|
|
33
|
+
componentService = new ComponentService(fsMock, pathMock);
|
|
34
|
+
|
|
35
|
+
// Act
|
|
36
|
+
componentService.ensureDirectoryExists('dir');
|
|
37
|
+
|
|
38
|
+
// Assert
|
|
39
|
+
expect(fsMock.mkdirSync).toHaveBeenCalledWith('dir', { recursive: true });
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('should not create a directory if it already exists', () => {
|
|
43
|
+
// Arrange
|
|
44
|
+
fsMock.existsSync.mockReturnValue(true);
|
|
45
|
+
componentService = new ComponentService(fsMock, pathMock);
|
|
46
|
+
|
|
47
|
+
// Act
|
|
48
|
+
componentService.ensureDirectoryExists('dir');
|
|
49
|
+
|
|
50
|
+
// Assert
|
|
51
|
+
expect(fsMock.mkdirSync).not.toHaveBeenCalled();
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
describe('readAndPreparePackageJson', () => {
|
|
56
|
+
it('should return the packageJson with empty exports and dependencies objects if they do not exist', () => {
|
|
57
|
+
// Arrange
|
|
58
|
+
fsMock.readFileSync.mockReturnValue('{}');
|
|
59
|
+
componentService = new ComponentService(fsMock, pathMock);
|
|
60
|
+
|
|
61
|
+
// Act
|
|
62
|
+
const result = componentService.readAndPreparePackageJson('packageJsonPath');
|
|
63
|
+
|
|
64
|
+
// Assert
|
|
65
|
+
expect(result.exports).toEqual({});
|
|
66
|
+
expect(result.dependencies).toEqual({});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('should return the packageJson with existing exports and dependencies objects if they exist', () => {
|
|
70
|
+
// Arrange
|
|
71
|
+
fsMock.readFileSync.mockReturnValue('{"exports": {"exportKey": "exportValue"}, "dependencies": {"depKey": "depValue"}}');
|
|
72
|
+
componentService = new ComponentService(fsMock, pathMock);
|
|
73
|
+
|
|
74
|
+
// Act
|
|
75
|
+
const result = componentService.readAndPreparePackageJson('/path/to/package.json');
|
|
76
|
+
|
|
77
|
+
// Assert
|
|
78
|
+
expect(result.exports).toEqual({ exportKey: 'exportValue' });
|
|
79
|
+
expect(result.dependencies).toEqual({ depKey: 'depValue' });
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
describe('verifyRootDirectory', () => {
|
|
84
|
+
beforeEach(() => {
|
|
85
|
+
pathMock.join.mockReturnValue('');
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('should throw an error if there is no package.json file in the working directory', () => {
|
|
89
|
+
// Arrange
|
|
90
|
+
fsMock.existsSync.mockReturnValue(false);
|
|
91
|
+
componentService = new ComponentService(fsMock, pathMock);
|
|
92
|
+
|
|
93
|
+
// Act & Assert
|
|
94
|
+
const testFn = () => componentService.verifyRootDirectory('workingDir', 'expectedPackageName');
|
|
95
|
+
expect(testFn).toThrowError('Please run this script from the root of the monorepo.');
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('should throw an error if the package.json does not match the expected name', () => {
|
|
99
|
+
// Arrange
|
|
100
|
+
fsMock.existsSync.mockReturnValue(true);
|
|
101
|
+
fsMock.readFileSync.mockReturnValue('{"name": "wrongPackageName"}');
|
|
102
|
+
componentService = new ComponentService(fsMock, pathMock);
|
|
103
|
+
|
|
104
|
+
// Act & Assert
|
|
105
|
+
const testFn = () => componentService.verifyRootDirectory('workingDir', 'expectedPackageName');
|
|
106
|
+
expect(testFn).toThrowError('Incorrect package: Please run this script from the root of the monorepo.');
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it('should not throw any errors if the package.json name matches the expected name', () => {
|
|
110
|
+
// Arrange
|
|
111
|
+
fsMock.existsSync.mockReturnValue(true);
|
|
112
|
+
fsMock.readFileSync.mockReturnValue('{"name": "expectedPackageName"}');
|
|
113
|
+
componentService = new ComponentService(fsMock, pathMock);
|
|
114
|
+
|
|
115
|
+
// Act & Assert
|
|
116
|
+
const testFn = () => componentService.verifyRootDirectory('workingDir', 'expectedPackageName');
|
|
117
|
+
expect(testFn).not.toThrow();
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
describe('createPackageJsonExports', () => {
|
|
122
|
+
it('should return an object with exports for the component', () => {
|
|
123
|
+
// Arrange
|
|
124
|
+
componentService = new ComponentService(fsMock, pathMock);
|
|
125
|
+
|
|
126
|
+
// Act
|
|
127
|
+
const result = componentService.createPackageJsonExports('component-name');
|
|
128
|
+
|
|
129
|
+
// Assert
|
|
130
|
+
expect(result).toEqual({
|
|
131
|
+
'./components/component-name.js': {
|
|
132
|
+
import: './components/component-name.js',
|
|
133
|
+
require: './components/component-name.js',
|
|
134
|
+
types: './components/component-name.d.ts',
|
|
135
|
+
},
|
|
136
|
+
'./react/component-name.js': {
|
|
137
|
+
import: './react/component-name.js',
|
|
138
|
+
require: './react/component-name.js',
|
|
139
|
+
types: './react/component-name.d.ts',
|
|
140
|
+
},
|
|
141
|
+
});
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
describe('writeFilesForComponent', () => {
|
|
146
|
+
it('should write the component files to the target directory', () => {
|
|
147
|
+
// Arrange
|
|
148
|
+
componentService = new ComponentService(fsMock, pathMock);
|
|
149
|
+
const target = {
|
|
150
|
+
dir: 'componentsTargetDir',
|
|
151
|
+
exportPath: 'packageName',
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
// Act
|
|
155
|
+
componentService.writeFilesForComponent('component-name', target);
|
|
156
|
+
|
|
157
|
+
// Assert
|
|
158
|
+
expect(fsMock.writeFileSync).toHaveBeenCalledWith('componentsTargetDir/component-name.js', "export * from 'packageName';\n");
|
|
159
|
+
expect(fsMock.writeFileSync).toHaveBeenCalledWith('componentsTargetDir/component-name.d.ts', "export * from 'packageName';\n");
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
describe('writePackageJson', () => {
|
|
164
|
+
it('should write the stringified content to the specified path', () => {
|
|
165
|
+
// Arrange
|
|
166
|
+
componentService = new ComponentService(fsMock, pathMock);
|
|
167
|
+
const packageJson = { name: 'package-name', version: '1.0.0' };
|
|
168
|
+
|
|
169
|
+
// Act
|
|
170
|
+
componentService.writePackageJson('path/to/package.json', packageJson);
|
|
171
|
+
|
|
172
|
+
// Assert
|
|
173
|
+
expect(fsMock.writeFileSync).toHaveBeenCalledWith('path/to/package.json', '{\n "name": "package-name",\n "version": "1.0.0"\n}\n');
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
describe('processComponents', () => {
|
|
178
|
+
let excludedFolders;
|
|
179
|
+
let workingDir;
|
|
180
|
+
|
|
181
|
+
beforeEach(() => {
|
|
182
|
+
excludedFolders = [];
|
|
183
|
+
workingDir = 'workingDir';
|
|
184
|
+
|
|
185
|
+
fsMock.readFileSync.mockReturnValue('{"version": "1.1.1"}'); // Component package.json
|
|
186
|
+
fsMock.readdirSync.mockReturnValue(['pie-component-name']); // Default, overridden by some tests
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it('should ignore non-component folders', () => {
|
|
190
|
+
// Arrange
|
|
191
|
+
fsMock.readdirSync.mockReturnValue(['not-a-component']);
|
|
192
|
+
componentService = new ComponentService(fsMock, pathMock);
|
|
193
|
+
|
|
194
|
+
// Act
|
|
195
|
+
componentService.processComponents(workingDir, excludedFolders, {});
|
|
196
|
+
|
|
197
|
+
// Assert
|
|
198
|
+
expect(fsMock.writeFileSync).not.toHaveBeenCalled();
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it('should ignore excluded folders', () => {
|
|
202
|
+
// Arrange
|
|
203
|
+
fsMock.readdirSync.mockReturnValue(['pie-excluded']);
|
|
204
|
+
componentService = new ComponentService(fsMock, pathMock);
|
|
205
|
+
|
|
206
|
+
excludedFolders = ['pie-excluded'];
|
|
207
|
+
|
|
208
|
+
// Act
|
|
209
|
+
componentService.processComponents(workingDir, excludedFolders, {});
|
|
210
|
+
|
|
211
|
+
// Assert
|
|
212
|
+
expect(fsMock.writeFileSync).not.toHaveBeenCalled();
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it('should add components as dependencies to the package.json', () => {
|
|
216
|
+
// Arrange
|
|
217
|
+
componentService = new ComponentService(fsMock, pathMock);
|
|
218
|
+
const spy = vi.spyOn(componentService, 'writeFilesForComponent');
|
|
219
|
+
|
|
220
|
+
// Act
|
|
221
|
+
componentService.processComponents(workingDir, excludedFolders, { dependencies: {} });
|
|
222
|
+
|
|
223
|
+
// Assert
|
|
224
|
+
expect(spy).toHaveBeenCalledWith('component-name', {
|
|
225
|
+
dir: 'workingDir/packages/components/pie-webc/components',
|
|
226
|
+
exportPath: '@justeattakeaway/pie-component-name',
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
expect(spy).toHaveBeenCalledWith('component-name', {
|
|
230
|
+
dir: 'workingDir/packages/components/pie-webc/react',
|
|
231
|
+
exportPath: '@justeattakeaway/pie-component-name/dist/react.js',
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
it('should add exports for each component to the package.json', () => {
|
|
236
|
+
// Arrange
|
|
237
|
+
componentService = new ComponentService(fsMock, pathMock);
|
|
238
|
+
const rootPackageJson = { dependencies: {} };
|
|
239
|
+
|
|
240
|
+
// Act
|
|
241
|
+
const { exports } = componentService.processComponents(workingDir, excludedFolders, rootPackageJson);
|
|
242
|
+
|
|
243
|
+
// Assert
|
|
244
|
+
expect(Object.keys(exports)).toHaveLength(2);
|
|
245
|
+
expect(Object.keys(exports)).toContain('./components/component-name.js');
|
|
246
|
+
expect(Object.keys(exports)).toContain('./react/component-name.js');
|
|
247
|
+
// Content of the exports object is tested in createPackageJsonExports
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
it('should preserve existing exports in the package.json', () => {
|
|
251
|
+
// Arrange
|
|
252
|
+
componentService = new ComponentService(fsMock, pathMock);
|
|
253
|
+
const rootPackageJson = {
|
|
254
|
+
dependencies: {},
|
|
255
|
+
exports: {
|
|
256
|
+
'./components/existing-component.js': {},
|
|
257
|
+
'./react/existing-component.js': {},
|
|
258
|
+
},
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
// Act
|
|
262
|
+
const { exports } = componentService.processComponents(workingDir, excludedFolders, rootPackageJson);
|
|
263
|
+
|
|
264
|
+
// Assert
|
|
265
|
+
expect(Object.keys(exports)).toContain('./components/existing-component.js');
|
|
266
|
+
expect(Object.keys(exports)).toContain('./react/existing-component.js');
|
|
267
|
+
expect(Object.keys(exports)).toHaveLength(4);
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
it('should override existing exports in the package.json', () => {
|
|
271
|
+
// Arrange
|
|
272
|
+
componentService = new ComponentService(fsMock, pathMock);
|
|
273
|
+
const rootPackageJson = {
|
|
274
|
+
dependencies: {},
|
|
275
|
+
exports: {
|
|
276
|
+
'./components/component-name.js': {},
|
|
277
|
+
'./react/component-name.js': {},
|
|
278
|
+
},
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
// Act
|
|
282
|
+
const { exports } = componentService.processComponents(workingDir, excludedFolders, rootPackageJson);
|
|
283
|
+
|
|
284
|
+
// Assert
|
|
285
|
+
expect(Object.keys(exports)).toContain('./components/component-name.js');
|
|
286
|
+
expect(Object.keys(exports)).toContain('./react/component-name.js');
|
|
287
|
+
expect(Object.keys(exports)).toHaveLength(2);
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
it('should preserve existing dependencies in the package.json', () => {
|
|
291
|
+
// Arrange
|
|
292
|
+
fsMock.readdirSync.mockReturnValue(['pie-new-component']);
|
|
293
|
+
|
|
294
|
+
componentService = new ComponentService(fsMock, pathMock);
|
|
295
|
+
const rootPackageJson = {
|
|
296
|
+
dependencies: { '@justeattakeaway/pie-existing-component': '1.0.0' },
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
// Act
|
|
300
|
+
const { dependencies } = componentService.processComponents(workingDir, excludedFolders, rootPackageJson);
|
|
301
|
+
|
|
302
|
+
// Assert
|
|
303
|
+
expect(dependencies).toEqual({
|
|
304
|
+
'@justeattakeaway/pie-existing-component': '1.0.0',
|
|
305
|
+
'@justeattakeaway/pie-new-component': '1.1.1',
|
|
306
|
+
});
|
|
307
|
+
});
|
|
308
|
+
});
|
|
309
|
+
});
|