@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.
Files changed (112) hide show
  1. package/dist/cli/__mocks__/create-package.d.ts +3 -0
  2. package/dist/cli/__mocks__/create-package.d.ts.map +1 -0
  3. package/dist/cli/__mocks__/create-package.js +9 -0
  4. package/dist/cli/__mocks__/create-package.js.map +1 -0
  5. package/dist/cli/__mocks__/index.d.ts +2 -0
  6. package/dist/cli/__mocks__/index.d.ts.map +1 -0
  7. package/dist/cli/__mocks__/index.js +18 -0
  8. package/dist/cli/__mocks__/index.js.map +1 -0
  9. package/dist/cli/commands/eslint.d.ts +12 -0
  10. package/dist/cli/commands/eslint.d.ts.map +1 -0
  11. package/dist/cli/commands/eslint.js +47 -0
  12. package/dist/cli/commands/eslint.js.map +1 -0
  13. package/dist/cli/commands/index.d.ts +1 -0
  14. package/dist/cli/commands/index.d.ts.map +1 -1
  15. package/dist/cli/commands/index.js +1 -0
  16. package/dist/cli/commands/index.js.map +1 -1
  17. package/dist/cli/commands/lint.d.ts +5 -0
  18. package/dist/cli/commands/lint.d.ts.map +1 -1
  19. package/dist/cli/commands/lint.js +22 -30
  20. package/dist/cli/commands/lint.js.map +1 -1
  21. package/dist/cli/index.js +20 -6
  22. package/dist/cli/index.js.map +1 -1
  23. package/dist/cli/utils/eslint.d.ts +7 -0
  24. package/dist/cli/utils/eslint.d.ts.map +1 -0
  25. package/dist/cli/utils/eslint.js +57 -0
  26. package/dist/cli/utils/eslint.js.map +1 -0
  27. package/dist/cli/utils/index.d.ts +2 -0
  28. package/dist/cli/utils/index.d.ts.map +1 -1
  29. package/dist/cli/utils/index.js +2 -0
  30. package/dist/cli/utils/index.js.map +1 -1
  31. package/dist/cli/utils/set-node-options.d.ts +7 -0
  32. package/dist/cli/utils/set-node-options.d.ts.map +1 -0
  33. package/dist/cli/utils/set-node-options.js +46 -0
  34. package/dist/cli/utils/set-node-options.js.map +1 -0
  35. package/dist/cli/utils/tcm.d.ts.map +1 -1
  36. package/dist/cli/utils/tcm.js +3 -1
  37. package/dist/cli/utils/tcm.js.map +1 -1
  38. package/dist/utils/get-configuration.d.ts +5 -1
  39. package/dist/utils/get-configuration.d.ts.map +1 -1
  40. package/dist/utils/get-configuration.js +2 -2
  41. package/dist/utils/get-configuration.js.map +1 -1
  42. package/dist/utils/get-jest-config.d.ts +1 -1
  43. package/dist/utils/get-jest-config.d.ts.map +1 -1
  44. package/dist/utils/get-jest-config.js +8 -6
  45. package/dist/utils/get-jest-config.js.map +1 -1
  46. package/dist/utils/get-package-data.js +3 -3
  47. package/dist/utils/get-package-data.js.map +1 -1
  48. package/dist/utils/get-package-name.d.ts.map +1 -1
  49. package/dist/utils/get-package-name.js +2 -2
  50. package/dist/utils/get-package-name.js.map +1 -1
  51. package/dist/utils/get-packages.js +8 -7
  52. package/dist/utils/get-packages.js.map +1 -1
  53. package/dist/webpack/shared.config.d.ts.map +1 -1
  54. package/dist/webpack/shared.config.js +1 -0
  55. package/dist/webpack/shared.config.js.map +1 -1
  56. package/package.json +4 -4
  57. package/src/cli/__mocks__/create-package.ts +12 -0
  58. package/src/cli/__mocks__/index.ts +1 -0
  59. package/src/cli/commands/__tests__/build.test.ts +135 -0
  60. package/src/cli/commands/__tests__/bundle-package.test.ts +72 -0
  61. package/src/cli/commands/__tests__/eslint.test.ts +19 -0
  62. package/src/cli/commands/__tests__/init.test.ts +42 -0
  63. package/src/cli/commands/__tests__/lint.test.ts +114 -0
  64. package/src/cli/commands/__tests__/mfe-package-clean.test.ts +193 -0
  65. package/src/cli/commands/__tests__/mfe-package-publish.test.ts +192 -0
  66. package/src/cli/commands/__tests__/mfe-publish.test.ts +172 -0
  67. package/src/cli/commands/__tests__/prepare-package.test.ts +75 -0
  68. package/src/cli/commands/__tests__/start.test.ts +111 -0
  69. package/src/cli/commands/__tests__/styles-check.test.ts +119 -0
  70. package/src/cli/commands/__tests__/tests.test.ts +43 -0
  71. package/src/cli/commands/eslint.ts +19 -0
  72. package/src/cli/commands/index.ts +1 -0
  73. package/src/cli/commands/lint.ts +29 -42
  74. package/src/cli/index.ts +17 -5
  75. package/src/cli/utils/__tests__/assets-copy.test.ts +48 -52
  76. package/src/cli/utils/__tests__/bundle.test.ts +361 -0
  77. package/src/cli/utils/__tests__/cli-git.test.ts +28 -0
  78. package/src/cli/utils/__tests__/cli-npm.test.ts +166 -0
  79. package/src/cli/utils/__tests__/cli-os.test.ts +103 -0
  80. package/src/cli/utils/__tests__/eslint.test.ts +91 -0
  81. package/src/cli/utils/__tests__/get-module-type.test.ts +56 -0
  82. package/src/cli/utils/__tests__/is-module-installed.test.ts +24 -0
  83. package/src/cli/utils/__tests__/set-node-options.test.ts +131 -0
  84. package/src/cli/utils/__tests__/styles-copy.test.ts +48 -44
  85. package/src/cli/utils/__tests__/tcm.test.ts +101 -192
  86. package/src/cli/utils/__tests__/tsc.test.ts +85 -0
  87. package/src/cli/utils/eslint.ts +50 -0
  88. package/src/cli/utils/index.ts +2 -0
  89. package/src/cli/utils/set-node-options.ts +45 -0
  90. package/src/cli/utils/tcm.ts +3 -1
  91. package/src/utils/__tests__/get-configuration.test.ts +215 -16
  92. package/src/utils/__tests__/get-destination-folders.test.ts +65 -0
  93. package/src/utils/__tests__/get-jest-config.test.ts +134 -0
  94. package/src/utils/__tests__/get-package-data.test.ts +90 -0
  95. package/src/utils/__tests__/get-packages.test.ts +165 -0
  96. package/src/utils/__tests__/get-tsconfig.test.ts +48 -0
  97. package/src/utils/__tests__/log.test.ts +123 -0
  98. package/src/utils/__tests__/to-array.test.ts +19 -0
  99. package/src/utils/get-configuration.ts +8 -3
  100. package/src/utils/get-jest-config.ts +3 -1
  101. package/src/utils/get-package-data.ts +1 -1
  102. package/src/utils/get-package-name.ts +1 -2
  103. package/src/utils/get-packages.ts +2 -2
  104. package/src/webpack/shared.config.ts +1 -0
  105. package/template/package.json +8 -1
  106. package/template/packages/application/package.json +5 -0
  107. package/template/packages/application/src/__tests__/app.test.tsx +33 -0
  108. package/template/packages/application/src/app.tsx +9 -6
  109. package/template/packages/application/src/main-page.tsx +5 -0
  110. package/template/packages/application/src/second-page.tsx +5 -0
  111. package/template/setupTests.ts +27 -0
  112. 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
+ }
@@ -1,5 +1,6 @@
1
1
  export * from './build';
2
2
  export * from './bundle-package';
3
+ export * from './eslint';
3
4
  export * from './init';
4
5
  export * from './install';
5
6
  export * from './kendo-ui-license';
@@ -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 = [...this.args._];
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 { disabled, ...config } = getESLintConfiguration();
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 eslint = new ESLint({
50
- extensions: ['.ts', '.tsx'],
51
- baseConfig: {
52
- ignorePatterns: [
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 formatter = await eslint.loadFormatter('stylish');
76
-
77
- process.stdout.write(formatter.format(results) as string);
56
+ const packages = getPackages({ scope, ignore });
57
+ const args = fix ? ['--fix'] : [];
78
58
 
79
- if (ESLint.getErrorResults(results).length) {
80
- process.exitCode = 1;
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 = this.paths.reduce((result, path) => {
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: this.args.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({ scope: this.args.scope, ignore: this.args.ignore });
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
- // eslint-disable-next-line @typescript-eslint/naming-convention
75
- new (getCommand(argvSync._[0].toString()))({ ...argvSync, _: argvSync._.slice(1) })
76
- .execute()
77
- .catch(() => {
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
- jest.mock('../../../utils/get-folders');
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
- test('assetsCopy saves structure', async () => {
20
- mock({
21
- src: {
22
- '1.jpg': '1.jpg',
23
- 'a': {
24
- '2.jpg': '2.jpg',
25
- },
26
- 'b': {
27
- c: {
28
- '3.jpg': '3.jpg',
29
- '4.jpg': '4.jpg',
30
- '5.jpg': '5.jpg',
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
- test('assetsCopy copies only allowed files', async () => {
46
- mock({
47
- src: {
48
- '1.jpg': '1.jpg',
49
- '2.tsx': '2.tsx',
50
- '3.png': '3.png',
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
  });