Merge branch '4.x'
# Conflicts: # Gruntfile.js # package-lock.json # package.json
This commit is contained in:
@@ -9,6 +9,7 @@ sauce_connect.log*
|
||||
yarn-error.log
|
||||
node_modules
|
||||
/handlebars-release.tgz
|
||||
.nyc_output
|
||||
|
||||
# Generated files
|
||||
lib/handlebars/compiler/parser.js
|
||||
|
||||
@@ -17,7 +17,6 @@ module.exports = {
|
||||
// Best Practices //
|
||||
//----------------//
|
||||
'default-case': 'warn',
|
||||
'dot-notation': ['error', { allowKeywords: false }],
|
||||
'guard-for-in': 'warn',
|
||||
'no-alert': 'error',
|
||||
'no-caller': 'error',
|
||||
|
||||
@@ -10,6 +10,7 @@ sauce_connect.log*
|
||||
/yarn.lock
|
||||
node_modules
|
||||
/handlebars-release.tgz
|
||||
.nyc_output
|
||||
|
||||
# Generated files
|
||||
lib/handlebars/compiler/parser.js
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
instrumentation:
|
||||
excludes: ['**/spec/**', '**/handlebars/compiler/parser.js']
|
||||
@@ -9,6 +9,7 @@ sauce_connect.log*
|
||||
yarn-error.log
|
||||
node_modules
|
||||
/handlebars-release.tgz
|
||||
.nyc_output
|
||||
|
||||
# Generated files
|
||||
lib/handlebars/compiler/parser.js
|
||||
|
||||
+3
-1
@@ -211,6 +211,8 @@ module.exports = function(grunt) {
|
||||
'copy:components'
|
||||
]);
|
||||
|
||||
this.registerTask('test', ['test:bin', 'test:cov']);
|
||||
|
||||
grunt.registerTask('bench', ['metrics']);
|
||||
|
||||
if (process.env.SAUCE_USERNAME) {
|
||||
@@ -225,7 +227,7 @@ module.exports = function(grunt) {
|
||||
'bgShell:integrationTests',
|
||||
'sauce',
|
||||
'metrics',
|
||||
'publish:latest'
|
||||
'publish-to-aws'
|
||||
]);
|
||||
grunt.registerTask('on-file-change', ['build', 'concat:tests', 'test']);
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
module.exports = {
|
||||
'check-coverage': true,
|
||||
branches: 100,
|
||||
lines: 100,
|
||||
functions: 100,
|
||||
statements: 100,
|
||||
exclude: ['**/spec/**', '**/handlebars/compiler/parser.js'],
|
||||
reporter: 'html'
|
||||
};
|
||||
Generated
+918
-275
File diff suppressed because it is too large
Load Diff
+4
-3
@@ -44,7 +44,8 @@
|
||||
"eslint-config-prettier": "^6.7.0",
|
||||
"eslint-plugin-compat": "^3.3.0",
|
||||
"eslint-plugin-es5": "^1.4.1",
|
||||
"grunt": "^1.0.3",
|
||||
"fs-extra": "^8.1.0",
|
||||
"grunt": "^1.0.4",
|
||||
"grunt-babel": "^5.0.0",
|
||||
"grunt-bg-shell": "^2.3.3",
|
||||
"grunt-cli": "^1",
|
||||
@@ -57,12 +58,12 @@
|
||||
"grunt-contrib-watch": "^1.1.0",
|
||||
"grunt-webpack": "^1.0.8",
|
||||
"husky": "^3.1.0",
|
||||
"istanbul": "kpdecker/istanbul",
|
||||
"jison": "0.4.16",
|
||||
"lint-staged": "^9.5.0",
|
||||
"mocha": "^5",
|
||||
"mock-stdin": "^0.3.0",
|
||||
"mustache": "^2.1.3",
|
||||
"nyc": "^14.1.1",
|
||||
"prettier": "^1.19.1",
|
||||
"semver": "^5.0.1",
|
||||
"sinon": "^7.5.0",
|
||||
@@ -86,7 +87,7 @@
|
||||
"lint": "eslint --max-warnings 0 . ",
|
||||
"dtslint": "dtslint types",
|
||||
"test": "grunt",
|
||||
"extensive-tests-and-publish-to-aws": "grunt --stack extensive-tests-and-publish-to-aws",
|
||||
"extensive-tests-and-publish-to-aws": "npx mocha tasks/task-tests/ && grunt --stack extensive-tests-and-publish-to-aws",
|
||||
"integration-test": "grunt integration-tests",
|
||||
"--- combined tasks ---": "",
|
||||
"check-before-pull-request": "concurrently --kill-others-on-fail npm:lint npm:dtslint npm:check-format npm:test"
|
||||
|
||||
@@ -710,6 +710,23 @@ describe('builtin helpers', function() {
|
||||
shouldCompileTo(string, hash, '');
|
||||
equals(true, called);
|
||||
});
|
||||
|
||||
it('should pass zero log arguments', function() {
|
||||
var string = '{{log}}';
|
||||
var hash = { blah: 'whee' };
|
||||
var called;
|
||||
|
||||
console.info = console.log = function() {
|
||||
expect(arguments.length).to.equal(0);
|
||||
called = true;
|
||||
console.log = $log;
|
||||
};
|
||||
|
||||
expectTemplate(string)
|
||||
.withInput(hash)
|
||||
.toCompileTo('');
|
||||
expect(called).to.be.true();
|
||||
});
|
||||
/* eslint-enable no-console */
|
||||
});
|
||||
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"globals": {
|
||||
"require": true
|
||||
},
|
||||
"rules": {
|
||||
// Disabling for tests, for now.
|
||||
"no-path-concat": 0,
|
||||
|
||||
"no-var": 0,
|
||||
"no-shadow": 0,
|
||||
"handle-callback-err": 0,
|
||||
"no-console": 0,
|
||||
"no-process-env": 0,
|
||||
"dot-notation": [2, {"allowKeywords": true}]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
module.exports = {
|
||||
extends: ['../.eslintrc.js'],
|
||||
parserOptions: {
|
||||
sourceType: 'module',
|
||||
ecmaVersion: 2017,
|
||||
ecmaFeatures: {}
|
||||
},
|
||||
rules: {
|
||||
'no-process-env': 'off',
|
||||
'prefer-const': 'warn',
|
||||
'compat/compat': 'off',
|
||||
'dot-notation': ['error', { allowKeywords: true }]
|
||||
}
|
||||
};
|
||||
+20
-17
@@ -1,26 +1,29 @@
|
||||
var _ = require('underscore'),
|
||||
async = require('neo-async'),
|
||||
metrics = require('../bench');
|
||||
const metrics = require('../bench');
|
||||
const { createRegisterAsyncTaskFn } = require('./util/async-grunt-task');
|
||||
|
||||
module.exports = function(grunt) {
|
||||
grunt.registerTask('metrics', function() {
|
||||
var done = this.async(),
|
||||
execName = grunt.option('name'),
|
||||
events = {};
|
||||
const registerAsyncTask = createRegisterAsyncTaskFn(grunt);
|
||||
|
||||
async.each(
|
||||
_.keys(metrics),
|
||||
function(name, complete) {
|
||||
if (/^_/.test(name) || (execName && name !== execName)) {
|
||||
return complete();
|
||||
}
|
||||
registerAsyncTask('metrics', function() {
|
||||
const onlyExecuteName = grunt.option('name');
|
||||
const events = {};
|
||||
|
||||
const promises = Object.keys(metrics).map(async name => {
|
||||
if (/^_/.test(name)) {
|
||||
return;
|
||||
}
|
||||
if (onlyExecuteName != null && name !== onlyExecuteName) {
|
||||
return;
|
||||
}
|
||||
|
||||
return new Promise(resolve => {
|
||||
metrics[name](grunt, function(data) {
|
||||
events[name] = data;
|
||||
complete();
|
||||
resolve();
|
||||
});
|
||||
},
|
||||
done
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
return Promise.all(promises);
|
||||
});
|
||||
};
|
||||
|
||||
+28
-34
@@ -1,39 +1,33 @@
|
||||
var childProcess = require('child_process');
|
||||
const { execFileWithInheritedOutput } = require('./util/exec-file');
|
||||
const { createRegisterAsyncTaskFn } = require('./util/async-grunt-task');
|
||||
|
||||
const OUTPUT_FILE = 'lib/handlebars/compiler/parser.js';
|
||||
|
||||
module.exports = function(grunt) {
|
||||
grunt.registerTask('parser', 'Generate jison parser.', function() {
|
||||
var done = this.async();
|
||||
const registerAsyncTask = createRegisterAsyncTaskFn(grunt);
|
||||
|
||||
var cmd = './node_modules/.bin/jison';
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
cmd = 'node_modules\\.bin\\jison.cmd';
|
||||
}
|
||||
|
||||
var child = childProcess.spawn(
|
||||
cmd,
|
||||
['-m', 'js', 'src/handlebars.yy', 'src/handlebars.l'],
|
||||
{ stdio: 'inherit' }
|
||||
);
|
||||
child.on('exit', function(code) {
|
||||
if (code != 0) {
|
||||
grunt.fatal('Jison failure: ' + code);
|
||||
done();
|
||||
return;
|
||||
}
|
||||
|
||||
var src = [
|
||||
'src/parser-prefix.js',
|
||||
'handlebars.js',
|
||||
'src/parser-suffix.js'
|
||||
]
|
||||
.map(grunt.file.read)
|
||||
.join('');
|
||||
grunt.file.delete('handlebars.js');
|
||||
|
||||
grunt.file.write('lib/handlebars/compiler/parser.js', src);
|
||||
grunt.log.writeln('Parser "lib/handlebars/compiler/parser.js" created.');
|
||||
done();
|
||||
});
|
||||
registerAsyncTask('parser', async () => {
|
||||
await runJison();
|
||||
combineWithPrefixAndSuffix();
|
||||
grunt.log.writeln(`Parser "${OUTPUT_FILE}" created.`);
|
||||
});
|
||||
|
||||
async function runJison() {
|
||||
await execFileWithInheritedOutput('jison', [
|
||||
'-m',
|
||||
'js',
|
||||
'src/handlebars.yy',
|
||||
'src/handlebars.l'
|
||||
]);
|
||||
}
|
||||
|
||||
function combineWithPrefixAndSuffix() {
|
||||
const combinedParserSourceCode =
|
||||
grunt.file.read('src/parser-prefix.js') +
|
||||
grunt.file.read('handlebars.js') +
|
||||
grunt.file.read('src/parser-suffix.js');
|
||||
|
||||
grunt.file.write(OUTPUT_FILE, combinedParserSourceCode);
|
||||
grunt.file.delete('handlebars.js');
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
const AWS = require('aws-sdk');
|
||||
const git = require('./util/git');
|
||||
const { createRegisterAsyncTaskFn } = require('./util/async-grunt-task');
|
||||
const semver = require('semver');
|
||||
|
||||
module.exports = function(grunt) {
|
||||
const registerAsyncTask = createRegisterAsyncTaskFn(grunt);
|
||||
|
||||
registerAsyncTask('publish-to-aws', async () => {
|
||||
grunt.log.writeln('remotes: ' + (await git.remotes()));
|
||||
grunt.log.writeln('branches: ' + (await git.branches()));
|
||||
|
||||
const commitInfo = await git.commitInfo();
|
||||
grunt.log.writeln('tag: ', commitInfo.tagName);
|
||||
|
||||
const suffixes = [];
|
||||
|
||||
// Publish the master as "latest" and with the commit-id
|
||||
if (commitInfo.isMaster) {
|
||||
suffixes.push('-latest');
|
||||
suffixes.push('-' + commitInfo.headSha);
|
||||
}
|
||||
|
||||
// Publish tags by their tag-name
|
||||
if (commitInfo.tagName != null && semver.valid(commitInfo.tagName)) {
|
||||
suffixes.push('-' + commitInfo.tagName);
|
||||
}
|
||||
|
||||
if (suffixes.length > 0) {
|
||||
initSDK();
|
||||
grunt.log.writeln(
|
||||
'publishing file-suffixes: ' + JSON.stringify(suffixes)
|
||||
);
|
||||
await publish(suffixes);
|
||||
}
|
||||
});
|
||||
|
||||
function initSDK() {
|
||||
const bucket = process.env.S3_BUCKET_NAME,
|
||||
key = process.env.S3_ACCESS_KEY_ID,
|
||||
secret = process.env.S3_SECRET_ACCESS_KEY;
|
||||
|
||||
if (!bucket || !key || !secret) {
|
||||
throw new Error('Missing S3 config values');
|
||||
}
|
||||
|
||||
AWS.config.update({ accessKeyId: key, secretAccessKey: secret });
|
||||
}
|
||||
|
||||
async function publish(suffixes) {
|
||||
const publishPromises = suffixes.map(suffix => publishSuffix(suffix));
|
||||
return Promise.all(publishPromises);
|
||||
}
|
||||
|
||||
async function publishSuffix(suffix) {
|
||||
const filenames = [
|
||||
'handlebars.js',
|
||||
'handlebars.min.js',
|
||||
'handlebars.runtime.js',
|
||||
'handlebars.runtime.min.js'
|
||||
];
|
||||
const publishPromises = filenames.map(filename => {
|
||||
const nameInBucket = getNameInBucket(filename, suffix);
|
||||
const localFile = getLocalFile(filename);
|
||||
uploadToBucket(localFile, nameInBucket);
|
||||
grunt.log.writeln(
|
||||
`Published ${localFile} to build server (${nameInBucket})`
|
||||
);
|
||||
});
|
||||
return Promise.all(publishPromises);
|
||||
}
|
||||
|
||||
async function uploadToBucket(localFile, nameInBucket) {
|
||||
const bucket = process.env.S3_BUCKET_NAME;
|
||||
const uploadParams = {
|
||||
Bucket: bucket,
|
||||
Key: nameInBucket,
|
||||
Body: grunt.file.read(localFile)
|
||||
};
|
||||
return s3PutObject(uploadParams);
|
||||
}
|
||||
};
|
||||
|
||||
function s3PutObject(uploadParams) {
|
||||
const s3 = new AWS.S3();
|
||||
return new Promise((resolve, reject) => {
|
||||
s3.putObject(uploadParams, err => {
|
||||
if (err != null) {
|
||||
return reject(err);
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function getNameInBucket(filename, suffix) {
|
||||
return filename.replace(/\.js$/, suffix + '.js');
|
||||
}
|
||||
|
||||
function getLocalFile(filename) {
|
||||
return 'dist/' + filename;
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
var _ = require('underscore'),
|
||||
async = require('neo-async'),
|
||||
AWS = require('aws-sdk'),
|
||||
git = require('./util/git'),
|
||||
semver = require('semver');
|
||||
|
||||
module.exports = function(grunt) {
|
||||
grunt.registerTask('publish:latest', function() {
|
||||
var done = this.async();
|
||||
|
||||
git.debug(function(remotes, branches) {
|
||||
grunt.log.writeln('remotes: ' + remotes);
|
||||
grunt.log.writeln('branches: ' + branches);
|
||||
|
||||
git.commitInfo(function(err, info) {
|
||||
grunt.log.writeln('tag: ' + info.tagName);
|
||||
|
||||
var files = [];
|
||||
|
||||
// Publish the master as "latest" and with the commit-id
|
||||
if (info.isMaster) {
|
||||
files.push('-latest');
|
||||
files.push('-' + info.head);
|
||||
}
|
||||
|
||||
// Publish tags by their tag-name
|
||||
if (info.tagName && semver.valid(info.tagName)) {
|
||||
files.push('-' + info.tagName);
|
||||
}
|
||||
|
||||
if (files.length > 0) {
|
||||
initSDK();
|
||||
grunt.log.writeln('publishing files: ' + JSON.stringify(files));
|
||||
publish(fileMap(files), done);
|
||||
} else {
|
||||
// Silently ignore for branches
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
grunt.registerTask('publish:version', function() {
|
||||
var done = this.async();
|
||||
initSDK();
|
||||
|
||||
git.commitInfo(function(err, info) {
|
||||
if (!info.tagName) {
|
||||
throw new Error('The current commit must be tagged');
|
||||
}
|
||||
publish(fileMap(['-' + info.tagName]), done);
|
||||
});
|
||||
});
|
||||
|
||||
function initSDK() {
|
||||
var bucket = process.env.S3_BUCKET_NAME,
|
||||
key = process.env.S3_ACCESS_KEY_ID,
|
||||
secret = process.env.S3_SECRET_ACCESS_KEY;
|
||||
|
||||
if (!bucket || !key || !secret) {
|
||||
throw new Error('Missing S3 config values');
|
||||
}
|
||||
|
||||
AWS.config.update({ accessKeyId: key, secretAccessKey: secret });
|
||||
}
|
||||
function publish(files, callback) {
|
||||
var s3 = new AWS.S3(),
|
||||
bucket = process.env.S3_BUCKET_NAME;
|
||||
|
||||
async.each(
|
||||
_.keys(files),
|
||||
function(file, callback) {
|
||||
var params = {
|
||||
Bucket: bucket,
|
||||
Key: file,
|
||||
Body: grunt.file.read(files[file])
|
||||
};
|
||||
s3.putObject(params, function(err) {
|
||||
if (err) {
|
||||
throw err;
|
||||
} else {
|
||||
grunt.log.writeln('Published ' + file + ' to build server.');
|
||||
callback();
|
||||
}
|
||||
});
|
||||
},
|
||||
callback
|
||||
);
|
||||
}
|
||||
function fileMap(suffixes) {
|
||||
var map = {};
|
||||
_.each(
|
||||
[
|
||||
'handlebars.js',
|
||||
'handlebars.min.js',
|
||||
'handlebars.runtime.js',
|
||||
'handlebars.runtime.min.js'
|
||||
],
|
||||
function(file) {
|
||||
_.each(suffixes, function(suffix) {
|
||||
map[file.replace(/\.js$/, suffix + '.js')] = 'dist/' + file;
|
||||
});
|
||||
}
|
||||
);
|
||||
return map;
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,9 @@
|
||||
module.exports = {
|
||||
extends: '../../.eslintrc.js',
|
||||
env: {
|
||||
mocha: true
|
||||
},
|
||||
parserOptions: {
|
||||
ecmaVersion: 2018
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1 @@
|
||||
Use `mocha tasks/task-tests` to run these tests
|
||||
@@ -0,0 +1,121 @@
|
||||
const os = require('os');
|
||||
const path = require('path');
|
||||
const fs = require('fs-extra');
|
||||
const chai = require('chai');
|
||||
chai.use(require('dirty-chai'));
|
||||
|
||||
const git = require('../util/git');
|
||||
|
||||
const expect = chai.expect;
|
||||
|
||||
const tmpBaseDir = path.join(os.tmpdir(), 'handlebars-task-tests');
|
||||
const tmpDir = path.join(tmpBaseDir, Date.now().toString(36));
|
||||
const remoteDir = path.join(tmpDir, 'remote-repo');
|
||||
const cloneDir = path.join(tmpDir, 'clone-repo');
|
||||
const oldCwd = process.cwd();
|
||||
|
||||
describe('utils/git', function() {
|
||||
beforeEach(async function() {
|
||||
await fs.remove(tmpDir);
|
||||
await createRepositoryThatActsAsRemote();
|
||||
process.chdir(tmpDir);
|
||||
await git.git('clone', 'remote-repo', 'clone-repo');
|
||||
process.chdir(cloneDir);
|
||||
});
|
||||
|
||||
async function createRepositoryThatActsAsRemote() {
|
||||
await fs.mkdirp(remoteDir);
|
||||
process.chdir(remoteDir);
|
||||
|
||||
await git.git('init');
|
||||
await fs.writeFile('testfile.txt', 'Testfile');
|
||||
await git.add('testfile.txt');
|
||||
await git.commit('commit message');
|
||||
}
|
||||
|
||||
afterEach(function() {
|
||||
process.chdir(oldCwd);
|
||||
});
|
||||
|
||||
describe('the "remotes"-function', function() {
|
||||
it('should list all remotes', async function() {
|
||||
await git.git('remote', 'set-url', 'origin', 'https://test.org/test');
|
||||
await git.git('remote', 'add', 'second-remote', 'https://test.org/test2');
|
||||
|
||||
const result = await git.remotes();
|
||||
|
||||
expect(result.trim().split('\n')).to.deep.equal([
|
||||
'origin\thttps://test.org/test (fetch)',
|
||||
'origin\thttps://test.org/test (push)',
|
||||
'second-remote\thttps://test.org/test2 (fetch)',
|
||||
'second-remote\thttps://test.org/test2 (push)'
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('the "branches"-function', function() {
|
||||
it('should list all branches', async function() {
|
||||
await git.git('branch', 'test');
|
||||
await git.git('branch', 'test2');
|
||||
|
||||
const result = await git.branches();
|
||||
expect(result.trim().split('\n')).to.deep.equal([
|
||||
'* master',
|
||||
' test',
|
||||
' test2',
|
||||
' remotes/origin/HEAD -> origin/master',
|
||||
' remotes/origin/master'
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('the "commitInfo"-function', function() {
|
||||
it('should list head and master sha', async function() {
|
||||
const result = await git.commitInfo();
|
||||
expect(result.masterSha).to.equal(result.headSha);
|
||||
expect(result.masterSha).to.match(/^[0-9a-f]+$/);
|
||||
expect(result.headSha).to.match(/^[0-9a-f]+$/);
|
||||
});
|
||||
|
||||
it('should have "isMaster=true" if the master branch is checked out', async function() {
|
||||
const result = await git.commitInfo();
|
||||
expect(result.isMaster).to.be.true();
|
||||
});
|
||||
|
||||
it('should have "isMaster=true" if the current commit is the last commit of the master branch', async function() {
|
||||
await git.git('checkout', '-b', 'new-branch');
|
||||
|
||||
const result = await git.commitInfo();
|
||||
expect(result.isMaster).to.be.true();
|
||||
});
|
||||
|
||||
it('should have "isMaster=false" if the current commit is NOT the last commit of the master branch', async function() {
|
||||
await git.git('checkout', '-b', 'new-branch');
|
||||
fs.writeFile('new-file.txt', 'new-file');
|
||||
await git.add('new-file.txt');
|
||||
await git.commit('added new file');
|
||||
|
||||
const result = await git.commitInfo();
|
||||
expect(result.isMaster).to.be.false();
|
||||
});
|
||||
|
||||
it('should show the current tag', async function() {
|
||||
await git.git('tag', 'test-tag');
|
||||
const result = await git.commitInfo();
|
||||
expect(result.tagName).to.be.equal('test-tag');
|
||||
});
|
||||
|
||||
it('should show a version tag rather than standard tags', async function() {
|
||||
await git.git('tag', 'test-tag');
|
||||
await git.git('tag', 'v1.2');
|
||||
await git.git('tag', 'test-tag2');
|
||||
const result = await git.commitInfo();
|
||||
expect(result.tagName).to.be.equal('v1.2');
|
||||
});
|
||||
|
||||
it('should show no tag if there is no tag', async function() {
|
||||
const result = await git.commitInfo();
|
||||
expect(result.tagName).to.be.null();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,45 @@
|
||||
const childProcess = require('child_process'),
|
||||
fs = require('fs'),
|
||||
os = require('os'),
|
||||
expect = require('chai').expect;
|
||||
|
||||
module.exports = function(grunt) {
|
||||
grunt.registerTask('test:bin', function() {
|
||||
const stdout = executeBinHandlebars(
|
||||
'-a',
|
||||
'spec/artifacts/empty.handlebars'
|
||||
);
|
||||
|
||||
const expectedOutput = fs.readFileSync(
|
||||
'./spec/expected/empty.amd.js',
|
||||
'utf-8'
|
||||
);
|
||||
|
||||
const normalizedOutput = normalizeCrlf(stdout);
|
||||
const normalizedExpectedOutput = normalizeCrlf(expectedOutput);
|
||||
|
||||
expect(normalizedOutput).to.equal(normalizedExpectedOutput);
|
||||
});
|
||||
};
|
||||
|
||||
// helper functions
|
||||
|
||||
function executeBinHandlebars(...args) {
|
||||
if (os.platform() === 'win32') {
|
||||
// On Windows, the executable handlebars.js file cannot be run directly
|
||||
const nodeJs = process.argv[0];
|
||||
return execFilesSyncUtf8(nodeJs, ['./bin/handlebars'].concat(args));
|
||||
}
|
||||
return execFilesSyncUtf8('./bin/handlebars', args);
|
||||
}
|
||||
|
||||
function execFilesSyncUtf8(command, args) {
|
||||
return childProcess.execFileSync(command, args, { encoding: 'utf-8' });
|
||||
}
|
||||
|
||||
function normalizeCrlf(string) {
|
||||
if (typeof string === 'string') {
|
||||
return string.replace(/\r\n/g, '\n');
|
||||
}
|
||||
return string;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
const { execNodeJsScriptWithInheritedOutput } = require('./util/exec-file');
|
||||
const { createRegisterAsyncTaskFn } = require('./util/async-grunt-task');
|
||||
const nodeJs = process.argv0;
|
||||
|
||||
module.exports = function(grunt) {
|
||||
const registerAsyncTask = createRegisterAsyncTaskFn(grunt);
|
||||
|
||||
registerAsyncTask('test:mocha', async () =>
|
||||
execNodeJsScriptWithInheritedOutput('./spec/env/runner')
|
||||
);
|
||||
|
||||
registerAsyncTask('test:cov', async () =>
|
||||
execNodeJsScriptWithInheritedOutput('node_modules/nyc/bin/nyc', [
|
||||
nodeJs,
|
||||
'./spec/env/runner.js'
|
||||
])
|
||||
);
|
||||
|
||||
registerAsyncTask('test:min', async () =>
|
||||
execNodeJsScriptWithInheritedOutput('./spec/env/runner', ['--min'])
|
||||
);
|
||||
};
|
||||
-107
@@ -1,107 +0,0 @@
|
||||
var childProcess = require('child_process'),
|
||||
fs = require('fs'),
|
||||
os = require('os');
|
||||
|
||||
module.exports = function(grunt) {
|
||||
grunt.registerTask('test:bin', function() {
|
||||
var done = this.async();
|
||||
|
||||
var cmd = './bin/handlebars';
|
||||
var args = ['-a', 'spec/artifacts/empty.handlebars'];
|
||||
|
||||
// On Windows, the executable handlebars.js file cannot be run directly
|
||||
if (os.platform() === 'win32') {
|
||||
args.unshift(cmd);
|
||||
cmd = process.argv[0];
|
||||
}
|
||||
childProcess.execFile(cmd, args, function(err, stdout) {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
var expected = fs
|
||||
.readFileSync('./spec/expected/empty.amd.js')
|
||||
.toString()
|
||||
.replace(/\r\n/g, '\n');
|
||||
|
||||
if (stdout.toString() !== expected) {
|
||||
throw new Error(
|
||||
'Expected binary output differed:\n\n"' +
|
||||
stdout +
|
||||
'"\n\n"' +
|
||||
expected +
|
||||
'"'
|
||||
);
|
||||
}
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
grunt.registerTask('test:mocha', function() {
|
||||
var done = this.async();
|
||||
|
||||
var runner = childProcess.fork('./spec/env/runner', [], {
|
||||
stdio: 'inherit'
|
||||
});
|
||||
runner.on('close', function(code) {
|
||||
if (code !== 0) {
|
||||
grunt.fatal(code + ' tests failed');
|
||||
}
|
||||
done();
|
||||
});
|
||||
});
|
||||
grunt.registerTask('test:cov', function() {
|
||||
var done = this.async();
|
||||
|
||||
var runner = childProcess.fork(
|
||||
'node_modules/istanbul/lib/cli.js',
|
||||
['cover', '--source-map', '--', './spec/env/runner.js'],
|
||||
{ stdio: 'inherit' }
|
||||
);
|
||||
runner.on('close', function(code) {
|
||||
if (code != 0) {
|
||||
grunt.fatal(code + ' tests failed');
|
||||
}
|
||||
done();
|
||||
});
|
||||
});
|
||||
grunt.registerTask('test:min', function() {
|
||||
var done = this.async();
|
||||
|
||||
var runner = childProcess.fork('./spec/env/runner', ['--min'], {
|
||||
stdio: 'inherit'
|
||||
});
|
||||
runner.on('close', function(code) {
|
||||
if (code !== 0) {
|
||||
grunt.fatal(code + ' tests failed');
|
||||
}
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
grunt.registerTask('test:check-cov', function() {
|
||||
var done = this.async();
|
||||
|
||||
var runner = childProcess.fork(
|
||||
'node_modules/istanbul/lib/cli.js',
|
||||
[
|
||||
'check-coverage',
|
||||
'--statements',
|
||||
'100',
|
||||
'--functions',
|
||||
'100',
|
||||
'--branches',
|
||||
'100',
|
||||
'--lines 100'
|
||||
],
|
||||
{ stdio: 'inherit' }
|
||||
);
|
||||
runner.on('close', function(code) {
|
||||
if (code != 0) {
|
||||
grunt.fatal('Coverage check failed: ' + code);
|
||||
}
|
||||
done();
|
||||
});
|
||||
});
|
||||
grunt.registerTask('test', ['test:bin', 'test:cov', 'test:check-cov']);
|
||||
};
|
||||
@@ -0,0 +1,13 @@
|
||||
module.exports = { createRegisterAsyncTaskFn };
|
||||
|
||||
function createRegisterAsyncTaskFn(grunt) {
|
||||
return function registerAsyncTask(name, asyncFunction) {
|
||||
grunt.registerTask(name, function() {
|
||||
asyncFunction()
|
||||
.catch(error => {
|
||||
grunt.fatal(error);
|
||||
})
|
||||
.finally(this.async());
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
const childProcess = require('child_process');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
execNodeJsScriptWithInheritedOutput,
|
||||
execFileWithInheritedOutput
|
||||
};
|
||||
|
||||
async function execNodeJsScriptWithInheritedOutput(command, args) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const child = childProcess.fork(command, args, { stdio: 'inherit' });
|
||||
child.on('close', code => {
|
||||
if (code !== 0) {
|
||||
reject(new Error(`Child process failed with exit-code ${code}`));
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async function execFileWithInheritedOutput(command, args) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const resolvedCommand = preferLocalDependencies(command);
|
||||
const child = childProcess.spawn(resolvedCommand, args, {
|
||||
stdio: 'inherit'
|
||||
});
|
||||
child.on('exit', code => {
|
||||
if (code !== 0) {
|
||||
reject(new Error(`Child process failed with exit-code ${code}`));
|
||||
}
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function preferLocalDependencies(command) {
|
||||
const localCandidate = resolveLocalCandidate(command);
|
||||
|
||||
if (fs.existsSync(localCandidate)) {
|
||||
return localCandidate;
|
||||
}
|
||||
return command;
|
||||
}
|
||||
|
||||
function resolveLocalCandidate(command) {
|
||||
if (process.platform === 'win32') {
|
||||
return path.join('node_modules', '.bin', command + '.cmd');
|
||||
}
|
||||
return path.join('node_modules', '.bin', command);
|
||||
}
|
||||
+65
-107
@@ -1,115 +1,73 @@
|
||||
var childProcess = require('child_process');
|
||||
const childProcess = require('child_process');
|
||||
|
||||
module.exports = {
|
||||
debug: function(callback) {
|
||||
childProcess.exec('git remote -v', {}, function(err, remotes) {
|
||||
if (err) {
|
||||
throw new Error('git.remote: ' + err.message);
|
||||
}
|
||||
|
||||
childProcess.exec('git branch -a', {}, function(err, branches) {
|
||||
if (err) {
|
||||
throw new Error('git.branch: ' + err.message);
|
||||
}
|
||||
|
||||
callback(remotes, branches);
|
||||
});
|
||||
});
|
||||
async remotes() {
|
||||
return git('remote', '-v');
|
||||
},
|
||||
clean: function(callback) {
|
||||
childProcess.exec('git diff-index --name-only HEAD --', {}, function(
|
||||
err,
|
||||
stdout
|
||||
) {
|
||||
callback(undefined, !err && !stdout);
|
||||
});
|
||||
async branches() {
|
||||
return git('branch', '-a');
|
||||
},
|
||||
|
||||
commitInfo: function(callback) {
|
||||
module.exports.head(function(err, headSha) {
|
||||
module.exports.master(function(err, masterSha) {
|
||||
module.exports.tagName(function(err, tagName) {
|
||||
callback(undefined, {
|
||||
head: headSha,
|
||||
master: masterSha,
|
||||
tagName: tagName,
|
||||
isMaster: headSha === masterSha
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
async commitInfo() {
|
||||
const headSha = await getHeadSha();
|
||||
const masterSha = await getMasterSha();
|
||||
return {
|
||||
headSha,
|
||||
masterSha,
|
||||
tagName: await getTagName(),
|
||||
isMaster: headSha === masterSha
|
||||
};
|
||||
},
|
||||
|
||||
head: function(callback) {
|
||||
childProcess.exec('git rev-parse --short HEAD', {}, function(err, stdout) {
|
||||
if (err) {
|
||||
throw new Error('git.head: ' + err.message);
|
||||
}
|
||||
|
||||
callback(undefined, stdout.trim());
|
||||
});
|
||||
async add(path) {
|
||||
return git('add', '-f', path);
|
||||
},
|
||||
master: function(callback) {
|
||||
childProcess.exec('git rev-parse --short origin/master', {}, function(
|
||||
err,
|
||||
stdout
|
||||
) {
|
||||
// This will error if master was not checked out but in this case we know we are not master
|
||||
// so we can ignore.
|
||||
if (err && !/Needed a single revision/.test(err.message)) {
|
||||
throw new Error('git.master: ' + err.message);
|
||||
}
|
||||
|
||||
callback(undefined, stdout.trim());
|
||||
});
|
||||
async commit(message) {
|
||||
return git('commit', '--message', message);
|
||||
},
|
||||
|
||||
add: function(path, callback) {
|
||||
childProcess.exec('git add -f ' + path, {}, function(err) {
|
||||
if (err) {
|
||||
throw new Error('git.add: ' + err.message);
|
||||
}
|
||||
|
||||
callback();
|
||||
});
|
||||
},
|
||||
commit: function(name, callback) {
|
||||
childProcess.exec('git commit --message=' + name, {}, function(err) {
|
||||
if (err) {
|
||||
throw new Error('git.commit: ' + err.message);
|
||||
}
|
||||
|
||||
callback();
|
||||
});
|
||||
},
|
||||
tag: function(name, callback) {
|
||||
childProcess.exec('git tag -a --message=' + name + ' ' + name, {}, function(
|
||||
err
|
||||
) {
|
||||
if (err) {
|
||||
throw new Error('git.tag: ' + err.message);
|
||||
}
|
||||
|
||||
callback();
|
||||
});
|
||||
},
|
||||
tagName: function(callback) {
|
||||
childProcess.exec('git describe --tags', {}, function(err, stdout) {
|
||||
if (err) {
|
||||
throw new Error('git.tagName: ' + err.message);
|
||||
}
|
||||
|
||||
var tags = stdout.trim().split(/\n/);
|
||||
tags = tags.filter(function(info) {
|
||||
info = info.split('-');
|
||||
return info.length == 1;
|
||||
});
|
||||
|
||||
var versionTags = tags.filter(function(info) {
|
||||
return /^v/.test(info[0]);
|
||||
});
|
||||
|
||||
callback(undefined, versionTags[0] || tags[0]);
|
||||
});
|
||||
}
|
||||
git // visible for testing
|
||||
};
|
||||
|
||||
async function getHeadSha() {
|
||||
const stdout = await git('rev-parse', '--short', 'HEAD');
|
||||
return stdout.trim();
|
||||
}
|
||||
|
||||
async function getMasterSha() {
|
||||
try {
|
||||
const stdout = await git('rev-parse', '--short', 'origin/master');
|
||||
return stdout.trim();
|
||||
} catch (error) {
|
||||
if (/Needed a single revision/.test(error.message)) {
|
||||
// Master was not checked out but in this case, so we know we are not master. We can ignore this
|
||||
return '';
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async function getTagName() {
|
||||
const stdout = await git('tag', '-l', '--points-at', 'HEAD');
|
||||
const trimmedStdout = stdout.trim();
|
||||
if (trimmedStdout === '') {
|
||||
return null; // there is no tag
|
||||
}
|
||||
|
||||
const tags = trimmedStdout.split(/\n|\r\n/);
|
||||
const versionTags = tags.filter(tag => /^v/.test(tag));
|
||||
if (versionTags[0] != null) {
|
||||
return versionTags[0];
|
||||
}
|
||||
return tags[0];
|
||||
}
|
||||
|
||||
async function git(...args) {
|
||||
return new Promise((resolve, reject) =>
|
||||
childProcess.execFile('git', args, (err, stdout) => {
|
||||
if (err != null) {
|
||||
return reject(
|
||||
new Error(`"git ${args.join(' ')}" caused error: ${err.message}`)
|
||||
);
|
||||
}
|
||||
resolve(stdout);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
+52
-58
@@ -1,68 +1,62 @@
|
||||
var async = require('neo-async'),
|
||||
git = require('./util/git'),
|
||||
semver = require('semver');
|
||||
const git = require('./util/git');
|
||||
const semver = require('semver');
|
||||
const { createRegisterAsyncTaskFn } = require('./util/async-grunt-task');
|
||||
|
||||
module.exports = function(grunt) {
|
||||
grunt.registerTask(
|
||||
'version',
|
||||
'Updates the current release version',
|
||||
function() {
|
||||
var done = this.async(),
|
||||
pkg = grunt.config('pkg'),
|
||||
version = grunt.option('ver');
|
||||
const registerAsyncTask = createRegisterAsyncTaskFn(grunt);
|
||||
|
||||
if (!semver.valid(version)) {
|
||||
throw new Error(
|
||||
'Must provide a version number (Ex: --ver=1.0.0):\n\t' +
|
||||
version +
|
||||
'\n\n'
|
||||
);
|
||||
}
|
||||
|
||||
pkg.version = version;
|
||||
grunt.config('pkg', pkg);
|
||||
|
||||
grunt.log.writeln('Updating to version ' + version);
|
||||
|
||||
async.each(
|
||||
[
|
||||
[
|
||||
'lib/handlebars/base.js',
|
||||
/const VERSION = ['"](.*)['"];/,
|
||||
"const VERSION = '" + version + "';"
|
||||
],
|
||||
[
|
||||
'components/bower.json',
|
||||
/"version":.*/,
|
||||
'"version": "' + version + '",'
|
||||
],
|
||||
[
|
||||
'components/package.json',
|
||||
/"version":.*/,
|
||||
'"version": "' + version + '",'
|
||||
],
|
||||
[
|
||||
'components/handlebars.js.nuspec',
|
||||
/<version>.*<\/version>/,
|
||||
'<version>' + version + '</version>'
|
||||
]
|
||||
],
|
||||
function(args, callback) {
|
||||
replace.apply(undefined, args);
|
||||
grunt.log.writeln(' - ' + args[0]);
|
||||
git.add(args[0], callback);
|
||||
},
|
||||
function() {
|
||||
grunt.task.run(['default']);
|
||||
done();
|
||||
}
|
||||
registerAsyncTask('version', async () => {
|
||||
const pkg = grunt.config('pkg');
|
||||
const version = grunt.option('ver');
|
||||
if (!semver.valid(version)) {
|
||||
throw new Error(
|
||||
'Must provide a version number (Ex: --ver=1.0.0):\n\t' +
|
||||
version +
|
||||
'\n\n'
|
||||
);
|
||||
}
|
||||
);
|
||||
pkg.version = version;
|
||||
grunt.config('pkg', pkg);
|
||||
|
||||
function replace(path, regex, value) {
|
||||
var content = grunt.file.read(path);
|
||||
const replaceSpec = [
|
||||
{
|
||||
path: 'lib/handlebars/base.js',
|
||||
regex: /const VERSION = ['"](.*)['"];/,
|
||||
replacement: `const VERSION = '${version}';`
|
||||
},
|
||||
{
|
||||
path: 'components/bower.json',
|
||||
regex: /"version":.*/,
|
||||
replacement: `"version": "${version}",`
|
||||
},
|
||||
{
|
||||
path: 'components/package.json',
|
||||
regex: /"version":.*/,
|
||||
replacement: `"version": "${version}",`
|
||||
},
|
||||
{
|
||||
path: 'components/handlebars.js.nuspec',
|
||||
regex: /<version>.*<\/version>/,
|
||||
replacement: `<version>${version}</version>`
|
||||
}
|
||||
];
|
||||
|
||||
await Promise.all(
|
||||
replaceSpec.map(replaceSpec =>
|
||||
replaceAndAdd(
|
||||
replaceSpec.path,
|
||||
replaceSpec.regex,
|
||||
replaceSpec.replacement
|
||||
)
|
||||
)
|
||||
);
|
||||
grunt.task.run(['default']);
|
||||
});
|
||||
|
||||
async function replaceAndAdd(path, regex, value) {
|
||||
let content = grunt.file.read(path);
|
||||
content = content.replace(regex, value);
|
||||
grunt.file.write(path, content);
|
||||
await git.add(path);
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user