@financial-times/dotcom-build-code-splitting 7.3.1 → 7.3.2

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@financial-times/dotcom-build-code-splitting",
3
- "version": "7.3.1",
3
+ "version": "7.3.2",
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",
@@ -0,0 +1,3 @@
1
+ exports.privacy1 = function() {
2
+ return 'module-privacy-1-' + Date.now()
3
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "name": "privacy-module-1",
3
+ "main": "index.js"
4
+ }
@@ -0,0 +1,3 @@
1
+ exports.privacy2 = function() {
2
+ return 'module-privacy-2-' + Date.now()
3
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "name": "privacy-module-2",
3
+ "main": "index.js"
4
+ }
@@ -0,0 +1,4 @@
1
+ const { privacy1 } = require('./@financial-times/privacy-module-1');
2
+ const { privacy2 } = require('./@financial-times/privacy-module-2');
3
+
4
+ console.log(privacy1(), privacy2())
@@ -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,4 @@
1
+ export default function createSafeChunkName(name: string) {
2
+ // Remove or replace any non-safe filename characters
3
+ return name.replace('@', '').replace('/', '-')
4
+ }
@@ -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
+ }