forked from bugsnag/webpack-bugsnag-plugins
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsource-map-uploader-plugin.js
126 lines (107 loc) · 4.3 KB
/
source-map-uploader-plugin.js
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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
'use strict'
const upload = require('bugsnag-sourcemaps').upload
const resolve = require('url').resolve
const parallel = require('run-parallel-limit')
const extname = require('path').extname
const LOG_PREFIX = `[BugsnagSourceMapUploaderPlugin]`
const PUBLIC_PATH_ERR =
'Cannot determine "minifiedUrl" argument for bugsnag-sourcemaps. ' +
'Please set "publicPath" in Webpack config ("output" section) ' +
'or set "publicPath" in BugsnagSourceMapUploaderPlugin constructor.'
class BugsnagSourceMapUploaderPlugin {
constructor (options) {
this.apiKey = options.apiKey
this.publicPath = options.publicPath
this.appVersion = options.appVersion
this.overwrite = options.overwrite
this.endpoint = options.endpoint
this.ignoredBundleExtensions = options.ignoredBundleExtensions || [ '.css' ]
this.validate()
}
validate () {
if (typeof this.apiKey !== 'string' || this.apiKey.length < 1) {
throw new Error(`${LOG_PREFIX} "apiKey" is required`)
}
}
apply (compiler) {
const plugin = (compilation, cb) => {
const compiler = compilation.compiler
const stats = compilation.getStats().toJson()
const publicPath = this.publicPath || stats.publicPath
const outputPath = compilation.getPath(compiler.outputPath)
if (!publicPath) {
console.warn(`${LOG_PREFIX} ${PUBLIC_PATH_ERR}`)
return cb()
}
const chunkToSourceMapDescriptors = chunk => {
// find .map files in this chunk
const maps = chunk.files.filter(file => /.+\.map(\?.*)?$/.test(file))
return maps.map(map => {
// for each *.map file, find a corresponding source file in the chunk
const source = chunk.files.find(file => map.replace('.map', '').endsWith(file))
if (!source) {
console.warn(`${LOG_PREFIX} no corresponding source found for "${map}" in chunk "${chunk.id}"`)
return null
}
if (!compilation.assets[source]) {
console.debug(`${LOG_PREFIX} source asset not found in compilation output "${source}"`)
return null
}
if (!compilation.assets[map]) {
console.debug(`${LOG_PREFIX} source map not found in compilation output "${map}"`)
return null
}
const outputChunkLocation = stripQuery(compiler.outputFileSystem.join(outputPath, source))
const outputSourceMapLocation = stripQuery(compiler.outputFileSystem.join(outputPath, map))
// only include this file if its extension is not in the ignore list
if (this.ignoredBundleExtensions.indexOf(extname(outputChunkLocation)) !== -1) {
return null
}
return {
source: outputChunkLocation,
map: outputSourceMapLocation,
url: resolve(
// ensure publicPath has a trailing slash
publicPath.replace(/[^/]$/, '$&/'),
// ensure source doesn't have a leading slash (sometimes it does, e.g.
// in laravel-mix, but this throws off the url resolve() call) see issue
// for more detail: https://github.com/bugsnag/webpack-bugsnag-plugins/issues/11
source.replace(/^\//, '')
).toString()
}
}).filter(Boolean)
}
const sourceMaps = stats.chunks.map(chunkToSourceMapDescriptors).reduce((accum, ds) => accum.concat(ds), [])
parallel(sourceMaps.map(sm => cb => {
console.log(`${LOG_PREFIX} uploading sourcemap for "${sm.url}"`)
upload(this.getUploadOpts(sm), cb)
}), 1, cb)
}
if (compiler.hooks) {
// webpack v4
compiler.hooks.afterEmit.tapAsync('BugsnagSourceMapUploaderPlugin', plugin)
} else {
// webpack v3
compiler.plugin('after-emit', plugin)
}
}
getUploadOpts (sm) {
const opts = {
apiKey: this.apiKey,
appVersion: this.appVersion,
minifiedUrl: sm.url,
minifiedFile: sm.source,
sourceMap: sm.map
}
if (this.endpoint) opts.endpoint = this.endpoint
if (this.overwrite) opts.overwrite = this.overwrite
return opts
}
}
module.exports = BugsnagSourceMapUploaderPlugin
// removes a querystring from a file path
const stripQuery = file => {
const queryStringIdx = file.indexOf('?')
if (queryStringIdx < 0) return file
return file.substr(0, queryStringIdx)
}