Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 25 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -183,10 +183,26 @@ whether the first character in the argument is a quotation mark.
] }
```

### interpolation

An interpolation of value, e.g. Sass interpolation `#{rgb(0,0,0)}`.

Interpolation nodes have nodes all other nodes nested within them.

Additional properties:

- **value**: Interpolation prefix, e.g. `#` in `#{rgb(0,0,0)}`.
- **before**: Whitespace after the opening curly bracket and before the first value,
e.g. ` ` in `#{ rgb(0,0,0)}`.
- **after**: Whitespace before the closing curly bracket and after the last value,
e.g. ` ` in `#{rgb(0,0,0) }`.
- **nodes**: More nodes representing the arguments to the interpolation.
- **unclosed**: `true` if the curly bracket was not closed properly. e.g. `#{ unclosed-interpolation `.

### unicode-range

The unicode-range CSS descriptor sets the specific range of characters to be
used from a font defined by @font-face and made available
The unicode-range CSS descriptor sets the specific range of characters to be
used from a font defined by @font-face and made available
for use on the current page (`unicode-range: U+0025-00FF`).

Node-specific properties:
Expand Down Expand Up @@ -242,10 +258,16 @@ The `callback` is invoked with three arguments: `callback(node, index, nodes)`.

Returns the `valueParser` instance.

### var parsed = valueParser(value)
### var parsed = valueParser(value, options)

Returns the parsed node tree.

### options

#### options.interpolationPrefix

Prefix used for interpolation, e.g. `#` for Sass interpolation.

### parsed.nodes

The array of nodes.
Expand Down
23 changes: 22 additions & 1 deletion lib/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,18 @@ declare namespace postcssValueParser {
nodes: Node[];
}

interface InterpolationNode
extends BaseNode,
ClosableNode,
AdjacentAwareNode {
type: "interpolation";

/**
* Nodes inside the interpolation
*/
nodes: Node[];
}

interface SpaceNode extends BaseNode {
type: "space";
}
Expand Down Expand Up @@ -75,6 +87,7 @@ declare namespace postcssValueParser {
| CommentNode
| DivNode
| FunctionNode
| InterpolationNode
| SpaceNode
| StringNode
| UnicodeRangeNode
Expand Down Expand Up @@ -124,6 +137,13 @@ declare namespace postcssValueParser {
walk(callback: WalkCallback, bubble?: boolean): this;
}

interface ValueParserOptions {
/**
* Prefix used for interpolation, e.g. `#` for Sass interpolation.
*/
interpolationPrefix?: string;
}

interface ValueParser {
/**
* Decompose a CSS dimension into its numeric and unit part
Expand Down Expand Up @@ -162,8 +182,9 @@ declare namespace postcssValueParser {
* Parse a CSS value into a series of nodes to operate on
*
* @param value The value to parse
* @param options Value parser options
*/
(value: string): ParsedValue;
(value: string, options?: ValueParserOptions): ParsedValue;
}
}

Expand Down
6 changes: 3 additions & 3 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ var parse = require("./parse");
var walk = require("./walk");
var stringify = require("./stringify");

function ValueParser(value) {
function ValueParser(value, options) {
if (this instanceof ValueParser) {
this.nodes = parse(value);
this.nodes = parse(value, options);
return this;
}
return new ValueParser(value);
return new ValueParser(value, options);
}

ValueParser.prototype.toString = function() {
Expand Down
39 changes: 31 additions & 8 deletions lib/parse.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ var star = "*".charCodeAt(0);
var uLower = "u".charCodeAt(0);
var uUpper = "U".charCodeAt(0);
var plus = "+".charCodeAt(0);
var openInterpolation = "{".charCodeAt(0);
var closeInterpolation = "}".charCodeAt(0);
var isUnicodeRange = /^[a-f0-9?-]+$/i;

module.exports = function(input) {
module.exports = function(input, options) {
var tokens = [];
var value = input;

Expand All @@ -35,6 +37,17 @@ module.exports = function(input) {
var before = "";
var after = "";

options = options || {};
var interpolationPrefix = null;
var interpolationPrefixCode = null;
if (
typeof options.interpolationPrefix !== "undefined" &&
options.interpolationPrefix !== null
) {
interpolationPrefix = options.interpolationPrefix;
interpolationPrefixCode = interpolationPrefix.charCodeAt(0);
}

while (pos < max) {
// Whitespaces
if (code <= 32) {
Expand All @@ -46,7 +59,10 @@ module.exports = function(input) {
token = value.slice(pos, next);

prev = tokens[tokens.length - 1];
if (code === closeParentheses && balanced) {
if (
(code === closeParentheses || code === closeInterpolation) &&
balanced
) {
after = token;
} else if (prev && prev.type === "div") {
prev.after = token;
Expand Down Expand Up @@ -151,7 +167,8 @@ module.exports = function(input) {
code = value.charCodeAt(pos);

// Open parentheses
} else if (openParentheses === code) {
} else if (openParentheses === code || openInterpolation === code) {
var isFunction = openParentheses === code;
// Whitespaces after open parentheses
next = pos;
do {
Expand All @@ -160,9 +177,9 @@ module.exports = function(input) {
} while (code <= 32);
parenthesesOpenPos = pos;
token = {
type: "function",
type: isFunction ? "function" : "interpolation",
sourceIndex: pos - name.length,
value: name,
value: isFunction ? name : interpolationPrefix,
before: value.slice(parenthesesOpenPos + 1, next)
};
pos = next;
Expand Down Expand Up @@ -230,7 +247,10 @@ module.exports = function(input) {
name = "";

// Close parentheses
} else if (closeParentheses === code && balanced) {
} else if (
(code === closeParentheses || code === closeInterpolation) &&
balanced
) {
pos += 1;
code = value.charCodeAt(pos);

Expand Down Expand Up @@ -260,19 +280,22 @@ module.exports = function(input) {
code === colon ||
code === slash ||
code === openParentheses ||
(interpolationPrefix !== null && code === openInterpolation) ||
(interpolationPrefix !== null && code === interpolationPrefixCode) ||
(code === star &&
parent &&
parent.type === "function" &&
parent.value === "calc") ||
(code === slash &&
parent.type === "function" &&
parent.value === "calc") ||
(code === closeParentheses && balanced)
((code === closeParentheses || code === closeInterpolation) &&
balanced)
)
);
token = value.slice(pos, next);

if (openParentheses === code) {
if (openParentheses === code || openInterpolation === code) {
name = token;
} else if (
(uLower === token.charCodeAt(0) || uUpper === token.charCodeAt(0)) &&
Expand Down
7 changes: 4 additions & 3 deletions lib/stringify.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,17 @@ function stringifyNode(node, custom) {
return (node.before || "") + value + (node.after || "");
} else if (Array.isArray(node.nodes)) {
buf = stringify(node.nodes, custom);
if (type !== "function") {
if (type !== "function" && type !== "interpolation") {
return buf;
}
var isFunction = type === "function";
return (
value +
"(" +
(isFunction ? "(" : "{") +
(node.before || "") +
buf +
(node.after || "") +
(node.unclosed ? "" : ")")
(node.unclosed ? "" : isFunction ? ")" : "}")
);
}
return value;
Expand Down
2 changes: 1 addition & 1 deletion lib/walk.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ module.exports = function walk(nodes, cb, bubble) {

if (
result !== false &&
node.type === "function" &&
(node.type === "function" || node.type === "interpolation") &&
Array.isArray(node.nodes)
) {
walk(node.nodes, cb, bubble);
Expand Down
131 changes: 130 additions & 1 deletion test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ test("ValueParser", function(tp) {
});

tp.test("walk", function(t) {
t.plan(4);
t.plan(7);
var result;

result = [];
Expand Down Expand Up @@ -74,6 +74,135 @@ test("ValueParser", function(tp) {

result = [];

parser("fn( ) #{fn2( fn3())}", { interpolationPrefix: "#" }).walk(function(
node
) {
if (node.type === "interpolation") {
result.push(node);
}
});

t.deepEqual(
result,
[
{
type: "interpolation",
sourceIndex: 6,
value: "#",
before: "",
after: "",
nodes: [
{
type: "function",
sourceIndex: 8,
value: "fn2",
before: " ",
after: "",
nodes: [
{
type: "function",
sourceIndex: 13,
value: "fn3",
before: "",
after: "",
nodes: []
}
]
}
]
}
],
"should process all interpolations"
);

result = [];

parser("fn( ) --#{fn2( fn3())}", { interpolationPrefix: "#" }).walk(
function(node) {
if (node.type === "interpolation") {
result.push(node);
}
}
);

t.deepEqual(
result,
[
{
type: "interpolation",
sourceIndex: 8,
value: "#",
before: "",
after: "",
nodes: [
{
type: "function",
sourceIndex: 10,
value: "fn2",
before: " ",
after: "",
nodes: [
{
type: "function",
sourceIndex: 15,
value: "fn3",
before: "",
after: "",
nodes: []
}
]
}
]
}
],
"should process all interpolations with word"
);

result = [];

parser("fn( ) /#{fn2( fn3())}", { interpolationPrefix: "#" }).walk(function(
node
) {
if (node.type === "interpolation") {
result.push(node);
}
});

t.deepEqual(
result,
[
{
type: "interpolation",
sourceIndex: 7,
value: "#",
before: "",
after: "",
nodes: [
{
type: "function",
sourceIndex: 9,
value: "fn2",
before: " ",
after: "",
nodes: [
{
type: "function",
sourceIndex: 14,
value: "fn3",
before: "",
after: "",
nodes: []
}
]
}
]
}
],
"should process all interpolations with divider (/)"
);

result = [];

parser("fn( ) fn2( fn3())").walk(function(node) {
if (node.type === "function") {
result.push(node);
Expand Down
Loading