Completed
Push — master ( a11cd2...01f33e )
by Vitaly
29s
created

get-source.js (1 issue)

Severity
1
"use strict";
2
3
/*  ------------------------------------------------------------------------ */
4
5
const O                 = Object,
0 ignored issues
show
Parsing error: Unexpected token const
Loading history...
6
      isBrowser         = (typeof window !== 'undefined') && (window.window === window) && window.navigator,
7
      SourceMapConsumer = require ('source-map').SourceMapConsumer,
8
      path              = require ('./impl/path'),
9
      dataURIToBuffer   = require ('data-uri-to-buffer'),
10
      lastOf            = x => x[x.length - 1]
11
12
/*  ------------------------------------------------------------------------ */
13
14
const memoize = f => {
15
    
16
    const m = x => (x in m.cache) ? m.cache[x] : (m.cache[x] = f(x))
17
    m.forgetEverything = () => { m.cache = Object.create (null) }
18
    m.cache = Object.create (null)
19
20
    return m
21
}
22
23
/*  ------------------------------------------------------------------------ */
24
25
const newSourceFileMemoized = memoize (file => new SourceFile (file))
26
27
const getSource = module.exports = file => { return newSourceFileMemoized (path.resolve (file)) }
28
29
getSource.resetCache = () => newSourceFileMemoized.forgetEverything ()
30
getSource.getCache = () => newSourceFileMemoized.cache
31
32
/*  ------------------------------------------------------------------------ */
33
34
class SourceMap {
35
36
    constructor (originalFilePath, sourceMapPath) {
37
38
        this.file = sourceMapPath.startsWith ('data:')
39
                        ? new SourceFile (originalFilePath, dataURIToBuffer (sourceMapPath).toString ())
40
                        : getSource (path.relativeToFile (originalFilePath, sourceMapPath))
41
42
        this.parsed    = (this.file.text && SourceMapConsumer (JSON.parse (this.file.text))) || null
43
        this.sourceFor = memoize (this.sourceFor.bind (this))
44
    }
45
46
    sourceFor (file) {
47
        const content = this.parsed.sourceContentFor (file, true /* return null on missing */)
48
        const fullPath = path.relativeToFile (this.file.path, file)
49
        return content ? new SourceFile (fullPath, content) : getSource (fullPath)
50
    }
51
52
    resolve (loc) {
53
54
        const originalLoc = this.parsed.originalPositionFor (loc)
55
        return originalLoc.source ? this.sourceFor (originalLoc.source)
56
                                        .resolve (O.assign ({}, loc, {
57
                                            line:   originalLoc.line,
58
                                            column: originalLoc.column + 1,
59
                                            name:   originalLoc.name
60
                                        }))
61
                                  : loc
62
    }
63
}
64
65
/*  ------------------------------------------------------------------------ */
66
67
class SourceFile {
68
69
    constructor (path, text /* optional */) {
70
        
71
        this.path = path
72
73
        if (text) {
74
            this.text = text }
75
76
        else {
77
            try {
78
                if (isBrowser) {
79
80
                    let xhr = new XMLHttpRequest ()
81
82
                        xhr.open ('GET', path, false /* SYNCHRONOUS XHR FTW :) */)
83
                        xhr.send (null)
84
                        
85
                    this.text = xhr.responseText }
86
87
                else {
88
                    this.text = module.require ('fs').readFileSync (path, { encoding: 'utf8' }) } }
89
90
            catch (e) {
91
                this.error = e
92
                this.text = '' } }
93
    }
94
95
    get lines () {
96
        return (this.lines_ = this.lines_ || this.text.split ('\n'))
97
    }
98
99
    get sourceMap () {
100
101
        try {
102
103
            if (this.sourceMap_ === undefined) {
104
105
                // Node v4 does not support destructuring...
106
                // const [,url] = this.text.match (/\u0023 sourceMappingURL=(.+)\n?/) || [undefined, undefined] // escape #, otherwise it will match this exact line.. %)
107
                
108
                const match = this.text.match (/\u0023 sourceMappingURL=(.+)\n?/) || [undefined, undefined] // escape #, otherwise it will match this exact line.. %)
109
                    , url = match[1]
110
111
                if (url) {
112
                    
113
                    const sourceMap = new SourceMap (this.path, url)
114
115
                    if (sourceMap.parsed) {
116
                        this.sourceMap_ = sourceMap
117
                    }
118
119
                } else {
120
121
                    this.sourceMap_ = null
122
                }
123
            }
124
        }
125
126
        catch (e) {
127
            this.sourceMap_ = null
128
            this.sourceMapError = e
129
        }
130
131
        return this.sourceMap_
132
    }
133
134
    resolve (loc /* { line[, column] } */) /* → { line, column, sourceFile, sourceLine } */ {
135
136
        if (this.sourceMap) {
137
            const newLoc = this.sourceMap.resolve (loc)
138
            if (newLoc.sourceFile) return newLoc
139
        }
140
141
        return O.assign ({}, loc, {
142
143
            sourceFile:  this,
144
            sourceLine: (this.lines[loc.line - 1] || ''),
145
            error:       this.error
146
        })
147
    }
148
}
149
150
/*  ------------------------------------------------------------------------ */
151