-
Notifications
You must be signed in to change notification settings - Fork 906
Expand file tree
/
Copy pathcreateClientPlugin.ts
More file actions
111 lines (91 loc) · 3.41 KB
/
createClientPlugin.ts
File metadata and controls
111 lines (91 loc) · 3.41 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
import { fs } from '@vuepress/utils'
import type { StatsModule, WebpackPluginInstance } from 'webpack'
import type { ClientManifest } from './types.js'
const isJS = (file: string): boolean => /\.js(\?[^.]+)?$/.test(file)
const isCSS = (file: string): boolean => /\.css(\?[^.]+)?$/.test(file)
/**
* Vuepress client plugin
*
* Collecting webpack bundled files info for SSR
*/
export const createClientPlugin = (
outputFile: string,
): WebpackPluginInstance => {
const clientPlugin: WebpackPluginInstance = {
apply(compiler) {
compiler.hooks.emit.tapPromise(
'vuepress-client-plugin',
async (compilation) => {
// get webpack stats object
const {
assets = [],
modules = [],
entrypoints = {},
chunks = [],
} = compilation.getStats().toJson()
// get all files
const allFiles = assets.map((a) => a.name)
// get initial entry files
const initialFiles = Object.keys(entrypoints)
.flatMap(
(name) =>
entrypoints[name].assets?.map((item) => item.name) ?? [],
)
.filter((file) => isJS(file) || isCSS(file))
// get files that should be loaded asynchronously
// i.e. script and style files that are not included in the initial entry files
const asyncFiles = allFiles.filter(
(file) =>
(isJS(file) || isCSS(file)) && !initialFiles.includes(file),
)
// get asset modules
const assetModules = modules.filter(
(
module,
): module is Required<Pick<StatsModule, 'assets'>> & StatsModule =>
Boolean(module.assets?.length),
)
// get modules for client manifest
const manifestModules: ClientManifest['modules'] = {}
const fileToIndex = (file: number | string): number =>
allFiles.indexOf(file.toString())
modules.forEach((module) => {
// ignore modules duplicated in multiple chunks
if (module.chunks?.length !== 1) {
return
}
const cid = module.chunks[0]
const chunk = chunks.find(({ id }) => id === cid)
if (!chunk?.files) {
return
}
// remove appended hash of module identifier
// which is the request string of the module
const request = module.identifier?.replace(/\|\w+$/, '')
// get chunk files index
const files = chunk.files.map(fileToIndex)
// find all asset modules associated with the same chunk
assetModules.forEach((item) => {
if (item.chunks?.some((id) => id === cid)) {
// get asset files
files.push(...item.assets.map(fileToIndex))
}
})
// map the module request to files index
if (request) manifestModules[request] = files
})
// generate client manifest json file
const clientManifest: ClientManifest = {
all: allFiles,
initial: initialFiles,
async: asyncFiles,
modules: manifestModules,
}
const clientManifestJson = JSON.stringify(clientManifest, null, 2)
await fs.outputFile(outputFile, clientManifestJson)
},
)
},
}
return clientPlugin
}