This commit is contained in:
Tutur33
2023-11-24 22:35:41 +01:00
parent 3c0b507a93
commit 7644b2a0f7
45165 changed files with 4803356 additions and 3 deletions
+6
View File
@@ -0,0 +1,6 @@
{
"parserOptions": {
"ecmaVersion": 6,
"sourceType": "script"
}
}
+24
View File
@@ -0,0 +1,24 @@
language: node_js
node_js:
- '10'
script:
- set -e
- npm run test
- npm run coveralls
# after_success:
# - git config --global user.email "travis@travis-ci.org"
# - git config --global user.name "Travis CI"
# - npm config set git-tag-version=false
# - NPM_VERSION=$(npm version patch)
# - git commit -a -m "${NPM_VERSION:1}" -m "[ci skip]"
# - git remote remove origin
# - git remote add origin https://${GITHUB_TOKEN}@github.com/xpl/get-source.git
# - git push origin HEAD:master
# deploy:
# provider: npm
# email: rocket.mind@gmail.com
# api_key:
# secure: jEWoCdiL3qadEKV36Aw1On1JNNvzSKIaZELVp0NiQLoWwbnTXzkXJuabHGDUQFnD9VXjhqtduS0GMpaV//HOXalSR72s8+VnnLYxsVj+Aslh1kDEwXW3OsQ2O5PSZY5nmGVYuZVjwFeOa70+cvMd0nE1v9HNgJBhhzeKiewgzusGrGBaW3ovXz9Bf7ITsbkVO55SbW8CroKILrQiPBTSqEUqH0Vx+ucHtb+gfJfOs7kXoCkjf8tu/yZjs5NIaHt1lKoWOmUlG3z7CjtFenieuzlQRe48jSQKnbXh5yJmziKvbiiwEfFnPhzPZqPKiXHDkBOr7Fm+geMSJcbRlu4lhB/aUfDoT5vlabnQsTcxz1wTKW+N/WR2xdl4kc5HPF9Tnx4c/MDVvQnC05NCRtcrtZNAla9r9pG/zmIvmFiP0ulPgDok8+Mq4GrDoNd5T4Dt8Xk+uD+rENjifYNetIU3Zcq7uslkwaoDZq29V/tSdbHVtXjMw+FSbUk5jJxD/4j3FxDaQGjOkjN/kqxcWc1IH5xi9bG0wCD5sKsdadPAuEMCJRFIlRP+EtyLD3CKwFYPKCQuTTvPJnZ6IOtCNGWI4Qe0eYSrmwURIdUkyxUkdeGZhjrTsGtJN7WXKq+JD2JU88978o3ZQ+CN1iqBaL8yhlDt8vhPtkzVqZXWI1LAB9A=
env:
global:
secure: SXc3ilPr7p+uac/b3ibx27zFEuKotjA0FLHkkFaU5Y74Ek++Yh++cmBbXjz8yS2XtaEmvr+EL/Z7cUsD7hbs787+lyc16AJNEf1l3PKwsFx7djsOBdXFf0mVPhmlx2BbAtgrVylPlVM0yzBDUTWOm2lFtMFp8v2vjJeSnePaWV2Gf7T4hpm4S4U0fXeVRFynFIDo+TfyJoNG6zg6iuUcIjiY5zrvE8U6pg3TtRUaQx8+JWZYdukiKEjPZ2mUHHGaHDRouM99wFz7Y1WTBkxYvyIQFNuf9zzJ/IRMFiLMhxPvSbIYHpNtimCWjYL/Hw105BWOxoTOFa2lsRT5dJAxv3LfroWrEG6vnV5BeJ35Ogcy4Mqf0N3lrMjo/vUgbn6nP2MTyADqBjNdJ0T6tqRSY4sE2M0nXddC/9/ONHOnqzOtAxqS72MJ93ZzSJ0VvMIaf2rygAnIHVHe7mzV3EEhs6l5APQYybYWI8bLD8A75LZeBG1tSBvdPdf5ny6l1GSyoQ1qIntfrl/FcGiJKbGVRl6by/XdRIGA9HOenOHdBzLrB58VF0NkOKzmizlu3O51LW21Yt/HwPRAoGb9t/7KAOT6otKplIk8qy/tTzIc0fZm6mG8FmRQsisIrfWTiWh37jSZU9FGdNGkF/tjjZ/bmEPiJ56fWiXvzVv9CQAeF/0=
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2016 Vitaly Gordon (https://github.com/xpl)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+112
View File
@@ -0,0 +1,112 @@
# get-source
[![Build Status](https://travis-ci.org/xpl/get-source.svg?branch=master)](https://travis-ci.org/xpl/get-source) [![Coverage Status](https://coveralls.io/repos/github/xpl/get-source/badge.svg)](https://coveralls.io/github/xpl/get-source) [![npm](https://img.shields.io/npm/v/get-source.svg)](https://npmjs.com/package/get-source)
Fetch source-mapped sources. Peek by file, line, column. Node & browsers. Sync & async.
```bash
npm install get-source
```
## Features
- [x] Allows to read source code files in Node and browsers
- [x] Full sourcemap support (path resolving, external/embedded/inline linking, and long chains)
- [x] **Synchronous** API — good for CLI tools (e.g. [logging](https://github.com/xpl/ololog)). Works in browsers!
- [x] **Asynchronous** API — good for everything web!
- [x] Built-in cache
## What for
- [x] Call stacks enhanced with source code information (see the [StackTracey](https://github.com/xpl/stacktracey) library)
- [x] [Advanced logging](https://github.com/xpl/ololog) / assertion printing
- [x] [Error displaying components](https://github.com/xpl/panic-overlay) for front-end web development
## Usage (Synchronous)
```javascript
import getSource from 'get-source'
```
```javascript
file = getSource ('./scripts/index.min.js')
```
Will read the file synchronously (either via XHR or by filesystem API, depending on the environment) and return it's cached representation. Result will contain the following fields:
```javascript
file.path // normalized file path
file.text // text contents
file.lines // array of lines
```
And the `resolve` method:
```javascript
file.resolve ({ line: 1, column: 8 }) // indexes here start from 1 (by widely accepted convention). Zero indexes are invalid.
```
It will look through the sourcemap chain, returning following:
```javascript
{
line: <original line number>,
column: <original column number>,
sourceFile: <original source file object>,
sourceLine: <original source line text>
}
```
In that returned object, `sourceFile` is the same kind of object that `getSource` returns. So you can access its `text`, `lines` and `path` fields to obtain the full information. And the `sourceLine` is returned just for the convenience, as a shortcut.
## Usage (Asynchronous)
Pretty much the same as synchronous, except it's `getSource.async`. It returns awaitable promises:
```javascript
file = await getSource.async ('./scripts/index.min.js')
location = await file.resolve ({ line: 1, column: 8 })
```
## Error handling
In synchronous mode, it never throws (due to backward compatibility reasons with existing code):
```javascript
nonsense = getSource ('/some/nonexistent/file')
nonsense.text // should be '' (so it's safe to access without checking)
nonsense.error // should be an Error object, representing an actual error thrown during reading/parsing
```
```javascript
resolved = nonsense.resolve ({ line: 5, column: 0 })
resolved.sourceLine // empty string (so it's safe to access without checking)
resolved.error // should be an Error object, representing an actual error thrown during reading/parsing
```
In asychronous mode, it throws an error:
```javascript
try {
file = await getSource.async ('/some/file')
location = await file.resolve ({ line: 5, column: 0 })
} catch (e) {
...
}
```
## Resetting Cache
E.g. when you need to force-reload files:
```javascript
getSource.resetCache () // sync cache
getSource.async.resetCache () // async cache
```
Also, viewing cached files:
```javascript
getSource.getCache () // sync cache
getSource.async.getCache () // async cache
```
+48
View File
@@ -0,0 +1,48 @@
declare interface Location {
line: number;
column: number;
}
declare interface ResolvedLocation<FileType> extends Location {
sourceFile: FileType;
sourceLine: string;
error?: Error;
}
declare interface File {
path: string;
text: string;
lines: string[];
error?: Error;
}
declare interface FileAsync extends File {
resolve (location: Location): Promise<ResolvedLocation<FileAsync>>
}
declare interface FileSync extends File {
resolve (location: Location): ResolvedLocation<FileSync>
}
declare interface FileCache<T> {
resetCache (): void;
getCache (): { [key: string]: T };
}
declare interface getSourceAsync extends FileCache<FileAsync> {
(path: string): Promise<FileAsync>;
}
declare interface getSourceSync extends FileCache<FileSync> {
(path: string): FileSync;
async: getSourceAsync;
}
declare const getSource: getSourceSync;
export = getSource;
+176
View File
@@ -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)
})
}
})
})
/* ------------------------------------------------------------------------ */
+51
View File
@@ -0,0 +1,51 @@
"use strict";
/* ------------------------------------------------------------------------ */
module.exports = class SyncPromise {
constructor (fn) {
try {
fn (
x => { this.setValue (x, false) }, // resolve
x => { this.setValue (x, true) } // reject
)
} catch (e) {
this.setValue (e, true)
}
}
setValue (x, rejected) {
this.val = (x instanceof SyncPromise) ? x.val : x
this.rejected = rejected || ((x instanceof SyncPromise) ? x.rejected : false)
}
static valueFrom (x) {
if (x instanceof SyncPromise) {
if (x.rejected) throw x.val
else return x.val
} else {
return x
}
}
then (fn) {
try { if (!this.rejected) return SyncPromise.resolve (fn (this.val)) }
catch (e) { return SyncPromise.reject (e) }
return this
}
catch (fn) {
try { if (this.rejected) return SyncPromise.resolve (fn (this.val)) }
catch (e) { return SyncPromise.reject (e) }
return this
}
static resolve (x) {
return new SyncPromise (resolve => { resolve (x) })
}
static reject (x) {
return new SyncPromise ((_, reject) => { reject (x) })
}
}
+62
View File
@@ -0,0 +1,62 @@
"use strict";
/* ------------------------------------------------------------------------ */
const isBrowser = (typeof window !== 'undefined') && (window.window === window) && window.navigator
const cwd = isBrowser ? window.location.href : process.cwd ()
const urlRegexp = new RegExp ("^((https|http)://)?[a-z0-9A-Z]{3}\.[a-z0-9A-Z][a-z0-9A-Z]{0,61}?[a-z0-9A-Z]\.com|net|cn|cc (:s[0-9]{1-4})?/$")
/* ------------------------------------------------------------------------ */
const path = module.exports = {
concat (a, b) {
const a_endsWithSlash = (a[a.length - 1] === '/'),
b_startsWithSlash = (b[0] === '/')
return a + ((a_endsWithSlash || b_startsWithSlash) ? '' : '/') +
((a_endsWithSlash && b_startsWithSlash) ? b.substring (1) : b)
},
resolve (x) {
if (path.isAbsolute (x)) {
return path.normalize (x) }
return path.normalize (path.concat (cwd, x))
},
normalize (x) {
let output = [],
skip = 0
x.split ('/').reverse ().filter (x => x !== '.').forEach (x => {
if (x === '..') { skip++ }
else if (skip === 0) { output.push (x) }
else { skip-- }
})
const result = output.reverse ().join ('/')
return ((isBrowser && (result[0] === '/')) ? result[1] === '/' ? window.location.protocol : window.location.origin : '') + result
},
isData: x => x.indexOf ('data:') === 0,
isURL: x => urlRegexp.test (x),
isAbsolute: x => (x[0] === '/') || /^[^\/]*:/.test (x),
relativeToFile (a, b) {
return (path.isData (a) || path.isAbsolute (b)) ?
path.normalize (b) :
path.normalize (path.concat (a.split ('/').slice (0, -1).join ('/'), b))
}
}
/* ------------------------------------------------------------------------ */
+43
View File
@@ -0,0 +1,43 @@
{
"name": "get-source",
"version": "2.0.12",
"description": "Fetch source-mapped sources. Peek by file, line, column. Node & browsers. Sync & async.",
"main": "get-source",
"types": "./get-source.d.ts",
"scripts": {
"test-browser": "mocha test/test.browser --reporter spec",
"test-node": "mocha test/test.node --reporter spec",
"test-path": "mocha test/test.path --reporter spec",
"test": "nyc --reporter=html --reporter=text mocha test/test.path test/test.node --reporter spec",
"coveralls": "nyc report --reporter=text-lcov | coveralls"
},
"repository": {
"type": "git",
"url": "https://github.com/xpl/get-source.git"
},
"keywords": [
"sources",
"sourcemap",
"read source",
"cached sources"
],
"author": "Vitaly Gordon <rocket.mind@gmail.com>",
"license": "Unlicense",
"bugs": {
"url": "https://github.com/xpl/get-source/issues"
},
"homepage": "https://github.com/xpl/get-source",
"devDependencies": {
"chai": "^3.5.0",
"coveralls": "^3.0.3",
"istanbul": "^0.4.5",
"memory-fs": "^0.3.0",
"mocha": "^8.0.1",
"nyc": "^15.1.0",
"webpack": "^4.43.0"
},
"dependencies": {
"data-uri-to-buffer": "^2.0.0",
"source-map": "^0.6.1"
}
}
+3
View File
@@ -0,0 +1,3 @@
require ('chai').should ()
window.path = require ('../../impl/path')
window.getSource = require ('../../get-source')
@@ -0,0 +1,9 @@
'use strict';
/* Dummy javascript file */
function hello() {
return 'hello world';
}
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm9yaWdpbmFsLmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBQUE7O0FBRUEsU0FBUyxLQUFULEdBQWtCO0FBQ2pCLFFBQU8sYUFBUDtBQUFzQiIsImZpbGUiOiJvcmlnaW5hbC5iYWJlbGVkLndpdGguaW5saW5lLnNvdXJjZW1hcC5qcyIsInNvdXJjZXNDb250ZW50IjpbIi8qXHREdW1teSBqYXZhc2NyaXB0IGZpbGVcdCovXG5cbmZ1bmN0aW9uIGhlbGxvICgpIHtcblx0cmV0dXJuICdoZWxsbyB3b3JsZCcgfSJdfQ==
+4
View File
@@ -0,0 +1,4 @@
/* Dummy javascript file */
function hello () {
return 'hello world' }
@@ -0,0 +1,4 @@
function hello() {
return "hello world";
}
//# sourceMappingURL=original.uglified.beautified.js.map
@@ -0,0 +1 @@
{"version":3,"sources":["original.uglified.js"],"names":["hello"],"mappings":"AAAA,SAASA;IAAQ,OAAM"}
+2
View File
@@ -0,0 +1,2 @@
function hello(){return"hello world"}
//# sourceMappingURL=original.uglified.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"sources":["original.js"],"names":["hello"],"mappings":"AAEA,QAASA,SACR,MAAO"}
@@ -0,0 +1,2 @@
function hello(){return"hello world"}
//# sourceMappingURL=original.uglified.with.sources.js.map
@@ -0,0 +1 @@
{"version":3,"sources":["## embedded ##"],"names":["hello"],"mappings":"AAEA,QAASA,SACR,MAAO","sourcesContent":["/*\tDummy javascript file\t*/\n\nfunction hello () {\n\treturn 'hello world' }"]}
+1
View File
@@ -0,0 +1 @@
<html></html>
+98
View File
@@ -0,0 +1,98 @@
/* TODO: make it work in Travis CI
------------------------------------------------------------------------ */
const selenium = require ('selenium-webdriver/testing')
/* ------------------------------------------------------------------------ */
selenium.describe ('Chrome test', (done) => {
const webdriver = require ('selenium-webdriver')
, path = require ('path')
, fs = require ('fs')
, memFS = new (require ('memory-fs')) ()
, it = selenium.it
, webpack = require ('webpack')
, logging = require ('selenium-webdriver/lib/logging')
let driver
/* Prepare ChromeDriver (with CORS disabled and log interception enabled) */
selenium.before (() => driver =
new webdriver
.Builder ()
.withCapabilities (
webdriver.Capabilities
.chrome ()
.setLoggingPrefs (new logging.Preferences ().setLevel (logging.Type.BROWSER, logging.Level.ALL))
.set ('chromeOptions', {
'args': ['--disable-web-security'] }))
.build ())
selenium.after (() => driver.quit ())
it ('works', async () => {
/* Compile get-source */
const compiledScript = await (new Promise (resolve => { Object.assign (webpack ({
entry: './test/files/get-source.webpack.entry.js',
output: { path: '/', filename: 'get-source.webpack.compiled.js' },
plugins: [ new webpack.IgnorePlugin(/^fs$/) ]
}), { outputFileSystem: memFS }).run ((err, stats) => {
if (err) throw err
resolve (memFS.readFileSync ('/get-source.webpack.compiled.js').toString ('utf-8'))
})
}))
/* Inject it into Chrome */
driver.get ('file://' + path.resolve ('./test/files/test.html'))
driver.executeScript (compiledScript)
/* Execute test */
const exec = fn => driver.executeScript (`(${fn.toString ()})()`)
try {
await exec (function () {
path.relativeToFile ('http://foo.com/scripts/bar.js', '../bar.js.map')
.should.equal ('http://foo.com/bar.js.map')
path.relativeToFile ('http://foo.com/scripts/bar.js', 'http://bar.js.map')
.should.equal ('http://bar.js.map')
path.relativeToFile ('http://foo.com/scripts/bar.js', '/bar.js.map')
.should.equal ('file:///bar.js.map')
path.relativeToFile ('http://foo.com/scripts/bar.js', '//bar.com/bar.js.map')
.should.equal ('http://bar.com/bar.js.map')
var loc = getSource ('../original.uglified.beautified.js').resolve ({ line: 2, column: 4 })
loc.line.should.equal (4)
loc.column.should.equal (2)
loc.sourceFile.path.should.contain ('test/files/original.js')
loc.sourceLine.should.equal ('\treturn \'hello world\' }')
})
} catch (e) { throw e } finally {
driver.manage ().logs ().get (logging.Type.BROWSER).then (entries => {
entries.forEach (entry => {
console.log('[BROWSER] [%s] %s', entry.level.name, entry.message);
})
})
}
})
})
/* ------------------------------------------------------------------------ */
+222
View File
@@ -0,0 +1,222 @@
"use strict";
/* NOTE: I've used supervisor to auto-restart mocha, because mocha --watch
didn't work for selenium tests (not reloading them)...
------------------------------------------------------------------ */
require ('chai').should ()
/* ------------------------------------------------------------------------ */
describe ('get-source', () => {
const getSource = require ('../get-source'),
fs = require ('fs'),
path = require ('path')
it ('cache sanity check', () => {
getSource ('./get-source.js').should.equal (getSource ('./get-source.js'))
getSource ('./get-source.js').should.not.equal (getSource ('./package.json'))
})
it ('reads sources (not sourcemapped)', () => {
const original = getSource ('./test/files/original.js')
original.path.should.equal (path.resolve ('./test/files/original.js')) // resolves input paths
original.text.should.equal (fs.readFileSync ('./test/files/original.js', { encoding: 'utf-8' }))
original.lines.should.deep.equal ([
'/*\tDummy javascript file\t*/',
'',
'function hello () {',
'\treturn \'hello world\' }'
])
const resolved = original.resolve ({ line: 4, column: 1 })
resolved.line.should.equal (4)
resolved.column.should.equal (1)
resolved.sourceFile.should.equal (original)
resolved.sourceLine.should.equal ('\treturn \'hello world\' }')
})
it ('reads sources (sourcemapped, with external links)', () => {
const uglified = getSource ('./test/files/original.uglified.js')
uglified.path.should.equal (path.resolve ('./test/files/original.uglified.js'))
uglified.lines.should.deep.equal ([
'function hello(){return"hello world"}',
'//# sourceMappingURL=original.uglified.js.map',
''
])
// uglified.sourceMap.should.not.equal (undefined)
// uglified.sourceMap.should.equal (uglified.sourceMap) // memoization should work
const resolved = uglified.resolve ({ line: 1, column: 18 }) // should be tolerant to column omission
resolved.line.should.equal (4)
resolved.column.should.equal (2)
resolved.sourceFile.should.equal (getSource ('./test/files/original.js'))
resolved.sourceLine.should.equal ('\treturn \'hello world\' }')
})
it ('reads sources (sourcemapped, with external links) — ASYNC', () => {
const uglified = getSource.async ('./test/files/original.uglified.js')
return uglified.then (uglified => {
uglified.path.should.equal (path.resolve ('./test/files/original.uglified.js'))
uglified.lines.should.deep.equal ([
'function hello(){return"hello world"}',
'//# sourceMappingURL=original.uglified.js.map',
''
])
// uglified.sourceMap.should.not.equal (undefined)
// uglified.sourceMap.should.equal (uglified.sourceMap) // memoization should work
return uglified.resolve ({ line: 1, column: 18 }).then (resolved => {
return getSource.async ('./test/files/original.js').then (originalFile => {
resolved.line.should.equal (4)
resolved.column.should.equal (2)
resolved.sourceFile.should.equal (originalFile)
resolved.sourceLine.should.equal ('\treturn \'hello world\' }')
})
})
})
})
it ('reads sources (sourcemapped, with embedded sources)', () => {
const uglified = getSource ('./test/files/original.uglified.with.sources.js')
uglified.path.should.equal (path.resolve ('./test/files/original.uglified.with.sources.js'))
uglified.lines.should.deep.equal ([
'function hello(){return"hello world"}',
'//# sourceMappingURL=original.uglified.with.sources.js.map',
''
])
// uglified.sourceMap.should.not.equal (undefined)
const resolved = uglified.resolve ({ line: 1, column: 18 })
resolved.line.should.equal (4)
resolved.column.should.equal (2)
resolved.sourceFile.path.should.equal (path.resolve ('./test/files') + '/## embedded ##') // I've changed the filename manually, by editing .map file
resolved.sourceLine.should.equal ('\treturn \'hello world\' }')
})
it ('reads sources (sourcemapped, with inline base64 sourcemaps)', () => {
const babeled = getSource ('./test/files/original.babeled.with.inline.sourcemap.js')
// babeled.sourceMap.should.not.equal (undefined)
// babeled.sourceMap.file.path.should.equal (babeled.path)
const resolved = babeled.resolve ({ line: 6, column: 1 })
resolved.line.should.equal (4)
resolved.column.should.equal (2)
resolved.sourceLine.should.equal ('\treturn \'hello world\' }')
})
it ('supports even CHAINED sourcemaps!', () => {
/* original.js → original.uglified.js → original.uglified.beautified.js */
const beautified = getSource ('./test/files/original.uglified.beautified.js')
beautified.path.should.equal (path.resolve ('./test/files/original.uglified.beautified.js'))
beautified.text.should.equal (fs.readFileSync ('./test/files/original.uglified.beautified.js', { encoding: 'utf-8' }))
// beautified.sourceMap.should.not.equal (undefined)
const resolved = beautified.resolve ({ line: 2, column: 4 })
resolved.line.should.equal (4)
resolved.column.should.equal (2)
resolved.sourceFile.path.should.equal (path.resolve ('./test/files/original.js'))
resolved.sourceLine.should.equal ('\treturn \'hello world\' }')
})
it ('adheres to async interface', () => {
return getSource.async ('./get-source.js').then (result => {
;(result.resolve ({ line: 1, column: 0 }) instanceof Promise).should.equal (true)
})
})
it ('supports even CHAINED sourcemaps! — ASYNC', () => {
/* original.js → original.uglified.js → original.uglified.beautified.js */
return getSource.async ('./test/files/original.uglified.beautified.js').then (beautified => {
beautified.text.should.equal (fs.readFileSync ('./test/files/original.uglified.beautified.js', { encoding: 'utf-8' }))
beautified.path.should.equal (path.resolve ('./test/files/original.uglified.beautified.js'))
return beautified.resolve ({ line: 2, column: 4 }).then (resolved => {
resolved.line.should.equal (4)
resolved.column.should.equal (2)
resolved.sourceFile.path.should.equal (path.resolve ('./test/files/original.js'))
resolved.sourceLine.should.equal ('\treturn \'hello world\' }')
})
})
})
it ('does some error handling', () => {
const nonsense = getSource ('abyrvalg')
nonsense.text.should.equal ('')
nonsense.error.should.be.an.instanceof (Error)
const resolved = nonsense.resolve ({ line: 5, column: 0 })
resolved.error.should.equal (nonsense.error)
resolved.sourceLine.should.equal ('')
resolved.sourceFile.path.should.equal ('abyrvalg')
})
it ('does some error handling - ASYNC', () => {
return getSource.async ('abyrvalg').then (x => { console.log (x) }).catch (error => {
error.should.be.an.instanceof (Error)
})
})
it ('allows absolute paths', () => {
getSource (require ('path').resolve ('./get-source.js')).should.equal (getSource ('./get-source.js'))
})
it ('caching works', () => {
const files =
[ './get-source.js',
'./package.json',
'./test/files/original.js',
'./test/files/original.uglified.js',
'./test/files/original.uglified.js.map',
'./test/files/original.uglified.with.sources.js',
'./test/files/original.uglified.with.sources.js.map',
'./test/files/original.babeled.with.inline.sourcemap.js',
'./test/files/original.uglified.beautified.js',
'./test/files/original.uglified.beautified.js.map',
'./abyrvalg' ]
Object.keys (getSource.getCache ()).should.deep.equal (files.map (x => path.resolve (x)))
getSource.resetCache ()
Object.keys (getSource.getCache ()).length.should.equal (0)
})
})
+56
View File
@@ -0,0 +1,56 @@
"use strict";
/* ------------------------------------------------------------------------ */
require ('chai').should ()
/* ------------------------------------------------------------------------ */
describe ('path', () => {
const path = require ('../impl/path')
it ('resolves', () => {
path.resolve ('./foo/bar/../qux').should.equal (process.cwd () + '/foo/qux')
})
it ('normalizes', () => {
path.normalize ('./foo/./bar/.././.././qux.map./').should.equal ('qux.map./')
path.normalize ('/a/b').should.equal ('/a/b')
path.normalize ('http://foo/bar').should.equal ('http://foo/bar')
})
it ('computes relative location', () => {
path.relativeToFile ('/foo/bar.js', './qux.map')
.should.equal ('/foo/qux.map')
path.relativeToFile ('/foo/bar/baz.js', './../.././qux.map')
.should.equal ('/qux.map')
path.relativeToFile ('/foo/bar', 'webpack:something')
.should.equal ('webpack:something')
path.relativeToFile ('/foo/bar', 'web/pack:something')
.should.equal ('/foo/web/pack:something')
})
it ('works with data URIs', () => {
path.relativeToFile ('/foo/bar.js', 'data:application/json;charset=utf-8;base64,eyJ2ZXJza==')
.should.equal ( 'data:application/json;charset=utf-8;base64,eyJ2ZXJza==')
path.relativeToFile ('data:application/json;charset=utf-8;base64,eyJ2ZXJza==', 'foo.js')
.should.equal ( 'foo.js')
})
it ('implements isURL', () => {
path.isURL ('foo.js').should.equal (false)
path.isURL ('/foo/bar.js').should.equal (false)
path.isURL ('https://google.com').should.equal (true)
})
})