Skip to content

Commit d2ad7ab

Browse files
Shao SicongShao Sicong
authored andcommitted
feat: basic support for webpack 5
1 parent ce7fde0 commit d2ad7ab

27 files changed

+724
-1870
lines changed

examples/vue-cssextract/webpack.config.js

Lines changed: 1 addition & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,7 @@ module.exports = {
1919
use: BabelMultiTargetPlugin.loader(),
2020
}, {
2121
test: /\.vue$/,
22-
use: [
23-
BabelMultiTargetPlugin.loader('vue-loader'),
24-
],
22+
use: BabelMultiTargetPlugin.loader('vue-loader'),
2523
},
2624
{
2725
test: /\.css$/,
@@ -31,18 +29,4 @@ module.exports = {
3129
],
3230
}],
3331
},
34-
35-
node: {
36-
// prevent webpack from injecting useless setImmediate polyfill because Vue
37-
// source contains it (although only uses it if it's native).
38-
setImmediate: false,
39-
// prevent webpack from injecting mocks to Node native modules
40-
// that does not make sense for the client
41-
dgram: 'empty',
42-
fs: 'empty',
43-
net: 'empty',
44-
tls: 'empty',
45-
// eslint-disable-next-line camelcase
46-
child_process: 'empty',
47-
},
4832
}

examples/vue-dynamic-import/webpack.config.js

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -38,18 +38,4 @@ module.exports = {
3838
},
3939
],
4040
},
41-
42-
node: {
43-
// prevent webpack from injecting useless setImmediate polyfill because Vue
44-
// source contains it (although only uses it if it's native).
45-
setImmediate: false,
46-
// prevent webpack from injecting mocks to Node native modules
47-
// that does not make sense for the client
48-
dgram: 'empty',
49-
fs: 'empty',
50-
net: 'empty',
51-
tls: 'empty',
52-
// eslint-disable-next-line camelcase
53-
child_process: 'empty',
54-
},
5541
}

src/babel-target.ts

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
import { BabelLoaderTransformOptions, BabelPresetOptions } from 'babel-loader'
2-
import * as webpack from 'webpack'
3-
import Chunk = webpack.compilation.Chunk
4-
import ChunkGroup = webpack.compilation.ChunkGroup
5-
import Entrypoint = webpack.compilation.Entrypoint
6-
import Module = webpack.compilation.Module
2+
import { Chunk, ChunkGroup, ChunkGraph, Entrypoint, Module } from 'webpack'
73

84
import { BabelLoaderCacheDirectoryOption } from './babel.multi.target.options'
95
import { BabelTargetOptions } from './babel.target.options'
@@ -33,8 +29,9 @@ export type BabelTargetInfo = { [TOption in keyof BabelTargetOptions]: BabelTarg
3329
// so, need to do this instead
3430
const SIG = {
3531
module: [
36-
'disconnect',
37-
'unseal',
32+
// These are removed in webpack@5
33+
// 'disconnect',
34+
// 'unseal',
3835
'isEntryModule',
3936
'isInChunk',
4037
],
@@ -51,7 +48,8 @@ const SIG = {
5148
'hasEntryModule',
5249
'addModule',
5350
'removeModule',
54-
'setModules',
51+
// This is removed in webpack@5
52+
// 'setModules',
5553
'getNumberOfModules',
5654
'addGroup',
5755
'isInGroup',
@@ -137,7 +135,7 @@ export class BabelTarget implements BabelTargetInfo {
137135
}
138136

139137
public static getTargetFromModule(module: Module): BabelTarget {
140-
if (module.options && module.options.babelTarget) {
138+
if (module.options?.babelTarget) {
141139
return module.options.babelTarget
142140
}
143141

@@ -146,7 +144,7 @@ export class BabelTarget implements BabelTargetInfo {
146144
}
147145

148146
for (const reason of module.reasons) {
149-
if (reason.dependency && reason.dependency.babelTarget) {
147+
if (reason.dependency?.babelTarget) {
150148
return reason.dependency.babelTarget
151149
}
152150
if (reason.module) {
@@ -162,10 +160,16 @@ export class BabelTarget implements BabelTargetInfo {
162160
}
163161

164162
public static getTargetFromEntrypoint(entrypoint: Entrypoint): BabelTarget {
165-
if (!entrypoint.runtimeChunk.hasEntryModule()) {
163+
if (!entrypoint.getRuntimeChunk().hasEntryModule()) {
166164
return undefined
167165
}
168-
return BabelTarget.getTargetFromModule(entrypoint.runtimeChunk.entryModule)
166+
const arr = Array.from(
167+
ChunkGraph.getChunkGraphForChunk(
168+
entrypoint.getRuntimeChunk(),
169+
'Chunk.entryModule',
170+
'DEP_WEBPACK_CHUNK_ENTRY_MODULE',
171+
).getChunkEntryModulesIterable(entrypoint.getRuntimeChunk()))
172+
return BabelTarget.getTargetFromModule(arr[0])
169173
}
170174

171175
// eslint-disable-next-line

src/babel.multi.target.html.updater.ts

Lines changed: 18 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,9 @@
1-
import { AlterAssetTagsData, HtmlTag, HtmlWebpackPlugin } from 'html-webpack-plugin'
2-
import { compilation, Compiler, Plugin } from 'webpack'
1+
import { getHooks, HtmlTagObject } from 'html-webpack-plugin'
2+
import { Chunk, ChunkGroup, Compilation, Compiler, Plugin } from 'webpack'
33

44
import { BabelTarget } from './babel-target'
5-
import { getAlterAssetTags, getHeadTags, getBodyTags } from './html-webpack-plugin.polyfill'
65
import { PLUGIN_NAME } from './plugin.name'
76
import { TargetedChunkMap } from './targeted.chunk'
8-
import Chunk = compilation.Chunk
9-
import ChunkGroup = compilation.ChunkGroup
10-
import Compilation = compilation.Compilation
117

128
// Works with HtmlWebpackPlugin to make sure the targeted assets are referenced correctly
139
// Tags for assets whose target has `esModule` set are updated with the `"type"="module"` attribute
@@ -20,15 +16,15 @@ export class BabelMultiTargetHtmlUpdater implements Plugin {
2016

2117
constructor(private targets: BabelTarget[]) {}
2218

23-
public updateScriptTags(chunkMap: TargetedChunkMap, tags: HtmlTag[]): void {
19+
public updateScriptTags(chunkMap: TargetedChunkMap, tags: HtmlTagObject[]): void {
2420

2521
tags
26-
.forEach((tag: HtmlTag) => {
22+
.forEach((tag: HtmlTagObject) => {
2723
if (tag.tagName !== 'script') {
2824
return
2925
}
3026

31-
const targetedChunks = chunkMap.get(tag.attributes.src)
27+
const targetedChunks = chunkMap.get(tag.attributes.src as string)
3228
// chunks that are added outside of an entry point (e.g. by HtmlWebpackIncludeAssetsPlugin) will not be targeted
3329
if (!targetedChunks) {
3430
return
@@ -83,7 +79,7 @@ export class BabelMultiTargetHtmlUpdater implements Plugin {
8379
public apply(compiler: Compiler): void {
8480

8581
compiler.hooks.afterPlugins.tap(PLUGIN_NAME, () => {
86-
const htmlWebpackPlugin: HtmlWebpackPlugin = compiler.options.plugins
82+
const htmlWebpackPlugin = compiler.options.plugins
8783
// instanceof can act wonky since we don't actually keep our own dependency on html-webpack-plugin
8884
// should we?
8985
.find(plugin => plugin.constructor.name === 'HtmlWebpackPlugin') as any
@@ -94,18 +90,18 @@ export class BabelMultiTargetHtmlUpdater implements Plugin {
9490

9591
// not sure if this is a problem since webpack will wait for dependencies to load, but sorting
9692
// by auto/dependency will result in a cyclic dependency error for lazy-loaded routes
97-
htmlWebpackPlugin.options.chunksSortMode = 'none' as any
93+
htmlWebpackPlugin.userOptions.chunksSortMode = 'none' as any
9894

99-
if ((htmlWebpackPlugin.options.chunks as any) !== 'all' &&
100-
htmlWebpackPlugin.options.chunks &&
101-
htmlWebpackPlugin.options.chunks.length
95+
if ((htmlWebpackPlugin.userOptions.chunks as any) !== 'all' &&
96+
htmlWebpackPlugin.userOptions.chunks &&
97+
htmlWebpackPlugin.userOptions.chunks.length
10298
) {
103-
htmlWebpackPlugin.options.chunks = this.mapChunkNames(htmlWebpackPlugin.options.chunks as string[])
99+
htmlWebpackPlugin.userOptions.chunks = this.mapChunkNames(htmlWebpackPlugin.userOptions.chunks as string[])
104100
}
105101

106-
if (htmlWebpackPlugin.options.excludeChunks &&
107-
htmlWebpackPlugin.options.excludeChunks.length) {
108-
htmlWebpackPlugin.options.excludeChunks = this.mapChunkNames(htmlWebpackPlugin.options.excludeChunks)
102+
if (htmlWebpackPlugin.userOptions.excludeChunks &&
103+
htmlWebpackPlugin.userOptions.excludeChunks.length) {
104+
htmlWebpackPlugin.userOptions.excludeChunks = this.mapChunkNames(htmlWebpackPlugin.userOptions.excludeChunks)
109105
}
110106

111107
compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation: Compilation) => {
@@ -114,10 +110,8 @@ export class BabelMultiTargetHtmlUpdater implements Plugin {
114110
return
115111
}
116112

117-
const hook = getAlterAssetTags(compilation)
118-
119-
hook.tapPromise(`${PLUGIN_NAME} update asset tags`,
120-
async (htmlPluginData: AlterAssetTagsData) => {
113+
getHooks(compilation).alterAssetTagGroups.tapPromise(`${PLUGIN_NAME} update asset tags`,
114+
async (htmlPluginData) => {
121115
const chunkMap: TargetedChunkMap = compilation.chunkGroups.reduce((result: TargetedChunkMap, chunkGroup: ChunkGroup) => {
122116
chunkGroup.chunks.forEach((chunk: Chunk) => {
123117
chunk.files.forEach((file: string) => {
@@ -126,8 +120,8 @@ export class BabelMultiTargetHtmlUpdater implements Plugin {
126120
})
127121
return result
128122
}, new TargetedChunkMap(compiler.options.output.publicPath))
129-
this.updateScriptTags(chunkMap, getHeadTags(htmlPluginData))
130-
this.updateScriptTags(chunkMap, getBodyTags(htmlPluginData))
123+
this.updateScriptTags(chunkMap, htmlPluginData.headTags)
124+
this.updateScriptTags(chunkMap, htmlPluginData.bodyTags)
131125
return htmlPluginData
132126
})
133127

src/babel.multi.target.plugin.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,8 @@ export class BabelMultiTargetPlugin implements Plugin {
7373
// magic starts here!
7474
new BabelTargetEntryOptionPlugin(this.targets).apply(compiler)
7575
new TargetingPlugin(this.targets, this.options.exclude, this.options.doNotTarget, compiler.options.externals).apply(compiler)
76-
new NormalizeCssChunksPlugin(this.targets).apply(compiler)
76+
// TODO
77+
// new NormalizeCssChunksPlugin(this.targets).apply(compiler)
7778
new BabelMultiTargetHtmlUpdater(this.targets).apply(compiler)
7879
if (this.options.safari10NoModuleFix) {
7980
new SafariNoModuleFixPlugin(this.options.safari10NoModuleFix).apply(compiler)
Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,40 @@
11
import { BabelTarget } from './babel-target'
2+
import { DEV_SERVER_CLIENT } from './constants'
23

3-
import Dependency = require('webpack/lib/Dependency')
4+
import ModuleDependency = require('webpack/lib/dependencies/ModuleDependency')
45

5-
export interface EntryLoc {
6+
interface EntryLoc {
67
name: string
78
index?: number
89
}
910

10-
export interface BabelTargetEntryDependency extends Dependency {
11-
babelTarget: BabelTarget
12-
loc: EntryLoc
13-
name: string
11+
// TODO what's makeSerializable?
12+
export class BabelTargetEntryDependency extends ModuleDependency {
13+
14+
public name: string
15+
public loc: EntryLoc
16+
17+
public get type(): string {
18+
return 'babel target entry'
19+
}
20+
21+
public getResourceIdentifier(): string {
22+
return `module${this.request}!${this.babelTarget.key}`
23+
}
24+
25+
public get category(): string {
26+
return 'esm'
27+
}
28+
29+
constructor(public babelTarget: BabelTarget, request: string, public originalName: string, loc?: EntryLoc) {
30+
super(`${request.startsWith(DEV_SERVER_CLIENT) ? request : babelTarget.getTargetedRequest(request)}`)
31+
32+
this.name = babelTarget.getTargetedAssetName(originalName)
33+
if (!loc) {
34+
loc = { name: `${this.request}:${babelTarget.key}` }
35+
} else {
36+
loc.name += `:${babelTarget.key}`
37+
}
38+
this.loc = loc
39+
}
1440
}

src/babel.target.entry.option.plugin.ts

Lines changed: 43 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import { Compiler, Plugin } from 'webpack'
22

33
import { BabelTarget } from './babel-target'
4-
import { BabelTargetMultiEntryPlugin } from './babel.target.multi.entry.plugin'
5-
import { BabelTargetSingleEntryPlugin } from './babel.target.single.entry.plugin'
4+
import { BabelTargetEntryPlugin } from './babel.target.entry.plugin'
65

76
// takes over processing of webpack's entry options so that it generates one entry per entry and target
87
// basically the same as webpack's built-in EntryOptionPlugin, just using the babel targeting stuff instead
@@ -15,27 +14,56 @@ export class BabelTargetEntryOptionPlugin implements Plugin {
1514
constructor(private targets: BabelTarget[]) {
1615
}
1716

18-
private itemToPlugin(context: string, item: string | string[], name: string): Plugin {
19-
if (Array.isArray(item)) {
20-
return new BabelTargetMultiEntryPlugin(this.targets, context, name, item)
17+
// private itemToPlugin(context: string, item: string | string[], name: string): Plugin {
18+
// if (Array.isArray(item)) {
19+
// return new BabelTargetMultiEntryPlugin(this.targets, context, name, item)
20+
// }
21+
// if (this.targets.find(target => !!(target.additionalModules && target.additionalModules.length))) {
22+
// return new BabelTargetMultiEntryPlugin(this.targets, context, name, [item])
23+
// }
24+
// return new BabelTargetSingleEntryPlugin(this.targets, context, name, item)
25+
// }
26+
27+
private static entryDescriptionToOptions(compiler: Compiler, name: string, desc: any): any {
28+
const options = {
29+
name,
30+
filename: desc.filename,
31+
runtime: desc.runtime,
32+
dependOn: desc.dependOn,
33+
chunkLoading: desc.chunkLoading,
34+
wasmLoading: desc.wasmLoading,
35+
library: desc.library,
36+
}
37+
// TODO what does those plugins do?
38+
if (desc.chunkLoading) {
39+
// const EnableChunkLoadingPlugin = require("./javascript/EnableChunkLoadingPlugin")
40+
// EnableChunkLoadingPlugin.checkEnabled(compiler, desc.chunkLoading)
2141
}
22-
if (this.targets.find(target => !!(target.additionalModules && target.additionalModules.length))) {
23-
return new BabelTargetMultiEntryPlugin(this.targets, context, name, [item])
42+
if (desc.wasmLoading) {
43+
// const EnableWasmLoadingPlugin = require("./wasm/EnableWasmLoadingPlugin")
44+
// EnableWasmLoadingPlugin.checkEnabled(compiler, desc.wasmLoading)
2445
}
25-
return new BabelTargetSingleEntryPlugin(this.targets, context, name, item)
46+
if (desc.library) {
47+
// const EnableLibraryPlugin = require("./library/EnableLibraryPlugin")
48+
// EnableLibraryPlugin.checkEnabled(compiler, desc.library.type)
49+
}
50+
return options
2651
}
2752

2853
public apply(compiler: Compiler): void {
2954
compiler.hooks.entryOption.tap('EntryOptionPlugin', (context: string, entry: any) => {
30-
if (typeof entry === 'string' || Array.isArray(entry)) {
31-
this.itemToPlugin(context, entry, 'main').apply(compiler)
32-
} else if (typeof entry === 'object') {
33-
for (const name of Object.keys(entry)) {
34-
this.itemToPlugin(context, entry[name], name).apply(compiler)
35-
}
36-
} else if (typeof entry === 'function') {
55+
if (typeof entry === 'function') {
56+
// TODO figure out why
3757
throw new Error('not supported')
3858
// new DynamicEntryPlugin(context, entry).apply(compiler)
59+
} else{
60+
for (const name of Object.keys(entry)) {
61+
const desc = entry[name]
62+
const options = BabelTargetEntryOptionPlugin.entryDescriptionToOptions(compiler, name, desc)
63+
for (const entry of desc.import) {
64+
new BabelTargetEntryPlugin(this.targets, context, entry, options).apply(compiler)
65+
}
66+
}
3967
}
4068
return true
4169
})

src/babel.target.entry.plugin.ts

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,32 @@
1-
import { compilation, Compiler, Plugin } from 'webpack'
2-
import Compilation = compilation.Compilation
3-
import Dependency = compilation.Dependency
4-
import NormalModuleFactory = compilation.NormalModuleFactory
1+
import { Compiler, EntryPlugin, Compilation, Dependency, NormalModuleFactory } from 'webpack'
52

63
import { BabelTarget } from './babel-target'
7-
import { BabelTargetSingleEntryDependency } from './babel.target.single.entry.dependency'
84
import { BabelTargetEntryDependency } from './babel.target.entry.dependency'
95

10-
export abstract class BabelTargetEntryPlugin implements Plugin {
11-
12-
protected constructor(protected targets: BabelTarget[], protected context: string, protected name: string) {
13-
}
6+
export class BabelTargetEntryPlugin implements EntryPlugin {
7+
public constructor(protected targets: BabelTarget[], public context: string,
8+
public entry: string, public options: EntryPlugin['options']) {}
149

1510
public apply(compiler: Compiler): void {
1611
compiler.hooks.compilation.tap(
1712
this.constructor.name,
1813
(compilation: Compilation, { normalModuleFactory }: { normalModuleFactory: NormalModuleFactory }) => {
19-
(compilation.dependencyFactories as Map<any, any>).set(
20-
BabelTargetSingleEntryDependency,
14+
compilation.dependencyFactories.set(
15+
BabelTargetEntryDependency,
2116
normalModuleFactory,
2217
)
2318
},
2419
)
20+
21+
compiler.hooks.make.tapPromise(
22+
this.constructor.name,
23+
async (compilation: Compilation) => {
24+
await Promise.all(this.targets.map(async target => {
25+
const dep = new BabelTargetEntryDependency(target, this.entry, (this.options as any).name)
26+
return await this.addEntry(compilation, dep)
27+
}))
28+
},
29+
)
2530
}
2631

2732
protected async addEntry(compilation: Compilation, dep: BabelTargetEntryDependency): Promise<void>

0 commit comments

Comments
 (0)