/
githubmirror
/
next.js
Обзор
Документация
Войти
/
githubmirror
/
next.js
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
canary
test/e2e/webpack-loader-parse-error/webpack-loader-parse-error.test.ts
303 строки
10 KB
Sebastian "Sebbie" Silbermann
[fragment-scroll] Remove `config.experimental.appNewScrollHandler` (#95602)
06 авг 2026, 12:53
Не верифицирован
06 авг 2026, 12:53
2c04735
Код
Авторство
О чём код?
import { nextTestSetup } from 'e2e-utils' import { retry } from 'next-test-utils' import stripAnsi from 'strip-ansi' /** * Strips the dynamic test directory prefix from paths so snapshots are stable. * Handles both absolute paths (/full/path/test/tmp/next-test-XXX/) and * relative paths (./test/tmp/next-test-XXX/). */ function normalizePaths(output: string, testDir: string): string { let result = output.replaceAll(testDir + '/', '') const relMatch = testDir.match(/(test\/tmp\/next-test-[^/]+)/) if (relMatch) { result = result.replaceAll('./' + relMatch[1] + '/', './') result = result.replaceAll(relMatch[1] + '/', '') } return result } /** * Extracts a single error block from CLI output by searching for a title string. * * Start boundary: the nearest `⨯` marker (dev) or file-path line (prod) * before the title. * End boundary: the first blank line after an "Import trace" section that * isn't followed by continuation content (indented lines or file paths). */ function extractErrorBlock(output: string, errorTitle: string): string { const titleIdx = output.indexOf(errorTitle) if (titleIdx === -1) return '' const beforeTitle = output.substring(0, titleIdx) // In dev mode, errors start with a `⨯` marker on the same line as the file // path (e.g. `⨯ ./app/file.js`). In prod mode, errors start with a bare // file-path line above the title. Match `⨯ ./` specifically so we don't latch // onto unrelated `⨯` markers in the startup output, such as the experimental // features list (e.g. `⨯ exampleFlag (disabled by ...)`). const markerIdx = beforeTitle.lastIndexOf('⨯ ./') let startIdx: number if (markerIdx !== -1) { startIdx = markerIdx } else { // Go back two lines: title is preceded by a file-path line (e.g. ./file:3:1) const lines = beforeTitle.split('\n') startIdx = lines.length >= 2 ? beforeTitle.length - lines[lines.length - 1].length - lines[lines.length - 2].length - 1 : 0 } // Scan forward to find the end of the Import trace section const block = output.substring(startIdx) const lines = block.split('\n') let inImportTrace = false for (let i = 0; i < lines.length; i++) { if (/^Import trace/.test(lines[i])) { inImportTrace = true } // A blank line after the import trace ends the block, unless the next // line is a continuation (indented or a file path) if (inImportTrace && lines[i].trim() === '' && i > 0) { const nextLine = lines[i + 1] if ( !nextLine || (!nextLine.startsWith(' ') && !nextLine.startsWith('./')) ) { return lines.slice(0, i).join('\n') } } } return block.trimEnd() } describe('webpack-loader-parse-error (development)', () => { const { next, isTurbopack, isNextDev, skipped } = nextTestSetup({ files: __dirname, skipDeployment: true, skipStart: true, }) if (skipped) return if (!isNextDev) { it('skipped in production mode', () => {}) return } beforeAll(() => next.start()) it('should show parse error for JS loader that returns broken code', async () => { const outputIndex = next.cliOutput.length // Fetch the page to trigger the error await next.fetch('/') // Check that the CLI output contains the parse error // Turbopack: "Parsing ecmascript source code failed" // Webpack: "Syntax Error" await retry(async () => { expect(next.cliOutput).toMatch( /Parsing ecmascript source code failed|Syntax Error/ ) }) if (isTurbopack) { const output = normalizePaths( stripAnsi(next.cliOutput.slice(outputIndex)), next.testDir ) const errorBlock = extractErrorBlock( output, 'Parsing ecmascript source code failed' ) expect(errorBlock).toMatchInlineSnapshot(` "⨯ ./app/data.broken.js:3:1 Error: Expected '</', got '{' 1 | // This file will be processed by broken-js-loader 2 | // The loader will return invalid JavaScript with a source map > 3 | export default function Data() { | ^ 4 | return <div>original source content</div> 5 | } 6 | Parsing ecmascript source code failed Generated code of loaders [broken-js-loader.js] transform of file content of app/data.broken.js: ./app/data.broken.js:3:46 1 | // Generated by broken-js-loader 2 | export default function Page() { > 3 | return <div>this is intentionally broken {{{ invalid jsx | ^ 4 | } 5 | Import trace: Server Component: ./app/data.broken.js ./app/page.js" `) } }) it('should show parse error for CSS loader that returns broken code', async () => { const outputIndex = next.cliOutput.length // Fetch the page to trigger the CSS error await next.fetch('/css-page') // Check that the CLI output contains the CSS parse error // Turbopack: "Parsing CSS source code failed" // Webpack: "Unknown word" await retry(async () => { expect(next.cliOutput).toMatch( /Parsing CSS source code failed|Unknown word/ ) }) if (isTurbopack) { const output = normalizePaths( stripAnsi(next.cliOutput.slice(outputIndex)), next.testDir ) const errorBlock = extractErrorBlock( output, 'Parsing CSS source code failed' ) expect(errorBlock).toMatchInlineSnapshot(` "⨯ ./app/css-page/styles.broken.css:2:3 Error: Parsing CSS source code failed 1 | .page { > 2 | color: blue; | ^ 3 | font-size: 16px; 4 | } 5 | Unexpected token CurlyBracketBlock Generated code of PostCSS transform of loaders [broken-css-loader.js] transform of file content of app/css-page/styles.broken.css: ./app/css-page/styles.broken.css:5:15 3 | color: red 4 | @@@ THIS IS NOT VALID CSS @@@; > 5 | background: {{{ invalid | ^ 6 | } 7 | Import trace: Client Component Browser: ./app/css-page/styles.broken.css [Client Component Browser] ./app/css-page/page.js [Server Component]" `) } }) }) describe('webpack-loader-parse-error (production)', () => { const { next, isNextStart, isTurbopack } = nextTestSetup({ files: __dirname, skipDeployment: true, skipStart: true, }) if (!isNextStart) { it('skipped in development mode', () => {}) return } it('should fail the build with parse errors from loaders', async () => { await expect(next.start()).rejects.toThrow( 'next build failed with code/signal 1' ) const output = normalizePaths(stripAnsi(next.cliOutput), next.testDir) if (isTurbopack) { const jsError = extractErrorBlock(output, "Expected '</', got '{'") expect(jsError).toMatchInlineSnapshot(` "./app/data.broken.js:3:1 Error: Expected '</', got '{' 1 | // This file will be processed by broken-js-loader 2 | // The loader will return invalid JavaScript with a source map > 3 | export default function Data() { | ^ 4 | return <div>original source content</div> 5 | } 6 | Parsing ecmascript source code failed Generated code of loaders [broken-js-loader.js] transform of file content of app/data.broken.js: ./app/data.broken.js:3:46 1 | // Generated by broken-js-loader 2 | export default function Page() { > 3 | return <div>this is intentionally broken {{{ invalid jsx | ^ 4 | } 5 | Import trace: Server Component: ./app/data.broken.js ./app/page.js" `) const cssError = extractErrorBlock( output, 'Parsing CSS source code failed' ) expect(cssError).toMatchInlineSnapshot(` "./app/css-page/styles.broken.css:5:15 Error: Parsing CSS source code failed 3 | color: red 4 | @@@ THIS IS NOT VALID CSS @@@; > 5 | background: {{{ invalid | ^ 6 | } 7 | Unexpected token CurlyBracketBlock Generated code of PostCSS transform of loaders [broken-css-loader.js] transform of file content of app/css-page/styles.broken.css: ./app/css-page/styles.broken.css:5:15 3 | color: red 4 | @@@ THIS IS NOT VALID CSS @@@; > 5 | background: {{{ invalid | ^ 6 | } 7 | Import trace: Client Component Browser: ./app/css-page/styles.broken.css [Client Component Browser] ./app/css-page/page.js [Server Component]" `) } else { // Webpack stops at the first error (JS), showing the generated code const jsError = extractErrorBlock(output, "Expected '</', got '{'") expect(jsError).toMatchInlineSnapshot(` "./app/data.broken.js Error: x Expected '</', got '{' ,-[3:1] 1 | // Generated by broken-js-loader 2 | export default function Page() { 3 | return <div>this is intentionally broken {{{ invalid jsx : ^ 4 | } \`---- Caused by: Syntax Error Import trace for requested module: ./app/data.broken.js ./app/page.js" `) } }) })