问题
I would like to be able to use reworkcss/css in the browser. I downloaded version 2.0.0 from github and installed the needed packages with nmp install. I have tried requireJS (which claims to be able to process the CommonJS Module Format), requiring index.js, but an error occurs regarding "exports" being undefined. I also attempted to condense it using browserify, but this is evidently not what it was intended for. Does anyone know what I should do? I can't use node.js for the project, but need the CSS module's abilities in the browser.
回答1:
I ended up condensing it by hand. Here's the result, if anyone's interested. It seems to work pretty well. If there are any mistakes, please mention them in a comment so they can be fixed.
/**
* 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;
};
};
来源:https://stackoverflow.com/questions/24769087/use-node-rework-css-in-browser