mirror of
https://github.com/hanzoai/next.js.git
synced 2026-09-24 17:00:51 +00:00
https://github.com/vercel/next.js/pull/94197 had a bug in the Turbopack case, where it didn't properly trace all necessary files referenced from `cacheHandler` and `cacheHandlers`. If you imported `lodash` in the cache handler, then - `node_modules/.pnpm/[email protected].1/node_modules/lodash/lodash.js` was correctly traced but missing were - `node_modules/.pnpm/[email protected].1/node_modules/lodash/package.json` and - `node_modules/lodash` (the symlink) The problem was that it wasn't including affecting sources of these entrypoints, because it was treating those entrypoints as bundled: ``` SingleModuleGraph::new_inner( entries: Vec<ChunkGroupEntry>, visited_modules: &FxIndexMap<ResolvedVc<Box<dyn Module>>, GraphNodeIndex>, include_traced: bool, include_binding_usage: bool, entries_are_traced: bool, // <------ problematic ) -> Result<Vc<Self>> ``` should really have been ``` SingleModuleGraph::new_inner( entries: &GraphEntries, // contains Vec<ChunkGroupEntry> and traced_modules: Vec<Modules visited_modules: &FxIndexMap<ResolvedVc<Box<dyn Module>>, GraphNodeIndex>, include_traced: bool, include_binding_usage: bool, ) -> Result<Vc<Self>> { ``` because the single `entries_are_traced: bool` can be different per entry.
104 lines
3.1 KiB
TypeScript
104 lines
3.1 KiB
TypeScript
import { nextTestSetup } from 'e2e-utils'
|
|
import fs from 'fs'
|
|
import { join } from 'path'
|
|
|
|
// Uses webpack internals (stats.json via webpack-bundle-analyzer).
|
|
;(process.env.IS_TURBOPACK_TEST ? describe.skip : describe)('Chunking', () => {
|
|
const { next } = nextTestSetup({
|
|
files: __dirname,
|
|
dependencies: {
|
|
lodash: '4.18.1',
|
|
'webpack-bundle-analyzer': 'latest',
|
|
},
|
|
})
|
|
|
|
let chunks: string[]
|
|
let stats: any
|
|
|
|
beforeAll(async () => {
|
|
const statsRaw = (await next.readFile('.next/stats.json')).replace(
|
|
/"static\\(.*?":?)/g,
|
|
(match) => match.replace(/\\/g, '\\\\')
|
|
)
|
|
stats = JSON.parse(statsRaw)
|
|
chunks = fs.readdirSync(join(next.testDir, '.next', 'static', 'chunks'))
|
|
})
|
|
|
|
const existsChunkNamed = (name: string) => {
|
|
return chunks.some((chunk) => new RegExp(name).test(chunk))
|
|
}
|
|
|
|
it('should use all url friendly names', () => {
|
|
expect(chunks).toEqual(chunks.map((name) => encodeURIComponent(name)))
|
|
})
|
|
|
|
it('should create a framework chunk', () => {
|
|
expect(existsChunkNamed('framework')).toBe(true)
|
|
})
|
|
|
|
it('should not create a commons chunk', () => {
|
|
expect(existsChunkNamed('commons')).toBe(false)
|
|
})
|
|
|
|
it('should not create a lib chunk for react or react-dom', () => {
|
|
expect(existsChunkNamed('react|react-dom')).toBe(false)
|
|
})
|
|
|
|
it('should not preload the build manifest', async () => {
|
|
const $ = await next.render$('/')
|
|
expect(
|
|
[].slice
|
|
.call($('link[rel="preload"][as="script"]'))
|
|
.map((e: any) => e.attribs.href)
|
|
.some((entry: string) => entry.includes('_buildManifest'))
|
|
).toBe(false)
|
|
})
|
|
|
|
it('should execute the build manifest', async () => {
|
|
const $ = await next.render$('/')
|
|
expect(
|
|
Array.from($('script'))
|
|
.map((e: any) => e.attribs.src)
|
|
.some((entry: string) => entry && entry.includes('_buildManifest'))
|
|
).toBe(true)
|
|
})
|
|
|
|
it('should not include more than one instance of react-dom', () => {
|
|
// Match react-dom only when it appears as a directory segment, so pnpm
|
|
// peer-dep path suffixes like ".pnpm/[email protected]_react-dom@..." (which
|
|
// appear in CI installs) are not treated as react-dom modules.
|
|
const reactDomModuleRegex = /[\\/]react-dom[\\/]/
|
|
const misplacedReactDom = stats.chunks.some((chunk: any) => {
|
|
if (chunk.names.includes('framework')) {
|
|
return false
|
|
}
|
|
return chunk.modules.some((module: any) => {
|
|
return reactDomModuleRegex.test(module.name)
|
|
})
|
|
})
|
|
expect(misplacedReactDom).toBe(false)
|
|
})
|
|
|
|
describe('Serving', () => {
|
|
it('should hydrate with aggressive chunking', async () => {
|
|
const browser = await next.browser('/page2')
|
|
const text = await browser.elementByCss('#padded-str').text()
|
|
expect(text).toBe('__rad__')
|
|
await browser.close()
|
|
})
|
|
|
|
it('should load chunks when navigating', async () => {
|
|
const browser = await next.browser('/page3')
|
|
const text = await browser
|
|
.elementByCss('#page2-link')
|
|
.click()
|
|
.waitForElementByCss('#padded-str')
|
|
.elementByCss('#padded-str')
|
|
.text()
|
|
|
|
expect(text).toBe('__rad__')
|
|
await browser.close()
|
|
})
|
|
})
|
|
})
|