feature: vor und nachteile

This commit is contained in:
Hikyu
2026-05-31 13:54:31 +02:00
parent 552a947a1c
commit e3dad17446
23270 changed files with 3024794 additions and 200 deletions
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2021-PRESENT Anthony Fu <https://github.com/antfu>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+14
View File
@@ -0,0 +1,14 @@
# @unocss/postcss
PostCSS plugin for UnoCSS. Supports `@apply`、`@screen` and `theme()` directives.
> **Warning**: Experimental
> This package is in an experimental state right now. It doesn't follow semver, and may introduce breaking changes in patch versions.
## Documentation
Please refer to the [documentation](https://unocss.dev/integrations/postcss).
## License
MIT License &copy; 2022-PRESENT [hannoeru](https://github.com/hannoeru) [sibbng](https://github.com/sibbng)
+21
View File
@@ -0,0 +1,21 @@
import { Root, Result } from 'postcss';
import { UserConfig } from '@unocss/core';
interface UnoPostcssPluginOptions {
content?: (string | {
raw: string;
extension: string;
})[];
directiveMap?: {
apply?: string;
screen?: string;
theme?: string;
unocss?: string;
};
cwd?: string;
configOrPath?: string | UserConfig;
}
declare function createPlugin(options: UnoPostcssPluginOptions): (root: Root, result: Result) => Promise<void>;
export { createPlugin };
+21
View File
@@ -0,0 +1,21 @@
import { Root, Result } from 'postcss';
import { UserConfig } from '@unocss/core';
interface UnoPostcssPluginOptions {
content?: (string | {
raw: string;
extension: string;
})[];
directiveMap?: {
apply?: string;
screen?: string;
theme?: string;
unocss?: string;
};
cwd?: string;
configOrPath?: string | UserConfig;
}
declare function createPlugin(options: UnoPostcssPluginOptions): (root: Root, result: Result) => Promise<void>;
export { createPlugin };
+281
View File
@@ -0,0 +1,281 @@
import { stat, readFile } from 'node:fs/promises';
import { normalize } from 'node:path';
import process from 'node:process';
import { createRecoveryConfigLoader } from '@unocss/config';
import { expandVariantGroup, notNull, regexScopePlaceholder, createGenerator } from '@unocss/core';
import { transformThemeFn, hasThemeFn } from '@unocss/rule-utils';
import postcss from 'postcss';
import { glob } from 'tinyglobby';
import { parse, generate, clone } from 'css-tree';
const defaultFilesystemGlobs = [
"**/*.{html,js,ts,jsx,tsx,vue,svelte,astro,elm,php,phtml,mdx,md}"
];
async function parseApply(root, uno, directiveName) {
const promises = [];
root.walkAtRules(directiveName, (rule) => {
promises.push((async () => {
if (!rule.parent)
return;
const source = rule.source;
const classNames = expandVariantGroup(rule.params).split(/\s+/g).map((className) => className.trim().replace(/\\/, ""));
const utils = (await Promise.all(
classNames.map((i) => uno.parseToken(i, "-"))
)).filter(notNull).flat().sort((a, b) => a[0] - b[0]).sort((a, b) => (a[3] ? uno.parentOrders.get(a[3]) ?? 0 : 0) - (b[3] ? uno.parentOrders.get(b[3]) ?? 0 : 0)).reduce((acc, item) => {
const target = acc.find((i) => i[1] === item[1] && i[3] === item[3]);
if (target)
target[2] += item[2];
else
acc.push([...item]);
return acc;
}, []);
if (!utils.length)
return;
const parentAfterNodes = [];
for (const i of utils) {
const [, _selector, body, parent] = i;
const selector = _selector?.replace(regexScopePlaceholder, " ") || _selector;
if (parent || selector && selector !== ".\\-") {
const node = parse(rule.parent.toString(), {
context: "rule"
});
let newSelector = generate(node.prelude);
if (selector && selector !== ".\\-") {
const selectorAST = parse(selector, {
context: "selector"
});
const prelude = clone(node.prelude);
prelude.children.forEach((child) => {
const parentSelectorAst = clone(selectorAST);
parentSelectorAst.children.forEach((i2) => {
if (i2.type === "ClassSelector" && i2.name === "\\-")
Object.assign(i2, clone(child));
});
Object.assign(child, parentSelectorAst);
});
newSelector = generate(prelude);
}
let css = `${newSelector}{${body}}`;
if (parent)
css = `${parent}{${css}}`;
const css_parsed = postcss.parse(css);
css_parsed.walkDecls((declaration) => {
declaration.source = source;
});
parentAfterNodes.push(...css_parsed.nodes);
} else {
const css = postcss.parse(body);
css.walkDecls((declaration) => {
declaration.source = source;
});
rule.parent.insertAfter(rule, css);
}
}
rule.parent.after(parentAfterNodes);
rule.remove();
})());
});
await Promise.all(promises);
}
async function parseScreen(root, uno, directiveName) {
root.walkAtRules(directiveName, async (rule) => {
let breakpointName = "";
let prefix = "";
if (rule.params)
breakpointName = rule.params.trim();
if (!breakpointName)
return;
const match = breakpointName.match(/^(?:(lt|at)-)?(\w+)$/);
if (match) {
prefix = match[1];
breakpointName = match[2];
}
const resolveBreakpoints = () => {
let breakpoints;
if (uno.userConfig && uno.userConfig.theme)
breakpoints = uno.userConfig.theme.breakpoints;
if (!breakpoints)
breakpoints = uno.config.theme.breakpoints;
return breakpoints ? Object.entries(breakpoints).sort((a, b) => Number.parseInt(a[1].replace(/[a-z]+/gi, "")) - Number.parseInt(b[1].replace(/[a-z]+/gi, ""))).map(([point, size]) => ({ point, size })) : void 0;
};
const variantEntries = (resolveBreakpoints() ?? []).map(({ point, size }, idx) => [point, size, idx]);
const generateMediaQuery = (breakpointName2, prefix2) => {
const [, size, idx] = variantEntries.find((i) => i[0] === breakpointName2);
if (prefix2) {
if (prefix2 === "lt")
return `(max-width: ${calcMaxWidthBySize(size)})`;
else if (prefix2 === "at")
return `(min-width: ${size})${variantEntries[idx + 1] ? ` and (max-width: ${calcMaxWidthBySize(variantEntries[idx + 1][1])})` : ""}`;
else throw new Error(`breakpoint variant not supported: ${prefix2}`);
}
return `(min-width: ${size})`;
};
if (!variantEntries.find((i) => i[0] === breakpointName))
throw new Error(`breakpoint ${breakpointName} not found`);
rule.name = "media";
rule.params = `${generateMediaQuery(breakpointName, prefix)}`;
});
}
function calcMaxWidthBySize(size) {
const value = size.match(/^-?\d+\.?\d*/)?.[0] || "";
const unit = size.slice(value.length);
const maxWidth = Number.parseFloat(value) - 0.1;
return Number.isNaN(maxWidth) ? size : `${maxWidth}${unit}`;
}
async function parseTheme(root, uno) {
root.walkDecls((decl) => {
decl.value = transformThemeFn(decl.value, uno.config.theme);
});
}
function createPlugin(options) {
const {
cwd = process.cwd(),
configOrPath
} = options;
const directiveMap = Object.assign({
apply: "apply",
theme: "theme",
screen: "screen",
unocss: "unocss"
}, options.directiveMap || {});
const fileMap = /* @__PURE__ */ new Map();
const fileClassMap = /* @__PURE__ */ new Map();
const classes = /* @__PURE__ */ new Set();
const targetCache = /* @__PURE__ */ new Set();
const loadConfig = createRecoveryConfigLoader();
const config = loadConfig(cwd, configOrPath);
let uno;
let promises = [];
let last_config_mtime = 0;
const targetRE = new RegExp(Object.values(directiveMap).join("|"));
return async function plugin(root, result) {
const from = result.opts.from?.split("?")[0];
if (!from)
return;
let isTarget = targetCache.has(from);
const isScanTarget = root.toString().includes(`@${directiveMap.unocss}`);
if (targetRE.test(root.toString())) {
if (!isTarget) {
root.walkAtRules((rule) => {
if (rule.name === directiveMap.unocss || rule.name === directiveMap.apply || rule.name === directiveMap.screen) {
isTarget = true;
}
if (isTarget)
return false;
});
if (!isTarget) {
root.walkDecls((decl) => {
if (hasThemeFn(decl.value)) {
isTarget = true;
return false;
}
});
} else {
targetCache.add(from);
}
}
} else if (targetCache.has(from)) {
targetCache.delete(from);
}
if (!isTarget)
return;
try {
const cfg = await config;
if (!uno) {
uno = await createGenerator(cfg.config);
} else if (cfg.sources.length) {
const config_mtime = (await stat(cfg.sources[0])).mtimeMs;
if (config_mtime > last_config_mtime) {
uno = await createGenerator((await loadConfig()).config);
last_config_mtime = config_mtime;
}
}
} catch (error) {
throw new Error(`UnoCSS config not found: ${error.message}`);
}
const globs = uno.config.content?.filesystem ?? defaultFilesystemGlobs;
const plainContent = uno.config.content?.inline ?? [];
const entries = await glob(isScanTarget ? globs : [from], {
cwd,
absolute: true,
ignore: ["**/node_modules/**"],
expandDirectories: false
});
await parseApply(root, uno, directiveMap.apply);
await parseTheme(root, uno);
await parseScreen(root, uno, directiveMap.screen);
promises.push(
...plainContent.map(async (c2, idx) => {
if (typeof c2 === "function")
c2 = await c2();
if (typeof c2 === "string")
c2 = { code: c2 };
const { matched } = await uno.generate(c2.code, { id: c2.id ?? `__plain_content_${idx}__` });
for (const candidate of matched)
classes.add(candidate);
})
);
await Promise.all(promises);
promises = [];
const BATCH_SIZE = 500;
for (let i = 0; i < entries.length; i += BATCH_SIZE) {
const batch = entries.slice(i, i + BATCH_SIZE);
promises.push(...batch.map(async (file) => {
result.messages.push({
type: "dependency",
plugin: directiveMap.unocss,
file: normalize(file),
parent: from
});
const { mtimeMs } = await stat(file);
if (fileMap.has(file) && mtimeMs <= fileMap.get(file))
return;
fileMap.set(file, mtimeMs);
const content = await readFile(file, "utf8");
const { matched } = await uno.generate(content, {
id: file
});
fileClassMap.set(file, matched);
}));
await Promise.all(promises);
promises = [];
}
for (const set of fileClassMap.values()) {
for (const candidate of set)
classes.add(candidate);
}
const c = await uno.generate(classes);
classes.clear();
const excludes = [];
root.walkAtRules(directiveMap.unocss, (rule) => {
if (rule.params) {
const source = rule.source;
const layers = rule.params.split(",").map((v) => v.trim());
const css = postcss.parse(
layers.map((i) => (i === "all" ? c.getLayers() : c.getLayer(i)) || "").filter(Boolean).join("\n")
);
css.walkDecls((declaration) => {
declaration.source = source;
});
rule.replaceWith(css);
excludes.push(rule.params);
}
});
root.walkAtRules(directiveMap.unocss, (rule) => {
if (!rule.params) {
const source = rule.source;
const css = postcss.parse(c.getLayers(void 0, excludes) || "");
css.walkDecls((declaration) => {
declaration.source = source;
});
rule.replaceWith(css);
}
});
};
}
export { createPlugin };
+19
View File
@@ -0,0 +1,19 @@
'use strict';
function unocss(options = {}) {
let promise;
return {
postcssPlugin: options.directiveMap?.unocss || "unocss",
plugins: [
async (root, result) => {
if (!promise)
promise = import('@unocss/postcss/esm').then((r) => r.createPlugin(options));
return await (await promise)(root, result);
}
]
};
}
unocss.postcss = true;
unocss.default = unocss;
module.exports = unocss;
+30
View File
@@ -0,0 +1,30 @@
import { Root, Result } from 'postcss';
import { UserConfig } from '@unocss/core';
interface UnoPostcssPluginOptions {
content?: (string | {
raw: string;
extension: string;
})[];
directiveMap?: {
apply?: string;
screen?: string;
theme?: string;
unocss?: string;
};
cwd?: string;
configOrPath?: string | UserConfig;
}
declare function unocss(options?: UnoPostcssPluginOptions): {
postcssPlugin: string;
plugins: ((root: Root, result: Result) => Promise<void>)[];
};
declare namespace unocss {
export var postcss: boolean;
var _a: typeof unocss;
export { _a as default };
}
export = unocss;
export { type UnoPostcssPluginOptions };
+29
View File
@@ -0,0 +1,29 @@
import { Root, Result } from 'postcss';
import { UserConfig } from '@unocss/core';
interface UnoPostcssPluginOptions {
content?: (string | {
raw: string;
extension: string;
})[];
directiveMap?: {
apply?: string;
screen?: string;
theme?: string;
unocss?: string;
};
cwd?: string;
configOrPath?: string | UserConfig;
}
declare function unocss(options?: UnoPostcssPluginOptions): {
postcssPlugin: string;
plugins: ((root: Root, result: Result) => Promise<void>)[];
};
declare namespace unocss {
export var postcss: boolean;
var _a: typeof unocss;
export { _a as default };
}
export { type UnoPostcssPluginOptions, unocss as default };
+30
View File
@@ -0,0 +1,30 @@
import { Root, Result } from 'postcss';
import { UserConfig } from '@unocss/core';
interface UnoPostcssPluginOptions {
content?: (string | {
raw: string;
extension: string;
})[];
directiveMap?: {
apply?: string;
screen?: string;
theme?: string;
unocss?: string;
};
cwd?: string;
configOrPath?: string | UserConfig;
}
declare function unocss(options?: UnoPostcssPluginOptions): {
postcssPlugin: string;
plugins: ((root: Root, result: Result) => Promise<void>)[];
};
declare namespace unocss {
export var postcss: boolean;
var _a: typeof unocss;
export { _a as default };
}
export = unocss;
export { type UnoPostcssPluginOptions };
+17
View File
@@ -0,0 +1,17 @@
function unocss(options = {}) {
let promise;
return {
postcssPlugin: options.directiveMap?.unocss || "unocss",
plugins: [
async (root, result) => {
if (!promise)
promise = import('@unocss/postcss/esm').then((r) => r.createPlugin(options));
return await (await promise)(root, result);
}
]
};
}
unocss.postcss = true;
unocss.default = unocss;
export { unocss as default };
+69
View File
@@ -0,0 +1,69 @@
{
"name": "@unocss/postcss",
"type": "module",
"version": "0.65.4",
"description": "PostCSS plugin for UnoCSS",
"author": "sibbng <sibbngheid@gmail.com>",
"license": "MIT",
"funding": "https://github.com/sponsors/antfu",
"homepage": "https://github.com/unocss/unocss/tree/main/packages/postcss#readme",
"repository": {
"type": "git",
"url": "https://github.com/unocss/unocss",
"directory": "packages/postcss"
},
"bugs": {
"url": "https://github.com/unocss/unocss/issues"
},
"keywords": [],
"sideEffects": false,
"exports": {
".": {
"import": {
"types": "./dist/index.d.mts",
"default": "./dist/index.mjs"
},
"require": {
"types": "./dist/index.d.cts",
"default": "./dist/index.cjs"
}
},
"./esm": {
"types": "./dist/esm.d.mts",
"default": "./dist/esm.mjs"
}
},
"main": "dist/index.cjs",
"module": "dist/index.mjs",
"types": "dist/index.d.ts",
"typesVersions": {
"*": {
"*": [
"dist/*",
"./*"
]
}
},
"files": [
"dist"
],
"engines": {
"node": ">=14"
},
"peerDependencies": {
"postcss": "^8.4.21"
},
"dependencies": {
"css-tree": "^3.1.0",
"postcss": "^8.4.49",
"tinyglobby": "^0.2.10",
"@unocss/rule-utils": "0.65.4",
"@unocss/core": "0.65.4",
"@unocss/config": "0.65.4"
},
"scripts": {
"build": "unbuild",
"stub": "unbuild --stub",
"test:attw": "attw --pack --config-path ../../.attw-esm-only.json"
}
}