Browse Source

build: Convert webpack config to typescript (#27369)

Evan Purkhiser 3 years ago
parent
commit
7d9927f17a

+ 1 - 0
.storybook/main.ts

@@ -1,5 +1,6 @@
 /* eslint-env node */
 /* eslint-env node */
 /* eslint import/no-nodejs-modules:0 */
 /* eslint import/no-nodejs-modules:0 */
+
 import path from 'path';
 import path from 'path';
 
 
 import {StorybookConfig} from '@storybook/core-common';
 import {StorybookConfig} from '@storybook/core-common';

+ 1 - 5
.storybook/webpack.config.ts

@@ -5,11 +5,7 @@ import path from 'path';
 
 
 import webpack from 'webpack';
 import webpack from 'webpack';
 
 
-import appConfigAny from '../webpack.config';
-
-// TODO(epurkhiser): Once we convert our webpack.config over to typescript we
-// can remove this
-const appConfig = appConfigAny as any;
+import appConfig from '../webpack.config';
 
 
 const staticPath = path.resolve(__dirname, '..', 'static', 'app');
 const staticPath = path.resolve(__dirname, '..', 'static', 'app');
 
 

+ 8 - 3
babel.config.js → babel.config.ts

@@ -1,5 +1,8 @@
 /* eslint-env node */
 /* eslint-env node */
-module.exports = {
+
+import {TransformOptions} from '@babel/core';
+
+const config: TransformOptions = {
   presets: [
   presets: [
     [
     [
       '@babel/preset-react',
       '@babel/preset-react',
@@ -50,8 +53,8 @@ module.exports = {
       plugins: [
       plugins: [
         '@emotion/babel-plugin',
         '@emotion/babel-plugin',
         '@babel/plugin-transform-react-jsx-source',
         '@babel/plugin-transform-react-jsx-source',
-        !!process.env.SENTRY_UI_HOT_RELOAD ? 'react-refresh/babel' : null,
-      ].filter(Boolean),
+        ...(process.env.SENTRY_UI_HOT_RELOAD ? ['react-refresh/babel'] : []),
+      ],
     },
     },
     test: {
     test: {
       // Required, see https://github.com/facebook/jest/issues/9430
       // Required, see https://github.com/facebook/jest/issues/9430
@@ -59,3 +62,5 @@ module.exports = {
     },
     },
   },
   },
 };
 };
+
+export default config;

+ 58 - 32
build-utils/integration-docs-fetch-plugin.js → build-utils/integration-docs-fetch-plugin.ts

@@ -1,16 +1,37 @@
 /* eslint-env node */
 /* eslint-env node */
 /* eslint import/no-nodejs-modules:0 */
 /* eslint import/no-nodejs-modules:0 */
-const fs = require('fs');
-const https = require('https');
-const path = require('path');
+
+import fs from 'fs';
+import https from 'https';
+import path from 'path';
+
+import webpack from 'webpack';
 
 
 const PLATFORMS_URL = 'https://docs.sentry.io/_platforms/_index.json';
 const PLATFORMS_URL = 'https://docs.sentry.io/_platforms/_index.json';
 const DOCS_INDEX_PATH = 'src/sentry/integration-docs/_platforms.json';
 const DOCS_INDEX_PATH = 'src/sentry/integration-docs/_platforms.json';
 
 
-const alphaSortFromKey = keyExtractor => (a, b) =>
-  keyExtractor(a).localeCompare(keyExtractor(b));
+const alphaSortFromKey =
+  <T>(keyExtractor: (key: T) => string) =>
+  (a: T, b: T) =>
+    keyExtractor(a).localeCompare(keyExtractor(b));
+
+type Platform = {
+  key: string;
+  type: 'language' | 'framework';
+  details: string;
+  doc_link: string;
+  name: string;
+  aliases: string[];
+  categories: string[];
+};
+
+type PlatformItem = {_self: Platform} & Record<string, Platform>;
+
+type PlatformsData = {
+  platforms: Record<string, PlatformItem>;
+};
 
 
-const transformPlatformsToList = ({platforms}) =>
+const transformPlatformsToList = ({platforms}: PlatformsData) =>
   Object.keys(platforms)
   Object.keys(platforms)
     .map(platformId => {
     .map(platformId => {
       const integrationMap = platforms[platformId];
       const integrationMap = platforms[platformId];
@@ -31,29 +52,10 @@ const transformPlatformsToList = ({platforms}) =>
     })
     })
     .sort(alphaSortFromKey(item => item.name));
     .sort(alphaSortFromKey(item => item.name));
 
 
-function fetch(_compilation, callback) {
-  https
-    .get(PLATFORMS_URL, res => {
-      res.setEncoding('utf8');
-      let buffer = '';
-      res
-        .on('data', data => {
-          buffer += data;
-        })
-        .on('end', () =>
-          fs.writeFile(
-            this.modulePath,
-            JSON.stringify({
-              platforms: transformPlatformsToList(JSON.parse(buffer)),
-            }),
-            callback
-          )
-        );
-    })
-    .on('error', callback);
-}
-
 class IntegrationDocsFetchPlugin {
 class IntegrationDocsFetchPlugin {
+  modulePath: string;
+  hasRun: boolean;
+
   constructor({basePath}) {
   constructor({basePath}) {
     this.modulePath = path.join(basePath, DOCS_INDEX_PATH);
     this.modulePath = path.join(basePath, DOCS_INDEX_PATH);
     this.hasRun = false;
     this.hasRun = false;
@@ -63,8 +65,32 @@ class IntegrationDocsFetchPlugin {
     }
     }
   }
   }
 
 
-  apply(compiler) {
-    compiler.hooks.beforeRun.tapAsync('IntegrationDocsFetchPlugin', fetch.bind(this));
+  fetch: Parameters<webpack.Compiler['hooks']['beforeRun']['tapAsync']>[1] = (
+    _compilation,
+    callback
+  ) =>
+    https
+      .get(PLATFORMS_URL, res => {
+        res.setEncoding('utf8');
+        let buffer = '';
+        res
+          .on('data', data => {
+            buffer += data;
+          })
+          .on('end', () =>
+            fs.writeFile(
+              this.modulePath,
+              JSON.stringify({
+                platforms: transformPlatformsToList(JSON.parse(buffer)),
+              }),
+              callback
+            )
+          );
+      })
+      .on('error', callback);
+
+  apply(compiler: webpack.Compiler) {
+    compiler.hooks.beforeRun.tapAsync('IntegrationDocsFetchPlugin', this.fetch);
 
 
     compiler.hooks.watchRun.tapAsync(
     compiler.hooks.watchRun.tapAsync(
       'IntegrationDocsFetchPlugin',
       'IntegrationDocsFetchPlugin',
@@ -75,11 +101,11 @@ class IntegrationDocsFetchPlugin {
           return;
           return;
         }
         }
 
 
-        fetch.call(this, compilation, callback);
+        this.fetch(compilation, callback);
         this.hasRun = true;
         this.hasRun = true;
       }
       }
     );
     );
   }
   }
 }
 }
 
 
-module.exports = IntegrationDocsFetchPlugin;
+export default IntegrationDocsFetchPlugin;

+ 16 - 5
build-utils/last-built-plugin.js → build-utils/last-built-plugin.ts

@@ -1,15 +1,25 @@
 /* eslint-env node */
 /* eslint-env node */
 /* eslint import/no-nodejs-modules:0 */
 /* eslint import/no-nodejs-modules:0 */
-const path = require('path');
-const fs = require('fs');
+
+import fs from 'fs';
+import path from 'path';
+
+import webpack from 'webpack';
+
+type Options = {
+  basePath: string;
+};
 
 
 class LastBuiltPlugin {
 class LastBuiltPlugin {
-  constructor({basePath}) {
+  basePath: string;
+  isWatchMode: boolean;
+
+  constructor({basePath}: Options) {
     this.basePath = basePath;
     this.basePath = basePath;
     this.isWatchMode = false;
     this.isWatchMode = false;
   }
   }
 
 
-  apply(compiler) {
+  apply(compiler: webpack.Compiler) {
     compiler.hooks.watchRun.tapAsync('LastBuiltPlugin', (_, callback) => {
     compiler.hooks.watchRun.tapAsync('LastBuiltPlugin', (_, callback) => {
       this.isWatchMode = true;
       this.isWatchMode = true;
       callback();
       callback();
@@ -35,4 +45,5 @@ class LastBuiltPlugin {
     });
     });
   }
   }
 }
 }
-module.exports = LastBuiltPlugin;
+
+export default LastBuiltPlugin;

+ 27 - 16
build-utils/sentry-instrumentation.js → build-utils/sentry-instrumentation.ts

@@ -1,8 +1,12 @@
 /* eslint-env node */
 /* eslint-env node */
 /* eslint import/no-nodejs-modules:0 */
 /* eslint import/no-nodejs-modules:0 */
-const crypto = require('crypto');
-const https = require('https');
-const os = require('os');
+
+import crypto from 'crypto';
+import https from 'https';
+import os from 'os';
+
+import type Sentry from '@sentry/node';
+import webpack from 'webpack';
 
 
 const {
 const {
   NODE_ENV,
   NODE_ENV,
@@ -11,17 +15,22 @@ const {
   GITHUB_SHA,
   GITHUB_SHA,
   GITHUB_REF,
   GITHUB_REF,
 } = process.env;
 } = process.env;
+
 const IS_CI = !!GITHUB_SHA;
 const IS_CI = !!GITHUB_SHA;
 
 
 const PLUGIN_NAME = 'SentryInstrumentation';
 const PLUGIN_NAME = 'SentryInstrumentation';
 const GB_BYTE = 1073741824;
 const GB_BYTE = 1073741824;
 
 
-const createSignature = function (secret, payload) {
+const createSignature = function (secret: string, payload: string) {
   const hmac = crypto.createHmac('sha1', secret);
   const hmac = crypto.createHmac('sha1', secret);
   return `sha1=${hmac.update(payload).digest('hex')}`;
   return `sha1=${hmac.update(payload).digest('hex')}`;
 };
 };
 
 
 class SentryInstrumentation {
 class SentryInstrumentation {
+  initialBuild: boolean = false;
+
+  Sentry?: typeof Sentry;
+
   constructor() {
   constructor() {
     // Only run if SENTRY_INSTRUMENTATION` is set or when in ci,
     // Only run if SENTRY_INSTRUMENTATION` is set or when in ci,
     // only in the javascript suite that runs webpack
     // only in the javascript suite that runs webpack
@@ -29,40 +38,41 @@ class SentryInstrumentation {
       return;
       return;
     }
     }
 
 
-    this.initialBuild = false;
-    this.Sentry = require('@sentry/node');
+    const sentry = require('@sentry/node');
     require('@sentry/tracing'); // This is required to patch Sentry
     require('@sentry/tracing'); // This is required to patch Sentry
 
 
-    this.Sentry.init({
+    sentry.init({
       dsn: 'https://3d282d186d924374800aa47006227ce9@sentry.io/2053674',
       dsn: 'https://3d282d186d924374800aa47006227ce9@sentry.io/2053674',
       environment: IS_CI ? 'ci' : 'local',
       environment: IS_CI ? 'ci' : 'local',
       tracesSampleRate: 1.0,
       tracesSampleRate: 1.0,
     });
     });
 
 
     if (IS_CI) {
     if (IS_CI) {
-      this.Sentry.setTag('branch', GITHUB_REF);
+      sentry.setTag('branch', GITHUB_REF);
     }
     }
 
 
     const cpus = os.cpus();
     const cpus = os.cpus();
-    this.Sentry.setTag('platform', os.platform());
-    this.Sentry.setTag('arch', os.arch());
-    this.Sentry.setTag(
+    sentry.setTag('platform', os.platform());
+    sentry.setTag('arch', os.arch());
+    sentry.setTag(
       'cpu',
       'cpu',
       cpus && cpus.length ? `${cpus[0].model} (cores: ${cpus.length})}` : 'N/A'
       cpus && cpus.length ? `${cpus[0].model} (cores: ${cpus.length})}` : 'N/A'
     );
     );
+
+    this.Sentry = sentry;
   }
   }
 
 
   /**
   /**
    * Measures the file sizes of assets emitted from the entrypoints
    * Measures the file sizes of assets emitted from the entrypoints
    */
    */
-  measureAssetSizes(compilation) {
+  measureAssetSizes(compilation: webpack.Compilation) {
     if (!SENTRY_WEBPACK_WEBHOOK_SECRET) {
     if (!SENTRY_WEBPACK_WEBHOOK_SECRET) {
       return;
       return;
     }
     }
 
 
     [...compilation.entrypoints].forEach(([entrypointName, entry]) =>
     [...compilation.entrypoints].forEach(([entrypointName, entry]) =>
       entry.chunks.forEach(chunk =>
       entry.chunks.forEach(chunk =>
-        chunk.files
+        Array.from(chunk.files)
           .filter(assetName => !assetName.endsWith('.map'))
           .filter(assetName => !assetName.endsWith('.map'))
           .forEach(assetName => {
           .forEach(assetName => {
             const asset = compilation.assets[assetName];
             const asset = compilation.assets[assetName];
@@ -96,7 +106,7 @@ class SentryInstrumentation {
     );
     );
   }
   }
 
 
-  measureBuildTime(startTime, endTime) {
+  measureBuildTime(startTime: number, endTime: number) {
     if (!this.Sentry) {
     if (!this.Sentry) {
       return;
       return;
     }
     }
@@ -127,7 +137,7 @@ class SentryInstrumentation {
     transaction.finish();
     transaction.finish();
   }
   }
 
 
-  apply(compiler) {
+  apply(compiler: webpack.Compiler) {
     compiler.hooks.done.tapAsync(
     compiler.hooks.done.tapAsync(
       PLUGIN_NAME,
       PLUGIN_NAME,
       async ({compilation, startTime, endTime}, done) => {
       async ({compilation, startTime, endTime}, done) => {
@@ -149,4 +159,5 @@ class SentryInstrumentation {
     );
     );
   }
   }
 }
 }
-module.exports = SentryInstrumentation;
+
+export default SentryInstrumentation;

+ 17 - 10
config/webpack.chartcuterie.config.js → config/webpack.chartcuterie.config.ts

@@ -1,21 +1,28 @@
 /* eslint-env node */
 /* eslint-env node */
 /* eslint import/no-nodejs-modules:0 */
 /* eslint import/no-nodejs-modules:0 */
 
 
-const path = require('path');
-const childProcess = require('child_process');
-const webpack = require('webpack');
+import childProcess from 'child_process';
+import path from 'path';
 
 
-const baseConfig = require('../webpack.config');
+import webpack from 'webpack';
+
+import baseConfig from '../webpack.config';
 
 
 const commitHash =
 const commitHash =
   process.env.SENTRY_BUILD ||
   process.env.SENTRY_BUILD ||
   childProcess.execSync('git rev-parse HEAD').toString().trim();
   childProcess.execSync('git rev-parse HEAD').toString().trim();
 
 
-const findLoader = loaderName =>
-  baseConfig.module.rules.find(rule => rule.use.loader === loaderName);
-
-const config = {
-  mode: process.env.NODE_ENV || 'development',
+const findLoader = (loaderName: string) =>
+  baseConfig.module?.rules?.find(
+    rule =>
+      typeof rule === 'object' &&
+      typeof rule.use === 'object' &&
+      !Array.isArray(rule.use) &&
+      rule.use.loader === loaderName
+  ) as webpack.RuleSetRule;
+
+const config: webpack.Configuration = {
+  mode: baseConfig.mode,
   context: baseConfig.context,
   context: baseConfig.context,
   resolve: baseConfig.resolve,
   resolve: baseConfig.resolve,
 
 
@@ -26,7 +33,7 @@ const config = {
 
 
   module: {
   module: {
     rules: [findLoader('babel-loader'), findLoader('po-catalog-loader')],
     rules: [findLoader('babel-loader'), findLoader('po-catalog-loader')],
-    noParse: baseConfig.module.noParse,
+    noParse: baseConfig.module?.noParse,
   },
   },
 
 
   plugins: [
   plugins: [

+ 0 - 30
config/webpack.css.config.js

@@ -1,30 +0,0 @@
-/* eslint-env node */
-/* eslint import/no-nodejs-modules:0 */
-const config = require('../webpack.config');
-
-config.entry = {
-  sentry: 'less/jest-ci.less',
-};
-config.module = {
-  ...config.module,
-  rules: [
-    ...config.module.rules.filter(({test}) =>
-      ['/\\.css/', '/\\.less$/'].includes(test.toString())
-    ),
-
-    {
-      test: /\.(jpe?g|png|gif|ttf|eot|svg|woff(2)?)(\?[a-z0-9=&.]+)?$/,
-      use: [
-        {
-          loader: 'url-loader',
-          options: {
-            esModule: false,
-            limit: true,
-          },
-        },
-      ],
-    },
-  ],
-};
-
-module.exports = config;

+ 34 - 0
config/webpack.css.config.ts

@@ -0,0 +1,34 @@
+/* eslint-env node */
+/* eslint import/no-nodejs-modules:0 */
+
+import config from '../webpack.config';
+
+// Select all CSS rules from the base webpack config
+const cssRules =
+  config?.module?.rules?.filter(
+    ruleSet =>
+      typeof ruleSet === 'object' &&
+      ['/\\.css/', '/\\.less$/'].includes(ruleSet.test?.toString() ?? '')
+  ) ?? [];
+
+config.entry = {
+  sentry: 'less/jest-ci.less',
+};
+
+config.module = {
+  ...config?.module,
+  rules: [
+    ...cssRules,
+    {
+      test: /\.(jpe?g|png|gif|ttf|eot|svg|woff(2)?)(\?[a-z0-9=&.]+)?$/,
+      use: [
+        {
+          loader: 'url-loader',
+          options: {esModule: false, limit: true},
+        },
+      ],
+    },
+  ],
+};
+
+export default config;

+ 7 - 3
package.json

@@ -23,6 +23,7 @@
     "@emotion/styled": "^11.3.0",
     "@emotion/styled": "^11.3.0",
     "@sentry-internal/global-search": "^0.0.43",
     "@sentry-internal/global-search": "^0.0.43",
     "@sentry/integrations": "6.8.0",
     "@sentry/integrations": "6.8.0",
+    "@sentry/node": "6.8.0",
     "@sentry/react": "6.8.0",
     "@sentry/react": "6.8.0",
     "@sentry/release-parser": "^1.1.4",
     "@sentry/release-parser": "^1.1.4",
     "@sentry/rrweb": "^0.3.1",
     "@sentry/rrweb": "^0.3.1",
@@ -31,7 +32,9 @@
     "@testing-library/jest-dom": "^5.14.1",
     "@testing-library/jest-dom": "^5.14.1",
     "@testing-library/react": "^12.0.0",
     "@testing-library/react": "^12.0.0",
     "@types/color": "^3.0.2",
     "@types/color": "^3.0.2",
+    "@types/compression-webpack-plugin": "^6.0.5",
     "@types/create-react-class": "^15.6.2",
     "@types/create-react-class": "^15.6.2",
+    "@types/css-minimizer-webpack-plugin": "^3.0.1",
     "@types/diff": "5.0.1",
     "@types/diff": "5.0.1",
     "@types/dompurify": "^2.2.3",
     "@types/dompurify": "^2.2.3",
     "@types/echarts": "^4.9.3",
     "@types/echarts": "^4.9.3",
@@ -40,6 +43,7 @@
     "@types/js-cookie": "^2.2.7",
     "@types/js-cookie": "^2.2.7",
     "@types/lodash": "^4.14.170",
     "@types/lodash": "^4.14.170",
     "@types/marked": "^0.7.2",
     "@types/marked": "^0.7.2",
+    "@types/mini-css-extract-plugin": "^1.4.3",
     "@types/papaparse": "^5.2.5",
     "@types/papaparse": "^5.2.5",
     "@types/pegjs": "^0.10.3",
     "@types/pegjs": "^0.10.3",
     "@types/react": "~17.0.13",
     "@types/react": "~17.0.13",
@@ -53,6 +57,7 @@
     "@types/react-virtualized": "^9.21.12",
     "@types/react-virtualized": "^9.21.12",
     "@types/reflux": "0.4.1",
     "@types/reflux": "0.4.1",
     "@types/scroll-to-element": "^2.0.1",
     "@types/scroll-to-element": "^2.0.1",
+    "@types/webpack-dev-server": "^3.11.4",
     "ansicolor": "^1.1.95",
     "ansicolor": "^1.1.95",
     "babel-loader": "^8.1.0",
     "babel-loader": "^8.1.0",
     "babel-plugin-add-react-displayname": "^0.0.5",
     "babel-plugin-add-react-displayname": "^0.0.5",
@@ -137,7 +142,6 @@
   "devDependencies": {
   "devDependencies": {
     "@babel/plugin-transform-react-jsx-source": "^7.14.5",
     "@babel/plugin-transform-react-jsx-source": "^7.14.5",
     "@pmmmwh/react-refresh-webpack-plugin": "^0.4.2",
     "@pmmmwh/react-refresh-webpack-plugin": "^0.4.2",
-    "@sentry/node": "6.8.0",
     "@size-limit/preset-small-lib": "^4.10.2",
     "@size-limit/preset-small-lib": "^4.10.2",
     "@storybook/addon-a11y": "6.3.4",
     "@storybook/addon-a11y": "6.3.4",
     "@storybook/addon-actions": "6.3.4",
     "@storybook/addon-actions": "6.3.4",
@@ -215,8 +219,8 @@
     "install-api-docs": "cd api-docs && yarn install",
     "install-api-docs": "cd api-docs && yarn install",
     "build-derefed-docs": "yarn install-api-docs && node api-docs/index.js api-docs/openapi.json",
     "build-derefed-docs": "yarn install-api-docs && node api-docs/index.js api-docs/openapi.json",
     "watch-api-docs": "sane 'yarn build-derefed-docs' api-docs",
     "watch-api-docs": "sane 'yarn build-derefed-docs' api-docs",
-    "build-css": "NODE_ENV=production yarn webpack --config=config/webpack.css.config.js",
-    "build-chartcuterie-config": "NODE_ENV=production yarn webpack --config=config/webpack.chartcuterie.config.js",
+    "build-css": "NODE_ENV=production yarn webpack --config=config/webpack.css.config.ts",
+    "build-chartcuterie-config": "NODE_ENV=production yarn webpack --config=config/webpack.chartcuterie.config.ts",
     "build-acceptance": "IS_ACCEPTANCE_TEST=1 NODE_ENV=production yarn webpack --mode development",
     "build-acceptance": "IS_ACCEPTANCE_TEST=1 NODE_ENV=production yarn webpack --mode development",
     "build-production": "NODE_ENV=production yarn webpack --mode production",
     "build-production": "NODE_ENV=production yarn webpack --mode production",
     "build": "yarn build-production --output-path=public",
     "build": "yarn build-production --output-path=public",

Some files were not shown because too many files changed in this diff