Issues (5)

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
                /*  Extract the last sourceMap occurence (TODO: support multiple sourcemaps)   */
106
107
                const re = /\u0023 sourceMappingURL=(.+)\n?/g
108
                let lastMatch = undefined
109
110
                while (true) {
111
                    const match = re.exec (this.text)
112
                    if (match) lastMatch = match
113
                    else break
114
                }
115
116
                const url = lastMatch && lastMatch[1]
117
118
                if (url) {
119
                    
120
                    const sourceMap = new SourceMap (this.path, url)
121
122
                    if (sourceMap.parsed) {
123
                        this.sourceMap_ = sourceMap
124
                    }
125
126
                } else {
127
128
                    this.sourceMap_ = null
129
                }
130
            }
131
        }
132
133
        catch (e) {
134
            this.sourceMap_ = null
135
            this.sourceMapError = e
136
        }
137
138
        return this.sourceMap_
139
    }
140
141
    resolve (loc /* { line[, column] } */) /* → { line, column, sourceFile, sourceLine } */ {
142
143
        if (this.sourceMap) {
144
            const newLoc = this.sourceMap.resolve (loc)
145
            if (newLoc.sourceFile) return newLoc
146
        }
147
148
        return O.assign ({}, loc, {
149
150
            sourceFile:  this,
151
            sourceLine: (this.lines[loc.line - 1] || ''),
152
            error:       this.error
153
        })
154
    }
155
}
156
157
/*  ------------------------------------------------------------------------ */
158