api.jonasjones.dev/node_modules/get-source/get-source.js

176 lines
7.4 KiB
JavaScript

"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)
})
}
})
})
/* ------------------------------------------------------------------------ */