Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
2 changes: 2 additions & 0 deletions packages/webpack-plugin/README_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ pnpm add @sentry/webpack-plugin --save-dev
```js
// webpack.config.js
const { sentryWebpackPlugin } = require("@sentry/webpack-plugin");
// for webpack 5.1 and webpack compatible environments
// const { sentryWebpackPlugin } = require("@sentry/webpack-plugin/webpack5");

module.exports = {
// ... other config above ...
Expand Down
15 changes: 12 additions & 3 deletions packages/webpack-plugin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,25 @@
"import": "./dist/esm/index.mjs",
"require": "./dist/cjs/index.js",
"types": "./dist/types/index.d.ts"
},
"./webpack5": {
"import": "./dist/esm/webpack5.mjs",
"require": "./dist/cjs/webpack5.js",
"types": "./dist/types/webpack5.d.ts"
}
},
"main": "dist/cjs/index.js",
"module": "dist/esm/index.mjs",
"types": "dist/types/index.d.ts",
"scripts": {
"build": "rimraf ./out && run-p build:rollup build:types",
"build": "rimraf ./dist && run-p build:rollup build:types",
"build:watch": "run-p build:rollup:watch build:types:watch",
"build:rollup": "rollup --config rollup.config.js",
"build:rollup:watch": "rollup --config rollup.config.js --watch --no-watch.clearScreen",
"build:rollup": "run-p build:rollup:default build:rollup:webpack5",
"build:rollup:default": "rollup --config rollup.config.default.js",
"build:rollup:webpack5": "rollup --config rollup.config.webpack5.js",
"build:rollup:watch": "run-p build:rollup:default:watch build:rollup:webpack5:watch",
"build:rollup:default:watch": "rollup --config rollup.config.default.js --watch --no-watch.clearScreen",
"build:rollup:webpack5:watch": "rollup --config rollup.config.webpack5.js --watch --no-watch.clearScreen",
"build:types": "tsc --project types.tsconfig.json",
"build:types:watch": "tsc --project types.tsconfig.json --watch --preserveWatchOutput",
"check:types": "run-p check:types:src check:types:test",
Expand Down
48 changes: 48 additions & 0 deletions packages/webpack-plugin/rollup.config.webpack5.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import resolve from "@rollup/plugin-node-resolve";
import babel from "@rollup/plugin-babel";
import packageJson from "./package.json";
import modulePackage from "module";

const input = ["src/webpack5.ts"];

const extensions = [".ts"];

export default {
input,
external: [...Object.keys(packageJson.dependencies), ...modulePackage.builtinModules, "webpack"],
onwarn: (warning) => {
if (warning.code === "CIRCULAR_DEPENDENCY") {
// Circular dependencies are usually not a big deal for us so let's just warn about them
console.warn(warning.message);
return;
}
// Warnings are usually high-consequence for us so let's throw to catch them
throw new Error(warning.message);
},
plugins: [
resolve({
extensions,
rootDir: "./src",
preferBuiltins: true,
}),
babel({
extensions,
babelHelpers: "bundled",
include: ["src/**/*"],
}),
],
output: [
{
file: packageJson.exports["./webpack5"].import,
format: "esm",
exports: "named",
sourcemap: true,
},
{
file: packageJson.exports["./webpack5"].require,
format: "cjs",
exports: "named",
sourcemap: true,
},
],
};
202 changes: 6 additions & 196 deletions packages/webpack-plugin/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,208 +1,18 @@
import {
getDebugIdSnippet,
Options,
sentryUnpluginFactory,
stringToUUID,
SentrySDKBuildFlags,
createComponentNameAnnotateHooks,
Logger,
} from "@sentry/bundler-plugin-core";
import * as path from "path";
import { UnpluginOptions } from "unplugin";
import { v4 as uuidv4 } from "uuid";
import { SentryWebpackPluginOptions, sentryWebpackUnpluginFactory } from "./plugin";

// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore webpack is a peer dep
import * as webpack4or5 from "webpack";

interface BannerPluginCallbackArg {
chunk?: {
hash?: string;
contentHash?: {
javascript?: string;
};
};
}
const BannerPlugin = webpack4or5?.BannerPlugin || webpack4or5?.default?.BannerPlugin;

function webpackReleaseInjectionPlugin(injectionCode: string): UnpluginOptions {
return {
name: "sentry-webpack-release-injection-plugin",
webpack(compiler) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore webpack version compatibility shenanigans
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
const BannerPlugin =
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore webpack version compatibility shenanigans
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
compiler?.webpack?.BannerPlugin ||
webpack4or5?.BannerPlugin ||
webpack4or5?.default?.BannerPlugin;
compiler.options.plugins = compiler.options.plugins || [];
compiler.options.plugins.push(
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-call
new BannerPlugin({
raw: true,
include: /\.(js|ts|jsx|tsx|mjs|cjs)(\?[^?]*)?(#[^#]*)?$/,
banner: injectionCode,
})
);
},
};
}
const DefinePlugin = webpack4or5?.DefinePlugin || webpack4or5?.default?.DefinePlugin;

function webpackComponentNameAnnotatePlugin(ignoredComponents?: string[]): UnpluginOptions {
return {
name: "sentry-webpack-component-name-annotate-plugin",
enforce: "pre",
// Webpack needs this hook for loader logic, so the plugin is not run on unsupported file types
transformInclude(id) {
return id.endsWith(".tsx") || id.endsWith(".jsx");
},
transform: createComponentNameAnnotateHooks(ignoredComponents).transform,
};
}

function webpackBundleSizeOptimizationsPlugin(
replacementValues: SentrySDKBuildFlags
): UnpluginOptions {
return {
name: "sentry-webpack-bundle-size-optimizations-plugin",
webpack(compiler) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore webpack version compatibility shenanigans
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
const DefinePlugin =
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore webpack version compatibility shenanigans
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
compiler?.webpack?.DefinePlugin ||
webpack4or5?.DefinePlugin ||
webpack4or5?.default?.DefinePlugin;
compiler.options.plugins = compiler.options.plugins || [];
compiler.options.plugins.push(
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-call
new DefinePlugin({
...replacementValues,
})
);
},
};
}

function webpackDebugIdInjectionPlugin(): UnpluginOptions {
return {
name: "sentry-webpack-debug-id-injection-plugin",
webpack(compiler) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore webpack version compatibility shenanigans
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
const BannerPlugin =
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore webpack version compatibility shenanigans
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
compiler?.webpack?.BannerPlugin ||
webpack4or5?.BannerPlugin ||
webpack4or5?.default?.BannerPlugin;
compiler.options.plugins = compiler.options.plugins || [];
compiler.options.plugins.push(
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-call
new BannerPlugin({
raw: true,
include: /\.(js|ts|jsx|tsx|mjs|cjs)(\?[^?]*)?(#[^#]*)?$/,
banner: (arg?: BannerPluginCallbackArg) => {
const hash = arg?.chunk?.contentHash?.javascript ?? arg?.chunk?.hash;
const debugId = hash ? stringToUUID(hash) : uuidv4();
return getDebugIdSnippet(debugId);
},
})
);
},
};
}

function webpackDebugIdUploadPlugin(
upload: (buildArtifacts: string[]) => Promise<void>,
logger: Logger,
forceExitOnBuildCompletion?: boolean
): UnpluginOptions {
const pluginName = "sentry-webpack-debug-id-upload-plugin";
return {
name: pluginName,
webpack(compiler) {
compiler.hooks.afterEmit.tapAsync(pluginName, (compilation, callback: () => void) => {
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
const outputPath = (compilation.outputOptions.path as string | undefined) ?? path.resolve();
const buildArtifacts = Object.keys(compilation.assets as Record<string, unknown>).map(
(asset) => path.join(outputPath, asset)
);
void upload(buildArtifacts).then(() => {
callback();
});
});

if (forceExitOnBuildCompletion && compiler.options.mode === "production") {
compiler.hooks.done.tap(pluginName, () => {
setTimeout(() => {
logger.debug("Exiting process after debug file upload");
process.exit(0);
});
});
}
},
};
}

function webpackModuleMetadataInjectionPlugin(injectionCode: string): UnpluginOptions {
return {
name: "sentry-webpack-module-metadata-injection-plugin",
webpack(compiler) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore webpack version compatibility shenanigans
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access
const BannerPlugin =
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore webpack version compatibility shenanigans
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
compiler?.webpack?.BannerPlugin ||
webpack4or5?.BannerPlugin ||
webpack4or5?.default?.BannerPlugin;
compiler.options.plugins = compiler.options.plugins || [];
compiler.options.plugins.push(
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument, @typescript-eslint/no-unsafe-call
new BannerPlugin({
raw: true,
include: /\.(js|ts|jsx|tsx|mjs|cjs)(\?[^?]*)?(#[^#]*)?$/,
banner: injectionCode,
})
);
},
};
}

const sentryUnplugin = sentryUnpluginFactory({
releaseInjectionPlugin: webpackReleaseInjectionPlugin,
componentNameAnnotatePlugin: webpackComponentNameAnnotatePlugin,
moduleMetadataInjectionPlugin: webpackModuleMetadataInjectionPlugin,
debugIdInjectionPlugin: webpackDebugIdInjectionPlugin,
debugIdUploadPlugin: webpackDebugIdUploadPlugin,
bundleSizeOptimizationsPlugin: webpackBundleSizeOptimizationsPlugin,
const sentryUnplugin = sentryWebpackUnpluginFactory({
BannerPlugin,
DefinePlugin,
});

type SentryWebpackPluginOptions = Options & {
_experiments?: Options["_experiments"] & {
/**
* If enabled, the webpack plugin will exit the build process after the build completes.
* Use this with caution, as it will terminate the process.
*
* More information: https://github.com/getsentry/sentry-javascript-bundler-plugins/issues/345
*
* @default false
*/
forceExitOnBuildCompletion?: boolean;
};
};

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const sentryWebpackPlugin: (options?: SentryWebpackPluginOptions) => any =
sentryUnplugin.webpack;
Expand Down
Loading
Loading