-
Notifications
You must be signed in to change notification settings - Fork 3
/
index.js
139 lines (120 loc) · 4.48 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
var assign = require('lodash.assign');
var processCss = requireFromLocalOrParent('css-loader/lib/processCss');
var loaderUtils = require('loader-utils');
var mkpath = require('mkpath');
var path = require('path');
var fs = require('fs');
var files = {};
var timer;
module.exports = function (source, map) {
if (this.cacheable) this.cacheable();
var resourcePath = this.resourcePath;
var query = loaderUtils.parseQuery(this.query);
var outputFile = query.outputFile;
var rootPath = query.rootPath || '/';
var minimalJson = !!query.minimalJson;
var writeDebounceMs = query.writeDebounceMs || 100;
var options = this.options.extractCssModuleClassnames || {};
var processOpts = {
mode: 'local',
loaderContext: {
options: {
context: this.options.context
},
// Note! This assumes that `extract-css-module-classnames-loader` is directly in front of
// the `css-loader`
loaderIndex: this.loaderIndex - 1,
resource: this.resource,
loaders: this.loaders,
request: this.request,
resourcePath: resourcePath
},
query: query
}
var aliases = getAliases(this.options);
var importPath = path.relative(rootPath, resourcePath);
files[importPath] = files[importPath] || {};
processCss(source, null, processOpts, function(err, result) {
if (err) throw err;
Object.keys(result.exports).forEach(function(key) {
var classes = result.exports[key].split(/\s+/);
files[importPath][key] = classes.map(function (className) {
if (isClassName(result, className)) {
return className;
}
var importItem = getImportItem(result, className);
var resolvedPath = resolvePath(importItem.url);
var composesPath = resolvedPath || path.resolve(path.dirname(resourcePath), importItem.url);
return [ path.relative(rootPath, composesPath), importItem.export ];
});
});
if (options.onOutput && typeof options.onOutput === 'function') {
options.onOutput(resourcePath, files[importPath], files);
} else {
if (!outputFile) {
throw new Error('Missing outputFile parameter in extract-css-module-classnames-loader');
}
clearTimeout(timer);
timer = setTimeout(function () {
mkpath(path.dirname(outputFile), function (err) {
if (err && err.code !== 'EEXIST') throw err;
var json;
if (minimalJson) {
json = JSON.stringify(files);
} else {
json = JSON.stringify(files, null, 2);
}
fs.writeFile(outputFile, json, function (err) {
if (err) throw err;
options.onAfterOutput && options.onAfterOutput(resourcePath, files[importPath], files);
});
});
}, writeDebounceMs);
}
});
function getAliases (options) {
// TODO: Investigate if there is a way to leverage
// the Webpack API to get all the aliases
var resolveAliases = options.resolve && options.resolve.alias;
var resolveLoaderAliases = options.resolveLoader && options.resolveLoader.alias;
return assign({}, resolveAliases || {}, resolveLoaderAliases || {});
}
function isClassName (result, className) {
result.importItemRegExpG.lastIndex = 0;
return !result.importItemRegExpG.test(className);
}
/**
* Return the unaliased path of an aliased import (if any)
*
* @param {String} importPath Import path (e.g. import 'path/to/foo.css')
* @return {String} The relative path of an aliased import (if any),
* otherwise empty string
*/
function resolvePath (importPath) {
// TODO: Investigate if there is a way to leverage
// the Webpack API to handle the alias resolution instead
return Object.keys(aliases).reduce(function (prev, alias) {
var regex = new RegExp('^' + alias, 'i');
if (!regex.test(importPath)) { return prev }
var unaliasedPath = importPath.replace(regex, aliases[alias]);
return path.resolve(rootPath, unaliasedPath);
}, '');
}
function getImportItem (result, className) {
var match = result.importItemRegExp.exec(className);
var idx = +match[1];
return result.importItems[idx];
}
return source;
};
// Needed to make require work with `npm link` since `css-loader`
// is a peerDependency
function requireFromLocalOrParent(id) {
var parent = module;
for (; parent; parent = parent.parent) {
try {
return parent.require(id);
} catch(ex) {}
}
throw new Error("Cannot find module '" + id + "'");
};