@servicetitan/startup 29.0.0 → 30.0.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/bin/index.js +1 -1
- package/dist/cli/commands/convert-eslint-config.d.ts +21 -0
- package/dist/cli/commands/convert-eslint-config.d.ts.map +1 -0
- package/dist/cli/commands/convert-eslint-config.js +235 -0
- package/dist/cli/commands/convert-eslint-config.js.map +1 -0
- package/dist/cli/commands/get-command.d.ts.map +1 -1
- package/dist/cli/commands/get-command.js +6 -0
- package/dist/cli/commands/get-command.js.map +1 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +4 -3
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/lint.d.ts +1 -1
- package/dist/cli/commands/lint.js +2 -2
- package/dist/cli/commands/run-task.d.ts +13 -0
- package/dist/cli/commands/run-task.d.ts.map +1 -0
- package/dist/cli/commands/run-task.js +53 -0
- package/dist/cli/commands/run-task.js.map +1 -0
- package/dist/cli/index.js +13 -4
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/tasks/cli-task.d.ts +16 -0
- package/dist/cli/tasks/cli-task.d.ts.map +1 -0
- package/dist/cli/tasks/cli-task.js +58 -0
- package/dist/cli/tasks/cli-task.js.map +1 -0
- package/dist/cli/tasks/swc-compile-package.d.ts +12 -0
- package/dist/cli/tasks/swc-compile-package.d.ts.map +1 -0
- package/dist/cli/tasks/swc-compile-package.js +66 -0
- package/dist/cli/tasks/swc-compile-package.js.map +1 -0
- package/dist/cli/tasks/task.d.ts +42 -0
- package/dist/cli/tasks/task.d.ts.map +1 -0
- package/dist/cli/tasks/task.js +113 -0
- package/dist/cli/tasks/task.js.map +1 -0
- package/dist/cli/tasks/tsc-compile-package.d.ts +12 -0
- package/dist/cli/tasks/tsc-compile-package.d.ts.map +1 -0
- package/dist/cli/tasks/tsc-compile-package.js +42 -0
- package/dist/cli/tasks/tsc-compile-package.js.map +1 -0
- package/dist/cli/tasks/tsc-compile.d.ts +16 -0
- package/dist/cli/tasks/tsc-compile.d.ts.map +1 -0
- package/dist/cli/tasks/tsc-compile.js +48 -0
- package/dist/cli/tasks/tsc-compile.js.map +1 -0
- package/dist/cli/utils/bundle.js +1 -1
- package/dist/cli/utils/bundle.js.map +1 -1
- package/dist/cli/utils/cli-os.js +2 -2
- package/dist/cli/utils/cli-os.js.map +1 -1
- package/dist/cli/utils/eslint.d.ts.map +1 -1
- package/dist/cli/utils/eslint.js +13 -12
- package/dist/cli/utils/eslint.js.map +1 -1
- package/dist/cli/utils/is-module-installed.js +1 -1
- package/dist/cli/utils/is-module-installed.js.map +1 -1
- package/dist/cli/utils/tsc.d.ts +3 -2
- package/dist/cli/utils/tsc.d.ts.map +1 -1
- package/dist/cli/utils/tsc.js +20 -16
- package/dist/cli/utils/tsc.js.map +1 -1
- package/dist/utils/get-configuration.d.ts +3 -1
- package/dist/utils/get-configuration.d.ts.map +1 -1
- package/dist/utils/get-configuration.js +2 -0
- package/dist/utils/get-configuration.js.map +1 -1
- package/dist/utils/get-destination-folders.d.ts.map +1 -1
- package/dist/utils/get-destination-folders.js +9 -13
- package/dist/utils/get-destination-folders.js.map +1 -1
- package/dist/utils/get-folders.js +2 -2
- package/dist/utils/get-folders.js.map +1 -1
- package/dist/utils/log.d.ts +1 -0
- package/dist/utils/log.d.ts.map +1 -1
- package/dist/utils/log.js +3 -0
- package/dist/utils/log.js.map +1 -1
- package/dist/webpack/configs/plugins/ignore-plugin/check-resource.js +1 -1
- package/dist/webpack/configs/plugins/ignore-plugin/check-resource.js.map +1 -1
- package/package.json +15 -9
- package/src/cli/commands/__tests__/convert-eslint-config.test.ts +455 -0
- package/src/cli/commands/__tests__/lint.test.ts +2 -2
- package/src/cli/commands/convert-eslint-config.ts +289 -0
- package/src/cli/commands/get-command.ts +6 -0
- package/src/cli/commands/init.ts +4 -3
- package/src/cli/commands/lint.ts +3 -3
- package/src/cli/commands/run-task.ts +41 -0
- package/src/cli/index.ts +16 -4
- package/src/cli/tasks/__tests__/cli-task.test.ts +115 -0
- package/src/cli/tasks/__tests__/swc-compile.test.ts +192 -0
- package/src/cli/tasks/__tests__/task.test.ts +88 -0
- package/src/cli/tasks/__tests__/tsc-compile-package.test.ts +72 -0
- package/src/cli/tasks/__tests__/tsc-compile.test.ts +156 -0
- package/src/cli/tasks/cli-task.ts +64 -0
- package/src/cli/tasks/swc-cli.d.ts +3 -0
- package/src/cli/tasks/swc-compile-package.ts +70 -0
- package/src/cli/tasks/task.ts +112 -0
- package/src/cli/tasks/tsc-compile-package.ts +47 -0
- package/src/cli/tasks/tsc-compile.ts +64 -0
- package/src/cli/utils/__tests__/assets-copy.test.ts +1 -1
- package/src/cli/utils/__tests__/bundle.test.ts +1 -1
- package/src/cli/utils/__tests__/cli-os.test.ts +2 -2
- package/src/cli/utils/__tests__/eslint.test.ts +37 -8
- package/src/cli/utils/__tests__/styles-copy.test.ts +1 -1
- package/src/cli/utils/__tests__/tsc.test.ts +34 -55
- package/src/cli/utils/bundle.ts +1 -1
- package/src/cli/utils/cli-os.ts +2 -2
- package/src/cli/utils/eslint.ts +16 -13
- package/src/cli/utils/is-module-installed.ts +1 -1
- package/src/cli/utils/tsc.ts +25 -20
- package/src/utils/__tests__/get-destination-folders.test.ts +1 -1
- package/src/utils/__tests__/get-folders.test.ts +6 -18
- package/src/utils/__tests__/log.test.ts +6 -0
- package/src/utils/get-configuration.ts +2 -0
- package/src/utils/get-destination-folders.ts +11 -17
- package/src/utils/get-folders.ts +2 -2
- package/src/utils/log.ts +4 -0
- package/src/webpack/configs/plugins/ignore-plugin/check-resource.ts +1 -1
|
@@ -0,0 +1,455 @@
|
|
|
1
|
+
import execa from 'execa';
|
|
2
|
+
import { fs, vol } from 'memfs';
|
|
3
|
+
import { ConvertEslintConfig } from '../convert-eslint-config';
|
|
4
|
+
|
|
5
|
+
jest.mock('fs', () => fs);
|
|
6
|
+
jest.mock('execa', () => jest.fn());
|
|
7
|
+
|
|
8
|
+
describe(`[startup] ${ConvertEslintConfig.name}`, () => {
|
|
9
|
+
const eslintignoreFile = '.eslintignore';
|
|
10
|
+
const eslintrcFile = '.eslintrc.json';
|
|
11
|
+
const outputFile = 'eslint.config.mjs';
|
|
12
|
+
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
vol.reset();
|
|
15
|
+
vol.fromJSON({ [eslintrcFile]: JSON.stringify({}) });
|
|
16
|
+
jest.clearAllMocks();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
const subject = async () => {
|
|
20
|
+
await new ConvertEslintConfig().execute();
|
|
21
|
+
return fs.readFileSync(outputFile, 'utf-8').toString();
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
function itReportsError(message: string | RegExp) {
|
|
25
|
+
test(`reports error: ${message}`, async () => {
|
|
26
|
+
jest.spyOn(process.stdout, 'write').mockImplementation(jest.fn()); // suppress error output
|
|
27
|
+
expect.assertions(1);
|
|
28
|
+
try {
|
|
29
|
+
await subject();
|
|
30
|
+
} catch (error: any) {
|
|
31
|
+
expect(error.message).toMatch(message);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function mockConfig(config: Record<string, any>) {
|
|
37
|
+
vol.fromJSON({ [eslintrcFile]: JSON.stringify(config) });
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
describe(`when ${eslintrcFile} does not exist`, () => {
|
|
41
|
+
beforeEach(() => fs.rmSync(eslintrcFile));
|
|
42
|
+
|
|
43
|
+
itReportsError(`${eslintrcFile} not found`);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
describe.each(['eslint.config.js', 'eslint.config.cjs', 'eslint.config.mjs'])(
|
|
47
|
+
'when %s already exists',
|
|
48
|
+
filename => {
|
|
49
|
+
beforeEach(() => fs.writeFileSync(filename, ''));
|
|
50
|
+
|
|
51
|
+
itReportsError(`flat config "${filename}" already exists`);
|
|
52
|
+
}
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
interface EmitOptions {
|
|
56
|
+
config: string;
|
|
57
|
+
import: { as: string; from: string };
|
|
58
|
+
name?: string;
|
|
59
|
+
files?: string[];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const extendsTestCases: Record<string, EmitOptions | null> = {
|
|
63
|
+
'@servicetitan/eslint-config/mono': {
|
|
64
|
+
import: { from: '@servicetitan/eslint-config/mono', as: 'mono' },
|
|
65
|
+
config: 'mono',
|
|
66
|
+
},
|
|
67
|
+
'@servicetitan/eslint-config/single': {
|
|
68
|
+
import: { as: 'single', from: '@servicetitan/eslint-config/single' },
|
|
69
|
+
config: 'single',
|
|
70
|
+
},
|
|
71
|
+
'plugin:chai-friendly/recommended': {
|
|
72
|
+
import: { as: 'pluginChaiFriendly', from: 'eslint-plugin-chai-friendly' },
|
|
73
|
+
config: 'pluginChaiFriendly.configs.recommendedFlat',
|
|
74
|
+
},
|
|
75
|
+
'plugin:cypress/recommended': {
|
|
76
|
+
import: { as: 'pluginCypress', from: 'eslint-plugin-cypress/flat' },
|
|
77
|
+
config: 'pluginCypress.configs.recommended',
|
|
78
|
+
},
|
|
79
|
+
'plugin:mocha/recommended': {
|
|
80
|
+
import: { as: 'pluginMocha', from: 'eslint-plugin-mocha' },
|
|
81
|
+
config: 'pluginMocha.configs.flat.recommended',
|
|
82
|
+
},
|
|
83
|
+
'plugin:storybook/recommended': {
|
|
84
|
+
import: { as: 'storybook', from: 'eslint-plugin-storybook' },
|
|
85
|
+
config: "storybook.configs['flat/recommended']",
|
|
86
|
+
},
|
|
87
|
+
'plugin:testing-library/react': {
|
|
88
|
+
import: { as: 'pluginTestingLibrary', from: 'eslint-plugin-testing-library' },
|
|
89
|
+
config: "pluginTestingLibrary.configs['flat/react']",
|
|
90
|
+
files: ['**/*.test.*'],
|
|
91
|
+
},
|
|
92
|
+
'prettier': null,
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
function itEmitsConfig(options: EmitOptions) {
|
|
96
|
+
test(`configures ${options.name ?? options.import.from}`, async () => {
|
|
97
|
+
const config = options.files?.length
|
|
98
|
+
? `{ files: ${JSON.stringify(options.files)}, ...${options.config}}`
|
|
99
|
+
: options.config;
|
|
100
|
+
|
|
101
|
+
expect(await subject()).toMatchConfig(`
|
|
102
|
+
import { defineConfig } from 'eslint/config';
|
|
103
|
+
import ${options.import.as} from '${options.import.from}';
|
|
104
|
+
|
|
105
|
+
export default defineConfig([${config}]);
|
|
106
|
+
`);
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function itIgnoresConfig() {
|
|
111
|
+
test(`ignores config`, async () => {
|
|
112
|
+
expect(await subject()).toMatchConfig(`
|
|
113
|
+
import { defineConfig } from 'eslint/config';
|
|
114
|
+
|
|
115
|
+
export default defineConfig([]);
|
|
116
|
+
`);
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
describe.each(Object.entries(extendsTestCases))(
|
|
121
|
+
'when config extends %s',
|
|
122
|
+
(name, emitOptions) => {
|
|
123
|
+
beforeEach(() => mockConfig({ extends: name }));
|
|
124
|
+
|
|
125
|
+
if (emitOptions === null) {
|
|
126
|
+
itIgnoresConfig();
|
|
127
|
+
} else {
|
|
128
|
+
itEmitsConfig(emitOptions);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
describe('when "extends" is a string', () => {
|
|
134
|
+
const config = { extends: '@servicetitan/eslint-config/mono' };
|
|
135
|
+
|
|
136
|
+
beforeEach(() => mockConfig(config));
|
|
137
|
+
|
|
138
|
+
itEmitsConfig({ name: 'successfully', ...extendsTestCases[config.extends]! });
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
describe('when "extends" config is not recognized', () => {
|
|
142
|
+
const extendsArray = ['plugin:foo/recommended', 'foobar'];
|
|
143
|
+
|
|
144
|
+
beforeEach(() => mockConfig({ extends: extendsArray }));
|
|
145
|
+
|
|
146
|
+
test('emits extends in separate block', async () => {
|
|
147
|
+
expect(await subject()).toMatchConfig(`
|
|
148
|
+
import { defineConfig } from 'eslint/config';
|
|
149
|
+
|
|
150
|
+
export default defineConfig([
|
|
151
|
+
${JSON.stringify({ extends: extendsArray })}
|
|
152
|
+
]);
|
|
153
|
+
`);
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
describe('when config contains "ignorePatterns"', () => {
|
|
158
|
+
const ignorePatterns = ['packages/api/**', ''];
|
|
159
|
+
|
|
160
|
+
beforeEach(() => mockConfig({ ignorePatterns }));
|
|
161
|
+
|
|
162
|
+
test('configures globalIgnores', async () => {
|
|
163
|
+
expect(await subject()).toMatchConfig(`
|
|
164
|
+
import { defineConfig, globalIgnores } from 'eslint/config';
|
|
165
|
+
|
|
166
|
+
export default defineConfig([globalIgnores(
|
|
167
|
+
${JSON.stringify(ignorePatterns.filter(glob => !!glob))}
|
|
168
|
+
)]);
|
|
169
|
+
`);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
describe('when pattern is not a glob', () => {
|
|
173
|
+
const pattern = '*.scss.d.ts';
|
|
174
|
+
|
|
175
|
+
beforeEach(() => mockConfig({ ignorePatterns: [pattern] }));
|
|
176
|
+
|
|
177
|
+
test('converts ignore pattern to a glob', async () => {
|
|
178
|
+
expect(await subject()).toMatchConfig(`
|
|
179
|
+
import { defineConfig, globalIgnores } from 'eslint/config';
|
|
180
|
+
|
|
181
|
+
export default defineConfig([globalIgnores(
|
|
182
|
+
${JSON.stringify([`**/${pattern}`])}
|
|
183
|
+
)]);
|
|
184
|
+
`);
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
describe('when pattern has leading slash', () => {
|
|
189
|
+
beforeEach(() => mockConfig({ ignorePatterns: ['/foo', '!/foo/bar'] }));
|
|
190
|
+
|
|
191
|
+
test('removes leading slash', async () => {
|
|
192
|
+
expect(await subject()).toMatchConfig(`
|
|
193
|
+
import { defineConfig, globalIgnores } from 'eslint/config';
|
|
194
|
+
|
|
195
|
+
export default defineConfig([globalIgnores(
|
|
196
|
+
${JSON.stringify(['foo', '!foo/bar'])}
|
|
197
|
+
)]);
|
|
198
|
+
`);
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
describe('when config contains "parserOptions"', () => {
|
|
204
|
+
const parserOptions = { project: ['packages/*/tsconfig*.json', 'tsconfig*.json'] };
|
|
205
|
+
|
|
206
|
+
beforeEach(() => mockConfig({ parserOptions }));
|
|
207
|
+
|
|
208
|
+
test('moves parserOptions to languageOptions.parserOptions', async () => {
|
|
209
|
+
expect(await subject()).toMatchConfig(`
|
|
210
|
+
import { defineConfig } from 'eslint/config';
|
|
211
|
+
|
|
212
|
+
export default defineConfig([
|
|
213
|
+
${JSON.stringify({ languageOptions: { parserOptions } })}
|
|
214
|
+
]);
|
|
215
|
+
`);
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
describe('when config contains "overrides"', () => {
|
|
220
|
+
const overrides = [
|
|
221
|
+
{
|
|
222
|
+
files: ['**/*.stories.ts{,x}'],
|
|
223
|
+
rules: { 'import/no-default-export': 'off' },
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
files: ['packages/utils/**/*'],
|
|
227
|
+
rules: { '@typescript-eslint/no-require-imports': 'off' },
|
|
228
|
+
},
|
|
229
|
+
];
|
|
230
|
+
|
|
231
|
+
beforeEach(() => mockConfig({ overrides }));
|
|
232
|
+
|
|
233
|
+
test('hoists overrides to top level', async () => {
|
|
234
|
+
expect(await subject()).toMatchConfig(`
|
|
235
|
+
import { defineConfig } from 'eslint/config';
|
|
236
|
+
|
|
237
|
+
export default defineConfig([
|
|
238
|
+
${overrides.map(item => JSON.stringify(item)).join()}
|
|
239
|
+
]);
|
|
240
|
+
`);
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
describe('when "files" pattern it not a glob', () => {
|
|
244
|
+
const pattern = '*.{test,stories}.ts{,x}';
|
|
245
|
+
|
|
246
|
+
beforeEach(() => mockConfig({ overrides: [{ files: [pattern] }] }));
|
|
247
|
+
|
|
248
|
+
test('converts pattern to a glob', async () => {
|
|
249
|
+
expect(await subject()).toMatchConfig(`
|
|
250
|
+
import { defineConfig } from 'eslint/config';
|
|
251
|
+
|
|
252
|
+
export default defineConfig([
|
|
253
|
+
${JSON.stringify({ files: [`**/${pattern}`] })}
|
|
254
|
+
]);
|
|
255
|
+
`);
|
|
256
|
+
});
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
describe('when override contains parserOptions', () => {
|
|
260
|
+
const parserOptions = {
|
|
261
|
+
project: ['./apps/**/tsconfig.json', './libraries/**/tsconfig.json'],
|
|
262
|
+
projectService: true,
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
beforeEach(() => mockConfig({ overrides: [{ parserOptions }] }));
|
|
266
|
+
|
|
267
|
+
test('moves parserOptions to language.parserOptions', async () => {
|
|
268
|
+
expect(await subject()).toMatchConfig(`
|
|
269
|
+
import { defineConfig } from 'eslint/config';
|
|
270
|
+
|
|
271
|
+
export default defineConfig([
|
|
272
|
+
${JSON.stringify({ languageOptions: { parserOptions } })}
|
|
273
|
+
]);
|
|
274
|
+
`);
|
|
275
|
+
});
|
|
276
|
+
});
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
describe('when config contains "plugins"', () => {
|
|
280
|
+
const name = 'foo-bar';
|
|
281
|
+
const camelizedName = 'fooBar';
|
|
282
|
+
|
|
283
|
+
beforeEach(() => mockConfig({ plugins: [name] }));
|
|
284
|
+
|
|
285
|
+
itEmitsConfig({
|
|
286
|
+
name: 'plugin',
|
|
287
|
+
import: { as: camelizedName, from: `eslint-plugin-${name}` },
|
|
288
|
+
config: `{ plugins: { '${name}': ${camelizedName} } }`,
|
|
289
|
+
});
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
describe('when config contains "rules"', () => {
|
|
293
|
+
const rules = {
|
|
294
|
+
'no-restricted-imports': [
|
|
295
|
+
'error',
|
|
296
|
+
{
|
|
297
|
+
paths: [
|
|
298
|
+
{
|
|
299
|
+
name: 'lodash',
|
|
300
|
+
message: 'Use precise path imports from lodash library.',
|
|
301
|
+
},
|
|
302
|
+
],
|
|
303
|
+
},
|
|
304
|
+
],
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
beforeEach(() => mockConfig({ rules }));
|
|
308
|
+
|
|
309
|
+
test('adds rules to config', async () => {
|
|
310
|
+
expect(await subject()).toMatchConfig(`
|
|
311
|
+
import { defineConfig } from 'eslint/config';
|
|
312
|
+
|
|
313
|
+
export default defineConfig([
|
|
314
|
+
${JSON.stringify({ rules })}
|
|
315
|
+
]);
|
|
316
|
+
`);
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
describe('when "rules" contains @typescript-eslint', () => {
|
|
320
|
+
const tsRules = {
|
|
321
|
+
'@typescript-eslint/no-base-to-string': 'warn',
|
|
322
|
+
'@typescript-eslint/no-redundant-type-constituents': 'warn',
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
beforeEach(() => mockConfig({ rules: { ...rules, ...tsRules } }));
|
|
326
|
+
|
|
327
|
+
test('moves @typescript-eslint rules to separate config with "files"', async () => {
|
|
328
|
+
expect(await subject()).toMatchConfig(`
|
|
329
|
+
import { defineConfig } from 'eslint/config';
|
|
330
|
+
|
|
331
|
+
export default defineConfig([
|
|
332
|
+
${JSON.stringify({ rules })},
|
|
333
|
+
${JSON.stringify({ files: ['**/*.ts{,x}'], rules: tsRules })}
|
|
334
|
+
]);
|
|
335
|
+
`);
|
|
336
|
+
});
|
|
337
|
+
});
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
describe('when config contains "settings"', () => {
|
|
341
|
+
const settings = { 'import/resolver': 'typescript' };
|
|
342
|
+
|
|
343
|
+
beforeEach(() => mockConfig({ settings }));
|
|
344
|
+
|
|
345
|
+
test('copies settings to output', async () => {
|
|
346
|
+
expect(await subject()).toMatchConfig(`
|
|
347
|
+
import { defineConfig } from 'eslint/config';
|
|
348
|
+
|
|
349
|
+
export default defineConfig([
|
|
350
|
+
${JSON.stringify({ settings })}
|
|
351
|
+
]);
|
|
352
|
+
`);
|
|
353
|
+
});
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
describe(`when directory contains ${eslintignoreFile}`, () => {
|
|
357
|
+
const lines = [
|
|
358
|
+
'coverage/',
|
|
359
|
+
'', // NOTE: Test includes blank line
|
|
360
|
+
'docusaurus/',
|
|
361
|
+
'packages/**/dist',
|
|
362
|
+
];
|
|
363
|
+
|
|
364
|
+
beforeEach(() => {
|
|
365
|
+
vol.fromJSON({ [eslintignoreFile]: lines.join('\n') });
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
test('configures globalIgnores', async () => {
|
|
369
|
+
expect(await subject()).toMatchConfig(`
|
|
370
|
+
import { defineConfig, globalIgnores } from 'eslint/config';
|
|
371
|
+
|
|
372
|
+
export default defineConfig([
|
|
373
|
+
globalIgnores(
|
|
374
|
+
${JSON.stringify(lines.filter(glob => !!glob))}
|
|
375
|
+
)
|
|
376
|
+
]);
|
|
377
|
+
`);
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
describe(`when ${eslintignoreFile} is empty`, () => {
|
|
381
|
+
beforeEach(() => {
|
|
382
|
+
vol.fromJSON({ [eslintignoreFile]: '\n \n' });
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
test('omits globalIgnores', async () => {
|
|
386
|
+
expect(await subject()).toMatchConfig(`
|
|
387
|
+
import { defineConfig } from 'eslint/config';
|
|
388
|
+
|
|
389
|
+
export default defineConfig([]);
|
|
390
|
+
`);
|
|
391
|
+
});
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
describe('when config contains "ignorePatterns"', () => {
|
|
395
|
+
const ignorePatterns = ['packages/api/**'];
|
|
396
|
+
|
|
397
|
+
beforeEach(() => mockConfig({ ignorePatterns }));
|
|
398
|
+
|
|
399
|
+
test('prepends ignorePatterns to globalIgnores', async () => {
|
|
400
|
+
expect(await subject()).toMatchConfig(`
|
|
401
|
+
import { defineConfig, globalIgnores } from 'eslint/config';
|
|
402
|
+
|
|
403
|
+
export default defineConfig([
|
|
404
|
+
globalIgnores(
|
|
405
|
+
${JSON.stringify([...ignorePatterns, ...lines].filter(glob => !!glob))}
|
|
406
|
+
)
|
|
407
|
+
]);
|
|
408
|
+
`);
|
|
409
|
+
});
|
|
410
|
+
});
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
test('prettifies the output', async () => {
|
|
414
|
+
await subject();
|
|
415
|
+
|
|
416
|
+
expect(execa).toHaveBeenCalledWith('npx', ['prettier', '-w', outputFile], {
|
|
417
|
+
stdout: 'ignore',
|
|
418
|
+
});
|
|
419
|
+
});
|
|
420
|
+
|
|
421
|
+
describe('when prettier fails', () => {
|
|
422
|
+
const exitCode = 123;
|
|
423
|
+
|
|
424
|
+
beforeEach(() => {
|
|
425
|
+
jest.mocked(execa).mockRejectedValue({ exitCode });
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
itReportsError(/prettier failed with exit code/);
|
|
429
|
+
});
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
declare global {
|
|
433
|
+
// eslint-disable-next-line @typescript-eslint/no-namespace
|
|
434
|
+
namespace jest {
|
|
435
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
436
|
+
interface Matchers<R, T = {}> {
|
|
437
|
+
toMatchConfig(value: string): CustomMatcherResult;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
expect.extend({
|
|
443
|
+
toMatchConfig(actual: string, value: string) {
|
|
444
|
+
const pass = actual.replace(/\s+/g, '') === value.replace(/\s+/g, '');
|
|
445
|
+
const message = pass
|
|
446
|
+
? () => `expected:\n${actual}\n\nnot to be:\n${unIndent(value)}`
|
|
447
|
+
: () => `expected:\n${actual}\n\nto be:\n${unIndent(value)}`;
|
|
448
|
+
return { message, pass };
|
|
449
|
+
},
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
function unIndent(text: string) {
|
|
453
|
+
const indent = /^( +)/m.exec(text.trim())?.[0].length;
|
|
454
|
+
return indent ? text.trim().replace(new RegExp(`^ {${indent}}`, 'gm'), '') : text;
|
|
455
|
+
}
|
|
@@ -68,8 +68,8 @@ describe(`[startup] ${Lint.name}`, () => {
|
|
|
68
68
|
});
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
describe('when instructed to
|
|
72
|
-
beforeEach(() => (args.
|
|
71
|
+
describe('when instructed to lint packages in parallel', () => {
|
|
72
|
+
beforeEach(() => (args.parallel = true));
|
|
73
73
|
|
|
74
74
|
itRunsLernaExec();
|
|
75
75
|
|