Improve initial setup with new App Router TypeScript project (#64826)
This commit is contained in:
parent
270a9db056
commit
3438b39fcf
35 changed files with 360 additions and 142 deletions
6
.github/workflows/build_reusable.yml
vendored
6
.github/workflows/build_reusable.yml
vendored
|
@ -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' }}
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
|
@ -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(
|
||||||
|
|
|
@ -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"]
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"]
|
||||||
|
|
|
@ -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"]
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"]
|
||||||
|
|
|
@ -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"]
|
||||||
|
|
|
@ -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"]
|
||||||
|
|
|
@ -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"]
|
||||||
|
|
|
@ -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"]
|
||||||
|
|
|
@ -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"]
|
||||||
|
|
|
@ -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"]
|
||||||
|
|
|
@ -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"]
|
||||||
|
|
|
@ -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"]
|
||||||
|
|
|
@ -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"]
|
||||||
|
|
|
@ -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"]
|
||||||
|
|
|
@ -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"]
|
||||||
|
|
|
@ -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"]
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -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,
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -74,6 +74,7 @@ export {
|
||||||
createNextApp,
|
createNextApp,
|
||||||
projectFilesShouldExist,
|
projectFilesShouldExist,
|
||||||
projectFilesShouldNotExist,
|
projectFilesShouldNotExist,
|
||||||
|
projectShouldHaveNoGitChanges,
|
||||||
shouldBeTemplateProject,
|
shouldBeTemplateProject,
|
||||||
shouldBeJavascriptProject,
|
shouldBeJavascriptProject,
|
||||||
shouldBeTypescriptProject,
|
shouldBeTypescriptProject,
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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"]
|
||||||
|
|
|
@ -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"]
|
||||||
|
|
|
@ -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"]
|
||||||
|
|
|
@ -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"]
|
||||||
|
|
|
@ -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"]
|
||||||
|
|
|
@ -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()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
Loading…
Reference in a new issue