Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Add `--source-map` build option. [closes #161]

  • Loading branch information...
commit 929d76eb19280bdadd890a97b03999c4b4ae5632 1 parent 74176a5
@jdalton jdalton authored
Showing with 134 additions and 50 deletions.
  1. +8 −7 README.md
  2. +41 −20 build.js
  3. +84 −22 build/minify.js
  4. +1 −1  build/post-compile.js
View
15 README.md
@@ -154,13 +154,14 @@ Unless specified by `-o` or `--output`, all files created are saved to the curre
The following options are also supported:
- * `-c`, `--stdout`     Write output to standard output
- * `-d`, `--debug`       Write only the debug output
- * `-h`, `--help`         Display help information
- * `-m`, `--minify`     Write only the minified output
- * `-o`, `--output`     Write output to a given path/filename
- * `-s`, `--silent`     Skip status updates normally logged to the console
- * `-V`, `--version`   Output current version of Lo-Dash
+ * `-c`, `--stdout`          Write output to standard output
+ * `-d`, `--debug`            Write only the debug output
+ * `-h`, `--help`              Display help information
+ * `-m`, `--minify`          Write only the minified output
+ * `-o`, `--output`          Write output to a given path/filename
+ * `-p`, `--source-map`   Generate a source map for the minified output
+ * `-s`, `--silent`          Skip status updates normally logged to the console
+ * `-V`, `--version`        Output current version of Lo-Dash
@mathiasbynens Owner

Why not use a table for this? Cleaner than having to           all over the place, and much easier to maintain.

@jdalton Owner
jdalton added a note

Yap, but I didn't like the look of the table, and considering most people view the readme through github/npm/jam repos the look is more important than the markup markdown.

what about just <pre>-wrapping it then and using normal spaces?

@jdalton Owner
jdalton added a note

Ya, good point. Will do.

@jdalton Owner
jdalton added a note

@leeoniya Drat. I tried it and that makes it look like it's wrapped in a generic code block, so the content loses its styling.

https://gist.github.com/4566362

not ideal, but better?

@jdalton Owner
jdalton added a note

@leeoniya That's great! @mathiasbynens if you've got some time to spare could you update with that?

it sucks you gotta choose whether to cater to fixed or variable-width display fonts, i'd prefer the former but ultimately it boils down to the larger audience.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
The `lodash` command-line utility is available when Lo-Dash is installed as a global package (i.e. `npm install -g lodash`).
View
61 build.js
@@ -580,13 +580,14 @@
'',
' Options:',
'',
- ' -c, --stdout Write output to standard output',
- ' -d, --debug Write only the debug output',
- ' -h, --help Display help information',
- ' -m, --minify Write only the minified output',
- ' -o, --output Write output to a given path/filename',
- ' -s, --silent Skip status updates normally logged to the console',
- ' -V, --version Output current version of Lo-Dash',
+ ' -c, --stdout Write output to standard output',
+ ' -d, --debug Write only the debug output',
+ ' -h, --help Display help information',
+ ' -m, --minify Write only the minified output',
+ ' -o, --output Write output to a given path/filename',
+ ' -p, --source-map Generate a source map for the minified output',
+ ' -s, --silent Skip status updates normally logged to the console',
+ ' -V, --version Output current version of Lo-Dash',
''
].join('\n'));
}
@@ -1182,7 +1183,7 @@
// used to report invalid command-line arguments
var invalidArgs = _.reject(options.slice(options[0] == 'node' ? 2 : 0), function(value, index, options) {
if (/^(?:-o|--output)$/.test(options[index - 1]) ||
- /^(?:category|exclude|exports|iife|include|moduleId|minus|plus|settings|template)=.*$/i.test(value)) {
+ /^(?:category|exclude|exports|iife|include|moduleId|minus|plus|settings|template)=.*$/.test(value)) {
return true;
}
return [
@@ -1198,6 +1199,7 @@
'-h', '--help',
'-m', '--minify',
'-o', '--output',
+ '-p', '--source-map',
'-s', '--silent',
'-V', '--version'
].indexOf(value) > -1;
@@ -1241,6 +1243,9 @@
return match ? match[1] : result;
}, null);
+ // the path to the source file
+ var filePath = path.join(__dirname, 'lodash.js');
+
// flag used to specify a Backbone build
var isBackbone = options.indexOf('backbone') > -1;
@@ -1256,8 +1261,11 @@
// flag used to specify an Underscore build
var isUnderscore = options.indexOf('underscore') > -1;
+ // flag used to specify creating a source map for the minified source
+ var isMapped = options.indexOf('-p') > -1 || options.indexOf('--source-map') > -1;
+
// flag used to specify only creating the minified build
- var isMinify = !isDebug && options.indexOf('-m') > -1 || options.indexOf('--minify')> -1;
+ var isMinify = options.indexOf('-m') > -1 || options.indexOf('--minify') > -1;
// flag used to specify a mobile build
var isMobile = !isLegacy && (isCSP || isUnderscore || options.indexOf('mobile') > -1);
@@ -1325,7 +1333,7 @@
var isTemplate = !!templatePattern;
// the lodash.js source
- var source = fs.readFileSync(path.join(__dirname, 'lodash.js'), 'utf8');
+ var source = fs.readFileSync(filePath, 'utf8');
// flag used to specify replacing Lo-Dash's `_.clone` with Underscore's
var useUnderscoreClone = isUnderscore;
@@ -2123,7 +2131,7 @@
/*------------------------------------------------------------------------*/
// used to specify creating a custom build
- var isCustom = isBackbone || isLegacy || isMobile || isStrict || isUnderscore ||
+ var isCustom = isBackbone || isLegacy || isMapped || isMobile || isStrict || isUnderscore ||
/(?:category|exclude|exports|iife|include|minus|plus)=/.test(options) ||
!_.isEqual(exportsOptions, exportsAll);
@@ -2144,7 +2152,10 @@
stdout.write(debugSource);
callback(debugSource);
} else if (!isStdOut) {
- callback(debugSource, (isDebug && outputPath) || path.join(cwd, basename + '.js'));
+ callback({
+ 'source': debugSource,
+ 'outputPath': (isDebug && outputPath) || path.join(cwd, basename + '.js')
+ });
}
}
// begin the minification process
@@ -2152,22 +2163,24 @@
outputPath || (outputPath = path.join(cwd, basename + '.min.js'));
minify(source, {
+ 'filePath': filePath,
+ 'isMapped': isMapped,
'isSilent': isSilent,
'isTemplate': isTemplate,
'outputPath': outputPath,
- 'onComplete': function(source) {
+ 'onComplete': function(data) {
// inject "use strict" directive
if (isStrict) {
- source = source.replace(/^([\s\S]*?function[^{]+{)([^"'])/, '$1"use strict";$2');
+ data.source = data.source.replace(/^([\s\S]*?function[^{]+{)([^"'])/, '$1"use strict";$2');
}
if (isCustom) {
- source = addCommandsToHeader(source, options);
+ data.source = addCommandsToHeader(data.source, options);
}
if (isStdOut) {
- stdout.write(source);
- callback(source);
+ stdout.write(data.source);
+ callback(data);
} else {
- callback(source, outputPath);
+ callback(data);
}
}
});
@@ -2182,8 +2195,16 @@
}
else {
// or invoked directly
- build(process.argv, function(source, filePath) {
- filePath && fs.writeFileSync(filePath, source, 'utf8');
+ build(process.argv, function(data) {
+ var outputPath = data.outputPath,
+ sourceMap = data.sourceMap;
+
+ if (outputPath) {
+ fs.writeFileSync(outputPath, data.source, 'utf8');
+ if (sourceMap) {
+ fs.writeFileSync(path.join(path.dirname(outputPath), path.basename(outputPath, '.js') + '.map'), sourceMap, 'utf8');
+ }
+ }
});
}
}());
View
106 build/minify.js
@@ -89,6 +89,7 @@
options = source;
var filePath = options[options.length - 1],
+ isMapped = options.indexOf('-p') > -1 || options.indexOf('--source-map') > -1,
isSilent = options.indexOf('-s') > -1 || options.indexOf('--silent') > -1,
isTemplate = options.indexOf('-t') > -1 || options.indexOf('--template') > -1,
outputPath = path.join(path.dirname(filePath), path.basename(filePath, '.js') + '.min.js');
@@ -102,6 +103,8 @@
}, outputPath);
options = {
+ 'filePath': filePath,
+ 'isMapped': isMapped,
'isSilent': isSilent,
'isTemplate': isTemplate,
'outputPath': outputPath
@@ -157,6 +160,8 @@
this.hybrid = { 'simple': {}, 'advanced': {} };
this.uglified = {};
+ this.filePath = options.filePath;
+ this.isMapped = !!options.isMapped;
this.isSilent = !!options.isSilent;
this.isTemplate = !!options.isTemplate;
this.outputPath = options.outputPath;
@@ -164,8 +169,14 @@
source = preprocess(source, options);
this.source = source;
- this.onComplete = options.onComplete || function(source) {
- fs.writeFileSync(this.outputPath, source, 'utf8');
+ this.onComplete = options.onComplete || function(data) {
+ var outputPath = this.outputPath,
+ sourceMap = data.sourceMap;
+
+ fs.writeFileSync(outputPath, data.source, 'utf8');
+ if (sourceMap) {
+ fs.writeFileSync(getMapPath(outputPath), sourceMap, 'utf8');
+ }
};
// begin the minification process
@@ -182,8 +193,7 @@
* @private
* @param {Object} options The options object.
* id - The Git object ID of the `.tar.gz` file.
- * onComplete - The function, invoked with one argument (exception),
- * called once the extraction has finished.
+ * onComplete - The function called once the extraction has finished.
* path - The path of the extraction directory.
* title - The dependency's title used in status updates logged to the console.
*/
@@ -242,6 +252,17 @@
});
}
+ /**
+ * Resolves the source map path from the given output path.
+ *
+ * @private
+ * @param {String} outputPath The output path.
+ * @returns {String} Returns the source map path.
+ */
+ function getMapPath(outputPath) {
+ return path.join(path.dirname(outputPath), path.basename(outputPath, '.js') + '.map');
+ }
+
/*--------------------------------------------------------------------------*/
/**
@@ -254,17 +275,26 @@
* @param {Function} callback The function called once the process has completed.
*/
function closureCompile(source, mode, callback) {
+ var filePath = this.filePath,
+ outputPath = this.outputPath,
+ isMapped = this.isMapped,
+ mapPath = getMapPath(outputPath),
+ options = closureOptions.slice();
+
// use simple optimizations when minifying template files
- var options = closureOptions.slice();
options.push('--compilation_level=' + optimizationModes[this.isTemplate ? 'simple' : mode]);
+ if (isMapped) {
+ options.push('--create_source_map=' + mapPath, '--source_map_format=V3');
+ }
+
// the standard error stream, standard output stream, and the Closure Compiler process
var error = '',
output = '',
compiler = spawn('java', ['-jar', closurePath].concat(options));
if (!this.isSilent) {
- console.log('Compressing ' + path.basename(this.outputPath, '.js') + ' using the Closure Compiler (' + mode + ')...');
+ console.log('Compressing ' + path.basename(outputPath, '.js') + ' using the Closure Compiler (' + mode + ')...');
}
compiler.stdout.on('data', function(data) {
// append the data to the output stream
@@ -282,7 +312,18 @@
var exception = new Error(error);
exception.status = status;
}
- callback(exception, output);
+ if (isMapped) {
+ var mapOutput = fs.readFileSync(mapPath, 'utf8');
+ fs.unlinkSync(mapPath);
+
+ output = output
+ .replace(/[\s;]*$/, '\n//@ sourceMappingURL=' + path.basename(mapPath));
+
+ mapOutput = mapOutput
+ .replace(/("file":)""/, '$1"' + path.basename(outputPath) + '"')
+ .replace(/("sources":)\["stdin"\]/, '$1["' + path.basename(filePath) + '"]');
+ }
+ callback(exception, output, mapOutput);
});
// proxy the standard input to the Closure Compiler
@@ -350,13 +391,17 @@
* @private
* @param {Object|Undefined} exception The error object.
* @param {String} result The resulting minified source.
+ * @param {String} map The source map output.
*/
- function onClosureSimpleCompile(exception, result) {
+ function onClosureSimpleCompile(exception, result, map) {
if (exception) {
throw exception;
}
result = postprocess(result);
- this.compiled.simple.source = result;
+
+ var simple = this.compiled.simple;
+ simple.source = result;
+ simple.sourceMap = map;
zlib.gzip(result, onClosureSimpleGzip.bind(this));
}
@@ -386,13 +431,17 @@
* @private
* @param {Object|Undefined} exception The error object.
* @param {String} result The resulting minified source.
+ * @param {String} map The source map output.
*/
- function onClosureAdvancedCompile(exception, result) {
+ function onClosureAdvancedCompile(exception, result, map) {
if (exception) {
throw exception;
}
result = postprocess(result);
- this.compiled.advanced.source = result;
+
+ var advanced = this.compiled.advanced;
+ advanced.source = result;
+ advanced.sourceMap = map;
zlib.gzip(result, onClosureAdvancedGzip.bind(this));
}
@@ -412,8 +461,14 @@
}
this.compiled.advanced.gzip = result;
- // next, minify the source using only UglifyJS
- uglify.call(this, this.source, 'UglifyJS', onUglify.bind(this));
+ // if mapped, finish by choosing the smallest compressed file
+ if (this.isMapped) {
+ onComplete.call(this);
+ }
+ // else, minify the source using UglifyJS
+ else {
+ uglify.call(this, this.source, 'UglifyJS', onUglify.bind(this));
+ }
}
/**
@@ -538,18 +593,25 @@
// select the smallest gzipped file and use its minified counterpart as the
// official minified release (ties go to the Closure Compiler)
- var min = Math.min(
- compiledSimple.gzip.length,
- compiledAdvanced.gzip.length,
- uglified.gzip.length,
- hybridSimple.gzip.length,
- hybridAdvanced.gzip.length
- );
+ var min = this.isMapped
+ ? Math.min(
+ compiledSimple.gzip.length,
+ compiledAdvanced.gzip.length
+ )
+ : Math.min(
+ compiledSimple.gzip.length,
+ compiledAdvanced.gzip.length,
+ uglified.gzip.length,
+ hybridSimple.gzip.length,
+ hybridAdvanced.gzip.length
+ );
// pass the minified source to the "onComplete" callback
[compiledSimple, compiledAdvanced, uglified, hybridSimple, hybridAdvanced].some(function(data) {
- if (data.gzip.length == min) {
- this.onComplete(data.source);
+ var gzip = data.gzip;
+ if (gzip && gzip.length == min) {
+ data.outputPath = this.outputPath;
+ this.onComplete(data);
}
}, this);
}
View
2  build/post-compile.js
@@ -49,7 +49,7 @@
// add trailing semicolon
if (source) {
- source = source.replace(/[\s;]*$/, ';');
+ source = source.replace(/[\s;]*(\n\/\/.+)?$/, ';$1');
}
// exit early if version snippet isn't found
var snippet = /VERSION\s*[=:]\s*([\'"])(.*?)\1/.exec(source);
Please sign in to comment.
Something went wrong with that request. Please try again.