]>
git.immae.eu Git - github/fretlink/purs-loader.git/blob - src/index.js
3 const debug_
= require('debug');
5 const debug
= debug_('purs-loader');
7 const debugVerbose
= debug_('purs-loader:verbose');
9 const loaderUtils
= require('loader-utils')
11 const Promise
= require('bluebird')
13 const path
= require('path')
15 const PsModuleMap
= require('./purs-module-map');
17 const compile
= require('./compile');
19 const bundle
= require('./bundle');
21 const ide
= require('./ide');
23 const toJavaScript
= require('./to-javascript');
25 const sourceMaps
= require('./source-maps');
27 const spawn
= require('cross-spawn').sync
29 const eol
= require('os').EOL
39 compilationStarted: false,
40 compilationFinished: false,
46 // include src files provided by psc-package or Spago
47 function requestDependencySources(packagerCommand
, srcPath
, loaderOptions
) {
48 const packagerArgs
= ['sources'];
50 const loaderSrc
= loaderOptions
.src
|| [
54 debug('%s %o', packagerCommand
, packagerArgs
);
56 const cmd
= spawn(packagerCommand
, packagerArgs
);
59 throw new Error(cmd
.error
);
61 else if (cmd
.status
!== 0) {
62 const error
= cmd
.stdout
.toString();
64 throw new Error(error
);
67 const result
= cmd
.stdout
.toString().split(eol
).filter(v
=> v
!= '').concat(loaderSrc
);
69 debug('%s result: %o', packagerCommand
, result
);
71 CACHE_VAR
.srcOption
= result
;
77 // 'spago output path' will return the output folder in a monorepo
78 function getSpagoSources() {
79 const cachedVal
= CACHE_VAR
.spagoOutputPath
;
83 const command
= "spago"
84 const args
= ["path", "output"]
86 const cmd
= spawn(command
, args
);
89 throw new Error(cmd
.error
);
91 else if (cmd
.status
!== 0) {
92 const error
= cmd
.stdout
.toString();
94 throw new Error(error
);
97 const result
= cmd
.stdout
.toString().split(eol
)[0]
99 debug('"spago path output" result: %o', result
);
101 CACHE_VAR
.spagoOutputPath
= result
;
107 module
.exports
= function purescriptLoader(source
, map
) {
108 this.cacheable
&& this.cacheable();
110 const webpackContext
= (this.options
&& this.options
.context
) || this.rootContext
;
112 const callback
= this.async();
114 const loaderOptions
= loaderUtils
.getOptions(this) || {};
116 const srcOption
= ((pscPackage
, spago
) => {
117 const srcPath
= path
.join('src', '**', '*.purs');
119 const bowerPath
= path
.join('bower_components', 'purescript-*', 'src', '**', '*.purs');
121 if (CACHE_VAR
.srcOption
.length
> 0) {
122 return CACHE_VAR
.srcOption
;
124 else if (pscPackage
) {
125 return requestDependencySources('psc-package', srcPath
, loaderOptions
)
128 return requestDependencySources('spago', srcPath
, loaderOptions
)
131 const result
= loaderOptions
.src
|| [
136 CACHE_VAR
.srcOption
= result
;
140 })(loaderOptions
.pscPackage
, loaderOptions
.spago
);
142 const outputPath
= loaderOptions
.spago
? getSpagoSources() : 'output'
144 const options
= Object
.assign({
145 context: webpackContext
,
151 pscIdeClientArgs: {},
153 pscIdeServerArgs: {},
154 pscIdeRebuildArgs: {},
156 pscIdeColors: loaderOptions
.psc
=== 'psa',
159 bundleOutput: 'output/bundle.js',
160 bundleNamespace: 'PS',
170 if (!CACHE_VAR
.installed
) {
171 debugVerbose('installing purs-loader with options: %O', options
);
173 CACHE_VAR
.installed
= true;
175 const invalidCb
= () => {
176 debugVerbose('invalidating loader CACHE_VAR');
179 rebuild: options
.pscIde
,
182 ideServer: CACHE_VAR
.ideServer
,
183 psModuleMap: CACHE_VAR
.psModuleMap
,
186 compilationStarted: false,
187 compilationFinished: false,
188 installed: CACHE_VAR
.installed
,
193 // invalidate loader CACHE_VAR when bundle is marked as invalid (in watch mode)
194 if(this._compiler
.hooks
){
195 this._compiler
.hooks
.invalid
.tap('purs-loader', invalidCb
);
197 this._compiler
.plugin('invalid', invalidCb
);
200 const afterCompileCb
= (compilation
, callback
) => {
201 CACHE_VAR
.warnings
.forEach(warning
=> {
202 compilation
.warnings
.push(warning
);
205 CACHE_VAR
.errors
.forEach(error
=> {
206 compilation
.errors
.push(error
);
212 // add psc warnings to webpack compilation warnings
213 if(this._compiler
.hooks
) {
214 this._compiler
.hooks
.afterCompile
.tapAsync('purs-loader', afterCompileCb
);
216 this._compiler
.plugin('after-compile', afterCompileCb
);
220 const psModuleName
= PsModuleMap
.matchModule(source
);
225 load: ({js
, map
}) => callback(null, js
, map
),
226 reject: error
=> callback(error
),
227 srcPath: this.resourcePath
,
228 remainingRequest: loaderUtils
.getRemainingRequest(this),
229 srcDir: path
.dirname(this.resourcePath
),
230 jsPath: path
.resolve(path
.join(options
.output
, psModuleName
, 'index.js')),
233 emitWarning: warning
=> {
234 if (options
.warnings
&& warning
.length
) {
235 CACHE_VAR
.warnings
.push(warning
);
238 emitError: error
=> {
240 CACHE_VAR
.errors
.push(error
);
245 debug('loading %s', psModule
.name
);
247 if (options
.bundle
) {
248 CACHE_VAR
.bundleModules
.push(psModule
.name
);
251 if (CACHE_VAR
.rebuild
) {
252 const connect
= () => {
253 if (!CACHE_VAR
.ideServer
) {
254 CACHE_VAR
.ideServer
= true;
256 return ide
.connect(psModule
)
258 CACHE_VAR
.ideServer
= ideServer
;
261 .then(ide
.loadWithRetry
)
263 if (CACHE_VAR
.ideServer
.kill
) {
264 debug('ide failed to initially load modules, stopping the ide server process');
266 CACHE_VAR
.ideServer
.kill();
269 CACHE_VAR
.ideServer
= null;
271 return Promise
.reject(error
);
276 return Promise
.resolve(psModule
);
280 const rebuild
= () =>
281 ide
.rebuild(psModule
)
283 toJavaScript(psModule
)
284 .then(js
=> sourceMaps(psModule
, js
))
286 .catch(psModule
.reject
)
289 if (error
instanceof ide
.UnknownModuleError
) {
290 // Store the modules that trigger a recompile due to an
291 // unknown module error. We need to wait until compilation is
292 // done before loading these files.
294 CACHE_VAR
.deferred
.push(psModule
);
296 if (!CACHE_VAR
.compilationStarted
) {
297 CACHE_VAR
.compilationStarted
= true;
299 return compile(psModule
)
301 CACHE_VAR
.compilationFinished
= true;
304 Promise
.map(CACHE_VAR
.deferred
, psModule
=>
306 .then(() => toJavaScript(psModule
))
307 .then(js
=> sourceMaps(psModule
, js
))
312 CACHE_VAR
.deferred
[0].reject(error
);
314 CACHE_VAR
.deferred
.slice(1).forEach(psModule
=> {
315 psModule
.reject(new Error('purs-loader failed'));
321 // The compilation has started. We must wait until it is
322 // done in order to ensure the module map contains all of
323 // the unknown modules.
327 debug('ide rebuild failed due to an unhandled error: %o', error
);
329 psModule
.reject(error
);
334 connect().then(rebuild
);
336 else if (CACHE_VAR
.compilationFinished
) {
337 debugVerbose('compilation is already finished, loading module %s', psModule
.name
);
339 toJavaScript(psModule
)
340 .then(js
=> sourceMaps(psModule
, js
))
342 .catch(psModule
.reject
);
345 // The compilation has not finished yet. We need to wait for
346 // compilation to finish before the loaders run so that references
347 // to compiled output are valid. Push the modules into the CACHE_VAR to
348 // be loaded once the complation is complete.
350 CACHE_VAR
.deferred
.push(psModule
);
352 if (!CACHE_VAR
.compilationStarted
) {
353 CACHE_VAR
.compilationStarted
= true;
357 CACHE_VAR
.compilationFinished
= true;
360 if (options
.bundle
) {
361 return bundle(options
, CACHE_VAR
.bundleModules
);
365 Promise
.map(CACHE_VAR
.deferred
, psModule
=>
366 toJavaScript(psModule
)
367 .then(js
=> sourceMaps(psModule
, js
))
372 CACHE_VAR
.deferred
[0].reject(error
);
374 CACHE_VAR
.deferred
.slice(1).forEach(psModule
=> {
375 psModule
.reject(new Error('purs-loader failed'));
381 // The complation has started. Nothing to do but wait until it is
382 // done before loading all of the modules.