From 2feeda933c504f0df4f8f27ca16e75034c0ca5b6 Mon Sep 17 00:00:00 2001 From: Josh Johnston Date: Sat, 8 Aug 2015 16:57:07 +1000 Subject: [PATCH 01/94] new option and updated readme --- README.md | 6 ++++-- index.js | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index de4617d..647b6c8 100644 --- a/README.md +++ b/README.md @@ -61,9 +61,11 @@ The following PostCSS plugins are enabled by default: (i.e. the [CSS Modules] specification). -You can supply your own additional PostCSS Plugins by passing `--use|-u` to `css-modulesify`. +You can override the default PostCSS Plugins (and add your own) by passing `--use|-u` to `css-modulesify`. -In addion you may also wish to configure defined PostCSS plugins by passing `--plugin.option true`. +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 addition you may also wish to configure defined PostCSS plugins by passing `--plugin.option true`. An example of this would be: diff --git a/index.js b/index.js index 5132d43..cb7ce1c 100644 --- a/index.js +++ b/index.js @@ -64,6 +64,8 @@ module.exports = function (browserify, options) { }); } + plugins = plugins.concat(options.postcssAfter || []); + // keep track of css files visited var filenames = []; From e3d2251f0490f991353bd3a34d7d4392135b06db Mon Sep 17 00:00:00 2001 From: zhengwenbing Date: Fri, 21 Aug 2015 16:12:52 +0800 Subject: [PATCH 02/94] there is no need to wrap bundle --- index.js | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/index.js b/index.js index 5132d43..a86402e 100644 --- a/index.js +++ b/index.js @@ -102,7 +102,7 @@ module.exports = function (browserify, options) { self.queue(output); self.queue(null); }, function (err) { - console.error('loader err', err); + browserify.emit('error', err); }); }); } @@ -111,27 +111,24 @@ module.exports = function (browserify, options) { global: true }); - // wrap the `bundle` function - var bundle = browserify.bundle; - browserify.bundle = function (cb) { - // reset the `tokensByFile` cache - tokensByFile = {}; + browserify.on('bundle', function(bundle) { + bundle.on('end', function() { + // reset the `tokensByFile` cache + tokensByFile = {}; - // call the original - var stream = bundle.call(browserify, function () { // Combine the collected sources into a single CSS file var css = Object.keys(sourceByFile).map(function(file) { return sourceByFile[file]; }).join('\n'); var args = arguments; - fs.writeFile(cssOutFilename, css, function () { - if (typeof cb === 'function') cb.apply(null, args); + fs.writeFile(cssOutFilename, css, function (err) { + if (err) { + browserify.emit('error', err); + } }); }); - - return stream; - }; + }); return browserify; }; From 3cc6dc626f5ed9bba7d28859fe7ed6787dc41fc7 Mon Sep 17 00:00:00 2001 From: Josh Johnston Date: Mon, 24 Aug 2015 12:27:51 +1000 Subject: [PATCH 03/94] added --after cli option (CLI counterpart to options.postcssAfter) --- index.js | 44 +++++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/index.js b/index.js index cb7ce1c..5b15892 100644 --- a/index.js +++ b/index.js @@ -37,34 +37,36 @@ module.exports = function (browserify, options) { if (typeof plugins === 'string') { plugins = [ plugins ]; } + } - plugins = plugins.map(function requirePlugin (name) { - // assume functions are already required plugins - if (typeof name === 'function') { - return name; - } + var postcssAfter = options.postcssAfter || options.after || []; + plugins = plugins.concat(postcssAfter); - var plugin = require(require.resolve(name)); + // 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; + } - // custom scoped name generation - if (name === 'postcss-modules-scope') { - options[name] = options[name] || {}; - if (!options[name].generateScopedName) { - options[name].generateScopedName = createScopedNameFunc(plugin); - } - } + var plugin = require(require.resolve(name)); - if (name in options) { - plugin = plugin(options[name]); - } else { - plugin = plugin.postcss || plugin(); + // custom scoped name generation + if (name === 'postcss-modules-scope') { + options[name] = options[name] || {}; + if (!options[name].generateScopedName) { + options[name].generateScopedName = createScopedNameFunc(plugin); } + } - return plugin; - }); - } + if (name in options) { + plugin = plugin(options[name]); + } else { + plugin = plugin.postcss || plugin(); + } - plugins = plugins.concat(options.postcssAfter || []); + return plugin; + }); // keep track of css files visited var filenames = []; From 2f052e4326e1b6f12e857174ba2c5af69153b9ad Mon Sep 17 00:00:00 2001 From: Josh Johnston Date: Mon, 24 Aug 2015 16:33:54 +1000 Subject: [PATCH 04/94] generate a manifest json of export tokens updated readme make the manifest a bit nicer by normalizing filenames to the root directory updated readme make the manifest a bit nicer by normalizing filenames to the root directory --- README.md | 1 + index.js | 34 +++++++++++++++++++++++++++++++++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 647b6c8..ae3da42 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,7 @@ b.bundle(); - `rootDir`: absolute path to your project's root directory. This is optional but providing it will result in better generated classnames. - `output`: path to write the generated css +- `json`: optional path to write a json manifest of classnames - `use`: optional array of postcss plugins (by default we use the css-modules core plugins) ## PostCSS Plugins diff --git a/index.js b/index.js index e344b09..f803ffe 100644 --- a/index.js +++ b/index.js @@ -18,17 +18,40 @@ function createScopedNameFunc (plugin) { } }; +/* + + Normalize the manifest paths so that they are always relative + to the project root directory. + +*/ +function normalizeManifestPaths (tokensByFile, rootDir) { + var output = {}; + var rootDirLength = rootDir.length + 1; + + Object.keys(tokensByFile).forEach(function (filename) { + var normalizedFilename = filename.substr(rootDirLength); + output[normalizedFilename] = tokensByFile[filename]; + }); + + return output; +} + var cssExt = /\.css$/; module.exports = function (browserify, options) { options = options || {}; - var rootDir = options.rootDir || options.d || '/'; + // 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(); } var cssOutFilename = options.output || options.o; if (!cssOutFilename) { throw new Error('css-modulesify needs the --output / -o option (path to output css file)'); } + var jsonOutFilename = options.json; + // PostCSS plugins passed to FileSystemLoader var plugins = options.use || options.u; if (!plugins) { @@ -131,6 +154,15 @@ module.exports = function (browserify, options) { browserify.emit('error', err); } }); + + // write the classname manifest + if (jsonOutFilename) { + fs.writeFile(jsonOutFilename, JSON.stringify(normalizeManifestPaths(tokensByFile, rootDir)), function (err) { + if (err) { + browserify.emit('error', err); + } + }); + } }); }); From ab2888601be4940924b8dec3951252394133ad17 Mon Sep 17 00:00:00 2001 From: Josh Johnston Date: Wed, 26 Aug 2015 21:44:46 +1000 Subject: [PATCH 05/94] added a note about options for server side usage --- README.md | 22 ++++++++++++++-------- index.js | 2 +- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index ae3da42..873ced3 100644 --- a/README.md +++ b/README.md @@ -48,9 +48,18 @@ b.bundle(); ### Options: - `rootDir`: absolute path to your project's root directory. This is optional but providing it will result in better generated classnames. -- `output`: path to write the generated css -- `json`: optional path to write a json manifest of classnames -- `use`: optional array of postcss plugins (by default we use the css-modules core plugins) +- `output`: path to write the generated css. +- `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). + +## Using CSS Modules on the backend + +If you want to use CSS Modules in server-generated templates there are a couple of options: + +- Option A (nodejs only): register the [require-hook](https://github.com/css-modules/css-modules-require-hook) so that `var styles = require('./foo.css')` operates the same way as on the frontend. Make sure that the `rootDir` option matches to guarantee that the classnames are the same. + +- Option B: configure the `jsonOutput` option with a file path and `css-modulesify` will generate a JSON manifest of classnames. + ## PostCSS Plugins @@ -71,11 +80,8 @@ In addition you may also wish to configure defined PostCSS plugins by passing `- An example of this would be: ``` -browserify -p [css-modulesify -u postcss-modules-local-by-default \ - -u postcss-modules-extract-imports \ - -u postcss-modules-scope \ - -u postcss-color-rebeccapurple \ - -u autoprefixer --autoprefixer.browsers '> 5%' \ +browserify -p [css-modulesify \ + --after autoprefixer --autoprefixer.browsers '> 5%' \ -o dist/main.css] -o dist/index.js src/index.js ``` diff --git a/index.js b/index.js index f803ffe..d92d936 100644 --- a/index.js +++ b/index.js @@ -50,7 +50,7 @@ module.exports = function (browserify, options) { throw new Error('css-modulesify needs the --output / -o option (path to output css file)'); } - var jsonOutFilename = options.json; + var jsonOutFilename = options.json || options.jsonOutput; // PostCSS plugins passed to FileSystemLoader var plugins = options.use || options.u; From 908ed38e4667d95dbe7fe7c3bf362592c03b7f6a Mon Sep 17 00:00:00 2001 From: Josh Johnston Date: Thu, 27 Aug 2015 11:24:23 +1000 Subject: [PATCH 06/94] 0.6.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index bd27301..163ea90 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "css-modulesify", - "version": "0.5.0", + "version": "0.6.0", "description": "A browserify transform to load CSS Modules", "main": "index.js", "dependencies": { From 54efc3abecc7ac026e88ecd3ef2bb2d6c0bd9f75 Mon Sep 17 00:00:00 2001 From: Josh Johnston Date: Thu, 27 Aug 2015 14:08:58 +1000 Subject: [PATCH 07/94] fixed order so token cache isn't reset until after manifest is written --- index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/index.js b/index.js index d92d936..9b6195d 100644 --- a/index.js +++ b/index.js @@ -140,9 +140,6 @@ module.exports = function (browserify, options) { browserify.on('bundle', function(bundle) { bundle.on('end', function() { - // reset the `tokensByFile` cache - tokensByFile = {}; - // Combine the collected sources into a single CSS file var css = Object.keys(sourceByFile).map(function(file) { return sourceByFile[file]; @@ -163,6 +160,9 @@ module.exports = function (browserify, options) { } }); } + + // reset the `tokensByFile` cache + tokensByFile = {}; }); }); From 111386589b21d06b2f9d560e205ebb00e3836809 Mon Sep 17 00:00:00 2001 From: Josh Johnston Date: Thu, 27 Aug 2015 14:09:04 +1000 Subject: [PATCH 08/94] 0.6.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 163ea90..0e57907 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "css-modulesify", - "version": "0.6.0", + "version": "0.6.1", "description": "A browserify transform to load CSS Modules", "main": "index.js", "dependencies": { From 4760b4c028b6cdefe38942fa7f3a742f02b8cff9 Mon Sep 17 00:00:00 2001 From: Giles Van Gruisen Date: Fri, 4 Sep 2015 10:52:27 -0700 Subject: [PATCH 09/94] css-modulesify is a plugin not a transform --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 873ced3..e82ec7b 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![Build Status](https://travis-ci.org/css-modules/css-modulesify.svg?branch=master)](https://travis-ci.org/css-modules/css-modulesify) -A browserify transform to load [CSS Modules]. +A browserify plugin to load [CSS Modules]. [CSS Modules]: https://github.com/css-modules/css-modules From 29d637930983a52076d458c8ca7b2963e60484f8 Mon Sep 17 00:00:00 2001 From: Joey Baker Date: Fri, 11 Sep 2015 10:02:13 -0700 Subject: [PATCH 10/94] Install rebundler as a dev dep --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 0e57907..f5536ff 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "devDependencies": { "browserify": "^11.0.1", "proxyquire": "^1.6.0", + "rebundler": "^0.2.0", "tape": "^4.0.1" }, "scripts": { From 6ff4ef83a4efd6d4ec1fbf0cb05532b93e6b0841 Mon Sep 17 00:00:00 2001 From: Joey Baker Date: Fri, 11 Sep 2015 10:02:51 -0700 Subject: [PATCH 11/94] internal: add failing test for re-bundling #32 --- tests/cache.js | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 tests/cache.js diff --git a/tests/cache.js b/tests/cache.js new file mode 100644 index 0000000..45c3c59 --- /dev/null +++ b/tests/cache.js @@ -0,0 +1,49 @@ +var tape = require('tape'); + +var browserify = require('browserify'); +var proxyquire = require('proxyquire'); +var fs = require('fs'); +var path = require('path'); +var rebundler = require('rebundler'); + +var casesDir = path.join(__dirname, 'cases'); +var simpleCaseDir = path.join(casesDir, 'simple'); +var cssOutFilename = 'out.css'; + +tape('multiple builds', function (t) { + var fakeFs = { + writeFile: function (filename, content, cb) { + var expected = fs.readFileSync(path.join(simpleCaseDir, 'expected.css'), 'utf8'); + + t.equal(filename, cssOutFilename, 'correct output filename'); + t.equal(content, expected, 'output matches expected'); + cb(); + } + }; + + var cssModulesify = proxyquire('../', { + fs: fakeFs + }); + + var getBundler = rebundler(function (cache, packageCache) { + return browserify(path.join(simpleCaseDir, 'main.js'), { + cache: cache + , packageCache: packageCache + , fullPaths: true + }) + .plugin(cssModulesify, { + rootDir: path.join(simpleCaseDir) + , output: cssOutFilename + }); + }); + + getBundler().bundle(function (err) { + t.error(err, 'initial bundle without a cache does not error'); + + getBundler().bundle(function (err2) { + t.error(err2, 'second pass bundle with a cache does not error'); + + t.end(); + }); + }); +}); From 194a6c6de3a8b7483f25f8b8ce565586cc55a2f4 Mon Sep 17 00:00:00 2001 From: Joey Baker Date: Fri, 11 Sep 2015 10:07:00 -0700 Subject: [PATCH 12/94] fix #32: ensure caches are persisted MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previously, the caches were re-initialized on every plugin initialization. This caused an incompatibility with other tools like rebundler or persistify. This change keeps the original intent of creating caches when the plugin is initialized, but ensures the process keeps the caches instead of the plugin instance itself. It’s not really possible to have a cache conflict since they’re keyed by file. --- index.js | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/index.js b/index.js index 9b6195d..0d21473 100644 --- a/index.js +++ b/index.js @@ -37,6 +37,22 @@ function normalizeManifestPaths (tokensByFile, rootDir) { } var cssExt = /\.css$/; + +// caches +// +// 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 = {}; + module.exports = function (browserify, options) { options = options || {}; @@ -91,17 +107,6 @@ module.exports = function (browserify, options) { return plugin; }); - // 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 = {}; - function transform (filename) { // only handle .css files if (!cssExt.test(filename)) { From fccd8300ecc346960431a9b4afc1bda47a906bb1 Mon Sep 17 00:00:00 2001 From: Joey Baker Date: Fri, 11 Sep 2015 10:32:56 -0700 Subject: [PATCH 13/94] Install eslint --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 0e57907..e4fa119 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ }, "devDependencies": { "browserify": "^11.0.1", + "eslint": "^1.4.0", "proxyquire": "^1.6.0", "tape": "^4.0.1" }, From e4a496f4f8ceb546ea81c21298ecae2c19cf7f03 Mon Sep 17 00:00:00 2001 From: Joey Baker Date: Fri, 11 Sep 2015 10:33:08 -0700 Subject: [PATCH 14/94] internal: add lint script --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index e4fa119..75f4836 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,8 @@ "tape": "^4.0.1" }, "scripts": { - "test": "tape tests/*.js" + "test": "tape tests/*.js", + "lint": "eslint index.js tests/" }, "author": "joshwnj", "license": "MIT", From 0b7ed818c401a8daa7d87199d39478bc35017f8d Mon Sep 17 00:00:00 2001 From: Joey Baker Date: Fri, 11 Sep 2015 10:33:34 -0700 Subject: [PATCH 15/94] internal: configure eslint --- .eslintignore | 1 + .eslintrc | 238 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 239 insertions(+) create mode 100644 .eslintignore create mode 100755 .eslintrc diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..c2658d7 --- /dev/null +++ b/.eslintignore @@ -0,0 +1 @@ +node_modules/ diff --git a/.eslintrc b/.eslintrc new file mode 100755 index 0000000..71199da --- /dev/null +++ b/.eslintrc @@ -0,0 +1,238 @@ +{ + "ecmaFeatures": { + }, + "env": { + "node": true + }, + "rules": { + "comma-dangle": [2, "never"], + "no-cond-assign": 2, + "no-constant-condition": 2, + "no-control-regex": 2, + "no-debugger": 2, + "no-dupe-keys": 2, + "no-empty": 2, + "no-empty-character-class": 2, + "no-ex-assign": 2, + "no-extra-boolean-cast": 2, + "no-extra-parens": 0, + "no-extra-semi": 2, + "no-func-assign": 2, + "no-inner-declarations": 2, + "no-invalid-regexp": 2, + "no-irregular-whitespace": 2, + "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, + "valid-typeof": 2, + "block-scoped-var": 0, + "consistent-return": 2, + "curly": [ + 1, + "multi-line" + ], + "default-case": 2, + "dot-notation": 2, + "eqeqeq": 2, + "guard-for-in": 2, + "no-alert": 1, + "no-caller": 2, + "no-div-regex": 2, + "no-eq-null": 2, + "no-eval": 2, + "no-extend-native": 2, + "no-extra-bind": 2, + "no-fallthrough": 2, + "no-floating-decimal": 2, + "no-implied-eval": 2, + "no-iterator": 2, + "no-labels": 2, + "no-lone-blocks": 2, + "no-loop-func": 2, + "no-multi-spaces": 1, + "no-multi-str": 2, + "no-native-reassign": 2, + "no-new": 2, + "no-new-func": 2, + "no-new-wrappers": 2, + "no-octal": 2, + "no-octal-escape": 2, + "no-proto": 2, + "no-redeclare": 2, + "no-return-assign": [2, "except-parens"], + "no-script-url": 2, + "no-self-compare": 2, + "no-sequences": 2, + "no-throw-literal": 2, + "no-unused-expressions": 0, + "no-with": 2, + "radix": 2, + "vars-on-top": 0, + "wrap-iife": 2, + "yoda": [ + 1, + "never" + ], + "strict": [ + 0, + "never" + ], + "no-delete-var": 2, + "no-label-var": 2, + "no-shadow": 2, + "no-shadow-restricted-names": 2, + "no-undef": 2, + "no-undef-init": 2, + "no-undefined": 2, + "no-unused-vars": [ + 2, + "all" + ], + "no-use-before-define": 2, + "handle-callback-err": 2, + "no-mixed-requires": 0, + "no-new-require": 2, + "no-path-concat": 2, + "indent": [ + 1, + 2, + {"SwitchCase": 1} + ], + "brace-style": [ + 1, + "stroustrup", + { + "allowSingleLine": true + } + ], + "comma-spacing": [ + 1, + { + "before": false, + "after": true + } + ], + "comma-style": [ + 2, + "first" + ], + "consistent-this": [ + 1, + "self" + ], + "eol-last": 2, + "func-names": 0, + "key-spacing": [ + 1, + { + "beforeColon": false, + "afterColon": true + } + ], + "max-nested-callbacks": [ + 2, + 3 + ], + "new-cap": 2, + "new-parens": 2, + "no-array-constructor": 0, + "no-inline-comments": 1, + "no-lonely-if": 0, + "no-mixed-spaces-and-tabs": 2, + "no-multiple-empty-lines": 2, + "no-nested-ternary": 0, + "no-new-object": 2, + "semi-spacing": [2, {"before": false, "after": true}], + "no-spaced-func": 1, + "no-ternary": 0, + "no-trailing-spaces": 2, + "no-underscore-dangle": 0, + "one-var": [ + 1, + { + "var": "never", + "let": "never", + "const": "never" + } + ], + "operator-assignment": [ + 2, + "always" + ], + "padded-blocks": [ + 1, + "never" + ], + "quote-props": [ + 1, + "as-needed" + ], + "quotes": [ + 2, + "single" + ], + "semi": [ + 2, + "always" + ], + "sort-vars": 0, + "space-after-keywords": [ + 1, + "always" + ], + "space-before-blocks": 0, + "space-before-function-paren": [ + 1, + "always" + ], + "object-curly-spacing": [ + 1, + "never" + ], + "array-bracket-spacing": [ + 1, + "never" + ], + "space-in-parens": [ + 1, + "never" + ], + "space-infix-ops": 2, + "space-return-throw-case": 2, + "space-unary-ops": [ + 1, + { + "words": true, + "nonwords": false + } + ], + "spaced-comment": [ + 1, + "always", + { + "exceptions": [ + "-" + ] + } + ], + "array-bracket-spacing": [1, "never"], + "wrap-regex": 0, + "constructor-super": 2, + "no-this-before-super": 2, + "require-yield": 2, + "prefer-spread": 1, + "no-useless-call": 1, + "no-invalid-this": 0, + "no-implicit-coercion": 0, + "no-const-assign": 2, + "no-class-assign": 2, + "init-declarations": 0, + "callback-return": [0, ["callback", "cb", "done", "next"]], + "arrow-spacing": [1, {"before": true, "after": true}], + "arrow-parens": 1 + } +} From 4f6404fa6add8b7aa736c4bf4b0c02e1bf6d17bc Mon Sep 17 00:00:00 2001 From: Joey Baker Date: Fri, 11 Sep 2015 10:33:44 -0700 Subject: [PATCH 16/94] internal: linter #cleanup --- index.js | 23 ++++++++++++----------- tests/cases/compose-node-module/main.js | 1 + tests/cases/import-node-module/main.js | 1 + tests/index.js | 16 ++++++++-------- 4 files changed, 22 insertions(+), 19 deletions(-) diff --git a/index.js b/index.js index 9b6195d..b4a8fb5 100644 --- a/index.js +++ b/index.js @@ -12,11 +12,11 @@ var stringHash = require('string-hash'); */ function createScopedNameFunc (plugin) { var orig = plugin.generateScopedName; - return function (name, path, css) { + return function (name, filename, css) { var hash = stringHash(css).toString(36).substr(0, 5); return orig.apply(plugin, arguments) + '___' + hash; - } -}; + }; +} /* @@ -56,9 +56,10 @@ module.exports = function (browserify, options) { var plugins = options.use || options.u; if (!plugins) { plugins = Core.defaultPlugins; - } else { + } + else { if (typeof plugins === 'string') { - plugins = [ plugins ]; + plugins = [plugins]; } } @@ -84,7 +85,8 @@ module.exports = function (browserify, options) { if (name in options) { plugin = plugin(options[name]); - } else { + } + else { plugin = plugin.postcss || plugin(); } @@ -119,7 +121,7 @@ module.exports = function (browserify, options) { loader.tokensByFile = tokensByFile; loader.fetch(path.relative(rootDir, filename), '/').then(function (tokens) { - var output = "module.exports = " + JSON.stringify(tokens); + var output = 'module.exports = ' + JSON.stringify(tokens); assign(tokensByFile, loader.tokensByFile); @@ -138,13 +140,12 @@ module.exports = function (browserify, options) { global: true }); - browserify.on('bundle', function(bundle) { - bundle.on('end', function() { + browserify.on('bundle', function (bundle) { + bundle.on('end', function () { // Combine the collected sources into a single CSS file - var css = Object.keys(sourceByFile).map(function(file) { + var css = Object.keys(sourceByFile).map(function (file) { return sourceByFile[file]; }).join('\n'); - var args = arguments; fs.writeFile(cssOutFilename, css, function (err) { if (err) { diff --git a/tests/cases/compose-node-module/main.js b/tests/cases/compose-node-module/main.js index 791235e..922a6aa 100644 --- a/tests/cases/compose-node-module/main.js +++ b/tests/cases/compose-node-module/main.js @@ -1 +1,2 @@ var styles = require('./styles.css'); +module.exports = styles; diff --git a/tests/cases/import-node-module/main.js b/tests/cases/import-node-module/main.js index 59f8d0d..729af44 100644 --- a/tests/cases/import-node-module/main.js +++ b/tests/cases/import-node-module/main.js @@ -1 +1,2 @@ var styles = require('cool-styles/styles.css'); +module.exports = styles; diff --git a/tests/index.js b/tests/index.js index 92e028d..f69e93d 100644 --- a/tests/index.js +++ b/tests/index.js @@ -8,11 +8,6 @@ var path = require('path'); var casesDir = path.join(__dirname, 'cases'); var cssOutFilename = 'out.css'; -// test cases are expected to have: -// - main.js (entry point) -// - expected.css (what to expect from css-modulesify output) -fs.readdirSync(path.join(__dirname, 'cases')).forEach(runTestCase); - function runTestCase (dir) { tape('case: ' + dir, function (t) { var fakeFs = { @@ -32,11 +27,11 @@ function runTestCase (dir) { var b = browserify(); b.add(path.join(casesDir, dir, 'main.js')); b.plugin(cssModulesify, { - rootDir: path.join(casesDir, dir), - output: cssOutFilename + rootDir: path.join(casesDir, dir) + , output: cssOutFilename }); - b.bundle(function (err, buf) { + b.bundle(function (err) { if (err) { console.error(err); return t.fail('Unexpected error'); @@ -46,3 +41,8 @@ function runTestCase (dir) { }); }); } + +// test cases are expected to have: +// - main.js (entry point) +// - expected.css (what to expect from css-modulesify output) +fs.readdirSync(path.join(__dirname, 'cases')).forEach(runTestCase); From 46b2c8334410fa1a009b13fafdd85151d77c3909 Mon Sep 17 00:00:00 2001 From: Josh Johnston Date: Sat, 12 Sep 2015 06:34:31 +1000 Subject: [PATCH 17/94] 0.7.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 345d784..ddeccaf 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "css-modulesify", - "version": "0.6.1", + "version": "0.7.0", "description": "A browserify transform to load CSS Modules", "main": "index.js", "dependencies": { From 4699457a25d2c4c099fa42b4ea5ee26f291e0bde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=BDiga?= Date: Thu, 17 Sep 2015 21:11:36 +0200 Subject: [PATCH 18/94] Emit error on stream. Otherwise watchify will silently fail and won't recover even when there are no errors. --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 0b3d64f..199971a 100644 --- a/index.js +++ b/index.js @@ -136,7 +136,7 @@ module.exports = function (browserify, options) { self.queue(output); self.queue(null); }, function (err) { - browserify.emit('error', err); + self.emit('error', err); }); }); } From 84652466baea2c5018a1bd26562f96dc1c399ccb Mon Sep 17 00:00:00 2001 From: Josh Johnston Date: Fri, 18 Sep 2015 09:04:05 +1000 Subject: [PATCH 19/94] 0.7.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ddeccaf..1ef9681 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "css-modulesify", - "version": "0.7.0", + "version": "0.7.1", "description": "A browserify transform to load CSS Modules", "main": "index.js", "dependencies": { From e09059cc62d073dde82d8ad0986143f18c8b5df3 Mon Sep 17 00:00:00 2001 From: Josh Johnston Date: Sat, 19 Sep 2015 16:42:56 +1000 Subject: [PATCH 20/94] update to allow passing a generateScopedName func directly, or fall back to default based on env --- README.md | 15 +++++++++++++++ index.js | 54 ++++++++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 61 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index e82ec7b..8be238c 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,7 @@ b.bundle(); - `output`: path to write the generated css. - `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). +- `generateScopedName`: (API only) a function to override the default behaviour of creating locally scoped classnames. ## Using CSS Modules on the backend @@ -89,6 +90,20 @@ browserify -p [css-modulesify \ [postcss-modules-extract-imports]: https://github.com/css-modules/postcss-modules-extract-imports [postcss-modules-scope]: https://github.com/css-modules/postcss-modules-scope +## Building for production + +If you set `NODE_ENV=production` then `css-modulesify` will generate shorter (though less useful) classnames. + +You can also manually switch to short names by setting the `generateScopedName` option. Eg: + +``` +browserify.plugin(cssModulesify, { + rootDir: __dirname, + output: './dist/main.css', + generateScopedName: cssModulesify.generateShortName +}) +``` + ## Example An example implementation can be found [here](https://github.com/css-modules/browserify-demo). diff --git a/index.js b/index.js index 199971a..9f464fa 100644 --- a/index.js +++ b/index.js @@ -6,16 +6,51 @@ 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. +*/ +function generateShortName (name, filename, css) { + // first occurrence of the name + // TOOD: better match with regex + var i = css.indexOf('.' + name); + var numLines = css.substr(0, i).split(/[\r\n]/).length; + + var hash = stringHash(css).toString(36).substr(0, 5); + return '_' + name + '_' + hash + '_' + numLines; +} + /* Custom `generateScopedName` function for `postcss-modules-scope`. Appends a hash of the css source. */ -function createScopedNameFunc (plugin) { - var orig = plugin.generateScopedName; - return function (name, filename, css) { - var hash = stringHash(css).toString(36).substr(0, 5); - return orig.apply(plugin, arguments) + '___' + hash; - }; +function generateLongName (name, filename, css) { + var sanitisedPath = filename.replace(/\.[^\.\/\\]+$/, '') + .replace(/[\W_]+/g, '_') + .replace(/^_|_$/g, ''); + + var hash = stringHash(css).toString(36).substr(0, 5); + return '_' + sanitisedPath + '__' + name + '___' + hash; +} + +/* + Get the default plugins and apply options. +*/ +function getDefaultPlugins (options) { + var scope = Core.scope; + var customNameFunc = options.generateScopedName; + var defaultNameFunc = process.env.NODE_ENV === 'production' ? + generateShortName : + generateLongName; + + scope.generateScopedName = customNameFunc || defaultNameFunc; + + return [ + Core.localByDefault + , Core.extractImports + , scope + ]; } /* @@ -71,7 +106,7 @@ module.exports = function (browserify, options) { // PostCSS plugins passed to FileSystemLoader var plugins = options.use || options.u; if (!plugins) { - plugins = Core.defaultPlugins; + plugins = getDefaultPlugins(options); } else { if (typeof plugins === 'string') { @@ -95,7 +130,7 @@ module.exports = function (browserify, options) { if (name === 'postcss-modules-scope') { options[name] = options[name] || {}; if (!options[name].generateScopedName) { - options[name].generateScopedName = createScopedNameFunc(plugin); + options[name].generateScopedName = generateLongName; } } @@ -174,3 +209,6 @@ module.exports = function (browserify, options) { return browserify; }; + +module.exports.generateShortName = generateShortName; +module.exports.generateLongName = generateLongName; From 354200334d24a921664c05448e2d55dd632bde6f Mon Sep 17 00:00:00 2001 From: Josh Johnston Date: Sat, 19 Sep 2015 16:47:44 +1000 Subject: [PATCH 21/94] by default, long-names are descriptive enough without a file hash --- index.js | 3 +-- tests/index.js | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/index.js b/index.js index 9f464fa..292d25e 100644 --- a/index.js +++ b/index.js @@ -30,8 +30,7 @@ function generateLongName (name, filename, css) { .replace(/[\W_]+/g, '_') .replace(/^_|_$/g, ''); - var hash = stringHash(css).toString(36).substr(0, 5); - return '_' + sanitisedPath + '__' + name + '___' + hash; + return '_' + sanitisedPath + '__' + name; } /* diff --git a/tests/index.js b/tests/index.js index f69e93d..556b76d 100644 --- a/tests/index.js +++ b/tests/index.js @@ -29,6 +29,7 @@ function runTestCase (dir) { b.plugin(cssModulesify, { rootDir: path.join(casesDir, dir) , output: cssOutFilename + , generateScopedName: cssModulesify.generateLongName }); b.bundle(function (err) { From 50f3a4fe092992c267354002b4636b1d3f96b42b Mon Sep 17 00:00:00 2001 From: Josh Johnston Date: Sat, 19 Sep 2015 16:48:18 +1000 Subject: [PATCH 22/94] pass linting --- index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.js b/index.js index 292d25e..3c673af 100644 --- a/index.js +++ b/index.js @@ -25,7 +25,7 @@ function generateShortName (name, filename, css) { Custom `generateScopedName` function for `postcss-modules-scope`. Appends a hash of the css source. */ -function generateLongName (name, filename, css) { +function generateLongName (name, filename) { var sanitisedPath = filename.replace(/\.[^\.\/\\]+$/, '') .replace(/[\W_]+/g, '_') .replace(/^_|_$/g, ''); From cde1b1945ae893f0739fd52127499260eedc8cff Mon Sep 17 00:00:00 2001 From: Josh Johnston Date: Mon, 21 Sep 2015 11:18:24 +1000 Subject: [PATCH 23/94] 0.8.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1ef9681..11daa31 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "css-modulesify", - "version": "0.7.1", + "version": "0.8.0", "description": "A browserify transform to load CSS Modules", "main": "index.js", "dependencies": { From aafa52198a4f0dafe8d594993947a3eb67e3c20e Mon Sep 17 00:00:00 2001 From: Josh Johnston Date: Fri, 25 Sep 2015 14:18:47 +1000 Subject: [PATCH 24/94] 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 25/94] 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 db193d51c9f10d8e89d6ebf2ba53961e42cfb5ed Mon Sep 17 00:00:00 2001 From: Joey Baker Date: Mon, 28 Sep 2015 15:08:43 -0700 Subject: [PATCH 26/94] fix a race-condition with tokensByFile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Because we’re setting `tokensByFile` before and after an async action, and many of these actions can happen at the same time, there’s a race condition where the incorrect `tokensByFile` can be set. This accomplishes the same desire for caching by just keeping the loader for every file since the loader already caches `tokensByFile`, and prevents the race condition. --- index.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/index.js b/index.js index 0b3d64f..d26a022 100644 --- a/index.js +++ b/index.js @@ -118,12 +118,9 @@ 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; - var loader = new FileSystemLoader(rootDir, plugins); - - // pre-populate the loader's tokensByFile - loader.tokensByFile = tokensByFile; loader.fetch(path.relative(rootDir, filename), '/').then(function (tokens) { var output = 'module.exports = ' + JSON.stringify(tokens); From 870cc33c4ba2859b9f7be5ba5ce2c1e4e58af6f6 Mon Sep 17 00:00:00 2001 From: Josh Johnston Date: Fri, 2 Oct 2015 10:32:11 +1000 Subject: [PATCH 27/94] 0.9.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 11daa31..6520cfc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "css-modulesify", - "version": "0.8.0", + "version": "0.9.0", "description": "A browserify transform to load CSS Modules", "main": "index.js", "dependencies": { From 7accbc376592713feec92faa700d51cc3c5db1f2 Mon Sep 17 00:00:00 2001 From: Joey Baker Date: Fri, 9 Oct 2015 15:48:56 -0700 Subject: [PATCH 28/94] internal: add test case for multiple JS files * ensures that the transform is called on each file * ensures the default class names contain folder names --- tests/cases/multiple-js-files/expected.css | 3 +++ tests/cases/multiple-js-files/main.js | 1 + 2 files changed, 4 insertions(+) create mode 100644 tests/cases/multiple-js-files/expected.css create mode 100644 tests/cases/multiple-js-files/main.js diff --git a/tests/cases/multiple-js-files/expected.css b/tests/cases/multiple-js-files/expected.css new file mode 100644 index 0000000..00c95c8 --- /dev/null +++ b/tests/cases/multiple-js-files/expected.css @@ -0,0 +1,3 @@ +._simple_styles__foo { + color: #F00; +} diff --git a/tests/cases/multiple-js-files/main.js b/tests/cases/multiple-js-files/main.js new file mode 100644 index 0000000..a095317 --- /dev/null +++ b/tests/cases/multiple-js-files/main.js @@ -0,0 +1 @@ +module.exports = require('../simple/main.js'); From 71e8bbf8c7c5fa5e44bb013fdd14c084a0426edd Mon Sep 17 00:00:00 2001 From: Joey Baker Date: Fri, 9 Oct 2015 15:49:22 -0700 Subject: [PATCH 29/94] internal: fix default tests to use t.error b/c tape internals are nice :) --- tests/index.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/index.js b/tests/index.js index 556b76d..f651e55 100644 --- a/tests/index.js +++ b/tests/index.js @@ -34,8 +34,7 @@ function runTestCase (dir) { b.bundle(function (err) { if (err) { - console.error(err); - return t.fail('Unexpected error'); + t.error(err, 'should not error'); } t.end(); From 9b8827a8ca6965edf34cff1844b02bbada0a1781 Mon Sep 17 00:00:00 2001 From: Joey Baker Date: Fri, 9 Oct 2015 15:54:22 -0700 Subject: [PATCH 30/94] fix #17: add a stream output option MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `bundle` now emits a `’css stream’` event. Users can listen to this and get a stream of the compiled CSS. I’m not a super big fan of this API, but I couldn’t think of a better way to give access to the stream. --- README.md | 21 ++++++++++++++- index.js | 44 +++++++++++++++++++++---------- tests/stream-output.js | 59 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 110 insertions(+), 14 deletions(-) create mode 100644 tests/stream-output.js diff --git a/README.md b/README.md index 8be238c..90f0c22 100644 --- a/README.md +++ b/README.md @@ -45,14 +45,33 @@ b.plugin(require('css-modulesify'), { b.bundle(); ``` +```js +// or, get the output as a stream +var b = require('browserify')(); +var fs = require('fs'); + +b.add('./main.js'); +b.plugin(require('css-modulesify'), { + rootDir: __dirname +}); + +var bundle = b.bundle() +bundle.on('css stream', function (css) { + css.pipe(fs.createWriteStream('mycss.css')); +}); +``` + ### Options: - `rootDir`: absolute path to your project's root directory. This is optional but providing it will result in better generated classnames. -- `output`: path to write the generated 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). - `generateScopedName`: (API only) a function to override the default behaviour of creating locally scoped classnames. +### Events +- `b.bundle().on('css stream', callback)` The callback is called with a readable stream containing the compiled CSS. You can write this to a file. + ## Using CSS Modules on the backend If you want to use CSS Modules in server-generated templates there are a couple of options: diff --git a/index.js b/index.js index 6b06cfa..4bbbbaa 100644 --- a/index.js +++ b/index.js @@ -5,6 +5,7 @@ var Core = require('css-modules-loader-core'); var FileSystemLoader = require('css-modules-loader-core/lib/file-system-loader'); var assign = require('object-assign'); var stringHash = require('string-hash'); +var ReadableStream = require('stream').Readable; /* @@ -96,10 +97,6 @@ module.exports = function (browserify, options) { if (!rootDir) { rootDir = process.cwd(); } var cssOutFilename = options.output || options.o; - if (!cssOutFilename) { - throw new Error('css-modulesify needs the --output / -o option (path to output css file)'); - } - var jsonOutFilename = options.json || options.jsonOutput; // PostCSS plugins passed to FileSystemLoader @@ -143,6 +140,10 @@ module.exports = function (browserify, options) { return plugin; }); + // the compiled CSS stream needs to be avalible to the transform, + // but re-created on each bundle call. + var compiledCssStream; + function transform (filename) { // only handle .css files if (!cssExt.test(filename)) { @@ -164,6 +165,8 @@ module.exports = function (browserify, options) { // store this file's source to be written out to disk later sourceByFile[filename] = loader.finalSource; + compiledCssStream.push(loader.finalSource); + self.queue(output); self.queue(null); }, function (err) { @@ -177,17 +180,32 @@ 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(); + compiledCssStream._read = function () {}; + + bundle.emit('css stream', compiledCssStream); + bundle.on('end', function () { // Combine the collected sources into a single CSS file - var css = Object.keys(sourceByFile).map(function (file) { - return sourceByFile[file]; - }).join('\n'); - - fs.writeFile(cssOutFilename, css, function (err) { - if (err) { - browserify.emit('error', err); - } - }); + var files = Object.keys(sourceByFile); + var css; + + // end the output stream + compiledCssStream.push(null); + + // write the css file + if (cssOutFilename) { + css = files.map(function (file) { + return sourceByFile[file]; + }).join('\n'); + + fs.writeFile(cssOutFilename, css, function (err) { + if (err) { + browserify.emit('error', err); + } + }); + } // write the classname manifest if (jsonOutFilename) { diff --git a/tests/stream-output.js b/tests/stream-output.js new file mode 100644 index 0000000..4268f7b --- /dev/null +++ b/tests/stream-output.js @@ -0,0 +1,59 @@ +var tape = require('tape'); + +var browserify = require('browserify'); +var proxyquire = require('proxyquire'); +var fs = require('fs'); +var path = require('path'); + +var casesDir = path.join(__dirname, 'cases'); +var simpleCaseDir = path.join(casesDir, 'simple'); +var cssFilesTotal = 1; +var cssOutFilename = 'out.css'; + +tape('stream output', function (t) { + var fakeFs = { + writeFile: function (filename, content, cb) { + var expected = fs.readFileSync(path.join(simpleCaseDir, 'expected.css'), 'utf8'); + + t.equal(filename, cssOutFilename, 'correct output filename'); + t.equal(content, expected, 'output matches expected'); + cb(); + } + }; + + var cssModulesify = proxyquire('../', { + fs: fakeFs + }); + + t.plan(cssFilesTotal * 2 + 1); + + var cssFilesCount = 0; + browserify(path.join(simpleCaseDir, 'main.js')) + .plugin(cssModulesify, { + rootDir: path.join(simpleCaseDir) + }) + .on('error', t.error) + .bundle(function noop () {}) + .on('css stream', function (stream) { + stream + .on('data', function onData (css) { + var cssString = css.toString(); + // just get the first class name, use that as an id + var cssId = cssString.split('\n')[0].split(' ')[0]; + + t.ok( + ++cssFilesCount <= cssFilesTotal + , 'emits data for ' + cssId + ); + + t.ok( + cssString.indexOf('._styles') === 0 + , 'emits compiled css for ' + cssId + ); + }) + .on('end', function onEnd () { + t.pass('ends the stream'); + }) + .on('error', t.error); + }); +}); From d342f7a5d0229b0a611d123a0f2bfe3baf672ed6 Mon Sep 17 00:00:00 2001 From: Josh Johnston Date: Mon, 12 Oct 2015 12:22:02 +1100 Subject: [PATCH 31/94] 0.10.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6520cfc..51a9bcd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "css-modulesify", - "version": "0.9.0", + "version": "0.10.0", "description": "A browserify transform to load CSS Modules", "main": "index.js", "dependencies": { From 7aeaaa082af63ec383a2af270f2bbfe58bea36f5 Mon Sep 17 00:00:00 2001 From: Josh Johnston Date: Tue, 3 Nov 2015 20:38:47 +0000 Subject: [PATCH 32/94] 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 33/94] 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 34/94] 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 35/94] 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 36/94] 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 37/94] 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 38/94] 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 39/94] 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 40/94] 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 41/94] 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 42/94] 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 43/94] 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 44/94] 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 45/94] 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 46/94] 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 47/94] 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 48/94] 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 49/94] 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 50/94] 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 51/94] 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 52/94] 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 53/94] 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 54/94] 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 55/94] 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 56/94] 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 57/94] 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 58/94] 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 59/94] 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 60/94] 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 61/94] 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 62/94] 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 63/94] 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 64/94] 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 160b2f6b0ec79d8f8d68079ab940ccfbd7db7c47 Mon Sep 17 00:00:00 2001 From: Joey Baker Date: Mon, 14 Mar 2016 20:59:46 -0700 Subject: [PATCH 65/94] Fix: correctly emit 'css stream' Previously, the event would only get emitted on the first run. This is a problem for watchify-like environments. Note: this is a breaking change. --- README.md | 4 +-- index.js | 2 +- tests/stream-output.js | 58 +++++++++++++++++++++++++----------------- 3 files changed, 37 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 90f0c22..cfcfcca 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ b.plugin(require('css-modulesify'), { }); var bundle = b.bundle() -bundle.on('css stream', function (css) { +b.on('css stream', function (css) { css.pipe(fs.createWriteStream('mycss.css')); }); ``` @@ -70,7 +70,7 @@ bundle.on('css stream', function (css) { - `generateScopedName`: (API only) a function to override the default behaviour of creating locally scoped classnames. ### Events -- `b.bundle().on('css stream', callback)` The callback is called with a readable stream containing the compiled CSS. You can write this to a file. +- `b.on('css stream', callback)` The callback is called with a readable stream containing the compiled CSS. You can write this to a file. ## Using CSS Modules on the backend diff --git a/index.js b/index.js index 4bbbbaa..e967841 100644 --- a/index.js +++ b/index.js @@ -184,7 +184,7 @@ module.exports = function (browserify, options) { compiledCssStream = new ReadableStream(); compiledCssStream._read = function () {}; - bundle.emit('css stream', compiledCssStream); + browserify.emit('css stream', compiledCssStream); bundle.on('end', function () { // Combine the collected sources into a single CSS file diff --git a/tests/stream-output.js b/tests/stream-output.js index 4268f7b..d33ff1a 100644 --- a/tests/stream-output.js +++ b/tests/stream-output.js @@ -28,32 +28,42 @@ tape('stream output', function (t) { t.plan(cssFilesTotal * 2 + 1); var cssFilesCount = 0; - browserify(path.join(simpleCaseDir, 'main.js')) + var b = browserify(path.join(simpleCaseDir, 'main.js')); + + b .plugin(cssModulesify, { rootDir: path.join(simpleCaseDir) }) .on('error', t.error) - .bundle(function noop () {}) - .on('css stream', function (stream) { - stream - .on('data', function onData (css) { - var cssString = css.toString(); - // just get the first class name, use that as an id - var cssId = cssString.split('\n')[0].split(' ')[0]; - - t.ok( - ++cssFilesCount <= cssFilesTotal - , 'emits data for ' + cssId - ); - - t.ok( - cssString.indexOf('._styles') === 0 - , 'emits compiled css for ' + cssId - ); - }) - .on('end', function onEnd () { - t.pass('ends the stream'); - }) - .on('error', t.error); - }); + .bundle(function noop () {}); + + b + .once('css stream', function (stream) { + stream + .on('data', function onData (css) { + var cssString = css.toString(); + // just get the first class name, use that as an id + var cssId = cssString.split('\n')[0].split(' ')[0]; + + t.ok( + ++cssFilesCount <= cssFilesTotal + , 'emits data for ' + cssId + ); + + t.ok( + cssString.indexOf('._styles') === 0 + , 'emits compiled css for ' + cssId + ); + }) + .on('end', function onEnd () { + t.pass('ends the stream'); + + b.bundle(function noop () {}); + + b.once('css stream', function (stream2) { + t.ok(stream2, 'registers a second event for a CSS stream'); + }); + }) + .on('error', t.error); + }); }); From 545c6fdfb34cf6f7e3a0abbd4e7de70d215dfaf8 Mon Sep 17 00:00:00 2001 From: Joey Baker Date: Mon, 14 Mar 2016 21:00:02 -0700 Subject: [PATCH 66/94] Docs: add missing `after` option to docs --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index cfcfcca..41e6f2e 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,8 @@ b.on('css stream', function (css) { - `rootDir`: absolute path to your project's root directory. This is optional but providing it will result in better generated classnames. - `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). +- `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. ### Events From bdee7dbc67c351f1d49053e3bc1cd87e12834a87 Mon Sep 17 00:00:00 2001 From: Josh Johnston Date: Tue, 15 Mar 2016 17:02:28 +1100 Subject: [PATCH 67/94] 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 68/94] 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 69/94] 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 70/94] 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 71/94] 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 72/94] 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 73/94] 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 74/94] 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 75/94] 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 76/94] 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 77/94] 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 78/94] 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 79/94] 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 80/94] 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 81/94] 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 82/94] 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 83/94] 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 84/94] 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 85/94] 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 86/94] 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 87/94] 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 88/94] 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 89/94] 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 90/94] 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 91/94] 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 92/94] 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 93/94] 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 94/94] 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",