Skip to content

Commit eb11672

Browse files
committed
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
1 parent 93a9163 commit eb11672

File tree

5 files changed

+153
-28
lines changed

5 files changed

+153
-28
lines changed

file-system-loader.js

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
'use strict';
2+
3+
var DepGraph = require('dependency-graph').DepGraph;
4+
5+
Object.defineProperty(exports, '__esModule', {
6+
value: true
7+
});
8+
9+
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; }; })();
10+
11+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
12+
13+
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
14+
15+
var _indexJs = require('css-modules-loader-core/lib/index.js');
16+
17+
var _indexJs2 = _interopRequireDefault(_indexJs);
18+
19+
var _fs = require('fs');
20+
21+
var _fs2 = _interopRequireDefault(_fs);
22+
23+
var _path = require('path');
24+
25+
var _path2 = _interopRequireDefault(_path);
26+
27+
// Sorts dependencies in the following way:
28+
// AAA comes before AA and A
29+
// AB comes after AA and before A
30+
// All Bs come after all As
31+
// This ensures that the files are always returned in the following order:
32+
// - In the order they were required, except
33+
// - After all their dependencies
34+
var traceKeySorter = function traceKeySorter(a, b) {
35+
if (a.length < b.length) {
36+
return a < b.substring(0, a.length) ? -1 : 1;
37+
} else if (a.length > b.length) {
38+
return a.substring(0, b.length) <= b ? -1 : 1;
39+
} else {
40+
return a < b ? -1 : 1;
41+
}
42+
};
43+
44+
var FileSystemLoader = (function () {
45+
function FileSystemLoader(root, plugins) {
46+
_classCallCheck(this, FileSystemLoader);
47+
48+
this.root = root;
49+
this.sources = {};
50+
this.traces = {};
51+
this.importNr = 0;
52+
this.core = new _indexJs2['default'](plugins);
53+
this.tokensByFile = {};
54+
this.deps = new DepGraph();
55+
}
56+
57+
_createClass(FileSystemLoader, [{
58+
key: 'fetch',
59+
value: function fetch(_newPath, relativeTo, _trace) {
60+
var _this = this;
61+
62+
var newPath = _newPath.replace(/^["']|["']$/g, ''),
63+
trace = _trace || String.fromCharCode(this.importNr++);
64+
return new Promise(function (resolve, reject) {
65+
var relativeDir = _path2['default'].dirname(relativeTo),
66+
rootRelativePath = _path2['default'].resolve(relativeDir, newPath),
67+
fileRelativePath = _path2['default'].resolve(_path2['default'].join(_this.root, relativeDir), newPath);
68+
69+
// if the path is not relative or absolute, try to resolve it in node_modules
70+
if (newPath[0] !== '.' && newPath[0] !== '/') {
71+
try {
72+
fileRelativePath = require.resolve(newPath);
73+
} catch (e) {}
74+
}
75+
76+
// first time? add a node
77+
if (_trace === undefined) {
78+
if (!_this.deps.hasNode(fileRelativePath)) {
79+
_this.deps.addNode(fileRelativePath);
80+
}
81+
}
82+
// otherwise add a dependency
83+
else {
84+
var parentFilePath = _path2['default'].join(_this.root, relativeTo);
85+
if (!_this.deps.hasNode(parentFilePath)) {
86+
console.error('NO NODE', parentFilePath, fileRelativePath)
87+
}
88+
if (!_this.deps.hasNode(fileRelativePath)) {
89+
_this.deps.addNode(fileRelativePath);
90+
}
91+
_this.deps.addDependency(parentFilePath, fileRelativePath);
92+
}
93+
94+
var tokens = _this.tokensByFile[fileRelativePath];
95+
if (tokens) {
96+
return resolve(tokens);
97+
}
98+
99+
_fs2['default'].readFile(fileRelativePath, 'utf-8', function (err, source) {
100+
if (err) reject(err);
101+
_this.core.load(source, rootRelativePath, trace, _this.fetch.bind(_this)).then(function (_ref) {
102+
var injectableSource = _ref.injectableSource;
103+
var exportTokens = _ref.exportTokens;
104+
105+
_this.sources[fileRelativePath] = injectableSource;
106+
_this.traces[trace] = fileRelativePath;
107+
_this.tokensByFile[fileRelativePath] = exportTokens;
108+
resolve(exportTokens);
109+
}, reject);
110+
});
111+
});
112+
}
113+
}, {
114+
key: 'finalSource',
115+
get: function () {
116+
var traces = this.traces;
117+
var sources = this.sources;
118+
var written = new Set();
119+
120+
return Object.keys(traces).sort(traceKeySorter).map(function (key) {
121+
var filename = traces[key];
122+
if (written.has(filename)) {
123+
return null;
124+
}
125+
written.add(filename);
126+
127+
return sources[filename];
128+
}).join('');
129+
}
130+
}]);
131+
132+
return FileSystemLoader;
133+
})();
134+
135+
exports['default'] = FileSystemLoader;
136+
module.exports = exports['default'];

index.js

Lines changed: 10 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ var fs = require('fs');
55
var path = require('path');
66
var Cmify = require('./cmify');
77
var Core = require('css-modules-loader-core');
8-
var FileSystemLoader = require('css-modules-loader-core/lib/file-system-loader');
8+
var FileSystemLoader = require('./file-system-loader');
99
var assign = require('object-assign');
1010
var stringHash = require('string-hash');
1111
var ReadableStream = require('stream').Readable;
@@ -16,7 +16,7 @@ var ReadableStream = require('stream').Readable;
1616
*/
1717
function generateShortName (name, filename, css) {
1818
// first occurrence of the name
19-
// TOOD: better match with regex
19+
// TODO: better match with regex
2020
var i = css.indexOf('.' + name);
2121
var numLines = css.substr(0, i).split(/[\r\n]/).length;
2222

@@ -74,19 +74,6 @@ function normalizeManifestPaths (tokensByFile, rootDir) {
7474
return output;
7575
}
7676

77-
function dedupeSources (sources) {
78-
var foundHashes = {}
79-
Object.keys(sources).forEach(function (key) {
80-
var hash = stringHash(sources[key]);
81-
if (foundHashes[hash]) {
82-
delete sources[key];
83-
}
84-
else {
85-
foundHashes[hash] = true;
86-
}
87-
})
88-
}
89-
9077
// caches
9178
//
9279
// persist these for as long as the process is running. #32
@@ -105,6 +92,11 @@ module.exports = function (browserify, options) {
10592
if (rootDir) { rootDir = path.resolve(rootDir); }
10693
if (!rootDir) { rootDir = process.cwd(); }
10794

95+
var transformOpts = {};
96+
if (options.global) {
97+
transformOpts.global = true;
98+
}
99+
108100
var cssOutFilename = options.output || options.o;
109101
var jsonOutFilename = options.json || options.jsonOutput;
110102
var sourceKey = cssOutFilename;
@@ -173,13 +165,8 @@ module.exports = function (browserify, options) {
173165
tokensByFile[filename] = loader.tokensByFile[filename] = null;
174166

175167
loader.fetch(relFilename, '/').then(function (tokens) {
176-
var newFiles = Object.keys(loader.tokensByFile)
177-
var oldFiles = Object.keys(tokensByFile)
178-
var diff = newFiles.filter(function (f) {
179-
return oldFiles.indexOf(f) === -1
180-
})
181-
182-
var output = diff.map(function (f) {
168+
var deps = loader.deps.dependenciesOf(filename);
169+
var output = deps.map(function (f) {
183170
return "require('" + f + "')\n"
184171
}) + '\n\n' + 'module.exports = ' + JSON.stringify(tokens);
185172

@@ -193,7 +180,7 @@ module.exports = function (browserify, options) {
193180
});
194181
};
195182

196-
browserify.transform(Cmify);
183+
browserify.transform(Cmify, transformOpts);
197184

198185
// ----
199186

@@ -205,10 +192,6 @@ module.exports = function (browserify, options) {
205192
bundle.emit('css stream', compiledCssStream);
206193

207194
bundle.on('end', function () {
208-
// under certain conditions (eg. with shared libraries) we can end up with
209-
// multiple occurrences of the same rule, so we need to remove duplicates
210-
dedupeSources(loader.sources)
211-
212195
// Combine the collected sources for a single bundle into a single CSS file
213196
var css = loader.finalSource;
214197

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"main": "index.js",
66
"dependencies": {
77
"css-modules-loader-core": "^1.0.0",
8+
"dependency-graph": "^0.4.1",
89
"object-assign": "^3.0.0",
910
"promise-polyfill": "^2.1.0",
1011
"string-hash": "^1.1.0",

tests/cases/compose-node-module/expected.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
._cool_styles_styles__foo {
1+
._node_modules_cool_styles_styles__foo {
22
color: #F00;
33
}
44
._styles__foo {

tests/index.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ var path = require('path');
88
var casesDir = path.join(__dirname, 'cases');
99
var cssOutFilename = 'out.css';
1010

11+
var globalCases = ['compose-node-module', 'import-node-module'];
12+
1113
function runTestCase (dir) {
1214
tape('case: ' + dir, function (t) {
1315
var fakeFs = {
@@ -30,6 +32,9 @@ function runTestCase (dir) {
3032
rootDir: path.join(casesDir, dir)
3133
, output: cssOutFilename
3234
, generateScopedName: cssModulesify.generateLongName
35+
36+
// only certain cases will use a global transform
37+
, global: globalCases.indexOf(dir) !== -1
3338
});
3439

3540
b.bundle(function (err) {

0 commit comments

Comments
 (0)