Skip to content

Commit ba9a81d

Browse files
author
Luca Forstner
committed
ref(core): Switch to updated options
1 parent 7dd58e7 commit ba9a81d

File tree

13 files changed

+174
-689
lines changed

13 files changed

+174
-689
lines changed

packages/bundler-plugin-core/src/index.ts

Lines changed: 38 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,10 @@ import { normalizeUserOptions, validateOptions } from "./options-mapping";
66
import { debugIdUploadPlugin } from "./plugins/debug-id-upload";
77
import { releaseManagementPlugin } from "./plugins/release-management";
88
import { telemetryPlugin } from "./plugins/telemetry";
9-
import { getSentryCli } from "./sentry/cli";
109
import { createLogger } from "./sentry/logger";
11-
import { createSentryInstance, allowedToSendTelemetry } from "./sentry/telemetry";
10+
import { allowedToSendTelemetry, createSentryInstance } from "./sentry/telemetry";
1211
import { Options } from "./types";
1312
import {
14-
determineReleaseName,
1513
generateGlobalInjectorCode,
1614
getDependencies,
1715
getPackageJson,
@@ -58,6 +56,14 @@ export function sentryUnpluginFactory({
5856
return createUnplugin<Options, true>((userOptions, unpluginMetaContext) => {
5957
const options = normalizeUserOptions(userOptions);
6058

59+
if (unpluginMetaContext.watchMode || options.disable) {
60+
return [
61+
{
62+
name: "sentry-noop-plugin",
63+
},
64+
];
65+
}
66+
6167
const shouldSendTelemetry = allowedToSendTelemetry(options);
6268
const { sentryHub, sentryClient } = createSentryInstance(
6369
options,
@@ -95,14 +101,15 @@ export function sentryUnpluginFactory({
95101
);
96102
}
97103

98-
const cli = getSentryCli(options, logger);
99-
100-
const releaseName = options.release ?? determineReleaseName();
101-
if (!releaseName) {
102-
handleRecoverableError(
103-
new Error("Unable to determine a release name. Please set the `release` option.")
104-
);
105-
}
104+
const cli = new SentryCli(null, {
105+
url: options.url,
106+
authToken: options.authToken,
107+
org: options.org,
108+
project: options.project,
109+
vcsRemote: options.release.vcsRemote,
110+
silent: options.silent,
111+
headers: options.headers,
112+
});
106113

107114
if (process.cwd().match(/\\node_modules\\|\/node_modules\//)) {
108115
logger.warn(
@@ -121,46 +128,51 @@ export function sentryUnpluginFactory({
121128
})
122129
);
123130

124-
if (options.injectRelease && releaseName) {
131+
if (!options.release.inject) {
132+
logger.debug("Release injection disabled via `release.inject`. Will not inject.");
133+
} else if (options.release.name === undefined) {
134+
logger.debug("No release name provided. Will not inject release.");
135+
} else {
125136
const injectionCode = generateGlobalInjectorCode({
126-
release: releaseName,
137+
release: options.release.name,
127138
injectBuildInformation: options._experiments.injectBuildInformation || false,
128139
});
129-
130140
plugins.push(releaseInjectionPlugin(injectionCode));
131141
}
132142

133143
if (options.sourcemaps?.assets) {
134144
plugins.push(debugIdInjectionPlugin());
135145
}
136146

137-
if (releaseName) {
147+
if (options.release.name === undefined) {
148+
logger.debug("No release name provided. Will not manage release.");
149+
} else {
138150
plugins.push(
139151
releaseManagementPlugin({
140152
logger,
141153
cliInstance: cli,
142-
releaseName: releaseName,
143-
shouldCleanArtifacts: options.cleanArtifacts,
144-
shouldUploadSourceMaps: options.uploadSourceMaps,
145-
shouldFinalizeRelease: options.finalize,
146-
include: options.include,
147-
setCommitsOption: options.setCommits,
148-
deployOptions: options.deploy,
149-
dist: options.dist,
154+
releaseName: options.release.name,
155+
shouldCreateRelease: options.release.create,
156+
shouldCleanArtifacts: options.release.cleanArtifacts,
157+
shouldFinalizeRelease: options.release.finalize,
158+
include: options.release.uploadlegacySourcemaps,
159+
setCommitsOption: options.release.setCommits,
160+
deployOptions: options.release.deploy,
161+
dist: options.release.dist,
150162
handleRecoverableError: handleRecoverableError,
151163
sentryHub,
152164
sentryClient,
153165
})
154166
);
155167
}
156168

157-
if (!unpluginMetaContext.watchMode && options.sourcemaps?.assets !== undefined) {
169+
if (options.sourcemaps) {
158170
plugins.push(
159171
debugIdUploadPlugin({
160172
assets: options.sourcemaps.assets,
161173
ignore: options.sourcemaps.ignore,
162-
dist: options.dist,
163-
releaseName: releaseName,
174+
dist: options.release.dist,
175+
releaseName: options.release.name,
164176
logger: logger,
165177
cliInstance: cli,
166178
handleRecoverableError: handleRecoverableError,

packages/bundler-plugin-core/src/options-mapping.ts

Lines changed: 18 additions & 175 deletions
Original file line numberDiff line numberDiff line change
@@ -1,196 +1,39 @@
11
import { Logger } from "./sentry/logger";
2-
import { IncludeEntry as UserIncludeEntry, Options as UserOptions } from "./types";
3-
import { arrayify } from "./utils";
2+
import { Options as UserOptions } from "./types";
3+
import { determineReleaseName } from "./utils";
44

5-
type RequiredInternalOptions = Required<
6-
Pick<
7-
UserOptions,
8-
| "finalize"
9-
| "dryRun"
10-
| "debug"
11-
| "silent"
12-
| "cleanArtifacts"
13-
| "telemetry"
14-
| "_experiments"
15-
| "injectRelease"
16-
| "uploadSourceMaps"
17-
>
18-
>;
19-
20-
type OptionalInternalOptions = Partial<
21-
Pick<
22-
UserOptions,
23-
| "org"
24-
| "project"
25-
| "authToken"
26-
| "url"
27-
| "vcsRemote"
28-
| "dist"
29-
| "errorHandler"
30-
| "setCommits"
31-
| "deploy"
32-
| "configFile"
33-
| "headers"
34-
| "sourcemaps"
35-
| "release"
36-
>
37-
>;
38-
39-
type NormalizedInternalOptions = {
40-
releaseInjectionTargets: (string | RegExp)[] | ((filePath: string) => boolean) | undefined;
41-
include: InternalIncludeEntry[];
42-
};
43-
44-
export type NormalizedOptions = RequiredInternalOptions &
45-
OptionalInternalOptions &
46-
NormalizedInternalOptions;
47-
48-
type RequiredInternalIncludeEntry = Required<
49-
Pick<
50-
UserIncludeEntry,
51-
"paths" | "ext" | "stripCommonPrefix" | "sourceMapReference" | "rewrite" | "validate"
52-
>
53-
>;
54-
55-
type OptionalInternalIncludeEntry = Partial<
56-
Pick<UserIncludeEntry, "ignoreFile" | "urlPrefix" | "urlSuffix" | "stripPrefix">
57-
>;
58-
59-
export type InternalIncludeEntry = RequiredInternalIncludeEntry &
60-
OptionalInternalIncludeEntry & {
61-
ignore: string[];
62-
};
5+
export type NormalizedOptions = ReturnType<typeof normalizeUserOptions>;
636

647
export const SENTRY_SAAS_URL = "https://sentry.io";
658

669
export function normalizeUserOptions(userOptions: UserOptions) {
6710
const options = {
68-
// include is the only strictly required option
69-
// (normalizeInclude needs all userOptions to access top-level include options)
70-
include: normalizeInclude(userOptions),
71-
72-
// These options must be set b/c we need them for release injection.
73-
// They can also be set as environment variables. Technically, they
74-
// could be set in the config file but this would be too late for
75-
// release injection because we only pass the config file path
76-
// to the CLI
7711
org: userOptions.org ?? process.env["SENTRY_ORG"],
7812
project: userOptions.project ?? process.env["SENTRY_PROJECT"],
79-
// Falling back to the empty string here b/c at a later point, we use
80-
// Sentry CLI to determine a release if none was specified via options
81-
// or env vars. In case we don't find one, we'll bail at that point.
82-
release: userOptions.release ?? process.env["SENTRY_RELEASE"],
83-
// We technically don't need the URL for anything release-specific
84-
// but we want to make sure that we're only sending Sentry data
85-
// of SaaS customers. Hence we want to read it anyway.
13+
authToken: userOptions.authToken, // env var: `SENTRY_AUTH_TOKEN`
8614
url: userOptions.url ?? process.env["SENTRY_URL"] ?? SENTRY_SAAS_URL,
87-
88-
// Options with default values
89-
finalize: userOptions.finalize ?? true,
90-
cleanArtifacts: userOptions.cleanArtifacts ?? false,
91-
dryRun: userOptions.dryRun ?? false,
15+
headers: userOptions.headers,
9216
debug: userOptions.debug ?? false,
9317
silent: userOptions.silent ?? false,
18+
errorHandler: userOptions.errorHandler,
9419
telemetry: userOptions.telemetry ?? true,
95-
injectRelease: userOptions.injectRelease ?? true,
96-
uploadSourceMaps: userOptions.uploadSourceMaps ?? true,
20+
disable: userOptions.disable ?? false,
9721
sourcemaps: userOptions.sourcemaps,
22+
release: {
23+
name: determineReleaseName(),
24+
inject: true,
25+
create: true,
26+
finalize: true,
27+
vcsRemote: process.env["SENTRY_VSC_REMOTE"] ?? "origin",
28+
cleanArtifacts: false,
29+
...userOptions.release,
30+
},
9831
_experiments: userOptions._experiments ?? {},
99-
100-
// These options and can also be set via env variables or the config file.
101-
// If they're set in the options, we simply pass them to the CLI constructor.
102-
// Sentry CLI will internally query env variables and read its config file if
103-
// the passed options are undefined.
104-
authToken: userOptions.authToken, // env var: `SENTRY_AUTH_TOKEN`
105-
106-
headers: userOptions.headers,
107-
108-
vcsRemote: userOptions.vcsRemote, // env var: `SENTRY_VSC_REMOTE`
109-
110-
// Optional options
111-
setCommits: userOptions.setCommits,
112-
deploy: userOptions.deploy,
113-
releaseInjectionTargets: normalizeReleaseInjectionTargets(userOptions.releaseInjectionTargets),
114-
dist: userOptions.dist,
115-
errorHandler: userOptions.errorHandler,
116-
configFile: userOptions.configFile,
11732
};
11833

11934
return options;
12035
}
12136

122-
/**
123-
* Converts the user-facing `releaseInjectionTargets` option to the internal
124-
* `releaseInjectionTargets` option
125-
*/
126-
function normalizeReleaseInjectionTargets(
127-
userReleaseInjectionTargets: UserOptions["releaseInjectionTargets"]
128-
): (string | RegExp)[] | ((filePath: string) => boolean) | undefined {
129-
if (userReleaseInjectionTargets === undefined) {
130-
return undefined;
131-
} else if (typeof userReleaseInjectionTargets === "function") {
132-
return userReleaseInjectionTargets;
133-
} else {
134-
return arrayify(userReleaseInjectionTargets);
135-
}
136-
}
137-
138-
/**
139-
* Converts the user-facing `include` option to the internal `include` option,
140-
* resulting in an array of `InternalIncludeEntry` objects. This later on lets us
141-
* work with only one type of include data structure instead of multiple.
142-
*
143-
* During the process, we hoist top-level include options (e.g. urlPrefix) into each
144-
* object if they were not alrady specified in an `IncludeEntry`, making every object
145-
* fully self-contained. This is also the reason why we pass the entire options
146-
* object and not just `include`.
147-
*
148-
* @param userOptions the entire user-facing `options` object
149-
*
150-
* @return an array of `InternalIncludeEntry` objects.
151-
*/
152-
function normalizeInclude(userOptions: UserOptions): InternalIncludeEntry[] {
153-
if (!userOptions.include) {
154-
return [];
155-
}
156-
157-
return arrayify(userOptions.include)
158-
.map((includeItem) =>
159-
typeof includeItem === "string" ? { paths: [includeItem] } : includeItem
160-
)
161-
.map((userIncludeEntry) => normalizeIncludeEntry(userOptions, userIncludeEntry));
162-
}
163-
164-
/**
165-
* Besides array-ifying the `ignore` option, this function hoists top level options into the items of the `include`
166-
* option. This is to simplify the handling of of the `include` items later on.
167-
*/
168-
function normalizeIncludeEntry(
169-
userOptions: UserOptions,
170-
includeEntry: UserIncludeEntry
171-
): InternalIncludeEntry {
172-
const ignoreOption = includeEntry.ignore ?? userOptions.ignore ?? ["node_modules"];
173-
const ignore = Array.isArray(ignoreOption) ? ignoreOption : [ignoreOption];
174-
175-
// We're prefixing all entries in the `ext` option with a `.` (if it isn't already) to align with Node.js' `path.extname()`
176-
const ext = includeEntry.ext ?? userOptions.ext ?? ["js", "map", "jsbundle", "bundle"];
177-
const dotPrefixedExt = ext.map((extension) => `.${extension.replace(/^\./, "")}`);
178-
179-
return {
180-
paths: includeEntry.paths,
181-
ignore,
182-
ignoreFile: includeEntry.ignoreFile ?? userOptions.ignoreFile,
183-
ext: dotPrefixedExt,
184-
urlPrefix: includeEntry.urlPrefix ?? userOptions.urlPrefix,
185-
urlSuffix: includeEntry.urlSuffix ?? userOptions.urlSuffix,
186-
stripPrefix: includeEntry.stripPrefix ?? userOptions.stripPrefix,
187-
stripCommonPrefix: includeEntry.stripCommonPrefix ?? userOptions.stripCommonPrefix ?? false,
188-
sourceMapReference: includeEntry.sourceMapReference ?? userOptions.sourceMapReference ?? true,
189-
rewrite: includeEntry.rewrite ?? userOptions.rewrite ?? true,
190-
validate: includeEntry.validate ?? userOptions.validate ?? false,
191-
};
192-
}
193-
19437
/**
19538
* Validates a few combinations of options that are not checked by Sentry CLI.
19639
*
@@ -204,7 +47,7 @@ function normalizeIncludeEntry(
20447
* @returns `true` if the options are valid, `false` otherwise
20548
*/
20649
export function validateOptions(options: NormalizedOptions, logger: Logger): boolean {
207-
const setCommits = options.setCommits;
50+
const setCommits = options.release?.setCommits;
20851
if (setCommits) {
20952
if (!setCommits.auto && !(setCommits.repo && setCommits.commit)) {
21053
logger.error(
@@ -222,7 +65,7 @@ export function validateOptions(options: NormalizedOptions, logger: Logger): boo
22265
}
22366
}
22467

225-
if (options.deploy && !options.deploy.env) {
68+
if (options.release?.deploy && !options.release?.deploy.env) {
22669
logger.error(
22770
"The `deploy` option was specified but is missing the required `env` property.",
22871
"Please set the `env` property."

packages/bundler-plugin-core/src/plugins/debug-id-upload.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ import * as util from "util";
66
import { UnpluginOptions } from "unplugin";
77
import { Logger } from "../sentry/logger";
88
import { promisify } from "util";
9-
import { SentryCLILike } from "../sentry/cli";
109
import { Hub, NodeClient } from "@sentry/node";
10+
import type SentryCli from "@sentry/cli";
1111

1212
interface DebugIdUploadPluginOptions {
1313
logger: Logger;
14-
cliInstance: SentryCLILike;
14+
cliInstance: SentryCli;
1515
assets: string | string[];
1616
ignore?: string | string[];
1717
releaseName?: string;

0 commit comments

Comments
 (0)