feat(frontend): add API client, layout, and routing
- Create Axios-based API client with sources, jobs, and dashboard endpoints - Add responsive Layout component with navigation sidebar - Set up React Router with Dashboard, Backups, and Settings routes - Configure TanStack Query provider with default options - Use lucide-react for navigation icons
This commit is contained in:
+1
@@ -0,0 +1 @@
|
||||
../acorn/bin/acorn
|
||||
+1
@@ -0,0 +1 @@
|
||||
../autoprefixer/bin/autoprefixer
|
||||
+1
@@ -0,0 +1 @@
|
||||
../baseline-browser-mapping/dist/cli.cjs
|
||||
+1
@@ -0,0 +1 @@
|
||||
../browserslist/cli.js
|
||||
+1
@@ -0,0 +1 @@
|
||||
../cssesc/bin/cssesc
|
||||
+1
@@ -0,0 +1 @@
|
||||
../esbuild/bin/esbuild
|
||||
+1
@@ -0,0 +1 @@
|
||||
../jiti/bin/jiti.js
|
||||
+1
@@ -0,0 +1 @@
|
||||
../jsesc/bin/jsesc
|
||||
+1
@@ -0,0 +1 @@
|
||||
../json5/lib/cli.js
|
||||
+1
@@ -0,0 +1 @@
|
||||
../loose-envify/cli.js
|
||||
+1
@@ -0,0 +1 @@
|
||||
../lz-string/bin/bin.js
|
||||
+1
@@ -0,0 +1 @@
|
||||
../nanoid/bin/nanoid.cjs
|
||||
+1
@@ -0,0 +1 @@
|
||||
../which/bin/node-which
|
||||
+1
@@ -0,0 +1 @@
|
||||
../@babel/parser/bin/babel-parser.js
|
||||
+1
@@ -0,0 +1 @@
|
||||
../resolve/bin/resolve
|
||||
+1
@@ -0,0 +1 @@
|
||||
../rollup/dist/bin/rollup
|
||||
+1
@@ -0,0 +1 @@
|
||||
../semver/bin/semver.js
|
||||
+1
@@ -0,0 +1 @@
|
||||
../sucrase/bin/sucrase
|
||||
+1
@@ -0,0 +1 @@
|
||||
../sucrase/bin/sucrase-node
|
||||
+1
@@ -0,0 +1 @@
|
||||
../tailwindcss/lib/cli.js
|
||||
+1
@@ -0,0 +1 @@
|
||||
../tailwindcss/lib/cli.js
|
||||
+1
@@ -0,0 +1 @@
|
||||
../typescript/bin/tsc
|
||||
+1
@@ -0,0 +1 @@
|
||||
../typescript/bin/tsserver
|
||||
+1
@@ -0,0 +1 @@
|
||||
../update-browserslist-db/cli.js
|
||||
+1
@@ -0,0 +1 @@
|
||||
../vite/bin/vite.js
|
||||
+1
@@ -0,0 +1 @@
|
||||
../vite-node/vite-node.mjs
|
||||
+1
@@ -0,0 +1 @@
|
||||
../vitest/vitest.mjs
|
||||
+1
@@ -0,0 +1 @@
|
||||
../why-is-node-running/cli.js
|
||||
+5116
File diff suppressed because it is too large
Load Diff
+10
@@ -0,0 +1,10 @@
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2012 TJ Holowaychuk <tj@vision-media.ca>
|
||||
Copyright (c) 2022 Jean-Philippe Zolesio <holblin@gmail.com>
|
||||
|
||||
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.
|
||||
+146
@@ -0,0 +1,146 @@
|
||||
# @adobe/css-tools
|
||||
|
||||
> A modern CSS parser and stringifier with TypeScript support
|
||||
|
||||
[](https://badge.fury.io/js/%40adobe%2Fcss-tools)
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
|
||||
Parse CSS into an Abstract Syntax Tree (AST) and convert it back to CSS with configurable formatting. Built with TypeScript for type safety and modern JavaScript features.
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
npm install @adobe/css-tools
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```js
|
||||
import { parse, stringify } from '@adobe/css-tools'
|
||||
|
||||
// Parse CSS to AST
|
||||
const ast = parse('body { font-size: 12px; }')
|
||||
|
||||
// Stringify AST back to CSS
|
||||
const css = stringify(ast)
|
||||
// => "body { font-size: 12px; }"
|
||||
|
||||
// Pretty print with custom indentation
|
||||
const formatted = stringify(ast, { indent: ' ' })
|
||||
// => "body {\n font-size: 12px;\n}"
|
||||
|
||||
// Minify output
|
||||
const minified = stringify(ast, { compress: true })
|
||||
// => "body{font-size:12px}"
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### `parse(code, options?)`
|
||||
|
||||
Parses CSS code and returns an Abstract Syntax Tree (AST).
|
||||
|
||||
**Parameters:**
|
||||
- `code` (string) - The CSS code to parse
|
||||
- `options` (object, optional) - Parsing options
|
||||
- `silent` (boolean) - Silently fail on parse errors instead of throwing
|
||||
- `source` (string) - File path for better error reporting
|
||||
|
||||
**Returns:** `CssStylesheetAST` - The parsed CSS as an AST
|
||||
|
||||
### `stringify(ast, options?)`
|
||||
|
||||
Converts a CSS AST back to CSS string with configurable formatting.
|
||||
|
||||
**Parameters:**
|
||||
- `ast` (CssStylesheetAST) - The CSS AST to stringify
|
||||
- `options` (object, optional) - Stringification options
|
||||
- `indent` (string) - Indentation string (default: `' '`)
|
||||
- `compress` (boolean) - Whether to compress/minify the output (default: `false`)
|
||||
|
||||
**Returns:** `string` - The formatted CSS string
|
||||
|
||||
## Features
|
||||
|
||||
- **Complete CSS Support**: All standard CSS features including selectors, properties, values, at-rules, and comments
|
||||
- **TypeScript Support**: Full type definitions for all AST nodes and functions
|
||||
- **Error Handling**: Configurable error handling with detailed position information
|
||||
- **Formatting Options**: Pretty print, minify, or custom formatting
|
||||
- **Performance Optimized**: Efficient parsing and stringification for large CSS files
|
||||
- **Source Maps**: Track original source positions for debugging and tooling
|
||||
|
||||
### Supported CSS Features
|
||||
|
||||
- **Selectors**: Element, class, ID, attribute, pseudo-class, pseudo-element selectors
|
||||
- **Properties**: All standard CSS properties and custom properties
|
||||
- **Values**: Colors, lengths, percentages, functions, calc(), etc.
|
||||
- **At-rules**: @media, @keyframes, @import, @charset, @namespace, @font-face, @page, @document, @supports, @container, @layer, @starting-style, @host, @custom-media
|
||||
- **Comments**: Both /* */ and // comments
|
||||
- **Whitespace**: Preserves formatting information
|
||||
- **Vendor prefixes**: Supports vendor-prefixed at-rules and properties
|
||||
- **Nested rules**: Media queries, supports, containers, etc.
|
||||
- **Complex selectors**: Combinators, pseudo-selectors, attribute selectors
|
||||
|
||||
## Examples
|
||||
|
||||
### Error Handling
|
||||
|
||||
```js
|
||||
import { parse } from '@adobe/css-tools'
|
||||
|
||||
const malformedCss = `
|
||||
body { color: red; }
|
||||
{ color: blue; } /* Missing selector */
|
||||
.valid { background: green; }
|
||||
`
|
||||
|
||||
// Parse with silent error handling
|
||||
const result = parse(malformedCss, { silent: true })
|
||||
|
||||
// Check for parsing errors
|
||||
if (result.stylesheet.parsingErrors) {
|
||||
console.log('Parsing errors:', result.stylesheet.parsingErrors.length)
|
||||
result.stylesheet.parsingErrors.forEach(error => {
|
||||
console.log(`Error at line ${error.line}: ${error.message}`)
|
||||
})
|
||||
}
|
||||
|
||||
// Valid rules are still parsed
|
||||
console.log('Valid rules:', result.stylesheet.rules.length)
|
||||
```
|
||||
|
||||
### Source Tracking
|
||||
|
||||
```js
|
||||
import { parse } from '@adobe/css-tools'
|
||||
|
||||
const css = 'body { color: red; }'
|
||||
const ast = parse(css, { source: 'styles.css' })
|
||||
|
||||
// Position information is available
|
||||
const rule = ast.stylesheet.rules[0]
|
||||
console.log(rule.position?.source) // "styles.css"
|
||||
console.log(rule.position?.start) // { line: 1, column: 1 }
|
||||
console.log(rule.position?.end) // { line: 1, column: 20 }
|
||||
```
|
||||
|
||||
For more examples, see the [Examples documentation](docs/EXAMPLES.md).
|
||||
|
||||
## Performance
|
||||
|
||||
The library is optimized for performance and can handle large CSS files efficiently. For benchmarking information, see the `benchmark/` directory in the source code.
|
||||
|
||||
## Documentation
|
||||
|
||||
- [API Reference](docs/API.md) - Complete API documentation
|
||||
- [AST Structure](docs/AST.md) - Detailed AST node types and structure
|
||||
- [Examples](docs/EXAMPLES.md) - Comprehensive usage examples
|
||||
- [Changelog](docs/CHANGELOG.md) - Version history and changes
|
||||
|
||||
## Background
|
||||
|
||||
This is a fork of the npm `css` package, maintained by Adobe with modern improvements including TypeScript support, enhanced performance, and security updates. It provides a robust foundation for CSS tooling, preprocessing, and analysis.
|
||||
|
||||
## License
|
||||
|
||||
[MIT](LICENSE)
|
||||
+2
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
+176
@@ -0,0 +1,176 @@
|
||||
declare class CssParseError extends Error {
|
||||
readonly reason: string;
|
||||
readonly filename?: string;
|
||||
readonly line: number;
|
||||
readonly column: number;
|
||||
readonly source: string;
|
||||
constructor(filename: string, msg: string, lineno: number, column: number, css: string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store position information for a node
|
||||
*/
|
||||
declare class Position {
|
||||
start: {
|
||||
line: number;
|
||||
column: number;
|
||||
};
|
||||
end: {
|
||||
line: number;
|
||||
column: number;
|
||||
};
|
||||
source?: string;
|
||||
constructor(start: {
|
||||
line: number;
|
||||
column: number;
|
||||
}, end: {
|
||||
line: number;
|
||||
column: number;
|
||||
}, source: string);
|
||||
}
|
||||
|
||||
declare enum CssTypes {
|
||||
stylesheet = "stylesheet",
|
||||
rule = "rule",
|
||||
declaration = "declaration",
|
||||
comment = "comment",
|
||||
container = "container",
|
||||
charset = "charset",
|
||||
document = "document",
|
||||
customMedia = "custom-media",
|
||||
fontFace = "font-face",
|
||||
host = "host",
|
||||
import = "import",
|
||||
keyframes = "keyframes",
|
||||
keyframe = "keyframe",
|
||||
layer = "layer",
|
||||
media = "media",
|
||||
namespace = "namespace",
|
||||
page = "page",
|
||||
startingStyle = "starting-style",
|
||||
supports = "supports"
|
||||
}
|
||||
type CssCommonAST = {
|
||||
type: CssTypes;
|
||||
};
|
||||
type CssCommonPositionAST = CssCommonAST & {
|
||||
position?: Position;
|
||||
parent?: unknown;
|
||||
};
|
||||
type CssStylesheetAST = CssCommonAST & {
|
||||
type: CssTypes.stylesheet;
|
||||
stylesheet: {
|
||||
source?: string;
|
||||
rules: Array<CssAtRuleAST>;
|
||||
parsingErrors?: Array<CssParseError>;
|
||||
};
|
||||
};
|
||||
type CssRuleAST = CssCommonPositionAST & {
|
||||
type: CssTypes.rule;
|
||||
selectors: Array<string>;
|
||||
declarations: Array<CssDeclarationAST | CssCommentAST>;
|
||||
};
|
||||
type CssDeclarationAST = CssCommonPositionAST & {
|
||||
type: CssTypes.declaration;
|
||||
property: string;
|
||||
value: string;
|
||||
};
|
||||
type CssCommentAST = CssCommonPositionAST & {
|
||||
type: CssTypes.comment;
|
||||
comment: string;
|
||||
};
|
||||
type CssContainerAST = CssCommonPositionAST & {
|
||||
type: CssTypes.container;
|
||||
container: string;
|
||||
rules: Array<CssAtRuleAST>;
|
||||
};
|
||||
type CssCharsetAST = CssCommonPositionAST & {
|
||||
type: CssTypes.charset;
|
||||
charset: string;
|
||||
};
|
||||
type CssCustomMediaAST = CssCommonPositionAST & {
|
||||
type: CssTypes.customMedia;
|
||||
name: string;
|
||||
media: string;
|
||||
};
|
||||
type CssDocumentAST = CssCommonPositionAST & {
|
||||
type: CssTypes.document;
|
||||
document: string;
|
||||
vendor?: string;
|
||||
rules: Array<CssAtRuleAST>;
|
||||
};
|
||||
type CssFontFaceAST = CssCommonPositionAST & {
|
||||
type: CssTypes.fontFace;
|
||||
declarations: Array<CssDeclarationAST | CssCommentAST>;
|
||||
};
|
||||
type CssHostAST = CssCommonPositionAST & {
|
||||
type: CssTypes.host;
|
||||
rules: Array<CssAtRuleAST>;
|
||||
};
|
||||
type CssImportAST = CssCommonPositionAST & {
|
||||
type: CssTypes.import;
|
||||
import: string;
|
||||
};
|
||||
type CssKeyframesAST = CssCommonPositionAST & {
|
||||
type: CssTypes.keyframes;
|
||||
name: string;
|
||||
vendor?: string;
|
||||
keyframes: Array<CssKeyframeAST | CssCommentAST>;
|
||||
};
|
||||
type CssKeyframeAST = CssCommonPositionAST & {
|
||||
type: CssTypes.keyframe;
|
||||
values: Array<string>;
|
||||
declarations: Array<CssDeclarationAST | CssCommentAST>;
|
||||
};
|
||||
type CssLayerAST = CssCommonPositionAST & {
|
||||
type: CssTypes.layer;
|
||||
layer: string;
|
||||
rules?: Array<CssAtRuleAST>;
|
||||
};
|
||||
type CssMediaAST = CssCommonPositionAST & {
|
||||
type: CssTypes.media;
|
||||
media: string;
|
||||
rules: Array<CssAtRuleAST>;
|
||||
};
|
||||
type CssNamespaceAST = CssCommonPositionAST & {
|
||||
type: CssTypes.namespace;
|
||||
namespace: string;
|
||||
};
|
||||
type CssPageAST = CssCommonPositionAST & {
|
||||
type: CssTypes.page;
|
||||
selectors: Array<string>;
|
||||
declarations: Array<CssDeclarationAST | CssCommentAST>;
|
||||
};
|
||||
type CssSupportsAST = CssCommonPositionAST & {
|
||||
type: CssTypes.supports;
|
||||
supports: string;
|
||||
rules: Array<CssAtRuleAST>;
|
||||
};
|
||||
type CssStartingStyleAST = CssCommonPositionAST & {
|
||||
type: CssTypes.startingStyle;
|
||||
rules: Array<CssAtRuleAST>;
|
||||
};
|
||||
type CssAtRuleAST = CssRuleAST | CssCommentAST | CssContainerAST | CssCharsetAST | CssCustomMediaAST | CssDocumentAST | CssFontFaceAST | CssHostAST | CssImportAST | CssKeyframesAST | CssLayerAST | CssMediaAST | CssNamespaceAST | CssPageAST | CssSupportsAST | CssStartingStyleAST;
|
||||
type CssAllNodesAST = CssAtRuleAST | CssStylesheetAST | CssDeclarationAST | CssKeyframeAST;
|
||||
|
||||
type CompilerOptions = {
|
||||
indent?: string;
|
||||
compress?: boolean;
|
||||
};
|
||||
|
||||
declare const parse: (css: string, options?: {
|
||||
source?: string;
|
||||
silent?: boolean;
|
||||
}) => CssStylesheetAST;
|
||||
declare const stringify: (node: CssStylesheetAST, options?: CompilerOptions) => string;
|
||||
|
||||
declare const _default: {
|
||||
parse: (css: string, options?: {
|
||||
source?: string;
|
||||
silent?: boolean;
|
||||
}) => CssStylesheetAST;
|
||||
stringify: (node: CssStylesheetAST, options?: CompilerOptions) => string;
|
||||
};
|
||||
|
||||
export { CssTypes, _default as default, parse, stringify };
|
||||
export type { CssAllNodesAST, CssAtRuleAST, CssCharsetAST, CssCommentAST, CssCommonAST, CssCommonPositionAST, CssContainerAST, CssCustomMediaAST, CssDeclarationAST, CssDocumentAST, CssFontFaceAST, CssHostAST, CssImportAST, CssKeyframeAST, CssKeyframesAST, CssLayerAST, CssMediaAST, CssNamespaceAST, CssPageAST, CssRuleAST, CssStartingStyleAST, CssStylesheetAST, CssSupportsAST };
|
||||
+176
@@ -0,0 +1,176 @@
|
||||
declare class CssParseError extends Error {
|
||||
readonly reason: string;
|
||||
readonly filename?: string;
|
||||
readonly line: number;
|
||||
readonly column: number;
|
||||
readonly source: string;
|
||||
constructor(filename: string, msg: string, lineno: number, column: number, css: string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store position information for a node
|
||||
*/
|
||||
declare class Position {
|
||||
start: {
|
||||
line: number;
|
||||
column: number;
|
||||
};
|
||||
end: {
|
||||
line: number;
|
||||
column: number;
|
||||
};
|
||||
source?: string;
|
||||
constructor(start: {
|
||||
line: number;
|
||||
column: number;
|
||||
}, end: {
|
||||
line: number;
|
||||
column: number;
|
||||
}, source: string);
|
||||
}
|
||||
|
||||
declare enum CssTypes {
|
||||
stylesheet = "stylesheet",
|
||||
rule = "rule",
|
||||
declaration = "declaration",
|
||||
comment = "comment",
|
||||
container = "container",
|
||||
charset = "charset",
|
||||
document = "document",
|
||||
customMedia = "custom-media",
|
||||
fontFace = "font-face",
|
||||
host = "host",
|
||||
import = "import",
|
||||
keyframes = "keyframes",
|
||||
keyframe = "keyframe",
|
||||
layer = "layer",
|
||||
media = "media",
|
||||
namespace = "namespace",
|
||||
page = "page",
|
||||
startingStyle = "starting-style",
|
||||
supports = "supports"
|
||||
}
|
||||
type CssCommonAST = {
|
||||
type: CssTypes;
|
||||
};
|
||||
type CssCommonPositionAST = CssCommonAST & {
|
||||
position?: Position;
|
||||
parent?: unknown;
|
||||
};
|
||||
type CssStylesheetAST = CssCommonAST & {
|
||||
type: CssTypes.stylesheet;
|
||||
stylesheet: {
|
||||
source?: string;
|
||||
rules: Array<CssAtRuleAST>;
|
||||
parsingErrors?: Array<CssParseError>;
|
||||
};
|
||||
};
|
||||
type CssRuleAST = CssCommonPositionAST & {
|
||||
type: CssTypes.rule;
|
||||
selectors: Array<string>;
|
||||
declarations: Array<CssDeclarationAST | CssCommentAST>;
|
||||
};
|
||||
type CssDeclarationAST = CssCommonPositionAST & {
|
||||
type: CssTypes.declaration;
|
||||
property: string;
|
||||
value: string;
|
||||
};
|
||||
type CssCommentAST = CssCommonPositionAST & {
|
||||
type: CssTypes.comment;
|
||||
comment: string;
|
||||
};
|
||||
type CssContainerAST = CssCommonPositionAST & {
|
||||
type: CssTypes.container;
|
||||
container: string;
|
||||
rules: Array<CssAtRuleAST>;
|
||||
};
|
||||
type CssCharsetAST = CssCommonPositionAST & {
|
||||
type: CssTypes.charset;
|
||||
charset: string;
|
||||
};
|
||||
type CssCustomMediaAST = CssCommonPositionAST & {
|
||||
type: CssTypes.customMedia;
|
||||
name: string;
|
||||
media: string;
|
||||
};
|
||||
type CssDocumentAST = CssCommonPositionAST & {
|
||||
type: CssTypes.document;
|
||||
document: string;
|
||||
vendor?: string;
|
||||
rules: Array<CssAtRuleAST>;
|
||||
};
|
||||
type CssFontFaceAST = CssCommonPositionAST & {
|
||||
type: CssTypes.fontFace;
|
||||
declarations: Array<CssDeclarationAST | CssCommentAST>;
|
||||
};
|
||||
type CssHostAST = CssCommonPositionAST & {
|
||||
type: CssTypes.host;
|
||||
rules: Array<CssAtRuleAST>;
|
||||
};
|
||||
type CssImportAST = CssCommonPositionAST & {
|
||||
type: CssTypes.import;
|
||||
import: string;
|
||||
};
|
||||
type CssKeyframesAST = CssCommonPositionAST & {
|
||||
type: CssTypes.keyframes;
|
||||
name: string;
|
||||
vendor?: string;
|
||||
keyframes: Array<CssKeyframeAST | CssCommentAST>;
|
||||
};
|
||||
type CssKeyframeAST = CssCommonPositionAST & {
|
||||
type: CssTypes.keyframe;
|
||||
values: Array<string>;
|
||||
declarations: Array<CssDeclarationAST | CssCommentAST>;
|
||||
};
|
||||
type CssLayerAST = CssCommonPositionAST & {
|
||||
type: CssTypes.layer;
|
||||
layer: string;
|
||||
rules?: Array<CssAtRuleAST>;
|
||||
};
|
||||
type CssMediaAST = CssCommonPositionAST & {
|
||||
type: CssTypes.media;
|
||||
media: string;
|
||||
rules: Array<CssAtRuleAST>;
|
||||
};
|
||||
type CssNamespaceAST = CssCommonPositionAST & {
|
||||
type: CssTypes.namespace;
|
||||
namespace: string;
|
||||
};
|
||||
type CssPageAST = CssCommonPositionAST & {
|
||||
type: CssTypes.page;
|
||||
selectors: Array<string>;
|
||||
declarations: Array<CssDeclarationAST | CssCommentAST>;
|
||||
};
|
||||
type CssSupportsAST = CssCommonPositionAST & {
|
||||
type: CssTypes.supports;
|
||||
supports: string;
|
||||
rules: Array<CssAtRuleAST>;
|
||||
};
|
||||
type CssStartingStyleAST = CssCommonPositionAST & {
|
||||
type: CssTypes.startingStyle;
|
||||
rules: Array<CssAtRuleAST>;
|
||||
};
|
||||
type CssAtRuleAST = CssRuleAST | CssCommentAST | CssContainerAST | CssCharsetAST | CssCustomMediaAST | CssDocumentAST | CssFontFaceAST | CssHostAST | CssImportAST | CssKeyframesAST | CssLayerAST | CssMediaAST | CssNamespaceAST | CssPageAST | CssSupportsAST | CssStartingStyleAST;
|
||||
type CssAllNodesAST = CssAtRuleAST | CssStylesheetAST | CssDeclarationAST | CssKeyframeAST;
|
||||
|
||||
type CompilerOptions = {
|
||||
indent?: string;
|
||||
compress?: boolean;
|
||||
};
|
||||
|
||||
declare const parse: (css: string, options?: {
|
||||
source?: string;
|
||||
silent?: boolean;
|
||||
}) => CssStylesheetAST;
|
||||
declare const stringify: (node: CssStylesheetAST, options?: CompilerOptions) => string;
|
||||
|
||||
declare const _default: {
|
||||
parse: (css: string, options?: {
|
||||
source?: string;
|
||||
silent?: boolean;
|
||||
}) => CssStylesheetAST;
|
||||
stringify: (node: CssStylesheetAST, options?: CompilerOptions) => string;
|
||||
};
|
||||
|
||||
export { CssTypes, _default as default, parse, stringify };
|
||||
export type { CssAllNodesAST, CssAtRuleAST, CssCharsetAST, CssCommentAST, CssCommonAST, CssCommonPositionAST, CssContainerAST, CssCustomMediaAST, CssDeclarationAST, CssDocumentAST, CssFontFaceAST, CssHostAST, CssImportAST, CssKeyframeAST, CssKeyframesAST, CssLayerAST, CssMediaAST, CssNamespaceAST, CssPageAST, CssRuleAST, CssStartingStyleAST, CssStylesheetAST, CssSupportsAST };
|
||||
+2
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
+176
@@ -0,0 +1,176 @@
|
||||
declare class CssParseError extends Error {
|
||||
readonly reason: string;
|
||||
readonly filename?: string;
|
||||
readonly line: number;
|
||||
readonly column: number;
|
||||
readonly source: string;
|
||||
constructor(filename: string, msg: string, lineno: number, column: number, css: string);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store position information for a node
|
||||
*/
|
||||
declare class Position {
|
||||
start: {
|
||||
line: number;
|
||||
column: number;
|
||||
};
|
||||
end: {
|
||||
line: number;
|
||||
column: number;
|
||||
};
|
||||
source?: string;
|
||||
constructor(start: {
|
||||
line: number;
|
||||
column: number;
|
||||
}, end: {
|
||||
line: number;
|
||||
column: number;
|
||||
}, source: string);
|
||||
}
|
||||
|
||||
declare enum CssTypes {
|
||||
stylesheet = "stylesheet",
|
||||
rule = "rule",
|
||||
declaration = "declaration",
|
||||
comment = "comment",
|
||||
container = "container",
|
||||
charset = "charset",
|
||||
document = "document",
|
||||
customMedia = "custom-media",
|
||||
fontFace = "font-face",
|
||||
host = "host",
|
||||
import = "import",
|
||||
keyframes = "keyframes",
|
||||
keyframe = "keyframe",
|
||||
layer = "layer",
|
||||
media = "media",
|
||||
namespace = "namespace",
|
||||
page = "page",
|
||||
startingStyle = "starting-style",
|
||||
supports = "supports"
|
||||
}
|
||||
type CssCommonAST = {
|
||||
type: CssTypes;
|
||||
};
|
||||
type CssCommonPositionAST = CssCommonAST & {
|
||||
position?: Position;
|
||||
parent?: unknown;
|
||||
};
|
||||
type CssStylesheetAST = CssCommonAST & {
|
||||
type: CssTypes.stylesheet;
|
||||
stylesheet: {
|
||||
source?: string;
|
||||
rules: Array<CssAtRuleAST>;
|
||||
parsingErrors?: Array<CssParseError>;
|
||||
};
|
||||
};
|
||||
type CssRuleAST = CssCommonPositionAST & {
|
||||
type: CssTypes.rule;
|
||||
selectors: Array<string>;
|
||||
declarations: Array<CssDeclarationAST | CssCommentAST>;
|
||||
};
|
||||
type CssDeclarationAST = CssCommonPositionAST & {
|
||||
type: CssTypes.declaration;
|
||||
property: string;
|
||||
value: string;
|
||||
};
|
||||
type CssCommentAST = CssCommonPositionAST & {
|
||||
type: CssTypes.comment;
|
||||
comment: string;
|
||||
};
|
||||
type CssContainerAST = CssCommonPositionAST & {
|
||||
type: CssTypes.container;
|
||||
container: string;
|
||||
rules: Array<CssAtRuleAST>;
|
||||
};
|
||||
type CssCharsetAST = CssCommonPositionAST & {
|
||||
type: CssTypes.charset;
|
||||
charset: string;
|
||||
};
|
||||
type CssCustomMediaAST = CssCommonPositionAST & {
|
||||
type: CssTypes.customMedia;
|
||||
name: string;
|
||||
media: string;
|
||||
};
|
||||
type CssDocumentAST = CssCommonPositionAST & {
|
||||
type: CssTypes.document;
|
||||
document: string;
|
||||
vendor?: string;
|
||||
rules: Array<CssAtRuleAST>;
|
||||
};
|
||||
type CssFontFaceAST = CssCommonPositionAST & {
|
||||
type: CssTypes.fontFace;
|
||||
declarations: Array<CssDeclarationAST | CssCommentAST>;
|
||||
};
|
||||
type CssHostAST = CssCommonPositionAST & {
|
||||
type: CssTypes.host;
|
||||
rules: Array<CssAtRuleAST>;
|
||||
};
|
||||
type CssImportAST = CssCommonPositionAST & {
|
||||
type: CssTypes.import;
|
||||
import: string;
|
||||
};
|
||||
type CssKeyframesAST = CssCommonPositionAST & {
|
||||
type: CssTypes.keyframes;
|
||||
name: string;
|
||||
vendor?: string;
|
||||
keyframes: Array<CssKeyframeAST | CssCommentAST>;
|
||||
};
|
||||
type CssKeyframeAST = CssCommonPositionAST & {
|
||||
type: CssTypes.keyframe;
|
||||
values: Array<string>;
|
||||
declarations: Array<CssDeclarationAST | CssCommentAST>;
|
||||
};
|
||||
type CssLayerAST = CssCommonPositionAST & {
|
||||
type: CssTypes.layer;
|
||||
layer: string;
|
||||
rules?: Array<CssAtRuleAST>;
|
||||
};
|
||||
type CssMediaAST = CssCommonPositionAST & {
|
||||
type: CssTypes.media;
|
||||
media: string;
|
||||
rules: Array<CssAtRuleAST>;
|
||||
};
|
||||
type CssNamespaceAST = CssCommonPositionAST & {
|
||||
type: CssTypes.namespace;
|
||||
namespace: string;
|
||||
};
|
||||
type CssPageAST = CssCommonPositionAST & {
|
||||
type: CssTypes.page;
|
||||
selectors: Array<string>;
|
||||
declarations: Array<CssDeclarationAST | CssCommentAST>;
|
||||
};
|
||||
type CssSupportsAST = CssCommonPositionAST & {
|
||||
type: CssTypes.supports;
|
||||
supports: string;
|
||||
rules: Array<CssAtRuleAST>;
|
||||
};
|
||||
type CssStartingStyleAST = CssCommonPositionAST & {
|
||||
type: CssTypes.startingStyle;
|
||||
rules: Array<CssAtRuleAST>;
|
||||
};
|
||||
type CssAtRuleAST = CssRuleAST | CssCommentAST | CssContainerAST | CssCharsetAST | CssCustomMediaAST | CssDocumentAST | CssFontFaceAST | CssHostAST | CssImportAST | CssKeyframesAST | CssLayerAST | CssMediaAST | CssNamespaceAST | CssPageAST | CssSupportsAST | CssStartingStyleAST;
|
||||
type CssAllNodesAST = CssAtRuleAST | CssStylesheetAST | CssDeclarationAST | CssKeyframeAST;
|
||||
|
||||
type CompilerOptions = {
|
||||
indent?: string;
|
||||
compress?: boolean;
|
||||
};
|
||||
|
||||
declare const parse: (css: string, options?: {
|
||||
source?: string;
|
||||
silent?: boolean;
|
||||
}) => CssStylesheetAST;
|
||||
declare const stringify: (node: CssStylesheetAST, options?: CompilerOptions) => string;
|
||||
|
||||
declare const _default: {
|
||||
parse: (css: string, options?: {
|
||||
source?: string;
|
||||
silent?: boolean;
|
||||
}) => CssStylesheetAST;
|
||||
stringify: (node: CssStylesheetAST, options?: CompilerOptions) => string;
|
||||
};
|
||||
|
||||
export { CssTypes, _default as default, parse, stringify };
|
||||
export type { CssAllNodesAST, CssAtRuleAST, CssCharsetAST, CssCommentAST, CssCommonAST, CssCommonPositionAST, CssContainerAST, CssCustomMediaAST, CssDeclarationAST, CssDocumentAST, CssFontFaceAST, CssHostAST, CssImportAST, CssKeyframeAST, CssKeyframesAST, CssLayerAST, CssMediaAST, CssNamespaceAST, CssPageAST, CssRuleAST, CssStartingStyleAST, CssStylesheetAST, CssSupportsAST };
|
||||
+2
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
+314
@@ -0,0 +1,314 @@
|
||||
# API Reference
|
||||
|
||||
## Overview
|
||||
|
||||
`@adobe/css-tools` provides a modern CSS parser and stringifier with comprehensive TypeScript support. It can parse CSS into an Abstract Syntax Tree (AST) and convert the AST back to CSS with various formatting options.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
npm install @adobe/css-tools
|
||||
```
|
||||
|
||||
## Core Functions
|
||||
|
||||
### `parse(code, options?)`
|
||||
|
||||
Parses CSS code and returns an Abstract Syntax Tree (AST).
|
||||
|
||||
#### Parameters
|
||||
|
||||
- `code` (string) - The CSS code to parse
|
||||
- `options` (object, optional) - Parsing options
|
||||
- `silent` (boolean) - Silently fail on parse errors instead of throwing. When `true`, errors are collected in `ast.stylesheet.parsingErrors`
|
||||
- `source` (string) - File path for better error reporting
|
||||
|
||||
#### Returns
|
||||
|
||||
- `CssStylesheetAST` - The parsed CSS as an AST
|
||||
|
||||
#### Example
|
||||
|
||||
```javascript
|
||||
import { parse } from '@adobe/css-tools';
|
||||
|
||||
const css = `
|
||||
.example {
|
||||
color: red;
|
||||
font-size: 16px;
|
||||
}
|
||||
`;
|
||||
|
||||
const ast = parse(css);
|
||||
console.log(ast.stylesheet.rules);
|
||||
```
|
||||
|
||||
### `stringify(ast, options?)`
|
||||
|
||||
Converts a CSS AST back to CSS string with configurable formatting.
|
||||
|
||||
#### Parameters
|
||||
|
||||
- `ast` (CssStylesheetAST) - The CSS AST to stringify
|
||||
- `options` (CompilerOptions, optional) - Stringification options
|
||||
- `indent` (string) - Indentation string (default: `' '`)
|
||||
- `compress` (boolean) - Whether to compress/minify the output (default: `false`)
|
||||
|
||||
#### Returns
|
||||
|
||||
- `string` - The formatted CSS string
|
||||
|
||||
#### Example
|
||||
|
||||
```javascript
|
||||
import { parse, stringify } from '@adobe/css-tools';
|
||||
|
||||
const css = '.example { color: red; }';
|
||||
const ast = parse(css);
|
||||
|
||||
// Pretty print
|
||||
const formatted = stringify(ast, { indent: ' ' });
|
||||
console.log(formatted);
|
||||
// Output:
|
||||
// .example {
|
||||
// color: red;
|
||||
// }
|
||||
|
||||
// Compressed
|
||||
const minified = stringify(ast, { compress: true });
|
||||
console.log(minified);
|
||||
// Output: .example{color:red}
|
||||
```
|
||||
|
||||
## Type Definitions
|
||||
|
||||
### Core Types
|
||||
|
||||
#### `CssStylesheetAST`
|
||||
The root AST node representing a complete CSS stylesheet.
|
||||
|
||||
```typescript
|
||||
type CssStylesheetAST = {
|
||||
type: CssTypes.stylesheet;
|
||||
stylesheet: {
|
||||
source?: string;
|
||||
rules: CssRuleAST[];
|
||||
parsingErrors?: CssParseError[];
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
#### `CssRuleAST`
|
||||
Represents a CSS rule (selector + declarations).
|
||||
|
||||
```typescript
|
||||
type CssRuleAST = {
|
||||
type: CssTypes.rule;
|
||||
selectors: string[];
|
||||
declarations: CssDeclarationAST[];
|
||||
position?: CssPosition;
|
||||
parent?: CssStylesheetAST;
|
||||
};
|
||||
```
|
||||
|
||||
#### `CssDeclarationAST`
|
||||
Represents a CSS property declaration.
|
||||
|
||||
```typescript
|
||||
type CssDeclarationAST = {
|
||||
type: CssTypes.declaration;
|
||||
property: string;
|
||||
value: string;
|
||||
position?: CssPosition;
|
||||
parent?: CssRuleAST;
|
||||
};
|
||||
```
|
||||
|
||||
#### `CssMediaAST`
|
||||
Represents a CSS @media rule.
|
||||
|
||||
```typescript
|
||||
type CssMediaAST = {
|
||||
type: CssTypes.media;
|
||||
media: string;
|
||||
rules: CssRuleAST[];
|
||||
position?: CssPosition;
|
||||
parent?: CssStylesheetAST;
|
||||
};
|
||||
```
|
||||
|
||||
#### `CssKeyframesAST`
|
||||
Represents a CSS @keyframes rule.
|
||||
|
||||
```typescript
|
||||
type CssKeyframesAST = {
|
||||
type: CssTypes.keyframes;
|
||||
name: string;
|
||||
keyframes: CssKeyframeAST[];
|
||||
position?: CssPosition;
|
||||
parent?: CssStylesheetAST;
|
||||
};
|
||||
```
|
||||
|
||||
#### `CssPosition`
|
||||
Represents source position information.
|
||||
|
||||
```typescript
|
||||
type CssPosition = {
|
||||
source?: string;
|
||||
start: {
|
||||
line: number;
|
||||
column: number;
|
||||
};
|
||||
end: {
|
||||
line: number;
|
||||
column: number;
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
#### `CssParseError`
|
||||
Represents a parsing error.
|
||||
|
||||
```typescript
|
||||
type CssParseError = {
|
||||
message: string;
|
||||
reason: string;
|
||||
filename?: string;
|
||||
line: number;
|
||||
column: number;
|
||||
source?: string;
|
||||
};
|
||||
```
|
||||
|
||||
### Compiler Options
|
||||
|
||||
#### `CompilerOptions`
|
||||
Options for the stringifier.
|
||||
|
||||
```typescript
|
||||
type CompilerOptions = {
|
||||
indent?: string; // Default: ' '
|
||||
compress?: boolean; // Default: false
|
||||
};
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Silent Parsing
|
||||
|
||||
When parsing malformed CSS, you can use the `silent` option to collect errors instead of throwing:
|
||||
|
||||
```javascript
|
||||
import { parse } from '@adobe/css-tools';
|
||||
|
||||
const malformedCss = `
|
||||
body { color: red; }
|
||||
{ color: blue; } /* Missing selector */
|
||||
.valid { background: green; }
|
||||
`;
|
||||
|
||||
const result = parse(malformedCss, { silent: true });
|
||||
|
||||
if (result.stylesheet.parsingErrors) {
|
||||
result.stylesheet.parsingErrors.forEach(error => {
|
||||
console.log(`Error at line ${error.line}: ${error.message}`);
|
||||
});
|
||||
}
|
||||
|
||||
// Valid rules are still parsed
|
||||
console.log('Valid rules:', result.stylesheet.rules.length);
|
||||
```
|
||||
|
||||
### Source Tracking
|
||||
|
||||
Enable source tracking for better error reporting:
|
||||
|
||||
```javascript
|
||||
import { parse } from '@adobe/css-tools';
|
||||
|
||||
const css = 'body { color: red; }';
|
||||
const ast = parse(css, { source: 'styles.css' });
|
||||
|
||||
const rule = ast.stylesheet.rules[0];
|
||||
console.log(rule.position?.source); // "styles.css"
|
||||
console.log(rule.position?.start); // { line: 1, column: 1 }
|
||||
console.log(rule.position?.end); // { line: 1, column: 20 }
|
||||
```
|
||||
|
||||
## Advanced Usage
|
||||
|
||||
### Working with At-Rules
|
||||
|
||||
```javascript
|
||||
import { parse, stringify } from '@adobe/css-tools';
|
||||
|
||||
const css = `
|
||||
@media (max-width: 768px) {
|
||||
.container {
|
||||
padding: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
`;
|
||||
|
||||
const ast = parse(css);
|
||||
|
||||
// Access media rules
|
||||
const mediaRule = ast.stylesheet.rules.find(rule => rule.type === 'media');
|
||||
console.log(mediaRule.media); // "(max-width: 768px)"
|
||||
|
||||
// Access keyframes
|
||||
const keyframesRule = ast.stylesheet.rules.find(rule => rule.type === 'keyframes');
|
||||
console.log(keyframesRule.name); // "fadeIn"
|
||||
```
|
||||
|
||||
### Custom Formatting
|
||||
|
||||
```javascript
|
||||
import { parse, stringify } from '@adobe/css-tools';
|
||||
|
||||
const css = '.example{color:red;font-size:16px}';
|
||||
const ast = parse(css);
|
||||
|
||||
// Custom indentation
|
||||
const formatted = stringify(ast, { indent: ' ' });
|
||||
console.log(formatted);
|
||||
// Output:
|
||||
// .example {
|
||||
// color: red;
|
||||
// font-size: 16px;
|
||||
// }
|
||||
|
||||
// Compressed with no spaces
|
||||
const compressed = stringify(ast, { compress: true });
|
||||
console.log(compressed);
|
||||
// Output: .example{color:red;font-size:16px}
|
||||
```
|
||||
|
||||
## TypeScript Integration
|
||||
|
||||
The library provides comprehensive TypeScript support with full type definitions for all AST nodes and functions:
|
||||
|
||||
```typescript
|
||||
import { parse, stringify, type CssStylesheetAST } from '@adobe/css-tools';
|
||||
|
||||
const css: string = '.example { color: red; }';
|
||||
const ast: CssStylesheetAST = parse(css);
|
||||
const output: string = stringify(ast);
|
||||
```
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
- The parser is optimized for large CSS files
|
||||
- AST nodes are lightweight and memory-efficient
|
||||
- Stringification is fast and supports streaming for large outputs
|
||||
- Consider using `compress: true` for production builds to reduce file size
|
||||
|
||||
## Browser Support
|
||||
|
||||
The library works in all modern browsers and Node.js environments. For older environments, you may need to use a bundler with appropriate polyfills.
|
||||
+366
@@ -0,0 +1,366 @@
|
||||
# Abstract Syntax Tree (AST)
|
||||
|
||||
## Overview
|
||||
|
||||
The AST represents CSS as a tree structure where each node has a specific type and properties. All nodes share common properties and have type-specific properties.
|
||||
|
||||
## Common Properties
|
||||
|
||||
All AST nodes have these properties:
|
||||
|
||||
### `type`
|
||||
|
||||
The node type as a string. See [Node Types](#node-types) for all possible values.
|
||||
|
||||
### `position` (optional)
|
||||
|
||||
Position information for the node in the source code:
|
||||
|
||||
```typescript
|
||||
{
|
||||
start: { line: number; column: number };
|
||||
end: { line: number; column: number };
|
||||
source?: string;
|
||||
}
|
||||
```
|
||||
|
||||
### `parent` (optional)
|
||||
|
||||
Reference to the parent node in the AST.
|
||||
|
||||
## Node Types
|
||||
|
||||
### `stylesheet`
|
||||
|
||||
The root node representing an entire CSS document.
|
||||
|
||||
**Properties:**
|
||||
- `stylesheet.source` (optional): Source file path
|
||||
- `stylesheet.rules`: Array of top-level rules
|
||||
- `stylesheet.parsingErrors` (optional): Array of parse errors when `silent` option is used
|
||||
|
||||
**Example:**
|
||||
```json
|
||||
{
|
||||
"type": "stylesheet",
|
||||
"stylesheet": {
|
||||
"rules": [
|
||||
// ... other nodes
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### `rule`
|
||||
|
||||
A CSS rule with selectors and declarations.
|
||||
|
||||
**Properties:**
|
||||
- `selectors`: Array of CSS selectors as strings
|
||||
- `declarations`: Array of declarations and comments
|
||||
|
||||
**Example:**
|
||||
```json
|
||||
{
|
||||
"type": "rule",
|
||||
"selectors": ["body", "html"],
|
||||
"declarations": [
|
||||
{
|
||||
"type": "declaration",
|
||||
"property": "color",
|
||||
"value": "red"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### `declaration`
|
||||
|
||||
A CSS property declaration.
|
||||
|
||||
**Properties:**
|
||||
- `property`: The CSS property name
|
||||
- `value`: The CSS property value as a string
|
||||
|
||||
**Example:**
|
||||
```json
|
||||
{
|
||||
"type": "declaration",
|
||||
"property": "background-color",
|
||||
"value": "#ffffff"
|
||||
}
|
||||
```
|
||||
|
||||
### `comment`
|
||||
|
||||
A CSS comment.
|
||||
|
||||
**Properties:**
|
||||
- `comment`: The comment text (without `/*` and `*/`)
|
||||
|
||||
**Example:**
|
||||
```json
|
||||
{
|
||||
"type": "comment",
|
||||
"comment": " This is a comment "
|
||||
}
|
||||
```
|
||||
|
||||
### `media`
|
||||
|
||||
A `@media` rule.
|
||||
|
||||
**Properties:**
|
||||
- `media`: The media query string
|
||||
- `rules`: Array of rules within the media block
|
||||
|
||||
**Example:**
|
||||
```json
|
||||
{
|
||||
"type": "media",
|
||||
"media": "screen and (max-width: 768px)",
|
||||
"rules": [
|
||||
// ... nested rules
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### `keyframes`
|
||||
|
||||
A `@keyframes` rule.
|
||||
|
||||
**Properties:**
|
||||
- `name`: The keyframes name
|
||||
- `vendor` (optional): Vendor prefix (e.g., "-webkit-")
|
||||
- `keyframes`: Array of keyframe rules and comments
|
||||
|
||||
**Example:**
|
||||
```json
|
||||
{
|
||||
"type": "keyframes",
|
||||
"name": "fade",
|
||||
"keyframes": [
|
||||
{
|
||||
"type": "keyframe",
|
||||
"values": ["from"],
|
||||
"declarations": [
|
||||
{
|
||||
"type": "declaration",
|
||||
"property": "opacity",
|
||||
"value": "0"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### `keyframe`
|
||||
|
||||
A keyframe within a `@keyframes` rule.
|
||||
|
||||
**Properties:**
|
||||
- `values`: Array of keyframe selectors (e.g., `["from"]`, `["to"]`, `["50%"]`)
|
||||
- `declarations`: Array of declarations and comments
|
||||
|
||||
### `import`
|
||||
|
||||
An `@import` rule.
|
||||
|
||||
**Properties:**
|
||||
- `import`: The import string (URL or media query)
|
||||
|
||||
**Example:**
|
||||
```json
|
||||
{
|
||||
"type": "import",
|
||||
"import": "url('styles.css')"
|
||||
}
|
||||
```
|
||||
|
||||
### `charset`
|
||||
|
||||
A `@charset` rule.
|
||||
|
||||
**Properties:**
|
||||
- `charset`: The character encoding
|
||||
|
||||
**Example:**
|
||||
```json
|
||||
{
|
||||
"type": "charset",
|
||||
"charset": "utf-8"
|
||||
}
|
||||
```
|
||||
|
||||
### `namespace`
|
||||
|
||||
A `@namespace` rule.
|
||||
|
||||
**Properties:**
|
||||
- `namespace`: The namespace declaration
|
||||
|
||||
**Example:**
|
||||
```json
|
||||
{
|
||||
"type": "namespace",
|
||||
"namespace": "url(http://www.w3.org/1999/xhtml)"
|
||||
}
|
||||
```
|
||||
|
||||
### `supports`
|
||||
|
||||
A `@supports` rule.
|
||||
|
||||
**Properties:**
|
||||
- `supports`: The supports condition
|
||||
- `rules`: Array of rules within the supports block
|
||||
|
||||
**Example:**
|
||||
```json
|
||||
{
|
||||
"type": "supports",
|
||||
"supports": "(display: grid)",
|
||||
"rules": [
|
||||
// ... nested rules
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### `document`
|
||||
|
||||
A `@document` rule.
|
||||
|
||||
**Properties:**
|
||||
- `document`: The document condition
|
||||
- `vendor` (optional): Vendor prefix
|
||||
- `rules`: Array of rules within the document block
|
||||
|
||||
### `page`
|
||||
|
||||
A `@page` rule.
|
||||
|
||||
**Properties:**
|
||||
- `selectors`: Array of page selectors
|
||||
- `declarations`: Array of declarations and comments
|
||||
|
||||
### `font-face`
|
||||
|
||||
A `@font-face` rule.
|
||||
|
||||
**Properties:**
|
||||
- `declarations`: Array of font declarations and comments
|
||||
|
||||
### `host`
|
||||
|
||||
A `:host` rule.
|
||||
|
||||
**Properties:**
|
||||
- `rules`: Array of rules within the host block
|
||||
|
||||
### `container`
|
||||
|
||||
A `@container` rule.
|
||||
|
||||
**Properties:**
|
||||
- `container`: The container query
|
||||
- `rules`: Array of rules within the container block
|
||||
|
||||
### `layer`
|
||||
|
||||
A `@layer` rule.
|
||||
|
||||
**Properties:**
|
||||
- `layer`: The layer name
|
||||
- `rules` (optional): Array of rules within the layer block
|
||||
|
||||
### `custom-media`
|
||||
|
||||
A `@custom-media` rule.
|
||||
|
||||
**Properties:**
|
||||
- `name`: The custom media query name
|
||||
- `media`: The media query definition
|
||||
|
||||
### `starting-style`
|
||||
|
||||
A `@starting-style` rule.
|
||||
|
||||
**Properties:**
|
||||
- `rules`: Array of rules within the starting-style block
|
||||
|
||||
## Type Hierarchy
|
||||
|
||||
The AST nodes are organized in the following hierarchy:
|
||||
|
||||
- `CssStylesheetAST` - Root node
|
||||
- `CssAtRuleAST` - Union of all at-rule and rule nodes
|
||||
- `CssAllNodesAST` - Union of all possible node types
|
||||
|
||||
## Working with the AST
|
||||
|
||||
### Traversing Nodes
|
||||
|
||||
```typescript
|
||||
import { parse, CssStylesheetAST } from '@adobe/css-tools';
|
||||
|
||||
const ast: CssStylesheetAST = parse('body { color: red; }');
|
||||
|
||||
// Access top-level rules
|
||||
ast.stylesheet.rules.forEach(rule => {
|
||||
if (rule.type === 'rule') {
|
||||
console.log('Selectors:', rule.selectors);
|
||||
rule.declarations.forEach(decl => {
|
||||
if (decl.type === 'declaration') {
|
||||
console.log(`${decl.property}: ${decl.value}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Modifying Nodes
|
||||
|
||||
```typescript
|
||||
// Add a new declaration
|
||||
const newDecl = {
|
||||
type: 'declaration' as const,
|
||||
property: 'font-size',
|
||||
value: '16px'
|
||||
};
|
||||
|
||||
// Find a rule and add the declaration
|
||||
ast.stylesheet.rules.forEach(rule => {
|
||||
if (rule.type === 'rule' && rule.selectors.includes('body')) {
|
||||
rule.declarations.push(newDecl);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Error Handling
|
||||
|
||||
When parsing with the `silent` option, errors are collected in the AST:
|
||||
|
||||
```typescript
|
||||
const ast = parse('invalid css {', { silent: true });
|
||||
|
||||
if (ast.stylesheet.parsingErrors) {
|
||||
ast.stylesheet.parsingErrors.forEach(error => {
|
||||
console.error('Parse error:', error.message);
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
## Position Information
|
||||
|
||||
Position information is available on most nodes and includes:
|
||||
|
||||
- `start.line` and `start.column`: Beginning of the node
|
||||
- `end.line` and `end.column`: End of the node
|
||||
- `source`: Source file path (if provided during parsing)
|
||||
|
||||
This is useful for:
|
||||
- Error reporting
|
||||
- Source mapping
|
||||
- Code analysis tools
|
||||
- IDE integration
|
||||
+191
@@ -0,0 +1,191 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [4.4.4] - 2025-07-22
|
||||
|
||||
### Changed
|
||||
- Switch from yarn to npm for package management
|
||||
- Switch from eslint to biome for code formatting and linting
|
||||
- Reformat codebase to comply with biome recommendations
|
||||
- Switch from webpack to rollup for bundling
|
||||
|
||||
### Fixed
|
||||
- Fix module exports to ensure proper compatibility with bundlers
|
||||
- Add validation check to prevent future export issues
|
||||
|
||||
## [4.4.3] - 2025-05-15
|
||||
|
||||
### Security
|
||||
- Fix polynomial regular expression vulnerability on uncontrolled data
|
||||
- Refactor code to enable GitHub security static analysis
|
||||
|
||||
### Performance
|
||||
- Improve parsing performance with minor optimizations
|
||||
- Replace regex patterns with string search (indexOf-based) for better performance
|
||||
|
||||
### Added
|
||||
- Add new utility functions with comprehensive unit tests
|
||||
- Add improved formatting for CSS Grid template areas (#283 by @jogibear9988)
|
||||
|
||||
### Fixed
|
||||
- Fix TypeScript error with ConstructorParameters in Parcel bundler (#444)
|
||||
|
||||
## [4.4.2] - 2025-02-12
|
||||
|
||||
### Fixed
|
||||
- Fix regular expression for parsing quoted values in parentheses
|
||||
|
||||
## [4.4.0] - 2024-06-05
|
||||
|
||||
### Added
|
||||
- Add support for CSS `@starting-style` at-rule (#319)
|
||||
|
||||
## [4.3.3] - 2024-01-24
|
||||
|
||||
### Changed
|
||||
- Update package export configuration (#271)
|
||||
|
||||
## [4.3.2] - 2023-11-28
|
||||
|
||||
### Security
|
||||
- Fix ReDoS vulnerability with crafted CSS strings - CVE-2023-48631
|
||||
|
||||
### Fixed
|
||||
- Fix parsing issues with `:is()` and nested `:nth-child()` selectors (#211)
|
||||
|
||||
## [4.3.1] - 2023-03-14
|
||||
|
||||
### Security
|
||||
- Fix ReDoS vulnerability with crafted CSS strings - CVE-2023-26364
|
||||
|
||||
## [4.3.0] - 2023-03-07
|
||||
|
||||
### Changed
|
||||
- Update build toolchain and dependencies
|
||||
- Update package exports configuration and file structure
|
||||
|
||||
## [4.2.0] - 2023-02-21
|
||||
|
||||
### Added
|
||||
- Add support for CSS `@container` at-rule
|
||||
- Add support for CSS `@layer` at-rule
|
||||
|
||||
## [4.1.0] - 2023-01-25
|
||||
|
||||
### Added
|
||||
- Add support for ES Modules (ESM)
|
||||
|
||||
## [4.0.2] - 2023-01-12
|
||||
|
||||
### Fixed
|
||||
- Fix `@import` parsing when URL contains semicolons (#71)
|
||||
- Fix regression in selector parsing for attribute selectors (#77)
|
||||
|
||||
## [4.0.1] - 2022-08-03
|
||||
|
||||
### Fixed
|
||||
- Fix `globalThis` configuration for webpack to enable UMD module usage in Node.js environments
|
||||
|
||||
## [4.0.0] - 2022-06-09
|
||||
|
||||
### Changed
|
||||
- Fork from original css library to Adobe's `@adobe/css-tools` package
|
||||
- Convert codebase from JavaScript to TypeScript
|
||||
- Improve parsing performance by approximately 25%
|
||||
- Update all dependencies to latest versions
|
||||
- Remove source map support
|
||||
|
||||
---
|
||||
|
||||
## Legacy Versions (Pre-Adobe Fork)
|
||||
|
||||
## [3.0.0] - 2020-07-01
|
||||
|
||||
### Changed
|
||||
- Bump major version due to major dependency updates and Node.js version requirement changes
|
||||
|
||||
## [2.2.1] - 2015-06-17
|
||||
|
||||
### Fixed
|
||||
- Fix parsing of escaped quotes in quoted strings
|
||||
|
||||
## [2.2.0] - 2015-02-18
|
||||
|
||||
### Added
|
||||
- Add `parsingErrors` property to list errors when parsing with `silent: true`
|
||||
- Accept EOL characters and all whitespace characters in at-rules such as `@media`
|
||||
|
||||
## [2.1.0] - 2014-08-05
|
||||
|
||||
### Added
|
||||
- Add `inputSourcemaps` option to disable input source map processing
|
||||
- Add `sourcemap: 'generator'` option to return the `SourceMapGenerator` object
|
||||
- Use `inherits` package for inheritance (fixes browser compatibility issues)
|
||||
|
||||
### Changed
|
||||
- Change error message format and add `.reason` property to error objects
|
||||
|
||||
## [2.0.0] - 2014-06-18
|
||||
|
||||
### Added
|
||||
- Add non-enumerable parent reference to each AST node
|
||||
- Add support for `@custom-media`, `@host`, and `@font-face` at-rules
|
||||
- Allow commas inside selector functions
|
||||
- Allow empty property values
|
||||
- Add `node.position.content` property
|
||||
- Integrate css-parse and css-stringify libraries
|
||||
- Apply original source maps from source files
|
||||
|
||||
### Changed
|
||||
- Change default `options.position` value to `true`
|
||||
- Remove comments from properties and values
|
||||
|
||||
### Removed
|
||||
- Drop Component(1) support
|
||||
|
||||
### Fixed
|
||||
- Fix assertion errors when selectors are missing
|
||||
|
||||
## [1.6.1] - 2014-01-02
|
||||
|
||||
### Fixed
|
||||
- Fix component.json configuration
|
||||
|
||||
## [1.6.0] - 2013-12-21
|
||||
|
||||
### Changed
|
||||
- Update dependencies
|
||||
|
||||
## [1.5.0] - 2013-12-03
|
||||
|
||||
### Changed
|
||||
- Update dependencies
|
||||
|
||||
## [1.1.0] - 2013-04-04
|
||||
|
||||
### Changed
|
||||
- Update dependencies
|
||||
|
||||
## [1.0.7] - 2012-11-21
|
||||
|
||||
### Fixed
|
||||
- Fix component.json configuration
|
||||
|
||||
## [1.0.4] - 2012-11-15
|
||||
|
||||
### Changed
|
||||
- Update css-stringify dependency
|
||||
|
||||
## [1.0.3] - 2012-09-01
|
||||
|
||||
### Added
|
||||
- Add Component support
|
||||
|
||||
## [0.0.1] - 2010-01-03
|
||||
|
||||
### Added
|
||||
- Initial release
|
||||
+450
@@ -0,0 +1,450 @@
|
||||
# Usage Examples
|
||||
|
||||
## Basic Usage
|
||||
|
||||
### Parsing CSS
|
||||
|
||||
```javascript
|
||||
import { parse } from '@adobe/css-tools';
|
||||
|
||||
// Basic CSS parsing
|
||||
const css = `
|
||||
body {
|
||||
font-size: 12px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
`;
|
||||
|
||||
const ast = parse(css);
|
||||
console.log(ast);
|
||||
```
|
||||
|
||||
### Stringifying AST
|
||||
|
||||
```javascript
|
||||
import { parse, stringify } from '@adobe/css-tools';
|
||||
|
||||
const css = 'body { font-size: 12px; color: #333; }';
|
||||
const ast = parse(css);
|
||||
|
||||
// Convert back to CSS
|
||||
const output = stringify(ast);
|
||||
console.log(output); // "body { font-size: 12px; color: #333; }"
|
||||
```
|
||||
|
||||
## Advanced Parsing Options
|
||||
|
||||
### Source Tracking
|
||||
|
||||
```javascript
|
||||
import { parse } from '@adobe/css-tools';
|
||||
|
||||
const css = 'body { color: red; }';
|
||||
const ast = parse(css, { source: 'styles.css' });
|
||||
|
||||
// Position information is available
|
||||
const rule = ast.stylesheet.rules[0];
|
||||
console.log(rule.position?.source); // "styles.css"
|
||||
console.log(rule.position?.start); // { line: 1, column: 1 }
|
||||
console.log(rule.position?.end); // { line: 1, column: 20 }
|
||||
```
|
||||
|
||||
### Silent Error Handling
|
||||
|
||||
```javascript
|
||||
import { parse } from '@adobe/css-tools';
|
||||
|
||||
const malformedCss = `
|
||||
body { color: red; }
|
||||
{ color: blue; } /* Missing selector */
|
||||
.valid { background: green; }
|
||||
`;
|
||||
|
||||
// Parse with silent error handling
|
||||
const result = parse(malformedCss, { silent: true });
|
||||
|
||||
// Check for parsing errors
|
||||
if (result.stylesheet.parsingErrors) {
|
||||
console.log('Parsing errors:', result.stylesheet.parsingErrors.length);
|
||||
result.stylesheet.parsingErrors.forEach(error => {
|
||||
console.log(`Error at line ${error.line}: ${error.message}`);
|
||||
});
|
||||
}
|
||||
|
||||
// Valid rules are still parsed
|
||||
console.log('Valid rules:', result.stylesheet.rules.length);
|
||||
```
|
||||
|
||||
## AST Structure Examples
|
||||
|
||||
### Basic Rule
|
||||
|
||||
```javascript
|
||||
import { parse } from '@adobe/css-tools';
|
||||
|
||||
const css = `
|
||||
.header {
|
||||
background: #f0f0f0;
|
||||
padding: 20px;
|
||||
border-bottom: 1px solid #ccc;
|
||||
}
|
||||
`;
|
||||
|
||||
const ast = parse(css);
|
||||
const rule = ast.stylesheet.rules[0];
|
||||
|
||||
console.log(rule.type); // "rule"
|
||||
console.log(rule.selectors); // [".header"]
|
||||
console.log(rule.declarations.length); // 3
|
||||
|
||||
rule.declarations.forEach(decl => {
|
||||
console.log(`${decl.property}: ${decl.value}`);
|
||||
});
|
||||
// Output:
|
||||
// background: #f0f0f0
|
||||
// padding: 20px
|
||||
// border-bottom: 1px solid #ccc
|
||||
```
|
||||
|
||||
### Media Queries
|
||||
|
||||
```javascript
|
||||
import { parse } from '@adobe/css-tools';
|
||||
|
||||
const css = `
|
||||
@media screen and (max-width: 768px) {
|
||||
.container {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const ast = parse(css);
|
||||
const mediaRule = ast.stylesheet.rules[0];
|
||||
|
||||
console.log(mediaRule.type); // "media"
|
||||
console.log(mediaRule.media); // "screen and (max-width: 768px)"
|
||||
console.log(mediaRule.rules.length); // 2
|
||||
```
|
||||
|
||||
### Keyframes
|
||||
|
||||
```javascript
|
||||
import { parse } from '@adobe/css-tools';
|
||||
|
||||
const css = `
|
||||
@keyframes fadeIn {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const ast = parse(css);
|
||||
const keyframesRule = ast.stylesheet.rules[0];
|
||||
|
||||
console.log(keyframesRule.type); // "keyframes"
|
||||
console.log(keyframesRule.name); // "fadeIn"
|
||||
console.log(keyframesRule.keyframes.length); // 2
|
||||
|
||||
keyframesRule.keyframes.forEach(keyframe => {
|
||||
console.log(`Keyframe: ${keyframe.values.join(', ')}`);
|
||||
keyframe.declarations.forEach(decl => {
|
||||
console.log(` ${decl.property}: ${decl.value}`);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### Comments
|
||||
|
||||
```javascript
|
||||
import { parse } from '@adobe/css-tools';
|
||||
|
||||
const css = `
|
||||
/* Header styles */
|
||||
.header {
|
||||
background: red; /* Fallback color */
|
||||
}
|
||||
|
||||
/* Footer styles */
|
||||
.footer {
|
||||
background: blue;
|
||||
}
|
||||
`;
|
||||
|
||||
const ast = parse(css);
|
||||
|
||||
ast.stylesheet.rules.forEach(rule => {
|
||||
if (rule.type === 'comment') {
|
||||
console.log(`Comment: ${rule.comment}`);
|
||||
} else if (rule.type === 'rule') {
|
||||
console.log(`Rule: ${rule.selectors.join(', ')}`);
|
||||
|
||||
rule.declarations.forEach(decl => {
|
||||
if (decl.type === 'comment') {
|
||||
console.log(` Comment: ${decl.comment}`);
|
||||
} else {
|
||||
console.log(` ${decl.property}: ${decl.value}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Stringifying Options
|
||||
|
||||
### Compressed Output
|
||||
|
||||
```javascript
|
||||
import { parse, stringify } from '@adobe/css-tools';
|
||||
|
||||
const css = `
|
||||
body {
|
||||
font-size: 12px;
|
||||
color: #333;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
`;
|
||||
|
||||
const ast = parse(css);
|
||||
|
||||
// Compressed output
|
||||
const compressed = stringify(ast, { compress: true });
|
||||
console.log(compressed);
|
||||
// Output: "body{font-size:12px;color:#333;margin:0;padding:0}"
|
||||
```
|
||||
|
||||
### Custom Indentation
|
||||
|
||||
```javascript
|
||||
import { parse, stringify } from '@adobe/css-tools';
|
||||
|
||||
const css = 'body { font-size: 12px; color: #333; }';
|
||||
const ast = parse(css);
|
||||
|
||||
// Custom indentation
|
||||
const formatted = stringify(ast, { indent: ' ' });
|
||||
console.log(formatted);
|
||||
// Output:
|
||||
// body {
|
||||
// font-size: 12px;
|
||||
// color: #333;
|
||||
// }
|
||||
```
|
||||
|
||||
## Working with Complex CSS
|
||||
|
||||
### Nested Rules and At-Rules
|
||||
|
||||
```javascript
|
||||
import { parse, stringify } from '@adobe/css-tools';
|
||||
|
||||
const complexCss = `
|
||||
@import url('https://fonts.googleapis.com/css2?family=Roboto');
|
||||
|
||||
@charset "UTF-8";
|
||||
|
||||
@media print {
|
||||
body {
|
||||
font-size: 12pt;
|
||||
}
|
||||
}
|
||||
|
||||
@supports (display: grid) {
|
||||
.grid {
|
||||
display: grid;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes slideIn {
|
||||
0% { transform: translateX(-100%); }
|
||||
100% { transform: translateX(0); }
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'CustomFont';
|
||||
src: url('custom-font.woff2') format('woff2');
|
||||
}
|
||||
`;
|
||||
|
||||
const ast = parse(complexCss);
|
||||
|
||||
ast.stylesheet.rules.forEach(rule => {
|
||||
switch (rule.type) {
|
||||
case 'import':
|
||||
console.log(`Import: ${rule.import}`);
|
||||
break;
|
||||
case 'charset':
|
||||
console.log(`Charset: ${rule.charset}`);
|
||||
break;
|
||||
case 'media':
|
||||
console.log(`Media query: ${rule.media}`);
|
||||
break;
|
||||
case 'supports':
|
||||
console.log(`Supports: ${rule.supports}`);
|
||||
break;
|
||||
case 'keyframes':
|
||||
console.log(`Keyframes: ${rule.name}`);
|
||||
break;
|
||||
case 'font-face':
|
||||
console.log('Font-face rule');
|
||||
break;
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### Manipulating the AST
|
||||
|
||||
```javascript
|
||||
import { parse, stringify } from '@adobe/css-tools';
|
||||
|
||||
const css = `
|
||||
.button {
|
||||
background: blue;
|
||||
color: white;
|
||||
padding: 10px;
|
||||
}
|
||||
`;
|
||||
|
||||
const ast = parse(css);
|
||||
const rule = ast.stylesheet.rules[0];
|
||||
|
||||
// Add a new declaration
|
||||
rule.declarations.push({
|
||||
type: 'declaration',
|
||||
property: 'border-radius',
|
||||
value: '5px'
|
||||
});
|
||||
|
||||
// Modify existing declaration
|
||||
const backgroundDecl = rule.declarations.find(d => d.property === 'background');
|
||||
if (backgroundDecl) {
|
||||
backgroundDecl.value = 'red';
|
||||
}
|
||||
|
||||
// Add a new selector
|
||||
rule.selectors.push('.btn');
|
||||
|
||||
const modifiedCss = stringify(ast);
|
||||
console.log(modifiedCss);
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Catching Parse Errors
|
||||
|
||||
```javascript
|
||||
import { parse, CssParseError } from '@adobe/css-tools';
|
||||
|
||||
try {
|
||||
const ast = parse('body { color: red; } { invalid }');
|
||||
} catch (error) {
|
||||
if (error instanceof CssParseError) {
|
||||
console.log(`Parse error at line ${error.line}, column ${error.column}:`);
|
||||
console.log(error.message);
|
||||
console.log(`Source: ${error.filename}`);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Working with Silent Errors
|
||||
|
||||
```javascript
|
||||
import { parse } from '@adobe/css-tools';
|
||||
|
||||
const problematicCss = `
|
||||
body { color: red; }
|
||||
{ color: blue; } /* Missing selector */
|
||||
.valid { background: green; }
|
||||
.another { border: 1px solid; } /* Missing closing brace */
|
||||
`;
|
||||
|
||||
const result = parse(problematicCss, {
|
||||
silent: true,
|
||||
source: 'problematic.css'
|
||||
});
|
||||
|
||||
// Process valid rules
|
||||
const validRules = result.stylesheet.rules.filter(rule => rule.type === 'rule');
|
||||
console.log(`Found ${validRules.length} valid rules`);
|
||||
|
||||
// Log errors for debugging
|
||||
if (result.stylesheet.parsingErrors) {
|
||||
result.stylesheet.parsingErrors.forEach(error => {
|
||||
console.log(`Error: ${error.message} at line ${error.line}`);
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### CSS Minification
|
||||
|
||||
```javascript
|
||||
import { parse, stringify } from '@adobe/css-tools';
|
||||
|
||||
function minifyCSS(css) {
|
||||
const ast = parse(css);
|
||||
return stringify(ast, { compress: true });
|
||||
}
|
||||
|
||||
const css = `
|
||||
body {
|
||||
font-size: 12px;
|
||||
color: #333;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
`;
|
||||
|
||||
const minified = minifyCSS(css);
|
||||
console.log(minified);
|
||||
// Output: "body{font-size:12px;color:#333;margin:0;padding:0}.container{max-width:1200px;margin:0 auto}"
|
||||
```
|
||||
|
||||
### CSS Validation
|
||||
|
||||
```javascript
|
||||
import { parse } from '@adobe/css-tools';
|
||||
|
||||
function validateCSS(css, filename = 'unknown') {
|
||||
try {
|
||||
const ast = parse(css, { source: filename });
|
||||
return {
|
||||
valid: true,
|
||||
rules: ast.stylesheet.rules.length,
|
||||
errors: []
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
valid: false,
|
||||
rules: 0,
|
||||
errors: [{
|
||||
message: error.message,
|
||||
line: error.line,
|
||||
column: error.column,
|
||||
source: error.filename
|
||||
}]
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
const result = validateCSS('body { color: red; } { invalid }', 'test.css');
|
||||
console.log(result);
|
||||
```
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
{
|
||||
"name": "@adobe/css-tools",
|
||||
"version": "4.4.4",
|
||||
"description": "A modern CSS parser and stringifier with TypeScript support",
|
||||
"source": "src/index.ts",
|
||||
"main": "./dist/cjs/adobe-css-tools.cjs",
|
||||
"module": "./dist/esm/adobe-css-tools.mjs",
|
||||
"browser": "./dist/umd/adobe-css-tools.js",
|
||||
"sideEffects": false,
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./dist/esm/adobe-css-tools.mjs",
|
||||
"require": "./dist/cjs/adobe-css-tools.cjs"
|
||||
},
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"types": "./dist/esm/adobe-css-tools.d.mts",
|
||||
"type": "module",
|
||||
"files": [
|
||||
"dist/cjs",
|
||||
"dist/esm",
|
||||
"dist/umd",
|
||||
"README.md",
|
||||
"docs/"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@arethetypeswrong/cli": "^0.18.2",
|
||||
"@biomejs/biome": "^2.1.2",
|
||||
"@rollup/plugin-commonjs": "^28.0.6",
|
||||
"@rollup/plugin-node-resolve": "^16.0.1",
|
||||
"@rollup/plugin-terser": "^0.4.4",
|
||||
"@rollup/plugin-typescript": "^12.1.4",
|
||||
"@types/benchmark": "^2.1.1",
|
||||
"@types/bytes": "^3.1.5",
|
||||
"@types/jest": "^29.5.14",
|
||||
"@types/node": "^24.0.3",
|
||||
"benchmark": "^2.1.4",
|
||||
"bytes": "^3.1.0",
|
||||
"jest": "^29.7.0",
|
||||
"rollup": "^4.45.1",
|
||||
"rollup-plugin-dts": "^6.2.1",
|
||||
"ts-jest": "^29.1.1",
|
||||
"tslib": "^2.8.1",
|
||||
"typescript": "^5.7.3"
|
||||
},
|
||||
"scripts": {
|
||||
"benchmark": "npm run build && node benchmark/index.mjs",
|
||||
"test": "jest",
|
||||
"clean": "rm -rf ./dist",
|
||||
"build": "rollup -c",
|
||||
"fix": "biome check --write",
|
||||
"check-package-bundle": "attw --pack .",
|
||||
"lint": "biome check",
|
||||
"prebuild": "npm run clean",
|
||||
"prepack": "rollup -c",
|
||||
"posttest": "npm run lint",
|
||||
"postbuild": "tar -czf dist/archive.tgz LICENSE README.md dist/cjs dist/esm dist/umd docs package.json && npx attw --format ascii dist/archive.tgz && rm dist/archive.tgz"
|
||||
},
|
||||
"author": "TJ Holowaychuk <tj@vision-media.ca>",
|
||||
"contributors": [
|
||||
"Jean-Philippe Zolesio <holblin@gmail.com>"
|
||||
],
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/adobe/css-tools.git"
|
||||
},
|
||||
"keywords": [
|
||||
"css",
|
||||
"parser",
|
||||
"stringifier",
|
||||
"stylesheet",
|
||||
"ast",
|
||||
"typescript",
|
||||
"css-parser",
|
||||
"css-ast",
|
||||
"css-tools",
|
||||
"minify",
|
||||
"format",
|
||||
"preprocessor"
|
||||
]
|
||||
}
|
||||
+128
@@ -0,0 +1,128 @@
|
||||
declare namespace QuickLRU {
|
||||
interface Options<KeyType, ValueType> {
|
||||
/**
|
||||
The maximum number of milliseconds an item should remain in the cache.
|
||||
|
||||
@default Infinity
|
||||
|
||||
By default, `maxAge` will be `Infinity`, which means that items will never expire.
|
||||
Lazy expiration upon the next write or read call.
|
||||
|
||||
Individual expiration of an item can be specified by the `set(key, value, maxAge)` method.
|
||||
*/
|
||||
readonly maxAge?: number;
|
||||
|
||||
/**
|
||||
The maximum number of items before evicting the least recently used items.
|
||||
*/
|
||||
readonly maxSize: number;
|
||||
|
||||
/**
|
||||
Called right before an item is evicted from the cache.
|
||||
|
||||
Useful for side effects or for items like object URLs that need explicit cleanup (`revokeObjectURL`).
|
||||
*/
|
||||
onEviction?: (key: KeyType, value: ValueType) => void;
|
||||
}
|
||||
}
|
||||
|
||||
declare class QuickLRU<KeyType, ValueType>
|
||||
implements Iterable<[KeyType, ValueType]> {
|
||||
/**
|
||||
The stored item count.
|
||||
*/
|
||||
readonly size: number;
|
||||
|
||||
/**
|
||||
Simple ["Least Recently Used" (LRU) cache](https://en.m.wikipedia.org/wiki/Cache_replacement_policies#Least_Recently_Used_.28LRU.29).
|
||||
|
||||
The instance is [`iterable`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Iteration_protocols) so you can use it directly in a [`for…of`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Statements/for...of) loop.
|
||||
|
||||
@example
|
||||
```
|
||||
import QuickLRU = require('quick-lru');
|
||||
|
||||
const lru = new QuickLRU({maxSize: 1000});
|
||||
|
||||
lru.set('🦄', '🌈');
|
||||
|
||||
lru.has('🦄');
|
||||
//=> true
|
||||
|
||||
lru.get('🦄');
|
||||
//=> '🌈'
|
||||
```
|
||||
*/
|
||||
constructor(options: QuickLRU.Options<KeyType, ValueType>);
|
||||
|
||||
[Symbol.iterator](): IterableIterator<[KeyType, ValueType]>;
|
||||
|
||||
/**
|
||||
Set an item. Returns the instance.
|
||||
|
||||
Individual expiration of an item can be specified with the `maxAge` option. If not specified, the global `maxAge` value will be used in case it is specified in the constructor, otherwise the item will never expire.
|
||||
|
||||
@returns The list instance.
|
||||
*/
|
||||
set(key: KeyType, value: ValueType, options?: {maxAge?: number}): this;
|
||||
|
||||
/**
|
||||
Get an item.
|
||||
|
||||
@returns The stored item or `undefined`.
|
||||
*/
|
||||
get(key: KeyType): ValueType | undefined;
|
||||
|
||||
/**
|
||||
Check if an item exists.
|
||||
*/
|
||||
has(key: KeyType): boolean;
|
||||
|
||||
/**
|
||||
Get an item without marking it as recently used.
|
||||
|
||||
@returns The stored item or `undefined`.
|
||||
*/
|
||||
peek(key: KeyType): ValueType | undefined;
|
||||
|
||||
/**
|
||||
Delete an item.
|
||||
|
||||
@returns `true` if the item is removed or `false` if the item doesn't exist.
|
||||
*/
|
||||
delete(key: KeyType): boolean;
|
||||
|
||||
/**
|
||||
Delete all items.
|
||||
*/
|
||||
clear(): void;
|
||||
|
||||
/**
|
||||
Update the `maxSize` in-place, discarding items as necessary. Insertion order is mostly preserved, though this is not a strong guarantee.
|
||||
|
||||
Useful for on-the-fly tuning of cache sizes in live systems.
|
||||
*/
|
||||
resize(maxSize: number): void;
|
||||
|
||||
/**
|
||||
Iterable for all the keys.
|
||||
*/
|
||||
keys(): IterableIterator<KeyType>;
|
||||
|
||||
/**
|
||||
Iterable for all the values.
|
||||
*/
|
||||
values(): IterableIterator<ValueType>;
|
||||
|
||||
/**
|
||||
Iterable for all entries, starting with the oldest (ascending in recency).
|
||||
*/
|
||||
entriesAscending(): IterableIterator<[KeyType, ValueType]>;
|
||||
|
||||
/**
|
||||
Iterable for all entries, starting with the newest (descending in recency).
|
||||
*/
|
||||
entriesDescending(): IterableIterator<[KeyType, ValueType]>;
|
||||
}
|
||||
|
||||
export = QuickLRU;
|
||||
+263
@@ -0,0 +1,263 @@
|
||||
'use strict';
|
||||
|
||||
class QuickLRU {
|
||||
constructor(options = {}) {
|
||||
if (!(options.maxSize && options.maxSize > 0)) {
|
||||
throw new TypeError('`maxSize` must be a number greater than 0');
|
||||
}
|
||||
|
||||
if (typeof options.maxAge === 'number' && options.maxAge === 0) {
|
||||
throw new TypeError('`maxAge` must be a number greater than 0');
|
||||
}
|
||||
|
||||
this.maxSize = options.maxSize;
|
||||
this.maxAge = options.maxAge || Infinity;
|
||||
this.onEviction = options.onEviction;
|
||||
this.cache = new Map();
|
||||
this.oldCache = new Map();
|
||||
this._size = 0;
|
||||
}
|
||||
|
||||
_emitEvictions(cache) {
|
||||
if (typeof this.onEviction !== 'function') {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const [key, item] of cache) {
|
||||
this.onEviction(key, item.value);
|
||||
}
|
||||
}
|
||||
|
||||
_deleteIfExpired(key, item) {
|
||||
if (typeof item.expiry === 'number' && item.expiry <= Date.now()) {
|
||||
if (typeof this.onEviction === 'function') {
|
||||
this.onEviction(key, item.value);
|
||||
}
|
||||
|
||||
return this.delete(key);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
_getOrDeleteIfExpired(key, item) {
|
||||
const deleted = this._deleteIfExpired(key, item);
|
||||
if (deleted === false) {
|
||||
return item.value;
|
||||
}
|
||||
}
|
||||
|
||||
_getItemValue(key, item) {
|
||||
return item.expiry ? this._getOrDeleteIfExpired(key, item) : item.value;
|
||||
}
|
||||
|
||||
_peek(key, cache) {
|
||||
const item = cache.get(key);
|
||||
|
||||
return this._getItemValue(key, item);
|
||||
}
|
||||
|
||||
_set(key, value) {
|
||||
this.cache.set(key, value);
|
||||
this._size++;
|
||||
|
||||
if (this._size >= this.maxSize) {
|
||||
this._size = 0;
|
||||
this._emitEvictions(this.oldCache);
|
||||
this.oldCache = this.cache;
|
||||
this.cache = new Map();
|
||||
}
|
||||
}
|
||||
|
||||
_moveToRecent(key, item) {
|
||||
this.oldCache.delete(key);
|
||||
this._set(key, item);
|
||||
}
|
||||
|
||||
* _entriesAscending() {
|
||||
for (const item of this.oldCache) {
|
||||
const [key, value] = item;
|
||||
if (!this.cache.has(key)) {
|
||||
const deleted = this._deleteIfExpired(key, value);
|
||||
if (deleted === false) {
|
||||
yield item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const item of this.cache) {
|
||||
const [key, value] = item;
|
||||
const deleted = this._deleteIfExpired(key, value);
|
||||
if (deleted === false) {
|
||||
yield item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get(key) {
|
||||
if (this.cache.has(key)) {
|
||||
const item = this.cache.get(key);
|
||||
|
||||
return this._getItemValue(key, item);
|
||||
}
|
||||
|
||||
if (this.oldCache.has(key)) {
|
||||
const item = this.oldCache.get(key);
|
||||
if (this._deleteIfExpired(key, item) === false) {
|
||||
this._moveToRecent(key, item);
|
||||
return item.value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
set(key, value, {maxAge = this.maxAge === Infinity ? undefined : Date.now() + this.maxAge} = {}) {
|
||||
if (this.cache.has(key)) {
|
||||
this.cache.set(key, {
|
||||
value,
|
||||
maxAge
|
||||
});
|
||||
} else {
|
||||
this._set(key, {value, expiry: maxAge});
|
||||
}
|
||||
}
|
||||
|
||||
has(key) {
|
||||
if (this.cache.has(key)) {
|
||||
return !this._deleteIfExpired(key, this.cache.get(key));
|
||||
}
|
||||
|
||||
if (this.oldCache.has(key)) {
|
||||
return !this._deleteIfExpired(key, this.oldCache.get(key));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
peek(key) {
|
||||
if (this.cache.has(key)) {
|
||||
return this._peek(key, this.cache);
|
||||
}
|
||||
|
||||
if (this.oldCache.has(key)) {
|
||||
return this._peek(key, this.oldCache);
|
||||
}
|
||||
}
|
||||
|
||||
delete(key) {
|
||||
const deleted = this.cache.delete(key);
|
||||
if (deleted) {
|
||||
this._size--;
|
||||
}
|
||||
|
||||
return this.oldCache.delete(key) || deleted;
|
||||
}
|
||||
|
||||
clear() {
|
||||
this.cache.clear();
|
||||
this.oldCache.clear();
|
||||
this._size = 0;
|
||||
}
|
||||
|
||||
resize(newSize) {
|
||||
if (!(newSize && newSize > 0)) {
|
||||
throw new TypeError('`maxSize` must be a number greater than 0');
|
||||
}
|
||||
|
||||
const items = [...this._entriesAscending()];
|
||||
const removeCount = items.length - newSize;
|
||||
if (removeCount < 0) {
|
||||
this.cache = new Map(items);
|
||||
this.oldCache = new Map();
|
||||
this._size = items.length;
|
||||
} else {
|
||||
if (removeCount > 0) {
|
||||
this._emitEvictions(items.slice(0, removeCount));
|
||||
}
|
||||
|
||||
this.oldCache = new Map(items.slice(removeCount));
|
||||
this.cache = new Map();
|
||||
this._size = 0;
|
||||
}
|
||||
|
||||
this.maxSize = newSize;
|
||||
}
|
||||
|
||||
* keys() {
|
||||
for (const [key] of this) {
|
||||
yield key;
|
||||
}
|
||||
}
|
||||
|
||||
* values() {
|
||||
for (const [, value] of this) {
|
||||
yield value;
|
||||
}
|
||||
}
|
||||
|
||||
* [Symbol.iterator]() {
|
||||
for (const item of this.cache) {
|
||||
const [key, value] = item;
|
||||
const deleted = this._deleteIfExpired(key, value);
|
||||
if (deleted === false) {
|
||||
yield [key, value.value];
|
||||
}
|
||||
}
|
||||
|
||||
for (const item of this.oldCache) {
|
||||
const [key, value] = item;
|
||||
if (!this.cache.has(key)) {
|
||||
const deleted = this._deleteIfExpired(key, value);
|
||||
if (deleted === false) {
|
||||
yield [key, value.value];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
* entriesDescending() {
|
||||
let items = [...this.cache];
|
||||
for (let i = items.length - 1; i >= 0; --i) {
|
||||
const item = items[i];
|
||||
const [key, value] = item;
|
||||
const deleted = this._deleteIfExpired(key, value);
|
||||
if (deleted === false) {
|
||||
yield [key, value.value];
|
||||
}
|
||||
}
|
||||
|
||||
items = [...this.oldCache];
|
||||
for (let i = items.length - 1; i >= 0; --i) {
|
||||
const item = items[i];
|
||||
const [key, value] = item;
|
||||
if (!this.cache.has(key)) {
|
||||
const deleted = this._deleteIfExpired(key, value);
|
||||
if (deleted === false) {
|
||||
yield [key, value.value];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
* entriesAscending() {
|
||||
for (const [key, value] of this._entriesAscending()) {
|
||||
yield [key, value.value];
|
||||
}
|
||||
}
|
||||
|
||||
get size() {
|
||||
if (!this._size) {
|
||||
return this.oldCache.size;
|
||||
}
|
||||
|
||||
let oldCacheSize = 0;
|
||||
for (const key of this.oldCache.keys()) {
|
||||
if (!this.cache.has(key)) {
|
||||
oldCacheSize++;
|
||||
}
|
||||
}
|
||||
|
||||
return Math.min(this._size + oldCacheSize, this.maxSize);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = QuickLRU;
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) Sindre Sorhus <sindresorhus@gmail.com> (sindresorhus.com)
|
||||
|
||||
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.
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"name": "@alloc/quick-lru",
|
||||
"version": "5.2.0",
|
||||
"description": "Simple “Least Recently Used” (LRU) cache",
|
||||
"license": "MIT",
|
||||
"repository": "sindresorhus/quick-lru",
|
||||
"funding": "https://github.com/sponsors/sindresorhus",
|
||||
"author": {
|
||||
"name": "Sindre Sorhus",
|
||||
"email": "sindresorhus@gmail.com",
|
||||
"url": "https://sindresorhus.com"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "xo && nyc ava && tsd"
|
||||
},
|
||||
"files": [
|
||||
"index.js",
|
||||
"index.d.ts"
|
||||
],
|
||||
"keywords": [
|
||||
"lru",
|
||||
"quick",
|
||||
"cache",
|
||||
"caching",
|
||||
"least",
|
||||
"recently",
|
||||
"used",
|
||||
"fast",
|
||||
"map",
|
||||
"hash",
|
||||
"buffer"
|
||||
],
|
||||
"devDependencies": {
|
||||
"ava": "^2.0.0",
|
||||
"coveralls": "^3.0.3",
|
||||
"nyc": "^15.0.0",
|
||||
"tsd": "^0.11.0",
|
||||
"xo": "^0.26.0"
|
||||
}
|
||||
}
|
||||
+139
@@ -0,0 +1,139 @@
|
||||
# quick-lru [](https://travis-ci.org/sindresorhus/quick-lru) [](https://coveralls.io/github/sindresorhus/quick-lru?branch=master)
|
||||
|
||||
> Simple [“Least Recently Used” (LRU) cache](https://en.m.wikipedia.org/wiki/Cache_replacement_policies#Least_Recently_Used_.28LRU.29)
|
||||
|
||||
Useful when you need to cache something and limit memory usage.
|
||||
|
||||
Inspired by the [`hashlru` algorithm](https://github.com/dominictarr/hashlru#algorithm), but instead uses [`Map`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Map) to support keys of any type, not just strings, and values can be `undefined`.
|
||||
|
||||
## Install
|
||||
|
||||
```
|
||||
$ npm install quick-lru
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```js
|
||||
const QuickLRU = require('quick-lru');
|
||||
|
||||
const lru = new QuickLRU({maxSize: 1000});
|
||||
|
||||
lru.set('🦄', '🌈');
|
||||
|
||||
lru.has('🦄');
|
||||
//=> true
|
||||
|
||||
lru.get('🦄');
|
||||
//=> '🌈'
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### new QuickLRU(options?)
|
||||
|
||||
Returns a new instance.
|
||||
|
||||
### options
|
||||
|
||||
Type: `object`
|
||||
|
||||
#### maxSize
|
||||
|
||||
*Required*\
|
||||
Type: `number`
|
||||
|
||||
The maximum number of items before evicting the least recently used items.
|
||||
|
||||
#### maxAge
|
||||
|
||||
Type: `number`\
|
||||
Default: `Infinity`
|
||||
|
||||
The maximum number of milliseconds an item should remain in cache.
|
||||
By default maxAge will be Infinity, which means that items will never expire.
|
||||
|
||||
Lazy expiration happens upon the next `write` or `read` call.
|
||||
|
||||
Individual expiration of an item can be specified by the `set(key, value, options)` method.
|
||||
|
||||
#### onEviction
|
||||
|
||||
*Optional*\
|
||||
Type: `(key, value) => void`
|
||||
|
||||
Called right before an item is evicted from the cache.
|
||||
|
||||
Useful for side effects or for items like object URLs that need explicit cleanup (`revokeObjectURL`).
|
||||
|
||||
### Instance
|
||||
|
||||
The instance is [`iterable`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Iteration_protocols) so you can use it directly in a [`for…of`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Statements/for...of) loop.
|
||||
|
||||
Both `key` and `value` can be of any type.
|
||||
|
||||
#### .set(key, value, options?)
|
||||
|
||||
Set an item. Returns the instance.
|
||||
|
||||
Individual expiration of an item can be specified with the `maxAge` option. If not specified, the global `maxAge` value will be used in case it is specified on the constructor, otherwise the item will never expire.
|
||||
|
||||
#### .get(key)
|
||||
|
||||
Get an item.
|
||||
|
||||
#### .has(key)
|
||||
|
||||
Check if an item exists.
|
||||
|
||||
#### .peek(key)
|
||||
|
||||
Get an item without marking it as recently used.
|
||||
|
||||
#### .delete(key)
|
||||
|
||||
Delete an item.
|
||||
|
||||
Returns `true` if the item is removed or `false` if the item doesn't exist.
|
||||
|
||||
#### .clear()
|
||||
|
||||
Delete all items.
|
||||
|
||||
#### .resize(maxSize)
|
||||
|
||||
Update the `maxSize`, discarding items as necessary. Insertion order is mostly preserved, though this is not a strong guarantee.
|
||||
|
||||
Useful for on-the-fly tuning of cache sizes in live systems.
|
||||
|
||||
#### .keys()
|
||||
|
||||
Iterable for all the keys.
|
||||
|
||||
#### .values()
|
||||
|
||||
Iterable for all the values.
|
||||
|
||||
#### .entriesAscending()
|
||||
|
||||
Iterable for all entries, starting with the oldest (ascending in recency).
|
||||
|
||||
#### .entriesDescending()
|
||||
|
||||
Iterable for all entries, starting with the newest (descending in recency).
|
||||
|
||||
#### .size
|
||||
|
||||
The stored item count.
|
||||
|
||||
---
|
||||
|
||||
<div align="center">
|
||||
<b>
|
||||
<a href="https://tidelift.com/subscription/pkg/npm-quick-lru?utm_source=npm-quick-lru&utm_medium=referral&utm_campaign=readme">Get professional support for this package with a Tidelift subscription</a>
|
||||
</b>
|
||||
<br>
|
||||
<sub>
|
||||
Tidelift helps make open source sustainable for maintainers while giving companies<br>assurances about security, maintenance, and licensing for their dependencies.
|
||||
</sub>
|
||||
</div>
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2024 asamuzaK (Kazz)
|
||||
|
||||
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.
|
||||
+316
@@ -0,0 +1,316 @@
|
||||
# CSS color
|
||||
|
||||
[](https://github.com/asamuzaK/cssColor/actions/workflows/node.js.yml)
|
||||
[](https://github.com/asamuzaK/cssColor/actions/workflows/github-code-scanning/codeql)
|
||||
[](https://www.npmjs.com/package/@asamuzakjp/css-color)
|
||||
|
||||
Resolve and convert CSS colors.
|
||||
|
||||
## Install
|
||||
|
||||
```console
|
||||
npm i @asamuzakjp/css-color
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```javascript
|
||||
import { convert, resolve, utils } from '@asamuzakjp/css-color';
|
||||
|
||||
const resolvedValue = resolve(
|
||||
'color-mix(in oklab, lch(67.5345 42.5 258.2), color(srgb 0 0.5 0))'
|
||||
);
|
||||
// 'oklab(0.620754 -0.0931934 -0.00374881)'
|
||||
|
||||
const convertedValue = covert.colorToHex('lab(46.2775% -47.5621 48.5837)');
|
||||
// '#008000'
|
||||
|
||||
const result = utils.isColor('green');
|
||||
// true
|
||||
```
|
||||
|
||||
<!-- Generated by documentation.js. Update this documentation by updating the source code. -->
|
||||
|
||||
### resolve(color, opt)
|
||||
|
||||
resolves CSS color
|
||||
|
||||
#### Parameters
|
||||
|
||||
- `color` **[string][133]** color value
|
||||
- system colors are not supported
|
||||
- `opt` **[object][135]?** options (optional, default `{}`)
|
||||
- `opt.currentColor` **[string][133]?**
|
||||
- color to use for `currentcolor` keyword
|
||||
- if omitted, it will be treated as a missing color,
|
||||
i.e. `rgb(none none none / none)`
|
||||
- `opt.customProperty` **[object][135]?**
|
||||
- custom properties
|
||||
- pair of `--` prefixed property name as a key and it's value,
|
||||
e.g.
|
||||
```javascript
|
||||
const opt = {
|
||||
customProperty: {
|
||||
'--some-color': '#008000',
|
||||
'--some-length': '16px'
|
||||
}
|
||||
};
|
||||
```
|
||||
- and/or `callback` function to get the value of the custom property,
|
||||
e.g.
|
||||
```javascript
|
||||
const node = document.getElementById('foo');
|
||||
const opt = {
|
||||
customProperty: {
|
||||
callback: node.style.getPropertyValue
|
||||
}
|
||||
};
|
||||
```
|
||||
- `opt.dimension` **[object][135]?**
|
||||
- dimension, e.g. for converting relative length to pixels
|
||||
- pair of unit as a key and number in pixels as it's value,
|
||||
e.g. suppose `1em === 12px`, `1rem === 16px` and `100vw === 1024px`, then
|
||||
```javascript
|
||||
const opt = {
|
||||
dimension: {
|
||||
em: 12,
|
||||
rem: 16,
|
||||
vw: 10.24
|
||||
}
|
||||
};
|
||||
```
|
||||
- and/or `callback` function to get the value as a number in pixels,
|
||||
e.g.
|
||||
```javascript
|
||||
const opt = {
|
||||
dimension: {
|
||||
callback: unit => {
|
||||
switch (unit) {
|
||||
case 'em':
|
||||
return 12;
|
||||
case 'rem':
|
||||
return 16;
|
||||
case 'vw':
|
||||
return 10.24;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
- `opt.format` **[string][133]?**
|
||||
- output format, one of below
|
||||
- `computedValue` (default), [computed value][139] of the color
|
||||
- `specifiedValue`, [specified value][140] of the color
|
||||
- `hex`, hex color notation, i.e. `#rrggbb`
|
||||
- `hexAlpha`, hex color notation with alpha channel, i.e. `#rrggbbaa`
|
||||
|
||||
Returns **[string][133]?** one of `rgba?()`, `#rrggbb(aa)?`, `color-name`, `color(color-space r g b / alpha)`, `color(color-space x y z / alpha)`, `(ok)?lab(l a b / alpha)`, `(ok)?lch(l c h / alpha)`, `'(empty-string)'`, `null`
|
||||
|
||||
- in `computedValue`, values are numbers, however `rgb()` values are integers
|
||||
- in `specifiedValue`, returns `empty string` for unknown and/or invalid color
|
||||
- in `hex`, returns `null` for `transparent`, and also returns `null` if any of `r`, `g`, `b`, `alpha` is not a number
|
||||
- in `hexAlpha`, returns `#00000000` for `transparent`, however returns `null` if any of `r`, `g`, `b`, `alpha` is not a number
|
||||
|
||||
### convert
|
||||
|
||||
Contains various color conversion functions.
|
||||
|
||||
### convert.numberToHex(value)
|
||||
|
||||
convert number to hex string
|
||||
|
||||
#### Parameters
|
||||
|
||||
- `value` **[number][134]** color value
|
||||
|
||||
Returns **[string][133]** hex string: 00..ff
|
||||
|
||||
### convert.colorToHex(value, opt)
|
||||
|
||||
convert color to hex
|
||||
|
||||
#### Parameters
|
||||
|
||||
- `value` **[string][133]** color value
|
||||
- `opt` **[object][135]?** options (optional, default `{}`)
|
||||
- `opt.alpha` **[boolean][136]?** return in #rrggbbaa notation
|
||||
- `opt.customProperty` **[object][135]?**
|
||||
- custom properties, see `resolve()` function above
|
||||
- `opt.dimension` **[object][135]?**
|
||||
- dimension, see `resolve()` function above
|
||||
|
||||
Returns **[string][133]** #rrggbb(aa)?
|
||||
|
||||
### convert.colorToHsl(value, opt)
|
||||
|
||||
convert color to hsl
|
||||
|
||||
#### Parameters
|
||||
|
||||
- `value` **[string][133]** color value
|
||||
- `opt` **[object][135]?** options (optional, default `{}`)
|
||||
- `opt.customProperty` **[object][135]?**
|
||||
- custom properties, see `resolve()` function above
|
||||
- `opt.dimension` **[object][135]?**
|
||||
- dimension, see `resolve()` function above
|
||||
|
||||
Returns **[Array][137]<[number][134]>** \[h, s, l, alpha]
|
||||
|
||||
### convert.colorToHwb(value, opt)
|
||||
|
||||
convert color to hwb
|
||||
|
||||
#### Parameters
|
||||
|
||||
- `value` **[string][133]** color value
|
||||
- `opt` **[object][135]?** options (optional, default `{}`)
|
||||
- `opt.customProperty` **[object][135]?**
|
||||
- custom properties, see `resolve()` function above
|
||||
- `opt.dimension` **[object][135]?**
|
||||
- dimension, see `resolve()` function above
|
||||
|
||||
Returns **[Array][137]<[number][134]>** \[h, w, b, alpha]
|
||||
|
||||
### convert.colorToLab(value, opt)
|
||||
|
||||
convert color to lab
|
||||
|
||||
#### Parameters
|
||||
|
||||
- `value` **[string][133]** color value
|
||||
- `opt` **[object][135]?** options (optional, default `{}`)
|
||||
- `opt.customProperty` **[object][135]?**
|
||||
- custom properties, see `resolve()` function above
|
||||
- `opt.dimension` **[object][135]?**
|
||||
- dimension, see `resolve()` function above
|
||||
|
||||
Returns **[Array][137]<[number][134]>** \[l, a, b, alpha]
|
||||
|
||||
### convert.colorToLch(value, opt)
|
||||
|
||||
convert color to lch
|
||||
|
||||
#### Parameters
|
||||
|
||||
- `value` **[string][133]** color value
|
||||
- `opt` **[object][135]?** options (optional, default `{}`)
|
||||
- `opt.customProperty` **[object][135]?**
|
||||
- custom properties, see `resolve()` function above
|
||||
- `opt.dimension` **[object][135]?**
|
||||
- dimension, see `resolve()` function above
|
||||
|
||||
Returns **[Array][137]<[number][134]>** \[l, c, h, alpha]
|
||||
|
||||
### convert.colorToOklab(value, opt)
|
||||
|
||||
convert color to oklab
|
||||
|
||||
#### Parameters
|
||||
|
||||
- `value` **[string][133]** color value
|
||||
- `opt` **[object][135]?** options (optional, default `{}`)
|
||||
- `opt.customProperty` **[object][135]?**
|
||||
- custom properties, see `resolve()` function above
|
||||
- `opt.dimension` **[object][135]?**
|
||||
- dimension, see `resolve()` function above
|
||||
|
||||
Returns **[Array][137]<[number][134]>** \[l, a, b, alpha]
|
||||
|
||||
### convert.colorToOklch(value, opt)
|
||||
|
||||
convert color to oklch
|
||||
|
||||
#### Parameters
|
||||
|
||||
- `value` **[string][133]** color value
|
||||
- `opt` **[object][135]?** options (optional, default `{}`)
|
||||
- `opt.customProperty` **[object][135]?**
|
||||
- custom properties, see `resolve()` function above
|
||||
- `opt.dimension` **[object][135]?**
|
||||
- dimension, see `resolve()` function above
|
||||
|
||||
Returns **[Array][137]<[number][134]>** \[l, c, h, alpha]
|
||||
|
||||
### convert.colorToRgb(value, opt)
|
||||
|
||||
convert color to rgb
|
||||
|
||||
#### Parameters
|
||||
|
||||
- `value` **[string][133]** color value
|
||||
- `opt` **[object][135]?** options (optional, default `{}`)
|
||||
- `opt.customProperty` **[object][135]?**
|
||||
- custom properties, see `resolve()` function above
|
||||
- `opt.dimension` **[object][135]?**
|
||||
- dimension, see `resolve()` function above
|
||||
|
||||
Returns **[Array][137]<[number][134]>** \[r, g, b, alpha]
|
||||
|
||||
### convert.colorToXyz(value, opt)
|
||||
|
||||
convert color to xyz
|
||||
|
||||
#### Parameters
|
||||
|
||||
- `value` **[string][133]** color value
|
||||
- `opt` **[object][135]?** options (optional, default `{}`)
|
||||
- `opt.customProperty` **[object][135]?**
|
||||
- custom properties, see `resolve()` function above
|
||||
- `opt.dimension` **[object][135]?**
|
||||
- dimension, see `resolve()` function above
|
||||
- `opt.d50` **[boolean][136]?** xyz in d50 white point
|
||||
|
||||
Returns **[Array][137]<[number][134]>** \[x, y, z, alpha]
|
||||
|
||||
### convert.colorToXyzD50(value, opt)
|
||||
|
||||
convert color to xyz-d50
|
||||
|
||||
#### Parameters
|
||||
|
||||
- `value` **[string][133]** color value
|
||||
- `opt` **[object][135]?** options (optional, default `{}`)
|
||||
- `opt.customProperty` **[object][135]?**
|
||||
- custom properties, see `resolve()` function above
|
||||
- `opt.dimension` **[object][135]?**
|
||||
- dimension, see `resolve()` function above
|
||||
|
||||
Returns **[Array][137]<[number][134]>** \[x, y, z, alpha]
|
||||
|
||||
### utils
|
||||
|
||||
Contains utility functions.
|
||||
|
||||
### utils.isColor(color)
|
||||
|
||||
is valid color type
|
||||
|
||||
#### Parameters
|
||||
|
||||
- `color` **[string][133]** color value
|
||||
- system colors are not supported
|
||||
|
||||
Returns **[boolean][136]**
|
||||
|
||||
## Acknowledgments
|
||||
|
||||
The following resources have been of great help in the development of the CSS color.
|
||||
|
||||
- [csstools/postcss-plugins](https://github.com/csstools/postcss-plugins)
|
||||
- [lru-cache](https://github.com/isaacs/node-lru-cache)
|
||||
|
||||
---
|
||||
|
||||
Copyright (c) 2024 [asamuzaK (Kazz)](https://github.com/asamuzaK/)
|
||||
|
||||
[133]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String
|
||||
[134]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number
|
||||
[135]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object
|
||||
[136]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean
|
||||
[137]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array
|
||||
[138]: https://w3c.github.io/csswg-drafts/css-color-4/#color-conversion-code
|
||||
[139]: https://developer.mozilla.org/en-US/docs/Web/CSS/computed_value
|
||||
[140]: https://developer.mozilla.org/en-US/docs/Web/CSS/specified_value
|
||||
[141]: https://www.npmjs.com/package/@csstools/css-calc
|
||||
+220
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
+5484
File diff suppressed because it is too large
Load Diff
+1
File diff suppressed because one or more lines are too long
+109
@@ -0,0 +1,109 @@
|
||||
/**
|
||||
* typedef
|
||||
*/
|
||||
/**
|
||||
* @typedef Options - options
|
||||
* @property [alpha] - enable alpha
|
||||
* @property [colorSpace] - color space
|
||||
* @property [currentColor] - color for currentcolor
|
||||
* @property [customPropeerty] - custom properties
|
||||
* @property [d50] - white point in d50
|
||||
* @property [dimension] - dimension
|
||||
* @property [format] - output format
|
||||
* @property [key] - key
|
||||
*/
|
||||
interface Options {
|
||||
alpha?: boolean;
|
||||
colorSpace?: string;
|
||||
currentColor?: string;
|
||||
customProperty?: Record<string, string | ((K: string) => string)>;
|
||||
d50?: boolean;
|
||||
delimiter?: string | string[];
|
||||
dimension?: Record<string, number | ((K: string) => number)>;
|
||||
format?: string;
|
||||
nullable?: boolean;
|
||||
preserveComment?: boolean;
|
||||
}
|
||||
/**
|
||||
* @type ColorChannels - color channels
|
||||
*/
|
||||
type ColorChannels = [x: number, y: number, z: number, alpha: number];
|
||||
|
||||
/**
|
||||
* convert
|
||||
*/
|
||||
|
||||
declare const convert: {
|
||||
colorToHex: (value: string, opt?: Options) => string | null;
|
||||
colorToHsl: (value: string, opt?: Options) => ColorChannels;
|
||||
colorToHwb: (value: string, opt?: Options) => ColorChannels;
|
||||
colorToLab: (value: string, opt?: Options) => ColorChannels;
|
||||
colorToLch: (value: string, opt?: Options) => ColorChannels;
|
||||
colorToOklab: (value: string, opt?: Options) => ColorChannels;
|
||||
colorToOklch: (value: string, opt?: Options) => ColorChannels;
|
||||
colorToRgb: (value: string, opt?: Options) => ColorChannels;
|
||||
colorToXyz: (value: string, opt?: Options) => ColorChannels;
|
||||
colorToXyzD50: (value: string, opt?: Options) => ColorChannels;
|
||||
numberToHex: (value: number) => string;
|
||||
};
|
||||
|
||||
/**
|
||||
* resolve
|
||||
*/
|
||||
|
||||
/**
|
||||
* resolve CSS color
|
||||
* @param value
|
||||
* - CSS color value
|
||||
* - system colors are not supported
|
||||
* @param [opt] - options
|
||||
* @param [opt.currentColor]
|
||||
* - color to use for `currentcolor` keyword
|
||||
* - if omitted, it will be treated as a missing color
|
||||
* i.e. `rgb(none none none / none)`
|
||||
* @param [opt.customProperty]
|
||||
* - custom properties
|
||||
* - pair of `--` prefixed property name and value,
|
||||
* e.g. `customProperty: { '--some-color': '#0000ff' }`
|
||||
* - and/or `callback` function to get the value of the custom property,
|
||||
* e.g. `customProperty: { callback: someDeclaration.getPropertyValue }`
|
||||
* @param [opt.dimension]
|
||||
* - dimension, convert relative length to pixels
|
||||
* - pair of unit and it's value as a number in pixels,
|
||||
* e.g. `dimension: { em: 12, rem: 16, vw: 10.26 }`
|
||||
* - and/or `callback` function to get the value as a number in pixels,
|
||||
* e.g. `dimension: { callback: convertUnitToPixel }`
|
||||
* @param [opt.format]
|
||||
* - output format, one of below
|
||||
* - `computedValue` (default), [computed value][139] of the color
|
||||
* - `specifiedValue`, [specified value][140] of the color
|
||||
* - `hex`, hex color notation, i.e. `rrggbb`
|
||||
* - `hexAlpha`, hex color notation with alpha channel, i.e. `#rrggbbaa`
|
||||
* @returns
|
||||
* - one of rgba?(), #rrggbb(aa)?, color-name, '(empty-string)',
|
||||
* color(color-space r g b / alpha), color(color-space x y z / alpha),
|
||||
* lab(l a b / alpha), lch(l c h / alpha), oklab(l a b / alpha),
|
||||
* oklch(l c h / alpha), null
|
||||
* - in `computedValue`, values are numbers, however `rgb()` values are
|
||||
* integers
|
||||
* - in `specifiedValue`, returns `empty string` for unknown and/or invalid
|
||||
* color
|
||||
* - in `hex`, returns `null` for `transparent`, and also returns `null` if
|
||||
* any of `r`, `g`, `b`, `alpha` is not a number
|
||||
* - in `hexAlpha`, returns `#00000000` for `transparent`,
|
||||
* however returns `null` if any of `r`, `g`, `b`, `alpha` is not a number
|
||||
*/
|
||||
declare const resolve: (value: string, opt?: Options) => string | null;
|
||||
|
||||
declare const utils: {
|
||||
cssCalc: (value: string, opt?: Options) => string;
|
||||
cssVar: (value: string, opt?: Options) => string;
|
||||
extractDashedIdent: (value: string) => string[];
|
||||
isColor: (value: unknown, opt?: Options) => boolean;
|
||||
isGradient: (value: string, opt?: Options) => boolean;
|
||||
splitValue: (value: string, opt?: Options) => string[];
|
||||
};
|
||||
declare const isColor: (value: unknown, opt?: Options) => boolean;
|
||||
declare const cssCalc: (value: string, opt?: Options) => string;
|
||||
|
||||
export { convert, cssCalc, isColor, resolve, utils };
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
/*!
|
||||
* CSS color - Resolve, parse, convert CSS color.
|
||||
* @license MIT
|
||||
* @copyright asamuzaK (Kazz)
|
||||
* @see {@link https://github.com/asamuzaK/cssColor/blob/main/LICENSE}
|
||||
*/
|
||||
export { convert } from './js/convert.js';
|
||||
export { resolve } from './js/resolve.js';
|
||||
export declare const utils: {
|
||||
cssCalc: (value: string, opt?: import('./js/typedef.js').Options) => string;
|
||||
cssVar: (value: string, opt?: import('./js/typedef.js').Options) => string;
|
||||
extractDashedIdent: (value: string) => string[];
|
||||
isColor: (value: unknown, opt?: import('./js/typedef.js').Options) => boolean;
|
||||
isGradient: (value: string, opt?: import('./js/typedef.js').Options) => boolean;
|
||||
splitValue: (value: string, opt?: import('./js/typedef.js').Options) => string[];
|
||||
};
|
||||
export declare const isColor: (value: unknown, opt?: import('./js/typedef.js').Options) => boolean;
|
||||
export declare const cssCalc: (value: string, opt?: import('./js/typedef.js').Options) => string;
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
import { cssCalc as cssCalc$1 } from "./js/css-calc.js";
|
||||
import { isGradient } from "./js/css-gradient.js";
|
||||
import { cssVar } from "./js/css-var.js";
|
||||
import { splitValue, isColor as isColor$1, extractDashedIdent } from "./js/util.js";
|
||||
import { convert } from "./js/convert.js";
|
||||
import { resolve } from "./js/resolve.js";
|
||||
/*!
|
||||
* CSS color - Resolve, parse, convert CSS color.
|
||||
* @license MIT
|
||||
* @copyright asamuzaK (Kazz)
|
||||
* @see {@link https://github.com/asamuzaK/cssColor/blob/main/LICENSE}
|
||||
*/
|
||||
const utils = {
|
||||
cssCalc: cssCalc$1,
|
||||
cssVar,
|
||||
extractDashedIdent,
|
||||
isColor: isColor$1,
|
||||
isGradient,
|
||||
splitValue
|
||||
};
|
||||
const isColor = utils.isColor;
|
||||
const cssCalc = utils.cssCalc;
|
||||
export {
|
||||
convert,
|
||||
cssCalc,
|
||||
isColor,
|
||||
resolve,
|
||||
utils
|
||||
};
|
||||
//# sourceMappingURL=index.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.js","sources":["../../src/index.ts"],"sourcesContent":["/*!\n * CSS color - Resolve, parse, convert CSS color.\n * @license MIT\n * @copyright asamuzaK (Kazz)\n * @see {@link https://github.com/asamuzaK/cssColor/blob/main/LICENSE}\n */\n\nimport { cssCalc as csscalc } from './js/css-calc';\nimport { isGradient } from './js/css-gradient';\nimport { cssVar } from './js/css-var';\nimport { extractDashedIdent, isColor as iscolor, splitValue } from './js/util';\n\nexport { convert } from './js/convert';\nexport { resolve } from './js/resolve';\n/* utils */\nexport const utils = {\n cssCalc: csscalc,\n cssVar,\n extractDashedIdent,\n isColor: iscolor,\n isGradient,\n splitValue\n};\n/* TODO: remove later */\n/* alias */\nexport const isColor = utils.isColor;\nexport const cssCalc = utils.cssCalc;\n"],"names":["csscalc","iscolor"],"mappings":";;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAeO,MAAM,QAAQ;AAAA,EACnB,SAASA;AAAAA,EACT;AAAA,EACA;AAAA,EACA,SAASC;AAAAA,EACT;AAAA,EACA;AACF;AAGO,MAAM,UAAU,MAAM;AACtB,MAAM,UAAU,MAAM;"}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
import { LRUCache } from 'lru-cache';
|
||||
import { Options } from './typedef.js';
|
||||
/**
|
||||
* CacheItem
|
||||
*/
|
||||
export declare class CacheItem {
|
||||
#private;
|
||||
/**
|
||||
* constructor
|
||||
*/
|
||||
constructor(item: unknown, isNull?: boolean);
|
||||
get item(): unknown;
|
||||
get isNull(): boolean;
|
||||
}
|
||||
/**
|
||||
* NullObject
|
||||
*/
|
||||
export declare class NullObject extends CacheItem {
|
||||
/**
|
||||
* constructor
|
||||
*/
|
||||
constructor();
|
||||
}
|
||||
export declare const lruCache: LRUCache<{}, {}, unknown>;
|
||||
/**
|
||||
* set cache
|
||||
* @param key - cache key
|
||||
* @param value - value to cache
|
||||
* @returns void
|
||||
*/
|
||||
export declare const setCache: (key: string, value: unknown) => void;
|
||||
/**
|
||||
* get cache
|
||||
* @param key - cache key
|
||||
* @returns cached item or false otherwise
|
||||
*/
|
||||
export declare const getCache: (key: string) => CacheItem | boolean;
|
||||
/**
|
||||
* create cache key
|
||||
* @param keyData - key data
|
||||
* @param [opt] - options
|
||||
* @returns cache key
|
||||
*/
|
||||
export declare const createCacheKey: (keyData: Record<string, string>, opt?: Options) => string;
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
var __typeError = (msg) => {
|
||||
throw TypeError(msg);
|
||||
};
|
||||
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
|
||||
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
||||
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
||||
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
|
||||
var _isNull, _item;
|
||||
import { LRUCache } from "lru-cache";
|
||||
import { valueToJsonString } from "./util.js";
|
||||
const MAX_CACHE = 4096;
|
||||
class CacheItem {
|
||||
/**
|
||||
* constructor
|
||||
*/
|
||||
constructor(item, isNull = false) {
|
||||
/* private */
|
||||
__privateAdd(this, _isNull);
|
||||
__privateAdd(this, _item);
|
||||
__privateSet(this, _item, item);
|
||||
__privateSet(this, _isNull, !!isNull);
|
||||
}
|
||||
get item() {
|
||||
return __privateGet(this, _item);
|
||||
}
|
||||
get isNull() {
|
||||
return __privateGet(this, _isNull);
|
||||
}
|
||||
}
|
||||
_isNull = new WeakMap();
|
||||
_item = new WeakMap();
|
||||
class NullObject extends CacheItem {
|
||||
/**
|
||||
* constructor
|
||||
*/
|
||||
constructor() {
|
||||
super(Symbol("null"), true);
|
||||
}
|
||||
}
|
||||
const lruCache = new LRUCache({
|
||||
max: MAX_CACHE
|
||||
});
|
||||
const setCache = (key, value) => {
|
||||
if (key) {
|
||||
if (value === null) {
|
||||
lruCache.set(key, new NullObject());
|
||||
} else if (value instanceof CacheItem) {
|
||||
lruCache.set(key, value);
|
||||
} else {
|
||||
lruCache.set(key, new CacheItem(value));
|
||||
}
|
||||
}
|
||||
};
|
||||
const getCache = (key) => {
|
||||
if (key && lruCache.has(key)) {
|
||||
const item = lruCache.get(key);
|
||||
if (item instanceof CacheItem) {
|
||||
return item;
|
||||
}
|
||||
lruCache.delete(key);
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
const createCacheKey = (keyData, opt = {}) => {
|
||||
const { customProperty = {}, dimension = {} } = opt;
|
||||
let cacheKey = "";
|
||||
if (keyData && Object.keys(keyData).length && typeof customProperty.callback !== "function" && typeof dimension.callback !== "function") {
|
||||
keyData.opt = valueToJsonString(opt);
|
||||
cacheKey = valueToJsonString(keyData);
|
||||
}
|
||||
return cacheKey;
|
||||
};
|
||||
export {
|
||||
CacheItem,
|
||||
NullObject,
|
||||
createCacheKey,
|
||||
getCache,
|
||||
lruCache,
|
||||
setCache
|
||||
};
|
||||
//# sourceMappingURL=cache.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"cache.js","sources":["../../../src/js/cache.ts"],"sourcesContent":["/**\n * cache\n */\n\nimport { LRUCache } from 'lru-cache';\nimport { Options } from './typedef';\nimport { valueToJsonString } from './util';\n\n/* numeric constants */\nconst MAX_CACHE = 4096;\n\n/**\n * CacheItem\n */\nexport class CacheItem {\n /* private */\n #isNull: boolean;\n #item: unknown;\n\n /**\n * constructor\n */\n constructor(item: unknown, isNull: boolean = false) {\n this.#item = item;\n this.#isNull = !!isNull;\n }\n\n get item() {\n return this.#item;\n }\n\n get isNull() {\n return this.#isNull;\n }\n}\n\n/**\n * NullObject\n */\nexport class NullObject extends CacheItem {\n /**\n * constructor\n */\n constructor() {\n super(Symbol('null'), true);\n }\n}\n\n/*\n * lru cache\n */\nexport const lruCache = new LRUCache({\n max: MAX_CACHE\n});\n\n/**\n * set cache\n * @param key - cache key\n * @param value - value to cache\n * @returns void\n */\nexport const setCache = (key: string, value: unknown): void => {\n if (key) {\n if (value === null) {\n lruCache.set(key, new NullObject());\n } else if (value instanceof CacheItem) {\n lruCache.set(key, value);\n } else {\n lruCache.set(key, new CacheItem(value));\n }\n }\n};\n\n/**\n * get cache\n * @param key - cache key\n * @returns cached item or false otherwise\n */\nexport const getCache = (key: string): CacheItem | boolean => {\n if (key && lruCache.has(key)) {\n const item = lruCache.get(key);\n if (item instanceof CacheItem) {\n return item;\n }\n // delete unexpected cached item\n lruCache.delete(key);\n return false;\n }\n return false;\n};\n\n/**\n * create cache key\n * @param keyData - key data\n * @param [opt] - options\n * @returns cache key\n */\nexport const createCacheKey = (\n keyData: Record<string, string>,\n opt: Options = {}\n): string => {\n const { customProperty = {}, dimension = {} } = opt;\n let cacheKey = '';\n if (\n keyData &&\n Object.keys(keyData).length &&\n typeof customProperty.callback !== 'function' &&\n typeof dimension.callback !== 'function'\n ) {\n keyData.opt = valueToJsonString(opt);\n cacheKey = valueToJsonString(keyData);\n }\n return cacheKey;\n};\n"],"names":[],"mappings":";;;;;;;;;;AASA,MAAM,YAAY;AAKX,MAAM,UAAU;AAAA;AAAA;AAAA;AAAA,EAQrB,YAAY,MAAe,SAAkB,OAAO;AANpD;AAAA;AACA;AAME,uBAAK,OAAQ;AACR,uBAAA,SAAU,CAAC,CAAC;AAAA,EAAA;AAAA,EAGnB,IAAI,OAAO;AACT,WAAO,mBAAK;AAAA,EAAA;AAAA,EAGd,IAAI,SAAS;AACX,WAAO,mBAAK;AAAA,EAAA;AAEhB;AAlBE;AACA;AAsBK,MAAM,mBAAmB,UAAU;AAAA;AAAA;AAAA;AAAA,EAIxC,cAAc;AACN,UAAA,OAAO,MAAM,GAAG,IAAI;AAAA,EAAA;AAE9B;AAKa,MAAA,WAAW,IAAI,SAAS;AAAA,EACnC,KAAK;AACP,CAAC;AAQY,MAAA,WAAW,CAAC,KAAa,UAAyB;AAC7D,MAAI,KAAK;AACP,QAAI,UAAU,MAAM;AAClB,eAAS,IAAI,KAAK,IAAI,WAAA,CAAY;AAAA,IAAA,WACzB,iBAAiB,WAAW;AAC5B,eAAA,IAAI,KAAK,KAAK;AAAA,IAAA,OAClB;AACL,eAAS,IAAI,KAAK,IAAI,UAAU,KAAK,CAAC;AAAA,IAAA;AAAA,EACxC;AAEJ;AAOa,MAAA,WAAW,CAAC,QAAqC;AAC5D,MAAI,OAAO,SAAS,IAAI,GAAG,GAAG;AACtB,UAAA,OAAO,SAAS,IAAI,GAAG;AAC7B,QAAI,gBAAgB,WAAW;AACtB,aAAA;AAAA,IAAA;AAGT,aAAS,OAAO,GAAG;AACZ,WAAA;AAAA,EAAA;AAEF,SAAA;AACT;AAQO,MAAM,iBAAiB,CAC5B,SACA,MAAe,OACJ;AACX,QAAM,EAAE,iBAAiB,CAAA,GAAI,YAAY,CAAA,EAAO,IAAA;AAChD,MAAI,WAAW;AACf,MACE,WACA,OAAO,KAAK,OAAO,EAAE,UACrB,OAAO,eAAe,aAAa,cACnC,OAAO,UAAU,aAAa,YAC9B;AACQ,YAAA,MAAM,kBAAkB,GAAG;AACnC,eAAW,kBAAkB,OAAO;AAAA,EAAA;AAE/B,SAAA;AACT;"}
|
||||
+537
@@ -0,0 +1,537 @@
|
||||
import { NullObject } from './cache.js';
|
||||
import { ColorChannels, Options, SpecifiedColorChannels } from './typedef.js';
|
||||
/**
|
||||
* @type TriColorChannels - color channels without alpha
|
||||
*/
|
||||
type TriColorChannels = [x: number, y: number, z: number];
|
||||
/**
|
||||
* @type ColorMatrix - color matrix
|
||||
*/
|
||||
type ColorMatrix = [
|
||||
r1: TriColorChannels,
|
||||
r2: TriColorChannels,
|
||||
r3: TriColorChannels
|
||||
];
|
||||
/**
|
||||
* named colors
|
||||
*/
|
||||
export declare const NAMED_COLORS: {
|
||||
readonly aliceblue: [240, 248, 255];
|
||||
readonly antiquewhite: [250, 235, 215];
|
||||
readonly aqua: [0, 255, 255];
|
||||
readonly aquamarine: [127, 255, 212];
|
||||
readonly azure: [240, 255, 255];
|
||||
readonly beige: [245, 245, 220];
|
||||
readonly bisque: [255, 228, 196];
|
||||
readonly black: [0, 0, 0];
|
||||
readonly blanchedalmond: [255, 235, 205];
|
||||
readonly blue: [0, 0, 255];
|
||||
readonly blueviolet: [138, 43, 226];
|
||||
readonly brown: [165, 42, 42];
|
||||
readonly burlywood: [222, 184, 135];
|
||||
readonly cadetblue: [95, 158, 160];
|
||||
readonly chartreuse: [127, 255, 0];
|
||||
readonly chocolate: [210, 105, 30];
|
||||
readonly coral: [255, 127, 80];
|
||||
readonly cornflowerblue: [100, 149, 237];
|
||||
readonly cornsilk: [255, 248, 220];
|
||||
readonly crimson: [220, 20, 60];
|
||||
readonly cyan: [0, 255, 255];
|
||||
readonly darkblue: [0, 0, 139];
|
||||
readonly darkcyan: [0, 139, 139];
|
||||
readonly darkgoldenrod: [184, 134, 11];
|
||||
readonly darkgray: [169, 169, 169];
|
||||
readonly darkgreen: [0, 100, 0];
|
||||
readonly darkgrey: [169, 169, 169];
|
||||
readonly darkkhaki: [189, 183, 107];
|
||||
readonly darkmagenta: [139, 0, 139];
|
||||
readonly darkolivegreen: [85, 107, 47];
|
||||
readonly darkorange: [255, 140, 0];
|
||||
readonly darkorchid: [153, 50, 204];
|
||||
readonly darkred: [139, 0, 0];
|
||||
readonly darksalmon: [233, 150, 122];
|
||||
readonly darkseagreen: [143, 188, 143];
|
||||
readonly darkslateblue: [72, 61, 139];
|
||||
readonly darkslategray: [47, 79, 79];
|
||||
readonly darkslategrey: [47, 79, 79];
|
||||
readonly darkturquoise: [0, 206, 209];
|
||||
readonly darkviolet: [148, 0, 211];
|
||||
readonly deeppink: [255, 20, 147];
|
||||
readonly deepskyblue: [0, 191, 255];
|
||||
readonly dimgray: [105, 105, 105];
|
||||
readonly dimgrey: [105, 105, 105];
|
||||
readonly dodgerblue: [30, 144, 255];
|
||||
readonly firebrick: [178, 34, 34];
|
||||
readonly floralwhite: [255, 250, 240];
|
||||
readonly forestgreen: [34, 139, 34];
|
||||
readonly fuchsia: [255, 0, 255];
|
||||
readonly gainsboro: [220, 220, 220];
|
||||
readonly ghostwhite: [248, 248, 255];
|
||||
readonly gold: [255, 215, 0];
|
||||
readonly goldenrod: [218, 165, 32];
|
||||
readonly gray: [128, 128, 128];
|
||||
readonly green: [0, 128, 0];
|
||||
readonly greenyellow: [173, 255, 47];
|
||||
readonly grey: [128, 128, 128];
|
||||
readonly honeydew: [240, 255, 240];
|
||||
readonly hotpink: [255, 105, 180];
|
||||
readonly indianred: [205, 92, 92];
|
||||
readonly indigo: [75, 0, 130];
|
||||
readonly ivory: [255, 255, 240];
|
||||
readonly khaki: [240, 230, 140];
|
||||
readonly lavender: [230, 230, 250];
|
||||
readonly lavenderblush: [255, 240, 245];
|
||||
readonly lawngreen: [124, 252, 0];
|
||||
readonly lemonchiffon: [255, 250, 205];
|
||||
readonly lightblue: [173, 216, 230];
|
||||
readonly lightcoral: [240, 128, 128];
|
||||
readonly lightcyan: [224, 255, 255];
|
||||
readonly lightgoldenrodyellow: [250, 250, 210];
|
||||
readonly lightgray: [211, 211, 211];
|
||||
readonly lightgreen: [144, 238, 144];
|
||||
readonly lightgrey: [211, 211, 211];
|
||||
readonly lightpink: [255, 182, 193];
|
||||
readonly lightsalmon: [255, 160, 122];
|
||||
readonly lightseagreen: [32, 178, 170];
|
||||
readonly lightskyblue: [135, 206, 250];
|
||||
readonly lightslategray: [119, 136, 153];
|
||||
readonly lightslategrey: [119, 136, 153];
|
||||
readonly lightsteelblue: [176, 196, 222];
|
||||
readonly lightyellow: [255, 255, 224];
|
||||
readonly lime: [0, 255, 0];
|
||||
readonly limegreen: [50, 205, 50];
|
||||
readonly linen: [250, 240, 230];
|
||||
readonly magenta: [255, 0, 255];
|
||||
readonly maroon: [128, 0, 0];
|
||||
readonly mediumaquamarine: [102, 205, 170];
|
||||
readonly mediumblue: [0, 0, 205];
|
||||
readonly mediumorchid: [186, 85, 211];
|
||||
readonly mediumpurple: [147, 112, 219];
|
||||
readonly mediumseagreen: [60, 179, 113];
|
||||
readonly mediumslateblue: [123, 104, 238];
|
||||
readonly mediumspringgreen: [0, 250, 154];
|
||||
readonly mediumturquoise: [72, 209, 204];
|
||||
readonly mediumvioletred: [199, 21, 133];
|
||||
readonly midnightblue: [25, 25, 112];
|
||||
readonly mintcream: [245, 255, 250];
|
||||
readonly mistyrose: [255, 228, 225];
|
||||
readonly moccasin: [255, 228, 181];
|
||||
readonly navajowhite: [255, 222, 173];
|
||||
readonly navy: [0, 0, 128];
|
||||
readonly oldlace: [253, 245, 230];
|
||||
readonly olive: [128, 128, 0];
|
||||
readonly olivedrab: [107, 142, 35];
|
||||
readonly orange: [255, 165, 0];
|
||||
readonly orangered: [255, 69, 0];
|
||||
readonly orchid: [218, 112, 214];
|
||||
readonly palegoldenrod: [238, 232, 170];
|
||||
readonly palegreen: [152, 251, 152];
|
||||
readonly paleturquoise: [175, 238, 238];
|
||||
readonly palevioletred: [219, 112, 147];
|
||||
readonly papayawhip: [255, 239, 213];
|
||||
readonly peachpuff: [255, 218, 185];
|
||||
readonly peru: [205, 133, 63];
|
||||
readonly pink: [255, 192, 203];
|
||||
readonly plum: [221, 160, 221];
|
||||
readonly powderblue: [176, 224, 230];
|
||||
readonly purple: [128, 0, 128];
|
||||
readonly rebeccapurple: [102, 51, 153];
|
||||
readonly red: [255, 0, 0];
|
||||
readonly rosybrown: [188, 143, 143];
|
||||
readonly royalblue: [65, 105, 225];
|
||||
readonly saddlebrown: [139, 69, 19];
|
||||
readonly salmon: [250, 128, 114];
|
||||
readonly sandybrown: [244, 164, 96];
|
||||
readonly seagreen: [46, 139, 87];
|
||||
readonly seashell: [255, 245, 238];
|
||||
readonly sienna: [160, 82, 45];
|
||||
readonly silver: [192, 192, 192];
|
||||
readonly skyblue: [135, 206, 235];
|
||||
readonly slateblue: [106, 90, 205];
|
||||
readonly slategray: [112, 128, 144];
|
||||
readonly slategrey: [112, 128, 144];
|
||||
readonly snow: [255, 250, 250];
|
||||
readonly springgreen: [0, 255, 127];
|
||||
readonly steelblue: [70, 130, 180];
|
||||
readonly tan: [210, 180, 140];
|
||||
readonly teal: [0, 128, 128];
|
||||
readonly thistle: [216, 191, 216];
|
||||
readonly tomato: [255, 99, 71];
|
||||
readonly turquoise: [64, 224, 208];
|
||||
readonly violet: [238, 130, 238];
|
||||
readonly wheat: [245, 222, 179];
|
||||
readonly white: [255, 255, 255];
|
||||
readonly whitesmoke: [245, 245, 245];
|
||||
readonly yellow: [255, 255, 0];
|
||||
readonly yellowgreen: [154, 205, 50];
|
||||
};
|
||||
/**
|
||||
* cache invalid color value
|
||||
* @param key - cache key
|
||||
* @param nullable - is nullable
|
||||
* @returns cached value
|
||||
*/
|
||||
export declare const cacheInvalidColorValue: (cacheKey: string, format: string, nullable?: boolean) => SpecifiedColorChannels | string | NullObject;
|
||||
/**
|
||||
* resolve invalid color value
|
||||
* @param format - output format
|
||||
* @param nullable - is nullable
|
||||
* @returns resolved value
|
||||
*/
|
||||
export declare const resolveInvalidColorValue: (format: string, nullable?: boolean) => SpecifiedColorChannels | string | NullObject;
|
||||
/**
|
||||
* validate color components
|
||||
* @param arr - color components
|
||||
* @param [opt] - options
|
||||
* @param [opt.alpha] - alpha channel
|
||||
* @param [opt.minLength] - min length
|
||||
* @param [opt.maxLength] - max length
|
||||
* @param [opt.minRange] - min range
|
||||
* @param [opt.maxRange] - max range
|
||||
* @param [opt.validateRange] - validate range
|
||||
* @returns result - validated color components
|
||||
*/
|
||||
export declare const validateColorComponents: (arr: ColorChannels | TriColorChannels, opt?: {
|
||||
alpha?: boolean;
|
||||
minLength?: number;
|
||||
maxLength?: number;
|
||||
minRange?: number;
|
||||
maxRange?: number;
|
||||
validateRange?: boolean;
|
||||
}) => ColorChannels | TriColorChannels;
|
||||
/**
|
||||
* transform matrix
|
||||
* @param mtx - 3 * 3 matrix
|
||||
* @param vct - vector
|
||||
* @param [skip] - skip validate
|
||||
* @returns TriColorChannels - [p1, p2, p3]
|
||||
*/
|
||||
export declare const transformMatrix: (mtx: ColorMatrix, vct: TriColorChannels, skip?: boolean) => TriColorChannels;
|
||||
/**
|
||||
* normalize color components
|
||||
* @param colorA - color components [v1, v2, v3, v4]
|
||||
* @param colorB - color components [v1, v2, v3, v4]
|
||||
* @param [skip] - skip validate
|
||||
* @returns result - [colorA, colorB]
|
||||
*/
|
||||
export declare const normalizeColorComponents: (colorA: [number | string, number | string, number | string, number | string], colorB: [number | string, number | string, number | string, number | string], skip?: boolean) => [ColorChannels, ColorChannels];
|
||||
/**
|
||||
* number to hex string
|
||||
* @param value - numeric value
|
||||
* @returns hex string
|
||||
*/
|
||||
export declare const numberToHexString: (value: number) => string;
|
||||
/**
|
||||
* angle to deg
|
||||
* @param angle
|
||||
* @returns deg: 0..360
|
||||
*/
|
||||
export declare const angleToDeg: (angle: string) => number;
|
||||
/**
|
||||
* parse alpha
|
||||
* @param [alpha] - alpha value
|
||||
* @returns alpha: 0..1
|
||||
*/
|
||||
export declare const parseAlpha: (alpha?: string) => number;
|
||||
/**
|
||||
* parse hex alpha
|
||||
* @param value - alpha value in hex string
|
||||
* @returns alpha: 0..1
|
||||
*/
|
||||
export declare const parseHexAlpha: (value: string) => number;
|
||||
/**
|
||||
* transform rgb to linear rgb
|
||||
* @param rgb - [r, g, b] r|g|b: 0..255
|
||||
* @param [skip] - skip validate
|
||||
* @returns TriColorChannels - [r, g, b] r|g|b: 0..1
|
||||
*/
|
||||
export declare const transformRgbToLinearRgb: (rgb: TriColorChannels, skip?: boolean) => TriColorChannels;
|
||||
/**
|
||||
* transform rgb to xyz
|
||||
* @param rgb - [r, g, b] r|g|b: 0..255
|
||||
* @param [skip] - skip validate
|
||||
* @returns TriColorChannels - [x, y, z]
|
||||
*/
|
||||
export declare const transformRgbToXyz: (rgb: TriColorChannels, skip?: boolean) => TriColorChannels;
|
||||
/**
|
||||
* transform rgb to xyz-d50
|
||||
* @param rgb - [r, g, b] r|g|b: 0..255 alpha: 0..1
|
||||
* @returns TriColorChannels - [x, y, z]
|
||||
*/
|
||||
export declare const transformRgbToXyzD50: (rgb: TriColorChannels) => TriColorChannels;
|
||||
/**
|
||||
* transform linear rgb to rgb
|
||||
* @param rgb - [r, g, b] r|g|b: 0..1
|
||||
* @param [round] - round result
|
||||
* @returns TriColorChannels - [r, g, b] r|g|b: 0..255
|
||||
*/
|
||||
export declare const transformLinearRgbToRgb: (rgb: TriColorChannels, round?: boolean) => TriColorChannels;
|
||||
/**
|
||||
* transform xyz to rgb
|
||||
* @param xyz - [x, y, z]
|
||||
* @param [skip] - skip validate
|
||||
* @returns TriColorChannels - [r, g, b] r|g|b: 0..255
|
||||
*/
|
||||
export declare const transformXyzToRgb: (xyz: TriColorChannels, skip?: boolean) => TriColorChannels;
|
||||
/**
|
||||
* transform xyz to xyz-d50
|
||||
* @param xyz - [x, y, z]
|
||||
* @returns TriColorChannels - [x, y, z]
|
||||
*/
|
||||
export declare const transformXyzToXyzD50: (xyz: TriColorChannels) => TriColorChannels;
|
||||
/**
|
||||
* transform xyz to hsl
|
||||
* @param xyz - [x, y, z]
|
||||
* @param [skip] - skip validate
|
||||
* @returns TriColorChannels - [h, s, l]
|
||||
*/
|
||||
export declare const transformXyzToHsl: (xyz: TriColorChannels, skip?: boolean) => TriColorChannels;
|
||||
/**
|
||||
* transform xyz to hwb
|
||||
* @param xyz - [x, y, z]
|
||||
* @param [skip] - skip validate
|
||||
* @returns TriColorChannels - [h, w, b]
|
||||
*/
|
||||
export declare const transformXyzToHwb: (xyz: TriColorChannels, skip?: boolean) => TriColorChannels;
|
||||
/**
|
||||
* transform xyz to oklab
|
||||
* @param xyz - [x, y, z]
|
||||
* @param [skip] - skip validate
|
||||
* @returns TriColorChannels - [l, a, b]
|
||||
*/
|
||||
export declare const transformXyzToOklab: (xyz: TriColorChannels, skip?: boolean) => TriColorChannels;
|
||||
/**
|
||||
* transform xyz to oklch
|
||||
* @param xyz - [x, y, z]
|
||||
* @param [skip] - skip validate
|
||||
* @returns TriColorChannels - [l, c, h]
|
||||
*/
|
||||
export declare const transformXyzToOklch: (xyz: TriColorChannels, skip?: boolean) => TriColorChannels;
|
||||
/**
|
||||
* transform xyz D50 to rgb
|
||||
* @param xyz - [x, y, z]
|
||||
* @param [skip] - skip validate
|
||||
* @returns TriColorChannels - [r, g, b] r|g|b: 0..255
|
||||
*/
|
||||
export declare const transformXyzD50ToRgb: (xyz: TriColorChannels, skip?: boolean) => TriColorChannels;
|
||||
/**
|
||||
* transform xyz-d50 to lab
|
||||
* @param xyz - [x, y, z]
|
||||
* @param [skip] - skip validate
|
||||
* @returns TriColorChannels - [l, a, b]
|
||||
*/
|
||||
export declare const transformXyzD50ToLab: (xyz: TriColorChannels, skip?: boolean) => TriColorChannels;
|
||||
/**
|
||||
* transform xyz-d50 to lch
|
||||
* @param xyz - [x, y, z]
|
||||
* @param [skip] - skip validate
|
||||
* @returns TriColorChannels - [l, c, h]
|
||||
*/
|
||||
export declare const transformXyzD50ToLch: (xyz: TriColorChannels, skip?: boolean) => TriColorChannels;
|
||||
/**
|
||||
* convert rgb to hex color
|
||||
* @param rgb - [r, g, b, alpha] r|g|b: 0..255 alpha: 0..1
|
||||
* @returns hex color
|
||||
*/
|
||||
export declare const convertRgbToHex: (rgb: ColorChannels) => string;
|
||||
/**
|
||||
* convert linear rgb to hex color
|
||||
* @param rgb - [r, g, b, alpha] r|g|b|alpha: 0..1
|
||||
* @param [skip] - skip validate
|
||||
* @returns hex color
|
||||
*/
|
||||
export declare const convertLinearRgbToHex: (rgb: ColorChannels, skip?: boolean) => string;
|
||||
/**
|
||||
* convert xyz to hex color
|
||||
* @param xyz - [x, y, z, alpha]
|
||||
* @returns hex color
|
||||
*/
|
||||
export declare const convertXyzToHex: (xyz: ColorChannels) => string;
|
||||
/**
|
||||
* convert xyz D50 to hex color
|
||||
* @param xyz - [x, y, z, alpha]
|
||||
* @returns hex color
|
||||
*/
|
||||
export declare const convertXyzD50ToHex: (xyz: ColorChannels) => string;
|
||||
/**
|
||||
* convert hex color to rgb
|
||||
* @param value - hex color value
|
||||
* @returns ColorChannels - [r, g, b, alpha] r|g|b: 0..255 alpha: 0..1
|
||||
*/
|
||||
export declare const convertHexToRgb: (value: string) => ColorChannels;
|
||||
/**
|
||||
* convert hex color to linear rgb
|
||||
* @param value - hex color value
|
||||
* @returns ColorChannels - [r, g, b, alpha] r|g|b|alpha: 0..1
|
||||
*/
|
||||
export declare const convertHexToLinearRgb: (value: string) => ColorChannels;
|
||||
/**
|
||||
* convert hex color to xyz
|
||||
* @param value - hex color value
|
||||
* @returns ColorChannels - [x, y, z, alpha]
|
||||
*/
|
||||
export declare const convertHexToXyz: (value: string) => ColorChannels;
|
||||
/**
|
||||
* parse rgb()
|
||||
* @param value - rgb color value
|
||||
* @param [opt] - options
|
||||
* @returns parsed color - ['rgb', r, g, b, alpha], '(empty)', NullObject
|
||||
*/
|
||||
export declare const parseRgb: (value: string, opt?: Options) => SpecifiedColorChannels | string | NullObject;
|
||||
/**
|
||||
* parse hsl()
|
||||
* @param value - hsl color value
|
||||
* @param [opt] - options
|
||||
* @returns parsed color - ['rgb', r, g, b, alpha], '(empty)', NullObject
|
||||
*/
|
||||
export declare const parseHsl: (value: string, opt?: Options) => SpecifiedColorChannels | string | NullObject;
|
||||
/**
|
||||
* parse hwb()
|
||||
* @param value - hwb color value
|
||||
* @param [opt] - options
|
||||
* @returns parsed color - ['rgb', r, g, b, alpha], '(empty)', NullObject
|
||||
*/
|
||||
export declare const parseHwb: (value: string, opt?: Options) => SpecifiedColorChannels | string | NullObject;
|
||||
/**
|
||||
* parse lab()
|
||||
* @param value - lab color value
|
||||
* @param [opt] - options
|
||||
* @returns parsed color
|
||||
* - [xyz-d50, x, y, z, alpha], ['lab', l, a, b, alpha], '(empty)', NullObject
|
||||
*/
|
||||
export declare const parseLab: (value: string, opt?: Options) => SpecifiedColorChannels | string | NullObject;
|
||||
/**
|
||||
* parse lch()
|
||||
* @param value - lch color value
|
||||
* @param [opt] - options
|
||||
* @returns parsed color
|
||||
* - ['xyz-d50', x, y, z, alpha], ['lch', l, c, h, alpha]
|
||||
* - '(empty)', NullObject
|
||||
*/
|
||||
export declare const parseLch: (value: string, opt?: Options) => SpecifiedColorChannels | string | NullObject;
|
||||
/**
|
||||
* parse oklab()
|
||||
* @param value - oklab color value
|
||||
* @param [opt] - options
|
||||
* @returns parsed color
|
||||
* - ['xyz-d65', x, y, z, alpha], ['oklab', l, a, b, alpha]
|
||||
* - '(empty)', NullObject
|
||||
*/
|
||||
export declare const parseOklab: (value: string, opt?: Options) => SpecifiedColorChannels | string | NullObject;
|
||||
/**
|
||||
* parse oklch()
|
||||
* @param value - oklch color value
|
||||
* @param [opt] - options
|
||||
* @returns parsed color
|
||||
* - ['xyz-d65', x, y, z, alpha], ['oklch', l, c, h, alpha]
|
||||
* - '(empty)', NullObject
|
||||
*/
|
||||
export declare const parseOklch: (value: string, opt?: Options) => SpecifiedColorChannels | string | NullObject;
|
||||
/**
|
||||
* parse color()
|
||||
* @param value - color function value
|
||||
* @param [opt] - options
|
||||
* @returns parsed color
|
||||
* - ['xyz-(d50|d65)', x, y, z, alpha], [cs, r, g, b, alpha]
|
||||
* - '(empty)', NullObject
|
||||
*/
|
||||
export declare const parseColorFunc: (value: string, opt?: Options) => SpecifiedColorChannels | string | NullObject;
|
||||
/**
|
||||
* parse color value
|
||||
* @param value - CSS color value
|
||||
* @param [opt] - options
|
||||
* @returns parsed color
|
||||
* - ['xyz-(d50|d65)', x, y, z, alpha], ['rgb', r, g, b, alpha]
|
||||
* - value, '(empty)', NullObject
|
||||
*/
|
||||
export declare const parseColorValue: (value: string, opt?: Options) => SpecifiedColorChannels | string | NullObject;
|
||||
/**
|
||||
* resolve color value
|
||||
* @param value - CSS color value
|
||||
* @param [opt] - options
|
||||
* @returns resolved color
|
||||
* - [cs, v1, v2, v3, alpha], value, '(empty)', NullObject
|
||||
*/
|
||||
export declare const resolveColorValue: (value: string, opt?: Options) => SpecifiedColorChannels | string | NullObject;
|
||||
/**
|
||||
* resolve color()
|
||||
* @param value - color function value
|
||||
* @param [opt] - options
|
||||
* @returns resolved color - [cs, v1, v2, v3, alpha], '(empty)', NullObject
|
||||
*/
|
||||
export declare const resolveColorFunc: (value: string, opt?: Options) => SpecifiedColorChannels | string | NullObject;
|
||||
/**
|
||||
* convert color value to linear rgb
|
||||
* @param value - CSS color value
|
||||
* @param [opt] - options
|
||||
* @returns ColorChannels | NullObject - [r, g, b, alpha] r|g|b|alpha: 0..1
|
||||
*/
|
||||
export declare const convertColorToLinearRgb: (value: string, opt?: {
|
||||
colorSpace?: string;
|
||||
format?: string;
|
||||
}) => ColorChannels | NullObject;
|
||||
/**
|
||||
* convert color value to rgb
|
||||
* @param value - CSS color value
|
||||
* @param [opt] - options
|
||||
* @returns ColorChannels | NullObject
|
||||
* - [r, g, b, alpha] r|g|b: 0..255 alpha: 0..1
|
||||
*/
|
||||
export declare const convertColorToRgb: (value: string, opt?: Options) => ColorChannels | NullObject;
|
||||
/**
|
||||
* convert color value to xyz
|
||||
* @param value - CSS color value
|
||||
* @param [opt] - options
|
||||
* @returns ColorChannels | NullObject - [x, y, z, alpha]
|
||||
*/
|
||||
export declare const convertColorToXyz: (value: string, opt?: Options) => ColorChannels | NullObject;
|
||||
/**
|
||||
* convert color value to hsl
|
||||
* @param value - CSS color value
|
||||
* @param [opt] - options
|
||||
* @returns ColorChannels | NullObject - [h, s, l, alpha], hue may be powerless
|
||||
*/
|
||||
export declare const convertColorToHsl: (value: string, opt?: Options) => ColorChannels | [number | string, number, number, number] | NullObject;
|
||||
/**
|
||||
* convert color value to hwb
|
||||
* @param value - CSS color value
|
||||
* @param [opt] - options
|
||||
* @returns ColorChannels | NullObject - [h, w, b, alpha], hue may be powerless
|
||||
*/
|
||||
export declare const convertColorToHwb: (value: string, opt?: Options) => ColorChannels | [number | string, number, number, number] | NullObject;
|
||||
/**
|
||||
* convert color value to lab
|
||||
* @param value - CSS color value
|
||||
* @param [opt] - options
|
||||
* @returns ColorChannels | NullObject - [l, a, b, alpha]
|
||||
*/
|
||||
export declare const convertColorToLab: (value: string, opt?: Options) => ColorChannels | NullObject;
|
||||
/**
|
||||
* convert color value to lch
|
||||
* @param value - CSS color value
|
||||
* @param [opt] - options
|
||||
* @returns ColorChannels | NullObject - [l, c, h, alpha], hue may be powerless
|
||||
*/
|
||||
export declare const convertColorToLch: (value: string, opt?: Options) => ColorChannels | [number, number, number | string, number] | NullObject;
|
||||
/**
|
||||
* convert color value to oklab
|
||||
* @param value - CSS color value
|
||||
* @param [opt] - options
|
||||
* @returns ColorChannels | NullObject - [l, a, b, alpha]
|
||||
*/
|
||||
export declare const convertColorToOklab: (value: string, opt?: Options) => ColorChannels | NullObject;
|
||||
/**
|
||||
* convert color value to oklch
|
||||
* @param value - CSS color value
|
||||
* @param [opt] - options
|
||||
* @returns ColorChannels | NullObject - [l, c, h, alpha], hue may be powerless
|
||||
*/
|
||||
export declare const convertColorToOklch: (value: string, opt?: Options) => ColorChannels | [number, number, number | string, number] | NullObject;
|
||||
/**
|
||||
* resolve color-mix()
|
||||
* @param value - color-mix color value
|
||||
* @param [opt] - options
|
||||
* @returns resolved color - [cs, v1, v2, v3, alpha], '(empty)'
|
||||
*/
|
||||
export declare const resolveColorMix: (value: string, opt?: Options) => SpecifiedColorChannels | string | NullObject;
|
||||
export {};
|
||||
+2792
File diff suppressed because it is too large
Load Diff
+1
File diff suppressed because one or more lines are too long
+21
@@ -0,0 +1,21 @@
|
||||
/**
|
||||
* common
|
||||
*/
|
||||
/**
|
||||
* get type
|
||||
* @param o - object to check
|
||||
* @returns type of object
|
||||
*/
|
||||
export declare const getType: (o: unknown) => string;
|
||||
/**
|
||||
* is string
|
||||
* @param o - object to check
|
||||
* @returns result
|
||||
*/
|
||||
export declare const isString: (o: unknown) => o is string;
|
||||
/**
|
||||
* is string or number
|
||||
* @param o - object to check
|
||||
* @returns result
|
||||
*/
|
||||
export declare const isStringOrNumber: (o: unknown) => boolean;
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
const isString = (o) => typeof o === "string" || o instanceof String;
|
||||
const isStringOrNumber = (o) => isString(o) || typeof o === "number";
|
||||
export {
|
||||
isString,
|
||||
isStringOrNumber
|
||||
};
|
||||
//# sourceMappingURL=common.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"common.js","sources":["../../../src/js/common.ts"],"sourcesContent":["/**\n * common\n */\n\n/* numeric constants */\nconst TYPE_FROM = 8;\nconst TYPE_TO = -1;\n\n/**\n * get type\n * @param o - object to check\n * @returns type of object\n */\nexport const getType = (o: unknown): string =>\n Object.prototype.toString.call(o).slice(TYPE_FROM, TYPE_TO);\n\n/**\n * is string\n * @param o - object to check\n * @returns result\n */\nexport const isString = (o: unknown): o is string =>\n typeof o === 'string' || o instanceof String;\n\n/**\n * is string or number\n * @param o - object to check\n * @returns result\n */\nexport const isStringOrNumber = (o: unknown): boolean =>\n isString(o) || typeof o === 'number';\n"],"names":[],"mappings":"AAqBO,MAAM,WAAW,CAAC,MACvB,OAAO,MAAM,YAAY,aAAa;AAOjC,MAAM,mBAAmB,CAAC,MAC/B,SAAS,CAAC,KAAK,OAAO,MAAM;"}
|
||||
+41
File diff suppressed because one or more lines are too long
+97
@@ -0,0 +1,97 @@
|
||||
const _DIGIT = "(?:0|[1-9]\\d*)";
|
||||
const _COMPARE = "clamp|max|min";
|
||||
const _EXPO = "exp|hypot|log|pow|sqrt";
|
||||
const _SIGN = "abs|sign";
|
||||
const _STEP = "mod|rem|round";
|
||||
const _TRIG = "a?(?:cos|sin|tan)|atan2";
|
||||
const _MATH = `${_COMPARE}|${_EXPO}|${_SIGN}|${_STEP}|${_TRIG}`;
|
||||
const _CALC = `calc|${_MATH}`;
|
||||
const _VAR = `var|${_CALC}`;
|
||||
const ANGLE = "deg|g?rad|turn";
|
||||
const LENGTH = "[cm]m|[dls]?v(?:[bhiw]|max|min)|in|p[ctx]|q|r?(?:[cl]h|cap|e[mx]|ic)";
|
||||
const NUM = `[+-]?(?:${_DIGIT}(?:\\.\\d*)?|\\.\\d+)(?:e-?${_DIGIT})?`;
|
||||
const NUM_POSITIVE = `\\+?(?:${_DIGIT}(?:\\.\\d*)?|\\.\\d+)(?:e-?${_DIGIT})?`;
|
||||
const NONE = "none";
|
||||
const PCT = `${NUM}%`;
|
||||
const SYN_FN_CALC = `^(?:${_CALC})\\(|(?<=[*\\/\\s\\(])(?:${_CALC})\\(`;
|
||||
const SYN_FN_MATH_START = `^(?:${_MATH})\\($`;
|
||||
const SYN_FN_VAR = "^var\\(|(?<=[*\\/\\s\\(])var\\(";
|
||||
const SYN_FN_VAR_START = `^(?:${_VAR})\\(`;
|
||||
const _ALPHA = `(?:\\s*\\/\\s*(?:${NUM}|${PCT}|${NONE}))?`;
|
||||
const _ALPHA_LV3 = `(?:\\s*,\\s*(?:${NUM}|${PCT}))?`;
|
||||
const _COLOR_FUNC = "(?:ok)?l(?:ab|ch)|color|hsla?|hwb|rgba?";
|
||||
const _COLOR_KEY = "[a-z]+|#[\\da-f]{3}|#[\\da-f]{4}|#[\\da-f]{6}|#[\\da-f]{8}";
|
||||
const _CS_HUE = "(?:ok)?lch|hsl|hwb";
|
||||
const _CS_HUE_ARC = "(?:de|in)creasing|longer|shorter";
|
||||
const _NUM_ANGLE = `${NUM}(?:${ANGLE})?`;
|
||||
const _NUM_ANGLE_NONE = `(?:${NUM}(?:${ANGLE})?|${NONE})`;
|
||||
const _NUM_PCT_NONE = `(?:${NUM}|${PCT}|${NONE})`;
|
||||
const CS_HUE = `(?:${_CS_HUE})(?:\\s(?:${_CS_HUE_ARC})\\shue)?`;
|
||||
const CS_HUE_CAPT = `(${_CS_HUE})(?:\\s(${_CS_HUE_ARC})\\shue)?`;
|
||||
const CS_LAB = "(?:ok)?lab";
|
||||
const CS_LCH = "(?:ok)?lch";
|
||||
const CS_SRGB = "srgb(?:-linear)?";
|
||||
const CS_RGB = `(?:a98|prophoto)-rgb|display-p3|rec2020|${CS_SRGB}`;
|
||||
const CS_XYZ = "xyz(?:-d(?:50|65))?";
|
||||
const CS_RECT = `${CS_LAB}|${CS_RGB}|${CS_XYZ}`;
|
||||
const CS_MIX = `${CS_HUE}|${CS_RECT}`;
|
||||
const FN_COLOR = "color(";
|
||||
const FN_MIX = "color-mix(";
|
||||
const FN_REL = `(?:${_COLOR_FUNC})\\(\\s*from\\s+`;
|
||||
const FN_REL_CAPT = `(${_COLOR_FUNC})\\(\\s*from\\s+`;
|
||||
const FN_VAR = "var(";
|
||||
const SYN_FN_COLOR = `(?:${CS_RGB}|${CS_XYZ})(?:\\s+${_NUM_PCT_NONE}){3}${_ALPHA}`;
|
||||
const SYN_FN_REL = `^${FN_REL}|(?<=[\\s])${FN_REL}`;
|
||||
const SYN_HSL = `${_NUM_ANGLE_NONE}(?:\\s+${_NUM_PCT_NONE}){2}${_ALPHA}`;
|
||||
const SYN_HSL_LV3 = `${_NUM_ANGLE}(?:\\s*,\\s*${PCT}){2}${_ALPHA_LV3}`;
|
||||
const SYN_LCH = `(?:${_NUM_PCT_NONE}\\s+){2}${_NUM_ANGLE_NONE}${_ALPHA}`;
|
||||
const SYN_MOD = `${_NUM_PCT_NONE}(?:\\s+${_NUM_PCT_NONE}){2}${_ALPHA}`;
|
||||
const SYN_RGB_LV3 = `(?:${NUM}(?:\\s*,\\s*${NUM}){2}|${PCT}(?:\\s*,\\s*${PCT}){2})${_ALPHA_LV3}`;
|
||||
const SYN_COLOR_TYPE = `${_COLOR_KEY}|hsla?\\(\\s*${SYN_HSL_LV3}\\s*\\)|rgba?\\(\\s*${SYN_RGB_LV3}\\s*\\)|(?:hsla?|hwb)\\(\\s*${SYN_HSL}\\s*\\)|(?:(?:ok)?lab|rgba?)\\(\\s*${SYN_MOD}\\s*\\)|(?:ok)?lch\\(\\s*${SYN_LCH}\\s*\\)|color\\(\\s*${SYN_FN_COLOR}\\s*\\)`;
|
||||
const SYN_MIX_PART = `(?:${SYN_COLOR_TYPE})(?:\\s+${PCT})?`;
|
||||
const SYN_MIX = `color-mix\\(\\s*in\\s+(?:${CS_MIX})\\s*,\\s*${SYN_MIX_PART}\\s*,\\s*${SYN_MIX_PART}\\s*\\)`;
|
||||
const SYN_MIX_CAPT = `color-mix\\(\\s*in\\s+(${CS_MIX})\\s*,\\s*(${SYN_MIX_PART})\\s*,\\s*(${SYN_MIX_PART})\\s*\\)`;
|
||||
const VAL_COMP = "computedValue";
|
||||
const VAL_MIX = "mixValue";
|
||||
const VAL_SPEC = "specifiedValue";
|
||||
export {
|
||||
ANGLE,
|
||||
CS_HUE,
|
||||
CS_HUE_CAPT,
|
||||
CS_LAB,
|
||||
CS_LCH,
|
||||
CS_MIX,
|
||||
CS_RECT,
|
||||
CS_RGB,
|
||||
CS_SRGB,
|
||||
CS_XYZ,
|
||||
FN_COLOR,
|
||||
FN_MIX,
|
||||
FN_REL,
|
||||
FN_REL_CAPT,
|
||||
FN_VAR,
|
||||
LENGTH,
|
||||
NONE,
|
||||
NUM,
|
||||
NUM_POSITIVE,
|
||||
PCT,
|
||||
SYN_COLOR_TYPE,
|
||||
SYN_FN_CALC,
|
||||
SYN_FN_COLOR,
|
||||
SYN_FN_MATH_START,
|
||||
SYN_FN_REL,
|
||||
SYN_FN_VAR,
|
||||
SYN_FN_VAR_START,
|
||||
SYN_HSL,
|
||||
SYN_HSL_LV3,
|
||||
SYN_LCH,
|
||||
SYN_MIX,
|
||||
SYN_MIX_CAPT,
|
||||
SYN_MIX_PART,
|
||||
SYN_MOD,
|
||||
SYN_RGB_LV3,
|
||||
VAL_COMP,
|
||||
VAL_MIX,
|
||||
VAL_SPEC
|
||||
};
|
||||
//# sourceMappingURL=constant.js.map
|
||||
+1
File diff suppressed because one or more lines are too long
+99
@@ -0,0 +1,99 @@
|
||||
import { NullObject } from './cache.js';
|
||||
import { ColorChannels, Options } from './typedef.js';
|
||||
/**
|
||||
* pre process
|
||||
* @param value - CSS color value
|
||||
* @param [opt] - options
|
||||
* @returns value
|
||||
*/
|
||||
export declare const preProcess: (value: string, opt?: Options) => string | NullObject;
|
||||
/**
|
||||
* convert number to hex string
|
||||
* @param value - numeric value
|
||||
* @returns hex string: 00..ff
|
||||
*/
|
||||
export declare const numberToHex: (value: number) => string;
|
||||
/**
|
||||
* convert color to hex
|
||||
* @param value - CSS color value
|
||||
* @param [opt] - options
|
||||
* @param [opt.alpha] - enable alpha channel
|
||||
* @returns #rrggbb | #rrggbbaa | null
|
||||
*/
|
||||
export declare const colorToHex: (value: string, opt?: Options) => string | null;
|
||||
/**
|
||||
* convert color to hsl
|
||||
* @param value - CSS color value
|
||||
* @param [opt] - options
|
||||
* @returns ColorChannels - [h, s, l, alpha]
|
||||
*/
|
||||
export declare const colorToHsl: (value: string, opt?: Options) => ColorChannels;
|
||||
/**
|
||||
* convert color to hwb
|
||||
* @param value - CSS color value
|
||||
* @param [opt] - options
|
||||
* @returns ColorChannels - [h, w, b, alpha]
|
||||
*/
|
||||
export declare const colorToHwb: (value: string, opt?: Options) => ColorChannels;
|
||||
/**
|
||||
* convert color to lab
|
||||
* @param value - CSS color value
|
||||
* @param [opt] - options
|
||||
* @returns ColorChannels - [l, a, b, alpha]
|
||||
*/
|
||||
export declare const colorToLab: (value: string, opt?: Options) => ColorChannels;
|
||||
/**
|
||||
* convert color to lch
|
||||
* @param value - CSS color value
|
||||
* @param [opt] - options
|
||||
* @returns ColorChannels - [l, c, h, alpha]
|
||||
*/
|
||||
export declare const colorToLch: (value: string, opt?: Options) => ColorChannels;
|
||||
/**
|
||||
* convert color to oklab
|
||||
* @param value - CSS color value
|
||||
* @param [opt] - options
|
||||
* @returns ColorChannels - [l, a, b, alpha]
|
||||
*/
|
||||
export declare const colorToOklab: (value: string, opt?: Options) => ColorChannels;
|
||||
/**
|
||||
* convert color to oklch
|
||||
* @param value - CSS color value
|
||||
* @param [opt] - options
|
||||
* @returns ColorChannels - [l, c, h, alpha]
|
||||
*/
|
||||
export declare const colorToOklch: (value: string, opt?: Options) => ColorChannels;
|
||||
/**
|
||||
* convert color to rgb
|
||||
* @param value - CSS color value
|
||||
* @param [opt] - options
|
||||
* @returns ColorChannels - [r, g, b, alpha]
|
||||
*/
|
||||
export declare const colorToRgb: (value: string, opt?: Options) => ColorChannels;
|
||||
/**
|
||||
* convert color to xyz
|
||||
* @param value - CSS color value
|
||||
* @param [opt] - options
|
||||
* @returns ColorChannels - [x, y, z, alpha]
|
||||
*/
|
||||
export declare const colorToXyz: (value: string, opt?: Options) => ColorChannels;
|
||||
/**
|
||||
* convert color to xyz-d50
|
||||
* @param value - CSS color value
|
||||
* @param [opt] - options
|
||||
* @returns ColorChannels - [x, y, z, alpha]
|
||||
*/
|
||||
export declare const colorToXyzD50: (value: string, opt?: Options) => ColorChannels;
|
||||
export declare const convert: {
|
||||
colorToHex: (value: string, opt?: Options) => string | null;
|
||||
colorToHsl: (value: string, opt?: Options) => ColorChannels;
|
||||
colorToHwb: (value: string, opt?: Options) => ColorChannels;
|
||||
colorToLab: (value: string, opt?: Options) => ColorChannels;
|
||||
colorToLch: (value: string, opt?: Options) => ColorChannels;
|
||||
colorToOklab: (value: string, opt?: Options) => ColorChannels;
|
||||
colorToOklch: (value: string, opt?: Options) => ColorChannels;
|
||||
colorToRgb: (value: string, opt?: Options) => ColorChannels;
|
||||
colorToXyz: (value: string, opt?: Options) => ColorChannels;
|
||||
colorToXyzD50: (value: string, opt?: Options) => ColorChannels;
|
||||
numberToHex: (value: number) => string;
|
||||
};
|
||||
+361
@@ -0,0 +1,361 @@
|
||||
import { NullObject, createCacheKey, getCache, CacheItem, setCache } from "./cache.js";
|
||||
import { numberToHexString, parseColorFunc, parseColorValue, convertColorToRgb, convertColorToOklch, convertColorToOklab, convertColorToLch, convertColorToLab, convertColorToHwb, convertColorToHsl } from "./color.js";
|
||||
import { isString } from "./common.js";
|
||||
import { cssCalc } from "./css-calc.js";
|
||||
import { resolveVar } from "./css-var.js";
|
||||
import { resolveRelativeColor } from "./relative-color.js";
|
||||
import { resolveColor } from "./resolve.js";
|
||||
import { VAL_COMP, SYN_FN_VAR, SYN_FN_REL, SYN_FN_CALC } from "./constant.js";
|
||||
const NAMESPACE = "convert";
|
||||
const REG_FN_CALC = new RegExp(SYN_FN_CALC);
|
||||
const REG_FN_REL = new RegExp(SYN_FN_REL);
|
||||
const REG_FN_VAR = new RegExp(SYN_FN_VAR);
|
||||
const preProcess = (value, opt = {}) => {
|
||||
if (isString(value)) {
|
||||
value = value.trim();
|
||||
if (!value) {
|
||||
return new NullObject();
|
||||
}
|
||||
} else {
|
||||
return new NullObject();
|
||||
}
|
||||
const cacheKey = createCacheKey(
|
||||
{
|
||||
namespace: NAMESPACE,
|
||||
name: "preProcess",
|
||||
value
|
||||
},
|
||||
opt
|
||||
);
|
||||
const cachedResult = getCache(cacheKey);
|
||||
if (cachedResult instanceof CacheItem) {
|
||||
if (cachedResult.isNull) {
|
||||
return cachedResult;
|
||||
}
|
||||
return cachedResult.item;
|
||||
}
|
||||
if (REG_FN_VAR.test(value)) {
|
||||
const resolvedValue = resolveVar(value, opt);
|
||||
if (isString(resolvedValue)) {
|
||||
value = resolvedValue;
|
||||
} else {
|
||||
setCache(cacheKey, null);
|
||||
return new NullObject();
|
||||
}
|
||||
}
|
||||
if (REG_FN_REL.test(value)) {
|
||||
const resolvedValue = resolveRelativeColor(value, opt);
|
||||
if (isString(resolvedValue)) {
|
||||
value = resolvedValue;
|
||||
} else {
|
||||
setCache(cacheKey, null);
|
||||
return new NullObject();
|
||||
}
|
||||
} else if (REG_FN_CALC.test(value)) {
|
||||
value = cssCalc(value, opt);
|
||||
}
|
||||
if (value.startsWith("color-mix")) {
|
||||
const clonedOpt = structuredClone(opt);
|
||||
clonedOpt.format = VAL_COMP;
|
||||
clonedOpt.nullable = true;
|
||||
const resolvedValue = resolveColor(value, clonedOpt);
|
||||
setCache(cacheKey, resolvedValue);
|
||||
return resolvedValue;
|
||||
}
|
||||
setCache(cacheKey, value);
|
||||
return value;
|
||||
};
|
||||
const numberToHex = (value) => {
|
||||
const hex = numberToHexString(value);
|
||||
return hex;
|
||||
};
|
||||
const colorToHex = (value, opt = {}) => {
|
||||
if (isString(value)) {
|
||||
const resolvedValue = preProcess(value, opt);
|
||||
if (resolvedValue instanceof NullObject) {
|
||||
return null;
|
||||
}
|
||||
value = resolvedValue.toLowerCase();
|
||||
} else {
|
||||
throw new TypeError(`${value} is not a string.`);
|
||||
}
|
||||
const { alpha = false } = opt;
|
||||
const cacheKey = createCacheKey(
|
||||
{
|
||||
namespace: NAMESPACE,
|
||||
name: "colorToHex",
|
||||
value
|
||||
},
|
||||
opt
|
||||
);
|
||||
const cachedResult = getCache(cacheKey);
|
||||
if (cachedResult instanceof CacheItem) {
|
||||
if (cachedResult.isNull) {
|
||||
return null;
|
||||
}
|
||||
return cachedResult.item;
|
||||
}
|
||||
let hex;
|
||||
opt.nullable = true;
|
||||
if (alpha) {
|
||||
opt.format = "hexAlpha";
|
||||
hex = resolveColor(value, opt);
|
||||
} else {
|
||||
opt.format = "hex";
|
||||
hex = resolveColor(value, opt);
|
||||
}
|
||||
if (isString(hex)) {
|
||||
setCache(cacheKey, hex);
|
||||
return hex;
|
||||
}
|
||||
setCache(cacheKey, null);
|
||||
return null;
|
||||
};
|
||||
const colorToHsl = (value, opt = {}) => {
|
||||
if (isString(value)) {
|
||||
const resolvedValue = preProcess(value, opt);
|
||||
if (resolvedValue instanceof NullObject) {
|
||||
return [0, 0, 0, 0];
|
||||
}
|
||||
value = resolvedValue.toLowerCase();
|
||||
} else {
|
||||
throw new TypeError(`${value} is not a string.`);
|
||||
}
|
||||
const cacheKey = createCacheKey(
|
||||
{
|
||||
namespace: NAMESPACE,
|
||||
name: "colorToHsl",
|
||||
value
|
||||
},
|
||||
opt
|
||||
);
|
||||
const cachedResult = getCache(cacheKey);
|
||||
if (cachedResult instanceof CacheItem) {
|
||||
return cachedResult.item;
|
||||
}
|
||||
opt.format = "hsl";
|
||||
const hsl = convertColorToHsl(value, opt);
|
||||
setCache(cacheKey, hsl);
|
||||
return hsl;
|
||||
};
|
||||
const colorToHwb = (value, opt = {}) => {
|
||||
if (isString(value)) {
|
||||
const resolvedValue = preProcess(value, opt);
|
||||
if (resolvedValue instanceof NullObject) {
|
||||
return [0, 0, 0, 0];
|
||||
}
|
||||
value = resolvedValue.toLowerCase();
|
||||
} else {
|
||||
throw new TypeError(`${value} is not a string.`);
|
||||
}
|
||||
const cacheKey = createCacheKey(
|
||||
{
|
||||
namespace: NAMESPACE,
|
||||
name: "colorToHwb",
|
||||
value
|
||||
},
|
||||
opt
|
||||
);
|
||||
const cachedResult = getCache(cacheKey);
|
||||
if (cachedResult instanceof CacheItem) {
|
||||
return cachedResult.item;
|
||||
}
|
||||
opt.format = "hwb";
|
||||
const hwb = convertColorToHwb(value, opt);
|
||||
setCache(cacheKey, hwb);
|
||||
return hwb;
|
||||
};
|
||||
const colorToLab = (value, opt = {}) => {
|
||||
if (isString(value)) {
|
||||
const resolvedValue = preProcess(value, opt);
|
||||
if (resolvedValue instanceof NullObject) {
|
||||
return [0, 0, 0, 0];
|
||||
}
|
||||
value = resolvedValue.toLowerCase();
|
||||
} else {
|
||||
throw new TypeError(`${value} is not a string.`);
|
||||
}
|
||||
const cacheKey = createCacheKey(
|
||||
{
|
||||
namespace: NAMESPACE,
|
||||
name: "colorToLab",
|
||||
value
|
||||
},
|
||||
opt
|
||||
);
|
||||
const cachedResult = getCache(cacheKey);
|
||||
if (cachedResult instanceof CacheItem) {
|
||||
return cachedResult.item;
|
||||
}
|
||||
const lab = convertColorToLab(value, opt);
|
||||
setCache(cacheKey, lab);
|
||||
return lab;
|
||||
};
|
||||
const colorToLch = (value, opt = {}) => {
|
||||
if (isString(value)) {
|
||||
const resolvedValue = preProcess(value, opt);
|
||||
if (resolvedValue instanceof NullObject) {
|
||||
return [0, 0, 0, 0];
|
||||
}
|
||||
value = resolvedValue.toLowerCase();
|
||||
} else {
|
||||
throw new TypeError(`${value} is not a string.`);
|
||||
}
|
||||
const cacheKey = createCacheKey(
|
||||
{
|
||||
namespace: NAMESPACE,
|
||||
name: "colorToLch",
|
||||
value
|
||||
},
|
||||
opt
|
||||
);
|
||||
const cachedResult = getCache(cacheKey);
|
||||
if (cachedResult instanceof CacheItem) {
|
||||
return cachedResult.item;
|
||||
}
|
||||
const lch = convertColorToLch(value, opt);
|
||||
setCache(cacheKey, lch);
|
||||
return lch;
|
||||
};
|
||||
const colorToOklab = (value, opt = {}) => {
|
||||
if (isString(value)) {
|
||||
const resolvedValue = preProcess(value, opt);
|
||||
if (resolvedValue instanceof NullObject) {
|
||||
return [0, 0, 0, 0];
|
||||
}
|
||||
value = resolvedValue.toLowerCase();
|
||||
} else {
|
||||
throw new TypeError(`${value} is not a string.`);
|
||||
}
|
||||
const cacheKey = createCacheKey(
|
||||
{
|
||||
namespace: NAMESPACE,
|
||||
name: "colorToOklab",
|
||||
value
|
||||
},
|
||||
opt
|
||||
);
|
||||
const cachedResult = getCache(cacheKey);
|
||||
if (cachedResult instanceof CacheItem) {
|
||||
return cachedResult.item;
|
||||
}
|
||||
const lab = convertColorToOklab(value, opt);
|
||||
setCache(cacheKey, lab);
|
||||
return lab;
|
||||
};
|
||||
const colorToOklch = (value, opt = {}) => {
|
||||
if (isString(value)) {
|
||||
const resolvedValue = preProcess(value, opt);
|
||||
if (resolvedValue instanceof NullObject) {
|
||||
return [0, 0, 0, 0];
|
||||
}
|
||||
value = resolvedValue.toLowerCase();
|
||||
} else {
|
||||
throw new TypeError(`${value} is not a string.`);
|
||||
}
|
||||
const cacheKey = createCacheKey(
|
||||
{
|
||||
namespace: NAMESPACE,
|
||||
name: "colorToOklch",
|
||||
value
|
||||
},
|
||||
opt
|
||||
);
|
||||
const cachedResult = getCache(cacheKey);
|
||||
if (cachedResult instanceof CacheItem) {
|
||||
return cachedResult.item;
|
||||
}
|
||||
const lch = convertColorToOklch(value, opt);
|
||||
setCache(cacheKey, lch);
|
||||
return lch;
|
||||
};
|
||||
const colorToRgb = (value, opt = {}) => {
|
||||
if (isString(value)) {
|
||||
const resolvedValue = preProcess(value, opt);
|
||||
if (resolvedValue instanceof NullObject) {
|
||||
return [0, 0, 0, 0];
|
||||
}
|
||||
value = resolvedValue.toLowerCase();
|
||||
} else {
|
||||
throw new TypeError(`${value} is not a string.`);
|
||||
}
|
||||
const cacheKey = createCacheKey(
|
||||
{
|
||||
namespace: NAMESPACE,
|
||||
name: "colorToRgb",
|
||||
value
|
||||
},
|
||||
opt
|
||||
);
|
||||
const cachedResult = getCache(cacheKey);
|
||||
if (cachedResult instanceof CacheItem) {
|
||||
return cachedResult.item;
|
||||
}
|
||||
const rgb = convertColorToRgb(value, opt);
|
||||
setCache(cacheKey, rgb);
|
||||
return rgb;
|
||||
};
|
||||
const colorToXyz = (value, opt = {}) => {
|
||||
if (isString(value)) {
|
||||
const resolvedValue = preProcess(value, opt);
|
||||
if (resolvedValue instanceof NullObject) {
|
||||
return [0, 0, 0, 0];
|
||||
}
|
||||
value = resolvedValue.toLowerCase();
|
||||
} else {
|
||||
throw new TypeError(`${value} is not a string.`);
|
||||
}
|
||||
const cacheKey = createCacheKey(
|
||||
{
|
||||
namespace: NAMESPACE,
|
||||
name: "colorToXyz",
|
||||
value
|
||||
},
|
||||
opt
|
||||
);
|
||||
const cachedResult = getCache(cacheKey);
|
||||
if (cachedResult instanceof CacheItem) {
|
||||
return cachedResult.item;
|
||||
}
|
||||
let xyz;
|
||||
if (value.startsWith("color(")) {
|
||||
[, ...xyz] = parseColorFunc(value, opt);
|
||||
} else {
|
||||
[, ...xyz] = parseColorValue(value, opt);
|
||||
}
|
||||
setCache(cacheKey, xyz);
|
||||
return xyz;
|
||||
};
|
||||
const colorToXyzD50 = (value, opt = {}) => {
|
||||
opt.d50 = true;
|
||||
return colorToXyz(value, opt);
|
||||
};
|
||||
const convert = {
|
||||
colorToHex,
|
||||
colorToHsl,
|
||||
colorToHwb,
|
||||
colorToLab,
|
||||
colorToLch,
|
||||
colorToOklab,
|
||||
colorToOklch,
|
||||
colorToRgb,
|
||||
colorToXyz,
|
||||
colorToXyzD50,
|
||||
numberToHex
|
||||
};
|
||||
export {
|
||||
colorToHex,
|
||||
colorToHsl,
|
||||
colorToHwb,
|
||||
colorToLab,
|
||||
colorToLch,
|
||||
colorToOklab,
|
||||
colorToOklch,
|
||||
colorToRgb,
|
||||
colorToXyz,
|
||||
colorToXyzD50,
|
||||
convert,
|
||||
numberToHex,
|
||||
preProcess
|
||||
};
|
||||
//# sourceMappingURL=convert.js.map
|
||||
+1
File diff suppressed because one or more lines are too long
+89
@@ -0,0 +1,89 @@
|
||||
import { CSSToken } from '@csstools/css-tokenizer';
|
||||
import { NullObject } from './cache.js';
|
||||
import { Options } from './typedef.js';
|
||||
/**
|
||||
* Calclator
|
||||
*/
|
||||
export declare class Calculator {
|
||||
#private;
|
||||
/**
|
||||
* constructor
|
||||
*/
|
||||
constructor();
|
||||
get hasNum(): boolean;
|
||||
set hasNum(value: boolean);
|
||||
get numSum(): number[];
|
||||
get numMul(): number[];
|
||||
get hasPct(): boolean;
|
||||
set hasPct(value: boolean);
|
||||
get pctSum(): number[];
|
||||
get pctMul(): number[];
|
||||
get hasDim(): boolean;
|
||||
set hasDim(value: boolean);
|
||||
get dimSum(): string[];
|
||||
get dimSub(): string[];
|
||||
get dimMul(): string[];
|
||||
get dimDiv(): string[];
|
||||
get hasEtc(): boolean;
|
||||
set hasEtc(value: boolean);
|
||||
get etcSum(): string[];
|
||||
get etcSub(): string[];
|
||||
get etcMul(): string[];
|
||||
get etcDiv(): string[];
|
||||
/**
|
||||
* clear values
|
||||
* @returns void
|
||||
*/
|
||||
clear(): void;
|
||||
/**
|
||||
* sort values
|
||||
* @param values - values
|
||||
* @returns sorted values
|
||||
*/
|
||||
sort(values?: string[]): string[];
|
||||
/**
|
||||
* multiply values
|
||||
* @returns resolved value
|
||||
*/
|
||||
multiply(): string;
|
||||
/**
|
||||
* sum values
|
||||
* @returns resolved value
|
||||
*/
|
||||
sum(): string;
|
||||
}
|
||||
/**
|
||||
* sort calc values
|
||||
* @param values - values to sort
|
||||
* @param [finalize] - finalize values
|
||||
* @returns sorted values
|
||||
*/
|
||||
export declare const sortCalcValues: (values?: (number | string)[], finalize?: boolean) => string;
|
||||
/**
|
||||
* serialize calc
|
||||
* @param value - CSS value
|
||||
* @param [opt] - options
|
||||
* @returns serialized value
|
||||
*/
|
||||
export declare const serializeCalc: (value: string, opt?: Options) => string;
|
||||
/**
|
||||
* resolve dimension
|
||||
* @param token - CSS token
|
||||
* @param [opt] - options
|
||||
* @returns resolved value
|
||||
*/
|
||||
export declare const resolveDimension: (token: CSSToken, opt?: Options) => string | NullObject;
|
||||
/**
|
||||
* parse tokens
|
||||
* @param tokens - CSS tokens
|
||||
* @param [opt] - options
|
||||
* @returns parsed tokens
|
||||
*/
|
||||
export declare const parseTokens: (tokens: CSSToken[], opt?: Options) => string[];
|
||||
/**
|
||||
* CSS calc()
|
||||
* @param value - CSS value including calc()
|
||||
* @param [opt] - options
|
||||
* @returns resolved value
|
||||
*/
|
||||
export declare const cssCalc: (value: string, opt?: Options) => string;
|
||||
+860
@@ -0,0 +1,860 @@
|
||||
var __typeError = (msg) => {
|
||||
throw TypeError(msg);
|
||||
};
|
||||
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
|
||||
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
||||
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
||||
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
|
||||
var _hasNum, _numSum, _numMul, _hasPct, _pctSum, _pctMul, _hasDim, _dimSum, _dimSub, _dimMul, _dimDiv, _hasEtc, _etcSum, _etcSub, _etcMul, _etcDiv;
|
||||
import { calc } from "@csstools/css-calc";
|
||||
import { TokenType, tokenize } from "@csstools/css-tokenizer";
|
||||
import { createCacheKey, getCache, CacheItem, setCache, NullObject } from "./cache.js";
|
||||
import { isString, isStringOrNumber } from "./common.js";
|
||||
import { resolveVar } from "./css-var.js";
|
||||
import { roundToPrecision } from "./util.js";
|
||||
import { VAL_SPEC, SYN_FN_VAR, SYN_FN_CALC, SYN_FN_VAR_START, NUM, ANGLE, LENGTH, SYN_FN_MATH_START } from "./constant.js";
|
||||
const {
|
||||
CloseParen: PAREN_CLOSE,
|
||||
Comment: COMMENT,
|
||||
Dimension: DIM,
|
||||
EOF,
|
||||
Function: FUNC,
|
||||
OpenParen: PAREN_OPEN,
|
||||
Whitespace: W_SPACE
|
||||
} = TokenType;
|
||||
const NAMESPACE = "css-calc";
|
||||
const TRIA = 3;
|
||||
const HEX = 16;
|
||||
const MAX_PCT = 100;
|
||||
const REG_FN_CALC = new RegExp(SYN_FN_CALC);
|
||||
const REG_FN_CALC_NUM = new RegExp(`^calc\\((${NUM})\\)$`);
|
||||
const REG_FN_MATH_START = new RegExp(SYN_FN_MATH_START);
|
||||
const REG_FN_VAR = new RegExp(SYN_FN_VAR);
|
||||
const REG_FN_VAR_START = new RegExp(SYN_FN_VAR_START);
|
||||
const REG_OPERATOR = /\s[*+/-]\s/;
|
||||
const REG_TYPE_DIM = new RegExp(`^(${NUM})(${ANGLE}|${LENGTH})$`);
|
||||
const REG_TYPE_DIM_PCT = new RegExp(`^(${NUM})(${ANGLE}|${LENGTH}|%)$`);
|
||||
const REG_TYPE_PCT = new RegExp(`^(${NUM})%$`);
|
||||
class Calculator {
|
||||
/**
|
||||
* constructor
|
||||
*/
|
||||
constructor() {
|
||||
/* private */
|
||||
// number
|
||||
__privateAdd(this, _hasNum);
|
||||
__privateAdd(this, _numSum);
|
||||
__privateAdd(this, _numMul);
|
||||
// percentage
|
||||
__privateAdd(this, _hasPct);
|
||||
__privateAdd(this, _pctSum);
|
||||
__privateAdd(this, _pctMul);
|
||||
// dimension
|
||||
__privateAdd(this, _hasDim);
|
||||
__privateAdd(this, _dimSum);
|
||||
__privateAdd(this, _dimSub);
|
||||
__privateAdd(this, _dimMul);
|
||||
__privateAdd(this, _dimDiv);
|
||||
// et cetra
|
||||
__privateAdd(this, _hasEtc);
|
||||
__privateAdd(this, _etcSum);
|
||||
__privateAdd(this, _etcSub);
|
||||
__privateAdd(this, _etcMul);
|
||||
__privateAdd(this, _etcDiv);
|
||||
__privateSet(this, _hasNum, false);
|
||||
__privateSet(this, _numSum, []);
|
||||
__privateSet(this, _numMul, []);
|
||||
__privateSet(this, _hasPct, false);
|
||||
__privateSet(this, _pctSum, []);
|
||||
__privateSet(this, _pctMul, []);
|
||||
__privateSet(this, _hasDim, false);
|
||||
__privateSet(this, _dimSum, []);
|
||||
__privateSet(this, _dimSub, []);
|
||||
__privateSet(this, _dimMul, []);
|
||||
__privateSet(this, _dimDiv, []);
|
||||
__privateSet(this, _hasEtc, false);
|
||||
__privateSet(this, _etcSum, []);
|
||||
__privateSet(this, _etcSub, []);
|
||||
__privateSet(this, _etcMul, []);
|
||||
__privateSet(this, _etcDiv, []);
|
||||
}
|
||||
get hasNum() {
|
||||
return __privateGet(this, _hasNum);
|
||||
}
|
||||
set hasNum(value) {
|
||||
__privateSet(this, _hasNum, !!value);
|
||||
}
|
||||
get numSum() {
|
||||
return __privateGet(this, _numSum);
|
||||
}
|
||||
get numMul() {
|
||||
return __privateGet(this, _numMul);
|
||||
}
|
||||
get hasPct() {
|
||||
return __privateGet(this, _hasPct);
|
||||
}
|
||||
set hasPct(value) {
|
||||
__privateSet(this, _hasPct, !!value);
|
||||
}
|
||||
get pctSum() {
|
||||
return __privateGet(this, _pctSum);
|
||||
}
|
||||
get pctMul() {
|
||||
return __privateGet(this, _pctMul);
|
||||
}
|
||||
get hasDim() {
|
||||
return __privateGet(this, _hasDim);
|
||||
}
|
||||
set hasDim(value) {
|
||||
__privateSet(this, _hasDim, !!value);
|
||||
}
|
||||
get dimSum() {
|
||||
return __privateGet(this, _dimSum);
|
||||
}
|
||||
get dimSub() {
|
||||
return __privateGet(this, _dimSub);
|
||||
}
|
||||
get dimMul() {
|
||||
return __privateGet(this, _dimMul);
|
||||
}
|
||||
get dimDiv() {
|
||||
return __privateGet(this, _dimDiv);
|
||||
}
|
||||
get hasEtc() {
|
||||
return __privateGet(this, _hasEtc);
|
||||
}
|
||||
set hasEtc(value) {
|
||||
__privateSet(this, _hasEtc, !!value);
|
||||
}
|
||||
get etcSum() {
|
||||
return __privateGet(this, _etcSum);
|
||||
}
|
||||
get etcSub() {
|
||||
return __privateGet(this, _etcSub);
|
||||
}
|
||||
get etcMul() {
|
||||
return __privateGet(this, _etcMul);
|
||||
}
|
||||
get etcDiv() {
|
||||
return __privateGet(this, _etcDiv);
|
||||
}
|
||||
/**
|
||||
* clear values
|
||||
* @returns void
|
||||
*/
|
||||
clear() {
|
||||
__privateSet(this, _hasNum, false);
|
||||
__privateSet(this, _numSum, []);
|
||||
__privateSet(this, _numMul, []);
|
||||
__privateSet(this, _hasPct, false);
|
||||
__privateSet(this, _pctSum, []);
|
||||
__privateSet(this, _pctMul, []);
|
||||
__privateSet(this, _hasDim, false);
|
||||
__privateSet(this, _dimSum, []);
|
||||
__privateSet(this, _dimSub, []);
|
||||
__privateSet(this, _dimMul, []);
|
||||
__privateSet(this, _dimDiv, []);
|
||||
__privateSet(this, _hasEtc, false);
|
||||
__privateSet(this, _etcSum, []);
|
||||
__privateSet(this, _etcSub, []);
|
||||
__privateSet(this, _etcMul, []);
|
||||
__privateSet(this, _etcDiv, []);
|
||||
}
|
||||
/**
|
||||
* sort values
|
||||
* @param values - values
|
||||
* @returns sorted values
|
||||
*/
|
||||
sort(values = []) {
|
||||
const arr = [...values];
|
||||
if (arr.length > 1) {
|
||||
arr.sort((a, b) => {
|
||||
let res;
|
||||
if (REG_TYPE_DIM_PCT.test(a) && REG_TYPE_DIM_PCT.test(b)) {
|
||||
const [, valA, unitA] = a.match(REG_TYPE_DIM_PCT);
|
||||
const [, valB, unitB] = b.match(REG_TYPE_DIM_PCT);
|
||||
if (unitA === unitB) {
|
||||
if (Number(valA) === Number(valB)) {
|
||||
res = 0;
|
||||
} else if (Number(valA) > Number(valB)) {
|
||||
res = 1;
|
||||
} else {
|
||||
res = -1;
|
||||
}
|
||||
} else if (unitA > unitB) {
|
||||
res = 1;
|
||||
} else {
|
||||
res = -1;
|
||||
}
|
||||
} else {
|
||||
if (a === b) {
|
||||
res = 0;
|
||||
} else if (a > b) {
|
||||
res = 1;
|
||||
} else {
|
||||
res = -1;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
});
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
/**
|
||||
* multiply values
|
||||
* @returns resolved value
|
||||
*/
|
||||
multiply() {
|
||||
const value = [];
|
||||
let num;
|
||||
if (__privateGet(this, _hasNum)) {
|
||||
num = 1;
|
||||
for (const i of __privateGet(this, _numMul)) {
|
||||
num *= i;
|
||||
if (num === 0 || !Number.isFinite(num) || Number.isNaN(num)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!__privateGet(this, _hasPct) && !__privateGet(this, _hasDim) && !this.hasEtc) {
|
||||
if (Number.isFinite(num)) {
|
||||
num = roundToPrecision(num, HEX);
|
||||
}
|
||||
value.push(num);
|
||||
}
|
||||
}
|
||||
if (__privateGet(this, _hasPct)) {
|
||||
if (typeof num !== "number") {
|
||||
num = 1;
|
||||
}
|
||||
for (const i of __privateGet(this, _pctMul)) {
|
||||
num *= i;
|
||||
if (num === 0 || !Number.isFinite(num) || Number.isNaN(num)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (Number.isFinite(num)) {
|
||||
num = `${roundToPrecision(num, HEX)}%`;
|
||||
}
|
||||
if (!__privateGet(this, _hasDim) && !this.hasEtc) {
|
||||
value.push(num);
|
||||
}
|
||||
}
|
||||
if (__privateGet(this, _hasDim)) {
|
||||
let dim = "";
|
||||
let mul = "";
|
||||
let div = "";
|
||||
if (__privateGet(this, _dimMul).length) {
|
||||
if (__privateGet(this, _dimMul).length === 1) {
|
||||
[mul] = __privateGet(this, _dimMul);
|
||||
} else {
|
||||
mul = `${this.sort(__privateGet(this, _dimMul)).join(" * ")}`;
|
||||
}
|
||||
}
|
||||
if (__privateGet(this, _dimDiv).length) {
|
||||
if (__privateGet(this, _dimDiv).length === 1) {
|
||||
[div] = __privateGet(this, _dimDiv);
|
||||
} else {
|
||||
div = `${this.sort(__privateGet(this, _dimDiv)).join(" * ")}`;
|
||||
}
|
||||
}
|
||||
if (Number.isFinite(num)) {
|
||||
if (mul) {
|
||||
if (div) {
|
||||
if (div.includes("*")) {
|
||||
dim = calc(`calc(${num} * ${mul} / (${div}))`, {
|
||||
toCanonicalUnits: true
|
||||
});
|
||||
} else {
|
||||
dim = calc(`calc(${num} * ${mul} / ${div})`, {
|
||||
toCanonicalUnits: true
|
||||
});
|
||||
}
|
||||
} else {
|
||||
dim = calc(`calc(${num} * ${mul})`, {
|
||||
toCanonicalUnits: true
|
||||
});
|
||||
}
|
||||
} else if (div.includes("*")) {
|
||||
dim = calc(`calc(${num} / (${div}))`, {
|
||||
toCanonicalUnits: true
|
||||
});
|
||||
} else {
|
||||
dim = calc(`calc(${num} / ${div})`, {
|
||||
toCanonicalUnits: true
|
||||
});
|
||||
}
|
||||
value.push(dim.replace(/^calc/, ""));
|
||||
} else {
|
||||
if (!value.length && num !== void 0) {
|
||||
value.push(num);
|
||||
}
|
||||
if (mul) {
|
||||
if (div) {
|
||||
if (div.includes("*")) {
|
||||
dim = calc(`calc(${mul} / (${div}))`, {
|
||||
toCanonicalUnits: true
|
||||
});
|
||||
} else {
|
||||
dim = calc(`calc(${mul} / ${div})`, {
|
||||
toCanonicalUnits: true
|
||||
});
|
||||
}
|
||||
} else {
|
||||
dim = calc(`calc(${mul})`, {
|
||||
toCanonicalUnits: true
|
||||
});
|
||||
}
|
||||
if (value.length) {
|
||||
value.push("*", dim.replace(/^calc/, ""));
|
||||
} else {
|
||||
value.push(dim.replace(/^calc/, ""));
|
||||
}
|
||||
} else {
|
||||
dim = calc(`calc(${div})`, {
|
||||
toCanonicalUnits: true
|
||||
});
|
||||
if (value.length) {
|
||||
value.push("/", dim.replace(/^calc/, ""));
|
||||
} else {
|
||||
value.push("1", "/", dim.replace(/^calc/, ""));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (__privateGet(this, _hasEtc)) {
|
||||
if (__privateGet(this, _etcMul).length) {
|
||||
if (!value.length && num !== void 0) {
|
||||
value.push(num);
|
||||
}
|
||||
const mul = this.sort(__privateGet(this, _etcMul)).join(" * ");
|
||||
if (value.length) {
|
||||
value.push(`* ${mul}`);
|
||||
} else {
|
||||
value.push(`${mul}`);
|
||||
}
|
||||
}
|
||||
if (__privateGet(this, _etcDiv).length) {
|
||||
const div = this.sort(__privateGet(this, _etcDiv)).join(" * ");
|
||||
if (div.includes("*")) {
|
||||
if (value.length) {
|
||||
value.push(`/ (${div})`);
|
||||
} else {
|
||||
value.push(`1 / (${div})`);
|
||||
}
|
||||
} else if (value.length) {
|
||||
value.push(`/ ${div}`);
|
||||
} else {
|
||||
value.push(`1 / ${div}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (value.length) {
|
||||
return value.join(" ");
|
||||
}
|
||||
return "";
|
||||
}
|
||||
/**
|
||||
* sum values
|
||||
* @returns resolved value
|
||||
*/
|
||||
sum() {
|
||||
const value = [];
|
||||
if (__privateGet(this, _hasNum)) {
|
||||
let num = 0;
|
||||
for (const i of __privateGet(this, _numSum)) {
|
||||
num += i;
|
||||
if (!Number.isFinite(num) || Number.isNaN(num)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
value.push(num);
|
||||
}
|
||||
if (__privateGet(this, _hasPct)) {
|
||||
let num = 0;
|
||||
for (const i of __privateGet(this, _pctSum)) {
|
||||
num += i;
|
||||
if (!Number.isFinite(num)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (Number.isFinite(num)) {
|
||||
num = `${num}%`;
|
||||
}
|
||||
if (value.length) {
|
||||
value.push(`+ ${num}`);
|
||||
} else {
|
||||
value.push(num);
|
||||
}
|
||||
}
|
||||
if (__privateGet(this, _hasDim)) {
|
||||
let dim, sum, sub;
|
||||
if (__privateGet(this, _dimSum).length) {
|
||||
sum = this.sort(__privateGet(this, _dimSum)).join(" + ");
|
||||
}
|
||||
if (__privateGet(this, _dimSub).length) {
|
||||
sub = this.sort(__privateGet(this, _dimSub)).join(" + ");
|
||||
}
|
||||
if (sum) {
|
||||
if (sub) {
|
||||
if (sub.includes("-")) {
|
||||
dim = calc(`calc(${sum} - (${sub}))`, {
|
||||
toCanonicalUnits: true
|
||||
});
|
||||
} else {
|
||||
dim = calc(`calc(${sum} - ${sub})`, {
|
||||
toCanonicalUnits: true
|
||||
});
|
||||
}
|
||||
} else {
|
||||
dim = calc(`calc(${sum})`, {
|
||||
toCanonicalUnits: true
|
||||
});
|
||||
}
|
||||
} else {
|
||||
dim = calc(`calc(-1 * (${sub}))`, {
|
||||
toCanonicalUnits: true
|
||||
});
|
||||
}
|
||||
if (value.length) {
|
||||
value.push("+", dim.replace(/^calc/, ""));
|
||||
} else {
|
||||
value.push(dim.replace(/^calc/, ""));
|
||||
}
|
||||
}
|
||||
if (__privateGet(this, _hasEtc)) {
|
||||
if (__privateGet(this, _etcSum).length) {
|
||||
const sum = this.sort(__privateGet(this, _etcSum)).map((item) => {
|
||||
let res;
|
||||
if (REG_OPERATOR.test(item) && !item.startsWith("(") && !item.endsWith(")")) {
|
||||
res = `(${item})`;
|
||||
} else {
|
||||
res = item;
|
||||
}
|
||||
return res;
|
||||
}).join(" + ");
|
||||
if (value.length) {
|
||||
if (__privateGet(this, _etcSum).length > 1) {
|
||||
value.push(`+ (${sum})`);
|
||||
} else {
|
||||
value.push(`+ ${sum}`);
|
||||
}
|
||||
} else {
|
||||
value.push(`${sum}`);
|
||||
}
|
||||
}
|
||||
if (__privateGet(this, _etcSub).length) {
|
||||
const sub = this.sort(__privateGet(this, _etcSub)).map((item) => {
|
||||
let res;
|
||||
if (REG_OPERATOR.test(item) && !item.startsWith("(") && !item.endsWith(")")) {
|
||||
res = `(${item})`;
|
||||
} else {
|
||||
res = item;
|
||||
}
|
||||
return res;
|
||||
}).join(" + ");
|
||||
if (value.length) {
|
||||
if (__privateGet(this, _etcSub).length > 1) {
|
||||
value.push(`- (${sub})`);
|
||||
} else {
|
||||
value.push(`- ${sub}`);
|
||||
}
|
||||
} else if (__privateGet(this, _etcSub).length > 1) {
|
||||
value.push(`-1 * (${sub})`);
|
||||
} else {
|
||||
value.push(`-1 * ${sub}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (value.length) {
|
||||
return value.join(" ");
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
_hasNum = new WeakMap();
|
||||
_numSum = new WeakMap();
|
||||
_numMul = new WeakMap();
|
||||
_hasPct = new WeakMap();
|
||||
_pctSum = new WeakMap();
|
||||
_pctMul = new WeakMap();
|
||||
_hasDim = new WeakMap();
|
||||
_dimSum = new WeakMap();
|
||||
_dimSub = new WeakMap();
|
||||
_dimMul = new WeakMap();
|
||||
_dimDiv = new WeakMap();
|
||||
_hasEtc = new WeakMap();
|
||||
_etcSum = new WeakMap();
|
||||
_etcSub = new WeakMap();
|
||||
_etcMul = new WeakMap();
|
||||
_etcDiv = new WeakMap();
|
||||
const sortCalcValues = (values = [], finalize = false) => {
|
||||
if (values.length < TRIA) {
|
||||
throw new Error(`Unexpected array length ${values.length}.`);
|
||||
}
|
||||
const start = values.shift();
|
||||
if (!isString(start) || !start.endsWith("(")) {
|
||||
throw new Error(`Unexpected token ${start}.`);
|
||||
}
|
||||
const end = values.pop();
|
||||
if (end !== ")") {
|
||||
throw new Error(`Unexpected token ${end}.`);
|
||||
}
|
||||
if (values.length === 1) {
|
||||
const [value] = values;
|
||||
if (!isStringOrNumber(value)) {
|
||||
throw new Error(`Unexpected token ${value}.`);
|
||||
}
|
||||
return `${start}${value}${end}`;
|
||||
}
|
||||
const sortedValues = [];
|
||||
const cal = new Calculator();
|
||||
let operator = "";
|
||||
const l = values.length;
|
||||
for (let i = 0; i < l; i++) {
|
||||
const value = values[i];
|
||||
if (!isStringOrNumber(value)) {
|
||||
throw new Error(`Unexpected token ${value}.`);
|
||||
}
|
||||
if (value === "*" || value === "/") {
|
||||
operator = value;
|
||||
} else if (value === "+" || value === "-") {
|
||||
const sortedValue = cal.multiply();
|
||||
if (sortedValue) {
|
||||
sortedValues.push(sortedValue, value);
|
||||
}
|
||||
cal.clear();
|
||||
operator = "";
|
||||
} else {
|
||||
const numValue = Number(value);
|
||||
const strValue = `${value}`;
|
||||
switch (operator) {
|
||||
case "/": {
|
||||
if (Number.isFinite(numValue)) {
|
||||
cal.hasNum = true;
|
||||
cal.numMul.push(1 / numValue);
|
||||
} else if (REG_TYPE_PCT.test(strValue)) {
|
||||
const [, val] = strValue.match(REG_TYPE_PCT);
|
||||
cal.hasPct = true;
|
||||
cal.pctMul.push(MAX_PCT * MAX_PCT / Number(val));
|
||||
} else if (REG_TYPE_DIM.test(strValue)) {
|
||||
cal.hasDim = true;
|
||||
cal.dimDiv.push(strValue);
|
||||
} else {
|
||||
cal.hasEtc = true;
|
||||
cal.etcDiv.push(strValue);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "*":
|
||||
default: {
|
||||
if (Number.isFinite(numValue)) {
|
||||
cal.hasNum = true;
|
||||
cal.numMul.push(numValue);
|
||||
} else if (REG_TYPE_PCT.test(strValue)) {
|
||||
const [, val] = strValue.match(REG_TYPE_PCT);
|
||||
cal.hasPct = true;
|
||||
cal.pctMul.push(Number(val));
|
||||
} else if (REG_TYPE_DIM.test(strValue)) {
|
||||
cal.hasDim = true;
|
||||
cal.dimMul.push(strValue);
|
||||
} else {
|
||||
cal.hasEtc = true;
|
||||
cal.etcMul.push(strValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (i === l - 1) {
|
||||
const sortedValue = cal.multiply();
|
||||
if (sortedValue) {
|
||||
sortedValues.push(sortedValue);
|
||||
}
|
||||
cal.clear();
|
||||
operator = "";
|
||||
}
|
||||
}
|
||||
let resolvedValue = "";
|
||||
if (finalize && (sortedValues.includes("+") || sortedValues.includes("-"))) {
|
||||
const finalizedValues = [];
|
||||
cal.clear();
|
||||
operator = "";
|
||||
const l2 = sortedValues.length;
|
||||
for (let i = 0; i < l2; i++) {
|
||||
const value = sortedValues[i];
|
||||
if (isStringOrNumber(value)) {
|
||||
if (value === "+" || value === "-") {
|
||||
operator = value;
|
||||
} else {
|
||||
const numValue = Number(value);
|
||||
const strValue = `${value}`;
|
||||
switch (operator) {
|
||||
case "-": {
|
||||
if (Number.isFinite(numValue)) {
|
||||
cal.hasNum = true;
|
||||
cal.numSum.push(-1 * numValue);
|
||||
} else if (REG_TYPE_PCT.test(strValue)) {
|
||||
const [, val] = strValue.match(REG_TYPE_PCT);
|
||||
cal.hasPct = true;
|
||||
cal.pctSum.push(-1 * Number(val));
|
||||
} else if (REG_TYPE_DIM.test(strValue)) {
|
||||
cal.hasDim = true;
|
||||
cal.dimSub.push(strValue);
|
||||
} else {
|
||||
cal.hasEtc = true;
|
||||
cal.etcSub.push(strValue);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "+":
|
||||
default: {
|
||||
if (Number.isFinite(numValue)) {
|
||||
cal.hasNum = true;
|
||||
cal.numSum.push(numValue);
|
||||
} else if (REG_TYPE_PCT.test(strValue)) {
|
||||
const [, val] = strValue.match(REG_TYPE_PCT);
|
||||
cal.hasPct = true;
|
||||
cal.pctSum.push(Number(val));
|
||||
} else if (REG_TYPE_DIM.test(strValue)) {
|
||||
cal.hasDim = true;
|
||||
cal.dimSum.push(strValue);
|
||||
} else {
|
||||
cal.hasEtc = true;
|
||||
cal.etcSum.push(strValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (i === l2 - 1) {
|
||||
const sortedValue = cal.sum();
|
||||
if (sortedValue) {
|
||||
finalizedValues.push(sortedValue);
|
||||
}
|
||||
cal.clear();
|
||||
operator = "";
|
||||
}
|
||||
}
|
||||
resolvedValue = finalizedValues.join(" ").replace(/\+\s-/g, "- ");
|
||||
} else {
|
||||
resolvedValue = sortedValues.join(" ").replace(/\+\s-/g, "- ");
|
||||
}
|
||||
if (resolvedValue.startsWith("(") && resolvedValue.endsWith(")") && resolvedValue.lastIndexOf("(") === 0 && resolvedValue.indexOf(")") === resolvedValue.length - 1) {
|
||||
resolvedValue = resolvedValue.replace(/^\(/, "").replace(/\)$/, "");
|
||||
}
|
||||
return `${start}${resolvedValue}${end}`;
|
||||
};
|
||||
const serializeCalc = (value, opt = {}) => {
|
||||
const { format = "" } = opt;
|
||||
if (isString(value)) {
|
||||
if (!REG_FN_VAR_START.test(value) || format !== VAL_SPEC) {
|
||||
return value;
|
||||
}
|
||||
value = value.toLowerCase().trim();
|
||||
} else {
|
||||
throw new TypeError(`${value} is not a string.`);
|
||||
}
|
||||
const cacheKey = createCacheKey(
|
||||
{
|
||||
namespace: NAMESPACE,
|
||||
name: "serializeCalc",
|
||||
value
|
||||
},
|
||||
opt
|
||||
);
|
||||
const cachedResult = getCache(cacheKey);
|
||||
if (cachedResult instanceof CacheItem) {
|
||||
return cachedResult.item;
|
||||
}
|
||||
const items = tokenize({ css: value }).map((token) => {
|
||||
const [type, value2] = token;
|
||||
let res = "";
|
||||
if (type !== W_SPACE && type !== COMMENT) {
|
||||
res = value2;
|
||||
}
|
||||
return res;
|
||||
}).filter((v) => v);
|
||||
let startIndex = items.findLastIndex((item) => /\($/.test(item));
|
||||
while (startIndex) {
|
||||
const endIndex = items.findIndex((item, index) => {
|
||||
return item === ")" && index > startIndex;
|
||||
});
|
||||
const slicedValues = items.slice(startIndex, endIndex + 1);
|
||||
let serializedValue = sortCalcValues(slicedValues);
|
||||
if (REG_FN_VAR_START.test(serializedValue)) {
|
||||
serializedValue = calc(serializedValue, {
|
||||
toCanonicalUnits: true
|
||||
});
|
||||
}
|
||||
items.splice(startIndex, endIndex - startIndex + 1, serializedValue);
|
||||
startIndex = items.findLastIndex((item) => /\($/.test(item));
|
||||
}
|
||||
const serializedCalc = sortCalcValues(items, true);
|
||||
setCache(cacheKey, serializedCalc);
|
||||
return serializedCalc;
|
||||
};
|
||||
const resolveDimension = (token, opt = {}) => {
|
||||
if (!Array.isArray(token)) {
|
||||
throw new TypeError(`${token} is not an array.`);
|
||||
}
|
||||
const [, , , , detail = {}] = token;
|
||||
const { unit, value } = detail;
|
||||
const { dimension = {} } = opt;
|
||||
if (unit === "px") {
|
||||
return `${value}${unit}`;
|
||||
}
|
||||
const relativeValue = Number(value);
|
||||
if (unit && Number.isFinite(relativeValue)) {
|
||||
let pixelValue;
|
||||
if (Object.hasOwnProperty.call(dimension, unit)) {
|
||||
pixelValue = dimension[unit];
|
||||
} else if (typeof dimension.callback === "function") {
|
||||
pixelValue = dimension.callback(unit);
|
||||
}
|
||||
pixelValue = Number(pixelValue);
|
||||
if (Number.isFinite(pixelValue)) {
|
||||
return `${relativeValue * pixelValue}px`;
|
||||
}
|
||||
}
|
||||
return new NullObject();
|
||||
};
|
||||
const parseTokens = (tokens, opt = {}) => {
|
||||
if (!Array.isArray(tokens)) {
|
||||
throw new TypeError(`${tokens} is not an array.`);
|
||||
}
|
||||
const { format = "" } = opt;
|
||||
const mathFunc = /* @__PURE__ */ new Set();
|
||||
let nest = 0;
|
||||
const res = [];
|
||||
while (tokens.length) {
|
||||
const token = tokens.shift();
|
||||
if (!Array.isArray(token)) {
|
||||
throw new TypeError(`${token} is not an array.`);
|
||||
}
|
||||
const [type = "", value = ""] = token;
|
||||
switch (type) {
|
||||
case DIM: {
|
||||
if (format === VAL_SPEC && !mathFunc.has(nest)) {
|
||||
res.push(value);
|
||||
} else {
|
||||
const resolvedValue = resolveDimension(token, opt);
|
||||
if (isString(resolvedValue)) {
|
||||
res.push(resolvedValue);
|
||||
} else {
|
||||
res.push(value);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FUNC:
|
||||
case PAREN_OPEN: {
|
||||
res.push(value);
|
||||
nest++;
|
||||
if (REG_FN_MATH_START.test(value)) {
|
||||
mathFunc.add(nest);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PAREN_CLOSE: {
|
||||
if (res.length) {
|
||||
const lastValue = res[res.length - 1];
|
||||
if (lastValue === " ") {
|
||||
res.splice(-1, 1, value);
|
||||
} else {
|
||||
res.push(value);
|
||||
}
|
||||
} else {
|
||||
res.push(value);
|
||||
}
|
||||
if (mathFunc.has(nest)) {
|
||||
mathFunc.delete(nest);
|
||||
}
|
||||
nest--;
|
||||
break;
|
||||
}
|
||||
case W_SPACE: {
|
||||
if (res.length) {
|
||||
const lastValue = res[res.length - 1];
|
||||
if (isString(lastValue) && !lastValue.endsWith("(") && lastValue !== " ") {
|
||||
res.push(value);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
if (type !== COMMENT && type !== EOF) {
|
||||
res.push(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
};
|
||||
const cssCalc = (value, opt = {}) => {
|
||||
const { format = "" } = opt;
|
||||
if (isString(value)) {
|
||||
if (REG_FN_VAR.test(value)) {
|
||||
if (format === VAL_SPEC) {
|
||||
return value;
|
||||
} else {
|
||||
const resolvedValue2 = resolveVar(value, opt);
|
||||
if (isString(resolvedValue2)) {
|
||||
return resolvedValue2;
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
} else if (!REG_FN_CALC.test(value)) {
|
||||
return value;
|
||||
}
|
||||
value = value.toLowerCase().trim();
|
||||
} else {
|
||||
throw new TypeError(`${value} is not a string.`);
|
||||
}
|
||||
const cacheKey = createCacheKey(
|
||||
{
|
||||
namespace: NAMESPACE,
|
||||
name: "cssCalc",
|
||||
value
|
||||
},
|
||||
opt
|
||||
);
|
||||
const cachedResult = getCache(cacheKey);
|
||||
if (cachedResult instanceof CacheItem) {
|
||||
return cachedResult.item;
|
||||
}
|
||||
const tokens = tokenize({ css: value });
|
||||
const values = parseTokens(tokens, opt);
|
||||
let resolvedValue = calc(values.join(""), {
|
||||
toCanonicalUnits: true
|
||||
});
|
||||
if (REG_FN_VAR_START.test(value)) {
|
||||
if (REG_TYPE_DIM_PCT.test(resolvedValue)) {
|
||||
const [, val, unit] = resolvedValue.match(
|
||||
REG_TYPE_DIM_PCT
|
||||
);
|
||||
resolvedValue = `${roundToPrecision(Number(val), HEX)}${unit}`;
|
||||
}
|
||||
if (resolvedValue && !REG_FN_VAR_START.test(resolvedValue) && format === VAL_SPEC) {
|
||||
resolvedValue = `calc(${resolvedValue})`;
|
||||
}
|
||||
}
|
||||
if (format === VAL_SPEC) {
|
||||
if (/\s[-+*/]\s/.test(resolvedValue) && !resolvedValue.includes("NaN")) {
|
||||
resolvedValue = serializeCalc(resolvedValue, opt);
|
||||
} else if (REG_FN_CALC_NUM.test(resolvedValue)) {
|
||||
const [, val] = resolvedValue.match(REG_FN_CALC_NUM);
|
||||
resolvedValue = `calc(${roundToPrecision(Number(val), HEX)})`;
|
||||
}
|
||||
}
|
||||
setCache(cacheKey, resolvedValue);
|
||||
return resolvedValue;
|
||||
};
|
||||
export {
|
||||
Calculator,
|
||||
cssCalc,
|
||||
parseTokens,
|
||||
resolveDimension,
|
||||
serializeCalc,
|
||||
sortCalcValues
|
||||
};
|
||||
//# sourceMappingURL=css-calc.js.map
|
||||
+1
File diff suppressed because one or more lines are too long
+54
@@ -0,0 +1,54 @@
|
||||
import { Options } from './typedef.js';
|
||||
/**
|
||||
* @type ColorStopList - list of color stops
|
||||
*/
|
||||
type ColorStopList = [string, string, ...string[]];
|
||||
/**
|
||||
* @typedef Gradient - parsed CSS gradient
|
||||
* @property value - input value
|
||||
* @property type - gradient type
|
||||
* @property [gradientLine] - gradient line
|
||||
* @property colorStopList - list of color stops
|
||||
*/
|
||||
interface Gradient {
|
||||
value: string;
|
||||
type: string;
|
||||
gradientLine?: string;
|
||||
colorStopList: ColorStopList;
|
||||
}
|
||||
/**
|
||||
* get gradient type
|
||||
* @param value - gradient value
|
||||
* @returns gradient type
|
||||
*/
|
||||
export declare const getGradientType: (value: string) => string;
|
||||
/**
|
||||
* validate gradient line
|
||||
* @param value - gradient line value
|
||||
* @param type - gradient type
|
||||
* @returns result
|
||||
*/
|
||||
export declare const validateGradientLine: (value: string, type: string) => boolean;
|
||||
/**
|
||||
* validate color stop list
|
||||
* @param list
|
||||
* @param type
|
||||
* @param [opt]
|
||||
* @returns result
|
||||
*/
|
||||
export declare const validateColorStopList: (list: string[], type: string, opt?: Options) => boolean;
|
||||
/**
|
||||
* parse CSS gradient
|
||||
* @param value - gradient value
|
||||
* @param [opt] - options
|
||||
* @returns parsed result
|
||||
*/
|
||||
export declare const parseGradient: (value: string, opt?: Options) => Gradient | null;
|
||||
/**
|
||||
* is CSS gradient
|
||||
* @param value - CSS value
|
||||
* @param [opt] - options
|
||||
* @returns result
|
||||
*/
|
||||
export declare const isGradient: (value: string, opt?: Options) => boolean;
|
||||
export {};
|
||||
+195
@@ -0,0 +1,195 @@
|
||||
import { createCacheKey, getCache, CacheItem, setCache } from "./cache.js";
|
||||
import { isString } from "./common.js";
|
||||
import { splitValue, isColor } from "./util.js";
|
||||
import { NUM, ANGLE, PCT, LENGTH, CS_RECT, CS_HUE, NUM_POSITIVE } from "./constant.js";
|
||||
const NAMESPACE = "css-gradient";
|
||||
const DIM_ANGLE = `${NUM}(?:${ANGLE})`;
|
||||
const DIM_ANGLE_PCT = `${DIM_ANGLE}|${PCT}`;
|
||||
const DIM_LEN = `${NUM}(?:${LENGTH})|0`;
|
||||
const DIM_LEN_PCT = `${DIM_LEN}|${PCT}`;
|
||||
const DIM_LEN_PCT_POSI = `${NUM_POSITIVE}(?:${LENGTH}|%)|0`;
|
||||
const DIM_LEN_POSI = `${NUM_POSITIVE}(?:${LENGTH})|0`;
|
||||
const CTR = "center";
|
||||
const L_R = "left|right";
|
||||
const T_B = "top|bottom";
|
||||
const S_E = "start|end";
|
||||
const AXIS_X = `${L_R}|x-(?:${S_E})`;
|
||||
const AXIS_Y = `${T_B}|y-(?:${S_E})`;
|
||||
const BLOCK = `block-(?:${S_E})`;
|
||||
const INLINE = `inline-(?:${S_E})`;
|
||||
const POS_1 = `${CTR}|${AXIS_X}|${AXIS_Y}|${BLOCK}|${INLINE}|${DIM_LEN_PCT}`;
|
||||
const POS_2 = [
|
||||
`(?:${CTR}|${AXIS_X})\\s+(?:${CTR}|${AXIS_Y})`,
|
||||
`(?:${CTR}|${AXIS_Y})\\s+(?:${CTR}|${AXIS_X})`,
|
||||
`(?:${CTR}|${AXIS_X}|${DIM_LEN_PCT})\\s+(?:${CTR}|${AXIS_Y}|${DIM_LEN_PCT})`,
|
||||
`(?:${CTR}|${BLOCK})\\s+(?:${CTR}|${INLINE})`,
|
||||
`(?:${CTR}|${INLINE})\\s+(?:${CTR}|${BLOCK})`,
|
||||
`(?:${CTR}|${S_E})\\s+(?:${CTR}|${S_E})`
|
||||
].join("|");
|
||||
const POS_4 = [
|
||||
`(?:${AXIS_X})\\s+(?:${DIM_LEN_PCT})\\s+(?:${AXIS_Y})\\s+(?:${DIM_LEN_PCT})`,
|
||||
`(?:${AXIS_Y})\\s+(?:${DIM_LEN_PCT})\\s+(?:${AXIS_X})\\s+(?:${DIM_LEN_PCT})`,
|
||||
`(?:${BLOCK})\\s+(?:${DIM_LEN_PCT})\\s+(?:${INLINE})\\s+(?:${DIM_LEN_PCT})`,
|
||||
`(?:${INLINE})\\s+(?:${DIM_LEN_PCT})\\s+(?:${BLOCK})\\s+(?:${DIM_LEN_PCT})`,
|
||||
`(?:${S_E})\\s+(?:${DIM_LEN_PCT})\\s+(?:${S_E})\\s+(?:${DIM_LEN_PCT})`
|
||||
].join("|");
|
||||
const RAD_EXTENT = "(?:clos|farth)est-(?:corner|side)";
|
||||
const RAD_SIZE = [
|
||||
`${RAD_EXTENT}(?:\\s+${RAD_EXTENT})?`,
|
||||
`${DIM_LEN_POSI}`,
|
||||
`(?:${DIM_LEN_PCT_POSI})\\s+(?:${DIM_LEN_PCT_POSI})`
|
||||
].join("|");
|
||||
const RAD_SHAPE = "circle|ellipse";
|
||||
const FROM_ANGLE = `from\\s+${DIM_ANGLE}`;
|
||||
const AT_POSITION = `at\\s+(?:${POS_1}|${POS_2}|${POS_4})`;
|
||||
const TO_SIDE_CORNER = `to\\s+(?:(?:${L_R})(?:\\s(?:${T_B}))?|(?:${T_B})(?:\\s(?:${L_R}))?)`;
|
||||
const IN_COLOR_SPACE = `in\\s+(?:${CS_RECT}|${CS_HUE})`;
|
||||
const REG_GRAD = /^(?:repeating-)?(?:conic|linear|radial)-gradient\(/;
|
||||
const REG_GRAD_CAPT = /^((?:repeating-)?(?:conic|linear|radial)-gradient)\(/;
|
||||
const getGradientType = (value) => {
|
||||
if (isString(value)) {
|
||||
value = value.trim();
|
||||
if (REG_GRAD.test(value)) {
|
||||
const [, type] = value.match(REG_GRAD_CAPT);
|
||||
return type;
|
||||
}
|
||||
}
|
||||
return "";
|
||||
};
|
||||
const validateGradientLine = (value, type) => {
|
||||
if (isString(value) && isString(type)) {
|
||||
value = value.trim();
|
||||
type = type.trim();
|
||||
let lineSyntax = "";
|
||||
if (/^(?:repeating-)?linear-gradient$/.test(type)) {
|
||||
lineSyntax = [
|
||||
`(?:${DIM_ANGLE}|${TO_SIDE_CORNER})(?:\\s+${IN_COLOR_SPACE})?`,
|
||||
`${IN_COLOR_SPACE}(?:\\s+(?:${DIM_ANGLE}|${TO_SIDE_CORNER}))?`
|
||||
].join("|");
|
||||
} else if (/^(?:repeating-)?radial-gradient$/.test(type)) {
|
||||
lineSyntax = [
|
||||
`(?:${RAD_SHAPE})(?:\\s+(?:${RAD_SIZE}))?(?:\\s+${AT_POSITION})?(?:\\s+${IN_COLOR_SPACE})?`,
|
||||
`(?:${RAD_SIZE})(?:\\s+(?:${RAD_SHAPE}))?(?:\\s+${AT_POSITION})?(?:\\s+${IN_COLOR_SPACE})?`,
|
||||
`${AT_POSITION}(?:\\s+${IN_COLOR_SPACE})?`,
|
||||
`${IN_COLOR_SPACE}(?:\\s+${RAD_SHAPE})(?:\\s+(?:${RAD_SIZE}))?(?:\\s+${AT_POSITION})?`,
|
||||
`${IN_COLOR_SPACE}(?:\\s+${RAD_SIZE})(?:\\s+(?:${RAD_SHAPE}))?(?:\\s+${AT_POSITION})?`,
|
||||
`${IN_COLOR_SPACE}(?:\\s+${AT_POSITION})?`
|
||||
].join("|");
|
||||
} else if (/^(?:repeating-)?conic-gradient$/.test(type)) {
|
||||
lineSyntax = [
|
||||
`${FROM_ANGLE}(?:\\s+${AT_POSITION})?(?:\\s+${IN_COLOR_SPACE})?`,
|
||||
`${AT_POSITION}(?:\\s+${IN_COLOR_SPACE})?`,
|
||||
`${IN_COLOR_SPACE}(?:\\s+${FROM_ANGLE})?(?:\\s+${AT_POSITION})?`
|
||||
].join("|");
|
||||
}
|
||||
if (lineSyntax) {
|
||||
const reg = new RegExp(`^(?:${lineSyntax})$`);
|
||||
return reg.test(value);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
const validateColorStopList = (list, type, opt = {}) => {
|
||||
if (Array.isArray(list) && list.length > 1) {
|
||||
const dimension = /^(?:repeating-)?conic-gradient$/.test(type) ? DIM_ANGLE_PCT : DIM_LEN_PCT;
|
||||
const regColorHint = new RegExp(`^(?:${dimension})$`);
|
||||
const regDimension = new RegExp(`(?:\\s+(?:${dimension})){1,2}$`);
|
||||
const arr = [];
|
||||
for (const item of list) {
|
||||
if (isString(item)) {
|
||||
if (regColorHint.test(item)) {
|
||||
arr.push("hint");
|
||||
} else {
|
||||
const color = item.replace(regDimension, "");
|
||||
if (isColor(color, opt)) {
|
||||
arr.push("color");
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const value = arr.join(",");
|
||||
return /^color(?:,(?:hint,)?color)+$/.test(value);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
const parseGradient = (value, opt = {}) => {
|
||||
if (isString(value)) {
|
||||
value = value.trim();
|
||||
const cacheKey = createCacheKey(
|
||||
{
|
||||
namespace: NAMESPACE,
|
||||
name: "parseGradient",
|
||||
value
|
||||
},
|
||||
opt
|
||||
);
|
||||
const cachedResult = getCache(cacheKey);
|
||||
if (cachedResult instanceof CacheItem) {
|
||||
if (cachedResult.isNull) {
|
||||
return null;
|
||||
}
|
||||
return cachedResult.item;
|
||||
}
|
||||
const type = getGradientType(value);
|
||||
const gradValue = value.replace(REG_GRAD, "").replace(/\)$/, "");
|
||||
if (type && gradValue) {
|
||||
const [lineOrColorStop = "", ...colorStops] = splitValue(gradValue, {
|
||||
delimiter: ","
|
||||
});
|
||||
const dimension = /^(?:repeating-)?conic-gradient$/.test(type) ? DIM_ANGLE_PCT : DIM_LEN_PCT;
|
||||
const regDimension = new RegExp(`(?:\\s+(?:${dimension})){1,2}$`);
|
||||
let isColorStop = false;
|
||||
if (regDimension.test(lineOrColorStop)) {
|
||||
const colorStop = lineOrColorStop.replace(regDimension, "");
|
||||
if (isColor(colorStop, opt)) {
|
||||
isColorStop = true;
|
||||
}
|
||||
} else if (isColor(lineOrColorStop, opt)) {
|
||||
isColorStop = true;
|
||||
}
|
||||
if (isColorStop) {
|
||||
colorStops.unshift(lineOrColorStop);
|
||||
const valid = validateColorStopList(colorStops, type, opt);
|
||||
if (valid) {
|
||||
const res = {
|
||||
value,
|
||||
type,
|
||||
colorStopList: colorStops
|
||||
};
|
||||
setCache(cacheKey, res);
|
||||
return res;
|
||||
}
|
||||
} else if (colorStops.length > 1) {
|
||||
const gradientLine = lineOrColorStop;
|
||||
const valid = validateGradientLine(gradientLine, type) && validateColorStopList(colorStops, type, opt);
|
||||
if (valid) {
|
||||
const res = {
|
||||
value,
|
||||
type,
|
||||
gradientLine,
|
||||
colorStopList: colorStops
|
||||
};
|
||||
setCache(cacheKey, res);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
}
|
||||
setCache(cacheKey, null);
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
const isGradient = (value, opt = {}) => {
|
||||
const gradient = parseGradient(value, opt);
|
||||
return gradient !== null;
|
||||
};
|
||||
export {
|
||||
getGradientType,
|
||||
isGradient,
|
||||
parseGradient,
|
||||
validateColorStopList,
|
||||
validateGradientLine
|
||||
};
|
||||
//# sourceMappingURL=css-gradient.js.map
|
||||
+1
File diff suppressed because one or more lines are too long
+31
@@ -0,0 +1,31 @@
|
||||
import { CSSToken } from '@csstools/css-tokenizer';
|
||||
import { NullObject } from './cache.js';
|
||||
import { Options } from './typedef.js';
|
||||
/**
|
||||
* resolve custom property
|
||||
* @param tokens - CSS tokens
|
||||
* @param [opt] - options
|
||||
* @returns result - [tokens, resolvedValue]
|
||||
*/
|
||||
export declare function resolveCustomProperty(tokens: CSSToken[], opt?: Options): [CSSToken[], string];
|
||||
/**
|
||||
* parse tokens
|
||||
* @param tokens - CSS tokens
|
||||
* @param [opt] - options
|
||||
* @returns parsed tokens
|
||||
*/
|
||||
export declare function parseTokens(tokens: CSSToken[], opt?: Options): string[] | NullObject;
|
||||
/**
|
||||
* resolve CSS var()
|
||||
* @param value - CSS value including var()
|
||||
* @param [opt] - options
|
||||
* @returns resolved value
|
||||
*/
|
||||
export declare function resolveVar(value: string, opt?: Options): string | NullObject;
|
||||
/**
|
||||
* CSS var()
|
||||
* @param value - CSS value including var()
|
||||
* @param [opt] - options
|
||||
* @returns resolved value
|
||||
*/
|
||||
export declare const cssVar: (value: string, opt?: Options) => string;
|
||||
+195
@@ -0,0 +1,195 @@
|
||||
import { TokenType, tokenize } from "@csstools/css-tokenizer";
|
||||
import { createCacheKey, getCache, CacheItem, setCache, NullObject } from "./cache.js";
|
||||
import { isString } from "./common.js";
|
||||
import { cssCalc } from "./css-calc.js";
|
||||
import { isColor } from "./util.js";
|
||||
import { VAL_SPEC, SYN_FN_VAR, FN_VAR, SYN_FN_CALC } from "./constant.js";
|
||||
const {
|
||||
CloseParen: PAREN_CLOSE,
|
||||
Comment: COMMENT,
|
||||
EOF,
|
||||
Ident: IDENT,
|
||||
Whitespace: W_SPACE
|
||||
} = TokenType;
|
||||
const NAMESPACE = "css-var";
|
||||
const REG_FN_CALC = new RegExp(SYN_FN_CALC);
|
||||
const REG_FN_VAR = new RegExp(SYN_FN_VAR);
|
||||
function resolveCustomProperty(tokens, opt = {}) {
|
||||
if (!Array.isArray(tokens)) {
|
||||
throw new TypeError(`${tokens} is not an array.`);
|
||||
}
|
||||
const { customProperty = {} } = opt;
|
||||
const items = [];
|
||||
while (tokens.length) {
|
||||
const token = tokens.shift();
|
||||
if (!Array.isArray(token)) {
|
||||
throw new TypeError(`${token} is not an array.`);
|
||||
}
|
||||
const [type, value] = token;
|
||||
if (type === PAREN_CLOSE) {
|
||||
break;
|
||||
}
|
||||
if (value === FN_VAR) {
|
||||
const [restTokens, item] = resolveCustomProperty(tokens, opt);
|
||||
tokens = restTokens;
|
||||
if (item) {
|
||||
items.push(item);
|
||||
}
|
||||
} else if (type === IDENT) {
|
||||
if (value.startsWith("--")) {
|
||||
let item;
|
||||
if (Object.hasOwnProperty.call(customProperty, value)) {
|
||||
item = customProperty[value];
|
||||
} else if (typeof customProperty.callback === "function") {
|
||||
item = customProperty.callback(value);
|
||||
}
|
||||
if (item) {
|
||||
items.push(item);
|
||||
}
|
||||
} else if (value) {
|
||||
items.push(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
let resolveAsColor = false;
|
||||
if (items.length > 1) {
|
||||
const lastValue = items[items.length - 1];
|
||||
resolveAsColor = isColor(lastValue);
|
||||
}
|
||||
let resolvedValue = "";
|
||||
for (let item of items) {
|
||||
item = item.trim();
|
||||
if (REG_FN_VAR.test(item)) {
|
||||
const resolvedItem = resolveVar(item, opt);
|
||||
if (isString(resolvedItem)) {
|
||||
if (resolveAsColor) {
|
||||
if (isColor(resolvedItem)) {
|
||||
resolvedValue = resolvedItem;
|
||||
}
|
||||
} else {
|
||||
resolvedValue = resolvedItem;
|
||||
}
|
||||
}
|
||||
} else if (REG_FN_CALC.test(item)) {
|
||||
item = cssCalc(item, opt);
|
||||
if (resolveAsColor) {
|
||||
if (isColor(item)) {
|
||||
resolvedValue = item;
|
||||
}
|
||||
} else {
|
||||
resolvedValue = item;
|
||||
}
|
||||
} else if (item && !/^(?:inherit|initial|revert(?:-layer)?|unset)$/.test(item)) {
|
||||
if (resolveAsColor) {
|
||||
if (isColor(item)) {
|
||||
resolvedValue = item;
|
||||
}
|
||||
} else {
|
||||
resolvedValue = item;
|
||||
}
|
||||
}
|
||||
if (resolvedValue) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return [tokens, resolvedValue];
|
||||
}
|
||||
function parseTokens(tokens, opt = {}) {
|
||||
const res = [];
|
||||
while (tokens.length) {
|
||||
const token = tokens.shift();
|
||||
const [type = "", value = ""] = token;
|
||||
if (value === FN_VAR) {
|
||||
const [restTokens, resolvedValue] = resolveCustomProperty(tokens, opt);
|
||||
if (!resolvedValue) {
|
||||
return new NullObject();
|
||||
}
|
||||
tokens = restTokens;
|
||||
res.push(resolvedValue);
|
||||
} else {
|
||||
switch (type) {
|
||||
case PAREN_CLOSE: {
|
||||
if (res.length) {
|
||||
const lastValue = res[res.length - 1];
|
||||
if (lastValue === " ") {
|
||||
res.splice(-1, 1, value);
|
||||
} else {
|
||||
res.push(value);
|
||||
}
|
||||
} else {
|
||||
res.push(value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case W_SPACE: {
|
||||
if (res.length) {
|
||||
const lastValue = res[res.length - 1];
|
||||
if (isString(lastValue) && !lastValue.endsWith("(") && lastValue !== " ") {
|
||||
res.push(value);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
if (type !== COMMENT && type !== EOF) {
|
||||
res.push(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
function resolveVar(value, opt = {}) {
|
||||
const { format = "" } = opt;
|
||||
if (isString(value)) {
|
||||
if (!REG_FN_VAR.test(value) || format === VAL_SPEC) {
|
||||
return value;
|
||||
}
|
||||
value = value.trim();
|
||||
} else {
|
||||
throw new TypeError(`${value} is not a string.`);
|
||||
}
|
||||
const cacheKey = createCacheKey(
|
||||
{
|
||||
namespace: NAMESPACE,
|
||||
name: "resolveVar",
|
||||
value
|
||||
},
|
||||
opt
|
||||
);
|
||||
const cachedResult = getCache(cacheKey);
|
||||
if (cachedResult instanceof CacheItem) {
|
||||
if (cachedResult.isNull) {
|
||||
return cachedResult;
|
||||
}
|
||||
return cachedResult.item;
|
||||
}
|
||||
const tokens = tokenize({ css: value });
|
||||
const values = parseTokens(tokens, opt);
|
||||
if (Array.isArray(values)) {
|
||||
let color = values.join("");
|
||||
if (REG_FN_CALC.test(color)) {
|
||||
color = cssCalc(color, opt);
|
||||
}
|
||||
setCache(cacheKey, color);
|
||||
return color;
|
||||
} else {
|
||||
setCache(cacheKey, null);
|
||||
return new NullObject();
|
||||
}
|
||||
}
|
||||
const cssVar = (value, opt = {}) => {
|
||||
const resolvedValue = resolveVar(value, opt);
|
||||
if (isString(resolvedValue)) {
|
||||
return resolvedValue;
|
||||
}
|
||||
return "";
|
||||
};
|
||||
export {
|
||||
cssVar,
|
||||
parseTokens,
|
||||
resolveCustomProperty,
|
||||
resolveVar
|
||||
};
|
||||
//# sourceMappingURL=css-var.js.map
|
||||
+1
File diff suppressed because one or more lines are too long
+29
@@ -0,0 +1,29 @@
|
||||
import { CSSToken } from '@csstools/css-tokenizer';
|
||||
import { NullObject } from './cache.js';
|
||||
import { ColorChannels, Options, StringColorChannels } from './typedef.js';
|
||||
/**
|
||||
* @type NumberOrStringColorChannels - color channel
|
||||
*/
|
||||
type NumberOrStringColorChannels = ColorChannels & StringColorChannels;
|
||||
/**
|
||||
* resolve relative color channels
|
||||
* @param tokens - CSS tokens
|
||||
* @param [opt] - options
|
||||
* @returns resolved color channels
|
||||
*/
|
||||
export declare function resolveColorChannels(tokens: CSSToken[], opt?: Options): NumberOrStringColorChannels | NullObject;
|
||||
/**
|
||||
* extract origin color
|
||||
* @param value - CSS color value
|
||||
* @param [opt] - options
|
||||
* @returns origin color value
|
||||
*/
|
||||
export declare function extractOriginColor(value: string, opt?: Options): string | NullObject;
|
||||
/**
|
||||
* resolve relative color
|
||||
* @param value - CSS relative color value
|
||||
* @param [opt] - options
|
||||
* @returns resolved value
|
||||
*/
|
||||
export declare function resolveRelativeColor(value: string, opt?: Options): string | NullObject;
|
||||
export {};
|
||||
+487
@@ -0,0 +1,487 @@
|
||||
import { SyntaxFlag, color } from "@csstools/css-color-parser";
|
||||
import { parseComponentValue } from "@csstools/css-parser-algorithms";
|
||||
import { TokenType, tokenize } from "@csstools/css-tokenizer";
|
||||
import { createCacheKey, getCache, CacheItem, NullObject, setCache } from "./cache.js";
|
||||
import { convertColorToRgb, NAMED_COLORS } from "./color.js";
|
||||
import { isString, isStringOrNumber } from "./common.js";
|
||||
import { resolveDimension, serializeCalc } from "./css-calc.js";
|
||||
import { resolveColor } from "./resolve.js";
|
||||
import { roundToPrecision } from "./util.js";
|
||||
import { VAL_SPEC, FN_VAR, NONE, SYN_FN_VAR, FN_REL, CS_LAB, CS_LCH, FN_REL_CAPT, SYN_COLOR_TYPE, SYN_MIX, SYN_FN_MATH_START } from "./constant.js";
|
||||
const {
|
||||
CloseParen: PAREN_CLOSE,
|
||||
Comment: COMMENT,
|
||||
Dimension: DIM,
|
||||
EOF,
|
||||
Function: FUNC,
|
||||
Ident: IDENT,
|
||||
Number: NUM,
|
||||
OpenParen: PAREN_OPEN,
|
||||
Percentage: PCT,
|
||||
Whitespace: W_SPACE
|
||||
} = TokenType;
|
||||
const { HasNoneKeywords: KEY_NONE } = SyntaxFlag;
|
||||
const NAMESPACE = "relative-color";
|
||||
const OCT = 8;
|
||||
const DEC = 10;
|
||||
const HEX = 16;
|
||||
const MAX_PCT = 100;
|
||||
const MAX_RGB = 255;
|
||||
const REG_COLOR_CAPT = new RegExp(
|
||||
`^${FN_REL}(${SYN_COLOR_TYPE}|${SYN_MIX})\\s+`
|
||||
);
|
||||
const REG_CS_HSL = /(?:hsla?|hwb)$/;
|
||||
const REG_CS_CIE = new RegExp(`^(?:${CS_LAB}|${CS_LCH})$`);
|
||||
const REG_FN_MATH_START = new RegExp(SYN_FN_MATH_START);
|
||||
const REG_FN_REL = new RegExp(FN_REL);
|
||||
const REG_FN_REL_CAPT = new RegExp(`^${FN_REL_CAPT}`);
|
||||
const REG_FN_REL_START = new RegExp(`^${FN_REL}`);
|
||||
const REG_FN_VAR = new RegExp(SYN_FN_VAR);
|
||||
function resolveColorChannels(tokens, opt = {}) {
|
||||
if (!Array.isArray(tokens)) {
|
||||
throw new TypeError(`${tokens} is not an array.`);
|
||||
}
|
||||
const { colorSpace = "", format = "" } = opt;
|
||||
const colorChannels = /* @__PURE__ */ new Map([
|
||||
["color", ["r", "g", "b", "alpha"]],
|
||||
["hsl", ["h", "s", "l", "alpha"]],
|
||||
["hsla", ["h", "s", "l", "alpha"]],
|
||||
["hwb", ["h", "w", "b", "alpha"]],
|
||||
["lab", ["l", "a", "b", "alpha"]],
|
||||
["lch", ["l", "c", "h", "alpha"]],
|
||||
["oklab", ["l", "a", "b", "alpha"]],
|
||||
["oklch", ["l", "c", "h", "alpha"]],
|
||||
["rgb", ["r", "g", "b", "alpha"]],
|
||||
["rgba", ["r", "g", "b", "alpha"]]
|
||||
]);
|
||||
const colorChannel = colorChannels.get(colorSpace);
|
||||
if (!colorChannel) {
|
||||
return new NullObject();
|
||||
}
|
||||
const mathFunc = /* @__PURE__ */ new Set();
|
||||
const channels = [[], [], [], []];
|
||||
let i = 0;
|
||||
let nest = 0;
|
||||
let func = false;
|
||||
while (tokens.length) {
|
||||
const token = tokens.shift();
|
||||
if (!Array.isArray(token)) {
|
||||
throw new TypeError(`${token} is not an array.`);
|
||||
}
|
||||
const [type, value, , , detail] = token;
|
||||
const channel = channels[i];
|
||||
if (Array.isArray(channel)) {
|
||||
switch (type) {
|
||||
case DIM: {
|
||||
const resolvedValue = resolveDimension(token, opt);
|
||||
if (isString(resolvedValue)) {
|
||||
channel.push(resolvedValue);
|
||||
} else {
|
||||
channel.push(value);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FUNC: {
|
||||
channel.push(value);
|
||||
func = true;
|
||||
nest++;
|
||||
if (REG_FN_MATH_START.test(value)) {
|
||||
mathFunc.add(nest);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IDENT: {
|
||||
if (!colorChannel.includes(value)) {
|
||||
return new NullObject();
|
||||
}
|
||||
channel.push(value);
|
||||
if (!func) {
|
||||
i++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NUM: {
|
||||
channel.push(Number(detail == null ? void 0 : detail.value));
|
||||
if (!func) {
|
||||
i++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PAREN_OPEN: {
|
||||
channel.push(value);
|
||||
nest++;
|
||||
break;
|
||||
}
|
||||
case PAREN_CLOSE: {
|
||||
if (func) {
|
||||
const lastValue = channel[channel.length - 1];
|
||||
if (lastValue === " ") {
|
||||
channel.splice(-1, 1, value);
|
||||
} else {
|
||||
channel.push(value);
|
||||
}
|
||||
if (mathFunc.has(nest)) {
|
||||
mathFunc.delete(nest);
|
||||
}
|
||||
nest--;
|
||||
if (nest === 0) {
|
||||
func = false;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case PCT: {
|
||||
channel.push(Number(detail == null ? void 0 : detail.value) / MAX_PCT);
|
||||
if (!func) {
|
||||
i++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case W_SPACE: {
|
||||
if (channel.length && func) {
|
||||
const lastValue = channel[channel.length - 1];
|
||||
if (typeof lastValue === "number") {
|
||||
channel.push(value);
|
||||
} else if (isString(lastValue) && !lastValue.endsWith("(") && lastValue !== " ") {
|
||||
channel.push(value);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
if (type !== COMMENT && type !== EOF && func) {
|
||||
channel.push(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const channelValues = [];
|
||||
for (const channel of channels) {
|
||||
if (channel.length === 1) {
|
||||
const [resolvedValue] = channel;
|
||||
if (isStringOrNumber(resolvedValue)) {
|
||||
channelValues.push(resolvedValue);
|
||||
}
|
||||
} else if (channel.length) {
|
||||
const resolvedValue = serializeCalc(channel.join(""), {
|
||||
format
|
||||
});
|
||||
channelValues.push(resolvedValue);
|
||||
}
|
||||
}
|
||||
return channelValues;
|
||||
}
|
||||
function extractOriginColor(value, opt = {}) {
|
||||
const { currentColor = "", format = "" } = opt;
|
||||
if (isString(value)) {
|
||||
value = value.toLowerCase().trim();
|
||||
if (!value) {
|
||||
return new NullObject();
|
||||
}
|
||||
if (!REG_FN_REL_START.test(value)) {
|
||||
return value;
|
||||
}
|
||||
} else {
|
||||
return new NullObject();
|
||||
}
|
||||
const cacheKey = createCacheKey(
|
||||
{
|
||||
namespace: NAMESPACE,
|
||||
name: "extractOriginColor",
|
||||
value
|
||||
},
|
||||
opt
|
||||
);
|
||||
const cachedResult = getCache(cacheKey);
|
||||
if (cachedResult instanceof CacheItem) {
|
||||
if (cachedResult.isNull) {
|
||||
return cachedResult;
|
||||
}
|
||||
return cachedResult.item;
|
||||
}
|
||||
if (/currentcolor/.test(value)) {
|
||||
if (currentColor) {
|
||||
value = value.replace(/currentcolor/g, currentColor);
|
||||
} else {
|
||||
setCache(cacheKey, null);
|
||||
return new NullObject();
|
||||
}
|
||||
}
|
||||
let colorSpace = "";
|
||||
if (REG_FN_REL_CAPT.test(value)) {
|
||||
[, colorSpace] = value.match(REG_FN_REL_CAPT);
|
||||
}
|
||||
opt.colorSpace = colorSpace;
|
||||
if (REG_COLOR_CAPT.test(value)) {
|
||||
const [, originColor] = value.match(REG_COLOR_CAPT);
|
||||
const [, restValue] = value.split(originColor);
|
||||
if (/^[a-z]+$/.test(originColor)) {
|
||||
if (!/^transparent$/.test(originColor) && !Object.prototype.hasOwnProperty.call(NAMED_COLORS, originColor)) {
|
||||
setCache(cacheKey, null);
|
||||
return new NullObject();
|
||||
}
|
||||
} else if (format === VAL_SPEC) {
|
||||
const resolvedOriginColor = resolveColor(originColor, opt);
|
||||
if (isString(resolvedOriginColor)) {
|
||||
value = value.replace(originColor, resolvedOriginColor);
|
||||
}
|
||||
}
|
||||
if (format === VAL_SPEC) {
|
||||
const tokens = tokenize({ css: restValue });
|
||||
const channelValues = resolveColorChannels(tokens, opt);
|
||||
if (channelValues instanceof NullObject) {
|
||||
setCache(cacheKey, null);
|
||||
return channelValues;
|
||||
}
|
||||
const [v1, v2, v3, v4] = channelValues;
|
||||
let channelValue = "";
|
||||
if (isStringOrNumber(v4)) {
|
||||
channelValue = ` ${v1} ${v2} ${v3} / ${v4})`;
|
||||
} else {
|
||||
channelValue = ` ${channelValues.join(" ")})`;
|
||||
}
|
||||
if (restValue !== channelValue) {
|
||||
value = value.replace(restValue, channelValue);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const [, restValue] = value.split(REG_FN_REL_START);
|
||||
const tokens = tokenize({ css: restValue });
|
||||
const originColor = [];
|
||||
let nest = 0;
|
||||
while (tokens.length) {
|
||||
const [type, tokenValue] = tokens.shift();
|
||||
switch (type) {
|
||||
case FUNC:
|
||||
case PAREN_OPEN: {
|
||||
originColor.push(tokenValue);
|
||||
nest++;
|
||||
break;
|
||||
}
|
||||
case PAREN_CLOSE: {
|
||||
const lastValue = originColor[originColor.length - 1];
|
||||
if (lastValue === " ") {
|
||||
originColor.splice(-1, 1, tokenValue);
|
||||
} else if (isString(lastValue)) {
|
||||
originColor.push(tokenValue);
|
||||
}
|
||||
nest--;
|
||||
break;
|
||||
}
|
||||
case W_SPACE: {
|
||||
const lastValue = originColor[originColor.length - 1];
|
||||
if (isString(lastValue) && !lastValue.endsWith("(") && lastValue !== " ") {
|
||||
originColor.push(tokenValue);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
if (type !== COMMENT && type !== EOF) {
|
||||
originColor.push(tokenValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (nest === 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
const resolvedOriginColor = resolveRelativeColor(
|
||||
originColor.join("").trim(),
|
||||
opt
|
||||
);
|
||||
if (resolvedOriginColor instanceof NullObject) {
|
||||
setCache(cacheKey, null);
|
||||
return resolvedOriginColor;
|
||||
}
|
||||
const channelValues = resolveColorChannels(tokens, opt);
|
||||
if (channelValues instanceof NullObject) {
|
||||
setCache(cacheKey, null);
|
||||
return channelValues;
|
||||
}
|
||||
const [v1, v2, v3, v4] = channelValues;
|
||||
let channelValue = "";
|
||||
if (isStringOrNumber(v4)) {
|
||||
channelValue = ` ${v1} ${v2} ${v3} / ${v4})`;
|
||||
} else {
|
||||
channelValue = ` ${channelValues.join(" ")})`;
|
||||
}
|
||||
value = value.replace(restValue, `${resolvedOriginColor}${channelValue}`);
|
||||
}
|
||||
setCache(cacheKey, value);
|
||||
return value;
|
||||
}
|
||||
function resolveRelativeColor(value, opt = {}) {
|
||||
const { format = "" } = opt;
|
||||
if (isString(value)) {
|
||||
if (REG_FN_VAR.test(value)) {
|
||||
if (format === VAL_SPEC) {
|
||||
return value;
|
||||
} else {
|
||||
throw new SyntaxError(`Unexpected token ${FN_VAR} found.`);
|
||||
}
|
||||
} else if (!REG_FN_REL.test(value)) {
|
||||
return value;
|
||||
}
|
||||
value = value.toLowerCase().trim();
|
||||
} else {
|
||||
throw new TypeError(`${value} is not a string.`);
|
||||
}
|
||||
const cacheKey = createCacheKey(
|
||||
{
|
||||
namespace: NAMESPACE,
|
||||
name: "resolveRelativeColor",
|
||||
value
|
||||
},
|
||||
opt
|
||||
);
|
||||
const cachedResult = getCache(cacheKey);
|
||||
if (cachedResult instanceof CacheItem) {
|
||||
if (cachedResult.isNull) {
|
||||
return cachedResult;
|
||||
}
|
||||
return cachedResult.item;
|
||||
}
|
||||
const originColor = extractOriginColor(value, opt);
|
||||
if (originColor instanceof NullObject) {
|
||||
setCache(cacheKey, null);
|
||||
return originColor;
|
||||
}
|
||||
value = originColor;
|
||||
if (format === VAL_SPEC) {
|
||||
if (value.startsWith("rgba(")) {
|
||||
value = value.replace(/^rgba\(/, "rgb(");
|
||||
} else if (value.startsWith("hsla(")) {
|
||||
value = value.replace(/^hsla\(/, "hsl(");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
const tokens = tokenize({ css: value });
|
||||
const components = parseComponentValue(tokens);
|
||||
const parsedComponents = color(components);
|
||||
if (!parsedComponents) {
|
||||
setCache(cacheKey, null);
|
||||
return new NullObject();
|
||||
}
|
||||
const {
|
||||
alpha: alphaComponent,
|
||||
channels: channelsComponent,
|
||||
colorNotation,
|
||||
syntaxFlags
|
||||
} = parsedComponents;
|
||||
let alpha;
|
||||
if (Number.isNaN(Number(alphaComponent))) {
|
||||
if (syntaxFlags instanceof Set && syntaxFlags.has(KEY_NONE)) {
|
||||
alpha = NONE;
|
||||
} else {
|
||||
alpha = 0;
|
||||
}
|
||||
} else {
|
||||
alpha = roundToPrecision(Number(alphaComponent), OCT);
|
||||
}
|
||||
let v1;
|
||||
let v2;
|
||||
let v3;
|
||||
[v1, v2, v3] = channelsComponent;
|
||||
let resolvedValue;
|
||||
if (REG_CS_CIE.test(colorNotation)) {
|
||||
const hasNone = syntaxFlags instanceof Set && syntaxFlags.has(KEY_NONE);
|
||||
if (Number.isNaN(v1)) {
|
||||
if (hasNone) {
|
||||
v1 = NONE;
|
||||
} else {
|
||||
v1 = 0;
|
||||
}
|
||||
} else {
|
||||
v1 = roundToPrecision(v1, HEX);
|
||||
}
|
||||
if (Number.isNaN(v2)) {
|
||||
if (hasNone) {
|
||||
v2 = NONE;
|
||||
} else {
|
||||
v2 = 0;
|
||||
}
|
||||
} else {
|
||||
v2 = roundToPrecision(v2, HEX);
|
||||
}
|
||||
if (Number.isNaN(v3)) {
|
||||
if (hasNone) {
|
||||
v3 = NONE;
|
||||
} else {
|
||||
v3 = 0;
|
||||
}
|
||||
} else {
|
||||
v3 = roundToPrecision(v3, HEX);
|
||||
}
|
||||
if (alpha === 1) {
|
||||
resolvedValue = `${colorNotation}(${v1} ${v2} ${v3})`;
|
||||
} else {
|
||||
resolvedValue = `${colorNotation}(${v1} ${v2} ${v3} / ${alpha})`;
|
||||
}
|
||||
} else if (REG_CS_HSL.test(colorNotation)) {
|
||||
if (Number.isNaN(v1)) {
|
||||
v1 = 0;
|
||||
}
|
||||
if (Number.isNaN(v2)) {
|
||||
v2 = 0;
|
||||
}
|
||||
if (Number.isNaN(v3)) {
|
||||
v3 = 0;
|
||||
}
|
||||
let [r, g, b] = convertColorToRgb(
|
||||
`${colorNotation}(${v1} ${v2} ${v3} / ${alpha})`
|
||||
);
|
||||
r = roundToPrecision(r / MAX_RGB, DEC);
|
||||
g = roundToPrecision(g / MAX_RGB, DEC);
|
||||
b = roundToPrecision(b / MAX_RGB, DEC);
|
||||
if (alpha === 1) {
|
||||
resolvedValue = `color(srgb ${r} ${g} ${b})`;
|
||||
} else {
|
||||
resolvedValue = `color(srgb ${r} ${g} ${b} / ${alpha})`;
|
||||
}
|
||||
} else {
|
||||
const cs = colorNotation === "rgb" ? "srgb" : colorNotation;
|
||||
const hasNone = syntaxFlags instanceof Set && syntaxFlags.has(KEY_NONE);
|
||||
if (Number.isNaN(v1)) {
|
||||
if (hasNone) {
|
||||
v1 = NONE;
|
||||
} else {
|
||||
v1 = 0;
|
||||
}
|
||||
} else {
|
||||
v1 = roundToPrecision(v1, DEC);
|
||||
}
|
||||
if (Number.isNaN(v2)) {
|
||||
if (hasNone) {
|
||||
v2 = NONE;
|
||||
} else {
|
||||
v2 = 0;
|
||||
}
|
||||
} else {
|
||||
v2 = roundToPrecision(v2, DEC);
|
||||
}
|
||||
if (Number.isNaN(v3)) {
|
||||
if (hasNone) {
|
||||
v3 = NONE;
|
||||
} else {
|
||||
v3 = 0;
|
||||
}
|
||||
} else {
|
||||
v3 = roundToPrecision(v3, DEC);
|
||||
}
|
||||
if (alpha === 1) {
|
||||
resolvedValue = `color(${cs} ${v1} ${v2} ${v3})`;
|
||||
} else {
|
||||
resolvedValue = `color(${cs} ${v1} ${v2} ${v3} / ${alpha})`;
|
||||
}
|
||||
}
|
||||
setCache(cacheKey, resolvedValue);
|
||||
return resolvedValue;
|
||||
}
|
||||
export {
|
||||
extractOriginColor,
|
||||
resolveColorChannels,
|
||||
resolveRelativeColor
|
||||
};
|
||||
//# sourceMappingURL=relative-color.js.map
|
||||
+1
File diff suppressed because one or more lines are too long
+52
@@ -0,0 +1,52 @@
|
||||
import { NullObject } from './cache.js';
|
||||
import { Options } from './typedef.js';
|
||||
/**
|
||||
* resolve color
|
||||
* @param value - CSS color value
|
||||
* @param [opt] - options
|
||||
* @returns resolved color
|
||||
*/
|
||||
export declare const resolveColor: (value: string, opt?: Options) => string | NullObject;
|
||||
/**
|
||||
* resolve CSS color
|
||||
* @param value
|
||||
* - CSS color value
|
||||
* - system colors are not supported
|
||||
* @param [opt] - options
|
||||
* @param [opt.currentColor]
|
||||
* - color to use for `currentcolor` keyword
|
||||
* - if omitted, it will be treated as a missing color
|
||||
* i.e. `rgb(none none none / none)`
|
||||
* @param [opt.customProperty]
|
||||
* - custom properties
|
||||
* - pair of `--` prefixed property name and value,
|
||||
* e.g. `customProperty: { '--some-color': '#0000ff' }`
|
||||
* - and/or `callback` function to get the value of the custom property,
|
||||
* e.g. `customProperty: { callback: someDeclaration.getPropertyValue }`
|
||||
* @param [opt.dimension]
|
||||
* - dimension, convert relative length to pixels
|
||||
* - pair of unit and it's value as a number in pixels,
|
||||
* e.g. `dimension: { em: 12, rem: 16, vw: 10.26 }`
|
||||
* - and/or `callback` function to get the value as a number in pixels,
|
||||
* e.g. `dimension: { callback: convertUnitToPixel }`
|
||||
* @param [opt.format]
|
||||
* - output format, one of below
|
||||
* - `computedValue` (default), [computed value][139] of the color
|
||||
* - `specifiedValue`, [specified value][140] of the color
|
||||
* - `hex`, hex color notation, i.e. `rrggbb`
|
||||
* - `hexAlpha`, hex color notation with alpha channel, i.e. `#rrggbbaa`
|
||||
* @returns
|
||||
* - one of rgba?(), #rrggbb(aa)?, color-name, '(empty-string)',
|
||||
* color(color-space r g b / alpha), color(color-space x y z / alpha),
|
||||
* lab(l a b / alpha), lch(l c h / alpha), oklab(l a b / alpha),
|
||||
* oklch(l c h / alpha), null
|
||||
* - in `computedValue`, values are numbers, however `rgb()` values are
|
||||
* integers
|
||||
* - in `specifiedValue`, returns `empty string` for unknown and/or invalid
|
||||
* color
|
||||
* - in `hex`, returns `null` for `transparent`, and also returns `null` if
|
||||
* any of `r`, `g`, `b`, `alpha` is not a number
|
||||
* - in `hexAlpha`, returns `#00000000` for `transparent`,
|
||||
* however returns `null` if any of `r`, `g`, `b`, `alpha` is not a number
|
||||
*/
|
||||
export declare const resolve: (value: string, opt?: Options) => string | null;
|
||||
+288
@@ -0,0 +1,288 @@
|
||||
import { NullObject, createCacheKey, getCache, CacheItem, setCache } from "./cache.js";
|
||||
import { resolveColorMix, resolveColorFunc, resolveColorValue, convertRgbToHex } from "./color.js";
|
||||
import { isString } from "./common.js";
|
||||
import { cssCalc } from "./css-calc.js";
|
||||
import { resolveVar } from "./css-var.js";
|
||||
import { resolveRelativeColor } from "./relative-color.js";
|
||||
import { VAL_COMP, VAL_SPEC, FN_MIX, FN_COLOR, SYN_FN_VAR, SYN_FN_REL, SYN_FN_CALC } from "./constant.js";
|
||||
const NAMESPACE = "resolve";
|
||||
const RGB_TRANSPARENT = "rgba(0, 0, 0, 0)";
|
||||
const REG_FN_CALC = new RegExp(SYN_FN_CALC);
|
||||
const REG_FN_REL = new RegExp(SYN_FN_REL);
|
||||
const REG_FN_VAR = new RegExp(SYN_FN_VAR);
|
||||
const resolveColor = (value, opt = {}) => {
|
||||
if (isString(value)) {
|
||||
value = value.trim();
|
||||
} else {
|
||||
throw new TypeError(`${value} is not a string.`);
|
||||
}
|
||||
const { currentColor = "", format = VAL_COMP, nullable = false } = opt;
|
||||
const cacheKey = createCacheKey(
|
||||
{
|
||||
namespace: NAMESPACE,
|
||||
name: "resolve",
|
||||
value
|
||||
},
|
||||
opt
|
||||
);
|
||||
const cachedResult = getCache(cacheKey);
|
||||
if (cachedResult instanceof CacheItem) {
|
||||
if (cachedResult.isNull) {
|
||||
return cachedResult;
|
||||
}
|
||||
return cachedResult.item;
|
||||
}
|
||||
if (REG_FN_VAR.test(value)) {
|
||||
if (format === VAL_SPEC) {
|
||||
setCache(cacheKey, value);
|
||||
return value;
|
||||
}
|
||||
const resolvedValue = resolveVar(value, opt);
|
||||
if (resolvedValue instanceof NullObject) {
|
||||
switch (format) {
|
||||
case "hex":
|
||||
case "hexAlpha": {
|
||||
setCache(cacheKey, resolvedValue);
|
||||
return resolvedValue;
|
||||
}
|
||||
default: {
|
||||
if (nullable) {
|
||||
setCache(cacheKey, resolvedValue);
|
||||
return resolvedValue;
|
||||
}
|
||||
const res2 = RGB_TRANSPARENT;
|
||||
setCache(cacheKey, res2);
|
||||
return res2;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
value = resolvedValue;
|
||||
}
|
||||
}
|
||||
if (opt.format !== format) {
|
||||
opt.format = format;
|
||||
}
|
||||
value = value.toLowerCase();
|
||||
if (REG_FN_REL.test(value)) {
|
||||
const resolvedValue = resolveRelativeColor(value, opt);
|
||||
if (format === VAL_COMP) {
|
||||
let res2;
|
||||
if (resolvedValue instanceof NullObject) {
|
||||
if (nullable) {
|
||||
res2 = resolvedValue;
|
||||
} else {
|
||||
res2 = RGB_TRANSPARENT;
|
||||
}
|
||||
} else {
|
||||
res2 = resolvedValue;
|
||||
}
|
||||
setCache(cacheKey, res2);
|
||||
return res2;
|
||||
}
|
||||
if (format === VAL_SPEC) {
|
||||
let res2 = "";
|
||||
if (resolvedValue instanceof NullObject) {
|
||||
res2 = "";
|
||||
} else {
|
||||
res2 = resolvedValue;
|
||||
}
|
||||
setCache(cacheKey, res2);
|
||||
return res2;
|
||||
}
|
||||
if (resolvedValue instanceof NullObject) {
|
||||
value = "";
|
||||
} else {
|
||||
value = resolvedValue;
|
||||
}
|
||||
}
|
||||
if (REG_FN_CALC.test(value)) {
|
||||
value = cssCalc(value, opt);
|
||||
}
|
||||
let cs = "";
|
||||
let r = NaN;
|
||||
let g = NaN;
|
||||
let b = NaN;
|
||||
let alpha = NaN;
|
||||
if (value === "transparent") {
|
||||
switch (format) {
|
||||
case VAL_SPEC: {
|
||||
setCache(cacheKey, value);
|
||||
return value;
|
||||
}
|
||||
case "hex": {
|
||||
setCache(cacheKey, null);
|
||||
return new NullObject();
|
||||
}
|
||||
case "hexAlpha": {
|
||||
const res2 = "#00000000";
|
||||
setCache(cacheKey, res2);
|
||||
return res2;
|
||||
}
|
||||
case VAL_COMP:
|
||||
default: {
|
||||
const res2 = RGB_TRANSPARENT;
|
||||
setCache(cacheKey, res2);
|
||||
return res2;
|
||||
}
|
||||
}
|
||||
} else if (value === "currentcolor") {
|
||||
if (format === VAL_SPEC) {
|
||||
setCache(cacheKey, value);
|
||||
return value;
|
||||
}
|
||||
if (currentColor) {
|
||||
let resolvedValue;
|
||||
if (currentColor.startsWith(FN_MIX)) {
|
||||
resolvedValue = resolveColorMix(currentColor, opt);
|
||||
} else if (currentColor.startsWith(FN_COLOR)) {
|
||||
resolvedValue = resolveColorFunc(currentColor, opt);
|
||||
} else {
|
||||
resolvedValue = resolveColorValue(currentColor, opt);
|
||||
}
|
||||
if (resolvedValue instanceof NullObject) {
|
||||
setCache(cacheKey, resolvedValue);
|
||||
return resolvedValue;
|
||||
}
|
||||
[cs, r, g, b, alpha] = resolvedValue;
|
||||
} else if (format === VAL_COMP) {
|
||||
const res2 = RGB_TRANSPARENT;
|
||||
setCache(cacheKey, res2);
|
||||
return res2;
|
||||
}
|
||||
} else if (format === VAL_SPEC) {
|
||||
if (value.startsWith(FN_MIX)) {
|
||||
const res2 = resolveColorMix(value, opt);
|
||||
setCache(cacheKey, res2);
|
||||
return res2;
|
||||
} else if (value.startsWith(FN_COLOR)) {
|
||||
const [scs, rr, gg, bb, aa] = resolveColorFunc(
|
||||
value,
|
||||
opt
|
||||
);
|
||||
let res2 = "";
|
||||
if (aa === 1) {
|
||||
res2 = `color(${scs} ${rr} ${gg} ${bb})`;
|
||||
} else {
|
||||
res2 = `color(${scs} ${rr} ${gg} ${bb} / ${aa})`;
|
||||
}
|
||||
setCache(cacheKey, res2);
|
||||
return res2;
|
||||
} else {
|
||||
const rgb = resolveColorValue(value, opt);
|
||||
if (isString(rgb)) {
|
||||
setCache(cacheKey, rgb);
|
||||
return rgb;
|
||||
}
|
||||
const [scs, rr, gg, bb, aa] = rgb;
|
||||
let res2 = "";
|
||||
if (scs === "rgb") {
|
||||
if (aa === 1) {
|
||||
res2 = `${scs}(${rr}, ${gg}, ${bb})`;
|
||||
} else {
|
||||
res2 = `${scs}a(${rr}, ${gg}, ${bb}, ${aa})`;
|
||||
}
|
||||
} else if (aa === 1) {
|
||||
res2 = `${scs}(${rr} ${gg} ${bb})`;
|
||||
} else {
|
||||
res2 = `${scs}(${rr} ${gg} ${bb} / ${aa})`;
|
||||
}
|
||||
setCache(cacheKey, res2);
|
||||
return res2;
|
||||
}
|
||||
} else if (value.startsWith(FN_MIX)) {
|
||||
if (/currentcolor/.test(value)) {
|
||||
if (currentColor) {
|
||||
value = value.replace(/currentcolor/g, currentColor);
|
||||
}
|
||||
}
|
||||
if (/transparent/.test(value)) {
|
||||
value = value.replace(/transparent/g, RGB_TRANSPARENT);
|
||||
}
|
||||
const resolvedValue = resolveColorMix(value, opt);
|
||||
if (resolvedValue instanceof NullObject) {
|
||||
setCache(cacheKey, resolvedValue);
|
||||
return resolvedValue;
|
||||
}
|
||||
[cs, r, g, b, alpha] = resolvedValue;
|
||||
} else if (value.startsWith(FN_COLOR)) {
|
||||
const resolvedValue = resolveColorFunc(value, opt);
|
||||
if (resolvedValue instanceof NullObject) {
|
||||
setCache(cacheKey, resolvedValue);
|
||||
return resolvedValue;
|
||||
}
|
||||
[cs, r, g, b, alpha] = resolvedValue;
|
||||
} else if (value) {
|
||||
const resolvedValue = resolveColorValue(value, opt);
|
||||
if (resolvedValue instanceof NullObject) {
|
||||
setCache(cacheKey, resolvedValue);
|
||||
return resolvedValue;
|
||||
}
|
||||
[cs, r, g, b, alpha] = resolvedValue;
|
||||
}
|
||||
let res = "";
|
||||
switch (format) {
|
||||
case "hex": {
|
||||
if (Number.isNaN(r) || Number.isNaN(g) || Number.isNaN(b) || Number.isNaN(alpha) || alpha === 0) {
|
||||
setCache(cacheKey, null);
|
||||
return new NullObject();
|
||||
}
|
||||
res = convertRgbToHex([r, g, b, 1]);
|
||||
break;
|
||||
}
|
||||
case "hexAlpha": {
|
||||
if (Number.isNaN(r) || Number.isNaN(g) || Number.isNaN(b) || Number.isNaN(alpha)) {
|
||||
setCache(cacheKey, null);
|
||||
return new NullObject();
|
||||
}
|
||||
res = convertRgbToHex([r, g, b, alpha]);
|
||||
break;
|
||||
}
|
||||
case VAL_COMP:
|
||||
default: {
|
||||
switch (cs) {
|
||||
case "rgb": {
|
||||
if (alpha === 1) {
|
||||
res = `${cs}(${r}, ${g}, ${b})`;
|
||||
} else {
|
||||
res = `${cs}a(${r}, ${g}, ${b}, ${alpha})`;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "lab":
|
||||
case "lch":
|
||||
case "oklab":
|
||||
case "oklch": {
|
||||
if (alpha === 1) {
|
||||
res = `${cs}(${r} ${g} ${b})`;
|
||||
} else {
|
||||
res = `${cs}(${r} ${g} ${b} / ${alpha})`;
|
||||
}
|
||||
break;
|
||||
}
|
||||
// color()
|
||||
default: {
|
||||
if (alpha === 1) {
|
||||
res = `color(${cs} ${r} ${g} ${b})`;
|
||||
} else {
|
||||
res = `color(${cs} ${r} ${g} ${b} / ${alpha})`;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
setCache(cacheKey, res);
|
||||
return res;
|
||||
};
|
||||
const resolve = (value, opt = {}) => {
|
||||
opt.nullable = false;
|
||||
const resolvedValue = resolveColor(value, opt);
|
||||
if (resolvedValue instanceof NullObject) {
|
||||
return null;
|
||||
}
|
||||
return resolvedValue;
|
||||
};
|
||||
export {
|
||||
resolve,
|
||||
resolveColor
|
||||
};
|
||||
//# sourceMappingURL=resolve.js.map
|
||||
+1
File diff suppressed because one or more lines are too long
+79
@@ -0,0 +1,79 @@
|
||||
/**
|
||||
* typedef
|
||||
*/
|
||||
/**
|
||||
* @typedef Options - options
|
||||
* @property [alpha] - enable alpha
|
||||
* @property [colorSpace] - color space
|
||||
* @property [currentColor] - color for currentcolor
|
||||
* @property [customPropeerty] - custom properties
|
||||
* @property [d50] - white point in d50
|
||||
* @property [dimension] - dimension
|
||||
* @property [format] - output format
|
||||
* @property [key] - key
|
||||
*/
|
||||
export interface Options {
|
||||
alpha?: boolean;
|
||||
colorSpace?: string;
|
||||
currentColor?: string;
|
||||
customProperty?: Record<string, string | ((K: string) => string)>;
|
||||
d50?: boolean;
|
||||
delimiter?: string | string[];
|
||||
dimension?: Record<string, number | ((K: string) => number)>;
|
||||
format?: string;
|
||||
nullable?: boolean;
|
||||
preserveComment?: boolean;
|
||||
}
|
||||
/**
|
||||
* @type ColorChannels - color channels
|
||||
*/
|
||||
export type ColorChannels = [x: number, y: number, z: number, alpha: number];
|
||||
/**
|
||||
* @type StringColorChannels - color channels
|
||||
*/
|
||||
export type StringColorChannels = [
|
||||
x: string,
|
||||
y: string,
|
||||
z: string,
|
||||
alpha: string | undefined
|
||||
];
|
||||
/**
|
||||
* @type StringColorSpacedChannels - specified value
|
||||
*/
|
||||
export type StringColorSpacedChannels = [
|
||||
cs: string,
|
||||
x: string,
|
||||
y: string,
|
||||
z: string,
|
||||
alpha: string | undefined
|
||||
];
|
||||
/**
|
||||
* @type ComputedColorChannels - computed value
|
||||
*/
|
||||
export type ComputedColorChannels = [
|
||||
cs: string,
|
||||
x: number,
|
||||
y: number,
|
||||
z: number,
|
||||
alpha: number
|
||||
];
|
||||
/**
|
||||
* @type SpecifiedColorChannels - specified value
|
||||
*/
|
||||
export type SpecifiedColorChannels = [
|
||||
cs: string,
|
||||
x: number | string,
|
||||
y: number | string,
|
||||
z: number | string,
|
||||
alpha: number | string
|
||||
];
|
||||
/**
|
||||
* @type MatchedRegExp - matched regexp array
|
||||
*/
|
||||
export type MatchedRegExp = [
|
||||
match: string,
|
||||
gr1: string,
|
||||
gr2: string,
|
||||
gr3: string,
|
||||
gr4: string
|
||||
];
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
import { Options } from './typedef.js';
|
||||
/**
|
||||
* split value
|
||||
* NOTE: comments are stripped, it can be preserved if, in the options param,
|
||||
* `delimiter` is either ',' or '/' and with `preserveComment` set to `true`
|
||||
* @param value - CSS value
|
||||
* @param [opt] - options
|
||||
* @returns array of values
|
||||
*/
|
||||
export declare const splitValue: (value: string, opt?: Options) => string[];
|
||||
/**
|
||||
* extract dashed-ident tokens
|
||||
* @param value - CSS value
|
||||
* @returns array of dashed-ident tokens
|
||||
*/
|
||||
export declare const extractDashedIdent: (value: string) => string[];
|
||||
/**
|
||||
* is color
|
||||
* @param value - CSS value
|
||||
* @param [opt] - options
|
||||
* @returns result
|
||||
*/
|
||||
export declare const isColor: (value: unknown, opt?: Options) => boolean;
|
||||
/**
|
||||
* value to JSON string
|
||||
* @param value - CSS value
|
||||
* @param [func] - stringify function
|
||||
* @returns stringified value in JSON notation
|
||||
*/
|
||||
export declare const valueToJsonString: (value: unknown, func?: boolean) => string;
|
||||
/**
|
||||
* round to specified precision
|
||||
* @param value - numeric value
|
||||
* @param bit - minimum bits
|
||||
* @returns rounded value
|
||||
*/
|
||||
export declare const roundToPrecision: (value: number, bit?: number) => number;
|
||||
/**
|
||||
* interpolate hue
|
||||
* @param hueA - hue value
|
||||
* @param hueB - hue value
|
||||
* @param arc - shorter | longer | increasing | decreasing
|
||||
* @returns result - [hueA, hueB]
|
||||
*/
|
||||
export declare const interpolateHue: (hueA: number, hueB: number, arc?: string) => [number, number];
|
||||
+279
@@ -0,0 +1,279 @@
|
||||
import { TokenType, tokenize } from "@csstools/css-tokenizer";
|
||||
import { createCacheKey, getCache, CacheItem, setCache } from "./cache.js";
|
||||
import { isString } from "./common.js";
|
||||
import { resolveColor } from "./resolve.js";
|
||||
import { NAMED_COLORS } from "./color.js";
|
||||
import { VAL_SPEC, SYN_COLOR_TYPE, SYN_MIX } from "./constant.js";
|
||||
const {
|
||||
CloseParen: PAREN_CLOSE,
|
||||
Comma: COMMA,
|
||||
Comment: COMMENT,
|
||||
Delim: DELIM,
|
||||
EOF,
|
||||
Function: FUNC,
|
||||
Ident: IDENT,
|
||||
OpenParen: PAREN_OPEN,
|
||||
Whitespace: W_SPACE
|
||||
} = TokenType;
|
||||
const NAMESPACE = "util";
|
||||
const DEC = 10;
|
||||
const HEX = 16;
|
||||
const DEG = 360;
|
||||
const DEG_HALF = 180;
|
||||
const REG_COLOR = new RegExp(`^(?:${SYN_COLOR_TYPE})$`);
|
||||
const REG_FN_COLOR = /^(?:(?:ok)?l(?:ab|ch)|color(?:-mix)?|hsla?|hwb|rgba?|var)\(/;
|
||||
const REG_MIX = new RegExp(SYN_MIX);
|
||||
const splitValue = (value, opt = {}) => {
|
||||
if (isString(value)) {
|
||||
value = value.trim();
|
||||
} else {
|
||||
throw new TypeError(`${value} is not a string.`);
|
||||
}
|
||||
const { delimiter = " ", preserveComment = false } = opt;
|
||||
const cacheKey = createCacheKey(
|
||||
{
|
||||
namespace: NAMESPACE,
|
||||
name: "splitValue",
|
||||
value
|
||||
},
|
||||
{
|
||||
delimiter,
|
||||
preserveComment
|
||||
}
|
||||
);
|
||||
const cachedResult = getCache(cacheKey);
|
||||
if (cachedResult instanceof CacheItem) {
|
||||
return cachedResult.item;
|
||||
}
|
||||
let regDelimiter;
|
||||
if (delimiter === ",") {
|
||||
regDelimiter = /^,$/;
|
||||
} else if (delimiter === "/") {
|
||||
regDelimiter = /^\/$/;
|
||||
} else {
|
||||
regDelimiter = /^\s+$/;
|
||||
}
|
||||
const tokens = tokenize({ css: value });
|
||||
let nest = 0;
|
||||
let str = "";
|
||||
const res = [];
|
||||
while (tokens.length) {
|
||||
const [type, value2] = tokens.shift();
|
||||
switch (type) {
|
||||
case COMMA: {
|
||||
if (regDelimiter.test(value2)) {
|
||||
if (nest === 0) {
|
||||
res.push(str.trim());
|
||||
str = "";
|
||||
} else {
|
||||
str += value2;
|
||||
}
|
||||
} else {
|
||||
str += value2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DELIM: {
|
||||
if (regDelimiter.test(value2)) {
|
||||
if (nest === 0) {
|
||||
res.push(str.trim());
|
||||
str = "";
|
||||
} else {
|
||||
str += value2;
|
||||
}
|
||||
} else {
|
||||
str += value2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case COMMENT: {
|
||||
if (preserveComment && (delimiter === "," || delimiter === "/")) {
|
||||
str += value2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FUNC:
|
||||
case PAREN_OPEN: {
|
||||
str += value2;
|
||||
nest++;
|
||||
break;
|
||||
}
|
||||
case PAREN_CLOSE: {
|
||||
str += value2;
|
||||
nest--;
|
||||
break;
|
||||
}
|
||||
case W_SPACE: {
|
||||
if (regDelimiter.test(value2)) {
|
||||
if (nest === 0) {
|
||||
if (str) {
|
||||
res.push(str.trim());
|
||||
str = "";
|
||||
}
|
||||
} else {
|
||||
str += " ";
|
||||
}
|
||||
} else if (!str.endsWith(" ")) {
|
||||
str += " ";
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
if (type === EOF) {
|
||||
res.push(str.trim());
|
||||
str = "";
|
||||
} else {
|
||||
str += value2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
setCache(cacheKey, res);
|
||||
return res;
|
||||
};
|
||||
const extractDashedIdent = (value) => {
|
||||
if (isString(value)) {
|
||||
value = value.trim();
|
||||
} else {
|
||||
throw new TypeError(`${value} is not a string.`);
|
||||
}
|
||||
const cacheKey = createCacheKey({
|
||||
namespace: NAMESPACE,
|
||||
name: "extractDashedIdent",
|
||||
value
|
||||
});
|
||||
const cachedResult = getCache(cacheKey);
|
||||
if (cachedResult instanceof CacheItem) {
|
||||
return cachedResult.item;
|
||||
}
|
||||
const tokens = tokenize({ css: value });
|
||||
const items = /* @__PURE__ */ new Set();
|
||||
while (tokens.length) {
|
||||
const [type, value2] = tokens.shift();
|
||||
if (type === IDENT && value2.startsWith("--")) {
|
||||
items.add(value2);
|
||||
}
|
||||
}
|
||||
const res = [...items];
|
||||
setCache(cacheKey, res);
|
||||
return res;
|
||||
};
|
||||
const isColor = (value, opt = {}) => {
|
||||
if (isString(value)) {
|
||||
value = value.toLowerCase().trim();
|
||||
if (value && isString(value)) {
|
||||
if (/^[a-z]+$/.test(value)) {
|
||||
if (/^(?:currentcolor|transparent)$/.test(value) || Object.prototype.hasOwnProperty.call(NAMED_COLORS, value)) {
|
||||
return true;
|
||||
}
|
||||
} else if (REG_COLOR.test(value) || REG_MIX.test(value)) {
|
||||
return true;
|
||||
} else if (REG_FN_COLOR.test(value)) {
|
||||
opt.nullable = true;
|
||||
if (!opt.format) {
|
||||
opt.format = VAL_SPEC;
|
||||
}
|
||||
const resolvedValue = resolveColor(value, opt);
|
||||
if (resolvedValue) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
const valueToJsonString = (value, func = false) => {
|
||||
if (typeof value === "undefined") {
|
||||
return "";
|
||||
}
|
||||
const res = JSON.stringify(value, (_key, val) => {
|
||||
let replacedValue;
|
||||
if (typeof val === "undefined") {
|
||||
replacedValue = null;
|
||||
} else if (typeof val === "function") {
|
||||
if (func) {
|
||||
replacedValue = val.toString().replace(/\s/g, "").substring(0, HEX);
|
||||
} else {
|
||||
replacedValue = val.name;
|
||||
}
|
||||
} else if (val instanceof Map || val instanceof Set) {
|
||||
replacedValue = [...val];
|
||||
} else if (typeof val === "bigint") {
|
||||
replacedValue = val.toString();
|
||||
} else {
|
||||
replacedValue = val;
|
||||
}
|
||||
return replacedValue;
|
||||
});
|
||||
return res;
|
||||
};
|
||||
const roundToPrecision = (value, bit = 0) => {
|
||||
if (!Number.isFinite(value)) {
|
||||
throw new TypeError(`${value} is not a finite number.`);
|
||||
}
|
||||
if (!Number.isFinite(bit)) {
|
||||
throw new TypeError(`${bit} is not a finite number.`);
|
||||
} else if (bit < 0 || bit > HEX) {
|
||||
throw new RangeError(`${bit} is not between 0 and ${HEX}.`);
|
||||
}
|
||||
if (bit === 0) {
|
||||
return Math.round(value);
|
||||
}
|
||||
let val;
|
||||
if (bit === HEX) {
|
||||
val = value.toPrecision(6);
|
||||
} else if (bit < DEC) {
|
||||
val = value.toPrecision(4);
|
||||
} else {
|
||||
val = value.toPrecision(5);
|
||||
}
|
||||
return parseFloat(val);
|
||||
};
|
||||
const interpolateHue = (hueA, hueB, arc = "shorter") => {
|
||||
if (!Number.isFinite(hueA)) {
|
||||
throw new TypeError(`${hueA} is not a finite number.`);
|
||||
}
|
||||
if (!Number.isFinite(hueB)) {
|
||||
throw new TypeError(`${hueB} is not a finite number.`);
|
||||
}
|
||||
switch (arc) {
|
||||
case "decreasing": {
|
||||
if (hueB > hueA) {
|
||||
hueA += DEG;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "increasing": {
|
||||
if (hueB < hueA) {
|
||||
hueB += DEG;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "longer": {
|
||||
if (hueB > hueA && hueB < hueA + DEG_HALF) {
|
||||
hueA += DEG;
|
||||
} else if (hueB > hueA + DEG_HALF * -1 && hueB <= hueA) {
|
||||
hueB += DEG;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "shorter":
|
||||
default: {
|
||||
if (hueB > hueA + DEG_HALF) {
|
||||
hueA += DEG;
|
||||
} else if (hueB < hueA + DEG_HALF * -1) {
|
||||
hueB += DEG;
|
||||
}
|
||||
}
|
||||
}
|
||||
return [hueA, hueB];
|
||||
};
|
||||
export {
|
||||
extractDashedIdent,
|
||||
interpolateHue,
|
||||
isColor,
|
||||
roundToPrecision,
|
||||
splitValue,
|
||||
valueToJsonString
|
||||
};
|
||||
//# sourceMappingURL=util.js.map
|
||||
+1
File diff suppressed because one or more lines are too long
+15
@@ -0,0 +1,15 @@
|
||||
The ISC License
|
||||
|
||||
Copyright (c) 2010-2023 Isaac Z. Schlueter and Contributors
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
||||
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
+331
@@ -0,0 +1,331 @@
|
||||
# lru-cache
|
||||
|
||||
A cache object that deletes the least-recently-used items.
|
||||
|
||||
Specify a max number of the most recently used items that you
|
||||
want to keep, and this cache will keep that many of the most
|
||||
recently accessed items.
|
||||
|
||||
This is not primarily a TTL cache, and does not make strong TTL
|
||||
guarantees. There is no preemptive pruning of expired items by
|
||||
default, but you _may_ set a TTL on the cache or on a single
|
||||
`set`. If you do so, it will treat expired items as missing, and
|
||||
delete them when fetched. If you are more interested in TTL
|
||||
caching than LRU caching, check out
|
||||
[@isaacs/ttlcache](http://npm.im/@isaacs/ttlcache).
|
||||
|
||||
As of version 7, this is one of the most performant LRU
|
||||
implementations available in JavaScript, and supports a wide
|
||||
diversity of use cases. However, note that using some of the
|
||||
features will necessarily impact performance, by causing the
|
||||
cache to have to do more work. See the "Performance" section
|
||||
below.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
npm install lru-cache --save
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```js
|
||||
// hybrid module, either works
|
||||
import { LRUCache } from 'lru-cache'
|
||||
// or:
|
||||
const { LRUCache } = require('lru-cache')
|
||||
// or in minified form for web browsers:
|
||||
import { LRUCache } from 'http://unpkg.com/lru-cache@9/dist/mjs/index.min.mjs'
|
||||
|
||||
// At least one of 'max', 'ttl', or 'maxSize' is required, to prevent
|
||||
// unsafe unbounded storage.
|
||||
//
|
||||
// In most cases, it's best to specify a max for performance, so all
|
||||
// the required memory allocation is done up-front.
|
||||
//
|
||||
// All the other options are optional, see the sections below for
|
||||
// documentation on what each one does. Most of them can be
|
||||
// overridden for specific items in get()/set()
|
||||
const options = {
|
||||
max: 500,
|
||||
|
||||
// for use with tracking overall storage size
|
||||
maxSize: 5000,
|
||||
sizeCalculation: (value, key) => {
|
||||
return 1
|
||||
},
|
||||
|
||||
// for use when you need to clean up something when objects
|
||||
// are evicted from the cache
|
||||
dispose: (value, key) => {
|
||||
freeFromMemoryOrWhatever(value)
|
||||
},
|
||||
|
||||
// how long to live in ms
|
||||
ttl: 1000 * 60 * 5,
|
||||
|
||||
// return stale items before removing from cache?
|
||||
allowStale: false,
|
||||
|
||||
updateAgeOnGet: false,
|
||||
updateAgeOnHas: false,
|
||||
|
||||
// async method to use for cache.fetch(), for
|
||||
// stale-while-revalidate type of behavior
|
||||
fetchMethod: async (
|
||||
key,
|
||||
staleValue,
|
||||
{ options, signal, context }
|
||||
) => {},
|
||||
}
|
||||
|
||||
const cache = new LRUCache(options)
|
||||
|
||||
cache.set('key', 'value')
|
||||
cache.get('key') // "value"
|
||||
|
||||
// non-string keys ARE fully supported
|
||||
// but note that it must be THE SAME object, not
|
||||
// just a JSON-equivalent object.
|
||||
var someObject = { a: 1 }
|
||||
cache.set(someObject, 'a value')
|
||||
// Object keys are not toString()-ed
|
||||
cache.set('[object Object]', 'a different value')
|
||||
assert.equal(cache.get(someObject), 'a value')
|
||||
// A similar object with same keys/values won't work,
|
||||
// because it's a different object identity
|
||||
assert.equal(cache.get({ a: 1 }), undefined)
|
||||
|
||||
cache.clear() // empty the cache
|
||||
```
|
||||
|
||||
If you put more stuff in the cache, then less recently used items
|
||||
will fall out. That's what an LRU cache is.
|
||||
|
||||
For full description of the API and all options, please see [the
|
||||
LRUCache typedocs](https://isaacs.github.io/node-lru-cache/)
|
||||
|
||||
## Storage Bounds Safety
|
||||
|
||||
This implementation aims to be as flexible as possible, within
|
||||
the limits of safe memory consumption and optimal performance.
|
||||
|
||||
At initial object creation, storage is allocated for `max` items.
|
||||
If `max` is set to zero, then some performance is lost, and item
|
||||
count is unbounded. Either `maxSize` or `ttl` _must_ be set if
|
||||
`max` is not specified.
|
||||
|
||||
If `maxSize` is set, then this creates a safe limit on the
|
||||
maximum storage consumed, but without the performance benefits of
|
||||
pre-allocation. When `maxSize` is set, every item _must_ provide
|
||||
a size, either via the `sizeCalculation` method provided to the
|
||||
constructor, or via a `size` or `sizeCalculation` option provided
|
||||
to `cache.set()`. The size of every item _must_ be a positive
|
||||
integer.
|
||||
|
||||
If neither `max` nor `maxSize` are set, then `ttl` tracking must
|
||||
be enabled. Note that, even when tracking item `ttl`, items are
|
||||
_not_ preemptively deleted when they become stale, unless
|
||||
`ttlAutopurge` is enabled. Instead, they are only purged the
|
||||
next time the key is requested. Thus, if `ttlAutopurge`, `max`,
|
||||
and `maxSize` are all not set, then the cache will potentially
|
||||
grow unbounded.
|
||||
|
||||
In this case, a warning is printed to standard error. Future
|
||||
versions may require the use of `ttlAutopurge` if `max` and
|
||||
`maxSize` are not specified.
|
||||
|
||||
If you truly wish to use a cache that is bound _only_ by TTL
|
||||
expiration, consider using a `Map` object, and calling
|
||||
`setTimeout` to delete entries when they expire. It will perform
|
||||
much better than an LRU cache.
|
||||
|
||||
Here is an implementation you may use, under the same
|
||||
[license](./LICENSE) as this package:
|
||||
|
||||
```js
|
||||
// a storage-unbounded ttl cache that is not an lru-cache
|
||||
const cache = {
|
||||
data: new Map(),
|
||||
timers: new Map(),
|
||||
set: (k, v, ttl) => {
|
||||
if (cache.timers.has(k)) {
|
||||
clearTimeout(cache.timers.get(k))
|
||||
}
|
||||
cache.timers.set(
|
||||
k,
|
||||
setTimeout(() => cache.delete(k), ttl)
|
||||
)
|
||||
cache.data.set(k, v)
|
||||
},
|
||||
get: k => cache.data.get(k),
|
||||
has: k => cache.data.has(k),
|
||||
delete: k => {
|
||||
if (cache.timers.has(k)) {
|
||||
clearTimeout(cache.timers.get(k))
|
||||
}
|
||||
cache.timers.delete(k)
|
||||
return cache.data.delete(k)
|
||||
},
|
||||
clear: () => {
|
||||
cache.data.clear()
|
||||
for (const v of cache.timers.values()) {
|
||||
clearTimeout(v)
|
||||
}
|
||||
cache.timers.clear()
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
If that isn't to your liking, check out
|
||||
[@isaacs/ttlcache](http://npm.im/@isaacs/ttlcache).
|
||||
|
||||
## Storing Undefined Values
|
||||
|
||||
This cache never stores undefined values, as `undefined` is used
|
||||
internally in a few places to indicate that a key is not in the
|
||||
cache.
|
||||
|
||||
You may call `cache.set(key, undefined)`, but this is just
|
||||
an alias for `cache.delete(key)`. Note that this has the effect
|
||||
that `cache.has(key)` will return _false_ after setting it to
|
||||
undefined.
|
||||
|
||||
```js
|
||||
cache.set(myKey, undefined)
|
||||
cache.has(myKey) // false!
|
||||
```
|
||||
|
||||
If you need to track `undefined` values, and still note that the
|
||||
key is in the cache, an easy workaround is to use a sigil object
|
||||
of your own.
|
||||
|
||||
```js
|
||||
import { LRUCache } from 'lru-cache'
|
||||
const undefinedValue = Symbol('undefined')
|
||||
const cache = new LRUCache(...)
|
||||
const mySet = (key, value) =>
|
||||
cache.set(key, value === undefined ? undefinedValue : value)
|
||||
const myGet = (key, value) => {
|
||||
const v = cache.get(key)
|
||||
return v === undefinedValue ? undefined : v
|
||||
}
|
||||
```
|
||||
|
||||
## Performance
|
||||
|
||||
As of January 2022, version 7 of this library is one of the most
|
||||
performant LRU cache implementations in JavaScript.
|
||||
|
||||
Benchmarks can be extremely difficult to get right. In
|
||||
particular, the performance of set/get/delete operations on
|
||||
objects will vary _wildly_ depending on the type of key used. V8
|
||||
is highly optimized for objects with keys that are short strings,
|
||||
especially integer numeric strings. Thus any benchmark which
|
||||
tests _solely_ using numbers as keys will tend to find that an
|
||||
object-based approach performs the best.
|
||||
|
||||
Note that coercing _anything_ to strings to use as object keys is
|
||||
unsafe, unless you can be 100% certain that no other type of
|
||||
value will be used. For example:
|
||||
|
||||
```js
|
||||
const myCache = {}
|
||||
const set = (k, v) => (myCache[k] = v)
|
||||
const get = k => myCache[k]
|
||||
|
||||
set({}, 'please hang onto this for me')
|
||||
set('[object Object]', 'oopsie')
|
||||
```
|
||||
|
||||
Also beware of "Just So" stories regarding performance. Garbage
|
||||
collection of large (especially: deep) object graphs can be
|
||||
incredibly costly, with several "tipping points" where it
|
||||
increases exponentially. As a result, putting that off until
|
||||
later can make it much worse, and less predictable. If a library
|
||||
performs well, but only in a scenario where the object graph is
|
||||
kept shallow, then that won't help you if you are using large
|
||||
objects as keys.
|
||||
|
||||
In general, when attempting to use a library to improve
|
||||
performance (such as a cache like this one), it's best to choose
|
||||
an option that will perform well in the sorts of scenarios where
|
||||
you'll actually use it.
|
||||
|
||||
This library is optimized for repeated gets and minimizing
|
||||
eviction time, since that is the expected need of a LRU. Set
|
||||
operations are somewhat slower on average than a few other
|
||||
options, in part because of that optimization. It is assumed
|
||||
that you'll be caching some costly operation, ideally as rarely
|
||||
as possible, so optimizing set over get would be unwise.
|
||||
|
||||
If performance matters to you:
|
||||
|
||||
1. If it's at all possible to use small integer values as keys,
|
||||
and you can guarantee that no other types of values will be
|
||||
used as keys, then do that, and use a cache such as
|
||||
[lru-fast](https://npmjs.com/package/lru-fast), or
|
||||
[mnemonist's
|
||||
LRUCache](https://yomguithereal.github.io/mnemonist/lru-cache)
|
||||
which uses an Object as its data store.
|
||||
|
||||
2. Failing that, if at all possible, use short non-numeric
|
||||
strings (ie, less than 256 characters) as your keys, and use
|
||||
[mnemonist's
|
||||
LRUCache](https://yomguithereal.github.io/mnemonist/lru-cache).
|
||||
|
||||
3. If the types of your keys will be anything else, especially
|
||||
long strings, strings that look like floats, objects, or some
|
||||
mix of types, or if you aren't sure, then this library will
|
||||
work well for you.
|
||||
|
||||
If you do not need the features that this library provides
|
||||
(like asynchronous fetching, a variety of TTL staleness
|
||||
options, and so on), then [mnemonist's
|
||||
LRUMap](https://yomguithereal.github.io/mnemonist/lru-map) is
|
||||
a very good option, and just slightly faster than this module
|
||||
(since it does considerably less).
|
||||
|
||||
4. Do not use a `dispose` function, size tracking, or especially
|
||||
ttl behavior, unless absolutely needed. These features are
|
||||
convenient, and necessary in some use cases, and every attempt
|
||||
has been made to make the performance impact minimal, but it
|
||||
isn't nothing.
|
||||
|
||||
## Breaking Changes in Version 7
|
||||
|
||||
This library changed to a different algorithm and internal data
|
||||
structure in version 7, yielding significantly better
|
||||
performance, albeit with some subtle changes as a result.
|
||||
|
||||
If you were relying on the internals of LRUCache in version 6 or
|
||||
before, it probably will not work in version 7 and above.
|
||||
|
||||
## Breaking Changes in Version 8
|
||||
|
||||
- The `fetchContext` option was renamed to `context`, and may no
|
||||
longer be set on the cache instance itself.
|
||||
- Rewritten in TypeScript, so pretty much all the types moved
|
||||
around a lot.
|
||||
- The AbortController/AbortSignal polyfill was removed. For this
|
||||
reason, **Node version 16.14.0 or higher is now required**.
|
||||
- Internal properties were moved to actual private class
|
||||
properties.
|
||||
- Keys and values must not be `null` or `undefined`.
|
||||
- Minified export available at `'lru-cache/min'`, for both CJS
|
||||
and MJS builds.
|
||||
|
||||
## Breaking Changes in Version 9
|
||||
|
||||
- Named export only, no default export.
|
||||
- AbortController polyfill returned, albeit with a warning when
|
||||
used.
|
||||
|
||||
## Breaking Changes in Version 10
|
||||
|
||||
- `cache.fetch()` return type is now `Promise<V | undefined>`
|
||||
instead of `Promise<V | void>`. This is an irrelevant change
|
||||
practically speaking, but can require changes for TypeScript
|
||||
users.
|
||||
|
||||
For more info, see the [change log](CHANGELOG.md).
|
||||
Generated
Vendored
+1277
File diff suppressed because it is too large
Load Diff
Generated
Vendored
+1
File diff suppressed because one or more lines are too long
Generated
Vendored
+1546
File diff suppressed because it is too large
Load Diff
Generated
Vendored
+1
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user