Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions dev-packages/rollup-utils/bundleHelpers.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,13 @@ import deepMerge from 'deepmerge';

import {
makeBrowserBuildPlugin,
makeCleanupPlugin,
makeCommonJSPlugin,
makeEsbuildPlugin,
makeIsDebugBuildPlugin,
makeLicensePlugin,
makeNodeResolvePlugin,
makeRrwebBuildPlugin,
makeSetSDKSourcePlugin,
makeSucrasePlugin,
makeTerserPlugin,
} from './plugins/index.mjs';
import { mergePlugins } from './utils.mjs';
Expand All @@ -24,11 +23,10 @@ import { makeProductionReplacePlugin } from './plugins/npmPlugins.mjs';
const BUNDLE_VARIANTS = ['.js', '.min.js', '.debug.min.js'];

export function makeBaseBundleConfig(options) {
const { bundleType, entrypoints, licenseTitle, outputFileBase, packageSpecificConfig, sucrase } = options;
const { bundleType, entrypoints, licenseTitle, outputFileBase, packageSpecificConfig, esbuild } = options;

const nodeResolvePlugin = makeNodeResolvePlugin();
const sucrasePlugin = makeSucrasePlugin({}, sucrase);
const cleanupPlugin = makeCleanupPlugin();
const transpilePlugin = makeEsbuildPlugin(esbuild);
const markAsBrowserBuildPlugin = makeBrowserBuildPlugin(true);
const licensePlugin = makeLicensePlugin(licenseTitle);
const rrwebBuildPlugin = makeRrwebBuildPlugin({
Expand Down Expand Up @@ -118,7 +116,7 @@ export function makeBaseBundleConfig(options) {
strict: false,
esModule: false,
},
plugins: [productionReplacePlugin, sucrasePlugin, nodeResolvePlugin, cleanupPlugin],
plugins: [productionReplacePlugin, transpilePlugin, nodeResolvePlugin],
treeshake: 'smallest',
};

Expand Down
21 changes: 13 additions & 8 deletions dev-packages/rollup-utils/npmHelpers.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,11 @@ import deepMerge from 'deepmerge';

import { defineConfig } from 'rollup';
import {
makeCleanupPlugin,
makeDebugBuildStatementReplacePlugin,
makeEsbuildPlugin,
makeNodeResolvePlugin,
makeProductionReplacePlugin,
makeRrwebBuildPlugin,
makeSucrasePlugin,
} from './plugins/index.mjs';
import { makePackageNodeEsm } from './plugins/make-esm-plugin.mjs';
import { mergeExternals, mergePlugins } from './utils.mjs';
Expand All @@ -34,14 +33,13 @@ export function makeBaseNPMConfig(options = {}) {
entrypoints = ['src/index.ts'],
hasBundles = false,
packageSpecificConfig = {},
sucrase = {},
esbuild = {},
bundledBuiltins = [],
} = options;

const nodeResolvePlugin = makeNodeResolvePlugin();
const sucrasePlugin = makeSucrasePlugin({}, sucrase);
const transpilePlugin = makeEsbuildPlugin(esbuild);
const debugBuildStatementReplacePlugin = makeDebugBuildStatementReplacePlugin();
const cleanupPlugin = makeCleanupPlugin();
const rrwebBuildPlugin = makeRrwebBuildPlugin({
excludeShadowDom: undefined,
excludeIframe: undefined,
Expand Down Expand Up @@ -104,7 +102,7 @@ export function makeBaseNPMConfig(options = {}) {
},
},

plugins: [nodeResolvePlugin, sucrasePlugin, debugBuildStatementReplacePlugin, rrwebBuildPlugin, cleanupPlugin],
plugins: [nodeResolvePlugin, transpilePlugin, debugBuildStatementReplacePlugin, rrwebBuildPlugin],

// don't include imported modules from outside the package in the final output
// also treat subpath exports (e.g. `@sentry/core/browser`) as external
Expand Down Expand Up @@ -154,8 +152,9 @@ export function makeNPMConfigVariants(baseConfig, options = {}) {
output: {
format: 'esm',
dir: path.join(baseConfig.output.dir, 'esm/prod'),
plugins: [makeProductionReplacePlugin(), makePackageNodeEsm()],
plugins: [makePackageNodeEsm()],
},
plugins: [makeProductionReplacePlugin()],
});
} else {
variantSpecificConfigs.push({
Expand All @@ -168,7 +167,13 @@ export function makeNPMConfigVariants(baseConfig, options = {}) {
}
}

return variantSpecificConfigs.map(variant => deepMerge(baseConfig, variant));
return variantSpecificConfigs.map(variant =>
// Plugin arrays must be merged in the right order or the build silently misbehaves
// (e.g. esbuild strips dev-mode marker comments before the replace plugin can act).
deepMerge(baseConfig, variant, {
customMerge: key => (key === 'plugins' ? mergePlugins : undefined),
}),
);
}

/**
Expand Down
22 changes: 18 additions & 4 deletions dev-packages/rollup-utils/plugins/bundlePlugins.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export function makeLicensePlugin(title) {
* 'false`
*/
export function makeIsDebugBuildPlugin(includeDebugging) {
return replace({
const plugin = replace({
// TODO `preventAssignment` will default to true in version 5.x of the replace plugin, at which point we can get rid
// of this. (It actually makes no difference in this case whether it's true or false, since we never assign to
// `__SENTRY_DEBUG__`, but if we don't give it a value, it will spam with warnings.)
Expand All @@ -58,16 +58,28 @@ export function makeIsDebugBuildPlugin(includeDebugging) {
__SENTRY_DEBUG__: includeDebugging,
},
});
plugin.name = 'replace-debug-flags';
return plugin;
}

/**
* Replaces the comment marker `/*! __SENTRY_SDK_SOURCE__ *\/` in core's `getSDKSource()` with a
* `return '<source>';` statement so the bundle reports the correct distribution channel.
*
* The marker uses the `/*! ... *\/` legal-comment syntax so it survives esbuild's transpile
* (esbuild strips ordinary block comments). The plugin sort order in utils.mjs also pins
* this name before `esbuild`, in case it ever runs on un-transpiled source directly.
*/
export function makeSetSDKSourcePlugin(sdkSource) {
return replace({
const plugin = replace({
preventAssignment: false,
delimiters: ['', ''],
values: {
'/* __SENTRY_SDK_SOURCE__ */': `return ${JSON.stringify(sdkSource)};`,
'/*! __SENTRY_SDK_SOURCE__ */': `return ${JSON.stringify(sdkSource)};`,
},
});
plugin.name = 'replace-sdk-source';
return plugin;
}

/**
Expand All @@ -77,13 +89,15 @@ export function makeSetSDKSourcePlugin(sdkSource) {
* @returns An instance of the `replace` plugin to do the replacement of the magic string with `true` or 'false`
*/
export function makeBrowserBuildPlugin(isBrowserBuild) {
return replace({
const plugin = replace({
// TODO This will be the default in the next version of the `replace` plugin
preventAssignment: true,
values: {
__SENTRY_BROWSER_BUNDLE__: isBrowserBuild,
},
});
plugin.name = 'replace-browser-bundle-flag';
return plugin;
}

// `terser` options reference: https://github.com/terser/terser#api-reference
Expand Down
100 changes: 50 additions & 50 deletions dev-packages/rollup-utils/plugins/npmPlugins.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,50 @@
* Rollup plugin hooks docs: https://rollupjs.org/guide/en/#build-hooks and
* https://rollupjs.org/guide/en/#output-generation-hooks
*
* Cleanup plugin docs: https://github.com/aMarCruz/rollup-plugin-cleanup
* esbuild plugin docs: https://github.com/egoist/rollup-plugin-esbuild
* Replace plugin docs: https://github.com/rollup/plugins/tree/master/packages/replace
* Sucrase plugin docs: https://github.com/rollup/plugins/tree/master/packages/sucrase
*/

import json from '@rollup/plugin-json';
import replace from '@rollup/plugin-replace';
import cleanup from 'rollup-plugin-cleanup';
import sucrase from './vendor/sucrase-plugin.mjs';
import esbuild from 'rollup-plugin-esbuild';

/**
* Create a plugin to transpile TS syntax using `sucrase`.
* Create a plugin to transpile TS/JSX syntax using `esbuild`.
*
* @returns An instance of the `@rollup/plugin-sucrase` plugin
* `target: 'es2020'` keeps ES2020-native syntax (`?.`, `??`, optional catch binding) and
* downlevels everything newer (logical assignment, numeric separators, class private
* fields, static class blocks, ...).
*
* `esbuildOptions` are forwarded to `rollup-plugin-esbuild` verbatim and can override
* any of the pinned defaults (e.g. JSX-related keys like `jsxFactory` / `jsxFragment`
* for packages that use a non-React pragma).
*/
export function makeSucrasePlugin(options = {}, sucraseOptions = {}) {
return sucrase(
{
// Required for bundling OTEL code properly
exclude: ['**/*.json'],
...options,
},
{
transforms: ['typescript', 'jsx'],
// We use a custom forked version of sucrase,
// where there is a new option `disableES2019Transforms`
disableESTransforms: false,
disableES2019Transforms: true,
...sucraseOptions,
export function makeEsbuildPlugin(esbuildOptions = {}) {
const plugin = esbuild({
// `.json` is handled by the JSON plugin further down the pipeline.
exclude: ['**/*.json'],
// ES2020 is our floor — keeps `?.`/`??` native, downlevels everything newer.
target: 'es2020',
// Don't read per-package tsconfig (they vary and can pull in unrelated settings).
// Pin only the compilerOptions that affect codegen.
tsconfig: false,
tsconfigRaw: {
compilerOptions: {
// Match the project tsconfig's effective behavior at target=es2020: class
// field initializers compile to `this.x = v` (set semantics), not via the
// `Object.defineProperty`-based `__publicField` helper esbuild emits by
// default. This is what tsc itself outputs at this target.
useDefineForClassFields: false,
},
},
);
sourceMap: true,
...esbuildOptions,
});

// Force a stable plugin name so the plugin sort order in utils.mjs can target it.
plugin.name = 'esbuild';
return plugin;
}

export function makeJsonPlugin() {
Expand Down Expand Up @@ -89,23 +102,6 @@ export function makeDebuggerPlugin(hookName) {
};
}

/**
* Create a plugin to clean up output files by:
* - Converting line endings unix line endings
* - Removing consecutive empty lines
*
* @returns A `rollup-plugin-cleanup` instance.
*/
export function makeCleanupPlugin() {
return cleanup({
// line endings are unix-ized by default
comments: 'all', // comments to keep
compactComments: 'false', // don't remove blank lines in multi-line comments
maxEmptyLines: 1,
extensions: ['js', 'jsx', 'ts', 'tsx'],
});
}

/**
* Creates a plugin to replace all instances of "__DEBUG_BUILD__" with a safe statement that
* a) evaluates to `true`
Expand All @@ -114,28 +110,30 @@ export function makeCleanupPlugin() {
* @returns A `@rollup/plugin-replace` instance.
*/
export function makeDebugBuildStatementReplacePlugin() {
return replace({
const plugin = replace({
preventAssignment: false,
values: {
__DEBUG_BUILD__: "(typeof __SENTRY_DEBUG__ === 'undefined' || __SENTRY_DEBUG__)",
},
});
plugin.name = 'replace-debug-build-statement';
return plugin;
}

export function makeProductionReplacePlugin() {
const pattern = /\/\* rollup-include-development-only \*\/[\s\S]*?\/\* rollup-include-development-only-end \*\/\s*/g;

function stripDevBlocks(code) {
if (!code) return null;
if (!code.includes('rollup-include-development-only')) return null;
const replaced = code.replace(pattern, '');
return { code: replaced, map: null };
}
// Markers use the `/*! ... */` legal-comment syntax so esbuild preserves them through
// transpile. We still run as a `transform` (per-module) hook rather than `renderChunk`:
// the block typically uses imports declared at the module top, and stripping it before
// rollup analyses module-graph imports lets those now-unused imports be tree-shaken away.
// The plugin sort order in utils.mjs pins this before `esbuild`.
const pattern =
/\/\*! rollup-include-development-only \*\/[\s\S]*?\/\*! rollup-include-development-only-end \*\/\s*/g;

return {
name: 'remove-dev-mode-blocks',
renderChunk(code) {
return stripDevBlocks(code);
transform(code) {
if (!code.includes('rollup-include-development-only')) return null;
return { code: code.replace(pattern, ''), map: null };
},
};
}
Expand All @@ -159,8 +157,10 @@ export function makeRrwebBuildPlugin({ excludeShadowDom, excludeIframe } = {}) {
values['__RRWEB_EXCLUDE_IFRAME__'] = excludeIframe;
}

return replace({
const plugin = replace({
preventAssignment: true,
values,
});
plugin.name = 'replace-rrweb-build-flags';
return plugin;
}
79 changes: 0 additions & 79 deletions dev-packages/rollup-utils/plugins/vendor/sucrase-plugin.mjs

This file was deleted.

Loading
Loading