From d90eabf53d8e40e721e22f06084fd8b01542eebb Mon Sep 17 00:00:00 2001 From: Dave Geddes Date: Sun, 21 Oct 2012 00:37:59 -0600 Subject: [PATCH 01/16] feat(Grunt): Switch from Rake to Grunt Migrates the Angular project from Rake to Grunt. Benefits: - Drops Ruby dependency - Lowers barrier to entry for contributions from JavaScript ninjas - Simplifies the Angular project setup and build process - Adopts industry-standard tools specific to JavaScript projects BREAKING CHANGE: Rake is completely replaced by Grunt. Below are the deprecated Rake tasks and their Grunt equivalents: rake --> grunt rake package --> grunt package rake init --> N/A rake clean --> grunt clean rake concat_scenario --> grunt build:scenario rake concat --> grunt build rake concat_scenario --> grunt build:scenario rake minify --> grunt minify rake version --> grunt write:version rake docs --> grunt docs rake webserver --> grunt webserver rake test --> grunt test rake test:unit --> grunt test:unit rake test: --> grunt test: rake test[Firefox+Safari] --> grunt test --in=[Firefox,Safari] rake test[Safari] --> grunt test --in=[Safari] rake autotest --> grunt autotest NOTES: * For convenience grunt test:e2e starts a webserver for you, while grunt test:end2end doesn't. Use grunt test:end2end if you already have the webserver running. * Removes duplicate entry for Describe.js in the angularScenario section of angularFiles.js * Updates docs/src/gen-docs.js to use #done intead of the deprecated #end * Uses grunt-contrib-connect instead of lib/nodeserver (removed) * Removes nodeserver.sh, travis now uses grunt webserver * Built and minified files are identical to Rake's output, with the exception of one less character for git revisions (using --short) and a couple minor whitespace differences Closes #199 --- .travis.yml | 7 +- Gruntfile.js | 121 +++++++++++++ Rakefile | 337 ------------------------------------- angularFiles.js | 1 - check-size.sh | 2 +- docs/src/gen-docs.js | 2 +- lib/grunt/plugins.js | 65 +++++++ lib/grunt/utils.js | 81 +++++++++ lib/nodeserver/favicon.ico | Bin 1150 -> 0 bytes lib/nodeserver/server.js | 273 ------------------------------ nodeserver.sh | 1 - package.json | 10 +- src/AngularPublic.js | 4 +- 13 files changed, 283 insertions(+), 621 deletions(-) create mode 100644 Gruntfile.js delete mode 100644 Rakefile create mode 100644 lib/grunt/plugins.js create mode 100644 lib/grunt/utils.js delete mode 100644 lib/nodeserver/favicon.ico delete mode 100644 lib/nodeserver/server.js delete mode 100755 nodeserver.sh diff --git a/.travis.yml b/.travis.yml index d209a1b20618..1020d6eaaf25 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,8 +6,9 @@ before_script: - export DISPLAY=:99.0 - sh -e /etc/init.d/xvfb start - npm install -g testacular@canary - - rake package - - ./nodeserver.sh > /dev/null & + - npm install -g grunt-cli + - grunt package + - grunt webserver > /dev/null & script: - - rake test[Firefox,"--reporters=dots"] + - grunt test --in=[Firefox] --reporters=dots diff --git a/Gruntfile.js b/Gruntfile.js new file mode 100644 index 000000000000..d13cbc58d132 --- /dev/null +++ b/Gruntfile.js @@ -0,0 +1,121 @@ +var files = require('./angularFiles').files; +var util = require('./lib/grunt/utils.js'); + +module.exports = function(grunt) { + //grunt plugins + grunt.loadNpmTasks('grunt-contrib-clean'); + grunt.loadNpmTasks('grunt-contrib-copy'); + grunt.loadNpmTasks('grunt-contrib-connect'); + grunt.loadTasks('lib/grunt'); + + var NG_VERSION = util.getVersion(); + //config + grunt.initConfig({ + NG_VERSION: NG_VERSION, + + test: { + jqlite: 'testacular-jqlite.conf.js', + jquery: 'testacular-jquery.conf.js', + modules: 'testacular-modules.conf.js', + //NOTE run grunt test:e2e instead and it will start a webserver for you + end2end: 'testacular-e2e.conf.js' + }, + + autotest: { + jqlite: 'testacular-jqlite.conf.js', + jquery: 'testacular-jquery.conf.js' + }, + + clean: {build: ['build']}, + + build: { + scenario: { + dest: 'build/angular-scenario.js', + src: [ + 'lib/jquery/jquery.js', + util.wrap([files['angularSrc'], files['angularScenario']], 'ngScenario/angular') + ], + styles: { + css: ['css/angular.css', 'css/angular-scenario.css'] + } + }, + angular: { + dest: 'build/angular.js', + src: util.wrap([files['angularSrc']], 'angular'), + styles: { + css: ['css/angular.css'], + minify: true + } + }, + loader: { + dest: 'build/angular-loader.js', + src: util.wrap(['src/loader.js'], 'loader') + }, + mocks: { + dest: 'build/angular-mocks.js', + src: ['src/ngMock/angular-mocks.js'], + strict: false + }, + sanitize: { + dest: 'build/angular-sanitize.js', + src: util.wrap([ + 'src/ngSanitize/sanitize.js', + 'src/ngSanitize/directive/ngBindHtml.js', + 'src/ngSanitize/filter/linky.js', + ], 'module') + }, + resource: { + dest: 'build/angular-resource.js', + src: util.wrap(['src/ngResource/resource.js'], 'module') + }, + cookies: { + dest: 'build/angular-cookies.js', + src: util.wrap(['src/ngCookies/cookies.js'], 'module') + }, + bootstrap: { + dest: 'build/angular-bootstrap.js', + src: util.wrap(['src/bootstrap/bootstrap.js'], 'module') + }, + bootstrapPrettify: { + dest: 'build/angular-bootstrap-prettify.js', + src: util.wrap(['src/bootstrap/bootstrap-prettify.js', 'src/bootstrap/google-prettify/prettify.js'], 'module'), + styles: { + css: ['src/bootstrap/google-prettify/prettify.css'], + minify: true + } + } + }, + + min: { + angular: 'build/angular.js', + cookies: 'build/angular-cookies.js', + loader: 'build/angular-loader.js', + resource: 'build/angular-resource.js', + sanitize: 'build/angular-sanitize.js', + bootstrap: 'build/angular-bootstrap.js', + bootstrapPrettify: 'build/angular-bootstrap-prettify.js' + }, + + docs: { + process: ['build/docs/*.html', 'build/docs/.htaccess'] + }, + + copy: { + i18n: { + files: {'build/i18n/': 'src/ngLocale/**'} + } + }, + + write: { + version: {file: 'build/version.txt', val: NG_VERSION.full} + } + }); + + //alias tasks + grunt.registerTask('test:unit', ['test:jqlite', 'test:jquery', 'test:modules']); + grunt.registerTask('minify', ['clean', 'build', 'min']); + grunt.registerTask('test:e2e', ['connect', 'test:end2end']); + grunt.registerTask('webserver', ['connect:keepalive']); + grunt.registerTask('package', ['clean', 'build', 'min', 'docs', 'copy', 'write', 'zip']); + grunt.registerTask('default', ['package']); +}; \ No newline at end of file diff --git a/Rakefile b/Rakefile deleted file mode 100644 index 3a8cc00ee34a..000000000000 --- a/Rakefile +++ /dev/null @@ -1,337 +0,0 @@ -require 'yaml' -include FileUtils - - -## High level flow of the build: -## -## clean -> init -> concat -> minify -> package -## - - -content = File.open('angularFiles.js', 'r') {|f| f.read } -files = eval(content.gsub(/\};(\s|\S)*/, '}'). - gsub(/angularFiles = /, ''). - gsub(/:/, '=>'). - gsub(/\/\//, '#')); - -BUILD_DIR = 'build' - -task :default => [:package] - - -desc 'Init the build workspace' -task :init do - FileUtils.mkdir(BUILD_DIR) unless File.directory?(BUILD_DIR) - - v = YAML::load( File.open( 'version.yaml' ) ) - match = v['version'].match(/^([^-]*)(-snapshot)?$/) - - NG_VERSION = Struct.new(:full, :major, :minor, :dot, :codename, :stable). - new(match[1] + (match[2] ? ('-' + %x(git rev-parse HEAD)[0..7]) : ''), - match[1].split('.')[0], - match[1].split('.')[1], - match[1].split('.')[2].sub(/\D+.*$/, ''), - v['codename'], - v['stable']) -end - - -desc 'Clean Generated Files' -task :clean do - FileUtils.rm_r(BUILD_DIR, :force => true) - FileUtils.mkdir(BUILD_DIR) - FileUtils.rm_r('test_out', :force => true) -end - - -desc 'Concat Scenario' -task :concat_scenario => :init do - - concat_file('angular-scenario.js', [ - 'lib/jquery/jquery.js', - 'src/ngScenario/angular.prefix', - files['angularSrc'], - files['angularScenario'], - 'src/ngScenario/angular.suffix', - ], gen_css('css/angular.css') + "\n" + gen_css('css/angular-scenario.css')) -end - - - -desc 'Concat AngularJS files' -task :concat => :init do - concat_file('angular.js', [ - 'src/angular.prefix', - files['angularSrc'], - 'src/angular.suffix', - ], gen_css('css/angular.css', true)) - - FileUtils.cp_r 'src/ngLocale', path_to('i18n') - - concat_file('angular-loader.js', [ - 'src/loader.prefix', - 'src/loader.js', - 'src/loader.suffix']) - - - concat_module('sanitize', [ - 'src/ngSanitize/sanitize.js', - 'src/ngSanitize/directive/ngBindHtml.js', - 'src/ngSanitize/filter/linky.js']) - - concat_module('resource', ['src/ngResource/resource.js']) - concat_module('cookies', ['src/ngCookies/cookies.js']) - concat_module('bootstrap', ['src/bootstrap/bootstrap.js']) - concat_module('bootstrap-prettify', ['src/bootstrap/bootstrap-prettify.js', - 'src/bootstrap/google-prettify/prettify.js'], - gen_css('src/bootstrap/google-prettify/prettify.css', true)) - - - FileUtils.cp 'src/ngMock/angular-mocks.js', path_to('angular-mocks.js') - - rewrite_file(path_to('angular-mocks.js')) do |content| - content.sub!('"NG_VERSION_FULL"', NG_VERSION.full) - end -end - - -desc 'Minify JavaScript' -task :minify => [:init, :concat, :concat_scenario] do - [ 'angular.js', - 'angular-cookies.js', - 'angular-loader.js', - 'angular-resource.js', - 'angular-sanitize.js', - 'angular-bootstrap.js', - 'angular-bootstrap-prettify.js' - ].each do |file| - fork { closure_compile(file) } - end - - Process.waitall -end - - -desc 'Generate version.txt file' -task :version => [:init] do - `echo #{NG_VERSION.full} > #{path_to('version.txt')}` -end - - -desc 'Generate docs' -task :docs => [:init] do - `node docs/src/gen-docs.js` - - [ path_to('docs/.htaccess'), - path_to('docs/index.html'), - path_to('docs/index-debug.html'), - path_to('docs/index-nocache.html'), - path_to('docs/index-jq.html'), - path_to('docs/index-jq-debug.html'), - path_to('docs/index-jq-nocache.html'), - path_to('docs/docs-scenario.html') - ].each do |src| - rewrite_file(src) do |content| - content.sub!('"NG_VERSION_FULL"', NG_VERSION.full). - sub('"NG_VERSION_STABLE"', NG_VERSION.stable) - end - end -end - - -desc 'Create angular distribution' -task :package => [:clean, :minify, :version, :docs] do - zip_dir = "angular-#{NG_VERSION.full}" - zip_file = "#{zip_dir}.zip" - - FileUtils.ln_s BUILD_DIR, zip_dir - %x(zip -r #{zip_file} #{zip_dir}) - FileUtils.rm zip_dir - - FileUtils.mv zip_file, path_to(zip_file) - - puts "Package created: #{path_to(zip_file)}" -end - - -desc 'Start development webserver' -task :webserver, :port do |t, args| - exec "node lib/nodeserver/server.js #{args[:port]}" -end - - -desc 'Run all AngularJS tests' -task :test, :browsers, :misc_options do |t, args| - [ 'test:jqlite', - 'test:jquery', - 'test:modules', - 'test:e2e' - ].each do |task| - Rake::Task[task].invoke(args[:browsers], args[:misc_options]) - end -end - - -namespace :test do - - desc 'Run all unit tests (single run)' - task :unit, :browsers, :misc_options do |t, args| - [ 'test:jqlite', - 'test:jquery', - 'test:modules' - ].each do |task| - Rake::Task[task].invoke(args[:browsers], args[:misc_options]) - end - end - - - desc 'Run jqLite-based unit test suite (single run)' - task :jqlite, :browsers, :misc_options do |t, args| - start_testacular('testacular-jqlite.conf.js', true, args[:browsers], args[:misc_options]) - end - - - desc 'Run jQuery-based unit test suite (single run)' - task :jquery, :browsers, :misc_options do |t, args| - start_testacular('testacular-jquery.conf.js', true, args[:browsers], args[:misc_options]) - end - - - desc 'Run bundled modules unit test suite (single run)' - task :modules, :browsers, :misc_options do |t, args| - start_testacular('testacular-modules.conf.js', true, args[:browsers], args[:misc_options]) - end - - - desc 'Run e2e test suite (single run)' - task :e2e, :browsers, :misc_options do |t, args| - start_testacular('testacular-e2e.conf.js', true, args[:browsers], args[:misc_options]) - end -end - - -namespace :autotest do - - desc 'Run jqLite-based unit test suite (autowatch)' - task :jqlite, :browsers, :misc_options do |t, args| - start_testacular('testacular-jqlite.conf.js', false, args[:browsers], args[:misc_options]) - end - - - desc 'Run jQuery-based unit test suite (autowatch)' - task :jquery, :browsers, :misc_options do |t, args| - start_testacular('testacular-jquery.conf.js', false, args[:browsers], args[:misc_options]) - end -end - - - -################### -# utility methods # -################### - - -## -# generates css snippet from a given files and optionally applies simple minification rules -# -def gen_css(cssFile, minify = false) - css = '' - File.open(cssFile, 'r') do |f| - css = f.read - end - - if minify - css.gsub! /\n/, '' - css.gsub! /\/\*.*?\*\//, '' - css.gsub! /:\s+/, ':' - css.gsub! /\s*\{\s*/, '{' - css.gsub! /\s*\}\s*/, '}' - css.gsub! /\s*\,\s*/, ',' - css.gsub! /\s*\;\s*/, ';' - end - - #escape for js - css.gsub! /\\/, "\\\\\\" - css.gsub! /'/, "\\\\'" - css.gsub! /\n/, "\\n" - - return %Q{angular.element(document).find('head').append('');} -end - - -## -# returns path to the file in the build directory -# -def path_to(filename) - return File.join(BUILD_DIR, *filename) -end - - -def closure_compile(filename) - puts "Minifying #{filename} ..." - - min_path = path_to(filename.gsub(/\.js$/, '.min.js')) - - %x(java \ - -client \ - -d32 \ - -jar lib/closure-compiler/compiler.jar \ - --compilation_level SIMPLE_OPTIMIZATIONS \ - --language_in ECMASCRIPT5_STRICT \ - --js #{path_to(filename)} \ - --js_output_file #{min_path}) - - rewrite_file(min_path) do |content| - content.sub!("'use strict';", ""). - sub!(/\(function\([^)]*\)\{/, "\\0'use strict';") - end -end - - -def concat_file(filename, deps, footer='') - puts "Creating #{filename} ..." - File.open(path_to(filename), 'w') do |f| - concat = 'cat ' + deps.flatten.join(' ') - - content = %x{#{concat}}. - gsub('"NG_VERSION_FULL"', NG_VERSION.full). - gsub('"NG_VERSION_MAJOR"', NG_VERSION.major). - gsub('"NG_VERSION_MINOR"', NG_VERSION.minor). - gsub('"NG_VERSION_DOT"', NG_VERSION.dot). - gsub('"NG_VERSION_CODENAME"', NG_VERSION.codename). - gsub(/^\s*['"]use strict['"];?\s*$/, ''). # remove all file-specific strict mode flags - sub(/\(function\([^)]*\)\s*\{/, "\\0\n'use strict';") # add single strict mode flag - - f.write(content) - f.write(footer) - end -end - - -def concat_module(name, files, footer='') - concat_file('angular-' + name + '.js', ['src/module.prefix'] + files + ['src/module.suffix'], footer) -end - - -def rewrite_file(filename) - File.open(filename, File::RDWR) do |f| - content = f.read - - content = yield content - - raise "File rewrite failed - No content!" unless content - - f.truncate 0 - f.rewind - f.write content - end -end - - -def start_testacular(config, singleRun, browsers, misc_options) - sh "./node_modules/testacular/bin/testacular start " + - "#{config} " + - "#{'--single-run=true' if singleRun} " + - "#{'--browsers=' + browsers.gsub('+', ',') if browsers} " + - "#{(misc_options || '').gsub('+', ',')}" -end diff --git a/angularFiles.js b/angularFiles.js index 02bdf3a1d062..47f18961f736 100644 --- a/angularFiles.js +++ b/angularFiles.js @@ -79,7 +79,6 @@ angularFiles = { 'src/ngScenario/Describe.js', 'src/ngScenario/Future.js', 'src/ngScenario/ObjectModel.js', - 'src/ngScenario/Describe.js', 'src/ngScenario/Runner.js', 'src/ngScenario/SpecRunner.js', 'src/ngScenario/dsl.js', diff --git a/check-size.sh b/check-size.sh index fd46d0c9227b..1e0b7c7e4a4d 100755 --- a/check-size.sh +++ b/check-size.sh @@ -1,5 +1,5 @@ #!/bin/bash -rake minify +grunt package gzip -c < build/angular.min.js > build/angular.min.js.gzip ls -l build/angular.min.* diff --git a/docs/src/gen-docs.js b/docs/src/gen-docs.js index 9c3ce57990f2..519a97121e83 100755 --- a/docs/src/gen-docs.js +++ b/docs/src/gen-docs.js @@ -36,7 +36,7 @@ writer.makeDir('build/docs/', true).then(function() { }); }).then(function printStats() { console.log('DONE. Generated ' + docs.length + ' pages in ' + (now()-start) + 'ms.' ); -}).end(); +}).done(); function writeTheRest(writesFuture) { diff --git a/lib/grunt/plugins.js b/lib/grunt/plugins.js new file mode 100644 index 000000000000..91f77158faa8 --- /dev/null +++ b/lib/grunt/plugins.js @@ -0,0 +1,65 @@ +var shell = require('shelljs'); +var util = require('./utils.js'); + +module.exports = function(grunt) { + grunt.registerMultiTask('min', 'minify JS files', function(){ + var file = this.data; + var minFile = file.replace(/\.js$/, '.min.js'); + shell.exec('java -client -d32 -jar lib/closure-compiler/compiler.jar' + + ' --compilation_level SIMPLE_OPTIMIZATIONS --language_in ECMASCRIPT5_STRICT ' + + '--js '+file+' --js_output_file ' + minFile); + grunt.file.write(minFile, util.singleStrict(grunt.file.read(minFile), '\n')); + grunt.log.ok(file + ' minified into ' + minFile); + }); + + grunt.registerMultiTask('build', 'build JS files', function(){ + var files = grunt.file.expand(this.data.src); + var styles = this.data.styles; + //concat + var src = files.map(function(filepath){ + return grunt.file.read(filepath); + }).join(grunt.util.normalizelf('\n')); + //process + var processed = util.process(src, grunt.config('NG_VERSION'), this.data.strict); + if (styles) processed = util.addStyle(processed, styles.css, styles.minify); + //write + grunt.file.write(this.data.dest, processed); + grunt.log.ok('File ' + this.data.dest + ' created.'); + }); + + grunt.registerTask('zip', 'zip up build directory', function(){ + var zipname = 'angular-' + grunt.config('NG_VERSION').full + '.zip'; + shell.exec('zip -r ' + zipname + ' build', {silent: true}); + shell.mv(zipname, 'build/' + zipname); + grunt.log.ok('zipped up angular build'); + }); + + grunt.registerMultiTask('write', 'write content to a file', function(){ + grunt.file.write(this.data.file, this.data.val); + grunt.log.ok('wrote to ' + this.data.file); + }); + + grunt.registerMultiTask('docs', 'create angular docs', function(){ + shell.exec('node docs/src/gen-docs.js'); + grunt.file.expand(this.data).forEach(function(file){ + grunt.file.write(file, util.process(grunt.file.read(file), grunt.config('NG_VERSION'), false)); + }); + grunt.log.ok('docs created'); + }); + + grunt.registerMultiTask('test', 'Run the unit tests with testacular', function(){ + util.startTestacular(this.data, { + singleRun: true, + browsers: grunt.option('in'), + reporters: grunt.option('reporters') + }, this.async()); + }); + + grunt.registerMultiTask('autotest', 'Run and watch the unit tests with testacular', function(){ + util.startTestacular(this.data, { + singleRun: false, + browsers: grunt.option('in'), + reporters: grunt.option('reporters') + }, this.async()); + }); +}; \ No newline at end of file diff --git a/lib/grunt/utils.js b/lib/grunt/utils.js new file mode 100644 index 000000000000..f4f517e41b58 --- /dev/null +++ b/lib/grunt/utils.js @@ -0,0 +1,81 @@ +var fs = require('fs'); +var shell = require('shelljs'); +var yaml = require('yaml-js'); + +module.exports = { + getVersion: function(){ + var version = yaml.load(fs.readFileSync('version.yaml', 'UTF-8')); + var match = version.version.match(/^([^\-]*)(-snapshot)?$/); + var semver = match[1].split('.'); + version.major = semver[0]; + version.minor = semver[1]; + version.dot = semver[2]; + var hash = shell.exec('git rev-parse --short HEAD', {silent: true}).output.replace('\n', ''); + version.full = (match[1] + (match[2] ? '-' + hash : '')); + return version; + }, + + startTestacular: function(config, options, fn){ + var browsers = options.browsers; + var reporters = options.reporters; + if(browsers) browsers = browsers.substr(1, browsers.length - 2); + shell.exec('./node_modules/testacular/bin/testacular start ' + config + + (options.singleRun ? ' --single-run=true' : '') + + (reporters ? ' --reporters=' + reporters : '') + + (browsers ? ' --browsers=' + browsers : ''), + fn); + }, + + wrap: function(src, name){ + src.unshift('src/' + name + '.prefix'); + src.push('src/' + name + '.suffix'); + return src; + }, + + addStyle: function(src, styles, minify){ + styles = styles.map(processCSS.bind(this)).join('\n'); + src += styles; + return src; + + function processCSS(file){ + var css = fs.readFileSync(file).toString(); + if(minify){ + css = css + .replace(/\n/g, '') + .replace(/\/\*.*?\*\//g, '') + .replace(/:\s+/g, ':') + .replace(/\s*\{\s*/g, '{') + .replace(/\s*\}\s*/g, '}') + .replace(/\s*\,\s*/g, ',') + .replace(/\s*\;\s*/g, ';'); + } + //espace for js + css = css + .replace(/\\/g, '\\\\') + .replace(/'/g, "\\'") + .replace(/\n/g, '\\n'); + return "angular.element(document).find('head').append('');"; + } + }, + + process: function(src, NG_VERSION, strict){ + var processed = src + .replace(/"NG_VERSION_FULL"/g, NG_VERSION.full) + .replace(/"NG_VERSION_MAJOR"/, NG_VERSION.major) + .replace(/"NG_VERSION_MINOR"/, NG_VERSION.minor) + .replace(/"NG_VERSION_DOT"/, NG_VERSION.dot) + .replace(/"NG_VERSION_STABLE"/, NG_VERSION.stable) + .replace(/"NG_VERSION_CODENAME"/, NG_VERSION.codename); + if (strict !== false) processed = this.singleStrict(processed, '\n\n', true); + return processed; + }, + + singleStrict: function(src, insert, newline){ + var useStrict = newline ? "$1\n'use strict';" : "$1'use strict';"; + return src + // .replace(/\s*("|')use strict("|');\s*/g, '\n\n') // remove all file-specific strict mode flags + .replace(/\s*("|')use strict("|');\s*/g, insert) // remove all file-specific strict mode flags + // .replace(/(\(function\([^)]*\)\s*\{)/, "$1\n'use strict';"); // add single strict mode flag + .replace(/(\(function\([^)]*\)\s*\{)/, useStrict); // add single strict mode flag + } +}; \ No newline at end of file diff --git a/lib/nodeserver/favicon.ico b/lib/nodeserver/favicon.ico deleted file mode 100644 index fe24a63a6ba4c4b4fb0e960abb60e51dc9097d43..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1150 zcmZuxZAep57(QaqUkSemBP}6IGixM-R$2;?eQEiH)}M%sFz|;4MNX|WiYN&)b(!;H z6Y*nt*TibqTMgRG)HS!ek&a*=1h9&ehx^|5d7kq==bVdB5WYh~ z5Pn9Zl1&JOBZT5`;1tgM&*K3h{B8XO04Pv69FAiQqf%+L-x}R+cT}L7K)Hp5g#({H zs)ujg7~xW4RgiVK6Q0-0+&Z1^>C)0tWFQR5EiNv`j*gB=t1D>k=&rAj6sm=NVV@x` zw2hCPj4lwp82qEx>mRPHtc3aV7>%aNIw8HBkub*N2zEW5@azt2gMusxh+8R_*=)Y< z8=bRvJutLfX4G3b|p{YRzFR?!H&x@}p?E$;po?|kZ?qnve^}WB- z4mRsN9Ew)M*>nl7U3gmR!xE0m1>G<`JzX|FKGB*SHR01IKAcZw@!XtnuR;$E^#jn_ zB7;5QtJ(ygeY^|so_h9fSglM7om_(_976-Yz&vAx!9fa&&aBlY_#=rOpwVb3X$NB# zL~g_vWg3C__e!);o8Sd8VxX1ul+kDuzL7I*^-Y>1J_;gMx#WE_a1pONsV9-NO$>#n zBv32t<%SgRXIPfK>)nHR-y~wL8ac%ns>*5ZLYf+~Q@=oRY&%@ak;0?', '>'). - replace('"', '"'); -} - -function createServlet(Class) { - var servlet = new Class(); - return servlet.handleRequest.bind(servlet); -} - -/** - * An Http server implementation that uses a map of methods to decide - * action routing. - * - * @param {Object} Map of method => Handler function - */ -function HttpServer(handlers) { - this.handlers = handlers; - this.server = http.createServer(this.handleRequest_.bind(this)); -} - -HttpServer.prototype.start = function(port) { - this.port = port; - this.server.listen(port); - sys.puts('Http Server running at http://127.0.0.1:' + port + '/'); -}; - -HttpServer.prototype.parseUrl_ = function(urlString) { - var parsed = url.parse(urlString); - parsed.pathname = url.resolve('/', parsed.pathname); - return url.parse(url.format(parsed), true); -}; - -HttpServer.prototype.handleRequest_ = function(req, res) { - var logEntry = req.method + ' ' + req.url; - if (req.headers['user-agent']) { - logEntry += ' ' + req.headers['user-agent']; - } - sys.puts(logEntry); - req.url = this.parseUrl_(req.url); - var handler = this.handlers[req.method]; - if (!handler) { - res.writeHead(501); - res.end(); - } else { - handler.call(this, req, res); - } -}; - -/** - * Handles static content. - */ -function StaticServlet() {} - -StaticServlet.MimeMap = { - 'txt': 'text/plain', - 'html': 'text/html', - 'css': 'text/css', - 'xml': 'application/xml', - 'json': 'application/json', - 'js': 'application/javascript', - 'jpg': 'image/jpeg', - 'jpeg': 'image/jpeg', - 'gif': 'image/gif', - 'png': 'image/png', - 'manifest': 'text/cache-manifest', - // it should be application/font-woff - // but only this silences chrome warnings - 'woff': 'font/opentype' -}; - -StaticServlet.prototype.handleRequest = function(req, res) { - var self = this; - var path = ('./' + req.url.pathname).replace('//','/').replace(/%(..)/g, function(match, hex){ - return String.fromCharCode(parseInt(hex, 16)); - }); - var parts = path.split('/'); - if (parts[parts.length-1].charAt(0) === '.') - return self.sendForbidden_(req, res, path); - - // favicon rewriting - if (path === './favicon.ico') - return self.sendFile_(req, res, './lib/nodeserver/favicon.ico'); - - // docs rewriting - var REWRITE = /\/(guide|api|cookbook|misc|tutorial).*$/, - IGNORED = /(\.(css|js|png|jpg)$|partials\/.*\.html$)/, - match; - - if (!IGNORED.test(path) && (match = path.match(REWRITE))) { - path = path.replace(match[0], '/index.html'); - sys.puts('Rewrite to ' + path); - } - - // end of docs rewriting - - fs.stat(path, function(err, stat) { - if (err) - return self.sendMissing_(req, res, path); - if (stat.isDirectory()) - return fs.stat(path + 'index.html', function(err, stat) { - // send index.html if exists - if (!err) - return self.sendFile_(req, res, path + 'index.html'); - - // list files otherwise - return self.sendDirectory_(req, res, path); - }); - - return self.sendFile_(req, res, path); - }); -}; - -StaticServlet.prototype.sendError_ = function(req, res, error) { - res.writeHead(500, { - 'Content-Type': 'text/html' - }); - res.write('\n'); - res.write('Internal Server Error\n'); - res.write('

Internal Server Error

'); - res.write('
' + escapeHtml(sys.inspect(error)) + '
'); - sys.puts('500 Internal Server Error'); - sys.puts(sys.inspect(error)); -}; - -StaticServlet.prototype.sendMissing_ = function(req, res, path) { - path = path.substring(1); - res.writeHead(404, { - 'Content-Type': 'text/html' - }); - res.write('\n'); - res.write('404 Not Found\n'); - res.write('

Not Found

'); - res.write( - '

The requested URL ' + - escapeHtml(path) + - ' was not found on this server.

' - ); - res.end(); - sys.puts('404 Not Found: ' + path); -}; - -StaticServlet.prototype.sendForbidden_ = function(req, res, path) { - path = path.substring(1); - res.writeHead(403, { - 'Content-Type': 'text/html' - }); - res.write('\n'); - res.write('403 Forbidden\n'); - res.write('

Forbidden

'); - res.write( - '

You do not have permission to access ' + - escapeHtml(path) + ' on this server.

' - ); - res.end(); - sys.puts('403 Forbidden: ' + path); -}; - -StaticServlet.prototype.sendRedirect_ = function(req, res, redirectUrl) { - res.writeHead(301, { - 'Content-Type': 'text/html', - 'Location': redirectUrl - }); - res.write('\n'); - res.write('301 Moved Permanently\n'); - res.write('

Moved Permanently

'); - res.write( - '

The document has moved here.

' - ); - res.end(); - sys.puts('401 Moved Permanently: ' + redirectUrl); -}; - -StaticServlet.prototype.sendFile_ = function(req, res, path) { - var self = this; - var file = fs.createReadStream(path); - res.writeHead(200, { - // CSP headers, uncomment to enable CSP - //"X-WebKit-CSP": "default-src 'self';", - //"X-Content-Security-Policy": "default-src 'self'", - 'Content-Type': StaticServlet. - MimeMap[path.split('.').pop()] || 'text/plain' - }); - if (req.method === 'HEAD') { - res.end(); - } else { - file.on('data', res.write.bind(res)); - file.on('close', function() { - res.end(); - }); - file.on('error', function(error) { - self.sendError_(req, res, error); - }); - } -}; - -StaticServlet.prototype.sendDirectory_ = function(req, res, path) { - var self = this; - if (path.match(/[^\/]$/)) { - req.url.pathname += '/'; - var redirectUrl = url.format(url.parse(url.format(req.url))); - return self.sendRedirect_(req, res, redirectUrl); - } - fs.readdir(path, function(err, files) { - if (err) - return self.sendError_(req, res, error); - - if (!files.length) - return self.writeDirectoryIndex_(req, res, path, []); - - var remaining = files.length; - files.forEach(function(fileName, index) { - fs.stat(path + '/' + fileName, function(err, stat) { - if (err) - return self.sendError_(req, res, err); - if (stat.isDirectory()) { - files[index] = fileName + '/'; - } - if (!(--remaining)) - return self.writeDirectoryIndex_(req, res, path, files); - }); - }); - }); -}; - -StaticServlet.prototype.writeDirectoryIndex_ = function(req, res, path, files) { - path = path.substring(1); - res.writeHead(200, { - 'Content-Type': 'text/html' - }); - if (req.method === 'HEAD') { - res.end(); - return; - } - res.write('\n'); - res.write('' + escapeHtml(path) + '\n'); - res.write('\n'); - res.write('

Directory: ' + escapeHtml(path) + '

'); - res.write('
    '); - files.forEach(function(fileName) { - if (fileName.charAt(0) !== '.') { - res.write('
  1. ' + - escapeHtml(fileName) + '
  2. '); - } - }); - res.write('
'); - res.end(); -}; - -// Must be last, -main(process.argv); diff --git a/nodeserver.sh b/nodeserver.sh deleted file mode 100755 index 2570ebfd8816..000000000000 --- a/nodeserver.sh +++ /dev/null @@ -1 +0,0 @@ -node lib/nodeserver/server.js $1 diff --git a/package.json b/package.json index b3833816fc02..493909485b12 100644 --- a/package.json +++ b/package.json @@ -2,9 +2,15 @@ "name": "AngularJS", "version": "0.0.0", "dependencies" : { - "testacular" : "canary", + "testacular": "canary", "jasmine-node" : "*", "q-fs" : "*", - "qq" : "*" + "qq" : "*", + "grunt": "0.4.x", + "grunt-contrib-clean": "0.4.x", + "grunt-contrib-copy": "0.3.x", + "grunt-contrib-connect": "0.1.x", + "shelljs": "0.0.x", + "yaml-js": "0.0.x" } } diff --git a/src/AngularPublic.js b/src/AngularPublic.js index 61c77af34c2e..d281a2d5c0ea 100644 --- a/src/AngularPublic.js +++ b/src/AngularPublic.js @@ -14,8 +14,8 @@ * - `codeName` – `{string}` – Code name of the release, such as "jiggling-armfat". */ var version = { - full: '"NG_VERSION_FULL"', // all of these placeholder strings will be replaced by rake's - major: "NG_VERSION_MAJOR", // compile task + full: '"NG_VERSION_FULL"', // all of these placeholder strings will be replaced by grunt's + major: "NG_VERSION_MAJOR", // package task minor: "NG_VERSION_MINOR", dot: "NG_VERSION_DOT", codeName: '"NG_VERSION_CODENAME"' From 8f5b5202155adf95305dcf8835e9dfaa960933cd Mon Sep 17 00:00:00 2001 From: Dave Geddes Date: Thu, 8 Nov 2012 20:19:46 -0700 Subject: [PATCH 02/16] doc(Grunt): documentation on using Grunt Replaces Rake documentation with Grunt documentation. --- README.md | 14 +++++----- docs/content/misc/contribute.ngdoc | 42 ++++++++++++++++++------------ docs/src/templates/.htaccess | 2 +- 3 files changed, 32 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index f29dd2dd0f1e..3747c974be1e 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ Building AngularJS --------- [Once you have your environment setup](http://docs.angularjs.org/misc/contribute) just run: - rake package + grunt package Running Tests @@ -32,14 +32,12 @@ Running tests requires installation of [Testacular](http://vojtajina.github.com/ To execute all unit tests, use: - rake test:unit + grunt test:unit To execute end-to-end (e2e) tests, use: - rake package - rake webserver & - rake test:e2e + grunt package + grunt test:e2e -To learn more about the rake tasks, run `rake -T` and also read our -[contribution guidelines](http://docs.angularjs.org/misc/contribute) and instructions in this -[commit message](https://github.com/angular/angular.js/commit/9d168f058f9c6d7eeae0daa7cb72ea4e02a0003a). +To learn more about the grunt tasks, run `grunt --help` and also read our +[contribution guidelines](http://docs.angularjs.org/misc/contribute). diff --git a/docs/content/misc/contribute.ngdoc b/docs/content/misc/contribute.ngdoc index bab8ae2edde1..9e9711b08253 100644 --- a/docs/content/misc/contribute.ngdoc +++ b/docs/content/misc/contribute.ngdoc @@ -79,17 +79,13 @@ Several steps are needed to check out and build AngularJS: ## Installation Dependencies Before you can build AngularJS, you must install or configure the following dependencies on your -machine: - -* {@link http://rake.rubyforge.org Rake}: We use Rake as our build system, which is pre-installed -on most Macintosh and Linux machines. If that is not true in your case, you can grab it from the -Rake website. +machine: * Git: The {@link http://help.github.com/mac-git-installation Github Guide to Installing Git} is quite a good source for information on Git. -* {@link http://nodejs.org Node.js}: We use Node to generate the documentation and to run a -development web server. Depending on your system, you can install Node either from source or as a +* {@link http://nodejs.org Node.js}: We use Node to generate the documentation, run a +development web server, run tests, and generate a build. Depending on your system, you can install Node either from source or as a pre-packaged bundle. Once installed, you'll also need several npms (node packages), which you can install once you checked out a local copy @@ -98,6 +94,10 @@ pre-packaged bundle. * `cd angular.js` * `npm install` +* {@link http://gruntjs.com Grunt}: We use Grunt as our build system. Install the grunt command-line tool globally with: + + * `sudo npm install -g grunt-cli` + ## Creating a Github Account and Forking Angular @@ -108,7 +108,7 @@ https://github.com/angular/angular.js main angular repository}. ## Building AngularJS -To build AngularJS, you check out the source code and use Rake to generate the non-minified and +To build AngularJS, you check out the source code and use Grunt to generate the non-minified and minified AngularJS files: 1. To clone your Github repository, run: @@ -125,7 +125,7 @@ minified AngularJS files: 4. To build AngularJS, run: - rake package + grunt package The build output can be located under the `build` directory. It consists of the following files and directories: @@ -154,7 +154,7 @@ made available a local web server based on Node.js. 1. To start the web server, run: - rake webserver + grunt webserver 2. To access the local server, go to this website: @@ -169,18 +169,20 @@ made available a local web server based on Node.js. Our unit and integration tests are written with Jasmine and executed with Testacular. To run all of the tests once on Chrome run: - rake test:unit + grunt test:unit To run the tests on other browsers (Chrome, ChromeCanary, Firefox, Opera and Safari are pre-configured) use: - rake test:unit[Opera+Firefox] + grunt test:unit --in=[Opera,Firefox] + +Note there should be no spaces between browsers. [Opera, Firefox] is invalid. During development it's however more productive to continuously run unit tests every time the source or test files change. To execute tests in this mode run: 1. To start the Testacular server, capture Chrome browser and run unit tests, run: - rake autotest:jqlite + grunt autotest:jqlite 2. To capture more browsers, open this url in the desired browser (url might be different if you have multiple instance of Testacular running, read Testacular's console output for the correct url): @@ -190,9 +192,9 @@ change. To execute tests in this mode run: 3. To re-run tests just change any source or test file. -To learn more about all of the preconfigured Rake tasks run: +To learn more about all of the preconfigured Grunt tasks run: - rake -T + grunt --help ## Running the end-to-end Test Suite @@ -201,7 +203,7 @@ To run the E2E test suite: 1. Start the local web server if it's not running already. - rake webserver + grunt webserver 2. In a browser, go to: @@ -209,7 +211,13 @@ To run the E2E test suite: or in terminal run: - rake test:e2e + grunt test:end2end + +For convenience you can also simply run: + + grunt test:e2e + +This will start the webserver for you and run the tests. diff --git a/docs/src/templates/.htaccess b/docs/src/templates/.htaccess index 98c7b7954679..e5a74cc4a4d4 100644 --- a/docs/src/templates/.htaccess +++ b/docs/src/templates/.htaccess @@ -4,7 +4,7 @@ # current angular version. If this rule matches the appcache-offline.manifest will be served for # requests to appcache.manifest # -# This file must be processed by Rake in order to replace %ANGULAR_VERSION% with the actual version. +# This file must be processed by Grunt in order to replace %ANGULAR_VERSION% with the actual version. Options -Indexes RewriteEngine on From 29a40956a6406b033fb6ccb93a4341d180bcdb7a Mon Sep 17 00:00:00 2001 From: Dave Geddes Date: Fri, 9 Nov 2012 16:11:17 -0700 Subject: [PATCH 03/16] fix(Grunt CI) execute correct grunt Instructs Travis to remove the local grunt executable placeholder. Should prevent Travis build from breaking. --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 1020d6eaaf25..3e54f6f81e46 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,6 +7,7 @@ before_script: - sh -e /etc/init.d/xvfb start - npm install -g testacular@canary - npm install -g grunt-cli + - rm node_modules/.bin/grunt - grunt package - grunt webserver > /dev/null & From d127edfaf221156ccc834600eebb637c0764bbfc Mon Sep 17 00:00:00 2001 From: Dave Geddes Date: Mon, 12 Nov 2012 17:32:08 -0700 Subject: [PATCH 04/16] refactor(Grunt) minify files in parallel Runs closure compiler in parallel, which speeds up minification. --- Gruntfile.js | 6 +++--- lib/grunt/plugins.js | 14 +++++++------- lib/grunt/utils.js | 15 +++++++++++++-- 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index d13cbc58d132..9ea789ca22b2 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -93,7 +93,7 @@ module.exports = function(grunt) { resource: 'build/angular-resource.js', sanitize: 'build/angular-sanitize.js', bootstrap: 'build/angular-bootstrap.js', - bootstrapPrettify: 'build/angular-bootstrap-prettify.js' + bootstrapPrettify: 'build/angular-bootstrap-prettify.js', }, docs: { @@ -113,9 +113,9 @@ module.exports = function(grunt) { //alias tasks grunt.registerTask('test:unit', ['test:jqlite', 'test:jquery', 'test:modules']); - grunt.registerTask('minify', ['clean', 'build', 'min']); + grunt.registerTask('minify', ['clean', 'build', 'minall']); grunt.registerTask('test:e2e', ['connect', 'test:end2end']); grunt.registerTask('webserver', ['connect:keepalive']); - grunt.registerTask('package', ['clean', 'build', 'min', 'docs', 'copy', 'write', 'zip']); + grunt.registerTask('package', ['clean', 'build', 'minall', 'docs', 'copy', 'write', 'zip']); grunt.registerTask('default', ['package']); }; \ No newline at end of file diff --git a/lib/grunt/plugins.js b/lib/grunt/plugins.js index 91f77158faa8..befc515c8141 100644 --- a/lib/grunt/plugins.js +++ b/lib/grunt/plugins.js @@ -3,13 +3,13 @@ var util = require('./utils.js'); module.exports = function(grunt) { grunt.registerMultiTask('min', 'minify JS files', function(){ - var file = this.data; - var minFile = file.replace(/\.js$/, '.min.js'); - shell.exec('java -client -d32 -jar lib/closure-compiler/compiler.jar' + - ' --compilation_level SIMPLE_OPTIMIZATIONS --language_in ECMASCRIPT5_STRICT ' + - '--js '+file+' --js_output_file ' + minFile); - grunt.file.write(minFile, util.singleStrict(grunt.file.read(minFile), '\n')); - grunt.log.ok(file + ' minified into ' + minFile); + util.min.call(util, this.data, this.async()); + }); + + grunt.registerTask('minall', 'minify all the JS files in parallel', function(){ + var files = grunt.config('min'); + files = Object.keys(files).map(function(key){ return files[key]; }); + grunt.util.async.forEach(files, util.min.bind(util), this.async()); }); grunt.registerMultiTask('build', 'build JS files', function(){ diff --git a/lib/grunt/utils.js b/lib/grunt/utils.js index f4f517e41b58..19cd513a8ef4 100644 --- a/lib/grunt/utils.js +++ b/lib/grunt/utils.js @@ -1,6 +1,7 @@ var fs = require('fs'); var shell = require('shelljs'); var yaml = require('yaml-js'); +var grunt = require('grunt'); module.exports = { getVersion: function(){ @@ -73,9 +74,19 @@ module.exports = { singleStrict: function(src, insert, newline){ var useStrict = newline ? "$1\n'use strict';" : "$1'use strict';"; return src - // .replace(/\s*("|')use strict("|');\s*/g, '\n\n') // remove all file-specific strict mode flags .replace(/\s*("|')use strict("|');\s*/g, insert) // remove all file-specific strict mode flags - // .replace(/(\(function\([^)]*\)\s*\{)/, "$1\n'use strict';"); // add single strict mode flag .replace(/(\(function\([^)]*\)\s*\{)/, useStrict); // add single strict mode flag + }, + + min: function(file, fn) { + var minFile = file.replace(/\.js$/, '.min.js'); + shell.exec('java -client -d32 -jar lib/closure-compiler/compiler.jar' + + ' --compilation_level SIMPLE_OPTIMIZATIONS --language_in ECMASCRIPT5_STRICT ' + + '--js ' + file + ' --js_output_file ' + minFile, + function() { + grunt.file.write(minFile, this.singleStrict(grunt.file.read(minFile), '\n')); + grunt.log.ok(file + ' minified into ' + minFile); + fn(); + }.bind(this)); } }; \ No newline at end of file From 5261d64ca43f86b991d0716c91a8bc8271cbd1b7 Mon Sep 17 00:00:00 2001 From: Dave Geddes Date: Tue, 13 Nov 2012 20:54:28 -0700 Subject: [PATCH 05/16] refactor(Grunt) improve error handling Make grunt tasks fail & abort on errors, failing tests, etc. No longer catch uncaughtException in docs/src/gen-docs.js so Grunt can catch it and abort Also use spawn instead of exec where appropriate to avoid hitting node's buffer size limit. --- docs/src/gen-docs.js | 4 ---- lib/grunt/plugins.js | 16 +++++----------- lib/grunt/utils.js | 26 +++++++++++++++++--------- 3 files changed, 22 insertions(+), 24 deletions(-) diff --git a/docs/src/gen-docs.js b/docs/src/gen-docs.js index 519a97121e83..a229955aac99 100755 --- a/docs/src/gen-docs.js +++ b/docs/src/gen-docs.js @@ -5,10 +5,6 @@ var reader = require('./reader.js'), appCache = require('./appCache.js').appCache, Q = require('qq'); -process.on('uncaughtException', function(err) { - console.error(err.stack || err); -}); - var start = now(); var docs; diff --git a/lib/grunt/plugins.js b/lib/grunt/plugins.js index befc515c8141..7744a5a88930 100644 --- a/lib/grunt/plugins.js +++ b/lib/grunt/plugins.js @@ -31,6 +31,7 @@ module.exports = function(grunt) { var zipname = 'angular-' + grunt.config('NG_VERSION').full + '.zip'; shell.exec('zip -r ' + zipname + ' build', {silent: true}); shell.mv(zipname, 'build/' + zipname); + if(shell.error()) grunt.fail.warn('error creating zip'); grunt.log.ok('zipped up angular build'); }); @@ -40,7 +41,8 @@ module.exports = function(grunt) { }); grunt.registerMultiTask('docs', 'create angular docs', function(){ - shell.exec('node docs/src/gen-docs.js'); + var res = shell.exec('node docs/src/gen-docs.js'); + if(res.code !== 0) grunt.fail.warn('Error creating docs'); grunt.file.expand(this.data).forEach(function(file){ grunt.file.write(file, util.process(grunt.file.read(file), grunt.config('NG_VERSION'), false)); }); @@ -48,18 +50,10 @@ module.exports = function(grunt) { }); grunt.registerMultiTask('test', 'Run the unit tests with testacular', function(){ - util.startTestacular(this.data, { - singleRun: true, - browsers: grunt.option('in'), - reporters: grunt.option('reporters') - }, this.async()); + util.startTestacular.call(util, this.data, true, this.async()); }); grunt.registerMultiTask('autotest', 'Run and watch the unit tests with testacular', function(){ - util.startTestacular(this.data, { - singleRun: false, - browsers: grunt.option('in'), - reporters: grunt.option('reporters') - }, this.async()); + util.startTestacular.call(util, this.data, false, this.async()); }); }; \ No newline at end of file diff --git a/lib/grunt/utils.js b/lib/grunt/utils.js index 19cd513a8ef4..5eb5eec8c9de 100644 --- a/lib/grunt/utils.js +++ b/lib/grunt/utils.js @@ -2,6 +2,7 @@ var fs = require('fs'); var shell = require('shelljs'); var yaml = require('yaml-js'); var grunt = require('grunt'); +var spawn = require('child_process').spawn; module.exports = { getVersion: function(){ @@ -16,15 +17,21 @@ module.exports = { return version; }, - startTestacular: function(config, options, fn){ - var browsers = options.browsers; - var reporters = options.reporters; + startTestacular: function(config, singleRun, done){ + var browsers = grunt.option('in'); + var reporters = grunt.option('reporters'); if(browsers) browsers = browsers.substr(1, browsers.length - 2); - shell.exec('./node_modules/testacular/bin/testacular start ' + config + - (options.singleRun ? ' --single-run=true' : '') + - (reporters ? ' --reporters=' + reporters : '') + - (browsers ? ' --browsers=' + browsers : ''), - fn); + var p = spawn('./node_modules/testacular/bin/testacular', ['start', config, + singleRun ? '--single-run=true' : '', + reporters ? '--reporters=' + reporters : '', + browsers ? '--browsers=' + browsers : '' + ]); + p.stdout.pipe(process.stdout); + p.stderr.pipe(process.stderr); + p.on('exit', function(code){ + if(code !== 0) grunt.fail.warn("Test(s) failed"); + done(); + }); }, wrap: function(src, name){ @@ -83,7 +90,8 @@ module.exports = { shell.exec('java -client -d32 -jar lib/closure-compiler/compiler.jar' + ' --compilation_level SIMPLE_OPTIMIZATIONS --language_in ECMASCRIPT5_STRICT ' + '--js ' + file + ' --js_output_file ' + minFile, - function() { + function(code) { + if (code !== 0) grunt.fail.warn('Error minifying ' + file); grunt.file.write(minFile, this.singleStrict(grunt.file.read(minFile), '\n')); grunt.log.ok(file + ' minified into ' + minFile); fn(); From 40509cf0f9098d27d0659d74199644eb05af9c0d Mon Sep 17 00:00:00 2001 From: Dave Geddes Date: Sun, 18 Nov 2012 22:18:51 -0700 Subject: [PATCH 06/16] refactor(Grunt) Windows support The build currently doesn't run in Windows. This creates a problem when a developer using Windows tries to contribute to Angular, but can't run the end-to-end tests. Fix the few things that prevented the build from working in Windows: Windows doesn't have "zip", that would be too intuitive. Solution: Use grunt-contrib-compress instead This speeds things up, but breaks for Windows 64bit users Solution: Remove flag Node can create symlinks in Windows, but you have to pass an extra "dir" option to fs. gen-docs.js uses q-fs.js to create symlinks, which doesn't pass this needed type argument. Solution: Add this third type option to qfs#symbolicLink, and pass the option in from gen-docs.js and writer.js q-fs.js Pull Request #7 pending that adds this feature (https://github.com/kriskowal/q-fs/pull/7) In the meantime, specify geddesign's fork as the dependency in package.json. Will switch back once merged. fix(gen-docs) handle symlinks correctly in Windows --- Gruntfile.js | 10 +++++++++- docs/src/gen-docs.js | 8 ++++---- docs/src/writer.js | 9 ++++----- lib/grunt/plugins.js | 8 -------- lib/grunt/utils.js | 4 ++-- package.json | 7 ++++--- 6 files changed, 23 insertions(+), 23 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 9ea789ca22b2..8b6b26f60cd1 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -6,9 +6,13 @@ module.exports = function(grunt) { grunt.loadNpmTasks('grunt-contrib-clean'); grunt.loadNpmTasks('grunt-contrib-copy'); grunt.loadNpmTasks('grunt-contrib-connect'); + grunt.loadNpmTasks('grunt-contrib-compress'); grunt.loadTasks('lib/grunt'); var NG_VERSION = util.getVersion(); + var dist = {}; + dist['build/angular-'+ NG_VERSION.full +'.zip'] = 'build/**'; + //config grunt.initConfig({ NG_VERSION: NG_VERSION, @@ -106,6 +110,10 @@ module.exports = function(grunt) { } }, + compress: { + zip: { files: dist } + }, + write: { version: {file: 'build/version.txt', val: NG_VERSION.full} } @@ -116,6 +124,6 @@ module.exports = function(grunt) { grunt.registerTask('minify', ['clean', 'build', 'minall']); grunt.registerTask('test:e2e', ['connect', 'test:end2end']); grunt.registerTask('webserver', ['connect:keepalive']); - grunt.registerTask('package', ['clean', 'build', 'minall', 'docs', 'copy', 'write', 'zip']); + grunt.registerTask('package', ['clean', 'build', 'minall', 'docs', 'copy', 'write', 'compress']); grunt.registerTask('default', ['package']); }; \ No newline at end of file diff --git a/docs/src/gen-docs.js b/docs/src/gen-docs.js index a229955aac99..1661d78677f5 100755 --- a/docs/src/gen-docs.js +++ b/docs/src/gen-docs.js @@ -38,10 +38,10 @@ writer.makeDir('build/docs/', true).then(function() { function writeTheRest(writesFuture) { var metadata = ngdoc.metadata(docs); - writesFuture.push(writer.symlinkTemplate('css')); - writesFuture.push(writer.symlinkTemplate('font')); - writesFuture.push(writer.symlink('../../docs/img', 'build/docs/img')); - writesFuture.push(writer.symlinkTemplate('js')); + writesFuture.push(writer.symlinkTemplate('css', 'dir')); + writesFuture.push(writer.symlinkTemplate('font', 'dir')); + writesFuture.push(writer.symlink('../../docs/img', 'build/docs/img', 'dir')); + writesFuture.push(writer.symlinkTemplate('js', 'dir')); var manifest = 'manifest="/build/docs/appcache.manifest"'; diff --git a/docs/src/writer.js b/docs/src/writer.js index 450f7cb55521..b6403e344d94 100644 --- a/docs/src/writer.js +++ b/docs/src/writer.js @@ -61,22 +61,21 @@ exports.copy = function(from, to, transform) { exports.symlink = symlink; -function symlink(from, to) { +function symlink(from, to, type) { return qfs.exists(to).then(function(exists) { if (!exists) { - return qfs.symbolicLink(to, from); + return qfs.symbolicLink(to, from, type); } }); } exports.symlinkTemplate = symlinkTemplate; -function symlinkTemplate(filename) { +function symlinkTemplate(filename, type) { var dest = OUTPUT_DIR + filename, dirDepth = dest.split('/').length, src = Array(dirDepth).join('../') + 'docs/src/templates/' + filename; - - return symlink(src, dest); + return symlink(src, dest, type); } diff --git a/lib/grunt/plugins.js b/lib/grunt/plugins.js index 7744a5a88930..2aefcd8059fd 100644 --- a/lib/grunt/plugins.js +++ b/lib/grunt/plugins.js @@ -27,14 +27,6 @@ module.exports = function(grunt) { grunt.log.ok('File ' + this.data.dest + ' created.'); }); - grunt.registerTask('zip', 'zip up build directory', function(){ - var zipname = 'angular-' + grunt.config('NG_VERSION').full + '.zip'; - shell.exec('zip -r ' + zipname + ' build', {silent: true}); - shell.mv(zipname, 'build/' + zipname); - if(shell.error()) grunt.fail.warn('error creating zip'); - grunt.log.ok('zipped up angular build'); - }); - grunt.registerMultiTask('write', 'write content to a file', function(){ grunt.file.write(this.data.file, this.data.val); grunt.log.ok('wrote to ' + this.data.file); diff --git a/lib/grunt/utils.js b/lib/grunt/utils.js index 5eb5eec8c9de..cac8b03f3638 100644 --- a/lib/grunt/utils.js +++ b/lib/grunt/utils.js @@ -21,7 +21,7 @@ module.exports = { var browsers = grunt.option('in'); var reporters = grunt.option('reporters'); if(browsers) browsers = browsers.substr(1, browsers.length - 2); - var p = spawn('./node_modules/testacular/bin/testacular', ['start', config, + var p = spawn('node', ['node_modules/testacular/bin/testacular', 'start', config, singleRun ? '--single-run=true' : '', reporters ? '--reporters=' + reporters : '', browsers ? '--browsers=' + browsers : '' @@ -87,7 +87,7 @@ module.exports = { min: function(file, fn) { var minFile = file.replace(/\.js$/, '.min.js'); - shell.exec('java -client -d32 -jar lib/closure-compiler/compiler.jar' + + shell.exec('java -client -jar lib/closure-compiler/compiler.jar' + ' --compilation_level SIMPLE_OPTIMIZATIONS --language_in ECMASCRIPT5_STRICT ' + '--js ' + file + ' --js_output_file ' + minFile, function(code) { diff --git a/package.json b/package.json index 493909485b12..eb831e183f0a 100644 --- a/package.json +++ b/package.json @@ -4,12 +4,13 @@ "dependencies" : { "testacular": "canary", "jasmine-node" : "*", - "q-fs" : "*", + "q-fs" : "https://github.com/geddesign/q-fs/archive/master.tar.gz", "qq" : "*", "grunt": "0.4.x", - "grunt-contrib-clean": "0.4.x", - "grunt-contrib-copy": "0.3.x", + "grunt-contrib-clean": "https://github.com/gruntjs/grunt-contrib-clean/tarball/devel", + "grunt-contrib-copy": "https://github.com/gruntjs/grunt-contrib-copy/tarball/devel", "grunt-contrib-connect": "0.1.x", + "grunt-contrib-compress": "https://github.com/gruntjs/grunt-contrib-compress/tarball/devel", "shelljs": "0.0.x", "yaml-js": "0.0.x" } From d8f952129347d95759942d9bfb77419e82261d54 Mon Sep 17 00:00:00 2001 From: Dave Geddes Date: Mon, 26 Nov 2012 00:51:16 -0700 Subject: [PATCH 07/16] chore(Grunt) use latest q-fs My pull request to q-fs was merged (https://github.com/kriskowal/q-fs/pull/7) So q-fs now supports the "type" option for symlinks, neccessary for Windows support. Switching back to the official repo of q-fs. --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index eb831e183f0a..3d5e63ff498d 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "dependencies" : { "testacular": "canary", "jasmine-node" : "*", - "q-fs" : "https://github.com/geddesign/q-fs/archive/master.tar.gz", + "q-fs" : "~0.1.35", "qq" : "*", "grunt": "0.4.x", "grunt-contrib-clean": "https://github.com/gruntjs/grunt-contrib-clean/tarball/devel", From 0a2173aef3126cfae735178275284990be8fa7e1 Mon Sep 17 00:00:00 2001 From: Dave Geddes Date: Mon, 26 Nov 2012 01:07:08 -0700 Subject: [PATCH 08/16] fix(travis) don't fail if local grunt executable already deleted When the build runs, it needs to use the global grunt-cli. But since node_modules/.bin is in the path on that server, the grunt command defaults to the local grunt, which is just a placeholder that gives a warning to use the global grunt-cli. So we rm the local one, but the build shouldn't fail if it's already been deleted. --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 3e54f6f81e46..58e7f2dd00e2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -7,7 +7,7 @@ before_script: - sh -e /etc/init.d/xvfb start - npm install -g testacular@canary - npm install -g grunt-cli - - rm node_modules/.bin/grunt + - rm -f node_modules/.bin/grunt - grunt package - grunt webserver > /dev/null & From cf3af50eb3ae89f2e18bb86d075dd488e9206420 Mon Sep 17 00:00:00 2001 From: Dave Geddes Date: Tue, 27 Nov 2012 05:59:10 -0700 Subject: [PATCH 09/16] chore(Grunt) use latest q-fs q-fs@0.1.35 introduced a regression with symlink creation. fixed in q-fs@0.1.36 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 3d5e63ff498d..aa1d5bea5ad8 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "dependencies" : { "testacular": "canary", "jasmine-node" : "*", - "q-fs" : "~0.1.35", + "q-fs" : "~0.1.36", "qq" : "*", "grunt": "0.4.x", "grunt-contrib-clean": "https://github.com/gruntjs/grunt-contrib-clean/tarball/devel", From 27112f165f467f550c88ffe94360bb60135ee07d Mon Sep 17 00:00:00 2001 From: Dave Geddes Date: Tue, 27 Nov 2012 16:29:24 -0700 Subject: [PATCH 10/16] chore(travis) update before_script no longer need to install testacular globally clear npm cache to avoid issues --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 58e7f2dd00e2..b1cd96d5c6b6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ node_js: before_script: - export DISPLAY=:99.0 - sh -e /etc/init.d/xvfb start - - npm install -g testacular@canary + - npm cache clear - npm install -g grunt-cli - rm -f node_modules/.bin/grunt - grunt package From 98e3739a13b41db12983d36b0d2663c8ae04b105 Mon Sep 17 00:00:00 2001 From: Dave Geddes Date: Tue, 27 Nov 2012 18:14:53 -0700 Subject: [PATCH 11/16] chore(Grunt) depend on specific commits Grunt 0.4 is yet to be released and is still evolving. While waiting for its release use specific, tested commits. --- Gruntfile.js | 18 ++++++++++++++---- package.json | 10 +++++----- 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 8b6b26f60cd1..6b9e7988364a 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -17,6 +17,15 @@ module.exports = function(grunt) { grunt.initConfig({ NG_VERSION: NG_VERSION, + connect: { + devserver: { + options: { + keepalive: true + } + }, + testserver: {} + }, + test: { jqlite: 'testacular-jqlite.conf.js', jquery: 'testacular-jquery.conf.js', @@ -104,9 +113,10 @@ module.exports = function(grunt) { process: ['build/docs/*.html', 'build/docs/.htaccess'] }, - copy: { + copy: { i18n: { - files: {'build/i18n/': 'src/ngLocale/**'} + files: {'build/i18n/': 'src/ngLocale/**'}, + options: {flatten: true} } }, @@ -122,8 +132,8 @@ module.exports = function(grunt) { //alias tasks grunt.registerTask('test:unit', ['test:jqlite', 'test:jquery', 'test:modules']); grunt.registerTask('minify', ['clean', 'build', 'minall']); - grunt.registerTask('test:e2e', ['connect', 'test:end2end']); - grunt.registerTask('webserver', ['connect:keepalive']); + grunt.registerTask('test:e2e', ['connect:testserver', 'test:end2end']); + grunt.registerTask('webserver', ['connect:devserver']); grunt.registerTask('package', ['clean', 'build', 'minall', 'docs', 'copy', 'write', 'compress']); grunt.registerTask('default', ['package']); }; \ No newline at end of file diff --git a/package.json b/package.json index aa1d5bea5ad8..c80824cbc8a0 100644 --- a/package.json +++ b/package.json @@ -6,11 +6,11 @@ "jasmine-node" : "*", "q-fs" : "~0.1.36", "qq" : "*", - "grunt": "0.4.x", - "grunt-contrib-clean": "https://github.com/gruntjs/grunt-contrib-clean/tarball/devel", - "grunt-contrib-copy": "https://github.com/gruntjs/grunt-contrib-copy/tarball/devel", - "grunt-contrib-connect": "0.1.x", - "grunt-contrib-compress": "https://github.com/gruntjs/grunt-contrib-compress/tarball/devel", + "grunt": "git+https://github.com/gruntjs/grunt.git#1f77fef26c", + "grunt-contrib-clean": "git+https://github.com/gruntjs/grunt-contrib-clean.git#15b40e46a6", + "grunt-contrib-copy": "git+https://github.com/gruntjs/grunt-contrib-copy.git#d658081862", + "grunt-contrib-connect": "git+https://github.com/gruntjs/grunt-contrib-connect.git#949bf6dd5e", + "grunt-contrib-compress": "git+https://github.com/gruntjs/grunt-contrib-compress.git#8f99393daa", "shelljs": "0.0.x", "yaml-js": "0.0.x" } From 6bfa8fde462095d1ee35e473e4fe04e0092c85e8 Mon Sep 17 00:00:00 2001 From: Dave Geddes Date: Wed, 5 Dec 2012 01:31:40 -0700 Subject: [PATCH 12/16] fix(docs/src) support for Windows when dealing with paths docs now set section and id correctly on Windows grunt docs task now works correctly in Windows --- docs/src/ngdoc.js | 2 +- docs/src/reader.js | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/src/ngdoc.js b/docs/src/ngdoc.js index ae612a55e6ec..8183c40c78ab 100644 --- a/docs/src/ngdoc.js +++ b/docs/src/ngdoc.js @@ -203,7 +203,7 @@ Doc.prototype = { flush(); this.shortName = this.name.split(/[\.:#]/).pop().trim(); this.id = this.id || // if we have an id just use it - (((this.file||'').match(/.*\/([^\/]*)\.ngdoc/)||{})[1]) || // try to extract it from file name + (((this.file||'').match(/.*(\/|\\)([^(\/|\\)]*)\.ngdoc/)||{})[2]) || // try to extract it from file name this.name; // default to name this.description = this.markdown(this.description); this.example = this.markdown(this.example); diff --git a/docs/src/reader.js b/docs/src/reader.js index ded573c16ad2..a021c05ff554 100644 --- a/docs/src/reader.js +++ b/docs/src/reader.js @@ -7,7 +7,8 @@ exports.collect = collect; var ngdoc = require('./ngdoc.js'), Q = require('qq'), - qfs = require('q-fs'); + qfs = require('q-fs'), + PATH = require('path'); var NEW_LINE = /\n\r?/; @@ -43,7 +44,7 @@ function collect() { var work2; if (file.match(/\.ngdoc$/)) { work2 = Q.when(qfs.read(file, 'b'), function(content){ - var section = '@section ' + file.split('/')[2] + '\n'; + var section = '@section ' + file.split(PATH.sep)[2] + '\n'; allDocs.push(new ngdoc.Doc(section + content.toString(),file, 1).parse()); }); } From 3c0989cb6c8061494cd5e5a05a401f79d6175688 Mon Sep 17 00:00:00 2001 From: Dave Geddes Date: Thu, 13 Dec 2012 17:24:26 -0700 Subject: [PATCH 13/16] refactor(Grunt) speed up grunt docs task by using spawn instead of exec Using spawn instead of exec shaves a couple seconds off the grunt docs task. --- lib/grunt/plugins.js | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/lib/grunt/plugins.js b/lib/grunt/plugins.js index 2aefcd8059fd..aec712b4b15b 100644 --- a/lib/grunt/plugins.js +++ b/lib/grunt/plugins.js @@ -1,5 +1,6 @@ var shell = require('shelljs'); var util = require('./utils.js'); +var spawn = require('child_process').spawn; module.exports = function(grunt) { grunt.registerMultiTask('min', 'minify JS files', function(){ @@ -33,12 +34,17 @@ module.exports = function(grunt) { }); grunt.registerMultiTask('docs', 'create angular docs', function(){ - var res = shell.exec('node docs/src/gen-docs.js'); - if(res.code !== 0) grunt.fail.warn('Error creating docs'); - grunt.file.expand(this.data).forEach(function(file){ - grunt.file.write(file, util.process(grunt.file.read(file), grunt.config('NG_VERSION'), false)); + var done = this.async(); + var files = this.data; + var docs = spawn('node', ['docs/src/gen-docs.js']); + docs.on('exit', function(code){ + if(code !== 0) grunt.fail.warn('Error creating docs'); + grunt.file.expand(files).forEach(function(file){ + grunt.file.write(file, util.process(grunt.file.read(file), grunt.config('NG_VERSION'), false)); + }); + grunt.log.ok('docs created'); + done(); }); - grunt.log.ok('docs created'); }); grunt.registerMultiTask('test', 'Run the unit tests with testacular', function(){ From 51c4a1f922487aa937c3f01b4e61111c35404f92 Mon Sep 17 00:00:00 2001 From: Dave Geddes Date: Thu, 13 Dec 2012 17:39:30 -0700 Subject: [PATCH 14/16] refactor(Grunt) only drop closure compiler's -d32 flag on Windows This speeds up the build again on non-Windows platforms by serveral seconds, while maintaining Windows compat. Win-win. --- lib/grunt/utils.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/grunt/utils.js b/lib/grunt/utils.js index cac8b03f3638..d956984d8e25 100644 --- a/lib/grunt/utils.js +++ b/lib/grunt/utils.js @@ -87,7 +87,8 @@ module.exports = { min: function(file, fn) { var minFile = file.replace(/\.js$/, '.min.js'); - shell.exec('java -client -jar lib/closure-compiler/compiler.jar' + + var d32 = (process.platform === "win32") ? '' : ' -d32'; + shell.exec('java -client -jar' + d32 + ' lib/closure-compiler/compiler.jar' + ' --compilation_level SIMPLE_OPTIMIZATIONS --language_in ECMASCRIPT5_STRICT ' + '--js ' + file + ' --js_output_file ' + minFile, function(code) { From 7e18dbb8ab7961e58769e60eca46e2252930cea5 Mon Sep 17 00:00:00 2001 From: Dave Geddes Date: Fri, 4 Jan 2013 14:56:38 -0700 Subject: [PATCH 15/16] refactor(Grunt) parallelize build task Executes each build in parallel for greater performance. --- Gruntfile.js | 2 +- lib/grunt/plugins.js | 19 +++++++------------ lib/grunt/utils.js | 18 +++++++++++++++++- 3 files changed, 25 insertions(+), 14 deletions(-) diff --git a/Gruntfile.js b/Gruntfile.js index 6b9e7988364a..def720513165 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -134,6 +134,6 @@ module.exports = function(grunt) { grunt.registerTask('minify', ['clean', 'build', 'minall']); grunt.registerTask('test:e2e', ['connect:testserver', 'test:end2end']); grunt.registerTask('webserver', ['connect:devserver']); - grunt.registerTask('package', ['clean', 'build', 'minall', 'docs', 'copy', 'write', 'compress']); + grunt.registerTask('package', ['clean', 'buildall', 'minall', 'docs', 'copy', 'write', 'compress']); grunt.registerTask('default', ['package']); }; \ No newline at end of file diff --git a/lib/grunt/plugins.js b/lib/grunt/plugins.js index aec712b4b15b..f1285a286ac4 100644 --- a/lib/grunt/plugins.js +++ b/lib/grunt/plugins.js @@ -14,18 +14,13 @@ module.exports = function(grunt) { }); grunt.registerMultiTask('build', 'build JS files', function(){ - var files = grunt.file.expand(this.data.src); - var styles = this.data.styles; - //concat - var src = files.map(function(filepath){ - return grunt.file.read(filepath); - }).join(grunt.util.normalizelf('\n')); - //process - var processed = util.process(src, grunt.config('NG_VERSION'), this.data.strict); - if (styles) processed = util.addStyle(processed, styles.css, styles.minify); - //write - grunt.file.write(this.data.dest, processed); - grunt.log.ok('File ' + this.data.dest + ' created.'); + util.build.call(util, this.data, this.async()); + }); + + grunt.registerTask('buildall', 'build all the JS files in parallel', function(){ + var builds = grunt.config('build'); + builds = Object.keys(builds).map(function(key){ return builds[key]; }); + grunt.util.async.forEach(builds, util.build.bind(util), this.async()); }); grunt.registerMultiTask('write', 'write content to a file', function(){ diff --git a/lib/grunt/utils.js b/lib/grunt/utils.js index d956984d8e25..d5525ef8e7d2 100644 --- a/lib/grunt/utils.js +++ b/lib/grunt/utils.js @@ -78,6 +78,22 @@ module.exports = { return processed; }, + build: function(config, fn){ + var files = grunt.file.expand(config.src); + var styles = config.styles; + //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) processed = this.addStyle(processed, styles.css, styles.minify); + //write + grunt.file.write(config.dest, processed); + grunt.log.ok('File ' + config.dest + ' created.'); + fn(); + }, + singleStrict: function(src, insert, newline){ var useStrict = newline ? "$1\n'use strict';" : "$1'use strict';"; return src @@ -90,7 +106,7 @@ module.exports = { var d32 = (process.platform === "win32") ? '' : ' -d32'; shell.exec('java -client -jar' + d32 + ' lib/closure-compiler/compiler.jar' + ' --compilation_level SIMPLE_OPTIMIZATIONS --language_in ECMASCRIPT5_STRICT ' + - '--js ' + file + ' --js_output_file ' + minFile, + '--js ' + file + ' --js_output_file ' + minFile, function(code) { if (code !== 0) grunt.fail.warn('Error minifying ' + file); grunt.file.write(minFile, this.singleStrict(grunt.file.read(minFile), '\n')); From 10dba5220bedfaba1472c5a67cb90f1e1bda2fc0 Mon Sep 17 00:00:00 2001 From: Dave Geddes Date: Fri, 4 Jan 2013 21:46:07 -0700 Subject: [PATCH 16/16] refactor(Grunt) CSP and rewrites for grunt webserver Makes the grunt webserver task handle the extras that the old nodeserver used to: - optional CSP headers - rewrite rules for the docs - favicon --- Gruntfile.js | 15 ++++++++++++++- images/favicon.ico | Bin 0 -> 1150 bytes lib/grunt/plugins.js | 1 - lib/grunt/utils.js | 24 ++++++++++++++++++++++++ 4 files changed, 38 insertions(+), 2 deletions(-) create mode 100644 images/favicon.ico diff --git a/Gruntfile.js b/Gruntfile.js index def720513165..4cec8829b345 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -20,7 +20,20 @@ module.exports = function(grunt) { connect: { devserver: { options: { - keepalive: true + port: 8000, + hostname: 'localhost', + base: '.', + keepalive: true, + middleware: function(connect, options){ + return [ + //uncomment to enable CSP + // util.csp(), + util.rewrite(), + connect.favicon('images/favicon.ico'), + connect.static(options.base), + connect.directory(options.base) + ]; + } } }, testserver: {} diff --git a/images/favicon.ico b/images/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..fe24a63a6ba4c4b4fb0e960abb60e51dc9097d43 GIT binary patch literal 1150 zcmZuxZAep57(QaqUkSemBP}6IGixM-R$2;?eQEiH)}M%sFz|;4MNX|WiYN&)b(!;H z6Y*nt*TibqTMgRG)HS!ek&a*=1h9&ehx^|5d7kq==bVdB5WYh~ z5Pn9Zl1&JOBZT5`;1tgM&*K3h{B8XO04Pv69FAiQqf%+L-x}R+cT}L7K)Hp5g#({H zs)ujg7~xW4RgiVK6Q0-0+&Z1^>C)0tWFQR5EiNv`j*gB=t1D>k=&rAj6sm=NVV@x` zw2hCPj4lwp82qEx>mRPHtc3aV7>%aNIw8HBkub*N2zEW5@azt2gMusxh+8R_*=)Y< z8=bRvJutLfX4G3b|p{YRzFR?!H&x@}p?E$;po?|kZ?qnve^}WB- z4mRsN9Ew)M*>nl7U3gmR!xE0m1>G<`JzX|FKGB*SHR01IKAcZw@!XtnuR;$E^#jn_ zB7;5QtJ(ygeY^|so_h9fSglM7om_(_976-Yz&vAx!9fa&&aBlY_#=rOpwVb3X$NB# zL~g_vWg3C__e!);o8Sd8VxX1ul+kDuzL7I*^-Y>1J_;gMx#WE_a1pONsV9-NO$>#n zBv32t<%SgRXIPfK>)nHR-y~wL8ac%ns>*5ZLYf+~Q@=oRY&%@ak;0?