在浏览器中使用节点返工css

时间:2014-07-15 22:15:53

标签: javascript node.js npm client-side browserify

我希望能够在浏览器中使用reworkcss / css。我从github下载了2.0.0版,并使用nmp install安装了所需的软件包。我已经尝试过requireJS(它声称能够处理CommonJS Module Format),需要index.js,但是关于“exports”未定义会出错。我还尝试使用browserify来压缩它,但这显然不是它的目的。有谁知道我应该怎么做?我不能将node.js用于项目,但需要浏览器中的CSS模块功能。

1 个答案:

答案 0 :(得分:1)

我最后用手冷凝了它。如果有人有兴趣,这是结果。它似乎工作得很好。如果有任何错误,请在评论中提及,以便修复。

/**
 * npm modules: reworkcss
 * author: TJ Holowaychuk <tj@vision-media.ca>
 * license: MIT
 * url: https://github.com/reworkcss/css.git
 * 
 * Package condensed for client-side use by Aaron Fjerstad, July 2014.
 */

//Compressed object
var Compressed = function (opts) {
  this.options = opts || {};

    //Emit 'str'
    this.emit = function(str) {
        return str;
    };

    //Visit 'node'
    this.visit = function(node){
        return this[node.type](node);
    };

    //Map visit over array of 'nodes', optionally using a 'delim'
    this.mapVisit = function(nodes, delim){
        var buf = '';
        delim = delim || '';

        for (var i = 0, length = nodes.length; i < length; i++) {
            buf += this.visit(nodes[i]);
            if (delim && i < length - 1) buf += this.emit(delim);
        }
        return buf;
    };

    //Compile 'node'
    this.compile = function(node){
        return node.stylesheet
            .rules.map(this.visit, this)
            .join('');
    };

    //Visit comment node.
    this.comment = function(node){
        return this.emit('', node.position);
    };

    //Visit import node.
    this.import = function(node){
        return this.emit('@import ' + node.import + ';', node.position);
    };

    //Visit media node.
    this.media = function(node){
        return this.emit('@media ' + node.media, node.position)
            + this.emit('{')
            + this.mapVisit(node.rules)
            + this.emit('}');
    };

    //Visit document node.
    this.document = function(node){
        var doc = '@' + (node.vendor || '') + 'document ' + node.document;

        return this.emit(doc, node.position)
            + this.emit('{')
            + this.mapVisit(node.rules)
            + this.emit('}');
    };

    //Visit charset node.
    this.charset = function(node){
        return this.emit('@charset ' + node.charset + ';', node.position);
    };

    //Visit namespace node.
    this.namespace = function(node){
        return this.emit('@namespace ' + node.namespace + ';', node.position);
    };

    //Visit supports node.
    this.supports = function(node){
        return this.emit('@supports ' + node.supports, node.position)
            + this.emit('{')
            + this.mapVisit(node.rules)
            + this.emit('}');
    };

    //Visit keyframes node.
    this.keyframes = function(node){
        return this.emit('@'
            + (node.vendor || '')
            + 'keyframes '
            + node.name, node.position)
            + this.emit('{')
            + this.mapVisit(node.keyframes)
            + this.emit('}');
    };

    //Visit keyframe node.
    this.keyframe = function(node){
        var decls = node.declarations;

        return this.emit(node.values.join(','), node.position)
            + this.emit('{')
            + this.mapVisit(decls)
            + this.emit('}');
    };

    //Visit page node.
    this.page = function(node){
        var sel = node.selectors.length
            ? node.selectors.join(', ')
            : '';

        return this.emit('@page ' + sel, node.position)
            + this.emit('{')
            + this.mapVisit(node.declarations)
            + this.emit('}');
    };

    //Visit font-face node.
    this['font-face'] = function(node){
        return this.emit('@font-face', node.position)
            + this.emit('{')
            + this.mapVisit(node.declarations)
            + this.emit('}');
    };

    //Visit host node.
    this.host = function(node){
        return this.emit('@host', node.position)
            + this.emit('{')
            + this.mapVisit(node.rules)
            + this.emit('}');
    };

    //Visit custom-media node.
    this['custom-media'] = function(node){
        return this.emit('@custom-media ' + node.name + ' ' + node.media + ';', node.position);
    };

    //Visit rule node.
    this.rule = function(node){
        var decls = node.declarations;
        if (!decls.length) return '';

        return this.emit(node.selectors.join(','), node.position)
            + this.emit('{')
            + this.mapVisit(decls)
            + this.emit('}');
    };

    //Visit declaration node.
    this.declaration = function(node){
        return this.emit(node.property + ':' + node.value, node.position) + this.emit(';');
    };
};

//Identity object
var Identity = function (opts) {
  this.options = opts || {};

    //Emit 'str'
    this.emit = function(str) {
        return str;
    };

    //Visit 'node'
    this.visit = function(node){
        return this[node.type](node);
    };

    //Map visit over array of 'nodes', optionally using a 'delim'
    this.mapVisit = function(nodes, delim){
        var buf = '';
        delim = delim || '';

        for (var i = 0, length = nodes.length; i < length; i++) {
            buf += this.visit(nodes[i]);
            if (delim && i < length - 1) buf += this.emit(delim);
        }
        return buf;
    };

    //Compile 'node'.
    this.compile = function(node){
        return this.stylesheet(node);
    };

    //Visit stylesheet node.
    this.stylesheet = function(node){
        return this.mapVisit(node.stylesheet.rules, '\n\n');
    };

    //Visit comment node.
    this.comment = function(node){
        return this.emit(this.indent() + '/*' + node.comment + '*/', node.position);
    };

    //Visit import node.
    this.import = function(node){
        return this.emit('@import ' + node.import + ';', node.position);
    };

    //Visit media node.
    this.media = function(node){
        return this.emit('@media ' + node.media, node.position)
            + this.emit(
                    ' {\n'
                    + this.indent(1))
            + this.mapVisit(node.rules, '\n\n')
            + this.emit(
                    this.indent(-1)
                    + '\n}');
    };

    //Visit document node.
    this.document = function(node){
        var doc = '@' + (node.vendor || '') + 'document ' + node.document;

        return this.emit(doc, node.position)
            + this.emit(
                    ' '
                + ' {\n'
                + this.indent(1))
            + this.mapVisit(node.rules, '\n\n')
            + this.emit(
                    this.indent(-1)
                    + '\n}');
    };

    //Visit charset node.
    this.charset = function(node){
        return this.emit('@charset ' + node.charset + ';', node.position);
    };

    //Visit namespace node.
    this.namespace = function(node){
        return this.emit('@namespace ' + node.namespace + ';', node.position);
    };

    //Visit supports node.
    this.supports = function(node){
        return this.emit('@supports ' + node.supports, node.position)
            + this.emit(
                ' {\n'
                + this.indent(1))
            + this.mapVisit(node.rules, '\n\n')
            + this.emit(
                    this.indent(-1)
                    + '\n}');
    };

    //Visit keyframes node.
    this.keyframes = function(node){
        return this.emit('@' + (node.vendor || '') + 'keyframes ' + node.name, node.position)
            + this.emit(
                ' {\n'
                + this.indent(1))
            + this.mapVisit(node.keyframes, '\n')
            + this.emit(
                    this.indent(-1)
                    + '}');
    };

    //Visit keyframe node.
    this.keyframe = function(node){
        var decls = node.declarations;

        return this.emit(this.indent())
            + this.emit(node.values.join(', '), node.position)
            + this.emit(
                ' {\n'
                + this.indent(1))
            + this.mapVisit(decls, '\n')
            + this.emit(
                this.indent(-1)
                + '\n'
                + this.indent() + '}\n');
    };

    //Visit page node.
    this.page = function(node){
        var sel = node.selectors.length
            ? node.selectors.join(', ') + ' '
            : '';

        return this.emit('@page ' + sel, node.position)
            + this.emit('{\n')
            + this.emit(this.indent(1))
            + this.mapVisit(node.declarations, '\n')
            + this.emit(this.indent(-1))
            + this.emit('\n}');
    };

    //Visit font-face node.
    this['font-face'] = function(node){
        return this.emit('@font-face ', node.position)
            + this.emit('{\n')
            + this.emit(this.indent(1))
            + this.mapVisit(node.declarations, '\n')
            + this.emit(this.indent(-1))
            + this.emit('\n}');
    };

    //Visit host node.
    this.host = function(node){
        return this.emit('@host', node.position)
            + this.emit(
                    ' {\n'
                    + this.indent(1))
            + this.mapVisit(node.rules, '\n\n')
            + this.emit(
                    this.indent(-1)
                    + '\n}');
    };

    //Visit custom-media node.
    this['custom-media'] = function(node){
        return this.emit('@custom-media ' + node.name + ' ' + node.media + ';', node.position);
    };

    //Visit rule node.
    this.rule = function(node){
        var indent = this.indent();
        var decls = node.declarations;
        if (!decls.length) return '';

        return this.emit(node.selectors.map(function(s){ return indent + s }).join(',\n'), node.position)
            + this.emit(' {\n')
            + this.emit(this.indent(1))
            + this.mapVisit(decls, '\n')
            + this.emit(this.indent(-1))
            + this.emit('\n' + this.indent() + '}');
    };

    //Visit declaration node.
    this.declaration = function(node){
        return this.emit(this.indent())
            + this.emit(node.property + ': ' + node.value, node.position)
            + this.emit(';');
    };

    //Increase, decrease or return current indentation.
    this.indent = function(level) {
        this.level = this.level || 1;

        if (null != level) {
            this.level += level;
            return '';
        }

        return Array(this.level).join(this.indentation || '  ');
    };
};

//CSS object
var CSS = function () {
    var commentre = /\/\*[^*]*\*+([^/*][^*]*\*+)*\//g
    /**
     * Trim `str`.
     */
    var trim = function (str) {
        return str ? str.replace(/^\s+|\s+$/g, '') : '';
    }
    //Adds non-enumerable parent node reference to each node.
    var addParent = function (obj, parent) {
        var isNode = obj && typeof obj.type === 'string';
        var childParent = isNode ? obj : parent;

        for (var k in obj) {
            var value = obj[k];
            if (Array.isArray(value)) {
                value.forEach(function(v) { addParent(v, childParent); });
            } else if (value && typeof value === 'object') {
                addParent(value, childParent);
            }
        }

        if (isNode) {
            Object.defineProperty(obj, 'parent', {
                configurable: true,
                writable: true,
                enumerable: false,
                value: parent || null
            });
        }

        return obj;
    }

    //CSS.parse()
    this.parse = function(css, options){
        options = options || {};

        //Positional.
        var lineno = 1;
        var column = 1;

        //Update lineno and column based on 'str'.
        function updatePosition(str) {
            var lines = str.match(/\n/g);
            if (lines) lineno += lines.length;
            var i = str.lastIndexOf('\n');
            column = ~i ? str.length - i : column + str.length;
        }

        //Mark position and patch 'node.position'.
        function position() {
            var start = { line: lineno, column: column };
            return function(node){
                node.position = new Position(start);
                whitespace();
                return node;
            };
        }

        //Store position information for a node
        function Position(start) {
            this.start = start;
            this.end = { line: lineno, column: column };
            this.source = options.source;
        }

        //Non-enumerable source string
        Position.prototype.content = css;

        //Error 'msg'.
        function error(msg) {
            if (options.silent === true) {
                return false;
            }

            var err = new Error(msg + ' near line ' + lineno + ':' + column);
            err.filename = options.source;
            err.line = lineno;
            err.column = column;
            err.source = css;
            throw err;
        }

        //Parse stylesheet.
        function stylesheet() {
            return {
                type: 'stylesheet',
                stylesheet: {
                    rules: rules()
                }
            };
        }

        //Opening brace.
        function open() {
            return match(/^{\s*/);
        }

        //Closing brace.
        function close() {
            return match(/^}/);
        }

        //Parse ruleset.
        function rules() {
            var node;
            var rules = [];
            whitespace();
            comments(rules);
            while (css.length && css.charAt(0) != '}' && (node = atrule() || rule())) {
                if (node !== false) {
                    rules.push(node);
                    comments(rules);
                }
            }
            return rules;
        }

        //Match 're' and return captures.
        function match(re) {
            var m = re.exec(css);
            if (!m) return;
            var str = m[0];
            updatePosition(str);
            css = css.slice(str.length);
            return m;
        }

        //Parse whitespace.
        function whitespace() {
            match(/^\s*/);
        }

        //Parse comments.
        function comments(rules) {
            var c;
            rules = rules || [];
            while (c = comment()) {
                if (c !== false) {
                    rules.push(c);
                }
            }
            return rules;
        }

        //Parse comment.
        function comment() {
            var pos = position();
            if ('/' != css.charAt(0) || '*' != css.charAt(1)) return;

            var i = 2;
            while ("" != css.charAt(i) && ('*' != css.charAt(i) || '/' != css.charAt(i + 1))) ++i;
            i += 2;

            if ("" === css.charAt(i-1)) {
                return error('End of comment missing');
            }

            var str = css.slice(2, i - 2);
            column += 2;
            updatePosition(str);
            css = css.slice(i);
            column += 2;

            return pos({
                type: 'comment',
                comment: str
            });
        }

        //Parse selector.
        function selector() {
            var m = match(/^([^{]+)/);
            if (!m) return;
            /* @fix Remove all comments from selectors
             * http://ostermiller.org/findcomment.html */
            return trim(m[0])
                .replace(/\/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*\/+/g, '')
                .replace(/(?:"[^"]*"|'[^']*')/g, function(m) {
                    return m.replace(/,/g, '\u200C');
                })
                .split(/\s*(?![^(]*\)),\s*/)
                .map(function(s) {
                    return s.replace(/\u200C/g, ',');
                });
        }

        //Parse declaration.
        function declaration() {
            var pos = position();

            // prop
            var prop = match(/^(\*?[-#\/\*\\\w]+(\[[0-9a-z_-]+\])?)\s*/);
            if (!prop) return;
            prop = trim(prop[0]);

            // :
            if (!match(/^:\s*/)) return error("property missing ':'");

            // val
            var val = match(/^((?:'(?:\\'|.)*?'|"(?:\\"|.)*?"|\([^\)]*?\)|[^};])+)/);

            var ret = pos({
                type: 'declaration',
                property: prop.replace(commentre, ''),
                value: val ? trim(val[0]).replace(commentre, '') : ''
            });

            // ;
            match(/^[;\s]*/);

            return ret;
        }

        //Parse declarations.
        function declarations() {
            var decls = [];

            if (!open()) return error("missing '{'");
            comments(decls);

            // declarations
            var decl;
            while (decl = declaration()) {
                if (decl !== false) {
                    decls.push(decl);
                    comments(decls);
                }
            }

            if (!close()) return error("missing '}'");
            return decls;
        }

        //Parse keyframe.
        function keyframe() {
            var m;
            var vals = [];
            var pos = position();

            while (m = match(/^((\d+\.\d+|\.\d+|\d+)%?|[a-z]+)\s*/)) {
                vals.push(m[1]);
                match(/^,\s*/);
            }

            if (!vals.length) return;

            return pos({
                type: 'keyframe',
                values: vals,
                declarations: declarations()
            });
        }

        //Parse keyframes.
        function atkeyframes() {
            var pos = position();
            var m = match(/^@([-\w]+)?keyframes */);

            if (!m) return;
            var vendor = m[1];

            // identifier
            var m = match(/^([-\w]+)\s*/);
            if (!m) return error("@keyframes missing name");
            var name = m[1];

            if (!open()) return error("@keyframes missing '{'");

            var frame;
            var frames = comments();
            while (frame = keyframe()) {
                frames.push(frame);
                frames = frames.concat(comments());
            }

            if (!close()) return error("@keyframes missing '}'");

            return pos({
                type: 'keyframes',
                name: name,
                vendor: vendor,
                keyframes: frames
            });
        }

        //Parse supports.
        function atsupports() {
            var pos = position();
            var m = match(/^@supports *([^{]+)/);

            if (!m) return;
            var supports = trim(m[1]);

            if (!open()) return error("@supports missing '{'");

            var style = comments().concat(rules());

            if (!close()) return error("@supports missing '}'");

            return pos({
                type: 'supports',
                supports: supports,
                rules: style
            });
        }

        //Parse host.
        function athost() {
            var pos = position();
            var m = match(/^@host */);

            if (!m) return;

            if (!open()) return error("@host missing '{'");

            var style = comments().concat(rules());

            if (!close()) return error("@host missing '}'");

            return pos({
                type: 'host',
                rules: style
            });
        }

        //Parse media.
        function atmedia() {
            var pos = position();
            var m = match(/^@media *([^{]+)/);

            if (!m) return;
            var media = trim(m[1]);

            if (!open()) return error("@media missing '{'");

            var style = comments().concat(rules());

            if (!close()) return error("@media missing '}'");

            return pos({
                type: 'media',
                media: media,
                rules: style
            });
        }


        //Parse custom-media.
        function atcustommedia() {
            var pos = position();
            var m = match(/^@custom-media (--[^\s]+) *([^{;]+);/);
            if (!m) return;

            return pos({
                type: 'custom-media',
                name: trim(m[1]),
                media: trim(m[2])
            });
        }

        //Parse paged media.
        function atpage() {
            var pos = position();
            var m = match(/^@page */);
            if (!m) return;

            var sel = selector() || [];

            if (!open()) return error("@page missing '{'");
            var decls = comments();

            // declarations
            var decl;
            while (decl = declaration()) {
                decls.push(decl);
                decls = decls.concat(comments());
            }

            if (!close()) return error("@page missing '}'");

            return pos({
                type: 'page',
                selectors: sel,
                declarations: decls
            });
        }

        //Parse document.
        function atdocument() {
            var pos = position();
            var m = match(/^@([-\w]+)?document *([^{]+)/);
            if (!m) return;

            var vendor = trim(m[1]);
            var doc = trim(m[2]);

            if (!open()) return error("@document missing '{'");

            var style = comments().concat(rules());

            if (!close()) return error("@document missing '}'");

            return pos({
                type: 'document',
                document: doc,
                vendor: vendor,
                rules: style
            });
        }

        //Parse font-face.
        function atfontface() {
            var pos = position();
            var m = match(/^@font-face */);
            if (!m) return;

            if (!open()) return error("@font-face missing '{'");
            var decls = comments();

            // declarations
            var decl;
            while (decl = declaration()) {
                decls.push(decl);
                decls = decls.concat(comments());
            }

            if (!close()) return error("@font-face missing '}'");

            return pos({
                type: 'font-face',
                declarations: decls
            });
        }

        //Parse import
        var atimport = _compileAtrule('import');

        //Parse charset
        var atcharset = _compileAtrule('charset');

        //Parse namespace
        var atnamespace = _compileAtrule('namespace');

        //Parse non-block at-rules
        function _compileAtrule(name) {
            var re = new RegExp('^@' + name + ' *([^;\\n]+);');
            return function() {
                var pos = position();
                var m = match(re);
                if (!m) return;
                var ret = { type: name };
                ret[name] = m[1].trim();
                return pos(ret);
            }
        }

        //Parse at rule.
        function atrule() {
            if (css[0] != '@') return;

            return atkeyframes()
                || atmedia()
                || atcustommedia()
                || atsupports()
                || atimport()
                || atcharset()
                || atnamespace()
                || atdocument()
                || atpage()
                || athost()
                || atfontface();
        }

        //Parse rule.
        function rule() {
            var pos = position();
            var sel = selector();

            if (!sel) return error('selector missing');
            comments();

            return pos({
                type: 'rule',
                selectors: sel,
                declarations: declarations()
            });
        }

        return addParent(stylesheet());
    };

    //CSS.stringify()
    this.stringify = function(node, options){
        options = options || {};

        var compiler = options.compress
            ? new Compressed(options)
            : new Identity(options);

        // source maps
        if (options.sourcemap) {
            var sourcemaps = require('./source-map-support');
            sourcemaps(compiler);

            var code = compiler.compile(node);
            compiler.applySourceMaps();
            return { code: code, map: compiler.map.toJSON() };
        }

        var code = compiler.compile(node);
        return code;
    };
};