Skip to content

Commit

Permalink
cache typechecking with incremental compilation (#24559)
Browse files Browse the repository at this point in the history
Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
  • Loading branch information
sokra and kodiakhq[bot] authored May 7, 2021
1 parent 42c4743 commit 4e8fac9
Show file tree
Hide file tree
Showing 24 changed files with 113 additions and 42 deletions.
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

0 comments on commit 4e8fac9

Please sign in to comment.