diff --git a/example/test.js b/example/test.js index c72b9854..101de9b5 100644 --- a/example/test.js +++ b/example/test.js @@ -8,14 +8,41 @@ console.log(); const content = fs.readFileSync('./dist/main.bundle.js'); if ( - content.toString() - .indexOf(`(typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}).SENTRY_RELEASE = { + content + .toString() + .indexOf( + `var _global = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};` + ) !== -1 +) { + console.log('Saul Goodman, found "var _global" assignment in bundle'); +} else { + console.error('Boom, did not find "var _global" assignment in bundle'); + process.exit(1); +} + +if ( + content.toString().indexOf( + `_global.SENTRY_RELEASE = { id: "foo" -}`) !== -1 +};` + ) !== -1 ) { console.log('Saul Goodman, found SENTRY_RELEASE in bundle'); - process.exit(0); } else { console.error('Boom, did not find SENTRY_RELEASE in bundle'); process.exit(1); } + +if ( + content.toString().indexOf( + `_global.SENTRY_RELEASES = _global.SENTRY_RELEASES || {}; +_global.SENTRY_RELEASES["my-project@my-org"] = { + id: "foo" +};` + ) !== -1 +) { + console.log('Saul Goodman, found SENTRY_RELEASES assignment in bundle'); +} else { + console.error('Boom, did not find SENTRY_RELEASES assignment in bundle'); + process.exit(1); +} diff --git a/example/webpack.config.js b/example/webpack.config.js index 2b8420b8..6866eabf 100644 --- a/example/webpack.config.js +++ b/example/webpack.config.js @@ -29,6 +29,8 @@ module.exports = { configFile: 'sentry.properties', dryRun: true, release: 'foo', + project: 'my-project', + org: 'my-org', dist: '123', }), ], diff --git a/src/index.js b/src/index.js index bc82e6cd..0ff58fb7 100644 --- a/src/index.js +++ b/src/index.js @@ -74,6 +74,63 @@ function attachAfterEmitHook(compiler, callback) { } } +function attachAfterCodeGenerationHook(compiler, options) { + if (!compiler.hooks || !compiler.hooks.make) { + return; + } + + let webpackSources; + try { + // eslint-disable-next-line global-require, import/no-extraneous-dependencies + webpackSources = require('webpack-sources'); + } catch (_e) { + console.warn( + 'Coud not resolve package: webpack-sources. Skipping injection for the remote entry file.' + ); + return; + } + + const { RawSource } = webpackSources; + const moduleFederationPlugin = + compiler.options && + compiler.options.plugins && + compiler.options.plugins.find( + x => x.constructor.name === 'ModuleFederationPlugin' + ); + + if (!moduleFederationPlugin) { + return; + } + + compiler.hooks.make.tapAsync('SentryCliPlugin', (compilation, cb) => { + options.releasePromise.then(version => { + compilation.hooks.afterCodeGeneration.tap('SentryCliPlugin', () => { + compilation.modules.forEach(module => { + // eslint-disable-next-line no-underscore-dangle + if (module._name !== moduleFederationPlugin._options.name) return; + const sourceMap = compilation.codeGenerationResults.get(module) + .sources; + const rawSource = sourceMap.get('javascript'); + sourceMap.set( + 'javascript', + new RawSource( + `${rawSource.source()} +(function (){ +var globalThis = (typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}); +globalThis.SENTRY_RELEASES = globalThis.SENTRY_RELEASES || {}; +globalThis.SENTRY_RELEASES["${options.project}@${ + options.org + }"] = {"id":"${version}"}; +})();` + ) + ); + }); + }); + cb(); + }); + }); +} + class SentryCliPlugin { constructor(options = {}) { const defaults = { @@ -318,6 +375,8 @@ class SentryCliPlugin { loader: SENTRY_LOADER, options: { releasePromise: this.release, + org: this.options.org || process.env.SENTRY_ORG, + project: this.options.project || process.env.SENTRY_PROJECT, }, }; @@ -333,6 +392,8 @@ class SentryCliPlugin { loader: SENTRY_LOADER, options: { releasePromise: this.release, + org: this.options.org || process.env.SENTRY_ORG, + project: this.options.project || process.env.SENTRY_PROJECT, }, }, ], @@ -501,6 +562,12 @@ class SentryCliPlugin { this.injectRelease(compilerOptions); } + attachAfterCodeGenerationHook(compiler, { + releasePromise: this.release, + org: this.options.org || process.env.SENTRY_ORG, + project: this.options.project || process.env.SENTRY_PROJECT, + }); + attachAfterEmitHook(compiler, (compilation, cb) => { if (!this.options.include || !this.options.include.length) { ensure(compilerOptions, 'output', Object); diff --git a/src/sentry.loader.js b/src/sentry.loader.js index 95f2634c..f18f5e1b 100644 --- a/src/sentry.loader.js +++ b/src/sentry.loader.js @@ -1,8 +1,15 @@ module.exports = function sentryLoader(content, map, meta) { - const { releasePromise } = this.query; + const { releasePromise, org, project } = this.query; const callback = this.async(); releasePromise.then(version => { - const sentryRelease = `(typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}).SENTRY_RELEASE={id:"${version}"};`; + let sentryRelease = `const _global = (typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}); _global.SENTRY_RELEASE={id:"${version}"};`; + if (project) { + const key = org ? `${project}@${org}` : project; + sentryRelease += ` + _global.SENTRY_RELEASES=_global.SENTRY_RELEASES || {}; + _global.SENTRY_RELEASES["${key}"]={id:"${version}"}; + `; + } callback(null, sentryRelease, map, meta); }); };