From e4145deeb7a72ac321113fde672e1b499ecb0d2d Mon Sep 17 00:00:00 2001 From: Mario Vavti Date: Wed, 18 Nov 2015 23:50:42 +0100 Subject: some events refacturing --- library/moment/tasks/bump_version.js | 65 +++++++ library/moment/tasks/check_sauce_creds.js | 13 ++ library/moment/tasks/component.js | 10 ++ library/moment/tasks/embed_locales.js | 47 +++++ library/moment/tasks/history.js | 123 +++++++++++++ library/moment/tasks/nuget.js | 29 +++ library/moment/tasks/qtest.js | 46 +++++ library/moment/tasks/size.js | 59 +++++++ library/moment/tasks/transpile.js | 284 ++++++++++++++++++++++++++++++ library/moment/tasks/update_index.js | 16 ++ library/moment/tasks/zones.js | 70 ++++++++ 11 files changed, 762 insertions(+) create mode 100644 library/moment/tasks/bump_version.js create mode 100644 library/moment/tasks/check_sauce_creds.js create mode 100644 library/moment/tasks/component.js create mode 100644 library/moment/tasks/embed_locales.js create mode 100644 library/moment/tasks/history.js create mode 100644 library/moment/tasks/nuget.js create mode 100644 library/moment/tasks/qtest.js create mode 100644 library/moment/tasks/size.js create mode 100644 library/moment/tasks/transpile.js create mode 100644 library/moment/tasks/update_index.js create mode 100644 library/moment/tasks/zones.js (limited to 'library/moment/tasks') diff --git a/library/moment/tasks/bump_version.js b/library/moment/tasks/bump_version.js new file mode 100644 index 000000000..f3dea3106 --- /dev/null +++ b/library/moment/tasks/bump_version.js @@ -0,0 +1,65 @@ +module.exports = function (grunt) { + grunt.registerTask('bump_version', function (version) { + if (!version || version.split('.').length !== 3) { + grunt.fail.fatal('malformed version. Use\n\n grunt bump_version:1.2.3'); + } + + grunt.config('string-replace.moment-js', { + files: {'src/moment.js': 'src/moment.js'}, + options: { + replacements: [ + { + pattern: /\/\/! version : .*/, + replacement: '//! version : ' + version + }, { + pattern: /moment\.version = '.*'/, + replacement: "moment.version = '" + version + "'" + } + ] + } + }); + + grunt.config('string-replace.package-json', { + files: {'package.json': 'package.json'}, + options: { + replacements: [ + { + pattern: /"version": .*/, + replacement: '"version": "' + version + '",' + } + ] + } + }); + + grunt.config('string-replace.component-json', { + files: {'component.json': 'component.json'}, + options: { + replacements: [ + { + pattern: /"version": .*/, + replacement: '"version": "' + version + '",' + } + ] + } + }); + + grunt.config('string-replace.moment-js-nuspec', { + files: {'Moment.js.nuspec': 'Moment.js.nuspec'}, + options: { + replacements: [ + { + pattern: /.*<\/version>/, + replacement: '' + version + '' + } + ] + } + }); + + grunt.task.run([ + 'string-replace:moment-js', + 'string-replace:package-json', + 'string-replace:component-json', + 'string-replace:moment-js-nuspec' + ]); + }); +}; diff --git a/library/moment/tasks/check_sauce_creds.js b/library/moment/tasks/check_sauce_creds.js new file mode 100644 index 000000000..e56951d47 --- /dev/null +++ b/library/moment/tasks/check_sauce_creds.js @@ -0,0 +1,13 @@ +module.exports = function (grunt) { + // Pull requests do not have secure variables enabled for security reasons. + // Use this task before launching travis-sauce-browser task, so it would + // exit early and won't try connecting to SauceLabs without credentials. + grunt.registerTask('check-sauce-creds', function () { + if (process.env.SAUCE_USERNAME === undefined) { + grunt.log.writeln('No sauce credentials found'); + grunt.task.clearQueue(); + } else { + grunt.log.writeln('Sauce credentials found'); + } + }); +}; diff --git a/library/moment/tasks/component.js b/library/moment/tasks/component.js new file mode 100644 index 000000000..f9398aead --- /dev/null +++ b/library/moment/tasks/component.js @@ -0,0 +1,10 @@ +module.exports = function (grunt) { + grunt.registerTask('component', function () { + var config = JSON.parse(grunt.file.read('component.json')); + + config.files = grunt.file.expand('locale/*.js'); + config.files.unshift('moment.js'); + + grunt.file.write('component.json', JSON.stringify(config, true, 2) + '\n'); + }); +}; diff --git a/library/moment/tasks/embed_locales.js b/library/moment/tasks/embed_locales.js new file mode 100644 index 000000000..5027af10c --- /dev/null +++ b/library/moment/tasks/embed_locales.js @@ -0,0 +1,47 @@ + +module.exports = function (grunt) { + grunt.registerTask('embedLocales', function () { + var config = grunt.config('embedLocales'); + + var files = grunt.file.expand(config.targetLocales); + var embeddedContents = determineEmbeddedContent(files); + + var momentContents = grunt.file.read(config.moment); + var modifiedContents = momentContents.replace('/* EMBED_LOCALES */', function () { + // If we don't do this, $ symbols in locale files may get interpreted in + // the regex replacement + return embeddedContents; + }); + + grunt.file.write(config.dest, modifiedContents); + }); + + var languageReset = 'moment.locale(\'en\');'; + + function determineEmbeddedContent(files) { + var embeddedContent = ''; + files.forEach(function (file) { + embeddedContent += transformFile(file); + }); + embeddedContent += '\n ' + languageReset + '\n'; + return embeddedContent; + } + + var reTransform = /function \(factory\) \{[^]*\}(?=\(function \(moment\) \{)/gm; + var replaceWith = + 'function (factory) {\n' + + ' factory(moment);\n' + + '}'; + + function transformFile(file) { + var fileContents = grunt.file.read(file); + + if (!fileContents.match(reTransform)) { + grunt.warn('Warning: all locale files must use the common UMD wrapper pattern. Failed locale file: ' + file); + return ''; + } + + return fileContents.replace(reTransform, replaceWith); + } +}; + diff --git a/library/moment/tasks/history.js b/library/moment/tasks/history.js new file mode 100644 index 000000000..85e83338f --- /dev/null +++ b/library/moment/tasks/history.js @@ -0,0 +1,123 @@ +var https = require('https'), + zlib = require('zlib'), + path = require('path'), + fs = require('fs'); + +var count = 0; +var resolved = 0; + +var outputs = []; + +var done; + +function check() { + if (resolved === count) { + normalize(); + display(); + } +} + +function makeBar(length) { + var i = ''; + while (i.length < length) { + i += '='; + } + return i; +} + +function normalize() { + var i, + max = 0, + max2 = 0; + for (i = 0; i < count; i++) { + max = Math.max(max, outputs[i].gzip); + max2 = Math.max(max2, outputs[i].original); + } + for (i = 0; i < count; i++) { + outputs[i].bargraph = makeBar((outputs[i].gzip / max) * 80); + outputs[i].bargraph2 = makeBar((outputs[i].original / max2) * 80); + } +} + +function display() { + var i; + for (i = 0; i < count; i++) { + console.log(outputs[i].version + ' ' + outputs[i].gzip + ' ' + outputs[i].original); + console.log('gzip ' + outputs[i].bargraph); + console.log('orig ' + outputs[i].bargraph2); + } + done(); +} + +function getSizeAtVersion(version, path) { + var data = '', + op = {}, + + req = https.request({ + host: 'raw.github.com', + port: 443, + path: '/timrwood/moment/' + version + path + }, function (res) { + res.setEncoding('utf8'); + res.on('data', function (chunk) { + data += chunk; + }); + res.on('end', function (e) { + zlib.gzip(data, function (error, result) { + op.version = version; + op.gzip = result.length; + op.original = data.length; + resolved++; + check(); + }); + }); + }); + + req.on('error', function (e) { + console.log('problem with request: ' + e.message); + }); + req.end(); + count++; + outputs.push(op); +} + +function getRemote() { + var oldVersions = '1.0.1 1.1.0 1.1.1 1.1.2 1.2.0 1.3.0 1.4.0'.split(' '), + newVersions = '1.5.0 1.5.1 1.6.0 1.6.1 1.7.0 1.7.1'.split(' '), + i; + + for (i = 0; i < oldVersions.length; i++) { + getSizeAtVersion(oldVersions[i], '/moment.min.js'); + } + for (i = 0; i < newVersions.length; i++) { + getSizeAtVersion(newVersions[i], '/min/moment.min.js'); + } +} + +function getLocal() { + count++; + var op = {}; + outputs.push(op); + fs.readFile(path.normalize(__dirname + '/../min/moment.min.js'), 'utf8', function (err, data) { + if (err) { + throw err; + } + zlib.gzip(data, function (error, result) { + op.version = '.next'; + op.gzip = result.length; + op.original = data.length; + resolved++; + check(); + }); + }); +} + + + +module.exports = function (grunt) { + grunt.registerTask('history', 'Check the codebase filesize over different releases.', function () { + done = this.async(); + getRemote(); + getLocal(); + }); +}; diff --git a/library/moment/tasks/nuget.js b/library/moment/tasks/nuget.js new file mode 100644 index 000000000..7827b2400 --- /dev/null +++ b/library/moment/tasks/nuget.js @@ -0,0 +1,29 @@ +module.exports = function (grunt) { + // If this fails you might need to follow: + // + // http://stackoverflow.com/questions/15181888/nuget-on-linux-error-getting-response-stream + // + // $ sudo mozroots --import --machine --sync + // $ sudo certmgr -ssl -m https://go.microsoft.com + // $ sudo certmgr -ssl -m https://nugetgallery.blob.core.windows.net + // $ sudo certmgr -ssl -m https://nuget.org + + grunt.config('nugetpack', { + dist: { + src: 'Moment.js.nuspec', + dest: './' + } + }); + grunt.config('nugetpush', { + dist: { + src: 'Moment.js.*.nupkg' + } + }); + grunt.config('clean.nuget', { + src: 'Moment.js.*.nupkg' + }); + + grunt.registerTask('nuget-publish', [ + 'nugetpack', 'nugetpush', 'clean:nuget' + ]); +}; diff --git a/library/moment/tasks/qtest.js b/library/moment/tasks/qtest.js new file mode 100644 index 000000000..ab925321b --- /dev/null +++ b/library/moment/tasks/qtest.js @@ -0,0 +1,46 @@ +module.exports = function (grunt) { + grunt.task.registerTask('qtest', 'run tests locally', function () { + var done = this.async(); + + var testrunner = require('qunit'); + + testrunner.options.log.assertions = false; + testrunner.options.log.tests = false; + testrunner.options.log.summary = false; + testrunner.options.log.testing = false; + testrunner.options.maxBlockDuration = 120000; + + var tests; + + if (grunt.option('only') != null) { + tests = grunt.file.expand.apply(null, grunt.option('only').split(',').map(function (file) { + if (file === 'moment') { + return 'build/umd/test/moment/*.js'; + } else if (file === 'locale') { + return 'build/umd/test/locale/*.js'; + } else { + return 'build/umd/test/' + file + '.js'; + } + })); + } else { + tests = grunt.file.expand('build/umd/test/moment/*.js', + 'build/umd/test/locale/*.js'); + } + + testrunner.run({ + code: 'build/umd/moment.js', + tests: tests + }, function (err, report) { + if (err) { + console.log('woot', err, report); + done(err); + return; + } + err = null; + if (report.failed !== 0) { + err = new Error(report.failed + ' tests failed'); + } + done(err); + }); + }); +}; diff --git a/library/moment/tasks/size.js b/library/moment/tasks/size.js new file mode 100644 index 000000000..2ab380e9f --- /dev/null +++ b/library/moment/tasks/size.js @@ -0,0 +1,59 @@ +var https = require('https'), + zlib = require('zlib'), + path = require('path'), + fs = require('fs'); + +var stable = '1.7.1', + done; + +function getVersion(path, cb) { + var data = '', + req = https.request({ + host: 'raw.github.com', + port: 443, + path: '/timrwood/moment/' + path + }, function (res) { + res.setEncoding('utf8'); + res.on('data', function (chunk) { + data += chunk; + }); + res.on('end', function (e) { + zlib.gzip(data, function (error, result) { + cb(data.length, result.length); + }); + }); + }); + req.on('error', function (e) { + console.log('problem with request: ' + e.message); + }); + req.end(); +} + +function printDiffs(stableLen, stableGzip, currentLen, currentGzip) { + var diff = currentLen - stableLen, + gzipDiff = currentGzip - stableGzip; + + console.log('Filesize difference from current branch to ' + stable); + console.log(stable + ' ' + stableLen + ' / ' + stableGzip); + console.log('curr ' + currentLen + ' / ' + currentGzip); + console.log('diff ' + (diff > 0 ? '+' : '') + diff); + console.log('gzip ' + (gzipDiff > 0 ? '+' : '') + gzipDiff); +} + + +module.exports = function (grunt) { + grunt.registerTask('size', 'Check the codebase filesize against the latest stable version.', function () { + done = this.async(); + fs.readFile(path.normalize(__dirname + '/../min/moment.min.js'), 'utf8', function (err, data) { + if (err) { + throw err; + } + zlib.gzip(data, function (error, result) { + getVersion(stable + '/min/moment.min.js', function (stableLength, stableGzipLength) { + printDiffs(stableLength, stableGzipLength, data.length, result.length); + done(); + }); + }); + }); + }); +}; diff --git a/library/moment/tasks/transpile.js b/library/moment/tasks/transpile.js new file mode 100644 index 000000000..d253407d1 --- /dev/null +++ b/library/moment/tasks/transpile.js @@ -0,0 +1,284 @@ +module.exports = function (grunt) { + var esperanto = require('esperanto'); + var path = require('path'); + var Promise = require('es6-promise').Promise; + var TMP_DIR = 'build/tmp'; + + function moveComments(code) { + var comments = [], rest = []; + code.split('\n').forEach(function (line) { + if (line.trim().slice(0, 3) === '//!') { + comments.push(line.trim()); + } else { + rest.push(line); + } + }); + + return comments.concat([''], rest).join('\n'); + } + + var headerCache = {}; + function getHeaderByFile(headerFile) { + if (!(headerFile in headerCache)) { + headerCache[headerFile] = grunt.file.read(headerFile); + } + return headerCache[headerFile]; + } + + function transpile(opts) { + // base, entry, skip, headerFile, skipLines, target + var umdName = opts.headerFile ? 'not_used' : opts.umdName, + header = opts.headerFile ? getHeaderByFile(opts.headerFile) : '', + skipLines = opts.skipLines ? opts.skipLines : 0; + + return esperanto.bundle({ + base: opts.base, + entry: opts.entry, + skip: opts.skip || [] + }).then(function (bundle) { + var umd = bundle.toUmd({name: umdName}), + fixed = header + umd.code.split('\n').slice(skipLines).join('\n'); + if (opts.moveComments) { + fixed = moveComments(fixed); + } + grunt.file.write(opts.target, fixed); + }); + } + + function transpileMany(opts) { + var batchSize = 50, + promise = Promise.resolve(null), + files = grunt.file.expand({cwd: opts.base}, opts.pattern), + i, + transpileOne = function (i) { + promise = promise.then(function () { + return Promise.all(files.slice(i, i + batchSize).map(function (file) { + return transpile({ + base: opts.base, + entry: file, + headerFile: opts.headerFile, + skip: opts.skip, + skipLines: opts.skipLines, + moveComments: opts.moveComments, + target: path.join(opts.targetDir, file) + }); + })); + }); + }; + + for (i = 0; i < files.length; i += batchSize) { + transpileOne(i); + } + + return promise; + } + + function prepareTemp(base) { + var files = grunt.file.expand({cwd: base}, '**/*.js'), + tmpDir = TMP_DIR; + if (grunt.file.exists(tmpDir)) { + return; + } + files.forEach(function (file) { + grunt.file.copy(path.join(base, file), path.join(tmpDir, file)); + }); + } + + function transpileCode(opts) { + var entry = opts.entry || path.basename(opts.target); + prepareTemp(opts.base); + grunt.file.write(path.join(TMP_DIR, entry), opts.code); + return transpile({ + base: TMP_DIR, + entry: entry, + umdName: opts.umdName || 'not_used', + headerFile: opts.headerFile, + skipLines: opts.skipLines, + moveComments: opts.moveComments, + target: opts.target, + skip: opts.skip + }); + } + + function generateLocales(target, localeFiles) { + var files = localeFiles, + code = files.map(function (file) { + var identifier = path.basename(file, '.js').replace('-', '_'); + return 'import ' + identifier + ' from "./' + file + '";'; + }).join('\n'); + return transpileCode({ + base: 'src', + code: code, + target: target, + skip: ['moment'], + headerFile: 'templates/locale-header.js', + skipLines: 5 + }); + } + + function generateMomentWithLocales(target, localeFiles) { + var files = localeFiles, + importCode = files.map(function (file) { + var identifier = path.basename(file, '.js').replace('-', '_'); + var fileNoExt = file.replace('.js', ''); + return 'import ' + identifier + ' from "./' + fileNoExt + '";'; + }).join('\n'), + code = 'import * as moment_export from "./moment";\n\n' + + importCode + '\n\n' + + 'export default moment_export;'; + + return transpileCode({ + base: 'src', + code: code, + umdName: 'moment', + target: target + }).then(function () { + var code = grunt.file.read(target); + var getDefaultRegExp = new RegExp('var ([a-z$_]+) =\\s+{[^]\\s+get default \\(\\) { return ([a-z$_]+); }[^]\\s+}', ''); + var crap = code.match(getDefaultRegExp); + if (crap.length !== 3) { + grunt.file.write('/tmp/crap.js', code); + throw new Error('Failed to detect get default crap, check /tmp/crap.js'); + } + code = code.replace(getDefaultRegExp, ''); + + var buildExportVars = ['moment_with_locales', 'moment_with_locales_custom']; + buildExportVars.forEach(function (buildExportVar) { + var languageReset = buildExportVar + '.locale(\'en\');'; + code = code.replace('var ' + buildExportVar + ' = ' + crap[1] + ';', + 'var ' + buildExportVar + ' = ' + crap[2] + ';\n' + + ' ' + languageReset); + }); + + if (code.match('get default')) { + grunt.file.write('/tmp/crap.js', code); + throw new Error('Stupid shit es6 get default plaguing the code, check /tmp/crap.js'); + } + grunt.file.write(target, code); + }); + } + + grunt.task.registerTask('transpile-raw', 'convert es6 to umd', function () { + var done = this.async(); + + transpile({ + base: 'src', + entry: 'moment.js', + umdName: 'moment', + target: 'build/umd/moment.js', + moveComments: true + }).then(function () { + grunt.log.ok('build/umd/moment.js'); + }).then(function () { + return transpileMany({ + base: 'src', + pattern: 'locale/*.js', + headerFile: 'templates/locale-header.js', + skipLines: 5, + moveComments: true, + targetDir: 'build/umd', + skip: ['moment'] + }); + }).then(function () { + grunt.log.ok('build/umd/locale/*.js'); + }).then(function () { + return transpileMany({ + base: 'src', + pattern: 'test/moment/*.js', + headerFile: 'templates/test-header.js', + skipLines: 5, + moveComments: true, + targetDir: 'build/umd', + skip: ['moment'] + }); + }).then(function () { + grunt.log.ok('build/umd/test/moment/*.js'); + }).then(function () { + return transpileMany({ + base: 'src', + pattern: 'test/locale/*.js', + headerFile: 'templates/test-header.js', + skipLines: 5, + moveComments: true, + targetDir: 'build/umd', + skip: ['moment'] + }); + }).then(function () { + grunt.log.ok('build/umd/test/locale/*.js'); + }).then(function () { + return generateLocales('build/umd/min/locales.js', + grunt.file.expand({cwd: 'src'}, 'locale/*.js')); + }).then(function () { + grunt.log.ok('build/umd/min/locales.js'); + }).then(function () { + return generateMomentWithLocales('build/umd/min/moment-with-locales.js', + grunt.file.expand({cwd: 'src'}, 'locale/*.js')); + }).then(function () { + grunt.log.ok('build/umd/min/moment-with-locales.js'); + }).then(done, function (e) { + grunt.log.error('error transpiling', e); + done(e); + }); + }); + + grunt.task.registerTask('transpile-custom-raw', + 'build just custom language bundles', + function (locales) { + var done = this.async(); + + var localeFiles = locales.split(',').map(function (locale) { + var file = grunt.file.expand({cwd: 'src'}, 'locale/' + locale + '.js'); + if (file.length !== 1) { + // we failed to find a locale + done(new Error('could not find locale: ' + locale)); + done = null; + } else { + return file[0]; + } + }); + + // There was an issue with a locale + if (done == null) { + return; + } + + return generateLocales( + 'build/umd/min/locales.custom.js', localeFiles + ).then(function () { + grunt.log.ok('build/umd/min/locales.custom.js'); + }).then(function () { + return generateMomentWithLocales('build/umd/min/moment-with-locales.custom.js', + localeFiles); + }).then(function () { + grunt.log.ok('build/umd/min/moment-with-locales.custom.js'); + }).then(done, function (e) { + grunt.log.error('error transpiling-custom', e); + done(e); + }); + }); + + grunt.config('clean.build', [ + 'build' + ]); + + grunt.config('concat.tests', { + src: 'build/umd/test/**/*.js', + dest: 'build/umd/min/tests.js' + }); + + grunt.task.registerTask('transpile', + 'builds all es5 files, optinally creating custom locales', + function (locales) { + var tasks = [ + 'clean:build', + 'transpile-raw', + 'concat:tests' + ]; + + if (locales) { + tasks.push('transpile-custom-raw:' + locales); + } + + grunt.task.run(tasks); + }); +}; diff --git a/library/moment/tasks/update_index.js b/library/moment/tasks/update_index.js new file mode 100644 index 000000000..c866e2694 --- /dev/null +++ b/library/moment/tasks/update_index.js @@ -0,0 +1,16 @@ +module.exports = function (grunt) { + grunt.config('copy.index-files', { + expand: true, + cwd: 'build/umd/', + src: [ + 'moment.js', + 'locale/*.js', + 'min/locales.js', + 'min/moment-with-locales.js', + 'min/tests.js' + ], + dest: '.' + }); + + grunt.registerTask('update-index', ['copy:index-files']); +}; diff --git a/library/moment/tasks/zones.js b/library/moment/tasks/zones.js new file mode 100644 index 000000000..7aa88b1f6 --- /dev/null +++ b/library/moment/tasks/zones.js @@ -0,0 +1,70 @@ +var fs = require('fs'); + + +module.exports = function (grunt) { + var ZONE_TAB = '/usr/share/zoneinfo/zone.tab'; + + grunt.registerTask('zones', 'Run the unit tests in different timezones.', function () { + var done = this.async(); + + getAllTimezones(function (err, zones) { + if (err != null) { + throw err; + } + (function iterator(i) { + if (i >= zones.length) { + return done(); + } + runTestsInZone(zones[i], function (err) { + if (err != null) { + throw err; + } + iterator(i + 1); + }); + }(0)); + }); + }); + + function getAllTimezones (callback) { + fs.readFile(ZONE_TAB, 'ascii', function (err, content) { + if (err != null) { + callback(err); + } + callback(null, content.split(/\r\n|\r|\n/) + // remove empty and commented lines + .filter(function (line) { + return line && !/^#/.test(line); + }) + // country code TAB coordinates TAB timezone + .map(function (line) { + return line.split('\t')[2]; + })); + }); + } + + function runTestsInZone (zone, next) { + grunt.log.ok('Running tests in zone ' + zone); + grunt.util.spawn({ + cmd: 'grunt', + opts: { + env: { + 'PATH': process.env.PATH, + 'TZ': zone + } + }, + args: ['--no-color', 'nodeunit'] + }, function (err, result, code) { + if (code !== 0) { + grunt.log.error(result.stdout.split(/\r\n|\r|\n/) + .filter(function (line) { + return /^(>>|Warning:|$)/.test(line); + }) + .map(function (line) { + return (line.substr(0, 3) === '>> ' ? line.substr(3) : line); + }) + .join('\n')); + } + next(); + }); + } +}; -- cgit v1.2.3