@servicetitan/startup 23.0.0 → 23.2.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/cli/commands/build.d.ts +1 -0
- package/dist/cli/commands/build.d.ts.map +1 -1
- package/dist/cli/commands/build.js +1 -0
- package/dist/cli/commands/build.js.map +1 -1
- package/dist/cli/commands/bundle-package.d.ts +1 -0
- package/dist/cli/commands/bundle-package.d.ts.map +1 -1
- package/dist/cli/commands/bundle-package.js +1 -0
- package/dist/cli/commands/bundle-package.js.map +1 -1
- package/dist/cli/commands/init.d.ts +8 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +25 -3
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/start.d.ts +1 -0
- package/dist/cli/commands/start.d.ts.map +1 -1
- package/dist/cli/commands/start.js +1 -0
- package/dist/cli/commands/start.js.map +1 -1
- package/dist/cli/utils/bundle.d.ts +1 -0
- package/dist/cli/utils/bundle.d.ts.map +1 -1
- package/dist/cli/utils/bundle.js +11 -5
- package/dist/cli/utils/bundle.js.map +1 -1
- package/dist/utils/get-configuration.d.ts +10 -6
- package/dist/utils/get-configuration.d.ts.map +1 -1
- package/dist/utils/get-configuration.js +47 -38
- package/dist/utils/get-configuration.js.map +1 -1
- package/dist/utils/get-jest-config.js +1 -1
- package/dist/utils/get-jest-config.js.map +1 -1
- package/dist/webpack/configs/plugins/ignore-plugin/check-resource.d.ts +2 -0
- package/dist/webpack/configs/plugins/ignore-plugin/check-resource.d.ts.map +1 -0
- package/dist/webpack/configs/plugins/ignore-plugin/check-resource.js +17 -0
- package/dist/webpack/configs/plugins/ignore-plugin/check-resource.js.map +1 -0
- package/dist/webpack/configs/plugins/ignore-plugin/ignore-plugin.d.ts +4 -0
- package/dist/webpack/configs/plugins/ignore-plugin/ignore-plugin.d.ts.map +1 -0
- package/dist/webpack/configs/plugins/ignore-plugin/ignore-plugin.js +15 -0
- package/dist/webpack/configs/plugins/ignore-plugin/ignore-plugin.js.map +1 -0
- package/dist/webpack/configs/plugins/ignore-plugin/index.d.ts +2 -0
- package/dist/webpack/configs/plugins/ignore-plugin/index.d.ts.map +1 -0
- package/dist/webpack/configs/plugins/ignore-plugin/index.js +18 -0
- package/dist/webpack/configs/plugins/ignore-plugin/index.js.map +1 -0
- package/dist/webpack/configs/plugins/ignore-plugin/is-optional-peer-dependency.d.ts +7 -0
- package/dist/webpack/configs/plugins/ignore-plugin/is-optional-peer-dependency.d.ts.map +1 -0
- package/dist/webpack/configs/plugins/ignore-plugin/is-optional-peer-dependency.js +8 -0
- package/dist/webpack/configs/plugins/ignore-plugin/is-optional-peer-dependency.js.map +1 -0
- package/dist/webpack/configs/plugins/index.d.ts +1 -0
- package/dist/webpack/configs/plugins/index.d.ts.map +1 -1
- package/dist/webpack/configs/plugins/index.js +1 -0
- package/dist/webpack/configs/plugins/index.js.map +1 -1
- package/dist/webpack/configs/plugins/virtual-modules-plugin.js +4 -3
- package/dist/webpack/configs/plugins/virtual-modules-plugin.js.map +1 -1
- package/dist/webpack/configs/plugins-config.d.ts.map +1 -1
- package/dist/webpack/configs/plugins-config.js +1 -0
- package/dist/webpack/configs/plugins-config.js.map +1 -1
- package/dist/webpack/configs/rules/tsx-rules.d.ts +1 -1
- package/dist/webpack/configs/rules/tsx-rules.d.ts.map +1 -1
- package/dist/webpack/configs/rules/tsx-rules.js +10 -8
- package/dist/webpack/configs/rules/tsx-rules.js.map +1 -1
- package/dist/webpack/configs/types.d.ts +2 -0
- package/dist/webpack/configs/types.d.ts.map +1 -1
- package/dist/webpack/create-webpack-config.d.ts.map +1 -1
- package/dist/webpack/create-webpack-config.js +2 -2
- package/dist/webpack/create-webpack-config.js.map +1 -1
- package/dist/webpack/types.d.ts +3 -2
- package/dist/webpack/types.d.ts.map +1 -1
- package/package.json +10 -8
- package/src/cli/commands/__tests__/build.test.ts +14 -0
- package/src/cli/commands/__tests__/init.test.ts +48 -17
- package/src/cli/commands/__tests__/start.test.ts +14 -0
- package/src/cli/commands/build.ts +2 -0
- package/src/cli/commands/bundle-package.ts +2 -0
- package/src/cli/commands/init.ts +26 -12
- package/src/cli/commands/start.ts +2 -0
- package/src/cli/utils/bundle.ts +14 -5
- package/src/utils/__tests__/get-configuration.test.ts +17 -5
- package/src/utils/__tests__/get-jest-config.test.ts +1 -1
- package/src/utils/get-configuration.ts +54 -43
- package/src/utils/get-jest-config.ts +1 -1
- package/src/webpack/__tests__/create-webpack-config-web-component.test.ts +34 -11
- package/src/webpack/__tests__/create-webpack-config.test.ts +46 -12
- package/src/webpack/configs/plugins/ignore-plugin/__tests__/check-resource.test.ts +37 -0
- package/src/webpack/configs/plugins/ignore-plugin/__tests__/is-optional-peer-dependency.test.ts +30 -0
- package/src/webpack/configs/plugins/ignore-plugin/check-resource.ts +12 -0
- package/src/webpack/configs/plugins/ignore-plugin/ignore-plugin.ts +13 -0
- package/src/webpack/configs/plugins/ignore-plugin/index.ts +1 -0
- package/src/webpack/configs/plugins/ignore-plugin/is-optional-peer-dependency.ts +10 -0
- package/src/webpack/configs/plugins/index.ts +1 -0
- package/src/webpack/configs/plugins/virtual-modules-plugin.ts +4 -3
- package/src/webpack/configs/plugins-config.ts +2 -0
- package/src/webpack/configs/rules/tsx-rules.ts +13 -9
- package/src/webpack/configs/types.ts +2 -0
- package/src/webpack/create-webpack-config.ts +5 -2
- package/src/webpack/types.ts +3 -2
- package/template/package.json +3 -3
- package/template/packages/application/package.json +9 -3
- package/template/packages/application/src/__tests__/app.test.tsx +1 -1
- package/template/packages/application/src/app.tsx +4 -3
- package/template-react18/packages/application/package.json +35 -0
- package/template-react18/packages/application/src/index.tsx +9 -0
- package/template-react18/packages/feature-a/package.json +19 -0
- package/template-react18/packages/feature-b/package.json +19 -0
- package/template-react18/packages/feature-c/package.json +19 -0
|
@@ -57,11 +57,15 @@ export enum CommandName {
|
|
|
57
57
|
}
|
|
58
58
|
/* eslint-enable @typescript-eslint/naming-convention */
|
|
59
59
|
|
|
60
|
+
interface WebComponentOptions {
|
|
61
|
+
legacyRoot: boolean;
|
|
62
|
+
}
|
|
63
|
+
|
|
60
64
|
type Configuration = {
|
|
61
65
|
'legacy'?: boolean;
|
|
62
66
|
'lint'?: { eslint: ESLintConfiguration; stylelint: StylelintConfiguration };
|
|
63
67
|
'test'?: JestConfiguration;
|
|
64
|
-
'web-component'?: boolean;
|
|
68
|
+
'web-component'?: boolean | WebComponentOptions;
|
|
65
69
|
'webpack'?: false | WebpackConfiguration;
|
|
66
70
|
} & {
|
|
67
71
|
[key in CommandName]: NodeConfiguration;
|
|
@@ -75,38 +79,6 @@ export function getConfigurationSafe(location = './'): Configuration {
|
|
|
75
79
|
return readJsonSafe(path.join(location, 'package.json'))?.cli ?? {};
|
|
76
80
|
}
|
|
77
81
|
|
|
78
|
-
export function isBundle(location?: string) {
|
|
79
|
-
return getConfiguration(location).webpack !== false;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
export function isCustomStyleRules() {
|
|
83
|
-
const configuration = getConfiguration();
|
|
84
|
-
|
|
85
|
-
if (typeof configuration.webpack !== 'object') {
|
|
86
|
-
return false;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
return configuration.webpack['custom-style-rules'] === true;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
export function isExposeSharedDependencies() {
|
|
93
|
-
const configuration = getConfiguration();
|
|
94
|
-
|
|
95
|
-
if (typeof configuration.webpack !== 'object') {
|
|
96
|
-
return false;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
return configuration.webpack['expose-shared-dependencies'] === true;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function pick<T, K extends keyof T>(target: T, keys: readonly K[]) {
|
|
103
|
-
return Object.fromEntries(
|
|
104
|
-
keys
|
|
105
|
-
.filter(key => Object.prototype.hasOwnProperty.call(target, key))
|
|
106
|
-
.map(key => [key, target[key]])
|
|
107
|
-
) as Pick<T, K>;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
82
|
export function getDevServerConfiguration() {
|
|
111
83
|
const configuration = getConfiguration();
|
|
112
84
|
|
|
@@ -130,24 +102,49 @@ export function getDevServerConfiguration() {
|
|
|
130
102
|
>;
|
|
131
103
|
}
|
|
132
104
|
|
|
133
|
-
export function isWebComponent() {
|
|
134
|
-
return getConfiguration()['web-component'] === true;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
export function isLegacy(location?: string) {
|
|
138
|
-
return getConfiguration(location).legacy === true;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
105
|
export function getESLintConfiguration() {
|
|
142
106
|
return getConfiguration().lint?.eslint ?? {};
|
|
143
107
|
}
|
|
144
108
|
|
|
109
|
+
export function getJestConfiguration() {
|
|
110
|
+
return getConfiguration().test ?? {};
|
|
111
|
+
}
|
|
112
|
+
|
|
145
113
|
export function getStylelintConfiguration() {
|
|
146
114
|
return getConfiguration().lint?.stylelint ?? {};
|
|
147
115
|
}
|
|
148
116
|
|
|
149
|
-
export function
|
|
150
|
-
return getConfiguration().
|
|
117
|
+
export function isBundle(location?: string) {
|
|
118
|
+
return getConfiguration(location).webpack !== false;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function isCustomStyleRules() {
|
|
122
|
+
const configuration = getConfiguration();
|
|
123
|
+
|
|
124
|
+
if (typeof configuration.webpack !== 'object') {
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return configuration.webpack['custom-style-rules'] === true;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export function isExposeSharedDependencies() {
|
|
132
|
+
const configuration = getConfiguration();
|
|
133
|
+
|
|
134
|
+
if (typeof configuration.webpack !== 'object') {
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return configuration.webpack['expose-shared-dependencies'] === true;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export function isLegacy(location?: string) {
|
|
142
|
+
return getConfiguration(location).legacy === true;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export function isLegacyRoot() {
|
|
146
|
+
const config = getConfiguration()['web-component'];
|
|
147
|
+
return typeof config === 'object' && config.legacyRoot === true;
|
|
151
148
|
}
|
|
152
149
|
|
|
153
150
|
export function isStyleCheckDisabled() {
|
|
@@ -159,3 +156,17 @@ export function isStyleCheckDisabled() {
|
|
|
159
156
|
|
|
160
157
|
return configuration.webpack?.['disable-style-check'] === true;
|
|
161
158
|
}
|
|
159
|
+
|
|
160
|
+
export function isWebComponent() {
|
|
161
|
+
const config = getConfiguration()['web-component'];
|
|
162
|
+
return config === true || typeof config === 'object';
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function pick<T extends object, K extends keyof T>(target: T, keys: readonly K[]) {
|
|
166
|
+
return keys.reduce((result, key) => {
|
|
167
|
+
if (Object.hasOwn(target, key)) {
|
|
168
|
+
result[key] = target[key];
|
|
169
|
+
}
|
|
170
|
+
return result;
|
|
171
|
+
}, {} as Pick<T, K>);
|
|
172
|
+
}
|
|
@@ -28,7 +28,7 @@ const getJestConfigBase = ({
|
|
|
28
28
|
verbose: true,
|
|
29
29
|
testEnvironment: 'jsdom',
|
|
30
30
|
testRunner: 'jest-circus/runner',
|
|
31
|
-
transformIgnorePatterns: ['node_modules/(?!(@servicetitan|@react-hook|nanoid)/)'],
|
|
31
|
+
transformIgnorePatterns: ['node_modules/(?!(@servicetitan|@react-hook|nanoid|axios)/)'],
|
|
32
32
|
modulePathIgnorePatterns: ['<rootDir>/.*/__mocks__'],
|
|
33
33
|
transform,
|
|
34
34
|
moduleNameMapper,
|
|
@@ -7,7 +7,14 @@ import WebpackAssetsManifest from 'webpack-assets-manifest';
|
|
|
7
7
|
import VirtualModulesPlugin from 'webpack-virtual-modules';
|
|
8
8
|
|
|
9
9
|
import { webpackDevConfigFileName, webpackProdConfigFileName } from '../../cli/utils';
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
getFolders,
|
|
12
|
+
getPackageData,
|
|
13
|
+
getPackages,
|
|
14
|
+
getTsConfig,
|
|
15
|
+
isLegacyRoot,
|
|
16
|
+
isWebComponent,
|
|
17
|
+
} from '../../utils';
|
|
11
18
|
import { webComponentStyleRules } from '../__mocks__';
|
|
12
19
|
import { generateMetadata } from '../configs/utils';
|
|
13
20
|
import { featureCohort, getCallerFile, splitByEntry } from '../utils';
|
|
@@ -33,6 +40,7 @@ jest.mock('../../utils', () => ({
|
|
|
33
40
|
getPackageData: jest.fn(),
|
|
34
41
|
getPackages: jest.fn(),
|
|
35
42
|
getTsConfig: jest.fn(),
|
|
43
|
+
isLegacyRoot: jest.fn(),
|
|
36
44
|
isWebComponent: jest.fn(),
|
|
37
45
|
}));
|
|
38
46
|
jest.mock('../configs/utils/generate-metadata');
|
|
@@ -56,12 +64,13 @@ describe(`[startup] ${createWebpackConfig.name}`, () => {
|
|
|
56
64
|
].join('\n');
|
|
57
65
|
|
|
58
66
|
const indexPath = () => path.join(process.cwd(), `${source}/index.ts`);
|
|
59
|
-
const indexCode =
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
67
|
+
const indexCode = ({ legacyRoot = false }: { legacyRoot?: boolean } = {}) =>
|
|
68
|
+
[
|
|
69
|
+
`import { register } from '@servicetitan/web-components';`,
|
|
70
|
+
`import { App } from './app';`,
|
|
71
|
+
`require('./design-system.css');`,
|
|
72
|
+
`register(App, false, { legacyRoot: ${legacyRoot} });`,
|
|
73
|
+
].join('\n');
|
|
65
74
|
|
|
66
75
|
let overrides: Parameters<typeof createWebpackConfig>[0];
|
|
67
76
|
let options: NonNullable<Parameters<typeof createWebpackConfig>[1]>;
|
|
@@ -86,6 +95,7 @@ describe(`[startup] ${createWebpackConfig.name}`, () => {
|
|
|
86
95
|
jest.mocked(getPackageData).mockReturnValue({ dependencies: {}, sharedDependencies: {} });
|
|
87
96
|
jest.mocked(getPackages).mockReturnValue([]);
|
|
88
97
|
jest.mocked(getTsConfig).mockReturnValue(tsConfig);
|
|
98
|
+
jest.mocked(isLegacyRoot).mockReturnValue(false);
|
|
89
99
|
|
|
90
100
|
vol.fromJSON({ [packageJson]: JSON.stringify({ name: packageName }) });
|
|
91
101
|
});
|
|
@@ -216,11 +226,24 @@ describe(`[startup] ${createWebpackConfig.name}`, () => {
|
|
|
216
226
|
expect(subject().plugins).toContainEqual(
|
|
217
227
|
new VirtualModulesPlugin({
|
|
218
228
|
[designSystemPath()]: designSystemCode,
|
|
219
|
-
[indexPath()]: indexCode,
|
|
229
|
+
[indexPath()]: indexCode(),
|
|
220
230
|
})
|
|
221
231
|
);
|
|
222
232
|
});
|
|
223
233
|
|
|
234
|
+
describe('with {legacyRoot: true}', () => {
|
|
235
|
+
beforeEach(() => jest.mocked(isLegacyRoot).mockReturnValue(true));
|
|
236
|
+
|
|
237
|
+
test('registers component with {legacyRoot: true}', () => {
|
|
238
|
+
expect(subject().plugins).toContainEqual(
|
|
239
|
+
new VirtualModulesPlugin({
|
|
240
|
+
[designSystemPath()]: designSystemCode,
|
|
241
|
+
[indexPath()]: indexCode({ legacyRoot: true }),
|
|
242
|
+
})
|
|
243
|
+
);
|
|
244
|
+
});
|
|
245
|
+
});
|
|
246
|
+
|
|
224
247
|
describe('when design-system.css exists', () => {
|
|
225
248
|
beforeEach(() => {
|
|
226
249
|
vol.fromJSON({
|
|
@@ -232,7 +255,7 @@ describe(`[startup] ${createWebpackConfig.name}`, () => {
|
|
|
232
255
|
test('omits design system virtual module', () => {
|
|
233
256
|
expect(subject().plugins).toContainEqual(
|
|
234
257
|
new VirtualModulesPlugin({
|
|
235
|
-
[indexPath()]: indexCode,
|
|
258
|
+
[indexPath()]: indexCode(),
|
|
236
259
|
})
|
|
237
260
|
);
|
|
238
261
|
});
|
|
@@ -245,9 +268,9 @@ describe(`[startup] ${createWebpackConfig.name}`, () => {
|
|
|
245
268
|
expect(subject().plugins).toContainEqual(
|
|
246
269
|
new VirtualModulesPlugin({
|
|
247
270
|
[designSystemPath()]: designSystemCode,
|
|
248
|
-
[indexPath()]: indexCode
|
|
271
|
+
[indexPath()]: indexCode()
|
|
249
272
|
.replace("require('./design-system.css');\n", '')
|
|
250
|
-
.replace('register(App, false
|
|
273
|
+
.replace('register(App, false', 'register(App, true'),
|
|
251
274
|
})
|
|
252
275
|
);
|
|
253
276
|
});
|
|
@@ -5,7 +5,7 @@ import { fs, vol } from 'memfs';
|
|
|
5
5
|
import MomentLocalesPlugin from 'moment-locales-webpack-plugin';
|
|
6
6
|
import os from 'os';
|
|
7
7
|
import path from 'path';
|
|
8
|
-
import { ProvidePlugin } from 'webpack';
|
|
8
|
+
import { IgnorePlugin, ProvidePlugin } from 'webpack';
|
|
9
9
|
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
|
|
10
10
|
const FilterWarningsPlugin = require('webpack-filter-warnings-plugin');
|
|
11
11
|
import { argv } from 'yargs';
|
|
@@ -37,6 +37,7 @@ jest.mock('mini-css-extract-plugin', () =>
|
|
|
37
37
|
jest.mock('moment-locales-webpack-plugin', () => jest.fn());
|
|
38
38
|
jest.mock('webpack', () => ({
|
|
39
39
|
...jest.requireActual('webpack'),
|
|
40
|
+
IgnorePlugin: jest.fn(),
|
|
40
41
|
ProvidePlugin: jest.fn(),
|
|
41
42
|
}));
|
|
42
43
|
jest.mock('webpack-assets-manifest', () => jest.fn());
|
|
@@ -86,7 +87,8 @@ describe(`[startup] ${createWebpackConfig.name}`, () => {
|
|
|
86
87
|
}
|
|
87
88
|
|
|
88
89
|
function setOptions(props: typeof options) {
|
|
89
|
-
|
|
90
|
+
options ??= {};
|
|
91
|
+
Object.assign(options, props);
|
|
90
92
|
}
|
|
91
93
|
|
|
92
94
|
beforeEach(() => {
|
|
@@ -108,13 +110,14 @@ describe(`[startup] ${createWebpackConfig.name}`, () => {
|
|
|
108
110
|
mockPlugIn('ForkTsCheckerWebpackPlugin')
|
|
109
111
|
);
|
|
110
112
|
jest.mocked(HtmlWebpackPlugin).mockImplementation(mockPlugIn('HtmlWebpackPlugin'));
|
|
113
|
+
jest.mocked(IgnorePlugin).mockImplementation(mockPlugIn('IgnorePlugin'));
|
|
111
114
|
jest.mocked(MomentLocalesPlugin).mockImplementation(mockPlugIn('MomentLocalesPlugin'));
|
|
112
115
|
jest.mocked(ProvidePlugin).mockImplementation(mockPlugIn('ProvidePlugin'));
|
|
113
116
|
|
|
114
|
-
jest.mocked(getFolders).mockImplementation((location?: string) =>
|
|
115
|
-
|
|
116
|
-
destination
|
|
117
|
-
})
|
|
117
|
+
jest.mocked(getFolders).mockImplementation((location?: string) => {
|
|
118
|
+
const prefix = location ? `${location.replace(/\W/g, '')}-` : '';
|
|
119
|
+
return { source: `${prefix}${source}`, destination };
|
|
120
|
+
});
|
|
118
121
|
jest.mocked(getPackageData).mockReturnValue({ dependencies, sharedDependencies });
|
|
119
122
|
jest.mocked(getPackageDependencyVersion).mockImplementation(
|
|
120
123
|
(_, defaultVersion) => defaultVersion
|
|
@@ -231,7 +234,19 @@ describe(`[startup] ${createWebpackConfig.name}`, () => {
|
|
|
231
234
|
});
|
|
232
235
|
});
|
|
233
236
|
|
|
234
|
-
function
|
|
237
|
+
function itConfiguresCodeCoverage() {
|
|
238
|
+
test('configures ".tsx" rules to use coverage-istanbul-loader', () => {
|
|
239
|
+
expect(subject().module?.rules).toEqual(
|
|
240
|
+
expect.arrayContaining([
|
|
241
|
+
expect.objectContaining({
|
|
242
|
+
test: /\.tsx?$/,
|
|
243
|
+
use: expect.arrayContaining(['@jsdevtools/coverage-istanbul-loader']),
|
|
244
|
+
}),
|
|
245
|
+
])
|
|
246
|
+
);
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
function itConfiguresESBuildLoader() {
|
|
235
250
|
test('configures ".tsx" rules to use esbuild-loader', () => {
|
|
236
251
|
expect(subject().module?.rules).toContainEqual({
|
|
237
252
|
test: /\.tsx?$/,
|
|
@@ -265,16 +280,26 @@ describe(`[startup] ${createWebpackConfig.name}`, () => {
|
|
|
265
280
|
expect(subject().plugins).toContainEqual(new ProvidePlugin({ React: 'react' }));
|
|
266
281
|
});
|
|
267
282
|
}
|
|
283
|
+
describe('with --code-coverage command line argument', () => {
|
|
284
|
+
beforeEach(() => Object.assign(argv, { 'code-coverage': true }));
|
|
285
|
+
itConfiguresCodeCoverage();
|
|
286
|
+
});
|
|
287
|
+
describe('with codeCoverage option', () => {
|
|
288
|
+
beforeEach(() => {
|
|
289
|
+
options = { codeCoverage: true };
|
|
290
|
+
});
|
|
291
|
+
itConfiguresCodeCoverage();
|
|
292
|
+
});
|
|
268
293
|
describe('with --esbuild command line argument', () => {
|
|
269
294
|
beforeEach(() => Object.assign(argv, { esbuild: true }));
|
|
270
|
-
|
|
295
|
+
itConfiguresESBuildLoader();
|
|
271
296
|
itConfiguresReactToLoadAutomatically();
|
|
272
297
|
});
|
|
273
298
|
describe('with esbuild option', () => {
|
|
274
299
|
beforeEach(() => {
|
|
275
300
|
options = { esbuild: true };
|
|
276
301
|
});
|
|
277
|
-
|
|
302
|
+
itConfiguresESBuildLoader();
|
|
278
303
|
itConfiguresReactToLoadAutomatically();
|
|
279
304
|
});
|
|
280
305
|
describe('with experimental-bundlers option', () => {
|
|
@@ -291,8 +316,8 @@ describe(`[startup] ${createWebpackConfig.name}`, () => {
|
|
|
291
316
|
});
|
|
292
317
|
|
|
293
318
|
test('configures ".css" rule to exclude .module.css files', () => {
|
|
294
|
-
const cssRule: any = subject().module?.rules?.find(({ test }: { test: RegExp }) =>
|
|
295
|
-
'.css'
|
|
319
|
+
const cssRule: any = subject().module?.rules?.find(({ test: regexp }: { test: RegExp }) =>
|
|
320
|
+
regexp.test('.css')
|
|
296
321
|
);
|
|
297
322
|
expect(
|
|
298
323
|
['design-system.css', 'foo.module.css', 'foo.css'].filter(path => cssRule.exclude(path))
|
|
@@ -344,6 +369,12 @@ describe(`[startup] ${createWebpackConfig.name}`, () => {
|
|
|
344
369
|
);
|
|
345
370
|
});
|
|
346
371
|
|
|
372
|
+
test('configures IgnorePlugin plugin', () => {
|
|
373
|
+
expect(subject().plugins).toContainEqual(
|
|
374
|
+
new IgnorePlugin({ checkResource: expect.any(Function) })
|
|
375
|
+
);
|
|
376
|
+
});
|
|
377
|
+
|
|
347
378
|
describe('when buildStat option is set to true', () => {
|
|
348
379
|
beforeEach(() => {
|
|
349
380
|
setOptions({ buildStat: true });
|
|
@@ -428,7 +459,10 @@ describe(`[startup] ${createWebpackConfig.name}`, () => {
|
|
|
428
459
|
});
|
|
429
460
|
|
|
430
461
|
describe('when mode is "production"', () => {
|
|
431
|
-
beforeEach(() =>
|
|
462
|
+
beforeEach(() => {
|
|
463
|
+
overrides.configuration ??= {};
|
|
464
|
+
Object.assign(overrides.configuration, { mode: 'production' });
|
|
465
|
+
});
|
|
432
466
|
|
|
433
467
|
test('changes "devtool" to "source-map"', () => {
|
|
434
468
|
expect(subject().devtool).toEqual('source-map');
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { checkResource } from '../check-resource';
|
|
2
|
+
import { isOptionalPeerDependency } from '../is-optional-peer-dependency';
|
|
3
|
+
|
|
4
|
+
jest.mock('../is-optional-peer-dependency');
|
|
5
|
+
|
|
6
|
+
describe(`${checkResource.name}`, () => {
|
|
7
|
+
let context: string;
|
|
8
|
+
let resource: string;
|
|
9
|
+
|
|
10
|
+
const subject = () => checkResource(resource, context);
|
|
11
|
+
|
|
12
|
+
function itReturns(value: boolean) {
|
|
13
|
+
test(`returns ${value}`, () => expect(subject()).toBe(value));
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
describe('when resource is not an optional peer dependency', () => {
|
|
17
|
+
beforeEach(() => jest.mocked(isOptionalPeerDependency).mockReturnValue(false));
|
|
18
|
+
|
|
19
|
+
itReturns(false);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
describe('when resource is optional peer dependency', () => {
|
|
23
|
+
beforeEach(() => jest.mocked(isOptionalPeerDependency).mockReturnValue(true));
|
|
24
|
+
|
|
25
|
+
describe('when resource is present', () => {
|
|
26
|
+
beforeEach(() => (resource = 'fs'));
|
|
27
|
+
|
|
28
|
+
itReturns(false);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
describe('when resource is not present', () => {
|
|
32
|
+
beforeEach(() => (resource = 'foo'));
|
|
33
|
+
|
|
34
|
+
itReturns(true);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
});
|
package/src/webpack/configs/plugins/ignore-plugin/__tests__/is-optional-peer-dependency.test.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { isOptionalPeerDependency } from '../is-optional-peer-dependency';
|
|
2
|
+
|
|
3
|
+
describe(`${isOptionalPeerDependency.name}`, () => {
|
|
4
|
+
const subject = (...args: Parameters<typeof isOptionalPeerDependency>) =>
|
|
5
|
+
isOptionalPeerDependency(...args);
|
|
6
|
+
|
|
7
|
+
describe.each([
|
|
8
|
+
{
|
|
9
|
+
resource: 'react-dom/client',
|
|
10
|
+
contexts: ['@servicetitan/web-components', '@servicetitan/ko-bridge'],
|
|
11
|
+
},
|
|
12
|
+
])('with dependency "$resource"', ({ resource, contexts }) => {
|
|
13
|
+
contexts.forEach(context => {
|
|
14
|
+
test(`with context ${context}, returns true`, () => {
|
|
15
|
+
expect(subject({ context, resource })).toBe(true);
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
describe.each([
|
|
21
|
+
{ resource: 'react-dom', contexts: ['@servicetitan/web-components'] },
|
|
22
|
+
{ resource: 'react-dom/client', contexts: ['@servicetitan/foo'] },
|
|
23
|
+
])('with dependency "$resource"', ({ resource, contexts }) => {
|
|
24
|
+
contexts.forEach(context => {
|
|
25
|
+
test(`with context ${context}, returns false`, () => {
|
|
26
|
+
expect(subject({ context, resource })).toBe(false);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { isOptionalPeerDependency } from './is-optional-peer-dependency';
|
|
2
|
+
|
|
3
|
+
export function checkResource(resource: string, context: string) {
|
|
4
|
+
if (isOptionalPeerDependency({ resource, context })) {
|
|
5
|
+
try {
|
|
6
|
+
require.resolve(resource);
|
|
7
|
+
} catch (e) {
|
|
8
|
+
return true;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { IgnorePlugin } from 'webpack';
|
|
2
|
+
|
|
3
|
+
import { Context, Overrides } from '../../types';
|
|
4
|
+
import { checkResource } from './check-resource';
|
|
5
|
+
|
|
6
|
+
export function ignorePlugin(_context: Context, _: Overrides) {
|
|
7
|
+
/**
|
|
8
|
+
* Ignore optional peer dependencies
|
|
9
|
+
* @see {@link: file://./../../../../../../web-components/src/render.ts}
|
|
10
|
+
* @see {@link: file://./../../../../../../ko-bridge/src/ko-binding-handlers.tsx}
|
|
11
|
+
*/
|
|
12
|
+
return new IgnorePlugin({ checkResource });
|
|
13
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './ignore-plugin';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
interface Dependency {
|
|
2
|
+
context: string;
|
|
3
|
+
resource: string;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export function isOptionalPeerDependency({ context, resource }: Dependency) {
|
|
7
|
+
return (
|
|
8
|
+
resource === 'react-dom/client' && /@servicetitan\/(ko-bridge|web-components)/.test(context)
|
|
9
|
+
);
|
|
10
|
+
}
|
|
@@ -4,6 +4,7 @@ export * from './define-exposed-dependencies-plugin';
|
|
|
4
4
|
export * from './define-web-component-name-plugin';
|
|
5
5
|
export * from './filter-warnings-plugin';
|
|
6
6
|
export * from './html-plugin';
|
|
7
|
+
export * from './ignore-plugin';
|
|
7
8
|
export * from './mini-css-extract-plugin';
|
|
8
9
|
export * from './moment-locales-plugin';
|
|
9
10
|
export * from './provide-react-plugin';
|
|
@@ -31,12 +31,13 @@ function designSystemCode() {
|
|
|
31
31
|
].join('\n');
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
function indexCode({ embed }: Context) {
|
|
34
|
+
function indexCode({ embed, isLegacyRoot }: Context) {
|
|
35
|
+
const options = `{ legacyRoot: ${isLegacyRoot} }`;
|
|
35
36
|
return [
|
|
36
37
|
`import { register } from '@servicetitan/web-components';`,
|
|
37
38
|
`import { App } from './app';`,
|
|
38
39
|
...(embed
|
|
39
|
-
? [`register(App, true);`]
|
|
40
|
-
: [`require('./design-system.css');`, `register(App, false);`]),
|
|
40
|
+
? [`register(App, true, ${options});`]
|
|
41
|
+
: [`require('./design-system.css');`, `register(App, false, ${options});`]),
|
|
41
42
|
].join('\n');
|
|
42
43
|
}
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
defineWebComponentNamePlugin,
|
|
7
7
|
filterWarningsPlugin,
|
|
8
8
|
htmlPlugin,
|
|
9
|
+
ignorePlugin,
|
|
9
10
|
miniCssExtractPlugin,
|
|
10
11
|
momentLocalesPlugin,
|
|
11
12
|
provideReactPlugin,
|
|
@@ -25,6 +26,7 @@ export function pluginsConfig(context: Context, overrides: Overrides): Result {
|
|
|
25
26
|
defineWebComponentNamePlugin,
|
|
26
27
|
filterWarningsPlugin,
|
|
27
28
|
htmlPlugin,
|
|
29
|
+
ignorePlugin,
|
|
28
30
|
miniCssExtractPlugin,
|
|
29
31
|
momentLocalesPlugin,
|
|
30
32
|
tsCheckerPlugin,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { RuleSetRule } from 'webpack';
|
|
1
|
+
import { RuleSetRule, RuleSetUseItem } from 'webpack';
|
|
2
2
|
import { Context } from '../types';
|
|
3
3
|
|
|
4
|
-
export function tsxRules({ esbuild, experimentalBundlers }: Context): RuleSetRule[] {
|
|
4
|
+
export function tsxRules({ codeCoverage, esbuild, experimentalBundlers }: Context): RuleSetRule[] {
|
|
5
5
|
let loader: Record<string, any> = {
|
|
6
6
|
loader: 'ts-loader',
|
|
7
7
|
options: { transpileOnly: true },
|
|
@@ -34,11 +34,15 @@ export function tsxRules({ esbuild, experimentalBundlers }: Context): RuleSetRul
|
|
|
34
34
|
};
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
37
|
+
const rule = {
|
|
38
|
+
test: /\.tsx?$/,
|
|
39
|
+
exclude: /node_modules/,
|
|
40
|
+
use: [loader] as RuleSetUseItem[],
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
if (codeCoverage) {
|
|
44
|
+
rule.use.unshift('@jsdevtools/coverage-istanbul-loader');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return [rule];
|
|
44
48
|
}
|
|
@@ -3,11 +3,13 @@ import { Options } from '../types';
|
|
|
3
3
|
export { Overrides } from '../types';
|
|
4
4
|
|
|
5
5
|
export interface Context extends Options {
|
|
6
|
+
codeCoverage: boolean;
|
|
6
7
|
destination: string;
|
|
7
8
|
esbuild: boolean;
|
|
8
9
|
experimentalBundlers: boolean;
|
|
9
10
|
isCustomStyleRules: boolean;
|
|
10
11
|
isExposeSharedDependencies: boolean;
|
|
12
|
+
isLegacyRoot: boolean;
|
|
11
13
|
isProduction: boolean;
|
|
12
14
|
isWebComponent: boolean;
|
|
13
15
|
packageData: ReturnType<typeof getPackageData>;
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
getPackageData,
|
|
9
9
|
isCustomStyleRules,
|
|
10
10
|
isExposeSharedDependencies,
|
|
11
|
+
isLegacyRoot,
|
|
11
12
|
isWebComponent,
|
|
12
13
|
loadSharedDependencies,
|
|
13
14
|
log,
|
|
@@ -41,12 +42,14 @@ export function createWebpackConfig(overrides: Overrides, options: Options = {})
|
|
|
41
42
|
);
|
|
42
43
|
|
|
43
44
|
const context: Context = {
|
|
45
|
+
codeCoverage: options.codeCoverage ?? !!(argv as Arguments)['code-coverage'],
|
|
44
46
|
destination,
|
|
45
|
-
esbuild: options.esbuild ?? !!(argv as Arguments).esbuild
|
|
47
|
+
esbuild: options.esbuild ?? !!(argv as Arguments).esbuild,
|
|
46
48
|
experimentalBundlers:
|
|
47
|
-
options.experimentalBundlers ?? !!(argv as Arguments)['experimental-bundlers']
|
|
49
|
+
options.experimentalBundlers ?? !!(argv as Arguments)['experimental-bundlers'],
|
|
48
50
|
isCustomStyleRules: isCustomStyleRules(),
|
|
49
51
|
isExposeSharedDependencies: isExposeSharedDependencies(),
|
|
52
|
+
isLegacyRoot: isLegacyRoot(),
|
|
50
53
|
isProduction: configuration.mode === 'production',
|
|
51
54
|
isWebComponent: isWebComponent(),
|
|
52
55
|
name: '',
|
package/src/webpack/types.ts
CHANGED
|
@@ -4,11 +4,12 @@ import { PluginOptions as MiniCssExtractPlugInOptions } from 'mini-css-extract-p
|
|
|
4
4
|
import { Configuration } from 'webpack';
|
|
5
5
|
|
|
6
6
|
export interface Options {
|
|
7
|
-
embed?: boolean;
|
|
8
7
|
buildStat?: boolean;
|
|
9
|
-
|
|
8
|
+
codeCoverage?: boolean;
|
|
9
|
+
embed?: boolean;
|
|
10
10
|
esbuild?: boolean;
|
|
11
11
|
experimentalBundlers?: boolean;
|
|
12
|
+
name?: string;
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
export interface Overrides {
|
package/template/package.json
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
"description": "Multi-package web application template",
|
|
5
5
|
"private": true,
|
|
6
6
|
"engines": {
|
|
7
|
-
"node": ">=
|
|
8
|
-
"npm": ">=
|
|
7
|
+
"node": ">=18",
|
|
8
|
+
"npm": ">=10"
|
|
9
9
|
},
|
|
10
10
|
"scripts": {
|
|
11
11
|
"bootstrap": "npx --yes @servicetitan/startup install",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"test": "startup test"
|
|
18
18
|
},
|
|
19
19
|
"devDependencies": {
|
|
20
|
-
"@servicetitan/startup": ">=22.
|
|
20
|
+
"@servicetitan/startup": ">=22.21.0"
|
|
21
21
|
},
|
|
22
22
|
"workspaces": [
|
|
23
23
|
"packages/*"
|
|
@@ -7,12 +7,18 @@
|
|
|
7
7
|
"typings": "./dist/index.d.ts",
|
|
8
8
|
"scripts": {},
|
|
9
9
|
"dependencies": {
|
|
10
|
-
"@servicetitan/design-system": "^
|
|
11
|
-
"@servicetitan/
|
|
12
|
-
"@servicetitan/
|
|
10
|
+
"@servicetitan/design-system": "^13.4.3",
|
|
11
|
+
"@servicetitan/hash-browser-router": "^23.1.0",
|
|
12
|
+
"@servicetitan/link-item": "^25.9.0",
|
|
13
|
+
"@servicetitan/log-service": "^23.1.0",
|
|
14
|
+
"@servicetitan/react-ioc": "^23.1.0",
|
|
15
|
+
"@servicetitan/web-components": "^23.1.0",
|
|
16
|
+
"axios": "^0.27.2",
|
|
13
17
|
"feature-a": "^0.0.0",
|
|
14
18
|
"feature-b": "^0.0.0",
|
|
15
19
|
"feature-c": "^0.0.0",
|
|
20
|
+
"history": "~4.10.1",
|
|
21
|
+
"mobx": "~6.10.0",
|
|
16
22
|
"react": "^17.0.2",
|
|
17
23
|
"react-dom": "^17.0.2",
|
|
18
24
|
"react-router-dom": "^5.3.0"
|
|
@@ -18,7 +18,7 @@ describe(`${App.name}`, () => {
|
|
|
18
18
|
|
|
19
19
|
const routes = Object.entries({
|
|
20
20
|
MainPage: '/',
|
|
21
|
-
SecondPage: '
|
|
21
|
+
SecondPage: '/#/second-page',
|
|
22
22
|
}).map(([component, path]) => ({ component, path }));
|
|
23
23
|
|
|
24
24
|
describe.each(routes)('when location is $path', ({ component, path }) => {
|