From 81a5f64e054463fe791855a99cd3abf69d6d45a1 Mon Sep 17 00:00:00 2001 From: Daniel Melanson <30221758+daniel-melanson@users.noreply.github.com> Date: Tue, 28 May 2024 00:48:30 -0400 Subject: [PATCH] feat: make cli generate esm config based on `type: "module"` (#4210) Closes #4012 --- src/cli/cli.spec.ts | 84 ++++++++++++++++++++++++++++++++++++++---- src/cli/config/init.ts | 6 ++- 2 files changed, 81 insertions(+), 9 deletions(-) diff --git a/src/cli/cli.spec.ts b/src/cli/cli.spec.ts index ad0dc1b7db..0ed1abae39 100644 --- a/src/cli/cli.spec.ts +++ b/src/cli/cli.spec.ts @@ -33,12 +33,6 @@ const runCli = async ( let mockedProcess: any const FAKE_CWD = normalize('/foo/bar') const FAKE_PKG = normalize(`${FAKE_CWD}/package.json`) -fs.existsSync.mockImplementation((f) => f === FAKE_PKG) -// eslint-disable-next-line @typescript-eslint/no-explicit-any -fs.readFileSync.mockImplementation((f): any => { - if (f === FAKE_PKG) return JSON.stringify({ name: 'mock', version: '0.0.0-mock.0' }) - throw new Error('ENOENT') -}) // === test =================================================================== @@ -65,7 +59,14 @@ afterEach(() => { describe('cli', () => { it('should output usage', async () => { + fs.existsSync.mockImplementation((f) => f === FAKE_PKG) + // eslint-disable-next-line @typescript-eslint/no-explicit-any + fs.readFileSync.mockImplementation((f): any => { + if (f === FAKE_PKG) return JSON.stringify({ name: 'mock', version: '0.0.0-mock.0' }) + throw new Error('ENOENT') + }) expect.assertions(2) + await expect(runCli()).resolves.toMatchInlineSnapshot(` { "exitCode": 0, @@ -121,9 +122,17 @@ describe('config', () => { 'ts', '--babel', ] + it('should create a jest.config.json (without options)', async () => { + fs.existsSync.mockImplementation((f) => f === FAKE_PKG) + // eslint-disable-next-line @typescript-eslint/no-explicit-any + fs.readFileSync.mockImplementation((f): any => { + if (f === FAKE_PKG) return JSON.stringify({ name: 'mock', version: '0.0.0-mock.0' }) + throw new Error('ENOENT') + }) expect.assertions(2) const res = await runCli(...noOption) + expect(res).toEqual({ exitCode: 0, log: '', @@ -143,9 +152,17 @@ module.exports = { ], ]) }) + it('should create a jest.config.foo.json (with all options set)', async () => { + fs.existsSync.mockImplementation((f) => f === FAKE_PKG) + // eslint-disable-next-line @typescript-eslint/no-explicit-any + fs.readFileSync.mockImplementation((f): any => { + if (f === FAKE_PKG) return JSON.stringify({ name: 'mock', version: '0.0.0-mock.0' }) + throw new Error('ENOENT') + }) expect.assertions(2) const res = await runCli(...fullOptions, 'jest.config.foo.js') + expect(res).toEqual({ exitCode: 0, log: '', @@ -172,9 +189,17 @@ module.exports = { ], ]) }) + it('should update package.json (without options)', async () => { + fs.existsSync.mockImplementation((f) => f === FAKE_PKG) + // eslint-disable-next-line @typescript-eslint/no-explicit-any + fs.readFileSync.mockImplementation((f): any => { + if (f === FAKE_PKG) return JSON.stringify({ name: 'mock', version: '0.0.0-mock.0' }) + throw new Error('ENOENT') + }) expect.assertions(2) const res = await runCli(...noOption, 'package.json') + expect(res).toEqual({ exitCode: 0, log: '', @@ -199,8 +224,15 @@ Jest configuration written to "${normalize('/foo/bar/package.json')}". }) it('should update package.json (with all options set)', async () => { + fs.existsSync.mockImplementation((f) => f === FAKE_PKG) + // eslint-disable-next-line @typescript-eslint/no-explicit-any + fs.readFileSync.mockImplementation((f): any => { + if (f === FAKE_PKG) return JSON.stringify({ name: 'mock', version: '0.0.0-mock.0' }) + throw new Error('ENOENT') + }) expect.assertions(2) const res = await runCli(...fullOptions, 'package.json') + expect(res).toEqual({ exitCode: 0, log: '', @@ -230,8 +262,16 @@ Jest configuration written to "${normalize('/foo/bar/package.json')}". ], ]) }) + it('should output help', async () => { + fs.existsSync.mockImplementation((f) => f === FAKE_PKG) + // eslint-disable-next-line @typescript-eslint/no-explicit-any + fs.readFileSync.mockImplementation((f): any => { + if (f === FAKE_PKG) return JSON.stringify({ name: 'mock', version: '0.0.0-mock.0' }) + throw new Error('ENOENT') + }) const res = await runCli('help', noOption[0]) + expect(res).toMatchInlineSnapshot(` { "exitCode": 0, @@ -259,7 +299,37 @@ Jest configuration written to "${normalize('/foo/bar/package.json')}". } `) }) - }) // init + + it('should create jest config with type "module" package.json', async () => { + fs.existsSync.mockImplementation((f) => f === FAKE_PKG) + // eslint-disable-next-line @typescript-eslint/no-explicit-any + fs.readFileSync.mockImplementation((f): any => { + if (f === FAKE_PKG) return JSON.stringify({ name: 'mock', version: '0.0.0-mock.0', type: 'module' }) + throw new Error('ENOENT') + }) + expect.assertions(2) + const res = await runCli(...noOption) + + expect(res).toEqual({ + exitCode: 0, + log: '', + stderr: ` +Jest configuration written to "${normalize('/foo/bar/jest.config.js')}". +`, + stdout: '', + }) + expect(fs.writeFileSync.mock.calls).toEqual([ + [ + normalize('/foo/bar/jest.config.js'), + `/** @type {import('ts-jest').JestConfigWithTsJest} */ +export default { + preset: 'ts-jest', + testEnvironment: 'node', +};`, + ], + ]) + }) + }) describe('migrate', () => { const pkgPaths = { diff --git a/src/cli/config/init.ts b/src/cli/config/init.ts index 0006449355..182650a693 100644 --- a/src/cli/config/init.ts +++ b/src/cli/config/init.ts @@ -108,12 +108,14 @@ export const run: CliCommand = async (args: CliCommandArgs /* , logger: Logger * body = JSON.stringify({ ...pkgJson, jest: jestConfig }, undefined, ' ') } else { // js config - const content = [] + const content: string[] = [] if (!jestPreset) { content.push(`${preset.jsImport('tsjPreset')};`, '') } content.push(`/** @type {import('ts-jest').JestConfigWithTsJest} */`) - content.push('module.exports = {') + const usesModules = pkgJson.type === 'module' + content.push(usesModules ? 'export default {' : 'module.exports = {') + if (jestPreset) { content.push(` preset: '${preset.name}',`) } else {