diff --git a/dist/config.json.js b/dist/config.json.js index 2a71745..e1f2373 100644 --- a/dist/config.json.js +++ b/dist/config.json.js @@ -297,6 +297,55 @@ var map = { "border-width": { shorthand: "border" }, + overflow: { + shorthand: "overflow", + pattern: "overflow-x overflow-y", + keywords: [ + "auto", + "visible", + "hidden", + "clip", + "scroll" + ], + "default": [ + ], + mapping: { + "visible visible": "visible", + "auto auto": "auto", + "hidden hidden": "hidden", + "scroll scroll": "scroll" + }, + properties: { + "overflow-x": { + "default": [ + ], + keywords: [ + "auto", + "visible", + "hidden", + "clip", + "scroll" + ] + }, + "overflow-y": { + "default": [ + ], + keywords: [ + "auto", + "visible", + "hidden", + "clip", + "scroll" + ] + } + } + }, + "overflow-x": { + shorthand: "overflow" + }, + "overflow-y": { + shorthand: "overflow" + }, outline: { shorthand: "outline", pattern: "outline-color outline-style outline-width", diff --git a/dist/index-umd-web.js b/dist/index-umd-web.js index b6e3e1a..1079025 100644 --- a/dist/index-umd-web.js +++ b/dist/index-umd-web.js @@ -1371,6 +1371,55 @@ "border-width": { shorthand: "border" }, + overflow: { + shorthand: "overflow", + pattern: "overflow-x overflow-y", + keywords: [ + "auto", + "visible", + "hidden", + "clip", + "scroll" + ], + "default": [ + ], + mapping: { + "visible visible": "visible", + "auto auto": "auto", + "hidden hidden": "hidden", + "scroll scroll": "scroll" + }, + properties: { + "overflow-x": { + "default": [ + ], + keywords: [ + "auto", + "visible", + "hidden", + "clip", + "scroll" + ] + }, + "overflow-y": { + "default": [ + ], + keywords: [ + "auto", + "visible", + "hidden", + "clip", + "scroll" + ] + } + } + }, + "overflow-x": { + shorthand: "overflow" + }, + "overflow-y": { + shorthand: "overflow" + }, outline: { shorthand: "outline", pattern: "outline-color outline-style outline-width", @@ -2424,6 +2473,14 @@ acc.push(...curr); return acc; }, []); + if (this.config.mapping != null) { + const val = values.reduce((acc, curr) => acc + renderToken(curr, { removeComments: true }), ''); + if (val in this.config.mapping) { + values.length = 0; + // @ts-ignore + values.push({ typ: ['"', "'"].includes(val.charAt(0)) ? 'String' : 'Iden', val: this.config.mapping[val] }); + } + } iterable = [{ typ: 'Declaration', nam: this.config.shorthand, @@ -3018,82 +3075,97 @@ } } // @ts-ignore - if (previous != null && 'chi' in previous && ('chi' in node)) { + if (previous != null) { // @ts-ignore - if (previous.typ == node.typ) { - let shouldMerge = true; + if ('chi' in previous && ('chi' in node)) { // @ts-ignore - let k = previous.chi.length; - while (k-- > 0) { - // @ts-ignore - if (previous.chi[k].typ == 'Comment') { - continue; - } - // @ts-ignore - shouldMerge = previous.chi[k].typ == 'Declaration'; - break; - } - if (shouldMerge) { + if (previous.typ == node.typ) { + let shouldMerge = true; // @ts-ignore - if ((node.typ == 'Rule' && node.sel == previous.sel) || + let k = previous.chi.length; + while (k-- > 0) { // @ts-ignore - (node.typ == 'AtRule') && node.val != 'font-face' && node.val == previous.val) { - // @ts-ignore - node.chi.unshift(...previous.chi); + if (previous.chi[k].typ == 'Comment') { + continue; + } // @ts-ignore - ast.chi.splice(nodeIndex, 1); + shouldMerge = previous.chi[k].typ == 'Declaration'; + break; + } + if (shouldMerge) { // @ts-ignore - if (hasDeclaration(node)) { + if ((node.typ == 'Rule' && node.sel == previous.sel) || // @ts-ignore - minifyRule(node); - } - else { - minify(node, options, recursive, errors); - } - i--; - previous = node; - nodeIndex = i; - continue; - } - else if (node.typ == 'Rule' && previous?.typ == 'Rule') { - const intersect = diff(previous, node, options); - if (intersect != null) { - if (intersect.node1.chi.length == 0) { - // @ts-ignore - ast.chi.splice(i--, 1); + (node.typ == 'AtRule') && node.val != 'font-face' && node.val == previous.val) { + // @ts-ignore + node.chi.unshift(...previous.chi); + // @ts-ignore + ast.chi.splice(nodeIndex, 1); + // @ts-ignore + if (hasDeclaration(node)) { // @ts-ignore - node = ast.chi[i]; + minifyRule(node); } else { - // @ts-ignore - ast.chi.splice(i, 1, intersect.node1); - node = intersect.node1; - } - if (intersect.node2.chi.length == 0) { - // @ts-ignore - ast.chi.splice(nodeIndex, 1, intersect.result); - previous = intersect.result; + minify(node, options, recursive, errors); } - else { - // @ts-ignore - ast.chi.splice(nodeIndex, 1, intersect.result, intersect.node2); - previous = intersect.result; - // @ts-ignore - i = nodeIndex; + i--; + previous = node; + nodeIndex = i; + continue; + } + else if (node.typ == 'Rule' && previous?.typ == 'Rule') { + const intersect = diff(previous, node, options); + if (intersect != null) { + if (intersect.node1.chi.length == 0) { + // @ts-ignore + ast.chi.splice(i--, 1); + // @ts-ignore + node = ast.chi[i]; + } + else { + // @ts-ignore + ast.chi.splice(i, 1, intersect.node1); + node = intersect.node1; + } + if (intersect.node2.chi.length == 0) { + // @ts-ignore + ast.chi.splice(nodeIndex, 1, intersect.result); + previous = intersect.result; + } + else { + // @ts-ignore + ast.chi.splice(nodeIndex, 1, intersect.result, intersect.node2); + previous = intersect.result; + // @ts-ignore + i = nodeIndex; + } } } } } - } - // @ts-ignore - if (recursive && previous != node) { // @ts-ignore - if (hasDeclaration(previous)) { + if (recursive && previous != node) { // @ts-ignore - minifyRule(previous); + if (hasDeclaration(previous)) { + // @ts-ignore + minifyRule(previous); + } + else { + minify(previous, options, recursive, errors); + } } - else { - minify(previous, options, recursive, errors); + } + else { + if ('chi' in previous) { + // @ts-ignore + if (hasDeclaration(previous)) { + // @ts-ignore + minifyRule(previous); + } + else { + minify(previous, options, recursive, errors); + } } } } @@ -4194,13 +4266,9 @@ } const iter = tokenize(iterator); let item; - while (true) { - item = iter.next().value; - if (item == null) { - break; - } - // console.debug({item}); + while (item = iter.next().value) { bytesIn = item.bytesIn; + // parse error if (item.hint != null && item.hint.startsWith('Bad-')) { // bad token continue; @@ -4249,6 +4317,17 @@ if (tokens.length > 0) { await parseNode(tokens); } + while (stack.length > 0 && context != ast) { + const previousNode = stack.pop(); + // @ts-ignore + context = stack[stack.length - 1] || ast; + // @ts-ignore + if (options.removeEmpty && previousNode != null && previousNode.chi.length == 0 && context.chi[context.chi.length - 1] == previousNode) { + context.chi.pop(); + continue; + } + break; + } const endParseTime = performance.now(); if (options.minify) { if (ast.chi.length > 0) { diff --git a/dist/index.cjs b/dist/index.cjs index c576dff..8fa74fe 100644 --- a/dist/index.cjs +++ b/dist/index.cjs @@ -1369,6 +1369,55 @@ var map = { "border-width": { shorthand: "border" }, + overflow: { + shorthand: "overflow", + pattern: "overflow-x overflow-y", + keywords: [ + "auto", + "visible", + "hidden", + "clip", + "scroll" + ], + "default": [ + ], + mapping: { + "visible visible": "visible", + "auto auto": "auto", + "hidden hidden": "hidden", + "scroll scroll": "scroll" + }, + properties: { + "overflow-x": { + "default": [ + ], + keywords: [ + "auto", + "visible", + "hidden", + "clip", + "scroll" + ] + }, + "overflow-y": { + "default": [ + ], + keywords: [ + "auto", + "visible", + "hidden", + "clip", + "scroll" + ] + } + } + }, + "overflow-x": { + shorthand: "overflow" + }, + "overflow-y": { + shorthand: "overflow" + }, outline: { shorthand: "outline", pattern: "outline-color outline-style outline-width", @@ -2422,6 +2471,14 @@ class PropertyMap { acc.push(...curr); return acc; }, []); + if (this.config.mapping != null) { + const val = values.reduce((acc, curr) => acc + renderToken(curr, { removeComments: true }), ''); + if (val in this.config.mapping) { + values.length = 0; + // @ts-ignore + values.push({ typ: ['"', "'"].includes(val.charAt(0)) ? 'String' : 'Iden', val: this.config.mapping[val] }); + } + } iterable = [{ typ: 'Declaration', nam: this.config.shorthand, @@ -3016,82 +3073,97 @@ function minify(ast, options = {}, recursive = false, errors) { } } // @ts-ignore - if (previous != null && 'chi' in previous && ('chi' in node)) { + if (previous != null) { // @ts-ignore - if (previous.typ == node.typ) { - let shouldMerge = true; + if ('chi' in previous && ('chi' in node)) { // @ts-ignore - let k = previous.chi.length; - while (k-- > 0) { - // @ts-ignore - if (previous.chi[k].typ == 'Comment') { - continue; - } - // @ts-ignore - shouldMerge = previous.chi[k].typ == 'Declaration'; - break; - } - if (shouldMerge) { + if (previous.typ == node.typ) { + let shouldMerge = true; // @ts-ignore - if ((node.typ == 'Rule' && node.sel == previous.sel) || + let k = previous.chi.length; + while (k-- > 0) { // @ts-ignore - (node.typ == 'AtRule') && node.val != 'font-face' && node.val == previous.val) { - // @ts-ignore - node.chi.unshift(...previous.chi); + if (previous.chi[k].typ == 'Comment') { + continue; + } // @ts-ignore - ast.chi.splice(nodeIndex, 1); + shouldMerge = previous.chi[k].typ == 'Declaration'; + break; + } + if (shouldMerge) { // @ts-ignore - if (hasDeclaration(node)) { + if ((node.typ == 'Rule' && node.sel == previous.sel) || // @ts-ignore - minifyRule(node); - } - else { - minify(node, options, recursive, errors); - } - i--; - previous = node; - nodeIndex = i; - continue; - } - else if (node.typ == 'Rule' && previous?.typ == 'Rule') { - const intersect = diff(previous, node, options); - if (intersect != null) { - if (intersect.node1.chi.length == 0) { - // @ts-ignore - ast.chi.splice(i--, 1); + (node.typ == 'AtRule') && node.val != 'font-face' && node.val == previous.val) { + // @ts-ignore + node.chi.unshift(...previous.chi); + // @ts-ignore + ast.chi.splice(nodeIndex, 1); + // @ts-ignore + if (hasDeclaration(node)) { // @ts-ignore - node = ast.chi[i]; + minifyRule(node); } else { - // @ts-ignore - ast.chi.splice(i, 1, intersect.node1); - node = intersect.node1; - } - if (intersect.node2.chi.length == 0) { - // @ts-ignore - ast.chi.splice(nodeIndex, 1, intersect.result); - previous = intersect.result; + minify(node, options, recursive, errors); } - else { - // @ts-ignore - ast.chi.splice(nodeIndex, 1, intersect.result, intersect.node2); - previous = intersect.result; - // @ts-ignore - i = nodeIndex; + i--; + previous = node; + nodeIndex = i; + continue; + } + else if (node.typ == 'Rule' && previous?.typ == 'Rule') { + const intersect = diff(previous, node, options); + if (intersect != null) { + if (intersect.node1.chi.length == 0) { + // @ts-ignore + ast.chi.splice(i--, 1); + // @ts-ignore + node = ast.chi[i]; + } + else { + // @ts-ignore + ast.chi.splice(i, 1, intersect.node1); + node = intersect.node1; + } + if (intersect.node2.chi.length == 0) { + // @ts-ignore + ast.chi.splice(nodeIndex, 1, intersect.result); + previous = intersect.result; + } + else { + // @ts-ignore + ast.chi.splice(nodeIndex, 1, intersect.result, intersect.node2); + previous = intersect.result; + // @ts-ignore + i = nodeIndex; + } } } } } - } - // @ts-ignore - if (recursive && previous != node) { // @ts-ignore - if (hasDeclaration(previous)) { + if (recursive && previous != node) { // @ts-ignore - minifyRule(previous); + if (hasDeclaration(previous)) { + // @ts-ignore + minifyRule(previous); + } + else { + minify(previous, options, recursive, errors); + } } - else { - minify(previous, options, recursive, errors); + } + else { + if ('chi' in previous) { + // @ts-ignore + if (hasDeclaration(previous)) { + // @ts-ignore + minifyRule(previous); + } + else { + minify(previous, options, recursive, errors); + } } } } @@ -4192,13 +4264,9 @@ async function parse$1(iterator, opt = {}) { } const iter = tokenize(iterator); let item; - while (true) { - item = iter.next().value; - if (item == null) { - break; - } - // console.debug({item}); + while (item = iter.next().value) { bytesIn = item.bytesIn; + // parse error if (item.hint != null && item.hint.startsWith('Bad-')) { // bad token continue; @@ -4247,6 +4315,17 @@ async function parse$1(iterator, opt = {}) { if (tokens.length > 0) { await parseNode(tokens); } + while (stack.length > 0 && context != ast) { + const previousNode = stack.pop(); + // @ts-ignore + context = stack[stack.length - 1] || ast; + // @ts-ignore + if (options.removeEmpty && previousNode != null && previousNode.chi.length == 0 && context.chi[context.chi.length - 1] == previousNode) { + context.chi.pop(); + continue; + } + break; + } const endParseTime = performance.now(); if (options.minify) { if (ast.chi.length > 0) { diff --git a/dist/lib/ast/minify.js b/dist/lib/ast/minify.js index d497b83..542687b 100644 --- a/dist/lib/ast/minify.js +++ b/dist/lib/ast/minify.js @@ -473,82 +473,97 @@ function minify(ast, options = {}, recursive = false, errors) { } } // @ts-ignore - if (previous != null && 'chi' in previous && ('chi' in node)) { + if (previous != null) { // @ts-ignore - if (previous.typ == node.typ) { - let shouldMerge = true; + if ('chi' in previous && ('chi' in node)) { // @ts-ignore - let k = previous.chi.length; - while (k-- > 0) { + if (previous.typ == node.typ) { + let shouldMerge = true; // @ts-ignore - if (previous.chi[k].typ == 'Comment') { - continue; - } - // @ts-ignore - shouldMerge = previous.chi[k].typ == 'Declaration'; - break; - } - if (shouldMerge) { - // @ts-ignore - if ((node.typ == 'Rule' && node.sel == previous.sel) || + let k = previous.chi.length; + while (k-- > 0) { // @ts-ignore - (node.typ == 'AtRule') && node.val != 'font-face' && node.val == previous.val) { - // @ts-ignore - node.chi.unshift(...previous.chi); + if (previous.chi[k].typ == 'Comment') { + continue; + } // @ts-ignore - ast.chi.splice(nodeIndex, 1); + shouldMerge = previous.chi[k].typ == 'Declaration'; + break; + } + if (shouldMerge) { // @ts-ignore - if (hasDeclaration(node)) { + if ((node.typ == 'Rule' && node.sel == previous.sel) || // @ts-ignore - minifyRule(node); - } - else { - minify(node, options, recursive, errors); - } - i--; - previous = node; - nodeIndex = i; - continue; - } - else if (node.typ == 'Rule' && previous?.typ == 'Rule') { - const intersect = diff(previous, node, options); - if (intersect != null) { - if (intersect.node1.chi.length == 0) { - // @ts-ignore - ast.chi.splice(i--, 1); + (node.typ == 'AtRule') && node.val != 'font-face' && node.val == previous.val) { + // @ts-ignore + node.chi.unshift(...previous.chi); + // @ts-ignore + ast.chi.splice(nodeIndex, 1); + // @ts-ignore + if (hasDeclaration(node)) { // @ts-ignore - node = ast.chi[i]; + minifyRule(node); } else { - // @ts-ignore - ast.chi.splice(i, 1, intersect.node1); - node = intersect.node1; + minify(node, options, recursive, errors); } - if (intersect.node2.chi.length == 0) { - // @ts-ignore - ast.chi.splice(nodeIndex, 1, intersect.result); - previous = intersect.result; - } - else { - // @ts-ignore - ast.chi.splice(nodeIndex, 1, intersect.result, intersect.node2); - previous = intersect.result; - // @ts-ignore - i = nodeIndex; + i--; + previous = node; + nodeIndex = i; + continue; + } + else if (node.typ == 'Rule' && previous?.typ == 'Rule') { + const intersect = diff(previous, node, options); + if (intersect != null) { + if (intersect.node1.chi.length == 0) { + // @ts-ignore + ast.chi.splice(i--, 1); + // @ts-ignore + node = ast.chi[i]; + } + else { + // @ts-ignore + ast.chi.splice(i, 1, intersect.node1); + node = intersect.node1; + } + if (intersect.node2.chi.length == 0) { + // @ts-ignore + ast.chi.splice(nodeIndex, 1, intersect.result); + previous = intersect.result; + } + else { + // @ts-ignore + ast.chi.splice(nodeIndex, 1, intersect.result, intersect.node2); + previous = intersect.result; + // @ts-ignore + i = nodeIndex; + } } } } } - } - // @ts-ignore - if (recursive && previous != node) { // @ts-ignore - if (hasDeclaration(previous)) { + if (recursive && previous != node) { // @ts-ignore - minifyRule(previous); + if (hasDeclaration(previous)) { + // @ts-ignore + minifyRule(previous); + } + else { + minify(previous, options, recursive, errors); + } } - else { - minify(previous, options, recursive, errors); + } + else { + if ('chi' in previous) { + // @ts-ignore + if (hasDeclaration(previous)) { + // @ts-ignore + minifyRule(previous); + } + else { + minify(previous, options, recursive, errors); + } } } } diff --git a/dist/lib/parser/declaration/map.js b/dist/lib/parser/declaration/map.js index 277940d..fd90070 100644 --- a/dist/lib/parser/declaration/map.js +++ b/dist/lib/parser/declaration/map.js @@ -340,6 +340,14 @@ class PropertyMap { acc.push(...curr); return acc; }, []); + if (this.config.mapping != null) { + const val = values.reduce((acc, curr) => acc + renderToken(curr, { removeComments: true }), ''); + if (val in this.config.mapping) { + values.length = 0; + // @ts-ignore + values.push({ typ: ['"', "'"].includes(val.charAt(0)) ? 'String' : 'Iden', val: this.config.mapping[val] }); + } + } iterable = [{ typ: 'Declaration', nam: this.config.shorthand, diff --git a/dist/lib/parser/parse.js b/dist/lib/parser/parse.js index 6986ca0..7d64902 100644 --- a/dist/lib/parser/parse.js +++ b/dist/lib/parser/parse.js @@ -328,13 +328,9 @@ async function parse(iterator, opt = {}) { } const iter = tokenize(iterator); let item; - while (true) { - item = iter.next().value; - if (item == null) { - break; - } - // console.debug({item}); + while (item = iter.next().value) { bytesIn = item.bytesIn; + // parse error if (item.hint != null && item.hint.startsWith('Bad-')) { // bad token continue; @@ -383,6 +379,17 @@ async function parse(iterator, opt = {}) { if (tokens.length > 0) { await parseNode(tokens); } + while (stack.length > 0 && context != ast) { + const previousNode = stack.pop(); + // @ts-ignore + context = stack[stack.length - 1] || ast; + // @ts-ignore + if (options.removeEmpty && previousNode != null && previousNode.chi.length == 0 && context.chi[context.chi.length - 1] == previousNode) { + context.chi.pop(); + continue; + } + break; + } const endParseTime = performance.now(); if (options.minify) { if (ast.chi.length > 0) { diff --git a/src/@types/shorthand.ts b/src/@types/shorthand.ts index 918c507..69767c9 100644 --- a/src/@types/shorthand.ts +++ b/src/@types/shorthand.ts @@ -53,6 +53,7 @@ export interface ShorthandMapType { pattern: string; keywords: string[]; default: string[]; + mapping?: string[]; multiple?: boolean; separator?: Token; properties: { diff --git a/src/config.json b/src/config.json index 6d632c8..e84af21 100644 --- a/src/config.json +++ b/src/config.json @@ -292,6 +292,52 @@ "border-width": { "shorthand": "border" }, + "overflow": { + "shorthand": "overflow", + "pattern": "overflow-x overflow-y", + "keywords": [ + "auto", + "visible", + "hidden", + "clip", + "scroll" + ], + "default": [], + "mapping": { + "visible visible": "visible", + "auto auto": "auto", + "hidden hidden": "hidden", + "scroll scroll": "scroll" + }, + "properties": { + "overflow-x": { + "default": [], + "keywords": [ + "auto", + "visible", + "hidden", + "clip", + "scroll" + ] + }, + "overflow-y": { + "default": [], + "keywords": [ + "auto", + "visible", + "hidden", + "clip", + "scroll" + ] + } + } + }, + "overflow-x": { + "shorthand": "overflow" + }, + "overflow-y": { + "shorthand": "overflow" + }, "outline": { "shorthand": "outline", "pattern": "outline-color outline-style outline-width", diff --git a/src/lib/ast/minify.ts b/src/lib/ast/minify.ts index dcb58df..4ed7b26 100644 --- a/src/lib/ast/minify.ts +++ b/src/lib/ast/minify.ts @@ -648,85 +648,110 @@ export function minify(ast: AstNode, options: ParserOptions = {}, recursive: boo node.sel = wrap ? node.optimized.optimized[0] + `:is(${rule})` : rule; } } + // @ts-ignore - if (previous != null && 'chi' in previous && ('chi' in node)) { + if (previous != null ) { + // @ts-ignore - if (previous.typ == node.typ) { - let shouldMerge = true; + if ('chi' in previous && ('chi' in node)) { + // @ts-ignore - let k = previous.chi.length; - while (k-- > 0) { + if (previous.typ == node.typ) { + let shouldMerge = true; // @ts-ignore - if (previous.chi[k].typ == 'Comment') { - continue; - } - // @ts-ignore - shouldMerge = previous.chi[k].typ == 'Declaration'; - break; - } - if (shouldMerge) { - // @ts-ignore - if ((node.typ == 'Rule' && node.sel == previous.sel) || + let k = previous.chi.length; + while (k-- > 0) { // @ts-ignore - (node.typ == 'AtRule') && node.val != 'font-face' && node.val == previous.val) { - - // @ts-ignore - node.chi.unshift(...previous.chi); + if (previous.chi[k].typ == 'Comment') { + continue; + } // @ts-ignore - ast.chi.splice(nodeIndex, 1); + shouldMerge = previous.chi[k].typ == 'Declaration'; + break; + } + if (shouldMerge) { // @ts-ignore - if (hasDeclaration(node)) { + if ((node.typ == 'Rule' && node.sel == previous.sel) || // @ts-ignore - minifyRule(node); - } - else { - minify(node, options, recursive, errors); - } + (node.typ == 'AtRule') && node.val != 'font-face' && node.val == previous.val) { - i--; - previous = node; - nodeIndex = i; - continue; - } else if (node.typ == 'Rule' && previous?.typ == 'Rule') { - const intersect = diff(previous, node, options); - if (intersect != null) { - if (intersect.node1.chi.length == 0) { - // @ts-ignore - ast.chi.splice(i--, 1); - // @ts-ignore - node = ast.chi[i]; - } else { + // @ts-ignore + node.chi.unshift(...previous.chi); + // @ts-ignore + ast.chi.splice(nodeIndex, 1); + // @ts-ignore + if (hasDeclaration(node)) { // @ts-ignore - ast.chi.splice(i, 1, intersect.node1); - node = intersect.node1; + minifyRule(node); } - if (intersect.node2.chi.length == 0) { - // @ts-ignore - ast.chi.splice(nodeIndex, 1, intersect.result); - previous = intersect.result; - } else { - // @ts-ignore - ast.chi.splice(nodeIndex, 1, intersect.result, intersect.node2); - previous = intersect.result; - // @ts-ignore - i = nodeIndex; + else { + minify(node, options, recursive, errors); + } + + i--; + previous = node; + nodeIndex = i; + continue; + } else if (node.typ == 'Rule' && previous?.typ == 'Rule') { + const intersect = diff(previous, node, options); + if (intersect != null) { + if (intersect.node1.chi.length == 0) { + // @ts-ignore + ast.chi.splice(i--, 1); + // @ts-ignore + node = ast.chi[i]; + } else { + // @ts-ignore + ast.chi.splice(i, 1, intersect.node1); + node = intersect.node1; + } + if (intersect.node2.chi.length == 0) { + // @ts-ignore + ast.chi.splice(nodeIndex, 1, intersect.result); + previous = intersect.result; + } else { + // @ts-ignore + ast.chi.splice(nodeIndex, 1, intersect.result, intersect.node2); + previous = intersect.result; + // @ts-ignore + i = nodeIndex; + } } } } } - } - // @ts-ignore - if (recursive && previous != node) { // @ts-ignore - if (hasDeclaration(previous)) { + if (recursive && previous != node) { + // @ts-ignore + if (hasDeclaration(previous)) { + // @ts-ignore + minifyRule(previous); + } else { + minify(previous, options, recursive, errors); + } + } + } + else { + + if ('chi' in previous) { + // @ts-ignore - minifyRule(previous); - } else { - minify(previous, options, recursive, errors); + if (hasDeclaration(previous)) { + + // @ts-ignore + minifyRule(previous); + } + + else { + + minify(previous, options, recursive, errors); + } } } } + + previous = node; nodeIndex = i; } diff --git a/src/lib/parser/declaration/map.ts b/src/lib/parser/declaration/map.ts index e8edf04..3b1dca7 100644 --- a/src/lib/parser/declaration/map.ts +++ b/src/lib/parser/declaration/map.ts @@ -501,6 +501,18 @@ export class PropertyMap { }, []); + if (this.config.mapping != null) { + + const val = values.reduce((acc, curr) => acc + renderToken(curr, {removeComments: true}), ''); + + if (val in this.config.mapping) { + + values.length = 0; + // @ts-ignore + values.push({typ: ['"', "'"].includes(val.charAt(0)) ? 'String' : 'Iden', val: this.config.mapping[val]}); + } + } + iterable = [{ typ: 'Declaration', nam: this.config.shorthand, diff --git a/src/lib/parser/parse.ts b/src/lib/parser/parse.ts index c529634..73313b6 100644 --- a/src/lib/parser/parse.ts +++ b/src/lib/parser/parse.ts @@ -433,19 +433,11 @@ export async function parse(iterator: string, opt: ParserOptions = {}): Promise< const iter = tokenize(iterator); let item: TokenizeResult; - while (true) { - - item = iter.next().value; - - if (item == null) { - - break; - } - - // console.debug({item}); + while (item = iter.next().value) { bytesIn = item.bytesIn; + // parse error if (item.hint != null && item.hint.startsWith('Bad-')) { // bad token @@ -514,6 +506,21 @@ export async function parse(iterator: string, opt: ParserOptions = {}): Promise< await parseNode(tokens); } + while (stack.length > 0 && context != ast) { + + const previousNode = stack.pop(); + + // @ts-ignore + context = stack[stack.length - 1] || ast; + // @ts-ignore + if (options.removeEmpty && previousNode != null && previousNode.chi.length == 0 && context.chi[context.chi.length - 1] == previousNode) { + context.chi.pop(); + continue; + } + + break; + } + const endParseTime: number = performance.now(); diff --git a/test/specs/shorthand.spec.js b/test/specs/shorthand.spec.js index 2b37ae8..480a736 100644 --- a/test/specs/shorthand.spec.js +++ b/test/specs/shorthand.spec.js @@ -311,6 +311,19 @@ button.jetpack-instant-search__overlay-close { text-shadow: none; text-transform: none; width: 60px +}`)); + }); + + it('shorthand parsing #17', function () { + return transform(` + +a { +overflow-x: hidden; +overflow-y: hidden; + +} +`, options).then(result => f(render(result.ast, {minify: false}).code).equals(`a { + overflow: hidden }`)); }); }); diff --git a/tools/shorthand.ts b/tools/shorthand.ts index c114181..190c554 100644 --- a/tools/shorthand.ts +++ b/tools/shorthand.ts @@ -13,7 +13,7 @@ function createProperties(data: ShorthandPropertyType) { [data.shorthand]: {...data}, ...data.properties.reduce((acc, property: string) => { - return Object.assign(acc,{ + return Object.assign(acc, { [property]: { map, shorthand: data.shorthand @@ -41,7 +41,8 @@ function createMap(data: ShorthandDef, fields: Array) { } }); }, { - [data.shorthand]: {...data, + [data.shorthand]: { + ...data, properties: {} } }) @@ -60,17 +61,46 @@ export const map: ShorthandMapType = [ [ { shorthand: 'border-color', - properties: { - } + properties: {} }, { shorthand: 'border-style', + properties: {} + }, + { + shorthand: 'border-width', + properties: {} + } + ] + ], + + [ + { + shorthand: 'overflow', + pattern: 'overflow-x overflow-y', + keywords: ['auto', 'visible', 'hidden', 'clip', 'scroll'], + default: [], + mapping: { + 'visible visible': 'visible', + 'auto auto': 'auto', + 'hidden hidden': 'hidden', + 'scroll scroll': 'scroll' + } + }, + [ + { + shorthand: 'overflow-x', properties: { + + default: [], + keywords: ['auto', 'visible', 'hidden', 'clip', 'scroll'] } }, { - shorthand: 'border-width', + shorthand: 'overflow-y', properties: { + default: [], + keywords: ['auto', 'visible', 'hidden', 'clip', 'scroll'] } } ] @@ -223,13 +253,13 @@ export const map: ShorthandMapType = [ ], [ { - shorthand: 'background', - pattern: 'background-repeat background-color background-image background-attachment background-clip background-origin background-position background-size', - keywords: ['none'], - default: [], - multiple: true, - separator: {typ: 'Comma'} - }, + shorthand: 'background', + pattern: 'background-repeat background-color background-image background-attachment background-clip background-origin background-position background-size', + keywords: ['none'], + default: [], + multiple: true, + separator: {typ: 'Comma'} + }, [ { shorthand: 'background-repeat',