@servicetitan/startup 22.14.0 → 22.16.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/__mocks__/create-package.d.ts +3 -0
- package/dist/cli/__mocks__/create-package.d.ts.map +1 -0
- package/dist/cli/__mocks__/create-package.js +9 -0
- package/dist/cli/__mocks__/create-package.js.map +1 -0
- package/dist/cli/__mocks__/index.d.ts +2 -0
- package/dist/cli/__mocks__/index.d.ts.map +1 -0
- package/dist/cli/__mocks__/index.js +18 -0
- package/dist/cli/__mocks__/index.js.map +1 -0
- package/dist/cli/commands/eslint.d.ts +12 -0
- package/dist/cli/commands/eslint.d.ts.map +1 -0
- package/dist/cli/commands/eslint.js +47 -0
- package/dist/cli/commands/eslint.js.map +1 -0
- package/dist/cli/commands/index.d.ts +1 -0
- package/dist/cli/commands/index.d.ts.map +1 -1
- package/dist/cli/commands/index.js +1 -0
- package/dist/cli/commands/index.js.map +1 -1
- package/dist/cli/commands/lint.d.ts +5 -0
- package/dist/cli/commands/lint.d.ts.map +1 -1
- package/dist/cli/commands/lint.js +22 -30
- package/dist/cli/commands/lint.js.map +1 -1
- package/dist/cli/index.js +20 -6
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/utils/eslint.d.ts +7 -0
- package/dist/cli/utils/eslint.d.ts.map +1 -0
- package/dist/cli/utils/eslint.js +57 -0
- package/dist/cli/utils/eslint.js.map +1 -0
- package/dist/cli/utils/index.d.ts +2 -0
- package/dist/cli/utils/index.d.ts.map +1 -1
- package/dist/cli/utils/index.js +2 -0
- package/dist/cli/utils/index.js.map +1 -1
- package/dist/cli/utils/set-node-options.d.ts +7 -0
- package/dist/cli/utils/set-node-options.d.ts.map +1 -0
- package/dist/cli/utils/set-node-options.js +46 -0
- package/dist/cli/utils/set-node-options.js.map +1 -0
- package/dist/cli/utils/tcm.d.ts.map +1 -1
- package/dist/cli/utils/tcm.js +3 -1
- package/dist/cli/utils/tcm.js.map +1 -1
- package/dist/utils/get-configuration.d.ts +5 -1
- package/dist/utils/get-configuration.d.ts.map +1 -1
- package/dist/utils/get-configuration.js +2 -2
- package/dist/utils/get-configuration.js.map +1 -1
- package/dist/utils/get-jest-config.d.ts +1 -1
- package/dist/utils/get-jest-config.d.ts.map +1 -1
- package/dist/utils/get-jest-config.js +8 -6
- package/dist/utils/get-jest-config.js.map +1 -1
- package/dist/utils/get-package-data.js +3 -3
- package/dist/utils/get-package-data.js.map +1 -1
- package/dist/utils/get-package-name.d.ts.map +1 -1
- package/dist/utils/get-package-name.js +2 -2
- package/dist/utils/get-package-name.js.map +1 -1
- package/dist/utils/get-packages.js +8 -7
- package/dist/utils/get-packages.js.map +1 -1
- package/dist/webpack/shared.config.d.ts.map +1 -1
- package/dist/webpack/shared.config.js +1 -0
- package/dist/webpack/shared.config.js.map +1 -1
- package/package.json +4 -4
- package/src/cli/__mocks__/create-package.ts +12 -0
- package/src/cli/__mocks__/index.ts +1 -0
- package/src/cli/commands/__tests__/build.test.ts +135 -0
- package/src/cli/commands/__tests__/bundle-package.test.ts +72 -0
- package/src/cli/commands/__tests__/eslint.test.ts +19 -0
- package/src/cli/commands/__tests__/init.test.ts +42 -0
- package/src/cli/commands/__tests__/lint.test.ts +114 -0
- package/src/cli/commands/__tests__/mfe-package-clean.test.ts +193 -0
- package/src/cli/commands/__tests__/mfe-package-publish.test.ts +192 -0
- package/src/cli/commands/__tests__/mfe-publish.test.ts +172 -0
- package/src/cli/commands/__tests__/prepare-package.test.ts +75 -0
- package/src/cli/commands/__tests__/start.test.ts +111 -0
- package/src/cli/commands/__tests__/styles-check.test.ts +119 -0
- package/src/cli/commands/__tests__/tests.test.ts +43 -0
- package/src/cli/commands/eslint.ts +19 -0
- package/src/cli/commands/index.ts +1 -0
- package/src/cli/commands/lint.ts +29 -42
- package/src/cli/index.ts +17 -5
- package/src/cli/utils/__tests__/assets-copy.test.ts +48 -52
- package/src/cli/utils/__tests__/bundle.test.ts +361 -0
- package/src/cli/utils/__tests__/cli-git.test.ts +28 -0
- package/src/cli/utils/__tests__/cli-npm.test.ts +166 -0
- package/src/cli/utils/__tests__/cli-os.test.ts +103 -0
- package/src/cli/utils/__tests__/eslint.test.ts +91 -0
- package/src/cli/utils/__tests__/get-module-type.test.ts +56 -0
- package/src/cli/utils/__tests__/is-module-installed.test.ts +24 -0
- package/src/cli/utils/__tests__/set-node-options.test.ts +131 -0
- package/src/cli/utils/__tests__/styles-copy.test.ts +48 -44
- package/src/cli/utils/__tests__/tcm.test.ts +101 -192
- package/src/cli/utils/__tests__/tsc.test.ts +85 -0
- package/src/cli/utils/eslint.ts +50 -0
- package/src/cli/utils/index.ts +2 -0
- package/src/cli/utils/set-node-options.ts +45 -0
- package/src/cli/utils/tcm.ts +3 -1
- package/src/utils/__tests__/get-configuration.test.ts +215 -16
- package/src/utils/__tests__/get-destination-folders.test.ts +65 -0
- package/src/utils/__tests__/get-jest-config.test.ts +134 -0
- package/src/utils/__tests__/get-package-data.test.ts +90 -0
- package/src/utils/__tests__/get-packages.test.ts +165 -0
- package/src/utils/__tests__/get-tsconfig.test.ts +48 -0
- package/src/utils/__tests__/log.test.ts +123 -0
- package/src/utils/__tests__/to-array.test.ts +19 -0
- package/src/utils/get-configuration.ts +8 -3
- package/src/utils/get-jest-config.ts +3 -1
- package/src/utils/get-package-data.ts +1 -1
- package/src/utils/get-package-name.ts +1 -2
- package/src/utils/get-packages.ts +2 -2
- package/src/webpack/shared.config.ts +1 -0
- package/template/package.json +8 -1
- package/template/packages/application/package.json +5 -0
- package/template/packages/application/src/__tests__/app.test.tsx +33 -0
- package/template/packages/application/src/app.tsx +9 -6
- package/template/packages/application/src/main-page.tsx +5 -0
- package/template/packages/application/src/second-page.tsx +5 -0
- package/template/setupTests.ts +27 -0
- package/template/tsconfig.test.json +1 -1
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { isBundle } from '../../../utils';
|
|
2
|
+
import {
|
|
3
|
+
assetsCopy,
|
|
4
|
+
assetsCopyWatch,
|
|
5
|
+
stylesCopy,
|
|
6
|
+
stylesCopyWatch,
|
|
7
|
+
tcm,
|
|
8
|
+
tcmWatch,
|
|
9
|
+
} from '../../utils';
|
|
10
|
+
|
|
11
|
+
import { PreparePackage } from '../prepare-package';
|
|
12
|
+
|
|
13
|
+
jest.mock('../../../utils', () => ({
|
|
14
|
+
...jest.requireActual('../../../utils'),
|
|
15
|
+
isBundle: jest.fn(),
|
|
16
|
+
}));
|
|
17
|
+
|
|
18
|
+
jest.mock('../../utils', () => ({
|
|
19
|
+
...jest.requireActual('../../utils'),
|
|
20
|
+
assetsCopy: jest.fn().mockResolvedValue(undefined),
|
|
21
|
+
assetsCopyWatch: jest.fn().mockResolvedValue(undefined),
|
|
22
|
+
stylesCopy: jest.fn().mockResolvedValue(undefined),
|
|
23
|
+
stylesCopyWatch: jest.fn().mockResolvedValue(undefined),
|
|
24
|
+
tcm: jest.fn().mockResolvedValue(undefined),
|
|
25
|
+
tcmWatch: jest.fn().mockResolvedValue(undefined),
|
|
26
|
+
}));
|
|
27
|
+
|
|
28
|
+
describe(`[startup] ${PreparePackage.name}`, () => {
|
|
29
|
+
let args: ConstructorParameters<typeof PreparePackage>[0];
|
|
30
|
+
|
|
31
|
+
beforeEach(() => {
|
|
32
|
+
jest.mocked(isBundle).mockReturnValue(true);
|
|
33
|
+
args = {};
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const subject = async () => new PreparePackage(args).execute();
|
|
37
|
+
|
|
38
|
+
test('runs tcm', async () => {
|
|
39
|
+
await subject();
|
|
40
|
+
|
|
41
|
+
expect(tcm).toHaveBeenCalled();
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
describe('when package is not a bundle', () => {
|
|
45
|
+
beforeEach(() => jest.mocked(isBundle).mockReturnValue(false));
|
|
46
|
+
|
|
47
|
+
test('copies assets and styles', async () => {
|
|
48
|
+
await subject();
|
|
49
|
+
|
|
50
|
+
expect(assetsCopy).toHaveBeenCalled();
|
|
51
|
+
expect(stylesCopy).toHaveBeenCalled();
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
describe('with "watch"', () => {
|
|
56
|
+
beforeEach(() => (args.watch = true));
|
|
57
|
+
|
|
58
|
+
test('runs tcm in watch mode', async () => {
|
|
59
|
+
await subject();
|
|
60
|
+
|
|
61
|
+
expect(tcmWatch).toHaveBeenCalled();
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
describe('when package is not a bundle', () => {
|
|
65
|
+
beforeEach(() => jest.mocked(isBundle).mockReturnValue(false));
|
|
66
|
+
|
|
67
|
+
test('copies assets and styles in watch mode', async () => {
|
|
68
|
+
await subject();
|
|
69
|
+
|
|
70
|
+
expect(assetsCopyWatch).toHaveBeenCalled();
|
|
71
|
+
expect(stylesCopyWatch).toHaveBeenCalled();
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
});
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
const exec = require('@lerna/exec');
|
|
2
|
+
import { Package, PackageType, getPackages } from '../../../utils';
|
|
3
|
+
import { tsc, tscWatch } from '../../utils';
|
|
4
|
+
import { createPackage } from '../../__mocks__';
|
|
5
|
+
|
|
6
|
+
import { Start } from '../start';
|
|
7
|
+
|
|
8
|
+
jest.mock('@lerna/exec', () => jest.fn());
|
|
9
|
+
jest.mock('../../../utils', () => ({
|
|
10
|
+
...jest.requireActual('../../../utils'),
|
|
11
|
+
getPackages: jest.fn(),
|
|
12
|
+
}));
|
|
13
|
+
jest.mock('../../utils', () => ({ tsc: jest.fn(), tscWatch: jest.fn() }));
|
|
14
|
+
|
|
15
|
+
describe(`[startup] ${Start.name}`, () => {
|
|
16
|
+
let args: ConstructorParameters<typeof Start>[0];
|
|
17
|
+
let packages: Package[];
|
|
18
|
+
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
args = {};
|
|
21
|
+
packages = [];
|
|
22
|
+
jest.mocked(getPackages).mockImplementation(() => packages);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const subject = async () => new Start(args).execute();
|
|
26
|
+
|
|
27
|
+
function itPreparesThePackages() {
|
|
28
|
+
test('prepares the package, then again in watch mode', async () => {
|
|
29
|
+
await subject();
|
|
30
|
+
|
|
31
|
+
expect(exec).toHaveBeenCalledWith({
|
|
32
|
+
cmd: 'startup prepare-package',
|
|
33
|
+
scope: packages.map(({ name }) => name),
|
|
34
|
+
stream: true,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
expect(exec).toHaveBeenCalledWith({
|
|
38
|
+
'cmd': 'startup prepare-package',
|
|
39
|
+
'scope': packages.map(({ name }) => name),
|
|
40
|
+
'parallel': true,
|
|
41
|
+
'stream': true,
|
|
42
|
+
'--': ['--watch'],
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
describe('with TSC package', () => {
|
|
48
|
+
beforeEach(() => packages.push(createPackage({ type: PackageType.TSC })));
|
|
49
|
+
|
|
50
|
+
itPreparesThePackages();
|
|
51
|
+
|
|
52
|
+
test('runs tsc, then again in watch mode', async () => {
|
|
53
|
+
await subject();
|
|
54
|
+
|
|
55
|
+
expect(tsc).toHaveBeenCalledWith(packages);
|
|
56
|
+
expect(tscWatch).toHaveBeenCalledWith(packages);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
describe('with Webpack package', () => {
|
|
61
|
+
beforeEach(() => packages.push(createPackage({ type: PackageType.Webpack })));
|
|
62
|
+
|
|
63
|
+
itPreparesThePackages();
|
|
64
|
+
|
|
65
|
+
test('bundles the package in watch mode', async () => {
|
|
66
|
+
await subject();
|
|
67
|
+
|
|
68
|
+
expect(exec).toHaveBeenCalledWith({
|
|
69
|
+
'cmd': 'startup bundle-package',
|
|
70
|
+
'scope': packages.map(({ name }) => name),
|
|
71
|
+
'parallel': true,
|
|
72
|
+
'stream': true,
|
|
73
|
+
'--': ['--watch'],
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
describe('with "esbuild"', () => {
|
|
78
|
+
beforeEach(() => (args.esbuild = true));
|
|
79
|
+
|
|
80
|
+
test('enables esbuild loader', async () => {
|
|
81
|
+
await subject();
|
|
82
|
+
|
|
83
|
+
expect(exec).toHaveBeenCalledWith(
|
|
84
|
+
expect.objectContaining({
|
|
85
|
+
'--': ['--watch', '--esbuild'],
|
|
86
|
+
})
|
|
87
|
+
);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
describe('with "scope"', () => {
|
|
93
|
+
beforeEach(() => (args.scope = 'foo'));
|
|
94
|
+
|
|
95
|
+
test('selects scoped packages', async () => {
|
|
96
|
+
await subject();
|
|
97
|
+
|
|
98
|
+
expect(getPackages).toHaveBeenCalledWith({ scope: args.scope });
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
describe('with "ignore"', () => {
|
|
103
|
+
beforeEach(() => (args.ignore = 'foo'));
|
|
104
|
+
|
|
105
|
+
test('selectively ignores packages', async () => {
|
|
106
|
+
await subject();
|
|
107
|
+
|
|
108
|
+
expect(getPackages).toHaveBeenCalledWith({ ignore: args.ignore });
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
});
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import mockFS from 'mock-fs';
|
|
2
|
+
import {
|
|
3
|
+
getFolders,
|
|
4
|
+
isBundle,
|
|
5
|
+
isLegacy,
|
|
6
|
+
isStyleCheckDisabled,
|
|
7
|
+
isWebComponent,
|
|
8
|
+
log,
|
|
9
|
+
} from '../../../utils';
|
|
10
|
+
|
|
11
|
+
import { StylesCheck } from '../styles-check';
|
|
12
|
+
|
|
13
|
+
jest.mock('../../../utils', () => ({
|
|
14
|
+
...jest.requireActual('../../../utils'),
|
|
15
|
+
getFolders: jest.fn(),
|
|
16
|
+
isBundle: jest.fn(),
|
|
17
|
+
isLegacy: jest.fn(),
|
|
18
|
+
isStyleCheckDisabled: jest.fn(),
|
|
19
|
+
isWebComponent: jest.fn(),
|
|
20
|
+
log: { error: jest.fn(), info: jest.fn(), warning: jest.fn() },
|
|
21
|
+
}));
|
|
22
|
+
|
|
23
|
+
describe(`[startup] ${StylesCheck.name}`, () => {
|
|
24
|
+
const subject = async () => new StylesCheck().execute();
|
|
25
|
+
|
|
26
|
+
beforeEach(() => {
|
|
27
|
+
jest.resetAllMocks();
|
|
28
|
+
jest.mocked(isBundle).mockReturnValue(true);
|
|
29
|
+
jest.mocked(getFolders).mockReturnValue({ source: 'src', destination: 'dist' });
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
afterEach(() => mockFS.restore());
|
|
33
|
+
|
|
34
|
+
test("warns that application doesn't have design-system.css", async () => {
|
|
35
|
+
await subject();
|
|
36
|
+
|
|
37
|
+
expect(log.warning).toHaveBeenCalledWith(
|
|
38
|
+
expect.stringContaining("application doesn't have design-system.css")
|
|
39
|
+
);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
describe('with a legacy package', () => {
|
|
43
|
+
beforeEach(() => jest.mocked(isLegacy).mockReturnValue(true));
|
|
44
|
+
|
|
45
|
+
test('exits silently', async () => {
|
|
46
|
+
await subject();
|
|
47
|
+
|
|
48
|
+
expect(getFolders).not.toHaveBeenCalled();
|
|
49
|
+
expect(log.info).not.toHaveBeenCalled();
|
|
50
|
+
expect(log.warning).not.toHaveBeenCalled();
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
describe('when style check is disabled', () => {
|
|
55
|
+
beforeEach(() => jest.mocked(isStyleCheckDisabled).mockReturnValue(true));
|
|
56
|
+
|
|
57
|
+
test('logs message and does not check styles', async () => {
|
|
58
|
+
await subject();
|
|
59
|
+
|
|
60
|
+
expect(log.info).toHaveBeenCalledWith('style check is disabled');
|
|
61
|
+
expect(getFolders).not.toHaveBeenCalled();
|
|
62
|
+
expect(log.warning).not.toHaveBeenCalled();
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
describe('when files contain deprecated patterns', () => {
|
|
67
|
+
const tokenCss = "@import '~@servicetitan/tokens/core/tokens.css';";
|
|
68
|
+
const anvilFontsCss = "@import '~@servicetitan/anvil-fonts/dist/css/anvil-fonts.css';";
|
|
69
|
+
const systemMinCss = "@import '~@servicetitan/design-system/dist/system.min.css';";
|
|
70
|
+
|
|
71
|
+
beforeEach(() => {
|
|
72
|
+
mockFS({
|
|
73
|
+
src: {
|
|
74
|
+
'design-system.css': [tokenCss, anvilFontsCss, systemMinCss].join('\r\n'),
|
|
75
|
+
'foo.css': `${tokenCss}\r\n`,
|
|
76
|
+
'bar.less': `${anvilFontsCss}\r\n`,
|
|
77
|
+
'styles': { 'baz.css': `${systemMinCss}\r\n` },
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
function itReportsErrors() {
|
|
83
|
+
test('reports errors', async () => {
|
|
84
|
+
try {
|
|
85
|
+
await subject();
|
|
86
|
+
} catch (error: any) {
|
|
87
|
+
expect(error.message).toMatch(/style check error/);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const errors = jest.mocked(log.error).mock.calls[0][0].split('\n');
|
|
91
|
+
expect(errors).toEqual([
|
|
92
|
+
expect.stringMatching(/style check error/i),
|
|
93
|
+
expect.stringMatching(/style check failed/i),
|
|
94
|
+
`file bar.less contains link to "${anvilFontsCss}"`,
|
|
95
|
+
`file foo.css contains link to "${tokenCss}"`,
|
|
96
|
+
`file styles/baz.css contains link to "${systemMinCss}"`,
|
|
97
|
+
]);
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
itReportsErrors();
|
|
102
|
+
|
|
103
|
+
describe('when package is web component', () => {
|
|
104
|
+
beforeEach(() => jest.mocked(isWebComponent).mockReturnValue(true));
|
|
105
|
+
|
|
106
|
+
itReportsErrors();
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
describe('when package is a library', () => {
|
|
110
|
+
beforeEach(() => jest.mocked(isBundle).mockReturnValue(false));
|
|
111
|
+
|
|
112
|
+
test('does not report errors', async () => {
|
|
113
|
+
await subject();
|
|
114
|
+
|
|
115
|
+
expect(log.error).not.toHaveBeenCalled();
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
});
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { runCLI } from '@jest/core';
|
|
2
|
+
import { Config } from '@jest/types';
|
|
3
|
+
import { getJestConfigCLI } from '../../../utils';
|
|
4
|
+
|
|
5
|
+
import { Tests } from '../tests';
|
|
6
|
+
|
|
7
|
+
jest.mock('@jest/core', () => ({ runCLI: jest.fn() }));
|
|
8
|
+
jest.mock('../../../utils', () => ({
|
|
9
|
+
...jest.requireActual('../../../utils'),
|
|
10
|
+
getJestConfigCLI: jest.fn(),
|
|
11
|
+
}));
|
|
12
|
+
|
|
13
|
+
describe(`[startup] ${Tests.name}`, () => {
|
|
14
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
15
|
+
const args: Config.Argv = { _: ['foo'], $0: 'bar' };
|
|
16
|
+
const jestConfig: Config.Argv = { ...args, setupFilesAfterEnv: ['./setupTests.js'] };
|
|
17
|
+
|
|
18
|
+
beforeAll(() => {
|
|
19
|
+
jest.mocked(getJestConfigCLI).mockReturnValue(jestConfig);
|
|
20
|
+
jest.mocked(runCLI).mockResolvedValue({ results: { success: true } } as any);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const subject = async () => new Tests(args).execute();
|
|
24
|
+
|
|
25
|
+
test('runs Jest CLI', async () => {
|
|
26
|
+
await subject();
|
|
27
|
+
|
|
28
|
+
expect(getJestConfigCLI).toHaveBeenCalledWith(args);
|
|
29
|
+
expect(runCLI).toHaveBeenCalledWith(jestConfig, [process.cwd()]);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
describe('when the command fails', () => {
|
|
33
|
+
beforeEach(() => {
|
|
34
|
+
jest.mocked(runCLI).mockResolvedValue({ results: { success: false } } as any);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('sets process execCode to 1', async () => {
|
|
38
|
+
await subject();
|
|
39
|
+
|
|
40
|
+
expect(process.exitCode).toBe(1);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Command } from '.';
|
|
2
|
+
import { logErrors } from '../../utils';
|
|
3
|
+
import { eslint } from '../utils';
|
|
4
|
+
|
|
5
|
+
interface Args {
|
|
6
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
7
|
+
_: string[];
|
|
8
|
+
fix?: boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export class ESLintCommand implements Command {
|
|
12
|
+
constructor(private args: Args) {}
|
|
13
|
+
|
|
14
|
+
@logErrors
|
|
15
|
+
async execute(): Promise<void> {
|
|
16
|
+
const { _: paths, fix } = this.args;
|
|
17
|
+
await eslint({ fix, paths });
|
|
18
|
+
}
|
|
19
|
+
}
|
package/src/cli/commands/lint.ts
CHANGED
|
@@ -1,33 +1,37 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
2
|
|
|
3
|
-
import { ESLint } from 'eslint';
|
|
4
3
|
import stylelint from 'stylelint';
|
|
5
4
|
|
|
6
5
|
import {
|
|
7
6
|
getDestinationFolders,
|
|
8
|
-
getESLintConfiguration,
|
|
9
7
|
getPackages,
|
|
10
8
|
getStylelintConfiguration,
|
|
11
9
|
log,
|
|
12
10
|
logErrors,
|
|
13
11
|
} from '../../utils';
|
|
14
12
|
import { Command } from '.';
|
|
13
|
+
import { eslint } from '../utils';
|
|
15
14
|
|
|
16
15
|
const exec = require('@lerna/exec');
|
|
17
16
|
|
|
18
17
|
interface Args {
|
|
19
18
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
20
19
|
_: string[];
|
|
20
|
+
/** Apply fixes for issues? */
|
|
21
21
|
fix?: boolean;
|
|
22
|
+
/** Packages to lint */
|
|
22
23
|
scope?: string | string[];
|
|
24
|
+
/** Packages to skip */
|
|
23
25
|
ignore?: string | string[];
|
|
26
|
+
/** Use \@lerna/exec to run eslint separately for each package? */
|
|
27
|
+
isolated?: boolean;
|
|
24
28
|
}
|
|
25
29
|
|
|
26
30
|
export class Lint implements Command {
|
|
27
31
|
paths: string[];
|
|
28
32
|
|
|
29
33
|
constructor(private args: Args) {
|
|
30
|
-
this.paths = [...
|
|
34
|
+
this.paths = [...args._];
|
|
31
35
|
}
|
|
32
36
|
|
|
33
37
|
async execute() {
|
|
@@ -38,61 +42,42 @@ export class Lint implements Command {
|
|
|
38
42
|
|
|
39
43
|
@logErrors
|
|
40
44
|
private async eslint() {
|
|
41
|
-
const {
|
|
42
|
-
|
|
43
|
-
if (disabled) {
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
45
|
+
const { fix, ignore, isolated, scope } = this.args;
|
|
46
|
+
const { paths } = this;
|
|
46
47
|
|
|
47
48
|
log.info('Running the eslint...');
|
|
48
49
|
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
'node_modules',
|
|
54
|
-
'*.css.d.ts',
|
|
55
|
-
'*.scss.d.ts',
|
|
56
|
-
'*.less.d.ts',
|
|
57
|
-
...getDestinationFolders(),
|
|
58
|
-
],
|
|
59
|
-
},
|
|
60
|
-
fix: this.args.fix,
|
|
61
|
-
...config,
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
let files = this.paths;
|
|
65
|
-
if (!files.length) {
|
|
66
|
-
files = [process.cwd()];
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
const results = await eslint.lintFiles(files);
|
|
70
|
-
|
|
71
|
-
if (this.args.fix) {
|
|
72
|
-
await ESLint.outputFixes(results);
|
|
50
|
+
const useESLint = !isolated && !scope?.length && !ignore?.length;
|
|
51
|
+
if (this.paths.length || useESLint) {
|
|
52
|
+
await eslint({ fix, paths });
|
|
53
|
+
return;
|
|
73
54
|
}
|
|
74
55
|
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
process.stdout.write(formatter.format(results) as string);
|
|
56
|
+
const packages = getPackages({ scope, ignore });
|
|
57
|
+
const args = fix ? ['--fix'] : [];
|
|
78
58
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
59
|
+
await exec({
|
|
60
|
+
cmd: 'startup eslint',
|
|
61
|
+
scope: packages.map(({ name }) => name),
|
|
62
|
+
bail: false,
|
|
63
|
+
args,
|
|
64
|
+
});
|
|
82
65
|
}
|
|
83
66
|
|
|
84
67
|
@logErrors
|
|
85
68
|
private async stylelint() {
|
|
86
69
|
const { disabled, ...config } = getStylelintConfiguration();
|
|
87
|
-
|
|
88
70
|
if (disabled) {
|
|
89
71
|
return;
|
|
90
72
|
}
|
|
91
73
|
|
|
74
|
+
const { fix } = this.args;
|
|
75
|
+
const { paths } = this;
|
|
76
|
+
|
|
92
77
|
log.info('Running the stylelint...');
|
|
93
78
|
const allowedExtensions = ['css', 'scss', 'less'];
|
|
94
79
|
const glob = `**/*.{${allowedExtensions.join(',')}}`;
|
|
95
|
-
let files =
|
|
80
|
+
let files = paths.reduce((result, path) => {
|
|
96
81
|
const extension = path.split('.').pop();
|
|
97
82
|
if (extension) {
|
|
98
83
|
if (allowedExtensions.includes(extension.toLowerCase())) {
|
|
@@ -112,7 +97,7 @@ export class Lint implements Command {
|
|
|
112
97
|
files,
|
|
113
98
|
ignorePattern: ['node_modules', ...getDestinationFolders()],
|
|
114
99
|
formatter: 'string',
|
|
115
|
-
fix
|
|
100
|
+
fix,
|
|
116
101
|
...config,
|
|
117
102
|
});
|
|
118
103
|
|
|
@@ -125,9 +110,11 @@ export class Lint implements Command {
|
|
|
125
110
|
|
|
126
111
|
@logErrors
|
|
127
112
|
private async checkStyles() {
|
|
113
|
+
const { ignore, scope } = this.args;
|
|
114
|
+
|
|
128
115
|
log.info('Running the style check...');
|
|
129
116
|
|
|
130
|
-
const packages = getPackages({
|
|
117
|
+
const packages = getPackages({ ignore, scope });
|
|
131
118
|
|
|
132
119
|
await exec({
|
|
133
120
|
cmd: 'startup styles-check',
|
package/src/cli/index.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import execa from 'execa';
|
|
1
2
|
import { argv, Arguments } from 'yargs';
|
|
2
3
|
|
|
3
4
|
import { log } from '../utils';
|
|
@@ -5,6 +6,7 @@ import {
|
|
|
5
6
|
Build,
|
|
6
7
|
BundlePackage,
|
|
7
8
|
Command,
|
|
9
|
+
ESLintCommand,
|
|
8
10
|
Init,
|
|
9
11
|
Install,
|
|
10
12
|
KendoUILicense,
|
|
@@ -17,6 +19,7 @@ import {
|
|
|
17
19
|
StylesCheck,
|
|
18
20
|
Tests,
|
|
19
21
|
} from './commands';
|
|
22
|
+
import { setNodeOptions } from './utils';
|
|
20
23
|
|
|
21
24
|
interface Newable<T> {
|
|
22
25
|
new (...args: any[]): T;
|
|
@@ -24,6 +27,9 @@ interface Newable<T> {
|
|
|
24
27
|
|
|
25
28
|
function getCommand(name: string): Newable<Command> {
|
|
26
29
|
switch (name) {
|
|
30
|
+
case 'eslint':
|
|
31
|
+
return ESLintCommand;
|
|
32
|
+
|
|
27
33
|
case 'init':
|
|
28
34
|
return Init;
|
|
29
35
|
|
|
@@ -70,10 +76,16 @@ function getCommand(name: string): Newable<Command> {
|
|
|
70
76
|
}
|
|
71
77
|
|
|
72
78
|
const argvSync = argv as Arguments;
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
79
|
+
const name = argvSync._[0].toString();
|
|
80
|
+
const Command = getCommand(name);
|
|
81
|
+
|
|
82
|
+
if (setNodeOptions(name)) {
|
|
83
|
+
// Run command in child process with amended NODE_OPTIONS
|
|
84
|
+
execa(process.argv[0], process.argv.slice(1), { stdio: 'inherit' });
|
|
85
|
+
} else {
|
|
86
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
87
|
+
const command = new Command({ ...argvSync, _: argvSync._.slice(1) });
|
|
88
|
+
command.execute().catch(() => {
|
|
78
89
|
process.exit(1);
|
|
79
90
|
});
|
|
91
|
+
}
|
|
@@ -1,60 +1,56 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import fs from 'fs';
|
|
4
|
-
import mock from 'mock-fs';
|
|
5
|
-
import { mocked } from 'ts-jest/utils';
|
|
6
|
-
|
|
1
|
+
import cpx from 'cpx';
|
|
7
2
|
import { getFolders } from '../../../utils';
|
|
8
|
-
import { assetsCopy } from '..';
|
|
9
|
-
|
|
10
|
-
describe('[Startup] utils:assets-copy', () => {
|
|
11
|
-
beforeAll(() => {
|
|
12
|
-
mocked(getFolders).mockReturnValue({ source: 'src', destination: 'dist' });
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
afterAll(() => {
|
|
16
|
-
mock.restore();
|
|
17
|
-
});
|
|
18
3
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
4
|
+
import { assetsCopy, assetsCopyWatch } from '../assets-copy';
|
|
5
|
+
import { assetExtensions } from '../index';
|
|
6
|
+
|
|
7
|
+
jest.mock('cpx', () => ({
|
|
8
|
+
copy: jest.fn().mockImplementation((...args: any[]) => {
|
|
9
|
+
const callback = args[args.length - 1];
|
|
10
|
+
callback(null);
|
|
11
|
+
}),
|
|
12
|
+
watch: jest.fn(() => ({ on: (_eventName: string, listener: Function) => listener() })),
|
|
13
|
+
}));
|
|
14
|
+
jest.mock('../../../utils', () => ({
|
|
15
|
+
getFolders: jest.fn(),
|
|
16
|
+
log: { info: jest.fn() }, // suppress log output
|
|
17
|
+
}));
|
|
18
|
+
|
|
19
|
+
describe('[startup] Cli Utils (Assets)', () => {
|
|
20
|
+
const source = 'foo';
|
|
21
|
+
const destination = 'bar';
|
|
22
|
+
|
|
23
|
+
beforeEach(() => jest.mocked(getFolders).mockReturnValue({ source, destination }));
|
|
24
|
+
|
|
25
|
+
describe(`${assetsCopy.name}`, () => {
|
|
26
|
+
const subject = async () => assetsCopy();
|
|
27
|
+
|
|
28
|
+
test('copies assets from source to destination', async () => {
|
|
29
|
+
await subject();
|
|
30
|
+
|
|
31
|
+
expect(cpx.copy).toHaveBeenCalledWith(
|
|
32
|
+
`${source}/**/*.{${assetExtensions.join()}}`,
|
|
33
|
+
destination,
|
|
34
|
+
expect.anything()
|
|
35
|
+
);
|
|
34
36
|
});
|
|
35
|
-
|
|
36
|
-
await assetsCopy();
|
|
37
|
-
|
|
38
|
-
expect(fs.readFileSync('./dist/1.jpg', 'utf8')).toEqual('1.jpg');
|
|
39
|
-
expect(fs.readFileSync('./dist/a/2.jpg', 'utf8')).toEqual('2.jpg');
|
|
40
|
-
expect(fs.readFileSync('./dist/b/c/3.jpg', 'utf8')).toEqual('3.jpg');
|
|
41
|
-
expect(fs.readFileSync('./dist/b/c/4.jpg', 'utf8')).toEqual('4.jpg');
|
|
42
|
-
expect(fs.readFileSync('./dist/b/c/5.jpg', 'utf8')).toEqual('5.jpg');
|
|
43
37
|
});
|
|
44
38
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
39
|
+
describe(`${assetsCopyWatch.name}`, () => {
|
|
40
|
+
const subject = async () => assetsCopyWatch();
|
|
41
|
+
|
|
42
|
+
test('copies assets from source to destination in watch mode', async () => {
|
|
43
|
+
try {
|
|
44
|
+
await subject();
|
|
45
|
+
} catch (error: any) {
|
|
46
|
+
// ignore rejected promise that terminates watch()
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
expect(cpx.watch).toHaveBeenCalledWith(
|
|
50
|
+
`${source}/**/*.{${assetExtensions.join()}}`,
|
|
51
|
+
destination,
|
|
52
|
+
{ initialCopy: false }
|
|
53
|
+
);
|
|
52
54
|
});
|
|
53
|
-
|
|
54
|
-
await assetsCopy();
|
|
55
|
-
|
|
56
|
-
expect(fs.existsSync('./dist/1.jpg')).toBeTruthy();
|
|
57
|
-
expect(fs.existsSync('./dist/2.tsx')).toBeFalsy();
|
|
58
|
-
expect(fs.existsSync('./dist/3.png')).toBeTruthy();
|
|
59
55
|
});
|
|
60
56
|
});
|