rsnext/packages/next/build/webpack/plugins/serverless-plugin.ts
Joe Haddad e5636dcdb0
Inject the build ID after webpack runs (#6618)
* Inject the build ID after webpack runs

* add webpack sources types

* use webpack plugin instead

* reset file

* Skip source maps if none were provided

Co-Authored-By: Timer <timer150@gmail.com>
2019-03-12 16:39:44 -04:00

96 lines
3.1 KiB
TypeScript

import MagicString from 'magic-string'
import { Compiler } from 'webpack'
import { SourceMapSource, RawSource } from 'webpack-sources'
import GraphHelpers from 'webpack/lib/GraphHelpers'
/**
* Makes sure there are no dynamic chunks when the target is serverless
* The dynamic chunks are integrated back into their parent chunk
* This is to make sure there is a single render bundle instead of that bundle importing dynamic chunks
*/
const NEXT_REPLACE_BUILD_ID = '__NEXT_REPLACE__BUILD_ID__'
export class ServerlessPlugin {
private buildId: string
private sourceMap: boolean
constructor(buildId: string, { sourceMap = false } = {}) {
this.buildId = buildId
this.sourceMap = sourceMap
}
apply(compiler: Compiler) {
compiler.hooks.compilation.tap('ServerlessPlugin', compilation => {
compilation.hooks.optimizeChunksBasic.tap('ServerlessPlugin', chunks => {
chunks.forEach(chunk => {
// If chunk is not an entry point skip them
if (chunk.hasEntryModule()) {
const dynamicChunks = chunk.getAllAsyncChunks()
if (dynamicChunks.size !== 0) {
for (const dynamicChunk of dynamicChunks) {
for (const module of dynamicChunk.modulesIterable) {
GraphHelpers.connectChunkAndModule(chunk, module)
}
}
}
}
})
compilation.hooks.afterOptimizeChunkAssets.tap(
'ServerlessPlugin',
chunks => {
chunks
.reduce(
(acc, chunk) => acc.concat(chunk.files || []),
[] as any[]
)
.forEach(file => {
const asset = compilation.assets[file]
let input
let inputSourceMap
if (this.sourceMap) {
if (asset.sourceAndMap) {
const sourceAndMap = asset.sourceAndMap()
inputSourceMap = sourceAndMap.map
input = sourceAndMap.source
} else {
inputSourceMap = asset.map()
input = asset.source()
}
} else {
input = asset.source()
}
const f = new MagicString(input)
const regex = new RegExp(NEXT_REPLACE_BUILD_ID, 'g')
let result
while ((result = regex.exec(input))) {
f.overwrite(
result.index,
result.index + NEXT_REPLACE_BUILD_ID.length,
this.buildId
)
}
if (this.sourceMap && inputSourceMap) {
compilation.assets[file] = new SourceMapSource(
f.toString(),
file,
f.generateMap({ hires: true }),
input,
inputSourceMap
)
} else {
compilation.assets[file] = new RawSource(f.toString())
}
})
}
)
})
})
}
}