Improve initial setup with new App Router TypeScript project (#64826)

This commit is contained in:
Hendrik Liebau 2024-04-26 18:19:50 +02:00 committed by GitHub
parent 270a9db056
commit 3438b39fcf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
35 changed files with 360 additions and 142 deletions

View file

@ -133,6 +133,12 @@ jobs:
# clean up any previous artifacts to avoid hitting disk space limits # clean up any previous artifacts to avoid hitting disk space limits
- run: git clean -xdf && rm -rf /tmp/next-repo-*; rm -rf /tmp/next-install-* /tmp/yarn-* /tmp/ncc-cache target - run: git clean -xdf && rm -rf /tmp/next-repo-*; rm -rf /tmp/next-install-* /tmp/yarn-* /tmp/ncc-cache target
# Configure a git user so that Create Next App can initialize git repos during integration tests.
- name: Set CI git user
run: |
git config --global user.name "vercel-ci-bot"
git config --global user.email "infra+ci@vercel.com"
- run: cargo clean - run: cargo clean
if: ${{ inputs.skipNativeBuild != 'yes' || inputs.needsNextest == 'yes' || inputs.needsRust == 'yes' }} if: ${{ inputs.skipNativeBuild != 'yes' || inputs.needsNextest == 'yes' || inputs.needsRust == 'yes' }}

View file

@ -1,5 +1,6 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "ES2017",
"lib": ["dom", "dom.iterable", "esnext"], "lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true, "allowJs": true,
"skipLibCheck": true, "skipLibCheck": true,

View file

@ -1,5 +1,6 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "ES2017",
"lib": ["dom", "dom.iterable", "esnext"], "lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true, "allowJs": true,
"skipLibCheck": true, "skipLibCheck": true,

View file

@ -1,5 +1,6 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "ES2017",
"lib": ["dom", "dom.iterable", "esnext"], "lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true, "allowJs": true,
"skipLibCheck": true, "skipLibCheck": true,

View file

@ -1,5 +1,6 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "ES2017",
"lib": ["dom", "dom.iterable", "esnext"], "lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true, "allowJs": true,
"skipLibCheck": true, "skipLibCheck": true,

View file

@ -0,0 +1,257 @@
import { mkdtemp, writeFile, readFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
// eslint-disable-next-line import/no-extraneous-dependencies
import ts from 'typescript'
import { writeConfigurationDefaults } from './writeConfigurationDefaults'
describe('writeConfigurationDefaults()', () => {
let consoleLogSpy: jest.SpyInstance
let distDir: string
let hasAppDir: boolean
let tmpDir: string
let tsConfigPath: string
let isFirstTimeSetup: boolean
let hasPagesDir: boolean
beforeEach(async () => {
consoleLogSpy = jest.spyOn(console, 'log').mockImplementation()
distDir = '.next'
tmpDir = await mkdtemp(join(tmpdir(), 'nextjs-test-'))
tsConfigPath = join(tmpDir, 'tsconfig.json')
isFirstTimeSetup = false
})
afterEach(() => {
consoleLogSpy.mockRestore()
})
describe('appDir', () => {
beforeEach(() => {
hasAppDir = true
hasPagesDir = false
})
it('applies suggested and mandatory defaults to existing tsconfig.json and logs them', async () => {
await writeFile(tsConfigPath, JSON.stringify({ compilerOptions: {} }), {
encoding: 'utf8',
})
await writeConfigurationDefaults(
ts,
tsConfigPath,
isFirstTimeSetup,
hasAppDir,
distDir,
hasPagesDir
)
const tsConfig = await readFile(tsConfigPath, { encoding: 'utf8' })
expect(JSON.parse(tsConfig)).toMatchInlineSnapshot(`
{
"compilerOptions": {
"allowJs": true,
"esModuleInterop": true,
"incremental": true,
"isolatedModules": true,
"jsx": "preserve",
"lib": [
"dom",
"dom.iterable",
"esnext",
],
"module": "esnext",
"moduleResolution": "node",
"noEmit": true,
"plugins": [
{
"name": "next",
},
],
"resolveJsonModule": true,
"skipLibCheck": true,
"strict": false,
"target": "ES2017",
},
"exclude": [
"node_modules",
],
"include": [
"next-env.d.ts",
".next/types/**/*.ts",
"**/*.ts",
"**/*.tsx",
],
}
`)
expect(
consoleLogSpy.mock.calls
.flat()
.join('\n')
// eslint-disable-next-line no-control-regex
.replace(/\x1B\[\d+m/g, '') // remove color control characters
).toMatchInlineSnapshot(`
"
We detected TypeScript in your project and reconfigured your tsconfig.json file for you. Strict-mode is set to false by default.
The following suggested values were added to your tsconfig.json. These values can be changed to fit your project's needs:
- target was set to ES2017 (For top-level \`await\`. Note: Next.js only polyfills for the esmodules target.)
- lib was set to dom,dom.iterable,esnext
- allowJs was set to true
- skipLibCheck was set to true
- strict was set to false
- noEmit was set to true
- incremental was set to true
- include was set to ['next-env.d.ts', '.next/types/**/*.ts', '**/*.ts', '**/*.tsx']
- plugins was updated to add { name: 'next' }
- exclude was set to ['node_modules']
The following mandatory changes were made to your tsconfig.json:
- module was set to esnext (for dynamic import() support)
- esModuleInterop was set to true (requirement for SWC / babel)
- moduleResolution was set to node (to match webpack resolution)
- resolveJsonModule was set to true (to match webpack resolution)
- isolatedModules was set to true (requirement for SWC / Babel)
- jsx was set to preserve (next.js implements its own optimized jsx transform)
"
`)
})
it('does not warn about disabled strict mode if strict mode was already enabled', async () => {
await writeFile(
tsConfigPath,
JSON.stringify({ compilerOptions: { strict: true } }),
{ encoding: 'utf8' }
)
await writeConfigurationDefaults(
ts,
tsConfigPath,
isFirstTimeSetup,
hasAppDir,
distDir,
hasPagesDir
)
expect(
consoleLogSpy.mock.calls
.flat()
.join('\n')
// eslint-disable-next-line no-control-regex
.replace(/\x1B\[\d+m/g, '') // remove color control characters
).not.toMatch('Strict-mode is set to false by default.')
})
describe('with tsconfig extends', () => {
let tsConfigBasePath: string
let nextAppTypes: string
beforeEach(() => {
tsConfigBasePath = join(tmpDir, 'tsconfig.base.json')
nextAppTypes = `${distDir}/types/**/*.ts`
})
it('should support empty includes when base provides it', async () => {
const include = ['**/*.ts', '**/*.tsx', nextAppTypes]
const content = { extends: './tsconfig.base.json' }
const baseContent = { include }
await writeFile(tsConfigPath, JSON.stringify(content, null, 2))
await writeFile(tsConfigBasePath, JSON.stringify(baseContent, null, 2))
await expect(
writeConfigurationDefaults(
ts,
tsConfigPath,
isFirstTimeSetup,
hasAppDir,
distDir,
hasPagesDir
)
).resolves.not.toThrow()
const output = await readFile(tsConfigPath, 'utf-8')
const parsed = JSON.parse(output)
expect(parsed.include).toBeUndefined()
})
it('should replace includes when base is missing appTypes', async () => {
const include = ['**/*.ts', '**/*.tsx']
const content = { extends: './tsconfig.base.json' }
const baseContent = { include }
await writeFile(tsConfigPath, JSON.stringify(content, null, 2))
await writeFile(tsConfigBasePath, JSON.stringify(baseContent, null, 2))
await expect(
writeConfigurationDefaults(
ts,
tsConfigPath,
isFirstTimeSetup,
hasAppDir,
distDir,
hasPagesDir
)
).resolves.not.toThrow()
const output = await readFile(tsConfigPath, 'utf8')
const parsed = JSON.parse(output)
expect(parsed.include.sort()).toMatchInlineSnapshot(`
[
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts",
]
`)
})
it('should not add strictNullChecks if base provides it', async () => {
const content = { extends: './tsconfig.base.json' }
const baseContent = {
compilerOptions: { strictNullChecks: true, strict: true },
}
await writeFile(tsConfigPath, JSON.stringify(content, null, 2))
await writeFile(tsConfigBasePath, JSON.stringify(baseContent, null, 2))
await writeConfigurationDefaults(
ts,
tsConfigPath,
isFirstTimeSetup,
hasAppDir,
distDir,
hasPagesDir
)
const output = await readFile(tsConfigPath, 'utf8')
const parsed = JSON.parse(output)
expect(parsed.compilerOptions.strictNullChecks).toBeUndefined()
})
})
})
})

View file

@ -176,9 +176,7 @@ export async function writeConfigurationDefaults(
cyan(optionKey) + cyan(optionKey) +
' was set to ' + ' was set to ' +
bold(check.suggested) + bold(check.suggested) +
check.reason (check.reason ? ` (${check.reason})` : '')
? ` (${check.reason})`
: ''
) )
} }
} else if ('value' in check) { } else if ('value' in check) {
@ -337,8 +335,13 @@ export async function writeConfigurationDefaults(
Log.info( Log.info(
`We detected TypeScript in your project and reconfigured your ${cyan( `We detected TypeScript in your project and reconfigured your ${cyan(
'tsconfig.json' 'tsconfig.json'
)} file for you. Strict-mode is set to ${cyan('false')} by default.` )} file for you.${
userTsConfig.compilerOptions?.strict
? ''
: ` Strict-mode is set to ${cyan('false')} by default.`
}`
) )
if (suggestedActions.length) { if (suggestedActions.length) {
Log.info( Log.info(
`The following suggested values were added to your ${cyan( `The following suggested values were added to your ${cyan(

View file

@ -14,8 +14,13 @@
"resolveJsonModule": true, "resolveJsonModule": true,
"isolatedModules": true, "isolatedModules": true,
"incremental": true, "incremental": true,
"baseUrl": "." "baseUrl": ".",
"plugins": [
{
"name": "next"
}
]
}, },
"exclude": ["node_modules"], "exclude": ["node_modules"],
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"] "include": ["**/*.ts", "**/*.tsx", "next-env.d.ts", ".next/types/**/*.ts"]
} }

View file

@ -16,7 +16,8 @@
{ {
"name": "next" "name": "next"
} }
] ],
"target": "ES2017"
}, },
"include": ["next-env.d.ts", ".next/types/**/*.ts", "**/*.ts", "**/*.tsx"], "include": ["next-env.d.ts", ".next/types/**/*.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"] "exclude": ["node_modules"]

View file

@ -15,5 +15,7 @@
"moduleResolution": "bundler", "moduleResolution": "bundler",
"resolveJsonModule": true, "resolveJsonModule": true,
"isolatedModules": true "isolatedModules": true
} },
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"]
} }

View file

@ -18,7 +18,8 @@
"name": "next" "name": "next"
} }
], ],
"strictNullChecks": true "strictNullChecks": true,
"target": "ES2017"
}, },
"include": ["next-env.d.ts", ".next/types/**/*.ts", "**/*.ts", "**/*.tsx"], "include": ["next-env.d.ts", ".next/types/**/*.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"] "exclude": ["node_modules"]

View file

@ -17,7 +17,8 @@
{ {
"name": "next" "name": "next"
} }
] ],
"target": "ES2017"
}, },
"include": ["next-env.d.ts", ".next/types/**/*.ts", "**/*.ts", "**/*.tsx"], "include": ["next-env.d.ts", ".next/types/**/*.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"] "exclude": ["node_modules"]

View file

@ -17,7 +17,8 @@
{ {
"name": "next" "name": "next"
} }
] ],
"target": "ES2017"
}, },
"include": ["next-env.d.ts", ".next/types/**/*.ts", "**/*.ts", "**/*.tsx"], "include": ["next-env.d.ts", ".next/types/**/*.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"] "exclude": ["node_modules"]

View file

@ -18,7 +18,8 @@
"name": "next" "name": "next"
} }
], ],
"strictNullChecks": true "strictNullChecks": true,
"target": "ES2017"
}, },
"include": ["next-env.d.ts", ".next/types/**/*.ts", "**/*.ts", "**/*.tsx"], "include": ["next-env.d.ts", ".next/types/**/*.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"] "exclude": ["node_modules"]

View file

@ -17,7 +17,8 @@
{ {
"name": "next" "name": "next"
} }
] ],
"target": "ES2017"
}, },
"include": ["next-env.d.ts", ".next/types/**/*.ts", "**/*.ts", "**/*.tsx"], "include": ["next-env.d.ts", ".next/types/**/*.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"] "exclude": ["node_modules"]

View file

@ -17,7 +17,8 @@
{ {
"name": "next" "name": "next"
} }
] ],
"target": "ES2017"
}, },
"include": ["next-env.d.ts", ".next/types/**/*.ts", "**/*.ts", "**/*.tsx"], "include": ["next-env.d.ts", ".next/types/**/*.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"] "exclude": ["node_modules"]

View file

@ -17,7 +17,8 @@
{ {
"name": "next" "name": "next"
} }
] ],
"target": "ES2017"
}, },
"include": ["next-env.d.ts", ".next/types/**/*.ts", "**/*.ts", "**/*.tsx"], "include": ["next-env.d.ts", ".next/types/**/*.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"] "exclude": ["node_modules"]

View file

@ -17,7 +17,8 @@
{ {
"name": "next" "name": "next"
} }
] ],
"target": "ES2017"
}, },
"include": ["next-env.d.ts", ".next/types/**/*.ts", "**/*.ts", "**/*.tsx"], "include": ["next-env.d.ts", ".next/types/**/*.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"] "exclude": ["node_modules"]

View file

@ -17,7 +17,8 @@
{ {
"name": "next" "name": "next"
} }
] ],
"target": "ES2017"
}, },
"include": ["next-env.d.ts", ".next/types/**/*.ts", "**/*.ts", "**/*.tsx"], "include": ["next-env.d.ts", ".next/types/**/*.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"] "exclude": ["node_modules"]

View file

@ -18,7 +18,8 @@
"name": "next" "name": "next"
} }
], ],
"strictNullChecks": true "strictNullChecks": true,
"target": "ES2017"
}, },
"include": ["next-env.d.ts", ".next/types/**/*.ts", "**/*.ts", "**/*.tsx"], "include": ["next-env.d.ts", ".next/types/**/*.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"] "exclude": ["node_modules"]

View file

@ -17,7 +17,9 @@
{ {
"name": "next" "name": "next"
} }
] ],
"target": "ES2017",
"strictNullChecks": true
}, },
"include": ["next-env.d.ts", ".next/types/**/*.ts", "**/*.ts", "**/*.tsx"], "include": ["next-env.d.ts", ".next/types/**/*.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"] "exclude": ["node_modules"]

View file

@ -18,7 +18,8 @@
"name": "next" "name": "next"
} }
], ],
"strictNullChecks": true "strictNullChecks": true,
"target": "ES2017"
}, },
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"] "exclude": ["node_modules"]

View file

@ -18,7 +18,8 @@
"name": "next" "name": "next"
} }
], ],
"strictNullChecks": true "strictNullChecks": true,
"target": "ES2017"
}, },
"include": ["next-env.d.ts", ".next/types/**/*.ts", "**/*.ts", "**/*.tsx"], "include": ["next-env.d.ts", ".next/types/**/*.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"] "exclude": ["node_modules"]

View file

@ -4,9 +4,9 @@
* This file contains utilities for `create-next-app` testing. * This file contains utilities for `create-next-app` testing.
*/ */
import { ChildProcess, spawn, SpawnOptions } from 'child_process' import { ChildProcess, execSync, spawn, SpawnOptions } from 'child_process'
import { existsSync } from 'fs' import { existsSync } from 'fs'
import { resolve } from 'path' import { join, resolve } from 'path'
import glob from 'glob' import glob from 'glob'
import Conf from 'next/dist/compiled/conf' import Conf from 'next/dist/compiled/conf'
@ -15,7 +15,12 @@ import {
mapSrcFiles, mapSrcFiles,
projectSpecification, projectSpecification,
} from './specification' } from './specification'
import { CustomTemplateOptions, ProjectDeps, ProjectFiles } from './types' import {
CustomTemplateOptions,
DefaultTemplateOptions,
ProjectDeps,
ProjectFiles,
} from './types'
const cli = require.resolve('create-next-app/dist/index.js') const cli = require.resolve('create-next-app/dist/index.js')
@ -71,6 +76,22 @@ export const spawnExitPromise = (childProcess: ChildProcess) => {
}) })
} }
export const projectShouldHaveNoGitChanges = ({
cwd,
projectName,
}: DefaultTemplateOptions) => {
const projectDirname = join(cwd, projectName)
try {
execSync('git diff --quiet', { cwd: projectDirname })
} catch {
execSync('git status', { cwd: projectDirname, stdio: 'inherit' })
execSync('git --no-pager diff', { cwd: projectDirname, stdio: 'inherit' })
throw new Error('Found unexpected git changes.')
}
}
export const projectFilesShouldExist = ({ export const projectFilesShouldExist = ({
cwd, cwd,
projectName, projectName,

View file

@ -1,5 +1,6 @@
import { import {
createNextApp, createNextApp,
projectShouldHaveNoGitChanges,
shouldBeTemplateProject, shouldBeTemplateProject,
spawnExitPromise, spawnExitPromise,
tryNextDev, tryNextDev,
@ -70,10 +71,8 @@ describe('create-next-app --app (App Router)', () => {
const exitCode = await spawnExitPromise(cp) const exitCode = await spawnExitPromise(cp)
expect(exitCode).toBe(0) expect(exitCode).toBe(0)
shouldBeTemplateProject({ cwd, projectName, template: 'app', mode: 'ts' }) shouldBeTemplateProject({ cwd, projectName, template: 'app', mode: 'ts' })
await tryNextDev({ await tryNextDev({ cwd, projectName })
cwd, await projectShouldHaveNoGitChanges({ cwd, projectName })
projectName,
})
}) })
}) })

View file

@ -1,5 +1,6 @@
import { import {
createNextApp, createNextApp,
projectShouldHaveNoGitChanges,
shouldBeTemplateProject, shouldBeTemplateProject,
spawnExitPromise, spawnExitPromise,
tryNextDev, tryNextDev,
@ -78,11 +79,8 @@ describe('create-next-app --no-app (Pages Router)', () => {
template: 'default', template: 'default',
mode: 'ts', mode: 'ts',
}) })
await tryNextDev({ await tryNextDev({ cwd, projectName, isApp: false })
cwd, await projectShouldHaveNoGitChanges({ cwd, projectName })
projectName,
isApp: false,
})
}) })
}) })

View file

@ -74,6 +74,7 @@ export {
createNextApp, createNextApp,
projectFilesShouldExist, projectFilesShouldExist,
projectFilesShouldNotExist, projectFilesShouldNotExist,
projectShouldHaveNoGitChanges,
shouldBeTemplateProject, shouldBeTemplateProject,
shouldBeJavascriptProject, shouldBeJavascriptProject,
shouldBeTypescriptProject, shouldBeTypescriptProject,

View file

@ -1,2 +1,5 @@
/// <reference types="next" /> /// <reference types="next" />
/// <reference types="next/types/global" /> /// <reference types="next/image-types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.

View file

@ -20,7 +20,8 @@
"amp-test-utils": ["test/lib/amp-test-utils"], "amp-test-utils": ["test/lib/amp-test-utils"],
"next-webdriver": ["test/lib/next-webdriver"], "next-webdriver": ["test/lib/next-webdriver"],
"e2e-utils": ["test/lib/e2e-utils"] "e2e-utils": ["test/lib/e2e-utils"]
} },
"target": "ES2017"
}, },
"include": [ "include": [
"test/integration/draft-mode/next-env.d.ts", "test/integration/draft-mode/next-env.d.ts",

View file

@ -18,7 +18,8 @@
"name": "next" "name": "next"
} }
], ],
"strictNullChecks": true "strictNullChecks": true,
"target": "ES2017"
}, },
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".build/types/**/*.ts"], "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".build/types/**/*.ts"],
"exclude": ["node_modules"] "exclude": ["node_modules"]

View file

@ -12,7 +12,8 @@
"moduleResolution": "bundler", "moduleResolution": "bundler",
"resolveJsonModule": true, "resolveJsonModule": true,
"isolatedModules": true, "isolatedModules": true,
"jsx": "preserve" "jsx": "preserve",
"incremental": true
}, },
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"] "exclude": ["node_modules"]

View file

@ -18,7 +18,8 @@
"name": "next" "name": "next"
} }
], ],
"strictNullChecks": true "strictNullChecks": true,
"target": "ES2017"
}, },
"include": ["next-env.d.ts", ".next/types/**/*.ts", "**/*.ts", "**/*.tsx"], "include": ["next-env.d.ts", ".next/types/**/*.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"] "exclude": ["node_modules"]

View file

@ -11,7 +11,8 @@
"moduleResolution": "node", "moduleResolution": "node",
"resolveJsonModule": true, "resolveJsonModule": true,
"isolatedModules": true, "isolatedModules": true,
"jsx": "preserve" "jsx": "preserve",
"target": "ES2017"
}, },
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"] "exclude": ["node_modules"]

View file

@ -11,7 +11,8 @@
"moduleResolution": "node", "moduleResolution": "node",
"resolveJsonModule": true, "resolveJsonModule": true,
"isolatedModules": true, "isolatedModules": true,
"jsx": "preserve" "jsx": "preserve",
"target": "ES2017"
}, },
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"] "exclude": ["node_modules"]

View file

@ -1,102 +0,0 @@
/* eslint-env jest */
import fs from 'fs-extra'
import { join } from 'path'
import { writeConfigurationDefaults } from 'next/dist/lib/typescript/writeConfigurationDefaults'
import * as ts from 'typescript'
const fixtureDir = join(__dirname, 'fixtures/config-ts')
const tsconfigFile = join(fixtureDir, 'tsconfig.json')
const tsconfigBaseFile = join(fixtureDir, 'tsconfig.base.json')
const distDir = '.next'
const nextAppTypes = `${distDir}/types/**/*.ts`
describe('tsconfig.base.json', () => {
beforeEach(async () => {
await fs.ensureDir(fixtureDir)
})
afterEach(async () => {
await fs.remove(tsconfigFile)
await fs.remove(tsconfigBaseFile)
})
describe('appDir', () => {
it('should support empty includes when base provides it', async () => {
const include = ['**/*.ts', '**/*.tsx', nextAppTypes]
const content = {
extends: './tsconfig.base.json',
}
const baseContent = {
include,
}
await fs.writeFile(tsconfigFile, JSON.stringify(content, null, 2))
await fs.writeFile(tsconfigBaseFile, JSON.stringify(baseContent, null, 2))
await expect(
writeConfigurationDefaults(ts, tsconfigFile, false, true, distDir, true)
).resolves.not.toThrow()
const output = await fs.readFile(tsconfigFile, 'utf8')
const parsed = JSON.parse(output)
expect(parsed.include).toBeUndefined()
})
it('should replace includes when base is missing appTypes', async () => {
const include = ['**/*.ts', '**/*.tsx']
const content = {
extends: './tsconfig.base.json',
}
const baseContent = {
include,
}
await fs.writeFile(tsconfigFile, JSON.stringify(content, null, 2))
await fs.writeFile(tsconfigBaseFile, JSON.stringify(baseContent, null, 2))
await expect(
writeConfigurationDefaults(ts, tsconfigFile, false, true, distDir, true)
).resolves.not.toThrow()
const output = await fs.readFile(tsconfigFile, 'utf8')
const parsed = JSON.parse(output)
expect(parsed.include.sort()).toMatchInlineSnapshot(`
[
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts",
]
`)
})
it('should not add strictNullChecks if base provides it', async () => {
const content = {
extends: './tsconfig.base.json',
}
const baseContent = {
compilerOptions: {
strictNullChecks: true,
strict: true,
},
}
await fs.writeFile(tsconfigFile, JSON.stringify(content, null, 2))
await fs.writeFile(tsconfigBaseFile, JSON.stringify(baseContent, null, 2))
await writeConfigurationDefaults(
ts,
tsconfigFile,
false,
true,
distDir,
true
)
const output = await fs.readFile(tsconfigFile, 'utf8')
const parsed = JSON.parse(output)
expect(parsed.compilerOptions.strictNullChecks).toBeUndefined()
})
})
})