From aafa52198a4f0dafe8d594993947a3eb67e3c20e Mon Sep 17 00:00:00 2001 From: Josh Johnston Date: Fri, 25 Sep 2015 14:18:47 +1000 Subject: [PATCH 01/63] upgrade css-modules-loader-core for Values support --- index.js | 3 ++- package.json | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 3c673af..6f20db9 100644 --- a/index.js +++ b/index.js @@ -46,7 +46,8 @@ function getDefaultPlugins (options) { scope.generateScopedName = customNameFunc || defaultNameFunc; return [ - Core.localByDefault + Core.values + , Core.localByDefault , Core.extractImports , scope ]; diff --git a/package.json b/package.json index 11daa31..98439a3 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "A browserify transform to load CSS Modules", "main": "index.js", "dependencies": { - "css-modules-loader-core": "0.0.12", + "css-modules-loader-core": "^1.0.0-beta4", "object-assign": "^3.0.0", "string-hash": "^1.1.0", "through": "^2.3.7" From 43868a5587e709a6f085d74677e5e924663eb818 Mon Sep 17 00:00:00 2001 From: Josh Johnston Date: Fri, 25 Sep 2015 14:51:52 +1000 Subject: [PATCH 02/63] use promise-polyfill so it still works in older versions of node --- index.js | 4 +++- package.json | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/index.js b/index.js index 6f20db9..d38a759 100644 --- a/index.js +++ b/index.js @@ -1,3 +1,6 @@ +// Some css-modules-loader-code dependencies use Promise so we'll provide it for older node versions +if (!global.Promise) { global.Promise = require('promise-polyfill') } + var fs = require('fs'); var path = require('path'); var through = require('through'); @@ -6,7 +9,6 @@ var FileSystemLoader = require('css-modules-loader-core/lib/file-system-loader') var assign = require('object-assign'); var stringHash = require('string-hash'); - /* Custom `generateScopedName` function for `postcss-modules-scope`. Short names consisting of source hash and line number. diff --git a/package.json b/package.json index 98439a3..24857f7 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "main": "index.js", "dependencies": { "css-modules-loader-core": "^1.0.0-beta4", + "promise-polyfill": "^2.1.0", "object-assign": "^3.0.0", "string-hash": "^1.1.0", "through": "^2.3.7" From 7aeaaa082af63ec383a2af270f2bbfe58bea36f5 Mon Sep 17 00:00:00 2001 From: Josh Johnston Date: Tue, 3 Nov 2015 20:38:47 +0000 Subject: [PATCH 03/63] upgrade css-modules-loader-core --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 51a9bcd..70e62ca 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "A browserify transform to load CSS Modules", "main": "index.js", "dependencies": { - "css-modules-loader-core": "0.0.12", + "css-modules-loader-core": "^1.0.0", "object-assign": "^3.0.0", "string-hash": "^1.1.0", "through": "^2.3.7" From 75c79b9ad4f5d3b74f17628b04929af84c564749 Mon Sep 17 00:00:00 2001 From: Josh Johnston Date: Tue, 3 Nov 2015 20:38:50 +0000 Subject: [PATCH 04/63] 0.11.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 70e62ca..2dbad90 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "css-modulesify", - "version": "0.10.0", + "version": "0.11.0", "description": "A browserify transform to load CSS Modules", "main": "index.js", "dependencies": { From 21b4568178c343f6db73ec8a9950866ce21967b4 Mon Sep 17 00:00:00 2001 From: Josh Johnston Date: Tue, 3 Nov 2015 21:09:45 +0000 Subject: [PATCH 05/63] added promise-polyfill for compatibility with older versions of node --- index.js | 2 ++ package.json | 1 + 2 files changed, 3 insertions(+) diff --git a/index.js b/index.js index 4bbbbaa..5431576 100644 --- a/index.js +++ b/index.js @@ -1,3 +1,5 @@ +if (!global.Promise) { global.Promise = require('promise-polyfill'); } + var fs = require('fs'); var path = require('path'); var through = require('through'); diff --git a/package.json b/package.json index 2dbad90..0acb34d 100644 --- a/package.json +++ b/package.json @@ -6,6 +6,7 @@ "dependencies": { "css-modules-loader-core": "^1.0.0", "object-assign": "^3.0.0", + "promise-polyfill": "^2.1.0", "string-hash": "^1.1.0", "through": "^2.3.7" }, From 9761c179dc33a3a8c05c8f897dad220be54d9996 Mon Sep 17 00:00:00 2001 From: Josh Johnston Date: Tue, 3 Nov 2015 21:10:17 +0000 Subject: [PATCH 06/63] 0.12.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 0acb34d..6484a7d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "css-modulesify", - "version": "0.11.0", + "version": "0.12.0", "description": "A browserify transform to load CSS Modules", "main": "index.js", "dependencies": { From 18df7ceaf6a14adda33be6ae71e0b6f105287a73 Mon Sep 17 00:00:00 2001 From: Josh Johnston Date: Tue, 3 Nov 2015 21:30:58 +0000 Subject: [PATCH 07/63] 0.13.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6484a7d..f59de1f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "css-modulesify", - "version": "0.12.0", + "version": "0.13.0", "description": "A browserify transform to load CSS Modules", "main": "index.js", "dependencies": { From ab0da81394630e1a99578373e4178767d4b033a5 Mon Sep 17 00:00:00 2001 From: Lochlan Bunn Date: Mon, 26 Oct 2015 11:08:10 +1000 Subject: [PATCH 08/63] allow parallel bundles to isolate their css source caches --- index.js | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/index.js b/index.js index 827907d..053e5e0 100644 --- a/index.js +++ b/index.js @@ -101,6 +101,11 @@ module.exports = function (browserify, options) { var cssOutFilename = options.output || options.o; var jsonOutFilename = options.json || options.jsonOutput; + var sourceKey = cssOutFilename; + + // keying our source caches by the name of our output file means we can + // isolate css compilation of seperate bundles that are running in parallel + sourceByFile[sourceKey] = sourceByFile[sourceKey] || {}; // PostCSS plugins passed to FileSystemLoader var plugins = options.use || options.u; @@ -166,7 +171,7 @@ module.exports = function (browserify, options) { assign(tokensByFile, loader.tokensByFile); // store this file's source to be written out to disk later - sourceByFile[filename] = loader.finalSource; + sourceByFile[sourceKey][filename] = loader.finalSource; compiledCssStream.push(loader.finalSource); @@ -190,8 +195,8 @@ module.exports = function (browserify, options) { bundle.emit('css stream', compiledCssStream); bundle.on('end', function () { - // Combine the collected sources into a single CSS file - var files = Object.keys(sourceByFile); + // Combine the collected sources for a single bundle into a single CSS file + var files = Object.keys(sourceByFile[sourceKey]); var css; // end the output stream @@ -200,7 +205,7 @@ module.exports = function (browserify, options) { // write the css file if (cssOutFilename) { css = files.map(function (file) { - return sourceByFile[file]; + return sourceByFile[sourceKey][file]; }).join('\n'); fs.writeFile(cssOutFilename, css, function (err) { From 882f90f38898b238d704efdaf2e716442b804958 Mon Sep 17 00:00:00 2001 From: Josh Johnston Date: Thu, 19 Nov 2015 21:15:28 +1100 Subject: [PATCH 09/63] 0.14.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f59de1f..e50b975 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "css-modulesify", - "version": "0.13.0", + "version": "0.14.0", "description": "A browserify transform to load CSS Modules", "main": "index.js", "dependencies": { From f9a2ea2825f219b05ddd931c6a1bd3cffdad2709 Mon Sep 17 00:00:00 2001 From: Josh Johnston Date: Fri, 20 Nov 2015 20:30:29 +1100 Subject: [PATCH 10/63] use the same loader across all files (within a context) --- index.js | 33 ++++++++------------------------- 1 file changed, 8 insertions(+), 25 deletions(-) diff --git a/index.js b/index.js index 053e5e0..c41eeb2 100644 --- a/index.js +++ b/index.js @@ -80,16 +80,11 @@ var cssExt = /\.css$/; // // persist these for as long as the process is running. #32 -// keep track of css files visited -var filenames = []; - // keep track of all tokens so we can avoid duplicates var tokensByFile = {}; -// keep track of all source files for later builds: when -// using watchify, not all files will be caught on subsequent -// bundles -var sourceByFile = {}; +// we need a separate loader for each entry point +var loadersByFile = {}; module.exports = function (browserify, options) { options = options || {}; @@ -103,9 +98,10 @@ module.exports = function (browserify, options) { var jsonOutFilename = options.json || options.jsonOutput; var sourceKey = cssOutFilename; - // keying our source caches by the name of our output file means we can - // isolate css compilation of seperate bundles that are running in parallel - sourceByFile[sourceKey] = sourceByFile[sourceKey] || {}; + var loader = loadersByFile[sourceKey]; + if (!loader) { + loader = loadersByFile[sourceKey] = new FileSystemLoader(rootDir, plugins); + } // PostCSS plugins passed to FileSystemLoader var plugins = options.use || options.u; @@ -158,10 +154,6 @@ module.exports = function (browserify, options) { return through(); } - // collect visited filenames - filenames.push(filename); - - var loader = new FileSystemLoader(rootDir, plugins); return through(function noop () {}, function end () { var self = this; @@ -170,11 +162,6 @@ module.exports = function (browserify, options) { assign(tokensByFile, loader.tokensByFile); - // store this file's source to be written out to disk later - sourceByFile[sourceKey][filename] = loader.finalSource; - - compiledCssStream.push(loader.finalSource); - self.queue(output); self.queue(null); }, function (err) { @@ -196,18 +183,14 @@ module.exports = function (browserify, options) { bundle.on('end', function () { // Combine the collected sources for a single bundle into a single CSS file - var files = Object.keys(sourceByFile[sourceKey]); - var css; + var css = loader.finalSource; // end the output stream + compiledCssStream.push(css); compiledCssStream.push(null); // write the css file if (cssOutFilename) { - css = files.map(function (file) { - return sourceByFile[sourceKey][file]; - }).join('\n'); - fs.writeFile(cssOutFilename, css, function (err) { if (err) { browserify.emit('error', err); From 8b05764e264e60e12e0902c6e982411f6aaba867 Mon Sep 17 00:00:00 2001 From: Josh Johnston Date: Fri, 20 Nov 2015 22:11:40 +1100 Subject: [PATCH 11/63] added failing test case, dedupe sources to make it pass --- index.js | 17 +++++++++++++++++ tests/cases/compose-from-shared/expected.css | 9 +++++++++ tests/cases/compose-from-shared/main.js | 2 ++ tests/cases/compose-from-shared/shared.css | 3 +++ tests/cases/compose-from-shared/styles-1.css | 4 ++++ tests/cases/compose-from-shared/styles-2.css | 4 ++++ 6 files changed, 39 insertions(+) create mode 100644 tests/cases/compose-from-shared/expected.css create mode 100644 tests/cases/compose-from-shared/main.js create mode 100644 tests/cases/compose-from-shared/shared.css create mode 100644 tests/cases/compose-from-shared/styles-1.css create mode 100644 tests/cases/compose-from-shared/styles-2.css diff --git a/index.js b/index.js index c41eeb2..5bcbc91 100644 --- a/index.js +++ b/index.js @@ -74,6 +74,19 @@ function normalizeManifestPaths (tokensByFile, rootDir) { return output; } +function dedupeSources (sources) { + var foundHashes = {} + Object.keys(sources).forEach(function (key) { + var hash = stringHash(sources[key]); + if (foundHashes[hash]) { + delete sources[key]; + } + else { + foundHashes[hash] = true; + } + }) +} + var cssExt = /\.css$/; // caches @@ -182,6 +195,10 @@ module.exports = function (browserify, options) { bundle.emit('css stream', compiledCssStream); bundle.on('end', function () { + // under certain conditions (eg. with shared libraries) we can end up with + // multiple occurrences of the same rule, so we need to remove duplicates + dedupeSources(loader.sources) + // Combine the collected sources for a single bundle into a single CSS file var css = loader.finalSource; diff --git a/tests/cases/compose-from-shared/expected.css b/tests/cases/compose-from-shared/expected.css new file mode 100644 index 0000000..9c004b1 --- /dev/null +++ b/tests/cases/compose-from-shared/expected.css @@ -0,0 +1,9 @@ +._shared__shared { + background: #000; +} +._styles_1__foo { + color: #F00; +} +._styles_2__bar { + background: #BAA; +} diff --git a/tests/cases/compose-from-shared/main.js b/tests/cases/compose-from-shared/main.js new file mode 100644 index 0000000..14cc992 --- /dev/null +++ b/tests/cases/compose-from-shared/main.js @@ -0,0 +1,2 @@ +require('./styles-1.css'); +require('./styles-2.css'); diff --git a/tests/cases/compose-from-shared/shared.css b/tests/cases/compose-from-shared/shared.css new file mode 100644 index 0000000..d6fc168 --- /dev/null +++ b/tests/cases/compose-from-shared/shared.css @@ -0,0 +1,3 @@ +.shared { + background: #000; +} diff --git a/tests/cases/compose-from-shared/styles-1.css b/tests/cases/compose-from-shared/styles-1.css new file mode 100644 index 0000000..2ffd802 --- /dev/null +++ b/tests/cases/compose-from-shared/styles-1.css @@ -0,0 +1,4 @@ +.foo { + composes: shared from "./shared.css"; + color: #F00; +} diff --git a/tests/cases/compose-from-shared/styles-2.css b/tests/cases/compose-from-shared/styles-2.css new file mode 100644 index 0000000..f9d32d8 --- /dev/null +++ b/tests/cases/compose-from-shared/styles-2.css @@ -0,0 +1,4 @@ +.bar { + composes: shared from "./shared.css"; + background: #BAA; +} From 01ccedcdbcb75e0854650c6daaed2b8757cf77e9 Mon Sep 17 00:00:00 2001 From: Josh Johnston Date: Sat, 21 Nov 2015 14:54:26 +1100 Subject: [PATCH 12/63] 0.15.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e50b975..d09d392 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "css-modulesify", - "version": "0.14.0", + "version": "0.15.0", "description": "A browserify transform to load CSS Modules", "main": "index.js", "dependencies": { From 93a9163975383bad0cc240ef1bdb95a2b9ececad Mon Sep 17 00:00:00 2001 From: Josh Johnston Date: Mon, 30 Nov 2015 23:11:47 +1100 Subject: [PATCH 13/63] updates to make watchify work + using a different kind of tranform that ends the stream correctly + inject require statements for composed css modules, so that they are added to browserify's dependency graph + clear token cache so that we can rebuild the parts that change --- cmify.js | 33 +++++++++++++++++++++++++++++ index.js | 63 +++++++++++++++++++++++++++++++------------------------- 2 files changed, 68 insertions(+), 28 deletions(-) create mode 100644 cmify.js diff --git a/cmify.js b/cmify.js new file mode 100644 index 0000000..cd34282 --- /dev/null +++ b/cmify.js @@ -0,0 +1,33 @@ +var stream = require("stream"); +var path = require("path"); +var util = require("util"); + +util.inherits(Cmify, stream.Transform); +function Cmify(filename, opts) { + if (!(this instanceof Cmify)) { + return new Cmify(filename, opts); + } + + stream.Transform.call(this); + + this.cssExt = /\.css$/; + this._data = ""; + this._filename = filename; +} + +Cmify.prototype.isCssFile = function (filename) { + return this.cssExt.test(filename) +} + +Cmify.prototype._transform = function (buf, enc, callback) { + // only handle .css files + if (!this.isCssFile(this._filename)) { + this.push(buf) + return callback() + } + + this._data += buf + callback() +}; + +module.exports = Cmify diff --git a/index.js b/index.js index 5bcbc91..ea1bf97 100644 --- a/index.js +++ b/index.js @@ -3,7 +3,7 @@ if (!global.Promise) { global.Promise = require('promise-polyfill') } var fs = require('fs'); var path = require('path'); -var through = require('through'); +var Cmify = require('./cmify'); var Core = require('css-modules-loader-core'); var FileSystemLoader = require('css-modules-loader-core/lib/file-system-loader'); var assign = require('object-assign'); @@ -87,8 +87,6 @@ function dedupeSources (sources) { }) } -var cssExt = /\.css$/; - // caches // // persist these for as long as the process is running. #32 @@ -161,31 +159,43 @@ module.exports = function (browserify, options) { // but re-created on each bundle call. var compiledCssStream; - function transform (filename) { - // only handle .css files - if (!cssExt.test(filename)) { - return through(); - } - - 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); + // TODO: clean this up so there's less scope crossing + Cmify.prototype._flush = function (callback) { + var self = this; + var filename = this._filename; - self.queue(output); - self.queue(null); - }, function (err) { - self.emit('error', err); - }); + // only handle .css files + if (!this.isCssFile(filename)) { return callback(); } + + // convert css to js before pushing + // reset the `tokensByFile` cache + var relFilename = path.relative(rootDir, filename) + tokensByFile[filename] = loader.tokensByFile[filename] = null; + + loader.fetch(relFilename, '/').then(function (tokens) { + var newFiles = Object.keys(loader.tokensByFile) + var oldFiles = Object.keys(tokensByFile) + var diff = newFiles.filter(function (f) { + return oldFiles.indexOf(f) === -1 + }) + + var output = diff.map(function (f) { + return "require('" + f + "')\n" + }) + '\n\n' + 'module.exports = ' + JSON.stringify(tokens); + + assign(tokensByFile, loader.tokensByFile); + + self.push(output); + return callback() + }, function (err) { + browserify.emit('error', err); + return callback() }); - } + }; - browserify.transform(transform, { - global: true - }); + browserify.transform(Cmify); + + // ---- browserify.on('bundle', function (bundle) { // on each bundle, create a new stream b/c the old one might have ended @@ -223,9 +233,6 @@ module.exports = function (browserify, options) { } }); } - - // reset the `tokensByFile` cache - tokensByFile = {}; }); }); From eb116727ea52c0645854189e43fbe3c521507718 Mon Sep 17 00:00:00 2001 From: Josh Johnston Date: Tue, 1 Dec 2015 20:55:41 +1100 Subject: [PATCH 14/63] bug fixes for watchify + using a local copy of file-system-loader for now (will move over to css-modules-loader-core later) + file-system-loader uses dependency-graph to record dependencies + removed dedupeSources (no longer needed) + global transform is optional (false by default) + updated tests to match --- file-system-loader.js | 136 +++++++++++++++++++ index.js | 37 ++--- package.json | 1 + tests/cases/compose-node-module/expected.css | 2 +- tests/index.js | 5 + 5 files changed, 153 insertions(+), 28 deletions(-) create mode 100644 file-system-loader.js diff --git a/file-system-loader.js b/file-system-loader.js new file mode 100644 index 0000000..1345a9c --- /dev/null +++ b/file-system-loader.js @@ -0,0 +1,136 @@ +'use strict'; + +var DepGraph = require('dependency-graph').DepGraph; + +Object.defineProperty(exports, '__esModule', { + value: true +}); + +var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } + +var _indexJs = require('css-modules-loader-core/lib/index.js'); + +var _indexJs2 = _interopRequireDefault(_indexJs); + +var _fs = require('fs'); + +var _fs2 = _interopRequireDefault(_fs); + +var _path = require('path'); + +var _path2 = _interopRequireDefault(_path); + +// Sorts dependencies in the following way: +// AAA comes before AA and A +// AB comes after AA and before A +// All Bs come after all As +// This ensures that the files are always returned in the following order: +// - In the order they were required, except +// - After all their dependencies +var traceKeySorter = function traceKeySorter(a, b) { + if (a.length < b.length) { + return a < b.substring(0, a.length) ? -1 : 1; + } else if (a.length > b.length) { + return a.substring(0, b.length) <= b ? -1 : 1; + } else { + return a < b ? -1 : 1; + } +}; + +var FileSystemLoader = (function () { + function FileSystemLoader(root, plugins) { + _classCallCheck(this, FileSystemLoader); + + this.root = root; + this.sources = {}; + this.traces = {}; + this.importNr = 0; + this.core = new _indexJs2['default'](plugins); + this.tokensByFile = {}; + this.deps = new DepGraph(); + } + + _createClass(FileSystemLoader, [{ + key: 'fetch', + value: function fetch(_newPath, relativeTo, _trace) { + var _this = this; + + var newPath = _newPath.replace(/^["']|["']$/g, ''), + trace = _trace || String.fromCharCode(this.importNr++); + return new Promise(function (resolve, reject) { + var relativeDir = _path2['default'].dirname(relativeTo), + rootRelativePath = _path2['default'].resolve(relativeDir, newPath), + fileRelativePath = _path2['default'].resolve(_path2['default'].join(_this.root, relativeDir), newPath); + + // if the path is not relative or absolute, try to resolve it in node_modules + if (newPath[0] !== '.' && newPath[0] !== '/') { + try { + fileRelativePath = require.resolve(newPath); + } catch (e) {} + } + + // first time? add a node + if (_trace === undefined) { + if (!_this.deps.hasNode(fileRelativePath)) { + _this.deps.addNode(fileRelativePath); + } + } + // otherwise add a dependency + else { + var parentFilePath = _path2['default'].join(_this.root, relativeTo); + if (!_this.deps.hasNode(parentFilePath)) { + console.error('NO NODE', parentFilePath, fileRelativePath) + } + if (!_this.deps.hasNode(fileRelativePath)) { + _this.deps.addNode(fileRelativePath); + } + _this.deps.addDependency(parentFilePath, fileRelativePath); + } + + var tokens = _this.tokensByFile[fileRelativePath]; + if (tokens) { + return resolve(tokens); + } + + _fs2['default'].readFile(fileRelativePath, 'utf-8', function (err, source) { + if (err) reject(err); + _this.core.load(source, rootRelativePath, trace, _this.fetch.bind(_this)).then(function (_ref) { + var injectableSource = _ref.injectableSource; + var exportTokens = _ref.exportTokens; + + _this.sources[fileRelativePath] = injectableSource; + _this.traces[trace] = fileRelativePath; + _this.tokensByFile[fileRelativePath] = exportTokens; + resolve(exportTokens); + }, reject); + }); + }); + } + }, { + key: 'finalSource', + get: function () { + var traces = this.traces; + var sources = this.sources; + var written = new Set(); + + return Object.keys(traces).sort(traceKeySorter).map(function (key) { + var filename = traces[key]; + if (written.has(filename)) { + return null; + } + written.add(filename); + + return sources[filename]; + }).join(''); + } + }]); + + return FileSystemLoader; +})(); + +exports['default'] = FileSystemLoader; +module.exports = exports['default']; diff --git a/index.js b/index.js index ea1bf97..37c93f9 100644 --- a/index.js +++ b/index.js @@ -5,7 +5,7 @@ var fs = require('fs'); var path = require('path'); var Cmify = require('./cmify'); var Core = require('css-modules-loader-core'); -var FileSystemLoader = require('css-modules-loader-core/lib/file-system-loader'); +var FileSystemLoader = require('./file-system-loader'); var assign = require('object-assign'); var stringHash = require('string-hash'); var ReadableStream = require('stream').Readable; @@ -16,7 +16,7 @@ var ReadableStream = require('stream').Readable; */ function generateShortName (name, filename, css) { // first occurrence of the name - // TOOD: better match with regex + // TODO: better match with regex var i = css.indexOf('.' + name); var numLines = css.substr(0, i).split(/[\r\n]/).length; @@ -74,19 +74,6 @@ function normalizeManifestPaths (tokensByFile, rootDir) { return output; } -function dedupeSources (sources) { - var foundHashes = {} - Object.keys(sources).forEach(function (key) { - var hash = stringHash(sources[key]); - if (foundHashes[hash]) { - delete sources[key]; - } - else { - foundHashes[hash] = true; - } - }) -} - // caches // // persist these for as long as the process is running. #32 @@ -105,6 +92,11 @@ module.exports = function (browserify, options) { if (rootDir) { rootDir = path.resolve(rootDir); } if (!rootDir) { rootDir = process.cwd(); } + var transformOpts = {}; + if (options.global) { + transformOpts.global = true; + } + var cssOutFilename = options.output || options.o; var jsonOutFilename = options.json || options.jsonOutput; var sourceKey = cssOutFilename; @@ -173,13 +165,8 @@ module.exports = function (browserify, options) { tokensByFile[filename] = loader.tokensByFile[filename] = null; loader.fetch(relFilename, '/').then(function (tokens) { - var newFiles = Object.keys(loader.tokensByFile) - var oldFiles = Object.keys(tokensByFile) - var diff = newFiles.filter(function (f) { - return oldFiles.indexOf(f) === -1 - }) - - var output = diff.map(function (f) { + var deps = loader.deps.dependenciesOf(filename); + var output = deps.map(function (f) { return "require('" + f + "')\n" }) + '\n\n' + 'module.exports = ' + JSON.stringify(tokens); @@ -193,7 +180,7 @@ module.exports = function (browserify, options) { }); }; - browserify.transform(Cmify); + browserify.transform(Cmify, transformOpts); // ---- @@ -205,10 +192,6 @@ module.exports = function (browserify, options) { bundle.emit('css stream', compiledCssStream); bundle.on('end', function () { - // under certain conditions (eg. with shared libraries) we can end up with - // multiple occurrences of the same rule, so we need to remove duplicates - dedupeSources(loader.sources) - // Combine the collected sources for a single bundle into a single CSS file var css = loader.finalSource; diff --git a/package.json b/package.json index d09d392..88d8634 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "main": "index.js", "dependencies": { "css-modules-loader-core": "^1.0.0", + "dependency-graph": "^0.4.1", "object-assign": "^3.0.0", "promise-polyfill": "^2.1.0", "string-hash": "^1.1.0", diff --git a/tests/cases/compose-node-module/expected.css b/tests/cases/compose-node-module/expected.css index ddd63b1..492da06 100644 --- a/tests/cases/compose-node-module/expected.css +++ b/tests/cases/compose-node-module/expected.css @@ -1,4 +1,4 @@ -._cool_styles_styles__foo { +._node_modules_cool_styles_styles__foo { color: #F00; } ._styles__foo { diff --git a/tests/index.js b/tests/index.js index f651e55..af446aa 100644 --- a/tests/index.js +++ b/tests/index.js @@ -8,6 +8,8 @@ var path = require('path'); var casesDir = path.join(__dirname, 'cases'); var cssOutFilename = 'out.css'; +var globalCases = ['compose-node-module', 'import-node-module']; + function runTestCase (dir) { tape('case: ' + dir, function (t) { var fakeFs = { @@ -30,6 +32,9 @@ function runTestCase (dir) { rootDir: path.join(casesDir, dir) , output: cssOutFilename , generateScopedName: cssModulesify.generateLongName + + // only certain cases will use a global transform + , global: globalCases.indexOf(dir) !== -1 }); b.bundle(function (err) { From 33f7e9d323bd26ef21fa4a24fba34cb05d281154 Mon Sep 17 00:00:00 2001 From: Josh Johnston Date: Tue, 1 Dec 2015 21:06:52 +1100 Subject: [PATCH 15/63] es5 compat --- file-system-loader.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/file-system-loader.js b/file-system-loader.js index 1345a9c..0a3d06a 100644 --- a/file-system-loader.js +++ b/file-system-loader.js @@ -115,14 +115,14 @@ var FileSystemLoader = (function () { get: function () { var traces = this.traces; var sources = this.sources; - var written = new Set(); + var written = {}; return Object.keys(traces).sort(traceKeySorter).map(function (key) { var filename = traces[key]; - if (written.has(filename)) { + if (written[filename] === true) { return null; } - written.add(filename); + written[filename] = true; return sources[filename]; }).join(''); From 7a9f650b00b4a0f4565bf2901812c896eea0c32c Mon Sep 17 00:00:00 2001 From: Josh Johnston Date: Thu, 3 Dec 2015 11:03:59 +1100 Subject: [PATCH 16/63] show an error if there's an undefined reference --- index.js | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/index.js b/index.js index 37c93f9..7505996 100644 --- a/index.js +++ b/index.js @@ -166,15 +166,33 @@ module.exports = function (browserify, options) { loader.fetch(relFilename, '/').then(function (tokens) { var deps = loader.deps.dependenciesOf(filename); - var output = deps.map(function (f) { - return "require('" + f + "')\n" - }) + '\n\n' + 'module.exports = ' + JSON.stringify(tokens); + var output = [ + deps.map(function (f) { + return "require('" + f + "')" + }).join('\n'), + 'module.exports = ' + JSON.stringify(tokens) + ].join('\n'); + + var isValid = true; + var isUndefined = /\bundefined\b/; + Object.keys(tokens).forEach(function (k) { + if (isUndefined.test(tokens[k])) { + isValid = false; + } + }); + + if (!isValid) { + var err = 'Composition in ' + filename + ' contains an undefined reference'; + console.error(err) + output += '\nconsole.error("' + err + '");'; + } assign(tokensByFile, loader.tokensByFile); self.push(output); return callback() - }, function (err) { + }).catch(function (err) { + self.push('console.error("' + err + '");'); browserify.emit('error', err); return callback() }); From 4184fb3806aae9f8dcdcac30ec9e4883d3c0618f Mon Sep 17 00:00:00 2001 From: Josh Johnston Date: Thu, 3 Dec 2015 11:15:20 +1100 Subject: [PATCH 17/63] 0.16.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 88d8634..2cb84e3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "css-modulesify", - "version": "0.15.0", + "version": "0.16.0", "description": "A browserify transform to load CSS Modules", "main": "index.js", "dependencies": { From 67c7d3fe130e4632605c084c3e137c79634eed4d Mon Sep 17 00:00:00 2001 From: Josh Johnston Date: Thu, 3 Dec 2015 11:24:54 +1100 Subject: [PATCH 18/63] fixed a bug where postcss plugins were being added to the file system loader _before_ they were actually ready --- index.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/index.js b/index.js index 7505996..35113b0 100644 --- a/index.js +++ b/index.js @@ -101,11 +101,6 @@ module.exports = function (browserify, options) { var jsonOutFilename = options.json || options.jsonOutput; var sourceKey = cssOutFilename; - var loader = loadersByFile[sourceKey]; - if (!loader) { - loader = loadersByFile[sourceKey] = new FileSystemLoader(rootDir, plugins); - } - // PostCSS plugins passed to FileSystemLoader var plugins = options.use || options.u; if (!plugins) { @@ -147,6 +142,12 @@ module.exports = function (browserify, options) { return plugin; }); + // get (or create) a loader for this entry file + var loader = loadersByFile[sourceKey]; + if (!loader) { + loader = loadersByFile[sourceKey] = new FileSystemLoader(rootDir, plugins); + } + // the compiled CSS stream needs to be avalible to the transform, // but re-created on each bundle call. var compiledCssStream; From a573dae8293c92fcfb999d0e89bc23ff14edb48e Mon Sep 17 00:00:00 2001 From: Josh Johnston Date: Fri, 4 Dec 2015 08:53:25 +1100 Subject: [PATCH 19/63] 0.16.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2cb84e3..5220d09 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "css-modulesify", - "version": "0.16.0", + "version": "0.16.1", "description": "A browserify transform to load CSS Modules", "main": "index.js", "dependencies": { From 2711ba42ee82a87ecb4a24c0085f2fbe5e88bb78 Mon Sep 17 00:00:00 2001 From: Ian VanSchooten Date: Wed, 6 Jan 2016 20:57:41 -0500 Subject: [PATCH 20/63] Add postcss-modules-values to default plugins --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 90f0c22..0008e23 100644 --- a/README.md +++ b/README.md @@ -88,6 +88,7 @@ The following PostCSS plugins are enabled by default: * [postcss-modules-local-by-default] * [postcss-modules-extract-imports] * [postcss-modules-scope] + * [postcss-modules-values] (i.e. the [CSS Modules] specification). @@ -108,6 +109,7 @@ browserify -p [css-modulesify \ [postcss-modules-local-by-default]: https://github.com/css-modules/postcss-modules-local-by-default [postcss-modules-extract-imports]: https://github.com/css-modules/postcss-modules-extract-imports [postcss-modules-scope]: https://github.com/css-modules/postcss-modules-scope +[postcss-modules-values]: https://github.com/css-modules/postcss-modules-values ## Building for production From afbad84ed6967ca1717ea8dbe21e0face9146822 Mon Sep 17 00:00:00 2001 From: tjallingt Date: Sat, 16 Jan 2016 14:30:14 +0100 Subject: [PATCH 21/63] Cleaning up code --- .gitignore | 1 + index.js | 26 ++++++++++++-------------- 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/.gitignore b/.gitignore index 8f5c351..ce25fd0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ node_modules/* !node_modules/cool-styles +npm-debug.log \ No newline at end of file diff --git a/index.js b/index.js index 35113b0..e727ce5 100644 --- a/index.js +++ b/index.js @@ -1,5 +1,5 @@ // Some css-modules-loader-code dependencies use Promise so we'll provide it for older node versions -if (!global.Promise) { global.Promise = require('promise-polyfill') } +if (!global.Promise) { global.Promise = require('promise-polyfill'); } var fs = require('fs'); var path = require('path'); @@ -145,7 +145,7 @@ module.exports = function (browserify, options) { // get (or create) a loader for this entry file var loader = loadersByFile[sourceKey]; if (!loader) { - loader = loadersByFile[sourceKey] = new FileSystemLoader(rootDir, plugins); + loader = loadersByFile[sourceKey] = new FileSystemLoader(rootDir, plugins); } // the compiled CSS stream needs to be avalible to the transform, @@ -162,17 +162,15 @@ module.exports = function (browserify, options) { // convert css to js before pushing // reset the `tokensByFile` cache - var relFilename = path.relative(rootDir, filename) + var relFilename = path.relative(rootDir, filename); tokensByFile[filename] = loader.tokensByFile[filename] = null; loader.fetch(relFilename, '/').then(function (tokens) { var deps = loader.deps.dependenciesOf(filename); - var output = [ - deps.map(function (f) { - return "require('" + f + "')" - }).join('\n'), - 'module.exports = ' + JSON.stringify(tokens) - ].join('\n'); + var output = deps.map(function (f) { + return 'require("' + f + '")'; + }); + output.push('module.exports = ' + JSON.stringify(tokens)); var isValid = true; var isUndefined = /\bundefined\b/; @@ -184,18 +182,18 @@ module.exports = function (browserify, options) { if (!isValid) { var err = 'Composition in ' + filename + ' contains an undefined reference'; - console.error(err) - output += '\nconsole.error("' + err + '");'; + console.error(err); + output.push('console.error("' + err + '");'); } assign(tokensByFile, loader.tokensByFile); - self.push(output); - return callback() + self.push(output.join('\n')); + return callback(); }).catch(function (err) { self.push('console.error("' + err + '");'); browserify.emit('error', err); - return callback() + return callback(); }); }; From 60ee38dacbdc452f4184e178eb65cdd1d66d872f Mon Sep 17 00:00:00 2001 From: tjallingt Date: Sat, 16 Jan 2016 14:53:33 +0100 Subject: [PATCH 22/63] Fixed async code accessing a variable after it has been changed. --- cmify.js | 1 + index.js | 19 +++++++++---------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/cmify.js b/cmify.js index cd34282..5247b86 100644 --- a/cmify.js +++ b/cmify.js @@ -13,6 +13,7 @@ function Cmify(filename, opts) { this.cssExt = /\.css$/; this._data = ""; this._filename = filename; + this._cssOutFilename = opts.cssOutFilename; } Cmify.prototype.isCssFile = function (filename) { diff --git a/index.js b/index.js index e727ce5..d1efa55 100644 --- a/index.js +++ b/index.js @@ -99,7 +99,7 @@ module.exports = function (browserify, options) { var cssOutFilename = options.output || options.o; var jsonOutFilename = options.json || options.jsonOutput; - var sourceKey = cssOutFilename; + transformOpts.cssOutFilename = cssOutFilename; // PostCSS plugins passed to FileSystemLoader var plugins = options.use || options.u; @@ -142,16 +142,11 @@ module.exports = function (browserify, options) { return plugin; }); - // get (or create) a loader for this entry file - var loader = loadersByFile[sourceKey]; - if (!loader) { - loader = loadersByFile[sourceKey] = new FileSystemLoader(rootDir, plugins); + // create a loader for this entry file + if (!loadersByFile[cssOutFilename]) { + loadersByFile[cssOutFilename] = new FileSystemLoader(rootDir, plugins); } - // the compiled CSS stream needs to be avalible to the transform, - // but re-created on each bundle call. - var compiledCssStream; - // TODO: clean this up so there's less scope crossing Cmify.prototype._flush = function (callback) { var self = this; @@ -160,6 +155,9 @@ module.exports = function (browserify, options) { // only handle .css files if (!this.isCssFile(filename)) { return callback(); } + // grab the correct loader + var loader = loadersByFile[this._cssOutFilename]; + // convert css to js before pushing // reset the `tokensByFile` cache var relFilename = path.relative(rootDir, filename); @@ -203,13 +201,14 @@ module.exports = function (browserify, options) { browserify.on('bundle', function (bundle) { // on each bundle, create a new stream b/c the old one might have ended - compiledCssStream = new ReadableStream(); + var compiledCssStream = new ReadableStream(); compiledCssStream._read = function () {}; bundle.emit('css stream', compiledCssStream); bundle.on('end', function () { // Combine the collected sources for a single bundle into a single CSS file + var loader = loadersByFile[cssOutFilename]; var css = loader.finalSource; // end the output stream From 95727b4a189055ca398815050a2adbb235556c99 Mon Sep 17 00:00:00 2001 From: Josh Johnston Date: Wed, 3 Feb 2016 13:33:35 +1100 Subject: [PATCH 23/63] 0.17.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5220d09..373755b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "css-modulesify", - "version": "0.16.1", + "version": "0.17.0", "description": "A browserify transform to load CSS Modules", "main": "index.js", "dependencies": { From cc947c35b71b10f8ef5dbbb963641098ab01af08 Mon Sep 17 00:00:00 2001 From: tjallingt Date: Wed, 3 Feb 2016 12:12:35 +0100 Subject: [PATCH 24/63] Added browserify._options.basedir as a fallback for rootDir --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index d1efa55..7dfd6a2 100644 --- a/index.js +++ b/index.js @@ -88,7 +88,7 @@ module.exports = function (browserify, options) { options = options || {}; // if no root directory is specified, assume the cwd - var rootDir = options.rootDir || options.d; + var rootDir = options.rootDir || browserify._options.basedir || options.d; if (rootDir) { rootDir = path.resolve(rootDir); } if (!rootDir) { rootDir = process.cwd(); } From 33be2bfef17983f8939b20c7ee2714874898a20e Mon Sep 17 00:00:00 2001 From: tjallingt Date: Wed, 3 Feb 2016 12:16:18 +0100 Subject: [PATCH 25/63] Updated README.md to reflect rootDir changes --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0008e23..4b5eabe 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ bundle.on('css stream', function (css) { ### Options: -- `rootDir`: absolute path to your project's root directory. This is optional but providing it will result in better generated classnames. +- `rootDir`: absolute path to your project's root directory. This is optional but providing it will result in better generated classnames. css-modulesify will try to use the browserify `basedir` if `rootDir` is not specified, if both are not specified it will use the location from which the command was executed. - `output`: path to write the generated css. If not provided, you'll need to listen to the `'css stream'` event on the bundle to get the output. - `jsonOutput`: optional path to write a json manifest of classnames. - `use`: optional array of postcss plugins (by default we use the css-modules core plugins). From 30cc430803f067f8ef77178017109f968def3079 Mon Sep 17 00:00:00 2001 From: Roy Fu Date: Fri, 5 Feb 2016 14:51:02 -0800 Subject: [PATCH 26/63] Fix bug where browserify didn't emit error --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index d1efa55..bd3ebfb 100644 --- a/index.js +++ b/index.js @@ -190,7 +190,7 @@ module.exports = function (browserify, options) { return callback(); }).catch(function (err) { self.push('console.error("' + err + '");'); - browserify.emit('error', err); + self.emit('error', err); return callback(); }); }; From bd37b382456d0408d46575aa964677a7ef6036e5 Mon Sep 17 00:00:00 2001 From: Josh Johnston Date: Mon, 8 Feb 2016 10:02:59 +1100 Subject: [PATCH 27/63] 0.17.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 373755b..60cfaf2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "css-modulesify", - "version": "0.17.0", + "version": "0.17.1", "description": "A browserify transform to load CSS Modules", "main": "index.js", "dependencies": { From 42665df9ee575150f3127649eaf697916e8f9a67 Mon Sep 17 00:00:00 2001 From: tjallingt Date: Tue, 9 Feb 2016 13:13:54 +0100 Subject: [PATCH 28/63] Corrected ordering on rootDir options --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 7dfd6a2..58689d4 100644 --- a/index.js +++ b/index.js @@ -88,7 +88,7 @@ module.exports = function (browserify, options) { options = options || {}; // if no root directory is specified, assume the cwd - var rootDir = options.rootDir || browserify._options.basedir || options.d; + var rootDir = options.rootDir || options.d || browserify._options.basedir; if (rootDir) { rootDir = path.resolve(rootDir); } if (!rootDir) { rootDir = process.cwd(); } From 17f59c53b5d0e5393d0a7b7decbc81bec2943398 Mon Sep 17 00:00:00 2001 From: Andrey Rublev Date: Fri, 19 Feb 2016 02:32:17 +0500 Subject: [PATCH 29/63] load plugins by name only if a string is used Add ability to load plugins like cssnext(options) (which returns processor object, not function) --- index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index bd3ebfb..0a8dc19 100644 --- a/index.js +++ b/index.js @@ -117,8 +117,8 @@ module.exports = function (browserify, options) { // 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') { + // assume not strings are already required plugins + if (typeof name !== 'string') { return name; } From ce8331523d85e6e239105bcb5cceb8aff979a916 Mon Sep 17 00:00:00 2001 From: Josh Johnston Date: Mon, 22 Feb 2016 14:05:35 +1100 Subject: [PATCH 30/63] 0.18.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 60cfaf2..bc11dff 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "css-modulesify", - "version": "0.17.1", + "version": "0.18.0", "description": "A browserify transform to load CSS Modules", "main": "index.js", "dependencies": { From c5b66d7d5af9ffa66f224f83694561e86f7bded6 Mon Sep 17 00:00:00 2001 From: Josh Johnston Date: Mon, 22 Feb 2016 14:09:06 +1100 Subject: [PATCH 31/63] 0.19.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index bc11dff..a00c101 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "css-modulesify", - "version": "0.18.0", + "version": "0.19.0", "description": "A browserify transform to load CSS Modules", "main": "index.js", "dependencies": { From f428deda84b2eda745f7dab233c86f8e99ebb6b0 Mon Sep 17 00:00:00 2001 From: Andrey Rublev Date: Sat, 5 Mar 2016 09:06:33 +0600 Subject: [PATCH 32/63] more accurate way for resolving files in node_modules --- file-system-loader.js | 6 ++++-- package.json | 1 + tests/cases/compose-local-node-module/expected.css | 6 ++++++ tests/cases/compose-local-node-module/main.js | 2 ++ .../node_modules/cool-local-styles/styles.css | 3 +++ tests/cases/compose-local-node-module/styles.css | 4 ++++ tests/index.js | 2 +- 7 files changed, 21 insertions(+), 3 deletions(-) create mode 100644 tests/cases/compose-local-node-module/expected.css create mode 100644 tests/cases/compose-local-node-module/main.js create mode 100644 tests/cases/compose-local-node-module/node_modules/cool-local-styles/styles.css create mode 100644 tests/cases/compose-local-node-module/styles.css diff --git a/file-system-loader.js b/file-system-loader.js index 0a3d06a..7424329 100644 --- a/file-system-loader.js +++ b/file-system-loader.js @@ -1,6 +1,7 @@ 'use strict'; var DepGraph = require('dependency-graph').DepGraph; +var nodeResolve = require('resolve'); Object.defineProperty(exports, '__esModule', { value: true @@ -64,12 +65,13 @@ var FileSystemLoader = (function () { return new Promise(function (resolve, reject) { var relativeDir = _path2['default'].dirname(relativeTo), rootRelativePath = _path2['default'].resolve(relativeDir, newPath), - fileRelativePath = _path2['default'].resolve(_path2['default'].join(_this.root, relativeDir), newPath); + rootRelativeDir = _path2['default'].join(_this.root, relativeDir), + fileRelativePath = _path2['default'].resolve(rootRelativeDir, newPath); // if the path is not relative or absolute, try to resolve it in node_modules if (newPath[0] !== '.' && newPath[0] !== '/') { try { - fileRelativePath = require.resolve(newPath); + fileRelativePath = nodeResolve.sync(newPath, { basedir: rootRelativeDir }); } catch (e) {} } diff --git a/package.json b/package.json index a00c101..05b66fe 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "dependency-graph": "^0.4.1", "object-assign": "^3.0.0", "promise-polyfill": "^2.1.0", + "resolve": "^1.1.7", "string-hash": "^1.1.0", "through": "^2.3.7" }, diff --git a/tests/cases/compose-local-node-module/expected.css b/tests/cases/compose-local-node-module/expected.css new file mode 100644 index 0000000..fa249e5 --- /dev/null +++ b/tests/cases/compose-local-node-module/expected.css @@ -0,0 +1,6 @@ +._node_modules_cool_local_styles_styles__foo { + color: #F00; +} +._styles__foo { + background: black; +} diff --git a/tests/cases/compose-local-node-module/main.js b/tests/cases/compose-local-node-module/main.js new file mode 100644 index 0000000..922a6aa --- /dev/null +++ b/tests/cases/compose-local-node-module/main.js @@ -0,0 +1,2 @@ +var styles = require('./styles.css'); +module.exports = styles; diff --git a/tests/cases/compose-local-node-module/node_modules/cool-local-styles/styles.css b/tests/cases/compose-local-node-module/node_modules/cool-local-styles/styles.css new file mode 100644 index 0000000..092b8ad --- /dev/null +++ b/tests/cases/compose-local-node-module/node_modules/cool-local-styles/styles.css @@ -0,0 +1,3 @@ +.foo { + color: #F00; +} diff --git a/tests/cases/compose-local-node-module/styles.css b/tests/cases/compose-local-node-module/styles.css new file mode 100644 index 0000000..2f17694 --- /dev/null +++ b/tests/cases/compose-local-node-module/styles.css @@ -0,0 +1,4 @@ +.foo { + composes: foo from "cool-local-styles/styles.css"; + background: black; +} diff --git a/tests/index.js b/tests/index.js index af446aa..9ea1bb7 100644 --- a/tests/index.js +++ b/tests/index.js @@ -8,7 +8,7 @@ var path = require('path'); var casesDir = path.join(__dirname, 'cases'); var cssOutFilename = 'out.css'; -var globalCases = ['compose-node-module', 'import-node-module']; +var globalCases = ['compose-node-module', 'compose-local-node-module', 'import-node-module']; function runTestCase (dir) { tape('case: ' + dir, function (t) { From 110902984511362d6b6992ac351a072194c69778 Mon Sep 17 00:00:00 2001 From: Josh Johnston Date: Mon, 7 Mar 2016 17:18:48 +1100 Subject: [PATCH 33/63] 0.20.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 05b66fe..f226534 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "css-modulesify", - "version": "0.19.0", + "version": "0.20.0", "description": "A browserify transform to load CSS Modules", "main": "index.js", "dependencies": { From 5af2f7a492ce19638b89f695b11133d344e3b0f4 Mon Sep 17 00:00:00 2001 From: Andrey Rublev Date: Tue, 8 Mar 2016 04:20:57 +0600 Subject: [PATCH 34/63] Pass correct `from` arg for resolved files Pass correct 'from' parameter to postcss for files which resolved via `nodeResolve.sync` Incorrect source path leads to wrong names which postcss-plugin-scope generates for example --- file-system-loader.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/file-system-loader.js b/file-system-loader.js index 7424329..c23cf95 100644 --- a/file-system-loader.js +++ b/file-system-loader.js @@ -72,6 +72,8 @@ var FileSystemLoader = (function () { if (newPath[0] !== '.' && newPath[0] !== '/') { try { fileRelativePath = nodeResolve.sync(newPath, { basedir: rootRelativeDir }); + // in this case we need to actualize rootRelativePath too + rootRelativePath = _path2['default'].relative(_this.root, fileRelativePath); } catch (e) {} } From 3720a7089a59eee353542814b45328933d30f4d9 Mon Sep 17 00:00:00 2001 From: Josh Johnston Date: Tue, 8 Mar 2016 16:42:40 +1100 Subject: [PATCH 35/63] 0.21.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f226534..ce31d96 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "css-modulesify", - "version": "0.20.0", + "version": "0.21.0", "description": "A browserify transform to load CSS Modules", "main": "index.js", "dependencies": { From bdee7dbc67c351f1d49053e3bc1cd87e12834a87 Mon Sep 17 00:00:00 2001 From: Josh Johnston Date: Tue, 15 Mar 2016 17:02:28 +1100 Subject: [PATCH 36/63] 0.22.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ce31d96..f38affd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "css-modulesify", - "version": "0.21.0", + "version": "0.22.0", "description": "A browserify transform to load CSS Modules", "main": "index.js", "dependencies": { From 65d73d616b5f193b67344bfd32617bf10dbae37b Mon Sep 17 00:00:00 2001 From: Andrey Rublev Date: Thu, 31 Mar 2016 20:01:40 +0600 Subject: [PATCH 37/63] use dependency graph for resolving overall order instead of traces traces in current implementation can't be correct, because trace doesn't count dependencies in case if dependency already loaded --- file-system-loader.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/file-system-loader.js b/file-system-loader.js index c23cf95..0af23db 100644 --- a/file-system-loader.js +++ b/file-system-loader.js @@ -48,7 +48,6 @@ var FileSystemLoader = (function () { this.root = root; this.sources = {}; - this.traces = {}; this.importNr = 0; this.core = new _indexJs2['default'](plugins); this.tokensByFile = {}; @@ -107,7 +106,6 @@ var FileSystemLoader = (function () { var exportTokens = _ref.exportTokens; _this.sources[fileRelativePath] = injectableSource; - _this.traces[trace] = fileRelativePath; _this.tokensByFile[fileRelativePath] = exportTokens; resolve(exportTokens); }, reject); @@ -117,12 +115,10 @@ var FileSystemLoader = (function () { }, { key: 'finalSource', get: function () { - var traces = this.traces; var sources = this.sources; var written = {}; - return Object.keys(traces).sort(traceKeySorter).map(function (key) { - var filename = traces[key]; + return this.deps.overallOrder().map(function (filename) { if (written[filename] === true) { return null; } From 2caa8bb2c5598e93360be2beac1abf4b914e014e Mon Sep 17 00:00:00 2001 From: Josh Johnston Date: Wed, 6 Apr 2016 10:11:37 +1000 Subject: [PATCH 38/63] added info about the global flag to readme --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 18848db..5c14c61 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,7 @@ b.on('css stream', function (css) { - `use`: optional array of postcss plugins (by default we use the css-modules core plugins). NOTE: it's safer to use `after` - `after`: optional array of postcss plugins to run after the required css-modules core plugins are run. - `generateScopedName`: (API only) a function to override the default behaviour of creating locally scoped classnames. +- `global`: optional boolean. Set to `true` if you want `css-modulesify` to apply to `node_modules` as well as local files. You can read more about it in the [browserify docs](https://github.com/substack/node-browserify/#btransformtr-opts). ### Events - `b.on('css stream', callback)` The callback is called with a readable stream containing the compiled CSS. You can write this to a file. From a0b2f24db08908ba15cf2c4b1e80067a0cc04aab Mon Sep 17 00:00:00 2001 From: Josh Johnston Date: Thu, 14 Apr 2016 16:07:10 +1000 Subject: [PATCH 39/63] 0.23.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f38affd..08e3839 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "css-modulesify", - "version": "0.22.0", + "version": "0.23.0", "description": "A browserify transform to load CSS Modules", "main": "index.js", "dependencies": { From 91b9c9d523f032a833989af880a23cba88def6db Mon Sep 17 00:00:00 2001 From: Daniel Moore Date: Wed, 27 Apr 2016 11:09:46 -0400 Subject: [PATCH 40/63] Move final CSS packing to the pack stage of the browserify pipeline --- index.js | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/index.js b/index.js index 1fb773c..1b6108a 100644 --- a/index.js +++ b/index.js @@ -9,6 +9,7 @@ var FileSystemLoader = require('./file-system-loader'); var assign = require('object-assign'); var stringHash = require('string-hash'); var ReadableStream = require('stream').Readable; +var through = require('through'); /* Custom `generateScopedName` function for `postcss-modules-scope`. @@ -206,8 +207,9 @@ module.exports = function (browserify, options) { browserify.emit('css stream', compiledCssStream); - bundle.on('end', function () { + browserify.pipeline.get('pack').push(through(null, function () { // Combine the collected sources for a single bundle into a single CSS file + var self = this; var loader = loadersByFile[cssOutFilename]; var css = loader.finalSource; @@ -215,28 +217,38 @@ module.exports = function (browserify, options) { compiledCssStream.push(css); compiledCssStream.push(null); + const writes = []; + // write the css file if (cssOutFilename) { - fs.writeFile(cssOutFilename, css, function (err) { - if (err) { - browserify.emit('error', err); - } - }); + writes.push(writeFile(cssOutFilename, css)); } // write the classname manifest if (jsonOutFilename) { - fs.writeFile(jsonOutFilename, JSON.stringify(normalizeManifestPaths(tokensByFile, rootDir)), function (err) { - if (err) { - browserify.emit('error', err); - } - }); + writes.push(writeFile(jsonOutFilename, JSON.stringify(normalizeManifestPaths(tokensByFile, rootDir)))); } - }); + + Promise.all(writes) + .then(function () { self.queue(null); }) + .catch(function (err) { self.emit('error', err); }) + })); }); + return browserify; }; +function writeFile(filename, content) { + return new Promise(function (resolve, reject) { + fs.writeFile(filename, content, function (err) { + if (err) + reject(err); + else + resolve(); + }); + }); +} + module.exports.generateShortName = generateShortName; module.exports.generateLongName = generateLongName; From 06268d7872f459cf932567380469f1be89cf634a Mon Sep 17 00:00:00 2001 From: Daniel Moore Date: Wed, 27 Apr 2016 11:11:00 -0400 Subject: [PATCH 41/63] Use `module.parent.require` to pull in plugins --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 1b6108a..4d123f3 100644 --- a/index.js +++ b/index.js @@ -123,7 +123,7 @@ module.exports = function (browserify, options) { return name; } - var plugin = require(require.resolve(name)); + var plugin = module.parent.require(name); // custom scoped name generation if (name === 'postcss-modules-scope') { From 1332b88b3e01b623ff81a0216b7dd527f54a7193 Mon Sep 17 00:00:00 2001 From: Saiichi Hashimoto Date: Thu, 5 May 2016 18:16:08 -0700 Subject: [PATCH 42/63] Run postcss plugins before the default --- README.md | 3 ++- index.js | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5c14c61..0212500 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,7 @@ b.on('css stream', function (css) { - `output`: path to write the generated css. If not provided, you'll need to listen to the `'css stream'` event on the bundle to get the output. - `jsonOutput`: optional path to write a json manifest of classnames. - `use`: optional array of postcss plugins (by default we use the css-modules core plugins). NOTE: it's safer to use `after` +- `before`: optional array of postcss plugins to run before the required css-modules core plugins are run. - `after`: optional array of postcss plugins to run after the required css-modules core plugins are run. - `generateScopedName`: (API only) a function to override the default behaviour of creating locally scoped classnames. - `global`: optional boolean. Set to `true` if you want `css-modulesify` to apply to `node_modules` as well as local files. You can read more about it in the [browserify docs](https://github.com/substack/node-browserify/#btransformtr-opts). @@ -96,7 +97,7 @@ The following PostCSS plugins are enabled by default: You can override the default PostCSS Plugins (and add your own) by passing `--use|-u` to `css-modulesify`. -Or if you just want to add some extra plugins to run after the default, add them to the `postcssAfter` array option (API only at this time). +Or if you just want to add some extra plugins to run after the default, add them to the `postcssAfter` array option (API only at this time). In the same way, add extra plugins to `postcssBefore` to run the before the defaults. In addition you may also wish to configure defined PostCSS plugins by passing `--plugin.option true`. diff --git a/index.js b/index.js index 1fb773c..98da524 100644 --- a/index.js +++ b/index.js @@ -112,8 +112,9 @@ module.exports = function (browserify, options) { } } + var postcssBefore = options.postcssBefore || options.before || []; var postcssAfter = options.postcssAfter || options.after || []; - plugins = plugins.concat(postcssAfter); + plugins = postcssBefore.concat(plugins).concat(postcssAfter); // load plugins by name (if a string is used) plugins = plugins.map(function requirePlugin (name) { From b2d62ecd19efcbff14cfba1792b9ea214f6e40a9 Mon Sep 17 00:00:00 2001 From: Saiichi Hashimoto Date: Thu, 5 May 2016 18:38:21 -0700 Subject: [PATCH 43/63] Account for `before` not being an array sometimes --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 98da524..c36f8fe 100644 --- a/index.js +++ b/index.js @@ -114,7 +114,7 @@ module.exports = function (browserify, options) { var postcssBefore = options.postcssBefore || options.before || []; var postcssAfter = options.postcssAfter || options.after || []; - plugins = postcssBefore.concat(plugins).concat(postcssAfter); + plugins = (Array.isArray(postcssBefore) ? postcssBefore : [postcssBefore]).concat(plugins).concat(postcssAfter); // load plugins by name (if a string is used) plugins = plugins.map(function requirePlugin (name) { From 482a5b8ae1705edc71a12b7f6629d97ab4e200f3 Mon Sep 17 00:00:00 2001 From: Josh Johnston Date: Fri, 20 May 2016 15:40:32 +1000 Subject: [PATCH 44/63] 0.24.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 08e3839..56bec84 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "css-modulesify", - "version": "0.23.0", + "version": "0.24.0", "description": "A browserify transform to load CSS Modules", "main": "index.js", "dependencies": { From 1220cda50c94f20a33ccd53dc296159c76791dd7 Mon Sep 17 00:00:00 2001 From: Andrey Rublev Date: Tue, 7 Jun 2016 18:09:29 +0200 Subject: [PATCH 45/63] respect NODE_PATH env variable while resolving modules --- file-system-loader.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/file-system-loader.js b/file-system-loader.js index 0af23db..922167e 100644 --- a/file-system-loader.js +++ b/file-system-loader.js @@ -70,7 +70,10 @@ var FileSystemLoader = (function () { // if the path is not relative or absolute, try to resolve it in node_modules if (newPath[0] !== '.' && newPath[0] !== '/') { try { - fileRelativePath = nodeResolve.sync(newPath, { basedir: rootRelativeDir }); + fileRelativePath = nodeResolve.sync(newPath, { + basedir: rootRelativeDir, + paths: process.env.NODE_PATH.split(_path2['default'].delimiter) + }); // in this case we need to actualize rootRelativePath too rootRelativePath = _path2['default'].relative(_this.root, fileRelativePath); } catch (e) {} From fc65803aef34897fb88432562cb0dee0cd3f9343 Mon Sep 17 00:00:00 2001 From: Josh Johnston Date: Sat, 18 Jun 2016 13:13:16 +1000 Subject: [PATCH 46/63] 0.25.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 56bec84..23082be 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "css-modulesify", - "version": "0.24.0", + "version": "0.25.0", "description": "A browserify transform to load CSS Modules", "main": "index.js", "dependencies": { From c0196fdbd8b36123592efa757fa5041fb0eb8dad Mon Sep 17 00:00:00 2001 From: Josh Johnston Date: Mon, 20 Jun 2016 09:27:14 +1000 Subject: [PATCH 47/63] fixed stream issue --- index.js | 26 +++++++++++++++----------- package.json | 2 +- tests/stream-output.js | 2 +- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/index.js b/index.js index 99b5aa5..c3ce681 100644 --- a/index.js +++ b/index.js @@ -9,7 +9,7 @@ var FileSystemLoader = require('./file-system-loader'); var assign = require('object-assign'); var stringHash = require('string-hash'); var ReadableStream = require('stream').Readable; -var through = require('through'); +var through = require('through2'); /* Custom `generateScopedName` function for `postcss-modules-scope`. @@ -201,14 +201,17 @@ module.exports = function (browserify, options) { // ---- - browserify.on('bundle', function (bundle) { - // on each bundle, create a new stream b/c the old one might have ended - var compiledCssStream = new ReadableStream(); - compiledCssStream._read = function () {}; + function addHooks () { + browserify.pipeline.get('pack').push(through(function write (row, enc, next) { + next(null, row) + }, function end (cb) { - browserify.emit('css stream', compiledCssStream); + // on each bundle, create a new stream b/c the old one might have ended + var compiledCssStream = new ReadableStream(); + compiledCssStream._read = function () {}; + + browserify.emit('css stream', compiledCssStream); - browserify.pipeline.get('pack').push(through(null, function () { // Combine the collected sources for a single bundle into a single CSS file var self = this; var loader = loadersByFile[cssOutFilename]; @@ -229,13 +232,14 @@ module.exports = function (browserify, options) { if (jsonOutFilename) { writes.push(writeFile(jsonOutFilename, JSON.stringify(normalizeManifestPaths(tokensByFile, rootDir)))); } - Promise.all(writes) - .then(function () { self.queue(null); }) - .catch(function (err) { self.emit('error', err); }) + .then(function () { cb(); }) + .catch(function (err) { self.emit('error', err); cb() }) })); - }); + } + browserify.on('reset', addHooks); + addHooks(); return browserify; }; diff --git a/package.json b/package.json index 23082be..d1f556f 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "promise-polyfill": "^2.1.0", "resolve": "^1.1.7", "string-hash": "^1.1.0", - "through": "^2.3.7" + "through2": "^2.0.1" }, "devDependencies": { "browserify": "^11.0.1", diff --git a/tests/stream-output.js b/tests/stream-output.js index d33ff1a..48fc861 100644 --- a/tests/stream-output.js +++ b/tests/stream-output.js @@ -25,7 +25,7 @@ tape('stream output', function (t) { fs: fakeFs }); - t.plan(cssFilesTotal * 2 + 1); + t.plan(cssFilesTotal * 2 + 2); var cssFilesCount = 0; var b = browserify(path.join(simpleCaseDir, 'main.js')); From bc53327df955c87ee97a0a2e10e59c9baece1cf6 Mon Sep 17 00:00:00 2001 From: Josh Johnston Date: Mon, 20 Jun 2016 09:27:22 +1000 Subject: [PATCH 48/63] 0.25.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d1f556f..966b34b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "css-modulesify", - "version": "0.25.0", + "version": "0.25.1", "description": "A browserify transform to load CSS Modules", "main": "index.js", "dependencies": { From 6b23561d97438705f8c1d085dfffb495eaa3cda9 Mon Sep 17 00:00:00 2001 From: keik Date: Tue, 4 Oct 2016 16:43:45 +0900 Subject: [PATCH 49/63] Remove duplicated lint rules --- .eslintrc | 2 -- 1 file changed, 2 deletions(-) diff --git a/.eslintrc b/.eslintrc index 71199da..f169c3f 100755 --- a/.eslintrc +++ b/.eslintrc @@ -24,7 +24,6 @@ "no-negated-in-lhs": 2, "no-obj-calls": 2, "no-regex-spaces": 2, - "quote-props": [1, "consistent-as-needed"], "no-sparse-arrays": 2, "no-unreachable": 2, "use-isnan": 2, @@ -219,7 +218,6 @@ ] } ], - "array-bracket-spacing": [1, "never"], "wrap-regex": 0, "constructor-super": 2, "no-this-before-super": 2, From 67edb36fd85d1f9e24d56fdad8b0f0e37f6bc112 Mon Sep 17 00:00:00 2001 From: keik Date: Tue, 4 Oct 2016 16:43:58 +0900 Subject: [PATCH 50/63] Fix to pass lint --- .eslintrc | 2 +- index.js | 15 ++++++--------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/.eslintrc b/.eslintrc index f169c3f..ef37a41 100755 --- a/.eslintrc +++ b/.eslintrc @@ -91,7 +91,7 @@ 2, "all" ], - "no-use-before-define": 2, + "no-use-before-define": 0, "handle-callback-err": 2, "no-mixed-requires": 0, "no-new-require": 2, diff --git a/index.js b/index.js index c3ce681..93172bb 100644 --- a/index.js +++ b/index.js @@ -203,9 +203,8 @@ module.exports = function (browserify, options) { function addHooks () { browserify.pipeline.get('pack').push(through(function write (row, enc, next) { - next(null, row) + next(null, row); }, function end (cb) { - // on each bundle, create a new stream b/c the old one might have ended var compiledCssStream = new ReadableStream(); compiledCssStream._read = function () {}; @@ -221,7 +220,7 @@ module.exports = function (browserify, options) { compiledCssStream.push(css); compiledCssStream.push(null); - const writes = []; + var writes = []; // write the css file if (cssOutFilename) { @@ -234,7 +233,7 @@ module.exports = function (browserify, options) { } Promise.all(writes) .then(function () { cb(); }) - .catch(function (err) { self.emit('error', err); cb() }) + .catch(function (err) { self.emit('error', err); cb(); }); })); } @@ -244,13 +243,11 @@ module.exports = function (browserify, options) { return browserify; }; -function writeFile(filename, content) { +function writeFile (filename, content) { return new Promise(function (resolve, reject) { fs.writeFile(filename, content, function (err) { - if (err) - reject(err); - else - resolve(); + if (err) reject(err); + else resolve(); }); }); } From 5e2c8c8c6ec4737d2b2be3726a5f1e1c8370a187 Mon Sep 17 00:00:00 2001 From: Andrey Rublev Date: Thu, 22 Dec 2016 22:12:49 +0700 Subject: [PATCH 51/63] NODE_PATH env variable could be undefined --- file-system-loader.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/file-system-loader.js b/file-system-loader.js index 922167e..4e033fc 100644 --- a/file-system-loader.js +++ b/file-system-loader.js @@ -69,10 +69,14 @@ var FileSystemLoader = (function () { // if the path is not relative or absolute, try to resolve it in node_modules if (newPath[0] !== '.' && newPath[0] !== '/') { + var paths; + if (process.env.NODE_PATH) { + paths = process.env.NODE_PATH.split(_path2['default'].delimiter); + } try { fileRelativePath = nodeResolve.sync(newPath, { basedir: rootRelativeDir, - paths: process.env.NODE_PATH.split(_path2['default'].delimiter) + paths: paths }); // in this case we need to actualize rootRelativePath too rootRelativePath = _path2['default'].relative(_this.root, fileRelativePath); From d47abd7b9a136f53b3c9d875b2df4074f30c2a28 Mon Sep 17 00:00:00 2001 From: Josh Johnston Date: Fri, 23 Dec 2016 09:42:47 +1100 Subject: [PATCH 52/63] 0.26.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 966b34b..f3941f1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "css-modulesify", - "version": "0.25.1", + "version": "0.26.0", "description": "A browserify transform to load CSS Modules", "main": "index.js", "dependencies": { From 482ba08ae38419c0b47af7270dac2cdab2cc942c Mon Sep 17 00:00:00 2001 From: tai2 Date: Mon, 26 Dec 2016 13:04:03 +0900 Subject: [PATCH 53/63] Add css file extension option. --- README.md | 1 + cmify.js | 4 ++-- index.js | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0212500..c5c8954 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,7 @@ b.on('css stream', function (css) { - `after`: optional array of postcss plugins to run after the required css-modules core plugins are run. - `generateScopedName`: (API only) a function to override the default behaviour of creating locally scoped classnames. - `global`: optional boolean. Set to `true` if you want `css-modulesify` to apply to `node_modules` as well as local files. You can read more about it in the [browserify docs](https://github.com/substack/node-browserify/#btransformtr-opts). +- `filePattern`: optional regular expression string to specify css file names. (default: `\.css$`) ### Events - `b.on('css stream', callback)` The callback is called with a readable stream containing the compiled CSS. You can write this to a file. diff --git a/cmify.js b/cmify.js index 5247b86..1ad8294 100644 --- a/cmify.js +++ b/cmify.js @@ -10,14 +10,14 @@ function Cmify(filename, opts) { stream.Transform.call(this); - this.cssExt = /\.css$/; + this.cssFilePattern = new RegExp(opts.cssFilePattern || '\.css$'); this._data = ""; this._filename = filename; this._cssOutFilename = opts.cssOutFilename; } Cmify.prototype.isCssFile = function (filename) { - return this.cssExt.test(filename) + return this.cssFilePattern.test(filename) } Cmify.prototype._transform = function (buf, enc, callback) { diff --git a/index.js b/index.js index 93172bb..ddf48c8 100644 --- a/index.js +++ b/index.js @@ -101,6 +101,7 @@ module.exports = function (browserify, options) { var cssOutFilename = options.output || options.o; var jsonOutFilename = options.json || options.jsonOutput; transformOpts.cssOutFilename = cssOutFilename; + transformOpts.cssFilePattern = options.filePattern; // PostCSS plugins passed to FileSystemLoader var plugins = options.use || options.u; From 0a13f76edf0b6d80d04ed2f08a7e56371f4cdc52 Mon Sep 17 00:00:00 2001 From: Josh Johnston Date: Tue, 3 Jan 2017 14:51:17 +1100 Subject: [PATCH 54/63] update test cases - test cases can provide custom.js with override options - added custom-ext test case for the filePattern option --- .../cases/compose-local-node-module/custom.js | 3 +++ tests/cases/compose-node-module/custom.js | 3 +++ tests/cases/custom-ext/custom.js | 3 +++ tests/cases/custom-ext/expected.css | 3 +++ tests/cases/custom-ext/main.js | 3 +++ tests/cases/custom-ext/styles.cssmodule | 3 +++ tests/cases/import-node-module/custom.js | 3 +++ tests/index.js | 23 +++++++++++++------ 8 files changed, 37 insertions(+), 7 deletions(-) create mode 100644 tests/cases/compose-local-node-module/custom.js create mode 100644 tests/cases/compose-node-module/custom.js create mode 100644 tests/cases/custom-ext/custom.js create mode 100644 tests/cases/custom-ext/expected.css create mode 100644 tests/cases/custom-ext/main.js create mode 100644 tests/cases/custom-ext/styles.cssmodule create mode 100644 tests/cases/import-node-module/custom.js diff --git a/tests/cases/compose-local-node-module/custom.js b/tests/cases/compose-local-node-module/custom.js new file mode 100644 index 0000000..7e10afd --- /dev/null +++ b/tests/cases/compose-local-node-module/custom.js @@ -0,0 +1,3 @@ +module.exports = { + global: true +} diff --git a/tests/cases/compose-node-module/custom.js b/tests/cases/compose-node-module/custom.js new file mode 100644 index 0000000..7e10afd --- /dev/null +++ b/tests/cases/compose-node-module/custom.js @@ -0,0 +1,3 @@ +module.exports = { + global: true +} diff --git a/tests/cases/custom-ext/custom.js b/tests/cases/custom-ext/custom.js new file mode 100644 index 0000000..548c51d --- /dev/null +++ b/tests/cases/custom-ext/custom.js @@ -0,0 +1,3 @@ +module.exports = { + filePattern: /\.cssmodule$/ +} diff --git a/tests/cases/custom-ext/expected.css b/tests/cases/custom-ext/expected.css new file mode 100644 index 0000000..f0a88f9 --- /dev/null +++ b/tests/cases/custom-ext/expected.css @@ -0,0 +1,3 @@ +._styles__foo { + color: #F00; +} diff --git a/tests/cases/custom-ext/main.js b/tests/cases/custom-ext/main.js new file mode 100644 index 0000000..96280a2 --- /dev/null +++ b/tests/cases/custom-ext/main.js @@ -0,0 +1,3 @@ +// test using a custom `filePattern` option +var styles = require('./styles.cssmodule'); +module.exports = styles; diff --git a/tests/cases/custom-ext/styles.cssmodule b/tests/cases/custom-ext/styles.cssmodule new file mode 100644 index 0000000..092b8ad --- /dev/null +++ b/tests/cases/custom-ext/styles.cssmodule @@ -0,0 +1,3 @@ +.foo { + color: #F00; +} diff --git a/tests/cases/import-node-module/custom.js b/tests/cases/import-node-module/custom.js new file mode 100644 index 0000000..7e10afd --- /dev/null +++ b/tests/cases/import-node-module/custom.js @@ -0,0 +1,3 @@ +module.exports = { + global: true +} diff --git a/tests/index.js b/tests/index.js index 9ea1bb7..97a3615 100644 --- a/tests/index.js +++ b/tests/index.js @@ -8,10 +8,18 @@ var path = require('path'); var casesDir = path.join(__dirname, 'cases'); var cssOutFilename = 'out.css'; -var globalCases = ['compose-node-module', 'compose-local-node-module', 'import-node-module']; - function runTestCase (dir) { tape('case: ' + dir, function (t) { + // load (optional) custom setup for this testcase + var customPath = path.join(casesDir, dir, 'custom.js') + var customOpts + try { + fs.accessSync(customPath) + customOpts = require(customPath) + } catch (e) { + customOpts = {} + } + var fakeFs = { writeFile: function (filename, content, cb) { var expected = fs.readFileSync(path.join(casesDir, dir, 'expected.css'), 'utf8'); @@ -27,15 +35,13 @@ function runTestCase (dir) { }); var b = browserify(); + b.add(path.join(casesDir, dir, 'main.js')); - b.plugin(cssModulesify, { + b.plugin(cssModulesify, Object.assign({}, { rootDir: path.join(casesDir, dir) , output: cssOutFilename , generateScopedName: cssModulesify.generateLongName - - // only certain cases will use a global transform - , global: globalCases.indexOf(dir) !== -1 - }); + }, customOpts)); b.bundle(function (err) { if (err) { @@ -50,4 +56,7 @@ function runTestCase (dir) { // test cases are expected to have: // - main.js (entry point) // - expected.css (what to expect from css-modulesify output) +// optional: +// - custom.js (module that exports an object of custom settings) + fs.readdirSync(path.join(__dirname, 'cases')).forEach(runTestCase); From 5bd7dd160db8453f687ad17cd3647d5c570e18ec Mon Sep 17 00:00:00 2001 From: Josh Johnston Date: Tue, 3 Jan 2017 14:52:11 +1100 Subject: [PATCH 55/63] 0.27.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index f3941f1..5b0598b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "css-modulesify", - "version": "0.26.0", + "version": "0.27.0", "description": "A browserify transform to load CSS Modules", "main": "index.js", "dependencies": { From b1c0f71040efeef874c1ea8326d5dd69e74b0498 Mon Sep 17 00:00:00 2001 From: Josh Johnston Date: Tue, 3 Jan 2017 14:56:21 +1100 Subject: [PATCH 56/63] updated node versions --- .travis.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 9ccb608..1d0ad98 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,5 @@ sudo: false language: node_js node_js: - - "0.10" - - "0.12" - - "iojs" + - "4" + - "6" From 435bc8854265ebb94648d9d580a988e3f73f2c0a Mon Sep 17 00:00:00 2001 From: Josh Johnston Date: Tue, 3 Jan 2017 14:57:49 +1100 Subject: [PATCH 57/63] 0.27.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5b0598b..aecef30 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "css-modulesify", - "version": "0.27.0", + "version": "0.27.1", "description": "A browserify transform to load CSS Modules", "main": "index.js", "dependencies": { From f7d6e9472e20390ef16e3214b57bff415085a633 Mon Sep 17 00:00:00 2001 From: Eugene Ware Date: Mon, 27 Mar 2017 21:44:47 +1100 Subject: [PATCH 58/63] allow global option via command line --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index ddf48c8..a921694 100644 --- a/index.js +++ b/index.js @@ -94,7 +94,7 @@ module.exports = function (browserify, options) { if (!rootDir) { rootDir = process.cwd(); } var transformOpts = {}; - if (options.global) { + if (options.global || options.g) { transformOpts.global = true; } From 286cb861830a5fdeead060728ea6842b1a821373 Mon Sep 17 00:00:00 2001 From: Josh Johnston Date: Tue, 4 Apr 2017 10:50:13 +1000 Subject: [PATCH 59/63] 0.27.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index aecef30..5507343 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "css-modulesify", - "version": "0.27.1", + "version": "0.27.2", "description": "A browserify transform to load CSS Modules", "main": "index.js", "dependencies": { From cda3b4b8959fc063962fe9b9b52d8b2b4bdb7e74 Mon Sep 17 00:00:00 2001 From: Michael Ledin Date: Wed, 17 May 2017 15:34:03 +0300 Subject: [PATCH 60/63] eslint fixes --- cmify.js | 18 +++++++++--------- tests/index.js | 10 +++++----- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/cmify.js b/cmify.js index 1ad8294..f477d77 100644 --- a/cmify.js +++ b/cmify.js @@ -3,7 +3,7 @@ var path = require("path"); var util = require("util"); util.inherits(Cmify, stream.Transform); -function Cmify(filename, opts) { +function Cmify (filename, opts) { if (!(this instanceof Cmify)) { return new Cmify(filename, opts); } @@ -11,24 +11,24 @@ function Cmify(filename, opts) { stream.Transform.call(this); this.cssFilePattern = new RegExp(opts.cssFilePattern || '\.css$'); - this._data = ""; + this._data = ''; this._filename = filename; this._cssOutFilename = opts.cssOutFilename; } Cmify.prototype.isCssFile = function (filename) { - return this.cssFilePattern.test(filename) -} + return this.cssFilePattern.test(filename); +}; Cmify.prototype._transform = function (buf, enc, callback) { // only handle .css files if (!this.isCssFile(this._filename)) { - this.push(buf) - return callback() + this.push(buf); + return callback(); } - this._data += buf - callback() + this._data += buf; + callback(); }; -module.exports = Cmify +module.exports = Cmify; diff --git a/tests/index.js b/tests/index.js index 97a3615..0270550 100644 --- a/tests/index.js +++ b/tests/index.js @@ -11,13 +11,13 @@ var cssOutFilename = 'out.css'; function runTestCase (dir) { tape('case: ' + dir, function (t) { // load (optional) custom setup for this testcase - var customPath = path.join(casesDir, dir, 'custom.js') - var customOpts + var customPath = path.join(casesDir, dir, 'custom.js'); + var customOpts; try { - fs.accessSync(customPath) - customOpts = require(customPath) + fs.accessSync(customPath); + customOpts = require(customPath); } catch (e) { - customOpts = {} + customOpts = {}; } var fakeFs = { From 1e817d1d0c20d276c1196dd1ad1faaa5fc957365 Mon Sep 17 00:00:00 2001 From: Michael Ledin Date: Wed, 17 May 2017 18:40:11 +0300 Subject: [PATCH 61/63] Move Cmify.prototype_flush to cmify module. Move plugin loading to separate function. Use cache option instead of global cache. --- README.md | 1 + cmify.js | 59 +++++++++++++++++++++-- index.js | 125 ++++++++++++++++++------------------------------- tests/cache.js | 2 + 4 files changed, 105 insertions(+), 82 deletions(-) diff --git a/README.md b/README.md index c5c8954..f806a7a 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,7 @@ b.on('css stream', function (css) { - `generateScopedName`: (API only) a function to override the default behaviour of creating locally scoped classnames. - `global`: optional boolean. Set to `true` if you want `css-modulesify` to apply to `node_modules` as well as local files. You can read more about it in the [browserify docs](https://github.com/substack/node-browserify/#btransformtr-opts). - `filePattern`: optional regular expression string to specify css file names. (default: `\.css$`) +- `cache`: optional object to persist cache between runs. ### Events - `b.on('css stream', callback)` The callback is called with a readable stream containing the compiled CSS. You can write this to a file. diff --git a/cmify.js b/cmify.js index f477d77..a8dec5a 100644 --- a/cmify.js +++ b/cmify.js @@ -1,6 +1,7 @@ -var stream = require("stream"); -var path = require("path"); -var util = require("util"); +var stream = require('stream'); +var util = require('util'); +var assign = require('object-assign'); +var path = require('path'); util.inherits(Cmify, stream.Transform); function Cmify (filename, opts) { @@ -14,6 +15,10 @@ function Cmify (filename, opts) { this._data = ''; this._filename = filename; this._cssOutFilename = opts.cssOutFilename; + this._loader = opts.loader; + this._tokensByFile = opts.tokensByFile; + this._rootDir = opts.rootDir; + opts.cssFiles.push(filename); } Cmify.prototype.isCssFile = function (filename) { @@ -31,4 +36,52 @@ Cmify.prototype._transform = function (buf, enc, callback) { callback(); }; +Cmify.prototype._flush = function (callback) { + var self = this; + var filename = this._filename; + + // only handle .css files + if (!this.isCssFile(filename)) { return callback(); } + + // grab the correct loader + var loader = this._loader; + var tokensByFile = this._tokensByFile; + + // convert css to js before pushing + // reset the `tokensByFile` state + var relFilename = path.relative(this._rootDir, filename); + tokensByFile[filename] = loader.tokensByFile[filename] = null; + + loader.fetch(relFilename, '/').then(function (tokens) { + var deps = loader.deps.dependenciesOf(filename); + var output = deps.map(function (f) { + return 'require("' + f + '")'; + }); + output.push('module.exports = ' + JSON.stringify(tokens)); + + var isValid = true; + var isUndefined = /\bundefined\b/; + Object.keys(tokens).forEach(function (k) { + if (isUndefined.test(tokens[k])) { + isValid = false; + } + }); + + if (!isValid) { + var err = 'Composition in ' + filename + ' contains an undefined reference'; + console.error(err); + output.push('console.error("' + err + '");'); + } + + assign(tokensByFile, loader.tokensByFile); + + self.push(output.join('\n')); + return callback(); + }).catch(function (err) { + self.push('console.error("' + err + '");'); + self.emit('error', err); + return callback(); + }); +}; + module.exports = Cmify; diff --git a/index.js b/index.js index a921694..d71525e 100644 --- a/index.js +++ b/index.js @@ -6,7 +6,6 @@ var path = require('path'); var Cmify = require('./cmify'); var Core = require('css-modules-loader-core'); var FileSystemLoader = require('./file-system-loader'); -var assign = require('object-assign'); var stringHash = require('string-hash'); var ReadableStream = require('stream').Readable; var through = require('through2'); @@ -75,35 +74,8 @@ function normalizeManifestPaths (tokensByFile, rootDir) { return output; } -// caches -// -// persist these for as long as the process is running. #32 - -// keep track of all tokens so we can avoid duplicates -var tokensByFile = {}; - -// we need a separate loader for each entry point -var loadersByFile = {}; - -module.exports = function (browserify, options) { - options = options || {}; - - // if no root directory is specified, assume the cwd - var rootDir = options.rootDir || options.d || browserify._options.basedir; - if (rootDir) { rootDir = path.resolve(rootDir); } - if (!rootDir) { rootDir = process.cwd(); } - - var transformOpts = {}; - if (options.global || options.g) { - transformOpts.global = true; - } - - var cssOutFilename = options.output || options.o; - var jsonOutFilename = options.json || options.jsonOutput; - transformOpts.cssOutFilename = cssOutFilename; - transformOpts.cssFilePattern = options.filePattern; - - // PostCSS plugins passed to FileSystemLoader +// PostCSS plugins passed to FileSystemLoader +function getPlugins (options) { var plugins = options.use || options.u; if (!plugins) { plugins = getDefaultPlugins(options); @@ -119,7 +91,7 @@ module.exports = function (browserify, options) { plugins = (Array.isArray(postcssBefore) ? postcssBefore : [postcssBefore]).concat(plugins).concat(postcssAfter); // load plugins by name (if a string is used) - plugins = plugins.map(function requirePlugin (name) { + return plugins.map(function requirePlugin (name) { // assume not strings are already required plugins if (typeof name !== 'string') { return name; @@ -144,58 +116,54 @@ module.exports = function (browserify, options) { return plugin; }); +} - // create a loader for this entry file - if (!loadersByFile[cssOutFilename]) { - loadersByFile[cssOutFilename] = new FileSystemLoader(rootDir, plugins); +module.exports = function (browserify, options) { + options = options || {}; + + // if no root directory is specified, assume the cwd + var rootDir = options.rootDir || options.d || browserify._options.basedir; + if (rootDir) { + rootDir = path.resolve(rootDir); + } + if (!rootDir) { + rootDir = process.cwd(); } - // TODO: clean this up so there's less scope crossing - Cmify.prototype._flush = function (callback) { - var self = this; - var filename = this._filename; - - // only handle .css files - if (!this.isCssFile(filename)) { return callback(); } - - // grab the correct loader - var loader = loadersByFile[this._cssOutFilename]; - - // convert css to js before pushing - // reset the `tokensByFile` cache - var relFilename = path.relative(rootDir, filename); - tokensByFile[filename] = loader.tokensByFile[filename] = null; - - loader.fetch(relFilename, '/').then(function (tokens) { - var deps = loader.deps.dependenciesOf(filename); - var output = deps.map(function (f) { - return 'require("' + f + '")'; - }); - output.push('module.exports = ' + JSON.stringify(tokens)); - - var isValid = true; - var isUndefined = /\bundefined\b/; - Object.keys(tokens).forEach(function (k) { - if (isUndefined.test(tokens[k])) { - isValid = false; - } - }); - - if (!isValid) { - var err = 'Composition in ' + filename + ' contains an undefined reference'; - console.error(err); - output.push('console.error("' + err + '");'); - } + var cssOutFilename = options.output || options.o; + var jsonOutFilename = options.json || options.jsonOutput; + var loader; + // keep track of all tokens so we can avoid duplicates + var tokensByFile; + if (options.cache) { + if (options.cache.loaders) { + loader = options.cache.loaders[cssOutFilename]; + } else { + options.cache.loaders = {}; + } + if (options.cache.tokens) { + tokensByFile = options.cache.tokens; + } else { + options.cache.tokens = {}; + } + } - assign(tokensByFile, loader.tokensByFile); + loader = loader || new FileSystemLoader(rootDir, getPlugins(options)); + tokensByFile = tokensByFile || {}; - self.push(output.join('\n')); - return callback(); - }).catch(function (err) { - self.push('console.error("' + err + '");'); - self.emit('error', err); - return callback(); - }); + if (options.cache) { + options.cache.loaders[cssOutFilename] = loader; + options.cache.tokens = tokensByFile; + } + + var transformOpts = { + cssFilePattern: options.filePattern + , cssFiles: [] + , cssOutFilename: cssOutFilename + , global: options.global || options.g + , loader: loader + , rootDir: rootDir + , tokensByFile: tokensByFile }; browserify.transform(Cmify, transformOpts); @@ -214,7 +182,6 @@ module.exports = function (browserify, options) { // Combine the collected sources for a single bundle into a single CSS file var self = this; - var loader = loadersByFile[cssOutFilename]; var css = loader.finalSource; // end the output stream diff --git a/tests/cache.js b/tests/cache.js index 45c3c59..3f59240 100644 --- a/tests/cache.js +++ b/tests/cache.js @@ -25,6 +25,7 @@ tape('multiple builds', function (t) { fs: fakeFs }); + var cssModulesifyCache = {}; var getBundler = rebundler(function (cache, packageCache) { return browserify(path.join(simpleCaseDir, 'main.js'), { cache: cache @@ -34,6 +35,7 @@ tape('multiple builds', function (t) { .plugin(cssModulesify, { rootDir: path.join(simpleCaseDir) , output: cssOutFilename + , cache: cssModulesifyCache }); }); From 4c82d208e9e6670a972cdf89ba14ea78af76c385 Mon Sep 17 00:00:00 2001 From: Josh Johnston Date: Thu, 18 May 2017 12:28:41 +1000 Subject: [PATCH 62/63] 0.28.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5507343..07db223 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "css-modulesify", - "version": "0.27.2", + "version": "0.28.0", "description": "A browserify transform to load CSS Modules", "main": "index.js", "dependencies": { From bf1fb9a60ba5c5cc38ba31dbcd18144ca0413bee Mon Sep 17 00:00:00 2001 From: Josh Johnston Date: Tue, 4 Jul 2017 15:52:52 +1000 Subject: [PATCH 63/63] upgraded css-modules-loader-core --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 07db223..bba0ee2 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "A browserify transform to load CSS Modules", "main": "index.js", "dependencies": { - "css-modules-loader-core": "^1.0.0", + "css-modules-loader-core": "^1.1.0", "dependency-graph": "^0.4.1", "object-assign": "^3.0.0", "promise-polyfill": "^2.1.0",