aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authoreric thul <thul.eric@gmail.com>2017-04-23 14:00:35 -0400
committereric <thul.eric@gmail.com>2017-04-23 18:20:22 -0400
commit7f0547d4e02d927e766de340152a2f75b659d889 (patch)
treed9dd41f5902d81f73ad64772b34291887f4b4fdf
parent1c12889c0adf91cf3116a9d5ff44b7466b1dfcc9 (diff)
downloadpurs-loader-7f0547d4e02d927e766de340152a2f75b659d889.tar.gz
purs-loader-7f0547d4e02d927e766de340152a2f75b659d889.tar.zst
purs-loader-7f0547d4e02d927e766de340152a2f75b659d889.zip
Refactoring cache usage
-rw-r--r--package.json2
-rw-r--r--src/bundle.js21
-rw-r--r--src/compile.js19
-rw-r--r--src/ide.js324
-rw-r--r--src/index.js226
-rw-r--r--src/to-javascript.js12
6 files changed, 357 insertions, 247 deletions
diff --git a/package.json b/package.json
index ca5e5c8..1a1cc7e 100644
--- a/package.json
+++ b/package.json
@@ -39,7 +39,7 @@
39 "chalk": "^1.1.3", 39 "chalk": "^1.1.3",
40 "cross-spawn": "^3.0.1", 40 "cross-spawn": "^3.0.1",
41 "dargs": "^5.1.0", 41 "dargs": "^5.1.0",
42 "debug": "^2.2.0", 42 "debug": "^2.6.0",
43 "globby": "^4.0.0", 43 "globby": "^4.0.0",
44 "js-string-escape": "^1.0.1", 44 "js-string-escape": "^1.0.1",
45 "lodash.difference": "^4.5.0", 45 "lodash.difference": "^4.5.0",
diff --git a/src/bundle.js b/src/bundle.js
index 6627ffe..3f55f01 100644
--- a/src/bundle.js
+++ b/src/bundle.js
@@ -12,12 +12,10 @@ const debug = require('debug')('purs-loader');
12 12
13const dargs = require('./dargs'); 13const dargs = require('./dargs');
14 14
15module.exports = function bundle(options, cache) { 15module.exports = function bundle(options, bundleModules) {
16 if (cache.bundle) return Promise.resolve(cache.bundle)
17
18 const stdout = [] 16 const stdout = []
19 17
20 const stderr = cache.bundle = [] 18 const stderr = []
21 19
22 const bundleCommand = options.pscBundle || 'purs'; 20 const bundleCommand = options.pscBundle || 'purs';
23 21
@@ -27,9 +25,9 @@ module.exports = function bundle(options, cache) {
27 namespace: options.bundleNamespace, 25 namespace: options.bundleNamespace,
28 }, options.pscBundleArgs))); 26 }, options.pscBundleArgs)));
29 27
30 cache.bundleModules.forEach(name => bundleArgs.push('--module', name)) 28 bundleModules.forEach(name => bundleArgs.push('--module', name))
31 29
32 debug('spawning bundler %s %o', bundleCommand, bundleArgs); 30 debug('bundle: %s %o', bundleCommand, bundleArgs);
33 31
34 return (new Promise((resolve, reject) => { 32 return (new Promise((resolve, reject) => {
35 debug('bundling PureScript...') 33 debug('bundling PureScript...')
@@ -45,15 +43,16 @@ module.exports = function bundle(options, cache) {
45 43
46 if (code !== 0) { 44 if (code !== 0) {
47 const errorMessage = stderr.join(''); 45 const errorMessage = stderr.join('');
46
48 if (errorMessage.length) { 47 if (errorMessage.length) {
49 psModule.emitError(errorMessage); 48 psModule.emitError(errorMessage);
50 } 49 }
51 return reject(new Error('bundling failed'))
52 }
53 50
54 cache.bundle = stderr 51 reject(new Error('bundling failed'))
55 52 }
56 resolve(fs.appendFileAsync(options.bundleOutput, `module.exports = ${options.bundleNamespace}`)) 53 else {
54 resolve(fs.appendFileAsync(options.bundleOutput, `module.exports = ${options.bundleNamespace}`))
55 }
57 }) 56 })
58 })) 57 }))
59}; 58};
diff --git a/src/compile.js b/src/compile.js
index 8b5d87f..707605c 100644
--- a/src/compile.js
+++ b/src/compile.js
@@ -4,17 +4,17 @@ const Promise = require('bluebird');
4 4
5const spawn = require('cross-spawn'); 5const spawn = require('cross-spawn');
6 6
7const debug = require('debug')('purs-loader'); 7const debug_ = require('debug');
8
9const debug = debug_('purs-loader');
10
11const debugVerbose = debug_('purs-loader:verbose');
8 12
9const dargs = require('./dargs'); 13const dargs = require('./dargs');
10 14
11module.exports = function compile(psModule) { 15module.exports = function compile(psModule) {
12 const options = psModule.options 16 const options = psModule.options
13 17
14 const cache = psModule.cache
15
16 const stderr = []
17
18 const compileCommand = options.psc || 'purs'; 18 const compileCommand = options.psc || 'purs';
19 19
20 const compileArgs = (options.psc ? [] : [ 'compile' ]).concat(dargs(Object.assign({ 20 const compileArgs = (options.psc ? [] : [ 'compile' ]).concat(dargs(Object.assign({
@@ -22,7 +22,9 @@ module.exports = function compile(psModule) {
22 output: options.output, 22 output: options.output,
23 }, options.pscArgs))) 23 }, options.pscArgs)))
24 24
25 debug('spawning compiler %s %o', compileCommand, compileArgs) 25 const stderr = [];
26
27 debug('compile %s %o', compileCommand, compileArgs)
26 28
27 return new Promise((resolve, reject) => { 29 return new Promise((resolve, reject) => {
28 debug('compiling PureScript...') 30 debug('compiling PureScript...')
@@ -33,8 +35,13 @@ module.exports = function compile(psModule) {
33 stderr.push(data.toString()); 35 stderr.push(data.toString());
34 }); 36 });
35 37
38 compilation.stdout.on('data', data => {
39 debugVerbose(data.toString());
40 });
41
36 compilation.on('close', code => { 42 compilation.on('close', code => {
37 debug('finished compiling PureScript.') 43 debug('finished compiling PureScript.')
44
38 if (code !== 0) { 45 if (code !== 0) {
39 const errorMessage = stderr.join(''); 46 const errorMessage = stderr.join('');
40 if (errorMessage.length) { 47 if (errorMessage.length) {
diff --git a/src/ide.js b/src/ide.js
index f839fd5..ac80789 100644
--- a/src/ide.js
+++ b/src/ide.js
@@ -12,193 +12,71 @@ const spawn = require('cross-spawn');
12 12
13const colors = require('chalk'); 13const colors = require('chalk');
14 14
15const debug = require('debug')('purs-loader'); 15const debug_ = require('debug');
16 16
17const dargs = require('./dargs'); 17const debug = debug_('purs-loader');
18
19const Psc = require('./Psc');
20
21const PsModuleMap = require('./PsModuleMap');
22
23module.exports.connect = function connect(psModule) {
24 const options = psModule.options
25 const cache = psModule.cache
26
27 if (cache.ideServer) return Promise.resolve(psModule)
28 18
29 cache.ideServer = true 19const debugVerbose = debug_('purs-loader:verbose');
30 20
31 const connect_ = () => new Promise((resolve, reject) => { 21const dargs = require('./dargs');
32 const args = dargs(options.pscIdeArgs)
33
34 debug('attempting to run purs ide client: %o', args)
35 22
36 const ideClient = spawn('purs', ['ide', 'client'].concat(args)) 23const compile = require('./compile');
37 24
38 ideClient.stderr.on('data', data => { 25const PsModuleMap = require('./PsModuleMap');
39 debug(data.toString())
40 cache.ideServer = false
41 reject(new Error('purs ide client failed'))
42 })
43 ideClient.stdout.once('data', data => {
44 debug(data.toString())
45 if (data.toString()[0] === '{') {
46 const res = JSON.parse(data.toString())
47 if (res.resultType === 'success') {
48 cache.ideServer = ideServer
49 resolve(psModule)
50 } else {
51 cache.ideServer = ideServer
52 reject(new Error('purs ide client failed'))
53 }
54 } else {
55 cache.ideServer = false
56 reject(new Error('purs ide client failed'))
57 }
58 })
59 ideClient.stdin.resume()
60 ideClient.stdin.write(JSON.stringify({ command: 'load' }))
61 ideClient.stdin.write('\n')
62 })
63 26
64 const serverArgs = dargs(Object.assign({ 27function UnknownModuleError() {
65 outputDirectory: options.output, 28 this.name = 'UnknownModuleError';
66 '_': options.src 29 this.stack = (new Error()).stack;
67 }, options.pscIdeServerArgs)) 30}
68 31
69 debug('attempting to start purs ide server: %o', serverArgs) 32UnknownModuleError.prototype = Object.create(Error.prototype);
70 33
71 const ideServer = cache.ideServer = spawn('purs', ['ide', 'server'].concat(serverArgs)) 34UnknownModuleError.prototype.constructor = UnknownModuleError;
72 35
73 ideServer.stdout.on('data', data => { 36module.exports.UnknownModuleError = UnknownModuleError;
74 debug('purs ide server stdout: %s', data.toString());
75 });
76 37
77 ideServer.stderr.on('data', data => { 38function spawnIdeClient(body, options) {
78 debug('purs ide server stderr: %s', data.toString()); 39 const ideClientCommand = 'purs';
79 });
80 40
81 ideServer.on('error', error => { 41 const ideClientArgs = ['ide', 'client'].concat(dargs(options.pscIdeArgs));
82 debug('purs ide server error: %o', error);
83 });
84 42
85 ideServer.on('close', (code, signal) => { 43 const stderr = [];
86 debug('purs ide server close: %s %s', code, signal);
87 });
88 44
89 return retryPromise((retry, number) => { 45 const stdout = [];
90 return connect_().catch(error => {
91 if (!cache.ideServer && number === 9) {
92 debug(error)
93 46
94 console.warn('Failed to connect to or start purs ide server. A full compilation will occur on rebuild'); 47 debug('ide client %s %o %o', ideClientCommand, ideClientArgs, body);
95 48
96 return Promise.resolve(psModule) 49 return new Promise((resolve, reject) => {
97 } 50 const ideClient = spawn(ideClientCommand, ideClientArgs);
98 51
99 return retry(error) 52 ideClient.stderr.on('data', data => {
53 stderr.push(data.toString());
100 }) 54 })
101 }, {
102 retries: 9,
103 factor: 1,
104 minTimeout: 333,
105 maxTimeout: 333,
106 })
107};
108
109module.exports.rebuild = function rebuild(psModule) {
110 const options = psModule.options
111 const cache = psModule.cache
112
113 debug('attempting rebuild with purs ide client %s', psModule.srcPath)
114
115 const request = (body) => new Promise((resolve, reject) => {
116 const args = dargs(options.pscIdeArgs)
117 const ideClient = spawn('purs', ['ide', 'client'].concat(args))
118
119 var stdout = ''
120 var stderr = ''
121 55
122 ideClient.stdout.on('data', data => { 56 ideClient.stdout.on('data', data => {
123 stdout = stdout + data.toString() 57 stdout.push(data.toString());
124 })
125
126 ideClient.stderr.on('data', data => {
127 stderr = stderr + data.toString()
128 }) 58 })
129 59
130 ideClient.on('close', code => { 60 ideClient.on('close', code => {
131 if (code !== 0) { 61 if (code !== 0) {
132 const error = stderr === '' ? 'Failed to spawn purs ide client' : stderr 62 const errorMessage = stderr.join('');
133 return reject(new Error(error))
134 }
135
136 let res = null
137 63
138 try { 64 reject(new Error(`ide client failed: ${errorMessage}`));
139 res = JSON.parse(stdout.toString())
140 debug(res)
141 } catch (err) {
142 return reject(err)
143 } 65 }
66 else {
67 const result = stdout.join('');
144 68
145 if (res && !Array.isArray(res.result)) { 69 resolve(result);
146 return resolve(psModule);
147 } 70 }
148
149 Promise.map(res.result, (item, i) => {
150 debug(item)
151 return formatIdeResult(item, options, i, res.result.length)
152 })
153 .then(compileMessages => {
154 if (res.resultType === 'error') {
155 if (res.result.some(item => {
156 const isModuleNotFound = item.errorCode === 'ModuleNotFound';
157
158 const isUnknownModule = item.errorCode === 'UnknownModule';
159
160 const isUnknownModuleImport = item.errorCode === 'UnknownName' && /Unknown module/.test(item.message);
161
162 return isModuleNotFound || isUnknownModule || isUnknownModuleImport;
163 })) {
164 debug('unknown module, attempting full recompile')
165 return Psc.compile(psModule)
166 .then(() => PsModuleMap.makeMap(options.src).then(map => {
167 debug('rebuilt module map after unknown module forced a recompile');
168 cache.psModuleMap = map;
169 }))
170 .then(() => request({ command: 'load' }))
171 .then(resolve)
172 .catch(() => resolve(psModule))
173 }
174 const errorMessage = compileMessages.join('\n');
175 if (errorMessage.length) {
176 psModule.emitError(errorMessage);
177 }
178 resolve(psModule);
179 } else {
180 const warningMessage = compileMessages.join('\n');
181 if (options.warnings && warningMessage.length) {
182 psModule.emitWarning(warningMessage);
183 }
184 resolve(psModule);
185 }
186 })
187 }) 71 })
188 72
189 debug('purs ide client stdin: %o', body); 73 ideClient.stdin.resume();
190 74
191 ideClient.stdin.write(JSON.stringify(body)) 75 ideClient.stdin.write(JSON.stringify(body));
192 ideClient.stdin.write('\n')
193 })
194 76
195 return request({ 77 ideClient.stdin.write('\n');
196 command: 'rebuild', 78 });
197 params: { 79}
198 file: psModule.srcPath,
199 }
200 })
201};
202 80
203function formatIdeResult(result, options, index, length) { 81function formatIdeResult(result, options, index, length) {
204 let numAndErr = `[${index+1}/${length} ${result.errorCode}]` 82 let numAndErr = `[${index+1}/${length} ${result.errorCode}]`
@@ -253,8 +131,140 @@ function formatIdeResult(result, options, index, length) {
253 } 131 }
254 132
255 return Promise.resolve(`\n${numAndErr} ${fileAndPos}\n\n${snippet}\n\n${result.message}`) 133 return Promise.resolve(`\n${numAndErr} ${fileAndPos}\n\n${snippet}\n\n${result.message}`)
256 }) 134 }).catch(error => {
135 debug('failed to format ide result: %o', error);
136
137 return Promise.resolve('');
138 });
257 } 139 }
258 140
259 return result.filename && result.position ? makeResultSnippet(result.filename, result.position) : makeResult(); 141 return result.filename && result.position ? makeResultSnippet(result.filename, result.position) : makeResult();
260} 142}
143
144module.exports.connect = function connect(psModule) {
145 const options = psModule.options
146
147 const serverCommand = 'purs';
148
149 const serverArgs = ['ide', 'server'].concat(dargs(Object.assign({
150 outputDirectory: options.output,
151 '_': options.src
152 }, options.pscIdeServerArgs)));
153
154 debug('ide server: %s %o', serverCommand, serverArgs);
155
156 const ideServer = spawn(serverCommand, serverArgs);
157
158 ideServer.stdout.on('data', data => {
159 debugVerbose('ide server stdout: %s', data.toString());
160 });
161
162 ideServer.stderr.on('data', data => {
163 debugVerbose('ide server stderr: %s', data.toString());
164 });
165
166 ideServer.on('error', error => {
167 debugVerbose('ide server error: %o', error);
168 });
169
170 ideServer.on('close', (code, signal) => {
171 debugVerbose('ide server close: %s %s', code, signal);
172 });
173
174 return Promise.resolve(ideServer);
175};
176
177module.exports.load = function load(psModule) {
178 const options = psModule.options
179
180 const body = {command: 'load'};
181
182 return spawnIdeClient(body, options);
183};
184
185module.exports.loadWithRetry = function loadWithRetry(psModule) {
186 const retries = 9;
187
188 return retryPromise((retry, number) => {
189 debugVerbose('attempting to load modules (%d out of %d attempts)', number, retries);
190
191 return module.exports.load(psModule).catch(retry);
192 }, {
193 retries: retries,
194 factor: 1,
195 minTimeout: 333,
196 maxTimeout: 333,
197 }).then(() => psModule);
198};
199
200module.exports.rebuild = function rebuild(psModule) {
201 const options = psModule.options;
202
203 const body = {
204 command: 'rebuild',
205 params: {
206 file: psModule.srcPath,
207 }
208 };
209
210 const parseResponse = response => {
211 try {
212 const parsed = JSON.parse(response);
213
214 debugVerbose('parsed JSON response: %o', parsed);
215
216 return Promise.resolve(parsed);
217 }
218 catch (error) {
219 return Promise.reject(error);
220 }
221 };
222
223 const formatResponse = parsed => {
224 const result = Array.isArray(parsed.result) ? parsed.result : [];
225
226 return Promise.map(result, (item, i) => {
227 debugVerbose('formatting result %o', item);
228
229 return formatIdeResult(item, options, i, result.length);
230 }).then(formatted => ({
231 parsed: parsed,
232 formatted: formatted,
233 formattedMessage: formatted.join('\n')
234 }));
235 };
236
237 return spawnIdeClient(body, options)
238 .then(parseResponse)
239 .then(formatResponse)
240 .then(({ parsed, formatted, formattedMessage }) => {
241 if (parsed.resultType === 'success') {
242 if (options.warnings && formattedMessage.length) {
243 psModule.emitWarning(formattedMessage);
244 }
245
246 return psModule;
247 }
248 else if ((parsed.result || []).some(item => {
249 const isModuleNotFound = item.errorCode === 'ModuleNotFound';
250
251 const isUnknownModule = item.errorCode === 'UnknownModule';
252
253 const isUnknownModuleImport = item.errorCode === 'UnknownName' && /Unknown module/.test(item.message);
254
255 return isModuleNotFound || isUnknownModule || isUnknownModuleImport;
256 })) {
257 debug('failed to rebuild because the module is unknown')
258
259 return Promise.reject(new UnknownModuleError());
260 }
261 else {
262 if (formattedMessage.length) {
263 psModule.emitError(formattedMessage);
264 }
265
266 return psModule;
267 }
268 })
269 ;
270};
diff --git a/src/index.js b/src/index.js
index 799f8f9..7cf942c 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,6 +1,10 @@
1'use strict' 1'use strict'
2 2
3const debug = require('debug')('purs-loader') 3const debug_ = require('debug');
4
5const debug = debug_('purs-loader');
6
7const debugVerbose = debug_('purs-loader:verbose');
4 8
5const loaderUtils = require('loader-utils') 9const loaderUtils = require('loader-utils')
6 10
@@ -25,33 +29,44 @@ const spawn = require('cross-spawn').sync
25const eol = require('os').EOL 29const eol = require('os').EOL
26 30
27module.exports = function purescriptLoader(source, map) { 31module.exports = function purescriptLoader(source, map) {
28 const callback = this.async() 32 this.cacheable && this.cacheable();
29 const config = this.options 33
30 const query = loaderUtils.getOptions(this) || {} 34 const callback = this.async();
31 const webpackOptions = this.options.purescriptLoader || {} 35
36 const webpackConfig = this.options;
37
38 const loaderOptions = loaderUtils.getOptions(this) || {};
32 39
33 const depsPaths = (pscPackage => { 40 const srcOption = (pscPackage => {
34 if (pscPackage) { 41 if (pscPackage) {
35 debug('calling psc-package...') 42 const pscPackageCommand = 'psc-package';
36 43
37 return spawn('psc-package', ['sources']).stdout.toString().split(eol).filter(v => v != '') 44 const pscPackageArgs = ['sources'];
45
46 debug('psc-package %s %o', pscPackageCommand, pscPackageArgs);
47
48 return spawn(pscPackageCommand, pscPackageArgs).stdout.toString().split(eol).filter(v => v != '').concat(
49 loaderOptions.src || [
50 path.join('src', '**', '*.purs'),
51 ]
52 )
38 } 53 }
39 else { 54 else {
40 return [ path.join('bower_components', 'purescript-*', 'src', '**', '*.purs') ] 55 return loaderOptions.src || [
56 path.join('bower_components', 'purescript-*', 'src', '**', '*.purs'),
57 path.join('src', '**', '*.purs'),
58 ];
41 } 59 }
42 }) 60 })(loaderOptions.pscPackage);
43
44 let options = Object.assign(webpackOptions, query)
45 61
46 const defaultDeps = depsPaths(options.pscPackage) 62 const options = Object.assign({
47 const defaultOptions = { 63 context: webpackConfig.context,
48 context: config.context,
49 psc: null, 64 psc: null,
50 pscArgs: {}, 65 pscArgs: {},
51 pscBundle: null, 66 pscBundle: null,
52 pscBundleArgs: {}, 67 pscBundleArgs: {},
53 pscIde: false, 68 pscIde: false,
54 pscIdeColors: options.psc === 'psa', 69 pscIdeColors: loaderOptions.psc === 'psa',
55 pscIdeArgs: {}, 70 pscIdeArgs: {},
56 pscPackage: false, 71 pscPackage: false,
57 bundleOutput: 'output/bundle.js', 72 bundleOutput: 'output/bundle.js',
@@ -60,36 +75,29 @@ module.exports = function purescriptLoader(source, map) {
60 warnings: true, 75 warnings: true,
61 watch: false, 76 watch: false,
62 output: 'output', 77 output: 'output',
63 src: [ 78 src: []
64 path.join('src', '**', '*.purs'), 79 }, loaderOptions, {
65 ...defaultDeps 80 src: srcOption
66 ] 81 });
67 }
68
69 this.cacheable && this.cacheable()
70 82
71 let cache = config.purescriptLoaderCache = config.purescriptLoaderCache || { 83 var cache = webpackConfig.purescriptLoaderCache = webpackConfig.purescriptLoaderCache || {
72 rebuild: false, 84 rebuild: false,
73 deferred: [], 85 deferred: [],
74 bundleModules: [], 86 bundleModules: [],
75 warnings: [], 87 warnings: [],
76 errors: [] 88 errors: []
77 } 89 };
78
79 if (options.pscPackage && options.src) {
80 options.src = options.src.concat(defaultDeps) // append psc-package-provided source paths with users'
81 }
82 90
83 options = Object.assign(defaultOptions, options) 91 if (!webpackConfig.purescriptLoaderInstalled) {
92 debugVerbose('installing purs-loader with options: %O', options);
84 93
85 if (!config.purescriptLoaderInstalled) { 94 webpackConfig.purescriptLoaderInstalled = true
86 config.purescriptLoaderInstalled = true
87 95
88 // invalidate loader cache when bundle is marked as invalid (in watch mode) 96 // invalidate loader cache when bundle is marked as invalid (in watch mode)
89 this._compiler.plugin('invalid', () => { 97 this._compiler.plugin('invalid', () => {
90 debug('invalidating loader cache'); 98 debugVerbose('invalidating loader cache');
91 99
92 cache = config.purescriptLoaderCache = { 100 cache = webpackConfig.purescriptLoaderCache = {
93 rebuild: options.pscIde, 101 rebuild: options.pscIde,
94 deferred: [], 102 deferred: [],
95 bundleModules: [], 103 bundleModules: [],
@@ -97,7 +105,7 @@ module.exports = function purescriptLoader(source, map) {
97 psModuleMap: cache.psModuleMap, 105 psModuleMap: cache.psModuleMap,
98 warnings: [], 106 warnings: [],
99 errors: [] 107 errors: []
100 } 108 };
101 }); 109 });
102 110
103 // add psc warnings to webpack compilation warnings 111 // add psc warnings to webpack compilation warnings
@@ -114,7 +122,8 @@ module.exports = function purescriptLoader(source, map) {
114 }); 122 });
115 } 123 }
116 124
117 const psModuleName = PsModuleMap.matchModule(source) 125 const psModuleName = PsModuleMap.matchModule(source);
126
118 const psModule = { 127 const psModule = {
119 name: psModuleName, 128 name: psModuleName,
120 load: js => callback(null, js), 129 load: js => callback(null, js),
@@ -136,51 +145,132 @@ module.exports = function purescriptLoader(source, map) {
136 } 145 }
137 } 146 }
138 147
139 debug('loader called', psModule.name) 148 debug('loading %s', psModule.name);
140 149
141 if (options.bundle) { 150 if (options.bundle) {
142 cache.bundleModules.push(psModule.name) 151 cache.bundleModules.push(psModule.name);
143 } 152 }
144 153
145 if (cache.rebuild) { 154 if (cache.rebuild) {
146 return ide.connect(psModule) 155 const connect = () => {
147 .then(ide.rebuild) 156 if (!cache.ideServer) {
157 cache.ideServer = true;
158
159 return ide.connect(psModule)
160 .then(ideServer => {
161 cache.ideServer = ideServer;
162 return psModule;
163 })
164 .then(ide.loadWithRetry)
165 .catch(error => {
166 if (cache.ideServer.kill) {
167 debug('ide failed to initially load modules, stopping the ide server process');
168
169 cache.ideServer.kill();
170 }
171
172 cache.ideServer = null;
173
174 return Promise.reject(error);
175 })
176 ;
177 }
178 else {
179 return Promise.resolve(psModule);
180 }
181 };
182
183 const rebuild = () =>
184 ide.rebuild(psModule).catch(error => {
185 if (error instanceof ide.UnknownModuleError) {
186 if (!cache.compilationStarted) {
187 cache.compilationStarted = true;
188
189 return compile(psModule)
190 .then(() => {
191 cache.compilationFinished = true;
192 })
193 .then(() =>
194 PsModuleMap.makeMap(options.src).then(map => {
195 debug('rebuilt module map after unknown module forced a recompilation');
196
197 cache.psModuleMap = map;
198 })
199 )
200 .then(() => ide.load(psModule))
201 .then(() => psModule)
202 ;
203 }
204 else {
205 return Promise.resolve(psModule);
206 }
207 }
208 else {
209 debug('ide rebuild failed due to an unhandled error: %o', error);
210
211 return Promise.reject(error);
212 }
213 })
214 ;
215
216 connect()
217 .then(rebuild)
148 .then(toJavaScript) 218 .then(toJavaScript)
149 .then(psModule.load) 219 .then(psModule.load)
150 .catch(psModule.reject) 220 .catch(psModule.reject)
221 ;
151 } 222 }
223 else if (cache.compilationFinished) {
224 debugVerbose('compilation is already finished, loading module %s', psModule.name);
152 225
153 if (cache.compilationFinished) { 226 toJavaScript(psModule)
154 return toJavaScript(psModule).then(psModule.load).catch(psModule.reject) 227 .then(psModule.load)
228 .catch(psModule.reject);
155 } 229 }
156 230 else {
157 // We need to wait for compilation to finish before the loaders run so that 231 // The compilation has not finished yet. We need to wait for
158 // references to compiled output are valid. 232 // compilation to finish before the loaders run so that references
159 cache.deferred.push(psModule) 233 // to compiled output are valid. Push the modules into the cache to
160 234 // be loaded once the complation is complete.
161 if (!cache.compilationStarted) { 235
162 cache.compilationStarted = true; 236 cache.deferred.push(psModule);
163 237
164 return compile(psModule) 238 if (!cache.compilationStarted) {
165 .then(() => { 239 cache.compilationStarted = true;
166 cache.compilationFinished = true; 240
167 241 compile(psModule)
168 const bundlePromise = options.bundle ? bundle(options, cache) : Promise.resolve(); 242 .then(() => {
169 243 cache.compilationFinished = true;
170 return bundlePromise.then(() => 244 })
245 .then(() => {
246 if (options.bundle) {
247 return bundle(options, cache.bundleModules);
248 }
249 })
250 .then(() =>
171 PsModuleMap.makeMap(options.src).then(map => { 251 PsModuleMap.makeMap(options.src).then(map => {
172 debug('rebuilt module map after compile'); 252 debug('rebuilt module map after compilation');
253
173 cache.psModuleMap = map; 254 cache.psModuleMap = map;
174 }) 255 })
175 ); 256 )
176 }) 257 .then(() =>
177 .then(() => Promise.map(cache.deferred, psModule => { 258 Promise.map(cache.deferred, psModule =>
178 if (typeof cache.ideServer === 'object') cache.ideServer.kill() 259 toJavaScript(psModule).then(psModule.load)
179 return toJavaScript(psModule).then(psModule.load) 260 )
180 })) 261 )
181 .catch(error => { 262 .catch(error => {
182 cache.deferred[0].reject(error) 263 cache.deferred[0].reject(error);
183 cache.deferred.slice(1).forEach(psModule => psModule.reject(new Error('purs-loader failed'))) 264
184 }) 265 cache.deferred.slice(1).forEach(psModule => {
266 psModule.reject(new Error('purs-loader failed'));
267 })
268 })
269 ;
270 }
271 else {
272 // The complation has started. Nothing to do but wait until it is
273 // done before loading all of the modules.
274 }
185 } 275 }
186} 276}
diff --git a/src/to-javascript.js b/src/to-javascript.js
index b402ad4..d0934d5 100644
--- a/src/to-javascript.js
+++ b/src/to-javascript.js
@@ -10,7 +10,11 @@ const jsStringEscape = require('js-string-escape');
10 10
11const difference = require('lodash.difference'); 11const difference = require('lodash.difference');
12 12
13const debug = require('debug')('purs-loader'); 13const debug_ = require('debug');
14
15const debug = debug_('purs-loader');
16
17const debugVerbose = debug_('purs-loader:verbose');
14 18
15const PsModuleMap = require('./PsModuleMap'); 19const PsModuleMap = require('./PsModuleMap');
16 20
@@ -99,7 +103,7 @@ function makeJS(psModule, psModuleMap, js) {
99 const additionalImports = difference(imports, replacedImports); 103 const additionalImports = difference(imports, replacedImports);
100 104
101 if (additionalImports.length) { 105 if (additionalImports.length) {
102 debug('additional imports for %s: %o', name, additionalImports); 106 debugVerbose('additional imports for %s: %o', name, additionalImports);
103 } 107 }
104 108
105 const additionalImportsResult = additionalImports.map(import_ => { 109 const additionalImportsResult = additionalImports.map(import_ => {
@@ -129,13 +133,13 @@ module.exports = function toJavaScript(psModule) {
129 133
130 const bundlePath = path.resolve(options.bundleOutput); 134 const bundlePath = path.resolve(options.bundleOutput);
131 135
132 const jsPath = cache.bundle ? bundlePath : psModule.jsPath; 136 const jsPath = options.bundle ? bundlePath : psModule.jsPath;
133 137
134 const js = fs.readFileAsync(jsPath, 'utf8').catch(() => ''); 138 const js = fs.readFileAsync(jsPath, 'utf8').catch(() => '');
135 139
136 const psModuleMap = updatePsModuleMap(psModule); 140 const psModuleMap = updatePsModuleMap(psModule);
137 141
138 debug('loading JavaScript for %s', psModule.name); 142 debugVerbose('loading JavaScript for %s', psModule.name);
139 143
140 return Promise.props({js: js, psModuleMap: psModuleMap}).then(result => 144 return Promise.props({js: js, psModuleMap: psModuleMap}).then(result =>
141 options.bundle ? 145 options.bundle ?