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.
+11
View File
@@ -0,0 +1,11 @@
# @unocss/inspector
The inspector UI for UnoCSS. Ships with `unocss` and `@unocss/vite`.
## Documentation
Please refer to the [documentation](https://unocss.dev/tools/inspector).
## License
MIT License &copy; 2021-PRESENT [Anthony Fu](https://github.com/antfu)
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,3 @@
import{d as x,r as S,u as b,a as k,b as V,f as B,o as y,c as w,e as s,g as a,w as p,h as C,i as _,v as R,j as l,k as P,n as f,_ as E,l as N,m as U,p as $,q as I}from"./index-CQcRy5Wf.js";const L={"h-full":"",grid:"~ rows-[max-content_1fr]","of-hidden":""},M={"h-full":"","of-hidden":"",grid:"","grid-cols-2":""},T=x({__name:"ReplPlayground",setup(v){const r=S(null),t=b(r,"repl-scrolls"),o=k("unocss:inspector:repl",`<div class="text-sm hover:text-red">
Hello World
</div>`),n=V("unocss-inspector-safelist",!1),{data:c}=B(o,n);return(z,e)=>{var i,m;const g=E,h=N,u=U;return y(),w("div",L,[s("div",{ref_key:"status",ref:r},[a(g,null,{default:p(()=>e[2]||(e[2]=[s("div",null," REPL Playground ",-1),s("div",{op60:""}," Edit your code below to test and play UnoCSS's matching and generating. ",-1)])),_:1}),a(h,{border:"b gray-400/20",title:""},{default:p(()=>[s("label",null,[C(s("input",{"onUpdate:modelValue":e[0]||(e[0]=d=>_(n)?n.value=d:null),type:"checkbox"},null,512),[[R,l(n)]]),e[3]||(e[3]=P(" Include safelist "))])]),_:1})],512),s("div",M,[a(u,{modelValue:l(o),"onUpdate:modelValue":e[1]||(e[1]=d=>_(o)?o.value=d:null),mode:"html",matched:((i=l(c))==null?void 0:i.matched)||[],class:"scrolls repl-scrolls",style:f(l(t))},null,8,["modelValue","matched","style"]),a(u,{border:"l main","model-value":((m=l(c))==null?void 0:m.css)||"/* empty */","read-only":!0,mode:"css",class:"scrolls repl-scrolls",style:f(l(t))},null,8,["model-value","style"])])])}}}),j={};function q(v,r){const t=T;return y(),I(t)}const H=$(j,[["render",q]]);export{H as default};
+5
View File
@@ -0,0 +1,5 @@
<svg width="220" height="220" viewBox="0 0 220 220" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M117.444 167.888C117.444 140.273 139.83 117.888 167.444 117.888V117.888C195.058 117.888 217.444 140.273 217.444 167.888V167.888C217.444 195.502 195.058 217.888 167.444 217.888V217.888C139.83 217.888 117.444 195.502 117.444 167.888V167.888Z" fill="#858585"/>
<path d="M117.444 53C117.444 25.3858 139.83 3 167.444 3V3C195.058 3 217.444 25.3858 217.444 53V98C217.444 100.761 215.205 103 212.444 103H122.444C119.683 103 117.444 100.761 117.444 98V53Z" fill="#CCCCCC"/>
<path d="M102 167.888C102 195.502 79.6142 217.888 52 217.888V217.888C24.3858 217.888 2 195.502 2 167.888L2.00001 122.888C2.00001 120.126 4.23859 117.888 7.00001 117.888L97 117.888C99.7614 117.888 102 120.126 102 122.888L102 167.888Z" fill="#4D4D4D"/>
</svg>

After

Width:  |  Height:  |  Size: 832 B

+17
View File
@@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>UnoCSS Inspector</title>
<link rel="icon" href="/__unocss/favicon.svg" type="image/svg+xml">
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@100;200;400&family=Fira+Code&display=swap" rel="stylesheet" />
<script type="module" crossorigin src="/__unocss/assets/index-CQcRy5Wf.js"></script>
<link rel="stylesheet" crossorigin href="/__unocss/assets/index-qBg2eJVp.css">
</head>
<body>
<div id="app"></div>
</body>
</html>
+6
View File
@@ -0,0 +1,6 @@
import { UnocssPluginContext } from '@unocss/core';
import { Plugin } from 'vite';
declare function UnocssInspector(ctx: UnocssPluginContext): Plugin;
export { UnocssInspector as default };
+6
View File
@@ -0,0 +1,6 @@
import { UnocssPluginContext } from '@unocss/core';
import { Plugin } from 'vite';
declare function UnocssInspector(ctx: UnocssPluginContext): Plugin;
export { UnocssInspector as default };
+929
View File
@@ -0,0 +1,929 @@
import { dirname, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';
import { escapeSelector, CountableSet, BetterMap } from '@unocss/core';
import { green, bold, cyan } from 'colorette';
import gzipSize from 'gzip-size';
import sirv from 'sirv';
import { createValueHandler, parseCssColor, getStringComponent } from '@unocss/rule-utils';
const SKIP_START_COMMENT = "@unocss-skip-start";
const SKIP_END_COMMENT = "@unocss-skip-end";
const SKIP_COMMENT_RE = new RegExp(`(//\\s*?${SKIP_START_COMMENT}\\s*?|\\/\\*\\s*?${SKIP_START_COMMENT}\\s*?\\*\\/|<!--\\s*?${SKIP_START_COMMENT}\\s*?-->)[\\s\\S]*?(//\\s*?${SKIP_END_COMMENT}\\s*?|\\/\\*\\s*?${SKIP_END_COMMENT}\\s*?\\*\\/|<!--\\s*?${SKIP_END_COMMENT}\\s*?-->)`, "g");
const basePositionMap = [
"top",
"top center",
"top left",
"top right",
"bottom",
"bottom center",
"bottom left",
"bottom right",
"left",
"left center",
"left top",
"left bottom",
"right",
"right center",
"right top",
"right bottom",
"center",
"center top",
"center bottom",
"center left",
"center right",
"center center"
];
Object.assign(
{},
...basePositionMap.map((p) => ({ [p.replace(/ /, "-")]: p })),
...basePositionMap.map((p) => ({ [p.replace(/\b(\w)\w+/g, "$1").replace(/ /, "")]: p }))
);
const globalKeywords = [
"inherit",
"initial",
"revert",
"revert-layer",
"unset"
];
const numberWithUnitRE = /^(-?\d*(?:\.\d+)?)(px|pt|pc|%|r?(?:em|ex|lh|cap|ch|ic)|(?:[sld]?v|cq)(?:[whib]|min|max)|in|cm|mm|rpx)?$/i;
const numberRE = /^(-?\d*(?:\.\d+)?)$/;
const unitOnlyRE = /^(px|[sld]?v[wh])$/i;
const unitOnlyMap = {
px: 1,
vw: 100,
vh: 100,
svw: 100,
svh: 100,
dvw: 100,
dvh: 100,
lvh: 100,
lvw: 100
};
const bracketTypeRe = /^\[(color|image|length|size|position|quoted|string):/i;
const cssProps = [
// basic props
"color",
"border-color",
"background-color",
"outline-color",
"text-decoration-color",
"flex-grow",
"flex",
"flex-shrink",
"caret-color",
"font",
"gap",
"opacity",
"visibility",
"z-index",
"font-weight",
"zoom",
"text-shadow",
"transform",
"box-shadow",
"border",
// positions
"background-position",
"left",
"right",
"top",
"bottom",
"object-position",
// sizes
"max-height",
"min-height",
"max-width",
"min-width",
"height",
"width",
"border-width",
"margin",
"padding",
"outline-width",
"outline-offset",
"font-size",
"line-height",
"text-indent",
"vertical-align",
"border-spacing",
"letter-spacing",
"word-spacing",
// enhances
"stroke",
"filter",
"backdrop-filter",
"fill",
"mask",
"mask-size",
"mask-border",
"clip-path",
"clip",
"border-radius"
];
function round(n) {
return +n.toFixed(10);
}
function numberWithUnit(str) {
const match = str.match(numberWithUnitRE);
if (!match)
return;
const [, n, unit] = match;
const num = Number.parseFloat(n);
if (unit && !Number.isNaN(num))
return `${round(num)}${unit}`;
}
function auto(str) {
if (str === "auto" || str === "a")
return "auto";
}
function rem(str) {
if (!str)
return;
if (unitOnlyRE.test(str))
return `${unitOnlyMap[str]}${str}`;
const match = str.match(numberWithUnitRE);
if (!match)
return;
const [, n, unit] = match;
const num = Number.parseFloat(n);
if (!Number.isNaN(num)) {
if (num === 0)
return "0";
return unit ? `${round(num)}${unit}` : `${round(num / 4)}rem`;
}
}
function px(str) {
if (unitOnlyRE.test(str))
return `${unitOnlyMap[str]}${str}`;
const match = str.match(numberWithUnitRE);
if (!match)
return;
const [, n, unit] = match;
const num = Number.parseFloat(n);
if (!Number.isNaN(num))
return unit ? `${round(num)}${unit}` : `${round(num)}px`;
}
function number(str) {
if (!numberRE.test(str))
return;
const num = Number.parseFloat(str);
if (!Number.isNaN(num))
return round(num);
}
function percent(str) {
if (str.endsWith("%"))
str = str.slice(0, -1);
if (!numberRE.test(str))
return;
const num = Number.parseFloat(str);
if (!Number.isNaN(num))
return `${round(num / 100)}`;
}
function fraction(str) {
if (!str)
return;
if (str === "full")
return "100%";
const [left, right] = str.split("/");
const num = Number.parseFloat(left) / Number.parseFloat(right);
if (!Number.isNaN(num)) {
if (num === 0)
return "0";
return `${round(num * 100)}%`;
}
}
function bracketWithType(str, requiredType) {
if (str && str.startsWith("[") && str.endsWith("]")) {
let base;
let hintedType;
const match = str.match(bracketTypeRe);
if (!match) {
base = str.slice(1, -1);
} else {
if (!requiredType)
hintedType = match[1];
base = str.slice(match[0].length, -1);
}
if (!base)
return;
if (base === '=""')
return;
if (base.startsWith("--"))
base = `var(${base})`;
let curly = 0;
for (const i of base) {
if (i === "[") {
curly += 1;
} else if (i === "]") {
curly -= 1;
if (curly < 0)
return;
}
}
if (curly)
return;
switch (hintedType) {
case "string":
return base.replace(/(^|[^\\])_/g, "$1 ").replace(/\\_/g, "_");
case "quoted":
return base.replace(/(^|[^\\])_/g, "$1 ").replace(/\\_/g, "_").replace(/(["\\])/g, "\\$1").replace(/^(.+)$/, '"$1"');
}
return base.replace(/(url\(.*?\))/g, (v) => v.replace(/_/g, "\\_")).replace(/(^|[^\\])_/g, "$1 ").replace(/\\_/g, "_").replace(/(?:calc|clamp|max|min)\((.*)/g, (match2) => {
const vars = [];
return match2.replace(/var\((--.+?)[,)]/g, (match3, g1) => {
vars.push(g1);
return match3.replace(g1, "--un-calc");
}).replace(/(-?\d*\.?\d(?!-\d.+[,)](?![^+\-/*])\D)(?:%|[a-z]+)?|\))([+\-/*])/g, "$1 $2 ").replace(/--un-calc/g, () => vars.shift());
});
}
}
function bracket(str) {
return bracketWithType(str);
}
function bracketOfColor(str) {
return bracketWithType(str, "color");
}
function bracketOfLength(str) {
return bracketWithType(str, "length");
}
function bracketOfPosition(str) {
return bracketWithType(str, "position");
}
function cssvar(str) {
if (/^\$[^\s'"`;{}]/.test(str)) {
const [name, defaultValue] = str.slice(1).split(",");
return `var(--${escapeSelector(name)}${defaultValue ? `, ${defaultValue}` : ""})`;
}
}
function time(str) {
const match = str.match(/^(-?[0-9.]+)(s|ms)?$/i);
if (!match)
return;
const [, n, unit] = match;
const num = Number.parseFloat(n);
if (!Number.isNaN(num)) {
if (num === 0 && !unit)
return "0s";
return unit ? `${round(num)}${unit}` : `${round(num)}ms`;
}
}
function degree(str) {
const match = str.match(/^(-?[0-9.]+)(deg|rad|grad|turn)?$/i);
if (!match)
return;
const [, n, unit] = match;
const num = Number.parseFloat(n);
if (!Number.isNaN(num)) {
if (num === 0)
return "0";
return unit ? `${round(num)}${unit}` : `${round(num)}deg`;
}
}
function global(str) {
if (globalKeywords.includes(str))
return str;
}
function properties(str) {
if (str.split(",").every((prop) => cssProps.includes(prop)))
return str;
}
function position(str) {
if (["top", "left", "right", "bottom", "center"].includes(str))
return str;
}
const valueHandlers = {
__proto__: null,
auto: auto,
bracket: bracket,
bracketOfColor: bracketOfColor,
bracketOfLength: bracketOfLength,
bracketOfPosition: bracketOfPosition,
cssvar: cssvar,
degree: degree,
fraction: fraction,
global: global,
number: number,
numberWithUnit: numberWithUnit,
percent: percent,
position: position,
properties: properties,
px: px,
rem: rem,
time: time
};
const handler = createValueHandler(valueHandlers);
const h = handler;
function getThemeColorForKey(theme, colors, key = "colors") {
let obj = theme[key];
let index = -1;
for (const c of colors) {
index += 1;
if (obj && typeof obj !== "string") {
const camel = colors.slice(index).join("-").replace(/(-[a-z])/g, (n) => n.slice(1).toUpperCase());
if (obj[camel])
return obj[camel];
if (obj[c]) {
obj = obj[c];
continue;
}
}
return void 0;
}
return obj;
}
function getThemeColor(theme, colors, key) {
return getThemeColorForKey(theme, colors, key) || getThemeColorForKey(theme, colors, "colors");
}
function splitShorthand(body, type) {
const [front, rest] = getStringComponent(body, "[", "]", ["/", ":"]) ?? [];
if (front != null) {
const match = (front.match(bracketTypeRe) ?? [])[1];
if (match == null || match === type)
return [front, rest];
}
}
function parseColor(body, theme, key) {
const split = splitShorthand(body, "color");
if (!split)
return;
const [main, opacity] = split;
const colors = main.replace(/([a-z])(\d)/g, "$1-$2").split(/-/g);
const [name] = colors;
if (!name)
return;
let color;
const bracket = h.bracketOfColor(main);
const bracketOrMain = bracket || main;
if (h.numberWithUnit(bracketOrMain))
return;
if (/^#[\da-f]+$/i.test(bracketOrMain))
color = bracketOrMain;
else if (/^hex-[\da-fA-F]+$/.test(bracketOrMain))
color = `#${bracketOrMain.slice(4)}`;
else if (main.startsWith("$"))
color = h.cssvar(main);
color = color || bracket;
if (!color) {
const colorData = getThemeColor(theme, [main], key);
if (typeof colorData === "string")
color = colorData;
}
let no = "DEFAULT";
if (!color) {
let keys = colors;
let _no;
const [scale] = colors.slice(-1);
if (/^\d+$/.test(scale)) {
no = _no = scale;
keys = colors.slice(0, -1);
}
const colorData = getThemeColor(theme, keys, key);
if (typeof colorData === "object") {
color = colorData[_no ?? no];
} else if (typeof colorData === "string" && !_no) {
color = colorData;
}
}
return {
opacity,
name,
no,
color,
cssColor: parseCssColor(color),
alpha: h.bracket.cssvar.percent(opacity ?? "")
};
}
const staticUtilities = {
"box-border": "boxSizing",
"box-content": "boxSizing",
"b": "border",
"border": "border",
"rounded": "border",
"block": "display",
"inline-block": "display",
"inline": "display",
"flex": "display",
"inline-flex": "display",
"table": "display",
"table-caption": "display",
"table-cell": "display",
"table-column": "display",
"table-column-group": "display",
"table-footer-group": "display",
"table-header-group": "display",
"table-row-group": "display",
"table-row": "display",
"flow-root": "display",
"grid": "display",
"inline-grid": "display",
"contents": "display",
"hidden": "display",
"float-right": "float",
"float-left": "float",
"float-none": "float",
"clear-left": "clear",
"clear-right": "clear",
"clear-both": "clear",
"clear-none": "clear",
"object-contain": "objectFit",
"object-cover": "objectFit",
"object-fill": "objectFit",
"object-none": "objectFit",
"object-scale-down": "objectFit",
"overflow-auto": "overflow",
"overflow-hidden": "overflow",
"overflow-visible": "overflow",
"overflow-scroll": "overflow",
"overflow-x-auto": "overflow",
"overflow-y-auto": "overflow",
"overflow-x-hidden": "overflow",
"overflow-y-hidden": "overflow",
"overflow-x-visible": "overflow",
"overflow-y-visible": "overflow",
"overflow-x-scroll": "overflow",
"overflow-y-scroll": "overflow",
"of-auto": "overflow",
"of-hidden": "overflow",
"of-visible": "overflow",
"of-scroll": "overflow",
"of-x-auto": "overflow",
"of-y-auto": "overflow",
"of-x-hidden": "overflow",
"of-y-hidden": "overflow",
"of-x-visible": "overflow",
"of-y-visible": "overflow",
"of-x-scroll": "overflow",
"of-y-scroll": "overflow",
"overscroll-auto": "overscrollBehavior",
"overscroll-contain": "overscrollBehavior",
"overscroll-none": "overscrollBehavior",
"overscroll-y-auto": "overscrollBehavior",
"overscroll-y-contain": "overscrollBehavior",
"overscroll-y-none": "overscrollBehavior",
"overscroll-x-auto": "overscrollBehavior",
"overscroll-x-contain": "overscrollBehavior",
"overscroll-x-none": "overscrollBehavior",
"static": "position",
"fixed": "position",
"absolute": "position",
"relative": "position",
"sticky": "position",
"visible": "visibility",
"invisible": "visibility",
"flex-row": "flex",
"flex-row-reverse": "flex",
"flex-col": "flex",
"flex-col-reverse": "flex",
"flex-wrap": "flex",
"flex-wrap-reverse": "flex",
"flex-nowrap": "flex",
"col-auto": "grid",
"row-auto": "grid",
"grid-flow-row": "grid",
"grid-flow-col": "grid",
"grid-flow-row-dense": "grid",
"grid-flow-col-dense": "grid",
"justify-start": "justifyContent",
"justify-end": "justifyContent",
"justify-center": "justifyContent",
"justify-between": "justifyContent",
"justify-around": "justifyContent",
"justify-evenly": "justifyContent",
"justify-left": "justifyContent",
"justify-right": "justifyContent",
"justify-items-auto": "justifyItems",
"justify-items-start": "justifyItems",
"justify-items-end": "justifyItems",
"justify-items-center": "justifyItems",
"justify-items-stretch": "justifyItems",
"justify-self-auto": "justifySelf",
"justify-self-start": "justifySelf",
"justify-self-end": "justifySelf",
"justify-self-center": "justifySelf",
"justify-self-stretch": "justifySelf",
"content-center": "alignContent",
"content-start": "alignContent",
"content-end": "alignContent",
"content-between": "alignContent",
"content-around": "alignContent",
"content-evenly": "alignContent",
"items-start": "alignItems",
"items-end": "alignItems",
"items-center": "alignItems",
"items-baseline": "alignItems",
"items-stretch": "alignItems",
"self-auto": "alignSelf",
"self-start": "alignSelf",
"self-end": "alignSelf",
"self-center": "alignSelf",
"self-stretch": "alignSelf",
"place-content-center": "placeContent",
"place-content-start": "placeContent",
"place-content-end": "placeContent",
"place-content-between": "placeContent",
"place-content-around": "placeContent",
"place-content-evenly": "placeContent",
"place-content-stretch": "placeContent",
"place-items-auto": "placeItems",
"place-items-start": "placeItems",
"place-items-end": "placeItems",
"place-items-center": "placeItems",
"place-items-stretch": "placeItems",
"place-self-auto": "placeSelf",
"place-self-start": "placeSelf",
"place-self-end": "placeSelf",
"place-self-center": "placeSelf",
"place-self-stretch": "placeSelf",
"antialiased": "fontSmoothing",
"subpixel-antialiased": "font",
"italic": "font",
"not-italic": "font",
"normal-nums": "font",
"ordinal": "font",
"slashed-zero": "font",
"lining-nums": "font",
"oldstyle-nums": "font",
"proportional-nums": "font",
"tabular-nums": "font",
"diagonal-fractions": "font",
"stacked-fractions": "font",
"list-inside": "listStylePosition",
"list-outside": "listStylePosition",
"text-left": "textAlign",
"text-center": "textAlign",
"text-right": "textAlign",
"text-justify": "textAlign",
"underline": "textDecoration",
"line-through": "textDecoration",
"no-underline": "textDecoration",
"uppercase": "textTransform",
"lowercase": "textTransform",
"capitalize": "textTransform",
"normal-case": "textTransform",
"truncate": "textOverflow",
"overflow-ellipsis": "textOverflow",
"overflow-clip": "textOverflow",
"align-baseline": "verticalAlign",
"align-top": "verticalAlign",
"align-middle": "verticalAlign",
"align-bottom": "verticalAlign",
"align-text-top": "verticalAlign",
"align-text-bottom": "verticalAlign",
"whitespace-normal": "whitespace",
"whitespace-nowrap": "whitespace",
"whitespace-pre": "whitespace",
"whitespace-pre-line": "whitespace",
"whitespace-pre-wrap": "whitespace",
"ws-normal": "whitespace",
"ws-nowrap": "whitespace",
"ws-pre": "whitespace",
"ws-pre-line": "whitespace",
"ws-pre-wrap": "whitespace",
"break-normal": "wordBreak",
"break-words": "wordBreak",
"break-all": "wordBreak",
"bg-fixed": "background",
"bg-local": "background",
"bg-scroll": "background",
"bg-clip-border": "background",
"bg-clip-padding": "background",
"bg-clip-content": "background",
"bg-clip-text": "background",
"bg-repeat": "background",
"bg-no-repeat": "background",
"bg-repeat-x": "background",
"bg-repeat-y": "background",
"bg-repeat-round": "background",
"bg-repeat-space": "background",
"border-solid": "border",
"border-dashed": "border",
"border-dotted": "border",
"border-double": "border",
"border-none": "border",
"border-collapse": "border",
"border-separate": "border",
"table-auto": "table",
"table-fixed": "table",
"transform": "transform",
"transform-gpu": "transform",
"transform-none": "transform",
"appearance-none": "appearance",
"pointer-events-none": "pointerEvents",
"pointer-events-auto": "pointerEvents",
"resize-none": "resize",
"resize-y": "resize",
"resize-x": "resize",
"resize": "resize",
"select-none": "userSelect",
"select-text": "userSelect",
"select-all": "userSelect",
"select-auto": "userSelect",
"fill-current": "fill",
"stroke-current": "stroke",
"sr-only": "accessibility",
"not-sr-only": "accessibility",
"filter": "filter",
"invert": "filter"
};
const dynamicUtilities = {
container: "container",
space: "space",
divide: "divide",
bg: "background",
from: "gradientColor",
via: "gradientColor",
to: "gradientColor",
border: "border",
b: "border",
rounded: "borderRadius",
cursor: "cursor",
flex: "flex",
shrink: "flex",
order: "order",
font: "font",
h: "size",
leading: "lineHeight",
list: "listStyleType",
m: "margin",
my: "margin",
mx: "margin",
mt: "margin",
mr: "margin",
mb: "margin",
ml: "margin",
min: "size",
max: "size",
object: "objectPosition",
op: "opacity",
opacity: "opacity",
outline: "outline",
p: "padding",
py: "padding",
px: "padding",
pt: "padding",
pr: "padding",
pb: "padding",
pl: "padding",
placeholder: "placeholder",
inset: "inset",
top: "position",
right: "position",
bottom: "position",
left: "position",
shadow: "boxShadow",
ring: "ring",
fill: "fill",
stroke: "stroke",
text: "text",
tracking: "letterSpacing",
w: "size",
z: "zIndex",
gap: "gap",
auto: "grid",
grid: "grid",
col: "grid",
row: "grid",
origin: "transform",
scale: "transform",
rotate: "transform",
translate: "transform",
skew: "transform",
transition: "animation",
ease: "animation",
duration: "animation",
delay: "animation",
animate: "animation",
filter: "filter",
backdrop: "filter",
invert: "filter"
};
function getSelectorCategory(selector) {
return staticUtilities[selector] || Object.entries(dynamicUtilities).find(([name]) => new RegExp(`^${name}+(-.+|[0-9]+)$`).test(selector))?.[1];
}
const ignoredColors = [
"transparent",
"current",
"currentColor",
"inherit",
"initial",
"unset",
"none"
];
function uniq(array) {
return [...new Set(array)];
}
async function analyzer(modules, ctx) {
const matched = [];
const icons = [];
const colors = [];
const tokensInfo = /* @__PURE__ */ new Map();
await Promise.all(modules.map(async (code, id) => {
const result = await ctx.uno.generate(code, { id, extendedInfo: true, preflights: false });
for (const [key, value] of result.matched.entries()) {
const prev = tokensInfo.get(key);
tokensInfo.set(key, {
data: value.data,
count: prev?.modules?.length ? value.count + prev.count : value.count,
modules: uniq([...prev?.modules || [], id])
});
}
}));
for (const [rawSelector, { data, count, modules: _modules }] of tokensInfo.entries()) {
const ruleContext = data[data.length - 1][5];
const ruleMeta = data[data.length - 1][4];
const body = data.map((d) => d[2]).join("\n---\n");
const baseSelector = ruleContext?.currentSelector;
const variants = ruleContext?.variants?.map((v) => v.name).filter(Boolean);
const layer = ruleMeta?.layer || "default";
if (baseSelector) {
const category = layer !== "default" ? layer : getSelectorCategory(baseSelector);
const body2 = baseSelector.replace(/^ring-offset|outline-solid|outline-dotted/, "head").replace(/^\w+-/, "");
if (category === "icons") {
const existing = icons.find((i) => i.baseSelector === baseSelector);
if (existing) {
existing.count += count;
existing.modules = uniq([...existing.modules, ..._modules]);
} else {
icons.push({
name: rawSelector,
rawSelector,
baseSelector,
category,
variants,
count,
ruleMeta,
modules: _modules,
body: body2
});
}
continue;
}
const parsedColor = parseColor(body2, ctx.uno.config.theme, "colors");
if (parsedColor?.color && !ignoredColors.includes(parsedColor?.color)) {
const existing = colors.find((c) => c.name === parsedColor.name && c.no === parsedColor.no);
if (existing) {
existing.count += count;
existing.modules = uniq([...existing.modules, ..._modules]);
} else {
colors.push({
name: parsedColor.name,
no: parsedColor.no,
color: parsedColor.color,
count,
modules: _modules
});
}
}
if (category) {
matched.push({
name: rawSelector,
rawSelector,
baseSelector,
category,
variants,
count,
ruleMeta,
modules: _modules,
body: body2
});
continue;
}
}
matched.push({
name: rawSelector,
rawSelector,
category: "other",
count,
modules: _modules,
body
});
}
return {
matched,
colors,
icons
};
}
const _dirname = typeof __dirname !== "undefined" ? __dirname : dirname(fileURLToPath(import.meta.url));
function UnocssInspector(ctx) {
async function configureServer(server) {
await ctx.ready;
const baseUrl = "__unocss";
server.middlewares.use(`/${baseUrl}`, sirv(resolve(_dirname, "../dist/client"), {
single: true,
dev: true
}));
server.middlewares.use(`/$baseUrl}_api`, async (req, res, next) => {
if (!req.url)
return next();
if (req.url === "/") {
const info = {
version: ctx.uno.version,
// use the resolved config from the dev server
root: server.config.root,
modules: Array.from(ctx.modules.keys()),
config: ctx.uno.config,
configSources: (await ctx.ready).sources
};
res.setHeader("Content-Type", "application/json");
res.write(JSON.stringify(info, getCircularReplacer(), 2));
res.end();
return;
}
if (req.url.startsWith("/module")) {
const query = new URLSearchParams(req.url.slice(8));
const id = query.get("id") || "";
const code = ctx.modules.get(id);
if (code == null) {
res.statusCode = 404;
res.end();
return;
}
const tokens = new CountableSet();
await ctx.uno.applyExtractors(code.replace(SKIP_COMMENT_RE, ""), id, tokens);
const result = await ctx.uno.generate(tokens, { id, extendedInfo: true, preflights: false });
const analyzed = await analyzer(new BetterMap([[id, code]]), ctx);
const mod = {
...result,
...analyzed,
layers: result.layers.map((name) => ({ name, css: result.getLayer(name) })),
gzipSize: await gzipSize(result.css),
code,
id
};
res.setHeader("Content-Type", "application/json");
res.write(JSON.stringify(mod, null, 2));
res.end();
return;
}
if (req.url.startsWith("/repl")) {
const query = new URLSearchParams(req.url.slice(5));
const token = query.get("token") || "";
const includeSafelist = JSON.parse(query.get("safelist") ?? "false");
const result = await ctx.uno.generate(token, { preflights: false, safelist: includeSafelist });
const mod = {
...result,
matched: Array.from(result.matched)
};
res.setHeader("Content-Type", "application/json");
res.write(JSON.stringify(mod, null, 2));
res.end();
return;
}
if (req.url.startsWith("/overview")) {
const result = await ctx.uno.generate(ctx.tokens, { preflights: false });
const analyzed = await analyzer(ctx.modules, ctx);
const mod = {
...result,
...analyzed,
gzipSize: await gzipSize(result.css),
layers: result.layers.map((name) => ({ name, css: result.getLayer(name) }))
};
res.setHeader("Content-Type", "application/json");
res.write(JSON.stringify(mod, null, 2));
res.end();
return;
}
next();
});
const _printUrls = server.printUrls;
const colorUrl = (url) => cyan(url.replace(/:(\d+)\//, (_, port) => `:${bold(port)}/`));
server.printUrls = () => {
_printUrls();
for (const url of server.resolvedUrls?.local ?? []) {
const inspectorUrl = url.endsWith("/") ? `${url}${baseUrl}/` : `${url}/${baseUrl}/`;
console.log(` ${green("\u279C")} ${bold("UnoCSS Inspector")}: ${colorUrl(`${inspectorUrl}`)}`);
}
};
}
return {
name: "unocss:inspector",
apply: "serve",
configureServer
};
}
function getCircularReplacer() {
const ancestors = [];
return function(key, value) {
if (typeof value !== "object" || value === null)
return value;
while (ancestors.length > 0 && ancestors.at(-1) !== this)
ancestors.pop();
if (ancestors.includes(value))
return "[Circular]";
ancestors.push(value);
return value;
};
}
export { UnocssInspector as default };
+52
View File
@@ -0,0 +1,52 @@
{
"name": "@unocss/inspector",
"type": "module",
"version": "0.65.4",
"description": "The inspector UI for UnoCSS",
"author": "Anthony Fu <anthonyfu117@hotmail.com>",
"license": "MIT",
"funding": "https://github.com/sponsors/antfu",
"homepage": "https://github.com/unocss/unocss/tree/main/packages/inspector#readme",
"repository": {
"type": "git",
"url": "https://github.com/unocss/unocss",
"directory": "packages/inspector"
},
"bugs": {
"url": "https://github.com/unocss/unocss/issues"
},
"keywords": [
"unocss",
"inspector",
"debugger"
],
"sideEffects": false,
"exports": {
".": {
"types": "./dist/index.d.mts",
"default": "./dist/index.mjs"
}
},
"main": "./dist/index.mjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"files": [
"dist"
],
"dependencies": {
"colorette": "^2.0.20",
"gzip-size": "^6.0.0",
"sirv": "^3.0.0",
"vue-flow-layout": "^0.1.1",
"@unocss/core": "0.65.4",
"@unocss/rule-utils": "0.65.4"
},
"scripts": {
"build": "unbuild",
"build-post": "vite build",
"stub": "unbuild --stub",
"dev": "nr stub && vite",
"update-post": "vite build",
"test:attw": "attw --pack --config-path ../../.attw-esm-only.json"
}
}