2
2
3
3
"use strict" ;
4
4
5
+ var fs = require ( 'fs' ) ;
6
+ var path = require ( 'path' ) ;
7
+
5
8
function countLines ( s ) {
6
9
var count = 0 ;
7
10
for ( var i = 0 , l = s . length ; i < l ; i ++ ) {
@@ -63,21 +66,39 @@ function extractComments(source, commentHandler) {
63
66
}
64
67
}
65
68
66
- function generateMap ( fileName , sourceRoot , mapFileBaseName , generatedLineOffset ) {
67
- var fs = require ( 'fs' ) ;
68
- var path = require ( 'path' ) ;
69
+ function getMappings ( source ) {
70
+ // generatedLineNumber -> { originalLineNumber, originalFileName }
71
+ var mappings = { } ;
72
+ extractComments ( source , function ( content , generatedLineNumber ) {
73
+ var matches = / @ l i n e ( \d + ) (?: " ( [ ^ " ] * ) " ) ? / . exec ( content ) ;
74
+ if ( matches === null ) return ;
75
+ var originalFileName = matches [ 2 ] ;
76
+ mappings [ generatedLineNumber ] = {
77
+ originalLineNumber : parseInt ( matches [ 1 ] , 10 ) ,
78
+ originalFileName : originalFileName
79
+ }
80
+ } ) ;
81
+ return mappings ;
82
+ }
83
+
84
+ function generateMap ( mappings , sourceRoot , mapFileBaseName , generatedLineOffset ) {
69
85
var SourceMapGenerator = require ( 'source-map' ) . SourceMapGenerator ;
70
86
71
87
var generator = new SourceMapGenerator ( { file : mapFileBaseName } ) ;
72
- var generatedSource = fs . readFileSync ( fileName , 'utf-8' ) ;
73
88
var seenFiles = Object . create ( null ) ;
74
89
75
- extractComments ( generatedSource , function ( content , generatedLineNumber ) {
76
- var matches = / @ l i n e ( \d + ) " ( [ ^ " ] * ) " / . exec ( content ) ;
77
- if ( matches === null ) return ;
78
- var originalLineNumber = parseInt ( matches [ 1 ] , 10 ) ;
79
- var originalFileName = matches [ 2 ] ;
90
+ for ( var generatedLineNumber in mappings ) {
91
+ var generatedLineNumber = parseInt ( generatedLineNumber , 10 ) ;
92
+ var mapping = mappings [ generatedLineNumber ] ;
93
+ var originalFileName = mapping . originalFileName ;
94
+ generator . addMapping ( {
95
+ generated : { line : generatedLineNumber + generatedLineOffset , column : 0 } ,
96
+ original : { line : mapping . originalLineNumber , column : 0 } ,
97
+ source : originalFileName
98
+ } ) ;
80
99
100
+ // we could call setSourceContent repeatedly, but readFileSync is slow, so
101
+ // avoid doing it unnecessarily
81
102
if ( ! ( originalFileName in seenFiles ) ) {
82
103
seenFiles [ originalFileName ] = true ;
83
104
var rootedPath = originalFileName [ 0 ] === path . sep ?
@@ -89,33 +110,64 @@ function generateMap(fileName, sourceRoot, mapFileBaseName, generatedLineOffset)
89
110
" at " + rootedPath ) ;
90
111
}
91
112
}
113
+ }
92
114
93
- generator . addMapping ( {
94
- generated : { line : generatedLineNumber + generatedLineOffset , column : 0 } ,
95
- original : { line : originalLineNumber , column : 0 } ,
96
- source : originalFileName
97
- } ) ;
98
- } ) ;
99
-
100
- var mapFileName = mapFileBaseName + '.map' ;
101
- fs . writeFileSync ( mapFileName , generator . toString ( ) ) ;
115
+ fs . writeFileSync ( mapFileBaseName + '.map' , generator . toString ( ) ) ;
116
+ }
102
117
103
- var lastLine = generatedSource . slice ( generatedSource . lastIndexOf ( '\n' ) ) ;
118
+ function appendMappingURL ( fileName , source , mapFileName ) {
119
+ var lastLine = source . slice ( source . lastIndexOf ( '\n' ) ) ;
104
120
if ( ! / s o u r c e M a p p i n g U R L / . test ( lastLine ) )
105
121
fs . appendFileSync ( fileName , '//@ sourceMappingURL=' + path . basename ( mapFileName ) ) ;
106
122
}
107
123
124
+ function parseArgs ( args ) {
125
+ var rv = { _ : [ ] } ; // unflagged args go into `_`; similar to the optimist library
126
+ for ( var i = 0 ; i < args . length ; i ++ ) {
127
+ if ( / ^ - - / . test ( args [ i ] ) ) rv [ args [ i ] . slice ( 2 ) ] = args [ ++ i ] ;
128
+ else rv . _ . push ( args [ i ] ) ;
129
+ }
130
+ return rv ;
131
+ }
132
+
108
133
if ( require . main === module ) {
109
134
if ( process . argv . length < 3 ) {
110
- console . log ( 'Usage: ./sourcemapper.js <filename> <source root (default: .)> ' +
111
- '<map file basename (default: filename)>' +
112
- '<generated line offset (default: 0)>' ) ;
135
+ console . log ( 'Usage: ./sourcemapper.js <original js> <optimized js file ...> \\\n' +
136
+ '\t--sourceRoot <default "."> \\\n' +
137
+ '\t--mapFileBaseName <default `filename`> \\\n' +
138
+ '\t--offset <default 0>' ) ;
113
139
process . exit ( 1 ) ;
114
140
} else {
115
- var sourceRoot = process . argv . length > 3 ? process . argv [ 3 ] : "." ;
116
- var mapFileBaseName = process . argv . length > 4 ? process . argv [ 4 ] : process . argv [ 2 ] ;
117
- var generatedLineOffset = process . argv . length > 5 ?
118
- parseInt ( process . argv [ 5 ] , 10 ) : 0 ;
119
- generateMap ( process . argv [ 2 ] , sourceRoot , mapFileBaseName , generatedLineOffset ) ;
141
+ var opts = parseArgs ( process . argv . slice ( 2 ) ) ;
142
+ var fileName = opts . _ [ 0 ] ;
143
+ var sourceRoot = opts . sourceRoot ? opts . sourceRoot : "." ;
144
+ var mapFileBaseName = opts . mapFileBaseName ? opts . mapFileBaseName : fileName ;
145
+ var generatedLineOffset = opts . offset ? parseInt ( opts . offset , 10 ) : 0 ;
146
+
147
+ var generatedSource = fs . readFileSync ( fileName , 'utf-8' ) ;
148
+ var source = generatedSource ;
149
+ var mappings = getMappings ( generatedSource ) ;
150
+ for ( var i = 1 , l = opts . _ . length ; i < l ; i ++ ) {
151
+ var optimizedSource = fs . readFileSync ( opts . _ [ i ] , 'utf-8' )
152
+ var optimizedMappings = getMappings ( optimizedSource ) ;
153
+ var newMappings = { } ;
154
+ // uglify processes the code between EMSCRIPTEN_START_FUNCS and
155
+ // EMSCRIPTEN_END_FUNCS, so its line number maps are relative to those
156
+ // markers. we correct for that here.
157
+ var startFuncsLineNumber = countLines (
158
+ source . slice ( 0 , source . indexOf ( '// EMSCRIPTEN_START_FUNCS' ) ) ) + 2 ;
159
+ for ( var line in optimizedMappings ) {
160
+ var originalLineNumber = optimizedMappings [ line ] . originalLineNumber + startFuncsLineNumber ;
161
+ if ( originalLineNumber in mappings ) {
162
+ newMappings [ line ] = mappings [ originalLineNumber ] ;
163
+ }
164
+ }
165
+ mappings = newMappings ;
166
+ source = optimizedSource ;
167
+ }
168
+
169
+ generateMap ( mappings , sourceRoot , mapFileBaseName , generatedLineOffset ) ;
170
+ appendMappingURL ( opts . _ [ opts . _ . length - 1 ] , generatedSource ,
171
+ opts . mapFileBaseName + '.map' ) ;
120
172
}
121
173
}
0 commit comments