Skip to content

Commit 520acf7

Browse files
committed
feat: support webpack v5
1 parent f6134c8 commit 520acf7

25 files changed

+581
-1208
lines changed
-7.71 KB
Binary file not shown.

package.json

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,6 @@
6767
"dependencies": {
6868
"@babel/cli": "^7.10.5",
6969
"@babel/core": "^7.10.5",
70-
"@babel/plugin-syntax-dynamic-import": "^7.8.3",
71-
"@babel/plugin-transform-runtime": "^7.10.5",
7270
"@babel/preset-env": "^7.10.4",
7371
"@babel/runtime": "^7.10.5",
7472
"babel-loader": "^8.1.0",

src/babel-target.ts

Lines changed: 46 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
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 { ModuleGraph, ChunkGraph } from 'webpack'
3+
4+
import Entrypoint = require('webpack/lib/Entrypoint')
5+
import Chunk = require('webpack/lib/Chunk')
6+
import ChunkGroup = require('webpack/lib/ChunkGroup')
7+
import Module = require('webpack/lib/Module')
78

89
import { BabelLoaderCacheDirectoryOption } from './babel.multi.target.options'
910
import { BabelTargetOptions } from './babel.target.options'
11+
import { BabelTargetEntryDependency } from './babel.target.entry.dependency'
1012
import { BrowserProfileName, StandardBrowserProfileName } from './browser.profile.name'
1113
import { DEV_SERVER_CLIENT } from './constants'
1214
import {
13-
DEFAULT_BABEL_PLUGINS,
1415
DEFAULT_BABEL_PRESET_OPTIONS,
1516
DEFAULT_BROWSERS,
1617
DEFAULT_TARGET_INFO,
@@ -28,67 +29,6 @@ export type BabelTargetInfo = { [TOption in keyof BabelTargetOptions]: BabelTarg
2829
readonly options: BabelLoaderTransformOptions
2930
}
3031

31-
// webpack doesn't actually export things from the `compilation` namespace, so can only use them to
32-
// type checking, not anything at runtime like instanceof
33-
// so, need to do this instead
34-
const SIG = {
35-
module: [
36-
'disconnect',
37-
'unseal',
38-
'isEntryModule',
39-
'isInChunk',
40-
],
41-
entrypoint: [
42-
'isInitial',
43-
'getFiles',
44-
'getRuntimeChunk',
45-
'setRuntimeChunk',
46-
],
47-
chunk: [
48-
'hasRuntime',
49-
'canBeInitial',
50-
'isOnlyInitial',
51-
'hasEntryModule',
52-
'addModule',
53-
'removeModule',
54-
'setModules',
55-
'getNumberOfModules',
56-
'addGroup',
57-
'isInGroup',
58-
'canBeIntegrated',
59-
],
60-
chunkGroup: [
61-
'unshiftChunk',
62-
'insertChunk',
63-
'pushChunk',
64-
'replaceChunk',
65-
'isInitial',
66-
'addChild',
67-
'getChildren',
68-
'getNumberOfChildren',
69-
],
70-
}
71-
72-
function hasSig(obj: any, sig: string[]): boolean {
73-
return sig.every(name => typeof obj[name] === 'function')
74-
}
75-
76-
function isModule(obj: any): obj is Module {
77-
return hasSig(obj, SIG.module)
78-
}
79-
80-
function isEntrypoint(obj: any): obj is Entrypoint {
81-
return hasSig(obj, SIG.entrypoint)
82-
}
83-
84-
function isChunkGroup(obj: any): obj is ChunkGroup {
85-
return hasSig(obj, SIG.chunkGroup)
86-
}
87-
88-
function isChunk(obj: any): obj is Chunk {
89-
return hasSig(obj, SIG.chunk)
90-
}
91-
9232
export class BabelTarget implements BabelTargetInfo {
9333

9434
public readonly profileName: BrowserProfileName
@@ -105,7 +45,11 @@ export class BabelTarget implements BabelTargetInfo {
10545
}
10646

10747
public getTargetedAssetName(name: string): string {
108-
return this.tagAssetsWithKey ? `${name}.${this.key}` : name
48+
// sometimes a asset will be targeted twice
49+
if (this.tagAssetsWithKey && !name.endsWith(`.${this.key}`)) {
50+
return `${name}.${this.key}`
51+
}
52+
return name
10953
}
11054

11155
public getTargetedRequest(request: string): string {
@@ -136,21 +80,20 @@ export class BabelTarget implements BabelTargetInfo {
13680
return targets.find(target => target.key === key)
13781
}
13882

139-
public static getTargetFromModule(module: Module): BabelTarget {
140-
if (module.options && module.options.babelTarget) {
83+
public static getTargetFromModule(module: Module, moduleGraph: ModuleGraph): BabelTarget {
84+
if (module.options?.babelTarget) {
14185
return module.options.babelTarget
14286
}
14387

144-
if (!module.reasons) {
145-
return undefined
146-
}
88+
// https://github.com/webpack/webpack/pull/7826/commits/381e2db2009b0020de358c23c702d074806980a5
89+
const reasons = Array.from(moduleGraph.getIncomingConnections(module))
14790

148-
for (const reason of module.reasons) {
149-
if (reason.dependency && reason.dependency.babelTarget) {
91+
for (const reason of reasons) {
92+
if (reason.dependency && reason.dependency instanceof BabelTargetEntryDependency) {
15093
return reason.dependency.babelTarget
15194
}
152-
if (reason.module) {
153-
const target = BabelTarget.getTargetFromModule(reason.module)
95+
if (reason.module && reason.module !== module) {
96+
const target = BabelTarget.getTargetFromModule(reason.module, moduleGraph)
15497
if (target) {
15598
return target
15699
}
@@ -161,39 +104,41 @@ export class BabelTarget implements BabelTargetInfo {
161104

162105
}
163106

164-
public static getTargetFromEntrypoint(entrypoint: Entrypoint): BabelTarget {
165-
if (!entrypoint.runtimeChunk.hasEntryModule()) {
107+
public static getTargetFromEntrypoint(entrypoint: Entrypoint, moduleGraph: ModuleGraph, chunkGraph: ChunkGraph): BabelTarget {
108+
const runtime = entrypoint.getRuntimeChunk()
109+
const modules = Array.from(chunkGraph.getChunkEntryModulesIterable(runtime))
110+
if (modules.length === 0) {
166111
return undefined
167112
}
168-
return BabelTarget.getTargetFromModule(entrypoint.runtimeChunk.entryModule)
113+
return BabelTarget.getTargetFromModule(modules[0], moduleGraph)
169114
}
170115

171116
// eslint-disable-next-line
172117
public static getTargetFromGroup(group: ChunkGroup): BabelTarget {
173118
return undefined
174119
}
175120

176-
public static getTargetFromChunk(chunk: Chunk): BabelTarget {
177-
if (chunk.entryModule) {
178-
return BabelTarget.getTargetFromModule(chunk.entryModule)
121+
public static getTargetFromChunk(chunk: Chunk, moduleGraph: ModuleGraph, chunkGraph: ChunkGraph): BabelTarget {
122+
const modules = Array.from(chunkGraph.getChunkEntryModulesIterable(chunk))
123+
if (modules.length === 0) {
124+
return undefined
179125
}
180-
181-
return undefined
126+
return BabelTarget.getTargetFromModule(modules[0], moduleGraph)
182127
}
183128

184-
public static findTarget(source: BabelTargetSource): BabelTarget {
129+
public static findTarget(source: BabelTargetSource, moduleGraph: ModuleGraph, chunkGraph: ChunkGraph): BabelTarget {
185130

186-
if (isModule(source)) {
187-
return BabelTarget.getTargetFromModule(source)
131+
if (source instanceof Module) {
132+
return BabelTarget.getTargetFromModule(source, moduleGraph)
188133
}
189-
if (isEntrypoint(source)) {
190-
return BabelTarget.getTargetFromEntrypoint(source)
134+
if (source instanceof Entrypoint) {
135+
return BabelTarget.getTargetFromEntrypoint(source, moduleGraph, chunkGraph)
191136
}
192-
if (isChunkGroup(source)) {
137+
if (source instanceof ChunkGroup) {
193138
return BabelTarget.getTargetFromGroup(source)
194139
}
195-
if (isChunk(source)) {
196-
return BabelTarget.getTargetFromChunk(source)
140+
if (source instanceof Chunk) {
141+
return BabelTarget.getTargetFromChunk(source, moduleGraph, chunkGraph)
197142
}
198143

199144
return undefined
@@ -228,18 +173,14 @@ export class BabelTargetFactory {
228173

229174
public createTransformOptions(key: string, browsers: string[], loaderOptions: { cacheDirectory?: BabelLoaderCacheDirectoryOption }): BabelLoaderTransformOptions {
230175

231-
const mergedPresetOptions: BabelPresetOptions = Object.assign(
232-
{},
233-
DEFAULT_BABEL_PRESET_OPTIONS,
234-
this.presetOptions,
235-
{
236-
targets: {
237-
browsers,
238-
},
239-
}, {
240-
modules: false,
176+
const mergedPresetOptions: BabelPresetOptions = {
177+
...DEFAULT_BABEL_PRESET_OPTIONS,
178+
...this.presetOptions,
179+
targets: {
180+
browsers,
241181
},
242-
)
182+
modules: false,
183+
}
243184

244185
const cacheDirectory = this.getCacheDirectory(key, loaderOptions.cacheDirectory)
245186

@@ -249,7 +190,7 @@ export class BabelTargetFactory {
249190
...this.presets,
250191
],
251192
plugins: [
252-
...DEFAULT_BABEL_PLUGINS,
193+
// https://github.com/babel/babel/issues/10271#issuecomment-528379505
253194
...this.plugins,
254195
],
255196
cacheDirectory,

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

Lines changed: 20 additions & 28 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,13 @@ export class BabelMultiTargetHtmlUpdater implements Plugin {
2016

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

23-
public updateScriptTags(chunkMap: TargetedChunkMap, tags: HtmlTag[]): void {
24-
19+
public updateScriptTags(chunkMap: TargetedChunkMap, tags: HtmlTagObject[]): void {
2520
tags
26-
.forEach((tag: HtmlTag) => {
21+
.forEach((tag: HtmlTagObject) => {
2722
if (tag.tagName !== 'script') {
2823
return
2924
}
30-
31-
const targetedChunks = chunkMap.get(tag.attributes.src)
25+
const targetedChunks = chunkMap.get(tag.attributes.src as string)
3226
// chunks that are added outside of an entry point (e.g. by HtmlWebpackIncludeAssetsPlugin) will not be targeted
3327
if (!targetedChunks) {
3428
return
@@ -83,7 +77,7 @@ export class BabelMultiTargetHtmlUpdater implements Plugin {
8377
public apply(compiler: Compiler): void {
8478

8579
compiler.hooks.afterPlugins.tap(PLUGIN_NAME, () => {
86-
const htmlWebpackPlugin: HtmlWebpackPlugin = compiler.options.plugins
80+
const htmlWebpackPlugin = compiler.options.plugins
8781
// instanceof can act wonky since we don't actually keep our own dependency on html-webpack-plugin
8882
// should we?
8983
.find(plugin => plugin.constructor.name === 'HtmlWebpackPlugin') as any
@@ -94,18 +88,16 @@ export class BabelMultiTargetHtmlUpdater implements Plugin {
9488

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

99-
if ((htmlWebpackPlugin.options.chunks as any) !== 'all' &&
100-
htmlWebpackPlugin.options.chunks &&
101-
htmlWebpackPlugin.options.chunks.length
93+
if ((htmlWebpackPlugin.userOptions.chunks) !== 'all' &&
94+
htmlWebpackPlugin.userOptions.chunks?.length
10295
) {
103-
htmlWebpackPlugin.options.chunks = this.mapChunkNames(htmlWebpackPlugin.options.chunks as string[])
96+
htmlWebpackPlugin.userOptions.chunks = this.mapChunkNames(htmlWebpackPlugin.userOptions.chunks as string[])
10497
}
10598

106-
if (htmlWebpackPlugin.options.excludeChunks &&
107-
htmlWebpackPlugin.options.excludeChunks.length) {
108-
htmlWebpackPlugin.options.excludeChunks = this.mapChunkNames(htmlWebpackPlugin.options.excludeChunks)
99+
if (htmlWebpackPlugin.userOptions.excludeChunks?.length) {
100+
htmlWebpackPlugin.userOptions.excludeChunks = this.mapChunkNames(htmlWebpackPlugin.userOptions.excludeChunks)
109101
}
110102

111103
compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation: Compilation) => {
@@ -114,20 +106,20 @@ export class BabelMultiTargetHtmlUpdater implements Plugin {
114106
return
115107
}
116108

117-
const hook = getAlterAssetTags(compilation)
118-
119-
hook.tapPromise(`${PLUGIN_NAME} update asset tags`,
120-
async (htmlPluginData: AlterAssetTagsData) => {
109+
getHooks(compilation).alterAssetTagGroups.tapPromise(`${PLUGIN_NAME} update asset tags`,
110+
async (htmlPluginData) => {
111+
const hash = compilation.hash
112+
const publicPath = compilation.getAssetPath(compilation.outputOptions.publicPath, { hash })
121113
const chunkMap: TargetedChunkMap = compilation.chunkGroups.reduce((result: TargetedChunkMap, chunkGroup: ChunkGroup) => {
122114
chunkGroup.chunks.forEach((chunk: Chunk) => {
123115
chunk.files.forEach((file: string) => {
124-
result.set(file, chunkGroup, chunk)
116+
result.set(file, chunkGroup, chunk, compilation.chunkGraph, compilation.moduleGraph)
125117
})
126118
})
127119
return result
128-
}, new TargetedChunkMap(compiler.options.output.publicPath))
129-
this.updateScriptTags(chunkMap, getHeadTags(htmlPluginData))
130-
this.updateScriptTags(chunkMap, getBodyTags(htmlPluginData))
120+
}, new TargetedChunkMap(publicPath))
121+
this.updateScriptTags(chunkMap, htmlPluginData.headTags)
122+
this.updateScriptTags(chunkMap, htmlPluginData.bodyTags)
131123
return htmlPluginData
132124
})
133125

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,43 @@
1-
import { BabelTarget } from './babel-target'
1+
import ModuleDependency = require('webpack/lib/dependencies/ModuleDependency')
22

3-
import Dependency = require('webpack/lib/Dependency')
3+
import { BabelTarget } from './babel-target'
4+
import { DEV_SERVER_CLIENT } from './constants'
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+
// @ts-ignore
18+
// webpack official typing is wrong
19+
public get type(): string {
20+
return 'babel target entry'
21+
}
22+
23+
public getResourceIdentifier(): string {
24+
return `module${this.request}!${this.babelTarget.key}`
25+
}
26+
27+
// @ts-ignore
28+
public get category(): string {
29+
return 'esm'
30+
}
31+
32+
constructor(public babelTarget: BabelTarget, request: string, public originalName: string, loc?: EntryLoc) {
33+
super(`${request.startsWith(DEV_SERVER_CLIENT) ? request : babelTarget.getTargetedRequest(request)}`)
34+
35+
this.name = babelTarget.getTargetedAssetName(originalName)
36+
if (!loc) {
37+
loc = { name: `${this.request}:${babelTarget.key}` }
38+
} else {
39+
loc.name += `:${babelTarget.key}`
40+
}
41+
this.loc = loc
42+
}
1443
}

0 commit comments

Comments
 (0)