Skip to content

Commit

Permalink
perf(tmpl-spec): improve runtime
Browse files Browse the repository at this point in the history
Rework a little where and how clearRequire-require combo works.
Just it improved performance a lot.

Also stopped usign this combo for references.
  • Loading branch information
qfox committed Mar 31, 2016
1 parent 61fec9a commit 551c3d3
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 168 deletions.
6 changes: 4 additions & 2 deletions lib/assets/it.jst
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,14 @@ if (saveHtml) {
}
%>
it('should be equal `${ it }` by ${ engine.name }${ titleSuffix }', function (<% if (saveHtml || engine.async) { %>done<% } %>) {
${ before }

var bemjson = references['${ it }']${ subreference }.bemjson,
expected = references['${ it }']${ subreference }.html;<%
if (!engine.async) { %>
// sync mode
var actual = engines['${ engine.name }']${ subreference }.apply(bemjson);
var actual = engine.apply(bemjson);
<% if (saveHtml) { %>
Expand All @@ -25,7 +27,7 @@ if (!engine.async) { %>
<% }
} else { // engine.async %>
// async mode
engines['${ engine.name }']${ subreference }.apply(bemjson, function(errs, actual) {
engine.apply(bemjson, function(errs, actual) {
if (errs !== null) {
done(errs);
return;
Expand Down
102 changes: 31 additions & 71 deletions lib/assets/tmpl-spec.jst
Original file line number Diff line number Diff line change
Expand Up @@ -21,104 +21,64 @@ var assert = require('assert'),
clearRequire = require('${ paths['clear-require'] }'),
HtmlDiffer = require('${ paths['html-differ'] }').HtmlDiffer,
htmlDiffer = new HtmlDiffer(${ JSON.stringify(diffOpts) }),
referencesFilename = require.resolve('${ paths.references }');
referencesJSON = JSON.stringify(require('${ paths.references }')),
getReferences = function () {
return JSON.parse(referencesJSON);
};

describe('${ describe }', function() {
var engines = {},
references;

beforeEach(function () {
// reload references
clearRequire(referencesFilename);
references = require(referencesFilename);
});

<% if (!(langs && langs.length)) { %>
//
// no langs mode
//
beforeEach(function () {
// reload engines artifacts
engines = {};
<% _.forEach(engines, function(engine) {
reRequire('engines', engine);
}); %>
});
<% _.forEach(its, function(it) { %>
describe('${ it }', function() {
<% _.forEach(engines, function(engine) {
var lang = engine.lang;
print(template('it', {
it: it,
before: [
'var references = getReferences();',
'var engine = ' + reRequire(engine.target, engine.exportName) + ';',
].join('\n'),
engine: engine,
lang: null,
lang: lang.isReal ? lang.name : null,
saveHtml: saveHtml
}));
}); %>
}); // describe ${ it }
<% }); // _.forEach its %>
<% } else { // langs.length > 0 %>
//
// langs mode
//
<% _.forEach(langs, function (lang) { %>
describe('with lang `${ lang }`', function() {
beforeEach(function () {
<% _.forEach(engines, function(engine) {
var engineObject = 'engines[\'' + engine.name + '\']';
print(engineObject + ' = {};\n');
reRequire(engineObject, engine.langs.filter(function (_lang) { return lang === _lang.name; })[0]);
}); %>
});
<% _.forEach(its, function(it) { %>
describe('${ it }', function() {
<% _.forEach(engines, function(engine) {
print(template('it', {
it: it,
engine: engine,
lang: lang,
saveHtml: saveHtml
}));
}); %>
});
<% }); %>
});
<% }); %>

<% }; // /if langs %>
}); // /describe ${ describe }
<%
function reRequire(where, what) {
var name = what.name,
target = what.target,
exportName = what.exportName;
print('clearRequire(require.resolve(\'' + target + '\'));\n');
print(where + '[\'' + name + '\'] = require(\'' + target + '\')' + exportName + ';\n');
}
%>

function assertHtml(actual, expected, done, filename) {
<% if (saveHtml) {%>
saveHtmlFile(filename, actual, function() {
<% } %>
if (htmlDiffer.isEqual(actual, expected)) {
// Throw error if actual is empty
assert.ok(actual);
done && done(null);
} else {
assert.fail(actual, expected, null, '\n');
assert.fail(actual, expected, null, '\n');
}
<% if (saveHtml) {%>
});
<% } %>
}
<%
function reRequire(target, exportName) {
return 'reRequire(' + JSON.stringify(target) + ', ' + JSON.stringify(exportName) + ')';
}
%>
function reRequire(file, exportName) {
try {
file = require.resolve(file);
clearRequire(file);
var res = require(file);
exportName && (res = res[exportName]);
return res;
} catch (e) {
console.error(e.stack);
return { apply: function() { return e.stack; } };
}
}
20 changes: 13 additions & 7 deletions lib/node-configurator.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ exports.configure = function (config, options) {
engines = options.engines,
coverageEngines = options.coverage.engines,
engineTargets = [],
specTargets = [],
nodePath = nodeConfig.getPath(),
isCoverageBundle = coverageRe.test(nodePath),
sourceLevels = [].concat(options.sourceLevels, options.sublevels[nodePath] || []);
Expand Down Expand Up @@ -71,6 +72,7 @@ exports.configure = function (config, options) {

// Add engines' techs
engines.forEach(function (engine) {
engine.hasOwnProperty('needsCoverage') || (engine.needsCoverage = _.contains(coverageEngines, engine.name));
nodeConfig.addTech([engine.tech, engine.options]);
});

Expand Down Expand Up @@ -107,29 +109,33 @@ exports.configure = function (config, options) {
}

// Add instrumented tech if we need coverage
if (_.contains(coverageEngines, engine.name)) {
if (engine.needsCoverage) {
target = destTarget;
destTarget = instrumentedTarget(destTarget);

nodeConfig.addTech([istanbul, { source: target, target: destTarget }]);
}

// For each engine and lang we have one target:
engineTargets.push(destTarget);
nodeConfig.addTarget(destTarget);

engineTargets.push(destTarget);
specTargets.push({
target: destTarget,
engine: engine,
lang: { name: lang, isMock: isMock, isReal: isReal }
});
});
});

nodeConfig.addTechs([
[references, { dirsTarget: '?.base.dirs' }],
[spec, {
engines: engines,
langs: langs,
diffOpts: options.diffOpts,
engineTargets: engineTargets,
specTargets: specTargets,
diffOpts: options.diffOpts,
saveHtml: options.saveHtml,
coverageEngines: coverageEngines,
coverageBundle: isCoverageBundle
isCoverageBundle: isCoverageBundle
}]
]);

Expand Down
141 changes: 53 additions & 88 deletions lib/techs/tmpl-spec.js
Original file line number Diff line number Diff line change
@@ -1,31 +1,34 @@
var path = require('path'),

_ = require('lodash'),
vow = require('vow'),

enb = require('enb'),
buildFlow = enb.buildFlow || require('enb/lib/build-flow'),
vfs = enb.asyncFs || require('enb/lib/fs/async-fs'),

assetsDirname = path.join(__dirname, '..', 'assets'),
readAssets = vow.all([
vfs.read(path.join(assetsDirname, 'tmpl-spec.jst'), 'utf-8'),
vfs.read(path.join(assetsDirname, 'it.jst'), 'utf-8')
]),
lodash = require('lodash'),
template = lodash.template,
contains = lodash.contains,

// Helpers for tests:
clearRequireFilename = require.resolve('clear-require'),
htmlDifferFilename = require.resolve('html-differ'),
jsBeautifyFilename = require.resolve('js-beautify'),
instrumentedTarget = require('../util').instrumentedTarget,
backSlash = new RegExp('\\\\', 'g');
relativePath = function (dirname, filename) {
return './' + path.relative(dirname, filename).replace(/\\\\/g, '/');
},

readAssets = vow.all([
vfs.read(path.join(assetsDirname, 'tmpl-spec.jst'), 'utf-8'),
vfs.read(path.join(assetsDirname, 'it.jst'), 'utf-8')
]);

module.exports = buildFlow.create()
.name('tmpl-spec')
.target('target', '?.tmpl-spec.js')
.defineRequiredOption('engines')
.defineOption('langs', [])
.defineOption('saveHtml', false)
.defineRequiredOption('specTargets')
.defineOption('diffOpts')
.defineOption('coverageEngines', [])
.defineOption('coverageBundle', false)
.defineOption('saveHtml', false)
.defineOption('isCoverageBundle', false)
.useSourceFilename('references', '?.references.js')
.useSourceListFilenames('engineTargets', [])
.needRebuild(function (cache) {
Expand All @@ -38,89 +41,51 @@ module.exports = buildFlow.create()
var node = this.node,
nodePath = node.getPath(),
nodeDirname = node.getDir(),
references = require(referencesFilename),
engines = this._engines,
langs = this._langs,

specTargets = this._specTargets,
diffOpts = this._diffOpts,
oneLang = langs === true,
manyLangs = Array.isArray(langs) && langs.length > 0,
saveHtml = this._saveHtml,
coverageEngines = this._coverageEngines,
coverageBundle = this._coverageBundle,
its = !this._coverageBundle ? Object.keys(references) : [];

return readAssets.spread(function (asset, it) {
var templates = {
it: it
},
data = {
describe: path.basename(nodePath) + ' (' + path.dirname(nodePath) + ')',
its: its,
diffOpts: diffOpts,
engines: engines.map(function (engine) {
var target = contains(coverageEngines, engine.name) ?
instrumentedTarget(engine.target) : engine.target,

exportName = engine.exportName ? '.' + engine.exportName : '';

if (oneLang) {
target = coverageBundle ?
target.replace('.js.instr.js', '.lang.js.instr.js') :
target.replace('.js', '.lang.js');
}
references = require(referencesFilename),
its = !this._isCoverageBundle ? Object.keys(references) : [],

target = './' + node.unmaskTargetName(target);
data = {
describe: path.basename(nodePath) + ' (' + path.dirname(nodePath) + ')',
its: its,
engines: specTargets.map(function (specTarget) {
var target = specTarget.target,
engine = specTarget.engine,
exportName = engine.exportName;

if (manyLangs) {
return {
name: engine.name,
langs: langs.map(function (lang) {
var langTarget = target.replace('.js', '.' + lang + '.js');
return {
name: lang,
async: engine.async,
target: langTarget,
exportName: exportName
};
})
};
}
target = './' + node.unmaskTargetName(target);

return {
name: engine.name,
async: engine.async,
target: target,
exportName: exportName
};
}),
langs: langs,
paths: {
references: [
'.', path.relative(nodeDirname, referencesFilename).replace(backSlash, '/')
].join('/'),
'clear-require': [
'.', path.relative(nodeDirname, clearRequireFilename).replace(backSlash, '/')
].join('/'),
'html-differ': [
'.', path.relative(nodeDirname, htmlDifferFilename).replace(backSlash, '/')
].join('/'),
'js-beautify': [
'.', path.relative(nodeDirname, jsBeautifyFilename).replace(backSlash, '/')
].join('/')
},
saveHtml: saveHtml
},
compiled = template(asset, {
imports: {
template: function (name, data) {
var compiled = template(templates[name]);
return {
name: engine.name,
async: engine.async,
lang: specTarget.lang,
target: target,
exportName: exportName
};
}),
paths: _.mapValues({
references: referencesFilename,
'clear-require': clearRequireFilename,
'html-differ': htmlDifferFilename,
'js-beautify': jsBeautifyFilename
}, _.partial(relativePath, nodeDirname)),
diffOpts: diffOpts,
saveHtml: saveHtml
};

return compiled(data);
}
return readAssets.spread(function (asset, it) {
var templates = { it: it },
imports = {
template: function (name, data) {
return _.template(templates[name])(data);
}
});
};

return compiled(data);
return _.template(asset, { imports: imports })(data);
});
})
.createTech();

0 comments on commit 551c3d3

Please sign in to comment.