Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cache typechecking with incremental compilation #24559

Merged
merged 7 commits into from
May 7, 2021
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@
"tailwindcss": "1.1.3",
"taskr": "1.1.0",
"tree-kill": "1.2.2",
"typescript": "3.8.3",
"typescript": "4.3.0-beta",
"wait-port": "0.2.2",
"web-streams-polyfill": "2.1.1",
"webpack-bundle-analyzer": "4.3.0",
Expand Down
4 changes: 2 additions & 2 deletions packages/next/build/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,8 +146,8 @@ export default async function build(

const { headers, rewrites, redirects } = customRoutes

const cacheDir = path.join(distDir, 'cache')
if (ciEnvironment.isCI && !ciEnvironment.hasNextSupport) {
const cacheDir = path.join(distDir, 'cache')
const hasCache = await fileExists(cacheDir)

if (!hasCache) {
Expand Down Expand Up @@ -193,7 +193,7 @@ export default async function build(
const verifyResult = await nextBuildSpan
.traceChild('verify-typescript-setup')
.traceAsyncFn(() =>
verifyTypeScriptSetup(dir, pagesDir, !ignoreTypeScriptErrors)
verifyTypeScriptSetup(dir, pagesDir, !ignoreTypeScriptErrors, cacheDir)
)

const typeCheckEnd = process.hrtime(typeCheckStart)
Expand Down
27 changes: 23 additions & 4 deletions packages/next/lib/typescript/runTypeCheck.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import path from 'path'
import {
DiagnosticCategory,
getFormattedDiagnostic,
Expand All @@ -18,7 +19,8 @@ export interface TypeCheckResult {
export async function runTypeCheck(
ts: typeof import('typescript'),
baseDir: string,
tsConfigPath: string
tsConfigPath: string,
cacheDir?: string
): Promise<TypeCheckResult> {
const effectiveConfiguration = await getTypeScriptConfiguration(
ts,
Expand All @@ -35,11 +37,28 @@ export async function runTypeCheck(
}
const requiredConfig = getRequiredConfiguration(ts)

const program = ts.createProgram(effectiveConfiguration.fileNames, {
const options = {
...effectiveConfiguration.options,
...requiredConfig,
noEmit: true,
})
}

let program: import('typescript').Program
let incremental = false
if (options.incremental && cacheDir) {
incremental = true
const builderProgram = ts.createIncrementalProgram({
rootNames: effectiveConfiguration.fileNames,
options: {
...options,
incremental: true,
tsBuildInfoFile: path.join(cacheDir, '.tsbuildinfo'),
},
})
program = builderProgram.getProgram()
} else {
program = ts.createProgram(effectiveConfiguration.fileNames, options)
}
const result = program.emit()

// Intended to match:
Expand Down Expand Up @@ -79,6 +98,6 @@ export async function runTypeCheck(
warnings,
inputFilesCount: effectiveConfiguration.fileNames.length,
totalFilesCount: program.getSourceFiles().length,
incremental: false,
incremental,
}
}
4 changes: 4 additions & 0 deletions packages/next/lib/typescript/writeConfigurationDefaults.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { promises as fs } from 'fs'
import chalk from 'chalk'
import * as CommentJson from 'next/dist/compiled/comment-json'
import semver from 'next/dist/compiled/semver'
import os from 'os'
import { getTypeScriptConfiguration } from './getTypeScriptConfiguration'

Expand Down Expand Up @@ -28,6 +29,9 @@ function getDesiredCompilerOptions(
strict: { suggested: false },
forceConsistentCasingInFileNames: { suggested: true },
noEmit: { suggested: true },
...(semver.gte(ts.version, '4.3.0-beta')
? { incremental: { suggested: true } }
: undefined),

// These values are required and cannot be changed by the user
// Keep this in sync with the webpack config
Expand Down
5 changes: 3 additions & 2 deletions packages/next/lib/verifyTypeScriptSetup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ import { writeConfigurationDefaults } from './typescript/writeConfigurationDefau
export async function verifyTypeScriptSetup(
dir: string,
pagesDir: string,
typeCheckPreflight: boolean
typeCheckPreflight: boolean,
cacheDir?: string
): Promise<{ result?: TypeCheckResult; version: string | null }> {
const tsConfigPath = path.join(dir, 'tsconfig.json')

Expand Down Expand Up @@ -48,7 +49,7 @@ export async function verifyTypeScriptSetup(
const { runTypeCheck } = require('./typescript/runTypeCheck')

// Verify the project passes type-checking before we go to webpack phase:
result = await runTypeCheck(ts, dir, tsConfigPath)
result = await runTypeCheck(ts, dir, tsConfigPath, cacheDir)
}
return { result, version: ts.version }
} catch (err) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export const ShadowPortal: React.FC<ShadowPortalProps> = function Portal({
let mountNode = React.useRef<HTMLDivElement | null>(null)
let portalNode = React.useRef<HTMLElement | null>(null)
let shadowNode = React.useRef<ShadowRoot | null>(null)
let [, forceUpdate] = React.useState()
let [, forceUpdate] = React.useState<{} | undefined>()

React.useLayoutEffect(() => {
const ownerDocument = mountNode.current!.ownerDocument!
Expand Down
1 change: 1 addition & 0 deletions test/integration/app-tree/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"strict": false,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"incremental": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
Expand Down
1 change: 1 addition & 0 deletions test/integration/custom-server-types/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"strict": false,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"incremental": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true
Expand Down
1 change: 1 addition & 0 deletions test/integration/handle-non-page-in-pages/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"strict": false,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"incremental": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"incremental": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true
Expand Down
1 change: 1 addition & 0 deletions test/integration/image-component/typescript/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"incremental": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true
Expand Down
6 changes: 6 additions & 0 deletions test/integration/tsconfig-verifier/test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ describe('tsconfig.json verifier', () => {
\\"strict\\": false,
\\"forceConsistentCasingInFileNames\\": true,
\\"noEmit\\": true,
\\"incremental\\": true,
\\"esModuleInterop\\": true,
\\"module\\": \\"esnext\\",
\\"moduleResolution\\": \\"node\\",
Expand Down Expand Up @@ -83,6 +84,7 @@ describe('tsconfig.json verifier', () => {
\\"strict\\": false,
\\"forceConsistentCasingInFileNames\\": true,
\\"noEmit\\": true,
\\"incremental\\": true,
\\"esModuleInterop\\": true,
\\"module\\": \\"esnext\\",
\\"moduleResolution\\": \\"node\\",
Expand Down Expand Up @@ -150,6 +152,7 @@ describe('tsconfig.json verifier', () => {
\\"strict\\": false,
\\"forceConsistentCasingInFileNames\\": true,
\\"noEmit\\": true,
\\"incremental\\": true,
\\"moduleResolution\\": \\"node\\",
\\"resolveJsonModule\\": true,
\\"isolatedModules\\": true,
Expand Down Expand Up @@ -198,6 +201,7 @@ describe('tsconfig.json verifier', () => {
\\"strict\\": false,
\\"forceConsistentCasingInFileNames\\": true,
\\"noEmit\\": true,
\\"incremental\\": true,
\\"moduleResolution\\": \\"node\\",
\\"resolveJsonModule\\": true,
\\"isolatedModules\\": true,
Expand Down Expand Up @@ -243,6 +247,7 @@ describe('tsconfig.json verifier', () => {
\\"strict\\": false,
\\"forceConsistentCasingInFileNames\\": true,
\\"noEmit\\": true,
\\"incremental\\": true,
\\"moduleResolution\\": \\"node\\",
\\"resolveJsonModule\\": true,
\\"isolatedModules\\": true,
Expand Down Expand Up @@ -281,6 +286,7 @@ describe('tsconfig.json verifier', () => {
"strict": false,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"incremental": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "node",
Expand Down
1 change: 1 addition & 0 deletions test/integration/typescript-baseurl/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"incremental": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"incremental": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"incremental": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true
Expand Down
1 change: 1 addition & 0 deletions test/integration/typescript-filtered-files/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"incremental": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true
Expand Down
1 change: 1 addition & 0 deletions test/integration/typescript-hmr/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"incremental": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true
Expand Down
85 changes: 53 additions & 32 deletions test/integration/typescript-ignore-errors/test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,44 +7,65 @@ jest.setTimeout(1000 * 60 * 2)

const appDir = join(__dirname, '..')
const nextConfigFile = new File(join(appDir, 'next.config.js'))
const tsConfigFile = new File(join(appDir, 'tsconfig.json'))

describe('TypeScript with error handling options', () => {
// Dev can no longer show errors (for now), logbox will cover this in the
// future.
for (const ignoreBuildErrors of [false, true]) {
describe(`ignoreBuildErrors: ${ignoreBuildErrors}`, () => {
beforeAll(() => {
const nextConfig = {
typescript: { ignoreBuildErrors },
}
nextConfigFile.write('module.exports = ' + JSON.stringify(nextConfig))
})
afterAll(() => {
nextConfigFile.restore()
})
for (const incremental of [false, true]) {
for (const ignoreBuildErrors of [false, true]) {
describe(`ignoreBuildErrors: ${ignoreBuildErrors}`, () => {
beforeAll(() => {
const nextConfig = {
typescript: { ignoreBuildErrors },
}
nextConfigFile.write('module.exports = ' + JSON.stringify(nextConfig))
const tsconfig = JSON.parse(tsConfigFile.originalContent)
tsConfigFile.write(
JSON.stringify(
{
...tsconfig,
compilerOptions: {
...tsconfig.compilerOptions,
incremental,
},
},
null,
2
)
)
})
afterAll(() => {
nextConfigFile.restore()
tsConfigFile.restore()
})

it(
ignoreBuildErrors
? 'Next builds the application despite type errors'
: 'Next fails to build the application despite type errors',
async () => {
const { stdout, stderr } = await nextBuild(appDir, [], {
stdout: true,
stderr: true,
})
it(
(ignoreBuildErrors
? 'Next builds the application despite type errors'
: 'Next fails to build the application despite type errors') +
(incremental
? ' in incremental mode'
: ' without incremental mode'),
async () => {
const { stdout, stderr } = await nextBuild(appDir, [], {
stdout: true,
stderr: true,
})

if (ignoreBuildErrors) {
expect(stdout).toContain('Compiled successfully')
expect(stderr).not.toContain('Failed to compile.')
expect(stderr).not.toContain("not assignable to type 'boolean'")
} else {
expect(stdout).not.toContain('Compiled successfully')
expect(stderr).toContain('Failed to compile.')
expect(stderr).toContain('./pages/index.tsx:2:31')
expect(stderr).toContain("not assignable to type 'boolean'")
if (ignoreBuildErrors) {
expect(stdout).toContain('Compiled successfully')
expect(stderr).not.toContain('Failed to compile.')
expect(stderr).not.toContain("not assignable to type 'boolean'")
} else {
expect(stdout).not.toContain('Compiled successfully')
expect(stderr).toContain('Failed to compile.')
expect(stderr).toContain('./pages/index.tsx:2:31')
expect(stderr).toContain("not assignable to type 'boolean'")
}
}
}
)
})
)
})
}
}
})
1 change: 1 addition & 0 deletions test/integration/typescript-ignore-errors/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"incremental": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"incremental": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true
Expand Down
1 change: 1 addition & 0 deletions test/integration/typescript-paths/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"incremental": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true
Expand Down
1 change: 1 addition & 0 deletions test/integration/typescript-workspaces-paths/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"incremental": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true
Expand Down
1 change: 1 addition & 0 deletions test/integration/typescript/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"incremental": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true
Expand Down
Loading