diff --git a/packages/browser/rollup.config.js b/packages/browser/rollup.config.js index aff948180f83..36882b0d0c4b 100644 --- a/packages/browser/rollup.config.js +++ b/packages/browser/rollup.config.js @@ -1,37 +1,17 @@ -import { makeBaseBundleConfig, makeLicensePlugin, terserPlugin } from '../../rollup.config'; +import { makeBaseBundleConfig, makeMinificationVariants } from '../../rollup.config'; const builds = []; -const licensePlugin = makeLicensePlugin(); - ['es5', 'es6'].forEach(jsVersion => { const baseBundleConfig = makeBaseBundleConfig({ input: 'src/index.ts', isAddOn: false, jsVersion, + licenseTitle: '@sentry/browser', outputFileBase: `build/bundle${jsVersion === 'es6' ? '.es6' : ''}`, }); - builds.push( - ...[ - { - ...baseBundleConfig, - output: { - ...baseBundleConfig.output, - file: `${baseBundleConfig.output.file}.js`, - }, - plugins: [...baseBundleConfig.plugins, licensePlugin], - }, - { - ...baseBundleConfig, - output: { - ...baseBundleConfig.output, - file: `${baseBundleConfig.output.file}.min.js`, - }, - plugins: [...baseBundleConfig.plugins, terserPlugin, licensePlugin], - }, - ], - ); + builds.push(...makeMinificationVariants(baseBundleConfig)); }); export default builds; diff --git a/packages/integrations/rollup.config.js b/packages/integrations/rollup.config.js index 622e5c548b5e..d136df8b1ec1 100644 --- a/packages/integrations/rollup.config.js +++ b/packages/integrations/rollup.config.js @@ -2,45 +2,25 @@ import * as fs from 'fs'; import commonjs from '@rollup/plugin-commonjs'; -import { makeBaseBundleConfig, terserPlugin } from '../../rollup.config'; - -function allIntegrations() { - return fs.readdirSync('./src').filter(file => file != 'index.ts'); -} - -function loadAllIntegrations() { - const builds = []; - - allIntegrations().forEach(file => { - const baseBundleConfig = makeBaseBundleConfig({ - input: `src/${file}`, - isAddOn: true, - jsVersion: 'es5', - outputFileBase: `build/${file.replace('.ts', '')}`, - }); - - [ - { - extension: '.js', - plugins: [...baseBundleConfig.plugins, commonjs()], - }, - { - extension: '.min.js', - plugins: [...baseBundleConfig.plugins, commonjs(), terserPlugin], - }, - ].forEach(build => { - builds.push({ - ...baseBundleConfig, - output: { - ...baseBundleConfig.output, - file: `${baseBundleConfig.output.file}${build.extension}`, - }, - plugins: build.plugins, - }); - }); +import { insertAt, makeBaseBundleConfig, makeMinificationVariants } from '../../rollup.config'; + +const builds = []; + +const integrationSourceFiles = fs.readdirSync('./src').filter(file => file != 'index.ts'); + +integrationSourceFiles.forEach(file => { + const baseBundleConfig = makeBaseBundleConfig({ + input: `src/${file}`, + isAddOn: true, + jsVersion: 'es5', + licenseTitle: '@sentry/integrations', + outputFileBase: `build/${file.replace('.ts', '')}`, }); - return builds; -} + // TODO We only need `commonjs` for localforage (used in the offline plugin). Once that's fixed, this can come out. + baseBundleConfig.plugins = insertAt(baseBundleConfig.plugins, -2, commonjs()); + + builds.push(...makeMinificationVariants(baseBundleConfig)); +}); -export default loadAllIntegrations(); +export default builds; diff --git a/packages/tracing/rollup.config.js b/packages/tracing/rollup.config.js index f25846501c1e..7729573881e4 100644 --- a/packages/tracing/rollup.config.js +++ b/packages/tracing/rollup.config.js @@ -1,37 +1,17 @@ -import { makeBaseBundleConfig, makeLicensePlugin, terserPlugin } from '../../rollup.config'; +import { makeBaseBundleConfig, makeMinificationVariants } from '../../rollup.config'; const builds = []; -const licensePlugin = makeLicensePlugin('@sentry/tracing & @sentry/browser'); - ['es5', 'es6'].forEach(jsVersion => { const baseBundleConfig = makeBaseBundleConfig({ input: 'src/index.bundle.ts', isAddOn: false, jsVersion, + licenseTitle: '@sentry/tracing & @sentry/browser', outputFileBase: `build/bundle.tracing${jsVersion === 'es6' ? '.es6' : ''}`, }); - builds.push( - ...[ - { - ...baseBundleConfig, - output: { - ...baseBundleConfig.output, - file: `${baseBundleConfig.output.file}.js`, - }, - plugins: [...baseBundleConfig.plugins, licensePlugin], - }, - { - ...baseBundleConfig, - output: { - ...baseBundleConfig.output, - file: `${baseBundleConfig.output.file}.min.js`, - }, - plugins: [...baseBundleConfig.plugins, terserPlugin, licensePlugin], - }, - ], - ); + builds.push(...makeMinificationVariants(baseBundleConfig)); }); export default builds; diff --git a/packages/vue/rollup.config.js b/packages/vue/rollup.config.js index 3b1f34a7b6ca..9980b428820e 100644 --- a/packages/vue/rollup.config.js +++ b/packages/vue/rollup.config.js @@ -1,29 +1,11 @@ -import { makeBaseBundleConfig, makeLicensePlugin, terserPlugin } from '../../rollup.config'; - -const licensePlugin = makeLicensePlugin(); +import { makeBaseBundleConfig, makeMinificationVariants } from '../../rollup.config'; const baseBundleConfig = makeBaseBundleConfig({ input: 'src/index.bundle.ts', isAddOn: false, jsVersion: 'es5', + licenseTitle: '@sentry/vue', outputFileBase: 'build/bundle.vue', }); -export default [ - { - ...baseBundleConfig, - output: { - ...baseBundleConfig.output, - file: `${baseBundleConfig.output.file}.js`, - }, - plugins: [...baseBundleConfig.plugins, licensePlugin], - }, - { - ...baseBundleConfig, - output: { - ...baseBundleConfig.output, - file: `${baseBundleConfig.output.file}.min.js`, - }, - plugins: [...baseBundleConfig.plugins, terserPlugin, licensePlugin], - }, -]; +export default makeMinificationVariants(baseBundleConfig); diff --git a/packages/wasm/rollup.config.js b/packages/wasm/rollup.config.js index 855156acdb96..614b861622dd 100644 --- a/packages/wasm/rollup.config.js +++ b/packages/wasm/rollup.config.js @@ -1,34 +1,11 @@ -import { makeBaseBundleConfig, terserPlugin } from '../../rollup.config'; +import { makeBaseBundleConfig, makeMinificationVariants } from '../../rollup.config'; const baseBundleConfig = makeBaseBundleConfig({ input: 'src/index.ts', isAddOn: true, jsVersion: 'es5', + licenseTitle: '@sentry/wasm', outputFileBase: 'build/wasm', }); -function loadAllIntegrations() { - const builds = []; - [ - { - extension: '.js', - plugins: baseBundleConfig.plugins, - }, - { - extension: '.min.js', - plugins: [...baseBundleConfig.plugins, terserPlugin], - }, - ].forEach(build => { - builds.push({ - ...baseBundleConfig, - output: { - ...baseBundleConfig.output, - file: `${baseBundleConfig.output.file}${build.extension}`, - }, - plugins: build.plugins, - }); - }); - return builds; -} - -export default loadAllIntegrations(); +export default makeMinificationVariants(baseBundleConfig); diff --git a/rollup.config.js b/rollup.config.js index 64956e2c9ecd..8b8bddcad948 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -1,13 +1,9 @@ /** - * Shared config used by individual packages' Rollup configs. Items here come in three flavors: - * - stand-alone: used by `@sentry/browser`, `@sentry/tracing`, and `@sentry/vue` (bundles which are a full SDK in - * and of themselves) - * - add-on: used by `@sentry/integrations` and `@sentry/wasm` (bundles which need to be combined with a stand-alone - * SDK bundle) - * - shared: used by both types of bundles - * + * Code for generating config used by individual packages' Rollup configs */ +import assert from 'assert'; + import deepMerge from 'deepmerge'; import license from 'rollup-plugin-license'; import resolve from '@rollup/plugin-node-resolve'; @@ -15,13 +11,22 @@ import replace from '@rollup/plugin-replace'; import { terser } from 'rollup-plugin-terser'; import typescript from 'rollup-plugin-typescript2'; -export const paths = { - '@sentry/browser': ['../browser/src'], - '@sentry/core': ['../core/src'], - '@sentry/hub': ['../hub/src'], - '@sentry/minimal': ['../minimal/src'], - '@sentry/types': ['../types/src'], - '@sentry/utils': ['../utils/src'], +/** + * Helper functions to compensate for the fact that JS can't handle negative array indices very well + * + * TODO `insertAt` is only exported so the integrations config can inject the `commonjs` plugin, for localforage (used + * in the offline plugin). Once that's fixed to no longer be necessary, this can stop being exported. + */ +const getLastElement = array => { + return array[array.length - 1]; +}; +export const insertAt = (arr, index, insertee) => { + const newArr = [...arr]; + // Add 1 to the array length so that the inserted element ends up in the right spot with respect to the length of the + // new array (which will be one element longer), rather than that of the current array + const destinationIndex = index >= 0 ? index : arr.length + 1 + index; + newArr.splice(destinationIndex, 0, insertee); + return newArr; }; /** @@ -30,12 +35,12 @@ export const paths = { * @param title The title to use for the SDK, if not the package name * @returns An instance of the `rollup-plugin-license` plugin */ -export function makeLicensePlugin(title) { +function makeLicensePlugin(title) { const commitHash = require('child_process').execSync('git rev-parse --short HEAD', { encoding: 'utf-8' }).trim(); return license({ banner: { - content: `/*! <%= data.title || pkg.name %> <%= pkg.version %> (${commitHash}) | https://github.com/getsentry/sentry-javascript */`, + content: `/*! <%= data.title %> <%= pkg.version %> (${commitHash}) | https://github.com/getsentry/sentry-javascript */`, data: { title }, }, }); @@ -64,95 +69,65 @@ export const terserPlugin = terser({ }, }); -export const markAsBrowserBuild = replace({ - // don't replace `__placeholder__` where it's followed immediately by a single `=` (to prevent ending up - // with something of the form `let "replacementValue" = "some assigned value"`, which would cause a - // syntax error) - preventAssignment: true, - // the replacement to make - values: { - __SENTRY_BROWSER_BUNDLE__: true, - }, -}); - -const baseTSPluginOptions = { - tsconfig: 'tsconfig.esm.json', - tsconfigOverride: { - compilerOptions: { - declaration: false, - declarationMap: false, - paths, - baseUrl: '.', - }, - }, - include: ['*.ts+(|x)', '**/*.ts+(|x)', '../**/*.ts+(|x)'], -}; +export function makeBaseBundleConfig(options) { + const { input, isAddOn, jsVersion, licenseTitle, outputFileBase } = options; -export const typescriptPluginES5 = typescript( - deepMerge(baseTSPluginOptions, { + const baseTSPluginOptions = { + tsconfig: 'tsconfig.esm.json', tsconfigOverride: { compilerOptions: { - target: 'es5', + declaration: false, + declarationMap: false, + paths: { + '@sentry/browser': ['../browser/src'], + '@sentry/core': ['../core/src'], + '@sentry/hub': ['../hub/src'], + '@sentry/minimal': ['../minimal/src'], + '@sentry/types': ['../types/src'], + '@sentry/utils': ['../utils/src'], + }, + baseUrl: '.', }, }, - }), -); + include: ['*.ts+(|x)', '**/*.ts+(|x)', '../**/*.ts+(|x)'], + }; -export const typescriptPluginES6 = typescript( - deepMerge(baseTSPluginOptions, { - tsconfigOverride: { - compilerOptions: { - target: 'es6', + const typescriptPluginES5 = typescript( + deepMerge(baseTSPluginOptions, { + tsconfigOverride: { + compilerOptions: { + target: 'es5', + }, + }, + }), + ); + + const typescriptPluginES6 = typescript( + deepMerge(baseTSPluginOptions, { + tsconfigOverride: { + compilerOptions: { + target: 'es6', + }, }, + }), + ); + + const nodeResolvePlugin = resolve(); + + const markAsBrowserBuildPlugin = replace({ + // don't replace `__placeholder__` where it's followed immediately by a single `=` (to prevent ending up + // with something of the form `let "replacementValue" = "some assigned value"`, which would cause a + // syntax error) + preventAssignment: true, + // the replacement to make + values: { + __SENTRY_BROWSER_BUNDLE__: true, }, - }), -); - -export const nodeResolvePlugin = resolve(); - -export const baseBundleConfig = { - output: { - sourcemap: true, - strict: false, - esModule: false, - }, - treeshake: 'smallest', -}; - -export const addOnBundleConfig = { - // These output settings are designed to mimic an IIFE. We don't use Rollup's `iife` format because we don't want to - // attach this code to a new global variable, but rather inject it into the existing SDK's `Integrations` object. - output: { - format: 'cjs', - - // code to add before the CJS wrapper - banner: '(function (__window) {', - - // code to add just inside the CJS wrapper, before any of the wrapped code - intro: 'var exports = {};', - - // code to add after all of the wrapped code, but still inside the CJS wrapper - outro: () => - [ - '', - " // Add this module's exports to the global `Sentry.Integrations`", - ' __window.Sentry = __window.Sentry || {};', - ' __window.Sentry.Integrations = __window.Sentry.Integrations || {};', - ' for (var key in exports) {', - ' if (Object.prototype.hasOwnProperty.call(exports, key)) {', - ' __window.Sentry.Integrations[key] = exports[key];', - ' }', - ' }', - ].join('\n'), - - // code to add after the CJS wrapper - footer: '}(window));', - }, -}; + }); -export function makeBaseBundleConfig(options) { - const { input, isAddOn, jsVersion, outputFileBase } = options; + const licensePlugin = makeLicensePlugin(licenseTitle); + // used by `@sentry/browser`, `@sentry/tracing`, and `@sentry/vue` (bundles which are a full SDK in and of themselves) const standAloneBundleConfig = { output: { format: 'iife', @@ -161,6 +136,7 @@ export function makeBaseBundleConfig(options) { context: 'window', }; + // used by `@sentry/integrations` and `@sentry/wasm` (bundles which need to be combined with a stand-alone SDK bundle) const addOnBundleConfig = { // These output settings are designed to mimic an IIFE. We don't use Rollup's `iife` format because we don't want to // attach this code to a new global variable, but rather inject it into the existing SDK's `Integrations` object. @@ -192,6 +168,7 @@ export function makeBaseBundleConfig(options) { }, }; + // used by all bundles const sharedBundleConfig = { input, output: { @@ -201,9 +178,57 @@ export function makeBaseBundleConfig(options) { strict: false, esModule: false, }, - plugins: [jsVersion === 'es5' ? typescriptPluginES5 : typescriptPluginES6, markAsBrowserBuild, nodeResolvePlugin], + plugins: [ + jsVersion === 'es5' ? typescriptPluginES5 : typescriptPluginES6, + markAsBrowserBuildPlugin, + nodeResolvePlugin, + licensePlugin, + ], treeshake: 'smallest', }; return deepMerge(sharedBundleConfig, isAddOn ? addOnBundleConfig : standAloneBundleConfig); } + +export function makeMinificationVariants(existingConfigs) { + const newConfigs = []; + + // ensure we've got an array of configs rather than a single config + existingConfigs = Array.isArray(existingConfigs) ? existingConfigs : [existingConfigs]; + + existingConfigs.forEach(existingConfig => { + const { plugins } = existingConfig; + + // The license plugin has to be last, so it ends up after terser. Otherwise, terser will remove the license banner. + assert( + getLastElement(plugins).name === 'rollup-plugin-license', + `Last plugin in given options should be \`rollup-plugin-license\`. Found ${getLastElement(plugins).name}`, + ); + + const bundleVariants = [ + { + output: { + file: `${existingConfig.output.file}.js`, + }, + plugins, + }, + { + output: { + file: `${existingConfig.output.file}.min.js`, + }, + plugins: insertAt(plugins, -2, terserPlugin), + }, + ]; + + bundleVariants.forEach(variant => { + const mergedConfig = deepMerge(existingConfig, variant, { + // this makes it so that instead of concatenating the `plugin` properties of the two objects, the first value is + // just overwritten by the second value + arrayMerge: (first, second) => second, + }); + newConfigs.push(mergedConfig); + }); + }); + + return newConfigs; +}