diff options
Diffstat (limited to 'node_modules/get-source/get-source.js')
| -rw-r--r-- | node_modules/get-source/get-source.js | 176 |
1 files changed, 176 insertions, 0 deletions
diff --git a/node_modules/get-source/get-source.js b/node_modules/get-source/get-source.js new file mode 100644 index 0000000..5f2d2d7 --- /dev/null +++ b/node_modules/get-source/get-source.js @@ -0,0 +1,176 @@ +"use strict"; + +/* ------------------------------------------------------------------------ */ + +const { assign } = Object, + isBrowser = (typeof window !== 'undefined') && (window.window === window) && window.navigator, + SourceMapConsumer = require ('source-map').SourceMapConsumer, + SyncPromise = require ('./impl/SyncPromise'), + path = require ('./impl/path'), + dataURIToBuffer = require ('data-uri-to-buffer'), + nodeRequire = isBrowser ? null : module.require + +/* ------------------------------------------------------------------------ */ + +const memoize = f => { + + const m = x => (x in m.cache) ? m.cache[x] : (m.cache[x] = f(x)) + m.forgetEverything = () => { m.cache = Object.create (null) } + m.cache = Object.create (null) + + return m +} + +function impl (fetchFile, sync) { + + const PromiseImpl = sync ? SyncPromise : Promise + const SourceFileMemoized = memoize (path => SourceFile (path, fetchFile (path))) + + function SourceFile (srcPath, text) { + if (text === undefined) return SourceFileMemoized (path.resolve (srcPath)) + + return PromiseImpl.resolve (text).then (text => { + + let file + let lines + let resolver + let _resolve = loc => (resolver = resolver || SourceMapResolverFromFetchedFile (file)) (loc) + + return (file = { + path: srcPath, + text, + get lines () { return lines = (lines || text.split ('\n')) }, + resolve (loc) { + const result = _resolve (loc) + if (sync) { + try { return SyncPromise.valueFrom (result) } + catch (e) { return assign ({}, loc, { error: e }) } + } else { + return Promise.resolve (result) + } + }, + _resolve, + }) + }) + } + + function SourceMapResolverFromFetchedFile (file) { + + /* Extract the last sourceMap occurence (TODO: support multiple sourcemaps) */ + + const re = /\u0023 sourceMappingURL=(.+)\n?/g + let lastMatch = undefined + + while (true) { + const match = re.exec (file.text) + if (match) lastMatch = match + else break + } + + const url = lastMatch && lastMatch[1] + + const defaultResolver = loc => assign ({}, loc, { + sourceFile: file, + sourceLine: (file.lines[loc.line - 1] || '') + }) + + return url ? SourceMapResolver (file.path, url, defaultResolver) + : defaultResolver + } + + function SourceMapResolver (originalFilePath, sourceMapPath, fallbackResolve) { + + const srcFile = sourceMapPath.startsWith ('data:') + ? SourceFile (originalFilePath, dataURIToBuffer (sourceMapPath).toString ()) + : SourceFile (path.relativeToFile (originalFilePath, sourceMapPath)) + + const parsedMap = srcFile.then (f => SourceMapConsumer (JSON.parse (f.text))) + + const sourceFor = memoize (function sourceFor (filePath) { + return srcFile.then (f => { + const fullPath = path.relativeToFile (f.path, filePath) + return parsedMap.then (x => SourceFile ( + fullPath, + x.sourceContentFor (filePath, true /* return null on missing */) || undefined)) + }) + }) + + return loc => parsedMap.then (x => { + const originalLoc = x.originalPositionFor (loc) + return originalLoc.source ? sourceFor (originalLoc.source).then (x => + x._resolve (assign ({}, loc, { + line: originalLoc.line, + column: originalLoc.column + 1, + name: originalLoc.name + })) + ) + : fallbackResolve (loc) + }).catch (e => + assign (fallbackResolve (loc), { sourceMapError: e })) + } + + return assign (function getSource (path) { + const file = SourceFile (path) + if (sync) { + try { return SyncPromise.valueFrom (file) } + catch (e) { + const noFile = { + path, + text: '', + lines: [], + error: e, + resolve (loc) { + return assign ({}, loc, { error: e, sourceLine: '', sourceFile: noFile }) + } + } + return noFile + } + } + return file + }, { + resetCache: () => SourceFileMemoized.forgetEverything (), + getCache: () => SourceFileMemoized.cache + }) +} + +/* ------------------------------------------------------------------------ */ + +module.exports = impl (function fetchFileSync (path) { + return new SyncPromise (resolve => { + if (isBrowser) { + let xhr = new XMLHttpRequest () + xhr.open ('GET', path, false /* SYNCHRONOUS XHR FTW :) */) + xhr.send (null) + resolve (xhr.responseText) + } else { + resolve (nodeRequire ('fs').readFileSync (path, { encoding: 'utf8' })) + } + }) + }, true) + +/* ------------------------------------------------------------------------ */ + +module.exports.async = impl (function fetchFileAsync (path) { + return new Promise ((resolve, reject) => { + if (isBrowser) { + let xhr = new XMLHttpRequest () + xhr.open ('GET', path) + xhr.onreadystatechange = event => { + if (xhr.readyState === 4) { + if (xhr.status === 200) { + resolve (xhr.responseText) + } else { + reject (new Error (xhr.statusText)) + } + } + } + xhr.send (null) + } else { + nodeRequire ('fs').readFile (path, { encoding: 'utf8' }, (e, x) => { + e ? reject (e) : resolve (x) + }) + } + }) + }) + +/* ------------------------------------------------------------------------ */ |
