Files
angular.js/lib/grunt/utils.js
T
Georgios Kalpakas 6304cde2fc chore(build): fix version placeholder matching
During the `build` task, the version placeholders will be replaced with the actual values using a
RegExp, which expects the placeholders to be surrounded by double quotes. By replacing the quotes
from double to single in #15011, the RegExp was not able to match the placeholders.

(For reference, the RegExps that match and replace the version placeholders are in
[lib/grunt/utils.js][1].)

[1]: https://github.com/angular/angular.js/blob/859348c7f61ff5f93b9f81eb7f46842bd018d8e3/lib/grunt/utils.js#L125-L130

Closes #15016
2016-08-12 18:18:30 +03:00

297 lines
9.9 KiB
JavaScript

'use strict';
var fs = require('fs');
var shell = require('shelljs');
var grunt = require('grunt');
var spawn = require('child_process').spawn;
var CSP_CSS_HEADER = '/* Include this file in your html if you are using the CSP mode. */\n\n';
module.exports = {
startKarma: function(config, singleRun, done) {
var browsers = grunt.option('browsers');
var reporters = grunt.option('reporters');
var noColor = grunt.option('no-colors');
var port = grunt.option('port');
var p = spawn('node', ['node_modules/karma/bin/karma', 'start', config,
singleRun ? '--single-run=true' : '',
reporters ? '--reporters=' + reporters : '',
browsers ? '--browsers=' + browsers : '',
noColor ? '--no-colors' : '',
port ? '--port=' + port : ''
]);
p.stdout.pipe(process.stdout);
p.stderr.pipe(process.stderr);
p.on('exit', function(code) {
if (code !== 0) grunt.fail.warn('Karma test(s) failed. Exit code: ' + code);
done();
});
},
updateWebdriver: function(done) {
if (process.env.TRAVIS) {
// Skip the webdriver-manager update on Travis, since the browsers will
// be provided remotely.
done();
return;
}
var p = spawn('node', ['node_modules/protractor/bin/webdriver-manager', 'update']);
p.stdout.pipe(process.stdout);
p.stderr.pipe(process.stderr);
p.on('exit', function(code) {
if (code !== 0) grunt.fail.warn('Webdriver failed to update');
done();
});
},
startProtractor: function(config, done) {
var sauceUser = grunt.option('sauceUser');
var sauceKey = grunt.option('sauceKey');
var tunnelIdentifier = grunt.option('capabilities.tunnel-identifier');
var sauceBuild = grunt.option('capabilities.build');
var browser = grunt.option('browser');
var specs = grunt.option('specs');
var args = ['node_modules/protractor/bin/protractor', config];
if (sauceUser) args.push('--sauceUser=' + sauceUser);
if (sauceKey) args.push('--sauceKey=' + sauceKey);
if (tunnelIdentifier) args.push('--capabilities.tunnel-identifier=' + tunnelIdentifier);
if (sauceBuild) args.push('--capabilities.build=' + sauceBuild);
if (specs) args.push('--specs=' + specs);
if (browser) {
args.push('--browser=' + browser);
}
var p = spawn('node', args);
p.stdout.pipe(process.stdout);
p.stderr.pipe(process.stderr);
p.on('exit', function(code) {
if (code !== 0) grunt.fail.warn('Protractor test(s) failed. Exit code: ' + code);
done();
});
},
wrap: function(src, name) {
src.unshift('src/' + name + '.prefix');
src.push('src/' + name + '.suffix');
return src;
},
addStyle: function(src, styles, minify) {
styles = styles.reduce(processCSS.bind(this), {
js: [src],
css: []
});
return {
js: styles.js.join('\n'),
css: styles.css.join('\n')
};
function processCSS(state, file) {
var css = fs.readFileSync(file).toString(),
js;
state.css.push(css);
if (minify) {
css = css
.replace(/\r?\n/g, '')
.replace(/\/\*.*?\*\//g, '')
.replace(/:\s+/g, ':')
.replace(/\s*\{\s*/g, '{')
.replace(/\s*\}\s*/g, '}')
.replace(/\s*,\s*/g, ',')
.replace(/\s*;\s*/g, ';');
}
//escape for js
css = css
.replace(/\\/g, '\\\\')
.replace(/'/g, '\\\'')
.replace(/\r?\n/g, '\\n');
js = '!window.angular.$$csp().noInlineStyle && window.angular.element(document.head).prepend(\'<style type="text/css">' + css + '</style>\');';
state.js.push(js);
return state;
}
},
process: function(src, NG_VERSION, strict) {
var processed = src
.replace(/(['"])NG_VERSION_FULL\1/g, NG_VERSION.full)
.replace(/(['"])NG_VERSION_MAJOR\1/, NG_VERSION.major)
.replace(/(['"])NG_VERSION_MINOR\1/, NG_VERSION.minor)
.replace(/(['"])NG_VERSION_DOT\1/, NG_VERSION.patch)
.replace(/(['"])NG_VERSION_CDN\1/, NG_VERSION.cdn)
.replace(/(['"])NG_VERSION_CODENAME\1/, NG_VERSION.codeName);
if (strict !== false) processed = this.singleStrict(processed, '\n\n', true);
return processed;
},
build: function(config, fn) {
var files = grunt.file.expand(config.src);
var styles = config.styles;
var processedStyles;
//concat
var src = files.map(function(filepath) {
return grunt.file.read(filepath);
}).join(grunt.util.normalizelf('\n'));
//process
var processed = this.process(src, grunt.config('NG_VERSION'), config.strict);
if (styles) {
processedStyles = this.addStyle(processed, styles.css, styles.minify);
processed = processedStyles.js;
if (config.styles.generateCspCssFile) {
grunt.file.write(removeSuffix(config.dest) + '-csp.css', CSP_CSS_HEADER + processedStyles.css);
}
}
//write
grunt.file.write(config.dest, processed);
grunt.log.ok('File ' + config.dest + ' created.');
fn();
function removeSuffix(fileName) {
return fileName.replace(/\.js$/, '');
}
},
singleStrict: function(src, insert) {
return src
.replace(/\s*("|')use strict("|');\s*/g, insert) // remove all file-specific strict mode flags
.replace(/(\(function\([^)]*\)\s*\{)/, '$1\'use strict\';'); // add single strict mode flag
},
sourceMap: function(mapFile, fileContents) {
var sourceMapLine = '//# sourceMappingURL=' + mapFile + '\n';
return fileContents + sourceMapLine;
},
min: function(file, done) {
var classPathSep = (process.platform === 'win32') ? ';' : ':';
var minFile = file.replace(/\.js$/, '.min.js');
var mapFile = minFile + '.map';
var mapFileName = mapFile.match(/[^\/]+$/)[0];
var errorFileName = file.replace(/\.js$/, '-errors.json');
var versionNumber = grunt.config('NG_VERSION').full;
var compilationLevel = (file === 'build/angular-message-format.js') ?
'ADVANCED_OPTIMIZATIONS' : 'SIMPLE_OPTIMIZATIONS';
shell.exec(
'java ' +
this.java32flags() + ' ' +
this.memoryRequirement() + ' ' +
'-cp bower_components/closure-compiler/compiler.jar' + classPathSep +
'bower_components/ng-closure-runner/ngcompiler.jar ' +
'org.angularjs.closurerunner.NgClosureRunner ' +
'--compilation_level ' + compilationLevel + ' ' +
'--language_in ECMASCRIPT5_STRICT ' +
'--minerr_pass ' +
'--minerr_errors ' + errorFileName + ' ' +
'--minerr_url http://errors.angularjs.org/' + versionNumber + '/ ' +
'--source_map_format=V3 ' +
'--create_source_map ' + mapFile + ' ' +
'--js ' + file + ' ' +
'--js_output_file ' + minFile,
function(code) {
if (code !== 0) grunt.fail.warn('Error minifying ' + file);
// closure creates the source map relative to build/ folder, we need to strip those references
grunt.file.write(mapFile, grunt.file.read(mapFile).replace('"file":"build/', '"file":"').
replace('"sources":["build/','"sources":["'));
// move add use strict into the closure + add source map pragma
grunt.file.write(minFile, this.sourceMap(mapFileName, this.singleStrict(grunt.file.read(minFile), '\n')));
grunt.log.ok(file + ' minified into ' + minFile);
done();
}.bind(this));
},
memoryRequirement: function() {
return (process.platform === 'win32') ? '' : '-Xmx2g';
},
//returns the 32-bit mode force flags for java compiler if supported, this makes the build much faster
java32flags: function() {
if (process.platform === 'win32') return '';
if (shell.exec('java -version -d32 2>&1', {silent: true}).code !== 0) return '';
return ' -d32 -client';
},
//collects and combines error messages stripped out in minify step
collectErrors: function() {
var combined = {
id: 'ng',
generated: new Date().toString(),
errors: {}
};
grunt.file.expand('build/*-errors.json').forEach(function(file) {
var errors = grunt.file.readJSON(file),
namespace;
Object.keys(errors).forEach(function(prop) {
if (typeof errors[prop] === 'object') {
namespace = errors[prop];
if (combined.errors[prop]) {
Object.keys(namespace).forEach(function(code) {
if (combined.errors[prop][code] && combined.errors[prop][code] !== namespace[code]) {
grunt.warn('[collect-errors] Duplicate minErr codes don\'t match!');
} else {
combined.errors[prop][code] = namespace[code];
}
});
} else {
combined.errors[prop] = namespace;
}
} else {
if (combined.errors[prop] && combined.errors[prop] !== errors[prop]) {
grunt.warn('[collect-errors] Duplicate minErr codes don\'t match!');
} else {
combined.errors[prop] = errors[prop];
}
}
});
});
grunt.file.write('build/errors.json', JSON.stringify(combined));
grunt.file.expand('build/*-errors.json').forEach(grunt.file.delete);
},
//csp connect middleware
conditionalCsp: function() {
return function(req, res, next) {
var CSP = /\.csp\W/;
if (CSP.test(req.url)) {
res.setHeader('X-WebKit-CSP', 'default-src \'self\';');
res.setHeader('X-Content-Security-Policy', 'default-src \'self\'');
res.setHeader('Content-Security-Policy', 'default-src \'self\'');
}
next();
};
},
//rewrite connect middleware
rewrite: function() {
return function(req, res, next) {
var REWRITE = /\/(guide|api|cookbook|misc|tutorial|error).*$/,
IGNORED = /(\.(css|js|png|jpg|gif|svg)$|partials\/.*\.html$)/,
match;
if (!IGNORED.test(req.url) && (match = req.url.match(REWRITE))) {
console.log('rewriting', req.url);
req.url = req.url.replace(match[0], '/index.html');
}
next();
};
}
};