mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-12 15:46:29 +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:
@@ -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;
|
||||
Reference in New Issue
Block a user