UNPKG

5.46 kBJavaScriptView Raw
1const path = require('path')
2
3class 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 // build target
54 target: this.options.target,
55 // project version
56 version: this.options.version,
57 // deploy env
58 env: this.options.env,
59 // debug mode
60 debug: this.options.debug,
61 // marax version
62 marax: this.options.marax,
63 publicPath: publicPath,
64 // assets manifest
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 // For nameless chunks, just map the files directly.
122 name = path
123 }
124
125 if (this.options.zenJs && isInitial) {
126 path = path.replace(/\.js$/, '')
127 }
128
129 // Webpack 4: .isOnlyInitial()
130 // Webpack 3: .isInitial()
131 // Webpack 1/2: .initial
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 // module assets don't show up in assetsByChunkName.
145 // we're getting them this way;
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 // 兼容 zenMode
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 // Don't add hot updates to manifest
185 const isUpdateChunk = file.path.indexOf('hot-update') >= 0
186
187 return !isUpdateChunk
188 })
189
190 // Append optional basepath onto all references.
191 // This allows output path to be reflected in the manifest.
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 // Similar to basePath but only affects the value (similar to how
201 // output.publicPath turns require('foo/bar') into '/public/foo/bar', see
202 // https://github.com/webpack/docs/wiki/configuration#outputpublicpath
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
226module.exports = BuildJsonPlugin