Comparing version 0.1.1 to 0.2.0
# changelog | ||
# 0.1.1 | ||
## 0.2.0 | ||
* Redesigned API around `sorcery.load()` - consult the [docs](https://github.com/Rich-Harris/sorcery/wiki) | ||
* Command line interface | ||
## 0.1.1 | ||
* `sorcery.resolve()` fulfils with `null` if the target file has no sourcemap | ||
# 0.1.0 | ||
## 0.1.0 | ||
* First release. Here be dragons. |
@@ -0,3 +1,11 @@ | ||
var Node = require( './Node' ); | ||
module.exports = { | ||
resolve: require( './resolve' ) | ||
load: function ( file ) { | ||
return new Node( file )._load(); | ||
}, | ||
loadSync: function ( file ) { | ||
return new Node( file )._loadSync(); | ||
} | ||
}; |
207
lib/Node.js
@@ -1,50 +0,80 @@ | ||
var sander = require( 'sander' ), | ||
extractMap = require( './extractMap' ), | ||
encodeMappings = require( './encodeMappings' ), | ||
decodeMappings = require( './decodeMappings' ); | ||
var path = require( 'path' ), | ||
sander = require( 'sander' ), | ||
SourceMap = require( './SourceMap' ), | ||
getRelativePath = require( './utils/getRelativePath' ), | ||
extractMap = require( './utils/extractMap' ), | ||
encodeMappings = require( './utils/encodeMappings' ), | ||
decodeMappings = require( './utils/decodeMappings' ), | ||
getSourceMappingUrl = require( './utils/getSourceMappingUrl' ), | ||
getMapFromUrl = require( './utils/getMapFromUrl' ); | ||
var Node = function ( file ) { | ||
this.file = file; | ||
this.file = path.resolve( file ); | ||
this.sourcesContentByPath = {}; | ||
}; | ||
Node.prototype = { | ||
getContent: function () { | ||
_load: function () { | ||
var self = this; | ||
return sander.readFile( this.file ).then( String ).then( function ( str ) { | ||
// store for later use | ||
return ( self.content = str ); | ||
return sander.readFile( this.file ).then( String ).then( function ( content ) { | ||
var url; | ||
self.content = content; | ||
self.lines = content.split( '\n' ); | ||
url = getSourceMappingUrl( content ); | ||
if ( !url ) { | ||
self.isOriginalSource = true; | ||
return self; | ||
} else { | ||
return getMapFromUrl( url, self.file ).then( function ( map ) { | ||
var promises; | ||
self.map = map; | ||
self.mappings = decodeMappings( map.mappings ); | ||
self.sources = map.sources.map( function ( source ) { | ||
return new Node( resolveSourcePath( self, source ) ); | ||
}); | ||
promises = self.sources.map( function ( node ) { | ||
return node._load(); | ||
}); | ||
return sander.Promise.all( promises ); | ||
}).then( function () { | ||
getSourcesContent( self ); | ||
return self; | ||
}); | ||
} | ||
}); | ||
}, | ||
init: function () { | ||
var self = this; | ||
_loadSync: function () { | ||
var self = this, url, map; | ||
return this.getContent().then( function () { | ||
return extractMap( self ); | ||
}).then( function ( map ) { | ||
var promises; | ||
this.content = sander.readFileSync( this.file ).toString(); | ||
this.lines = this.content.split( '\n' ); | ||
self.map = map; | ||
self.mappings = decodeMappings( map.mappings ); | ||
url = getSourceMappingUrl( this.content ); | ||
self.sources = map.sources.map( function ( source ) { | ||
return new Node( resolveSourcePath( self, source ) ); | ||
}); | ||
if ( !url ) { | ||
self.isOriginalSource = true; | ||
} else { | ||
self.map = getMapFromUrl( url, this.file, true ); | ||
self.mappings = decodeMappings( self.map.mappings ); | ||
promises = self.sources.map( function ( node ) { | ||
return node.init(); | ||
self.sources = self.map.sources.map( function ( source ) { | ||
return new Node( resolveSourcePath( self, source ) )._loadSync(); | ||
}); | ||
return sander.Promise.all( promises ); | ||
}, function ( err ) { | ||
if ( err.code === 'SOURCEMAP_COMMENT_NOTFOUND' ) { | ||
self.isOriginalSource = true; | ||
} else { | ||
throw err; | ||
} | ||
}); | ||
getSourcesContent( self ); | ||
} | ||
return this; | ||
}, | ||
build: function () { | ||
apply: function ( options ) { | ||
var self = this, | ||
@@ -57,56 +87,49 @@ resolved, | ||
sourceIndices = {}, | ||
mappings; | ||
mappings, | ||
includeContent; | ||
resolved = this.mappings.map( function ( line, i ) { | ||
var resolved = []; | ||
options = options || {}; | ||
includeContent = options.includeContent !== false; | ||
resolved = this.mappings.map( function ( line ) { | ||
var result = []; | ||
line.forEach( function ( segment ) { | ||
var result, parent, mapping, nameIndex, sourceIndex; | ||
var source, traced, newSegment, sourceIndex, nameIndex; | ||
if ( !segment.length ) { | ||
result = {}; | ||
} | ||
if ( segment.length === 1 ) { | ||
// TODO not sure what to do here...? | ||
resolved.push({ generatedColumn: segment[0] }); | ||
resolved.push([ segment[0] ]); | ||
return; | ||
} | ||
parent = self.sources[ segment[1] ]; | ||
source = self.sources[ segment[1] ]; | ||
traced = source.trace( segment[2] + 1, segment[3], self.map.names[ segment[4] ] ); | ||
mapping = parent.trace( segment[2], segment[3], self.map.names[ segment[4] ] ); | ||
if ( mapping === null ) { | ||
if ( !traced ) { | ||
return; | ||
} | ||
// Store name references | ||
if ( mapping.name && !~names.indexOf( mapping.name ) ) { | ||
nameIndex = names.length; | ||
nameIndices[ mapping.name ] = nameIndex; | ||
names.push( mapping.name ); | ||
} | ||
// Store source references | ||
if ( !~sources.indexOf( mapping.source ) ) { | ||
sourceIndex = sources.indexOf( traced.source ); | ||
if ( !~sourceIndex ) { | ||
sourceIndex = sources.length; | ||
sourceIndices[ mapping.source ] = sourceIndex; | ||
sources.push( mapping.source ); | ||
sources.push( traced.source ); | ||
} | ||
result = [ | ||
segment[0], | ||
sources.indexOf( mapping.source ), | ||
mapping.sourceLine, | ||
mapping.sourceColumn | ||
]; | ||
newSegment = [ segment[0], sourceIndex, traced.line - 1, traced.column ]; | ||
if ( mapping.name ) { | ||
result.push( names.indexOf( mapping.name ) ); | ||
if ( traced.name ) { | ||
nameIndex = names.indexOf( traced.name ); | ||
if ( !~nameIndex ) { | ||
nameIndex = names.length; | ||
names.push( traced.name ); | ||
} | ||
newSegment.push( nameIndex ); | ||
} | ||
resolved.push( result ); | ||
result.push( newSegment ); | ||
}); | ||
return resolved; | ||
return result; | ||
}); | ||
@@ -116,19 +139,17 @@ | ||
map = { | ||
version: 3, | ||
file: this.file, | ||
sources: sources.map( function ( source ) { return source.file; }), | ||
sourcesContent: sources.map( function ( source ) { return source.content; }), | ||
return new SourceMap({ | ||
file: this.file.split( '/' ).pop(), | ||
sources: sources.map( function ( source ) { | ||
return getRelativePath( self.file, source ); | ||
}), | ||
sourcesContent: sources.map( function ( source ) { | ||
return options.includeContent ? self.sourcesContentByPath[ source ] : null; | ||
}), | ||
names: names, | ||
mappings: mappings, | ||
toString: function () { | ||
return JSON.stringify( this ); | ||
} | ||
}; | ||
return map; | ||
mappings: mappings | ||
}); | ||
}, | ||
trace: function ( lineIndex, columnIndex, name ) { | ||
var segments, segment, lastSegment, len, i, parent; | ||
var segments, line, segment, lastSegment, len, i, parent; | ||
@@ -139,5 +160,5 @@ // If this node doesn't have a source map, we treat it as | ||
return { | ||
source: this, | ||
sourceLine: lineIndex, | ||
sourceColumn: columnIndex, | ||
source: this.file, | ||
line: lineIndex, | ||
column: columnIndex, | ||
name: name | ||
@@ -149,3 +170,3 @@ }; | ||
// the intermediate file corresponds to in *its* source | ||
segments = this.mappings[ lineIndex ]; | ||
segments = this.mappings[ lineIndex - 1 ]; | ||
@@ -156,2 +177,10 @@ if ( !segments ) { | ||
if ( columnIndex === undefined ) { | ||
// we only have a line to go on. Use the first non-whitespace character | ||
line = this.lines[ lineIndex - 1 ]; | ||
leadingWhitespace = /^\s+/.exec( line ); | ||
columnIndex = leadingWhitespace ? leadingWhitespace[0].length : 0; | ||
} | ||
len = segments.length; | ||
@@ -164,3 +193,3 @@ | ||
parent = this.sources[ segment[1] ]; | ||
return parent.trace( segment[2], segment[3], this.map.names[ segment[4] ] || name ); | ||
return parent.trace( segment[2] + 1, segment[3], this.map.names[ segment[4] ] || name ); | ||
} | ||
@@ -178,6 +207,14 @@ | ||
function resolveSourcePath ( node, source ) { | ||
var path = require( 'path' ); | ||
// TODO handle sourceRoot | ||
return path.resolve( path.dirname( node.file ), source ); | ||
} | ||
function getSourcesContent ( node ) { | ||
node.sources.forEach( function ( source ) { | ||
node.sourcesContentByPath[ source.file ] = source.content; | ||
Object.keys( source.sourcesContentByPath ).forEach( function ( file ) { | ||
node.sourcesContentByPath[ file ] = source.sourcesContentByPath[ file ]; | ||
}); | ||
}); | ||
} |
{ | ||
"name": "sorcery", | ||
"description": "Resolve a chain of sourcemaps back to the original source", | ||
"version": "0.1.1", | ||
"version": "0.2.0", | ||
"author": "Rich Harris", | ||
@@ -10,2 +10,3 @@ "repository": "https://github.com/Rich-Harris/sorcery", | ||
"dependencies": { | ||
"minimist": "^1.1.0", | ||
"sander": "^0.1.6", | ||
@@ -16,4 +17,8 @@ "vlq": "^0.1.0" | ||
"coffee-script": "^1.8.0", | ||
"source-map": "^0.1.40", | ||
"uglify-js": "^2.4.15" | ||
}, | ||
"bin": { | ||
"sorcery": "bin/index.js" | ||
} | ||
} |
@@ -14,4 +14,8 @@ # sorcery.js | ||
## Installation | ||
## Usage | ||
### As a node module | ||
Install sorcery locally: | ||
```bash | ||
@@ -21,8 +25,67 @@ npm install sorcery | ||
```js | ||
var sorcery = require( 'sorcery' ); | ||
## Usage | ||
sorcery.load( 'some/generated/code.min.js' ).then( function ( chain ) { | ||
// generate a flattened sourcemap | ||
var map = chain.apply(); // { version: 3, file: 'code.min.js', ... } | ||
API still in flux, lots of work to do... instructions coming soon! Try cloning this repo and looking inside the `test` folder to get started. | ||
// get a JSON representation of the sourcemap | ||
map.toString(); // '{"version":3,"file":"code.min.js",...}' | ||
// get a data URI representation | ||
map.toUrl(); // 'data:application/json;charset=utf-8;base64,eyJ2ZXJ...' | ||
// find the origin of line x, column y. Returns an object with | ||
// `source`, `line`, `column` and (if applicable) `name` properties. | ||
// Note - for consistency with other tools, line numbers are always | ||
// one-based, column numbers are always zero-based. It's daft, I know. | ||
var loc = chain.trace( x, y ); | ||
}); | ||
// You can also use sorcery synchronously: | ||
var chain = sorcery.loadSync( 'some/generated/code.min.js' ); | ||
var map = chain.apply(); | ||
var loc = chain.trace( x, y ); | ||
``` | ||
### On the command line | ||
First, install sorcery globally: | ||
```bash | ||
npm install -g sorcery | ||
``` | ||
``` | ||
Usage: | ||
sorcery [options] | ||
Options: | ||
-h, --help Show help message | ||
-v, --version Show version | ||
-i, --input <file> Input file | ||
-o, --output <file> Output file (if absent, will overwrite input) | ||
-d, --datauri Append map as a data URI, rather than separate file | ||
-x, --excludeContent Don't populate the sourcesContent array | ||
``` | ||
Examples: | ||
```bash | ||
# overwrite sourcemap in place (will write map to | ||
# some/generated/code.min.js.map, and update | ||
# sourceMappingURL comment if necessary | ||
sorcery -i some/generated/code.min.js | ||
# append flattened sourcemap as an inline data URI | ||
# (will delete existing .map file, if applicable) | ||
sorcery -i -d some/generated/code.min.js | ||
# write to a new file (will create newfile.js and | ||
# newfile.js.map) | ||
sorcery -i some/generated/code.min.js -o newfile.js | ||
``` | ||
## License | ||
@@ -29,0 +92,0 @@ |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
19755
26
452
93
3
3
2
1
+ Addedminimist@^1.1.0