]> git.immae.eu Git - github/fretlink/purs-loader.git/blob - lib/index.js
Merge remote-tracking branch 'fretlink/master' into latest
[github/fretlink/purs-loader.git] / lib / index.js
1 'use strict';
2
3 var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
4
5 var debug_ = require('debug');
6
7 var debug = debug_('purs-loader');
8
9 var debugVerbose = debug_('purs-loader:verbose');
10
11 var loaderUtils = require('loader-utils');
12
13 var Promise = require('bluebird');
14
15 var path = require('path');
16
17 var PsModuleMap = require('./purs-module-map');
18
19 var compile = require('./compile');
20
21 var bundle = require('./bundle');
22
23 var ide = require('./ide');
24
25 var toJavaScript = require('./to-javascript');
26
27 var sourceMaps = require('./source-maps');
28
29 var dargs = require('./dargs');
30
31 var utils = require('./utils');
32
33 var spawn = require('cross-spawn').sync;
34
35 var eol = require('os').EOL;
36
37 var CACHE_VAR = {
38 rebuild: false,
39 deferred: [],
40 bundleModules: [],
41 ideServer: null,
42 psModuleMap: null,
43 warnings: [],
44 errors: [],
45 compilationStarted: false,
46 compilationFinished: false,
47 compilationFailed: false,
48 installed: false,
49 srcOption: []
50 };
51
52 module.exports = function purescriptLoader(source, map) {
53 var _this = this;
54
55 this.cacheable && this.cacheable();
56
57 var webpackContext = this.options && this.options.context || this.rootContext;
58
59 var callback = this.async();
60
61 var loaderOptions = loaderUtils.getOptions(this) || {};
62
63 var srcOption = function (pscPackage) {
64 var srcPath = path.join('src', '**', '*.purs');
65
66 var bowerPath = path.join('bower_components', 'purescript-*', 'src', '**', '*.purs');
67
68 if (CACHE_VAR.srcOption.length > 0) {
69 return CACHE_VAR.srcOption;
70 } else if (pscPackage) {
71 var pscPackageCommand = 'psc-package';
72
73 var pscPackageArgs = ['sources'];
74
75 var loaderSrc = loaderOptions.src || [srcPath];
76
77 debug('psc-package %s %o', pscPackageCommand, pscPackageArgs);
78
79 var cmd = spawn(pscPackageCommand, pscPackageArgs);
80
81 if (cmd.error) {
82 throw new Error(cmd.error);
83 } else if (cmd.status !== 0) {
84 var error = cmd.stdout.toString();
85
86 throw new Error(error);
87 } else {
88 var result = cmd.stdout.toString().split(eol).filter(function (v) {
89 return v != '';
90 }).concat(loaderSrc);
91
92 debug('psc-package result: %o', result);
93
94 CACHE_VAR.srcOption = result;
95
96 return result;
97 }
98 } else {
99 var _result = loaderOptions.src || [bowerPath, srcPath];
100
101 CACHE_VAR.srcOption = _result;
102
103 return _result;
104 }
105 }(loaderOptions.pscPackage);
106
107 var options = Object.assign({
108 context: webpackContext,
109 psc: null,
110 pscArgs: {},
111 pscBundle: null,
112 pscBundleArgs: {},
113 pscIdeClient: null,
114 pscIdeClientArgs: {},
115 pscIdeServer: null,
116 pscIdeServerArgs: {},
117 pscIde: false,
118 pscIdeColors: loaderOptions.psc === 'psa',
119 pscPackage: false,
120 bundleOutput: 'output/bundle.js',
121 bundleNamespace: 'PS',
122 bundle: false,
123 warnings: true,
124 watch: false,
125 output: 'output',
126 src: []
127 }, loaderOptions, {
128 src: srcOption
129 });
130
131 if (!CACHE_VAR.installed) {
132 debugVerbose('installing purs-loader with options: %O', options);
133
134 CACHE_VAR.installed = true;
135
136 // invalidate loader CACHE_VAR when bundle is marked as invalid (in watch mode)
137 this._compiler.plugin('invalid', function () {
138 debugVerbose('invalidating loader CACHE_VAR');
139
140 CACHE_VAR = {
141 rebuild: options.pscIde,
142 deferred: [],
143 bundleModules: [],
144 ideServer: CACHE_VAR.ideServer,
145 psModuleMap: CACHE_VAR.psModuleMap,
146 warnings: [],
147 errors: [],
148 compilationStarted: false,
149 compilationFinished: false,
150 compilationFailed: false,
151 installed: CACHE_VAR.installed,
152 srcOption: []
153 };
154 });
155
156 // add psc warnings to webpack compilation warnings
157 this._compiler.plugin('after-compile', function (compilation, callback) {
158 CACHE_VAR.warnings.forEach(function (warning) {
159 compilation.warnings.push(warning);
160 });
161
162 CACHE_VAR.errors.forEach(function (error) {
163 compilation.errors.push(error);
164 });
165
166 callback();
167 });
168 }
169
170 var psModuleName = PsModuleMap.matchModule(source);
171
172 var psModule = {
173 name: psModuleName,
174 source: source,
175 load: function load(_ref) {
176 var js = _ref.js,
177 map = _ref.map;
178 return callback(null, js, map);
179 },
180 reject: function reject(error) {
181 return callback(error);
182 },
183 srcPath: this.resourcePath,
184 remainingRequest: loaderUtils.getRemainingRequest(this),
185 srcDir: path.dirname(this.resourcePath),
186 jsPath: path.resolve(path.join(options.output, psModuleName, 'index.js')),
187 options: options,
188 cache: CACHE_VAR,
189 emitWarning: function emitWarning(warning) {
190 if (options.warnings && warning.length) {
191 CACHE_VAR.warnings.push(warning);
192 }
193 },
194 emitError: function emitError(pscMessage) {
195 if (pscMessage.length) {
196 var modules = [];
197
198 var matchErrorsSeparator = /\n(?=Error)/;
199 var errors = pscMessage.split(matchErrorsSeparator);
200 var _iteratorNormalCompletion = true;
201 var _didIteratorError = false;
202 var _iteratorError = undefined;
203
204 try {
205 for (var _iterator = errors[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
206 var error = _step.value;
207
208 var matchErrLocation = /at (.+\.purs):(\d+):(\d+) - (\d+):(\d+) \(line \2, column \3 - line \4, column \5\)/;
209
210 var _ref2 = matchErrLocation.exec(error) || [],
211 _ref3 = _slicedToArray(_ref2, 2),
212 filename = _ref3[1];
213
214 if (!filename) continue;
215
216 var baseModulePath = path.join(_this.rootContext, filename);
217 _this.addDependency(baseModulePath);
218
219 var foreignModulesErrorCodes = ['ErrorParsingFFIModule', 'MissingFFIImplementations', 'UnusedFFIImplementations', 'MissingFFIModule'];
220 var _iteratorNormalCompletion2 = true;
221 var _didIteratorError2 = false;
222 var _iteratorError2 = undefined;
223
224 try {
225 for (var _iterator2 = foreignModulesErrorCodes[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
226 var code = _step2.value;
227
228 if (error.includes(code)) {
229 var resolved = utils.resolveForeignModule(baseModulePath);
230 _this.addDependency(resolved);
231 }
232 }
233 } catch (err) {
234 _didIteratorError2 = true;
235 _iteratorError2 = err;
236 } finally {
237 try {
238 if (!_iteratorNormalCompletion2 && _iterator2.return) {
239 _iterator2.return();
240 }
241 } finally {
242 if (_didIteratorError2) {
243 throw _iteratorError2;
244 }
245 }
246 }
247
248 var matchErrModuleName = /in module ((?:\w+\.)*\w+)/;
249
250 var _ref4 = matchErrModuleName.exec(error) || [],
251 _ref5 = _slicedToArray(_ref4, 2),
252 baseModuleName = _ref5[1];
253
254 if (!baseModuleName) continue;
255
256 var matchMissingModuleName = /Module ((?:\w+\.)*\w+) was not found/;
257 var matchMissingImportFromModuleName = /Cannot import value \w+ from module ((?:\w+\.)*\w+)/;
258 var _arr = [matchMissingModuleName, matchMissingImportFromModuleName];
259 for (var _i = 0; _i < _arr.length; _i++) {
260 var re = _arr[_i];
261 var _ref6 = re.exec(error) || [],
262 _ref7 = _slicedToArray(_ref6, 2),
263 targetModuleName = _ref7[1];
264
265 if (targetModuleName) {
266 var _resolved = utils.resolvePursModule({
267 baseModulePath: baseModulePath,
268 baseModuleName: baseModuleName,
269 targetModuleName: targetModuleName
270 });
271 _this.addDependency(_resolved);
272 }
273 }
274
275 var desc = {
276 name: baseModuleName,
277 filename: baseModulePath
278 };
279
280 if (typeof _this.describePscError === 'function') {
281 var _describePscError = _this.describePscError(error, desc),
282 _describePscError$dep = _describePscError.dependencies,
283 dependencies = _describePscError$dep === undefined ? [] : _describePscError$dep,
284 details = _describePscError.details;
285
286 var _iteratorNormalCompletion3 = true;
287 var _didIteratorError3 = false;
288 var _iteratorError3 = undefined;
289
290 try {
291
292 for (var _iterator3 = dependencies[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
293 var dep = _step3.value;
294
295 _this.addDependency(dep);
296 }
297 } catch (err) {
298 _didIteratorError3 = true;
299 _iteratorError3 = err;
300 } finally {
301 try {
302 if (!_iteratorNormalCompletion3 && _iterator3.return) {
303 _iterator3.return();
304 }
305 } finally {
306 if (_didIteratorError3) {
307 throw _iteratorError3;
308 }
309 }
310 }
311
312 Object.assign(desc, details);
313 }
314
315 modules.push(desc);
316 }
317 } catch (err) {
318 _didIteratorError = true;
319 _iteratorError = err;
320 } finally {
321 try {
322 if (!_iteratorNormalCompletion && _iterator.return) {
323 _iterator.return();
324 }
325 } finally {
326 if (_didIteratorError) {
327 throw _iteratorError;
328 }
329 }
330 }
331
332 CACHE_VAR.errors.push(new utils.PscError(pscMessage, modules));
333 }
334 }
335 };
336
337 debug('loading %s', psModule.name);
338
339 if (options.bundle) {
340 CACHE_VAR.bundleModules.push(psModule.name);
341 }
342
343 if (CACHE_VAR.rebuild) {
344 var connect = function connect() {
345 if (!CACHE_VAR.ideServer) {
346 CACHE_VAR.ideServer = true;
347
348 return ide.connect(psModule).then(function (ideServer) {
349 CACHE_VAR.ideServer = ideServer;
350 return psModule;
351 }).then(ide.loadWithRetry).catch(function (error) {
352 if (CACHE_VAR.ideServer.kill) {
353 debug('ide failed to initially load modules, stopping the ide server process');
354
355 CACHE_VAR.ideServer.kill();
356 }
357
358 CACHE_VAR.ideServer = null;
359
360 return Promise.reject(error);
361 });
362 } else {
363 return Promise.resolve(psModule);
364 }
365 };
366
367 var rebuild = function rebuild() {
368 return ide.rebuild(psModule).then(function () {
369 return toJavaScript(psModule).then(function (js) {
370 return sourceMaps(psModule, js);
371 }).then(psModule.load).catch(psModule.reject);
372 }).catch(function (error) {
373 if (error instanceof ide.UnknownModuleError) {
374 // Store the modules that trigger a recompile due to an
375 // unknown module error. We need to wait until compilation is
376 // done before loading these files.
377
378 CACHE_VAR.deferred.push(psModule);
379
380 if (!CACHE_VAR.compilationStarted) {
381 CACHE_VAR.compilationStarted = true;
382
383 return compile(psModule).then(function () {
384 CACHE_VAR.compilationFinished = true;
385 }).then(function () {
386 return Promise.map(CACHE_VAR.deferred, function (psModule) {
387 return ide.load(psModule).then(function () {
388 return toJavaScript(psModule);
389 }).then(function (js) {
390 return sourceMaps(psModule, js);
391 }).then(psModule.load);
392 });
393 }).catch(function (error) {
394 CACHE_VAR.compilationFailed = true;
395
396 CACHE_VAR.deferred[0].reject(error);
397
398 CACHE_VAR.deferred.slice(1).forEach(function (psModule) {
399 psModule.reject(new Error('purs-loader failed'));
400 });
401 });
402 } else if (CACHE_VAR.compilationFailed) {
403 CACHE_VAR.deferred.pop().reject(new Error('purs-loader failed'));
404 } else {
405 // The compilation has started. We must wait until it is
406 // done in order to ensure the module map contains all of
407 // the unknown modules.
408 }
409 } else {
410 debug('ide rebuild failed due to an unhandled error: %o', error);
411
412 psModule.reject(error);
413 }
414 });
415 };
416
417 connect().then(rebuild);
418 } else if (CACHE_VAR.compilationFinished) {
419 debugVerbose('compilation is already finished, loading module %s', psModule.name);
420
421 toJavaScript(psModule).then(function (js) {
422 return sourceMaps(psModule, js);
423 }).then(psModule.load).catch(psModule.reject);
424 } else {
425 // The compilation has not finished yet. We need to wait for
426 // compilation to finish before the loaders run so that references
427 // to compiled output are valid. Push the modules into the CACHE_VAR to
428 // be loaded once the complation is complete.
429
430 CACHE_VAR.deferred.push(psModule);
431
432 if (!CACHE_VAR.compilationStarted) {
433 CACHE_VAR.compilationStarted = true;
434
435 compile(psModule).then(function () {
436 CACHE_VAR.compilationFinished = true;
437 }).then(function () {
438 if (options.bundle) {
439 return bundle(options, CACHE_VAR.bundleModules);
440 }
441 }).then(function () {
442 return Promise.map(CACHE_VAR.deferred, function (psModule) {
443 return toJavaScript(psModule).then(function (js) {
444 return sourceMaps(psModule, js);
445 }).then(psModule.load);
446 });
447 }).catch(function (error) {
448 CACHE_VAR.compilationFailed = true;
449
450 CACHE_VAR.deferred[0].reject(error);
451
452 CACHE_VAR.deferred.slice(1).forEach(function (psModule) {
453 psModule.reject(new Error('purs-loader failed'));
454 });
455 });
456 } else if (CACHE_VAR.compilationFailed) {
457 CACHE_VAR.deferred.pop().reject(new Error('purs-loader failed'));
458 } else {
459 // The complation has started. Nothing to do but wait until it is
460 // done before loading all of the modules.
461 }
462 }
463 };