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.
+15
View File
@@ -0,0 +1,15 @@
# @unocss/preset-attributify
Attributify Mode for [UnoCSS](https://github.com/unocss/unocss).
## Documentation
Please refer to the [documentation](https://unocss.dev/presets/attributify).
## Credits
Initial idea by [@Tahul](https://github.com/Tahul) and [@antfu](https://github.com/antfu). Prior implementation in Windi CSS by [@voorjaar](https://github.com/voorjaar).
## License
MIT License &copy; 2021-PRESENT [Anthony Fu](https://github.com/antfu)
+89
View File
@@ -0,0 +1,89 @@
import * as _unocss_core from '@unocss/core';
import { PresetOptions, AutoCompleteExtractor, Extractor, VariantObject } from '@unocss/core';
interface AttributifyOptions extends PresetOptions {
/**
* Only generate CSS for attributify or class
*
* @default false
*/
strict?: boolean;
/**
* @default 'un-'
*/
prefix?: string;
/**
* Only match for prefixed attributes
*
* @default false
*/
prefixedOnly?: boolean;
/**
* Support matching non-valued attributes
*
* For example
* ```html
* <div mt-2 />
* ```
*
* @default true
*/
nonValuedAttribute?: boolean;
/**
* A list of attributes to be ignored from extracting.
*/
ignoreAttributes?: string[];
/**
* Non-valued attributes will also match if the actual value represented in DOM is `true`.
* This option exists for supporting frameworks that encodes non-valued attributes as `true`.
* Enabling this option will break rules that ends with `true`.
*
* @default false
*/
trueToNonValued?: boolean;
}
declare function autocompleteExtractorAttributify(options?: AttributifyOptions): AutoCompleteExtractor;
declare const defaultIgnoreAttributes: string[];
declare function extractorAttributify(options?: AttributifyOptions): Extractor;
type TwoStringsCompositionPrefix = 'm' | 'p';
type TwoStringsCompositionSuffix = 'r' | 'b' | 'l' | 't' | 'a' | 'x' | 'y';
/** Some words can compose with two strings to become a complete unocss rule such as ha, mr, mb */
type TwoStringsComposition = `${TwoStringsCompositionPrefix}${TwoStringsCompositionSuffix}` | 'ha' | 'wa';
/** Some words can be a complete unocss rule by itself */
type SpecialSingleWord = 'container' | 'flex' | 'block' | 'inline' | 'table' | 'isolate' | 'absolute' | 'relative' | 'fixed' | 'sticky' | 'static' | 'visible' | 'invisible' | 'grow' | 'shrink' | 'antialiased' | 'italic' | 'ordinal' | 'overline' | 'underline' | 'uppercase' | 'lowercase' | 'capitalize' | 'truncate' | 'border' | 'rounded' | 'outline' | 'ring' | 'shadow' | 'blur' | 'grayscale' | 'invert' | 'sepia' | 'transition' | 'resize' | 'transform' | 'filter';
type PseudoPrefix = 'active' | 'before' | 'after' | 'dark' | 'light' | 'first' | 'last' | 'focus' | 'hover' | 'link' | 'root' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | 'enabled' | 'disabled' | 'all' | 'children';
/** Some words can be used to separate utilities, such as font="mono light", text="sm white" */
type SeparateEnabled = 'align' | 'animate' | 'backdrop' | 'bg' | 'blend' | 'border' | 'box' | 'container' | 'content' | 'cursor' | 'display' | 'divide' | 'filter' | 'flex' | 'font' | 'fw' | 'gap' | 'gradient' | 'grid' | 'h' | 'icon' | 'items' | 'justify' | 'list' | 'm' | 'op' | 'opacity' | 'order' | 'outline' | 'overflow' | 'p' | 'place' | 'pos' | 'position' | 'ring' | 'select' | 'shadow' | 'size' | 'space' | 'table' | 'text' | 'transform' | 'transition' | 'underline' | 'w' | 'z' | PseudoPrefix;
type BasicAttributes = SpecialSingleWord | TwoStringsComposition | SeparateEnabled;
type AttributifyNames<Prefix extends string = ''> = `${Prefix}${BasicAttributes}` | `${Prefix}${PseudoPrefix}:${BasicAttributes}`;
interface AttributifyAttributes extends Partial<Record<AttributifyNames, string | boolean>> {
}
declare const variantsRE: RegExp;
declare function variantAttributify(options?: AttributifyOptions): VariantObject;
/**
* This enables the attributify mode for other presets.
*
* @example
*
* ```html
* <button
* bg="blue-400 hover:blue-500 dark:blue-500 dark:hover:blue-600"
* text="sm white"
* font="mono light"
* p="y-2 x-4"
* border="2 rounded blue-200"
* >
* Button
* </button>
* ```
*
* @see https://unocss.dev/presets/attributify
*/
declare const presetAttributify: _unocss_core.PresetFactory<object, AttributifyOptions>;
export { type AttributifyAttributes, type AttributifyNames, type AttributifyOptions, type BasicAttributes, type PseudoPrefix, type SeparateEnabled, type SpecialSingleWord, type TwoStringsComposition, type TwoStringsCompositionPrefix, type TwoStringsCompositionSuffix, autocompleteExtractorAttributify, presetAttributify as default, defaultIgnoreAttributes, extractorAttributify, presetAttributify, variantAttributify, variantsRE };
+89
View File
@@ -0,0 +1,89 @@
import * as _unocss_core from '@unocss/core';
import { PresetOptions, AutoCompleteExtractor, Extractor, VariantObject } from '@unocss/core';
interface AttributifyOptions extends PresetOptions {
/**
* Only generate CSS for attributify or class
*
* @default false
*/
strict?: boolean;
/**
* @default 'un-'
*/
prefix?: string;
/**
* Only match for prefixed attributes
*
* @default false
*/
prefixedOnly?: boolean;
/**
* Support matching non-valued attributes
*
* For example
* ```html
* <div mt-2 />
* ```
*
* @default true
*/
nonValuedAttribute?: boolean;
/**
* A list of attributes to be ignored from extracting.
*/
ignoreAttributes?: string[];
/**
* Non-valued attributes will also match if the actual value represented in DOM is `true`.
* This option exists for supporting frameworks that encodes non-valued attributes as `true`.
* Enabling this option will break rules that ends with `true`.
*
* @default false
*/
trueToNonValued?: boolean;
}
declare function autocompleteExtractorAttributify(options?: AttributifyOptions): AutoCompleteExtractor;
declare const defaultIgnoreAttributes: string[];
declare function extractorAttributify(options?: AttributifyOptions): Extractor;
type TwoStringsCompositionPrefix = 'm' | 'p';
type TwoStringsCompositionSuffix = 'r' | 'b' | 'l' | 't' | 'a' | 'x' | 'y';
/** Some words can compose with two strings to become a complete unocss rule such as ha, mr, mb */
type TwoStringsComposition = `${TwoStringsCompositionPrefix}${TwoStringsCompositionSuffix}` | 'ha' | 'wa';
/** Some words can be a complete unocss rule by itself */
type SpecialSingleWord = 'container' | 'flex' | 'block' | 'inline' | 'table' | 'isolate' | 'absolute' | 'relative' | 'fixed' | 'sticky' | 'static' | 'visible' | 'invisible' | 'grow' | 'shrink' | 'antialiased' | 'italic' | 'ordinal' | 'overline' | 'underline' | 'uppercase' | 'lowercase' | 'capitalize' | 'truncate' | 'border' | 'rounded' | 'outline' | 'ring' | 'shadow' | 'blur' | 'grayscale' | 'invert' | 'sepia' | 'transition' | 'resize' | 'transform' | 'filter';
type PseudoPrefix = 'active' | 'before' | 'after' | 'dark' | 'light' | 'first' | 'last' | 'focus' | 'hover' | 'link' | 'root' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | 'enabled' | 'disabled' | 'all' | 'children';
/** Some words can be used to separate utilities, such as font="mono light", text="sm white" */
type SeparateEnabled = 'align' | 'animate' | 'backdrop' | 'bg' | 'blend' | 'border' | 'box' | 'container' | 'content' | 'cursor' | 'display' | 'divide' | 'filter' | 'flex' | 'font' | 'fw' | 'gap' | 'gradient' | 'grid' | 'h' | 'icon' | 'items' | 'justify' | 'list' | 'm' | 'op' | 'opacity' | 'order' | 'outline' | 'overflow' | 'p' | 'place' | 'pos' | 'position' | 'ring' | 'select' | 'shadow' | 'size' | 'space' | 'table' | 'text' | 'transform' | 'transition' | 'underline' | 'w' | 'z' | PseudoPrefix;
type BasicAttributes = SpecialSingleWord | TwoStringsComposition | SeparateEnabled;
type AttributifyNames<Prefix extends string = ''> = `${Prefix}${BasicAttributes}` | `${Prefix}${PseudoPrefix}:${BasicAttributes}`;
interface AttributifyAttributes extends Partial<Record<AttributifyNames, string | boolean>> {
}
declare const variantsRE: RegExp;
declare function variantAttributify(options?: AttributifyOptions): VariantObject;
/**
* This enables the attributify mode for other presets.
*
* @example
*
* ```html
* <button
* bg="blue-400 hover:blue-500 dark:blue-500 dark:hover:blue-600"
* text="sm white"
* font="mono light"
* p="y-2 x-4"
* border="2 rounded blue-200"
* >
* Button
* </button>
* ```
*
* @see https://unocss.dev/presets/attributify
*/
declare const presetAttributify: _unocss_core.PresetFactory<object, AttributifyOptions>;
export { type AttributifyAttributes, type AttributifyNames, type AttributifyOptions, type BasicAttributes, type PseudoPrefix, type SeparateEnabled, type SpecialSingleWord, type TwoStringsComposition, type TwoStringsCompositionPrefix, type TwoStringsCompositionSuffix, autocompleteExtractorAttributify, presetAttributify as default, defaultIgnoreAttributes, extractorAttributify, presetAttributify, variantAttributify, variantsRE };
+217
View File
@@ -0,0 +1,217 @@
import { isAttributifySelector, isValidSelector, definePreset } from '@unocss/core';
const variantsRE = /^(?!.*\[[^:]+:.+\]$)((?:.+:)?!?)(.*)$/;
function variantAttributify(options = {}) {
const prefix = options.prefix ?? "un-";
const prefixedOnly = options.prefixedOnly ?? false;
const trueToNonValued = options.trueToNonValued ?? false;
let variantsValueRE;
return {
name: "attributify",
match(input, { generator }) {
const match = isAttributifySelector(input);
if (!match)
return;
let name = match[1];
if (name.startsWith(prefix))
name = name.slice(prefix.length);
else if (prefixedOnly)
return;
const content = match[2];
const [, variants = "", body = content] = content.match(variantsRE) || [];
if (body === "~" || trueToNonValued && body === "true" || !body) {
return `${variants}${name}`;
}
if (variantsValueRE == null) {
const separators = generator?.config?.separators?.join("|");
if (separators)
variantsValueRE = new RegExp(`^(.*\\](?:${separators}))(\\[[^\\]]+?\\])$`);
else
variantsValueRE = false;
}
if (variantsValueRE) {
const [, bodyVariant, bracketValue] = content.match(variantsValueRE) || [];
if (bracketValue)
return `${bodyVariant}${variants}${name}-${bracketValue}`;
}
if (variants && body.match(/^[\d.]+$/)) {
const variantParts = variants.split(/([^:]*:)/g).filter(Boolean);
const _body = variantParts.pop() + body;
const _variants = variantParts.join("");
return [
{ matcher: `${variants}${name}-${body}` },
{ matcher: `${_variants}${name}-${_body}` }
];
}
return `${variants}${name}-${body}`;
}
};
}
const elementRE$1 = /(<\w[\w:.$-]*\s)((?:'[^>']*'|"[^>"]*"|`[^>`]*`|\{[^>}]*\}|[^>]*?)*)/g;
const valuedAttributeRE$1 = /(\?|(?!\d|-{2}|-\d)[\w\u00A0-\uFFFF-:%]+)(?:=("[^"]*|'[^']*))?/g;
const splitterRE$1 = /[\s'"`;>]+/;
function autocompleteExtractorAttributify(options) {
return {
name: "attributify",
extract: ({ content, cursor }) => {
const matchedElements = content.matchAll(elementRE$1);
let attrs;
let elPos = 0;
for (const match of matchedElements) {
const [, prefix, content2] = match;
const currentPos2 = match.index + prefix.length;
if (cursor > currentPos2 && cursor <= currentPos2 + content2.length) {
elPos = currentPos2;
attrs = content2;
break;
}
}
if (!attrs)
return null;
const matchedAttributes = attrs.matchAll(valuedAttributeRE$1);
let attrsPos = 0;
let attrName;
let attrValues;
for (const match of matchedAttributes) {
const [matched, name, rawValues] = match;
const currentPos2 = elPos + match.index;
if (cursor > currentPos2 && cursor <= currentPos2 + matched.length) {
attrsPos = currentPos2;
attrName = name;
attrValues = rawValues?.slice(1);
break;
}
}
if (!attrName)
return null;
if (attrName === "class" || attrName === "className" || attrName === ":class")
return null;
const hasPrefix = !!options?.prefix && attrName.startsWith(options.prefix);
if (options?.prefixedOnly && !hasPrefix)
return null;
const attrNameWithoutPrefix = hasPrefix ? attrName.slice(options.prefix.length) : attrName;
if (attrValues === void 0) {
return {
extracted: attrNameWithoutPrefix,
resolveReplacement(suggestion) {
const startOffset = hasPrefix ? options.prefix.length : 0;
return {
start: attrsPos + startOffset,
end: attrsPos + attrName.length,
replacement: suggestion
};
}
};
}
const attrValuePos = attrsPos + attrName.length + 2;
let matchSplit = splitterRE$1.exec(attrValues);
let currentPos = 0;
let value;
while (matchSplit) {
const [matched] = matchSplit;
if (cursor > attrValuePos + currentPos && cursor <= attrValuePos + currentPos + matchSplit.index) {
value = attrValues.slice(currentPos, currentPos + matchSplit.index);
break;
}
currentPos += matchSplit.index + matched.length;
matchSplit = splitterRE$1.exec(attrValues.slice(currentPos));
}
if (value === void 0)
value = attrValues.slice(currentPos);
const [, variants = "", body] = value.match(variantsRE) || [];
return {
extracted: `${variants}${attrNameWithoutPrefix}-${body}`,
transformSuggestions(suggestions) {
return suggestions.filter((v) => v.startsWith(`${variants}${attrNameWithoutPrefix}-`)).map((v) => variants + v.slice(variants.length + attrNameWithoutPrefix.length + 1));
},
resolveReplacement(suggestion) {
return {
start: currentPos + attrValuePos,
end: currentPos + attrValuePos + value.length,
replacement: variants + suggestion.slice(variants.length + attrNameWithoutPrefix.length + 1)
};
}
};
}
};
}
const strippedPrefixes = [
"v-bind:",
":"
];
const splitterRE = /[\s'"`;]+/g;
const elementRE = /<[^>\s]*\s((?:'[^']*'|"[^"]*"|`[^`]*`|\{[^}]*\}|=>|[^>]*?)*)/g;
const valuedAttributeRE = /(\?|(?!\d|-{2}|-\d)[\w\u00A0-\uFFFF:!%.~<-]+)=?(?:"([^"]*)"|'([^']*)'|\{([^}]*)\})?/g;
const defaultIgnoreAttributes = ["placeholder", "fill", "opacity", "stroke-opacity"];
function extractorAttributify(options) {
const ignoreAttributes = options?.ignoreAttributes ?? defaultIgnoreAttributes;
const nonValuedAttribute = options?.nonValuedAttribute ?? true;
const trueToNonValued = options?.trueToNonValued ?? false;
return {
name: "@unocss/preset-attributify/extractor",
extract({ code }) {
return Array.from(code.matchAll(elementRE)).flatMap((match) => Array.from((match[1] || "").matchAll(valuedAttributeRE))).flatMap(([, name, ...contents]) => {
const content = contents.filter(Boolean).join("");
if (ignoreAttributes.includes(name))
return [];
for (const prefix of strippedPrefixes) {
if (name.startsWith(prefix)) {
name = name.slice(prefix.length);
break;
}
}
if (!content) {
if (isValidSelector(name) && nonValuedAttribute !== false) {
const result = [`[${name}=""]`];
if (trueToNonValued)
result.push(`[${name}="true"]`);
return result;
}
return [];
}
if (["class", "className"].includes(name)) {
return content.split(splitterRE).filter(isValidSelector);
} else if (elementRE.test(content)) {
elementRE.lastIndex = 0;
return this.extract({ code: content });
} else {
if (options?.prefixedOnly && options.prefix && !name.startsWith(options.prefix))
return [];
return content.split(splitterRE).filter((v) => Boolean(v) && v !== ":").map((v) => `[${name}~="${v}"]`);
}
});
}
};
}
const presetAttributify = definePreset((options = {}) => {
options.strict = options.strict ?? false;
options.prefix = options.prefix ?? "un-";
options.prefixedOnly = options.prefixedOnly ?? false;
options.nonValuedAttribute = options.nonValuedAttribute ?? true;
options.ignoreAttributes = options.ignoreAttributes ?? defaultIgnoreAttributes;
const variants = [
variantAttributify(options)
];
const extractors = [
extractorAttributify(options)
];
const autocompleteExtractors = [
autocompleteExtractorAttributify(options)
];
return {
name: "@unocss/preset-attributify",
enforce: "post",
variants,
extractors,
options,
autocomplete: {
extractors: autocompleteExtractors
},
extractorDefault: options.strict ? false : void 0
};
});
export { autocompleteExtractorAttributify, presetAttributify as default, defaultIgnoreAttributes, extractorAttributify, presetAttributify, variantAttributify, variantsRE };
+43
View File
@@ -0,0 +1,43 @@
{
"name": "@unocss/preset-attributify",
"type": "module",
"version": "0.65.4",
"description": "Attributify preset 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/preset-attributify#readme",
"repository": {
"type": "git",
"url": "https://github.com/unocss/unocss",
"directory": "packages/preset-attributify"
},
"bugs": {
"url": "https://github.com/unocss/unocss/issues"
},
"keywords": [
"unocss",
"unocss-preset"
],
"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": {
"@unocss/core": "0.65.4"
},
"scripts": {
"build": "unbuild",
"stub": "unbuild --stub",
"test:attw": "attw --pack --config-path ../../.attw-esm-only.json"
}
}