diff options
Diffstat (limited to 'lib/to-javascript.js')
-rw-r--r-- | lib/to-javascript.js | 152 |
1 files changed, 152 insertions, 0 deletions
diff --git a/lib/to-javascript.js b/lib/to-javascript.js new file mode 100644 index 0000000..ce49704 --- /dev/null +++ b/lib/to-javascript.js | |||
@@ -0,0 +1,152 @@ | |||
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 | }; \ No newline at end of file | ||