Skip to content

Commit

Permalink
Add configuration flags to disable integrated type checker (#9138)
Browse files Browse the repository at this point in the history
* Add a configuration flag to disable integrated type checker

* Add tests for typescript-transpileonly

* Restore removed argument

* Make output more coherent

* Split transpileOnly into ignoreDevErrors and ignoreBuildErrors

* Minor stylistic change
  • Loading branch information
kachkaev authored and Timer committed Oct 28, 2019
1 parent 2d9f199 commit ad2a3d6
Show file tree
Hide file tree
Showing 9 changed files with 144 additions and 3 deletions.
16 changes: 16 additions & 0 deletions packages/next/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2316,6 +2316,22 @@ To learn more about TypeScript checkout its [documentation](https://www.typescri
> **Note**: Next.js does not enable TypeScript's `strict` mode by default.
> When you feel comfortable with TypeScript, you may turn this option on in your `tsconfig.json`.

> **Note**: By default, Next.js reports TypeScript errors during development for pages you are actively working on.
> TypeScript errors for inactive pages do not block the development process.
> Trying to run `next build` for an app that has TypeScript errors on any page will fail.
>
> If you don't want to leverage this behavior and prefer to do type checks manually, set the following options in your `next.config.js`:
>
> ```js
> // next.config.js
> module.exports = {
> typescript: {
> ignoreDevErrors: true,
> ignoreBuildErrors: true,
> },
> }
> ```
### Exported types
Next.js provides `NextPage` type that can be used for pages in the `pages` directory. `NextPage` adds definitions for [`getInitialProps`](#fetching-data-and-component-lifecycle) so that it can be used without any extra typing needed.
Expand Down
2 changes: 1 addition & 1 deletion packages/next/build/output/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ store.subscribe(state => {
}

if (state.typeChecking) {
Log.info('bundled successfully, waiting for typecheck results ...')
Log.info('bundled successfully, waiting for typecheck results...')
return
}

Expand Down
4 changes: 4 additions & 0 deletions packages/next/build/webpack-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,9 @@ export default async function getBaseWebpackConfig(
const useTypeScript = Boolean(
typeScriptPath && (await fileExists(tsConfigPath))
)
const ignoreTypeScriptErrors = dev
? config.typescript && config.typescript.ignoreDevErrors
: config.typescript && config.typescript.ignoreBuildErrors

const resolveConfig = {
// Disable .mjs for node_modules bundling
Expand Down Expand Up @@ -845,6 +848,7 @@ export default async function getBaseWebpackConfig(
}),
!isServer &&
useTypeScript &&
!ignoreTypeScriptErrors &&
new ForkTsCheckerWebpackPlugin(
PnpWebpackPlugin.forkTsCheckerOptions({
typescript: typeScriptPath,
Expand Down
4 changes: 3 additions & 1 deletion packages/next/server/hot-reloader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -349,11 +349,13 @@ export default class HotReloader {
async prepareBuildTools(multiCompiler: webpack.MultiCompiler) {
const tsConfigPath = join(this.dir, 'tsconfig.json')
const useTypeScript = await fileExists(tsConfigPath)
const ignoreTypeScriptErrors =
this.config.typescript && this.config.typescript.ignoreDevErrors

watchCompilers(
multiCompiler.compilers[0],
multiCompiler.compilers[1],
useTypeScript,
useTypeScript && !ignoreTypeScriptErrors,
({ errors, warnings }) => this.send('typeChecked', { errors, warnings })
)

Expand Down
1 change: 1 addition & 0 deletions test/integration/typescript-ignore-errors/next.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = {}
2 changes: 2 additions & 0 deletions test/integration/typescript-ignore-errors/pages/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// Below type error is intentional, it helps check typescript → ignoreDevErrors / ignoreBuildErrors flags in next.config.js
export default (): boolean => 'Index page'
84 changes: 84 additions & 0 deletions test/integration/typescript-ignore-errors/test/index.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/* eslint-env jest */
/* global jasmine */
import { join } from 'path'
import {
renderViaHTTP,
nextBuild,
findPort,
launchApp,
killApp,
File
} from 'next-test-utils'

jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000 * 60 * 2

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

describe('TypeScript with error handling options', () => {
for (const ignoreDevErrors of [false, true]) {
for (const ignoreBuildErrors of [false, true]) {
describe(`ignoreDevErrors: ${ignoreDevErrors}, ignoreBuildErrors: ${ignoreBuildErrors}`, () => {
beforeAll(() => {
const nextConfig = {
typescript: { ignoreDevErrors, ignoreBuildErrors }
}
nextConfigFile.replace('{}', JSON.stringify(nextConfig))
})
afterAll(() => {
nextConfigFile.restore()
})

it(
ignoreDevErrors
? 'Next renders the page in dev despite type errors'
: 'Next dev does not render the page in dev because of type errors',
async () => {
let app
let output = ''
try {
const appPort = await findPort()
app = await launchApp(appDir, appPort, {
onStdout: msg => (output += msg),
onStderr: msg => (output += msg)
})
await renderViaHTTP(appPort, '')

if (ignoreDevErrors) {
expect(output).not.toContain('waiting for typecheck results...')
expect(output).not.toContain("not assignable to type 'boolean'")
} else {
expect(output).toContain('waiting for typecheck results...')
expect(output).toContain("not assignable to type 'boolean'")
}
} finally {
await killApp(app)
}
}
)

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
})

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("not assignable to type 'boolean'")
}
}
)
})
}
}
})
19 changes: 19 additions & 0 deletions test/integration/typescript-ignore-errors/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"compilerOptions": {
"esModuleInterop": true,
"module": "esnext",
"jsx": "preserve",
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noEmit": true,
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true
},
"exclude": ["node_modules"],
"include": ["next-env.d.ts", "pages"]
}
15 changes: 14 additions & 1 deletion test/integration/typescript/test/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000 * 60 * 2
const appDir = join(__dirname, '..')
let appPort
let app
let output

const handleOutput = msg => {
output += msg
}

async function get$ (path, query) {
const html = await renderViaHTTP(appPort, path, query)
Expand All @@ -26,8 +31,12 @@ async function get$ (path, query) {
describe('TypeScript Features', () => {
describe('default behavior', () => {
beforeAll(async () => {
output = ''
appPort = await findPort()
app = await launchApp(appDir, appPort)
app = await launchApp(appDir, appPort, {
onStdout: handleOutput,
onStderr: handleOutput
})
})
afterAll(() => killApp(app))

Expand All @@ -36,6 +45,10 @@ describe('TypeScript Features', () => {
expect($('body').text()).toMatch(/Hello World/)
})

it('should report type checking to stdout', async () => {
expect(output).toContain('waiting for typecheck results...')
})

it('should not fail to render when an inactive page has an error', async () => {
await killApp(app)
let evilFile = join(appDir, 'pages', 'evil.tsx')
Expand Down

0 comments on commit ad2a3d6

Please sign in to comment.