]>
Commit | Line | Data |
---|---|---|
7de41f10 AM |
1 | 'use strict' |
2 | ||
7de41f10 AM |
3 | const debug = require('debug')('purs-loader') |
4 | const loaderUtils = require('loader-utils') | |
7de41f10 | 5 | const Promise = require('bluebird') |
7de41f10 | 6 | const path = require('path') |
531c751f | 7 | const PsModuleMap = require('./PsModuleMap'); |
8 | const Psc = require('./Psc'); | |
9 | const PscIde = require('./PscIde'); | |
8e21ab0a | 10 | const toJavaScript = require('./to-javascript'); |
531c751f | 11 | const dargs = require('./dargs'); |
86e2b3d4 AD |
12 | const spawn = require('cross-spawn').sync |
13 | const eol = require('os').EOL | |
7de41f10 | 14 | |
7de41f10 AM |
15 | module.exports = function purescriptLoader(source, map) { |
16 | const callback = this.async() | |
17 | const config = this.options | |
18 | const query = loaderUtils.parseQuery(this.query) | |
19 | const webpackOptions = this.options.purescriptLoader || {} | |
20 | ||
86e2b3d4 AD |
21 | const depsPaths = (pscPackage => { |
22 | if (pscPackage) { | |
23 | debug('calling psc-package...') | |
24 | ||
25 | return spawn('psc-package', ['sources']).stdout.toString().split(eol).filter(v => v != '') | |
26 | } | |
27 | else { | |
28 | return [ path.join('bower_components', 'purescript-*', 'src', '**', '*.purs') ] | |
29 | } | |
30 | }) | |
31 | ||
32 | let options = Object.assign(webpackOptions, query) | |
33 | ||
34 | const defaultDeps = depsPaths(options.pscPackage) | |
35 | const defaultOptions = { | |
7de41f10 AM |
36 | context: config.context, |
37 | psc: 'psc', | |
38 | pscArgs: {}, | |
39 | pscBundle: 'psc-bundle', | |
40 | pscBundleArgs: {}, | |
5163b5a2 | 41 | pscIde: false, |
86e2b3d4 | 42 | pscIdeColors: options.psc === 'psa', |
7de41f10 | 43 | pscIdeArgs: {}, |
86e2b3d4 | 44 | pscPackage: false, |
7de41f10 AM |
45 | bundleOutput: 'output/bundle.js', |
46 | bundleNamespace: 'PS', | |
47 | bundle: false, | |
48 | warnings: true, | |
df8798fa | 49 | watch: false, |
7de41f10 AM |
50 | output: 'output', |
51 | src: [ | |
52 | path.join('src', '**', '*.purs'), | |
86e2b3d4 | 53 | ...defaultDeps |
2b2207bd | 54 | ] |
86e2b3d4 | 55 | } |
7de41f10 AM |
56 | |
57 | this.cacheable && this.cacheable() | |
58 | ||
59 | let cache = config.purescriptLoaderCache = config.purescriptLoaderCache || { | |
60 | rebuild: false, | |
61 | deferred: [], | |
b683b0b1 | 62 | bundleModules: [], |
63 | warnings: [], | |
64 | errors: [] | |
7de41f10 AM |
65 | } |
66 | ||
86e2b3d4 AD |
67 | if (options.pscPackage && options.src) { |
68 | options.src = options.src.concat(defaultDeps) // append psc-package-provided source paths with users' | |
69 | } | |
70 | ||
71 | options = Object.assign(defaultOptions, options) | |
72 | ||
7de41f10 AM |
73 | if (!config.purescriptLoaderInstalled) { |
74 | config.purescriptLoaderInstalled = true | |
75 | ||
76 | // invalidate loader cache when bundle is marked as invalid (in watch mode) | |
77 | this._compiler.plugin('invalid', () => { | |
531c751f | 78 | debug('invalidating loader cache'); |
79 | ||
7de41f10 | 80 | cache = config.purescriptLoaderCache = { |
5163b5a2 | 81 | rebuild: options.pscIde, |
7de41f10 | 82 | deferred: [], |
531c751f | 83 | bundleModules: [], |
84 | ideServer: cache.ideServer, | |
b683b0b1 | 85 | psModuleMap: cache.psModuleMap, |
86 | warnings: [], | |
87 | errors: [] | |
7de41f10 | 88 | } |
b683b0b1 | 89 | }); |
90 | ||
91 | // add psc warnings to webpack compilation warnings | |
92 | this._compiler.plugin('after-compile', (compilation, callback) => { | |
93 | cache.warnings.forEach(warning => { | |
94 | compilation.warnings.push(warning); | |
95 | }); | |
96 | ||
97 | cache.errors.forEach(error => { | |
98 | compilation.errors.push(error); | |
99 | }); | |
100 | ||
101 | callback() | |
102 | }); | |
7de41f10 AM |
103 | } |
104 | ||
531c751f | 105 | const psModuleName = PsModuleMap.match(source) |
7de41f10 AM |
106 | const psModule = { |
107 | name: psModuleName, | |
108 | load: js => callback(null, js), | |
109 | reject: error => callback(error), | |
110 | srcPath: this.resourcePath, | |
111 | srcDir: path.dirname(this.resourcePath), | |
112 | jsPath: path.resolve(path.join(options.output, psModuleName, 'index.js')), | |
113 | options: options, | |
114 | cache: cache, | |
b683b0b1 | 115 | emitWarning: warning => { |
116 | if (options.warnings && warning.length) { | |
117 | cache.warnings.push(warning); | |
118 | } | |
119 | }, | |
120 | emitError: error => { | |
121 | if (error.length) { | |
122 | cache.errors.push(error); | |
123 | } | |
124 | } | |
7de41f10 AM |
125 | } |
126 | ||
67888496 AM |
127 | debug('loader called', psModule.name) |
128 | ||
7de41f10 AM |
129 | if (options.bundle) { |
130 | cache.bundleModules.push(psModule.name) | |
131 | } | |
132 | ||
133 | if (cache.rebuild) { | |
531c751f | 134 | return PscIde.connect(psModule) |
135 | .then(PscIde.rebuild) | |
7de41f10 AM |
136 | .then(toJavaScript) |
137 | .then(psModule.load) | |
138 | .catch(psModule.reject) | |
139 | } | |
140 | ||
67888496 | 141 | if (cache.compilationFinished) { |
7de41f10 AM |
142 | return toJavaScript(psModule).then(psModule.load).catch(psModule.reject) |
143 | } | |
144 | ||
145 | // We need to wait for compilation to finish before the loaders run so that | |
146 | // references to compiled output are valid. | |
147 | cache.deferred.push(psModule) | |
148 | ||
67888496 | 149 | if (!cache.compilationStarted) { |
531c751f | 150 | return Psc.compile(psModule) |
151 | .then(() => PsModuleMap.makeMap(options.src).then(map => { | |
4305f5b0 | 152 | debug('rebuilt module map after compile'); |
531c751f | 153 | cache.psModuleMap = map; |
154 | })) | |
7de41f10 AM |
155 | .then(() => Promise.map(cache.deferred, psModule => { |
156 | if (typeof cache.ideServer === 'object') cache.ideServer.kill() | |
157 | return toJavaScript(psModule).then(psModule.load) | |
158 | })) | |
159 | .catch(error => { | |
160 | cache.deferred[0].reject(error) | |
4b99e432 | 161 | cache.deferred.slice(1).forEach(psModule => psModule.reject(new Error('purs-loader failed'))) |
7de41f10 AM |
162 | }) |
163 | } | |
164 | } |