@sentry/wizard 3.11.0 → 3.12.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.
- package/CHANGELOG.md +7 -0
- package/dist/package.json +1 -1
- package/dist/src/android/android-wizard.js +8 -0
- package/dist/src/android/android-wizard.js.map +1 -1
- package/dist/src/android/code-tools.d.ts +8 -0
- package/dist/src/android/code-tools.js +20 -8
- package/dist/src/android/code-tools.js.map +1 -1
- package/dist/src/android/gradle.js +6 -1
- package/dist/src/android/gradle.js.map +1 -1
- package/dist/src/sourcemaps/tools/vite.js +36 -111
- package/dist/src/sourcemaps/tools/vite.js.map +1 -1
- package/dist/src/sourcemaps/tools/webpack.d.ts +6 -1
- package/dist/src/sourcemaps/tools/webpack.js +290 -25
- package/dist/src/sourcemaps/tools/webpack.js.map +1 -1
- package/dist/src/sveltekit/sdk-setup.js +2 -2
- package/dist/src/sveltekit/sdk-setup.js.map +1 -1
- package/dist/src/utils/ast-utils.d.ts +7 -3
- package/dist/src/utils/ast-utils.js +20 -5
- package/dist/src/utils/ast-utils.js.map +1 -1
- package/dist/src/utils/clack-utils.d.ts +52 -0
- package/dist/src/utils/clack-utils.js +169 -12
- package/dist/src/utils/clack-utils.js.map +1 -1
- package/dist/test/android/code-tools.test.d.ts +1 -0
- package/dist/test/android/code-tools.test.js +34 -0
- package/dist/test/android/code-tools.test.js.map +1 -0
- package/dist/test/sourcemaps/tools/webpack.test.d.ts +1 -0
- package/dist/test/sourcemaps/tools/webpack.test.js +179 -0
- package/dist/test/sourcemaps/tools/webpack.test.js.map +1 -0
- package/dist/test/utils/ast-utils.test.js +42 -7
- package/dist/test/utils/ast-utils.test.js.map +1 -1
- package/dist/test/utils/clack-utils.test.d.ts +1 -0
- package/dist/test/utils/clack-utils.test.js +200 -0
- package/dist/test/utils/clack-utils.test.js.map +1 -0
- package/package.json +1 -1
- package/src/android/android-wizard.ts +8 -0
- package/src/android/code-tools.ts +21 -7
- package/src/android/gradle.ts +6 -1
- package/src/sourcemaps/tools/vite.ts +22 -88
- package/src/sourcemaps/tools/webpack.ts +369 -30
- package/src/sveltekit/sdk-setup.ts +6 -2
- package/src/utils/ast-utils.ts +23 -7
- package/src/utils/clack-utils.ts +150 -2
- package/test/android/code-tools.test.ts +49 -0
- package/test/sourcemaps/tools/webpack.test.ts +303 -0
- package/test/utils/ast-utils.test.ts +28 -9
- package/test/utils/clack-utils.test.ts +142 -0
package/src/utils/clack-utils.ts
CHANGED
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
installPackageWithPackageManager,
|
|
18
18
|
packageManagers,
|
|
19
19
|
} from './package-manager';
|
|
20
|
+
import { debug } from './debug';
|
|
20
21
|
|
|
21
22
|
const opn = require('opn') as (
|
|
22
23
|
url: string,
|
|
@@ -778,14 +779,21 @@ async function askForWizardLogin(options: {
|
|
|
778
779
|
async function askForProjectSelection(
|
|
779
780
|
projects: SentryProjectData[],
|
|
780
781
|
): Promise<SentryProjectData> {
|
|
782
|
+
const label = (project: SentryProjectData): string => {
|
|
783
|
+
return `${project.organization.slug}/${project.slug}`;
|
|
784
|
+
};
|
|
785
|
+
const sortedProjects = [...projects];
|
|
786
|
+
sortedProjects.sort((a: SentryProjectData, b: SentryProjectData) => {
|
|
787
|
+
return label(a).localeCompare(label(b));
|
|
788
|
+
});
|
|
781
789
|
const selection: SentryProjectData | symbol = await abortIfCancelled(
|
|
782
790
|
clack.select({
|
|
783
791
|
maxItems: 12,
|
|
784
792
|
message: 'Select your Sentry project.',
|
|
785
|
-
options:
|
|
793
|
+
options: sortedProjects.map((project) => {
|
|
786
794
|
return {
|
|
787
795
|
value: project,
|
|
788
|
-
label:
|
|
796
|
+
label: label(project),
|
|
789
797
|
};
|
|
790
798
|
}),
|
|
791
799
|
}),
|
|
@@ -797,3 +805,143 @@ async function askForProjectSelection(
|
|
|
797
805
|
|
|
798
806
|
return selection;
|
|
799
807
|
}
|
|
808
|
+
|
|
809
|
+
/**
|
|
810
|
+
* Asks users if they have a config file for @param tool (e.g. Vite).
|
|
811
|
+
* If yes, asks users to specify the path to their config file.
|
|
812
|
+
*
|
|
813
|
+
* Use this helper function as a fallback mechanism if the lookup for
|
|
814
|
+
* a config file with its most usual location/name fails.
|
|
815
|
+
*
|
|
816
|
+
* @param toolName Name of the tool for which we're looking for the config file
|
|
817
|
+
* @param configFileName Name of the most common config file name (e.g. vite.config.js)
|
|
818
|
+
*
|
|
819
|
+
* @returns a user path to the config file or undefined if the user doesn't have a config file
|
|
820
|
+
*/
|
|
821
|
+
export async function askForToolConfigPath(
|
|
822
|
+
toolName: string,
|
|
823
|
+
configFileName: string,
|
|
824
|
+
): Promise<string | undefined> {
|
|
825
|
+
const hasConfig = await abortIfCancelled(
|
|
826
|
+
clack.confirm({
|
|
827
|
+
message: `Do you have a ${toolName} config file (e.g. ${chalk.cyan(
|
|
828
|
+
configFileName,
|
|
829
|
+
)}?`,
|
|
830
|
+
initialValue: true,
|
|
831
|
+
}),
|
|
832
|
+
);
|
|
833
|
+
|
|
834
|
+
if (!hasConfig) {
|
|
835
|
+
return undefined;
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
return await abortIfCancelled(
|
|
839
|
+
clack.text({
|
|
840
|
+
message: `Please enter the path to your ${toolName} config file:`,
|
|
841
|
+
placeholder: path.join('.', configFileName),
|
|
842
|
+
validate: (value) => {
|
|
843
|
+
if (!value) {
|
|
844
|
+
return 'Please enter a path.';
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
try {
|
|
848
|
+
fs.accessSync(value);
|
|
849
|
+
} catch {
|
|
850
|
+
return 'Could not access the file at this path.';
|
|
851
|
+
}
|
|
852
|
+
},
|
|
853
|
+
}),
|
|
854
|
+
);
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
/**
|
|
858
|
+
* Prints copy/paste-able instructions to the console.
|
|
859
|
+
* Afterwards asks the user if they added the code snippet to their file.
|
|
860
|
+
*
|
|
861
|
+
* While there's no point in providing a "no" answer here, it gives users time to fulfill the
|
|
862
|
+
* task before the wizard continues with additional steps.
|
|
863
|
+
*
|
|
864
|
+
* Use this function if you want to show users instructions on how to add/modify
|
|
865
|
+
* code in their file. This is helpful if automatic insertion failed or is not possible/feasible.
|
|
866
|
+
*
|
|
867
|
+
* @param filename the name of the file to which the code snippet should be applied.
|
|
868
|
+
* If a path is provided, only the filename will be used.
|
|
869
|
+
* @param codeSnippet the snippet to be printed.
|
|
870
|
+
* Make sure to follow the diff-like format of highlighting lines that require changes
|
|
871
|
+
* and showing unchanged lines in gray.
|
|
872
|
+
*
|
|
873
|
+
* TODO: Link to wizard spec (develop) once it is live
|
|
874
|
+
* TODO: refactor copy paste instructions across different wizards to use this function.
|
|
875
|
+
* this might require adding a custom message parameter to the function
|
|
876
|
+
*/
|
|
877
|
+
export async function showCopyPasteInstructions(
|
|
878
|
+
filename: string,
|
|
879
|
+
codeSnippet: string,
|
|
880
|
+
): Promise<void> {
|
|
881
|
+
clack.log.step(
|
|
882
|
+
`Add the following code to your ${chalk.cyan(
|
|
883
|
+
path.basename(filename),
|
|
884
|
+
)} file:`,
|
|
885
|
+
);
|
|
886
|
+
|
|
887
|
+
// Intentionally logging directly to console here so that the code can be copied/pasted directly
|
|
888
|
+
// eslint-disable-next-line no-console
|
|
889
|
+
console.log(`\n${codeSnippet}`);
|
|
890
|
+
|
|
891
|
+
await abortIfCancelled(
|
|
892
|
+
clack.select({
|
|
893
|
+
message: 'Did you apply the snippet above?',
|
|
894
|
+
options: [{ label: 'Yes, continue!', value: true }],
|
|
895
|
+
initialValue: true,
|
|
896
|
+
}),
|
|
897
|
+
);
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
/**
|
|
901
|
+
* Creates a new config file with the given @param filepath and @param codeSnippet.
|
|
902
|
+
*
|
|
903
|
+
* Use this function to create a new config file for users. This is useful
|
|
904
|
+
* when users answered that they don't yet have a config file for a tool.
|
|
905
|
+
*
|
|
906
|
+
* (This doesn't mean that they don't yet have some other way of configuring
|
|
907
|
+
* their tool but we can leave it up to them to figure out how to merge configs
|
|
908
|
+
* here.)
|
|
909
|
+
*
|
|
910
|
+
* @param filepath absolute path to the new config file
|
|
911
|
+
* @param codeSnippet the snippet to be inserted into the file
|
|
912
|
+
* @param moreInformation (optional) the message to be printed after the file was created
|
|
913
|
+
* For example, this can be a link to more information about configuring the tool.
|
|
914
|
+
*
|
|
915
|
+
* @returns true on sucess, false otherwise
|
|
916
|
+
*/
|
|
917
|
+
export async function createNewConfigFile(
|
|
918
|
+
filepath: string,
|
|
919
|
+
codeSnippet: string,
|
|
920
|
+
moreInformation?: string,
|
|
921
|
+
): Promise<boolean> {
|
|
922
|
+
if (!path.isAbsolute(filepath)) {
|
|
923
|
+
debug(`createNewConfigFile: filepath is not absolute: ${filepath}`);
|
|
924
|
+
return false;
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
const prettyFilename = chalk.cyan(path.relative(process.cwd(), filepath));
|
|
928
|
+
|
|
929
|
+
try {
|
|
930
|
+
await fs.promises.writeFile(filepath, codeSnippet);
|
|
931
|
+
|
|
932
|
+
clack.log.success(`Added new ${prettyFilename} file.`);
|
|
933
|
+
|
|
934
|
+
if (moreInformation) {
|
|
935
|
+
clack.log.info(chalk.gray(moreInformation));
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
return true;
|
|
939
|
+
} catch (e) {
|
|
940
|
+
debug(e);
|
|
941
|
+
clack.log.warn(
|
|
942
|
+
`Could not create a new ${prettyFilename} file. Please create one manually and follow the instructions below.`,
|
|
943
|
+
);
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
return false;
|
|
947
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
//@ts-ignore
|
|
2
|
+
import { getLastImportLineLocation } from '../../src/android/code-tools';
|
|
3
|
+
|
|
4
|
+
describe('code-tools', () => {
|
|
5
|
+
describe('getLastImportLineLocation', () => {
|
|
6
|
+
it('returns proper line index', () => {
|
|
7
|
+
const code = `import a.b.c;\n` + `//<insert-location>\n` + `class X {}`;
|
|
8
|
+
expect(getLastImportLineLocation(code)).toBe(
|
|
9
|
+
code.indexOf('//<insert-location>'),
|
|
10
|
+
);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('returns proper line index when static import is used', () => {
|
|
14
|
+
const code =
|
|
15
|
+
`import static a.b.c;\n` + `//<insert-location>\n` + `class X {}`;
|
|
16
|
+
expect(getLastImportLineLocation(code)).toBe(
|
|
17
|
+
code.indexOf('//<insert-location>'),
|
|
18
|
+
);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
it('returns proper line index when wildcard import is used', () => {
|
|
22
|
+
const code = `import a.b.*\n` + `//<insert-location>\n` + `class X {}`;
|
|
23
|
+
expect(getLastImportLineLocation(code)).toBe(
|
|
24
|
+
code.indexOf('//<insert-location>'),
|
|
25
|
+
);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('returns proper line index when alias import is used', () => {
|
|
29
|
+
const code =
|
|
30
|
+
`import static a.b.c as d\n` + `//<insert-location>\n` + `class X {}`;
|
|
31
|
+
expect(getLastImportLineLocation(code)).toBe(
|
|
32
|
+
code.indexOf('//<insert-location>'),
|
|
33
|
+
);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('returns proper line index when multiple imports are present', () => {
|
|
37
|
+
const code =
|
|
38
|
+
`import static a.b.c as d\n` +
|
|
39
|
+
`import a.b.*\n` +
|
|
40
|
+
`import static a.b.c;\n` +
|
|
41
|
+
`import a.b.c;\n` +
|
|
42
|
+
`//<insert-location>\n` +
|
|
43
|
+
`class X {}`;
|
|
44
|
+
expect(getLastImportLineLocation(code)).toBe(
|
|
45
|
+
code.indexOf('//<insert-location>'),
|
|
46
|
+
);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
});
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
|
|
3
|
+
import { modifyWebpackConfig } from '../../../src/sourcemaps/tools/webpack';
|
|
4
|
+
|
|
5
|
+
function updateFileContent(content: string): void {
|
|
6
|
+
fileContent = content;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
let fileContent = '';
|
|
10
|
+
|
|
11
|
+
jest.mock('@clack/prompts', () => {
|
|
12
|
+
return {
|
|
13
|
+
log: {
|
|
14
|
+
info: jest.fn(),
|
|
15
|
+
success: jest.fn(),
|
|
16
|
+
},
|
|
17
|
+
select: jest.fn().mockImplementation(() => Promise.resolve(true)),
|
|
18
|
+
isCancel: jest.fn().mockReturnValue(false),
|
|
19
|
+
};
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
jest
|
|
23
|
+
.spyOn(fs.promises, 'readFile')
|
|
24
|
+
.mockImplementation(() => Promise.resolve(fileContent));
|
|
25
|
+
|
|
26
|
+
const writeFileSpy = jest
|
|
27
|
+
.spyOn(fs.promises, 'writeFile')
|
|
28
|
+
.mockImplementation(() => Promise.resolve(void 0));
|
|
29
|
+
|
|
30
|
+
const noSourcemapNoPluginsPojo = `module.exports = {
|
|
31
|
+
entry: "./src/index.js",
|
|
32
|
+
output: {
|
|
33
|
+
filename: "main.js",
|
|
34
|
+
path: path.resolve(__dirname, "build"),
|
|
35
|
+
},
|
|
36
|
+
};`;
|
|
37
|
+
|
|
38
|
+
const noSourcemapNoPluginsPojoResult = `const {
|
|
39
|
+
sentryWebpackPlugin
|
|
40
|
+
} = require("@sentry/webpack-plugin");
|
|
41
|
+
|
|
42
|
+
module.exports = {
|
|
43
|
+
entry: "./src/index.js",
|
|
44
|
+
|
|
45
|
+
output: {
|
|
46
|
+
filename: "main.js",
|
|
47
|
+
path: path.resolve(__dirname, "build"),
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
devtool: "source-map",
|
|
51
|
+
|
|
52
|
+
plugins: [sentryWebpackPlugin({
|
|
53
|
+
authToken: process.env.SENTRY_AUTH_TOKEN,
|
|
54
|
+
org: "my-org",
|
|
55
|
+
project: "my-project"
|
|
56
|
+
})]
|
|
57
|
+
};`;
|
|
58
|
+
|
|
59
|
+
const noSourcemapsNoPluginsId = `const config = {
|
|
60
|
+
entry: "./src/index.js",
|
|
61
|
+
|
|
62
|
+
output: {
|
|
63
|
+
filename: "main.js",
|
|
64
|
+
path: path.resolve(__dirname, "build"),
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
module.exports = config;`;
|
|
69
|
+
|
|
70
|
+
const noSourcemapsNoPluginsIdResult = `const {
|
|
71
|
+
sentryWebpackPlugin
|
|
72
|
+
} = require("@sentry/webpack-plugin");
|
|
73
|
+
|
|
74
|
+
const config = {
|
|
75
|
+
entry: "./src/index.js",
|
|
76
|
+
|
|
77
|
+
output: {
|
|
78
|
+
filename: "main.js",
|
|
79
|
+
path: path.resolve(__dirname, "build"),
|
|
80
|
+
},
|
|
81
|
+
|
|
82
|
+
devtool: "source-map",
|
|
83
|
+
|
|
84
|
+
plugins: [sentryWebpackPlugin({
|
|
85
|
+
authToken: process.env.SENTRY_AUTH_TOKEN,
|
|
86
|
+
org: "my-org",
|
|
87
|
+
project: "my-project"
|
|
88
|
+
})]
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
module.exports = config;`;
|
|
92
|
+
|
|
93
|
+
const hiddenSourcemapNoPluginsId = `const config = {
|
|
94
|
+
entry: "./src/index.js",
|
|
95
|
+
|
|
96
|
+
output: {
|
|
97
|
+
filename: "main.js",
|
|
98
|
+
path: path.resolve(__dirname, "build"),
|
|
99
|
+
},
|
|
100
|
+
|
|
101
|
+
devtool: "hidden-cheap-source-map",
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
module.exports = config;
|
|
105
|
+
`;
|
|
106
|
+
const hiddenSourcemapNoPluginsIdResult = `const {
|
|
107
|
+
sentryWebpackPlugin
|
|
108
|
+
} = require("@sentry/webpack-plugin");
|
|
109
|
+
|
|
110
|
+
const config = {
|
|
111
|
+
entry: "./src/index.js",
|
|
112
|
+
|
|
113
|
+
output: {
|
|
114
|
+
filename: "main.js",
|
|
115
|
+
path: path.resolve(__dirname, "build"),
|
|
116
|
+
},
|
|
117
|
+
|
|
118
|
+
devtool: "hidden-source-map",
|
|
119
|
+
|
|
120
|
+
plugins: [sentryWebpackPlugin({
|
|
121
|
+
authToken: process.env.SENTRY_AUTH_TOKEN,
|
|
122
|
+
org: "my-org",
|
|
123
|
+
project: "my-project"
|
|
124
|
+
})]
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
module.exports = config;`;
|
|
128
|
+
|
|
129
|
+
const arbitrarySourcemapNoPluginsId = `
|
|
130
|
+
const config = {
|
|
131
|
+
entry: "./src/index.js",
|
|
132
|
+
|
|
133
|
+
output: {
|
|
134
|
+
filename: "main.js",
|
|
135
|
+
path: path.resolve(__dirname, "build"),
|
|
136
|
+
},
|
|
137
|
+
|
|
138
|
+
devtool: getSourcemapSetting(),
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
module.exports = config;
|
|
142
|
+
`;
|
|
143
|
+
const arbitrarySourcemapNoPluginsIdResult = `const {
|
|
144
|
+
sentryWebpackPlugin
|
|
145
|
+
} = require("@sentry/webpack-plugin");
|
|
146
|
+
|
|
147
|
+
const config = {
|
|
148
|
+
entry: "./src/index.js",
|
|
149
|
+
|
|
150
|
+
output: {
|
|
151
|
+
filename: "main.js",
|
|
152
|
+
path: path.resolve(__dirname, "build"),
|
|
153
|
+
},
|
|
154
|
+
|
|
155
|
+
devtool: "source-map",
|
|
156
|
+
|
|
157
|
+
plugins: [sentryWebpackPlugin({
|
|
158
|
+
authToken: process.env.SENTRY_AUTH_TOKEN,
|
|
159
|
+
org: "my-org",
|
|
160
|
+
project: "my-project"
|
|
161
|
+
})]
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
module.exports = config;`;
|
|
165
|
+
|
|
166
|
+
const noSourcemapUndefinedPluginsPojo = `module.exports = {
|
|
167
|
+
entry: "./src/index.js",
|
|
168
|
+
plugins: undefined,
|
|
169
|
+
output: {
|
|
170
|
+
filename: "main.js",
|
|
171
|
+
path: path.resolve(__dirname, "build"),
|
|
172
|
+
},
|
|
173
|
+
};`;
|
|
174
|
+
|
|
175
|
+
const noSourcemapUndefinedPluginsPojoResult = `const {
|
|
176
|
+
sentryWebpackPlugin
|
|
177
|
+
} = require("@sentry/webpack-plugin");
|
|
178
|
+
|
|
179
|
+
module.exports = {
|
|
180
|
+
entry: "./src/index.js",
|
|
181
|
+
|
|
182
|
+
plugins: [sentryWebpackPlugin({
|
|
183
|
+
authToken: process.env.SENTRY_AUTH_TOKEN,
|
|
184
|
+
org: "my-org",
|
|
185
|
+
project: "my-project"
|
|
186
|
+
})],
|
|
187
|
+
|
|
188
|
+
output: {
|
|
189
|
+
filename: "main.js",
|
|
190
|
+
path: path.resolve(__dirname, "build"),
|
|
191
|
+
},
|
|
192
|
+
|
|
193
|
+
devtool: "source-map"
|
|
194
|
+
};`;
|
|
195
|
+
|
|
196
|
+
const noSourcemapPluginsPojo = `module.exports = {
|
|
197
|
+
entry: "./src/index.js",
|
|
198
|
+
plugins: [
|
|
199
|
+
new HtmlWebpackPlugin(),
|
|
200
|
+
new MiniCssExtractPlugin(),
|
|
201
|
+
],
|
|
202
|
+
output: {
|
|
203
|
+
filename: "main.js",
|
|
204
|
+
path: path.resolve(__dirname, "build"),
|
|
205
|
+
},
|
|
206
|
+
};`;
|
|
207
|
+
|
|
208
|
+
const noSourcemapPluginsPojoResult = `const {
|
|
209
|
+
sentryWebpackPlugin
|
|
210
|
+
} = require("@sentry/webpack-plugin");
|
|
211
|
+
|
|
212
|
+
module.exports = {
|
|
213
|
+
entry: "./src/index.js",
|
|
214
|
+
|
|
215
|
+
plugins: [new HtmlWebpackPlugin(), new MiniCssExtractPlugin(), sentryWebpackPlugin({
|
|
216
|
+
authToken: process.env.SENTRY_AUTH_TOKEN,
|
|
217
|
+
org: "my-org",
|
|
218
|
+
project: "my-project"
|
|
219
|
+
})],
|
|
220
|
+
|
|
221
|
+
output: {
|
|
222
|
+
filename: "main.js",
|
|
223
|
+
path: path.resolve(__dirname, "build"),
|
|
224
|
+
},
|
|
225
|
+
|
|
226
|
+
devtool: "source-map"
|
|
227
|
+
};`;
|
|
228
|
+
|
|
229
|
+
describe('modifyWebpackConfig', () => {
|
|
230
|
+
afterEach(() => {
|
|
231
|
+
fileContent = '';
|
|
232
|
+
jest.clearAllMocks();
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
it.each([
|
|
236
|
+
[
|
|
237
|
+
'no sourcemap option, no plugins, object',
|
|
238
|
+
noSourcemapNoPluginsPojo,
|
|
239
|
+
noSourcemapNoPluginsPojoResult,
|
|
240
|
+
],
|
|
241
|
+
[
|
|
242
|
+
'no sourcemap option, no plugins, identifier',
|
|
243
|
+
noSourcemapsNoPluginsId,
|
|
244
|
+
noSourcemapsNoPluginsIdResult,
|
|
245
|
+
],
|
|
246
|
+
[
|
|
247
|
+
'hidden sourcemap option, no plugins, identifier',
|
|
248
|
+
hiddenSourcemapNoPluginsId,
|
|
249
|
+
hiddenSourcemapNoPluginsIdResult,
|
|
250
|
+
],
|
|
251
|
+
[
|
|
252
|
+
'arbitrary sourcemap option, no plugins, identifier',
|
|
253
|
+
arbitrarySourcemapNoPluginsId,
|
|
254
|
+
arbitrarySourcemapNoPluginsIdResult,
|
|
255
|
+
],
|
|
256
|
+
[
|
|
257
|
+
'no sourcemap option, plugins, object',
|
|
258
|
+
noSourcemapUndefinedPluginsPojo,
|
|
259
|
+
noSourcemapUndefinedPluginsPojoResult,
|
|
260
|
+
],
|
|
261
|
+
[
|
|
262
|
+
'no sourcemap option, plugins, object',
|
|
263
|
+
noSourcemapPluginsPojo,
|
|
264
|
+
noSourcemapPluginsPojoResult,
|
|
265
|
+
],
|
|
266
|
+
])(
|
|
267
|
+
'adds plugin and source maps emission to the webpack config (%s)',
|
|
268
|
+
async (_, originalCode, expectedCode) => {
|
|
269
|
+
updateFileContent(originalCode);
|
|
270
|
+
|
|
271
|
+
// updateFileContent(originalCode);
|
|
272
|
+
const addedCode = await modifyWebpackConfig('', {
|
|
273
|
+
authToken: '',
|
|
274
|
+
orgSlug: 'my-org',
|
|
275
|
+
projectSlug: 'my-project',
|
|
276
|
+
selfHosted: false,
|
|
277
|
+
url: 'https://sentry.io/',
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
expect(writeFileSpy).toHaveBeenCalledTimes(1);
|
|
281
|
+
const [[, fileContent]] = writeFileSpy.mock.calls;
|
|
282
|
+
expect(fileContent).toBe(expectedCode);
|
|
283
|
+
expect(addedCode).toBe(true);
|
|
284
|
+
},
|
|
285
|
+
);
|
|
286
|
+
|
|
287
|
+
it('adds the url parameter to the webpack plugin options if self-hosted', async () => {
|
|
288
|
+
updateFileContent(noSourcemapNoPluginsPojo);
|
|
289
|
+
|
|
290
|
+
const addedCode = await modifyWebpackConfig('', {
|
|
291
|
+
authToken: '',
|
|
292
|
+
orgSlug: 'my-org',
|
|
293
|
+
projectSlug: 'my-project',
|
|
294
|
+
selfHosted: true,
|
|
295
|
+
url: 'https://santry.io/',
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
expect(writeFileSpy).toHaveBeenCalledTimes(1);
|
|
299
|
+
const [[, fileContent]] = writeFileSpy.mock.calls;
|
|
300
|
+
expect(fileContent).toContain('url: "https://santry.io/"');
|
|
301
|
+
expect(addedCode).toBe(true);
|
|
302
|
+
});
|
|
303
|
+
});
|
|
@@ -1,22 +1,37 @@
|
|
|
1
|
-
//@ts-ignore
|
|
2
|
-
import { parseModule } from 'magicast';
|
|
3
1
|
import { hasSentryContent } from '../../src/utils/ast-utils';
|
|
4
2
|
|
|
3
|
+
import * as recast from 'recast';
|
|
4
|
+
|
|
5
5
|
describe('AST utils', () => {
|
|
6
6
|
describe('hasSentryContent', () => {
|
|
7
|
-
it(
|
|
8
|
-
|
|
7
|
+
it.each([
|
|
8
|
+
`
|
|
9
|
+
const { sentryVitePlugin } = require("@sentry/vite-plugin");
|
|
10
|
+
const somethingelse = require('gs');
|
|
11
|
+
`,
|
|
12
|
+
`
|
|
9
13
|
import { sentryVitePlugin } from "@sentry/vite-plugin";
|
|
10
14
|
import * as somethingelse from 'gs';
|
|
11
15
|
|
|
12
16
|
export default {
|
|
13
17
|
plugins: [sentryVitePlugin()]
|
|
14
18
|
}
|
|
15
|
-
|
|
19
|
+
`,
|
|
20
|
+
])(
|
|
21
|
+
"returns true if a require('@sentry/') call was found in the parsed module",
|
|
22
|
+
(code) => {
|
|
23
|
+
// recast.parse returns a Program node (or fails) but it's badly typed as any
|
|
24
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
25
|
+
const program = recast.parse(code)
|
|
26
|
+
.program as recast.types.namedTypes.Program;
|
|
27
|
+
expect(hasSentryContent(program)).toBe(true);
|
|
28
|
+
},
|
|
29
|
+
);
|
|
16
30
|
|
|
17
|
-
expect(hasSentryContent(parseModule(code))).toBe(true);
|
|
18
|
-
});
|
|
19
31
|
it.each([
|
|
32
|
+
`const whatever = require('something')`,
|
|
33
|
+
`// const {sentryWebpackPlugin} = require('@sentry/webpack-plugin')`,
|
|
34
|
+
`const {sAntryWebpackPlugin} = require('webpack-plugin-@sentry')`,
|
|
20
35
|
`
|
|
21
36
|
import * as somethingelse from 'gs';
|
|
22
37
|
export default {
|
|
@@ -35,9 +50,13 @@ describe('AST utils', () => {
|
|
|
35
50
|
}
|
|
36
51
|
`,
|
|
37
52
|
])(
|
|
38
|
-
"
|
|
53
|
+
"returns false if the file doesn't contain any require('@sentry/') calls",
|
|
39
54
|
(code) => {
|
|
40
|
-
|
|
55
|
+
// recast.parse returns a Program node (or fails) but it's badly typed as any
|
|
56
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
57
|
+
const program = recast.parse(code)
|
|
58
|
+
.program as recast.types.namedTypes.Program;
|
|
59
|
+
expect(hasSentryContent(program)).toBe(false);
|
|
41
60
|
},
|
|
42
61
|
);
|
|
43
62
|
});
|