| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | import type { |
| | DynamicCssManifest, |
| | ReactLoadableManifest, |
| | } from '../../../server/load-components' |
| | import path from 'path' |
| | import { webpack, sources } from 'next/dist/compiled/webpack/webpack' |
| | import { DYNAMIC_CSS_MANIFEST } from '../../../shared/lib/constants' |
| |
|
| | function getModuleId(compilation: any, module: any): string | number { |
| | return compilation.chunkGraph.getModuleId(module) |
| | } |
| |
|
| | function getModuleFromDependency( |
| | compilation: any, |
| | dep: any |
| | ): webpack.Module & { resource?: string } { |
| | return compilation.moduleGraph.getModule(dep) |
| | } |
| |
|
| | function getOriginModuleFromDependency( |
| | compilation: any, |
| | dep: any |
| | ): webpack.Module & { resource?: string } { |
| | return compilation.moduleGraph.getParentModule(dep) |
| | } |
| |
|
| | function getChunkGroupFromBlock( |
| | compilation: any, |
| | block: any |
| | ): webpack.Compilation['chunkGroups'] { |
| | return compilation.chunkGraph.getBlockChunkGroup(block) |
| | } |
| |
|
| | function buildManifest( |
| | _compiler: webpack.Compiler, |
| | compilation: webpack.Compilation, |
| | projectSrcDir: string | undefined, |
| | dev: boolean, |
| | shouldCreateDynamicCssManifest: boolean |
| | ): { |
| | reactLoadableManifest: ReactLoadableManifest |
| | dynamicCssManifest: DynamicCssManifest |
| | } { |
| | if (!projectSrcDir) { |
| | return { |
| | reactLoadableManifest: {}, |
| | dynamicCssManifest: [], |
| | } |
| | } |
| | const dynamicCssManifestSet = new Set<string>() |
| | let manifest: ReactLoadableManifest = {} |
| |
|
| | |
| | |
| |
|
| | |
| | |
| | |
| |
|
| | |
| | const handleBlock = (block: any) => { |
| | block.blocks.forEach(handleBlock) |
| | const chunkGroup = getChunkGroupFromBlock(compilation, block) |
| | for (const dependency of block.dependencies) { |
| | if (dependency.type.startsWith('import()')) { |
| | |
| | const module = getModuleFromDependency(compilation, dependency) |
| | if (!module) return |
| |
|
| | |
| | const originModule = getOriginModuleFromDependency( |
| | compilation, |
| | dependency |
| | ) |
| | const originRequest: string | undefined = originModule?.resource |
| | if (!originRequest) return |
| |
|
| | |
| | |
| | |
| | const key = `${path.relative(projectSrcDir, originRequest)} -> ${ |
| | dependency.request |
| | }` |
| |
|
| | |
| | const files = new Set<string>() |
| |
|
| | if (manifest[key]) { |
| | |
| | |
| | |
| | |
| | |
| | for (const file of manifest[key].files) { |
| | files.add(file) |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | if (chunkGroup) { |
| | for (const chunk of (chunkGroup as any) |
| | .chunks as webpack.Compilation['chunks']) { |
| | chunk.files.forEach((file: string) => { |
| | if ( |
| | (file.endsWith('.js') || file.endsWith('.css')) && |
| | file.match(/^static\/(chunks|css)\//) |
| | ) { |
| | files.add(file) |
| |
|
| | if (shouldCreateDynamicCssManifest && file.endsWith('.css')) { |
| | dynamicCssManifestSet.add(file) |
| | } |
| | } |
| | }) |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| |
|
| | |
| | const id = dev ? key : getModuleId(compilation, module) |
| | manifest[key] = { id, files: Array.from(files) } |
| | } |
| | } |
| | } |
| | for (const module of compilation.modules) { |
| | module.blocks.forEach(handleBlock) |
| | } |
| |
|
| | manifest = Object.keys(manifest) |
| | .sort() |
| | |
| | .reduce((a, c) => ((a[c] = manifest[c]), a), {} as any) |
| |
|
| | return { |
| | reactLoadableManifest: manifest, |
| | dynamicCssManifest: Array.from(dynamicCssManifestSet), |
| | } |
| | } |
| |
|
| | export class ReactLoadablePlugin { |
| | private filename: string |
| | private pagesOrAppDir: string | undefined |
| | private isPagesDir: boolean |
| | private runtimeAsset?: string |
| | private dev: boolean |
| |
|
| | constructor(opts: { |
| | filename: string |
| | pagesDir?: string |
| | appDir?: string |
| | runtimeAsset?: string |
| | dev: boolean |
| | }) { |
| | this.filename = opts.filename |
| | this.pagesOrAppDir = opts.pagesDir || opts.appDir |
| | this.isPagesDir = Boolean(opts.pagesDir) |
| | this.runtimeAsset = opts.runtimeAsset |
| | this.dev = opts.dev |
| | } |
| |
|
| | createAssets(compiler: any, compilation: any) { |
| | const projectSrcDir = this.pagesOrAppDir |
| | ? path.dirname(this.pagesOrAppDir) |
| | : undefined |
| | const shouldCreateDynamicCssManifest = !this.dev && this.isPagesDir |
| | const { reactLoadableManifest, dynamicCssManifest } = buildManifest( |
| | compiler, |
| | compilation, |
| | projectSrcDir, |
| | this.dev, |
| | shouldCreateDynamicCssManifest |
| | ) |
| |
|
| | compilation.emitAsset( |
| | this.filename, |
| | new sources.RawSource(JSON.stringify(reactLoadableManifest, null, 2)) |
| | ) |
| |
|
| | if (this.runtimeAsset) { |
| | compilation.emitAsset( |
| | this.runtimeAsset, |
| | new sources.RawSource( |
| | `self.__REACT_LOADABLE_MANIFEST=${JSON.stringify( |
| | JSON.stringify(reactLoadableManifest) |
| | )}` |
| | ) |
| | ) |
| | } |
| |
|
| | |
| | |
| | |
| | if (shouldCreateDynamicCssManifest) { |
| | compilation.emitAsset( |
| | `${DYNAMIC_CSS_MANIFEST}.json`, |
| | new sources.RawSource(JSON.stringify(dynamicCssManifest, null, 2)) |
| | ) |
| |
|
| | |
| | compilation.emitAsset( |
| | `server/${DYNAMIC_CSS_MANIFEST}.js`, |
| | new sources.RawSource( |
| | `self.__DYNAMIC_CSS_MANIFEST=${JSON.stringify( |
| | JSON.stringify(dynamicCssManifest) |
| | )}` |
| | ) |
| | ) |
| | } |
| | } |
| |
|
| | apply(compiler: webpack.Compiler) { |
| | compiler.hooks.make.tap('ReactLoadableManifest', (compilation) => { |
| | compilation.hooks.processAssets.tap( |
| | { |
| | name: 'ReactLoadableManifest', |
| | stage: webpack.Compilation.PROCESS_ASSETS_STAGE_ADDITIONS, |
| | }, |
| | () => { |
| | this.createAssets(compiler, compilation) |
| | } |
| | ) |
| | }) |
| | } |
| | } |
| |
|