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