Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

executable file 298 lines (288 sloc) 13.5 kb
#!/usr/bin/env node
var optimist = require('optimist'),
commandLineOptions = optimist
.usage('$0 --root <inputRootDirectory> --outroot <dir> [options] <htmlFile(s)>')
.options('h', {
alias: 'help',
describe: 'Show this help',
type: 'boolean',
default: false
})
.options('root', {
describe: 'Path to your web root (will be deduced from your input files if not specified)',
type: 'string',
demand: false
})
.options('outroot', {
describe: 'Path to the output folder. Will be generated if non-existing',
type: 'string',
demand: true
})
.options('cdnroot', {
describe: 'URI root where the static assets will be deployed. Must be either an absolute or a protocol-relative url.',
type: 'string',
demand: false
})
.options('cdnoutroot', {
describe: 'Path to the CDN output folder. Will be generated if non-existing',
type: 'string',
demand: false
})
.options('exclude', {
describe: 'Url pattern to exclude from the build. Supports * wildcards. You can create multiple of these: --exclude *.php --exclude http://example.com/*.gif',
type: 'string',
demand: false
})
.options('label', {
describe: 'Registers labels as custom protocols for path resolving. You can create multiple of these: --label <labelName>=<dir> --label <otherLabelName>=<otherDir>',
type: 'string',
demand: false
})
.options('parentdir', {
describe: 'If an unknown label (scheme) is found, look for at parent dir of that name before failing (breaks custom protocols)',
type: 'boolean',
demand: false
})
.options('locales', {
describe: 'Comma-separated list of locales to build seperate versions for',
type: 'string',
demand: false
})
.options('defaultlocale', {
describe: 'The locale of the default value in TR statements and tags with a data-i18n attribute',
type: 'string',
default: 'en'
})
.options('localecookiename', {
describe: 'The name of your locale cookie (exposed as LOCALECOOKIENAME)',
type: 'string',
default: 'en'
})
.options('optimizepngs', {
describe: '(deprecated, please use --optimizeimages or --pngquant --pngcrush --optipng) Tries optimal palette reduction, removes ancillary chunks and tries for better compression.',
type: 'boolean',
default: false
})
.options('optimizejpegs', {
describe: '(deprecated, please use --jpegtran or --optimizeimages) Runs jpegtran optimization on all jpeg images',
type: 'boolean',
default: false
})
.options('jpegtran', {
describe: 'Runs jpegtran optimization on all jpeg images',
type: 'boolean',
default: false
})
.options('pngquant', {
describe: 'Runs pngquant on all png images',
type: 'boolean',
default: false
})
.options('pngcrush', {
describe: 'Runs pngcrush on all png images',
type: 'boolean',
default: false
})
.options('optipng', {
describe: 'Runs optipng on all png images',
type: 'boolean',
default: false
})
.options('inlinesize', {
describe: 'Inline CSS backgrounds below this threshold as data-uris',
default: 8192
})
.options('deferscripts', {
describe: 'Sets the "defer" attribute on all script tags',
type: 'boolean',
default: false
})
.options('asyncscripts', {
describe: 'Sets the "async" attribute on all script tags',
type: 'boolean',
default: false
})
.options('define', {
alias: 'd',
describe: '--define SYMBOL[=value] will be passed to UglifyJS as is (see the docs at https://github.com/mishoo/UglifyJS#usage). Remember to protect quotes from the shell, eg. --define foo=\\"bar\\".',
type: 'string'
})
.options('nocompress', {
describe: 'Prettifies HTML, CSS and Javascript for easier debugging',
type: 'boolean',
default: false
})
.options('mangletoplevel', {
describe: 'Wraps your javascript code in a function literal that pulls global variables into local variables for better minification. WARNING: This may break your JS',
type: 'boolean',
default: false
})
.options('manifest', {
describe: 'Generates an appcache manifest file with all static assets included',
type: 'boolean',
default: false
})
.options('negotiatemanifest', {
describe: 'Removes the locale id from the <html manifest="..."> references so all manifests are assumed to be accessible from the same url. Useful if you want the browser to pick up the right cache manifest and HTML after a locale change (your static file server needs to support content negotiation). Only makes sense when both --manifest and --locale have been specified',
type: 'boolean',
default: false
})
.options('less', {
describe: 'Translates .less files to CSS',
type: 'boolean',
default: false
})
.options('stoponwarning', {
describe: 'Whether to stop with a non-zero exit code when a warning is encountered',
type: 'boolean',
default: false
})
.options('version', {
describe: 'Adds or updates <meta http-equiv="Content-Version" content="..."> to the specified value. Use {0} to refer to the current value, eg. --version {0}/production or --version `git describe --long --tags --always --dirty 2>/dev/null || echo unknown`',
type: 'string'
})
.check(function (argv) {
return typeof argv.inlinesize === 'number';
})
.wrap(72)
.argv;
if (commandLineOptions.h) {
optimist.showHelp();
process.exit(1);
}
var _ = require('underscore'),
util = require('util'),
uglifyJs = require('uglify-js-papandreou'),
AssetGraph = require('../lib/AssetGraph'),
i18nTools = require('../lib/i18nTools'),
query = AssetGraph.query,
urlTools = require('assetgraph/lib/util/urlTools'),
uglifyAst = require('uglifyast'),
outRoot = urlTools.fsDirToFileUrl(commandLineOptions.outroot),
cdnRoot = commandLineOptions.cdnroot && urlTools.ensureTrailingSlash(commandLineOptions.cdnroot),
fullCdnRoot = (/^\/\//.test(cdnRoot) ? 'http:' : '') + cdnRoot,
cdnOutRoot = commandLineOptions.cdnoutroot && urlTools.fsDirToFileUrl(commandLineOptions.cdnoutroot),
blacklistUrlRegExp = /^$/,
rootUrl = commandLineOptions.root && urlTools.urlOrFsPathToUrl(commandLineOptions.root, true),
localeIds = commandLineOptions.locales && _.flatten(_.flatten([commandLineOptions.locales]).map(function (localeId) {
return localeId.split(",");
})).map(i18nTools.normalizeLocaleId),
defaultLocaleId = commandLineOptions.defaultlocale && i18nTools.normalizeLocaleId(commandLineOptions.defaultlocale),
localizationInfoObject = {},
defines = {},
inputUrls;
(commandLineOptions.define ? _.flatten(_.flatten([commandLineOptions.define])) : []).forEach(function (define) {
var matchDefine = define.match(/^(\w+)(?:=(.*))?$/);
if (matchDefine) {
var valueAst;
if (matchDefine[2]) {
try {
valueAst = uglifyAst.parseExpression(matchDefine[2]);
} catch (e) {
console.error('Invalid --define ' + matchDefine[1] + ': Could not parse ' + matchDefine[2] + ' as a JavaScript expression. Missing shell escapes?');
console.error(e.message + ' (line ' + e.line + ', column ' + e.col + ')');
process.exit(1);
}
} else {
valueAst = ['name', 'true'];
}
defines[matchDefine[1]] = valueAst;
}
});
if (commandLineOptions._.length > 0) {
inputUrls = commandLineOptions._.map(function (urlOrFsPath) {
return urlTools.urlOrFsPathToUrl(urlOrFsPath, false);
});
if (!rootUrl) {
rootUrl = urlTools.findCommonUrlPrefix(inputUrls.filter(function (inputUrl) {
return /^file:/.test(inputUrl);
}));
if (rootUrl) {
console.warn("Guessing --root from input files: " + rootUrl);
}
}
} else if (rootUrl && /^file:/.test(rootUrl)) {
inputUrls = [rootUrl + '**/*.html'];
console.warn('No input files specified, defaulting to ' + inputUrls[0]);
} else {
throw new Error("No input files and no --root specified (or it isn't file:), cannot proceed");
}
if (commandLineOptions.exclude) {
blacklistUrlRegExp = new RegExp('(?:' +
_.flatten(_.flatten([commandLineOptions.exclude])).map(function (wildcard) {
return wildcard.replace(/[\.\+\{\}\[\]\(\)\?\^\$]/g, '\\$&').replace(/\*/g, '.*?');
}).join('|') +
')');
}
new AssetGraph({root: rootUrl})
.on('afterTransform', function (transform, elapsedTime) {
console.log((elapsedTime / 1000).toFixed(3) + " secs: " + transform.name);
})
.on('warn', function (err) {
// These are way too noisy
if (err.relationType !== 'JavaScriptCommonJsRequire') {
console.warn((err.asset ? err.asset.urlOrDescription + ': ' : '') + err.message);
if (commandLineOptions.stoponwarning) {
process.exit(1);
}
}
})
.on('error', function (err) {
console.error((err.asset ? err.asset.urlOrDescription + ': ' : '') + err.stack);
process.exit(1);
})
.registerRequireJsConfig()
.registerLabelsAsCustomProtocols(commandLineOptions.label, {installFindParentDirectoryAsDefault: commandLineOptions.parentdir})
.loadAssets(inputUrls)
.buildProduction({
version: commandLineOptions.version,
less: commandLineOptions.less,
blacklistUrlRegExp: blacklistUrlRegExp,
jpegtran: commandLineOptions.jpegtran || commandLineOptions.optimizeimages || commandLineOptions.optimizejpegs,
pngquant: commandLineOptions.pngquant || commandLineOptions.optimizeimages || commandLineOptions.optimizepngs,
pngcrush: commandLineOptions.pngcrush || commandLineOptions.optimizeimages || commandLineOptions.optimizepngs,
optipng: commandLineOptions.optipng || commandLineOptions.optimizeimages || commandLineOptions.optimizepngs,
inlineSize: commandLineOptions.inlinesize,
mangleTopLevel: commandLineOptions.mangletoplevel,
defines: defines,
localeIds: localeIds,
localeCookieName: commandLineOptions.localecookiename,
localizationInfoObject: localizationInfoObject,
defaultLocaleId: defaultLocaleId,
manifest: commandLineOptions.manifest,
negotiateManifest: commandLineOptions.negotiatemanifest,
asyncScripts: commandLineOptions.asyncscripts,
deferScripts: commandLineOptions.deferscripts,
cdnRoot: cdnRoot,
prettyPrint: commandLineOptions.prettyPrint
})
.writeAssetsToDisc({url: /^file:/, isLoaded: true}, outRoot)
.if(cdnRoot)
.writeAssetsToDisc({url: query.createPrefixMatcher(fullCdnRoot), isLoaded: true}, cdnOutRoot || outRoot, fullCdnRoot)
.endif()
.writeStatsToStderr()
.run(function (err) {
if (err) {
throw err;
}
if (localeIds) {
var missingKeys = Object.keys(localizationInfoObject.localeIdsByMissingKey || {});
if (missingKeys.length > 0) {
console.warn('The following keys were missing:\n ' + missingKeys.map(function (missingKey) {
return missingKey + ' (' + localizationInfoObject.localeIdsByMissingKey[missingKey].join(',') + ')';
}).join('\n '));
}
var defaultValueMismatchKeys = Object.keys(localizationInfoObject.defaultValueMismatchesByKey || {});
if (defaultValueMismatchKeys.length > 0) {
console.warn('The following keys had mismatching default and/or ' + defaultLocaleId + ' values:\n ' + defaultValueMismatchKeys.map(function (defaultValueMismatchKey) {
return defaultValueMismatchKey + ':\n ' + util.inspect(localizationInfoObject.defaultValueMismatchesByKey[defaultValueMismatchKey], false, 99);
}).join('\n '));
}
var whitespaceWarningKeys = Object.keys(localizationInfoObject.whitespaceWarningsByKey || {});
if (whitespaceWarningKeys.length > 0) {
console.warn('The following keys had leading or trailing whitespace:\n ' + whitespaceWarningKeys.map(function (whitespaceWarningKey) {
return whitespaceWarningKey + ':\n ' + util.inspect(localizationInfoObject.whitespaceWarningsByKey[whitespaceWarningKey], false, 99);
}).join('\n '));
}
}
});
Jump to Line
Something went wrong with that request. Please try again.