diff --git a/lib/index.d.ts b/lib/index.d.ts new file mode 100644 index 0000000..36c052c --- /dev/null +++ b/lib/index.d.ts @@ -0,0 +1,172 @@ +declare namespace postcssValueParser { + interface BaseNode { + /** + * The offset inside the CSS value at which the node starts + */ + sourceIndex: number; + + /** + * The node's characteristic value + */ + value: string; + } + + interface ClosableNode { + /** + * Whether the parsed CSS value ended before the node was properly closed + */ + unclosed?: true; + } + + interface AdjacentAwareNode { + /** + * The token at the start of the node + */ + before: string; + + /** + * The token at the end of the node + */ + after: string; + } + + interface CommentNode extends BaseNode, ClosableNode { + type: "comment"; + } + + interface DivNode extends BaseNode, AdjacentAwareNode { + type: "div"; + } + + interface FunctionNode extends BaseNode, ClosableNode, AdjacentAwareNode { + type: "function"; + + /** + * Nodes inside the function + */ + nodes: Node[]; + } + + interface SpaceNode extends BaseNode { + type: "space"; + } + + interface StringNode extends BaseNode, ClosableNode { + type: "string"; + + /** + * The quote type delimiting the string + */ + quote: '"' | "'"; + } + + interface UnicodeRangeNode extends BaseNode { + type: "unicode-range"; + } + + interface WordNode extends BaseNode { + type: "word"; + } + + /** + * Any node parsed from a CSS value + */ + type Node = + | CommentNode + | DivNode + | FunctionNode + | SpaceNode + | StringNode + | UnicodeRangeNode + | WordNode; + + interface CustomStringifierCallback { + /** + * @param node The node to stringify + * @returns The serialized CSS representation of the node + */ + (nodes: Node): string; + } + + interface WalkCallback { + /** + * @param node The currently visited node + * @param index The index of the node in the series of parsed nodes + * @param nodes The series of parsed nodes + * @returns Returning `false` will prevent traversal of descendant nodes (only applies if `bubble` was set to `true` in the `walk()` call) + */ + (node: Node, index: number, nodes: Node[]): void | false; + } + + /** + * A CSS dimension, decomposed into its numeric and unit parts + */ + interface Dimension { + number: string; + unit: string; + } + + /** + * A wrapper around a parsed CSS value that allows for inspecting and walking nodes + */ + interface ParsedValue { + /** + * The series of parsed nodes + */ + nodes: Node[]; + + /** + * Walk all parsed nodes, applying a callback + * + * @param callback A visitor callback that will be executed for each node + * @param bubble When set to `true`, walking will be done inside-out instead of outside-in + */ + walk(callback: WalkCallback, bubble?: boolean): this; + } + + interface ValueParser { + /** + * Decompose a CSSĀ dimension into its numeric and unit part + * + * @param value The dimension to decompose + * @returns An object representing `number` and `unit` part of the dimension or `false` if the decomposing fails + */ + unit(value: string): Dimension | false; + + /** + * Serialize a series of nodes into a CSS value + * + * @param nodes The nodes to stringify + * @param custom A custom stringifier callback + * @returns The generated CSS value + */ + stringify(nodes: Node[], custom?: CustomStringifierCallback): string; + + /** + * Walk a series of nodes, applying a callback + * + * @param nodes The nodes to walk + * @param callback A visitor callback that will be executed for each node + * @param bubble When set to `true`, walking will be done inside-out instead of outside-in + */ + walk(nodes: Node[], callback: WalkCallback, bubble?: boolean): void; + + /** + * Parse a CSS value into a series of nodes to operate on + * + * @param value The value to parse + */ + new (value: string): ParsedValue; + + /** + * Parse a CSS value into a series of nodes to operate on + * + * @param value The value to parse + */ + (value: string): ParsedValue; + } +} + +declare const postcssValueParser: postcssValueParser.ValueParser; + +export = postcssValueParser; diff --git a/lib/parse.js b/lib/parse.js index d9d995f..502b5ba 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -53,7 +53,10 @@ module.exports = function(input) { } else if ( code === comma || code === colon || - (code === slash && value.charCodeAt(next + 1) !== star) + (code === slash && + value.charCodeAt(next + 1) !== star && + (!parent || + (parent && parent.type === "function" && parent.value !== "calc"))) ) { before = token; } else { diff --git a/lib/unit.js b/lib/unit.js index 884d223..09d6142 100644 --- a/lib/unit.js +++ b/lib/unit.js @@ -22,6 +22,28 @@ module.exports = function(value) { break; } sciPos = pos; + + var nextCode = value.charCodeAt(pos + 1); + + if ( + nextCode === plus || + nextCode === minus || + (nextCode >= 48 && nextCode <= 57) + ) { + if (nextCode === plus || nextCode === minus) { + var nextNextCode = value.charCodeAt(pos + 2); + + if (nextNextCode < 48 || nextNextCode > 57) { + break; + } + + pos += 1; + } + + pos += 1; + } else { + break; + } } else if (code === dot) { if (dotted) { break; diff --git a/package.json b/package.json index 35122da..875ad2d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "postcss-value-parser", - "version": "4.0.0", + "version": "4.0.1", "description": "Transforms css values and at-rule params into the tree", "main": "lib/index.js", "files": [ diff --git a/test/parse.js b/test/parse.js index 5cd41e0..41fdc53 100644 --- a/test/parse.js +++ b/test/parse.js @@ -492,6 +492,46 @@ var tests = [ } ] }, + { + message: "should correctly parse subtraction with spaces", + fixture: "calc(1 - 2)", + expected: [ + { + type: "function", + sourceIndex: 0, + value: "calc", + before: "", + after: "", + nodes: [ + { + type: "word", + sourceIndex: 5, + value: "1" + }, + { + type: "space", + sourceIndex: 6, + value: " " + }, + { + type: "word", + sourceIndex: 7, + value: "-" + }, + { + type: "space", + sourceIndex: 8, + value: " " + }, + { + type: "word", + sourceIndex: 9, + value: "2" + } + ] + } + ] + }, { message: "should correctly parse multiplication with spaces", fixture: "calc(1 * 2)", @@ -532,6 +572,46 @@ var tests = [ } ] }, + { + message: "should correctly parse division with spaces", + fixture: "calc(1 / 2)", + expected: [ + { + type: "function", + sourceIndex: 0, + value: "calc", + before: "", + after: "", + nodes: [ + { + type: "word", + sourceIndex: 5, + value: "1" + }, + { + type: "space", + sourceIndex: 6, + value: " " + }, + { + type: "word", + sourceIndex: 7, + value: "/" + }, + { + type: "space", + sourceIndex: 8, + value: " " + }, + { + type: "word", + sourceIndex: 9, + value: "2" + } + ] + } + ] + }, { message: "should correctly parse multiplication without spaces", fixture: "calc(1*2)", @@ -562,6 +642,36 @@ var tests = [ } ] }, + { + message: "should correctly parse division without spaces", + fixture: "calc(1/2)", + expected: [ + { + type: "function", + sourceIndex: 0, + value: "calc", + before: "", + after: "", + nodes: [ + { + type: "word", + sourceIndex: 5, + value: "1" + }, + { + type: "word", + sourceIndex: 6, + value: "/" + }, + { + type: "word", + sourceIndex: 7, + value: "2" + } + ] + } + ] + }, { message: "should correctly process nested calc functions", fixture: "calc(((768px - 100vw) / 2) - 15px)", diff --git a/test/unit.js b/test/unit.js index 87279ed..57ec4fa 100644 --- a/test/unit.js +++ b/test/unit.js @@ -57,6 +57,98 @@ var tests = [ { fixture: "e1", expected: false + }, + { + fixture: "2rem", + expected: { number: "2", unit: "rem" } + }, + { + fixture: "2.000rem", + expected: { number: "2.000", unit: "rem" } + }, + { + fixture: "+2rem", + expected: { number: "+2", unit: "rem" } + }, + { + fixture: "-2rem", + expected: { number: "-2", unit: "rem" } + }, + { + fixture: "1.1rem", + expected: { number: "1.1", unit: "rem" } + }, + { + fixture: "+1.1rem", + expected: { number: "+1.1", unit: "rem" } + }, + { + fixture: "-1.1rem", + expected: { number: "-1.1", unit: "rem" } + }, + { + fixture: "1.1e1rem", + expected: { number: "1.1e1", unit: "rem" } + }, + { + fixture: "+1.1e1rem", + expected: { number: "+1.1e1", unit: "rem" } + }, + { + fixture: "-1.1e1rem", + expected: { number: "-1.1e1", unit: "rem" } + }, + { + fixture: "1.1e+1rem", + expected: { number: "1.1e+1", unit: "rem" } + }, + { + fixture: "1.1e-1rem", + expected: { number: "1.1e-1", unit: "rem" } + }, + { + fixture: "1.1e1e1rem", + expected: { number: "1.1e1", unit: "e1rem" } + }, + { + fixture: "1.1e-1e", + expected: { number: "1.1e-1", unit: "e" } + }, + { + fixture: "1.1e-1rem", + expected: { number: "1.1e-1", unit: "rem" } + }, + { + fixture: "1.1e--++1e", + expected: { number: "1.1", unit: "e--++1e" } + }, + { + fixture: "1.1e--++1rem", + expected: { number: "1.1", unit: "e--++1rem" } + }, + { + fixture: "100+px", + expected: { number: "100", unit: "+px" } + }, + { + fixture: "100.0.0px", + expected: { number: "100.0", unit: ".0px" } + }, + { + fixture: "100e1epx", + expected: { number: "100e1", unit: "epx" } + }, + { + fixture: "100e1e1px", + expected: { number: "100e1", unit: "e1px" } + }, + { + fixture: "+100.1e+1e+1px", + expected: { number: "+100.1e+1", unit: "e+1px" } + }, + { + fixture: "-100.1e-1e-1px", + expected: { number: "-100.1e-1", unit: "e-1px" } } ];