main
js 292 lines 8.47 KB
Raw
1 'use strict';
2
3 const {resolve, isAbsolute, relative} = require('path');
4 const Webpack = require('webpack');
5 const TerserPlugin = require('terser-webpack-plugin');
6 const {GITHUB_URL, getVersionString} = require('./utils');
7 const {resolveFeatureFlags} = require('react-devtools-shared/buildUtils');
8 const SourceMapIgnoreListPlugin = require('react-devtools-shared/SourceMapIgnoreListPlugin');
9 const {StatsWriterPlugin} = require('webpack-stats-plugin');
10
11 const NODE_ENV = process.env.NODE_ENV;
12 if (!NODE_ENV) {
13 console.error('NODE_ENV not set');
14 process.exit(1);
15 }
16
17 const builtModulesDir = resolve(
18 __dirname,
19 '..',
20 '..',
21 'build',
22 'oss-experimental',
23 );
24
25 const __DEV__ = NODE_ENV === 'development';
26
27 const DEVTOOLS_VERSION = getVersionString(process.env.DEVTOOLS_VERSION);
28
29 const EDITOR_URL = process.env.EDITOR_URL || null;
30 const LOGGING_URL = process.env.LOGGING_URL || null;
31
32 const IS_CHROME = process.env.IS_CHROME === 'true';
33 const IS_FIREFOX = process.env.IS_FIREFOX === 'true';
34 const IS_EDGE = process.env.IS_EDGE === 'true';
35 const IS_INTERNAL_VERSION = process.env.FEATURE_FLAG_TARGET === 'extension-fb';
36
37 const featureFlagTarget = process.env.FEATURE_FLAG_TARGET || 'extension-oss';
38
39 let statsFileName = `webpack-stats.${featureFlagTarget}.${__DEV__ ? 'development' : 'production'}`;
40 if (IS_CHROME) {
41 statsFileName += `.chrome`;
42 }
43 if (IS_FIREFOX) {
44 statsFileName += `.firefox`;
45 }
46 if (IS_EDGE) {
47 statsFileName += `.edge`;
48 }
49 statsFileName += '.json';
50
51 const babelOptions = {
52 configFile: resolve(
53 __dirname,
54 '..',
55 'react-devtools-shared',
56 'babel.config.js',
57 ),
58 };
59
60 module.exports = {
61 mode: __DEV__ ? 'development' : 'production',
62 devtool: false,
63 entry: {
64 backend: './src/backend.js',
65 background: './src/background/index.js',
66 backendManager: './src/contentScripts/backendManager.js',
67 fallbackEvalContext: './src/contentScripts/fallbackEvalContext.js',
68 fileFetcher: './src/contentScripts/fileFetcher.js',
69 main: './src/main/index.js',
70 panel: './src/panel.js',
71 proxy: './src/contentScripts/proxy.js',
72 prepareInjection: './src/contentScripts/prepareInjection.js',
73 installHook: './src/contentScripts/installHook.js',
74 hookSettingsInjector: './src/contentScripts/hookSettingsInjector.js',
75 },
76 output: {
77 path: __dirname + '/build',
78 publicPath: '/build/',
79 filename: chunkData => {
80 switch (chunkData.chunk.name) {
81 case 'backend':
82 return 'react_devtools_backend_compact.js';
83 default:
84 return '[name].js';
85 }
86 },
87 chunkFilename: '[name].chunk.js',
88 },
89 node: {
90 global: false,
91 },
92 resolve: {
93 alias: {
94 react: resolve(builtModulesDir, 'react'),
95 'react-debug-tools': resolve(builtModulesDir, 'react-debug-tools'),
96 'react-devtools-feature-flags': resolveFeatureFlags(featureFlagTarget),
97 'react-dom/client': resolve(builtModulesDir, 'react-dom/client'),
98 'react-dom': resolve(builtModulesDir, 'react-dom'),
99 'react-is': resolve(builtModulesDir, 'react-is'),
100 scheduler: resolve(builtModulesDir, 'scheduler'),
101 },
102 },
103 optimization: {
104 minimize: !__DEV__,
105 minimizer: [
106 new TerserPlugin({
107 terserOptions: {
108 compress: {
109 unused: true,
110 dead_code: true,
111 },
112 mangle: {
113 keep_fnames: true,
114 },
115 format: {
116 comments: false,
117 },
118 },
119 extractComments: false,
120 }),
121 ],
122 },
123 plugins: [
124 new Webpack.ProvidePlugin({
125 process: 'process/browser',
126 }),
127 new Webpack.DefinePlugin({
128 __DEV__,
129 __EXPERIMENTAL__: true,
130 __EXTENSION__: true,
131 __PROFILE__: false,
132 __TEST__: NODE_ENV === 'test',
133 __IS_CHROME__: IS_CHROME,
134 __IS_FIREFOX__: IS_FIREFOX,
135 __IS_EDGE__: IS_EDGE,
136 __IS_NATIVE__: false,
137 __IS_INTERNAL_VERSION__: IS_INTERNAL_VERSION,
138 'process.env.DEVTOOLS_PACKAGE': `"react-devtools-extensions"`,
139 'process.env.DEVTOOLS_VERSION': `"${DEVTOOLS_VERSION}"`,
140 'process.env.EDITOR_URL': EDITOR_URL != null ? `"${EDITOR_URL}"` : null,
141 'process.env.GITHUB_URL': `"${GITHUB_URL}"`,
142 'process.env.LOGGING_URL': `"${LOGGING_URL}"`,
143 'process.env.NODE_ENV': `"${NODE_ENV}"`,
144 }),
145 new Webpack.SourceMapDevToolPlugin({
146 filename: '[file].map',
147 include: ['installHook.js', 'react_devtools_backend_compact.js'],
148 noSources: !__DEV__,
149 // https://github.com/webpack/webpack/issues/3603#issuecomment-1743147144
150 moduleFilenameTemplate(info) {
151 const {absoluteResourcePath, namespace, resourcePath} = info;
152
153 if (isAbsolute(absoluteResourcePath)) {
154 return relative(__dirname + '/build', absoluteResourcePath);
155 }
156
157 // Mimic Webpack's default behavior:
158 return `webpack://${namespace}/${resourcePath}`;
159 },
160 }),
161 new SourceMapIgnoreListPlugin({
162 shouldIgnoreSource: (assetName, _source) => {
163 if (__DEV__) {
164 // Don't ignore list anything in DEV build for debugging purposes
165 return false;
166 }
167
168 const contentScriptNamesToIgnoreList = [
169 'react_devtools_backend_compact',
170 // This is where we override console
171 'installHook',
172 ];
173
174 return contentScriptNamesToIgnoreList.some(ignoreListName =>
175 assetName.startsWith(ignoreListName),
176 );
177 },
178 }),
179 {
180 apply(compiler) {
181 if (__DEV__) {
182 return;
183 }
184
185 const {RawSource} = compiler.webpack.sources;
186 compiler.hooks.compilation.tap(
187 'CustomContentForHookScriptPlugin',
188 compilation => {
189 compilation.hooks.processAssets.tap(
190 {
191 name: 'CustomContentForHookScriptPlugin',
192 stage: Webpack.Compilation.PROCESS_ASSETS_STAGE_DEV_TOOLING,
193 additionalAssets: true,
194 },
195 assets => {
196 // eslint-disable-next-line no-for-of-loops/no-for-of-loops
197 for (const [name, asset] of Object.entries(assets)) {
198 if (name !== 'installHook.js.map') {
199 continue;
200 }
201
202 const mapContent = asset.source().toString();
203 if (!mapContent) {
204 continue;
205 }
206
207 const map = JSON.parse(mapContent);
208 map.sourcesContent = map.sources.map(sourceName => {
209 if (!sourceName.endsWith('/hook.js')) {
210 return null;
211 }
212
213 return (
214 '/*\n' +
215 ' * This script is from React DevTools.\n' +
216 " * You're likely here because you thought it sent an error or warning to the console.\n" +
217 ' * React DevTools patches the console to support features like appending component stacks, \n' +
218 ' * so this file appears as a source. However, the console call actually came from another script.\n' +
219 " * To remove this script from stack traces, open your browser's DevTools (to enable source mapping) before these console calls happen.\n" +
220 ' */'
221 );
222 });
223
224 compilation.updateAsset(
225 name,
226 new RawSource(JSON.stringify(map)),
227 );
228 }
229 },
230 );
231 },
232 );
233 },
234 },
235 new StatsWriterPlugin({
236 stats: 'verbose',
237 filename: statsFileName,
238 }),
239 ],
240 module: {
241 defaultRules: [
242 {
243 type: 'javascript/auto',
244 resolve: {},
245 },
246 {
247 test: /\.json$/i,
248 type: 'json',
249 },
250 ],
251
252 rules: [
253 {
254 test: /\.worker\.js$/,
255 use: [
256 {
257 loader: 'workerize-loader',
258 options: {
259 inline: false,
260 name: '[name]',
261 },
262 },
263 {
264 loader: 'babel-loader',
265 options: babelOptions,
266 },
267 ],
268 },
269 {
270 test: /\.js$/,
271 loader: 'babel-loader',
272 options: babelOptions,
273 },
274 {
275 test: /\.css$/,
276 use: [
277 {
278 loader: 'style-loader',
279 },
280 {
281 loader: 'css-loader',
282 options: {
283 sourceMap: __DEV__,
284 modules: true,
285 localIdentName: '[local]___[hash:base64:5]',
286 },
287 },
288 ],
289 },
290 ],
291 },
292 };