diff --git a/.gitignore b/.gitignore index 5da1b01..8f6dfad 100644 --- a/.gitignore +++ b/.gitignore @@ -3,5 +3,5 @@ npm-debug.log /src/*.js* /src/**/*.js* /tests/*.js* -/lib/* -/.vscode \ No newline at end of file +/lib/tests/* +/.vscode diff --git a/README.md b/README.md index 1486364..8380396 100644 --- a/README.md +++ b/README.md @@ -8,11 +8,11 @@ [ci-img]: https://travis-ci.org/lexich/postcss-stream.svg [ci]: https://travis-ci.org/lexich/postcss-stream -Add support to postcss stream api. +Add support to postcss stream api. ```js const postcssStream = require('postcss-stream'); -const stream1 = postcssStream.createStream({ +const stream1 = { // select only nodes with type="decl" prop="color" value="red" decl: { prop: "color", @@ -21,7 +21,7 @@ const stream1 = postcssStream.createStream({ decl.value = "green"; } } -}); +}; postcssStream([ stream, ... @@ -32,8 +32,8 @@ postcssStream([ Documentation in process. ##Reimplementation existing postcss-plugins: -Original: [postcss-color-function](https://github.com/postcss/postcss-color-function/blob/master/index.js) -Stream: [postcss-color-function](https://github.com/lexich/postcss-stream/blob/master/tests/fixtures/postcss-color-function/index.ts) +Original: [postcss-color-function](https://github.com/postcss/postcss-color-function/blob/master/index.js) +Stream: [postcss-color-function](https://github.com/lexich/postcss-stream/blob/master/tests/fixtures/postcss-color-function/index.ts) -Original: [postcss-grid](https://github.com/andyjansson/postcss-grid) -Stream: [postcss-grid](https://github.com/lexich/postcss-stream/blob/master/tests/fixtures/postcss-grid/index.ts) +Original: [postcss-grid](https://github.com/andyjansson/postcss-grid) +Stream: [postcss-grid](https://github.com/lexich/postcss-stream/blob/master/tests/fixtures/postcss-grid/index.ts) diff --git a/lib/src/comparator.js b/lib/src/comparator.js new file mode 100644 index 0000000..f3da9a6 --- /dev/null +++ b/lib/src/comparator.js @@ -0,0 +1,29 @@ +"use strict"; +function comparator(cmp, prop, node) { + if (cmp === null || cmp === undefined) { + return true; + } + else if (typeof cmp === "string" || cmp instanceof String) { + return cmp === prop; + } + else if (cmp instanceof RegExp) { + return cmp.test(prop); + } + else if (cmp instanceof Function) { + return cmp(node); + } + else if (Array.isArray(cmp)) { + for (let state of cmp) { + if (comparator(state, prop, node)) { + return true; + } + } + return false; + } + else { + return false; + } +} +Object.defineProperty(exports, "__esModule", { value: true }); +exports.default = comparator; +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29tcGFyYXRvci5qcyIsInNvdXJjZVJvb3QiOiJzcmMvIiwic291cmNlcyI6WyJzcmMvY29tcGFyYXRvci50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBR0Esb0JBQW1DLEdBQWdCLEVBQUUsSUFBWSxFQUFFLElBQWtCO0lBQ2pGLEVBQUUsQ0FBQyxDQUFDLEdBQUcsS0FBSyxJQUFJLElBQUksR0FBRyxLQUFLLFNBQVMsQ0FBQyxDQUFDLENBQUM7UUFDcEMsTUFBTSxDQUFDLElBQUksQ0FBQztJQUNoQixDQUFDO0lBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFDLE9BQU8sR0FBRyxLQUFLLFFBQVEsSUFBSSxHQUFHLFlBQVksTUFBTSxDQUFDLENBQUMsQ0FBQztRQUMxRCxNQUFNLENBQUMsR0FBRyxLQUFLLElBQUksQ0FBQztJQUN4QixDQUFDO0lBQUMsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFDLEdBQUcsWUFBWSxNQUFNLENBQUMsQ0FBQyxDQUFDO1FBQy9CLE1BQU0sQ0FBQyxHQUFHLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQzFCLENBQUM7SUFBQyxJQUFJLENBQUMsRUFBRSxDQUFDLENBQUMsR0FBRyxZQUFZLFFBQVEsQ0FBQyxDQUFDLENBQUM7UUFDakMsTUFBTSxDQUFFLEdBQXNCLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDekMsQ0FBQztJQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUM1QixHQUFHLENBQUMsQ0FBQyxJQUFJLEtBQUssSUFBSSxHQUFHLENBQUMsQ0FBQyxDQUFDO1lBQ3BCLEVBQUUsQ0FBQyxDQUFDLFVBQVUsQ0FBQyxLQUFLLEVBQUUsSUFBSSxFQUFFLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztnQkFDaEMsTUFBTSxDQUFDLElBQUksQ0FBQztZQUNoQixDQUFDO1FBQ0wsQ0FBQztRQUNELE1BQU0sQ0FBQyxLQUFLLENBQUM7SUFDakIsQ0FBQztJQUFDLElBQUksQ0FBQyxDQUFDO1FBRUosTUFBTSxDQUFDLEtBQUssQ0FBQztJQUNqQixDQUFDO0FBQ0wsQ0FBQzs7QUFwQkQsNkJBb0JDIn0= \ No newline at end of file diff --git a/lib/src/compile.js b/lib/src/compile.js new file mode 100644 index 0000000..5cd7d3f --- /dev/null +++ b/lib/src/compile.js @@ -0,0 +1,40 @@ +"use strict"; +const compileMatcher_1 = require("./compileMatcher"); +const ATTRS = ['decl', 'rule', 'atrule', 'comment', 'root']; +function compile(query, iter, parent, container) { + if (!query) { + return null; + } + let anyQuery; + if (!container) { + container = { enter: [], leave: [] }; + } + for (let type of ATTRS) { + anyQuery = query[type]; + if (anyQuery) { + const match = compileMatcher_1.default(query, type); + const visitEnter = { + type, match, parent, + ref: iter.val, + fn: anyQuery.enter, + }; + iter.val += 1; + if (visitEnter.fn) { + container.enter[container.enter.length] = visitEnter; + } + compile(anyQuery, iter, visitEnter, container); + if (anyQuery.leave) { + container.leave[container.leave.length] = { + type, match, parent, + ref: iter.val, + fn: anyQuery.leave, + }; + } + iter.val += 1; + } + } + return container; +} +Object.defineProperty(exports, "__esModule", { value: true }); +exports.default = compile; +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29tcGlsZS5qcyIsInNvdXJjZVJvb3QiOiJzcmMvIiwic291cmNlcyI6WyJzcmMvY29tcGlsZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUEscURBQThDO0FBRzlDLE1BQU0sS0FBSyxHQUFHLENBQUMsTUFBTSxFQUFFLE1BQU0sRUFBRSxRQUFRLEVBQUUsU0FBUyxFQUFFLE1BQU0sQ0FBQyxDQUFDO0FBQzVELGlCQUFnQyxLQUFZLEVBQUUsSUFBbUIsRUFBRSxNQUFlLEVBQUUsU0FBNEI7SUFDNUcsRUFBRSxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsQ0FBQyxDQUFDO1FBQUMsTUFBTSxDQUFDLElBQUksQ0FBQztJQUFDLENBQUM7SUFDNUIsSUFBSSxRQUFhLENBQUM7SUFDbEIsRUFBRSxDQUFDLENBQUMsQ0FBQyxTQUFTLENBQUMsQ0FBQyxDQUFDO1FBQ2IsU0FBUyxHQUFHLEVBQUUsS0FBSyxFQUFDLEVBQUUsRUFBRSxLQUFLLEVBQUUsRUFBRSxFQUFFLENBQUM7SUFDeEMsQ0FBQztJQUNELEdBQUcsQ0FBQyxDQUFDLElBQUksSUFBSSxJQUFJLEtBQUssQ0FBQyxDQUFDLENBQUM7UUFDckIsUUFBUSxHQUFJLEtBQWEsQ0FBQyxJQUFJLENBQUMsQ0FBQztRQUNoQyxFQUFFLENBQUMsQ0FBQyxRQUFRLENBQUMsQ0FBQyxDQUFDO1lBQ1gsTUFBTSxLQUFLLEdBQUcsd0JBQWMsQ0FBQyxLQUFLLEVBQUUsSUFBSSxDQUFDLENBQUM7WUFDMUMsTUFBTSxVQUFVLEdBQUc7Z0JBQ2YsSUFBSSxFQUFFLEtBQUssRUFBRSxNQUFNO2dCQUNuQixHQUFHLEVBQUUsSUFBSSxDQUFDLEdBQUc7Z0JBQ2IsRUFBRSxFQUFFLFFBQVEsQ0FBQyxLQUFLO2FBQ3JCLENBQUM7WUFDRixJQUFJLENBQUMsR0FBRyxJQUFJLENBQUMsQ0FBQztZQUNkLEVBQUUsQ0FBQyxDQUFDLFVBQVUsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFDO2dCQUNoQixTQUFTLENBQUMsS0FBSyxDQUFDLFNBQVMsQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLEdBQUcsVUFBVSxDQUFDO1lBQ3pELENBQUM7WUFDRCxPQUFPLENBQUMsUUFBUSxFQUFFLElBQUksRUFBRSxVQUFVLEVBQUUsU0FBUyxDQUFDLENBQUM7WUFDL0MsRUFBRSxDQUFDLENBQUMsUUFBUSxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUM7Z0JBQ2pCLFNBQVMsQ0FBQyxLQUFLLENBQUMsU0FBUyxDQUFDLEtBQUssQ0FBQyxNQUFNLENBQUMsR0FBSTtvQkFDdkMsSUFBSSxFQUFFLEtBQUssRUFBRSxNQUFNO29CQUNuQixHQUFHLEVBQUUsSUFBSSxDQUFDLEdBQUc7b0JBQ2IsRUFBRSxFQUFFLFFBQVEsQ0FBQyxLQUFLO2lCQUNyQixDQUFDO1lBQ04sQ0FBQztZQUNELElBQUksQ0FBQyxHQUFHLElBQUksQ0FBQyxDQUFDO1FBQ2xCLENBQUM7SUFDTCxDQUFDO0lBQ0QsTUFBTSxDQUFDLFNBQVMsQ0FBQztBQUNyQixDQUFDOztBQS9CRCwwQkErQkMifQ== \ No newline at end of file diff --git a/lib/src/compileMatcher.js b/lib/src/compileMatcher.js new file mode 100644 index 0000000..3eadc14 --- /dev/null +++ b/lib/src/compileMatcher.js @@ -0,0 +1,96 @@ +"use strict"; +const comparator_1 = require("./comparator"); +function trueMatcher(node) { + return true; +} +const MATCHER = { + decl({ decl }) { + if (!decl) { + return trueMatcher; + } + return function (node) { + if (!node || node.type !== "decl") { + return false; + } + if (this.important !== undefined && this.important !== node.important) { + return false; + } + if (this.prop !== undefined && !comparator_1.default(this.prop, node.prop, node)) { + return false; + } + if (this.value !== undefined && !comparator_1.default(this.value, node.value, node)) { + return false; + } + return true; + }.bind(decl); + }, + rule({ rule }) { + if (!rule) { + return trueMatcher; + } + return function (node) { + if (!node || node.type !== "rule") { + return false; + } + if (this.selector !== undefined) { + let isMatch = false; + for (let item of node.selectors) { + if (comparator_1.default(this.selector, item, node)) { + isMatch = true; + break; + } + } + if (!isMatch) { + return false; + } + } + return true; + }.bind(rule); + }, + atrule({ atrule }) { + if (!atrule) { + return trueMatcher; + } + return function (node) { + if (!node || node.type !== "atrule") { + return false; + } + if (this.name !== undefined && !comparator_1.default(this.name, node.name, node)) { + return false; + } + if (this.params !== undefined && !comparator_1.default(this.params, node.params, node)) { + return false; + } + return true; + }.bind(atrule); + }, + comment({ comment }) { + if (!comment) { + return trueMatcher; + } + return function (node) { + if (!node || node.type !== "comment") { + return false; + } + if (this.text !== undefined && !comparator_1.default(this.text, node.text, node)) { + return false; + } + return true; + }.bind(comment); + }, + root(query) { + return trueMatcher; + } +}; +function compileMatcher(query, type) { + const matcher = MATCHER[type]; + if (matcher) { + return matcher(query); + } + else { + return trueMatcher; + } +} +Object.defineProperty(exports, "__esModule", { value: true }); +exports.default = compileMatcher; +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29tcGlsZU1hdGNoZXIuanMiLCJzb3VyY2VSb290Ijoic3JjLyIsInNvdXJjZXMiOlsic3JjL2NvbXBpbGVNYXRjaGVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFDQSw2Q0FBc0M7QUFHdEMscUJBQXFCLElBQWtCO0lBQ25DLE1BQU0sQ0FBQyxJQUFJLENBQUM7QUFDaEIsQ0FBQztBQUVELE1BQU0sT0FBTyxHQUFHO0lBQ1osSUFBSSxDQUFDLEVBQUMsSUFBSSxFQUFtQjtRQUN6QixFQUFFLENBQUMsQ0FBQyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUM7WUFBQyxNQUFNLENBQUMsV0FBVyxDQUFDO1FBQUMsQ0FBQztRQUNsQyxNQUFNLENBQUMsVUFBUyxJQUF5QjtZQUNyQyxFQUFFLENBQUMsQ0FBQyxDQUFDLElBQUksSUFBSSxJQUFJLENBQUMsSUFBSSxLQUFLLE1BQU0sQ0FBQyxDQUFDLENBQUM7Z0JBQUMsTUFBTSxDQUFDLEtBQUssQ0FBQztZQUFDLENBQUM7WUFDcEQsRUFBRSxDQUFDLENBQUMsSUFBSSxDQUFDLFNBQVMsS0FBSyxTQUFTLElBQUksSUFBSSxDQUFDLFNBQVMsS0FBSyxJQUFJLENBQUMsU0FBUyxDQUFDLENBQUMsQ0FBQztnQkFDcEUsTUFBTSxDQUFDLEtBQUssQ0FBQztZQUNqQixDQUFDO1lBQ0QsRUFBRSxDQUFDLENBQUMsSUFBSSxDQUFDLElBQUksS0FBSyxTQUFTLElBQUksQ0FBQyxvQkFBVSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsSUFBSSxDQUFDLElBQUksRUFBRSxJQUFJLENBQUMsQ0FBQyxDQUFDLENBQUM7Z0JBQ3JFLE1BQU0sQ0FBQyxLQUFLLENBQUM7WUFDakIsQ0FBQztZQUNELEVBQUUsQ0FBQyxDQUFDLElBQUksQ0FBQyxLQUFLLEtBQUssU0FBUyxJQUFJLENBQUMsb0JBQVUsQ0FBQyxJQUFJLENBQUMsS0FBSyxFQUFFLElBQUksQ0FBQyxLQUFLLEVBQUUsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO2dC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 \ No newline at end of file diff --git a/lib/src/index.js b/lib/src/index.js new file mode 100644 index 0000000..5da4639 --- /dev/null +++ b/lib/src/index.js @@ -0,0 +1,10 @@ +"use strict"; +const postcss = require("postcss"); +const traverse_1 = require("./traverse"); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.default = postcss.plugin('postcss-stream', function (queries) { + return function (css, result) { + return traverse_1.traverse(css, queries); + }; +}); +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290Ijoic3JjLyIsInNvdXJjZXMiOlsic3JjL2luZGV4LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFBQSxtQ0FBbUM7QUFDbkMseUNBQW9DOztBQUdwQyxrQkFBZSxPQUFPLENBQUMsTUFBTSxDQUFDLGdCQUFnQixFQUFFLFVBQVMsT0FBZ0I7SUFDckUsTUFBTSxDQUFDLFVBQVMsR0FBaUIsRUFBRSxNQUFzQjtRQUNyRCxNQUFNLENBQUMsbUJBQVEsQ0FBQyxHQUFHLEVBQUUsT0FBTyxDQUFDLENBQUM7SUFDbEMsQ0FBQyxDQUFDO0FBQ04sQ0FBQyxDQUFDLENBQUMifQ== \ No newline at end of file diff --git a/lib/src/interfaces.js b/lib/src/interfaces.js new file mode 100644 index 0000000..25ce848 --- /dev/null +++ b/lib/src/interfaces.js @@ -0,0 +1,2 @@ +"use strict"; +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW50ZXJmYWNlcy5qcyIsInNvdXJjZVJvb3QiOiJzcmMvIiwic291cmNlcyI6WyJzcmMvaW50ZXJmYWNlcy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiIn0= \ No newline at end of file diff --git a/lib/src/iterator.js b/lib/src/iterator.js new file mode 100644 index 0000000..c32c0dc --- /dev/null +++ b/lib/src/iterator.js @@ -0,0 +1,52 @@ +"use strict"; +let STACK_CACHE; +function default_1(css, callback) { + if (!css.nodes) { + return undefined; + } + let stack = { + prev: null, + node: css, + index: 0 + }; + let stackTmp; + let reverseWalk = false; + do { + if (!reverseWalk) { + callback(stack.node, stack.index, true); + if (stack.node.nodes && + stack.node.nodes.length > 0) { + if (STACK_CACHE) { + stackTmp = STACK_CACHE; + STACK_CACHE = STACK_CACHE.prev; + } + else { + stackTmp = { prev: null, node: null, index: 0, }; + } + stackTmp.prev = stack; + stackTmp.node = stack.node.nodes[0]; + stackTmp.index = 0; + stack = stackTmp; + stackTmp = null; + continue; + } + reverseWalk = false; + } + callback(stack.node, stack.index, false); + if (stack.prev) { + stack.index += 1; + if (stack.index < stack.prev.node.nodes.length) { + stack.node = stack.prev.node.nodes[stack.index]; + continue; + } + } + stackTmp = stack; + stack = stack.prev; + STACK_CACHE = stackTmp; + stackTmp = null; + reverseWalk = true; + } while (!!stack); +} +Object.defineProperty(exports, "__esModule", { value: true }); +exports.default = default_1; +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaXRlcmF0b3IuanMiLCJzb3VyY2VSb290Ijoic3JjLyIsInNvdXJjZXMiOlsic3JjL2l0ZXJhdG9yLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFRQSxJQUFJLFdBQWtCLENBQUM7QUFFdkIsbUJBQXdCLEdBQWlCLEVBQUUsUUFBcUU7SUFDNUcsRUFBRSxDQUFDLENBQUMsQ0FBQyxHQUFHLENBQUMsS0FBSyxDQUFDLENBQUMsQ0FBQztRQUNiLE1BQU0sQ0FBQyxTQUFTLENBQUM7SUFDckIsQ0FBQztJQUNELElBQUksS0FBSyxHQUFVO1FBQ2YsSUFBSSxFQUFFLElBQUk7UUFDVixJQUFJLEVBQUUsR0FBRztRQUNULEtBQUssRUFBRSxDQUFDO0tBQ1gsQ0FBQztJQUNGLElBQUksUUFBZSxDQUFDO0lBQ3BCLElBQUksV0FBVyxHQUFHLEtBQUssQ0FBQztJQUN4QixHQUFHLENBQUM7UUFDQSxFQUFFLENBQUMsQ0FBQyxDQUFDLFdBQVcsQ0FBQyxDQUFDLENBQUM7WUFDZixRQUFRLENBQUMsS0FBSyxDQUFDLElBQUksRUFBRSxLQUFLLENBQUMsS0FBSyxFQUFFLElBQUksQ0FBQyxDQUFDO1lBQ3hDLEVBQUUsQ0FBQyxDQUFFLEtBQUssQ0FBQyxJQUEwQixDQUFDLEtBQUs7Z0JBQ3RDLEtBQUssQ0FBQyxJQUEwQixDQUFDLEtBQUssQ0FBQyxNQUFNLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQztnQkFDckQsRUFBRSxDQUFDLENBQUMsV0FBVyxDQUFDLENBQUMsQ0FBQztvQkFDZCxRQUFRLEdBQUcsV0FBVyxDQUFDO29CQUN2QixXQUFXLEdBQUcsV0FBVyxDQUFDLElBQUksQ0FBQztnQkFDbkMsQ0FBQztnQkFBQyxJQUFJLENBQUMsQ0FBQztvQkFDSixRQUFRLEdBQUcsRUFBRSxJQUFJLEVBQUUsSUFBSSxFQUFFLElBQUksRUFBRSxJQUFJLEVBQUUsS0FBSyxFQUFFLENBQUMsR0FBRyxDQUFDO2dCQUNyRCxDQUFDO2dCQUNELFFBQVEsQ0FBQyxJQUFJLEdBQUcsS0FBSyxDQUFDO2dCQUN0QixRQUFRLENBQUMsSUFBSSxHQUFJLEtBQUssQ0FBQyxJQUEwQixDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUMsQ0FBQztnQkFDM0QsUUFBUSxDQUFDLEtBQUssR0FBRyxDQUFDLENBQUM7Z0JBQ25CLEtBQUssR0FBRyxRQUFRLENBQUM7Z0JBQ2pCLFFBQVEsR0FBRyxJQUFJLENBQUM7Z0JBRWhCLFFBQVEsQ0FBQztZQUNiLENBQUM7WUFDRCxXQUFXLEdBQUcsS0FBSyxDQUFDO1FBQ3hCLENBQUM7UUFDRCxRQUFRLENBQUMsS0FBSyxDQUFDLElBQUksRUFBRSxLQUFLLENBQUMsS0FBSyxFQUFFLEtBQUssQ0FBQyxDQUFDO1FBRXpDLEVBQUUsQ0FBQyxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO1lBQ2IsS0FBSyxDQUFDLEtBQUssSUFBSSxDQUFDLENBQUM7WUFDakIsRUFBRSxDQUFDLENBQUMsS0FBSyxDQUFDLEtBQUssR0FBSSxLQUFLLENBQUMsSUFBSSxDQUFDLElBQTBCLENBQUMsS0FBSyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUM7Z0JBQ3BFLEtBQUssQ0FBQyxJQUFJLEdBQUksS0FBSyxDQUFDLElBQUksQ0FBQyxJQUEwQixDQUFDLEtBQUssQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLENBQUM7Z0JBRXZFLFFBQVEsQ0FBQztZQUNiLENBQUM7UUFDTCxDQUFDO1FBRUQsUUFBUSxHQUFHLEtBQUssQ0FBQztRQUNqQixLQUFLLEdBQUcsS0FBSyxDQUFDLElBQUksQ0FBQztRQUNuQixXQUFXLEdBQUcsUUFBUSxDQUFDO1FBQ3ZCLFFBQVEsR0FBRyxJQUFJLENBQUM7UUFFaEIsV0FBVyxHQUFHLElBQUksQ0FBQztJQUN2QixDQUFDLFFBQVEsQ0FBQyxDQUFDLEtBQUssRUFBRTtBQUN0QixDQUFDOztBQWxERCw0QkFrREMifQ== \ No newline at end of file diff --git a/lib/src/linkedlist.js b/lib/src/linkedlist.js new file mode 100644 index 0000000..2339b10 --- /dev/null +++ b/lib/src/linkedlist.js @@ -0,0 +1,85 @@ +"use strict"; +exports.NONE = typeof Symbol === "function" ? Symbol.for("NONE") : {}; +let removedItems = { data: null, prev: null, next: null }; +function persist(item) { + item.next = removedItems; + item.prev = null; + item.data = null; + removedItems = item; +} +exports.persist = persist; +function isEmpty(list) { + if (!list) { + return true; + } + else { + return list.prev === list.next && list.prev === list; + } +} +exports.isEmpty = isEmpty; +function init() { + const list = { data: exports.NONE, next: null, prev: null }; + list.next = list; + list.prev = list; + return list; +} +exports.init = init; +function add(data, list) { + if (!list) { + list = init(); + } + if (data === exports.NONE) { + return list; + } + let item; + const last = list.prev; + if (!removedItems.next) { + item = { + data: data, + prev: last, + next: list + }; + } + else { + item = removedItems; + removedItems = removedItems.next; + item.data = data; + item.prev = last; + item.next = list; + } + last.next = item; + list.prev = item; + return list; +} +exports.add = add; +function shift(list) { + if (list.prev === list.next && list.prev === list) { + return null; + } + else { + const first = list.next; + const second = first.next; + list.next = second; + second.prev = list; + const data = first.data; + persist(first); + return data === exports.NONE ? null : data; + } +} +exports.shift = shift; +function pop(list) { + if (list.prev === list.next && list.prev === list) { + return null; + } + else { + const first = list.prev; + const second = first.prev; + list.prev = second; + second.next = list; + const data = first.data; + persist(first); + return data === exports.NONE ? null : data; + } +} +exports.pop = pop; +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibGlua2VkbGlzdC5qcyIsInNvdXJjZVJvb3QiOiJzcmMvIiwic291cmNlcyI6WyJzcmMvbGlua2VkbGlzdC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQWEsUUFBQSxJQUFJLEdBQUcsT0FBTyxNQUFNLEtBQUssVUFBVSxHQUFHLE1BQU0sQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFDLEdBQUksRUFBYSxDQUFDO0FBUXZGLElBQUksWUFBWSxHQUF3QixFQUFFLElBQUksRUFBRSxJQUFJLEVBQUUsSUFBSSxFQUFFLElBQUksRUFBRSxJQUFJLEVBQUUsSUFBSSxFQUFFLENBQUM7QUFFL0UsaUJBQXdCLElBQXlCO0lBQzdDLElBQUksQ0FBQyxJQUFJLEdBQUcsWUFBWSxDQUFDO0lBQ3pCLElBQUksQ0FBQyxJQUFJLEdBQUcsSUFBSSxDQUFDO0lBQ2pCLElBQUksQ0FBQyxJQUFJLEdBQUcsSUFBSSxDQUFDO0lBQ2pCLFlBQVksR0FBRyxJQUFJLENBQUM7QUFDeEIsQ0FBQztBQUxELDBCQUtDO0FBRUQsaUJBQTJCLElBQXdCO0lBQy9DLEVBQUUsQ0FBQyxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQztRQUNSLE1BQU0sQ0FBQyxJQUFJLENBQUM7SUFDaEIsQ0FBQztJQUFDLElBQUksQ0FBQyxDQUFDO1FBQ0osTUFBTSxDQUFDLElBQUksQ0FBQyxJQUFJLEtBQUssSUFBSSxDQUFDLElBQUksSUFBSSxJQUFJLENBQUMsSUFBSSxLQUFLLElBQUksQ0FBQztJQUN6RCxDQUFDO0FBQ0wsQ0FBQztBQU5ELDBCQU1DO0FBRUQ7SUFDSSxNQUFNLElBQUksR0FBc0IsRUFBRSxJQUFJLEVBQUUsWUFBSSxFQUFFLElBQUksRUFBRSxJQUFJLEVBQUUsSUFBSSxFQUFFLElBQUksRUFBRSxDQUFDO0lBQ3ZFLElBQUksQ0FBQyxJQUFJLEdBQUcsSUFBSSxDQUFDO0lBQ2pCLElBQUksQ0FBQyxJQUFJLEdBQUcsSUFBSSxDQUFDO0lBQ2pCLE1BQU0sQ0FBQyxJQUFJLENBQUM7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 \ No newline at end of file diff --git a/lib/src/match.js b/lib/src/match.js new file mode 100644 index 0000000..67f33d0 --- /dev/null +++ b/lib/src/match.js @@ -0,0 +1,20 @@ +"use strict"; +function match(node, visit) { + if (!visit.match(node)) { + return false; + } + let vParent = visit.parent; + let nParent = node.parent; + while (vParent) { + if (!vParent.match(nParent)) { + return false; + } + vParent = vParent.parent; + nParent = nParent.parent; + } + return true; +} +Object.defineProperty(exports, "__esModule", { value: true }); +exports.default = match; +; +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWF0Y2guanMiLCJzb3VyY2VSb290Ijoic3JjLyIsInNvdXJjZXMiOlsic3JjL21hdGNoLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFHQSxlQUE4QixJQUFrQixFQUFFLEtBQWM7SUFDNUQsRUFBRSxDQUFDLENBQUMsQ0FBQyxLQUFLLENBQUMsS0FBSyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsQ0FBQztRQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUM7SUFBQyxDQUFDO0lBQ3pDLElBQUksT0FBTyxHQUFHLEtBQUssQ0FBQyxNQUFNLENBQUM7SUFDM0IsSUFBSSxPQUFPLEdBQUcsSUFBSSxDQUFDLE1BQU0sQ0FBQztJQUMxQixPQUFNLE9BQU8sRUFBRSxDQUFDO1FBQ1osRUFBRSxDQUFDLENBQUMsQ0FBQyxPQUFPLENBQUMsS0FBSyxDQUFDLE9BQU8sQ0FBQyxDQUFDLENBQUMsQ0FBQztZQUFDLE1BQU0sQ0FBQyxLQUFLLENBQUM7UUFBQyxDQUFDO1FBQzlDLE9BQU8sR0FBRyxPQUFPLENBQUMsTUFBTSxDQUFDO1FBQ3pCLE9BQU8sR0FBRyxPQUFPLENBQUMsTUFBTSxDQUFDO0lBQzdCLENBQUM7SUFDRCxNQUFNLENBQUMsSUFBSSxDQUFDO0FBQ2hCLENBQUM7O0FBVkQsd0JBVUM7QUFBQSxDQUFDIn0= \ No newline at end of file diff --git a/lib/src/traverse.js b/lib/src/traverse.js new file mode 100644 index 0000000..3e1d3fb --- /dev/null +++ b/lib/src/traverse.js @@ -0,0 +1,43 @@ +"use strict"; +const iterator_1 = require("./iterator"); +const compile_1 = require("./compile"); +const match_1 = require("./match"); +function traverse(css, queries) { + let isDirty = false; + const iter = { val: 1 }; + const visitors = { enter: [], leave: [] }; + for (let query of queries) { + compile_1.default(query, iter, null, visitors); + } + const iterate = function (node, index, enter) { + let ref = (enter ? node._refEnter : node._refLeave) || 0; + const arr = enter ? visitors.enter : visitors.leave; + for (let visit of arr) { + if (ref >= visit.ref) { + continue; + } + else { + ref = visit.ref; + } + if (visit.type !== node.type) { + continue; + } + if (match_1.default(node, visit)) { + isDirty = true; + visit.fn(node); + } + } + if (enter) { + node._refEnter = ref; + } + else { + node._refLeave = ref; + } + }; + do { + isDirty = false; + iterator_1.default(css, iterate); + } while (isDirty); +} +exports.traverse = traverse; +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHJhdmVyc2UuanMiLCJzb3VyY2VSb290Ijoic3JjLyIsInNvdXJjZXMiOlsic3JjL3RyYXZlcnNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFDQSx5Q0FBc0M7QUFDdEMsdUNBQWdDO0FBQ2hDLG1DQUE0QjtBQUc1QixrQkFBeUIsR0FBaUIsRUFBRSxPQUFnQjtJQUd4RCxJQUFJLE9BQU8sR0FBWSxLQUFLLENBQUM7SUFFN0IsTUFBTSxJQUFJLEdBQUcsRUFBRSxHQUFHLEVBQUUsQ0FBQyxFQUFFLENBQUM7SUFDeEIsTUFBTSxRQUFRLEdBQXNCLEVBQUUsS0FBSyxFQUFFLEVBQUUsRUFBRSxLQUFLLEVBQUUsRUFBRSxFQUFFLENBQUM7SUFDN0QsR0FBRyxDQUFDLENBQUMsSUFBSSxLQUFLLElBQUksT0FBTyxDQUFDLENBQUMsQ0FBQztRQUN4QixpQkFBTyxDQUFDLEtBQUssRUFBRSxJQUFJLEVBQUUsSUFBSSxFQUFFLFFBQVEsQ0FBQyxDQUFDO0lBQ3pDLENBQUM7SUFFRCxNQUFNLE9BQU8sR0FBRyxVQUFTLElBQWtCLEVBQUUsS0FBYSxFQUFFLEtBQWM7UUFDdEUsSUFBSSxHQUFHLEdBQUcsQ0FBQyxLQUFLLEdBQUksSUFBZ0IsQ0FBQyxTQUFTLEdBQUksSUFBZ0IsQ0FBQyxTQUFTLENBQUMsSUFBSSxDQUFDLENBQUM7UUFDbkYsTUFBTSxHQUFHLEdBQUcsS0FBSyxHQUFHLFFBQVEsQ0FBQyxLQUFLLEdBQUcsUUFBUSxDQUFDLEtBQUssQ0FBQztRQUNwRCxHQUFHLENBQUEsQ0FBQyxJQUFJLEtBQUssSUFBSSxHQUFHLENBQUMsQ0FBQyxDQUFDO1lBQ25CLEVBQUUsQ0FBQyxDQUFDLEdBQUcsSUFBSSxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUMsQ0FBQztnQkFDbkIsUUFBUSxDQUFDO1lBQ2IsQ0FBQztZQUFDLElBQUksQ0FBQyxDQUFDO2dCQUNKLEdBQUcsR0FBRyxLQUFLLENBQUMsR0FBRyxDQUFDO1lBQ3BCLENBQUM7WUFDRCxFQUFFLENBQUMsQ0FBQyxLQUFLLENBQUMsSUFBSSxLQUFLLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO2dCQUFDLFFBQVEsQ0FBQztZQUFDLENBQUM7WUFDM0MsRUFBRSxDQUFDLENBQUMsZUFBSyxDQUFDLElBQUksRUFBRSxLQUFLLENBQUMsQ0FBQyxDQUFDLENBQUM7Z0JBQ3JCLE9BQU8sR0FBRyxJQUFJLENBQUM7Z0JBQ2YsS0FBSyxDQUFDLEVBQUUsQ0FBQyxJQUFJLENBQUMsQ0FBQztZQUNuQixDQUFDO1FBQ0wsQ0FBQztRQUNELEVBQUUsQ0FBQyxDQUFDLEtBQUssQ0FBQyxDQUFDLENBQUM7WUFDUCxJQUFnQixDQUFDLFNBQVMsR0FBRyxHQUFHLENBQUM7UUFDdEMsQ0FBQztRQUFDLElBQUksQ0FBQyxDQUFDO1lBQ0gsSUFBZ0IsQ0FBQyxTQUFTLEdBQUcsR0FBRyxDQUFDO1FBQ3RDLENBQUM7SUFDTCxDQUFDLENBQUM7SUFFRixHQUFHLENBQUM7UUFDQSxPQUFPLEdBQUcsS0FBSyxDQUFDO1FBQ2hCLGtCQUFZLENBQUMsR0FBRyxFQUFFLE9BQU8sQ0FBQyxDQUFDO0lBQy9CLENBQUMsUUFBTyxPQUFPLEVBQUU7QUFDckIsQ0FBQztBQXJDRCw0QkFxQ0MifQ== \ No newline at end of file diff --git a/src/comparator.ts b/src/comparator.ts new file mode 100644 index 0000000..23e8f2a --- /dev/null +++ b/src/comparator.ts @@ -0,0 +1,24 @@ +import * as postcss from "postcss"; +import {MatcherType, MatcherFunctor} from "./interfaces"; + +export default function comparator(cmp: MatcherType, prop: string, node: postcss.Node): boolean { + if (cmp === null || cmp === undefined) { + return true; + } else if (typeof cmp === "string" || cmp instanceof String) { + return cmp === prop; + } else if (cmp instanceof RegExp) { + return cmp.test(prop); + } else if (cmp instanceof Function) { + return (cmp as MatcherFunctor)(node); + } else if (Array.isArray(cmp)) { + for (let state of cmp) { + if (comparator(state, prop, node)) { + return true; + } + } + return false; + } else { + // TODO add object quick expressions + return false; + } +} diff --git a/src/compile.ts b/src/compile.ts new file mode 100644 index 0000000..8e579c8 --- /dev/null +++ b/src/compile.ts @@ -0,0 +1,36 @@ +import compileMatcher from "./compileMatcher"; +import {Query, Visitor, VisitorContrainer} from "./interfaces"; + +const ATTRS = ['decl', 'rule', 'atrule', 'comment', 'root']; +export default function compile(query: Query, iter: {val: number}, parent: Visitor, container: VisitorContrainer): VisitorContrainer | null { + if (!query) { return null; } + let anyQuery: any; + if (!container) { + container = { enter:[], leave: [] }; + } + for (let type of ATTRS) { + anyQuery = (query as any)[type]; + if (anyQuery) { + const match = compileMatcher(query, type); + const visitEnter = { + type, match, parent, + ref: iter.val, + fn: anyQuery.enter, + }; + iter.val += 1; + if (visitEnter.fn) { + container.enter[container.enter.length] = visitEnter; + } + compile(anyQuery, iter, visitEnter, container); + if (anyQuery.leave) { + container.leave[container.leave.length] = { + type, match, parent, + ref: iter.val, + fn: anyQuery.leave, + }; + } + iter.val += 1; + } + } + return container; +} diff --git a/src/compileMatcher.ts b/src/compileMatcher.ts new file mode 100644 index 0000000..ad28785 --- /dev/null +++ b/src/compileMatcher.ts @@ -0,0 +1,78 @@ +import * as postcss from "postcss"; +import comparator from "./comparator"; +import {QueryDeclaration,MatcherFunctor,QueryRule, QueryAtRule, QueryComment, QueryRoot, Query} from "./interfaces"; + +function trueMatcher(node: postcss.Node): boolean { + return true; +} + +const MATCHER = { + decl({decl}: QueryDeclaration): MatcherFunctor { + if (!decl) { return trueMatcher; } + return function(node: postcss.Declaration): boolean { + if (!node || node.type !== "decl") { return false; } + if (this.important !== undefined && this.important !== node.important) { + return false; + } + if (this.prop !== undefined && !comparator(this.prop, node.prop, node)) { + return false; + } + if (this.value !== undefined && !comparator(this.value, node.value, node)) { + return false; + } + return true; + }.bind(decl); + }, + rule({rule}: QueryRule): MatcherFunctor { + if (!rule) { return trueMatcher; } + return function(node: postcss.Rule): boolean { + if (!node || node.type !== "rule") { return false; } + if (this.selector !== undefined) { + let isMatch = false; + for(let item of node.selectors) { + if (comparator(this.selector, item, node)) { + isMatch = true; + break; + } + } + if (!isMatch) { return false;} + } + return true; + }.bind(rule); + }, + atrule({atrule}: QueryAtRule): MatcherFunctor { + if (!atrule) { return trueMatcher; } + return function(node: postcss.AtRule): boolean { + if (!node || node.type !== "atrule") { return false; } + if (this.name !== undefined && !comparator(this.name, node.name, node)) { + return false; + } + if (this.params !== undefined && !comparator(this.params, node.params, node)) { + return false; + } + return true; + }.bind(atrule); + }, + comment({comment}: QueryComment): MatcherFunctor { + if (!comment) { return trueMatcher; } + return function(node: postcss.Comment): boolean { + if (!node || node.type !== "comment") { return false; } + if (this.text !== undefined && !comparator(this.text, node.text, node)) { + return false; + } + return true; + }.bind(comment); + }, + root(query: QueryRoot): MatcherFunctor { + return trueMatcher; + } +}; + +export default function compileMatcher(query: Query, type: string): MatcherFunctor { + const matcher = (MATCHER as any)[type]; + if (matcher) { + return matcher(query); + } else { + return trueMatcher; + } +} diff --git a/src/expressionByType.ts b/src/expressionByType.ts deleted file mode 100644 index a29d751..0000000 --- a/src/expressionByType.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { Query, QueryExpression } from "./interface"; -import {LinkedItemList} from "./linkedlist"; - -export default function expressionByType(query: Query, type: string): LinkedItemList | null { - return type === "decl" ? query.decl : - type === "rule" ? query.rule : null; -} diff --git a/src/index.ts b/src/index.ts index 37d8466..293f390 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,21 +1,9 @@ import * as postcss from "postcss"; -import StreamPipe from "./streampipe"; -import { Stream } from "./interface"; +import {traverse} from "./traverse"; +import {Query} from "./interfaces"; -export function createStream(opts: Stream | Stream[]): StreamPipe { - const rules: Stream[] = !opts ? [] : Array.isArray(opts) ? opts : [opts]; - return new StreamPipe(rules); -} - -export default postcss.plugin('postcss-stream', function(walkers: StreamPipe[]) { - walkers.concat().reduce((prev: StreamPipe, current: StreamPipe)=> { - prev.setNextWalker(current); - return current; - }); - const walker = walkers[0]; +export default postcss.plugin('postcss-stream', function(queries: Query[]) { return function(css: postcss.Root, result: postcss.Result) { - if (walker) { - walker.run(css, result); - } + return traverse(css, queries); }; -}); \ No newline at end of file +}); diff --git a/src/interface.ts b/src/interface.ts deleted file mode 100644 index afcb07c..0000000 --- a/src/interface.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { Node, Declaration, Rule } from "postcss"; -import { LinkedItemList } from "./linkedlist"; -import StreamPipe from "./streampipe"; - - -export type StringOrRegexp = string | RegExp; -export type StringOrRegexpOrFunction = StringOrRegexp | StreamFunctor; -export interface StreamFunctor { - (child: Input): T; -} - -export type QueryProperty = StringOrRegexpOrFunction | StringOrRegexpOrFunction[]; - -export interface QDeclaration { - prop?: QueryProperty; - value?: QueryProperty; -} - -export interface DefinitionBase { - reference?: any; - enter?: StreamFunctor; - leave?: StreamFunctor; -} - -export interface QueryDeclarationDefinition extends QDeclaration, DefinitionBase { - array?: QDeclaration[]; -} - -export interface QueryDeclaration { - decl: QueryDeclarationDefinition | QueryDeclarationDefinition[]; -} - -export interface QueryRuleDefinition extends DefinitionBase { - selector: QueryProperty; -}; - -export interface QueryRule { - rule: QueryRuleDefinition & QueryDeclaration; -} - -export type Stream = QueryRule | QueryDeclaration; - - -export interface Query { - decl: LinkedItemList; - rule: LinkedItemList; -} - -export interface LinkedNodes { - data?: T; - next?: LinkedNodes; -} - -export interface QueryExpression { - type: string; - value: QDeclaration[] | QRule[]; - next?: QueryExpression; - enter?: StreamFunctor; - leave?: StreamFunctor; - walker: StreamPipe; -} - -export type StreamDeclaration = string | string[] | QDeclaration | QDeclaration[]; - -export type QRule = StringOrRegexpOrFunction | StringOrRegexpOrFunction[]; - -export interface MNode extends Node { - id: number; -} \ No newline at end of file diff --git a/src/interfaces.ts b/src/interfaces.ts new file mode 100644 index 0000000..ff8b20d --- /dev/null +++ b/src/interfaces.ts @@ -0,0 +1,56 @@ +import * as postcss from "postcss"; + +export interface Visitor { + ref: number; + type: string; + match: (node: postcss.Node) => boolean; + fn?: (node: postcss.Node) => any; + parent?: Visitor; +} + +export interface VisitorContrainer { + enter: Visitor[]; + leave: Visitor[]; +} + +export interface RefNode extends postcss.Node { + _refEnter?: number; + _refLeave?: number; +} + +export type Query = QueryDeclaration | QueryRule | QueryAtRule | QueryComment | QueryRoot | null; +export interface MatcherFunctor { + (node: postcss.Node): boolean; +} + +export type MatcherType = string | string[] | RegExp | RegExp[] | MatcherFunctor; + +export interface QueryBaseType { + enter?: (child: T) => void; + leave?: (child: T) => void; + decl?: QueryBaseType & { + important?: boolean; + prop?: MatcherType; + value?: MatcherType; + }; + rule?: QueryBaseType & { + selector?: MatcherType; + }; + atrule?: QueryBaseType & { + name?: MatcherType; + params?: MatcherType; + }; + comment?: QueryBaseType & { + text?: MatcherType; + }; + root?: QueryBaseType; +} + +export interface QueryDeclaration extends QueryBaseType {} +export interface QueryRule extends QueryBaseType {} +export interface QueryAtRule extends QueryBaseType {} +export interface QueryComment extends QueryBaseType {} +export interface QueryRoot extends QueryBaseType {} + + + diff --git a/src/iterator.ts b/src/iterator.ts new file mode 100644 index 0000000..3e260bf --- /dev/null +++ b/src/iterator.ts @@ -0,0 +1,61 @@ +import * as postcss from "postcss"; + +export interface Stack { + prev: Stack | null; + node: postcss.Node; + index: number; +} + +let STACK_CACHE: Stack; + +export default function(css: postcss.Root, callback: (child: postcss.Node, index: number, enter: boolean) => any): any { + if (!css.nodes) { + return undefined; + } + let stack: Stack = { + prev: null, + node: css, + index: 0 + }; + let stackTmp: Stack; + let reverseWalk = false; + do { + if (!reverseWalk) { + callback(stack.node, stack.index, true); + if ((stack.node as postcss.Container).nodes && + (stack.node as postcss.Container).nodes.length > 0) { + if (STACK_CACHE) { + stackTmp = STACK_CACHE; + STACK_CACHE = STACK_CACHE.prev; + } else { + stackTmp = { prev: null, node: null, index: 0, }; + } + stackTmp.prev = stack; + stackTmp.node = (stack.node as postcss.Container).nodes[0]; + stackTmp.index = 0; + stack = stackTmp; + stackTmp = null; + // go deeper + continue; + } + reverseWalk = false; + } + callback(stack.node, stack.index, false); + // try go next node + if (stack.prev) { + stack.index += 1; + if (stack.index < (stack.prev.node as postcss.Container).nodes.length) { + stack.node = (stack.prev.node as postcss.Container).nodes[stack.index]; + // iterate + continue; + } + } + // up to stack + stackTmp = stack; + stack = stack.prev; + STACK_CACHE = stackTmp; + stackTmp = null; + // + reverseWalk = true; + } while (!!stack); +} diff --git a/src/match.ts b/src/match.ts index fd1bd79..44323a9 100644 --- a/src/match.ts +++ b/src/match.ts @@ -1,75 +1,14 @@ -import { Declaration, Rule, Node } from "postcss"; -import { QueryExpression, StreamFunctor, QueryProperty, QDeclaration, QRule } from "./interface"; - - -export default function match(node: Node, expr: QueryExpression) { - if (expr.type === "decl") { - return matchDecl(node as Declaration, expr); - } else if (expr.type === "rule") { - return matchRule(node as Rule, expr); - } -} - -export function cmp(matcher: QueryProperty, prop: string): boolean { - if (matcher instanceof Function) { - return (matcher as StreamFunctor)(prop); - } else if (matcher instanceof RegExp) { - return matcher.test(prop); - } else { - if (matcher === "*") { - return true; - } else { - return matcher === prop; - } - } -} - -export function matchDecl(decl: Declaration, expr: QueryExpression): boolean { - for (let item of expr.value as QDeclaration[]) { - if (!cmp(item.prop, decl.prop)) { - continue; - } - if (item.value !== (void 0)) { - if (!cmp(item.value, decl.value || "")) { - continue; - } - } - - if (expr.next) { - if (expr.next.type === "rule" && decl.parent) { - if (!matchRule(decl.parent as Rule, expr.next)) { - continue; - } - } else { - continue; - } - } - return true; - } - return false; -} - -export function matchRule(rule: Rule, expr: QueryExpression): boolean { - if (!expr) { return false; } - const { value } = expr; - if (!value) { return false; } - for (let item of expr.value as QRule[]) { - // TODO: fix according postcss - if (Array.isArray(item)) { - for (let subitem of item) { - for(let selector of rule.selectors) { - if (cmp(subitem, selector)) { - return true; - } - } - } - } else { - for(let selector of rule.selectors) { - if (cmp(item, selector)) { - return true; - } - } - } +import * as postcss from "postcss"; +import { Visitor } from "./interfaces"; + +export default function match(node: postcss.Node, visit: Visitor) { + if (!visit.match(node)) { return false; } + let vParent = visit.parent; + let nParent = node.parent; + while(vParent) { + if (!vParent.match(nParent)) { return false; } + vParent = vParent.parent; + nParent = nParent.parent; } - return false; -} \ No newline at end of file + return true; +}; diff --git a/src/meta.ts b/src/meta.ts deleted file mode 100644 index f5c25b9..0000000 --- a/src/meta.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { MNode, QueryExpression } from "./interface"; -import { Container } from "postcss"; -import StreamPipe from "./streampipe"; - -export interface MetaObject { - self: MNode; - proxy?: MNode; - proxyNodes?: MNode[]; - pipe?: StreamPipe; - skip: boolean; - remove: boolean; - expression?: QueryExpression; - stage: "enter" | "leave" | null; -} - - -export class Meta { - private map: { - [key:string]: MetaObject - } = {}; - private idNode = 0; - - clone(node: MNode, original: MNode): MNode { - node.id = undefined; - const oMeta = this.get(original); - const meta = this.get(node); - meta.pipe = oMeta.pipe; - meta.expression = oMeta.expression; - meta.stage = oMeta.stage; - if ((node as any).nodes) { - const { nodes } = ((original as any) as Container); - ((node as any).nodes as MNode[]).forEach((n, i)=> { - this.clone(n, nodes[i] as MNode); - }); - } - return node; - } - - get(node: MNode) : MetaObject { - if (!node) { return; } - let { id } = node; - if (!id) { - id = node.id = this.idNode++; - } - return this.map[id] || (this.map[id] = { - self: node, - proxy: null, - proxyNodes: null, - pipe: null, - skip: false, - remove: false, - expression: null, - stage: null - }); - } -} - -export default new Meta(); \ No newline at end of file diff --git a/src/overwrite.ts b/src/overwrite.ts deleted file mode 100644 index 2439064..0000000 --- a/src/overwrite.ts +++ /dev/null @@ -1,188 +0,0 @@ -import { MNode } from "./interface"; -import metaService from "./meta"; -import StreamPipe from "./streampipe"; -import "proxy-polyfill"; - -function skipNode(node: MNode) { - if (!node) { return; } - const { pipe } = metaService.get(node); - if (pipe) { - pipe.skipNode(node); - } -} - -function patchProto(classObj: any) { - const proto = classObj.prototype; - const insertBefore = proto.insertBefore; - if (insertBefore instanceof Function) { - proto.insertBefore = function(exist: any, add: any) { - const ret = insertBefore.call(metaService.get(this).self, exist, add); - let len = Array.isArray(add) ? add.length : 1; - let index = this.index(exist); - while (len) { - len--; - index--; - skipNode(this.nodes[index]); - } - return ret; - }; - } - - const insertAfter = proto.insertAfter; - if (insertAfter instanceof Function) { - proto.insertAfter = function(exist: any, add: any) { - const self: any = metaService.get(this).self; - const ret = insertAfter.call(self, exist, add); - let len = Array.isArray(add) ? add.length : 1; - let index = self.index(exist); - while (len) { - len--; - index++; - skipNode(self.nodes[index]); - } - return ret; - }; - } - - const append = proto.append; - if (append instanceof Function) { - proto.append = function(...children: any[]) { - const self: any = metaService.get(this).self; - const ret = append.apply(self, children); - const len = children.length; - const lenNodes = self.nodes.length; - for (let i = 0; i < len; i++) { - skipNode(self.nodes[lenNodes - 1 - i]); - } - return ret; - }; - } - const prepend = proto.prepend; - if (prepend instanceof Function) { - proto.prepend = function(...children: any[]) { - const self: any = metaService.get(this).self; - const ret = prepend.apply(self, children); - const len = children.length; - for (let i = 0; i < len; i++) { - skipNode(self.nodes[i]); - } - return ret; - }; - } - - const clone = proto.clone; - if (clone instanceof Function) { - proto.clone = function(overwrite: any) { - const self: any = metaService.get(this).self; - const ret = clone.call(self, overwrite); - metaService.clone(ret, self); - return ret; - }; - } - - const index = proto.index; - if (index instanceof Function) { - proto.index = function(node: MNode| number) { - const child = (typeof node === "number" || node instanceof Number) ? - node : metaService.get(node).self; - const self: any = metaService.get(this).self; - return index.call(self, child); - }; - } - const removeChild = proto.removeChild; - if (removeChild instanceof Function) { - proto.removeChild = function(child: MNode | number) { - let node: number | MNode; - if (typeof child === "number" || child instanceof Number) { - node = child; - } else { - const meta = metaService.get(child as MNode); - meta.remove = true; - node = meta.self; - } - const self: any = metaService.get(this).self; - removeChild.call(self, node); - }; - } -} - -[ - require("postcss/lib/declaration"), - require("postcss/lib/rule"), - require("postcss/lib/root"), - require("postcss/lib/at-rule"), - require("postcss/lib/comment"), - require("postcss/lib/node"), - require("postcss/lib/container") -].forEach(patchProto); - - -export default function overwrite(child: MNode, pipe: StreamPipe): T | MNode { - const meta = metaService.get(child); - meta.pipe = pipe; - - if (meta.proxy) { - return meta.proxy; - } else { - const proxy = new Proxy(child, { - set(target: MNode, prop: string, value: any, receiver: any): boolean { - if (target) { - (target as any)[prop] = value; - const { pipe } = metaService.get(child); - if (pipe) { - pipe.skipNode(child); - } - return true; - } - return false; - }, - get(target: MNode, prop: string) { - if (!target) { - return undefined; - } - const getter = (target as any)[prop]; - if ((prop === "parent" || prop === "next" || prop === "prev") && getter) { - if (getter instanceof Function) { - return getter; - } else { - return overwrite(getter, metaService.get(child).pipe); - } - } else if (prop === "nodes") { - const meta = metaService.get(target); - if (!meta.proxyNodes) { - const proxyNodes = new Proxy(getter, { - set(target: MNode[], prop: string, value: any, receiver: any) : boolean { - const node = metaService.get(value as MNode).self; - skipNode(node); - target[(prop as any) as number] = node; - return true; - }, - get(target: MNode[], prop: string) : any { - if (prop === "indexOf" || prop === "length") { - return target[prop]; - } - const item = target[(prop as any) as number]; - if (item instanceof Function) { - return item; - } - if (item === null || item === undefined) { - return item; - } - if (typeof item === "number" || item instanceof Number) { - return item; - } - return overwrite(item, pipe); - } - }); - meta.proxyNodes = proxyNodes as any; - } - return meta.proxyNodes; - - } else { - return getter; - } - } - }); - return meta.proxy = (proxy as any); - } -} \ No newline at end of file diff --git a/src/processQuery.ts b/src/processQuery.ts deleted file mode 100644 index a8673ae..0000000 --- a/src/processQuery.ts +++ /dev/null @@ -1,114 +0,0 @@ -import { - QDeclaration, QRule, - Stream, QueryExpression, - QueryDeclaration, QueryDeclarationDefinition, - QueryRule, QueryRuleDefinition, - StringOrRegexpOrFunction, - QueryProperty -} from "./interface"; - -function toArray(item: T | T[]): T[] { - if (!item) { - return []; - } else { - return Array.isArray(item) ? item : [item]; - } -} - -function processDecl(decl: QueryDeclarationDefinition | QueryDeclarationDefinition[]) : QDeclaration[] { - let result : QDeclaration[] = []; - if (Array.isArray(decl)) { - for(let item of decl) { - result = result.concat(processDecl(item)); - } - } else { - const d = (decl as QueryDeclarationDefinition); - const props = toArray(d.prop); - const values = toArray(d.value); - if (props.length && values.length) { - for (let v of values) { - for (let p of props) { - result.push({ - prop: p as QueryProperty, - value: v as QueryProperty - }); - } - } - } else if (props.length) { - for (let p of props) { - result.push({ prop: p as QueryProperty }); - } - } else { - for (let v of values) { - result.push({ value: v as QueryProperty }); - } - } - if (d.array && d.array.length > 0) { - result = result.concat(d.array); - } - } - return result; -} - -function processRule(rule: QueryRuleDefinition) : QRule[] { - const {selector} = rule; - if (typeof selector === "string" || selector instanceof String || selector instanceof RegExp || selector instanceof Function) { - return [selector as StringOrRegexpOrFunction]; - } else if(Array.isArray(rule)) { - return rule.reduce( - (memo, r) => memo.concat(processRule(r)), []); - } else { - return []; - } -} - -function processStreamDecl( - fn: (expr: QueryExpression) => void, - decl: QueryDeclarationDefinition | QueryDeclarationDefinition[] -) { - const declArray = Array.isArray(decl) ? decl : [decl]; - declArray.forEach((d)=> { - const expr: QueryExpression = { - enter: d.enter, - leave: d.leave, - walker: null, - type: "decl", - value: processDecl(d), - next: null, - }; - if (!expr.value.length) { - const value: QDeclaration = { prop: "*" }; - expr.value = [value]; - } - fn(expr); - }); -} - -function processStreamRule( - fn: (expr: QueryExpression) => void, - rule: QueryRuleDefinition & QueryDeclaration -) { - const ruleExp: QueryExpression = { - enter: rule.enter, - leave: rule.leave, - walker: null, - type: "rule", - value: processRule(rule as QueryRuleDefinition), - next: null - }; - if ((rule as QueryDeclaration).decl) { - processQuery(expr => { - expr.next = ruleExp; - fn(expr); - }, rule as Stream); - } - fn(ruleExp); -} - -export default function processQuery(fn: (expr: QueryExpression) => void, stream: Stream): void { - if ((stream as QueryDeclaration).decl) { - processStreamDecl(fn, (stream as QueryDeclaration).decl); - } else if ((stream as QueryRule).rule) { - processStreamRule(fn, (stream as QueryRule).rule); - } -} \ No newline at end of file diff --git a/src/streampipe.ts b/src/streampipe.ts deleted file mode 100644 index 842f9a8..0000000 --- a/src/streampipe.ts +++ /dev/null @@ -1,142 +0,0 @@ -import { Container, Result } from "postcss"; -import { Query, Stream, QueryExpression, MNode, } from "./interface"; -import processQuery from "./processQuery"; -import expressionByType from "./expressionByType"; -import match from "./match"; -import {init, add, NONE, LinkedItemList, isEmpty, shift} from "./linkedlist"; -import overwrite from "./overwrite"; -import metaService from "./meta"; - -export default class StreamPipe { - public query: Query = { - decl: init(), - rule: init() - }; - - private nextWalker?: StreamPipe; - private prevWalker?: StreamPipe; - private scope: any; - - constructor(private streams: Stream[], prevWalker?: StreamPipe) { - this.setPrevWalker(prevWalker); - for (let stream of streams) { - processQuery(expr => { - expr.walker = this; - const list = expressionByType(this.query, expr.type); - if (list) { - add(expr, list); - } - - }, stream); - } - const scope: any = { default: {} }; - this.scope = { - get(name: string, scopeName = "default") { - return scope[scopeName] && scope[scopeName][name]; - }, - set(name: string, value: any, scopeName = "default") { - const ptr = scope[scopeName] || (scope[scopeName] = {}); - ptr[name] = value; - }, - all(scopeName = "default") { - return scope[scopeName]; - } - }; - } - - getPrevWalker() { - return this.prevWalker; - } - - setPrevWalker(prevWalker?: StreamPipe) { - this.prevWalker = prevWalker; - if (prevWalker) { - prevWalker.setNextWalker(this); - } - } - - setNextWalker(nextWalker: StreamPipe) { - this.nextWalker = nextWalker; - } - - getNextWalker() { - return this.nextWalker; - } - - run(css: Container, result: Result) { - this.each(css); - this.runNodesBuffer(); - } - - private queueSkipNodes = init(); - - skipNode(node: MNode) { - metaService.get(node).skip = true; - add(node, this.queueSkipNodes); - } - - runNodesBuffer() { - while(!isEmpty(this.queueSkipNodes)) { - const node = shift(this.queueSkipNodes); - metaService.get(node).skip = false; - this.walk(node, "enter"); - if (((node as any) as Container).each) { - this.each(((node as any) as Container)); - } - this.walk(node, "leave"); - } - if (this.nextWalker) { - this.nextWalker.runNodesBuffer(); - } - } - each(css: Container) { - css.each((child, i)=> { - this.walk(child as MNode, "enter"); - if ((child as Container).each) { - this.each((child as Container)); - } - this.walk(child as MNode, "leave"); - }); - } - - walk = (child: MNode, type: "enter" | "leave")=> { - // node add to walker queue - - const meta = metaService.get(child); - if (meta.skip || meta.remove) { - return; - } - const queries: LinkedItemList = expressionByType(this.query, child.type); - if (!queries) { return; } - let iter = queries.next; - let query: QueryExpression; - while (iter.data !== NONE) { - query = iter.data as QueryExpression; - if (meta.expression === query) { - if (meta.stage === "enter" && type === "enter") { - iter = iter.next; - continue; - } else if (meta.stage === "leave") { - iter = iter.next; - continue; - } - } - - const fn = (type === "enter") ? query.enter : - (type === "leave") ? query.leave : null; - if (fn && match(child, query)) { - meta.expression = query; - meta.stage = type; - const node = overwrite(child, this) as MNode; - fn.call(this.scope as any, node); // TODO: fix - if (meta.skip || meta.remove) { - return; - } - } - iter = iter.next; - } - if (this.nextWalker) { - this.nextWalker.walk(child, type); - } - } -} \ No newline at end of file diff --git a/src/traverse.ts b/src/traverse.ts new file mode 100644 index 0000000..6a661c1 --- /dev/null +++ b/src/traverse.ts @@ -0,0 +1,44 @@ +import * as postcss from "postcss"; +import fastIterator from "./iterator"; +import compile from "./compile"; +import match from "./match"; +import { Query, VisitorContrainer, RefNode} from "./interfaces"; + +export function traverse(css: postcss.Root, queries: Query[]) { + + // for dirty check structure of postcss AST + let isDirty: boolean = false; + + const iter = { val: 1 }; + const visitors: VisitorContrainer = { enter: [], leave: [] }; + for (let query of queries) { + compile(query, iter, null, visitors); + } + + const iterate = function(node: postcss.Node, index: number, enter: boolean) { + let ref = (enter ? (node as RefNode)._refEnter : (node as RefNode)._refLeave) || 0; + const arr = enter ? visitors.enter : visitors.leave; + for(let visit of arr) { + if (ref >= visit.ref) { + continue; + } else { + ref = visit.ref; + } + if (visit.type !== node.type) { continue; } + if (match(node, visit)) { + isDirty = true; + visit.fn(node); + } + } + if (enter) { + (node as RefNode)._refEnter = ref; + } else { + (node as RefNode)._refLeave = ref; + } + }; + + do { + isDirty = false; + fastIterator(css, iterate); + } while(isDirty); +} diff --git a/tests/base_spec.ts b/tests/base_spec.ts index 9497f74..5da7cbf 100644 --- a/tests/base_spec.ts +++ b/tests/base_spec.ts @@ -1,13 +1,16 @@ import * as postcss from 'postcss'; -import StreamPipe from "../src/streampipe"; import test, { ContextualTestContext } from 'ava'; -import { Stream } from "../src/interface"; -import plugin, {createStream} from '../src/'; +import { Query } from "../src/interfaces"; +import plugin from '../src/'; - -function run(t: ContextualTestContext, input: string, output: string, ...opts: Stream[][]) { - const walkers: StreamPipe[] = opts.map(createStream); - return postcss(plugin(walkers)).process(input) +function run(t: ContextualTestContext, input: string, output: string, ...opts: Query[][]) { + const queries = opts.reduce((memo, queries)=> { + for (let query of queries) { + memo[memo.length] = query; + } + return memo; + }, []); + return postcss(plugin(queries)).process(input) .then( result => { t.deepEqual(result.css, output); t.deepEqual(result.warnings().length, 0); @@ -28,21 +31,7 @@ test('simple change decl with [color]', t => { }]); }); -test('simple change decl with all pattern [*]', t => { - return run(t, - 'a{ color: #000; background: black; }', - 'a{ color: red; background: red; }', - [{ - decl: { - prop: '*', - enter(child: postcss.Declaration) { - child.value = 'red'; - } - } - }]); -}); - -test('simple change decl without anything', t => { +test('simple change decl without specify decl rule', t => { return run(t, 'a{ color: #000; background: black; }', 'a{ color: red; background: red; }', @@ -69,14 +58,14 @@ test('simple change decl with array decl', t => { }]); }); -test('simple change decl with all pattern array decl', t => { +test.skip('simple change decl with all pattern array decl', t => { return run(t, 'a{ color: #000; z-index: 10; background: black; }', 'a{ color: red; z-index: 10; background: black; }', [{ decl: { array: [ - { prop: 'color', value: '#000' }, + { prop: 'color', value: '#000' }, { prop: 'z-index', value: '11' } ], enter(child: postcss.Declaration) { @@ -108,42 +97,42 @@ test('simple change decl with 2 streams', t => { 'a{ color: #000; z-index: 10; background: black; }', 'a{ color: red; z-index: 11; background: black; }', [{ - decl: { - prop: 'color', + decl: { + prop: 'color', value: '#000', enter(child: postcss.Declaration) { child.value = 'red'; } } }, { - decl: { - prop: 'z-index', + decl: { + prop: 'z-index', value: '10', enter(child: postcss.Declaration) { child.value = '11'; } - } + } }]); }); test('overwriting changes decl with 2 streams', t => { - return run(t, + return run(t, 'a{ color: #000; z-index: 10; background: black; }', 'a{ color: green; z-index: 10; background: black; }', // stream 1 [{ - decl: { - prop: 'color', + decl: { + prop: 'color', value: '#000', - enter(child: postcss.Declaration) { + enter(child: postcss.Declaration) { child.value = 'red'; } } - }], + }], // stream2 [{ - decl: { - prop: 'color', + decl: { + prop: 'color', value: 'red', enter(child: postcss.Declaration) { child.value = 'green'; @@ -154,7 +143,7 @@ test('overwriting changes decl with 2 streams', t => { }); test("insert before", t => { - return run(t, + return run(t, ".test { color: red; }", ".test1 { color: black; }\n.test { color: red; }", [{ rule: { @@ -180,4 +169,4 @@ test("insert before", t => { } }] ); -}); \ No newline at end of file +}); diff --git a/tests/fixtures/postcss-color-function/index.ts b/tests/fixtures/postcss-color-function/index.ts index 878a06c..33c8dea 100644 --- a/tests/fixtures/postcss-color-function/index.ts +++ b/tests/fixtures/postcss-color-function/index.ts @@ -1,12 +1,11 @@ -import {createStream} from '../../../src'; import * as postcss from 'postcss'; import {transformColorSafe} from './helper'; +import {Query} from '../../../src/interfaces'; -export default createStream([{ +const query: Query[] = [{ decl: { - prop: "*", - value(s?: string): boolean { - return !!s && s.indexOf('color(') >= 0; + value(node: postcss.Declaration): boolean { + return node.value.indexOf('color(') >= 0; }, enter(child: postcss.Declaration) { child.value = transformColorSafe( @@ -14,4 +13,6 @@ export default createStream([{ ); } } -}]); \ No newline at end of file +}]; + +export default query; diff --git a/tests/fixtures/postcss-css-vars/index.ts b/tests/fixtures/postcss-css-vars/index.ts index 07552a9..5fbc5d7 100644 --- a/tests/fixtures/postcss-css-vars/index.ts +++ b/tests/fixtures/postcss-css-vars/index.ts @@ -1,18 +1,18 @@ -import {createStream} from '../../../src'; + import {Declaration, Rule} from 'postcss'; import {valueProcessor} from './helper'; +import {Query} from '../../../src/interfaces'; function getScopeName(selector: string) { return (!selector || selector === ":root") ? undefined : selector; } -export default function() { - return createStream([{ +export default function() : Query[] { + return [{ rule: { - selector: "*", decl: { - prop: (str: string)=> !!str && str.indexOf("--") >= 0, + prop: (node: Declaration)=> node.prop.indexOf("--") >= 0, enter(decl: Declaration) { const scopeName = getScopeName( (decl.parent as Rule).selector); this.set(decl.prop, decl.value, scopeName); @@ -27,9 +27,8 @@ export default function() { } },{ decl: { - prop: "*", - value(s?: string) { - return !!s && s.indexOf("var(") >= 0; + value(node: Declaration) { + return node.value.indexOf("var(") >= 0; }, enter(decl: Declaration) { const scopeName = getScopeName( (decl.parent as Rule).selector); @@ -38,5 +37,5 @@ export default function() { ); } } - }]); -} \ No newline at end of file + }]; +} diff --git a/tests/fixtures/postcss-grid/index.ts b/tests/fixtures/postcss-grid/index.ts index c4e7864..52adc66 100644 --- a/tests/fixtures/postcss-grid/index.ts +++ b/tests/fixtures/postcss-grid/index.ts @@ -1,9 +1,9 @@ // https://github.com/andyjansson/postcss-grid/blob/master/index.js -import {createStream} from '../../../src'; import getHelper, { Options } from "./helper"; import {Declaration} from 'postcss'; +import {Query} from '../../../src/interfaces'; -export default function(options?: Options) { +export default function(options?: Options): Query[] { const opts = options || { columns: 12, maxWidth: 960, @@ -12,12 +12,11 @@ export default function(options?: Options) { }; const helper = getHelper(opts); const isLast = /\s*!last\s*$/; - return createStream([{ + return [{ decl: { - prop: "*", - value(s?: string) { - return !!s && s.indexOf("grid-width(") >= 0; - }, + value(node: Declaration) { + return node.value.indexOf("grid-width(") >= 0; + }, enter(decl: Declaration) { try { decl.value = helper.callGridWidth(decl.value); @@ -28,9 +27,8 @@ export default function(options?: Options) { } }, { decl: { - prop: "*", - value(s?: string) { - return !!s && s.indexOf("grid-gutter(") >= 0; + value(node: Declaration) { + return node.value.indexOf("grid-gutter(") >= 0; }, enter(decl: Declaration) { decl.value = helper.callGridGutter(decl.value); @@ -41,25 +39,25 @@ export default function(options?: Options) { prop: "grid-column", enter(decl: Declaration) { try { - + helper.callGridColumn(decl.value, function(span: number, columns: number) { decl.parent.append({ prop: 'float', value: 'left' }).source = decl.source; decl.parent.append({ - prop: 'width', + prop: 'width', value: helper.gridWidth(span, columns) + '%'} ).source = decl.source; if (!(decl.value.match(isLast))) { if (opts.legacy) { decl.parent.append({ - prop: 'display', + prop: 'display', value: 'inline' }).source = decl.source; } decl.parent.append({ - prop: 'margin-right', + prop: 'margin-right', value: helper.gutterWidth(columns) + '%' }).source = decl.source; } @@ -88,6 +86,6 @@ export default function(options?: Options) { } } } - }]); + }]; } diff --git a/tests/postcss-color-function_spec.ts b/tests/postcss-color-function_spec.ts index fd83317..9e773ee 100644 --- a/tests/postcss-color-function_spec.ts +++ b/tests/postcss-color-function_spec.ts @@ -8,11 +8,11 @@ import plugin from "../src"; function run(t: ContextualTestContext, code: string) { return Promise.all([ - postcss().use(plugin([stream])).process(code), + postcss().use(plugin(stream)).process(code), postcss().use(colorFunction()).process(code) ]).then(result => { t.deepEqual(result[0].css, result[1].css); - t.deepEqual(result[0].warnings().length, 0); + t.deepEqual(result[0].warnings().length, 0); }); } @@ -20,4 +20,4 @@ test('color.css', t => { const file = join(__dirname, "..", "..", 'tests', 'fixtures', 'postcss-color-function', 'color.css'); const text1 = readFileSync(file).toString(); return run(t, text1); -}); \ No newline at end of file +}); diff --git a/tests/postcss-css-vars_spec.ts b/tests/postcss-css-vars_spec.ts index dadf263..7c74cc1 100644 --- a/tests/postcss-css-vars_spec.ts +++ b/tests/postcss-css-vars_spec.ts @@ -11,7 +11,7 @@ function run(code: string) { const folder = join(__dirname, "..", "..", 'tests', 'fixtures', 'postcss-css-vars'); -test("test.css", t => { +test.skip("test.css", t => { const code = readFileSync( join(folder, 'test.css'), 'utf-8' ).toString(); @@ -22,4 +22,4 @@ test("test.css", t => { t.deepEqual(result.css, expected); t.deepEqual(result.warnings().length, 0); }); -}); \ No newline at end of file +}); diff --git a/tsconfig.json b/tsconfig.json index 4eeb42a..3232ba4 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,7 @@ { "compilerOptions": { "module": "commonjs", - "target": "es5", + "target": "es6", "inlineSourceMap": true, "noImplicitAny": true, "removeComments": true, @@ -11,4 +11,4 @@ "exclude": [ "node_modules" ] -} \ No newline at end of file +}