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

the proof of concept about the new plugins API #56

Open
wants to merge 11 commits into
base: master
Choose a base branch
from
70 changes: 70 additions & 0 deletions extractor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
var postcss = require('postcss');
var genericNames = require('generic-names');
var path = require('path');

var Values = require('postcss-modules-values');
var LocalByDefault = require('postcss-modules-local-by-default');
var ExtractImports = require('postcss-modules-extract-imports');
var Scope = require('postcss-modules-scope');
var Parser = require('postcss-modules-parser');

/**
* @param {array} options.append
* @param {array} options.prepend
* @param {array} options.use
* @param {function} options.createImportedName
* @param {function|string} options.generateScopedName
* @param {string} options.mode
* @param {string} options.rootDir
* @param {function} fetch
* @return {object}
*/
module.exports = function extractor(options, fetch) {
options = options || {};
var append = options.append;
var prepend = options.prepend;
var createImportedName = options.createImportedName;
var generateScopedName = options.generateScopedName;
var mode = options.mode;
var use = options.use;
var context = options.rootDir || process.cwd();

var scopedName;
if (generateScopedName) {
scopedName = typeof generateScopedName !== 'function'
? genericNames(generateScopedName || '[name]__[local]___[hash:base64:5]', {context: context})
: function (local, filename, css) {
// had to wrap that function cause i didn't expected,
// that generateShortName() and generateLongName() functions
// use the fake path to file (relative to rootDir)
// which result in the generated class names
return generateScopedName(local, filename, css, context);
};
} else {
// small fallback
scopedName = function (local, filename) {
return Scope.generateScopedName(local, path.relative(context, filename));
}
}

var plugins;
if (use) {
plugins = use;
} else {
plugins = (prepend || [])
.concat([
Values,
mode
? new LocalByDefault({mode: mode})
: LocalByDefault,
createImportedName
? new ExtractImports({createImportedName: createImportedName})
: ExtractImports,
new Scope({generateScopedName: scopedName}),
], append || []);
}

plugins = plugins.concat(new Parser({fetch: fetch}));

return postcss(plugins);
Copy link
Member Author

Choose a reason for hiding this comment

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

I'd like to move that file to the public module

}
113 changes: 50 additions & 63 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ if (!global.Promise) { global.Promise = require('promise-polyfill') }
var fs = require('fs');
var path = require('path');
var through = require('through');
var Core = require('css-modules-loader-core');
var extractor = require('./extractor');
var FileSystemLoader = require('css-modules-loader-core/lib/file-system-loader');
var assign = require('object-assign');
var stringHash = require('string-hash');
Expand All @@ -14,7 +14,8 @@ var ReadableStream = require('stream').Readable;
Custom `generateScopedName` function for `postcss-modules-scope`.
Short names consisting of source hash and line number.
*/
function generateShortName (name, filename, css) {
function generateShortName (name, filename, css, context) {
filename = path.relative(context, filename);
// first occurrence of the name
// TOOD: better match with regex
var i = css.indexOf('.' + name);
Expand All @@ -28,7 +29,8 @@ function generateShortName (name, filename, css) {
Custom `generateScopedName` function for `postcss-modules-scope`.
Appends a hash of the css source.
*/
function generateLongName (name, filename) {
function generateLongName (name, filename, css, context) {
filename = path.relative(context, filename);
var sanitisedPath = filename.replace(/\.[^\.\/\\]+$/, '')
.replace(/[\W_]+/g, '_')
.replace(/^_|_$/g, '');
Expand Down Expand Up @@ -93,59 +95,50 @@ var sourceByFile = {};

module.exports = function (browserify, options) {
options = options || {};

// if no root directory is specified, assume the cwd
var rootDir = options.rootDir || options.d;
if (rootDir) { rootDir = path.resolve(rootDir); }
if (!rootDir) { rootDir = process.cwd(); }
options.rootDir = options.rootDir || options.d || undefined;
options.append = options.postcssAfter || options.after || [];
options.use = options.use || options.u || undefined;

var cssOutFilename = options.output || options.o;
var jsonOutFilename = options.json || options.jsonOutput;

// PostCSS plugins passed to FileSystemLoader
var plugins = options.use || options.u;
if (!plugins) {
plugins = getDefaultPlugins(options);
}
else {
if (typeof plugins === 'string') {
plugins = [plugins];
}
}

var postcssAfter = options.postcssAfter || options.after || [];
plugins = plugins.concat(postcssAfter);

// load plugins by name (if a string is used)
plugins = plugins.map(function requirePlugin (name) {
// assume functions are already required plugins
if (typeof name === 'function') {
return name;
}

var plugin = require(require.resolve(name));

// custom scoped name generation
if (name === 'postcss-modules-scope') {
options[name] = options[name] || {};
if (!options[name].generateScopedName) {
options[name].generateScopedName = generateLongName;
// the compiled CSS stream needs to be avalible to the transform,
// but re-created on each bundle call.
var compiledCssStream;
var instance = extractor(options, fetch);

function fetch(_to, from) {
var to = _to.replace(/^["']|["']$/g, '');

return new Promise(function (resolve, reject) {
try {
var filename = /\w/i.test(to[0])
? require.resolve(to)
: path.resolve(path.dirname(from), to);
} catch (e) {
return void reject(e);
}
}

if (name in options) {
plugin = plugin(options[name]);
}
else {
plugin = plugin.postcss || plugin();
}
fs.readFile(filename, 'utf8', function (err, css) {
if (err) {
return void reject(err);
}

return plugin;
});
instance.process(css, {from: filename})
.then(function (result) {
var css = result.css;
var tokens = result.root.tokens;

// the compiled CSS stream needs to be avalible to the transform,
// but re-created on each bundle call.
var compiledCssStream;
assign(tokensByFile, tokens);
sourceByFile[filename] = css;
compiledCssStream.push(css);

resolve(tokens);
})
.catch(reject);
});
});
}

function transform (filename) {
// only handle .css files
Expand All @@ -156,25 +149,19 @@ module.exports = function (browserify, options) {
// collect visited filenames
filenames.push(filename);

var loader = new FileSystemLoader(rootDir, plugins);
return through(function noop () {}, function end () {
var self = this;

loader.fetch(path.relative(rootDir, filename), '/').then(function (tokens) {
var output = 'module.exports = ' + JSON.stringify(tokens);

assign(tokensByFile, loader.tokensByFile);

// store this file's source to be written out to disk later
sourceByFile[filename] = loader.finalSource;
fetch(filename, filename)
.then(function (tokens) {
var output = 'module.exports = ' + JSON.stringify(tokens);

compiledCssStream.push(loader.finalSource);

self.queue(output);
self.queue(null);
}, function (err) {
self.emit('error', err);
});
self.queue(output);
self.queue(null);
})
.catch(function (err) {
self.emit('error', err);
});
});
}

Expand Down
7 changes: 7 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,14 @@
"main": "index.js",
"dependencies": {
"css-modules-loader-core": "^1.0.0",
"generic-names": "^1.0.1",
"object-assign": "^3.0.0",
"postcss": "^5.0.10",
"postcss-modules-extract-imports": "^1.0.0",
"postcss-modules-local-by-default": "^1.0.0",
"postcss-modules-parser": "^1.0.1",
"postcss-modules-scope": "^1.0.0",
"postcss-modules-values": "^1.1.1",
"promise-polyfill": "^2.1.0",
"string-hash": "^1.1.0",
"through": "^2.3.7"
Expand Down
3 changes: 2 additions & 1 deletion tests/cases/compose-node-module/expected.css
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
._cool_styles_styles__foo {
._node_modules_cool_styles_styles__foo {
color: #F00;
}

._styles__foo {
background: black;
}
1 change: 1 addition & 0 deletions tests/cases/import-and-compose/expected.css
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
._styles_2__bar {
background: #BAA;
}

._styles_1__foo {
color: #F00;
}