Skip to content

Commit

Permalink
test/style: refactor parser task
Browse files Browse the repository at this point in the history
  • Loading branch information
nknapp committed Dec 14, 2019
1 parent dde108e commit 3a5b65e
Show file tree
Hide file tree
Showing 5 changed files with 151 additions and 133 deletions.
58 changes: 26 additions & 32 deletions tasks/parser.js
Original file line number Diff line number Diff line change
@@ -1,39 +1,33 @@
const childProcess = require('child_process');
const { execFileWithInheritedOutput } = require('./util/exec-file');
const { createRegisterAsyncTaskFn } = require('./util/async-grunt-task');

module.exports = function(grunt) {
grunt.registerTask('parser', 'Generate jison parser.', function() {
const done = this.async();
const OUTPUT_FILE = 'lib/handlebars/compiler/parser.js';

let cmd = './node_modules/.bin/jison';
module.exports = function(grunt) {
const registerAsyncTask = createRegisterAsyncTaskFn(grunt);

if (process.platform === 'win32') {
cmd = 'node_modules\\.bin\\jison.cmd';
}
registerAsyncTask('parser', async () => {
await runJison();
combineWithPrefixAndSuffix();
grunt.log.writeln(`Parser "${OUTPUT_FILE}" created.`);
});

const 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;
}
async function runJison() {
await execFileWithInheritedOutput('jison', [
'-m',
'js',
'src/handlebars.yy',
'src/handlebars.l'
]);
}

const src = [
'src/parser-prefix.js',
'handlebars.js',
'src/parser-suffix.js'
]
.map(grunt.file.read)
.join('');
grunt.file.delete('handlebars.js');
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('lib/handlebars/compiler/parser.js', src);
grunt.log.writeln('Parser "lib/handlebars/compiler/parser.js" created.');
done();
});
});
grunt.file.write(OUTPUT_FILE, combinedParserSourceCode);
grunt.file.delete('handlebars.js');
}
};
80 changes: 35 additions & 45 deletions tasks/test-bin.js
Original file line number Diff line number Diff line change
@@ -1,55 +1,45 @@
const childProcess = require('child_process'),
fs = require('fs'),
os = require('os'),
expect = require('chai').expect,
util = require('util');

const readFile = util.promisify(fs.readFile);
const execFile = util.promisify(childProcess.execFile);
expect = require('chai').expect;

module.exports = function(grunt) {
grunt.registerTask(
'test:bin',
wrapAsync(async function() {
const { stdout } = await execFileWithWin32Fallback('./bin/handlebars', [
'-a',
'spec/artifacts/empty.handlebars'
]);

const expectedOutput = await readFile(
'./spec/expected/empty.amd.js',
'utf-8'
);

const normalizedOutput = normalizeCrlf(stdout);
const normalizedExpectedOutput = normalizeCrlf(expectedOutput);
expect(normalizedOutput).to.equal(normalizedExpectedOutput);
})
);

async function execFileWithWin32Fallback(command, args) {
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
if (os.platform() === 'win32') {
args.unshift(command);
command = process.argv[0];
}
return execFile(command, args, { encoding: 'utf-8' });
const nodeJs = process.argv[0];
return execFilesSyncUtf8(nodeJs, ['./bin/handlebars'].concat(args));
}
return execFilesSyncUtf8('./bin/handlebars', args);
}

function normalizeCrlf(string) {
if (string != null) {
return string.replace(/\r\n/g, '\n');
}
return string;
}
function execFilesSyncUtf8(command, args) {
return childProcess.execFileSync(command, args, { encoding: 'utf-8' });
}

function wrapAsync(asyncFunction) {
return function() {
asyncFunction()
.catch(error => {
grunt.fatal(error);
})
.finally(this.async());
};
function normalizeCrlf(string) {
if (typeof string === 'string') {
return string.replace(/\r\n/g, '\n');
}
};
return string;
}
82 changes: 26 additions & 56 deletions tasks/test-mocha.js
Original file line number Diff line number Diff line change
@@ -1,66 +1,36 @@
const childProcess = require('child_process');
const { execNodeJsScriptWithInheritedOutput } = require('./util/exec-file');
const { createRegisterAsyncTaskFn } = require('./util/async-grunt-task');

module.exports = function(grunt) {
grunt.registerTask(
'test:mocha',
promiseBasedTask(async () => forkAndWait('./spec/env/runner'))
);
const registerAsyncTask = createRegisterAsyncTaskFn(grunt);

grunt.registerTask(
'test:cov',
promiseBasedTask(async () =>
forkAndWait(
'node_modules/istanbul/lib/cli.js',
'cover',
'--source-map',
'--',
'./spec/env/runner.js'
)
)
registerAsyncTask('test:mocha', async () =>
execNodeJsScriptWithInheritedOutput('./spec/env/runner')
);

grunt.registerTask(
'test:min',
promiseBasedTask(async () => forkAndWait('./spec/env/runner', '--min'))
registerAsyncTask('test:cov', async () =>
execNodeJsScriptWithInheritedOutput('node_modules/istanbul/lib/cli.js', [
'cover',
'--source-map',
'--',
'./spec/env/runner.js'
])
);

grunt.registerTask(
'test:check-cov',
promiseBasedTask(() =>
forkAndWait(
'node_modules/istanbul/lib/cli.js',
'check-coverage',
'--statements',
'100',
'--functions',
'100',
'--branches',
'100',
'--lines 100'
)
)
registerAsyncTask('test:min', async () =>
execNodeJsScriptWithInheritedOutput('./spec/env/runner', ['--min'])
);

function promiseBasedTask(asyncFunction) {
return function() {
asyncFunction()
.catch(error => {
grunt.fatal(error);
})
.finally(this.async());
};
}

async function forkAndWait(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}`));
}
});
});
}

grunt.registerTask('test', ['test:bin', 'test:cov', 'test:check-cov']);
registerAsyncTask('test:check-cov', async () =>
execNodeJsScriptWithInheritedOutput('node_modules/istanbul/lib/cli.js', [
'check-coverage',
'--statements',
'100',
'--functions',
'100',
'--branches',
'100',
'--lines 100'
])
);
};
13 changes: 13 additions & 0 deletions tasks/util/async-grunt-task.js
Original file line number Diff line number Diff line change
@@ -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());
});
};
}
51 changes: 51 additions & 0 deletions tasks/util/exec-file.js
Original file line number Diff line number Diff line change
@@ -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);
}

0 comments on commit 3a5b65e

Please sign in to comment.