-
Notifications
You must be signed in to change notification settings - Fork 50
feat(core): Automatically annotate React components at build time #464
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
a53c5df
04c3099
d2409f5
96adebe
045ae81
9f22305
9898f32
11c45c8
c2a37d3
10b08c4
9102567
e2b42bc
55ab44d
f7b3622
f611ea8
6b16b2a
f1beec7
51ae921
b5e3324
a105893
c8ac897
0928c68
5e9fc7a
3340184
fbe80fd
c86dd16
96765d9
0855cfc
712c93b
bf9c2a8
08ec6b1
c140195
e311706
972b761
c37750d
965d2db
2e5c7ee
c554b3c
2072848
e58de0b
210835f
a8fe6e8
e013cbb
a4414ff
bd5d4cc
057e280
fa85813
5f77ceb
94a0327
7e8ac0f
57d3963
49145d3
303b033
6394e93
298487a
5cb45e0
84d88f0
caf0f6f
3b667bf
09a0e99
4a9bc92
5f9913f
fd748b8
4293a38
5f2a93b
5aeaf95
a70cc64
5166fc4
f4b0d39
da92b05
8749bdf
c787dbe
3864fe6
85b4cfd
022443c
f52691a
f84648e
6ae09be
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,8 +1,9 @@ | ||
| import SentryCli from "@sentry/cli"; | ||
| import { transformAsync } from "@babel/core"; | ||
| import * as fs from "fs"; | ||
| import * as path from "path"; | ||
| import MagicString from "magic-string"; | ||
| import { createUnplugin, UnpluginOptions } from "unplugin"; | ||
| import { createUnplugin, TransformResult, UnpluginOptions } from "unplugin"; | ||
| import { normalizeUserOptions, validateOptions } from "./options-mapping"; | ||
| import { createDebugIdUploadFunction } from "./debug-id-upload"; | ||
| import { releaseManagementPlugin } from "./plugins/release-management"; | ||
|
|
@@ -22,9 +23,16 @@ import { | |
| } from "./utils"; | ||
| import * as dotenv from "dotenv"; | ||
| import { glob } from "glob"; | ||
| import pkg from "@sentry/utils"; | ||
| const { logger } = pkg; | ||
|
|
||
| // eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
| // @ts-ignore Importing the annotate plugin which is not concerned with types | ||
| import { reactAnnotate } from "./plugins/react-annotate-plugin"; | ||
|
|
||
| interface SentryUnpluginFactoryOptions { | ||
| releaseInjectionPlugin: (injectionCode: string) => UnpluginOptions; | ||
| reactAnnotatePlugin: () => UnpluginOptions; | ||
| moduleMetadataInjectionPlugin?: (injectionCode: string) => UnpluginOptions; | ||
| debugIdInjectionPlugin: () => UnpluginOptions; | ||
| debugIdUploadPlugin: (upload: (buildArtifacts: string[]) => Promise<void>) => UnpluginOptions; | ||
|
|
@@ -60,6 +68,7 @@ interface SentryUnpluginFactoryOptions { | |
| */ | ||
| export function sentryUnpluginFactory({ | ||
| releaseInjectionPlugin, | ||
| reactAnnotatePlugin, | ||
| moduleMetadataInjectionPlugin, | ||
| debugIdInjectionPlugin, | ||
| debugIdUploadPlugin, | ||
|
|
@@ -317,6 +326,20 @@ export function sentryUnpluginFactory({ | |
| ); | ||
| } | ||
|
|
||
| if (!options.reactAnnotate) { | ||
| logger.warn( | ||
| "No options provided for the react annotate plugin. Please set `reactAnnotate.enabled` to `true` if you would like to have your React components automatically annotated at build-time." | ||
| ); | ||
| } else if (!options.reactAnnotate.enabled) { | ||
| logger.info( | ||
| "The react annotate plugin is currently disabled. Skipping react component name annotations." | ||
| ); | ||
| } else { | ||
| { | ||
| plugins.push(reactAnnotatePlugin()); | ||
| } | ||
| } | ||
|
|
||
| return plugins; | ||
| }); | ||
| } | ||
|
|
@@ -346,7 +369,6 @@ export function sentryCliBinaryExists(): boolean { | |
|
|
||
| export function createRollupReleaseInjectionHooks(injectionCode: string) { | ||
| const virtualReleaseInjectionFileId = "\0sentry-release-injection-file"; | ||
|
|
||
| return { | ||
| resolveId(id: string) { | ||
| if (id === virtualReleaseInjectionFileId) { | ||
|
|
@@ -510,6 +532,58 @@ export function createRollupDebugIdUploadHooks( | |
| }; | ||
| } | ||
|
|
||
| export function createReactAnnotateHooks() { | ||
| return { | ||
| async transform(this: void, code: string, id: string): Promise<TransformResult> { | ||
| // id may contain query and hash which will trip up our file extension logic below | ||
| const idWithoutQueryAndHash = stripQueryAndHashFromPath(id); | ||
|
|
||
| if (idWithoutQueryAndHash.match(/\\node_modules\\|\/node_modules\//)) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. m: Does this work with windows file paths?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This code is copied from another transform hook in our release injection plugin, so I assume so (but if not, it's broken in that plugin too :p) |
||
| return null; | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. m: is returning null safe here? Do we have to
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good point,
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. However, we already return |
||
| } | ||
|
|
||
| // We will only apply this plugin on jsx and tsx files | ||
| if (![".jsx", ".tsx"].some((ending) => idWithoutQueryAndHash.endsWith(ending))) { | ||
| return null; | ||
| } | ||
|
|
||
| const parserPlugins = []; | ||
| if (idWithoutQueryAndHash.endsWith(".jsx")) { | ||
| parserPlugins.push("jsx"); | ||
| } else if (idWithoutQueryAndHash.endsWith(".tsx")) { | ||
| parserPlugins.push("jsx", "typescript"); | ||
| } | ||
|
|
||
| try { | ||
| const result = await transformAsync(code, { | ||
| plugins: [[reactAnnotate]], | ||
0Calories marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| filename: id, | ||
| parserOpts: { | ||
| sourceType: "module", | ||
| allowAwaitOutsideFunction: true, | ||
| // eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
| // @ts-ignore This array will always be type compliant when using 'jsx' or 'typescript' | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. m: What is the type error? Can we change the
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've tried to make this type compliant, but it was a colossal pain. The appropriate type is available only in
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we pick the type from using
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I tried that yesterday but was not successful 😢
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice, that did the trick! |
||
| plugins: parserPlugins, | ||
| }, | ||
| generatorOpts: { | ||
| decoratorsBeforeExport: true, | ||
| }, | ||
| sourceMaps: true, | ||
| }); | ||
|
|
||
| return { | ||
| code: result?.code ?? code, | ||
| map: result?.map, | ||
| }; | ||
| } catch (e) { | ||
| logger.error(`Failed to apply react annotate plugin`, e); | ||
| } | ||
|
|
||
| return { code }; | ||
| }, | ||
| }; | ||
| } | ||
|
|
||
| export function getDebugIdSnippet(debugId: string): string { | ||
| return `;!function(){try{var e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},n=(new Error).stack;n&&(e._sentryDebugIds=e._sentryDebugIds||{},e._sentryDebugIds[n]="${debugId}",e._sentryDebugIdIdentifier="sentry-dbid-${debugId}")}catch(e){}}();`; | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Had to move this to a regular dependency, since it is necessary to run
transformAsyncfrom@babel/coreduring the transform hook