This commit is contained in:
2026-05-25 17:07:28 +08:00
parent 30cba5ee96
commit df501f6151
10000 changed files with 276005 additions and 0 deletions
+9
View File
@@ -0,0 +1,9 @@
The MIT License (MIT)
Copyright (c) 2019-present afc163
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.
+141
View File
@@ -0,0 +1,141 @@
# @ant-design/cssinjs
[![NPM version][npm-image]][npm-url] [![npm download][download-image]][download-url] [![dumi](https://img.shields.io/badge/docs%20by-dumi-blue?style=flat-square)](https://github.com/umijs/dumi) [![build status][github-actions-image]][github-actions-url] [![Codecov][codecov-image]][codecov-url] [![bundle size][bundlephobia-image]][bundlephobia-url]
[npm-image]: http://img.shields.io/npm/v/@ant-design/cssinjs.svg?style=flat-square
[npm-url]: http://npmjs.org/package/@ant-design/cssinjs
[github-actions-image]: https://github.com/ant-design/cssinjs/workflows/CI/badge.svg
[github-actions-url]: https://github.com/ant-design/cssinjs/actions
[codecov-image]: https://img.shields.io/codecov/c/github/ant-design/cssinjs/master.svg?style=flat-square
[codecov-url]: https://codecov.io/gh/ant-design/cssinjs/branch/master
[download-image]: https://img.shields.io/npm/dm/@ant-design/cssinjs.svg?style=flat-square
[download-url]: https://npmjs.org/package/@ant-design/cssinjs
[bundlephobia-url]: https://bundlephobia.com/result?p=@ant-design/cssinjs
[bundlephobia-image]: https://badgen.net/bundlephobia/minzip/@ant-design/cssinjs
Component level cssinjs solution used in [ant.design](https://ant.design). It's a subset of [Emotion](https://emotion.sh/) with design token logic wrapper. Please feel free to use emotion directly if you want to find a web cssinjs solution. cssinjs related dep packages:
- stylis
- @emotion/hash
- @emotion/unitless
## Live Demo
https://ant-design.github.io/cssinjs/
## Install
```bash
npm install @ant-design/cssinjs
```
or
```bash
yarn add @ant-design/cssinjs
```
```bash
pnpm add @ant-design/cssinjs
```
## Development
```
npm install
npm start
```
## License
@ant-design/cssinjs is released under the MIT license.
## API
### StyleProvider
| Prop | Desc | Type | Default |
| --- | --- | --- | --- |
| autoClear | Clear inject style element when component remove. | boolean | false |
| cache | Config cssinjs cache entity. Only set when you need ssr to extract style on you own. | CacheEntity | - |
| hashPriority | Use `:where` selector to reduce hashId css selector priority | `'low' \| 'high'` | `'low'` |
| container | Tell cssinjs where to inject style in. | Element \| ShadowRoot | `document.head` |
| ssrInline | Component wil render inline `<style />` for fallback in SSR. Not recommend. | boolean | false |
| transformers | Transform css before inject in document. Please note that `transformers` do not support dynamic update | Transformer[] | - |
### createCache
return CacheEntity for StyleProvider.
### createTheme
Create theme object. When same algorithm provided, it will return same object.
### Design Token related API
Since `@ant-design/cssinjs` use strong constraints for cache hit performance, we recommend to view demo `basic.tsx` for usage and `animation.tsx` for animation usage.
### extractStyle
Extracts the styles from the cache and returns them as a string.
#### Parameters
- `cache` (Cache): The cache instance containing the styles.
- `options` (object | boolean, optional): Options for extracting the styles.
- `plain` (boolean, optional): If true, the styles will be returned in plain format. Default is false.
- `types` (string | string[], optional): The types of styles to extract. Default is ['style', 'token', 'cssVar'].
#### Returns
- (string): The extracted styles as a string.
#### Example
```typescript
import { extractStyle, createCache } from '@ant-design/cssinjs';
// 创建并填充缓存
const cache = createCache();
// 注意:在实际使用中,缓存通常会在渲染组件时自动填充
// 提取样式
const styles = extractStyle(cache, { plain: true, types: ['style', 'token'] });
// 使用提取的样式
const styleElement = document.createElement('style');
styleElement.innerHTML = styles;
document.head.appendChild(styleElement);
```
## Transform
When you need transform CSSObject before inject style. You can use `transformers` to handle this:
```tsx | pure
import {
legacyLogicalPropertiesTransformer,
StyleProvider,
} from '@ant-design/cssinjs';
export default () => (
<StyleProvider transformers={[legacyLogicalPropertiesTransformer]}>
<MyApp />
</StyleProvider>
);
```
Follow are the transform we provide:
#### legacyLogicalPropertiesTransformer
Convert logical properties to legacy properties. e.g. `marginBlockStart` to `marginTop`:
- inset
- margin
- padding
- border
#### px2remTransformer
Convert pixel units to rem units. [px2remTransformer.options](./src/transformers/px2rem.ts)
File diff suppressed because one or more lines are too long
+20
View File
@@ -0,0 +1,20 @@
export type KeyType = string | number;
type ValueType = [number, any];
/** Connect key with `SPLIT` */
export declare function pathKey(keys: KeyType[]): string;
declare class Entity {
instanceId: string;
constructor(instanceId: string);
/** @private Internal cache map. Do not access this directly */
cache: Map<string, ValueType>;
/** @private Record update times for each key */
updateTimes: Map<string, number>;
extracted: Set<string>;
get(keys: KeyType[]): ValueType | null;
/** A fast get cache with `get` concat. */
opGet(keyPathStr: string): ValueType | null;
update(keys: KeyType[], valueFn: (origin: ValueType | null) => ValueType | null): void;
/** A fast get cache with `get` concat. */
opUpdate(keyPathStr: string, valueFn: (origin: ValueType | null) => ValueType | null): void;
}
export default Entity;
+50
View File
@@ -0,0 +1,50 @@
// [times, realValue]
const SPLIT = '%';
/** Connect key with `SPLIT` */
export function pathKey(keys) {
return keys.join(SPLIT);
}
/** Record update id for extract static style order. */
let updateId = 0;
class Entity {
instanceId;
constructor(instanceId) {
this.instanceId = instanceId;
}
/** @private Internal cache map. Do not access this directly */
cache = new Map();
/** @private Record update times for each key */
updateTimes = new Map();
extracted = new Set();
get(keys) {
return this.opGet(pathKey(keys));
}
/** A fast get cache with `get` concat. */
opGet(keyPathStr) {
return this.cache.get(keyPathStr) || null;
}
update(keys, valueFn) {
return this.opUpdate(pathKey(keys), valueFn);
}
/** A fast get cache with `get` concat. */
opUpdate(keyPathStr, valueFn) {
const prevValue = this.cache.get(keyPathStr);
const nextValue = valueFn(prevValue);
if (nextValue === null) {
this.cache.delete(keyPathStr);
this.updateTimes.delete(keyPathStr);
} else {
this.cache.set(keyPathStr, nextValue);
this.updateTimes.set(keyPathStr, updateId);
updateId += 1;
}
}
}
export default Entity;
+9
View File
@@ -0,0 +1,9 @@
import type { CSSInterpolation } from './hooks/useStyleRegister';
declare class Keyframe {
private name;
style: CSSInterpolation;
constructor(name: string, style: CSSInterpolation);
getName(hashId?: string): string;
_keyframe: boolean;
}
export default Keyframe;
+13
View File
@@ -0,0 +1,13 @@
class Keyframe {
name;
style;
constructor(name, style) {
this.name = name;
this.style = style;
}
getName(hashId = '') {
return hashId ? `${hashId}-${this.name}` : this.name;
}
_keyframe = true;
}
export default Keyframe;
+45
View File
@@ -0,0 +1,45 @@
import * as React from 'react';
import CacheEntity from './Cache';
import type { Linter } from './linters/interface';
import type { Transformer } from './transformers/interface';
export declare const ATTR_TOKEN = "data-token-hash";
export declare const ATTR_MARK = "data-css-hash";
export declare const ATTR_CACHE_PATH = "data-cache-path";
export declare const CSS_IN_JS_INSTANCE = "__cssinjs_instance__";
export declare function createCache(): CacheEntity;
export type HashPriority = 'low' | 'high';
export interface StyleContextProps {
/** @private Test only. Not work in production. */
mock?: 'server' | 'client';
/**
* Only set when you need ssr to extract style on you own.
* If not provided, it will auto create <style /> on the end of Provider in server side.
*/
cache: CacheEntity;
/** Tell children that this context is default generated context */
defaultCache: boolean;
/** Use `:where` selector to reduce hashId css selector priority */
hashPriority?: HashPriority;
/** Tell cssinjs where to inject style in */
container?: Element | ShadowRoot;
/** Component wil render inline `<style />` for fallback in SSR. Not recommend. */
ssrInline?: boolean;
/** Transform css before inject in document. Please note that `transformers` do not support dynamic update */
transformers?: Transformer[];
/**
* Linters to lint css before inject in document.
* Styles will be linted after transforming.
* Please note that `linters` do not support dynamic update.
*/
linters?: Linter[];
/** Wrap css in a layer to avoid global style conflict */
layer?: boolean;
/** Hardcode here since transformer not support take effect on serialize currently */
autoPrefix?: boolean;
}
declare const StyleContext: React.Context<StyleContextProps>;
export type StyleProviderProps = Partial<Omit<StyleContextProps, 'autoPrefix'>> & {
children?: React.ReactNode;
};
export declare const StyleProvider: React.FC<StyleProviderProps>;
export default StyleContext;
+85
View File
@@ -0,0 +1,85 @@
import useMemo from "@rc-component/util/es/hooks/useMemo";
import isEqual from "@rc-component/util/es/isEqual";
import * as React from 'react';
import CacheEntity from "./Cache";
import { AUTO_PREFIX } from "./transformers/autoPrefix";
export const ATTR_TOKEN = 'data-token-hash';
export const ATTR_MARK = 'data-css-hash';
export const ATTR_CACHE_PATH = 'data-cache-path';
// Mark css-in-js instance in style element
export const CSS_IN_JS_INSTANCE = '__cssinjs_instance__';
export function createCache() {
const cssinjsInstanceId = Math.random().toString(12).slice(2);
// Tricky SSR: Move all inline style to the head.
// PS: We do not recommend tricky mode.
if (typeof document !== 'undefined' && document.head && document.body) {
const styles = document.body.querySelectorAll(`style[${ATTR_MARK}]`) || [];
const {
firstChild
} = document.head;
Array.from(styles).forEach(style => {
style[CSS_IN_JS_INSTANCE] ||= cssinjsInstanceId;
// Not force move if no head
if (style[CSS_IN_JS_INSTANCE] === cssinjsInstanceId) {
document.head.insertBefore(style, firstChild);
}
});
// Deduplicate of moved styles
const styleHash = {};
Array.from(document.querySelectorAll(`style[${ATTR_MARK}]`)).forEach(style => {
const hash = style.getAttribute(ATTR_MARK);
if (styleHash[hash]) {
if (style[CSS_IN_JS_INSTANCE] === cssinjsInstanceId) {
style.parentNode?.removeChild(style);
}
} else {
styleHash[hash] = true;
}
});
}
return new CacheEntity(cssinjsInstanceId);
}
const StyleContext = /*#__PURE__*/React.createContext({
hashPriority: 'low',
cache: createCache(),
defaultCache: true,
autoPrefix: false
});
export const StyleProvider = props => {
const {
children,
...restProps
} = props;
const parentContext = React.useContext(StyleContext);
const context = useMemo(() => {
const mergedContext = {
...parentContext
};
Object.keys(restProps).forEach(key => {
const value = restProps[key];
if (restProps[key] !== undefined) {
mergedContext[key] = value;
}
});
const {
cache,
transformers = []
} = restProps;
mergedContext.cache = mergedContext.cache || createCache();
mergedContext.defaultCache = !cache && parentContext.defaultCache;
// autoPrefix
if (transformers.includes(AUTO_PREFIX)) {
mergedContext.autoPrefix = true;
}
return mergedContext;
}, [parentContext, restProps], (prev, next) => !isEqual(prev[0], next[0], true) || !isEqual(prev[1], next[1], true));
return /*#__PURE__*/React.createElement(StyleContext.Provider, {
value: context
}, children);
};
export default StyleContext;
+13
View File
@@ -0,0 +1,13 @@
import type Cache from './Cache';
declare const ExtractStyleFns: {
style: import("./hooks/useGlobalCache").ExtractStyle<[styleStr: string, styleId: string, effectStyle: Record<string, string>, clientOnly: boolean | undefined, order: number]>;
token: import("./hooks/useGlobalCache").ExtractStyle<[token: any, hashId: string, realToken: any, cssVarStr: string, cssVarKey: string]>;
cssVar: import("./hooks/useGlobalCache").ExtractStyle<[cssVarToken: import("./util/css-variables").TokenWithCSSVar<any, Record<string, any>>, cssVarStr: string, styleId: string, cssVarKey: string]>;
};
type ExtractStyleType = keyof typeof ExtractStyleFns;
export default function extractStyle(cache: Cache, options?: boolean | {
plain?: boolean;
types?: ExtractStyleType | ExtractStyleType[];
once?: boolean;
}): string;
export {};
+69
View File
@@ -0,0 +1,69 @@
import { extract as tokenExtractStyle, TOKEN_PREFIX } from "./hooks/useCacheToken";
import { CSS_VAR_PREFIX, extract as cssVarExtractStyle } from "./hooks/useCSSVarRegister";
import { extract as styleExtractStyle, STYLE_PREFIX } from "./hooks/useStyleRegister";
import { toStyleStr } from "./util";
import { ATTR_CACHE_MAP, serialize as serializeCacheMap } from "./util/cacheMapUtil";
const ExtractStyleFns = {
[STYLE_PREFIX]: styleExtractStyle,
[TOKEN_PREFIX]: tokenExtractStyle,
[CSS_VAR_PREFIX]: cssVarExtractStyle
};
function isNotNull(value) {
return value !== null;
}
export default function extractStyle(cache, options) {
const {
plain = false,
types = ['style', 'token', 'cssVar'],
once = false
} = typeof options === 'boolean' ? {
plain: options
} : options || {};
const matchPrefixRegexp = new RegExp(`^(${(typeof types === 'string' ? [types] : types).join('|')})%`);
// prefix with `style` is used for `useStyleRegister` to cache style context
const styleKeys = Array.from(cache.cache.keys()).filter(key => matchPrefixRegexp.test(key));
// Common effect styles like animation
const effectStyles = {};
// Mapping of cachePath to style hash
const cachePathMap = {};
let styleText = '';
styleKeys.map(key => {
if (once && cache.extracted.has(key)) {
return null; // Skip if already extracted
}
const cachePath = key.replace(matchPrefixRegexp, '').replace(/%/g, '|');
const [prefix] = key.split('%');
const extractFn = ExtractStyleFns[prefix];
const extractedStyle = extractFn(cache.cache.get(key)[1], effectStyles, {
plain
});
if (!extractedStyle) {
return null;
}
const updateTime = cache.updateTimes.get(key) || 0;
const [order, styleId, styleStr] = extractedStyle;
if (key.startsWith('style')) {
cachePathMap[cachePath] = styleId;
}
// record that this style has been extracted
cache.extracted.add(key);
return [order, styleStr, updateTime];
}).filter(isNotNull).sort(([o1,, u1], [o2,, u2]) => {
if (o1 !== o2) {
return o1 - o2;
}
return u1 - u2;
}).forEach(([, style]) => {
styleText += style;
});
// ==================== Fill Cache Path ====================
styleText += toStyleStr(`.${ATTR_CACHE_MAP}{content:"${serializeCacheMap(cachePathMap)}";}`, undefined, undefined, {
[ATTR_CACHE_MAP]: ATTR_CACHE_MAP
}, plain);
return styleText;
}
@@ -0,0 +1,22 @@
import type { TokenWithCSSVar } from '../util/css-variables';
import type { ExtractStyle } from './useGlobalCache';
export declare const CSS_VAR_PREFIX = "cssVar";
type CSSVarCacheValue<V, T extends Record<string, V> = Record<string, V>> = [
cssVarToken: TokenWithCSSVar<V, T>,
cssVarStr: string,
styleId: string,
cssVarKey: string
];
declare const useCSSVarRegister: <V, T extends Record<string, V>>(config: {
path: string[];
key: string;
prefix?: string;
unitless?: Record<string, boolean>;
ignore?: Record<string, boolean>;
scope?: string | string[];
token: any;
hashId?: string;
nonce?: string | (() => string);
}, fn: () => T) => CSSVarCacheValue<V, T>;
export declare const extract: ExtractStyle<CSSVarCacheValue<any>>;
export default useCSSVarRegister;
@@ -0,0 +1,89 @@
import { removeCSS, updateCSS } from "@rc-component/util/es/Dom/dynamicCSS";
import { useContext } from 'react';
import StyleContext, { ATTR_MARK, ATTR_TOKEN, CSS_IN_JS_INSTANCE } from "../StyleContext";
import { injectCSPNonce, isClientSide, toStyleStr } from "../util";
import { transformToken } from "../util/css-variables";
import useGlobalCache from "./useGlobalCache";
import { uniqueHash } from "./useStyleRegister";
export const CSS_VAR_PREFIX = 'cssVar';
const useCSSVarRegister = (config, fn) => {
const {
key,
prefix,
unitless,
ignore,
token,
hashId,
scope,
nonce
} = config;
const {
cache: {
instanceId
},
container,
hashPriority
} = useContext(StyleContext);
const {
_tokenKey: tokenKey
} = token;
const scopeKey = Array.isArray(scope) ? scope.join('@@') : scope;
const stylePath = [...config.path, key, scopeKey, tokenKey];
const cache = useGlobalCache(CSS_VAR_PREFIX, stylePath, () => {
const originToken = fn();
const [mergedToken, cssVarsStr] = transformToken(originToken, key, {
prefix,
unitless,
ignore,
scope,
hashPriority,
hashCls: hashId
});
const styleId = uniqueHash(stylePath, cssVarsStr);
return [mergedToken, cssVarsStr, styleId, key];
}, ([,, styleId]) => {
if (isClientSide) {
removeCSS(styleId, {
mark: ATTR_MARK,
attachTo: container
});
}
}, ([, cssVarsStr, styleId]) => {
if (!cssVarsStr) {
return;
}
let mergedCSSConfig = {
mark: ATTR_MARK,
prepend: 'queue',
attachTo: container,
priority: -999
};
mergedCSSConfig = injectCSPNonce(mergedCSSConfig, nonce);
const style = updateCSS(cssVarsStr, styleId, mergedCSSConfig);
style[CSS_IN_JS_INSTANCE] = instanceId;
// Used for `useCacheToken` to remove on batch when token removed
style.setAttribute(ATTR_TOKEN, key);
});
return cache;
};
export const extract = (cache, effectStyles, options) => {
const [, styleStr, styleId, cssVarKey] = cache;
const {
plain
} = options || {};
if (!styleStr) {
return null;
}
const order = -999;
// ====================== Style ======================
// Used for @rc-component/util
const sharedAttrs = {
'data-rc-order': 'prependQueue',
'data-rc-priority': `${order}`
};
const styleText = toStyleStr(styleStr, cssVarKey, styleId, sharedAttrs, plain);
return [order, styleId, styleText];
};
export default useCSSVarRegister;
@@ -0,0 +1,69 @@
import type Theme from '../theme/Theme';
import type { ExtractStyle } from './useGlobalCache';
export interface Option<DerivativeToken, DesignToken> {
/**
* Generate token with salt.
* This is used to generate different hashId even same derivative token for different version.
*/
salt?: string;
override?: object;
/**
* Format token as you need. Such as:
*
* - rename token
* - merge token
* - delete token
*
* This should always be the same since it's one time process.
* It's ok to useMemo outside but this has better cache strategy.
*/
formatToken?: (mergedToken: any) => DerivativeToken;
/**
* Get final token with origin token, override token and theme.
* The parameters do not contain formatToken since it's passed by user.
* @param origin The original token.
* @param override Extra tokens to override.
* @param theme Theme instance. Could get derivative token by `theme.getDerivativeToken`
*/
getComputedToken?: (origin: DesignToken, override: object, theme: Theme<any, any>) => DerivativeToken;
/**
* Transform token to css variables.
*/
cssVar: {
hashed?: boolean;
/** Prefix for css variables */
prefix?: string;
/** Tokens that should not be appended with unit */
unitless?: Record<string, boolean>;
/** Tokens that should not be transformed to css variables */
ignore?: Record<string, boolean>;
/** Tokens that preserves origin value */
preserve?: Record<string, boolean>;
/** Key for current theme. Useful for customizing and should be unique */
key: string;
};
/**
* CSP nonce for style element.
* Can be a string or a function that returns a string.
*/
nonce?: string | (() => string);
}
export declare const getComputedToken: <DerivativeToken = object, DesignToken = DerivativeToken>(originToken: DesignToken, overrideToken: object, theme: Theme<any, any>, format?: ((token: DesignToken) => DerivativeToken) | undefined) => any;
export declare const TOKEN_PREFIX = "token";
type TokenCacheValue<DerivativeToken> = [
token: DerivativeToken,
hashId: string,
realToken: DerivativeToken,
cssVarStr: string,
cssVarKey: string
];
/**
* Cache theme derivative token as global shared one
* @param theme Theme entity
* @param tokens List of tokens, used for cache. Please do not dynamic generate object directly
* @param option Additional config
* @returns Call Theme.getDerivativeToken(tokenObject) to get token
*/
export default function useCacheToken<DerivativeToken = object, DesignToken = DerivativeToken>(theme: Theme<any, any>, tokens: Partial<DesignToken>[], option: Option<DerivativeToken, DesignToken>): TokenCacheValue<DerivativeToken>;
export declare const extract: ExtractStyle<TokenCacheValue<any>>;
export {};
@@ -0,0 +1,155 @@
import hash from '@emotion/hash';
import { updateCSS } from "@rc-component/util/es/Dom/dynamicCSS";
import { useContext } from 'react';
import StyleContext, { ATTR_MARK, ATTR_TOKEN, CSS_IN_JS_INSTANCE } from "../StyleContext";
import { flattenToken, injectCSPNonce, memoResult, token2key, toStyleStr } from "../util";
import { transformToken } from "../util/css-variables";
import useGlobalCache from "./useGlobalCache";
const EMPTY_OVERRIDE = {};
// Generate different prefix to make user selector break in production env.
// This helps developer not to do style override directly on the hash id.
const hashPrefix = process.env.NODE_ENV !== 'production' ? 'css-dev-only-do-not-override' : 'css';
const tokenKeys = new Map();
function recordCleanToken(tokenKey) {
tokenKeys.set(tokenKey, (tokenKeys.get(tokenKey) || 0) + 1);
}
function removeStyleTags(key, instanceId) {
if (typeof document !== 'undefined') {
const styles = document.querySelectorAll(`style[${ATTR_TOKEN}="${key}"]`);
styles.forEach(style => {
if (style[CSS_IN_JS_INSTANCE] === instanceId) {
style.parentNode?.removeChild(style);
}
});
}
}
const TOKEN_THRESHOLD = -1;
// Remove will check current keys first
function cleanTokenStyle(tokenKey, instanceId) {
tokenKeys.set(tokenKey, (tokenKeys.get(tokenKey) || 0) - 1);
const cleanableKeyList = new Set();
tokenKeys.forEach((value, key) => {
if (value <= 0) cleanableKeyList.add(key);
});
// Should keep tokens under threshold for not to insert style too often
if (tokenKeys.size - cleanableKeyList.size > TOKEN_THRESHOLD) {
cleanableKeyList.forEach(key => {
removeStyleTags(key, instanceId);
tokenKeys.delete(key);
});
}
}
export const getComputedToken = (originToken, overrideToken, theme, format) => {
const derivativeToken = theme.getDerivativeToken(originToken);
// Merge with override
let mergedDerivativeToken = {
...derivativeToken,
...overrideToken
};
// Format if needed
if (format) {
mergedDerivativeToken = format(mergedDerivativeToken);
}
return mergedDerivativeToken;
};
export const TOKEN_PREFIX = 'token';
/**
* Cache theme derivative token as global shared one
* @param theme Theme entity
* @param tokens List of tokens, used for cache. Please do not dynamic generate object directly
* @param option Additional config
* @returns Call Theme.getDerivativeToken(tokenObject) to get token
*/
export default function useCacheToken(theme, tokens, option) {
const {
cache: {
instanceId
},
container,
hashPriority
} = useContext(StyleContext);
const {
salt = '',
override = EMPTY_OVERRIDE,
formatToken,
getComputedToken: compute,
cssVar,
nonce
} = option;
// Basic - We do basic cache here
const mergedToken = memoResult(() => Object.assign({}, ...tokens), tokens);
const tokenStr = flattenToken(mergedToken);
const overrideTokenStr = flattenToken(override);
const cssVarStr = flattenToken(cssVar);
const cachedToken = useGlobalCache(TOKEN_PREFIX, [salt, theme.id, tokenStr, overrideTokenStr, cssVarStr], () => {
const mergedDerivativeToken = compute ? compute(mergedToken, override, theme) : getComputedToken(mergedToken, override, theme, formatToken);
const actualToken = {
...mergedDerivativeToken
};
// Optimize for `useStyleRegister` performance
const mergedSalt = `${salt}_${cssVar.prefix}`;
const hashId = hash(mergedSalt);
const hashCls = `${hashPrefix}-${hashId}`;
actualToken._tokenKey = token2key(actualToken, mergedSalt);
// Replace token value with css variables
const [tokenWithCssVar, cssVarsStr] = transformToken(mergedDerivativeToken, cssVar.key, {
prefix: cssVar.prefix,
ignore: cssVar.ignore,
unitless: cssVar.unitless,
preserve: cssVar.preserve,
hashPriority,
hashCls: cssVar.hashed ? hashCls : undefined
});
tokenWithCssVar._hashId = hashId;
recordCleanToken(cssVar.key);
return [tokenWithCssVar, hashCls, actualToken, cssVarsStr, cssVar.key];
}, ([,,,, themeKey]) => {
// Remove token will remove all related style
cleanTokenStyle(themeKey, instanceId);
}, ([,,, cssVarsStr, themeKey]) => {
if (!cssVarsStr) {
return;
}
let mergedCSSConfig = {
mark: ATTR_MARK,
prepend: 'queue',
attachTo: container,
priority: -999
};
mergedCSSConfig = injectCSPNonce(mergedCSSConfig, nonce);
const style = updateCSS(cssVarsStr, hash(`css-var-${themeKey}`), mergedCSSConfig);
style[CSS_IN_JS_INSTANCE] = instanceId;
// Used for `useCacheToken` to remove on batch when token removed
style.setAttribute(ATTR_TOKEN, themeKey);
});
return cachedToken;
}
export const extract = (cache, effectStyles, options) => {
const [,, realToken, styleStr, cssVarKey] = cache;
const {
plain
} = options || {};
if (!styleStr) {
return null;
}
const styleId = realToken._tokenKey;
const order = -999;
// ====================== Style ======================
// Used for @rc-component/util
const sharedAttrs = {
'data-rc-order': 'prependQueue',
'data-rc-priority': `${order}`
};
const styleText = toStyleStr(styleStr, cssVarKey, styleId, sharedAttrs, plain);
return [order, styleId, styleText];
};
@@ -0,0 +1,3 @@
import type { DependencyList } from 'react';
declare const useEffectCleanupRegister: (deps?: DependencyList) => (fn: () => void) => void;
export default useEffectCleanupRegister;
@@ -0,0 +1,29 @@
import { warning } from "@rc-component/util/es/warning";
import { useEffect } from 'react';
// DO NOT register functions in useEffect cleanup function, or functions that registered will never be called.
const useEffectCleanupRegister = deps => {
const effectCleanups = [];
let cleanupFlag = false;
function register(fn) {
if (cleanupFlag) {
if (process.env.NODE_ENV !== 'production') {
warning(false, '[Ant Design CSS-in-JS] You are registering a cleanup function after unmount, which will not have any effect.');
}
return;
}
effectCleanups.push(fn);
}
useEffect(() => {
// Compatible with strict mode
cleanupFlag = false;
return () => {
cleanupFlag = true;
if (effectCleanups.length) {
effectCleanups.forEach(fn => fn());
}
};
}, deps);
return register;
};
export default useEffectCleanupRegister;
@@ -0,0 +1,6 @@
import { type KeyType } from '../Cache';
export type ExtractStyle<CacheValue> = (cache: CacheValue, effectStyles: Record<string, boolean>, options?: {
plain?: boolean;
autoPrefix?: boolean;
}) => [order: number, styleId: string, style: string] | null;
export default function useGlobalCache<CacheType>(prefix: string, keyPath: KeyType[], cacheFn: () => CacheType, onCacheRemove?: (cache: CacheType, fromHMR: boolean) => void, onCacheEffect?: (cachedValue: CacheType) => void): CacheType;
@@ -0,0 +1,77 @@
import * as React from 'react';
import { useInsertionEffect } from 'react';
import { pathKey } from "../Cache";
import StyleContext from "../StyleContext";
import useHMR from "./useHMR";
const effectMap = new Map();
export default function useGlobalCache(prefix, keyPath, cacheFn, onCacheRemove,
// Add additional effect trigger by `useInsertionEffect`
onCacheEffect) {
const {
cache: globalCache
} = React.useContext(StyleContext);
const fullPath = [prefix, ...keyPath];
const fullPathStr = pathKey(fullPath);
const HMRUpdate = useHMR();
const buildCache = updater => {
globalCache.opUpdate(fullPathStr, prevCache => {
const [times = 0, cache] = prevCache || [undefined, undefined];
// HMR should always ignore cache since developer may change it
let tmpCache = cache;
if (process.env.NODE_ENV !== 'production' && cache && HMRUpdate) {
onCacheRemove?.(tmpCache, HMRUpdate);
tmpCache = null;
}
const mergedCache = tmpCache || cacheFn();
const data = [times, mergedCache];
// Call updater if need additional logic
return updater ? updater(data) : data;
});
};
// Create cache
React.useMemo(() => {
buildCache();
}, /* eslint-disable react-hooks/exhaustive-deps */
[fullPathStr]
/* eslint-enable */);
let cacheEntity = globalCache.opGet(fullPathStr);
// HMR clean the cache but not trigger `useMemo` again
// Let's fallback of this
// ref https://github.com/ant-design/cssinjs/issues/127
if (process.env.NODE_ENV !== 'production' && !cacheEntity) {
buildCache();
cacheEntity = globalCache.opGet(fullPathStr);
}
const cacheContent = cacheEntity[1];
// Remove if no need anymore
useInsertionEffect(() => {
buildCache(([times, cache]) => [times + 1, cache]);
if (!effectMap.has(fullPathStr)) {
onCacheEffect?.(cacheContent);
effectMap.set(fullPathStr, true);
// 微任务清理缓存,可以认为是单次 batch render 中只触发一次 effect
Promise.resolve().then(() => {
effectMap.delete(fullPathStr);
});
}
return () => {
globalCache.opUpdate(fullPathStr, prevCache => {
const [times = 0, cache] = prevCache || [];
const nextCount = times - 1;
if (nextCount === 0) {
onCacheRemove?.(cache, false);
effectMap.delete(fullPathStr);
return null;
}
return [times - 1, cache];
});
};
}, [fullPathStr]);
return cacheContent;
}
@@ -0,0 +1,3 @@
declare function useProdHMR(): boolean;
declare const _default: typeof useProdHMR;
export default _default;
+26
View File
@@ -0,0 +1,26 @@
function useProdHMR() {
return false;
}
let webpackHMR = false;
function useDevHMR() {
return webpackHMR;
}
export default process.env.NODE_ENV === 'production' ? useProdHMR : useDevHMR;
// Webpack `module.hot.accept` do not support any deps update trigger
// We have to hack handler to force mark as HRM
if (process.env.NODE_ENV !== 'production' && typeof module !== 'undefined' && module && module.hot && typeof window !== 'undefined') {
// Use `globalThis` first, and `window` for older browsers
// const win = globalThis as any;
const win = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : null;
if (win && typeof win.webpackHotUpdate === 'function') {
const originWebpackHotUpdate = win.webpackHotUpdate;
win.webpackHotUpdate = (...args) => {
webpackHMR = true;
setTimeout(() => {
webpackHMR = false;
}, 0);
return originWebpackHotUpdate(...args);
};
}
}
@@ -0,0 +1,75 @@
import type * as CSS from 'csstype';
import type { Theme, Transformer } from '..';
import type Keyframes from '../Keyframes';
import type { Linter } from '../linters';
import type { HashPriority } from '../StyleContext';
import type { ExtractStyle } from './useGlobalCache';
declare const SKIP_CHECK = "_skip_check_";
declare const MULTI_VALUE = "_multi_value_";
export interface LayerConfig {
name: string;
dependencies?: string[];
}
export type CSSProperties = Omit<CSS.PropertiesFallback<number | string>, 'animationName'> & {
animationName?: CSS.PropertiesFallback<number | string>['animationName'] | Keyframes;
};
export type CSSPropertiesWithMultiValues = {
[K in keyof CSSProperties]: CSSProperties[K] | readonly Extract<CSSProperties[K], string>[] | {
[SKIP_CHECK]?: boolean;
[MULTI_VALUE]?: boolean;
value: CSSProperties[K] | CSSProperties[K][];
};
};
export type CSSPseudos = {
[K in CSS.Pseudos]?: CSSObject;
};
type ArrayCSSInterpolation = readonly CSSInterpolation[];
export type InterpolationPrimitive = null | undefined | boolean | number | string | CSSObject;
export type CSSInterpolation = InterpolationPrimitive | ArrayCSSInterpolation | Keyframes;
export type CSSOthersObject = Record<string, CSSInterpolation>;
export interface CSSObject extends CSSPropertiesWithMultiValues, CSSPseudos, CSSOthersObject {
}
export declare function normalizeStyle(styleStr: string, autoPrefix: boolean): string;
export interface ParseConfig {
hashId?: string;
hashPriority?: HashPriority;
layer?: LayerConfig;
path?: string;
transformers?: Transformer[];
linters?: Linter[];
}
export interface ParseInfo {
root?: boolean;
injectHash?: boolean;
parentSelectors: string[];
}
export declare const parseStyle: (interpolation: CSSInterpolation, config?: ParseConfig, { root, injectHash, parentSelectors }?: ParseInfo) => [parsedStr: string, effectStyle: Record<string, string>];
export declare function uniqueHash(path: (string | number)[], styleStr: string): string;
export declare const STYLE_PREFIX = "style";
type StyleCacheValue = [
styleStr: string,
styleId: string,
effectStyle: Record<string, string>,
clientOnly: boolean | undefined,
order: number
];
/**
* Register a style to the global style sheet.
*/
export default function useStyleRegister(info: {
theme: Theme<any, any>;
token: any;
path: string[];
hashId?: string;
layer?: LayerConfig;
nonce?: string | (() => string);
clientOnly?: boolean;
/**
* Tell cssinjs the insert order of style.
* It's useful when you need to insert style
* before other style to overwrite for the same selector priority.
*/
order?: number;
}, styleFn: () => CSSInterpolation): void;
export declare const extract: ExtractStyle<StyleCacheValue>;
export {};
@@ -0,0 +1,363 @@
import hash from '@emotion/hash';
import { removeCSS, updateCSS } from "@rc-component/util/es/Dom/dynamicCSS";
import * as React from 'react';
// @ts-ignore
import unitless from '@emotion/unitless';
import { compile, middleware, prefixer, serialize, stringify } from 'stylis';
import { contentQuotesLinter, hashedAnimationLinter } from "../linters";
import StyleContext, { ATTR_CACHE_PATH, ATTR_MARK, CSS_IN_JS_INSTANCE } from "../StyleContext";
import { injectCSPNonce, isClientSide, isNonNullable, toStyleStr, where } from "../util";
import { CSS_FILE_STYLE, existPath, getStyleAndHash } from "../util/cacheMapUtil";
import useGlobalCache from "./useGlobalCache";
const SKIP_CHECK = '_skip_check_';
const MULTI_VALUE = '_multi_value_';
// ============================================================================
// == Parser ==
// ============================================================================
// Preprocessor style content to browser support one
export function normalizeStyle(styleStr, autoPrefix) {
const serialized = autoPrefix ? serialize(compile(styleStr), middleware([prefixer, stringify])) : serialize(compile(styleStr), stringify);
return serialized.replace(/\{%%%\:[^;];}/g, ';');
}
function isCompoundCSSProperty(value) {
return typeof value === 'object' && value && (SKIP_CHECK in value || MULTI_VALUE in value);
}
// 注入 hash 值
function injectSelectorHash(key, hashId, hashPriority = 'high') {
if (!hashId) {
return key;
}
const hashSelector = where({
hashCls: hashId,
hashPriority
});
// 注入 hashId
const keys = key.split(',').map(k => {
const fullPath = k.trim().split(/\s+/);
// 如果 Selector 第一个是 HTML Element,那我们就插到它的后面。反之,就插到最前面。
let firstPath = fullPath[0] || '';
const htmlElement = firstPath.match(/^\w+/)?.[0] || '';
firstPath = `${htmlElement}${hashSelector}${firstPath.slice(htmlElement.length)}`;
return [firstPath, ...fullPath.slice(1)].join(' ');
});
return keys.join(',');
}
// Parse CSSObject to style content
export const parseStyle = (interpolation, config = {}, {
root,
injectHash,
parentSelectors
} = {
root: true,
parentSelectors: []
}) => {
const {
hashId,
layer,
path,
hashPriority,
transformers = [],
linters = []
} = config;
let styleStr = '';
let effectStyle = {};
function parseKeyframes(keyframes) {
const animationName = keyframes.getName(hashId);
if (!effectStyle[animationName]) {
const [parsedStr] = parseStyle(keyframes.style, config, {
root: false,
parentSelectors
});
effectStyle[animationName] = `@keyframes ${keyframes.getName(hashId)}${parsedStr}`;
}
}
function flattenList(list, fullList = []) {
list.forEach(item => {
if (Array.isArray(item)) {
flattenList(item, fullList);
} else if (item) {
fullList.push(item);
}
});
return fullList;
}
const flattenStyleList = flattenList(Array.isArray(interpolation) ? interpolation : [interpolation]);
flattenStyleList.forEach(originStyle => {
// Only root level can use raw string
const style = typeof originStyle === 'string' && !root ? {} : originStyle;
if (typeof style === 'string') {
styleStr += `${style}\n`;
} else if (style._keyframe) {
// Keyframe
parseKeyframes(style);
} else {
const mergedStyle = transformers.reduce((prev, trans) => trans?.visit?.(prev) || prev, style);
// Normal CSSObject
Object.keys(mergedStyle).forEach(key => {
const value = mergedStyle[key];
if (typeof value === 'object' && value && (key !== 'animationName' || !value._keyframe) && !isCompoundCSSProperty(value)) {
let subInjectHash = false;
// 当成嵌套对象来处理
let mergedKey = key.trim();
// Whether treat child as root. In most case it is false.
let nextRoot = false;
// 拆分多个选择器
if ((root || injectHash) && hashId) {
if (mergedKey.startsWith('@')) {
// 略过媒体查询,交给子节点继续插入 hashId
subInjectHash = true;
} else if (mergedKey === '&') {
// 抹掉 root selector 上的单个 &
mergedKey = injectSelectorHash('', hashId, hashPriority);
} else {
// 注入 hashId
mergedKey = injectSelectorHash(key, hashId, hashPriority);
}
} else if (root && !hashId && (mergedKey === '&' || mergedKey === '')) {
// In case of `{ '&': { a: { color: 'red' } } }` or `{ '': { a: { color: 'red' } } }` without hashId,
// we will get `&{a:{color:red;}}` or `{a:{color:red;}}` string for stylis to compile.
// But it does not conform to stylis syntax,
// and finally we will get `{color:red;}` as css, which is wrong.
// So we need to remove key in root, and treat child `{ a: { color: 'red' } }` as root.
mergedKey = '';
nextRoot = true;
}
const [parsedStr, childEffectStyle] = parseStyle(value, config, {
root: nextRoot,
injectHash: subInjectHash,
parentSelectors: [...parentSelectors, mergedKey]
});
effectStyle = {
...effectStyle,
...childEffectStyle
};
styleStr += `${mergedKey}${parsedStr}`;
} else {
function appendStyle(cssKey, cssValue) {
if (process.env.NODE_ENV !== 'production' && (typeof value !== 'object' || !value?.[SKIP_CHECK])) {
[contentQuotesLinter, hashedAnimationLinter, ...linters].forEach(linter => linter(cssKey, cssValue, {
path,
hashId,
parentSelectors
}));
}
// 如果是样式则直接插入
const styleName = cssKey.replace(/[A-Z]/g, match => `-${match.toLowerCase()}`);
// Auto suffix with px
let formatValue = cssValue;
if (!unitless[cssKey] && typeof formatValue === 'number' && formatValue !== 0) {
formatValue = `${formatValue}px`;
}
// handle animationName & Keyframe value
if (cssKey === 'animationName' && cssValue?._keyframe) {
parseKeyframes(cssValue);
formatValue = cssValue.getName(hashId);
}
styleStr += `${styleName}:${formatValue};`;
}
const actualValue = value?.value ?? value;
if (typeof value === 'object' && value?.[MULTI_VALUE] && Array.isArray(actualValue)) {
actualValue.forEach(item => {
appendStyle(key, item);
});
} else {
if (isNonNullable(actualValue)) {
appendStyle(key, actualValue);
}
}
}
});
}
});
if (!root) {
styleStr = `{${styleStr}}`;
} else if (layer) {
// fixme: https://github.com/thysultan/stylis/pull/339
if (styleStr) {
styleStr = `@layer ${layer.name} {${styleStr}}`;
}
if (layer.dependencies) {
effectStyle[`@layer ${layer.name}`] = layer.dependencies.map(deps => `@layer ${deps}, ${layer.name};`).join('\n');
}
}
return [styleStr, effectStyle];
};
// ============================================================================
// == Register ==
// ============================================================================
export function uniqueHash(path, styleStr) {
return hash(`${path.join('%')}${styleStr}`);
}
export const STYLE_PREFIX = 'style';
/**
* Register a style to the global style sheet.
*/
export default function useStyleRegister(info, styleFn) {
const {
path,
hashId,
layer,
nonce,
clientOnly,
order = 0
} = info;
const {
mock,
hashPriority,
container,
transformers,
linters,
cache,
layer: enableLayer,
autoPrefix
} = React.useContext(StyleContext);
const fullPath = [hashId || ''];
if (enableLayer) {
fullPath.push('layer');
}
fullPath.push(...path);
// Check if need insert style
let isMergedClientSide = isClientSide;
if (process.env.NODE_ENV !== 'production' && mock !== undefined) {
isMergedClientSide = mock === 'client';
}
useGlobalCache(STYLE_PREFIX, fullPath,
// Create cache if needed
() => {
const cachePath = fullPath.join('|');
// Get style from SSR inline style directly
if (existPath(cachePath)) {
const [inlineCacheStyleStr, styleHash] = getStyleAndHash(cachePath);
if (inlineCacheStyleStr) {
return [inlineCacheStyleStr, styleHash, {}, clientOnly, order];
}
}
// Generate style
const styleObj = styleFn();
const [parsedStyle, effectStyle] = parseStyle(styleObj, {
hashId,
hashPriority,
layer: enableLayer ? layer : undefined,
path: path.join('-'),
transformers,
linters
});
const styleStr = normalizeStyle(parsedStyle, autoPrefix || false);
const styleId = uniqueHash(fullPath, styleStr);
return [styleStr, styleId, effectStyle, clientOnly, order];
},
// Remove cache if no need
(cacheValue, fromHMR) => {
const [, styleId] = cacheValue;
if (fromHMR && isClientSide) {
removeCSS(styleId, {
mark: ATTR_MARK,
attachTo: container
});
}
},
// Effect: Inject style here
cacheValue => {
const [styleStr, styleId, effectStyle,, priority] = cacheValue;
if (isMergedClientSide && styleStr !== CSS_FILE_STYLE) {
let mergedCSSConfig = {
mark: ATTR_MARK,
prepend: enableLayer ? false : 'queue',
attachTo: container,
priority
};
mergedCSSConfig = injectCSPNonce(mergedCSSConfig, nonce);
// ================= Split Effect Style =================
// We will split effectStyle here since @layer should be at the top level
const effectLayerKeys = [];
const effectRestKeys = [];
Object.keys(effectStyle).forEach(key => {
if (key.startsWith('@layer')) {
effectLayerKeys.push(key);
} else {
effectRestKeys.push(key);
}
});
// ================= Inject Layer Style =================
// Inject layer style
effectLayerKeys.forEach(effectKey => {
updateCSS(normalizeStyle(effectStyle[effectKey], autoPrefix || false), `_layer-${effectKey}`, {
...mergedCSSConfig,
prepend: true
});
});
// ==================== Inject Style ====================
// Inject style
const style = updateCSS(styleStr, styleId, mergedCSSConfig);
style[CSS_IN_JS_INSTANCE] = cache.instanceId;
// Debug usage. Dev only
if (process.env.NODE_ENV !== 'production') {
style.setAttribute(ATTR_CACHE_PATH, fullPath.join('|'));
}
// ================ Inject Effect Style =================
// Inject client side effect style
effectRestKeys.forEach(effectKey => {
updateCSS(normalizeStyle(effectStyle[effectKey], autoPrefix || false), `_effect-${effectKey}`, mergedCSSConfig);
});
}
});
}
export const extract = (cache, effectStyles, options) => {
const [styleStr, styleId, effectStyle, clientOnly, order] = cache;
const {
plain,
autoPrefix
} = options || {};
// Skip client only style
if (clientOnly) {
return null;
}
let keyStyleText = styleStr;
// ====================== Share ======================
// Used for @rc-component/util
const sharedAttrs = {
'data-rc-order': 'prependQueue',
'data-rc-priority': `${order}`
};
// ====================== Style ======================
keyStyleText = toStyleStr(styleStr, undefined, styleId, sharedAttrs, plain);
// =============== Create effect style ===============
if (effectStyle) {
Object.keys(effectStyle).forEach(effectKey => {
// Effect style can be reused
if (!effectStyles[effectKey]) {
effectStyles[effectKey] = true;
const effectStyleStr = normalizeStyle(effectStyle[effectKey], autoPrefix || false);
const effectStyleHTML = toStyleStr(effectStyleStr, undefined, `_effect-${effectKey}`, sharedAttrs, plain);
if (effectKey.startsWith('@layer')) {
keyStyleText = effectStyleHTML + keyStyleText;
} else {
keyStyleText += effectStyleHTML;
}
}
});
}
return [order, styleId, keyStyleText];
};
+23
View File
@@ -0,0 +1,23 @@
import extractStyle from './extractStyle';
import useCacheToken, { getComputedToken } from './hooks/useCacheToken';
import useCSSVarRegister from './hooks/useCSSVarRegister';
import type { CSSInterpolation, CSSObject } from './hooks/useStyleRegister';
import useStyleRegister from './hooks/useStyleRegister';
import Keyframes from './Keyframes';
import type { Linter } from './linters';
import { legacyNotSelectorLinter, logicalPropertiesLinter, NaNLinter, parentSelectorLinter } from './linters';
import type { StyleProviderProps } from './StyleContext';
import StyleContext, { createCache, StyleProvider } from './StyleContext';
import type { AbstractCalculator, DerivativeFunc, TokenType } from './theme';
import { createTheme, genCalc, Theme } from './theme';
import type { Transformer } from './transformers/interface';
import autoPrefixTransformer from './transformers/autoPrefix';
import legacyLogicalPropertiesTransformer from './transformers/legacyLogicalProperties';
import px2remTransformer from './transformers/px2rem';
import { unit } from './util';
import { token2CSSVar } from './util/css-variables';
export { Theme, createTheme, useStyleRegister, useCSSVarRegister, useCacheToken, createCache, StyleProvider, StyleContext, Keyframes, extractStyle, getComputedToken, autoPrefixTransformer, legacyLogicalPropertiesTransformer, px2remTransformer, logicalPropertiesLinter, legacyNotSelectorLinter, parentSelectorLinter, NaNLinter, token2CSSVar, unit, genCalc, };
export type { TokenType, CSSObject, CSSInterpolation, DerivativeFunc, Transformer, Linter, StyleProviderProps, AbstractCalculator, };
export declare const _experimental: {
supportModernCSS: () => boolean;
};
+23
View File
@@ -0,0 +1,23 @@
import extractStyle from "./extractStyle";
import useCacheToken, { getComputedToken } from "./hooks/useCacheToken";
import useCSSVarRegister from "./hooks/useCSSVarRegister";
import useStyleRegister from "./hooks/useStyleRegister";
import Keyframes from "./Keyframes";
import { legacyNotSelectorLinter, logicalPropertiesLinter, NaNLinter, parentSelectorLinter } from "./linters";
import StyleContext, { createCache, StyleProvider } from "./StyleContext";
import { createTheme, genCalc, Theme } from "./theme";
import autoPrefixTransformer from "./transformers/autoPrefix";
import legacyLogicalPropertiesTransformer from "./transformers/legacyLogicalProperties";
import px2remTransformer from "./transformers/px2rem";
import { supportLogicProps, supportWhere, unit } from "./util";
import { token2CSSVar } from "./util/css-variables";
export { Theme, createTheme, useStyleRegister, useCSSVarRegister, useCacheToken, createCache, StyleProvider, StyleContext, Keyframes, extractStyle, getComputedToken,
// Transformer
autoPrefixTransformer, legacyLogicalPropertiesTransformer, px2remTransformer,
// Linters
logicalPropertiesLinter, legacyNotSelectorLinter, parentSelectorLinter, NaNLinter,
// util
token2CSSVar, unit, genCalc };
export const _experimental = {
supportModernCSS: () => supportWhere() && supportLogicProps()
};
@@ -0,0 +1,3 @@
import type { Linter } from './interface';
declare const linter: Linter;
export default linter;
@@ -0,0 +1,7 @@
import { lintWarning } from "./utils";
const linter = (key, value, info) => {
if (typeof value === 'string' && /NaN/g.test(value) || Number.isNaN(value)) {
lintWarning(`Unexpected 'NaN' in property '${key}: ${value}'.`, info);
}
};
export default linter;
@@ -0,0 +1,3 @@
import type { Linter } from './interface';
declare const linter: Linter;
export default linter;
@@ -0,0 +1,12 @@
import { lintWarning } from "./utils";
const linter = (key, value, info) => {
if (key === 'content') {
// From emotion: https://github.com/emotion-js/emotion/blob/main/packages/serialize/src/index.js#L63
const contentValuePattern = /(attr|counters?|url|(((repeating-)?(linear|radial))|conic)-gradient)\(|(no-)?(open|close)-quote/;
const contentValues = ['normal', 'none', 'initial', 'inherit', 'unset'];
if (typeof value !== 'string' || contentValues.indexOf(value) === -1 && !contentValuePattern.test(value) && !value.startsWith('var(') && (value.charAt(0) !== value.charAt(value.length - 1) || value.charAt(0) !== '"' && value.charAt(0) !== "'")) {
lintWarning(`You seem to be using a value for 'content' without quotes, try replacing it with \`content: '"${value}"'\`.`, info);
}
}
};
export default linter;
@@ -0,0 +1,3 @@
import type { Linter } from './interface';
declare const linter: Linter;
export default linter;
@@ -0,0 +1,9 @@
import { lintWarning } from "./utils";
const linter = (key, value, info) => {
if (key === 'animation') {
if (info.hashId && value !== 'none') {
lintWarning(`You seem to be using hashed animation '${value}', in which case 'animationName' with Keyframe as value is recommended.`, info);
}
}
};
export default linter;
@@ -0,0 +1,7 @@
export { default as contentQuotesLinter } from './contentQuotesLinter';
export { default as hashedAnimationLinter } from './hashedAnimationLinter';
export type { Linter } from './interface';
export { default as legacyNotSelectorLinter } from './legacyNotSelectorLinter';
export { default as logicalPropertiesLinter } from './logicalPropertiesLinter';
export { default as NaNLinter } from './NaNLinter';
export { default as parentSelectorLinter } from './parentSelectorLinter';
+6
View File
@@ -0,0 +1,6 @@
export { default as contentQuotesLinter } from "./contentQuotesLinter";
export { default as hashedAnimationLinter } from "./hashedAnimationLinter";
export { default as legacyNotSelectorLinter } from "./legacyNotSelectorLinter";
export { default as logicalPropertiesLinter } from "./logicalPropertiesLinter";
export { default as NaNLinter } from "./NaNLinter";
export { default as parentSelectorLinter } from "./parentSelectorLinter";
@@ -0,0 +1,8 @@
export interface LinterInfo {
path?: string;
hashId?: string;
parentSelectors: string[];
}
export interface Linter {
(key: string, value: string | number, info: LinterInfo): void;
}
@@ -0,0 +1 @@
export {};
@@ -0,0 +1,3 @@
import type { Linter } from './interface';
declare const linter: Linter;
export default linter;
@@ -0,0 +1,25 @@
import { lintWarning } from "./utils";
function isConcatSelector(selector) {
const notContent = selector.match(/:not\(([^)]*)\)/)?.[1] || '';
// split selector. e.g.
// `h1#a.b` => ['h1', #a', '.b']
const splitCells = notContent.split(/(\[[^[]*])|(?=[.#])/).filter(str => str);
return splitCells.length > 1;
}
function parsePath(info) {
return info.parentSelectors.reduce((prev, cur) => {
if (!prev) {
return cur;
}
return cur.includes('&') ? cur.replace(/&/g, prev) : `${prev} ${cur}`;
}, '');
}
const linter = (key, value, info) => {
const parentSelectorPath = parsePath(info);
const notList = parentSelectorPath.match(/:not\([^)]*\)/g) || [];
if (notList.length > 0 && notList.some(isConcatSelector)) {
lintWarning(`Concat ':not' selector not support in legacy browsers.`, info);
}
};
export default linter;
@@ -0,0 +1,3 @@
import type { Linter } from './interface';
declare const linter: Linter;
export default linter;
@@ -0,0 +1,72 @@
import { lintWarning } from "./utils";
const linter = (key, value, info) => {
switch (key) {
case 'marginLeft':
case 'marginRight':
case 'paddingLeft':
case 'paddingRight':
case 'left':
case 'right':
case 'borderLeft':
case 'borderLeftWidth':
case 'borderLeftStyle':
case 'borderLeftColor':
case 'borderRight':
case 'borderRightWidth':
case 'borderRightStyle':
case 'borderRightColor':
case 'borderTopLeftRadius':
case 'borderTopRightRadius':
case 'borderBottomLeftRadius':
case 'borderBottomRightRadius':
lintWarning(`You seem to be using non-logical property '${key}' which is not compatible with RTL mode. Please use logical properties and values instead. For more information: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Logical_Properties.`, info);
return;
case 'margin':
case 'padding':
case 'borderWidth':
case 'borderStyle':
// case 'borderColor':
if (typeof value === 'string') {
const valueArr = value.split(' ').map(item => item.trim());
if (valueArr.length === 4 && valueArr[1] !== valueArr[3]) {
lintWarning(`You seem to be using '${key}' property with different left ${key} and right ${key}, which is not compatible with RTL mode. Please use logical properties and values instead. For more information: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Logical_Properties.`, info);
}
}
return;
case 'clear':
case 'textAlign':
if (value === 'left' || value === 'right') {
lintWarning(`You seem to be using non-logical value '${value}' of ${key}, which is not compatible with RTL mode. Please use logical properties and values instead. For more information: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Logical_Properties.`, info);
}
return;
case 'borderRadius':
if (typeof value === 'string') {
const radiusGroups = value.split('/').map(item => item.trim());
const invalid = radiusGroups.reduce((result, group) => {
if (result) {
return result;
}
const radiusArr = group.split(' ').map(item => item.trim());
// borderRadius: '2px 4px'
if (radiusArr.length >= 2 && radiusArr[0] !== radiusArr[1]) {
return true;
}
// borderRadius: '4px 4px 2px'
if (radiusArr.length === 3 && radiusArr[1] !== radiusArr[2]) {
return true;
}
// borderRadius: '4px 4px 2px 4px'
if (radiusArr.length === 4 && radiusArr[2] !== radiusArr[3]) {
return true;
}
return result;
}, false);
if (invalid) {
lintWarning(`You seem to be using non-logical value '${value}' of ${key}, which is not compatible with RTL mode. Please use logical properties and values instead. For more information: https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Logical_Properties.`, info);
}
}
return;
default:
}
};
export default linter;
@@ -0,0 +1,3 @@
import type { Linter } from '..';
declare const linter: Linter;
export default linter;
@@ -0,0 +1,10 @@
import { lintWarning } from "./utils";
const linter = (key, value, info) => {
if (info.parentSelectors.some(selector => {
const selectors = selector.split(',');
return selectors.some(item => item.split('&').length > 2);
})) {
lintWarning('Should not use more than one `&` in a selector.', info);
}
};
export default linter;
@@ -0,0 +1,2 @@
import type { LinterInfo } from './interface';
export declare function lintWarning(message: string, info: LinterInfo): void;
+8
View File
@@ -0,0 +1,8 @@
import devWarning from "@rc-component/util/es/warning";
export function lintWarning(message, info) {
const {
path,
parentSelectors
} = info;
devWarning(false, `[Ant Design CSS-in-JS] ${path ? `Error in ${path}: ` : ''}${message}${parentSelectors.length ? ` Selector: ${parentSelectors.join(' | ')}` : ''}`);
}
+11
View File
@@ -0,0 +1,11 @@
import type { DerivativeFunc, TokenType } from './interface';
/**
* Theme with algorithms to derive tokens from design tokens.
* Use `createTheme` first which will help to manage the theme instance cache.
*/
export default class Theme<DesignToken extends TokenType, DerivativeToken extends TokenType> {
private derivatives;
readonly id: number;
constructor(derivatives: DerivativeFunc<DesignToken, DerivativeToken> | DerivativeFunc<DesignToken, DerivativeToken>[]);
getDerivativeToken(token: DesignToken): DerivativeToken;
}
+22
View File
@@ -0,0 +1,22 @@
import { warning } from "@rc-component/util/es/warning";
let uuid = 0;
/**
* Theme with algorithms to derive tokens from design tokens.
* Use `createTheme` first which will help to manage the theme instance cache.
*/
export default class Theme {
derivatives;
id;
constructor(derivatives) {
this.derivatives = Array.isArray(derivatives) ? derivatives : [derivatives];
this.id = uuid;
if (derivatives.length === 0) {
warning(derivatives.length > 0, '[Ant Design CSS-in-JS] Theme should have at least one derivative function.');
}
uuid += 1;
}
getDerivativeToken(token) {
return this.derivatives.reduce((result, derivative) => derivative(token, result), undefined);
}
}
@@ -0,0 +1,20 @@
import type Theme from './Theme';
import type { DerivativeFunc } from './interface';
type DerivativeOptions = DerivativeFunc<any, any>[];
export declare function sameDerivativeOption(left: DerivativeOptions, right: DerivativeOptions): boolean;
export default class ThemeCache {
static MAX_CACHE_SIZE: number;
static MAX_CACHE_OFFSET: number;
private readonly cache;
private keys;
private cacheCallTimes;
constructor();
size(): number;
private internalGet;
get(derivativeOption: DerivativeOptions): Theme<any, any> | undefined;
has(derivativeOption: DerivativeOptions): boolean;
set(derivativeOption: DerivativeOptions, value: Theme<any, any>): void;
private deleteByPath;
delete(derivativeOption: DerivativeOptions): Theme<any, any> | undefined;
}
export {};
+110
View File
@@ -0,0 +1,110 @@
// ================================== Cache ==================================
export function sameDerivativeOption(left, right) {
if (left.length !== right.length) {
return false;
}
for (let i = 0; i < left.length; i++) {
if (left[i] !== right[i]) {
return false;
}
}
return true;
}
export default class ThemeCache {
static MAX_CACHE_SIZE = 20;
static MAX_CACHE_OFFSET = 5;
cache;
keys;
cacheCallTimes;
constructor() {
this.cache = new Map();
this.keys = [];
this.cacheCallTimes = 0;
}
size() {
return this.keys.length;
}
internalGet(derivativeOption, updateCallTimes = false) {
let cache = {
map: this.cache
};
derivativeOption.forEach(derivative => {
if (!cache) {
cache = undefined;
} else {
cache = cache?.map?.get(derivative);
}
});
if (cache?.value && updateCallTimes) {
cache.value[1] = this.cacheCallTimes++;
}
return cache?.value;
}
get(derivativeOption) {
return this.internalGet(derivativeOption, true)?.[0];
}
has(derivativeOption) {
return !!this.internalGet(derivativeOption);
}
set(derivativeOption, value) {
// New cache
if (!this.has(derivativeOption)) {
if (this.size() + 1 > ThemeCache.MAX_CACHE_SIZE + ThemeCache.MAX_CACHE_OFFSET) {
const [targetKey] = this.keys.reduce((result, key) => {
const [, callTimes] = result;
if (this.internalGet(key)[1] < callTimes) {
return [key, this.internalGet(key)[1]];
}
return result;
}, [this.keys[0], this.cacheCallTimes]);
this.delete(targetKey);
}
this.keys.push(derivativeOption);
}
let cache = this.cache;
derivativeOption.forEach((derivative, index) => {
if (index === derivativeOption.length - 1) {
cache.set(derivative, {
value: [value, this.cacheCallTimes++]
});
} else {
const cacheValue = cache.get(derivative);
if (!cacheValue) {
cache.set(derivative, {
map: new Map()
});
} else if (!cacheValue.map) {
cacheValue.map = new Map();
}
cache = cache.get(derivative).map;
}
});
}
deleteByPath(currentCache, derivatives) {
const cache = currentCache.get(derivatives[0]);
if (derivatives.length === 1) {
if (!cache.map) {
currentCache.delete(derivatives[0]);
} else {
currentCache.set(derivatives[0], {
map: cache.map
});
}
return cache.value?.[0];
}
const result = this.deleteByPath(cache.map, derivatives.slice(1));
if ((!cache.map || cache.map.size === 0) && !cache.value) {
currentCache.delete(derivatives[0]);
}
return result;
}
delete(derivativeOption) {
// If cache exists
if (this.has(derivativeOption)) {
this.keys = this.keys.filter(item => !sameDerivativeOption(item, derivativeOption));
return this.deleteByPath(this.cache, derivativeOption);
}
return undefined;
}
}
@@ -0,0 +1,15 @@
import AbstractCalculator from './calculator';
export default class CSSCalculator extends AbstractCalculator {
result: string;
unitlessCssVar: Set<string>;
lowPriority?: boolean;
constructor(num: number | string | AbstractCalculator, unitlessCssVar: Set<string>);
add(num: number | string | AbstractCalculator): this;
sub(num: number | string | AbstractCalculator): this;
mul(num: number | string | AbstractCalculator): this;
div(num: number | string | AbstractCalculator): this;
getResult(force?: boolean): string;
equal(options?: {
unit?: boolean;
}): string;
}
@@ -0,0 +1,87 @@
import AbstractCalculator from "./calculator";
const CALC_UNIT = 'CALC_UNIT';
const regexp = new RegExp(CALC_UNIT, 'g');
function unit(value) {
if (typeof value === 'number') {
return `${value}${CALC_UNIT}`;
}
return value;
}
export default class CSSCalculator extends AbstractCalculator {
result = '';
unitlessCssVar;
lowPriority;
constructor(num, unitlessCssVar) {
super();
const numType = typeof num;
this.unitlessCssVar = unitlessCssVar;
if (num instanceof CSSCalculator) {
this.result = `(${num.result})`;
} else if (numType === 'number') {
this.result = unit(num);
} else if (numType === 'string') {
this.result = num;
}
}
add(num) {
if (num instanceof CSSCalculator) {
this.result = `${this.result} + ${num.getResult()}`;
} else if (typeof num === 'number' || typeof num === 'string') {
this.result = `${this.result} + ${unit(num)}`;
}
this.lowPriority = true;
return this;
}
sub(num) {
if (num instanceof CSSCalculator) {
this.result = `${this.result} - ${num.getResult()}`;
} else if (typeof num === 'number' || typeof num === 'string') {
this.result = `${this.result} - ${unit(num)}`;
}
this.lowPriority = true;
return this;
}
mul(num) {
if (this.lowPriority) {
this.result = `(${this.result})`;
}
if (num instanceof CSSCalculator) {
this.result = `${this.result} * ${num.getResult(true)}`;
} else if (typeof num === 'number' || typeof num === 'string') {
this.result = `${this.result} * ${num}`;
}
this.lowPriority = false;
return this;
}
div(num) {
if (this.lowPriority) {
this.result = `(${this.result})`;
}
if (num instanceof CSSCalculator) {
this.result = `${this.result} / ${num.getResult(true)}`;
} else if (typeof num === 'number' || typeof num === 'string') {
this.result = `${this.result} / ${num}`;
}
this.lowPriority = false;
return this;
}
getResult(force) {
return this.lowPriority || force ? `(${this.result})` : this.result;
}
equal(options) {
const {
unit: cssUnit
} = options || {};
let mergedUnit = true;
if (typeof cssUnit === 'boolean') {
mergedUnit = cssUnit;
} else if (Array.from(this.unitlessCssVar).some(cssVar => this.result.includes(cssVar))) {
mergedUnit = false;
}
this.result = this.result.replace(regexp, mergedUnit ? 'px' : '');
if (typeof this.lowPriority !== 'undefined') {
return `calc(${this.result})`;
}
return this.result;
}
}
@@ -0,0 +1,10 @@
import AbstractCalculator from './calculator';
export default class NumCalculator extends AbstractCalculator {
result: number;
constructor(num: number | string | AbstractCalculator);
add(num: number | string | AbstractCalculator): this;
sub(num: number | string | AbstractCalculator): this;
mul(num: number | string | AbstractCalculator): this;
div(num: number | string | AbstractCalculator): this;
equal(): number;
}
@@ -0,0 +1,47 @@
import AbstractCalculator from "./calculator";
export default class NumCalculator extends AbstractCalculator {
result = 0;
constructor(num) {
super();
if (num instanceof NumCalculator) {
this.result = num.result;
} else if (typeof num === 'number') {
this.result = num;
}
}
add(num) {
if (num instanceof NumCalculator) {
this.result += num.result;
} else if (typeof num === 'number') {
this.result += num;
}
return this;
}
sub(num) {
if (num instanceof NumCalculator) {
this.result -= num.result;
} else if (typeof num === 'number') {
this.result -= num;
}
return this;
}
mul(num) {
if (num instanceof NumCalculator) {
this.result *= num.result;
} else if (typeof num === 'number') {
this.result *= num;
}
return this;
}
div(num) {
if (num instanceof NumCalculator) {
this.result /= num.result;
} else if (typeof num === 'number') {
this.result /= num;
}
return this;
}
equal() {
return this.result;
}
}
@@ -0,0 +1,30 @@
declare abstract class AbstractCalculator {
/**
* @descCN 计算两数的和,例如:1 + 2
* @descEN Calculate the sum of two numbers, e.g. 1 + 2
*/
abstract add(num: number | string | AbstractCalculator): this;
/**
* @descCN 计算两数的差,例如:1 - 2
* @descEN Calculate the difference between two numbers, e.g. 1 - 2
*/
abstract sub(num: number | string | AbstractCalculator): this;
/**
* @descCN 计算两数的积,例如:1 * 2
* @descEN Calculate the product of two numbers, e.g. 1 * 2
*/
abstract mul(num: number | string | AbstractCalculator): this;
/**
* @descCN 计算两数的商,例如:1 / 2
* @descEN Calculate the quotient of two numbers, e.g. 1 / 2
*/
abstract div(num: number | string | AbstractCalculator): this;
/**
* @descCN 获取计算结果
* @descEN Get the calculation result
*/
abstract equal(options?: {
unit?: boolean;
}): string | number;
}
export default AbstractCalculator;
@@ -0,0 +1,2 @@
class AbstractCalculator {}
export default AbstractCalculator;
@@ -0,0 +1,5 @@
import type AbstractCalculator from './calculator';
import CSSCalculator from './CSSCalculator';
import NumCalculator from './NumCalculator';
declare const genCalc: (type: 'css' | 'js', unitlessCssVar: Set<string>) => (num: number | string | AbstractCalculator) => CSSCalculator | NumCalculator;
export default genCalc;
@@ -0,0 +1,7 @@
import CSSCalculator from "./CSSCalculator";
import NumCalculator from "./NumCalculator";
const genCalc = (type, unitlessCssVar) => {
const Calculator = type === 'css' ? CSSCalculator : NumCalculator;
return num => new Calculator(num, unitlessCssVar);
};
export default genCalc;
@@ -0,0 +1,6 @@
import Theme from './Theme';
import type { DerivativeFunc, TokenType } from './interface';
/**
* Same as new Theme, but will always return same one if `derivative` not changed.
*/
export default function createTheme<DesignToken extends TokenType, DerivativeToken extends TokenType>(derivatives: DerivativeFunc<DesignToken, DerivativeToken>[] | DerivativeFunc<DesignToken, DerivativeToken>): Theme<any, any>;
@@ -0,0 +1,17 @@
import ThemeCache from "./ThemeCache";
import Theme from "./Theme";
const cacheThemes = new ThemeCache();
/**
* Same as new Theme, but will always return same one if `derivative` not changed.
*/
export default function createTheme(derivatives) {
const derivativeArr = Array.isArray(derivatives) ? derivatives : [derivatives];
// Create new theme if not exist
if (!cacheThemes.has(derivativeArr)) {
cacheThemes.set(derivativeArr, new Theme(derivativeArr));
}
// Get theme from cache and return
return cacheThemes.get(derivativeArr);
}
+6
View File
@@ -0,0 +1,6 @@
export { default as genCalc } from './calc';
export type { default as AbstractCalculator } from './calc/calculator';
export { default as createTheme } from './createTheme';
export type { DerivativeFunc, TokenType } from './interface';
export { default as Theme } from './Theme';
export { default as ThemeCache } from './ThemeCache';
+4
View File
@@ -0,0 +1,4 @@
export { default as genCalc } from "./calc";
export { default as createTheme } from "./createTheme";
export { default as Theme } from "./Theme";
export { default as ThemeCache } from "./ThemeCache";
@@ -0,0 +1,2 @@
export type TokenType = object;
export type DerivativeFunc<DesignToken extends TokenType, DerivativeToken extends TokenType> = (designToken: DesignToken, derivativeToken?: DerivativeToken) => DerivativeToken;
@@ -0,0 +1 @@
export {};
@@ -0,0 +1,4 @@
import type { Transformer } from './interface';
export declare const AUTO_PREFIX: {};
declare const transform: Transformer;
export default transform;
@@ -0,0 +1,3 @@
export const AUTO_PREFIX = {};
const transform = AUTO_PREFIX;
export default transform;
@@ -0,0 +1,4 @@
import type { CSSObject } from '..';
export interface Transformer {
visit?: (cssObj: CSSObject) => CSSObject;
}
@@ -0,0 +1 @@
export {};
@@ -0,0 +1,12 @@
import type { Transformer } from './interface';
/**
* Convert css logical properties to legacy properties.
* Such as: `margin-block-start` to `margin-top`.
* Transform list:
* - inset
* - margin
* - padding
* - border
*/
declare const transform: Transformer;
export default transform;
@@ -0,0 +1,143 @@
function splitValues(value) {
if (typeof value === 'number') {
return [[value], false];
}
const rawStyle = String(value).trim();
const importantCells = rawStyle.match(/(.*)(!important)/);
const splitStyle = (importantCells ? importantCells[1] : rawStyle).trim().split(/\s+/);
// Combine styles split in brackets, like `calc(1px + 2px)`
let temp = [];
let brackets = 0;
return [splitStyle.reduce((list, item) => {
if (item.includes('(') || item.includes(')')) {
const left = item.split('(').length - 1;
const right = item.split(')').length - 1;
brackets += left - right;
}
if (brackets >= 0) temp.push(item);
if (brackets === 0) {
list.push(temp.join(' '));
temp = [];
}
return list;
}, []), !!importantCells];
}
function noSplit(list) {
list.notSplit = true;
return list;
}
const keyMap = {
// Inset
inset: ['top', 'right', 'bottom', 'left'],
insetBlock: ['top', 'bottom'],
insetBlockStart: ['top'],
insetBlockEnd: ['bottom'],
insetInline: ['left', 'right'],
insetInlineStart: ['left'],
insetInlineEnd: ['right'],
// Margin
marginBlock: ['marginTop', 'marginBottom'],
marginBlockStart: ['marginTop'],
marginBlockEnd: ['marginBottom'],
marginInline: ['marginLeft', 'marginRight'],
marginInlineStart: ['marginLeft'],
marginInlineEnd: ['marginRight'],
// Padding
paddingBlock: ['paddingTop', 'paddingBottom'],
paddingBlockStart: ['paddingTop'],
paddingBlockEnd: ['paddingBottom'],
paddingInline: ['paddingLeft', 'paddingRight'],
paddingInlineStart: ['paddingLeft'],
paddingInlineEnd: ['paddingRight'],
// Border
borderBlock: noSplit(['borderTop', 'borderBottom']),
borderBlockStart: noSplit(['borderTop']),
borderBlockEnd: noSplit(['borderBottom']),
borderInline: noSplit(['borderLeft', 'borderRight']),
borderInlineStart: noSplit(['borderLeft']),
borderInlineEnd: noSplit(['borderRight']),
// Border width
borderBlockWidth: ['borderTopWidth', 'borderBottomWidth'],
borderBlockStartWidth: ['borderTopWidth'],
borderBlockEndWidth: ['borderBottomWidth'],
borderInlineWidth: ['borderLeftWidth', 'borderRightWidth'],
borderInlineStartWidth: ['borderLeftWidth'],
borderInlineEndWidth: ['borderRightWidth'],
// Border style
borderBlockStyle: ['borderTopStyle', 'borderBottomStyle'],
borderBlockStartStyle: ['borderTopStyle'],
borderBlockEndStyle: ['borderBottomStyle'],
borderInlineStyle: ['borderLeftStyle', 'borderRightStyle'],
borderInlineStartStyle: ['borderLeftStyle'],
borderInlineEndStyle: ['borderRightStyle'],
// Border color
borderBlockColor: ['borderTopColor', 'borderBottomColor'],
borderBlockStartColor: ['borderTopColor'],
borderBlockEndColor: ['borderBottomColor'],
borderInlineColor: ['borderLeftColor', 'borderRightColor'],
borderInlineStartColor: ['borderLeftColor'],
borderInlineEndColor: ['borderRightColor'],
// Border radius
borderStartStartRadius: ['borderTopLeftRadius'],
borderStartEndRadius: ['borderTopRightRadius'],
borderEndStartRadius: ['borderBottomLeftRadius'],
borderEndEndRadius: ['borderBottomRightRadius']
};
function wrapImportantAndSkipCheck(value, important) {
let parsedValue = value;
if (important) {
parsedValue = `${parsedValue} !important`;
}
return {
_skip_check_: true,
value: parsedValue
};
}
/**
* Convert css logical properties to legacy properties.
* Such as: `margin-block-start` to `margin-top`.
* Transform list:
* - inset
* - margin
* - padding
* - border
*/
const transform = {
visit: cssObj => {
const clone = {};
Object.keys(cssObj).forEach(key => {
const value = cssObj[key];
const matchValue = keyMap[key];
if (matchValue && (typeof value === 'number' || typeof value === 'string')) {
const [values, important] = splitValues(value);
if (matchValue.length && matchValue.notSplit) {
// not split means always give same value like border
matchValue.forEach(matchKey => {
clone[matchKey] = wrapImportantAndSkipCheck(value, important);
});
} else if (matchValue.length === 1) {
// Handle like `marginBlockStart` => `marginTop`
clone[matchValue[0]] = wrapImportantAndSkipCheck(values[0], important);
} else if (matchValue.length === 2) {
// Handle like `marginBlock` => `marginTop` & `marginBottom`
matchValue.forEach((matchKey, index) => {
clone[matchKey] = wrapImportantAndSkipCheck(values[index] ?? values[0], important);
});
} else if (matchValue.length === 4) {
// Handle like `inset` => `top` & `right` & `bottom` & `left`
matchValue.forEach((matchKey, index) => {
clone[matchKey] = wrapImportantAndSkipCheck(values[index] ?? values[index - 2] ?? values[0], important);
});
} else {
clone[key] = value;
}
} else {
clone[key] = value;
}
});
return clone;
}
};
export default transform;
@@ -0,0 +1,20 @@
import type { Transformer } from './interface';
interface Options {
/**
* The root font size.
* @default 16
*/
rootValue?: number;
/**
* The decimal numbers to allow the REM units to grow to.
* @default 5
*/
precision?: number;
/**
* Whether to allow px to be converted in media queries.
* @default false
*/
mediaQuery?: boolean;
}
declare const transform: (options?: Options) => Transformer;
export default transform;
@@ -0,0 +1,55 @@
/**
* respect https://github.com/cuth/postcss-pxtorem
*/
// @ts-ignore
import unitless from '@emotion/unitless';
const pxRegex = /url\([^)]+\)|var\([^)]+\)|(\d*\.?\d+)px/g;
function toFixed(number, precision) {
const multiplier = Math.pow(10, precision + 1),
wholeNumber = Math.floor(number * multiplier);
return Math.round(wholeNumber / 10) * 10 / multiplier;
}
const transform = (options = {}) => {
const {
rootValue = 16,
precision = 5,
mediaQuery = false
} = options;
const pxReplace = (m, $1) => {
if (!$1) return m;
const pixels = parseFloat($1);
// covenant: pixels <= 1, not transform to rem @zombieJ
if (pixels <= 1) return m;
const fixedVal = toFixed(pixels / rootValue, precision);
return `${fixedVal}rem`;
};
const visit = cssObj => {
const clone = {
...cssObj
};
Object.entries(cssObj).forEach(([key, value]) => {
if (typeof value === 'string' && value.includes('px')) {
const newValue = value.replace(pxRegex, pxReplace);
clone[key] = newValue;
}
// no unit
if (!unitless[key] && typeof value === 'number' && value !== 0) {
clone[key] = `${value}px`.replace(pxRegex, pxReplace);
}
// Media queries
const mergedKey = key.trim();
if (mergedKey.startsWith('@') && mergedKey.includes('px') && mediaQuery) {
const newKey = key.replace(pxRegex, pxReplace);
clone[newKey] = clone[key];
delete clone[key];
}
});
return clone;
};
return {
visit
};
};
export default transform;
@@ -0,0 +1,14 @@
export declare const ATTR_CACHE_MAP = "data-ant-cssinjs-cache-path";
/**
* This marks style from the css file.
* Which means not exist in `<style />` tag.
*/
export declare const CSS_FILE_STYLE = "_FILE_STYLE__";
export declare function serialize(cachePathMap: Record<string, string>): string;
/**
* @private Test usage only. Can save remove if no need.
*/
export declare function reset(mockCache?: Record<string, string>, fromFile?: boolean): void;
export declare function prepare(): void;
export declare function existPath(path: string): boolean;
export declare function getStyleAndHash(path: string): [style: string | null, hash: string];
@@ -0,0 +1,76 @@
import canUseDom from "@rc-component/util/es/Dom/canUseDom";
import { ATTR_MARK } from "../StyleContext";
export const ATTR_CACHE_MAP = 'data-ant-cssinjs-cache-path';
/**
* This marks style from the css file.
* Which means not exist in `<style />` tag.
*/
export const CSS_FILE_STYLE = '_FILE_STYLE__';
export function serialize(cachePathMap) {
return Object.keys(cachePathMap).map(path => {
const hash = cachePathMap[path];
return `${path}:${hash}`;
}).join(';');
}
let cachePathMap;
let fromCSSFile = true;
/**
* @private Test usage only. Can save remove if no need.
*/
export function reset(mockCache, fromFile = true) {
cachePathMap = mockCache;
fromCSSFile = fromFile;
}
export function prepare() {
if (!cachePathMap) {
cachePathMap = {};
if (canUseDom()) {
const div = document.createElement('div');
div.className = ATTR_CACHE_MAP;
div.style.position = 'fixed';
div.style.visibility = 'hidden';
div.style.top = '-9999px';
document.body.appendChild(div);
let content = getComputedStyle(div).content || '';
content = content.replace(/^"/, '').replace(/"$/, '');
// Fill data
content.split(';').forEach(item => {
const [path, hash] = item.split(':');
cachePathMap[path] = hash;
});
// Remove inline record style
const inlineMapStyle = document.querySelector(`style[${ATTR_CACHE_MAP}]`);
if (inlineMapStyle) {
fromCSSFile = false;
inlineMapStyle.parentNode?.removeChild(inlineMapStyle);
}
document.body.removeChild(div);
}
}
}
export function existPath(path) {
prepare();
return !!cachePathMap[path];
}
export function getStyleAndHash(path) {
const hash = cachePathMap[path];
let styleStr = null;
if (hash && canUseDom()) {
if (fromCSSFile) {
styleStr = CSS_FILE_STYLE;
} else {
const style = document.querySelector(`style[${ATTR_MARK}="${cachePathMap[path]}"]`);
if (style) {
styleStr = style.innerHTML;
} else {
// Clean up since not exist anymore
delete cachePathMap[path];
}
}
}
return [styleStr, hash];
}
@@ -0,0 +1,19 @@
import type { HashPriority } from '../StyleContext';
export declare const token2CSSVar: (token: string, prefix?: string) => string;
export declare const serializeCSSVar: <T extends Record<string, any>>(cssVars: T, hashId: string, options?: {
scope?: string | string[];
hashCls?: string;
hashPriority?: HashPriority;
}) => string;
export type TokenWithCSSVar<V, T extends Record<string, V> = Record<string, V>> = {
[key in keyof T]?: string | V;
};
export declare const transformToken: <V, T extends Record<string, V> = Record<string, V>>(token: T, themeKey: string, config?: {
prefix?: string | undefined;
ignore?: { [key in keyof T]?: boolean | undefined; } | undefined;
unitless?: { [key_1 in keyof T]?: boolean | undefined; } | undefined;
preserve?: { [key_2 in keyof T]?: boolean | undefined; } | undefined;
scope?: string | string[] | undefined;
hashCls?: string | undefined;
hashPriority?: HashPriority | undefined;
} | undefined) => [TokenWithCSSVar<V, T>, string];
@@ -0,0 +1,47 @@
import { where } from "../util";
export const token2CSSVar = (token, prefix = '') => {
return `--${prefix ? `${prefix}-` : ''}${token}`.replace(/([a-z0-9])([A-Z])/g, '$1-$2').replace(/([A-Z]+)([A-Z][a-z0-9]+)/g, '$1-$2').replace(/([a-z])([A-Z0-9])/g, '$1-$2').toLowerCase();
};
export const serializeCSSVar = (cssVars, hashId, options) => {
const {
hashCls,
hashPriority = 'low',
scope
} = options || {};
if (!Object.keys(cssVars).length) {
return '';
}
const baseSelector = `${where({
hashCls,
hashPriority
})}.${hashId}`;
const scopes = [scope].flat().filter(Boolean);
const selector = scopes.length ? scopes.map(s => `${baseSelector}.${s}`).join(', ') : baseSelector;
return `${selector}{${Object.entries(cssVars).map(([key, value]) => `${key}:${value};`).join('')}}`;
};
export const transformToken = (token, themeKey, config) => {
const {
hashCls,
hashPriority = 'low',
prefix,
unitless,
ignore,
preserve
} = config || {};
const cssVars = {};
const result = {};
Object.entries(token).forEach(([key, value]) => {
if (preserve?.[key]) {
result[key] = value;
} else if ((typeof value === 'string' || typeof value === 'number') && !ignore?.[key]) {
const cssVar = token2CSSVar(key, prefix);
cssVars[cssVar] = typeof value === 'number' && !unitless?.[key] ? `${value}px` : String(value);
result[key] = `var(${cssVar})`;
}
});
return [result, serializeCSSVar(cssVars, themeKey, {
scope: config?.scope,
hashCls,
hashPriority
})];
};
+30
View File
@@ -0,0 +1,30 @@
import type { HashPriority } from '../StyleContext';
export declare function memoResult<T extends object, R>(callback: () => R, deps: T[]): R;
/**
* Flatten token to string, this will auto cache the result when token not change
*/
export declare function flattenToken(token: any): string;
/**
* Convert derivative token to key string
*/
export declare function token2key(token: any, salt: string): string;
export declare function supportLayer(): boolean;
export declare function supportWhere(): boolean;
export declare function supportLogicProps(): boolean;
export declare const isClientSide: boolean;
export declare function unit(num: string | number): string;
export declare function toStyleStr(style: string, tokenKey?: string, styleId?: string, customizeAttrs?: Record<string, string>, plain?: boolean): string;
export declare function where(options?: {
hashPriority?: HashPriority;
hashCls?: string;
}): string;
export declare const isNonNullable: <T>(val: T) => val is NonNullable<T>;
export type Nonce = string | (() => string);
/**
* Get nonce value and inject it into CSS config if available.
*/
export declare function injectCSPNonce<T extends {
csp?: {
nonce?: string;
};
}>(config: T, nonce: Nonce | undefined): T;
+165
View File
@@ -0,0 +1,165 @@
import hash from '@emotion/hash';
import canUseDom from "@rc-component/util/es/Dom/canUseDom";
import { removeCSS, updateCSS } from "@rc-component/util/es/Dom/dynamicCSS";
import { ATTR_MARK, ATTR_TOKEN } from "../StyleContext";
import { Theme } from "../theme";
// Create a cache for memo concat
const resultCache = new WeakMap();
const RESULT_VALUE = {};
export function memoResult(callback, deps) {
let current = resultCache;
for (let i = 0; i < deps.length; i += 1) {
const dep = deps[i];
if (!current.has(dep)) {
current.set(dep, new WeakMap());
}
current = current.get(dep);
}
if (!current.has(RESULT_VALUE)) {
current.set(RESULT_VALUE, callback());
}
return current.get(RESULT_VALUE);
}
// Create a cache here to avoid always loop generate
const flattenTokenCache = new WeakMap();
/**
* Flatten token to string, this will auto cache the result when token not change
*/
export function flattenToken(token) {
let str = flattenTokenCache.get(token) || '';
if (!str) {
Object.keys(token).forEach(key => {
const value = token[key];
str += key;
if (value instanceof Theme) {
str += value.id;
} else if (value && typeof value === 'object') {
str += flattenToken(value);
} else {
str += value;
}
});
// https://github.com/ant-design/ant-design/issues/48386
// Should hash the string to avoid style tag name too long
str = hash(str);
// Put in cache
flattenTokenCache.set(token, str);
}
return str;
}
/**
* Convert derivative token to key string
*/
export function token2key(token, salt) {
return hash(`${salt}_${flattenToken(token)}`);
}
const randomSelectorKey = `random-${Date.now()}-${Math.random()}`.replace(/\./g, '');
// Magic `content` for detect selector support
const checkContent = '_bAmBoO_';
function supportSelector(styleStr, handleElement, supportCheck) {
if (canUseDom()) {
updateCSS(styleStr, randomSelectorKey);
const ele = document.createElement('div');
ele.style.position = 'fixed';
ele.style.left = '0';
ele.style.top = '0';
handleElement?.(ele);
document.body.appendChild(ele);
if (process.env.NODE_ENV !== 'production') {
ele.innerHTML = 'Test';
ele.style.zIndex = '9999999';
}
const support = supportCheck ? supportCheck(ele) : getComputedStyle(ele).content?.includes(checkContent);
ele.parentNode?.removeChild(ele);
removeCSS(randomSelectorKey);
return support;
}
return false;
}
let canLayer = undefined;
export function supportLayer() {
if (canLayer === undefined) {
canLayer = supportSelector(`@layer ${randomSelectorKey} { .${randomSelectorKey} { content: "${checkContent}"!important; } }`, ele => {
ele.className = randomSelectorKey;
});
}
return canLayer;
}
let canWhere = undefined;
export function supportWhere() {
if (canWhere === undefined) {
canWhere = supportSelector(`:where(.${randomSelectorKey}) { content: "${checkContent}"!important; }`, ele => {
ele.className = randomSelectorKey;
});
}
return canWhere;
}
let canLogic = undefined;
export function supportLogicProps() {
if (canLogic === undefined) {
canLogic = supportSelector(`.${randomSelectorKey} { inset-block: 93px !important; }`, ele => {
ele.className = randomSelectorKey;
}, ele => getComputedStyle(ele).bottom === '93px');
}
return canLogic;
}
export const isClientSide = canUseDom();
export function unit(num) {
if (typeof num === 'number') {
return `${num}px`;
}
return num;
}
export function toStyleStr(style, tokenKey, styleId, customizeAttrs = {}, plain = false) {
if (plain) {
return style;
}
const attrs = {
...customizeAttrs,
[ATTR_TOKEN]: tokenKey,
[ATTR_MARK]: styleId
};
const attrStr = Object.keys(attrs).map(attr => {
const val = attrs[attr];
return val ? `${attr}="${val}"` : null;
}).filter(v => v).join(' ');
return `<style ${attrStr}>${style}</style>`;
}
export function where(options) {
const {
hashCls,
hashPriority = 'low'
} = options || {};
if (!hashCls) {
return '';
}
const hashSelector = `.${hashCls}`;
return hashPriority === 'low' ? `:where(${hashSelector})` : hashSelector;
}
export const isNonNullable = val => {
return val !== undefined && val !== null;
};
/**
* Get nonce value and inject it into CSS config if available.
*/
export function injectCSPNonce(config, nonce) {
const nonceStr = typeof nonce === 'function' ? nonce() : nonce;
if (nonceStr) {
return {
...config,
csp: {
...config.csp,
nonce: nonceStr
}
};
}
return config;
}
+17
View File
@@ -0,0 +1,17 @@
export type KeyType = string | number;
type ValueType = [number, any];
/** Connect key with `SPLIT` */
export declare function pathKey(keys: KeyType[]): string;
declare class Entity {
instanceId: string;
constructor(instanceId: string);
/** @private Internal cache map. Do not access this directly */
cache: Map<string, ValueType>;
get(keys: KeyType[]): ValueType | null;
/** A fast get cache with `get` concat. */
opGet(keyPathStr: string): ValueType | null;
update(keys: KeyType[], valueFn: (origin: ValueType | null) => ValueType | null): void;
/** A fast get cache with `get` concat. */
opUpdate(keyPathStr: string, valueFn: (origin: ValueType | null) => ValueType | null): void;
}
export default Entity;
+40
View File
@@ -0,0 +1,40 @@
// [times, realValue]
const SPLIT = '%';
/** Connect key with `SPLIT` */
export function pathKey(keys) {
return keys.join(SPLIT);
}
class Entity {
instanceId;
constructor(instanceId) {
this.instanceId = instanceId;
}
/** @private Internal cache map. Do not access this directly */
cache = new Map();
get(keys) {
return this.opGet(pathKey(keys));
}
/** A fast get cache with `get` concat. */
opGet(keyPathStr) {
return this.cache.get(keyPathStr) || null;
}
update(keys, valueFn) {
return this.opUpdate(pathKey(keys), valueFn);
}
/** A fast get cache with `get` concat. */
opUpdate(keyPathStr, valueFn) {
const prevValue = this.cache.get(keyPathStr);
const nextValue = valueFn(prevValue);
if (nextValue === null) {
this.cache.delete(keyPathStr);
} else {
this.cache.set(keyPathStr, nextValue);
}
}
}
export default Entity;
+20
View File
@@ -0,0 +1,20 @@
export type KeyType = string | number;
type ValueType = [number, any];
/** Connect key with `SPLIT` */
export declare function pathKey(keys: KeyType[]): string;
declare class Entity {
instanceId: string;
constructor(instanceId: string);
/** @private Internal cache map. Do not access this directly */
cache: Map<string, ValueType>;
/** @private Record update times for each key */
updateTimes: Map<string, number>;
extracted: Set<string>;
get(keys: KeyType[]): ValueType | null;
/** A fast get cache with `get` concat. */
opGet(keyPathStr: string): ValueType | null;
update(keys: KeyType[], valueFn: (origin: ValueType | null) => ValueType | null): void;
/** A fast get cache with `get` concat. */
opUpdate(keyPathStr: string, valueFn: (origin: ValueType | null) => ValueType | null): void;
}
export default Entity;
+57
View File
@@ -0,0 +1,57 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
exports.pathKey = pathKey;
// [times, realValue]
const SPLIT = '%';
/** Connect key with `SPLIT` */
function pathKey(keys) {
return keys.join(SPLIT);
}
/** Record update id for extract static style order. */
let updateId = 0;
class Entity {
instanceId;
constructor(instanceId) {
this.instanceId = instanceId;
}
/** @private Internal cache map. Do not access this directly */
cache = new Map();
/** @private Record update times for each key */
updateTimes = new Map();
extracted = new Set();
get(keys) {
return this.opGet(pathKey(keys));
}
/** A fast get cache with `get` concat. */
opGet(keyPathStr) {
return this.cache.get(keyPathStr) || null;
}
update(keys, valueFn) {
return this.opUpdate(pathKey(keys), valueFn);
}
/** A fast get cache with `get` concat. */
opUpdate(keyPathStr, valueFn) {
const prevValue = this.cache.get(keyPathStr);
const nextValue = valueFn(prevValue);
if (nextValue === null) {
this.cache.delete(keyPathStr);
this.updateTimes.delete(keyPathStr);
} else {
this.cache.set(keyPathStr, nextValue);
this.updateTimes.set(keyPathStr, updateId);
updateId += 1;
}
}
}
var _default = exports.default = Entity;
+9
View File
@@ -0,0 +1,9 @@
import type { CSSInterpolation } from './hooks/useStyleRegister';
declare class Keyframe {
private name;
style: CSSInterpolation;
constructor(name: string, style: CSSInterpolation);
getName(hashId?: string): string;
_keyframe: boolean;
}
export default Keyframe;
+19
View File
@@ -0,0 +1,19 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
class Keyframe {
name;
style;
constructor(name, style) {
this.name = name;
this.style = style;
}
getName(hashId = '') {
return hashId ? `${hashId}-${this.name}` : this.name;
}
_keyframe = true;
}
var _default = exports.default = Keyframe;
+45
View File
@@ -0,0 +1,45 @@
import * as React from 'react';
import CacheEntity from './Cache';
import type { Linter } from './linters/interface';
import type { Transformer } from './transformers/interface';
export declare const ATTR_TOKEN = "data-token-hash";
export declare const ATTR_MARK = "data-css-hash";
export declare const ATTR_CACHE_PATH = "data-cache-path";
export declare const CSS_IN_JS_INSTANCE = "__cssinjs_instance__";
export declare function createCache(): CacheEntity;
export type HashPriority = 'low' | 'high';
export interface StyleContextProps {
/** @private Test only. Not work in production. */
mock?: 'server' | 'client';
/**
* Only set when you need ssr to extract style on you own.
* If not provided, it will auto create <style /> on the end of Provider in server side.
*/
cache: CacheEntity;
/** Tell children that this context is default generated context */
defaultCache: boolean;
/** Use `:where` selector to reduce hashId css selector priority */
hashPriority?: HashPriority;
/** Tell cssinjs where to inject style in */
container?: Element | ShadowRoot;
/** Component wil render inline `<style />` for fallback in SSR. Not recommend. */
ssrInline?: boolean;
/** Transform css before inject in document. Please note that `transformers` do not support dynamic update */
transformers?: Transformer[];
/**
* Linters to lint css before inject in document.
* Styles will be linted after transforming.
* Please note that `linters` do not support dynamic update.
*/
linters?: Linter[];
/** Wrap css in a layer to avoid global style conflict */
layer?: boolean;
/** Hardcode here since transformer not support take effect on serialize currently */
autoPrefix?: boolean;
}
declare const StyleContext: React.Context<StyleContextProps>;
export type StyleProviderProps = Partial<Omit<StyleContextProps, 'autoPrefix'>> & {
children?: React.ReactNode;
};
export declare const StyleProvider: React.FC<StyleProviderProps>;
export default StyleContext;
+97
View File
@@ -0,0 +1,97 @@
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.StyleProvider = exports.CSS_IN_JS_INSTANCE = exports.ATTR_TOKEN = exports.ATTR_MARK = exports.ATTR_CACHE_PATH = void 0;
exports.createCache = createCache;
exports.default = void 0;
var _useMemo = _interopRequireDefault(require("@rc-component/util/lib/hooks/useMemo"));
var _isEqual = _interopRequireDefault(require("@rc-component/util/lib/isEqual"));
var React = _interopRequireWildcard(require("react"));
var _Cache = _interopRequireDefault(require("./Cache"));
var _autoPrefix = require("./transformers/autoPrefix");
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
const ATTR_TOKEN = exports.ATTR_TOKEN = 'data-token-hash';
const ATTR_MARK = exports.ATTR_MARK = 'data-css-hash';
const ATTR_CACHE_PATH = exports.ATTR_CACHE_PATH = 'data-cache-path';
// Mark css-in-js instance in style element
const CSS_IN_JS_INSTANCE = exports.CSS_IN_JS_INSTANCE = '__cssinjs_instance__';
function createCache() {
const cssinjsInstanceId = Math.random().toString(12).slice(2);
// Tricky SSR: Move all inline style to the head.
// PS: We do not recommend tricky mode.
if (typeof document !== 'undefined' && document.head && document.body) {
const styles = document.body.querySelectorAll(`style[${ATTR_MARK}]`) || [];
const {
firstChild
} = document.head;
Array.from(styles).forEach(style => {
style[CSS_IN_JS_INSTANCE] ||= cssinjsInstanceId;
// Not force move if no head
if (style[CSS_IN_JS_INSTANCE] === cssinjsInstanceId) {
document.head.insertBefore(style, firstChild);
}
});
// Deduplicate of moved styles
const styleHash = {};
Array.from(document.querySelectorAll(`style[${ATTR_MARK}]`)).forEach(style => {
const hash = style.getAttribute(ATTR_MARK);
if (styleHash[hash]) {
if (style[CSS_IN_JS_INSTANCE] === cssinjsInstanceId) {
style.parentNode?.removeChild(style);
}
} else {
styleHash[hash] = true;
}
});
}
return new _Cache.default(cssinjsInstanceId);
}
const StyleContext = /*#__PURE__*/React.createContext({
hashPriority: 'low',
cache: createCache(),
defaultCache: true,
autoPrefix: false
});
const StyleProvider = props => {
const {
children,
...restProps
} = props;
const parentContext = React.useContext(StyleContext);
const context = (0, _useMemo.default)(() => {
const mergedContext = {
...parentContext
};
Object.keys(restProps).forEach(key => {
const value = restProps[key];
if (restProps[key] !== undefined) {
mergedContext[key] = value;
}
});
const {
cache,
transformers = []
} = restProps;
mergedContext.cache = mergedContext.cache || createCache();
mergedContext.defaultCache = !cache && parentContext.defaultCache;
// autoPrefix
if (transformers.includes(_autoPrefix.AUTO_PREFIX)) {
mergedContext.autoPrefix = true;
}
return mergedContext;
}, [parentContext, restProps], (prev, next) => !(0, _isEqual.default)(prev[0], next[0], true) || !(0, _isEqual.default)(prev[1], next[1], true));
return /*#__PURE__*/React.createElement(StyleContext.Provider, {
value: context
}, children);
};
exports.StyleProvider = StyleProvider;
var _default = exports.default = StyleContext;
+13
View File
@@ -0,0 +1,13 @@
import type Cache from './Cache';
declare const ExtractStyleFns: {
style: import("./hooks/useGlobalCache").ExtractStyle<[styleStr: string, styleId: string, effectStyle: Record<string, string>, clientOnly: boolean | undefined, order: number]>;
token: import("./hooks/useGlobalCache").ExtractStyle<[token: any, hashId: string, realToken: any, cssVarStr: string, cssVarKey: string]>;
cssVar: import("./hooks/useGlobalCache").ExtractStyle<[cssVarToken: import("./util/css-variables").TokenWithCSSVar<any, Record<string, any>>, cssVarStr: string, styleId: string, cssVarKey: string]>;
};
type ExtractStyleType = keyof typeof ExtractStyleFns;
export default function extractStyle(cache: Cache, options?: boolean | {
plain?: boolean;
types?: ExtractStyleType | ExtractStyleType[];
once?: boolean;
}): string;
export {};
+75
View File
@@ -0,0 +1,75 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = extractStyle;
var _useCacheToken = require("./hooks/useCacheToken");
var _useCSSVarRegister = require("./hooks/useCSSVarRegister");
var _useStyleRegister = require("./hooks/useStyleRegister");
var _util = require("./util");
var _cacheMapUtil = require("./util/cacheMapUtil");
const ExtractStyleFns = {
[_useStyleRegister.STYLE_PREFIX]: _useStyleRegister.extract,
[_useCacheToken.TOKEN_PREFIX]: _useCacheToken.extract,
[_useCSSVarRegister.CSS_VAR_PREFIX]: _useCSSVarRegister.extract
};
function isNotNull(value) {
return value !== null;
}
function extractStyle(cache, options) {
const {
plain = false,
types = ['style', 'token', 'cssVar'],
once = false
} = typeof options === 'boolean' ? {
plain: options
} : options || {};
const matchPrefixRegexp = new RegExp(`^(${(typeof types === 'string' ? [types] : types).join('|')})%`);
// prefix with `style` is used for `useStyleRegister` to cache style context
const styleKeys = Array.from(cache.cache.keys()).filter(key => matchPrefixRegexp.test(key));
// Common effect styles like animation
const effectStyles = {};
// Mapping of cachePath to style hash
const cachePathMap = {};
let styleText = '';
styleKeys.map(key => {
if (once && cache.extracted.has(key)) {
return null; // Skip if already extracted
}
const cachePath = key.replace(matchPrefixRegexp, '').replace(/%/g, '|');
const [prefix] = key.split('%');
const extractFn = ExtractStyleFns[prefix];
const extractedStyle = extractFn(cache.cache.get(key)[1], effectStyles, {
plain
});
if (!extractedStyle) {
return null;
}
const updateTime = cache.updateTimes.get(key) || 0;
const [order, styleId, styleStr] = extractedStyle;
if (key.startsWith('style')) {
cachePathMap[cachePath] = styleId;
}
// record that this style has been extracted
cache.extracted.add(key);
return [order, styleStr, updateTime];
}).filter(isNotNull).sort(([o1,, u1], [o2,, u2]) => {
if (o1 !== o2) {
return o1 - o2;
}
return u1 - u2;
}).forEach(([, style]) => {
styleText += style;
});
// ==================== Fill Cache Path ====================
styleText += (0, _util.toStyleStr)(`.${_cacheMapUtil.ATTR_CACHE_MAP}{content:"${(0, _cacheMapUtil.serialize)(cachePathMap)}";}`, undefined, undefined, {
[_cacheMapUtil.ATTR_CACHE_MAP]: _cacheMapUtil.ATTR_CACHE_MAP
}, plain);
return styleText;
}
@@ -0,0 +1,22 @@
import type { TokenWithCSSVar } from '../util/css-variables';
import type { ExtractStyle } from './useGlobalCache';
export declare const CSS_VAR_PREFIX = "cssVar";
type CSSVarCacheValue<V, T extends Record<string, V> = Record<string, V>> = [
cssVarToken: TokenWithCSSVar<V, T>,
cssVarStr: string,
styleId: string,
cssVarKey: string
];
declare const useCSSVarRegister: <V, T extends Record<string, V>>(config: {
path: string[];
key: string;
prefix?: string;
unitless?: Record<string, boolean>;
ignore?: Record<string, boolean>;
scope?: string | string[];
token: any;
hashId?: string;
nonce?: string | (() => string);
}, fn: () => T) => CSSVarCacheValue<V, T>;
export declare const extract: ExtractStyle<CSSVarCacheValue<any>>;
export default useCSSVarRegister;
@@ -0,0 +1,99 @@
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.extract = exports.default = exports.CSS_VAR_PREFIX = void 0;
var _dynamicCSS = require("@rc-component/util/lib/Dom/dynamicCSS");
var _react = require("react");
var _StyleContext = _interopRequireWildcard(require("../StyleContext"));
var _util = require("../util");
var _cssVariables = require("../util/css-variables");
var _useGlobalCache = _interopRequireDefault(require("./useGlobalCache"));
var _useStyleRegister = require("./useStyleRegister");
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
const CSS_VAR_PREFIX = exports.CSS_VAR_PREFIX = 'cssVar';
const useCSSVarRegister = (config, fn) => {
const {
key,
prefix,
unitless,
ignore,
token,
hashId,
scope,
nonce
} = config;
const {
cache: {
instanceId
},
container,
hashPriority
} = (0, _react.useContext)(_StyleContext.default);
const {
_tokenKey: tokenKey
} = token;
const scopeKey = Array.isArray(scope) ? scope.join('@@') : scope;
const stylePath = [...config.path, key, scopeKey, tokenKey];
const cache = (0, _useGlobalCache.default)(CSS_VAR_PREFIX, stylePath, () => {
const originToken = fn();
const [mergedToken, cssVarsStr] = (0, _cssVariables.transformToken)(originToken, key, {
prefix,
unitless,
ignore,
scope,
hashPriority,
hashCls: hashId
});
const styleId = (0, _useStyleRegister.uniqueHash)(stylePath, cssVarsStr);
return [mergedToken, cssVarsStr, styleId, key];
}, ([,, styleId]) => {
if (_util.isClientSide) {
(0, _dynamicCSS.removeCSS)(styleId, {
mark: _StyleContext.ATTR_MARK,
attachTo: container
});
}
}, ([, cssVarsStr, styleId]) => {
if (!cssVarsStr) {
return;
}
let mergedCSSConfig = {
mark: _StyleContext.ATTR_MARK,
prepend: 'queue',
attachTo: container,
priority: -999
};
mergedCSSConfig = (0, _util.injectCSPNonce)(mergedCSSConfig, nonce);
const style = (0, _dynamicCSS.updateCSS)(cssVarsStr, styleId, mergedCSSConfig);
style[_StyleContext.CSS_IN_JS_INSTANCE] = instanceId;
// Used for `useCacheToken` to remove on batch when token removed
style.setAttribute(_StyleContext.ATTR_TOKEN, key);
});
return cache;
};
const extract = (cache, effectStyles, options) => {
const [, styleStr, styleId, cssVarKey] = cache;
const {
plain
} = options || {};
if (!styleStr) {
return null;
}
const order = -999;
// ====================== Style ======================
// Used for @rc-component/util
const sharedAttrs = {
'data-rc-order': 'prependQueue',
'data-rc-priority': `${order}`
};
const styleText = (0, _util.toStyleStr)(styleStr, cssVarKey, styleId, sharedAttrs, plain);
return [order, styleId, styleText];
};
exports.extract = extract;
var _default = exports.default = useCSSVarRegister;
@@ -0,0 +1,69 @@
import type Theme from '../theme/Theme';
import type { ExtractStyle } from './useGlobalCache';
export interface Option<DerivativeToken, DesignToken> {
/**
* Generate token with salt.
* This is used to generate different hashId even same derivative token for different version.
*/
salt?: string;
override?: object;
/**
* Format token as you need. Such as:
*
* - rename token
* - merge token
* - delete token
*
* This should always be the same since it's one time process.
* It's ok to useMemo outside but this has better cache strategy.
*/
formatToken?: (mergedToken: any) => DerivativeToken;
/**
* Get final token with origin token, override token and theme.
* The parameters do not contain formatToken since it's passed by user.
* @param origin The original token.
* @param override Extra tokens to override.
* @param theme Theme instance. Could get derivative token by `theme.getDerivativeToken`
*/
getComputedToken?: (origin: DesignToken, override: object, theme: Theme<any, any>) => DerivativeToken;
/**
* Transform token to css variables.
*/
cssVar: {
hashed?: boolean;
/** Prefix for css variables */
prefix?: string;
/** Tokens that should not be appended with unit */
unitless?: Record<string, boolean>;
/** Tokens that should not be transformed to css variables */
ignore?: Record<string, boolean>;
/** Tokens that preserves origin value */
preserve?: Record<string, boolean>;
/** Key for current theme. Useful for customizing and should be unique */
key: string;
};
/**
* CSP nonce for style element.
* Can be a string or a function that returns a string.
*/
nonce?: string | (() => string);
}
export declare const getComputedToken: <DerivativeToken = object, DesignToken = DerivativeToken>(originToken: DesignToken, overrideToken: object, theme: Theme<any, any>, format?: ((token: DesignToken) => DerivativeToken) | undefined) => any;
export declare const TOKEN_PREFIX = "token";
type TokenCacheValue<DerivativeToken> = [
token: DerivativeToken,
hashId: string,
realToken: DerivativeToken,
cssVarStr: string,
cssVarKey: string
];
/**
* Cache theme derivative token as global shared one
* @param theme Theme entity
* @param tokens List of tokens, used for cache. Please do not dynamic generate object directly
* @param option Additional config
* @returns Call Theme.getDerivativeToken(tokenObject) to get token
*/
export default function useCacheToken<DerivativeToken = object, DesignToken = DerivativeToken>(theme: Theme<any, any>, tokens: Partial<DesignToken>[], option: Option<DerivativeToken, DesignToken>): TokenCacheValue<DerivativeToken>;
export declare const extract: ExtractStyle<TokenCacheValue<any>>;
export {};
@@ -0,0 +1,168 @@
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.TOKEN_PREFIX = void 0;
exports.default = useCacheToken;
exports.getComputedToken = exports.extract = void 0;
var _hash = _interopRequireDefault(require("@emotion/hash"));
var _dynamicCSS = require("@rc-component/util/lib/Dom/dynamicCSS");
var _react = require("react");
var _StyleContext = _interopRequireWildcard(require("../StyleContext"));
var _util = require("../util");
var _cssVariables = require("../util/css-variables");
var _useGlobalCache = _interopRequireDefault(require("./useGlobalCache"));
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
const EMPTY_OVERRIDE = {};
// Generate different prefix to make user selector break in production env.
// This helps developer not to do style override directly on the hash id.
const hashPrefix = process.env.NODE_ENV !== 'production' ? 'css-dev-only-do-not-override' : 'css';
const tokenKeys = new Map();
function recordCleanToken(tokenKey) {
tokenKeys.set(tokenKey, (tokenKeys.get(tokenKey) || 0) + 1);
}
function removeStyleTags(key, instanceId) {
if (typeof document !== 'undefined') {
const styles = document.querySelectorAll(`style[${_StyleContext.ATTR_TOKEN}="${key}"]`);
styles.forEach(style => {
if (style[_StyleContext.CSS_IN_JS_INSTANCE] === instanceId) {
style.parentNode?.removeChild(style);
}
});
}
}
const TOKEN_THRESHOLD = -1;
// Remove will check current keys first
function cleanTokenStyle(tokenKey, instanceId) {
tokenKeys.set(tokenKey, (tokenKeys.get(tokenKey) || 0) - 1);
const cleanableKeyList = new Set();
tokenKeys.forEach((value, key) => {
if (value <= 0) cleanableKeyList.add(key);
});
// Should keep tokens under threshold for not to insert style too often
if (tokenKeys.size - cleanableKeyList.size > TOKEN_THRESHOLD) {
cleanableKeyList.forEach(key => {
removeStyleTags(key, instanceId);
tokenKeys.delete(key);
});
}
}
const getComputedToken = (originToken, overrideToken, theme, format) => {
const derivativeToken = theme.getDerivativeToken(originToken);
// Merge with override
let mergedDerivativeToken = {
...derivativeToken,
...overrideToken
};
// Format if needed
if (format) {
mergedDerivativeToken = format(mergedDerivativeToken);
}
return mergedDerivativeToken;
};
exports.getComputedToken = getComputedToken;
const TOKEN_PREFIX = exports.TOKEN_PREFIX = 'token';
/**
* Cache theme derivative token as global shared one
* @param theme Theme entity
* @param tokens List of tokens, used for cache. Please do not dynamic generate object directly
* @param option Additional config
* @returns Call Theme.getDerivativeToken(tokenObject) to get token
*/
function useCacheToken(theme, tokens, option) {
const {
cache: {
instanceId
},
container,
hashPriority
} = (0, _react.useContext)(_StyleContext.default);
const {
salt = '',
override = EMPTY_OVERRIDE,
formatToken,
getComputedToken: compute,
cssVar,
nonce
} = option;
// Basic - We do basic cache here
const mergedToken = (0, _util.memoResult)(() => Object.assign({}, ...tokens), tokens);
const tokenStr = (0, _util.flattenToken)(mergedToken);
const overrideTokenStr = (0, _util.flattenToken)(override);
const cssVarStr = (0, _util.flattenToken)(cssVar);
const cachedToken = (0, _useGlobalCache.default)(TOKEN_PREFIX, [salt, theme.id, tokenStr, overrideTokenStr, cssVarStr], () => {
const mergedDerivativeToken = compute ? compute(mergedToken, override, theme) : getComputedToken(mergedToken, override, theme, formatToken);
const actualToken = {
...mergedDerivativeToken
};
// Optimize for `useStyleRegister` performance
const mergedSalt = `${salt}_${cssVar.prefix}`;
const hashId = (0, _hash.default)(mergedSalt);
const hashCls = `${hashPrefix}-${hashId}`;
actualToken._tokenKey = (0, _util.token2key)(actualToken, mergedSalt);
// Replace token value with css variables
const [tokenWithCssVar, cssVarsStr] = (0, _cssVariables.transformToken)(mergedDerivativeToken, cssVar.key, {
prefix: cssVar.prefix,
ignore: cssVar.ignore,
unitless: cssVar.unitless,
preserve: cssVar.preserve,
hashPriority,
hashCls: cssVar.hashed ? hashCls : undefined
});
tokenWithCssVar._hashId = hashId;
recordCleanToken(cssVar.key);
return [tokenWithCssVar, hashCls, actualToken, cssVarsStr, cssVar.key];
}, ([,,,, themeKey]) => {
// Remove token will remove all related style
cleanTokenStyle(themeKey, instanceId);
}, ([,,, cssVarsStr, themeKey]) => {
if (!cssVarsStr) {
return;
}
let mergedCSSConfig = {
mark: _StyleContext.ATTR_MARK,
prepend: 'queue',
attachTo: container,
priority: -999
};
mergedCSSConfig = (0, _util.injectCSPNonce)(mergedCSSConfig, nonce);
const style = (0, _dynamicCSS.updateCSS)(cssVarsStr, (0, _hash.default)(`css-var-${themeKey}`), mergedCSSConfig);
style[_StyleContext.CSS_IN_JS_INSTANCE] = instanceId;
// Used for `useCacheToken` to remove on batch when token removed
style.setAttribute(_StyleContext.ATTR_TOKEN, themeKey);
});
return cachedToken;
}
const extract = (cache, effectStyles, options) => {
const [,, realToken, styleStr, cssVarKey] = cache;
const {
plain
} = options || {};
if (!styleStr) {
return null;
}
const styleId = realToken._tokenKey;
const order = -999;
// ====================== Style ======================
// Used for @rc-component/util
const sharedAttrs = {
'data-rc-order': 'prependQueue',
'data-rc-priority': `${order}`
};
const styleText = (0, _util.toStyleStr)(styleStr, cssVarKey, styleId, sharedAttrs, plain);
return [order, styleId, styleText];
};
exports.extract = extract;
@@ -0,0 +1,3 @@
import type { DependencyList } from 'react';
declare const useEffectCleanupRegister: (deps?: DependencyList) => (fn: () => void) => void;
export default useEffectCleanupRegister;
@@ -0,0 +1,34 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _warning = require("@rc-component/util/lib/warning");
var _react = require("react");
// DO NOT register functions in useEffect cleanup function, or functions that registered will never be called.
const useEffectCleanupRegister = deps => {
const effectCleanups = [];
let cleanupFlag = false;
function register(fn) {
if (cleanupFlag) {
if (process.env.NODE_ENV !== 'production') {
(0, _warning.warning)(false, '[Ant Design CSS-in-JS] You are registering a cleanup function after unmount, which will not have any effect.');
}
return;
}
effectCleanups.push(fn);
}
(0, _react.useEffect)(() => {
// Compatible with strict mode
cleanupFlag = false;
return () => {
cleanupFlag = true;
if (effectCleanups.length) {
effectCleanups.forEach(fn => fn());
}
};
}, deps);
return register;
};
var _default = exports.default = useEffectCleanupRegister;
@@ -0,0 +1,6 @@
import { type KeyType } from '../Cache';
export type ExtractStyle<CacheValue> = (cache: CacheValue, effectStyles: Record<string, boolean>, options?: {
plain?: boolean;
autoPrefix?: boolean;
}) => [order: number, styleId: string, style: string] | null;
export default function useGlobalCache<CacheType>(prefix: string, keyPath: KeyType[], cacheFn: () => CacheType, onCacheRemove?: (cache: CacheType, fromHMR: boolean) => void, onCacheEffect?: (cachedValue: CacheType) => void): CacheType;
@@ -0,0 +1,86 @@
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = useGlobalCache;
var _react = _interopRequireWildcard(require("react"));
var React = _react;
var _Cache = require("../Cache");
var _StyleContext = _interopRequireDefault(require("../StyleContext"));
var _useHMR = _interopRequireDefault(require("./useHMR"));
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
const effectMap = new Map();
function useGlobalCache(prefix, keyPath, cacheFn, onCacheRemove,
// Add additional effect trigger by `useInsertionEffect`
onCacheEffect) {
const {
cache: globalCache
} = React.useContext(_StyleContext.default);
const fullPath = [prefix, ...keyPath];
const fullPathStr = (0, _Cache.pathKey)(fullPath);
const HMRUpdate = (0, _useHMR.default)();
const buildCache = updater => {
globalCache.opUpdate(fullPathStr, prevCache => {
const [times = 0, cache] = prevCache || [undefined, undefined];
// HMR should always ignore cache since developer may change it
let tmpCache = cache;
if (process.env.NODE_ENV !== 'production' && cache && HMRUpdate) {
onCacheRemove?.(tmpCache, HMRUpdate);
tmpCache = null;
}
const mergedCache = tmpCache || cacheFn();
const data = [times, mergedCache];
// Call updater if need additional logic
return updater ? updater(data) : data;
});
};
// Create cache
React.useMemo(() => {
buildCache();
}, /* eslint-disable react-hooks/exhaustive-deps */
[fullPathStr]
/* eslint-enable */);
let cacheEntity = globalCache.opGet(fullPathStr);
// HMR clean the cache but not trigger `useMemo` again
// Let's fallback of this
// ref https://github.com/ant-design/cssinjs/issues/127
if (process.env.NODE_ENV !== 'production' && !cacheEntity) {
buildCache();
cacheEntity = globalCache.opGet(fullPathStr);
}
const cacheContent = cacheEntity[1];
// Remove if no need anymore
(0, _react.useInsertionEffect)(() => {
buildCache(([times, cache]) => [times + 1, cache]);
if (!effectMap.has(fullPathStr)) {
onCacheEffect?.(cacheContent);
effectMap.set(fullPathStr, true);
// 微任务清理缓存,可以认为是单次 batch render 中只触发一次 effect
Promise.resolve().then(() => {
effectMap.delete(fullPathStr);
});
}
return () => {
globalCache.opUpdate(fullPathStr, prevCache => {
const [times = 0, cache] = prevCache || [];
const nextCount = times - 1;
if (nextCount === 0) {
onCacheRemove?.(cache, false);
effectMap.delete(fullPathStr);
return null;
}
return [times - 1, cache];
});
};
}, [fullPathStr]);
return cacheContent;
}
@@ -0,0 +1,3 @@
declare function useProdHMR(): boolean;
declare const _default: typeof useProdHMR;
export default _default;
+30
View File
@@ -0,0 +1,30 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
function useProdHMR() {
return false;
}
let webpackHMR = false;
function useDevHMR() {
return webpackHMR;
}
var _default = exports.default = process.env.NODE_ENV === 'production' ? useProdHMR : useDevHMR; // Webpack `module.hot.accept` do not support any deps update trigger
// We have to hack handler to force mark as HRM
if (process.env.NODE_ENV !== 'production' && typeof module !== 'undefined' && module && module.hot && typeof window !== 'undefined') {
// Use `globalThis` first, and `window` for older browsers
// const win = globalThis as any;
const win = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : null;
if (win && typeof win.webpackHotUpdate === 'function') {
const originWebpackHotUpdate = win.webpackHotUpdate;
win.webpackHotUpdate = (...args) => {
webpackHMR = true;
setTimeout(() => {
webpackHMR = false;
}, 0);
return originWebpackHotUpdate(...args);
};
}
}
@@ -0,0 +1,75 @@
import type * as CSS from 'csstype';
import type { Theme, Transformer } from '..';
import type Keyframes from '../Keyframes';
import type { Linter } from '../linters';
import type { HashPriority } from '../StyleContext';
import type { ExtractStyle } from './useGlobalCache';
declare const SKIP_CHECK = "_skip_check_";
declare const MULTI_VALUE = "_multi_value_";
export interface LayerConfig {
name: string;
dependencies?: string[];
}
export type CSSProperties = Omit<CSS.PropertiesFallback<number | string>, 'animationName'> & {
animationName?: CSS.PropertiesFallback<number | string>['animationName'] | Keyframes;
};
export type CSSPropertiesWithMultiValues = {
[K in keyof CSSProperties]: CSSProperties[K] | readonly Extract<CSSProperties[K], string>[] | {
[SKIP_CHECK]?: boolean;
[MULTI_VALUE]?: boolean;
value: CSSProperties[K] | CSSProperties[K][];
};
};
export type CSSPseudos = {
[K in CSS.Pseudos]?: CSSObject;
};
type ArrayCSSInterpolation = readonly CSSInterpolation[];
export type InterpolationPrimitive = null | undefined | boolean | number | string | CSSObject;
export type CSSInterpolation = InterpolationPrimitive | ArrayCSSInterpolation | Keyframes;
export type CSSOthersObject = Record<string, CSSInterpolation>;
export interface CSSObject extends CSSPropertiesWithMultiValues, CSSPseudos, CSSOthersObject {
}
export declare function normalizeStyle(styleStr: string, autoPrefix: boolean): string;
export interface ParseConfig {
hashId?: string;
hashPriority?: HashPriority;
layer?: LayerConfig;
path?: string;
transformers?: Transformer[];
linters?: Linter[];
}
export interface ParseInfo {
root?: boolean;
injectHash?: boolean;
parentSelectors: string[];
}
export declare const parseStyle: (interpolation: CSSInterpolation, config?: ParseConfig, { root, injectHash, parentSelectors }?: ParseInfo) => [parsedStr: string, effectStyle: Record<string, string>];
export declare function uniqueHash(path: (string | number)[], styleStr: string): string;
export declare const STYLE_PREFIX = "style";
type StyleCacheValue = [
styleStr: string,
styleId: string,
effectStyle: Record<string, string>,
clientOnly: boolean | undefined,
order: number
];
/**
* Register a style to the global style sheet.
*/
export default function useStyleRegister(info: {
theme: Theme<any, any>;
token: any;
path: string[];
hashId?: string;
layer?: LayerConfig;
nonce?: string | (() => string);
clientOnly?: boolean;
/**
* Tell cssinjs the insert order of style.
* It's useful when you need to insert style
* before other style to overwrite for the same selector priority.
*/
order?: number;
}, styleFn: () => CSSInterpolation): void;
export declare const extract: ExtractStyle<StyleCacheValue>;
export {};
@@ -0,0 +1,380 @@
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.STYLE_PREFIX = void 0;
exports.default = useStyleRegister;
exports.extract = void 0;
exports.normalizeStyle = normalizeStyle;
exports.parseStyle = void 0;
exports.uniqueHash = uniqueHash;
var _hash = _interopRequireDefault(require("@emotion/hash"));
var _dynamicCSS = require("@rc-component/util/lib/Dom/dynamicCSS");
var React = _interopRequireWildcard(require("react"));
var _unitless = _interopRequireDefault(require("@emotion/unitless"));
var _stylis = require("stylis");
var _linters = require("../linters");
var _StyleContext = _interopRequireWildcard(require("../StyleContext"));
var _util = require("../util");
var _cacheMapUtil = require("../util/cacheMapUtil");
var _useGlobalCache = _interopRequireDefault(require("./useGlobalCache"));
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
// @ts-ignore
const SKIP_CHECK = '_skip_check_';
const MULTI_VALUE = '_multi_value_';
// ============================================================================
// == Parser ==
// ============================================================================
// Preprocessor style content to browser support one
function normalizeStyle(styleStr, autoPrefix) {
const serialized = autoPrefix ? (0, _stylis.serialize)((0, _stylis.compile)(styleStr), (0, _stylis.middleware)([_stylis.prefixer, _stylis.stringify])) : (0, _stylis.serialize)((0, _stylis.compile)(styleStr), _stylis.stringify);
return serialized.replace(/\{%%%\:[^;];}/g, ';');
}
function isCompoundCSSProperty(value) {
return typeof value === 'object' && value && (SKIP_CHECK in value || MULTI_VALUE in value);
}
// 注入 hash 值
function injectSelectorHash(key, hashId, hashPriority = 'high') {
if (!hashId) {
return key;
}
const hashSelector = (0, _util.where)({
hashCls: hashId,
hashPriority
});
// 注入 hashId
const keys = key.split(',').map(k => {
const fullPath = k.trim().split(/\s+/);
// 如果 Selector 第一个是 HTML Element,那我们就插到它的后面。反之,就插到最前面。
let firstPath = fullPath[0] || '';
const htmlElement = firstPath.match(/^\w+/)?.[0] || '';
firstPath = `${htmlElement}${hashSelector}${firstPath.slice(htmlElement.length)}`;
return [firstPath, ...fullPath.slice(1)].join(' ');
});
return keys.join(',');
}
// Parse CSSObject to style content
const parseStyle = (interpolation, config = {}, {
root,
injectHash,
parentSelectors
} = {
root: true,
parentSelectors: []
}) => {
const {
hashId,
layer,
path,
hashPriority,
transformers = [],
linters = []
} = config;
let styleStr = '';
let effectStyle = {};
function parseKeyframes(keyframes) {
const animationName = keyframes.getName(hashId);
if (!effectStyle[animationName]) {
const [parsedStr] = parseStyle(keyframes.style, config, {
root: false,
parentSelectors
});
effectStyle[animationName] = `@keyframes ${keyframes.getName(hashId)}${parsedStr}`;
}
}
function flattenList(list, fullList = []) {
list.forEach(item => {
if (Array.isArray(item)) {
flattenList(item, fullList);
} else if (item) {
fullList.push(item);
}
});
return fullList;
}
const flattenStyleList = flattenList(Array.isArray(interpolation) ? interpolation : [interpolation]);
flattenStyleList.forEach(originStyle => {
// Only root level can use raw string
const style = typeof originStyle === 'string' && !root ? {} : originStyle;
if (typeof style === 'string') {
styleStr += `${style}\n`;
} else if (style._keyframe) {
// Keyframe
parseKeyframes(style);
} else {
const mergedStyle = transformers.reduce((prev, trans) => trans?.visit?.(prev) || prev, style);
// Normal CSSObject
Object.keys(mergedStyle).forEach(key => {
const value = mergedStyle[key];
if (typeof value === 'object' && value && (key !== 'animationName' || !value._keyframe) && !isCompoundCSSProperty(value)) {
let subInjectHash = false;
// 当成嵌套对象来处理
let mergedKey = key.trim();
// Whether treat child as root. In most case it is false.
let nextRoot = false;
// 拆分多个选择器
if ((root || injectHash) && hashId) {
if (mergedKey.startsWith('@')) {
// 略过媒体查询,交给子节点继续插入 hashId
subInjectHash = true;
} else if (mergedKey === '&') {
// 抹掉 root selector 上的单个 &
mergedKey = injectSelectorHash('', hashId, hashPriority);
} else {
// 注入 hashId
mergedKey = injectSelectorHash(key, hashId, hashPriority);
}
} else if (root && !hashId && (mergedKey === '&' || mergedKey === '')) {
// In case of `{ '&': { a: { color: 'red' } } }` or `{ '': { a: { color: 'red' } } }` without hashId,
// we will get `&{a:{color:red;}}` or `{a:{color:red;}}` string for stylis to compile.
// But it does not conform to stylis syntax,
// and finally we will get `{color:red;}` as css, which is wrong.
// So we need to remove key in root, and treat child `{ a: { color: 'red' } }` as root.
mergedKey = '';
nextRoot = true;
}
const [parsedStr, childEffectStyle] = parseStyle(value, config, {
root: nextRoot,
injectHash: subInjectHash,
parentSelectors: [...parentSelectors, mergedKey]
});
effectStyle = {
...effectStyle,
...childEffectStyle
};
styleStr += `${mergedKey}${parsedStr}`;
} else {
function appendStyle(cssKey, cssValue) {
if (process.env.NODE_ENV !== 'production' && (typeof value !== 'object' || !value?.[SKIP_CHECK])) {
[_linters.contentQuotesLinter, _linters.hashedAnimationLinter, ...linters].forEach(linter => linter(cssKey, cssValue, {
path,
hashId,
parentSelectors
}));
}
// 如果是样式则直接插入
const styleName = cssKey.replace(/[A-Z]/g, match => `-${match.toLowerCase()}`);
// Auto suffix with px
let formatValue = cssValue;
if (!_unitless.default[cssKey] && typeof formatValue === 'number' && formatValue !== 0) {
formatValue = `${formatValue}px`;
}
// handle animationName & Keyframe value
if (cssKey === 'animationName' && cssValue?._keyframe) {
parseKeyframes(cssValue);
formatValue = cssValue.getName(hashId);
}
styleStr += `${styleName}:${formatValue};`;
}
const actualValue = value?.value ?? value;
if (typeof value === 'object' && value?.[MULTI_VALUE] && Array.isArray(actualValue)) {
actualValue.forEach(item => {
appendStyle(key, item);
});
} else {
if ((0, _util.isNonNullable)(actualValue)) {
appendStyle(key, actualValue);
}
}
}
});
}
});
if (!root) {
styleStr = `{${styleStr}}`;
} else if (layer) {
// fixme: https://github.com/thysultan/stylis/pull/339
if (styleStr) {
styleStr = `@layer ${layer.name} {${styleStr}}`;
}
if (layer.dependencies) {
effectStyle[`@layer ${layer.name}`] = layer.dependencies.map(deps => `@layer ${deps}, ${layer.name};`).join('\n');
}
}
return [styleStr, effectStyle];
};
// ============================================================================
// == Register ==
// ============================================================================
exports.parseStyle = parseStyle;
function uniqueHash(path, styleStr) {
return (0, _hash.default)(`${path.join('%')}${styleStr}`);
}
const STYLE_PREFIX = exports.STYLE_PREFIX = 'style';
/**
* Register a style to the global style sheet.
*/
function useStyleRegister(info, styleFn) {
const {
path,
hashId,
layer,
nonce,
clientOnly,
order = 0
} = info;
const {
mock,
hashPriority,
container,
transformers,
linters,
cache,
layer: enableLayer,
autoPrefix
} = React.useContext(_StyleContext.default);
const fullPath = [hashId || ''];
if (enableLayer) {
fullPath.push('layer');
}
fullPath.push(...path);
// Check if need insert style
let isMergedClientSide = _util.isClientSide;
if (process.env.NODE_ENV !== 'production' && mock !== undefined) {
isMergedClientSide = mock === 'client';
}
(0, _useGlobalCache.default)(STYLE_PREFIX, fullPath,
// Create cache if needed
() => {
const cachePath = fullPath.join('|');
// Get style from SSR inline style directly
if ((0, _cacheMapUtil.existPath)(cachePath)) {
const [inlineCacheStyleStr, styleHash] = (0, _cacheMapUtil.getStyleAndHash)(cachePath);
if (inlineCacheStyleStr) {
return [inlineCacheStyleStr, styleHash, {}, clientOnly, order];
}
}
// Generate style
const styleObj = styleFn();
const [parsedStyle, effectStyle] = parseStyle(styleObj, {
hashId,
hashPriority,
layer: enableLayer ? layer : undefined,
path: path.join('-'),
transformers,
linters
});
const styleStr = normalizeStyle(parsedStyle, autoPrefix || false);
const styleId = uniqueHash(fullPath, styleStr);
return [styleStr, styleId, effectStyle, clientOnly, order];
},
// Remove cache if no need
(cacheValue, fromHMR) => {
const [, styleId] = cacheValue;
if (fromHMR && _util.isClientSide) {
(0, _dynamicCSS.removeCSS)(styleId, {
mark: _StyleContext.ATTR_MARK,
attachTo: container
});
}
},
// Effect: Inject style here
cacheValue => {
const [styleStr, styleId, effectStyle,, priority] = cacheValue;
if (isMergedClientSide && styleStr !== _cacheMapUtil.CSS_FILE_STYLE) {
let mergedCSSConfig = {
mark: _StyleContext.ATTR_MARK,
prepend: enableLayer ? false : 'queue',
attachTo: container,
priority
};
mergedCSSConfig = (0, _util.injectCSPNonce)(mergedCSSConfig, nonce);
// ================= Split Effect Style =================
// We will split effectStyle here since @layer should be at the top level
const effectLayerKeys = [];
const effectRestKeys = [];
Object.keys(effectStyle).forEach(key => {
if (key.startsWith('@layer')) {
effectLayerKeys.push(key);
} else {
effectRestKeys.push(key);
}
});
// ================= Inject Layer Style =================
// Inject layer style
effectLayerKeys.forEach(effectKey => {
(0, _dynamicCSS.updateCSS)(normalizeStyle(effectStyle[effectKey], autoPrefix || false), `_layer-${effectKey}`, {
...mergedCSSConfig,
prepend: true
});
});
// ==================== Inject Style ====================
// Inject style
const style = (0, _dynamicCSS.updateCSS)(styleStr, styleId, mergedCSSConfig);
style[_StyleContext.CSS_IN_JS_INSTANCE] = cache.instanceId;
// Debug usage. Dev only
if (process.env.NODE_ENV !== 'production') {
style.setAttribute(_StyleContext.ATTR_CACHE_PATH, fullPath.join('|'));
}
// ================ Inject Effect Style =================
// Inject client side effect style
effectRestKeys.forEach(effectKey => {
(0, _dynamicCSS.updateCSS)(normalizeStyle(effectStyle[effectKey], autoPrefix || false), `_effect-${effectKey}`, mergedCSSConfig);
});
}
});
}
const extract = (cache, effectStyles, options) => {
const [styleStr, styleId, effectStyle, clientOnly, order] = cache;
const {
plain,
autoPrefix
} = options || {};
// Skip client only style
if (clientOnly) {
return null;
}
let keyStyleText = styleStr;
// ====================== Share ======================
// Used for @rc-component/util
const sharedAttrs = {
'data-rc-order': 'prependQueue',
'data-rc-priority': `${order}`
};
// ====================== Style ======================
keyStyleText = (0, _util.toStyleStr)(styleStr, undefined, styleId, sharedAttrs, plain);
// =============== Create effect style ===============
if (effectStyle) {
Object.keys(effectStyle).forEach(effectKey => {
// Effect style can be reused
if (!effectStyles[effectKey]) {
effectStyles[effectKey] = true;
const effectStyleStr = normalizeStyle(effectStyle[effectKey], autoPrefix || false);
const effectStyleHTML = (0, _util.toStyleStr)(effectStyleStr, undefined, `_effect-${effectKey}`, sharedAttrs, plain);
if (effectKey.startsWith('@layer')) {
keyStyleText = effectStyleHTML + keyStyleText;
} else {
keyStyleText += effectStyleHTML;
}
}
});
}
return [order, styleId, keyStyleText];
};
exports.extract = extract;
+23
View File
@@ -0,0 +1,23 @@
import extractStyle from './extractStyle';
import useCacheToken, { getComputedToken } from './hooks/useCacheToken';
import useCSSVarRegister from './hooks/useCSSVarRegister';
import type { CSSInterpolation, CSSObject } from './hooks/useStyleRegister';
import useStyleRegister from './hooks/useStyleRegister';
import Keyframes from './Keyframes';
import type { Linter } from './linters';
import { legacyNotSelectorLinter, logicalPropertiesLinter, NaNLinter, parentSelectorLinter } from './linters';
import type { StyleProviderProps } from './StyleContext';
import StyleContext, { createCache, StyleProvider } from './StyleContext';
import type { AbstractCalculator, DerivativeFunc, TokenType } from './theme';
import { createTheme, genCalc, Theme } from './theme';
import type { Transformer } from './transformers/interface';
import autoPrefixTransformer from './transformers/autoPrefix';
import legacyLogicalPropertiesTransformer from './transformers/legacyLogicalProperties';
import px2remTransformer from './transformers/px2rem';
import { unit } from './util';
import { token2CSSVar } from './util/css-variables';
export { Theme, createTheme, useStyleRegister, useCSSVarRegister, useCacheToken, createCache, StyleProvider, StyleContext, Keyframes, extractStyle, getComputedToken, autoPrefixTransformer, legacyLogicalPropertiesTransformer, px2remTransformer, logicalPropertiesLinter, legacyNotSelectorLinter, parentSelectorLinter, NaNLinter, token2CSSVar, unit, genCalc, };
export type { TokenType, CSSObject, CSSInterpolation, DerivativeFunc, Transformer, Linter, StyleProviderProps, AbstractCalculator, };
export declare const _experimental: {
supportModernCSS: () => boolean;
};
+151
View File
@@ -0,0 +1,151 @@
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
Object.defineProperty(exports, "Keyframes", {
enumerable: true,
get: function () {
return _Keyframes.default;
}
});
Object.defineProperty(exports, "NaNLinter", {
enumerable: true,
get: function () {
return _linters.NaNLinter;
}
});
Object.defineProperty(exports, "StyleContext", {
enumerable: true,
get: function () {
return _StyleContext.default;
}
});
Object.defineProperty(exports, "StyleProvider", {
enumerable: true,
get: function () {
return _StyleContext.StyleProvider;
}
});
Object.defineProperty(exports, "Theme", {
enumerable: true,
get: function () {
return _theme.Theme;
}
});
exports._experimental = void 0;
Object.defineProperty(exports, "autoPrefixTransformer", {
enumerable: true,
get: function () {
return _autoPrefix.default;
}
});
Object.defineProperty(exports, "createCache", {
enumerable: true,
get: function () {
return _StyleContext.createCache;
}
});
Object.defineProperty(exports, "createTheme", {
enumerable: true,
get: function () {
return _theme.createTheme;
}
});
Object.defineProperty(exports, "extractStyle", {
enumerable: true,
get: function () {
return _extractStyle.default;
}
});
Object.defineProperty(exports, "genCalc", {
enumerable: true,
get: function () {
return _theme.genCalc;
}
});
Object.defineProperty(exports, "getComputedToken", {
enumerable: true,
get: function () {
return _useCacheToken.getComputedToken;
}
});
Object.defineProperty(exports, "legacyLogicalPropertiesTransformer", {
enumerable: true,
get: function () {
return _legacyLogicalProperties.default;
}
});
Object.defineProperty(exports, "legacyNotSelectorLinter", {
enumerable: true,
get: function () {
return _linters.legacyNotSelectorLinter;
}
});
Object.defineProperty(exports, "logicalPropertiesLinter", {
enumerable: true,
get: function () {
return _linters.logicalPropertiesLinter;
}
});
Object.defineProperty(exports, "parentSelectorLinter", {
enumerable: true,
get: function () {
return _linters.parentSelectorLinter;
}
});
Object.defineProperty(exports, "px2remTransformer", {
enumerable: true,
get: function () {
return _px2rem.default;
}
});
Object.defineProperty(exports, "token2CSSVar", {
enumerable: true,
get: function () {
return _cssVariables.token2CSSVar;
}
});
Object.defineProperty(exports, "unit", {
enumerable: true,
get: function () {
return _util.unit;
}
});
Object.defineProperty(exports, "useCSSVarRegister", {
enumerable: true,
get: function () {
return _useCSSVarRegister.default;
}
});
Object.defineProperty(exports, "useCacheToken", {
enumerable: true,
get: function () {
return _useCacheToken.default;
}
});
Object.defineProperty(exports, "useStyleRegister", {
enumerable: true,
get: function () {
return _useStyleRegister.default;
}
});
var _extractStyle = _interopRequireDefault(require("./extractStyle"));
var _useCacheToken = _interopRequireWildcard(require("./hooks/useCacheToken"));
var _useCSSVarRegister = _interopRequireDefault(require("./hooks/useCSSVarRegister"));
var _useStyleRegister = _interopRequireDefault(require("./hooks/useStyleRegister"));
var _Keyframes = _interopRequireDefault(require("./Keyframes"));
var _linters = require("./linters");
var _StyleContext = _interopRequireWildcard(require("./StyleContext"));
var _theme = require("./theme");
var _autoPrefix = _interopRequireDefault(require("./transformers/autoPrefix"));
var _legacyLogicalProperties = _interopRequireDefault(require("./transformers/legacyLogicalProperties"));
var _px2rem = _interopRequireDefault(require("./transformers/px2rem"));
var _util = require("./util");
var _cssVariables = require("./util/css-variables");
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
const _experimental = exports._experimental = {
supportModernCSS: () => (0, _util.supportWhere)() && (0, _util.supportLogicProps)()
};
@@ -0,0 +1,3 @@
import type { Linter } from './interface';
declare const linter: Linter;
export default linter;

Some files were not shown because too many files have changed in this diff Show More