1 |
|
2 | if (!global.Promise) { global.Promise = require('promise-polyfill'); }
|
3 |
|
4 | var fs = require('fs');
|
5 | var path = require('path');
|
6 | var Cmify = require('./cmify');
|
7 | var Core = require('css-modules-loader-core');
|
8 | var FileSystemLoader = require('./file-system-loader');
|
9 | var assign = require('object-assign');
|
10 | var stringHash = require('string-hash');
|
11 | var ReadableStream = require('stream').Readable;
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 | function generateShortName (name, filename, css) {
|
18 |
|
19 |
|
20 | var i = css.indexOf('.' + name);
|
21 | var numLines = css.substr(0, i).split(/[\r\n]/).length;
|
22 |
|
23 | var hash = stringHash(css).toString(36).substr(0, 5);
|
24 | return '_' + name + '_' + hash + '_' + numLines;
|
25 | }
|
26 |
|
27 |
|
28 |
|
29 |
|
30 |
|
31 | function generateLongName (name, filename) {
|
32 | var sanitisedPath = filename.replace(/\.[^\.\/\\]+$/, '')
|
33 | .replace(/[\W_]+/g, '_')
|
34 | .replace(/^_|_$/g, '');
|
35 |
|
36 | return '_' + sanitisedPath + '__' + name;
|
37 | }
|
38 |
|
39 |
|
40 |
|
41 |
|
42 | function getDefaultPlugins (options) {
|
43 | var scope = Core.scope;
|
44 | var customNameFunc = options.generateScopedName;
|
45 | var defaultNameFunc = process.env.NODE_ENV === 'production' ?
|
46 | generateShortName :
|
47 | generateLongName;
|
48 |
|
49 | scope.generateScopedName = customNameFunc || defaultNameFunc;
|
50 |
|
51 | return [
|
52 | Core.values
|
53 | , Core.localByDefault
|
54 | , Core.extractImports
|
55 | , scope
|
56 | ];
|
57 | }
|
58 |
|
59 |
|
60 |
|
61 |
|
62 |
|
63 |
|
64 |
|
65 | function normalizeManifestPaths (tokensByFile, rootDir) {
|
66 | var output = {};
|
67 | var rootDirLength = rootDir.length + 1;
|
68 |
|
69 | Object.keys(tokensByFile).forEach(function (filename) {
|
70 | var normalizedFilename = filename.substr(rootDirLength);
|
71 | output[normalizedFilename] = tokensByFile[filename];
|
72 | });
|
73 |
|
74 | return output;
|
75 | }
|
76 |
|
77 |
|
78 |
|
79 |
|
80 |
|
81 |
|
82 | var tokensByFile = {};
|
83 |
|
84 |
|
85 | var loadersByFile = {};
|
86 |
|
87 | module.exports = function (browserify, options) {
|
88 | options = options || {};
|
89 |
|
90 |
|
91 | var rootDir = options.rootDir || options.d;
|
92 | if (rootDir) { rootDir = path.resolve(rootDir); }
|
93 | if (!rootDir) { rootDir = process.cwd(); }
|
94 |
|
95 | var transformOpts = {};
|
96 | if (options.global) {
|
97 | transformOpts.global = true;
|
98 | }
|
99 |
|
100 | var cssOutFilename = options.output || options.o;
|
101 | var jsonOutFilename = options.json || options.jsonOutput;
|
102 | transformOpts.cssOutFilename = cssOutFilename;
|
103 |
|
104 |
|
105 | var plugins = options.use || options.u;
|
106 | if (!plugins) {
|
107 | plugins = getDefaultPlugins(options);
|
108 | }
|
109 | else {
|
110 | if (typeof plugins === 'string') {
|
111 | plugins = [plugins];
|
112 | }
|
113 | }
|
114 |
|
115 | var postcssAfter = options.postcssAfter || options.after || [];
|
116 | plugins = plugins.concat(postcssAfter);
|
117 |
|
118 |
|
119 | plugins = plugins.map(function requirePlugin (name) {
|
120 |
|
121 | if (typeof name === 'function') {
|
122 | return name;
|
123 | }
|
124 |
|
125 | var plugin = require(require.resolve(name));
|
126 |
|
127 |
|
128 | if (name === 'postcss-modules-scope') {
|
129 | options[name] = options[name] || {};
|
130 | if (!options[name].generateScopedName) {
|
131 | options[name].generateScopedName = generateLongName;
|
132 | }
|
133 | }
|
134 |
|
135 | if (name in options) {
|
136 | plugin = plugin(options[name]);
|
137 | }
|
138 | else {
|
139 | plugin = plugin.postcss || plugin();
|
140 | }
|
141 |
|
142 | return plugin;
|
143 | });
|
144 |
|
145 |
|
146 | if (!loadersByFile[cssOutFilename]) {
|
147 | loadersByFile[cssOutFilename] = new FileSystemLoader(rootDir, plugins);
|
148 | }
|
149 |
|
150 |
|
151 | Cmify.prototype._flush = function (callback) {
|
152 | var self = this;
|
153 | var filename = this._filename;
|
154 |
|
155 |
|
156 | if (!this.isCssFile(filename)) { return callback(); }
|
157 |
|
158 |
|
159 | var loader = loadersByFile[this._cssOutFilename];
|
160 |
|
161 |
|
162 |
|
163 | var relFilename = path.relative(rootDir, filename);
|
164 | tokensByFile[filename] = loader.tokensByFile[filename] = null;
|
165 |
|
166 | loader.fetch(relFilename, '/').then(function (tokens) {
|
167 | var deps = loader.deps.dependenciesOf(filename);
|
168 | var output = deps.map(function (f) {
|
169 | return 'require("' + f + '")';
|
170 | });
|
171 | output.push('module.exports = ' + JSON.stringify(tokens));
|
172 |
|
173 | var isValid = true;
|
174 | var isUndefined = /\bundefined\b/;
|
175 | Object.keys(tokens).forEach(function (k) {
|
176 | if (isUndefined.test(tokens[k])) {
|
177 | isValid = false;
|
178 | }
|
179 | });
|
180 |
|
181 | if (!isValid) {
|
182 | var err = 'Composition in ' + filename + ' contains an undefined reference';
|
183 | console.error(err);
|
184 | output.push('console.error("' + err + '");');
|
185 | }
|
186 |
|
187 | assign(tokensByFile, loader.tokensByFile);
|
188 |
|
189 | self.push(output.join('\n'));
|
190 | return callback();
|
191 | }).catch(function (err) {
|
192 | self.push('console.error("' + err + '");');
|
193 | self.emit('error', err);
|
194 | return callback();
|
195 | });
|
196 | };
|
197 |
|
198 | browserify.transform(Cmify, transformOpts);
|
199 |
|
200 |
|
201 |
|
202 | browserify.on('bundle', function (bundle) {
|
203 |
|
204 | var compiledCssStream = new ReadableStream();
|
205 | compiledCssStream._read = function () {};
|
206 |
|
207 | bundle.emit('css stream', compiledCssStream);
|
208 |
|
209 | bundle.on('end', function () {
|
210 |
|
211 | var loader = loadersByFile[cssOutFilename];
|
212 | var css = loader.finalSource;
|
213 |
|
214 |
|
215 | compiledCssStream.push(css);
|
216 | compiledCssStream.push(null);
|
217 |
|
218 |
|
219 | if (cssOutFilename) {
|
220 | fs.writeFile(cssOutFilename, css, function (err) {
|
221 | if (err) {
|
222 | browserify.emit('error', err);
|
223 | }
|
224 | });
|
225 | }
|
226 |
|
227 |
|
228 | if (jsonOutFilename) {
|
229 | fs.writeFile(jsonOutFilename, JSON.stringify(normalizeManifestPaths(tokensByFile, rootDir)), function (err) {
|
230 | if (err) {
|
231 | browserify.emit('error', err);
|
232 | }
|
233 | });
|
234 | }
|
235 | });
|
236 | });
|
237 |
|
238 | return browserify;
|
239 | };
|
240 |
|
241 | module.exports.generateShortName = generateShortName;
|
242 | module.exports.generateLongName = generateLongName;
|