@sentry/webpack-plugin 1.19.1 → 2.0.0-alpha.1

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/src/index.js DELETED
@@ -1,587 +0,0 @@
1
- const SentryCli = require('@sentry/cli');
2
- const path = require('path');
3
- const util = require('util');
4
- const { RawSource } = require('webpack-sources');
5
-
6
- const SENTRY_LOADER = path.resolve(__dirname, 'sentry.loader.js');
7
- const SENTRY_MODULE = path.resolve(__dirname, 'sentry-webpack.module.js');
8
-
9
- /**
10
- * Helper function that ensures an object key is defined. This mutates target!
11
- *
12
- * @param {object} target The target object
13
- * @param {string} key The object key
14
- * @param {function} factory A function that creates the new element
15
- * @returns {any} The existing or created element.
16
- */
17
- function ensure(target, key, factory) {
18
- // eslint-disable-next-line no-param-reassign
19
- target[key] = typeof target[key] !== 'undefined' ? target[key] : factory();
20
- return target[key];
21
- }
22
-
23
- /** Deep copy of a given input */
24
- function sillyClone(input) {
25
- try {
26
- return JSON.parse(JSON.stringify(input));
27
- } catch (oO) {
28
- return undefined;
29
- }
30
- }
31
-
32
- /** Diffs two arrays */
33
- function diffArray(prev, next) {
34
- // eslint-disable-next-line no-param-reassign
35
- prev = Array.isArray(prev) ? prev : [prev];
36
- // eslint-disable-next-line no-param-reassign
37
- next = Array.isArray(next) ? next : [next];
38
-
39
- return {
40
- removed: prev.filter(x => !next.includes(x)),
41
- added: next.filter(x => !prev.includes(x)),
42
- };
43
- }
44
-
45
- /** Extracts loader's name independently of Webpack's version */
46
- function getLoaderName(entry) {
47
- return (
48
- entry.loader ||
49
- (entry.use && entry.use[0] && entry.use[0].loader) ||
50
- '<unknown loader>'
51
- );
52
- }
53
-
54
- /**
55
- * Wraps the given value in an array if it is not already an array itself.
56
- * Ignores `undefined` and `null`, returning them as is.
57
- *
58
- * @param {any} value Either an array or a value that should be wrapped in an array
59
- * @returns {array} The resulting array, or the original value if it's null/undefined
60
- */
61
- function toArray(value) {
62
- if (Array.isArray(value) || value === null || value === undefined) {
63
- return value;
64
- }
65
-
66
- return [value];
67
- }
68
-
69
- /** Backwards compatible version of `compiler.plugin.afterEmit.tapAsync()`. */
70
- function attachAfterEmitHook(compiler, callback) {
71
- if (compiler.hooks && compiler.hooks.afterEmit) {
72
- compiler.hooks.afterEmit.tapAsync('SentryCliPlugin', callback);
73
- } else {
74
- compiler.plugin('after-emit', callback);
75
- }
76
- }
77
-
78
- function attachAfterCodeGenerationHook(compiler, options) {
79
- // This is only a problem for folks on webpack 3 and below
80
- if (!compiler.hooks || !compiler.hooks.make) {
81
- return;
82
- }
83
-
84
- const moduleFederationPlugin =
85
- compiler.options &&
86
- compiler.options.plugins &&
87
- compiler.options.plugins.find(
88
- x => x.constructor.name === 'ModuleFederationPlugin'
89
- );
90
-
91
- if (!moduleFederationPlugin) {
92
- return;
93
- }
94
-
95
- compiler.hooks.make.tapAsync('SentryCliPlugin', (compilation, cb) => {
96
- options.releasePromise.then(version => {
97
- compilation.hooks.afterCodeGeneration.tap('SentryCliPlugin', () => {
98
- compilation.modules.forEach(module => {
99
- // eslint-disable-next-line no-underscore-dangle
100
- if (module._name !== moduleFederationPlugin._options.name) {
101
- return;
102
- }
103
-
104
- const sourceMap = compilation.codeGenerationResults.get(module)
105
- .sources;
106
- const rawSource = sourceMap.get('javascript');
107
-
108
- if (rawSource) {
109
- sourceMap.set(
110
- 'javascript',
111
- new RawSource(
112
- `${rawSource.source()}
113
- (function (){
114
- var globalThis = (typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {});
115
- globalThis.SENTRY_RELEASES = globalThis.SENTRY_RELEASES || {};
116
- globalThis.SENTRY_RELEASES["${options.project}@${
117
- options.org
118
- }"] = {"id":"${version}"};
119
- })();`
120
- )
121
- );
122
- }
123
- });
124
- });
125
- cb();
126
- });
127
- });
128
- }
129
-
130
- class SentryCliPlugin {
131
- constructor(options = {}) {
132
- const defaults = {
133
- finalize: true,
134
- rewrite: true,
135
- };
136
-
137
- this.options = Object.assign({}, defaults, options);
138
-
139
- // the webpack plugin has looser type requirements than `@sentry/cli` -
140
- // ensure `include` and `ignore` options are in the right format
141
- if (options.include) {
142
- this.options.include = toArray(options.include);
143
- this.options.include.forEach(includeEntry => {
144
- if (
145
- typeof includeEntry === 'object' &&
146
- includeEntry.ignore !== undefined
147
- ) {
148
- // eslint-disable-next-line no-param-reassign
149
- includeEntry.ignore = toArray(includeEntry.ignore);
150
- }
151
- });
152
- }
153
-
154
- if (options.ignore) this.options.ignore = toArray(options.ignore);
155
-
156
- this.cli = this.getSentryCli();
157
- this.release = this.getReleasePromise();
158
- }
159
-
160
- /**
161
- * Pretty-prints debug information
162
- *
163
- * @param {string} label Label to be printed as a prefix for the data
164
- * @param {any} data Input to be pretty-printed
165
- */
166
- outputDebug(label, data) {
167
- if (this.isSilent()) {
168
- return;
169
- }
170
- if (data !== undefined) {
171
- // eslint-disable-next-line no-console
172
- console.log(
173
- `[Sentry Webpack Plugin] ${label} ${util.inspect(
174
- data,
175
- false,
176
- null,
177
- true
178
- )}`
179
- );
180
- } else {
181
- // eslint-disable-next-line no-console
182
- console.log(`[Sentry Webpack Plugin] ${label}`);
183
- }
184
- }
185
-
186
- /** Returns whether this plugin should emit any data to stdout. */
187
- isSilent() {
188
- return this.options.silent === true;
189
- }
190
-
191
- /** Returns whether this plugin is in dryRun mode. */
192
- isDryRun() {
193
- return this.options.dryRun === true;
194
- }
195
-
196
- /** Creates a new Sentry CLI instance. */
197
- getSentryCli() {
198
- const cli = new SentryCli(this.options.configFile, {
199
- silent: this.isSilent(),
200
- org: this.options.org,
201
- project: this.options.project,
202
- authToken: this.options.authToken,
203
- url: this.options.url,
204
- vcsRemote: this.options.vcsRemote,
205
- });
206
-
207
- if (this.isDryRun()) {
208
- this.outputDebug('DRY Run Mode');
209
-
210
- return {
211
- releases: {
212
- proposeVersion: () =>
213
- cli.releases.proposeVersion().then(version => {
214
- this.outputDebug('Proposed version:\n', version);
215
- return version;
216
- }),
217
- new: release => {
218
- this.outputDebug('Creating new release:\n', release);
219
- return Promise.resolve(release);
220
- },
221
- uploadSourceMaps: (release, config) => {
222
- this.outputDebug('Calling upload-sourcemaps with:\n', config);
223
- return Promise.resolve(release, config);
224
- },
225
- finalize: release => {
226
- this.outputDebug('Finalizing release:\n', release);
227
- return Promise.resolve(release);
228
- },
229
- setCommits: (release, config) => {
230
- this.outputDebug('Calling set-commits with:\n', config);
231
- return Promise.resolve(release, config);
232
- },
233
- newDeploy: (release, config) => {
234
- this.outputDebug('Calling deploy with:\n', config);
235
- return Promise.resolve(release, config);
236
- },
237
- },
238
- };
239
- }
240
-
241
- return cli;
242
- }
243
-
244
- /**
245
- * Returns a Promise that will solve to the configured release.
246
- *
247
- * If no release is specified, it uses Sentry CLI to propose a version.
248
- * The release string is always trimmed.
249
- * Returns undefined if proposeVersion failed.
250
- */
251
- getReleasePromise() {
252
- const userSpecifiedRelease =
253
- this.options.release || process.env.SENTRY_RELEASE;
254
- return (userSpecifiedRelease
255
- ? Promise.resolve(userSpecifiedRelease)
256
- : this.cli.releases.proposeVersion()
257
- )
258
- .then(version => `${version}`.trim())
259
- .catch(() => undefined);
260
- }
261
-
262
- /** Checks if the given named entry point should be handled. */
263
- shouldInjectEntry(key) {
264
- const { entries } = this.options;
265
- if (entries == null) {
266
- return true;
267
- }
268
-
269
- if (typeof entries === 'function') {
270
- return entries(key);
271
- }
272
-
273
- if (entries instanceof RegExp) {
274
- return entries.test(key);
275
- }
276
-
277
- if (Array.isArray(entries)) {
278
- return entries.includes(key);
279
- }
280
-
281
- throw new Error(
282
- 'Invalid `entries` option: Must be an array, RegExp or function'
283
- );
284
- }
285
-
286
- /** Injects the release string into the given entry point. */
287
- injectEntry(entry, sentryModule) {
288
- if (!entry) {
289
- return sentryModule;
290
- }
291
-
292
- /**
293
- * in:
294
- * entry: 'index.js'
295
- * out:
296
- * entry: ['sentry-webpack.module.js', 'index.js']
297
- */
298
- if (typeof entry === 'string') {
299
- return [sentryModule, entry];
300
- }
301
-
302
- /**
303
- * in:
304
- * entry: ['index.js', 'header.js', 'footer.js']
305
- * out:
306
- * entry: ['sentry-webpack.module.js', 'index.js', 'header.js', 'footer.js']
307
- */
308
- if (Array.isArray(entry)) {
309
- return [sentryModule].concat(entry);
310
- }
311
-
312
- /**
313
- * in:
314
- * entry: () => 'index.js'
315
- * entry: () => ['index.js']
316
- * out:
317
- * entry: ['sentry-webpack.module.js', 'index.js']
318
- * entry: ['sentry-webpack.module.js', 'index.js']
319
- */
320
- if (typeof entry === 'function') {
321
- return () =>
322
- Promise.resolve(entry()).then(resolvedEntry =>
323
- this.injectEntry(resolvedEntry, sentryModule)
324
- );
325
- }
326
-
327
- /**
328
- * in:
329
- * entry: {
330
- * home: './home.js',
331
- * about: ['./about.js'],
332
- * contact: () => './contact.js',
333
- * login: {
334
- * import: './login.js',
335
- * },
336
- * logout: {
337
- * import: ['./logout.js']
338
- * }
339
- * }
340
- * out:
341
- * entry: {
342
- * home: ['sentry-webpack.module.js', './home.js'],
343
- * about: ['sentry-webpack.module.js', './about.js'],
344
- * contact: ['sentry-webpack.module.js', './contact.js'],
345
- * login: {
346
- * import: ['sentry-webpack.module.js', './login.js']
347
- * },
348
- * logout: {
349
- * import: ['sentry-webpack.module.js', './logout.js']
350
- * }
351
- * }
352
- */
353
- const modifiedEntry = { ...entry };
354
- Object.keys(modifiedEntry)
355
- .filter(key => this.shouldInjectEntry(key))
356
- .forEach(key => {
357
- if (entry[key] && entry[key].import) {
358
- modifiedEntry[key].import = this.injectEntry(
359
- entry[key].import,
360
- sentryModule
361
- );
362
- } else {
363
- modifiedEntry[key] = this.injectEntry(entry[key], sentryModule);
364
- }
365
- });
366
- return modifiedEntry;
367
- }
368
-
369
- /** Webpack 2: Adds a new loader for the release module. */
370
- injectLoader(loaders) {
371
- const loader = {
372
- test: /sentry-webpack\.module\.js$/,
373
- loader: SENTRY_LOADER,
374
- options: {
375
- releasePromise: this.release,
376
- org: this.options.org || process.env.SENTRY_ORG,
377
- project: this.options.project || process.env.SENTRY_PROJECT,
378
- },
379
- };
380
-
381
- return (loaders || []).concat([loader]);
382
- }
383
-
384
- /** Webpack 3+: Injects a new rule for the release module. */
385
- injectRule(rules) {
386
- const rule = {
387
- test: /sentry-webpack\.module\.js$/,
388
- use: [
389
- {
390
- loader: SENTRY_LOADER,
391
- options: {
392
- // We check for `process.env.SENTRY_RELEASE` earlier, which is why
393
- // it's not used here the way `process.env.SENTRY_ORG` and
394
- // `process.env.SENTRY_PROJECT` are
395
- releasePromise: this.release,
396
- org: this.options.org || process.env.SENTRY_ORG,
397
- project: this.options.project || process.env.SENTRY_PROJECT,
398
- },
399
- },
400
- ],
401
- };
402
-
403
- return (rules || []).concat([rule]);
404
- }
405
-
406
- /** Injects the release entry points and rules into the given options. */
407
- injectRelease(compilerOptions) {
408
- const options = compilerOptions;
409
- options.entry = this.injectEntry(options.entry, SENTRY_MODULE);
410
- if (options.module.loaders) {
411
- // Handle old `options.module.loaders` syntax
412
- options.module.loaders = this.injectLoader(options.module.loaders);
413
- } else {
414
- options.module.rules = this.injectRule(options.module.rules);
415
- }
416
- }
417
-
418
- /** injectRelease with printable debug info */
419
- injectReleaseWithDebug(compilerOptions) {
420
- const input = {
421
- loaders: sillyClone(
422
- compilerOptions.module.loaders || compilerOptions.module.rules
423
- ).map(getLoaderName),
424
- entry: sillyClone(compilerOptions.entry),
425
- };
426
-
427
- this.injectRelease(compilerOptions);
428
-
429
- const output = {
430
- loaders: sillyClone(
431
- compilerOptions.module.loaders || compilerOptions.module.rules
432
- ).map(getLoaderName),
433
- entry: sillyClone(compilerOptions.entry),
434
- };
435
-
436
- const loaders = diffArray(input.loaders, output.loaders);
437
- const entry = diffArray(input.entry, output.entry);
438
-
439
- this.outputDebug('DEBUG: Injecting release code');
440
- this.outputDebug('DEBUG: Loaders:\n', output.loaders);
441
- this.outputDebug('DEBUG: Added loaders:\n', loaders.added);
442
- this.outputDebug('DEBUG: Removed loaders:\n', loaders.removed);
443
- this.outputDebug('DEBUG: Entry:\n', output.entry);
444
- this.outputDebug('DEBUG: Added entry:\n', entry.added);
445
- this.outputDebug('DEBUG: Removed entry:\n', entry.removed);
446
- }
447
-
448
- /** Creates and finalizes a release on Sentry. */
449
- finalizeRelease(compilation) {
450
- const {
451
- include,
452
- errorHandler = (_, invokeErr) => {
453
- invokeErr();
454
- },
455
- } = this.options;
456
-
457
- let release;
458
- return this.release
459
- .then(proposedVersion => {
460
- release = proposedVersion;
461
-
462
- if (!include) {
463
- throw new Error(`\`include\` option is required`);
464
- }
465
-
466
- if (!release) {
467
- throw new Error(
468
- `Unable to determine version. Make sure to include \`release\` option or use the environment that supports auto-detection https://docs.sentry.io/cli/releases/#creating-releases`
469
- );
470
- }
471
-
472
- return this.cli.releases.new(release);
473
- })
474
- .then(() => {
475
- if (this.options.cleanArtifacts) {
476
- return this.cli.releases.execute(
477
- ['releases', 'files', release, 'delete', '--all'],
478
- true
479
- );
480
- }
481
- return undefined;
482
- })
483
- .then(() => this.cli.releases.uploadSourceMaps(release, this.options))
484
- .then(() => {
485
- const {
486
- commit,
487
- previousCommit,
488
- repo,
489
- auto,
490
- ignoreMissing,
491
- ignoreEmpty,
492
- } = this.options.setCommits || this.options;
493
-
494
- if (auto || (repo && commit)) {
495
- return this.cli.releases.setCommits(release, {
496
- commit,
497
- previousCommit,
498
- repo,
499
- auto,
500
- ignoreMissing,
501
- ignoreEmpty,
502
- });
503
- }
504
- return undefined;
505
- })
506
- .then(() => {
507
- if (this.options.finalize) {
508
- return this.cli.releases.finalize(release);
509
- }
510
- return undefined;
511
- })
512
- .then(() => {
513
- const { env, started, finished, time, name, url } =
514
- this.options.deploy || {};
515
-
516
- if (env) {
517
- return this.cli.releases.newDeploy(release, {
518
- env,
519
- started,
520
- finished,
521
- time,
522
- name,
523
- url,
524
- });
525
- }
526
- return undefined;
527
- })
528
- .catch(err => {
529
- errorHandler(
530
- err,
531
- () =>
532
- compilation.errors.push(
533
- new Error(`Sentry CLI Plugin: ${err.message}`)
534
- ),
535
- compilation
536
- );
537
- });
538
- }
539
-
540
- /** Webpack lifecycle hook to update compiler options. */
541
- apply(compiler) {
542
- /**
543
- * Determines whether plugin should be applied not more than once during whole webpack run.
544
- * Useful when the process is performing multiple builds using the same config.
545
- * It cannot be stored on the instance, as every run is creating a new one.
546
- */
547
- if (this.options.runOnce && module.alreadyRun) {
548
- if (this.options.debug) {
549
- this.outputDebug(
550
- '`runOnce` option set and plugin already ran. Skipping release.'
551
- );
552
- }
553
- return;
554
- }
555
- module.alreadyRun = true;
556
-
557
- const compilerOptions = compiler.options || {};
558
- ensure(compilerOptions, 'module', Object);
559
-
560
- if (this.options.debug) {
561
- this.injectReleaseWithDebug(compilerOptions);
562
- } else {
563
- this.injectRelease(compilerOptions);
564
- }
565
-
566
- attachAfterCodeGenerationHook(compiler, {
567
- // We check for `process.env.SENTRY_RELEASE` earlier, which is why it's
568
- // not used here the way `process.env.SENTRY_ORG` and
569
- // `process.env.SENTRY_PROJECT` are
570
- releasePromise: this.release,
571
- org: this.options.org || process.env.SENTRY_ORG,
572
- project: this.options.project || process.env.SENTRY_PROJECT,
573
- });
574
-
575
- attachAfterEmitHook(compiler, (compilation, cb) => {
576
- if (!this.options.include || !this.options.include.length) {
577
- ensure(compilerOptions, 'output', Object);
578
- if (compilerOptions.output.path) {
579
- this.options.include = [compilerOptions.output.path];
580
- }
581
- }
582
- this.finalizeRelease(compilation).then(() => cb());
583
- });
584
- }
585
- }
586
-
587
- module.exports.default = SentryCliPlugin;
@@ -1 +0,0 @@
1
- // This will be replaced
@@ -1,15 +0,0 @@
1
- module.exports = function sentryLoader(content, map, meta) {
2
- const { releasePromise, org, project } = this.query;
3
- const callback = this.async();
4
- releasePromise.then(version => {
5
- let sentryRelease = `var _global = (typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}); _global.SENTRY_RELEASE={id:"${version}"};`;
6
- if (project) {
7
- const key = org ? `${project}@${org}` : project;
8
- sentryRelease += `
9
- _global.SENTRY_RELEASES=_global.SENTRY_RELEASES || {};
10
- _global.SENTRY_RELEASES["${key}"]={id:"${version}"};
11
- `;
12
- }
13
- callback(null, sentryRelease, map, meta);
14
- });
15
- };