1 | const path = require('path')
|
2 |
|
3 | class BuildJsonPlugin {
|
4 | constructor(options) {
|
5 | const defOpt = {
|
6 | target: '',
|
7 | version: '',
|
8 | debug: false,
|
9 | marax: '',
|
10 | env: 'online',
|
11 | publicPath: null,
|
12 | basePath: '',
|
13 | zenJs: false
|
14 | }
|
15 |
|
16 | this.fileName = 'build.json'
|
17 | this.options = Object.assign(defOpt, options)
|
18 | }
|
19 |
|
20 | apply(compiler) {
|
21 | this.moduleAssets = {}
|
22 |
|
23 | const pluginOptions = {
|
24 | name: this.constructor.name,
|
25 | stage: Infinity
|
26 | }
|
27 |
|
28 | compiler.hooks.compilation.tap(pluginOptions, compilation => {
|
29 | compilation.hooks.moduleAsset.tap(
|
30 | pluginOptions,
|
31 | this.collectModuleAsset.bind(this)
|
32 | )
|
33 | })
|
34 |
|
35 | compiler.hooks.emit.tap(pluginOptions, compilation => {
|
36 | const publicPath =
|
37 | this.options.publicPath != null
|
38 | ? this.options.publicPath
|
39 | : compilation.options.output.publicPath
|
40 |
|
41 | const manifest = this.getAssetsManifest(compilation, publicPath)
|
42 |
|
43 | compilation.assets[this.fileName] = this.getBuildJson(
|
44 | manifest,
|
45 | publicPath
|
46 | )
|
47 | })
|
48 | }
|
49 |
|
50 | getBuildJson(manifest = {}, publicPath) {
|
51 | const source = JSON.stringify(
|
52 | {
|
53 |
|
54 | target: this.options.target,
|
55 |
|
56 | version: this.options.version,
|
57 |
|
58 | env: this.options.env,
|
59 |
|
60 | debug: this.options.debug,
|
61 |
|
62 | marax: this.options.marax,
|
63 | publicPath: publicPath,
|
64 |
|
65 | manifest: manifest
|
66 | },
|
67 | null,
|
68 | 2
|
69 | )
|
70 |
|
71 | return {
|
72 | source: () => source,
|
73 | size: () => source.length
|
74 | }
|
75 | }
|
76 |
|
77 | sortAssetField(files) {
|
78 | const isJS = val => /\.js$/.test(val)
|
79 | const isCSS = val => /\.css$/.test(val)
|
80 |
|
81 | return files.sort((a, b) => {
|
82 | if (isJS(a.name) && isCSS(b.name)) return -1
|
83 | if (isCSS(a.name) && isJS(b.name)) return 1
|
84 |
|
85 | return 1
|
86 | })
|
87 | }
|
88 |
|
89 | collectModuleAsset(module, file) {
|
90 | if (module.userRequest) {
|
91 | this.moduleAssets[file] = path.join(
|
92 | path.dirname(file),
|
93 | path.basename(module.userRequest)
|
94 | )
|
95 | }
|
96 | }
|
97 |
|
98 | getAssetsManifest(compilation, publicPath) {
|
99 | const stats = compilation.getStats()
|
100 |
|
101 | const getFileType = str => {
|
102 | str = str.replace(/\?.*/, '')
|
103 |
|
104 | return str.split('.').pop()
|
105 | }
|
106 |
|
107 | let files = compilation.chunks.reduce((files, chunk) => {
|
108 | return chunk.files.reduce((files, path) => {
|
109 | let name = chunk.name ? chunk.name : null
|
110 | const isInitial = chunk.isOnlyInitial()
|
111 |
|
112 | if (name) {
|
113 | const fileType = getFileType(path)
|
114 |
|
115 | if (isInitial) {
|
116 | name = `static/${fileType}/` + name
|
117 | }
|
118 |
|
119 | name += '.' + fileType
|
120 | } else {
|
121 |
|
122 | name = path
|
123 | }
|
124 |
|
125 | if (this.options.zenJs && isInitial) {
|
126 | path = path.replace(/\.js$/, '')
|
127 | }
|
128 |
|
129 |
|
130 |
|
131 |
|
132 | return files.concat({
|
133 | path,
|
134 | chunk,
|
135 | name,
|
136 | isInitial,
|
137 | isChunk: true,
|
138 | isAsset: false,
|
139 | isModuleAsset: false
|
140 | })
|
141 | }, files)
|
142 | }, [])
|
143 |
|
144 |
|
145 |
|
146 | files = stats.toJson().assets.reduce((files, asset) => {
|
147 | const name = this.moduleAssets[asset.name]
|
148 |
|
149 | if (name) {
|
150 | return files.concat({
|
151 | path: asset.name,
|
152 | name: name,
|
153 | isInitial: false,
|
154 | isChunk: false,
|
155 | isAsset: true,
|
156 | isModuleAsset: true
|
157 | })
|
158 | }
|
159 |
|
160 | const isEntryAsset = asset.chunks.length > 0
|
161 |
|
162 | if (isEntryAsset) return files
|
163 |
|
164 |
|
165 | let fixAssetName = asset.name
|
166 | if (
|
167 | stats.compilation.assets[asset.name] &&
|
168 | stats.compilation.assets[asset.name]._fileExt === 'js'
|
169 | ) {
|
170 | fixAssetName = asset.name.replace(/\.js$/, '') + '.js'
|
171 | }
|
172 |
|
173 | return files.concat({
|
174 | path: asset.name,
|
175 | name: fixAssetName,
|
176 | isInitial: false,
|
177 | isChunk: false,
|
178 | isAsset: true,
|
179 | isModuleAsset: false
|
180 | })
|
181 | }, files)
|
182 |
|
183 | files = files.filter(file => {
|
184 |
|
185 | const isUpdateChunk = file.path.indexOf('hot-update') >= 0
|
186 |
|
187 | return !isUpdateChunk
|
188 | })
|
189 |
|
190 |
|
191 |
|
192 | if (this.options.basePath) {
|
193 | files = files.map(file => {
|
194 | file.name = this.options.basePath + file.name
|
195 | return file
|
196 | })
|
197 | }
|
198 |
|
199 | if (publicPath) {
|
200 |
|
201 |
|
202 |
|
203 | files = files.map(file => {
|
204 | file.path = publicPath + file.path
|
205 | return file
|
206 | })
|
207 | }
|
208 |
|
209 | files = files.map(file => {
|
210 | file.name = file.name.replace(/\\/g, '/')
|
211 | file.path = file.path.replace(/\\/g, '/')
|
212 |
|
213 | return file
|
214 | })
|
215 |
|
216 | files = this.sortAssetField(files)
|
217 |
|
218 | return files.reduce((manifest, file) => {
|
219 | manifest[file.name] = file.path
|
220 |
|
221 | return manifest
|
222 | }, {})
|
223 | }
|
224 | }
|
225 |
|
226 | module.exports = BuildJsonPlugin
|