@financial-times/dotcom-build-code-splitting 7.3.1 → 7.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +4 -3
- package/src/__test__/__fixtures__/@financial-times/privacy-module-1/index.js +3 -0
- package/src/__test__/__fixtures__/@financial-times/privacy-module-1/package.json +4 -0
- package/src/__test__/__fixtures__/@financial-times/privacy-module-2/index.js +3 -0
- package/src/__test__/__fixtures__/@financial-times/privacy-module-2/package.json +4 -0
- package/src/__test__/__fixtures__/entry-point.js +4 -0
- package/src/__test__/index.spec.ts +36 -0
- package/src/bundleTypes.ts +168 -0
- package/src/createSafeChunkName.ts +4 -0
- package/src/extractPackageName.ts +12 -0
- package/src/index.ts +109 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@financial-times/dotcom-build-code-splitting",
|
|
3
|
-
"version": "7.3.
|
|
3
|
+
"version": "7.3.3",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/node/index.js",
|
|
6
6
|
"types": "src/index.ts",
|
|
@@ -36,7 +36,8 @@
|
|
|
36
36
|
"npm": "7.x || 8.x"
|
|
37
37
|
},
|
|
38
38
|
"files": [
|
|
39
|
-
"dist/"
|
|
39
|
+
"dist/",
|
|
40
|
+
"src/"
|
|
40
41
|
],
|
|
41
42
|
"repository": {
|
|
42
43
|
"type": "git",
|
|
@@ -47,4 +48,4 @@
|
|
|
47
48
|
"volta": {
|
|
48
49
|
"extends": "../../package.json"
|
|
49
50
|
}
|
|
50
|
-
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { PageKitCodeSplittingPlugin } from '../index'
|
|
2
|
+
import webpack from 'webpack'
|
|
3
|
+
import path from 'path'
|
|
4
|
+
|
|
5
|
+
describe('dotcom-build-code-splitting', () => {
|
|
6
|
+
it('create chunk for privacy modules', async () => {
|
|
7
|
+
await new Promise((resolve) =>
|
|
8
|
+
webpack(
|
|
9
|
+
{
|
|
10
|
+
mode: 'none',
|
|
11
|
+
entry: {
|
|
12
|
+
scripts: path.join(__dirname, '/__fixtures__', 'entry-point.js')
|
|
13
|
+
},
|
|
14
|
+
output: {
|
|
15
|
+
filename: '[name].js',
|
|
16
|
+
path: path.join(__dirname, '/tmp')
|
|
17
|
+
},
|
|
18
|
+
plugins: [new PageKitCodeSplittingPlugin()]
|
|
19
|
+
},
|
|
20
|
+
function (error, stats) {
|
|
21
|
+
if (error) {
|
|
22
|
+
throw error
|
|
23
|
+
} else if (stats.hasErrors()) {
|
|
24
|
+
throw stats.toString()
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const files = stats.toJson().assets.map((asset) => asset.name)
|
|
28
|
+
|
|
29
|
+
expect(files).toEqual(expect.arrayContaining(['privacy-components.js']))
|
|
30
|
+
|
|
31
|
+
resolve()
|
|
32
|
+
}
|
|
33
|
+
)
|
|
34
|
+
)
|
|
35
|
+
})
|
|
36
|
+
})
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import extractPackageName from './extractPackageName'
|
|
2
|
+
import createSafeChunkName from './createSafeChunkName'
|
|
3
|
+
import DisableTreeShakingForChunk from 'disable-tree-shaking-for-chunk-plugin'
|
|
4
|
+
import type webpack from 'webpack'
|
|
5
|
+
|
|
6
|
+
interface IBundleWithPackageNames {
|
|
7
|
+
compiler: webpack.Compiler
|
|
8
|
+
name: string
|
|
9
|
+
packages: string[]
|
|
10
|
+
// This prevents the tracking of named exports and their usage
|
|
11
|
+
usedInUnknownWay?: boolean
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface IBundleWithRegExp {
|
|
15
|
+
compiler: webpack.Compiler
|
|
16
|
+
name: string
|
|
17
|
+
pattern: RegExp
|
|
18
|
+
// This prevents the tracking of named exports and their usage
|
|
19
|
+
usedInUnknownWay?: boolean
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const isJS = (module) => module.type && module.type.startsWith('javascript/')
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Create a chunk which includes all packages in the given list of names
|
|
26
|
+
*/
|
|
27
|
+
export function createBundleWithPackages({
|
|
28
|
+
compiler,
|
|
29
|
+
name,
|
|
30
|
+
packages,
|
|
31
|
+
usedInUnknownWay
|
|
32
|
+
}: IBundleWithPackageNames) {
|
|
33
|
+
if (usedInUnknownWay) {
|
|
34
|
+
const disableTreeShakingPlugin = new DisableTreeShakingForChunk({
|
|
35
|
+
test: name
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
disableTreeShakingPlugin.apply(compiler)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
optimization: {
|
|
43
|
+
splitChunks: {
|
|
44
|
+
cacheGroups: {
|
|
45
|
+
[name]: {
|
|
46
|
+
name,
|
|
47
|
+
test: (module) => {
|
|
48
|
+
const packageName = isJS(module) && extractPackageName(module.context)
|
|
49
|
+
return packageName ? packages.includes(packageName) : false
|
|
50
|
+
},
|
|
51
|
+
enforce: true
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Create a chunk which includes all modules which match the given pattern
|
|
61
|
+
*/
|
|
62
|
+
export function createBundleWithRegExp({ compiler, name, pattern, usedInUnknownWay }: IBundleWithRegExp) {
|
|
63
|
+
if (usedInUnknownWay) {
|
|
64
|
+
const disableTreeShakingPlugin = new DisableTreeShakingForChunk({
|
|
65
|
+
test: name
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
disableTreeShakingPlugin.apply(compiler)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
optimization: {
|
|
73
|
+
splitChunks: {
|
|
74
|
+
cacheGroups: {
|
|
75
|
+
[name]: {
|
|
76
|
+
name,
|
|
77
|
+
test: (module) => {
|
|
78
|
+
return isJS(module) && pattern.test(module.context)
|
|
79
|
+
},
|
|
80
|
+
enforce: true
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Create a chunk for each package in the given list of names
|
|
90
|
+
*/
|
|
91
|
+
export function createBundlesForPackages({
|
|
92
|
+
compiler,
|
|
93
|
+
name,
|
|
94
|
+
packages,
|
|
95
|
+
usedInUnknownWay
|
|
96
|
+
}: IBundleWithPackageNames) {
|
|
97
|
+
const generatedChunkNames = new Set()
|
|
98
|
+
|
|
99
|
+
if (usedInUnknownWay) {
|
|
100
|
+
const disableTreeShakingPlugin = new DisableTreeShakingForChunk({
|
|
101
|
+
test: generatedChunkNames
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
disableTreeShakingPlugin.apply(compiler)
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
optimization: {
|
|
109
|
+
splitChunks: {
|
|
110
|
+
cacheGroups: {
|
|
111
|
+
[name]: {
|
|
112
|
+
name(module) {
|
|
113
|
+
const packageName = extractPackageName(module.context)
|
|
114
|
+
const chunkName = createSafeChunkName(packageName)
|
|
115
|
+
|
|
116
|
+
generatedChunkNames.add(chunkName)
|
|
117
|
+
|
|
118
|
+
return chunkName
|
|
119
|
+
},
|
|
120
|
+
test(module) {
|
|
121
|
+
const packageName = isJS(module) && extractPackageName(module.context)
|
|
122
|
+
return packageName ? packages.includes(packageName) : false
|
|
123
|
+
},
|
|
124
|
+
enforce: true
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Create a chunk for each group of modules which match the given pattern
|
|
134
|
+
*/
|
|
135
|
+
export function createBundlesForRegExp({ compiler, name, pattern, usedInUnknownWay }: IBundleWithRegExp) {
|
|
136
|
+
const generatedChunkNames = new Set()
|
|
137
|
+
|
|
138
|
+
if (usedInUnknownWay) {
|
|
139
|
+
const disableTreeShakingPlugin = new DisableTreeShakingForChunk({
|
|
140
|
+
test: generatedChunkNames
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
disableTreeShakingPlugin.apply(compiler)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return {
|
|
147
|
+
optimization: {
|
|
148
|
+
splitChunks: {
|
|
149
|
+
cacheGroups: {
|
|
150
|
+
[name]: {
|
|
151
|
+
name(module) {
|
|
152
|
+
const packageName = extractPackageName(module.context)
|
|
153
|
+
const chunkName = createSafeChunkName(packageName)
|
|
154
|
+
|
|
155
|
+
generatedChunkNames.add(chunkName)
|
|
156
|
+
|
|
157
|
+
return chunkName
|
|
158
|
+
},
|
|
159
|
+
test(module) {
|
|
160
|
+
return isJS(module) && pattern.test(module.context)
|
|
161
|
+
},
|
|
162
|
+
enforce: true
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import memoize from 'memoize-one'
|
|
2
|
+
import getPackageName from 'get-package-name'
|
|
3
|
+
|
|
4
|
+
const moduleType = /(node_modules)/
|
|
5
|
+
|
|
6
|
+
function extractPackageName(modulePath: string) {
|
|
7
|
+
const type = modulePath.match(moduleType)
|
|
8
|
+
return Array.isArray(type) ? getPackageName(modulePath, type[type.length - 1]) : null
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// Memoize these calls as modules often need to be resolved many times.
|
|
12
|
+
export default memoize(extractPackageName)
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import assignDeep from 'assign-deep'
|
|
2
|
+
import ReliableModuleIdsPlugin from 'reliable-module-ids-plugin'
|
|
3
|
+
import type webpack from 'webpack'
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
createBundleWithPackages,
|
|
7
|
+
createBundleWithRegExp,
|
|
8
|
+
createBundlesForPackages,
|
|
9
|
+
createBundlesForRegExp
|
|
10
|
+
} from './bundleTypes'
|
|
11
|
+
|
|
12
|
+
export class PageKitCodeSplittingPlugin {
|
|
13
|
+
apply(compiler: webpack.Compiler) {
|
|
14
|
+
const addInitialCodeSplitting = {
|
|
15
|
+
optimization: {
|
|
16
|
+
// Creates a separate bundle for webpack runtime.
|
|
17
|
+
// Specifying the name prevents multiple runtime bundles from being created.
|
|
18
|
+
runtimeChunk: {
|
|
19
|
+
name: 'webpack-runtime'
|
|
20
|
+
},
|
|
21
|
+
splitChunks: {
|
|
22
|
+
chunks: 'all'
|
|
23
|
+
},
|
|
24
|
+
// We're going to implement our own algorithm so don't double effort
|
|
25
|
+
moduleIds: false,
|
|
26
|
+
chunkIds: 'named'
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Split each o-, n-, x- and next- prefixed packages into a separate bundles
|
|
31
|
+
// NOTE: we need to check we're in a package directory as our apps are usually prefixed with "next-"
|
|
32
|
+
const addComponentCodeSplitting = createBundlesForRegExp({
|
|
33
|
+
compiler,
|
|
34
|
+
name: 'shared-components',
|
|
35
|
+
pattern: /(node_modules\/@financial-times)\/(o|n|x|next)-/,
|
|
36
|
+
usedInUnknownWay: true
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
// split all dotcom-ui- packages into one bundle file
|
|
40
|
+
const addPageKitCodeSplittingPlugin = createBundleWithRegExp({
|
|
41
|
+
compiler,
|
|
42
|
+
name: 'page-kit-components',
|
|
43
|
+
pattern: /[\\\/]dotcom-ui-/,
|
|
44
|
+
usedInUnknownWay: true
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
// split any of these JS frameworks and libraries into separate bundle files
|
|
48
|
+
const addLibraryCodeSplitting = createBundlesForPackages({
|
|
49
|
+
compiler,
|
|
50
|
+
name: 'js-frameworks',
|
|
51
|
+
packages: ['react', 'preact', 'hyperons', 'dateformat', 'regenerator-runtime']
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
// These packages are a dependency of ads, marketing, MyFT, syndication, cookie banners
|
|
55
|
+
// and other components but they are not all dependencies of our apps.
|
|
56
|
+
const addSuperstoreCodeSplitting = createBundleWithPackages({
|
|
57
|
+
compiler,
|
|
58
|
+
name: 'superstore',
|
|
59
|
+
packages: ['superstore', 'superstore-sync'],
|
|
60
|
+
usedInUnknownWay: true
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
// split all privacy- packages into one bundle file
|
|
64
|
+
const addPrivacyCodeSplitting = createBundleWithRegExp({
|
|
65
|
+
compiler,
|
|
66
|
+
name: 'privacy-components',
|
|
67
|
+
pattern: /@financial-times\/privacy-/,
|
|
68
|
+
usedInUnknownWay: true
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
// split packages used by all pages (i.e. used by Page Kit) into a shared bundle
|
|
72
|
+
const addSharedStableCodeSplitting = createBundleWithPackages({
|
|
73
|
+
compiler,
|
|
74
|
+
name: 'shared.stable',
|
|
75
|
+
packages: [
|
|
76
|
+
'focus-visible',
|
|
77
|
+
'fontfaceobserver',
|
|
78
|
+
'ftdomdelegate',
|
|
79
|
+
'morphdom',
|
|
80
|
+
'n-topic-search',
|
|
81
|
+
'n-ui-foundations',
|
|
82
|
+
'ready-state'
|
|
83
|
+
],
|
|
84
|
+
usedInUnknownWay: true
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
// split packages which are commonly used together around FT.com into a shared bundle
|
|
88
|
+
const addSharedVolatileCodeSplitting = createBundleWithPackages({
|
|
89
|
+
compiler,
|
|
90
|
+
name: 'shared.volatile',
|
|
91
|
+
packages: ['@financial-times/n-ads', '@financial-times/n-tracking', 'n-syndication', 'n-feedback'],
|
|
92
|
+
usedInUnknownWay: true
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
new ReliableModuleIdsPlugin().apply(compiler)
|
|
96
|
+
|
|
97
|
+
assignDeep(
|
|
98
|
+
compiler.options,
|
|
99
|
+
addInitialCodeSplitting,
|
|
100
|
+
addPageKitCodeSplittingPlugin,
|
|
101
|
+
addLibraryCodeSplitting,
|
|
102
|
+
addComponentCodeSplitting,
|
|
103
|
+
addSuperstoreCodeSplitting,
|
|
104
|
+
addPrivacyCodeSplitting,
|
|
105
|
+
addSharedStableCodeSplitting,
|
|
106
|
+
addSharedVolatileCodeSplitting
|
|
107
|
+
)
|
|
108
|
+
}
|
|
109
|
+
}
|