aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--lib/bundle.js63
-rw-r--r--lib/compile.js64
-rw-r--r--lib/dargs.js7
-rw-r--r--lib/ide.js270
-rw-r--r--lib/index.js434
-rw-r--r--lib/purs-module-map.js78
-rw-r--r--lib/source-maps.js61
-rw-r--r--lib/to-javascript.js152
-rw-r--r--lib/utils.js63
9 files changed, 1192 insertions, 0 deletions
diff --git a/lib/bundle.js b/lib/bundle.js
new file mode 100644
index 0000000..943e08b
--- /dev/null
+++ b/lib/bundle.js
@@ -0,0 +1,63 @@
1'use strict';
2
3var path = require('path');
4
5var Promise = require('bluebird');
6
7var fs = Promise.promisifyAll(require('fs'));
8
9var spawn = require('cross-spawn');
10
11var debug = require('debug')('purs-loader');
12
13var dargs = require('./dargs');
14
15module.exports = function bundle(options, bundleModules) {
16 var stdout = [];
17
18 var stderr = [];
19
20 var bundleCommand = options.pscBundle || 'purs';
21
22 var bundleArgs = (options.pscBundle ? [] : ['bundle']).concat(dargs(Object.assign({
23 _: [path.join(options.output, '*', '*.js')],
24 output: options.bundleOutput,
25 namespace: options.bundleNamespace
26 }, options.pscBundleArgs)));
27
28 bundleModules.forEach(function (name) {
29 return bundleArgs.push('--module', name);
30 });
31
32 debug('bundle: %s %O', bundleCommand, bundleArgs);
33
34 return new Promise(function (resolve, reject) {
35 debug('bundling PureScript...');
36
37 var compilation = spawn(bundleCommand, bundleArgs);
38
39 compilation.stdout.on('data', function (data) {
40 return stdout.push(data.toString());
41 });
42
43 compilation.stderr.on('data', function (data) {
44 return stderr.push(data.toString());
45 });
46
47 compilation.on('close', function (code) {
48 debug('finished bundling PureScript.');
49
50 if (code !== 0) {
51 var errorMessage = stderr.join('');
52
53 if (errorMessage.length) {
54 psModule.emitError(errorMessage);
55 }
56
57 reject(new Error('bundling failed'));
58 } else {
59 resolve(fs.appendFileAsync(options.bundleOutput, 'module.exports = ' + options.bundleNamespace));
60 }
61 });
62 });
63}; \ No newline at end of file
diff --git a/lib/compile.js b/lib/compile.js
new file mode 100644
index 0000000..a1bf211
--- /dev/null
+++ b/lib/compile.js
@@ -0,0 +1,64 @@
1'use strict';
2
3var Promise = require('bluebird');
4
5var spawn = require('cross-spawn');
6
7var debug_ = require('debug');
8
9var debug = debug_('purs-loader');
10
11var debugVerbose = debug_('purs-loader:verbose');
12
13var dargs = require('./dargs');
14
15module.exports = function compile(psModule) {
16 var options = psModule.options;
17
18 var compileCommand = options.psc || 'purs';
19
20 var compileArgs = (options.psc ? [] : ['compile']).concat(dargs(Object.assign({
21 _: options.src,
22 output: options.output
23 }, options.pscArgs)));
24
25 var stderr = [];
26
27 debug('compile %s %O', compileCommand, compileArgs);
28
29 return new Promise(function (resolve, reject) {
30 debug('compiling PureScript...');
31
32 var compilation = spawn(compileCommand, compileArgs);
33
34 compilation.stderr.on('data', function (data) {
35 stderr.push(data.toString());
36 });
37
38 compilation.stdout.on('data', function (data) {
39 debugVerbose(data.toString());
40 });
41
42 compilation.on('close', function (code) {
43 debug('finished compiling PureScript.');
44
45 if (code !== 0) {
46 var errorMessage = stderr.join('');
47 if (errorMessage.length) {
48 psModule.emitError(errorMessage);
49 }
50 if (options.watch) {
51 resolve(psModule);
52 } else {
53 reject(new Error('compilation failed'));
54 }
55 } else {
56 var warningMessage = stderr.join('');
57 if (options.warnings && warningMessage.length) {
58 psModule.emitWarning(warningMessage);
59 }
60 resolve(psModule);
61 }
62 });
63 });
64}; \ No newline at end of file
diff --git a/lib/dargs.js b/lib/dargs.js
new file mode 100644
index 0000000..191ab44
--- /dev/null
+++ b/lib/dargs.js
@@ -0,0 +1,7 @@
1'use strict';
2
3var dargs = require('dargs');
4
5module.exports = function (obj) {
6 return dargs(obj, { ignoreFalse: true });
7}; \ No newline at end of file
diff --git a/lib/ide.js b/lib/ide.js
new file mode 100644
index 0000000..6087e1c
--- /dev/null
+++ b/lib/ide.js
@@ -0,0 +1,270 @@
1'use strict';
2
3var path = require('path');
4
5var Promise = require('bluebird');
6
7var fs = Promise.promisifyAll(require('fs'));
8
9var retryPromise = require('promise-retry');
10
11var spawn = require('cross-spawn');
12
13var colors = require('chalk');
14
15var debug_ = require('debug');
16
17var debug = debug_('purs-loader');
18
19var debugVerbose = debug_('purs-loader:verbose');
20
21var dargs = require('./dargs');
22
23var compile = require('./compile');
24
25var PsModuleMap = require('./purs-module-map');
26
27function UnknownModuleError() {
28 this.name = 'UnknownModuleError';
29 this.stack = new Error().stack;
30}
31
32UnknownModuleError.prototype = Object.create(Error.prototype);
33
34UnknownModuleError.prototype.constructor = UnknownModuleError;
35
36module.exports.UnknownModuleError = UnknownModuleError;
37
38function spawnIdeClient(body, options) {
39 var ideClientCommand = options.pscIdeClient || 'purs';
40
41 var ideClientArgs = (options.pscIdeClient ? [] : ['ide', 'client']).concat(dargs(options.pscIdeClientArgs));
42
43 var stderr = [];
44
45 var stdout = [];
46
47 debug('ide client %s %o %O', ideClientCommand, ideClientArgs, body);
48
49 return new Promise(function (resolve, reject) {
50 var ideClient = spawn(ideClientCommand, ideClientArgs);
51
52 ideClient.stderr.on('data', function (data) {
53 stderr.push(data.toString());
54 });
55
56 ideClient.stdout.on('data', function (data) {
57 stdout.push(data.toString());
58 });
59
60 ideClient.on('close', function (code) {
61 if (code !== 0) {
62 var errorMessage = stderr.join('');
63
64 reject(new Error('ide client failed: ' + errorMessage));
65 } else {
66 var result = stdout.join('');
67
68 resolve(result);
69 }
70 });
71
72 ideClient.stdin.resume();
73
74 ideClient.stdin.write(JSON.stringify(body));
75
76 ideClient.stdin.write('\n');
77 });
78}
79
80function formatIdeResult(result, options, index, length) {
81 var numAndErr = '[' + (index + 1) + '/' + length + ' ' + result.errorCode + ']';
82 numAndErr = options.pscIdeColors ? colors.yellow(numAndErr) : numAndErr;
83
84 function makeResult() {
85 return Promise.resolve('\n' + numAndErr + ' ' + result.message);
86 }
87
88 function makeResultSnippet(filename, pos) {
89 var srcPath = path.relative(options.context, filename);
90 var fileAndPos = srcPath + ':' + pos.startLine + ':' + pos.startColumn;
91
92 return fs.readFileAsync(filename, 'utf8').then(function (source) {
93 var lines = source.split('\n').slice(pos.startLine - 1, pos.endLine);
94 var endsOnNewline = pos.endColumn === 1 && pos.startLine !== pos.endLine;
95 var up = options.pscIdeColors ? colors.red('^') : '^';
96 var down = options.pscIdeColors ? colors.red('v') : 'v';
97 var trimmed = lines.slice(0);
98
99 if (endsOnNewline) {
100 lines.splice(lines.length - 1, 1);
101 pos.endLine = pos.endLine - 1;
102 pos.endColumn = lines[lines.length - 1].length || 1;
103 }
104
105 // strip newlines at the end
106 if (endsOnNewline) {
107 trimmed = lines.reverse().reduce(function (trimmed, line, i) {
108 if (i === 0 && line === '') trimmed.trimming = true;
109 if (!trimmed.trimming) trimmed.push(line);
110 if (trimmed.trimming && line !== '') {
111 trimmed.trimming = false;
112 trimmed.push(line);
113 }
114 return trimmed;
115 }, []).reverse();
116 pos.endLine = pos.endLine - (lines.length - trimmed.length);
117 pos.endColumn = trimmed[trimmed.length - 1].length || 1;
118 }
119
120 var spaces = ' '.repeat(String(pos.endLine).length);
121 var snippet = trimmed.map(function (line, i) {
122 return ' ' + (pos.startLine + i) + ' ' + line;
123 }).join('\n');
124
125 if (trimmed.length === 1) {
126 snippet += '\n ' + spaces + ' ' + ' '.repeat(pos.startColumn - 1) + up.repeat(pos.endColumn - pos.startColumn + 1);
127 } else {
128 snippet = ' ' + spaces + ' ' + ' '.repeat(pos.startColumn - 1) + down + '\n' + snippet;
129 snippet += '\n ' + spaces + ' ' + ' '.repeat(pos.endColumn - 1) + up;
130 }
131
132 return Promise.resolve('\n' + numAndErr + ' ' + fileAndPos + '\n\n' + snippet + '\n\n' + result.message);
133 }).catch(function (error) {
134 debug('failed to format ide result: %o', error);
135
136 return Promise.resolve('');
137 });
138 }
139
140 return result.filename && result.position ? makeResultSnippet(result.filename, result.position) : makeResult();
141}
142
143module.exports.connect = function connect(psModule) {
144 var options = psModule.options;
145
146 var serverCommand = options.pscIdeServer || 'purs';
147
148 var serverArgs = (options.pscIdeServer ? [] : ['ide', 'server']).concat(dargs(Object.assign({
149 outputDirectory: options.output,
150 '_': options.src
151 }, options.pscIdeServerArgs)));
152
153 debug('ide server: %s %o', serverCommand, serverArgs);
154
155 var ideServer = spawn(serverCommand, serverArgs);
156
157 ideServer.stdout.on('data', function (data) {
158 debugVerbose('ide server stdout: %s', data.toString());
159 });
160
161 ideServer.stderr.on('data', function (data) {
162 debugVerbose('ide server stderr: %s', data.toString());
163 });
164
165 ideServer.on('error', function (error) {
166 debugVerbose('ide server error: %o', error);
167 });
168
169 ideServer.on('close', function (code, signal) {
170 debugVerbose('ide server close: %s %s', code, signal);
171 });
172
173 return Promise.resolve(ideServer);
174};
175
176module.exports.load = function load(psModule) {
177 var options = psModule.options;
178
179 var body = { command: 'load' };
180
181 return spawnIdeClient(body, options);
182};
183
184module.exports.loadWithRetry = function loadWithRetry(psModule) {
185 var retries = 9;
186
187 return retryPromise(function (retry, number) {
188 debugVerbose('attempting to load modules (%d out of %d attempts)', number, retries);
189
190 return module.exports.load(psModule).catch(retry);
191 }, {
192 retries: retries,
193 factor: 1,
194 minTimeout: 333,
195 maxTimeout: 333
196 }).then(function () {
197 return psModule;
198 });
199};
200
201module.exports.rebuild = function rebuild(psModule) {
202 var options = psModule.options;
203
204 var body = {
205 command: 'rebuild',
206 params: {
207 file: psModule.srcPath
208 }
209 };
210
211 var parseResponse = function parseResponse(response) {
212 try {
213 var parsed = JSON.parse(response);
214
215 debugVerbose('parsed JSON response: %O', parsed);
216
217 return Promise.resolve(parsed);
218 } catch (error) {
219 return Promise.reject(error);
220 }
221 };
222
223 var formatResponse = function formatResponse(parsed) {
224 var result = Array.isArray(parsed.result) ? parsed.result : [];
225
226 return Promise.map(result, function (item, i) {
227 debugVerbose('formatting result %O', item);
228
229 return formatIdeResult(item, options, i, result.length);
230 }).then(function (formatted) {
231 return {
232 parsed: parsed,
233 formatted: formatted,
234 formattedMessage: formatted.join('\n')
235 };
236 });
237 };
238
239 return spawnIdeClient(body, options).then(parseResponse).then(formatResponse).then(function (_ref) {
240 var parsed = _ref.parsed,
241 formatted = _ref.formatted,
242 formattedMessage = _ref.formattedMessage;
243
244 if (parsed.resultType === 'success') {
245 if (options.warnings && formattedMessage.length) {
246 psModule.emitWarning(formattedMessage);
247 }
248
249 return psModule;
250 } else if ((parsed.result || []).some(function (item) {
251 var isModuleNotFound = item.errorCode === 'ModuleNotFound';
252
253 var isUnknownModule = item.errorCode === 'UnknownModule';
254
255 var isUnknownModuleImport = item.errorCode === 'UnknownName' && /Unknown module/.test(item.message);
256
257 return isModuleNotFound || isUnknownModule || isUnknownModuleImport;
258 })) {
259 debug('module %s was not rebuilt because the module is unknown', psModule.name);
260
261 return Promise.reject(new UnknownModuleError());
262 } else {
263 if (formattedMessage.length) {
264 psModule.emitError(formattedMessage);
265 }
266
267 return psModule;
268 }
269 });
270}; \ No newline at end of file
diff --git a/lib/index.js b/lib/index.js
new file mode 100644
index 0000000..e648869
--- /dev/null
+++ b/lib/index.js
@@ -0,0 +1,434 @@
1'use strict';
2
3var _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
5var debug_ = require('debug');
6
7var debug = debug_('purs-loader');
8
9var debugVerbose = debug_('purs-loader:verbose');
10
11var loaderUtils = require('loader-utils');
12
13var Promise = require('bluebird');
14
15var path = require('path');
16
17var PsModuleMap = require('./purs-module-map');
18
19var compile = require('./compile');
20
21var bundle = require('./bundle');
22
23var ide = require('./ide');
24
25var toJavaScript = require('./to-javascript');
26
27var sourceMaps = require('./source-maps');
28
29var dargs = require('./dargs');
30
31var utils = require('./utils');
32
33var spawn = require('cross-spawn').sync;
34
35var eol = require('os').EOL;
36
37var 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
52module.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) line (\d+), column (\d+) - line (\d+), column (\d+)/;
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 matchErrModuleName = /in module ((?:\w+\.)*\w+)/;
220
221 var _ref4 = matchErrModuleName.exec(error) || [],
222 _ref5 = _slicedToArray(_ref4, 2),
223 baseModuleName = _ref5[1];
224
225 if (!baseModuleName) continue;
226
227 var matchMissingModuleName = /Module ((?:\w+\.)*\w+) was not found/;
228 var matchMissingImportFromModuleName = /Cannot import value \w+ from module ((?:\w+\.)*\w+)/;
229 var _arr = [matchMissingModuleName, matchMissingImportFromModuleName];
230 for (var _i = 0; _i < _arr.length; _i++) {
231 var re = _arr[_i];
232 var _ref6 = re.exec(error) || [],
233 _ref7 = _slicedToArray(_ref6, 2),
234 targetModuleName = _ref7[1];
235
236 if (targetModuleName) {
237 var resolved = utils.resolvePursModule({
238 baseModulePath: baseModulePath,
239 baseModuleName: baseModuleName,
240 targetModuleName: targetModuleName
241 });
242 _this.addDependency(resolved);
243 }
244 }
245
246 var desc = {
247 name: baseModuleName,
248 filename: baseModulePath
249 };
250
251 if (typeof _this.describePscError === 'function') {
252 var _describePscError = _this.describePscError(error, desc),
253 _describePscError$dep = _describePscError.dependencies,
254 dependencies = _describePscError$dep === undefined ? [] : _describePscError$dep,
255 details = _describePscError.details;
256
257 var _iteratorNormalCompletion2 = true;
258 var _didIteratorError2 = false;
259 var _iteratorError2 = undefined;
260
261 try {
262
263 for (var _iterator2 = dependencies[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
264 var dep = _step2.value;
265
266 _this.addDependency(dep);
267 }
268 } catch (err) {
269 _didIteratorError2 = true;
270 _iteratorError2 = err;
271 } finally {
272 try {
273 if (!_iteratorNormalCompletion2 && _iterator2.return) {
274 _iterator2.return();
275 }
276 } finally {
277 if (_didIteratorError2) {
278 throw _iteratorError2;
279 }
280 }
281 }
282
283 Object.assign(desc, details);
284 }
285
286 modules.push(desc);
287 }
288 } catch (err) {
289 _didIteratorError = true;
290 _iteratorError = err;
291 } finally {
292 try {
293 if (!_iteratorNormalCompletion && _iterator.return) {
294 _iterator.return();
295 }
296 } finally {
297 if (_didIteratorError) {
298 throw _iteratorError;
299 }
300 }
301 }
302
303 CACHE_VAR.errors.push(new utils.PscError(pscMessage, modules));
304 }
305 }
306 };
307
308 debug('loading %s', psModule.name);
309
310 if (options.bundle) {
311 CACHE_VAR.bundleModules.push(psModule.name);
312 }
313
314 if (CACHE_VAR.rebuild) {
315 var connect = function connect() {
316 if (!CACHE_VAR.ideServer) {
317 CACHE_VAR.ideServer = true;
318
319 return ide.connect(psModule).then(function (ideServer) {
320 CACHE_VAR.ideServer = ideServer;
321 return psModule;
322 }).then(ide.loadWithRetry).catch(function (error) {
323 if (CACHE_VAR.ideServer.kill) {
324 debug('ide failed to initially load modules, stopping the ide server process');
325
326 CACHE_VAR.ideServer.kill();
327 }
328
329 CACHE_VAR.ideServer = null;
330
331 return Promise.reject(error);
332 });
333 } else {
334 return Promise.resolve(psModule);
335 }
336 };
337
338 var rebuild = function rebuild() {
339 return ide.rebuild(psModule).then(function () {
340 return toJavaScript(psModule).then(function (js) {
341 return sourceMaps(psModule, js);
342 }).then(psModule.load).catch(psModule.reject);
343 }).catch(function (error) {
344 if (error instanceof ide.UnknownModuleError) {
345 // Store the modules that trigger a recompile due to an
346 // unknown module error. We need to wait until compilation is
347 // done before loading these files.
348
349 CACHE_VAR.deferred.push(psModule);
350
351 if (!CACHE_VAR.compilationStarted) {
352 CACHE_VAR.compilationStarted = true;
353
354 return compile(psModule).then(function () {
355 CACHE_VAR.compilationFinished = true;
356 }).then(function () {
357 return Promise.map(CACHE_VAR.deferred, function (psModule) {
358 return ide.load(psModule).then(function () {
359 return toJavaScript(psModule);
360 }).then(function (js) {
361 return sourceMaps(psModule, js);
362 }).then(psModule.load);
363 });
364 }).catch(function (error) {
365 CACHE_VAR.compilationFailed = true;
366
367 CACHE_VAR.deferred[0].reject(error);
368
369 CACHE_VAR.deferred.slice(1).forEach(function (psModule) {
370 psModule.reject(new Error('purs-loader failed'));
371 });
372 });
373 } else if (CACHE_VAR.compilationFailed) {
374 CACHE_VAR.deferred.pop().reject(new Error('purs-loader failed'));
375 } else {
376 // The compilation has started. We must wait until it is
377 // done in order to ensure the module map contains all of
378 // the unknown modules.
379 }
380 } else {
381 debug('ide rebuild failed due to an unhandled error: %o', error);
382
383 psModule.reject(error);
384 }
385 });
386 };
387
388 connect().then(rebuild);
389 } else if (CACHE_VAR.compilationFinished) {
390 debugVerbose('compilation is already finished, loading module %s', psModule.name);
391
392 toJavaScript(psModule).then(function (js) {
393 return sourceMaps(psModule, js);
394 }).then(psModule.load).catch(psModule.reject);
395 } else {
396 // The compilation has not finished yet. We need to wait for
397 // compilation to finish before the loaders run so that references
398 // to compiled output are valid. Push the modules into the CACHE_VAR to
399 // be loaded once the complation is complete.
400
401 CACHE_VAR.deferred.push(psModule);
402
403 if (!CACHE_VAR.compilationStarted) {
404 CACHE_VAR.compilationStarted = true;
405
406 compile(psModule).then(function () {
407 CACHE_VAR.compilationFinished = true;
408 }).then(function () {
409 if (options.bundle) {
410 return bundle(options, CACHE_VAR.bundleModules);
411 }
412 }).then(function () {
413 return Promise.map(CACHE_VAR.deferred, function (psModule) {
414 return toJavaScript(psModule).then(function (js) {
415 return sourceMaps(psModule, js);
416 }).then(psModule.load);
417 });
418 }).catch(function (error) {
419 CACHE_VAR.compilationFailed = true;
420
421 CACHE_VAR.deferred[0].reject(error);
422
423 CACHE_VAR.deferred.slice(1).forEach(function (psModule) {
424 psModule.reject(new Error('purs-loader failed'));
425 });
426 });
427 } else if (CACHE_VAR.compilationFailed) {
428 CACHE_VAR.deferred.pop().reject(new Error('purs-loader failed'));
429 } else {
430 // The complation has started. Nothing to do but wait until it is
431 // done before loading all of the modules.
432 }
433 }
434}; \ No newline at end of file
diff --git a/lib/purs-module-map.js b/lib/purs-module-map.js
new file mode 100644
index 0000000..cb06322
--- /dev/null
+++ b/lib/purs-module-map.js
@@ -0,0 +1,78 @@
1'use strict';
2
3var path = require('path');
4
5var Promise = require('bluebird');
6
7var fs = Promise.promisifyAll(require('fs'));
8
9var globby = require('globby');
10
11var debug = require('debug')('purs-loader');
12
13var srcModuleRegex = /(?:^|\n)module\s+([\w\.]+)/i;
14
15var importModuleRegex = /(?:^|\n)\s*import\s+([\w\.]+)/ig;
16
17module.exports.matchModule = function matchModule(str) {
18 var matches = str.match(srcModuleRegex);
19 return matches && matches[1];
20};
21
22module.exports.matchImports = function matchImports(str) {
23 var matches = str.match(importModuleRegex);
24 return (matches || []).map(function (a) {
25 return a.replace(/\n?\s*import\s+/i, '');
26 });
27};
28
29module.exports.makeMapEntry = function makeMapEntry(filePurs) {
30 var dirname = path.dirname(filePurs);
31
32 var basename = path.basename(filePurs, '.purs');
33
34 var fileJs = path.join(dirname, basename + '.js');
35
36 var result = Promise.props({
37 filePurs: fs.readFileAsync(filePurs, 'utf8'),
38 fileJs: fs.readFileAsync(fileJs, 'utf8').catch(function () {
39 return undefined;
40 })
41 }).then(function (fileMap) {
42 var sourcePurs = fileMap.filePurs;
43
44 var sourceJs = fileMap.fileJs;
45
46 var moduleName = module.exports.matchModule(sourcePurs);
47
48 var imports = module.exports.matchImports(sourcePurs);
49
50 var map = {};
51
52 map[moduleName] = map[moduleName] || {};
53
54 map[moduleName].src = path.resolve(filePurs);
55
56 map[moduleName].imports = imports;
57
58 if (sourceJs) {
59 map[moduleName].ffi = path.resolve(fileJs);
60 }
61
62 return map;
63 });
64
65 return result;
66};
67
68module.exports.makeMap = function makeMap(src) {
69 debug('loading PureScript source and FFI files from %o', src);
70
71 var globs = [].concat(src);
72
73 return globby(globs).then(function (paths) {
74 return Promise.all(paths.map(module.exports.makeMapEntry)).then(function (result) {
75 return result.reduce(Object.assign, {});
76 });
77 });
78}; \ No newline at end of file
diff --git a/lib/source-maps.js b/lib/source-maps.js
new file mode 100644
index 0000000..3ad70dd
--- /dev/null
+++ b/lib/source-maps.js
@@ -0,0 +1,61 @@
1'use strict';
2
3var Promise = require('bluebird');
4
5var fs = require('fs');
6
7var path = require('path');
8
9var debug_ = require('debug');
10
11var debugVerbose = debug_('purs-loader:verbose');
12
13module.exports = function sourceMap(psModule, js) {
14 var options = psModule.options;
15
16 var jsPath = psModule.jsPath;
17
18 var srcPath = psModule.srcPath;
19
20 var source = psModule.source;
21
22 var remainingRequest = psModule.remainingRequest;
23
24 var sourceMapPath = path.join(path.dirname(jsPath), 'index.js.map');
25
26 var isSourceMapsEnabled = options.pscArgs && options.pscArgs.sourceMaps;
27
28 return new Promise(function (resolve, reject) {
29 if (!isSourceMapsEnabled) {
30 resolve({
31 js: js,
32 map: undefined
33 });
34 } else {
35 debugVerbose('loading source map %s', sourceMapPath);
36
37 fs.readFile(sourceMapPath, 'utf-8', function (error, result) {
38 if (error) {
39 reject(error);
40 } else {
41 try {
42 var map = Object.assign(JSON.parse(result), {
43 sources: [remainingRequest],
44 file: path.normalize(srcPath),
45 sourcesContent: [source]
46 });
47
48 var jsRemovedMapUrl = js.replace(/^\/\/# sourceMappingURL=[^\r\n]*/gm, '');
49
50 resolve({
51 js: jsRemovedMapUrl,
52 map: map
53 });
54 } catch (error) {
55 reject(error);
56 }
57 }
58 });
59 }
60 });
61}; \ No newline at end of file
diff --git a/lib/to-javascript.js b/lib/to-javascript.js
new file mode 100644
index 0000000..ce49704
--- /dev/null
+++ b/lib/to-javascript.js
@@ -0,0 +1,152 @@
1'use strict';
2
3var Promise = require('bluebird');
4
5var fs = Promise.promisifyAll(require('fs'));
6
7var path = require('path');
8
9var jsStringEscape = require('js-string-escape');
10
11var difference = require('lodash.difference');
12
13var debug_ = require('debug');
14
15var debug = debug_('purs-loader');
16
17var debugVerbose = debug_('purs-loader:verbose');
18
19var PsModuleMap = require('./purs-module-map');
20
21function updatePsModuleMap(psModule) {
22 var options = psModule.options;
23
24 var cache = psModule.cache;
25
26 var filePurs = psModule.srcPath;
27
28 if (!cache.psModuleMap) {
29 debugVerbose('module mapping does not exist - making a new module map');
30
31 cache.psModuleMap = PsModuleMap.makeMap(options.src);
32
33 return cache.psModuleMap;
34 } else {
35 debugVerbose('module mapping exists - updating module map for %s', filePurs);
36
37 cache.psModuleMap = cache.psModuleMap.then(function (psModuleMap) {
38 return PsModuleMap.makeMapEntry(filePurs).then(function (result) {
39 var map = Object.assign(psModuleMap, result);
40
41 return map;
42 });
43 });
44
45 return cache.psModuleMap;
46 }
47}
48
49// Reference the bundle.
50function makeBundleJS(psModule) {
51 var bundleOutput = psModule.options.bundleOutput;
52
53 var name = psModule.name;
54
55 var srcDir = psModule.srcDir;
56
57 var escaped = jsStringEscape(path.relative(srcDir, bundleOutput));
58
59 var result = 'module.exports = require("' + escaped + '")["' + name + '"]';
60
61 return Promise.resolve(result);
62}
63
64// Replace require paths to output files generated by psc with paths
65// to purescript sources, which are then also run through this loader.
66// Additionally, the imports replaced are tracked so that in the event
67// the compiler fails to compile the PureScript source, we can tack on
68// any new imports in order to allow webpack to watch the new files
69// before they have been successfully compiled.
70function makeJS(psModule, psModuleMap, js) {
71 var requireRE = /require\(['"]\.\.\/([\w\.]+)(?:\/index\.js)?['"]\)/g;
72
73 var foreignRE = /require\(['"]\.\/foreign(?:\.js)?['"]\)/g;
74
75 var name = psModule.name;
76
77 var imports = psModuleMap[name].imports;
78
79 var replacedImports = [];
80
81 var result = js.replace(requireRE, function (m, p1) {
82 var moduleValue = psModuleMap[p1];
83
84 if (!moduleValue) {
85 debug('module %s was not found in the map, replacing require with null', p1);
86
87 return 'null';
88 } else {
89 var escapedPath = jsStringEscape(moduleValue.src);
90
91 replacedImports.push(p1);
92
93 return 'require("' + escapedPath + '")';
94 }
95 }).replace(foreignRE, function () {
96 var escapedPath = jsStringEscape(psModuleMap[name].ffi);
97
98 return 'require("' + escapedPath + '")';
99 });
100
101 var additionalImports = difference(imports, replacedImports);
102
103 if (!additionalImports.length) {
104 return Promise.resolve(result);
105 } else {
106 debug('rebuilding module map due to additional imports for %s: %o', name, additionalImports);
107
108 psModule.cache.psModuleMap = null;
109
110 return updatePsModuleMap(psModule).then(function (updatedPsModuleMap) {
111 var additionalImportsResult = additionalImports.map(function (import_) {
112 var moduleValue = updatedPsModuleMap[import_];
113
114 if (!moduleValue) {
115 debug('module %s was not found in the map, skipping require', import_);
116
117 return null;
118 } else {
119 var escapedPath = jsStringEscape(moduleValue.src);
120
121 return 'var ' + import_.replace(/\./g, '_') + ' = require("' + escapedPath + '")';
122 }
123 }).filter(function (a) {
124 return a !== null;
125 }).join('\n');
126
127 return result + '\n' + additionalImportsResult;
128 });
129 }
130}
131
132module.exports = function toJavaScript(psModule) {
133 var options = psModule.options;
134
135 var cache = psModule.cache;
136
137 var bundlePath = path.resolve(options.bundleOutput);
138
139 var jsPath = options.bundle ? bundlePath : psModule.jsPath;
140
141 var js = fs.readFileAsync(jsPath, 'utf8').catch(function () {
142 return '';
143 });
144
145 var psModuleMap = updatePsModuleMap(psModule);
146
147 debugVerbose('loading JavaScript for %s', psModule.name);
148
149 return Promise.props({ js: js, psModuleMap: psModuleMap }).then(function (result) {
150 return options.bundle ? makeBundleJS(psModule) : makeJS(psModule, result.psModuleMap, result.js);
151 });
152}; \ No newline at end of file
diff --git a/lib/utils.js b/lib/utils.js
new file mode 100644
index 0000000..ebee1d1
--- /dev/null
+++ b/lib/utils.js
@@ -0,0 +1,63 @@
1'use strict';
2
3var _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; }; }();
4
5function _toArray(arr) { return Array.isArray(arr) ? arr : Array.from(arr); }
6
7function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
8
9function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
10
11function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
12
13function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
14
15var path = require('path');
16
17exports.PscError = function (_Error) {
18 _inherits(PscError, _Error);
19
20 function PscError(message, modules) {
21 _classCallCheck(this, PscError);
22
23 var _this = _possibleConstructorReturn(this, (PscError.__proto__ || Object.getPrototypeOf(PscError)).call(this, message));
24
25 _this.modules = modules;
26 return _this;
27 }
28
29 _createClass(PscError, null, [{
30 key: 'name',
31 get: function get() {
32 return 'PscError';
33 }
34 }]);
35
36 return PscError;
37}(Error);
38
39var repeat = function repeat(value, times) {
40 return times <= 0 ? [] : [value].concat(_toConsumableArray(repeat(value, times - 1)));
41};
42var diffPursModuleNames = function diffPursModuleNames(from, target, parts) {
43 if (!from.length) return parts.concat(target);
44 if (!target.length) return parts.concat(repeat('..', from.length));
45
46 var _from = _toArray(from),
47 head_from = _from[0],
48 tail_from = _from.slice(1);
49
50 var _target = _toArray(target),
51 head_target = _target[0],
52 tail_target = _target.slice(1);
53
54 return head_from === head_target ? diffPursModuleNames(tail_from, tail_target, parts) : parts.concat(repeat('..', from.length), target);
55};
56exports.resolvePursModule = function (_ref) {
57 var baseModulePath = _ref.baseModulePath,
58 baseModuleName = _ref.baseModuleName,
59 targetModuleName = _ref.targetModuleName;
60
61 var parts = diffPursModuleNames(baseModuleName.split('.'), targetModuleName.split('.'), []);
62 return parts.length ? path.resolve(baseModulePath, path.join.apply(path, _toConsumableArray(parts)) + '.purs') : baseModulePath;
63}; \ No newline at end of file