AFFiNE/apps/core/.webpack/config.ts

452 lines
13 KiB
TypeScript
Raw Normal View History

2023-07-18 19:53:10 +03:00
import { join, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { createRequire } from 'node:module';
2023-07-18 19:53:10 +03:00
import type { Configuration as DevServerConfiguration } from 'webpack-dev-server';
import { PerfseePlugin } from '@perfsee/webpack';
import { sentryWebpackPlugin } from '@sentry/webpack-plugin';
import CopyPlugin from 'copy-webpack-plugin';
import ReactRefreshWebpackPlugin from '@pmmmwh/react-refresh-webpack-plugin';
import TerserPlugin from 'terser-webpack-plugin';
import webpack from 'webpack';
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
import { compact } from 'lodash-es';
2023-07-18 19:53:10 +03:00
import { productionCacheGroups } from './cache-group.js';
import type { BuildFlags } from '@affine/cli/config';
import { projectRoot } from '@affine/cli/config';
import { VanillaExtractPlugin } from '@vanilla-extract/webpack-plugin';
import type { RuntimeConfig } from '@affine/env/global';
import { WebpackS3Plugin, gitShortHash } from './s3-plugin.js';
2023-07-18 19:53:10 +03:00
const IN_CI = !!process.env.CI;
export const rootPath = fileURLToPath(new URL('..', import.meta.url));
const require = createRequire(rootPath);
const OptimizeOptionOptions: (
buildFlags: BuildFlags
) => webpack.Configuration['optimization'] = buildFlags => ({
minimize: buildFlags.mode === 'production',
minimizer: [
new TerserPlugin({
minify: TerserPlugin.swcMinify,
2023-07-30 21:10:45 +03:00
exclude: [/plugins\/.+\/.+\.js$/, /plugins\/.+\/.+\.mjs$/],
2023-07-18 19:53:10 +03:00
parallel: true,
extractComments: true,
terserOptions: {
ecma: 2020,
compress: {
unused: true,
},
mangle: true,
},
}),
],
removeEmptyChunks: true,
providedExports: true,
usedExports: true,
sideEffects: true,
removeAvailableModules: true,
runtimeChunk: {
name: 'runtime',
},
splitChunks: {
chunks: 'all',
minSize: 1,
minChunks: 1,
maxInitialRequests: Number.MAX_SAFE_INTEGER,
maxAsyncRequests: Number.MAX_SAFE_INTEGER,
cacheGroups:
buildFlags.mode === 'production'
? productionCacheGroups
: {
default: false,
vendors: false,
},
},
});
2023-08-29 19:51:57 +03:00
export const getPublicPath = (buildFlags: BuildFlags) => {
const { BUILD_TYPE } = process.env;
const publicPath = process.env.PUBLIC_PATH ?? '/';
2023-08-29 19:51:57 +03:00
if (process.env.COVERAGE || buildFlags.distribution === 'desktop') {
return publicPath;
}
2023-08-29 19:51:57 +03:00
if (BUILD_TYPE === 'canary') {
return `https://dev.affineassets.com/${gitShortHash()}/`;
} else if (BUILD_TYPE === 'beta' || BUILD_TYPE === 'stable') {
return `https://prod.affineassets.com/${gitShortHash()}/`;
}
return publicPath;
2023-08-29 19:51:57 +03:00
};
2023-07-18 19:53:10 +03:00
export const createConfiguration: (
buildFlags: BuildFlags,
runtimeConfig: RuntimeConfig
) => webpack.Configuration = (buildFlags, runtimeConfig) => {
const blocksuiteBaseDir = buildFlags.localBlockSuite;
2023-07-18 19:53:10 +03:00
const config = {
name: 'affine',
// to set a correct base path for the source map
context: projectRoot,
2023-08-04 02:05:46 +03:00
experiments: {
topLevelAwait: true,
outputModule: false,
syncWebAssembly: true,
2023-08-04 02:05:46 +03:00
},
2023-07-18 19:53:10 +03:00
output: {
environment: {
module: true,
dynamicImport: true,
},
filename:
buildFlags.mode === 'production'
? 'js/[name]-[contenthash:8].js'
: 'js/[name].js',
2023-07-18 19:53:10 +03:00
// In some cases webpack will emit files starts with "_" which is reserved in web extension.
chunkFilename:
buildFlags.mode === 'production'
? 'js/chunk.[name]-[contenthash:8].js'
: 'js/chunk.[name].js',
assetModuleFilename: 'assets/[name]-[contenthash:8][ext][query]',
2023-07-18 19:53:10 +03:00
devtoolModuleFilenameTemplate: 'webpack://[namespace]/[resource-path]',
hotUpdateChunkFilename: 'hot/[id].[fullhash].js',
hotUpdateMainFilename: 'hot/[runtime].[fullhash].json',
path: join(rootPath, 'dist'),
clean: buildFlags.mode === 'production',
globalObject: 'globalThis',
2023-08-29 19:51:57 +03:00
publicPath: getPublicPath(buildFlags),
2023-07-18 19:53:10 +03:00
},
target: ['web', 'es2022'],
mode: buildFlags.mode,
devtool:
buildFlags.mode === 'production'
? 'source-map'
2023-07-18 19:53:10 +03:00
: 'eval-cheap-module-source-map',
resolve: {
2023-07-27 08:36:29 +03:00
symlinks: true,
2023-07-18 19:53:10 +03:00
extensionAlias: {
'.js': ['.js', '.tsx', '.ts'],
'.mjs': ['.mjs', '.mts'],
},
extensions: ['.js', '.ts', '.tsx'],
fallback:
blocksuiteBaseDir === undefined
? undefined
: {
events: false,
},
alias:
blocksuiteBaseDir === undefined
? undefined
: {
yjs: require.resolve('yjs'),
'@blocksuite/block-std': resolve(
blocksuiteBaseDir,
'packages',
2023-08-23 17:08:37 +03:00
'block-std',
'src'
),
'@blocksuite/blocks': resolve(
blocksuiteBaseDir,
'packages',
2023-08-23 17:08:37 +03:00
'blocks',
'src'
),
'@blocksuite/editor': resolve(
blocksuiteBaseDir,
'packages',
2023-08-23 17:08:37 +03:00
'editor',
'src'
),
'@blocksuite/global': resolve(
blocksuiteBaseDir,
'packages',
2023-08-23 17:08:37 +03:00
'global',
'src'
),
'@blocksuite/lit': resolve(
blocksuiteBaseDir,
'packages',
'lit',
'src'
),
'@blocksuite/phasor': resolve(
blocksuiteBaseDir,
'packages',
2023-08-23 17:08:37 +03:00
'phasor',
'src'
),
'@blocksuite/store/providers/broadcast-channel': resolve(
blocksuiteBaseDir,
'packages',
'store',
'src/providers/broadcast-channel'
),
'@blocksuite/store': resolve(
blocksuiteBaseDir,
'packages',
2023-08-23 17:08:37 +03:00
'store',
'src'
),
'@blocksuite/virgo': resolve(
blocksuiteBaseDir,
'packages',
2023-08-23 17:08:37 +03:00
'virgo',
'src'
),
},
2023-07-18 19:53:10 +03:00
},
module: {
parser: {
javascript: {
2023-08-04 02:05:46 +03:00
// Do not mock Node.js globals
node: false,
requireJs: false,
import: true,
2023-07-18 19:53:10 +03:00
// Treat as missing export as error
strictExportPresence: true,
},
},
rules: [
{
test: /\.m?js?$/,
2023-08-04 02:05:46 +03:00
enforce: 'pre',
use: [
{
loader: require.resolve('source-map-loader'),
options: {
filterSourceMappingUrl: (
_url: string,
resourcePath: string
) => {
return resourcePath.includes('@blocksuite');
},
},
},
],
2023-07-18 19:53:10 +03:00
resolve: {
fullySpecified: false,
},
},
{
oneOf: [
{
test: /\.tsx?$/,
exclude: /node_modules/,
2023-07-18 19:53:10 +03:00
loader: require.resolve('swc-loader'),
options: {
// https://swc.rs/docs/configuring-swc/
jsc: {
preserveAllComments: true,
parser: {
syntax: 'typescript',
dynamicImport: true,
topLevelAwait: false,
tsx: true,
decorators: true,
2023-07-18 19:53:10 +03:00
},
target: 'es2022',
externalHelpers: false,
2023-07-18 19:53:10 +03:00
transform: {
react: {
runtime: 'automatic',
refresh: buildFlags.mode === 'development' && {
refreshReg: '$RefreshReg$',
refreshSig: '$RefreshSig$',
emitFullSignatures: true,
},
},
useDefineForClassFields: false,
2023-07-18 19:53:10 +03:00
},
experimental: {
keepImportAssertions: true,
plugins: [
buildFlags.coverage && [
'swc-plugin-coverage-instrument',
{},
],
].filter(Boolean),
},
},
},
},
{
test: /\.svg$/,
use: [
'thread-loader',
{
loader: '@svgr/webpack',
options: {
icon: true,
},
},
],
exclude: [/node_modules/],
},
{
test: /\.(png|jpg|gif|svg|webp)$/,
type: 'asset/resource',
},
{
2023-07-20 19:04:26 +03:00
test: /\.(ttf|eot|woff|woff2)$/,
2023-07-18 19:53:10 +03:00
type: 'asset/resource',
},
{
test: /\.txt$/,
loader: 'raw-loader',
},
{
test: /\.css$/,
use: [
2023-07-19 18:52:21 +03:00
buildFlags.mode === 'development'
? 'style-loader'
: MiniCssExtractPlugin.loader,
2023-07-18 19:53:10 +03:00
{
loader: 'css-loader',
options: {
url: true,
2023-07-18 19:53:10 +03:00
sourceMap: false,
modules: false,
import: true,
importLoaders: 1,
},
},
{
loader: 'postcss-loader',
options: {
postcssOptions: {
config: resolve(
rootPath,
'.webpack',
'postcss.config.cjs'
),
},
},
},
],
},
],
},
],
},
plugins: compact([
IN_CI ? null : new webpack.ProgressPlugin({ percentBy: 'entries' }),
buildFlags.mode === 'development'
? new ReactRefreshWebpackPlugin({ overlay: false, esModule: true })
: new MiniCssExtractPlugin({
filename: `[name].[contenthash:8].css`,
ignoreOrder: true,
}),
2023-07-18 19:53:10 +03:00
new VanillaExtractPlugin(),
new webpack.DefinePlugin({
'process.env': JSON.stringify({}),
2023-07-20 19:04:26 +03:00
'process.env.COVERAGE': JSON.stringify(!!buildFlags.coverage),
2023-07-20 05:44:50 +03:00
'process.env.NODE_ENV': JSON.stringify(buildFlags.mode),
'process.env.SHOULD_REPORT_TRACE': `${Boolean(
process.env.SHOULD_REPORT_TRACE
)}`,
'process.env.TRACE_REPORT_ENDPOINT': `"${process.env.TRACE_REPORT_ENDPOINT}"`,
2023-07-18 19:53:10 +03:00
runtimeConfig: JSON.stringify(runtimeConfig),
}),
new CopyPlugin({
patterns: [
{
from: resolve(rootPath, 'public'),
to: resolve(rootPath, 'dist'),
},
2023-07-18 19:53:10 +03:00
],
}),
buildFlags.mode === 'production' && process.env.R2_SECRET_ACCESS_KEY
? new WebpackS3Plugin()
: null,
]),
2023-07-18 19:53:10 +03:00
optimization: OptimizeOptionOptions(buildFlags),
devServer: {
hot: 'only',
2023-07-20 13:52:29 +03:00
liveReload: true,
2023-07-18 19:53:10 +03:00
client: undefined,
historyApiFallback: true,
static: {
directory: resolve(rootPath, 'public'),
publicPath: '/',
2023-07-20 13:52:29 +03:00
watch: true,
2023-07-18 19:53:10 +03:00
},
proxy: {
'/api': 'http://localhost:3010',
'/socket.io': {
target: 'http://localhost:3010',
ws: true,
},
'/graphql': 'http://localhost:3010',
},
2023-07-18 19:53:10 +03:00
} as DevServerConfiguration,
} satisfies webpack.Configuration;
if (buildFlags.mode === 'production' && process.env.PERFSEE_TOKEN) {
config.devtool = 'hidden-nosources-source-map';
config.plugins.push(
new PerfseePlugin({
project: 'affine-toeverything',
})
);
}
2023-07-19 18:52:21 +03:00
if (buildFlags.mode === 'development') {
config.optimization = {
...config.optimization,
minimize: false,
runtimeChunk: false,
splitChunks: {
maxInitialRequests: Infinity,
chunks: 'all',
cacheGroups: {
defaultVendors: {
test: `[\\/]node_modules[\\/](?!.*vanilla-extract)`,
priority: -10,
reuseExistingChunk: true,
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true,
},
styles: {
name: 'styles',
type: 'css/mini-extract',
chunks: 'all',
enforce: true,
},
},
},
};
}
2023-07-18 19:53:10 +03:00
if (
process.env.SENTRY_AUTH_TOKEN &&
process.env.SENTRY_ORG &&
process.env.SENTRY_PROJECT
) {
config.plugins.push(
sentryWebpackPlugin({
org: process.env.SENTRY_ORG,
project: process.env.SENTRY_PROJECT,
authToken: process.env.SENTRY_AUTH_TOKEN,
})
);
}
return config;
};