Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support import options #4

Merged
merged 1 commit into from
Apr 30, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
151 changes: 151 additions & 0 deletions import.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
//
// # Import
//
// Subsitute `@import { file: filename; }` with the contents of `filename`.
//

/*jslint node: true */
"use strict";

var fs = require('fs');
var path = require('path');
var whitespace = require('css-whitespace');
var rework = require('rework');

//
// ## Register plugin
//
// * **opts**, options object. May contain the following:
//
// * path: base path for resolving imports.
// * whitespace: boolean, set to true if imported files use significant
// whitespace instead of curlies.
//
module.exports = function (opts) {
return function (style) {
return new Import(opts).visit(style);
};
};

//
// ## Importer
//
function Import(opts) {
if(!opts.base) {
throw new Error("Must specify a file path");
}

opts = opts || {};
this.opts = opts;
this.base = opts.base || process.cwd();
this.path = opts.path;
this.visit = this.visit.bind(this);
this.importFile = this.importFile.bind(this);
this.map = opts.map || [];
this.target = opts.target;

// is relative?
if(path.resolve(this.path) !== this.path) {
this.path = path.resolve(this.base, this.path);
}
}

Import.prototype.visit = function (node, index, arr) {
if (!node) return;
var type = node.type || 'stylesheet';
if (!this[type]) return;
this[type](node, index, arr);
};

Import.prototype.stylesheet = function (stylesheet) {
for (var i = stylesheet.rules.length; i >= 0; i-=1) {
this.visit(stylesheet.rules[i], i, stylesheet.rules);
}
};

Import.prototype.import = function (node, index, arr) {
var regex = /url\(['"]?(.*?)['"]?\)/;
var filename = node.import.match(regex);
if (filename && filename[1] && !isUrl(filename[1])) {
if(this.target) {
// /(\w+)_(common|target)\.\w+/
var targetRegex = new RegExp('(\\w+)_(' + this.target.join('|') + ')\\.\\w+');
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have no decision here.
Besides _layout style, is it reasonable to also accept .layout style such as imprt.large.css, skin.default.css?

var matchTarget = filename[1].match(targetRegex);
if(!matchTarget) {
arr.splice(index, 1);
return;
}
}

var ast = this.parseFile(filename[1]);
var i = 0;
arr.splice(index, 1);

ast.rules.forEach(function (rule) {
arr.splice(0 + i + index, 0, rule);
i++;
});
}
};

Import.prototype.rule = function (rule, index, base) {
if (rule.selectors[0] == '@import') {
var ast = rule.declarations.map(this.importFile);
var rules = [];
ast.filter(function (item) {
return !!item;
}).forEach(function (item) {
rules = rules.concat(item.rules);
});

var removed = base.splice(index, 1);
// Insert rules at same index
var i = 0; // To make imports in order.
rules.forEach(function (rule) {
var removed = base.splice(index + i, 0, rule);
i++;
});
}
};

Import.prototype.importFile = function (declaration) {
if (declaration.property !== 'file') return;
return this.parseFile(declaration.value);
};

Import.prototype.parseFile = function (file) {
var load;
//is absolute?
if(path.resolve(file) === file) {
load = path.join(this.base, file);
} else {
load = path.resolve(path.dirname(this.path), file);
}

// Skip circular imports.
if (this.map.indexOf(load) !== -1) {
return false;
}
var data = fs.readFileSync(load, this.opts.encoding || 'utf8');

if (this.opts.whitespace) {
data = whitespace(data);
}

this.map.push(load);
// Create AST and look for imports in imported code.
var opts = {
whitespace: this.opts.whitespace,
map: this.map,
base: this.base,
path: load
};

var ast = rework(data).use(module.exports(opts));
return ast.obj.stylesheet;
};


function isUrl(url) {
return (/^([\w]+:)?\/\/./).test(url);
}
41 changes: 20 additions & 21 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ var rework = require('rework');
var calc = require('rework-calc');
var hex = require('rework-hex-alpha');
var vars = require('rework-vars')();
var imprt = require('rework-importer');
var imprt = require('./import.js');
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please also remove the 'rework-importer' definition in package.json

var path = require('path');

/**
Expand All @@ -22,30 +22,29 @@ module.exports = provecss;
*/

function provecss (string, options) {
var browsers;
var import_path, import_base;
if(options && options.browsers) {
browsers = options.browsers;
}
if(options && options.path) {
import_path = path.basename(options.path);
if(!options.base) {
import_base = path.dirname(options.path);
} else {
import_base = options.base;
}
options = options || {};
this.browsers = options.browsers;
if(options.path) {
this.import_path = path.basename(options.path);
this.import_base = options.base || path.dirname(options.path);
}
this.layout_target = options.target;

//not run autoprefixer by default
if(browsers) {
string = prefixes(browsers).process(string).css;
if(this.browsers) {
string = prefixes(this.browsers).process(string).css;
}
if(import_path) {
string = rework(string)
.use(imprt({
path: import_path,
base: import_base
})).toString();

//handle import inlining if any
if(this.import_path) {
var opts = {
path: this.import_path,
base: this.import_base,
target: this.layout_target
};
string = rework(string).use(imprt(opts)).toString();
}

return rework(string, options)
.use(vars)
.use(hex)
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@
},
"dependencies": {
"autoprefixer": "~1.1.20140429",
"css-whitespace": "~1.1.0",
"rework": "~0.20.2",
"rework-calc": "~0.2.1",
"rework-color-function": "~1.0.0",
"rework-font-variant": "~1.0.0",
"rework-hex-alpha": "~1.0.0",
"rework-vars": "~3.0.0",
"rework-importer": "~0.3.1"
"rework-vars": "~3.0.0"
},
"devDependencies": {
"mocha": "~1.18.2"
Expand Down
3 changes: 2 additions & 1 deletion test/features/imprt.css
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
@import url("imprt_core.css");
@import url("imprt_large.css");
@import url("imprt_large.css");
@import url("imprt_xlarge.css");
6 changes: 6 additions & 0 deletions test/features/imprt.out.css
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,10 @@ headers {
headers {
background-color: black;
}
}

@media (min-width: 1024px) {
headers {
background-color: red;
}
}
3 changes: 3 additions & 0 deletions test/features/imprt_core.out.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
headers {
background-color: orange;
}
2 changes: 1 addition & 1 deletion test/features/imprt_large.css
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
headers {
background-color: black;
}
}
}
9 changes: 9 additions & 0 deletions test/features/imprt_large.out.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
headers {
background-color: orange;
}

@media (min-width: 768px) {
headers {
background-color: black;
}
}
5 changes: 5 additions & 0 deletions test/features/imprt_xlarge.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@media (min-width: 1024px) {
headers {
background-color: red;
}
}
9 changes: 9 additions & 0 deletions test/features/imprt_xlarge.out.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
headers {
background-color: orange;
}

@media (min-width: 1024px) {
headers {
background-color: red;
}
}
43 changes: 42 additions & 1 deletion test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ describe('@import inlining feature', function () {
{path:"test/features/imprt.css"}).trim(),
output.trim());
});

it('should add base option support', function () {
var input = read('features/imprt');
var output = read('features/imprt.out');
Expand All @@ -56,6 +56,47 @@ describe('@import inlining feature', function () {
}).trim(),
output.trim());
});

it('should generate css based on target option (core)', function () {
var input = read('features/imprt');
var output = read('features/imprt_core.out');
var option = {
path: 'test/features/imprt.css',
target: ['core']
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if target or exclude is more useful for general case. But I think it's ok for first PR

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

was an proposal to rework-importer simme/rework-importer#8

};
assert.equal(provecss(input, option).trim(), output.trim());
});

it('should generate css based on target option (core+large)', function () {
var input = read('features/imprt');
var output = read('features/imprt_large.out');
var option = {
path: 'test/features/imprt.css',
target: ['core', 'large']
};
assert.equal(provecss(input, option).trim(), output.trim());
});

it('should generate css based on target option (core+xlarge)', function () {
var input = read('features/imprt');
var output = read('features/imprt_xlarge.out');
var option = {
path: 'test/features/imprt.css',
target: ['core', 'xlarge']
};
assert.equal(provecss(input, option).trim(), output.trim());
});

it('should generate css based on target option (core+large+xlarge)', function () {
var input = read('features/imprt');
var output = read('features/imprt.out');
var option = {
path: 'test/features/imprt.css',
target: ['core', 'large' , 'xlarge']
};
assert.equal(provecss(input, option).trim(), output.trim());
});

});

/**
Expand Down