mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-26 10:45:57 +08:00
feat: improve admin build (#14485)
#### PR Dependency Tree * **PR #14485** 👈 This tree was auto-generated by [Charcoal](https://github.com/danerwilliams/charcoal) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Chores** * Admin static assets now served under /admin for self-hosted installs * CLI is directly executable from the command line * Build tooling supports a configurable self-hosted public path * Updated admin package script for adding UI components * Added a PostCSS dependency and plugin to the build toolchain for admin builds * **Style** * Switched queue module to a local queuedash stylesheet, added queuedash Tailwind layer, and scoped queuedash styles for the admin UI * **Bug Fixes** * Improved error propagation in the Electron renderer * Migration compatibility to repair a legacy checksum during native storage upgrades * **Tests** * Added tests covering the migration repair flow <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
#!/usr/bin/env node
|
||||
import { spawnSync } from 'node:child_process';
|
||||
|
||||
spawnSync('yarn', ['r', 'affine.ts', ...process.argv.slice(2)], {
|
||||
|
||||
@@ -39,6 +39,7 @@
|
||||
"node-loader": "^2.1.0",
|
||||
"postcss": "^8.4.49",
|
||||
"postcss-loader": "^8.1.1",
|
||||
"postcss-selector-parser": "^7.1.0",
|
||||
"prettier": "^3.7.4",
|
||||
"react-refresh": "^0.17.0",
|
||||
"source-map-loader": "^5.0.0",
|
||||
|
||||
@@ -88,7 +88,11 @@ function getWebpackBundleConfigs(pkg: Package): webpack.MultiConfiguration {
|
||||
switch (pkg.name) {
|
||||
case '@affine/admin': {
|
||||
return [
|
||||
createWebpackHTMLTargetConfig(pkg, pkg.srcPath.join('index.tsx').value),
|
||||
createWebpackHTMLTargetConfig(
|
||||
pkg,
|
||||
pkg.srcPath.join('index.tsx').value,
|
||||
{ selfhostPublicPath: '/admin/' }
|
||||
),
|
||||
] as webpack.MultiConfiguration;
|
||||
}
|
||||
case '@affine/web':
|
||||
@@ -158,7 +162,9 @@ function getRspackBundleConfigs(pkg: Package): MultiRspackOptions {
|
||||
switch (pkg.name) {
|
||||
case '@affine/admin': {
|
||||
return [
|
||||
createRspackHTMLTargetConfig(pkg, pkg.srcPath.join('index.tsx').value),
|
||||
createRspackHTMLTargetConfig(pkg, pkg.srcPath.join('index.tsx').value, {
|
||||
selfhostPublicPath: '/admin/',
|
||||
}),
|
||||
] as MultiRspackOptions;
|
||||
}
|
||||
case '@affine/web':
|
||||
|
||||
111
tools/cli/src/postcss/queuedash-scope.ts
Normal file
111
tools/cli/src/postcss/queuedash-scope.ts
Normal file
@@ -0,0 +1,111 @@
|
||||
import type { AtRule, Container, Node, PluginCreator, Rule } from 'postcss';
|
||||
import selectorParser from 'postcss-selector-parser';
|
||||
|
||||
export interface QueuedashScopeOptions {
|
||||
scopeClass?: string;
|
||||
}
|
||||
|
||||
function normalizeFilePath(filePath: string) {
|
||||
return filePath.replaceAll('\\', '/').split('?')[0];
|
||||
}
|
||||
|
||||
function isInKeyframes(rule: Rule) {
|
||||
let parent: Node | undefined = rule.parent;
|
||||
while (parent) {
|
||||
if (parent.type === 'atrule') {
|
||||
const name = (parent as AtRule).name?.toLowerCase();
|
||||
if (name && name.endsWith('keyframes')) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
parent = parent.parent;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const DEFAULT_SCOPE_CLASS = 'affine-queuedash';
|
||||
|
||||
export const queuedashScopePostcssPlugin: PluginCreator<
|
||||
QueuedashScopeOptions
|
||||
> = (options = {}) => {
|
||||
const scopeClass = options.scopeClass ?? DEFAULT_SCOPE_CLASS;
|
||||
const scopeSelector = `:where(.${scopeClass})`;
|
||||
|
||||
const scopeAst = selectorParser().astSync(scopeSelector);
|
||||
const scopeNodes = scopeAst.nodes[0]?.nodes;
|
||||
|
||||
if (!scopeNodes) {
|
||||
throw new Error(
|
||||
`[queuedashScopePostcssPlugin] Failed to parse scope selector: ${scopeSelector}`
|
||||
);
|
||||
}
|
||||
|
||||
const scopeProcessor = selectorParser(selectors => {
|
||||
selectors.each(selector => {
|
||||
const raw = selector.toString().trim();
|
||||
|
||||
if (
|
||||
raw.startsWith(scopeSelector) ||
|
||||
raw.startsWith(`.${scopeClass}`) ||
|
||||
raw.startsWith(`:where(.${scopeClass})`)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
raw === 'html' ||
|
||||
raw === 'body' ||
|
||||
raw === ':host' ||
|
||||
raw === ':root'
|
||||
) {
|
||||
selector.nodes = scopeNodes.map(node => node.clone());
|
||||
return;
|
||||
}
|
||||
|
||||
const prefixNodes = scopeNodes.map(node => node.clone());
|
||||
const space = selectorParser.combinator({ value: ' ' });
|
||||
selector.nodes = [...prefixNodes, space, ...selector.nodes];
|
||||
});
|
||||
});
|
||||
|
||||
return {
|
||||
postcssPlugin: 'affine-queuedash-scope',
|
||||
Once(root, { result }) {
|
||||
const from =
|
||||
root.source?.input.file ||
|
||||
root.source?.input.from ||
|
||||
result.opts.from ||
|
||||
'';
|
||||
|
||||
const normalized = from ? normalizeFilePath(from) : '';
|
||||
const isQueuedashVendorCss = normalized.endsWith(
|
||||
'/@queuedash/ui/dist/styles.css'
|
||||
);
|
||||
|
||||
const queuedashLayers: AtRule[] = [];
|
||||
root.walkAtRules('layer', atRule => {
|
||||
if (atRule.params?.trim() === 'queuedash' && atRule.nodes?.length) {
|
||||
queuedashLayers.push(atRule);
|
||||
}
|
||||
});
|
||||
|
||||
if (!isQueuedashVendorCss && queuedashLayers.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const targets: Container[] =
|
||||
queuedashLayers.length > 0 ? queuedashLayers : [root];
|
||||
|
||||
targets.forEach(container => {
|
||||
container.walkRules(rule => {
|
||||
if (!rule.selector || isInKeyframes(rule)) {
|
||||
return;
|
||||
}
|
||||
rule.selector = scopeProcessor.processSync(rule.selector);
|
||||
});
|
||||
});
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
queuedashScopePostcssPlugin.postcss = true;
|
||||
@@ -12,6 +12,7 @@ import { VanillaExtractPlugin } from '@vanilla-extract/webpack-plugin';
|
||||
import cssnano from 'cssnano';
|
||||
import { compact, merge } from 'lodash-es';
|
||||
|
||||
import { queuedashScopePostcssPlugin } from '../postcss/queuedash-scope.js';
|
||||
import { productionCacheGroups } from '../webpack/cache-group.js';
|
||||
import {
|
||||
type CreateHTMLPluginConfig,
|
||||
@@ -228,6 +229,9 @@ export function createHTMLTargetConfig(
|
||||
require(pkg.join('tailwind.config.js').value),
|
||||
],
|
||||
['autoprefixer'],
|
||||
...(buildConfig.isAdmin
|
||||
? [queuedashScopePostcssPlugin()]
|
||||
: []),
|
||||
]
|
||||
: [
|
||||
cssnano({
|
||||
|
||||
@@ -79,6 +79,7 @@ const currentDir = Path.dir(import.meta.url);
|
||||
export interface CreateHTMLPluginConfig {
|
||||
filename?: string;
|
||||
additionalEntryForSelfhost?: boolean;
|
||||
selfhostPublicPath?: string;
|
||||
injectGlobalErrorHandler?: boolean;
|
||||
emitAssetsManifest?: boolean;
|
||||
}
|
||||
@@ -206,6 +207,7 @@ export function createHTMLPlugins(
|
||||
): WebpackPluginInstance[] {
|
||||
const publicPath = getPublicPath(BUILD_CONFIG);
|
||||
const htmlPluginOptions = getHTMLPluginOptions(BUILD_CONFIG);
|
||||
const selfhostPublicPath = config.selfhostPublicPath ?? '/';
|
||||
|
||||
const plugins: WebpackPluginInstance[] = [];
|
||||
plugins.push(
|
||||
@@ -269,9 +271,10 @@ export function createHTMLPlugins(
|
||||
new HTMLPlugin({
|
||||
...htmlPluginOptions,
|
||||
chunks: ['index'],
|
||||
publicPath: selfhostPublicPath,
|
||||
meta: {
|
||||
'env:isSelfHosted': 'true',
|
||||
'env:publicPath': '/',
|
||||
'env:publicPath': selfhostPublicPath,
|
||||
},
|
||||
filename: 'selfhost.html',
|
||||
templateParameters: {
|
||||
|
||||
@@ -13,6 +13,7 @@ import MiniCssExtractPlugin from 'mini-css-extract-plugin';
|
||||
import TerserPlugin from 'terser-webpack-plugin';
|
||||
import webpack from 'webpack';
|
||||
|
||||
import { queuedashScopePostcssPlugin } from '../postcss/queuedash-scope.js';
|
||||
import { productionCacheGroups } from './cache-group.js';
|
||||
import {
|
||||
type CreateHTMLPluginConfig,
|
||||
@@ -230,6 +231,9 @@ export function createHTMLTargetConfig(
|
||||
require(pkg.join('tailwind.config.js').value),
|
||||
],
|
||||
['autoprefixer'],
|
||||
...(buildConfig.isAdmin
|
||||
? [queuedashScopePostcssPlugin()]
|
||||
: []),
|
||||
]
|
||||
: [
|
||||
cssnano({
|
||||
|
||||
Reference in New Issue
Block a user