diff --git a/.gitignore b/.gitignore index 1b0f138..aa9d7c5 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ /errors.txt /syntax.txt +/ROADMAP.draft.md # Logs logs diff --git a/.npmignore b/.npmignore index d47bc21..7051701 100644 --- a/.npmignore +++ b/.npmignore @@ -2,6 +2,7 @@ /benchmark /tools /ROADMAP.md +/ROADMAP.draft.md /rollup.config.mjs /tsconfig.json /src diff --git a/README.md b/README.md index bff1b91..69c8e0c 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ $ npm install @tbela99/css-parser - efficient minification, see [benchmark](https://tbela99.github.io/css-parser/benchmark/index.html) - replace @import at-rules with actual css content of the imported rule - automatically generate nested css rules +- compute css shorthands. see the list - expand nested css - works the same way in node and web browser diff --git a/ROADMAP.md b/ROADMAP.md new file mode 100644 index 0000000..7118db1 --- /dev/null +++ b/ROADMAP.md @@ -0,0 +1,59 @@ + +## v0.0.1 + +### Minification + +- [x] merge identical rules +- [x] merge adjacent rules +- [x] minify colors +- [x] minify numbers and Dimensions tokens +- [x] compute shorthand: see the list below +- [x] remove redundant declarations +- [x] simple shorthand properties (padding, margin, etc). must have all required properties +- [x] complex shorthand properties (background, font, etc.). may have optional properties +- [x] conditionally unwrap :is() +- [x] automatic css nesting +- [x] automatically wrap selectors using :is() +- [x] multi-level shorthand properties (border - [border-width, border-color, border-style, etc.]) https://developer.mozilla.org/en-US/docs/Web/CSS/Shorthand_properties +- [x] avoid reparsing (declarations, selectors, at-rule) +- [x] node and browser versions +- [x] decode and replace utf-8 escape sequence + +### Computed shorthands +- [x] background +- [x] border +- [x] border-bottom +- [x] border-color +- [x] border-left +- [x] border-radius +- [x] border-right +- [x] border-style +- [x] border-top +- [x] border-width +- [x] font +- [x] inset +- [x] margin +- [x] outline +- [x] overflow +- [x] padding +- [x] text-decoration + +### Performance +- [x] flatten @import + +### Error handling +- [x] parse bad comments / cdo comments +- [x] parse bad string 1 +- [x] parse bad string 2 +- [x] parse empty declaration +- [x] parse unclosed rule +- [x] parse unclosed at-rule +- [x] parse bad import + +# Testing +- [x] node tests +- [x] browser tests + +# Code Coverage +- [x] node +- [x] browser diff --git a/dist/index-umd-web.js b/dist/index-umd-web.js index 005c7b1..7b7de95 100644 --- a/dist/index-umd-web.js +++ b/dist/index-umd-web.js @@ -1950,7 +1950,7 @@ } let key; for (key of k1) { - if (!eq(a[key], b[key])) { + if (!(key in b) || !eq(a[key], b[key])) { return false; } } @@ -3897,10 +3897,6 @@ const result = []; if (ast.typ == 'Rule') { let i = 0; - // @ts-ignore - delete ast.raw; - // @ts-ignore - delete ast.optimized; for (; i < ast.chi.length; i++) { if (ast.chi[i].typ == 'Rule') { const rule = ast.chi[i]; @@ -3915,8 +3911,6 @@ else { rule.sel = replaceCompound(rule.sel, ast.sel); } - delete rule.raw; - delete rule.optimized; ast.chi.splice(i--, 1); result.push(...expandRule(rule)); } diff --git a/dist/index.cjs b/dist/index.cjs index abc7ca8..45909e6 100644 --- a/dist/index.cjs +++ b/dist/index.cjs @@ -1948,7 +1948,7 @@ function eq(a, b) { } let key; for (key of k1) { - if (!eq(a[key], b[key])) { + if (!(key in b) || !eq(a[key], b[key])) { return false; } } @@ -3895,10 +3895,6 @@ function expandRule(node) { const result = []; if (ast.typ == 'Rule') { let i = 0; - // @ts-ignore - delete ast.raw; - // @ts-ignore - delete ast.optimized; for (; i < ast.chi.length; i++) { if (ast.chi[i].typ == 'Rule') { const rule = ast.chi[i]; @@ -3913,8 +3909,6 @@ function expandRule(node) { else { rule.sel = replaceCompound(rule.sel, ast.sel); } - delete rule.raw; - delete rule.optimized; ast.chi.splice(i--, 1); result.push(...expandRule(rule)); } diff --git a/dist/lib/ast/expand.js b/dist/lib/ast/expand.js index 55f2819..358df03 100644 --- a/dist/lib/ast/expand.js +++ b/dist/lib/ast/expand.js @@ -52,10 +52,6 @@ function expandRule(node) { const result = []; if (ast.typ == 'Rule') { let i = 0; - // @ts-ignore - delete ast.raw; - // @ts-ignore - delete ast.optimized; for (; i < ast.chi.length; i++) { if (ast.chi[i].typ == 'Rule') { const rule = ast.chi[i]; @@ -70,8 +66,6 @@ function expandRule(node) { else { rule.sel = replaceCompound(rule.sel, ast.sel); } - delete rule.raw; - delete rule.optimized; ast.chi.splice(i--, 1); result.push(...expandRule(rule)); } diff --git a/dist/lib/parser/utils/eq.js b/dist/lib/parser/utils/eq.js index 4fa5874..8644365 100644 --- a/dist/lib/parser/utils/eq.js +++ b/dist/lib/parser/utils/eq.js @@ -27,7 +27,7 @@ function eq(a, b) { } let key; for (key of k1) { - if (!eq(a[key], b[key])) { + if (!(key in b) || !eq(a[key], b[key])) { return false; } } diff --git a/src/lib/ast/expand.ts b/src/lib/ast/expand.ts index d04e2cd..5a5ae96 100644 --- a/src/lib/ast/expand.ts +++ b/src/lib/ast/expand.ts @@ -1,4 +1,4 @@ -import {AstAtRule, AstNode, AstRule, AstRuleStyleSheet} from "../../@types"; +import {AstAtRule, AstNode, AstRule, AstRuleStyleSheet, Token} from "../../@types"; import {combinators, splitRule} from "./minify"; import {parseString} from "../parser"; import {walkValues} from "./walk"; @@ -65,23 +65,18 @@ export function expand(ast: AstNode): AstNode { function expandRule(node: AstRule): Array { - const ast = {...node, chi: node.chi.slice()}; + const ast: AstRule = {...node, chi: node.chi.slice()}; const result: Array = []; if (ast.typ == 'Rule') { - let i = 0; - - // @ts-ignore - delete ast.raw; - // @ts-ignore - delete ast.optimized; + let i:number = 0; for (; i < ast.chi.length; i++) { if (ast.chi[i].typ == 'Rule') { - const rule = (ast).chi[i]; + const rule: AstRule = (ast).chi[i]; if (!rule.sel.includes('&')) { @@ -99,16 +94,13 @@ function expandRule(node: AstRule): Array { rule.sel = replaceCompound(rule.sel, ast.sel); } - delete rule.raw; - delete rule.optimized; - ast.chi.splice(i--, 1); result.push(...expandRule(rule)); } else if (ast.chi[i].typ == 'AtRule') { - let astAtRule = ast.chi[i]; + let astAtRule: AstAtRule = ast.chi[i]; const values = >[]; if (astAtRule.nam == 'scope') { @@ -125,7 +117,7 @@ function expandRule(node: AstRule): Array { else { // @ts-ignore - const clone = {...ast, chi: astAtRule.chi.slice()}; + const clone: AstRule = {...ast, chi: astAtRule.chi.slice()}; // @ts-ignore astAtRule.chi.length = 0; @@ -174,7 +166,7 @@ function expandRule(node: AstRule): Array { export function replaceCompound(input: string, replace: string) { - const tokens = parseString(input); + const tokens: Token[] = parseString(input); for (const t of walkValues(tokens)) { @@ -197,7 +189,7 @@ function replaceCompoundLiteral(selector: string, replace: string) { const tokens: string[] = ['']; - let i = 0; + let i: number = 0; for (; i < selector.length; i++) { @@ -219,5 +211,5 @@ function replaceCompoundLiteral(selector: string, replace: string) { } return b == '&' ? -1 : 0; - }).reduce((acc, curr) => acc + (curr == '&' ? replace : curr), ''); + }).reduce((acc: string, curr: string) => acc + (curr == '&' ? replace : curr), ''); } diff --git a/src/lib/parser/utils/eq.ts b/src/lib/parser/utils/eq.ts index abb64ff..cc0bcb7 100644 --- a/src/lib/parser/utils/eq.ts +++ b/src/lib/parser/utils/eq.ts @@ -47,7 +47,7 @@ export function eq(a: { [key: string]: any }, b: { [key: string]: any }): boolea for (key of k1) { - if (!eq(a[key], b[key])) { + if (!(key in b) || !eq(a[key], b[key])) { return false; }