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 dargs
= require('./dargs');
27 const spawn
= require('cross-spawn').sync
29 const eol
= require('os').EOL
31 module
.exports
= function purescriptLoader(source
, map
) {
32 this.cacheable
&& this.cacheable();
34 const callback
= this.async();
36 const webpackConfig
= this.options
;
38 const loaderOptions
= loaderUtils
.getOptions(this) || {};
40 const srcOption
= (pscPackage
=> {
42 const pscPackageCommand
= 'psc-package';
44 const pscPackageArgs
= ['sources'];
46 debug('psc-package %s %o', pscPackageCommand
, pscPackageArgs
);
48 return spawn(pscPackageCommand
, pscPackageArgs
).stdout
.toString().split(eol
).filter(v
=> v
!= '').concat(
49 loaderOptions
.src
|| [
50 path
.join('src', '**', '*.purs'),
55 return loaderOptions
.src
|| [
56 path
.join('bower_components', 'purescript-*', 'src', '**', '*.purs'),
57 path
.join('src', '**', '*.purs'),
60 })(loaderOptions
.pscPackage
);
62 const options
= Object
.assign({
63 context: webpackConfig
.context
,
69 pscIdeColors: loaderOptions
.psc
=== 'psa',
72 bundleOutput: 'output/bundle.js',
73 bundleNamespace: 'PS',
83 var cache
= webpackConfig
.purescriptLoaderCache
= webpackConfig
.purescriptLoaderCache
|| {
91 if (!webpackConfig
.purescriptLoaderInstalled
) {
92 debugVerbose('installing purs-loader with options: %O', options
);
94 webpackConfig
.purescriptLoaderInstalled
= true
96 // invalidate loader cache when bundle is marked as invalid (in watch mode)
97 this._compiler
.plugin('invalid', () => {
98 debugVerbose('invalidating loader cache');
100 cache
= webpackConfig
.purescriptLoaderCache
= {
101 rebuild: options
.pscIde
,
104 ideServer: cache
.ideServer
,
105 psModuleMap: cache
.psModuleMap
,
111 // add psc warnings to webpack compilation warnings
112 this._compiler
.plugin('after-compile', (compilation
, callback
) => {
113 cache
.warnings
.forEach(warning
=> {
114 compilation
.warnings
.push(warning
);
117 cache
.errors
.forEach(error
=> {
118 compilation
.errors
.push(error
);
125 const psModuleName
= PsModuleMap
.matchModule(source
);
129 load: js
=> callback(null, js
),
130 reject: error
=> callback(error
),
131 srcPath: this.resourcePath
,
132 srcDir: path
.dirname(this.resourcePath
),
133 jsPath: path
.resolve(path
.join(options
.output
, psModuleName
, 'index.js')),
136 emitWarning: warning
=> {
137 if (options
.warnings
&& warning
.length
) {
138 cache
.warnings
.push(warning
);
141 emitError: error
=> {
143 cache
.errors
.push(error
);
148 debug('loading %s', psModule
.name
);
150 if (options
.bundle
) {
151 cache
.bundleModules
.push(psModule
.name
);
155 const connect
= () => {
156 if (!cache
.ideServer
) {
157 cache
.ideServer
= true;
159 return ide
.connect(psModule
)
161 cache
.ideServer
= ideServer
;
164 .then(ide
.loadWithRetry
)
166 if (cache
.ideServer
.kill
) {
167 debug('ide failed to initially load modules, stopping the ide server process');
169 cache
.ideServer
.kill();
172 cache
.ideServer
= null;
174 return Promise
.reject(error
);
179 return Promise
.resolve(psModule
);
183 const rebuild
= () =>
184 ide
.rebuild(psModule
).catch(error
=> {
185 if (error
instanceof ide
.UnknownModuleError
) {
186 if (!cache
.compilationStarted
) {
187 cache
.compilationStarted
= true;
189 return compile(psModule
)
191 cache
.compilationFinished
= true;
194 PsModuleMap
.makeMap(options
.src
).then(map
=> {
195 debug('rebuilt module map after unknown module forced a recompilation');
197 cache
.psModuleMap
= map
;
200 .then(() => ide
.load(psModule
))
201 .then(() => psModule
)
205 return Promise
.resolve(psModule
);
209 debug('ide rebuild failed due to an unhandled error: %o', error
);
211 return Promise
.reject(error
);
220 .catch(psModule
.reject
)
223 else if (cache
.compilationFinished
) {
224 debugVerbose('compilation is already finished, loading module %s', psModule
.name
);
226 toJavaScript(psModule
)
228 .catch(psModule
.reject
);
231 // The compilation has not finished yet. We need to wait for
232 // compilation to finish before the loaders run so that references
233 // to compiled output are valid. Push the modules into the cache to
234 // be loaded once the complation is complete.
236 cache
.deferred
.push(psModule
);
238 if (!cache
.compilationStarted
) {
239 cache
.compilationStarted
= true;
243 cache
.compilationFinished
= true;
246 if (options
.bundle
) {
247 return bundle(options
, cache
.bundleModules
);
251 PsModuleMap
.makeMap(options
.src
).then(map
=> {
252 debug('rebuilt module map after compilation');
254 cache
.psModuleMap
= map
;
258 Promise
.map(cache
.deferred
, psModule
=>
259 toJavaScript(psModule
).then(psModule
.load
)
263 cache
.deferred
[0].reject(error
);
265 cache
.deferred
.slice(1).forEach(psModule
=> {
266 psModule
.reject(new Error('purs-loader failed'));
272 // The complation has started. Nothing to do but wait until it is
273 // done before loading all of the modules.