forked from elm-community/elm-webpack-loader
-
Notifications
You must be signed in to change notification settings - Fork 1
/
index.js
217 lines (173 loc) · 6.02 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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
'use strict';
var fs = require("fs")
var path = require('path')
var loaderUtils = require('loader-utils');
var elmCompiler = require('node-elm-compiler');
var yargs = require('yargs');
var runningInstances = 0;
var alreadyCompiledFiles = [];
var defaultOptions = {
cache: false,
forceWatch: false,
yes: true
};
var getFiles = function(options) {
var basepath = path.dirname(this.resourcePath);
var files = options && options.files;
if (files === undefined) return [this.resourcePath];
if (!Array.isArray(files)) {
throw new Error('files option must be an array');
}
if (files.length === 0) {
throw new Error("You specified the 'files' option but didn't list any files");
}
delete options.files;
return files;
};
var getOptions = function() {
var globalOptions = this.options
? this.options.elm || {}
: this.query.elm || {};
var loaderOptions = loaderUtils.getOptions(this) || {};
return Object.assign({
emitWarning: this.emitWarning
}, defaultOptions, globalOptions, loaderOptions);
};
var _addDependencies = function(dependency) {
this.addDependency(dependency);
};
var _addDirDependency = function(dirs){
dirs.forEach(this.addContextDependency.bind(this));
};
var isFlagSet = function(args, flag) {
return typeof args[flag] !== "undefined" && args[flag];
};
/* Figures out if webpack has been run in watch mode
This currently means either that the `watch` command was used
Or it was run via `webpack-dev-server`
*/
var isInWatchMode = function(){
// parse the argv given to run this webpack instance
var argv = yargs(process.argv)
.alias('w', 'watch')
.alias('stdin', 'watch-stdin')
.argv;
var hasWatchArg = isFlagSet(argv, 'watch');
var hasStdinArg = isFlagSet(argv, 'watch-stdin');
var hasWebpackDevServer = Array.prototype.filter.call(process.argv, function (arg) {
return arg.indexOf('webpack-dev-server') !== -1;
}).length > 0;
return hasWebpackDevServer || hasWatchArg || hasStdinArg;
};
/* Takes a working dir, tries to read elm-package.json, then grabs all the modules from in there
*/
var filesToWatch = function(cwd){
var readFile = fs.readFileSync(path.join(cwd, "elm-package.json"), 'utf8');
var elmPackage = JSON.parse(readFile);
var paths = elmPackage["source-directories"].map(function(dir){
return path.join(cwd, dir);
});
return paths;
};
var dependenciesFor = function(resourcePath, files) {
return findAllDependencies(files)
.then(function (dependencies) {
return unique(dependencies.concat(remove(resourcePath, files)));
});
}
var findAllDependencies = function(files) {
return Promise.all(files.map(
function(f) { return elmCompiler.findAllDependencies(f) }
))
.then(flatten);
}
module.exports = function() {
this.cacheable && this.cacheable();
var callback = this.async();
if (!callback) {
throw 'elm-webpack-loader currently only supports async mode.';
}
// bind helper functions to `this`
var addDependencies = _addDependencies.bind(this);
var addDirDependency = _addDirDependency.bind(this);
var emitError = this.emitError.bind(this);
var options = getOptions.call(this);
var files = getFiles.call(this, options);
var resourcePath = this.resourcePath;
var promises = [];
// we only need to track deps if we are in watch mode
// otherwise, we trust elm to do it's job
if (options.forceWatch || isInWatchMode()){
// we can do a glob to track deps we care about if cwd is set
if (typeof options.cwd !== "undefined" && options.cwd !== null){
// watch elm-package.json
var elmPackage = path.join(options.cwd, "elm-package.json");
addDependencies(elmPackage);
var dirs = filesToWatch(options.cwd);
// watch all the dirs in elm-package.json
addDirDependency.bind(this)(dirs);
}
// find all the deps, adding them to the watch list if we successfully parsed everything
// otherwise return an error which is currently ignored
var dependencies = dependenciesFor(resourcePath, files).then(function(dependencies){
// add each dependency to the tree
dependencies.map(addDependencies);
return { kind: 'success', result: true };
}).catch(function(v){
emitError(v);
return { kind: 'error', error: v };
})
promises.push(dependencies);
}
delete options.forceWatch
var maxInstances = options.maxInstances;
if (typeof maxInstances === "undefined"){
maxInstances = 1;
} else {
delete options.maxInstances;
}
var intervalId = setInterval(function(){
if (runningInstances >= maxInstances) return;
runningInstances += 1;
clearInterval(intervalId);
// If we are running in watch mode, and we have previously compiled
// the current file, then let the user know that elm-make is running
// and can be slow
if (alreadyCompiledFiles.indexOf(resourcePath) > -1){
console.log('Started compiling Elm..');
}
var compilation = elmCompiler.compileToString(files, options)
.then(function(v) { runningInstances -= 1; return { kind: 'success', result: v }; })
.catch(function(v) { runningInstances -= 1; return { kind: 'error', error: v }; });
promises.push(compilation);
Promise.all(promises)
.then(function(results) {
var output = results[results.length - 1]; // compilation output is always last
if (output.kind == 'success') {
alreadyCompiledFiles.push(resourcePath);
callback(null, output.result);
} else {
output.error.message = 'Compiler process exited with error ' + output.error.message;
callback(output.error);
}
}).catch(function(err){
callback(err);
});
}, 200);
}
// HELPERS
function flatten(arrayOfArrays) {
return arrayOfArrays.reduce(function(flattened, array) {
return flattened.concat(array)
}, []);
}
function unique(items) {
return items.filter(function(item, index, array) {
return array.indexOf(item) === index;
});
}
function remove(condemned, items) {
return items.filter(function(item) {
return item !== condemned;
});
}