@sentry/wizard 6.6.0 → 6.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/LICENSE +97 -8
  3. package/dist/bin.js +5 -0
  4. package/dist/bin.js.map +1 -1
  5. package/dist/e2e-tests/tests/help-message.test.js +5 -1
  6. package/dist/e2e-tests/tests/help-message.test.js.map +1 -1
  7. package/dist/e2e-tests/tests/nextjs-15.test.js +79 -0
  8. package/dist/e2e-tests/tests/nextjs-15.test.js.map +1 -1
  9. package/dist/e2e-tests/tests/react-router.test.d.ts +1 -0
  10. package/dist/e2e-tests/tests/react-router.test.js +255 -0
  11. package/dist/e2e-tests/tests/react-router.test.js.map +1 -0
  12. package/dist/e2e-tests/utils/index.d.ts +8 -2
  13. package/dist/e2e-tests/utils/index.js +72 -21
  14. package/dist/e2e-tests/utils/index.js.map +1 -1
  15. package/dist/lib/Constants.d.ts +1 -0
  16. package/dist/lib/Constants.js +5 -0
  17. package/dist/lib/Constants.js.map +1 -1
  18. package/dist/src/android/android-wizard.js +8 -1
  19. package/dist/src/android/android-wizard.js.map +1 -1
  20. package/dist/src/angular/angular-wizard.js +8 -1
  21. package/dist/src/angular/angular-wizard.js.map +1 -1
  22. package/dist/src/apple/apple-wizard.js +8 -1
  23. package/dist/src/apple/apple-wizard.js.map +1 -1
  24. package/dist/src/flutter/flutter-wizard.js +8 -1
  25. package/dist/src/flutter/flutter-wizard.js.map +1 -1
  26. package/dist/src/nextjs/nextjs-wizard.js +35 -9
  27. package/dist/src/nextjs/nextjs-wizard.js.map +1 -1
  28. package/dist/src/nextjs/templates.d.ts +3 -3
  29. package/dist/src/nextjs/templates.js +18 -7
  30. package/dist/src/nextjs/templates.js.map +1 -1
  31. package/dist/src/nuxt/nuxt-wizard.js +8 -1
  32. package/dist/src/nuxt/nuxt-wizard.js.map +1 -1
  33. package/dist/src/react-native/react-native-wizard.js +8 -1
  34. package/dist/src/react-native/react-native-wizard.js.map +1 -1
  35. package/dist/src/react-router/codemods/client.entry.d.ts +1 -0
  36. package/dist/src/react-router/codemods/client.entry.js +73 -0
  37. package/dist/src/react-router/codemods/client.entry.js.map +1 -0
  38. package/dist/src/react-router/codemods/react-router-config.d.ts +9 -0
  39. package/dist/src/react-router/codemods/react-router-config.js +178 -0
  40. package/dist/src/react-router/codemods/react-router-config.js.map +1 -0
  41. package/dist/src/react-router/codemods/root.d.ts +1 -0
  42. package/dist/src/react-router/codemods/root.js +171 -0
  43. package/dist/src/react-router/codemods/root.js.map +1 -0
  44. package/dist/src/react-router/codemods/routes-config.d.ts +1 -0
  45. package/dist/src/react-router/codemods/routes-config.js +106 -0
  46. package/dist/src/react-router/codemods/routes-config.js.map +1 -0
  47. package/dist/src/react-router/codemods/server-entry.d.ts +4 -0
  48. package/dist/src/react-router/codemods/server-entry.js +275 -0
  49. package/dist/src/react-router/codemods/server-entry.js.map +1 -0
  50. package/dist/src/react-router/codemods/utils.d.ts +2 -0
  51. package/dist/src/react-router/codemods/utils.js +13 -0
  52. package/dist/src/react-router/codemods/utils.js.map +1 -0
  53. package/dist/src/react-router/codemods/vite.d.ts +8 -0
  54. package/dist/src/react-router/codemods/vite.js +169 -0
  55. package/dist/src/react-router/codemods/vite.js.map +1 -0
  56. package/dist/src/react-router/react-router-wizard.d.ts +2 -0
  57. package/dist/src/react-router/react-router-wizard.js +254 -0
  58. package/dist/src/react-router/react-router-wizard.js.map +1 -0
  59. package/dist/src/react-router/sdk-example.d.ts +18 -0
  60. package/dist/src/react-router/sdk-example.js +306 -0
  61. package/dist/src/react-router/sdk-example.js.map +1 -0
  62. package/dist/src/react-router/sdk-setup.d.ts +17 -0
  63. package/dist/src/react-router/sdk-setup.js +250 -0
  64. package/dist/src/react-router/sdk-setup.js.map +1 -0
  65. package/dist/src/react-router/templates.d.ts +11 -0
  66. package/dist/src/react-router/templates.js +273 -0
  67. package/dist/src/react-router/templates.js.map +1 -0
  68. package/dist/src/remix/remix-wizard.js +8 -1
  69. package/dist/src/remix/remix-wizard.js.map +1 -1
  70. package/dist/src/run.d.ts +2 -1
  71. package/dist/src/run.js +6 -0
  72. package/dist/src/run.js.map +1 -1
  73. package/dist/src/sourcemaps/sourcemaps-wizard.js +8 -1
  74. package/dist/src/sourcemaps/sourcemaps-wizard.js.map +1 -1
  75. package/dist/src/sveltekit/sveltekit-wizard.js +8 -1
  76. package/dist/src/sveltekit/sveltekit-wizard.js.map +1 -1
  77. package/dist/src/utils/ast-utils.d.ts +30 -0
  78. package/dist/src/utils/ast-utils.js +71 -1
  79. package/dist/src/utils/ast-utils.js.map +1 -1
  80. package/dist/src/utils/clack/index.d.ts +5 -2
  81. package/dist/src/utils/clack/index.js +14 -2
  82. package/dist/src/utils/clack/index.js.map +1 -1
  83. package/dist/src/utils/types.d.ts +9 -0
  84. package/dist/src/utils/types.js.map +1 -1
  85. package/dist/src/version.d.ts +1 -1
  86. package/dist/src/version.js +1 -1
  87. package/dist/src/version.js.map +1 -1
  88. package/dist/test/nextjs/templates.test.js +20 -0
  89. package/dist/test/nextjs/templates.test.js.map +1 -1
  90. package/dist/test/react-router/codemods/client-entry.test.d.ts +1 -0
  91. package/dist/test/react-router/codemods/client-entry.test.js +168 -0
  92. package/dist/test/react-router/codemods/client-entry.test.js.map +1 -0
  93. package/dist/test/react-router/codemods/react-router-config.test.d.ts +1 -0
  94. package/dist/test/react-router/codemods/react-router-config.test.js +168 -0
  95. package/dist/test/react-router/codemods/react-router-config.test.js.map +1 -0
  96. package/dist/test/react-router/codemods/root.test.d.ts +1 -0
  97. package/dist/test/react-router/codemods/root.test.js +178 -0
  98. package/dist/test/react-router/codemods/root.test.js.map +1 -0
  99. package/dist/test/react-router/codemods/server-entry.test.d.ts +1 -0
  100. package/dist/test/react-router/codemods/server-entry.test.js +415 -0
  101. package/dist/test/react-router/codemods/server-entry.test.js.map +1 -0
  102. package/dist/test/react-router/codemods/vite.test.d.ts +1 -0
  103. package/dist/test/react-router/codemods/vite.test.js +158 -0
  104. package/dist/test/react-router/codemods/vite.test.js.map +1 -0
  105. package/dist/test/react-router/routes-config.test.d.ts +1 -0
  106. package/dist/test/react-router/routes-config.test.js +156 -0
  107. package/dist/test/react-router/routes-config.test.js.map +1 -0
  108. package/dist/test/react-router/sdk-setup.test.d.ts +1 -0
  109. package/dist/test/react-router/sdk-setup.test.js +411 -0
  110. package/dist/test/react-router/sdk-setup.test.js.map +1 -0
  111. package/dist/test/react-router/templates.test.d.ts +1 -0
  112. package/dist/test/react-router/templates.test.js +220 -0
  113. package/dist/test/react-router/templates.test.js.map +1 -0
  114. package/dist/test/utils/clack/index.test.js +1 -1
  115. package/dist/test/utils/clack/index.test.js.map +1 -1
  116. package/package.json +2 -2
@@ -0,0 +1,171 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.instrumentRoot = void 0;
27
+ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
28
+ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
29
+ /* eslint-disable @typescript-eslint/no-unsafe-call */
30
+ /* eslint-disable @typescript-eslint/no-unsafe-argument */
31
+ const recast = __importStar(require("recast"));
32
+ const path = __importStar(require("path"));
33
+ const magicast_1 = require("magicast");
34
+ const templates_1 = require("../templates");
35
+ const ast_utils_1 = require("../../utils/ast-utils");
36
+ const debug_1 = require("../../utils/debug");
37
+ function hasCaptureExceptionCall(node) {
38
+ let found = false;
39
+ recast.visit(node, {
40
+ visitCallExpression(path) {
41
+ const callee = path.value.callee;
42
+ if ((callee.type === 'MemberExpression' &&
43
+ callee.object?.name === 'Sentry' &&
44
+ callee.property?.name === 'captureException') ||
45
+ (callee.type === 'Identifier' && callee.name === 'captureException')) {
46
+ found = true;
47
+ }
48
+ this.traverse(path);
49
+ },
50
+ });
51
+ return found;
52
+ }
53
+ function addCaptureExceptionCall(functionNode) {
54
+ const captureExceptionCall = recast.parse(`Sentry.captureException(error);`)
55
+ .program.body[0];
56
+ const functionBody = (0, ast_utils_1.safeGetFunctionBody)(functionNode);
57
+ if (functionBody) {
58
+ if (!(0, ast_utils_1.safeInsertBeforeReturn)(functionBody, captureExceptionCall)) {
59
+ functionBody.push(captureExceptionCall);
60
+ }
61
+ }
62
+ else {
63
+ (0, debug_1.debug)('Could not safely access ErrorBoundary function body');
64
+ }
65
+ }
66
+ function findErrorBoundaryInExports(namedExports) {
67
+ return namedExports.some((namedExport) => {
68
+ const declaration = namedExport.declaration;
69
+ if (!declaration) {
70
+ return namedExport.specifiers?.some((spec) => spec.type === 'ExportSpecifier' &&
71
+ spec.exported?.type === 'Identifier' &&
72
+ spec.exported.name === 'ErrorBoundary');
73
+ }
74
+ if (declaration.type === 'FunctionDeclaration') {
75
+ return declaration.id?.name === 'ErrorBoundary';
76
+ }
77
+ if (declaration.type === 'VariableDeclaration') {
78
+ return declaration.declarations.some((decl) => {
79
+ // @ts-expect-error - id should always have a name in this case
80
+ return decl.id?.name === 'ErrorBoundary';
81
+ });
82
+ }
83
+ return false;
84
+ });
85
+ }
86
+ async function instrumentRoot(rootFileName) {
87
+ const filePath = path.join(process.cwd(), 'app', rootFileName);
88
+ const rootRouteAst = await (0, magicast_1.loadFile)(filePath);
89
+ const exportsAst = rootRouteAst.exports.$ast;
90
+ const namedExports = exportsAst.body.filter((node) => node.type === 'ExportNamedDeclaration');
91
+ const foundErrorBoundary = findErrorBoundaryInExports(namedExports);
92
+ const alreadyHasSentry = (0, ast_utils_1.hasSentryContent)(rootRouteAst.$ast);
93
+ if (!alreadyHasSentry) {
94
+ rootRouteAst.imports.$add({
95
+ from: '@sentry/react-router',
96
+ imported: '*',
97
+ local: 'Sentry',
98
+ });
99
+ }
100
+ if (!foundErrorBoundary) {
101
+ const hasIsRouteErrorResponseImport = rootRouteAst.imports.$items.some((item) => item.imported === 'isRouteErrorResponse' &&
102
+ item.from === 'react-router');
103
+ if (!hasIsRouteErrorResponseImport) {
104
+ rootRouteAst.imports.$add({
105
+ from: 'react-router',
106
+ imported: 'isRouteErrorResponse',
107
+ local: 'isRouteErrorResponse',
108
+ });
109
+ }
110
+ recast.visit(rootRouteAst.$ast, {
111
+ visitExportDefaultDeclaration(path) {
112
+ const implementation = recast.parse(templates_1.ERROR_BOUNDARY_TEMPLATE).program
113
+ .body[0];
114
+ path.insertBefore(recast.types.builders.exportDeclaration(false, implementation));
115
+ this.traverse(path);
116
+ },
117
+ });
118
+ }
119
+ else {
120
+ recast.visit(rootRouteAst.$ast, {
121
+ visitExportNamedDeclaration(path) {
122
+ const declaration = path.value.declaration;
123
+ if (!declaration) {
124
+ this.traverse(path);
125
+ return;
126
+ }
127
+ let functionToInstrument = null;
128
+ if (declaration.type === 'FunctionDeclaration' &&
129
+ declaration.id?.name === 'ErrorBoundary') {
130
+ functionToInstrument = declaration;
131
+ }
132
+ else if (declaration.type === 'VariableDeclaration' &&
133
+ declaration.declarations?.[0]?.id?.name === 'ErrorBoundary') {
134
+ const init = declaration.declarations[0].init;
135
+ if (init &&
136
+ (init.type === 'FunctionExpression' ||
137
+ init.type === 'ArrowFunctionExpression')) {
138
+ functionToInstrument = init;
139
+ }
140
+ }
141
+ if (functionToInstrument &&
142
+ !hasCaptureExceptionCall(functionToInstrument)) {
143
+ addCaptureExceptionCall(functionToInstrument);
144
+ }
145
+ this.traverse(path);
146
+ },
147
+ visitVariableDeclaration(path) {
148
+ if (path.value.declarations?.[0]?.id?.name === 'ErrorBoundary') {
149
+ const init = path.value.declarations[0].init;
150
+ if (init &&
151
+ (init.type === 'FunctionExpression' ||
152
+ init.type === 'ArrowFunctionExpression') &&
153
+ !hasCaptureExceptionCall(init)) {
154
+ addCaptureExceptionCall(init);
155
+ }
156
+ }
157
+ this.traverse(path);
158
+ },
159
+ visitFunctionDeclaration(path) {
160
+ if (path.value.id?.name === 'ErrorBoundary' &&
161
+ !hasCaptureExceptionCall(path.value)) {
162
+ addCaptureExceptionCall(path.value);
163
+ }
164
+ this.traverse(path);
165
+ },
166
+ });
167
+ }
168
+ await (0, magicast_1.writeFile)(rootRouteAst.$ast, filePath);
169
+ }
170
+ exports.instrumentRoot = instrumentRoot;
171
+ //# sourceMappingURL=root.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"root.js","sourceRoot":"","sources":["../../../../src/react-router/codemods/root.ts"],"names":[],"mappings":"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","sourcesContent":["/* eslint-disable @typescript-eslint/no-unsafe-member-access */\n/* eslint-disable @typescript-eslint/no-unsafe-assignment */\n/* eslint-disable @typescript-eslint/no-unsafe-call */\n/* eslint-disable @typescript-eslint/no-unsafe-argument */\nimport * as recast from 'recast';\nimport * as path from 'path';\n\nimport type { ExportNamedDeclaration } from '@babel/types';\nimport type { namedTypes as t } from 'ast-types';\n\nimport {\n loadFile,\n writeFile,\n // @ts-expect-error - magicast is ESM and TS complains about that. It works though\n} from 'magicast';\n\nimport { ERROR_BOUNDARY_TEMPLATE } from '../templates';\nimport {\n hasSentryContent,\n safeGetFunctionBody,\n safeInsertBeforeReturn,\n} from '../../utils/ast-utils';\nimport { debug } from '../../utils/debug';\n\nfunction hasCaptureExceptionCall(node: t.Node): boolean {\n let found = false;\n recast.visit(node, {\n visitCallExpression(path) {\n const callee = path.value.callee;\n if (\n (callee.type === 'MemberExpression' &&\n callee.object?.name === 'Sentry' &&\n callee.property?.name === 'captureException') ||\n (callee.type === 'Identifier' && callee.name === 'captureException')\n ) {\n found = true;\n }\n this.traverse(path);\n },\n });\n return found;\n}\n\nfunction addCaptureExceptionCall(functionNode: t.Node): void {\n const captureExceptionCall = recast.parse(`Sentry.captureException(error);`)\n .program.body[0];\n\n const functionBody = safeGetFunctionBody(functionNode);\n if (functionBody) {\n if (!safeInsertBeforeReturn(functionBody, captureExceptionCall)) {\n functionBody.push(captureExceptionCall);\n }\n } else {\n debug('Could not safely access ErrorBoundary function body');\n }\n}\n\nfunction findErrorBoundaryInExports(\n namedExports: ExportNamedDeclaration[],\n): boolean {\n return namedExports.some((namedExport) => {\n const declaration = namedExport.declaration;\n\n if (!declaration) {\n return namedExport.specifiers?.some(\n (spec) =>\n spec.type === 'ExportSpecifier' &&\n spec.exported?.type === 'Identifier' &&\n spec.exported.name === 'ErrorBoundary',\n );\n }\n\n if (declaration.type === 'FunctionDeclaration') {\n return declaration.id?.name === 'ErrorBoundary';\n }\n\n if (declaration.type === 'VariableDeclaration') {\n return declaration.declarations.some((decl) => {\n // @ts-expect-error - id should always have a name in this case\n return decl.id?.name === 'ErrorBoundary';\n });\n }\n\n return false;\n });\n}\n\nexport async function instrumentRoot(rootFileName: string): Promise<void> {\n const filePath = path.join(process.cwd(), 'app', rootFileName);\n const rootRouteAst = await loadFile(filePath);\n\n const exportsAst = rootRouteAst.exports.$ast as t.Program;\n const namedExports = exportsAst.body.filter(\n (node) => node.type === 'ExportNamedDeclaration',\n ) as ExportNamedDeclaration[];\n\n const foundErrorBoundary = findErrorBoundaryInExports(namedExports);\n const alreadyHasSentry = hasSentryContent(rootRouteAst.$ast as t.Program);\n\n if (!alreadyHasSentry) {\n rootRouteAst.imports.$add({\n from: '@sentry/react-router',\n imported: '*',\n local: 'Sentry',\n });\n }\n\n if (!foundErrorBoundary) {\n const hasIsRouteErrorResponseImport = rootRouteAst.imports.$items.some(\n (item) =>\n item.imported === 'isRouteErrorResponse' &&\n item.from === 'react-router',\n );\n\n if (!hasIsRouteErrorResponseImport) {\n rootRouteAst.imports.$add({\n from: 'react-router',\n imported: 'isRouteErrorResponse',\n local: 'isRouteErrorResponse',\n });\n }\n\n recast.visit(rootRouteAst.$ast, {\n visitExportDefaultDeclaration(path) {\n const implementation = recast.parse(ERROR_BOUNDARY_TEMPLATE).program\n .body[0];\n\n path.insertBefore(\n recast.types.builders.exportDeclaration(false, implementation),\n );\n\n this.traverse(path);\n },\n });\n } else {\n recast.visit(rootRouteAst.$ast, {\n visitExportNamedDeclaration(path) {\n const declaration = path.value.declaration;\n if (!declaration) {\n this.traverse(path);\n return;\n }\n\n let functionToInstrument = null;\n\n if (\n declaration.type === 'FunctionDeclaration' &&\n declaration.id?.name === 'ErrorBoundary'\n ) {\n functionToInstrument = declaration;\n } else if (\n declaration.type === 'VariableDeclaration' &&\n declaration.declarations?.[0]?.id?.name === 'ErrorBoundary'\n ) {\n const init = declaration.declarations[0].init;\n if (\n init &&\n (init.type === 'FunctionExpression' ||\n init.type === 'ArrowFunctionExpression')\n ) {\n functionToInstrument = init;\n }\n }\n\n if (\n functionToInstrument &&\n !hasCaptureExceptionCall(functionToInstrument)\n ) {\n addCaptureExceptionCall(functionToInstrument);\n }\n\n this.traverse(path);\n },\n\n visitVariableDeclaration(path) {\n if (path.value.declarations?.[0]?.id?.name === 'ErrorBoundary') {\n const init = path.value.declarations[0].init;\n if (\n init &&\n (init.type === 'FunctionExpression' ||\n init.type === 'ArrowFunctionExpression') &&\n !hasCaptureExceptionCall(init)\n ) {\n addCaptureExceptionCall(init);\n }\n }\n this.traverse(path);\n },\n\n visitFunctionDeclaration(path) {\n if (\n path.value.id?.name === 'ErrorBoundary' &&\n !hasCaptureExceptionCall(path.value)\n ) {\n addCaptureExceptionCall(path.value);\n }\n this.traverse(path);\n },\n });\n }\n\n await writeFile(rootRouteAst.$ast, filePath);\n}\n"]}
@@ -0,0 +1 @@
1
+ export declare function addRoutesToConfig(routesConfigPath: string, isTS: boolean): Promise<void>;
@@ -0,0 +1,106 @@
1
+ "use strict";
2
+ /* eslint-disable @typescript-eslint/no-unsafe-argument */
3
+ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
4
+ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
5
+ /* eslint-disable @typescript-eslint/no-unsafe-call */
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
18
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
19
+ }) : function(o, v) {
20
+ o["default"] = v;
21
+ });
22
+ var __importStar = (this && this.__importStar) || function (mod) {
23
+ if (mod && mod.__esModule) return mod;
24
+ var result = {};
25
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
26
+ __setModuleDefault(result, mod);
27
+ return result;
28
+ };
29
+ Object.defineProperty(exports, "__esModule", { value: true });
30
+ exports.addRoutesToConfig = void 0;
31
+ const recast = __importStar(require("recast"));
32
+ const fs = __importStar(require("fs"));
33
+ // @ts-expect-error - magicast is ESM and TS complains about that. It works though
34
+ const magicast_1 = require("magicast");
35
+ async function addRoutesToConfig(routesConfigPath, isTS) {
36
+ // Check if file exists first
37
+ if (!fs.existsSync(routesConfigPath)) {
38
+ return;
39
+ }
40
+ const routesAst = await (0, magicast_1.loadFile)(routesConfigPath);
41
+ // Check if routes are already added
42
+ const routesCode = routesAst.$code;
43
+ if (routesCode.includes('sentry-example-page') &&
44
+ routesCode.includes('sentry-example-api')) {
45
+ return;
46
+ }
47
+ // Add route import if not already present
48
+ const hasRouteImport = routesAst.imports.$items.some(
49
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
50
+ (item) => item.imported === 'route' && item.from === '@react-router/dev/routes');
51
+ if (!hasRouteImport) {
52
+ routesAst.imports.$add({
53
+ from: '@react-router/dev/routes',
54
+ imported: 'route',
55
+ local: 'route',
56
+ });
57
+ }
58
+ // Set up the new routes
59
+ const routeExtension = isTS ? 'tsx' : 'jsx';
60
+ const apiExtension = isTS ? 'ts' : 'js';
61
+ const pageRouteCode = `route("/sentry-example-page", "routes/sentry-example-page.${routeExtension}")`;
62
+ const apiRouteCode = `route("/api/sentry-example-api", "routes/api.sentry-example-api.${apiExtension}")`;
63
+ let foundDefaultExport = false;
64
+ // Get the AST program
65
+ const program = routesAst.$ast;
66
+ // Find the default export
67
+ for (let i = 0; i < program.body.length; i++) {
68
+ const node = program.body[i];
69
+ if (node.type === 'ExportDefaultDeclaration') {
70
+ foundDefaultExport = true;
71
+ const declaration = node.declaration;
72
+ let arrayExpression = null;
73
+ if (declaration && declaration.type === 'ArrayExpression') {
74
+ arrayExpression = declaration;
75
+ }
76
+ else if (declaration && declaration.type === 'TSSatisfiesExpression') {
77
+ // Handle TypeScript satisfies expression like: [...] satisfies RouteConfig
78
+ if (declaration.expression &&
79
+ declaration.expression.type === 'ArrayExpression') {
80
+ arrayExpression = declaration.expression;
81
+ }
82
+ }
83
+ if (arrayExpression) {
84
+ // Parse and add the new route calls directly to the elements array
85
+ const pageRouteCall = recast.parse(pageRouteCode).program.body[0].expression;
86
+ const apiRouteCall = recast.parse(apiRouteCode).program.body[0].expression;
87
+ arrayExpression.elements.push(pageRouteCall);
88
+ arrayExpression.elements.push(apiRouteCall);
89
+ }
90
+ break;
91
+ }
92
+ }
93
+ // If no default export found, add one
94
+ if (!foundDefaultExport) {
95
+ // Create a simple array export without satisfies for now
96
+ const newExportCode = `export default [
97
+ ${pageRouteCode},
98
+ ${apiRouteCode},
99
+ ];`;
100
+ const newExport = recast.parse(newExportCode).program.body[0];
101
+ program.body.push(newExport);
102
+ }
103
+ await (0, magicast_1.writeFile)(routesAst.$ast, routesConfigPath);
104
+ }
105
+ exports.addRoutesToConfig = addRoutesToConfig;
106
+ //# sourceMappingURL=routes-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routes-config.js","sourceRoot":"","sources":["../../../../src/react-router/codemods/routes-config.ts"],"names":[],"mappings":"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","sourcesContent":["/* eslint-disable @typescript-eslint/no-unsafe-argument */\n/* eslint-disable @typescript-eslint/no-unsafe-member-access */\n/* eslint-disable @typescript-eslint/no-unsafe-assignment */\n/* eslint-disable @typescript-eslint/no-unsafe-call */\n\nimport * as recast from 'recast';\nimport * as fs from 'fs';\nimport type { namedTypes as t } from 'ast-types';\n\n// @ts-expect-error - magicast is ESM and TS complains about that. It works though\nimport { loadFile, writeFile } from 'magicast';\n\nexport async function addRoutesToConfig(\n routesConfigPath: string,\n isTS: boolean,\n): Promise<void> {\n // Check if file exists first\n if (!fs.existsSync(routesConfigPath)) {\n return;\n }\n\n const routesAst = await loadFile(routesConfigPath);\n\n // Check if routes are already added\n const routesCode = routesAst.$code;\n if (\n routesCode.includes('sentry-example-page') &&\n routesCode.includes('sentry-example-api')\n ) {\n return;\n }\n\n // Add route import if not already present\n const hasRouteImport = routesAst.imports.$items.some(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n (item: any) =>\n item.imported === 'route' && item.from === '@react-router/dev/routes',\n );\n\n if (!hasRouteImport) {\n routesAst.imports.$add({\n from: '@react-router/dev/routes',\n imported: 'route',\n local: 'route',\n });\n }\n\n // Set up the new routes\n const routeExtension = isTS ? 'tsx' : 'jsx';\n const apiExtension = isTS ? 'ts' : 'js';\n\n const pageRouteCode = `route(\"/sentry-example-page\", \"routes/sentry-example-page.${routeExtension}\")`;\n const apiRouteCode = `route(\"/api/sentry-example-api\", \"routes/api.sentry-example-api.${apiExtension}\")`;\n\n let foundDefaultExport = false;\n\n // Get the AST program\n const program = routesAst.$ast as t.Program;\n\n // Find the default export\n for (let i = 0; i < program.body.length; i++) {\n const node = program.body[i];\n\n if (node.type === 'ExportDefaultDeclaration') {\n foundDefaultExport = true;\n\n const declaration = node.declaration;\n\n let arrayExpression = null;\n\n if (declaration && declaration.type === 'ArrayExpression') {\n arrayExpression = declaration;\n } else if (declaration && declaration.type === 'TSSatisfiesExpression') {\n // Handle TypeScript satisfies expression like: [...] satisfies RouteConfig\n if (\n declaration.expression &&\n declaration.expression.type === 'ArrayExpression'\n ) {\n arrayExpression = declaration.expression;\n }\n }\n\n if (arrayExpression) {\n // Parse and add the new route calls directly to the elements array\n const pageRouteCall =\n recast.parse(pageRouteCode).program.body[0].expression;\n const apiRouteCall =\n recast.parse(apiRouteCode).program.body[0].expression;\n\n arrayExpression.elements.push(pageRouteCall);\n arrayExpression.elements.push(apiRouteCall);\n }\n break;\n }\n }\n\n // If no default export found, add one\n if (!foundDefaultExport) {\n // Create a simple array export without satisfies for now\n const newExportCode = `export default [\n ${pageRouteCode},\n ${apiRouteCode},\n];`;\n\n const newExport = recast.parse(newExportCode).program.body[0];\n program.body.push(newExport);\n }\n\n await writeFile(routesAst.$ast, routesConfigPath);\n}\n"]}
@@ -0,0 +1,4 @@
1
+ import type { ProxifiedModule } from 'magicast';
2
+ export declare function instrumentServerEntry(serverEntryPath: string): Promise<void>;
3
+ export declare function instrumentHandleRequest(originalEntryServerMod: ProxifiedModule<any>): void;
4
+ export declare function instrumentHandleError(originalEntryServerMod: ProxifiedModule<any>): void;
@@ -0,0 +1,275 @@
1
+ "use strict";
2
+ /* eslint-disable @typescript-eslint/no-unsafe-argument */
3
+ /* eslint-disable @typescript-eslint/no-explicit-any */
4
+ /* eslint-disable @typescript-eslint/no-unsafe-call */
5
+ /* eslint-disable @typescript-eslint/no-unsafe-member-access */
6
+ /* eslint-disable @typescript-eslint/no-unsafe-return */
7
+ /* eslint-disable @typescript-eslint/no-unsafe-assignment */
8
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
9
+ if (k2 === undefined) k2 = k;
10
+ var desc = Object.getOwnPropertyDescriptor(m, k);
11
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
12
+ desc = { enumerable: true, get: function() { return m[k]; } };
13
+ }
14
+ Object.defineProperty(o, k2, desc);
15
+ }) : (function(o, m, k, k2) {
16
+ if (k2 === undefined) k2 = k;
17
+ o[k2] = m[k];
18
+ }));
19
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
20
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
21
+ }) : function(o, v) {
22
+ o["default"] = v;
23
+ });
24
+ var __importStar = (this && this.__importStar) || function (mod) {
25
+ if (mod && mod.__esModule) return mod;
26
+ var result = {};
27
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
28
+ __setModuleDefault(result, mod);
29
+ return result;
30
+ };
31
+ var __importDefault = (this && this.__importDefault) || function (mod) {
32
+ return (mod && mod.__esModule) ? mod : { "default": mod };
33
+ };
34
+ Object.defineProperty(exports, "__esModule", { value: true });
35
+ exports.instrumentHandleError = exports.instrumentHandleRequest = exports.instrumentServerEntry = void 0;
36
+ const recast = __importStar(require("recast"));
37
+ // @ts-expect-error - clack is ESM and TS complains about that. It works though
38
+ const prompts_1 = __importDefault(require("@clack/prompts"));
39
+ const chalk_1 = __importDefault(require("chalk"));
40
+ // @ts-expect-error - magicast is ESM and TS complains about that. It works though
41
+ const magicast_1 = require("magicast");
42
+ const debug_1 = require("../../utils/debug");
43
+ const ast_utils_1 = require("../../utils/ast-utils");
44
+ const utils_1 = require("./utils");
45
+ async function instrumentServerEntry(serverEntryPath) {
46
+ const serverEntryAst = await (0, magicast_1.loadFile)(serverEntryPath);
47
+ if (!(0, ast_utils_1.hasSentryContent)(serverEntryAst.$ast)) {
48
+ serverEntryAst.imports.$add({
49
+ from: '@sentry/react-router',
50
+ imported: '*',
51
+ local: 'Sentry',
52
+ });
53
+ }
54
+ instrumentHandleError(serverEntryAst);
55
+ instrumentHandleRequest(serverEntryAst);
56
+ await (0, magicast_1.writeFile)(serverEntryAst.$ast, serverEntryPath);
57
+ }
58
+ exports.instrumentServerEntry = instrumentServerEntry;
59
+ function instrumentHandleRequest(originalEntryServerMod) {
60
+ const originalEntryServerModAST = originalEntryServerMod.$ast;
61
+ const defaultServerEntryExport = originalEntryServerModAST.body.find((node) => {
62
+ return node.type === 'ExportDefaultDeclaration';
63
+ });
64
+ if (!defaultServerEntryExport) {
65
+ prompts_1.default.log.warn(`Could not find function ${chalk_1.default.cyan('handleRequest')} in your server entry file. Creating one for you.`);
66
+ let foundServerRouterImport = false;
67
+ let foundRenderToPipeableStreamImport = false;
68
+ let foundCreateReadableStreamFromReadableImport = false;
69
+ originalEntryServerMod.imports.$items.forEach((item) => {
70
+ if (item.imported === 'ServerRouter' && item.from === 'react-router') {
71
+ foundServerRouterImport = true;
72
+ }
73
+ if (item.imported === 'renderToPipeableStream' &&
74
+ item.from === 'react-dom/server') {
75
+ foundRenderToPipeableStreamImport = true;
76
+ }
77
+ if (item.imported === 'createReadableStreamFromReadable' &&
78
+ item.from === '@react-router/node') {
79
+ foundCreateReadableStreamFromReadableImport = true;
80
+ }
81
+ });
82
+ if (!foundServerRouterImport) {
83
+ originalEntryServerMod.imports.$add({
84
+ from: 'react-router',
85
+ imported: 'ServerRouter',
86
+ local: 'ServerRouter',
87
+ });
88
+ }
89
+ if (!foundRenderToPipeableStreamImport) {
90
+ originalEntryServerMod.imports.$add({
91
+ from: 'react-dom/server',
92
+ imported: 'renderToPipeableStream',
93
+ local: 'renderToPipeableStream',
94
+ });
95
+ }
96
+ if (!foundCreateReadableStreamFromReadableImport) {
97
+ originalEntryServerMod.imports.$add({
98
+ from: '@react-router/node',
99
+ imported: 'createReadableStreamFromReadable',
100
+ local: 'createReadableStreamFromReadable',
101
+ });
102
+ }
103
+ const implementation = recast.parse(`const handleRequest = Sentry.createSentryHandleRequest({
104
+ ServerRouter,
105
+ renderToPipeableStream,
106
+ createReadableStreamFromReadable,
107
+ })`).program.body[0];
108
+ try {
109
+ originalEntryServerModAST.body.splice((0, utils_1.getAfterImportsInsertionIndex)(originalEntryServerModAST), 0, implementation);
110
+ originalEntryServerModAST.body.push({
111
+ type: 'ExportDefaultDeclaration',
112
+ declaration: {
113
+ type: 'Identifier',
114
+ name: 'handleRequest',
115
+ },
116
+ });
117
+ }
118
+ catch (error) {
119
+ (0, debug_1.debug)('Failed to insert handleRequest implementation:', error);
120
+ throw new Error('Could not automatically instrument handleRequest. Please add it manually.');
121
+ }
122
+ }
123
+ else if (defaultServerEntryExport &&
124
+ // @ts-expect-error - StatementKind works here because the AST is proxified by magicast
125
+ (0, magicast_1.generateCode)(defaultServerEntryExport).code.includes('wrapSentryHandleRequest')) {
126
+ (0, debug_1.debug)('wrapSentryHandleRequest is already used, skipping wrapping again');
127
+ prompts_1.default.log.info('Sentry handleRequest wrapper already detected, skipping instrumentation.');
128
+ }
129
+ else {
130
+ let defaultExportNode = null;
131
+ const defaultExportIndex = originalEntryServerModAST.body.findIndex((node) => {
132
+ const found = node.type === 'ExportDefaultDeclaration';
133
+ if (found) {
134
+ defaultExportNode = node;
135
+ }
136
+ return found;
137
+ });
138
+ if (defaultExportIndex !== -1 && defaultExportNode !== null) {
139
+ recast.visit(defaultExportNode, {
140
+ visitCallExpression(path) {
141
+ if ((0, ast_utils_1.safeCalleeIdentifierMatch)(path.value.callee, 'pipe') &&
142
+ path.value.arguments.length &&
143
+ path.value.arguments[0].type === 'Identifier' &&
144
+ (0, ast_utils_1.safeGetIdentifierName)(path.value.arguments[0]) === 'body') {
145
+ const wrapped = recast.types.builders.callExpression(recast.types.builders.memberExpression(recast.types.builders.identifier('Sentry'), recast.types.builders.identifier('getMetaTagTransformer')), [path.value.arguments[0]]);
146
+ path.value.arguments[0] = wrapped;
147
+ }
148
+ this.traverse(path);
149
+ },
150
+ });
151
+ // Replace the existing default export with the wrapped one
152
+ originalEntryServerModAST.body.splice(defaultExportIndex, 1,
153
+ // @ts-expect-error - declaration works here because the AST is proxified by magicast
154
+ defaultExportNode.declaration);
155
+ // Adding our wrapped export
156
+ originalEntryServerModAST.body.push(recast.types.builders.exportDefaultDeclaration(recast.types.builders.callExpression(recast.types.builders.memberExpression(recast.types.builders.identifier('Sentry'), recast.types.builders.identifier('wrapSentryHandleRequest')), [recast.types.builders.identifier('handleRequest')])));
157
+ }
158
+ }
159
+ }
160
+ exports.instrumentHandleRequest = instrumentHandleRequest;
161
+ function instrumentHandleError(originalEntryServerMod) {
162
+ const originalEntryServerModAST = originalEntryServerMod.$ast;
163
+ const handleErrorFunctionExport = originalEntryServerModAST.body.find((node) => {
164
+ return (node.type === 'ExportNamedDeclaration' &&
165
+ node.declaration?.type === 'FunctionDeclaration' &&
166
+ node.declaration.id?.name === 'handleError');
167
+ });
168
+ const handleErrorFunctionVariableDeclarationExport = originalEntryServerModAST.body.find((node) => {
169
+ if (node.type !== 'ExportNamedDeclaration' ||
170
+ node.declaration?.type !== 'VariableDeclaration') {
171
+ return false;
172
+ }
173
+ const declarations = node.declaration.declarations;
174
+ if (!declarations || declarations.length === 0) {
175
+ return false;
176
+ }
177
+ const firstDeclaration = declarations[0];
178
+ if (!firstDeclaration || firstDeclaration.type !== 'VariableDeclarator') {
179
+ return false;
180
+ }
181
+ const id = firstDeclaration.id;
182
+ return id && id.type === 'Identifier' && id.name === 'handleError';
183
+ });
184
+ if (!handleErrorFunctionExport &&
185
+ !handleErrorFunctionVariableDeclarationExport) {
186
+ prompts_1.default.log.warn(`Could not find function ${chalk_1.default.cyan('handleError')} in your server entry file. Creating one for you.`);
187
+ const implementation = recast.parse(`const handleError = Sentry.createSentryHandleError({
188
+ logErrors: false
189
+ })`).program.body[0];
190
+ originalEntryServerModAST.body.splice((0, utils_1.getAfterImportsInsertionIndex)(originalEntryServerModAST), 0, recast.types.builders.exportNamedDeclaration(implementation));
191
+ }
192
+ else if ((handleErrorFunctionExport &&
193
+ // @ts-expect-error - StatementKind works here because the AST is proxified by magicast
194
+ (0, magicast_1.generateCode)(handleErrorFunctionExport).code.includes('captureException')) ||
195
+ (handleErrorFunctionVariableDeclarationExport &&
196
+ // @ts-expect-error - StatementKind works here because the AST is proxified by magicast
197
+ (0, magicast_1.generateCode)(handleErrorFunctionVariableDeclarationExport).code.includes('captureException'))) {
198
+ (0, debug_1.debug)('Found captureException inside handleError, skipping adding it again');
199
+ }
200
+ else if ((handleErrorFunctionExport &&
201
+ // @ts-expect-error - StatementKind works here because the AST is proxified by magicast
202
+ (0, magicast_1.generateCode)(handleErrorFunctionExport).code.includes('createSentryHandleError')) ||
203
+ (handleErrorFunctionVariableDeclarationExport &&
204
+ // @ts-expect-error - StatementKind works here because the AST is proxified by magicast
205
+ (0, magicast_1.generateCode)(handleErrorFunctionVariableDeclarationExport).code.includes('createSentryHandleError'))) {
206
+ (0, debug_1.debug)('createSentryHandleError is already used, skipping adding it again');
207
+ }
208
+ else if (handleErrorFunctionExport) {
209
+ // Create the Sentry captureException call as an IfStatement
210
+ const sentryCall = recast.parse(`if (!request.signal.aborted) {
211
+ Sentry.captureException(error);
212
+ }`).program.body[0];
213
+ // Safely insert the Sentry call at the beginning of the handleError function body
214
+ // @ts-expect-error - declaration works here because the AST is proxified by magicast
215
+ const declaration = handleErrorFunctionExport.declaration;
216
+ if (declaration &&
217
+ declaration.body &&
218
+ declaration.body.body &&
219
+ Array.isArray(declaration.body.body)) {
220
+ declaration.body.body.unshift(sentryCall);
221
+ }
222
+ else {
223
+ (0, debug_1.debug)('Cannot safely access handleError function body, skipping instrumentation');
224
+ }
225
+ }
226
+ else if (handleErrorFunctionVariableDeclarationExport) {
227
+ // Create the Sentry captureException call as an IfStatement
228
+ const sentryCall = recast.parse(`if (!request.signal.aborted) {
229
+ Sentry.captureException(error);
230
+ }`).program.body[0];
231
+ // Safe access to existing handle error implementation with proper null checks
232
+ // We know this is ExportNamedDeclaration with VariableDeclaration from the earlier find
233
+ const exportDeclaration = handleErrorFunctionVariableDeclarationExport;
234
+ if (!exportDeclaration.declaration ||
235
+ exportDeclaration.declaration.type !== 'VariableDeclaration' ||
236
+ !exportDeclaration.declaration.declarations ||
237
+ exportDeclaration.declaration.declarations.length === 0) {
238
+ (0, debug_1.debug)('Cannot safely access handleError variable declaration, skipping instrumentation');
239
+ return;
240
+ }
241
+ const firstDeclaration = exportDeclaration.declaration.declarations[0];
242
+ if (!firstDeclaration ||
243
+ firstDeclaration.type !== 'VariableDeclarator' ||
244
+ !firstDeclaration.init) {
245
+ (0, debug_1.debug)('Cannot safely access handleError variable declarator init, skipping instrumentation');
246
+ return;
247
+ }
248
+ const existingHandleErrorImplementation = firstDeclaration.init;
249
+ const existingParams = existingHandleErrorImplementation.params;
250
+ const existingBody = existingHandleErrorImplementation.body;
251
+ const requestParam = {
252
+ ...recast.types.builders.property('init', recast.types.builders.identifier('request'), // key
253
+ recast.types.builders.identifier('request')),
254
+ shorthand: true,
255
+ };
256
+ // Add error and {request} parameters to handleError function if not present
257
+ // When none of the parameters exist
258
+ if (existingParams.length === 0) {
259
+ existingParams.push(recast.types.builders.identifier('error'), recast.types.builders.objectPattern([requestParam]));
260
+ // When only error parameter exists
261
+ }
262
+ else if (existingParams.length === 1) {
263
+ existingParams.push(recast.types.builders.objectPattern([requestParam]));
264
+ // When both parameters exist, but request is not destructured
265
+ }
266
+ else if (existingParams[1].type === 'ObjectPattern' &&
267
+ !existingParams[1].properties.some((prop) => (0, ast_utils_1.safeGetIdentifierName)(prop.key) === 'request')) {
268
+ existingParams[1].properties.push(requestParam);
269
+ }
270
+ // Add the Sentry call to the function body
271
+ existingBody.body.push(sentryCall);
272
+ }
273
+ }
274
+ exports.instrumentHandleError = instrumentHandleError;
275
+ //# sourceMappingURL=server-entry.js.map