-
Notifications
You must be signed in to change notification settings - Fork 21
/
Copy pathindex.js
173 lines (155 loc) · 5.65 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
var fs = require('fs')
var path = require('path')
var mkdirp = require('mkdirp')
var ES6Transpiler = require('es6-module-transpiler').Compiler
var jsStringEscape = require('js-string-escape')
var helpers = require('broccoli-kitchen-sink-helpers')
var Writer = require('broccoli-writer')
module.exports = ES6Concatenator
ES6Concatenator.prototype = Object.create(Writer.prototype)
ES6Concatenator.prototype.constructor = ES6Concatenator
function ES6Concatenator(inputTree, options) {
if (!(this instanceof ES6Concatenator)) return new ES6Concatenator(inputTree, options)
this.inputTree = inputTree
for (var key in options) {
if (options.hasOwnProperty(key)) {
this[key] = options[key]
}
}
this.cache = {
es6: {},
legacy: {}
}
}
ES6Concatenator.prototype.getWrapInEval = function () {
// default to true for now
return this.wrapInEval == null ? true : this.wrapInEval
}
ES6Concatenator.prototype.write = function (readTree, destDir) {
var self = this
return readTree(this.inputTree).then(function (srcDir) {
var modulesAdded = {}
var output = []
// When we are done compiling, we replace this.cache with newCache, so that
// unused cache entries are garbage-collected
var newCache = {
es6: {},
legacy: {}
}
if (self.loaderFile) {
addLegacyFile(self.loaderFile)
}
// This glob tends to be the biggest performance hog
var inputFiles = helpers.multiGlob(self.inputFiles, {cwd: srcDir})
for (var i = 0; i < inputFiles.length; i++) {
var inputFile = inputFiles[i]
if (inputFile.slice(-3) !== '.js') {
throw new Error('ES6 file does not end in .js: ' + inputFile)
}
var moduleName = inputFile.slice(0, -3)
addModule(moduleName)
}
if (self.legacyFilesToAppend && self.legacyFilesToAppend.length) {
var legacyFiles = helpers.multiGlob(self.legacyFilesToAppend, {cwd: srcDir})
for (i = 0; i < legacyFiles.length; i++) {
addLegacyFile(legacyFiles[i])
}
}
helpers.assertAbsolutePaths([self.outputFile])
mkdirp.sync(path.join(destDir, path.dirname(self.outputFile)))
fs.writeFileSync(path.join(destDir, self.outputFile), output.join('\n;'))
self.cache = newCache
function isIgnored(moduleName) {
if (typeof(self.ignoredModules) === 'function') {
return self.ignoredModules(moduleName)
}
if (self.ignoredModules) {
return self.ignoredModules.indexOf(moduleName) !== -1
}
return false;
}
function addModule (moduleName, sourceFile) {
if (modulesAdded[moduleName]) return
if (isIgnored(moduleName)) return
var i
var modulePath = moduleName + '.js'
var fullPath = srcDir + '/' + modulePath
var imports
try {
var statsHash = helpers.hashStats(fs.statSync(fullPath), modulePath)
} catch(e) {
e.file = sourceFile
throw e
}
try {
var cacheObject = self.cache.es6[statsHash]
if (cacheObject == null) { // cache miss
var fileContents = fs.readFileSync(fullPath).toString()
var compiler = new ES6Transpiler(fileContents, moduleName)
// Resolve relative imports by mutating the compiler's list of import nodes
for (i = 0; i < compiler.imports.length; i++) {
var importNode = compiler.imports[i]
if ((importNode.type !== 'ImportDeclaration' &&
importNode.type !== 'ModuleDeclaration') ||
!importNode.source ||
importNode.source.type !== 'Literal' ||
!importNode.source.value) {
throw new Error('Internal error: Esprima import node has unexpected structure')
}
// Mutate node
if (importNode.source.value.slice(0, 1) === '.') {
importNode.source.value = path.join(moduleName, '..', importNode.source.value).replace(/\\/g, '/')
}
}
var compiledModule = compiler.toAMD()
if (self.getWrapInEval()) {
compiledModule = wrapInEval(compiledModule, modulePath)
}
cacheObject = {
output: compiledModule,
imports: compiler.imports.map(function (importNode) {
return importNode.source.value
})
}
}
newCache.es6[statsHash] = cacheObject
imports = cacheObject.imports
output.push(cacheObject.output)
modulesAdded[moduleName] = true
} catch (err) {
// Bug: When a non-existent file is referenced, this is the referenced
// file, not the parent
err.file = modulePath
throw err
}
for (i = 0; i < imports.length; i++) {
var importName = imports[i]
addModule(importName, modulePath)
}
}
function addLegacyFile (filePath) {
// This function is just slow enough that we benefit from caching
var statsHash = helpers.hashStats(fs.statSync(srcDir + '/' + filePath), filePath)
var cacheObject = self.cache.legacy[statsHash]
if (cacheObject == null) { // cache miss
var fileContents = fs.readFileSync(srcDir + '/' + filePath, { encoding: 'utf8' })
if (self.getWrapInEval()) {
fileContents = wrapInEval(fileContents, filePath)
}
cacheObject = {
output: fileContents
}
}
newCache.legacy[statsHash] = cacheObject
output.push(cacheObject.output)
}
})
}
function wrapInEval (fileContents, fileName) {
// Should pull out copyright comment headers
// Eventually we want source maps instead of sourceURL
return 'eval("' +
jsStringEscape(fileContents) +
'//# sourceURL=' + jsStringEscape(fileName) +
'");\n'
}