]> git.immae.eu Git - github/fretlink/purs-loader.git/blob - lib/to-javascript.js
Build v4.2.0
[github/fretlink/purs-loader.git] / lib / to-javascript.js
1 'use strict';
2
3 var Promise = require('bluebird');
4
5 var fs = Promise.promisifyAll(require('fs'));
6
7 var path = require('path');
8
9 var jsStringEscape = require('js-string-escape');
10
11 var difference = require('lodash.difference');
12
13 var debug_ = require('debug');
14
15 var debug = debug_('purs-loader');
16
17 var debugVerbose = debug_('purs-loader:verbose');
18
19 var PsModuleMap = require('./purs-module-map');
20
21 function updatePsModuleMap(psModule) {
22 var options = psModule.options;
23
24 var cache = psModule.cache;
25
26 var filePurs = psModule.srcPath;
27
28 if (!cache.psModuleMap) {
29 debugVerbose('module mapping does not exist - making a new module map');
30
31 cache.psModuleMap = PsModuleMap.makeMap(options.src);
32
33 return cache.psModuleMap;
34 } else {
35 debugVerbose('module mapping exists - updating module map for %s', filePurs);
36
37 cache.psModuleMap = cache.psModuleMap.then(function (psModuleMap) {
38 return PsModuleMap.makeMapEntry(filePurs).then(function (result) {
39 var map = Object.assign(psModuleMap, result);
40
41 return map;
42 });
43 });
44
45 return cache.psModuleMap;
46 }
47 }
48
49 // Reference the bundle.
50 function makeBundleJS(psModule) {
51 var bundleOutput = psModule.options.bundleOutput;
52
53 var name = psModule.name;
54
55 var srcDir = psModule.srcDir;
56
57 var escaped = jsStringEscape(path.relative(srcDir, bundleOutput));
58
59 var result = 'module.exports = require("' + escaped + '")["' + name + '"]';
60
61 return Promise.resolve(result);
62 }
63
64 // Replace require paths to output files generated by psc with paths
65 // to purescript sources, which are then also run through this loader.
66 // Additionally, the imports replaced are tracked so that in the event
67 // the compiler fails to compile the PureScript source, we can tack on
68 // any new imports in order to allow webpack to watch the new files
69 // before they have been successfully compiled.
70 function makeJS(psModule, psModuleMap, js) {
71 var requireRE = /require\(['"]\.\.\/([\w\.]+)(?:\/index\.js)?['"]\)/g;
72
73 var foreignRE = /require\(['"]\.\/foreign(?:\.js)?['"]\)/g;
74
75 var name = psModule.name;
76
77 var imports = psModuleMap[name].imports;
78
79 var replacedImports = [];
80
81 var result = js.replace(requireRE, function (m, p1) {
82 var moduleValue = psModuleMap[p1];
83
84 if (!moduleValue) {
85 debug('module %s was not found in the map, replacing require with null', p1);
86
87 return 'null';
88 } else {
89 var escapedPath = jsStringEscape(moduleValue.src);
90
91 replacedImports.push(p1);
92
93 return 'require("' + escapedPath + '")';
94 }
95 }).replace(foreignRE, function () {
96 var escapedPath = jsStringEscape(psModuleMap[name].ffi);
97
98 return 'require("' + escapedPath + '")';
99 });
100
101 var additionalImports = difference(imports, replacedImports);
102
103 if (!additionalImports.length) {
104 return Promise.resolve(result);
105 } else {
106 debug('rebuilding module map due to additional imports for %s: %o', name, additionalImports);
107
108 psModule.cache.psModuleMap = null;
109
110 return updatePsModuleMap(psModule).then(function (updatedPsModuleMap) {
111 var additionalImportsResult = additionalImports.map(function (import_) {
112 var moduleValue = updatedPsModuleMap[import_];
113
114 if (!moduleValue) {
115 debug('module %s was not found in the map, skipping require', import_);
116
117 return null;
118 } else {
119 var escapedPath = jsStringEscape(moduleValue.src);
120
121 return 'var ' + import_.replace(/\./g, '_') + ' = require("' + escapedPath + '")';
122 }
123 }).filter(function (a) {
124 return a !== null;
125 }).join('\n');
126
127 return result + '\n' + additionalImportsResult;
128 });
129 }
130 }
131
132 module.exports = function toJavaScript(psModule) {
133 var options = psModule.options;
134
135 var cache = psModule.cache;
136
137 var bundlePath = path.resolve(options.bundleOutput);
138
139 var jsPath = options.bundle ? bundlePath : psModule.jsPath;
140
141 var js = fs.readFileAsync(jsPath, 'utf8').catch(function () {
142 return '';
143 });
144
145 var psModuleMap = updatePsModuleMap(psModule);
146
147 debugVerbose('loading JavaScript for %s', psModule.name);
148
149 return Promise.props({ js: js, psModuleMap: psModuleMap }).then(function (result) {
150 return options.bundle ? makeBundleJS(psModule) : makeJS(psModule, result.psModuleMap, result.js);
151 });
152 };