Merge branch '4.x' into merge-4.x

This commit is contained in:
Jakob Linskeseder
2021-12-29 23:10:11 +01:00
100 changed files with 425 additions and 280 deletions
+10
View File
@@ -0,0 +1,10 @@
module.exports = {
root: true,
extends: ['eslint:recommended', 'prettier'],
env: {
node: true
},
parserOptions: {
ecmaVersion: 6
}
};
+43
View File
@@ -0,0 +1,43 @@
var async = require('neo-async'),
fs = require('fs'),
zlib = require('zlib');
module.exports = function(grunt, callback) {
var distFiles = fs.readdirSync('dist'),
distSizes = {};
async.each(
distFiles,
function(file, callback) {
var content;
try {
content = fs.readFileSync('dist/' + file);
} catch (err) {
if (err.code === 'EISDIR') {
callback();
return;
} else {
throw err;
}
}
file = file.replace(/\.js/, '').replace(/\./g, '_');
distSizes[file] = content.length;
zlib.gzip(content, function(err, data) {
if (err) {
throw err;
}
distSizes[file + '_gz'] = data.length;
callback();
});
},
function() {
grunt.log.writeln(
'Distribution sizes: ' + JSON.stringify(distSizes, undefined, 2)
);
callback([distSizes]);
}
);
};
+14
View File
@@ -0,0 +1,14 @@
var fs = require('fs');
var metrics = fs.readdirSync(__dirname);
metrics.forEach(function(metric) {
if (metric === 'index.js' || !/(.*)\.js$/.test(metric)) {
return;
}
var name = RegExp.$1;
metric = require('./' + name);
if (metric instanceof Function) {
module.exports[name] = metric;
}
});
+24
View File
@@ -0,0 +1,24 @@
var _ = require('underscore'),
templates = require('./templates');
module.exports = function(grunt, callback) {
// Deferring to here in case we have a build for parser, etc as part of this grunt exec
var Handlebars = require('../../lib');
var templateSizes = {};
_.each(templates, function(info, template) {
var src = info.handlebars,
compiled = Handlebars.precompile(src, {}),
knownHelpers = Handlebars.precompile(src, {
knownHelpersOnly: true,
knownHelpers: info.helpers
});
templateSizes[template] = compiled.length;
templateSizes['knownOnly_' + template] = knownHelpers.length;
});
grunt.log.writeln(
'Precompiled sizes: ' + JSON.stringify(templateSizes, undefined, 2)
);
callback([templateSizes]);
};
+13
View File
@@ -0,0 +1,13 @@
module.exports = {
helpers: {
foo: function() {
return '';
}
},
context: {
bar: true
},
handlebars:
'{{foo person "person" 1 true foo=bar foo="person" foo=1 foo=true}}'
};
+14
View File
@@ -0,0 +1,14 @@
module.exports = {
context: {
names: [
{ name: 'Moe' },
{ name: 'Larry' },
{ name: 'Curly' },
{ name: 'Shemp' }
]
},
handlebars: '{{#each names}}{{name}}{{/each}}',
dust: '{#names}{name}{/names}',
mustache: '{{#names}}{{name}}{{/names}}',
eco: '<% for item in @names: %><%= item.name %><% end %>'
};
+11
View File
@@ -0,0 +1,11 @@
module.exports = {
context: {
names: [
{ name: 'Moe' },
{ name: 'Larry' },
{ name: 'Curly' },
{ name: 'Shemp' }
]
},
handlebars: '{{#names}}{{name}}{{/names}}'
};
+14
View File
@@ -0,0 +1,14 @@
<h1>{header}</h1>
{?items}
<ul>
{#items}
{#current}
<li><strong>{name}</strong></li>
{:else}
<li><a href="{url}">{name}</a></li>
{/current}
{/items}
</ul>
{:else}
<p>The list is empty.</p>
{/items}
+14
View File
@@ -0,0 +1,14 @@
<h1><%= @header() %></h1>
<% if @items.length: %>
<ul>
<% for item in @items: %>
<% if item.current: %>
<li><strong><%= item.name %></strong></li>
<% else: %>
<li><a href="<%= item.url %>"><%= item.name %></a></li>
<% end %>
<% end %>
</ul>
<% else: %>
<p>The list is empty.</p>
<% end %>
+14
View File
@@ -0,0 +1,14 @@
<h1>{{header}}</h1>
{{#if items}}
<ul>
{{#each items}}
{{#if current}}
<li><strong>{{name}}</strong></li>
{{^}}
<li><a href="{{url}}">{{name}}</a></li>
{{/if}}
{{/each}}
</ul>
{{^}}
<p>The list is empty.</p>
{{/if}}
+20
View File
@@ -0,0 +1,20 @@
var fs = require('fs');
module.exports = {
context: {
header: function() {
return 'Colors';
},
hasItems: true, // To make things fairer in mustache land due to no `{{if}}` construct on arrays
items: [
{ name: 'red', current: true, url: '#Red' },
{ name: 'green', current: false, url: '#Green' },
{ name: 'blue', current: false, url: '#Blue' }
]
},
handlebars: fs.readFileSync(__dirname + '/complex.handlebars').toString(),
dust: fs.readFileSync(__dirname + '/complex.dust').toString(),
eco: fs.readFileSync(__dirname + '/complex.eco').toString(),
mustache: fs.readFileSync(__dirname + '/complex.mustache').toString()
};
+13
View File
@@ -0,0 +1,13 @@
<h1>{{header}}</h1>
{{#hasItems}}
<ul>
{{#items}}
{{#current}}
<li><strong>{{name}}</strong></li>
{{/current}}
{{^current}}
<li><a href="{{url}}">{{name}}</a></li>
{{/current}}
{{/items}}
</ul>
{{/hasItems}}
+11
View File
@@ -0,0 +1,11 @@
module.exports = {
context: {
names: [
{ name: 'Moe' },
{ name: 'Larry' },
{ name: 'Curly' },
{ name: 'Shemp' }
]
},
handlebars: '{{#each names}}{{@index}}{{name}}{{/each}}'
};
+14
View File
@@ -0,0 +1,14 @@
module.exports = {
context: {
names: [
{ name: 'Moe' },
{ name: 'Larry' },
{ name: 'Curly' },
{ name: 'Shemp' }
],
foo: 'bar'
},
handlebars: '{{#each names}}{{../foo}}{{/each}}',
mustache: '{{#names}}{{foo}}{{/names}}',
eco: '<% for item in @names: %><%= @foo %><% end %>'
};
+16
View File
@@ -0,0 +1,16 @@
module.exports = {
context: {
names: [
{ bat: 'foo', name: ['Moe'] },
{ bat: 'foo', name: ['Larry'] },
{ bat: 'foo', name: ['Curly'] },
{ bat: 'foo', name: ['Shemp'] }
],
foo: 'bar'
},
handlebars:
'{{#each names}}{{#each name}}{{../bat}}{{../../foo}}{{/each}}{{/each}}',
mustache: '{{#names}}{{#name}}{{bat}}{{foo}}{{/name}}{{/names}}',
eco:
'<% for item in @names: %><% for child in item.name: %><%= item.bat %><%= @foo %><% end %><% end %>'
};
+9
View File
@@ -0,0 +1,9 @@
var fs = require('fs');
var templates = fs.readdirSync(__dirname);
templates.forEach(function(template) {
if (template === 'index.js' || !/(.*)\.js$/.test(template)) {
return;
}
module.exports[RegExp.$1] = require('./' + RegExp.$1);
});
+4
View File
@@ -0,0 +1,4 @@
module.exports = {
context: { person: { name: 'Larry', age: 45 } },
handlebars: '{{#person}}{{name}}{{age}}{{/person}}'
};
+7
View File
@@ -0,0 +1,7 @@
module.exports = {
context: { person: { name: 'Larry', age: 45 } },
handlebars: '{{#with person}}{{name}}{{age}}{{/with}}',
dust: '{#person}{name}{age}{/person}',
eco: '<%= @person.name %><%= @person.age %>',
mustache: '{{#person}}{{name}}{{age}}{{/person}}'
};
@@ -0,0 +1,13 @@
module.exports = {
context: {
name: '1',
kids: [{ name: '1.1', kids: [{ name: '1.1.1', kids: [] }] }]
},
partials: {
mustache: { recursion: '{{name}}{{#kids}}{{>recursion}}{{/kids}}' },
handlebars: { recursion: '{{name}}{{#each kids}}{{>recursion}}{{/each}}' }
},
handlebars: '{{name}}{{#each kids}}{{>recursion}}{{/each}}',
dust: '{name}{#kids}{>recursion:./}{/kids}',
mustache: '{{name}}{{#kids}}{{>recursion}}{{/kids}}'
};
+19
View File
@@ -0,0 +1,19 @@
module.exports = {
context: {
peeps: [
{ name: 'Moe', count: 15 },
{ name: 'Larry', count: 5 },
{ name: 'Curly', count: 1 }
]
},
partials: {
mustache: { variables: 'Hello {{name}}! You have {{count}} new messages.' },
handlebars: {
variables: 'Hello {{name}}! You have {{count}} new messages.'
}
},
handlebars: '{{#each peeps}}{{>variables}}{{/each}}',
dust: '{#peeps}{>variables/}{/peeps}',
mustache: '{{#peeps}}{{>variables}}{{/peeps}}'
};
+9
View File
@@ -0,0 +1,9 @@
module.exports = {
context: { person: { name: { bar: { baz: 'Larry' } }, age: 45 } },
handlebars:
'{{person.name.bar.baz}}{{person.age}}{{person.foo}}{{animal.age}}',
dust: '{person.name.bar.baz}{person.age}{person.foo}{animal.age}',
eco:
'<%= @person.name.bar.baz %><%= @person.age %><%= @person.foo %><% if @animal: %><%= @animal.age %><% end %>',
mustache: '{{person.name.bar.baz}}{{person.age}}{{person.foo}}{{animal.age}}'
};
+7
View File
@@ -0,0 +1,7 @@
module.exports = {
context: {},
handlebars: 'Hello world',
dust: 'Hello world',
mustache: 'Hello world',
eco: 'Hello world'
};
+14
View File
@@ -0,0 +1,14 @@
module.exports = {
helpers: {
echo: function(value) {
return 'foo ' + value;
},
header: function() {
return 'Colors';
}
},
handlebars: '{{echo (header)}}',
eco: '<%= @echo(@header()) %>'
};
module.exports.context = module.exports.helpers;
+7
View File
@@ -0,0 +1,7 @@
module.exports = {
context: { name: 'Mick', count: 30 },
handlebars: 'Hello {{name}}! You have {{count}} new messages.',
dust: 'Hello {name}! You have {count} new messages.',
mustache: 'Hello {{name}}! You have {{count}} new messages.',
eco: 'Hello <%= @name %>! You have <%= @count %> new messages.'
};
+154
View File
@@ -0,0 +1,154 @@
var _ = require('underscore'),
runner = require('./util/template-runner'),
eco,
dust,
Handlebars,
Mustache;
try {
dust = require('dustjs-linkedin');
} catch (err) {
/* NOP */
}
try {
Mustache = require('mustache');
} catch (err) {
/* NOP */
}
try {
eco = require('eco');
} catch (err) {
/* NOP */
}
function error() {
throw new Error('EWOT');
}
function makeSuite(bench, name, template, handlebarsOnly) {
// Create aliases to minimize any impact from having to walk up the closure tree.
var templateName = name,
context = template.context,
partials = template.partials,
handlebarsOut,
compatOut,
dustOut,
ecoOut,
mustacheOut;
var handlebar = Handlebars.compile(template.handlebars, { data: false }),
compat = Handlebars.compile(template.handlebars, {
data: false,
compat: true
}),
options = { helpers: template.helpers };
_.each(template.partials && template.partials.handlebars, function(
partial,
partialName
) {
Handlebars.registerPartial(
partialName,
Handlebars.compile(partial, { data: false })
);
});
handlebarsOut = handlebar(context, options);
bench('handlebars', function() {
handlebar(context, options);
});
compatOut = compat(context, options);
bench('compat', function() {
compat(context, options);
});
if (handlebarsOnly) {
return;
}
if (dust) {
if (template.dust) {
dustOut = false;
dust.loadSource(dust.compile(template.dust, templateName));
dust.render(templateName, context, function(err, out) {
dustOut = out;
});
bench('dust', function() {
dust.render(templateName, context, function() {});
});
} else {
bench('dust', error);
}
}
if (eco) {
if (template.eco) {
var ecoTemplate = eco.compile(template.eco);
ecoOut = ecoTemplate(context);
bench('eco', function() {
ecoTemplate(context);
});
} else {
bench('eco', error);
}
}
if (Mustache) {
var mustacheSource = template.mustache,
mustachePartials = partials && partials.mustache;
if (mustacheSource) {
mustacheOut = Mustache.to_html(mustacheSource, context, mustachePartials);
bench('mustache', function() {
Mustache.to_html(mustacheSource, context, mustachePartials);
});
} else {
bench('mustache', error);
}
}
// Hack around whitespace until we have whitespace control
handlebarsOut = handlebarsOut.replace(/\s/g, '');
function compare(b, lang) {
if (b == null) {
return;
}
b = b.replace(/\s/g, '');
if (handlebarsOut !== b) {
throw new Error(
'Template output mismatch: ' +
name +
'\n\nHandlebars: ' +
handlebarsOut +
'\n\n' +
lang +
': ' +
b
);
}
}
compare(compatOut, 'compat');
compare(dustOut, 'dust');
compare(ecoOut, 'eco');
compare(mustacheOut, 'mustache');
}
module.exports = function(grunt, callback) {
// Deferring load incase we are being run inline with the grunt build
Handlebars = require('../../lib');
console.log('Execution Throughput');
runner(grunt, makeSuite, function(times, scaled) {
callback(scaled);
});
};
+221
View File
@@ -0,0 +1,221 @@
var _ = require('underscore'),
Benchmark = require('benchmark');
function BenchWarmer() {
this.benchmarks = [];
this.currentBenches = [];
this.names = [];
this.times = {};
this.minimum = Infinity;
this.maximum = -Infinity;
this.errors = {};
}
BenchWarmer.prototype = {
winners: function(benches) {
return Benchmark.filter(benches, 'fastest');
},
suite: function(suite, fn) {
this.suiteName = suite;
this.times[suite] = {};
this.first = true;
var self = this;
fn(function(name, benchFn) {
self.push(name, benchFn);
});
},
push: function(name, fn) {
if (this.names.indexOf(name) === -1) {
this.names.push(name);
}
var first = this.first,
suiteName = this.suiteName,
self = this;
this.first = false;
var bench = new Benchmark(fn, {
name: this.suiteName + ': ' + name,
onComplete: function() {
if (first) {
self.startLine(suiteName);
}
self.writeBench(bench);
self.currentBenches.push(bench);
},
onError: function() {
self.errors[this.name] = this;
}
});
bench.suiteName = this.suiteName;
bench.benchName = name;
this.benchmarks.push(bench);
},
bench: function(callback) {
var self = this;
this.printHeader('ops/msec', true);
Benchmark.invoke(this.benchmarks, {
name: 'run',
onComplete: function() {
self.scaleTimes();
self.startLine('');
console.log('\n');
self.printHeader('scaled');
_.each(self.scaled, function(value, name) {
self.startLine(name);
_.each(self.names, function(lang) {
self.writeValue(value[lang] || '');
});
});
console.log('\n');
var errors = false,
prop,
bench;
for (prop in self.errors) {
if (
Object.prototype.hasOwnProperty.call(self, prop) &&
self.errors[prop].error.message !== 'EWOT'
) {
errors = true;
break;
}
}
if (errors) {
console.log('\n\nErrors:\n');
Object.keys(self.errors).forEach(function(prop) {
if (self.errors[prop].error.message !== 'EWOT') {
bench = self.errors[prop];
console.log('\n' + bench.name + ':\n');
console.log(bench.error.message);
if (bench.error.stack) {
console.log(bench.error.stack.join('\n'));
}
console.log('\n');
}
});
}
callback();
}
});
console.log('\n');
},
scaleTimes: function() {
var scaled = (this.scaled = {});
_.each(
this.times,
function(times, name) {
var output = (scaled[name] = {});
_.each(
times,
function(time, lang) {
output[lang] = (
((time - this.minimum) / (this.maximum - this.minimum)) *
100
).toFixed(2);
},
this
);
},
this
);
},
printHeader: function(title, winners) {
var benchSize = 0,
names = this.names,
i,
l;
for (i = 0, l = names.length; i < l; i++) {
var name = names[i];
if (benchSize < name.length) {
benchSize = name.length;
}
}
this.nameSize = benchSize + 2;
this.benchSize = 20;
var horSize = 0;
this.startLine(title);
horSize = horSize + this.benchSize;
for (i = 0, l = names.length; i < l; i++) {
this.writeValue(names[i]);
horSize = horSize + this.benchSize;
}
if (winners) {
console.log('WINNER(S)');
horSize = horSize + 'WINNER(S)'.length;
}
console.log('\n' + new Array(horSize + 1).join('-'));
},
startLine: function(name) {
var winners = Benchmark.map(this.winners(this.currentBenches), function(
bench
) {
return bench.name.split(': ')[1];
});
this.currentBenches = [];
console.log(winners.join(', '));
console.log('\n');
if (name) {
this.writeValue(name);
}
},
writeBench: function(bench) {
var out;
if (!bench.error) {
var count = bench.hz,
moe = (count * bench.stats.rme) / 100,
minimum,
maximum;
count = Math.round(count / 1000);
moe = Math.round(moe / 1000);
minimum = count - moe;
maximum = count + moe;
out = count + ' ±' + moe + ' (' + bench.cycles + ')';
this.times[bench.suiteName][bench.benchName] = count;
this.minimum = Math.min(this.minimum, minimum);
this.maximum = Math.max(this.maximum, maximum);
} else if (bench.error.message === 'EWOT') {
out = 'NA';
} else {
out = 'E';
}
this.writeValue(out);
},
writeValue: function(out) {
var padding = this.benchSize - out.length + 1;
out = out + new Array(padding).join(' ');
console.log(out);
}
};
module.exports = BenchWarmer;
+29
View File
@@ -0,0 +1,29 @@
var _ = require('underscore'),
BenchWarmer = require('./benchwarmer'),
templates = require('../templates');
module.exports = function(grunt, makeSuite, callback) {
var warmer = new BenchWarmer();
var handlebarsOnly = grunt.option('handlebars-only'),
grep = grunt.option('grep');
if (grep) {
grep = new RegExp(grep);
}
_.each(templates, function(template, name) {
if (!template.handlebars || (grep && !grep.test(name))) {
return;
}
warmer.suite(name, function(bench) {
makeSuite(bench, name, template, handlebarsOnly);
});
});
warmer.bench(function() {
if (callback) {
callback(warmer.times, warmer.scaled);
}
});
};
+5
View File
@@ -0,0 +1,5 @@
module.exports = {
parserOptions: {
ecmaVersion: 2020
}
};
+14
View File
@@ -0,0 +1,14 @@
# Browser Tests with Playwright
These tests execute Mocha tests from the `spec`-folder in multiple browsers.
## Using Docker
Execute the following commands in the project root:
```bash
npm install
npx grunt prepare
docker pull mcr.microsoft.com/playwright:focal
docker run -it --rm --volume $(pwd):/srv/app --workdir /srv/app --ipc=host mcr.microsoft.com/playwright:focal npm run test:browser
```
+27
View File
@@ -0,0 +1,27 @@
const { devices } = require('@playwright/test');
/** @type {import('@playwright/test').PlaywrightTestConfig} */
const config = {
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] }
},
{
name: 'firefox',
use: { ...devices['Desktop Firefox'] }
},
{
name: 'webkit',
use: { ...devices['Desktop Safari'] }
}
],
reporter: 'list',
webServer: {
command: 'npm run test:serve',
port: 9999,
reuseExistingServer: false
}
};
module.exports = config;
+33
View File
@@ -0,0 +1,33 @@
const { test, expect } = require('@playwright/test');
async function waitForMochaAndAssertResult(page) {
await page.waitForFunction(() => window.mochaResults); // eslint-disable-line no-undef
const mochaResults = await page.evaluate('window.mochaResults');
expect(mochaResults.failures).toBe(0);
}
test('Spec handlebars.js', async ({ page, baseURL }) => {
await page.goto(`${baseURL}/spec/?headless=true`);
await waitForMochaAndAssertResult(page);
});
test('Spec handlebars.amd.js (AMD)', async ({ page, baseURL }) => {
await page.goto(`${baseURL}/spec/amd.html?headless=true`);
await waitForMochaAndAssertResult(page);
});
test('Spec handlebars.runtime.amd.js (AMD)', async ({ page, baseURL }) => {
await page.goto(`${baseURL}/spec/amd-runtime.html?headless=true`);
await waitForMochaAndAssertResult(page);
});
test('Spec handlebars.js (UMD)', async ({ page, baseURL }) => {
await page.goto(`${baseURL}/spec/umd.html?headless=true`);
await waitForMochaAndAssertResult(page);
});
test('Spec handlebars.runtime.js (UMD)', async ({ page, baseURL }) => {
await page.goto(`${baseURL}/spec/umd-runtime.html?headless=true`);
await waitForMochaAndAssertResult(page);
});
+10
View File
@@ -0,0 +1,10 @@
Add a new integration test by creating a new subfolder
Add a file "test.sh" to that runs the test. "test.sh" should exit with a non-zero exit code
and display an error message, if something goes wrong.
- An integration test should reflect real-world setups that use handlebars.
- It should compile a minimal template and compare the output to an expected output.
- It should use "../.." as dependency for Handlebars so that the currently built library is used.
Currently, integration tests are only running on Linux, especially in our CI GitHub action.
@@ -0,0 +1,13 @@
module.exports = {
extends: ['eslint:recommended', 'plugin:es5/no-es2015', 'prettier'],
globals: {
self: false
},
env: {
node: true
},
rules: {
'no-console': 'off',
'no-var': 'off'
}
};
@@ -0,0 +1,2 @@
target
package-lock.json
@@ -0,0 +1,16 @@
{
"name": "multi-nodejs-test",
"version": "1.0.0",
"description": "Simple integration test with all relevant NodeJS-versions.",
"keywords": [],
"author": "Nils Knappmeier",
"private": true,
"license": "MIT",
"dependencies": {
"handlebars": "file:../../.."
},
"scripts": {
"test": "node run-handlebars.js",
"test-precompile": "handlebars precompile-test-template.txt.hbs"
}
}
@@ -0,0 +1 @@
Author: {{author}}
+11
View File
@@ -0,0 +1,11 @@
// This test should run successfully with node 0.10++ as long as Handlebars has been compiled before
var assert = require('assert');
var Handlebars = require('handlebars');
console.log('Testing built Handlebars with Node version ' + process.version);
var template = Handlebars.compile('Author: {{author}}');
var output = template({ author: 'Yehuda' });
assert.strictEqual(output, 'Author: Yehuda');
console.log('Success');
+28
View File
@@ -0,0 +1,28 @@
#!/bin/bash
set -e
cd "$( dirname "$( readlink -f "$0" )" )" || exit 1
# shellcheck disable=SC1090
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
# This script tests with precompiler and the built distribution with multiple NodeJS version.
# This test is simple by design. It merely ensures, that calling Handlebars does not fail with old versions.
# It does (almost) not test for correctness, because that is already done in the mocha-tests.
# And it does not use any NodeJS based testing framework to make this part independent of the Node version.
unset npm_config_prefix
echo "Handlebars should be able to run in various versions of NodeJS"
for node_version_to_test in 10 11 12 13 14 15 16 17; do
rm target node_modules package-lock.json -rf
mkdir target
nvm install "$node_version_to_test"
nvm exec "$node_version_to_test" npm install
nvm exec "$node_version_to_test" npm run test
nvm exec "$node_version_to_test" npm run test-precompile
echo Success
done
@@ -0,0 +1,11 @@
module.exports = {
root: true,
extends: ['eslint:recommended', 'prettier'],
env: {
browser: true
},
parserOptions: {
sourceType: 'module',
ecmaVersion: 6
}
};
+3
View File
@@ -0,0 +1,3 @@
node_modules
dist
package-lock.json
@@ -0,0 +1,14 @@
{
"name": "rollup-test",
"description": "Various tests with Handlebars and rollup",
"version": "1.0.0",
"scripts": {
"build": "rollup --config rollup.config.js --failAfterWarnings"
},
"private": true,
"devDependencies": {
"@rollup/plugin-node-resolve": "^13.1.1",
"handlebars": "file:../../..",
"rollup": "^2.61.1"
}
}
@@ -0,0 +1,10 @@
import { nodeResolve } from '@rollup/plugin-node-resolve';
export default {
input: 'src/index.js',
output: {
file: 'dist/bundle.js',
format: 'es'
},
plugins: [nodeResolve()]
};
@@ -0,0 +1,8 @@
import Handlebars from 'handlebars/lib/handlebars';
const template = Handlebars.compile('Author: {{author}}');
const result = template({ author: 'Yehuda' });
if (result !== 'Author: Yehuda') {
throw Error('Assertion failed');
}
+11
View File
@@ -0,0 +1,11 @@
#!/bin/bash
set -e
# Cleanup: package-lock and "npm ci" is not working with local dependencies
rm dist package-lock.json -rf
npm install
npm run build
node dist/bundle.js
echo "Success"
+15
View File
@@ -0,0 +1,15 @@
#!/bin/bash
set -e
cd "$( dirname "$( readlink -f "$0" )" )"
for i in */test.sh ; do
(
echo "----------------------------------------"
echo "-- Running integration test: $i"
echo "----------------------------------------"
cd "$( dirname "$i" )"
./test.sh
)
done
@@ -0,0 +1,11 @@
{
"plugins": [
"@roundingwellos/babel-plugin-handlebars-inline-precompile"
// "istanbul"
],
"presets": [
[
"@babel/preset-env"
]
]
}
@@ -0,0 +1,3 @@
node_modules
dist
package-lock.json
@@ -0,0 +1,24 @@
{
"name": "webpack-babel-test",
"version": "1.0.0",
"description": "",
"main": "index.js",
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"@babel/core": "^7.5.5",
"@babel/preset-env": "^7.5.5",
"@roundingwellos/babel-plugin-handlebars-inline-precompile": "^3.0.1",
"babel-loader": "^8.0.6",
"babel-plugin-istanbul": "^5.2.0",
"handlebars": "file:../../..",
"handlebars-loader": "^1.7.1",
"nyc": "^14.1.1",
"webpack": "^4.39.3",
"webpack-cli": "^3.3.7"
},
"scripts": {
"build": "webpack --config webpack.config.js"
}
}
@@ -0,0 +1,11 @@
module.exports = {
root: true,
extends: ['eslint:recommended', 'prettier'],
env: {
browser: true
},
parserOptions: {
sourceType: 'module',
ecmaVersion: 6
}
};
@@ -0,0 +1,12 @@
import * as Handlebars from 'handlebars/runtime';
import hbs from 'handlebars-inline-precompile';
import { assertEquals } from '../../webpack-test/src/lib/assert';
Handlebars.registerHelper('loud', function(text) {
return text.toUpperCase();
});
const template = hbs`{{loud name}}`;
const output = template({ name: 'yehuda' });
assertEquals(output, 'YEHUDA');
@@ -0,0 +1,5 @@
export function assertEquals(actual, expected) {
if (actual !== expected) {
throw new Error(`Expected "${actual}" to equal "${expected}"`);
}
}
+16
View File
@@ -0,0 +1,16 @@
#!/bin/bash
set -e
# Cleanup: package-lock and "npm ci" is not working with local dependencies
rm dist package-lock.json -rf
npm install
npm run build
for i in dist/*-test.js ; do
echo "----------------------"
echo "-- Running $i"
echo "----------------------"
node "$i"
echo "Success"
done
@@ -0,0 +1,32 @@
const fs = require('fs');
const testFiles = fs.readdirSync('src');
const entryPoints = {};
testFiles
.filter(file => file.match(/-test.js$/))
.forEach(file => {
entryPoints[file] = `./src/${file}`;
});
module.exports = {
entry: entryPoints,
output: {
filename: '[name]',
path: __dirname + '/dist'
},
module: {
rules: [
{
test: /\.js?$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: { cacheDirectory: false }
}
}
]
},
optimization: {
minimize: false
}
};
@@ -0,0 +1,3 @@
node_modules
dist
package-lock.json
@@ -0,0 +1,21 @@
{
"name": "webpack-test",
"description": "Various tests with Handlebars and Webpack",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"build": "webpack --config webpack.config.js",
"test": "node dist/main.js"
},
"private": true,
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {},
"devDependencies": {
"handlebars": "file:../../..",
"handlebars-loader": "^1.7.1",
"webpack": "^4.39.3",
"webpack-cli": "^3.3.7"
}
}
@@ -0,0 +1,12 @@
module.exports = {
root: true,
extends: ['eslint:recommended', 'prettier'],
env: {
node: true,
browser: true
},
parserOptions: {
sourceType: 'module',
ecmaVersion: 6
}
};
@@ -0,0 +1,6 @@
import Handlebars from 'handlebars/dist/handlebars';
import { assertEquals } from './lib/assert';
const template = Handlebars.compile('Author: {{author}}');
assertEquals(template({ author: 'Yehuda' }), 'Author: Yehuda');
@@ -0,0 +1,5 @@
import Handlebars from 'handlebars';
import { assertEquals } from './lib/assert';
const template = Handlebars.compile('Author: {{author}}');
assertEquals(template({ author: 'Yehuda' }), 'Author: Yehuda');
@@ -0,0 +1,10 @@
import { assertEquals } from './lib/assert';
import testTemplate from './test-template.handlebars';
assertEquals(testTemplate({ author: 'Yehuda' }).trim(), 'Author: Yehuda');
const testTemplateRequire = require('./test-template.handlebars');
assertEquals(
testTemplateRequire({ author: 'Yehuda' }).trim(),
'Author: Yehuda'
);
@@ -0,0 +1,10 @@
import * as HandlebarsViaImport from 'handlebars';
const HandlebarsViaRequire = require('handlebars');
import { assertEquals } from './lib/assert';
HandlebarsViaImport.registerHelper('loud', function(text) {
return text.toUpperCase();
});
const template = HandlebarsViaRequire.compile('Author: {{loud author}}');
assertEquals(template({ author: 'Yehuda' }), 'Author: YEHUDA');
@@ -0,0 +1,6 @@
import * as Handlebars from 'handlebars/dist/handlebars';
import { assertEquals } from './lib/assert';
const template = Handlebars.compile('Author: {{author}}');
assertEquals(template({ author: 'Yehuda' }), 'Author: Yehuda');
@@ -0,0 +1,5 @@
import * as Handlebars from 'handlebars';
import { assertEquals } from './lib/assert';
const template = Handlebars.compile('Author: {{author}}');
assertEquals(template({ author: 'Yehuda' }), 'Author: Yehuda');
@@ -0,0 +1,5 @@
export function assertEquals(actual, expected) {
if (actual !== expected) {
throw new Error(`Expected "${actual}" to equal "${expected}"`);
}
}
@@ -0,0 +1,2 @@
Author: {{author}}
+16
View File
@@ -0,0 +1,16 @@
#!/bin/bash
set -e
# Cleanup: package-lock and "npm ci" is not working with local dependencies
rm dist package-lock.json -rf
npm install
npm run build
for i in dist/*-test.js ; do
echo "----------------------"
echo "-- Running $i"
echo "----------------------"
node "$i"
echo "Success"
done
@@ -0,0 +1,20 @@
const fs = require('fs');
const testFiles = fs.readdirSync('src');
const entryPoints = {};
testFiles
.filter(file => file.match(/-test.js$/))
.forEach(file => {
entryPoints[file] = `./src/${file}`;
});
module.exports = {
entry: entryPoints,
output: {
filename: '[name]',
path: __dirname + '/dist'
},
module: {
rules: [{ test: /\.handlebars$/, loader: 'handlebars-loader' }]
}
};
+114
View File
@@ -0,0 +1,114 @@
/* eslint-disable no-console, no-var */
// Util script for debugging source code generation issues
var script = process.argv[2].replace(/\\n/g, '\n'),
verbose = process.argv[3] === '-v';
var Handlebars = require('./../lib'),
SourceMap = require('source-map'),
SourceMapConsumer = SourceMap.SourceMapConsumer;
var template = Handlebars.precompile(script, {
srcName: 'input.hbs',
destName: 'output.js',
assumeObjects: true,
compat: false,
strict: true,
trackIds: true,
knownHelpersOnly: false
});
if (!verbose) {
console.log(template);
} else {
var consumer = new SourceMapConsumer(template.map),
lines = template.code.split('\n'),
srcLines = script.split('\n');
console.log();
console.log('Source:');
srcLines.forEach(function(source, index) {
console.log(index + 1, source);
});
console.log();
console.log('Generated:');
console.log(template.code);
lines.forEach(function(source, index) {
console.log(index + 1, source);
});
console.log();
console.log('Map:');
console.log(template.map);
console.log();
// eslint-disable-next-line no-inner-declarations
function collectSource(lines, lineName, colName, order) {
var ret = {},
ordered = [],
last;
function collect(current) {
if (last) {
var mapLines = lines.slice(
last[lineName] - 1,
current && current[lineName]
);
if (mapLines.length) {
if (current) {
mapLines[mapLines.length - 1] = mapLines[mapLines.length - 1].slice(
0,
current[colName]
);
}
mapLines[0] = mapLines[0].slice(last[colName]);
}
ret[last[lineName] + ':' + last[colName]] = mapLines.join('\n');
ordered.push({
startLine: last[lineName],
startCol: last[colName],
endLine: current && current[lineName]
});
}
last = current;
}
consumer.eachMapping(collect, undefined, order);
collect();
return ret;
}
srcLines = collectSource(
srcLines,
'originalLine',
'originalColumn',
SourceMapConsumer.ORIGINAL_ORDER
);
lines = collectSource(lines, 'generatedLine', 'generatedColumn');
consumer.eachMapping(function(mapping) {
var originalSrc =
srcLines[mapping.originalLine + ':' + mapping.originalColumn],
generatedSrc =
lines[mapping.generatedLine + ':' + mapping.generatedColumn];
if (!mapping.originalLine) {
console.log(
'generated',
mapping.generatedLine + ':' + mapping.generatedColumn,
generatedSrc
);
} else {
console.log(
'map',
mapping.source,
mapping.originalLine + ':' + mapping.originalColumn,
originalSrc,
'->',
mapping.generatedLine + ':' + mapping.generatedColumn,
generatedSrc
);
}
});
}