diff --git a/packages/playground/build-esbuild.js b/packages/playground/build-esbuild.js index 80703d13..9259fe59 100644 --- a/packages/playground/build-esbuild.js +++ b/packages/playground/build-esbuild.js @@ -1,10 +1,15 @@ const { sentryEsbuildPlugin } = require("@sentry/unplugin"); const { build } = require("esbuild"); +const placeHolderOptions = require("./config.json"); build({ entryPoints: ["./src/entrypoint1.js"], outdir: "./out/esbuild", - plugins: [sentryEsbuildPlugin()], + plugins: [ + sentryEsbuildPlugin({ + ...placeHolderOptions, + }), + ], minify: true, bundle: true, format: "cjs", diff --git a/packages/playground/build-webpack4.js b/packages/playground/build-webpack4.js index 23174363..3114a1f8 100644 --- a/packages/playground/build-webpack4.js +++ b/packages/playground/build-webpack4.js @@ -3,6 +3,8 @@ const path = require("path"); const webpack4 = require("webpack4"); const { sentryWebpackPlugin } = require("@sentry/unplugin"); +const placeHolderOptions = require("./config.json"); + webpack4( { mode: "production", @@ -14,7 +16,16 @@ webpack4( library: "ExampleBundle", libraryTarget: "commonjs", }, - plugins: [sentryWebpackPlugin()], + plugins: [ + sentryWebpackPlugin({ + org: "sentry-sdks", + project: "someProj", + authToken: "1234", + include: "*", + debugLogging: true, + debug: true, + }), + ], devtool: "source-map", }, (err) => { diff --git a/packages/playground/build-webpack5.js b/packages/playground/build-webpack5.js index 495384d1..886763ce 100644 --- a/packages/playground/build-webpack5.js +++ b/packages/playground/build-webpack5.js @@ -3,6 +3,8 @@ const path = require("path"); const webpack5 = require("webpack"); const { sentryWebpackPlugin } = require("@sentry/unplugin"); +const placeHolderOptions = require("./config.json"); + webpack5( { cache: false, @@ -16,7 +18,7 @@ webpack5( }, }, mode: "production", - plugins: [sentryWebpackPlugin()], + plugins: [sentryWebpackPlugin({ ...placeHolderOptions })], devtool: "source-map", }, (err) => { diff --git a/packages/playground/config.json b/packages/playground/config.json new file mode 100644 index 00000000..e40336e7 --- /dev/null +++ b/packages/playground/config.json @@ -0,0 +1,9 @@ +{ + "release": "0.0.1", + "org": "some-org", + "project": "some-proj", + "authToken": "some-auth-token", + "include": "/dist", + "debugLogging": true, + "debug": true +} diff --git a/packages/playground/rollup.config.js b/packages/playground/rollup.config.js index 67a529a7..ab714fbf 100644 --- a/packages/playground/rollup.config.js +++ b/packages/playground/rollup.config.js @@ -2,6 +2,7 @@ import commonjs from "@rollup/plugin-commonjs"; import resolve from "@rollup/plugin-node-resolve"; import { sentryRollupPlugin } from "@sentry/unplugin"; +import placeHolderOptions from "./config.json"; const input = ["src/entrypoint1.js"]; @@ -9,7 +10,13 @@ const extensions = [".js"]; export default { input, - plugins: [resolve({ extensions }), commonjs(), sentryRollupPlugin()], + plugins: [ + resolve({ extensions }), + commonjs(), + sentryRollupPlugin({ + ...placeHolderOptions, + }), + ], output: { dir: "./out/rollup", format: "cjs", diff --git a/packages/playground/vite.config.js b/packages/playground/vite.config.js index 1632e01f..90c5df75 100644 --- a/packages/playground/vite.config.js +++ b/packages/playground/vite.config.js @@ -2,6 +2,7 @@ import { sentryVitePlugin } from "@sentry/unplugin"; import { defineConfig } from "vite"; import * as path from "path"; +import placeHolderOptions from "./config.json"; export default defineConfig({ build: { @@ -14,5 +15,9 @@ export default defineConfig({ }, sourcemap: true, }, - plugins: [sentryVitePlugin()], + plugins: [ + sentryVitePlugin({ + ...placeHolderOptions, + }), + ], }); diff --git a/packages/unplugin/package.json b/packages/unplugin/package.json index 248f2c0e..ca0057ef 100644 --- a/packages/unplugin/package.json +++ b/packages/unplugin/package.json @@ -17,9 +17,10 @@ "check:types:src": "tsc --project ./src/tsconfig.json --noEmit", "check:types:test": "tsc --project ./test/tsconfig.json --noEmit", "test": "jest", - "lint": "eslint ./src ./test --max-warnings=0" + "lint": "eslint ./src ./test" }, "dependencies": { + "@sentry/cli": "1.74.5", "magic-string": "0.26.2", "unplugin": "0.9.4" }, @@ -29,6 +30,7 @@ "@babel/preset-typescript": "7.17.12", "@rollup/plugin-babel": "5.3.1", "@rollup/plugin-commonjs": "22.0.1", + "@rollup/plugin-json": "^4.1.0", "@rollup/plugin-node-resolve": "13.3.0", "@swc/core": "^1.2.205", "@swc/jest": "^0.2.21", diff --git a/packages/unplugin/rollup.config.js b/packages/unplugin/rollup.config.js index 112364f4..4b84dba1 100644 --- a/packages/unplugin/rollup.config.js +++ b/packages/unplugin/rollup.config.js @@ -2,6 +2,7 @@ import commonjs from "@rollup/plugin-commonjs"; import resolve from "@rollup/plugin-node-resolve"; import babel from "@rollup/plugin-babel"; import packageJson from "./package.json"; +import json from "@rollup/plugin-json"; const input = ["src/index.ts"]; @@ -14,6 +15,7 @@ export default { plugins: [ resolve({ extensions, preferBuiltins: true }), commonjs(), + json(), babel({ extensions, babelHelpers: "bundled", diff --git a/packages/unplugin/src/cli.ts b/packages/unplugin/src/cli.ts new file mode 100644 index 00000000..e1a5123e --- /dev/null +++ b/packages/unplugin/src/cli.ts @@ -0,0 +1,52 @@ +import SentryCli from "@sentry/cli"; +import { Options } from "./types"; + +/** Creates a new Sentry CLI instance. */ +export function makeSentryCli(options: Options) { + //TODO: pass config file instead of null + const cli = new SentryCli(undefined, { + silent: false, //TODO read from options + org: options.org, + project: options.project, + authToken: options.authToken, + url: options.url, + vcsRemote: "origin", //TODO set from options, + }); + + // Let's not worry about dry run for now + // if (this.isDryRun()) { + // this.outputDebug("DRY Run Mode"); + + // return { + // releases: { + // proposeVersion: () => + // cli.releases.proposeVersion().then((version) => { + // this.outputDebug("Proposed version:\n", version); + // return version; + // }), + // new: (release) => { + // this.outputDebug("Creating new release:\n", release); + // return Promise.resolve(release); + // }, + // uploadSourceMaps: (release, config) => { + // this.outputDebug("Calling upload-sourcemaps with:\n", config); + // return Promise.resolve(release, config); + // }, + // finalize: (release) => { + // this.outputDebug("Finalizing release:\n", release); + // return Promise.resolve(release); + // }, + // setCommits: (release, config) => { + // this.outputDebug("Calling set-commits with:\n", config); + // return Promise.resolve(release, config); + // }, + // newDeploy: (release, config) => { + // this.outputDebug("Calling deploy with:\n", config); + // return Promise.resolve(release, config); + // }, + // }, + // }; + // } + + return cli; +} diff --git a/packages/unplugin/src/facade.ts b/packages/unplugin/src/facade.ts new file mode 100644 index 00000000..5308ff05 --- /dev/null +++ b/packages/unplugin/src/facade.ts @@ -0,0 +1,68 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ +/* eslint-disable no-console */ +//TODO: remove eslint rules + +// Build a facade that exposes necessary sentry functionality +// Idea: We start out with Sentry-CLI and replace the cli-commands one by one afterwards. +// Goal: eventually replace everything sentry-cli does with "native" code here +// Reason: We don't want to depend on a binary that gets downloaded in a postinstall hook +// - no fixed version +// - huge download +// - unnecessary functionality + +import { makeSentryCli } from "./cli"; +import { Options } from "./types"; + +export type SentryFacade = { + createNewRelease: () => any; + cleanArtifacts: () => any; + uploadSourceMaps: () => any; + setCommits: () => any; + finalizeRelease: () => any; + addDeploy: () => any; +}; + +/** + * Factory function that provides all necessary Sentry functionality for creating + * a release on Sentry. This includes uploading source maps and finalizing the release + */ +export function makeSentryFacade(version: string, options: Options): SentryFacade { + makeSentryCli(options); + //TODO: remove + // void cli.execute(["--version"], true); + + return { + createNewRelease: () => createNewRelease(version), + cleanArtifacts: () => cleanArtifacts(), + uploadSourceMaps: () => uploadSourceMaps(version), + setCommits: () => setCommits(version), + finalizeRelease: () => finalizeRelease(version), + addDeploy: () => addDeploy(version), + }; +} + +function createNewRelease(version: string) { + //TODO(must have): implement release creation logic here +} + +function uploadSourceMaps(version: string) { + //TODO(must have): implement source maps upload logic here +} + +function finalizeRelease(version: string) { + //TODO(must have): implement release finalization logic here +} + +// TODO: Stuff we worry about later: + +function cleanArtifacts() { + // NOOP for now +} + +function setCommits(version: string) { + // NOOP for now +} + +function addDeploy(version: string) { + // NOOP for now +} diff --git a/packages/unplugin/src/index.ts b/packages/unplugin/src/index.ts index bd7ae4f5..240fbb06 100644 --- a/packages/unplugin/src/index.ts +++ b/packages/unplugin/src/index.ts @@ -2,6 +2,8 @@ import { createUnplugin } from "unplugin"; import MagicString from "magic-string"; import { getReleaseName } from "./getReleaseName"; import * as path from "path"; +import { Options } from "./types"; +import { makeSentryFacade } from "./facade"; function generateGlobalInjectorCode({ release }: { release: string }) { return ` @@ -17,15 +19,12 @@ function generateGlobalInjectorCode({ release }: { release: string }) { _global.SENTRY_RELEASE={id:"${release}"};`; } -export interface Options { - debugLogging?: boolean; -} - const unplugin = createUnplugin((options) => { - function debugLog(message: string) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + function debugLog(...args: any) { if (options?.debugLogging) { // eslint-disable-next-line no-console - console.log(`[Sentry-plugin] ${message}`); + console.log("[Sentry-plugin]}", args); } } @@ -88,7 +87,9 @@ const unplugin = createUnplugin((options) => { transform(code, id) { if (entrypoints.has(path.normalize(id))) { const ms = new MagicString(code); - ms.prepend(generateGlobalInjectorCode({ release: getReleaseName() })); + ms.prepend( + generateGlobalInjectorCode({ release: getReleaseName(options.release || "0.0.1") }) + ); return { code: ms.toString(), map: ms.generateMap(), @@ -98,6 +99,11 @@ const unplugin = createUnplugin((options) => { return undefined; } }, + buildEnd() { + const sentryFacade = makeSentryFacade(getReleaseName(options.release || "0.0.1"), options); + //TODO: do stuff with the facade here lol + debugLog("this is my facade:", sentryFacade); + }, }; }); @@ -109,3 +115,5 @@ export const sentryRollupPlugin: (options: Options) => any = unplugin.rollup; export const sentryWebpackPlugin: (options: Options) => any = unplugin.webpack; // eslint-disable-next-line @typescript-eslint/no-explicit-any export const sentryEsbuildPlugin: (options: Options) => any = unplugin.esbuild; + +export type { Options } from "./types"; diff --git a/packages/unplugin/src/types.ts b/packages/unplugin/src/types.ts new file mode 100644 index 00000000..d4041e28 --- /dev/null +++ b/packages/unplugin/src/types.ts @@ -0,0 +1,63 @@ +//TODO: JsDoc for all properties +export type Options = { + debugLogging?: boolean; + + /* --- authentication/identification: */ + org?: string; + project?: string; + authToken?: string; + url?: string; + // configFile: string + + /* --- release properties: */ + release?: string; + // dist: string, + // entries: string[] | RegExp | ((key: string) => boolean); + + /* --- source maps properties: */ + //TODO: make this required + include?: string; //| string[] | IncludeEntry[]; + // ignoreFile: string + // ignore: string | string[] + // ext: string[] + // urlPrefix: string, + // urlSuffix: string, + // validate: boolean + // stripPrefix?: boolean, + // stripCommonPrefix?: boolean, + // sourceMapReference?: boolean, + // rewrite?: boolean, + + /* --- other unimportant (for now) stuff- properties: */ + // vcsRemote: string, + // customHeader: string, + + // finalize?: boolean, + // dryRun?: boolean, + debug?: boolean; + // silent?: boolean, + // cleanArtifacts?: boolean, + // errorHandler?: (err: Error, invokeErr: function(): void, compilation: unknown) => void, + // setCommits?: { + // repo?: string, + // commit?: string, + // previousCommit?: string, + // auto?: boolean, + // ignoreMissing?: boolean + // }, + // deploy?: { + // env: string, + // started?: number, + // finished?: number, + // time?: number, + // name?: string, + // url?: string, + // } +}; + +/* +type IncludeEntry = { + paths: string[]; + //TODO: what about the other entries?? +}; +*/ diff --git a/yarn.lock b/yarn.lock index 42490257..0927cbd4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2032,6 +2032,13 @@ magic-string "^0.25.7" resolve "^1.17.0" +"@rollup/plugin-json@^4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@rollup/plugin-json/-/plugin-json-4.1.0.tgz#54e09867ae6963c593844d8bd7a9c718294496f3" + integrity sha512-yfLbTdNS6amI/2OpmbiBoW12vngr5NW2jCJVZSBEz+H5KfUJZ2M7sDjk0U6GOOdCWFVScShte29o9NezJ53TPw== + dependencies: + "@rollup/pluginutils" "^3.0.8" + "@rollup/plugin-node-resolve@13.3.0": version "13.3.0" resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.3.0.tgz#da1c5c5ce8316cef96a2f823d111c1e4e498801c" @@ -2044,7 +2051,7 @@ is-module "^1.0.0" resolve "^1.19.0" -"@rollup/pluginutils@^3.1.0": +"@rollup/pluginutils@^3.0.8", "@rollup/pluginutils@^3.1.0": version "3.1.0" resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-3.1.0.tgz#706b4524ee6dc8b103b3c995533e5ad680c02b9b" integrity sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg== @@ -2053,6 +2060,19 @@ estree-walker "^1.0.1" picomatch "^2.2.2" +"@sentry/cli@1.74.5": + version "1.74.5" + resolved "https://registry.yarnpkg.com/@sentry/cli/-/cli-1.74.5.tgz#4a5c622913087c9ab6f82994da9a7526423779b8" + integrity sha512-Ze1ec306ZWHtrxKypOJ8nhtFqkrx2f/6bRH+DcJzEQ3bBePQ0ZnqJTTe4BBHADYBtxFIaUWzCZ6DquLz2Zv/sw== + dependencies: + https-proxy-agent "^5.0.0" + mkdirp "^0.5.5" + node-fetch "^2.6.7" + npmlog "^4.1.2" + progress "^2.0.3" + proxy-from-env "^1.1.0" + which "^2.0.2" + "@sinclair/typebox@^0.23.3": version "0.23.5" resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.23.5.tgz#93f7b9f4e3285a7a9ade7557d9a8d36809cbc47d" @@ -2735,6 +2755,13 @@ agent-base@4, agent-base@^4.3.0: dependencies: es6-promisify "^5.0.0" +agent-base@6: + version "6.0.2" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" + integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== + dependencies: + debug "4" + agent-base@~4.2.1: version "4.2.1" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-4.2.1.tgz#d89e5999f797875674c07d87f260fc41e83e8ca9" @@ -4053,6 +4080,13 @@ debug@3.1.0: dependencies: ms "2.0.0" +debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + debug@^2.2.0, debug@^2.3.3: version "2.6.9" resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" @@ -4067,13 +4101,6 @@ debug@^3.1.0: dependencies: ms "^2.1.1" -debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4: - version "4.3.4" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" - integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== - dependencies: - ms "2.1.2" - debuglog@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" @@ -5701,6 +5728,14 @@ https-proxy-agent@^2.2.1, https-proxy-agent@^2.2.3: agent-base "^4.3.0" debug "^3.1.0" +https-proxy-agent@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" + integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== + dependencies: + agent-base "6" + debug "4" + human-signals@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-1.1.1.tgz#c5b1cd14f50aeae09ab6c59fe63ba3395fe4dfa3" @@ -8339,6 +8374,11 @@ process@^0.11.10: resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182" integrity sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A== +progress@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" + integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== + promise-inflight@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" @@ -8393,6 +8433,11 @@ protoduck@^5.0.1: dependencies: genfun "^5.0.0" +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + prr@~1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" @@ -10326,7 +10371,7 @@ which@1, which@^1.2.9, which@^1.3.1: dependencies: isexe "^2.0.0" -which@^2.0.1: +which@^2.0.1, which@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==