@servicetitan/startup 22.21.0 → 23.1.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 (73) hide show
  1. package/dist/cli/commands/build.d.ts +1 -0
  2. package/dist/cli/commands/build.d.ts.map +1 -1
  3. package/dist/cli/commands/build.js +1 -0
  4. package/dist/cli/commands/build.js.map +1 -1
  5. package/dist/cli/commands/bundle-package.d.ts +1 -0
  6. package/dist/cli/commands/bundle-package.d.ts.map +1 -1
  7. package/dist/cli/commands/bundle-package.js +1 -0
  8. package/dist/cli/commands/bundle-package.js.map +1 -1
  9. package/dist/cli/commands/init.d.ts +8 -1
  10. package/dist/cli/commands/init.d.ts.map +1 -1
  11. package/dist/cli/commands/init.js +25 -3
  12. package/dist/cli/commands/init.js.map +1 -1
  13. package/dist/cli/commands/start.d.ts +1 -0
  14. package/dist/cli/commands/start.d.ts.map +1 -1
  15. package/dist/cli/commands/start.js +1 -0
  16. package/dist/cli/commands/start.js.map +1 -1
  17. package/dist/cli/utils/bundle.d.ts +1 -0
  18. package/dist/cli/utils/bundle.d.ts.map +1 -1
  19. package/dist/cli/utils/bundle.js +11 -5
  20. package/dist/cli/utils/bundle.js.map +1 -1
  21. package/dist/cli/utils/compile-sass.d.ts +1 -1
  22. package/dist/cli/utils/compile-sass.d.ts.map +1 -1
  23. package/dist/cli/utils/compile-sass.js +13 -5
  24. package/dist/cli/utils/compile-sass.js.map +1 -1
  25. package/dist/cli/utils/tcm.d.ts.map +1 -1
  26. package/dist/cli/utils/tcm.js +14 -7
  27. package/dist/cli/utils/tcm.js.map +1 -1
  28. package/dist/utils/get-jest-config.d.ts.map +1 -1
  29. package/dist/utils/get-jest-config.js +18 -13
  30. package/dist/utils/get-jest-config.js.map +1 -1
  31. package/dist/webpack/configs/rules/tsx-rules.d.ts +1 -1
  32. package/dist/webpack/configs/rules/tsx-rules.d.ts.map +1 -1
  33. package/dist/webpack/configs/rules/tsx-rules.js +10 -8
  34. package/dist/webpack/configs/rules/tsx-rules.js.map +1 -1
  35. package/dist/webpack/configs/types.d.ts +1 -0
  36. package/dist/webpack/configs/types.d.ts.map +1 -1
  37. package/dist/webpack/create-webpack-config.d.ts.map +1 -1
  38. package/dist/webpack/create-webpack-config.js +2 -2
  39. package/dist/webpack/create-webpack-config.js.map +1 -1
  40. package/dist/webpack/types.d.ts +3 -2
  41. package/dist/webpack/types.d.ts.map +1 -1
  42. package/dist/webpack/utils/testing/normalize-errors.js +1 -1
  43. package/dist/webpack/utils/testing/normalize-errors.js.map +1 -1
  44. package/package.json +15 -13
  45. package/src/cli/commands/__tests__/build.test.ts +14 -0
  46. package/src/cli/commands/__tests__/init.test.ts +48 -17
  47. package/src/cli/commands/__tests__/start.test.ts +14 -0
  48. package/src/cli/commands/build.ts +2 -0
  49. package/src/cli/commands/bundle-package.ts +2 -0
  50. package/src/cli/commands/init.ts +26 -12
  51. package/src/cli/commands/start.ts +2 -0
  52. package/src/cli/utils/__tests__/tcm.test.ts +15 -6
  53. package/src/cli/utils/bundle.ts +14 -5
  54. package/src/cli/utils/compile-sass.ts +3 -7
  55. package/src/cli/utils/tcm.ts +13 -11
  56. package/src/utils/__tests__/get-jest-config.test.ts +15 -38
  57. package/src/utils/get-jest-config.ts +32 -45
  58. package/src/webpack/__tests__/create-webpack-config.test.ts +25 -3
  59. package/src/webpack/configs/rules/tsx-rules.ts +13 -9
  60. package/src/webpack/configs/types.ts +1 -0
  61. package/src/webpack/create-webpack-config.ts +3 -2
  62. package/src/webpack/loaders/expose-loader/__tests__/__snapshots__/index.test.ts.snap +14 -14
  63. package/src/webpack/types.ts +3 -2
  64. package/src/webpack/utils/testing/normalize-errors.ts +1 -1
  65. package/template/package.json +3 -3
  66. package/template/packages/application/package.json +6 -0
  67. package/template/packages/application/src/__tests__/app.test.tsx +1 -1
  68. package/template/packages/application/src/app.tsx +4 -3
  69. package/template-react18/packages/application/package.json +35 -0
  70. package/template-react18/packages/application/src/index.tsx +9 -0
  71. package/template-react18/packages/feature-a/package.json +19 -0
  72. package/template-react18/packages/feature-b/package.json +19 -0
  73. package/template-react18/packages/feature-c/package.json +19 -0
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/webpack/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iCAAiC,EAAE,MAAM,mDAAmD,CAAC;AACtG,OAAO,EAAE,OAAO,IAAI,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAC1E,OAAO,EAAE,aAAa,IAAI,2BAA2B,EAAE,MAAM,yBAAyB,CAAC;AACvF,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAExC,MAAM,WAAW,OAAO;IACpB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAClC;AAED,MAAM,WAAW,SAAS;IACtB,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,OAAO,CAAC,EAAE;QACN,0BAA0B,CAAC,EAAE,iCAAiC,CAAC;QAC/D,iBAAiB,CAAC,EAAE,wBAAwB,CAAC;QAC7C,oBAAoB,CAAC,EAAE,2BAA2B,CAAC;KACtD,CAAC;CACL"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/webpack/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iCAAiC,EAAE,MAAM,mDAAmD,CAAC;AACtG,OAAO,EAAE,OAAO,IAAI,wBAAwB,EAAE,MAAM,qBAAqB,CAAC;AAC1E,OAAO,EAAE,aAAa,IAAI,2BAA2B,EAAE,MAAM,yBAAyB,CAAC;AACvF,OAAO,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAExC,MAAM,WAAW,OAAO;IACpB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,SAAS;IACtB,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,OAAO,CAAC,EAAE;QACN,0BAA0B,CAAC,EAAE,iCAAiC,CAAC;QAC/D,iBAAiB,CAAC,EAAE,wBAAwB,CAAC;QAC7C,oBAAoB,CAAC,EAAE,2BAA2B,CAAC;KACtD,CAAC;CACL"}
@@ -12,7 +12,7 @@ function removeCWD(str) {
12
12
  return str.replace(new RegExp(cwd, 'g'), '');
13
13
  }
14
14
  function normalizeErrors(errors) {
15
- return errors.map(error => removeCWD(error.toString().split('\n').slice(0, 2).join('\n')));
15
+ return errors.map(error => removeCWD(error.message.split('\n').slice(0, 2).join('\n')));
16
16
  }
17
17
  exports.normalizeErrors = normalizeErrors;
18
18
  //# sourceMappingURL=normalize-errors.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"normalize-errors.js","sourceRoot":"","sources":["../../../../src/webpack/utils/testing/normalize-errors.ts"],"names":[],"mappings":";;;AAEA,SAAS,SAAS,CAAC,GAAW;IAC1B,MAAM,KAAK,GAAG,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC;IAC3C,IAAI,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAExB,IAAI,KAAK,EAAE;QACP,6CAA6C;QAC7C,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QAC9B,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;KACjC;IAED,OAAO,GAAG,CAAC,OAAO,CAAC,IAAI,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC;AACjD,CAAC;AAED,SAAgB,eAAe,CAAC,MAAsB;IAClD,OAAO,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAC/F,CAAC;AAFD,0CAEC"}
1
+ {"version":3,"file":"normalize-errors.js","sourceRoot":"","sources":["../../../../src/webpack/utils/testing/normalize-errors.ts"],"names":[],"mappings":";;;AAEA,SAAS,SAAS,CAAC,GAAW;IAC1B,MAAM,KAAK,GAAG,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC;IAC3C,IAAI,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;IAExB,IAAI,KAAK,EAAE;QACP,6CAA6C;QAC7C,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;QAC9B,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;KACjC;IAED,OAAO,GAAG,CAAC,OAAO,CAAC,IAAI,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC;AACjD,CAAC;AAED,SAAgB,eAAe,CAAC,MAAsB;IAClD,OAAO,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAC5F,CAAC;AAFD,0CAEC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@servicetitan/startup",
3
- "version": "22.21.0",
3
+ "version": "23.1.0",
4
4
  "description": "",
5
5
  "homepage": "https://docs.st.dev/docs/frontend/startup",
6
6
  "repository": {
@@ -16,6 +16,7 @@
16
16
  "jest",
17
17
  "src",
18
18
  "template",
19
+ "template-react18",
19
20
  "template/.gitignore",
20
21
  "template/.npmrc",
21
22
  "tsconfig",
@@ -24,7 +25,7 @@
24
25
  "bin": "./bin/index.js",
25
26
  "devDependencies": {
26
27
  "@types/cpx": "~1.5.2",
27
- "@types/eslint": "~8.44.2",
28
+ "@types/eslint": "~8.56.3",
28
29
  "@types/less": "~3.0.3",
29
30
  "@types/mini-css-extract-plugin": "~2.4.0",
30
31
  "@types/moment-locales-webpack-plugin": "~1.2.2",
@@ -35,11 +36,11 @@
35
36
  },
36
37
  "dependencies": {
37
38
  "@babel/preset-env": "~7.22.20",
38
- "@jest/core": "~27.5.1",
39
- "@jest/types": "~27.5.1",
39
+ "@jest/core": "~29.7.0",
40
+ "@jest/types": "~29.6.3",
40
41
  "@lerna/exec": "~5.6.2",
41
- "@servicetitan/eslint-config": "22.21.0",
42
- "@servicetitan/stylelint-config": "22.21.0",
42
+ "@servicetitan/eslint-config": "23.1.0",
43
+ "@servicetitan/stylelint-config": "23.1.0",
43
44
  "@swc/core": "1.3.100",
44
45
  "@types/jest": "~27.4.0",
45
46
  "chalk": "~4.1.2",
@@ -49,20 +50,21 @@
49
50
  "debug": "^4.3.4",
50
51
  "deepmerge": "~4.3.1",
51
52
  "esbuild-loader": "~4.0.2",
52
- "eslint": "~8.48.0",
53
+ "eslint": "~8.57.0",
53
54
  "execa": "~5.1.1",
54
55
  "fork-ts-checker-webpack-plugin": "~7.3.0",
55
56
  "glob": "~7.2.0",
56
57
  "html-webpack-plugin": "~5.5.3",
57
58
  "identity-obj-proxy": "~3.0.0",
58
- "jest": "~27.5.1",
59
- "jest-circus": "~27.5.1",
59
+ "jest": "~29.7.0",
60
+ "jest-circus": "~29.7.0",
61
+ "jest-environment-jsdom": "^29.7.0",
60
62
  "jest-fetch-mock": "~3.0.3",
61
63
  "lerna": "~5.6.2",
62
64
  "less": "~4.2.0",
63
65
  "less-loader": "~10.2.0",
64
66
  "less-plugin-npm-import": "~2.1.0",
65
- "memfs": "~4.6.0",
67
+ "memfs": "~4.7.7",
66
68
  "mini-css-extract-plugin": "~2.7.6",
67
69
  "moment": "^2.29.4",
68
70
  "moment-locales-webpack-plugin": "~1.2.0",
@@ -74,10 +76,10 @@
74
76
  "style-loader": "~3.3.3",
75
77
  "stylelint": "~14.16.1",
76
78
  "swc-loader": "0.2.3",
77
- "ts-jest": "~27.1.4",
79
+ "ts-jest": "~29.1.2",
78
80
  "ts-loader": "~9.4.4",
79
81
  "ts-node": "~10.9.1",
80
- "typed-css-modules": "~0.7.2",
82
+ "typed-css-modules": "~0.8.1",
81
83
  "typescript": "~4.7.4",
82
84
  "username": "~5.1.0",
83
85
  "webpack": "~5.78.0",
@@ -97,5 +99,5 @@
97
99
  "cli": {
98
100
  "webpack": false
99
101
  },
100
- "gitHead": "729b7ca4eb38c323ac538a73573cadd54a4ff839"
102
+ "gitHead": "7267270e54cdb3a2926f61feb4939d057e82adb3"
101
103
  }
@@ -73,6 +73,20 @@ describe(`[startup] ${Build.name}`, () => {
73
73
  });
74
74
  });
75
75
 
76
+ describe('with "code-coverage"', () => {
77
+ beforeEach(() => (args['code-coverage'] = true));
78
+
79
+ test('enables code-coverage', async () => {
80
+ await subject();
81
+
82
+ expect(exec).toHaveBeenCalledWith(
83
+ expect.objectContaining({
84
+ '--': ['--code-coverage'],
85
+ })
86
+ );
87
+ });
88
+ });
89
+
76
90
  describe('with "esbuild"', () => {
77
91
  beforeEach(() => (args.esbuild = true));
78
92
 
@@ -13,32 +13,63 @@ jest.mock('cpx', () => ({
13
13
  }),
14
14
  }));
15
15
 
16
- describe(`[startup] ${Init.name}`, () => {
17
- const subject = async () => new Init().execute();
18
- const gitIgnoreAlias = '.npmignore';
16
+ jest.mock('../../../utils', () => ({
17
+ log: { info: jest.fn() }, // suppress log output
18
+ }));
19
19
 
20
- test('copies template to current directory', async () => {
21
- await subject();
20
+ describe(`[startup] ${Init.name}`, () => {
21
+ let args: ConstructorParameters<typeof Init>[0];
22
22
 
23
- expect(cpx.copy).toHaveBeenCalledWith(
24
- expect.stringContaining(path.join('template', '**', '{.*,*,.*/*}')),
25
- process.cwd(),
26
- expect.anything()
27
- );
23
+ beforeEach(() => {
24
+ args = {};
25
+ vol.reset();
28
26
  });
29
27
 
30
- describe(`when ${gitIgnoreAlias} is present`, () => {
31
- const gitIgnoreContent = ['foo', 'bar', 'baz'].join('\r\n');
28
+ const subject = async () => new Init(args).execute();
29
+
30
+ function itCopies(...names: string[]) {
31
+ test.each(names)(`copies %s to current directory`, async (name: string) => {
32
+ await subject();
33
+
34
+ expect(cpx.copy).toHaveBeenCalledWith(
35
+ expect.stringContaining(path.join(name, '**', '{.*,*,.*/*}')),
36
+ process.cwd(),
37
+ expect.anything()
38
+ );
39
+ });
40
+ }
41
+
42
+ itCopies('template');
43
+
44
+ describe('when instructed to create React 18 template', () => {
45
+ beforeEach(() => (args.react18 = true));
46
+
47
+ itCopies('template', 'template-react18');
48
+ });
32
49
 
33
- beforeEach(() => vol.fromJSON({ [gitIgnoreAlias]: gitIgnoreContent }));
50
+ describe('with an output location', () => {
51
+ beforeEach(() => (args.output = 'foo/bar'));
34
52
 
35
- afterEach(() => vol.reset());
53
+ test('copies template to output location', async () => {
54
+ const mkdirSpy = jest.spyOn(fs, 'mkdirSync');
55
+ const destination = path.resolve(args.output!);
36
56
 
37
- test(`moves ${gitIgnoreAlias} -> .gitignore`, async () => {
38
57
  await subject();
39
58
 
40
- expect(fs.readFileSync('.gitignore').toString()).toBe(gitIgnoreContent);
41
- expect(fs.existsSync(gitIgnoreAlias)).toBe(false);
59
+ expect(mkdirSpy).toHaveBeenCalledWith(destination, { recursive: true });
60
+ expect(cpx.copy).toHaveBeenCalledWith(
61
+ expect.any(String),
62
+ destination,
63
+ expect.anything()
64
+ );
65
+ });
66
+
67
+ describe('when output location is a file', () => {
68
+ beforeEach(() => vol.fromJSON({ [args.output!]: '' }));
69
+
70
+ test('raises error', async () => {
71
+ await expect(subject()).rejects.toThrowError(/is not a directory/);
72
+ });
42
73
  });
43
74
  });
44
75
  });
@@ -75,6 +75,20 @@ describe(`[startup] ${Start.name}`, () => {
75
75
  });
76
76
  });
77
77
 
78
+ describe('with "code-coverage"', () => {
79
+ beforeEach(() => (args['code-coverage'] = true));
80
+
81
+ test('enables code-coverage', async () => {
82
+ await subject();
83
+
84
+ expect(exec).toHaveBeenCalledWith(
85
+ expect.objectContaining({
86
+ '--': ['--watch', '--code-coverage'],
87
+ })
88
+ );
89
+ });
90
+ });
91
+
78
92
  describe('with "esbuild"', () => {
79
93
  beforeEach(() => (args.esbuild = true));
80
94
 
@@ -6,6 +6,7 @@ import { Command, kendoUILicense } from '.';
6
6
 
7
7
  interface Args {
8
8
  'cdn-path'?: string;
9
+ 'code-coverage'?: boolean;
9
10
  'config'?: string;
10
11
  'esbuild'?: boolean;
11
12
  'experimental-bundlers'?: boolean;
@@ -51,6 +52,7 @@ export class Build implements Command {
51
52
  'stream': true,
52
53
  '--': [
53
54
  ...[this.args.config ? `--config "${this.args.config}"` : undefined],
55
+ ...[this.args['code-coverage'] ? '--code-coverage' : undefined],
54
56
  ...[this.args.esbuild ? '--esbuild' : undefined],
55
57
  ...[this.args['experimental-bundlers'] ? '--experimental-bundlers' : undefined],
56
58
  ...[this.args.stat ? '--stat' : undefined],
@@ -6,6 +6,7 @@ import { Command } from '.';
6
6
 
7
7
  interface Args {
8
8
  'config'?: string;
9
+ 'code-coverage'?: boolean;
9
10
  'stat'?: boolean;
10
11
  'watch'?: boolean;
11
12
  'esbuild'?: boolean;
@@ -29,6 +30,7 @@ export class BundlePackage implements Command {
29
30
 
30
31
  const options = {
31
32
  config,
33
+ codeCoverage: this.args['code-coverage'],
32
34
  esbuild: this.args.esbuild,
33
35
  experimentalBundlers: this.args['experimental-bundlers'],
34
36
  };
@@ -3,22 +3,36 @@ import cpx from 'cpx';
3
3
  import util from 'util';
4
4
  import path from 'path';
5
5
 
6
- import { logErrors } from '../../utils';
7
- import { Command } from '.';
6
+ import { log, logErrors } from '../../utils';
7
+ import { Command } from './';
8
+
9
+ interface Args {
10
+ react18?: boolean;
11
+ output?: string;
12
+ }
8
13
 
9
14
  export class Init implements Command {
15
+ constructor(private args: Args) {}
16
+
10
17
  @logErrors
11
18
  async execute() {
12
- await util.promisify(cpx.copy)(
13
- path.resolve(__dirname, '../../../template/**/{.*,*,.*/*}'),
14
- process.cwd()
15
- );
16
-
17
- if (fs.existsSync(path.join(process.cwd(), '.npmignore'))) {
18
- fs.renameSync(
19
- path.join(process.cwd(), '.npmignore'),
20
- path.join(process.cwd(), '.gitignore')
21
- );
19
+ const destination = path.resolve(this.args.output ?? '.');
20
+ if (!fs.existsSync(destination)) {
21
+ fs.mkdirSync(destination, { recursive: true });
22
+ } else if (!fs.lstatSync(destination).isDirectory()) {
23
+ throw new Error(`${destination} is not a directory`);
22
24
  }
25
+
26
+ await copyFiles('template', destination);
27
+
28
+ if (this.args.react18) {
29
+ await copyFiles('template-react18', destination);
30
+ }
31
+
32
+ log.info(`copied${this.args.react18 ? ' React 18' : ''} template to ${destination}`);
23
33
  }
24
34
  }
35
+
36
+ async function copyFiles(from: string, to: string) {
37
+ await util.promisify(cpx.copy)(path.resolve(__dirname, `../../../${from}/**/{.*,*,.*/*}`), to);
38
+ }
@@ -6,6 +6,7 @@ import { Command } from '.';
6
6
 
7
7
  interface Args {
8
8
  'config'?: string;
9
+ 'code-coverage'?: boolean;
9
10
  'esbuild'?: boolean;
10
11
  'experimental-bundlers'?: boolean;
11
12
  'ignore'?: string | string[];
@@ -60,6 +61,7 @@ export class Start implements Command {
60
61
  '--': [
61
62
  '--watch',
62
63
  this.args.config ? `--config "${this.args.config}"` : undefined,
64
+ this.args['code-coverage'] ? '--code-coverage' : undefined,
63
65
  this.args.esbuild ? '--esbuild' : undefined,
64
66
  this.args['experimental-bundlers']
65
67
  ? '--experimental-bundlers'
@@ -1,5 +1,8 @@
1
- import { fs, vol } from 'memfs';
1
+ /**
2
+ * @jest-environment node
3
+ */
2
4
  import chokidar from 'chokidar';
5
+ import fs from 'fs';
3
6
 
4
7
  import { getFolders, log } from '../../../utils';
5
8
  import * as sassModule from '../compile-sass';
@@ -7,26 +10,32 @@ import * as sassModule from '../compile-sass';
7
10
  import { styleExtensions } from '..';
8
11
  import { tcm, tcmWatch } from '../tcm';
9
12
 
10
- jest.mock('fs', () => fs);
11
13
  jest.mock('../../../utils', () => ({
12
14
  ...jest.requireActual('../../../utils'),
13
15
  getFolders: jest.fn(),
14
16
  log: { error: jest.fn(), info: jest.fn() }, // suppress test output
15
17
  }));
16
18
 
19
+ /**
20
+ * Note: These tests don't use memfs because the typed-css-modules package
21
+ * uses node: URLs to import the fs modules, which bypasses the memfs filesystem
22
+ * (as of memfs ~4.6.0).
23
+ */
24
+
17
25
  describe('[startup] Cli Utils', () => {
18
- const source = 'src';
26
+ const source = fs.mkdtempSync('src');
19
27
 
20
28
  beforeEach(() => {
21
29
  jest.resetAllMocks();
22
30
  jest.mocked(getFolders).mockReturnValue({ source, destination: undefined });
31
+ fs.readdirSync(source).forEach(file => fs.rmSync(`${source}/${file}`));
23
32
  });
24
33
 
25
- afterEach(() => vol.reset());
34
+ afterAll(() => fs.rmSync(source, { recursive: true }));
26
35
 
27
36
  function mockStylesModule(module: string, id = 'foo') {
28
37
  const content = `.${id} { display: none; }`;
29
- vol.fromJSON({ [`${source}/${module}`]: content });
38
+ fs.writeFileSync(`${source}/${module}`, content);
30
39
  }
31
40
 
32
41
  function expectTypeDefinitions(module: string, id = 'foo') {
@@ -78,7 +87,7 @@ describe('[startup] Cli Utils', () => {
78
87
  beforeEach(() => {
79
88
  fsWatcher = { on: jest.fn() };
80
89
  watchSpy = jest.spyOn(chokidar, 'watch').mockReturnValue(fsWatcher as any);
81
- vol.fromJSON({ [`${source}/foo.bar`]: '' });
90
+ fs.writeFileSync(`${source}/foo.bar`, '');
82
91
  });
83
92
 
84
93
  const subject = () => tcmWatch();
@@ -11,6 +11,7 @@ import { Overrides, createWebpackConfig } from '../../webpack';
11
11
 
12
12
  interface Options {
13
13
  buildStat?: boolean;
14
+ codeCoverage?: boolean;
14
15
  config?: string;
15
16
  esbuild?: boolean;
16
17
  experimentalBundlers?: boolean;
@@ -35,7 +36,7 @@ export async function bundle(options: Options = {}) {
35
36
  const mode = 'production';
36
37
  const fallback = `./${webpackProdConfigFileName}`;
37
38
  const config = readWebpackConfig({ ...options, fallback });
38
- const { buildStat, esbuild, experimentalBundlers } = options;
39
+ const { buildStat, codeCoverage, esbuild, experimentalBundlers } = options;
39
40
 
40
41
  const run = async (config: Configuration) => {
41
42
  const compiler = webpack(config);
@@ -57,7 +58,7 @@ export async function bundle(options: Options = {}) {
57
58
  process.stdout.write(stats.toString(config.stats) + '\n');
58
59
  };
59
60
 
60
- const webpackOptions = { name, buildStat, esbuild, experimentalBundlers };
61
+ const webpackOptions = { name, buildStat, codeCoverage, esbuild, experimentalBundlers };
61
62
 
62
63
  if (isWebComponent()) {
63
64
  const webpackConfig: Overrides = {
@@ -80,7 +81,7 @@ export async function bundleWatch(options: Options = {}) {
80
81
  const name = getName();
81
82
  const mode = 'development';
82
83
  const config = readWebpackConfig({ ...options, fallback: `./${webpackDevConfigFileName}` });
83
- const { esbuild, experimentalBundlers } = options;
84
+ const { codeCoverage, esbuild, experimentalBundlers } = options;
84
85
 
85
86
  const runServe = async ({ devServer = {}, ...config }: Configuration) => {
86
87
  const compiler = webpack(config);
@@ -125,13 +126,21 @@ export async function bundleWatch(options: Options = {}) {
125
126
  return Promise.all([
126
127
  run(
127
128
  createWebpackConfig(webpackConfig, {
129
+ codeCoverage,
128
130
  embed: true,
129
131
  name,
130
132
  esbuild,
131
133
  experimentalBundlers,
132
134
  })
133
135
  ),
134
- runServe(createWebpackConfig(webpackConfig, { name, esbuild, experimentalBundlers })),
136
+ runServe(
137
+ createWebpackConfig(webpackConfig, {
138
+ name,
139
+ codeCoverage,
140
+ esbuild,
141
+ experimentalBundlers,
142
+ })
143
+ ),
135
144
  ]);
136
145
  }
137
146
 
@@ -139,7 +148,7 @@ export async function bundleWatch(options: Options = {}) {
139
148
  config ??
140
149
  createWebpackConfig(
141
150
  { configuration: { mode } },
142
- { name, esbuild, experimentalBundlers }
151
+ { name, codeCoverage, esbuild, experimentalBundlers }
143
152
  )
144
153
  );
145
154
  }
@@ -1,13 +1,9 @@
1
- import fs from 'fs';
2
1
  import path from 'path';
3
2
 
4
3
  import sass from 'sass';
5
4
 
6
- export function compileSass(filePath: string) {
7
- const result = sass.renderSync({
8
- data: fs.readFileSync(filePath, 'utf8'),
9
- includePaths: [path.dirname(filePath), '../../node_modules', 'node_modules'],
10
- });
11
-
5
+ export async function compileSass(filePath: string) {
6
+ const loadPaths = [path.dirname(filePath), '../../node_modules', 'node_modules'];
7
+ const result = await sass.compileAsync(filePath, { loadPaths });
12
8
  return result.css.toString();
13
9
  }
@@ -11,10 +11,19 @@ interface RunOptions {
11
11
  watch?: boolean;
12
12
  }
13
13
 
14
+ async function compile(filePath: string) {
15
+ if (filePath.endsWith('.less')) {
16
+ return compileLess(filePath);
17
+ }
18
+ if (filePath.endsWith('.scss')) {
19
+ return compileSass(filePath);
20
+ }
21
+ }
22
+
14
23
  async function run({ watch }: RunOptions = {}) {
15
24
  const { source } = getFolders();
16
25
 
17
- const filesPattern = `${source}/**/*.module.{${styleExtensions.join()}}`;
26
+ const filesPattern = `${source}/**/*.module.{${styleExtensions.join()}}`.replace(/\\/g, '/');
18
27
 
19
28
  const creator = new DtsCreator({
20
29
  camelCase: true,
@@ -23,15 +32,8 @@ async function run({ watch }: RunOptions = {}) {
23
32
 
24
33
  const writeFile = async (filePath: string) => {
25
34
  try {
26
- const content = await creator.create(
27
- filePath,
28
- filePath.endsWith('.less')
29
- ? await compileLess(filePath)
30
- : filePath.endsWith('.scss')
31
- ? compileSass(filePath)
32
- : undefined,
33
- !!watch
34
- );
35
+ const initialContents = await compile(filePath);
36
+ const content = await creator.create(filePath, initialContents, !!watch);
35
37
  await content.writeFile();
36
38
  } catch (error) {
37
39
  log.error(String(error));
@@ -45,7 +47,7 @@ async function run({ watch }: RunOptions = {}) {
45
47
  } else {
46
48
  log.info('Watch ' + filesPattern + '...');
47
49
 
48
- const watcher = chokidar.watch([filesPattern.replace(/\\/g, '/')], { ignoreInitial: true });
50
+ const watcher = chokidar.watch([filesPattern], { ignoreInitial: true });
49
51
  watcher.on('add', writeFile);
50
52
  watcher.on('change', writeFile);
51
53
 
@@ -13,23 +13,22 @@ jest.mock('../get-configuration', () => ({
13
13
 
14
14
  describe('[startup] Utils', () => {
15
15
  const destinationFolders = ['dist'];
16
- const defaultConfig = {
16
+ const defaultConfig: Record<string, any> = {
17
17
  verbose: true,
18
18
  testEnvironment: 'jsdom',
19
19
  testRunner: 'jest-circus/runner',
20
- transformIgnorePatterns: ['node_modules/(?!@servicetitan/)'],
20
+ transformIgnorePatterns: ['node_modules/(?!(@servicetitan|@react-hook|nanoid|axios)/)'],
21
21
  modulePathIgnorePatterns: ['<rootDir>/.*/__mocks__'],
22
22
  transform: {
23
23
  '^.+\\.jsx?$': [
24
24
  'babel-jest',
25
25
  { presets: [['@babel/preset-env', { targets: { node: 'current' } }]] },
26
26
  ],
27
- '^.+\\.tsx?$': 'ts-jest',
27
+ '^.+\\.tsx?$': ['ts-jest', { tsconfig: './tsconfig.test.json' }],
28
28
  },
29
29
  moduleNameMapper: {
30
30
  '\\.(css|scss|less|png|svg|jpg|jpeg|gif|woff|woff2|eot|ttf|otf)$': 'identity-obj-proxy',
31
31
  },
32
- globals: { 'ts-jest': { tsconfig: './tsconfig.test.json' } },
33
32
  testPathIgnorePatterns: ['\\.yalc', ...destinationFolders],
34
33
  setupFiles: [expect.stringContaining(path.join('jest', 'setup.js'))],
35
34
  coveragePathIgnorePatterns: ['^.+\\.d\\.ts$'],
@@ -73,25 +72,6 @@ describe('[startup] Utils', () => {
73
72
  }
74
73
  );
75
74
 
76
- describe.each(['globals'])(
77
- 'with custom "%s"',
78
- (option: keyof Pick<typeof defaultConfig, 'globals'>) => {
79
- const customValue = { foo: 'bar' };
80
-
81
- beforeEach(() => {
82
- jest.mocked(getJestConfiguration).mockReturnValue({ [option]: customValue });
83
- });
84
-
85
- test(`merges custom value with "${option}"`, () => {
86
- expect(subject()).toEqual(
87
- expect.objectContaining({
88
- [option]: { ...defaultConfig[option], ...customValue },
89
- })
90
- );
91
- });
92
- }
93
- );
94
-
95
75
  describe('with other custom options', () => {
96
76
  const customOptions = { resetMocks: true, testEnvironment: 'node' };
97
77
 
@@ -104,31 +84,28 @@ describe('[startup] Utils', () => {
104
84
  });
105
85
 
106
86
  describe(`${getJestConfigCLI.name}`, () => {
107
- let args: Config.Argv;
87
+ // eslint-disable-next-line @typescript-eslint/naming-convention
88
+ const args: Config.Argv = { _: [], $0: '', runInBand: true };
89
+ const globals = { foo: 'bar' };
108
90
 
109
91
  const subject = () => getJestConfigCLI(args);
110
92
 
111
93
  beforeEach(() => {
112
- const commandLineOptions: Omit<Config.Argv, '_' | '$0'> = { runInBand: true };
113
- // eslint-disable-next-line @typescript-eslint/naming-convention
114
- args = { _: [], $0: '', ...commandLineOptions };
94
+ jest.mocked(getJestConfiguration).mockReturnValue({ globals });
115
95
  });
116
96
 
117
- const stringified: (keyof typeof defaultConfig)[] = [
118
- 'globals',
119
- 'moduleNameMapper',
120
- 'transform',
121
- ];
97
+ const stringified = ['collectCoverageFrom', 'moduleNameMapper', 'transform'].reduce(
98
+ (result, key) => ({ ...result, [key]: JSON.stringify(defaultConfig[key]) }),
99
+ { globals: JSON.stringify(globals) }
100
+ );
122
101
 
123
- test(`merges command line args with default config and stringifies "${stringified.join(
124
- '", "'
125
- )}"`, () => {
102
+ test(`merges command line args with default config and stringifies "${Object.keys(
103
+ stringified
104
+ ).join('", "')}"`, () => {
126
105
  expect(subject()).toEqual({
127
106
  ...defaultConfig,
128
107
  ...args,
129
- ...Object.fromEntries(
130
- stringified.map(option => [option, JSON.stringify(defaultConfig[option])])
131
- ),
108
+ ...stringified,
132
109
  });
133
110
  });
134
111
  });