mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-21 03:56:23 +08:00
chore: add monorepo tools (#9196)
This commit is contained in:
@@ -0,0 +1,197 @@
|
||||
import { execSync } from 'node:child_process';
|
||||
import { readFileSync } from 'node:fs';
|
||||
|
||||
import type { BUILD_CONFIG_TYPE } from '@affine/env/global';
|
||||
import { Path, ProjectRoot } from '@affine-tools/utils/path';
|
||||
import { Repository } from '@napi-rs/simple-git';
|
||||
import HTMLPlugin from 'html-webpack-plugin';
|
||||
import once from 'lodash-es/once';
|
||||
import type { Compiler, WebpackPluginInstance } from 'webpack';
|
||||
import webpack from 'webpack';
|
||||
|
||||
import type { BuildFlags } from './types.js';
|
||||
|
||||
export const getPublicPath = (
|
||||
flags: BuildFlags,
|
||||
BUILD_CONFIG: BUILD_CONFIG_TYPE
|
||||
) => {
|
||||
const { BUILD_TYPE } = process.env;
|
||||
if (typeof process.env.PUBLIC_PATH === 'string') {
|
||||
return process.env.PUBLIC_PATH;
|
||||
}
|
||||
|
||||
if (
|
||||
flags.mode === 'development' ||
|
||||
BUILD_CONFIG.distribution === 'desktop' ||
|
||||
BUILD_CONFIG.distribution === 'ios' ||
|
||||
BUILD_CONFIG.distribution === 'android'
|
||||
) {
|
||||
return '/';
|
||||
}
|
||||
|
||||
switch (BUILD_TYPE) {
|
||||
case 'stable':
|
||||
return 'https://prod.affineassets.com/';
|
||||
case 'beta':
|
||||
return 'https://beta.affineassets.com/';
|
||||
default:
|
||||
return 'https://dev.affineassets.com/';
|
||||
}
|
||||
};
|
||||
|
||||
const DESCRIPTION = `There can be more than Notion and Miro. AFFiNE is a next-gen knowledge base that brings planning, sorting and creating all together.`;
|
||||
|
||||
const gitShortHash = once(() => {
|
||||
const { GITHUB_SHA } = process.env;
|
||||
if (GITHUB_SHA) {
|
||||
return GITHUB_SHA.substring(0, 9);
|
||||
}
|
||||
const repo = new Repository(ProjectRoot.path);
|
||||
const shortSha = repo.head().target()?.substring(0, 9);
|
||||
if (shortSha) {
|
||||
return shortSha;
|
||||
}
|
||||
const sha = execSync(`git rev-parse --short HEAD`, {
|
||||
encoding: 'utf-8',
|
||||
}).trim();
|
||||
return sha;
|
||||
});
|
||||
|
||||
const currentDir = Path.dir(import.meta.url);
|
||||
|
||||
function getHTMLPluginOptions(
|
||||
flags: BuildFlags,
|
||||
BUILD_CONFIG: BUILD_CONFIG_TYPE
|
||||
) {
|
||||
const publicPath = getPublicPath(flags, BUILD_CONFIG);
|
||||
const cdnOrigin = publicPath.startsWith('/')
|
||||
? undefined
|
||||
: new URL(publicPath).origin;
|
||||
|
||||
const templateParams = {
|
||||
GIT_SHORT_SHA: gitShortHash(),
|
||||
DESCRIPTION,
|
||||
PRECONNECT: cdnOrigin
|
||||
? `<link rel="preconnect" href="${cdnOrigin}" />`
|
||||
: '',
|
||||
VIEWPORT_FIT: BUILD_CONFIG.isMobileEdition ? 'cover' : 'auto',
|
||||
};
|
||||
|
||||
return {
|
||||
template: currentDir.join('template.html').toString(),
|
||||
inject: 'body',
|
||||
filename: 'index.html',
|
||||
minify: false,
|
||||
templateParameters: templateParams,
|
||||
chunks: ['app'],
|
||||
} satisfies HTMLPlugin.Options;
|
||||
}
|
||||
|
||||
export function createShellHTMLPlugin(
|
||||
flags: BuildFlags,
|
||||
BUILD_CONFIG: BUILD_CONFIG_TYPE
|
||||
) {
|
||||
const htmlPluginOptions = getHTMLPluginOptions(flags, BUILD_CONFIG);
|
||||
|
||||
return new HTMLPlugin({
|
||||
...htmlPluginOptions,
|
||||
chunks: ['shell'],
|
||||
filename: `shell.html`,
|
||||
});
|
||||
}
|
||||
|
||||
export function createHTMLPlugins(
|
||||
flags: BuildFlags,
|
||||
BUILD_CONFIG: BUILD_CONFIG_TYPE
|
||||
): WebpackPluginInstance[] {
|
||||
const publicPath = getPublicPath(flags, BUILD_CONFIG);
|
||||
const globalErrorHandler = [
|
||||
'js/global-error-handler.js',
|
||||
readFileSync(currentDir.join('./error-handler.js').toString(), 'utf-8'),
|
||||
];
|
||||
|
||||
const htmlPluginOptions = getHTMLPluginOptions(flags, BUILD_CONFIG);
|
||||
|
||||
return [
|
||||
{
|
||||
apply(compiler: Compiler) {
|
||||
compiler.hooks.compilation.tap(
|
||||
'assets-manifest-plugin',
|
||||
compilation => {
|
||||
HTMLPlugin.getHooks(compilation).beforeAssetTagGeneration.tap(
|
||||
'assets-manifest-plugin',
|
||||
arg => {
|
||||
if (
|
||||
!BUILD_CONFIG.isElectron &&
|
||||
!compilation.getAsset(globalErrorHandler[0])
|
||||
) {
|
||||
compilation.emitAsset(
|
||||
globalErrorHandler[0],
|
||||
new webpack.sources.RawSource(globalErrorHandler[1])
|
||||
);
|
||||
arg.assets.js.unshift(
|
||||
arg.assets.publicPath + globalErrorHandler[0]
|
||||
);
|
||||
}
|
||||
|
||||
if (!compilation.getAsset('assets-manifest.json')) {
|
||||
compilation.emitAsset(
|
||||
globalErrorHandler[0],
|
||||
new webpack.sources.RawSource(globalErrorHandler[1])
|
||||
);
|
||||
compilation.emitAsset(
|
||||
`assets-manifest.json`,
|
||||
new webpack.sources.RawSource(
|
||||
JSON.stringify(
|
||||
{
|
||||
...arg.assets,
|
||||
js: arg.assets.js.map(file =>
|
||||
file.substring(arg.assets.publicPath.length)
|
||||
),
|
||||
css: arg.assets.css.map(file =>
|
||||
file.substring(arg.assets.publicPath.length)
|
||||
),
|
||||
gitHash:
|
||||
htmlPluginOptions.templateParameters.GIT_SHORT_SHA,
|
||||
description:
|
||||
htmlPluginOptions.templateParameters.DESCRIPTION,
|
||||
},
|
||||
null,
|
||||
2
|
||||
)
|
||||
),
|
||||
{
|
||||
immutable: false,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return arg;
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
},
|
||||
},
|
||||
new HTMLPlugin({
|
||||
...htmlPluginOptions,
|
||||
publicPath,
|
||||
meta: {
|
||||
'env:publicPath': publicPath,
|
||||
},
|
||||
}),
|
||||
// selfhost html
|
||||
new HTMLPlugin({
|
||||
...htmlPluginOptions,
|
||||
meta: {
|
||||
'env:isSelfHosted': 'true',
|
||||
'env:publicPath': '/',
|
||||
},
|
||||
filename: 'selfhost.html',
|
||||
templateParameters: {
|
||||
...htmlPluginOptions.templateParameters,
|
||||
PRECONNECT: '',
|
||||
},
|
||||
}),
|
||||
];
|
||||
}
|
||||
@@ -1,32 +1,33 @@
|
||||
import { join } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { createRequire } from 'node:module';
|
||||
|
||||
import type { BUILD_CONFIG_TYPE } from '@affine/env/global';
|
||||
import { getBuildConfig } from '@affine-tools/utils/build-config';
|
||||
import { ProjectRoot } from '@affine-tools/utils/path';
|
||||
import type { Package } from '@affine-tools/utils/workspace';
|
||||
import { PerfseePlugin } from '@perfsee/webpack';
|
||||
import ReactRefreshWebpackPlugin from '@pmmmwh/react-refresh-webpack-plugin';
|
||||
import { sentryWebpackPlugin } from '@sentry/webpack-plugin';
|
||||
import { VanillaExtractPlugin } from '@vanilla-extract/webpack-plugin';
|
||||
import CopyPlugin from 'copy-webpack-plugin';
|
||||
import { compact } from 'lodash-es';
|
||||
import compact from 'lodash-es/compact';
|
||||
import MiniCssExtractPlugin from 'mini-css-extract-plugin';
|
||||
import TerserPlugin from 'terser-webpack-plugin';
|
||||
import webpack from 'webpack';
|
||||
import type { Configuration as DevServerConfiguration } from 'webpack-dev-server';
|
||||
|
||||
import { projectRoot } from '../config/cwd.cjs';
|
||||
import type { BuildFlags } from '../config/index.js';
|
||||
import { productionCacheGroups } from './cache-group.js';
|
||||
import { createHTMLPlugins, createShellHTMLPlugin } from './html-plugin.js';
|
||||
import { WebpackS3Plugin } from './s3-plugin.js';
|
||||
import type { BuildFlags } from './types';
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
const cssnano = require('cssnano');
|
||||
|
||||
const IN_CI = !!process.env.CI;
|
||||
|
||||
export const rootPath = join(fileURLToPath(import.meta.url), '..', '..');
|
||||
export const workspaceRoot = join(rootPath, '..', '..', '..');
|
||||
|
||||
const OptimizeOptionOptions: (
|
||||
buildFlags: BuildFlags
|
||||
) => webpack.Configuration['optimization'] = buildFlags => ({
|
||||
minimize: buildFlags.mode === 'production',
|
||||
flags: BuildFlags
|
||||
) => webpack.Configuration['optimization'] = flags => ({
|
||||
minimize: flags.mode === 'production',
|
||||
minimizer: [
|
||||
new TerserPlugin({
|
||||
minify: TerserPlugin.swcMinify,
|
||||
@@ -60,71 +61,48 @@ const OptimizeOptionOptions: (
|
||||
},
|
||||
});
|
||||
|
||||
export const getPublicPath = (buildFlags: BuildFlags) => {
|
||||
const { BUILD_TYPE } = process.env;
|
||||
if (typeof process.env.PUBLIC_PATH === 'string') {
|
||||
return process.env.PUBLIC_PATH;
|
||||
}
|
||||
export function createWebpackConfig(
|
||||
pkg: Package,
|
||||
flags: BuildFlags
|
||||
): webpack.Configuration {
|
||||
const buildConfig = getBuildConfig(pkg, flags);
|
||||
|
||||
if (
|
||||
buildFlags.mode === 'development' ||
|
||||
process.env.COVERAGE ||
|
||||
buildFlags.distribution === 'desktop' ||
|
||||
buildFlags.distribution === 'ios' ||
|
||||
buildFlags.distribution === 'android'
|
||||
) {
|
||||
return '/';
|
||||
}
|
||||
|
||||
switch (BUILD_TYPE) {
|
||||
case 'stable':
|
||||
return 'https://prod.affineassets.com/';
|
||||
case 'beta':
|
||||
return 'https://beta.affineassets.com/';
|
||||
default:
|
||||
return 'https://dev.affineassets.com/';
|
||||
}
|
||||
};
|
||||
|
||||
export const createConfiguration: (
|
||||
cwd: string,
|
||||
buildFlags: BuildFlags,
|
||||
buildConfig: BUILD_CONFIG_TYPE
|
||||
) => webpack.Configuration = (cwd, buildFlags, buildConfig) => {
|
||||
const config = {
|
||||
name: 'affine',
|
||||
// to set a correct base path for the source map
|
||||
context: cwd,
|
||||
context: pkg.path.value,
|
||||
experiments: {
|
||||
topLevelAwait: true,
|
||||
outputModule: false,
|
||||
syncWebAssembly: true,
|
||||
},
|
||||
entry: {
|
||||
app: pkg.entry ?? './src/index.tsx',
|
||||
},
|
||||
output: {
|
||||
environment: {
|
||||
module: true,
|
||||
dynamicImport: true,
|
||||
},
|
||||
filename:
|
||||
buildFlags.mode === 'production'
|
||||
flags.mode === 'production'
|
||||
? 'js/[name].[contenthash:8].js'
|
||||
: 'js/[name].js',
|
||||
// In some cases webpack will emit files starts with "_" which is reserved in web extension.
|
||||
chunkFilename: pathData =>
|
||||
pathData.chunk?.name?.endsWith?.('worker')
|
||||
? 'js/[name].[contenthash:8].js'
|
||||
: buildFlags.mode === 'production'
|
||||
: flags.mode === 'production'
|
||||
? 'js/chunk.[name].[contenthash:8].js'
|
||||
: 'js/chunk.[name].js',
|
||||
assetModuleFilename:
|
||||
buildFlags.mode === 'production'
|
||||
flags.mode === 'production'
|
||||
? 'assets/[name].[contenthash:8][ext][query]'
|
||||
: '[name].[contenthash:8][ext]',
|
||||
devtoolModuleFilenameTemplate: 'webpack://[namespace]/[resource-path]',
|
||||
hotUpdateChunkFilename: 'hot/[id].[fullhash].js',
|
||||
hotUpdateMainFilename: 'hot/[runtime].[fullhash].json',
|
||||
path: join(cwd, 'dist'),
|
||||
clean: buildFlags.mode === 'production',
|
||||
path: pkg.distPath.value,
|
||||
clean: flags.mode === 'production',
|
||||
globalObject: 'globalThis',
|
||||
// NOTE(@forehalo): always keep it '/'
|
||||
publicPath: '/',
|
||||
@@ -132,10 +110,10 @@ export const createConfiguration: (
|
||||
},
|
||||
target: ['web', 'es2022'],
|
||||
|
||||
mode: buildFlags.mode,
|
||||
mode: flags.mode,
|
||||
|
||||
devtool:
|
||||
buildFlags.mode === 'production'
|
||||
flags.mode === 'production'
|
||||
? 'source-map'
|
||||
: 'eval-cheap-module-source-map',
|
||||
|
||||
@@ -147,14 +125,13 @@ export const createConfiguration: (
|
||||
},
|
||||
extensions: ['.js', '.ts', '.tsx'],
|
||||
alias: {
|
||||
yjs: join(workspaceRoot, 'node_modules', 'yjs'),
|
||||
lit: join(workspaceRoot, 'node_modules', 'lit'),
|
||||
'@preact/signals-core': join(
|
||||
workspaceRoot,
|
||||
yjs: ProjectRoot.join('node_modules', 'yjs').value,
|
||||
lit: ProjectRoot.join('node_modules', 'lit').value,
|
||||
'@preact/signals-core': ProjectRoot.join(
|
||||
'node_modules',
|
||||
'@preact',
|
||||
'signals-core'
|
||||
),
|
||||
).value,
|
||||
},
|
||||
},
|
||||
|
||||
@@ -230,7 +207,7 @@ export const createConfiguration: (
|
||||
transform: {
|
||||
react: {
|
||||
runtime: 'automatic',
|
||||
refresh: buildFlags.mode === 'development' && {
|
||||
refresh: flags.mode === 'development' && {
|
||||
refreshReg: '$RefreshReg$',
|
||||
refreshSig: '$RefreshSig$',
|
||||
emitFullSignatures: true,
|
||||
@@ -263,7 +240,7 @@ export const createConfiguration: (
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: [
|
||||
buildFlags.mode === 'development'
|
||||
flags.mode === 'development'
|
||||
? 'style-loader'
|
||||
: MiniCssExtractPlugin.loader,
|
||||
{
|
||||
@@ -280,7 +257,25 @@ export const createConfiguration: (
|
||||
loader: 'postcss-loader',
|
||||
options: {
|
||||
postcssOptions: {
|
||||
config: join(rootPath, 'webpack', 'postcss.config.cjs'),
|
||||
plugins: [
|
||||
cssnano({
|
||||
preset: [
|
||||
'default',
|
||||
{
|
||||
convertValues: false,
|
||||
},
|
||||
],
|
||||
}),
|
||||
].concat(
|
||||
pkg.join('tailwind.config.js').exists()
|
||||
? [
|
||||
require('tailwindcss')(
|
||||
require(pkg.join('tailwind.config.js').path)
|
||||
),
|
||||
'autoprefixer',
|
||||
]
|
||||
: []
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -292,7 +287,7 @@ export const createConfiguration: (
|
||||
},
|
||||
plugins: compact([
|
||||
IN_CI ? null : new webpack.ProgressPlugin({ percentBy: 'entries' }),
|
||||
buildFlags.mode === 'development'
|
||||
flags.mode === 'development'
|
||||
? new ReactRefreshWebpackPlugin({
|
||||
overlay: false,
|
||||
esModule: true,
|
||||
@@ -305,7 +300,7 @@ export const createConfiguration: (
|
||||
}),
|
||||
new VanillaExtractPlugin(),
|
||||
new webpack.DefinePlugin({
|
||||
'process.env.NODE_ENV': JSON.stringify(buildFlags.mode),
|
||||
'process.env.NODE_ENV': JSON.stringify(flags.mode),
|
||||
'process.env.CAPTCHA_SITE_KEY': JSON.stringify(
|
||||
process.env.CAPTCHA_SITE_KEY
|
||||
),
|
||||
@@ -323,24 +318,19 @@ export const createConfiguration: (
|
||||
{} as Record<string, string>
|
||||
),
|
||||
}),
|
||||
buildFlags.distribution === 'admin'
|
||||
buildConfig.isAdmin
|
||||
? null
|
||||
: new CopyPlugin({
|
||||
patterns: [
|
||||
{
|
||||
// copy the shared public assets into dist
|
||||
from: join(
|
||||
workspaceRoot,
|
||||
'packages',
|
||||
'frontend',
|
||||
'core',
|
||||
'public'
|
||||
),
|
||||
to: join(cwd, 'dist'),
|
||||
from: pkg.workspace.getPackage('@affine/core').join('public')
|
||||
.value,
|
||||
to: pkg.distPath.value,
|
||||
},
|
||||
],
|
||||
}),
|
||||
buildFlags.mode === 'production' &&
|
||||
flags.mode === 'production' &&
|
||||
(buildConfig.isWeb || buildConfig.isMobileWeb || buildConfig.isAdmin) &&
|
||||
process.env.R2_SECRET_ACCESS_KEY
|
||||
? new WebpackS3Plugin()
|
||||
@@ -349,14 +339,12 @@ export const createConfiguration: (
|
||||
stats: {
|
||||
errorDetails: true,
|
||||
},
|
||||
|
||||
optimization: OptimizeOptionOptions(buildFlags),
|
||||
|
||||
optimization: OptimizeOptionOptions(flags),
|
||||
devServer: {
|
||||
host: '0.0.0.0',
|
||||
allowedHosts: 'all',
|
||||
hot: buildFlags.static ? false : 'only',
|
||||
liveReload: !buildFlags.static,
|
||||
hot: true,
|
||||
liveReload: true,
|
||||
client: {
|
||||
overlay: process.env.DISABLE_DEV_OVERLAY === 'true' ? false : undefined,
|
||||
},
|
||||
@@ -374,20 +362,10 @@ export const createConfiguration: (
|
||||
},
|
||||
static: [
|
||||
{
|
||||
directory: join(
|
||||
projectRoot,
|
||||
'packages',
|
||||
'frontend',
|
||||
'core',
|
||||
'public'
|
||||
),
|
||||
directory: pkg.workspace.getPackage('@affine/core').join('public')
|
||||
.value,
|
||||
publicPath: '/',
|
||||
watch: !buildFlags.static,
|
||||
},
|
||||
{
|
||||
directory: join(cwd, 'public'),
|
||||
publicPath: '/',
|
||||
watch: !buildFlags.static,
|
||||
watch: true,
|
||||
},
|
||||
],
|
||||
proxy: [
|
||||
@@ -404,7 +382,7 @@ export const createConfiguration: (
|
||||
} as DevServerConfiguration,
|
||||
} satisfies webpack.Configuration;
|
||||
|
||||
if (buildFlags.mode === 'production' && process.env.PERFSEE_TOKEN) {
|
||||
if (flags.mode === 'production' && process.env.PERFSEE_TOKEN) {
|
||||
config.plugins.push(
|
||||
new PerfseePlugin({
|
||||
project: 'affine-toeverything',
|
||||
@@ -412,7 +390,7 @@ export const createConfiguration: (
|
||||
);
|
||||
}
|
||||
|
||||
if (buildFlags.mode === 'development') {
|
||||
if (flags.mode === 'development') {
|
||||
config.optimization = {
|
||||
...config.optimization,
|
||||
minimize: false,
|
||||
@@ -456,5 +434,11 @@ export const createConfiguration: (
|
||||
);
|
||||
}
|
||||
|
||||
config.plugins = config.plugins.concat(createHTMLPlugins(flags, buildConfig));
|
||||
|
||||
if (buildConfig.isElectron) {
|
||||
config.plugins.push(createShellHTMLPlugin(flags, buildConfig));
|
||||
}
|
||||
|
||||
return config;
|
||||
};
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
const { join } = require('node:path');
|
||||
|
||||
const cssnano = require('cssnano');
|
||||
const tailwindcss = require('tailwindcss');
|
||||
const autoprefixer = require('autoprefixer');
|
||||
|
||||
const { getCwdFromDistribution } = require('../config/cwd.cjs');
|
||||
|
||||
const projectCwd = getCwdFromDistribution(process.env.DISTRIBUTION);
|
||||
|
||||
const twConfig = (function () {
|
||||
try {
|
||||
const config = require(`${projectCwd}/tailwind.config.js`);
|
||||
const { content } = config;
|
||||
if (Array.isArray(content)) {
|
||||
config.content = content.map(c =>
|
||||
c.startsWith(projectCwd) ? c : join(projectCwd, c)
|
||||
);
|
||||
}
|
||||
return config;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
})();
|
||||
|
||||
module.exports = function (context) {
|
||||
const plugins = [
|
||||
cssnano({
|
||||
preset: [
|
||||
'default',
|
||||
{
|
||||
convertValues: false,
|
||||
},
|
||||
],
|
||||
}),
|
||||
];
|
||||
|
||||
if (twConfig) {
|
||||
plugins.push(tailwindcss(twConfig), autoprefixer());
|
||||
}
|
||||
|
||||
return {
|
||||
from: context.from,
|
||||
plugins,
|
||||
to: context.to,
|
||||
};
|
||||
};
|
||||
@@ -1,86 +0,0 @@
|
||||
import type { BUILD_CONFIG_TYPE } from '@affine/env/global';
|
||||
|
||||
import packageJson from '../../package.json' with { type: 'json' };
|
||||
import type { BuildFlags } from '../config';
|
||||
|
||||
export function getBuildConfig(buildFlags: BuildFlags): BUILD_CONFIG_TYPE {
|
||||
const buildPreset: Record<BuildFlags['channel'], BUILD_CONFIG_TYPE> = {
|
||||
get stable() {
|
||||
return {
|
||||
debug: buildFlags.mode === 'development',
|
||||
distribution: buildFlags.distribution,
|
||||
isDesktopEdition: (
|
||||
['web', 'desktop', 'admin'] as BuildFlags['distribution'][]
|
||||
).includes(buildFlags.distribution),
|
||||
isMobileEdition: (
|
||||
['mobile', 'ios', 'android'] as BuildFlags['distribution'][]
|
||||
).includes(buildFlags.distribution),
|
||||
isElectron: buildFlags.distribution === 'desktop',
|
||||
isWeb: buildFlags.distribution === 'web',
|
||||
isMobileWeb: buildFlags.distribution === 'mobile',
|
||||
isIOS: buildFlags.distribution === 'ios',
|
||||
isAndroid: buildFlags.distribution === 'android',
|
||||
isAdmin: buildFlags.distribution === 'admin',
|
||||
|
||||
appBuildType: 'stable' as const,
|
||||
serverUrlPrefix: 'https://app.affine.pro',
|
||||
appVersion: packageJson.version,
|
||||
editorVersion: packageJson.devDependencies['@blocksuite/affine'],
|
||||
githubUrl: 'https://github.com/toeverything/AFFiNE',
|
||||
changelogUrl: 'https://affine.pro/what-is-new',
|
||||
downloadUrl: 'https://affine.pro/download',
|
||||
imageProxyUrl: '/api/worker/image-proxy',
|
||||
linkPreviewUrl: '/api/worker/link-preview',
|
||||
};
|
||||
},
|
||||
get beta() {
|
||||
return {
|
||||
...this.stable,
|
||||
appBuildType: 'beta' as const,
|
||||
serverUrlPrefix: 'https://insider.affine.pro',
|
||||
changelogUrl: 'https://github.com/toeverything/AFFiNE/releases',
|
||||
};
|
||||
},
|
||||
get internal() {
|
||||
return {
|
||||
...this.stable,
|
||||
appBuildType: 'internal' as const,
|
||||
serverUrlPrefix: 'https://insider.affine.pro',
|
||||
changelogUrl: 'https://github.com/toeverything/AFFiNE/releases',
|
||||
};
|
||||
},
|
||||
// canary will be aggressive and enable all features
|
||||
get canary() {
|
||||
return {
|
||||
...this.stable,
|
||||
appBuildType: 'canary' as const,
|
||||
serverUrlPrefix: 'https://affine.fail',
|
||||
changelogUrl: 'https://github.com/toeverything/AFFiNE/releases',
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
const currentBuild = buildFlags.channel;
|
||||
|
||||
if (!(currentBuild in buildPreset)) {
|
||||
throw new Error(`BUILD_TYPE ${currentBuild} is not supported`);
|
||||
}
|
||||
|
||||
const currentBuildPreset = buildPreset[currentBuild];
|
||||
|
||||
const environmentPreset = {
|
||||
changelogUrl: process.env.CHANGELOG_URL ?? currentBuildPreset.changelogUrl,
|
||||
};
|
||||
|
||||
if (buildFlags.mode === 'development') {
|
||||
currentBuildPreset.serverUrlPrefix = 'http://localhost:8080';
|
||||
}
|
||||
|
||||
return {
|
||||
...currentBuildPreset,
|
||||
// environment preset will overwrite current build preset
|
||||
// this environment variable is for debug proposes only
|
||||
// do not put them into CI
|
||||
...(process.env.CI ? {} : environmentPreset),
|
||||
};
|
||||
}
|
||||
@@ -15,9 +15,7 @@ export class WebpackS3Plugin implements WebpackPluginInstance {
|
||||
region: 'auto',
|
||||
endpoint: `https://${process.env.R2_ACCOUNT_ID}.r2.cloudflarestorage.com`,
|
||||
credentials: {
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
accessKeyId: process.env.R2_ACCESS_KEY_ID!,
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
secretAccessKey: process.env.R2_SECRET_ACCESS_KEY!,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface BuildFlags {
|
||||
mode: 'development' | 'production';
|
||||
channel: 'stable' | 'beta' | 'canary' | 'internal';
|
||||
}
|
||||
@@ -1,183 +0,0 @@
|
||||
import { execSync } from 'node:child_process';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { join, resolve } from 'node:path';
|
||||
|
||||
import type { BuildFlags } from '@affine/cli/config';
|
||||
import { Repository } from '@napi-rs/simple-git';
|
||||
import HTMLPlugin from 'html-webpack-plugin';
|
||||
import { once } from 'lodash-es';
|
||||
import type { Compiler } from 'webpack';
|
||||
import webpack from 'webpack';
|
||||
import { merge } from 'webpack-merge';
|
||||
|
||||
import {
|
||||
createConfiguration,
|
||||
getPublicPath,
|
||||
rootPath,
|
||||
workspaceRoot,
|
||||
} from './config.js';
|
||||
import { getBuildConfig } from './runtime-config.js';
|
||||
|
||||
const DESCRIPTION = `There can be more than Notion and Miro. AFFiNE is a next-gen knowledge base that brings planning, sorting and creating all together.`;
|
||||
|
||||
const gitShortHash = once(() => {
|
||||
const { GITHUB_SHA } = process.env;
|
||||
if (GITHUB_SHA) {
|
||||
return GITHUB_SHA.substring(0, 9);
|
||||
}
|
||||
const repo = new Repository(workspaceRoot);
|
||||
const shortSha = repo.head().target()?.substring(0, 9);
|
||||
if (shortSha) {
|
||||
return shortSha;
|
||||
}
|
||||
const sha = execSync(`git rev-parse --short HEAD`, {
|
||||
encoding: 'utf-8',
|
||||
}).trim();
|
||||
return sha;
|
||||
});
|
||||
|
||||
export function createWebpackConfig(cwd: string, flags: BuildFlags) {
|
||||
console.log('build flags', flags);
|
||||
const runtimeConfig = getBuildConfig(flags);
|
||||
console.log('BUILD_CONFIG', runtimeConfig);
|
||||
const config = createConfiguration(cwd, flags, runtimeConfig);
|
||||
const entry =
|
||||
typeof flags.entry === 'string' || !flags.entry
|
||||
? {
|
||||
app: flags.entry ?? resolve(cwd, 'src/index.tsx'),
|
||||
}
|
||||
: flags.entry;
|
||||
|
||||
const publicPath = getPublicPath(flags);
|
||||
const cdnOrigin = publicPath.startsWith('/')
|
||||
? undefined
|
||||
: new URL(publicPath).origin;
|
||||
|
||||
const globalErrorHandler = [
|
||||
'js/global-error-handler.js',
|
||||
readFileSync(
|
||||
join(workspaceRoot, 'tools/cli/src/webpack/error-handler.js'),
|
||||
'utf-8'
|
||||
),
|
||||
];
|
||||
|
||||
const templateParams = {
|
||||
GIT_SHORT_SHA: gitShortHash(),
|
||||
DESCRIPTION,
|
||||
PRECONNECT: cdnOrigin
|
||||
? `<link rel="preconnect" href="${cdnOrigin}" />`
|
||||
: '',
|
||||
VIEWPORT_FIT:
|
||||
flags.distribution === 'mobile' ||
|
||||
flags.distribution === 'ios' ||
|
||||
flags.distribution === 'android'
|
||||
? 'cover'
|
||||
: 'auto',
|
||||
};
|
||||
|
||||
const createHTMLPlugins = (entryName: string) => {
|
||||
const htmlPluginOptions = {
|
||||
template: join(rootPath, 'webpack', 'template.html'),
|
||||
inject: 'body',
|
||||
filename: 'index.html',
|
||||
minify: false,
|
||||
templateParameters: templateParams,
|
||||
chunks: [entryName],
|
||||
} satisfies HTMLPlugin.Options;
|
||||
|
||||
if (entryName === 'app') {
|
||||
return [
|
||||
{
|
||||
apply(compiler: Compiler) {
|
||||
compiler.hooks.compilation.tap(
|
||||
'assets-manifest-plugin',
|
||||
compilation => {
|
||||
HTMLPlugin.getHooks(compilation).beforeAssetTagGeneration.tap(
|
||||
'assets-manifest-plugin',
|
||||
arg => {
|
||||
if (
|
||||
flags.distribution !== 'desktop' &&
|
||||
!compilation.getAsset(globalErrorHandler[0])
|
||||
) {
|
||||
compilation.emitAsset(
|
||||
globalErrorHandler[0],
|
||||
new webpack.sources.RawSource(globalErrorHandler[1])
|
||||
);
|
||||
arg.assets.js.unshift(
|
||||
arg.assets.publicPath + globalErrorHandler[0]
|
||||
);
|
||||
}
|
||||
|
||||
if (!compilation.getAsset('assets-manifest.json')) {
|
||||
compilation.emitAsset(
|
||||
globalErrorHandler[0],
|
||||
new webpack.sources.RawSource(globalErrorHandler[1])
|
||||
);
|
||||
compilation.emitAsset(
|
||||
`assets-manifest.json`,
|
||||
new webpack.sources.RawSource(
|
||||
JSON.stringify(
|
||||
{
|
||||
...arg.assets,
|
||||
js: arg.assets.js.map(file =>
|
||||
file.substring(arg.assets.publicPath.length)
|
||||
),
|
||||
css: arg.assets.css.map(file =>
|
||||
file.substring(arg.assets.publicPath.length)
|
||||
),
|
||||
gitHash: templateParams.GIT_SHORT_SHA,
|
||||
description: templateParams.DESCRIPTION,
|
||||
},
|
||||
null,
|
||||
2
|
||||
)
|
||||
),
|
||||
{
|
||||
immutable: false,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return arg;
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
},
|
||||
},
|
||||
new HTMLPlugin({
|
||||
...htmlPluginOptions,
|
||||
publicPath,
|
||||
meta: {
|
||||
'env:publicPath': publicPath,
|
||||
},
|
||||
}),
|
||||
// selfhost html
|
||||
new HTMLPlugin({
|
||||
...htmlPluginOptions,
|
||||
meta: {
|
||||
'env:isSelfHosted': 'true',
|
||||
'env:publicPath': '/',
|
||||
},
|
||||
filename: 'selfhost.html',
|
||||
templateParameters: {
|
||||
...htmlPluginOptions.templateParameters,
|
||||
PRECONNECT: '',
|
||||
},
|
||||
}),
|
||||
];
|
||||
} else {
|
||||
return [
|
||||
new HTMLPlugin({
|
||||
...htmlPluginOptions,
|
||||
filename: `${entryName}.html`,
|
||||
}),
|
||||
];
|
||||
}
|
||||
};
|
||||
|
||||
return merge(config, {
|
||||
entry,
|
||||
plugins: Object.keys(entry).map(createHTMLPlugins).flat(),
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user