]> git.immae.eu Git - github/fretlink/purs-loader.git/blame - src/index.js
Add options for ide commands
[github/fretlink/purs-loader.git] / src / index.js
CommitLineData
7de41f10
AM
1'use strict'
2
7f0547d4 3const debug_ = require('debug');
4
5const debug = debug_('purs-loader');
6
7const debugVerbose = debug_('purs-loader:verbose');
1c12889c 8
7de41f10 9const loaderUtils = require('loader-utils')
1c12889c 10
7de41f10 11const Promise = require('bluebird')
1c12889c 12
7de41f10 13const path = require('path')
1c12889c 14
15const PsModuleMap = require('./purs-module-map');
16
17const compile = require('./compile');
18
19const bundle = require('./bundle');
20
21const ide = require('./ide');
22
8e21ab0a 23const toJavaScript = require('./to-javascript');
1c12889c 24
531c751f 25const dargs = require('./dargs');
1c12889c 26
86e2b3d4 27const spawn = require('cross-spawn').sync
1c12889c 28
86e2b3d4 29const eol = require('os').EOL
7de41f10 30
7de41f10 31module.exports = function purescriptLoader(source, map) {
7f0547d4 32 this.cacheable && this.cacheable();
33
7f0547d4 34 const webpackConfig = this.options;
35
e1719658 36 var cache = webpackConfig.purescriptLoaderCache = webpackConfig.purescriptLoaderCache || {
37 rebuild: false,
38 deferred: [],
39 bundleModules: [],
40 ideServer: null,
41 psModuleMap: null,
42 warnings: [],
43 errors: [],
44 compilationStarted: false,
45 compilationFinished: false,
46 installed: false,
47 srcOption: []
48 };
49
50 const callback = this.async();
51
7f0547d4 52 const loaderOptions = loaderUtils.getOptions(this) || {};
7de41f10 53
7f0547d4 54 const srcOption = (pscPackage => {
e1719658 55 const srcPath = path.join('src', '**', '*.purs');
56
57 const bowerPath = path.join('bower_components', 'purescript-*', 'src', '**', '*.purs');
58
59 if (cache.srcOption.length > 0) {
60 return cache.srcOption;
61 }
62 else if (pscPackage) {
7f0547d4 63 const pscPackageCommand = 'psc-package';
86e2b3d4 64
7f0547d4 65 const pscPackageArgs = ['sources'];
66
e1719658 67 const loaderSrc = loaderOptions.src || [
68 srcPath
69 ];
70
7f0547d4 71 debug('psc-package %s %o', pscPackageCommand, pscPackageArgs);
72
e1719658 73 const cmd = spawn(pscPackageCommand, pscPackageArgs);
74
6ccb09a5 75 if (cmd.error) {
76 throw new Error(cmd.error);
77 }
78 else if (cmd.status !== 0) {
e1719658 79 const error = cmd.stdout.toString();
80
81 throw new Error(error);
82 }
83 else {
84 const result = cmd.stdout.toString().split(eol).filter(v => v != '').concat(loaderSrc);
85
86 debug('psc-package result: %o', result);
87
88 cache.srcOption = result;
89
90 return result;
91 }
86e2b3d4
AD
92 }
93 else {
e1719658 94 const result = loaderOptions.src || [
95 bowerPath,
96 srcPath
7f0547d4 97 ];
e1719658 98
99 cache.srcOption = result;
100
101 return result;
86e2b3d4 102 }
7f0547d4 103 })(loaderOptions.pscPackage);
86e2b3d4 104
7f0547d4 105 const options = Object.assign({
106 context: webpackConfig.context,
1c12889c 107 psc: null,
7de41f10 108 pscArgs: {},
1c12889c 109 pscBundle: null,
7de41f10 110 pscBundleArgs: {},
71a96808 111 pscIdeClient: null,
112 pscIdeClientArgs: {},
113 pscIdeServer: null,
114 pscIdeServerArgs: {},
5163b5a2 115 pscIde: false,
7f0547d4 116 pscIdeColors: loaderOptions.psc === 'psa',
86e2b3d4 117 pscPackage: false,
7de41f10
AM
118 bundleOutput: 'output/bundle.js',
119 bundleNamespace: 'PS',
120 bundle: false,
121 warnings: true,
df8798fa 122 watch: false,
7de41f10 123 output: 'output',
7f0547d4 124 src: []
125 }, loaderOptions, {
126 src: srcOption
127 });
7de41f10 128
e1719658 129 if (!cache.installed) {
7f0547d4 130 debugVerbose('installing purs-loader with options: %O', options);
86e2b3d4 131
e1719658 132 cache.installed = true;
7de41f10
AM
133
134 // invalidate loader cache when bundle is marked as invalid (in watch mode)
135 this._compiler.plugin('invalid', () => {
7f0547d4 136 debugVerbose('invalidating loader cache');
531c751f 137
7f0547d4 138 cache = webpackConfig.purescriptLoaderCache = {
5163b5a2 139 rebuild: options.pscIde,
7de41f10 140 deferred: [],
531c751f 141 bundleModules: [],
142 ideServer: cache.ideServer,
b683b0b1 143 psModuleMap: cache.psModuleMap,
144 warnings: [],
e1719658 145 errors: [],
78e2b0d9 146 compilationStarted: false,
147 compilationFinished: false,
e1719658 148 installed: cache.installed,
6ccb09a5 149 srcOption: []
7f0547d4 150 };
b683b0b1 151 });
152
153 // add psc warnings to webpack compilation warnings
154 this._compiler.plugin('after-compile', (compilation, callback) => {
155 cache.warnings.forEach(warning => {
156 compilation.warnings.push(warning);
157 });
158
159 cache.errors.forEach(error => {
160 compilation.errors.push(error);
161 });
162
163 callback()
164 });
7de41f10
AM
165 }
166
7f0547d4 167 const psModuleName = PsModuleMap.matchModule(source);
168
7de41f10
AM
169 const psModule = {
170 name: psModuleName,
171 load: js => callback(null, js),
172 reject: error => callback(error),
173 srcPath: this.resourcePath,
174 srcDir: path.dirname(this.resourcePath),
175 jsPath: path.resolve(path.join(options.output, psModuleName, 'index.js')),
176 options: options,
177 cache: cache,
b683b0b1 178 emitWarning: warning => {
179 if (options.warnings && warning.length) {
180 cache.warnings.push(warning);
181 }
182 },
183 emitError: error => {
184 if (error.length) {
185 cache.errors.push(error);
186 }
187 }
7de41f10
AM
188 }
189
7f0547d4 190 debug('loading %s', psModule.name);
67888496 191
7de41f10 192 if (options.bundle) {
7f0547d4 193 cache.bundleModules.push(psModule.name);
7de41f10
AM
194 }
195
196 if (cache.rebuild) {
7f0547d4 197 const connect = () => {
198 if (!cache.ideServer) {
199 cache.ideServer = true;
200
201 return ide.connect(psModule)
202 .then(ideServer => {
203 cache.ideServer = ideServer;
204 return psModule;
205 })
206 .then(ide.loadWithRetry)
207 .catch(error => {
208 if (cache.ideServer.kill) {
209 debug('ide failed to initially load modules, stopping the ide server process');
210
211 cache.ideServer.kill();
212 }
213
214 cache.ideServer = null;
215
216 return Promise.reject(error);
217 })
218 ;
219 }
220 else {
221 return Promise.resolve(psModule);
222 }
223 };
224
225 const rebuild = () =>
78e2b0d9 226 ide.rebuild(psModule)
227 .then(() =>
228 toJavaScript(psModule)
229 .then(psModule.load)
230 .catch(psModule.reject)
231 )
232 .catch(error => {
7f0547d4 233 if (error instanceof ide.UnknownModuleError) {
78e2b0d9 234 // Store the modules that trigger a recompile due to an
235 // unknown module error. We need to wait until compilation is
236 // done before loading these files.
237
238 cache.deferred.push(psModule);
239
7f0547d4 240 if (!cache.compilationStarted) {
241 cache.compilationStarted = true;
242
243 return compile(psModule)
244 .then(() => {
245 cache.compilationFinished = true;
246 })
247 .then(() =>
248 PsModuleMap.makeMap(options.src).then(map => {
249 debug('rebuilt module map after unknown module forced a recompilation');
250
251 cache.psModuleMap = map;
252 })
253 )
78e2b0d9 254 .then(() =>
255 Promise.map(cache.deferred, psModule =>
256 ide.load(psModule)
257 .then(() => toJavaScript(psModule))
258 .then(psModule.load)
259 )
260 )
261 .catch(error => {
262 cache.deferred[0].reject(error);
263
264 cache.deferred.slice(1).forEach(psModule => {
265 psModule.reject(new Error('purs-loader failed'));
266 })
267 })
7f0547d4 268 ;
269 }
270 else {
78e2b0d9 271 // The compilation has started. We must wait until it is
272 // done in order to ensure the module map contains all of
273 // the unknown modules.
7f0547d4 274 }
275 }
276 else {
277 debug('ide rebuild failed due to an unhandled error: %o', error);
278
78e2b0d9 279 psModule.reject(error);
7f0547d4 280 }
281 })
282 ;
283
78e2b0d9 284 connect().then(rebuild);
7de41f10 285 }
7f0547d4 286 else if (cache.compilationFinished) {
287 debugVerbose('compilation is already finished, loading module %s', psModule.name);
7de41f10 288
7f0547d4 289 toJavaScript(psModule)
290 .then(psModule.load)
291 .catch(psModule.reject);
7de41f10 292 }
7f0547d4 293 else {
294 // The compilation has not finished yet. We need to wait for
295 // compilation to finish before the loaders run so that references
296 // to compiled output are valid. Push the modules into the cache to
297 // be loaded once the complation is complete.
298
299 cache.deferred.push(psModule);
300
301 if (!cache.compilationStarted) {
302 cache.compilationStarted = true;
303
304 compile(psModule)
305 .then(() => {
306 cache.compilationFinished = true;
307 })
308 .then(() => {
309 if (options.bundle) {
310 return bundle(options, cache.bundleModules);
311 }
312 })
313 .then(() =>
1c12889c 314 PsModuleMap.makeMap(options.src).then(map => {
7f0547d4 315 debug('rebuilt module map after compilation');
316
1c12889c 317 cache.psModuleMap = map;
318 })
7f0547d4 319 )
320 .then(() =>
321 Promise.map(cache.deferred, psModule =>
78e2b0d9 322 toJavaScript(psModule)
323 .then(psModule.load)
7f0547d4 324 )
325 )
326 .catch(error => {
327 cache.deferred[0].reject(error);
328
329 cache.deferred.slice(1).forEach(psModule => {
330 psModule.reject(new Error('purs-loader failed'));
331 })
332 })
333 ;
334 }
335 else {
336 // The complation has started. Nothing to do but wait until it is
337 // done before loading all of the modules.
338 }
7de41f10
AM
339 }
340}