未验证 提交 f1423518 编写于 作者: J Joe Haddad 提交者: GitHub

Better Babel syntax errors (#12821)

上级 615f658d
......@@ -50,6 +50,7 @@ import WebpackConformancePlugin, {
MinificationConformanceCheck,
ReactSyncScriptsConformanceCheck,
} from './webpack/plugins/webpack-conformance-plugin'
import { WellKnownErrorsPlugin } from './webpack/plugins/wellknown-errors-plugin'
type ExcludesFalse = <T>(x: T | false) => x is T
......@@ -1004,6 +1005,7 @@ export default async function getBaseWebpackConfig(
}),
].filter(Boolean),
}),
new WellKnownErrorsPlugin(),
].filter((Boolean as any) as ExcludesFalse),
}
......
import { Compiler } from 'webpack'
import { getModuleBuildError } from './webpackModuleError'
export class WellKnownErrorsPlugin {
apply(compiler: Compiler) {
compiler.hooks.compilation.tap('WellKnownErrorsPlugin', compilation => {
compilation.hooks.seal.tap('WellKnownErrorsPlugin', () => {
if (compilation.errors?.length) {
compilation.errors = compilation.errors.map(err => {
const moduleError = getModuleBuildError(compilation, err)
return moduleError === false ? err : moduleError
})
}
})
})
}
}
import Chalk from 'next/dist/compiled/chalk'
import { SimpleWebpackError } from './simpleWebpackError'
const chalk = new Chalk.constructor({ enabled: true })
export function getBabelError(
fileName: string,
err: Error & {
code?: 'BABEL_PARSE_ERROR'
loc?: { line: number; column: number }
}
): SimpleWebpackError | false {
if (err.code !== 'BABEL_PARSE_ERROR') {
return false
}
// https://github.com/babel/babel/blob/34693d6024da3f026534dd8d569f97ac0109602e/packages/babel-core/src/parser/index.js
if (err.loc) {
const lineNumber = Math.max(1, err.loc.line)
const column = Math.max(1, err.loc.column)
let message = err.message
// Remove file information, which instead is provided by webpack.
.replace(/^.+?: /, '')
// Remove column information from message
.replace(
new RegExp(`[^\\S\\r\\n]*\\(${lineNumber}:${column}\\)[^\\S\\r\\n]*`),
''
)
return new SimpleWebpackError(
`${chalk.cyan(fileName)}:${chalk.yellow(
lineNumber.toString()
)}:${chalk.yellow(column.toString())}`,
chalk.red.bold('Syntax error').concat(`: ${message}`)
)
}
return false
}
// This class creates a simplified webpack error that formats nicely based on
// webpack's build in serializer.
// https://github.com/webpack/webpack/blob/c9d4ff7b054fc581c96ce0e53432d44f9dd8ca72/lib/Stats.js#L294-L356
export class SimpleWebpackError extends Error {
file: string
constructor(file: string, message: string) {
super(message)
this.file = file
}
}
import * as path from 'path'
// eslint-disable-next-line @typescript-eslint/no-unused-vars
import { compilation } from 'webpack'
import { getBabelError } from './parseBabel'
import { SimpleWebpackError } from './simpleWebpackError'
function getFilename(compilation: compilation.Compilation, m: any): string {
const requestShortener = compilation.requestShortener
if (typeof m?.readableIdentifier === 'function') {
return m.readableIdentifier(requestShortener)
}
if (typeof m.resource === 'string') {
const res = path.relative(compilation.context, m.resource)
return res.startsWith('.') ? res : `.${path.sep}${res}`
}
return m.request ?? '<unknown>'
}
export function getModuleBuildError(
compilation: compilation.Compilation,
input: any
): SimpleWebpackError | false {
if (
!(
typeof input === 'object' &&
input?.name === 'ModuleBuildError' &&
Boolean(input.module) &&
input.error instanceof Error
)
) {
return false
}
const err: Error = input.error
const sourceFilename = getFilename(compilation, input.module)
const babel = getBabelError(sourceFilename, err)
if (babel !== false) {
return babel
}
return false
}
......@@ -33,7 +33,13 @@ test('logbox: can recover from a syntax error without losing state', async () =>
await session.patch('index.js', `export default () => <div/`)
expect(await session.hasRedbox(true)).toBe(true)
expect(await session.getRedboxSource()).toMatch('SyntaxError')
expect(await session.getRedboxSource()).toMatchInlineSnapshot(`
"./index.js:1:26
Syntax error: Unexpected token, expected \\"jsxTagEnd\\"
> 1 | export default () => <div/
| ^"
`)
await session.patch(
'index.js',
......@@ -482,18 +488,33 @@ test('syntax > runtime error', async () => {
i++
throw Error('no ' + i)
}, 1000)
export default function FunctionNamed() {
`
export default function FunctionNamed() {`
)
await new Promise(resolve => setTimeout(resolve, 1000))
expect(await session.hasRedbox(true)).toBe(true)
expect(await session.getRedboxSource()).toMatch('SyntaxError')
expect(await session.getRedboxSource()).toMatchInlineSnapshot(`
"./index.js:8:47
Syntax error: Unexpected token
6 | throw Error('no ' + i)
7 | }, 1000)
> 8 | export default function FunctionNamed() {
| ^"
`)
// Test that runtime error does not take over:
await new Promise(resolve => setTimeout(resolve, 2000))
expect(await session.hasRedbox(true)).toBe(true)
expect(await session.getRedboxSource()).toMatch('SyntaxError')
expect(await session.getRedboxSource()).toMatchInlineSnapshot(`
"./index.js:8:47
Syntax error: Unexpected token
6 | throw Error('no ' + i)
7 | }, 1000)
> 8 | export default function FunctionNamed() {
| ^"
`)
await cleanup()
})
......@@ -600,8 +621,18 @@ test('unterminated JSX', async () => {
expect(await session.hasRedbox(true)).toBe(true)
const source = await session.getRedboxSource()
expect(source).not.toMatch('Unexpected token')
expect(source).toMatch('Unterminated JSX contents')
expect(source).toMatchInlineSnapshot(`
"./index.js:5:22
Syntax error: Unterminated JSX contents
3 | return (
4 | <div>
> 5 | <p>lol</p>
| ^
6 | div
7 | )
8 | }"
`)
await cleanup()
})
......
......@@ -46,7 +46,7 @@ describe('Custom _error', () => {
await fs.writeFile(page404, 'export default <h1>')
const html = await renderViaHTTP(appPort, '/404')
await fs.remove(page404)
expect(html).toContain('Module build failed')
expect(html).toContain('Syntax error')
expect(stderr).not.toMatch(customErrNo404Match)
})
})
......
......@@ -16,7 +16,7 @@ describe('TypeScript Exclusivity of Numeric Separator', () => {
expect(code).toBe(1)
expect(stderr).toContain('Failed to compile.')
expect(stderr).toContain('SyntaxError:')
expect(stderr).toContain('Syntax error')
expect(stderr).toContain('config to enable transformation')
})
})
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册