Switch to ESM (#2138)
This commit is contained in:
+1
-1
@@ -116,7 +116,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"files": ["tests/bench/**/*.mjs"],
|
"files": ["tests/bench/**/*.js"],
|
||||||
"rules": {
|
"rules": {
|
||||||
"no-console": "off"
|
"no-console": "off"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://swc.rs/schema.json",
|
|
||||||
"module": {
|
|
||||||
"type": "commonjs",
|
|
||||||
"importInterop": "swc"
|
|
||||||
},
|
|
||||||
"sourceMaps": "inline"
|
|
||||||
}
|
|
||||||
@@ -1,11 +1,8 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
|
|
||||||
import { createRequire } from 'node:module';
|
import { loadTemplates, cli } from '../lib/precompiler.js';
|
||||||
import yargs from 'yargs';
|
import yargs from 'yargs';
|
||||||
|
|
||||||
const require = createRequire(import.meta.url);
|
|
||||||
const Precompiler = require('../dist/cjs/precompiler');
|
|
||||||
|
|
||||||
const parser = yargs(process.argv.slice(2))
|
const parser = yargs(process.argv.slice(2))
|
||||||
.usage('Precompile handlebar templates.\nUsage: $0 [template|directory]...')
|
.usage('Precompile handlebar templates.\nUsage: $0 [template|directory]...')
|
||||||
.help(false)
|
.help(false)
|
||||||
@@ -19,23 +16,6 @@ const parser = yargs(process.argv.slice(2))
|
|||||||
type: 'string',
|
type: 'string',
|
||||||
description: 'Source Map File',
|
description: 'Source Map File',
|
||||||
})
|
})
|
||||||
.option('a', {
|
|
||||||
type: 'boolean',
|
|
||||||
description: 'Exports amd style (require.js)',
|
|
||||||
alias: 'amd',
|
|
||||||
})
|
|
||||||
.option('c', {
|
|
||||||
type: 'string',
|
|
||||||
description: 'Exports CommonJS style, path to Handlebars module',
|
|
||||||
alias: 'commonjs',
|
|
||||||
default: null,
|
|
||||||
})
|
|
||||||
.option('h', {
|
|
||||||
type: 'string',
|
|
||||||
description: 'Path to handlebar.js (only valid for amd-style)',
|
|
||||||
alias: 'handlebarPath',
|
|
||||||
default: '',
|
|
||||||
})
|
|
||||||
.option('k', {
|
.option('k', {
|
||||||
type: 'string',
|
type: 'string',
|
||||||
description: 'Known helpers',
|
description: 'Known helpers',
|
||||||
@@ -117,7 +97,7 @@ const argv = parser.parseSync();
|
|||||||
argv.files = argv._;
|
argv.files = argv._;
|
||||||
delete argv._;
|
delete argv._;
|
||||||
|
|
||||||
Precompiler.loadTemplates(argv, function (err, opts) {
|
loadTemplates(argv, function (err, opts) {
|
||||||
if (err) {
|
if (err) {
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
@@ -127,7 +107,7 @@ Precompiler.loadTemplates(argv, function (err, opts) {
|
|||||||
} else {
|
} else {
|
||||||
// cli() is async (returns a Promise), so errors would become unhandled
|
// cli() is async (returns a Promise), so errors would become unhandled
|
||||||
// rejections. Re-throw via nextTick to surface them as uncaught exceptions.
|
// rejections. Re-throw via nextTick to surface them as uncaught exceptions.
|
||||||
Promise.resolve(Precompiler.cli(opts)).catch((error) => {
|
Promise.resolve(cli(opts)).catch((error) => {
|
||||||
process.nextTick(() => {
|
process.nextTick(() => {
|
||||||
throw error;
|
throw error;
|
||||||
});
|
});
|
||||||
+9
-5
@@ -5,14 +5,18 @@ import {
|
|||||||
Visitor,
|
Visitor,
|
||||||
} from '@handlebars/parser';
|
} from '@handlebars/parser';
|
||||||
|
|
||||||
import runtime from './handlebars.runtime';
|
import runtime from './handlebars.runtime.js';
|
||||||
|
|
||||||
// Compiler imports
|
// Compiler imports
|
||||||
import AST from './handlebars/compiler/ast';
|
import AST from './handlebars/compiler/ast.js';
|
||||||
import { Compiler, compile, precompile } from './handlebars/compiler/compiler';
|
import {
|
||||||
import JavaScriptCompiler from './handlebars/compiler/javascript-compiler';
|
Compiler,
|
||||||
|
compile,
|
||||||
|
precompile,
|
||||||
|
} from './handlebars/compiler/compiler.js';
|
||||||
|
import JavaScriptCompiler from './handlebars/compiler/javascript-compiler.js';
|
||||||
|
|
||||||
import noConflict from './handlebars/no-conflict';
|
import noConflict from './handlebars/no-conflict.js';
|
||||||
|
|
||||||
let _create = runtime.create;
|
let _create = runtime.create;
|
||||||
function create() {
|
function create() {
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { Exception } from '@handlebars/parser';
|
import { Exception } from '@handlebars/parser';
|
||||||
import * as base from './handlebars/base';
|
import * as base from './handlebars/base.js';
|
||||||
|
|
||||||
// Each of these augment the Handlebars object. No need to setup here.
|
// Each of these augment the Handlebars object. No need to setup here.
|
||||||
// (This is done to easily share code between commonjs and browse envs)
|
// (This is done to easily share code between module systems and browser envs)
|
||||||
import SafeString from './handlebars/safe-string';
|
import SafeString from './handlebars/safe-string.js';
|
||||||
import * as Utils from './handlebars/utils';
|
import * as Utils from './handlebars/utils.js';
|
||||||
import * as runtime from './handlebars/runtime';
|
import * as runtime from './handlebars/runtime.js';
|
||||||
|
|
||||||
import noConflict from './handlebars/no-conflict';
|
import noConflict from './handlebars/no-conflict.js';
|
||||||
|
|
||||||
// For compatibility and usage outside of module systems, make the Handlebars object a namespace
|
// For compatibility and usage outside of module systems, make the Handlebars object a namespace
|
||||||
function create() {
|
function create() {
|
||||||
@@ -37,4 +37,15 @@ noConflict(inst);
|
|||||||
|
|
||||||
inst['default'] = inst;
|
inst['default'] = inst;
|
||||||
|
|
||||||
|
// Named re-exports for CJS interop.
|
||||||
|
// See the comment in lib/index.js for the full explanation. In short:
|
||||||
|
// require('handlebars/runtime').COMPILER_REVISION must be directly accessible
|
||||||
|
// for tools like handlebars-loader that compare compiler and runtime revisions.
|
||||||
|
export {
|
||||||
|
VERSION,
|
||||||
|
COMPILER_REVISION,
|
||||||
|
LAST_COMPATIBLE_COMPILER_REVISION,
|
||||||
|
REVISION_CHANGES,
|
||||||
|
} from './handlebars/base.js';
|
||||||
|
|
||||||
export default inst;
|
export default inst;
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
import { Exception } from '@handlebars/parser';
|
import { Exception } from '@handlebars/parser';
|
||||||
import { createFrame, extend, toString } from './utils';
|
import { createFrame, extend, toString } from './utils.js';
|
||||||
import { registerDefaultHelpers } from './helpers';
|
import { registerDefaultHelpers } from './helpers.js';
|
||||||
import { registerDefaultDecorators } from './decorators';
|
import { registerDefaultDecorators } from './decorators.js';
|
||||||
import logger from './logger';
|
import logger from './logger.js';
|
||||||
import { resetLoggedProperties } from './internal/proto-access';
|
import { resetLoggedProperties } from './internal/proto-access.js';
|
||||||
|
|
||||||
export const VERSION = '4.7.7';
|
export const VERSION = '4.7.7';
|
||||||
export const COMPILER_REVISION = 8;
|
export const COMPILER_REVISION = 8;
|
||||||
|
|||||||
@@ -1,56 +1,5 @@
|
|||||||
/* global define */
|
import { isArray } from '../utils.js';
|
||||||
import { isArray } from '../utils';
|
import { SourceNode } from '#source-node';
|
||||||
|
|
||||||
let SourceNode;
|
|
||||||
|
|
||||||
try {
|
|
||||||
/* v8 ignore next */
|
|
||||||
if (typeof define !== 'function' || !define.amd) {
|
|
||||||
// We don't support this in AMD environments. For these environments, we assume that
|
|
||||||
// they are running on the browser and thus have no need for the source-map library.
|
|
||||||
// The variable indirection prevents bundlers from statically resolving and bundling
|
|
||||||
// source-map (which requires Node-built-ins). The stub SourceNode below handles
|
|
||||||
// browser/bundled usage. Bundlers may emit a "Critical dependency" warning — this
|
|
||||||
// is expected and harmless.
|
|
||||||
let mod = 'source-map';
|
|
||||||
let SourceMap = require(mod);
|
|
||||||
SourceNode = SourceMap.SourceNode;
|
|
||||||
}
|
|
||||||
// oxlint-disable-next-line no-unused-vars -- Babel 5 requires named catch param
|
|
||||||
} catch (err) {
|
|
||||||
/* NOP */
|
|
||||||
}
|
|
||||||
|
|
||||||
/* v8 ignore next -- tested but not covered due to dist build */
|
|
||||||
if (!SourceNode) {
|
|
||||||
SourceNode = function (line, column, srcFile, chunks) {
|
|
||||||
this.src = '';
|
|
||||||
if (chunks) {
|
|
||||||
this.add(chunks);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
/* v8 ignore next */
|
|
||||||
SourceNode.prototype = {
|
|
||||||
add: function (chunks) {
|
|
||||||
if (isArray(chunks)) {
|
|
||||||
chunks = chunks.join('');
|
|
||||||
}
|
|
||||||
this.src += chunks;
|
|
||||||
},
|
|
||||||
prepend: function (chunks) {
|
|
||||||
if (isArray(chunks)) {
|
|
||||||
chunks = chunks.join('');
|
|
||||||
}
|
|
||||||
this.src = chunks + this.src;
|
|
||||||
},
|
|
||||||
toStringWithSourceMap: function () {
|
|
||||||
return { code: this.toString() };
|
|
||||||
},
|
|
||||||
toString: function () {
|
|
||||||
return this.src;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function castChunk(chunk, codeGen, loc) {
|
function castChunk(chunk, codeGen, loc) {
|
||||||
if (isArray(chunk)) {
|
if (isArray(chunk)) {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { Exception } from '@handlebars/parser';
|
import { Exception } from '@handlebars/parser';
|
||||||
import { isArray, indexOf, extend } from '../utils';
|
import { isArray, indexOf, extend } from '../utils.js';
|
||||||
import AST from './ast';
|
import AST from './ast.js';
|
||||||
|
|
||||||
const slice = [].slice;
|
const slice = [].slice;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Exception } from '@handlebars/parser';
|
import { Exception } from '@handlebars/parser';
|
||||||
import { COMPILER_REVISION, REVISION_CHANGES } from '../base';
|
import { COMPILER_REVISION, REVISION_CHANGES } from '../base.js';
|
||||||
import { isArray } from '../utils';
|
import { isArray } from '../utils.js';
|
||||||
import CodeGen from './code-gen';
|
import CodeGen from './code-gen.js';
|
||||||
|
|
||||||
function Literal(value) {
|
function Literal(value) {
|
||||||
this.value = value;
|
this.value = value;
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import { isArray } from '../utils.js';
|
||||||
|
|
||||||
|
// Lightweight stub for browser environments where the source-map package
|
||||||
|
// (which depends on Node.js built-ins) is not available.
|
||||||
|
export function SourceNode(line, column, srcFile, chunks) {
|
||||||
|
this.src = '';
|
||||||
|
if (chunks) {
|
||||||
|
this.add(chunks);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SourceNode.prototype = {
|
||||||
|
add(chunks) {
|
||||||
|
if (isArray(chunks)) {
|
||||||
|
chunks = chunks.join('');
|
||||||
|
}
|
||||||
|
this.src += chunks;
|
||||||
|
},
|
||||||
|
prepend(chunks) {
|
||||||
|
if (isArray(chunks)) {
|
||||||
|
chunks = chunks.join('');
|
||||||
|
}
|
||||||
|
this.src = chunks + this.src;
|
||||||
|
},
|
||||||
|
toStringWithSourceMap() {
|
||||||
|
return { code: this.toString() };
|
||||||
|
},
|
||||||
|
toString() {
|
||||||
|
return this.src;
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
export { SourceNode } from 'source-map';
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import registerInline from './decorators/inline';
|
import registerInline from './decorators/inline.js';
|
||||||
|
|
||||||
export function registerDefaultDecorators(instance) {
|
export function registerDefaultDecorators(instance) {
|
||||||
registerInline(instance);
|
registerInline(instance);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { extend } from '../utils';
|
import { extend } from '../utils.js';
|
||||||
|
|
||||||
export default function (instance) {
|
export default function (instance) {
|
||||||
instance.registerDecorator(
|
instance.registerDecorator(
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
import registerBlockHelperMissing from './helpers/block-helper-missing';
|
import registerBlockHelperMissing from './helpers/block-helper-missing.js';
|
||||||
import registerEach from './helpers/each';
|
import registerEach from './helpers/each.js';
|
||||||
import registerHelperMissing from './helpers/helper-missing';
|
import registerHelperMissing from './helpers/helper-missing.js';
|
||||||
import registerIf from './helpers/if';
|
import registerIf from './helpers/if.js';
|
||||||
import registerLog from './helpers/log';
|
import registerLog from './helpers/log.js';
|
||||||
import registerLookup from './helpers/lookup';
|
import registerLookup from './helpers/lookup.js';
|
||||||
import registerWith from './helpers/with';
|
import registerWith from './helpers/with.js';
|
||||||
|
|
||||||
export function registerDefaultHelpers(instance) {
|
export function registerDefaultHelpers(instance) {
|
||||||
registerBlockHelperMissing(instance);
|
registerBlockHelperMissing(instance);
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { isArray } from '../utils';
|
import { isArray } from '../utils.js';
|
||||||
|
|
||||||
export default function (instance) {
|
export default function (instance) {
|
||||||
instance.registerHelper('blockHelperMissing', function (context, options) {
|
instance.registerHelper('blockHelperMissing', function (context, options) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Exception } from '@handlebars/parser';
|
import { Exception } from '@handlebars/parser';
|
||||||
import { createFrame, isArray, isFunction, isMap, isSet } from '../utils';
|
import { createFrame, isArray, isFunction, isMap, isSet } from '../utils.js';
|
||||||
|
|
||||||
export default function (instance) {
|
export default function (instance) {
|
||||||
instance.registerHelper('each', function (context, options) {
|
instance.registerHelper('each', function (context, options) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Exception } from '@handlebars/parser';
|
import { Exception } from '@handlebars/parser';
|
||||||
import { isEmpty, isFunction } from '../utils';
|
import { isEmpty, isFunction } from '../utils.js';
|
||||||
|
|
||||||
export default function (instance) {
|
export default function (instance) {
|
||||||
instance.registerHelper('if', function (conditional, options) {
|
instance.registerHelper('if', function (conditional, options) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Exception } from '@handlebars/parser';
|
import { Exception } from '@handlebars/parser';
|
||||||
import { isEmpty, isFunction } from '../utils';
|
import { isEmpty, isFunction } from '../utils.js';
|
||||||
|
|
||||||
export default function (instance) {
|
export default function (instance) {
|
||||||
instance.registerHelper('with', function (context, options) {
|
instance.registerHelper('with', function (context, options) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { extend } from '../utils';
|
import { extend } from '../utils.js';
|
||||||
import logger from '../logger';
|
import logger from '../logger.js';
|
||||||
|
|
||||||
const loggedProperties = Object.create(null);
|
const loggedProperties = Object.create(null);
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { indexOf } from './utils';
|
import { indexOf } from './utils.js';
|
||||||
|
|
||||||
let logger = {
|
let logger = {
|
||||||
methodMap: ['debug', 'info', 'warn', 'error'],
|
methodMap: ['debug', 'info', 'warn', 'error'],
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
import { Exception } from '@handlebars/parser';
|
import { Exception } from '@handlebars/parser';
|
||||||
import * as Utils from './utils';
|
import * as Utils from './utils.js';
|
||||||
import {
|
import {
|
||||||
COMPILER_REVISION,
|
COMPILER_REVISION,
|
||||||
createFrame,
|
createFrame,
|
||||||
LAST_COMPATIBLE_COMPILER_REVISION,
|
LAST_COMPATIBLE_COMPILER_REVISION,
|
||||||
REVISION_CHANGES,
|
REVISION_CHANGES,
|
||||||
} from './base';
|
} from './base.js';
|
||||||
import { moveHelperToHooks } from './helpers';
|
import { moveHelperToHooks } from './helpers.js';
|
||||||
import { wrapHelper } from './internal/wrapHelper';
|
import { wrapHelper } from './internal/wrapHelper.js';
|
||||||
import {
|
import {
|
||||||
createProtoAccessControl,
|
createProtoAccessControl,
|
||||||
resultIsAllowed,
|
resultIsAllowed,
|
||||||
} from './internal/proto-access';
|
} from './internal/proto-access.js';
|
||||||
|
|
||||||
export function checkRevision(compilerInfo) {
|
export function checkRevision(compilerInfo) {
|
||||||
const compilerRevision = (compilerInfo && compilerInfo[0]) || 1,
|
const compilerRevision = (compilerInfo && compilerInfo[0]) || 1,
|
||||||
|
|||||||
+44
-23
@@ -1,25 +1,46 @@
|
|||||||
// USAGE:
|
import handlebars from './handlebars.js';
|
||||||
// var handlebars = require('handlebars');
|
import { PrintVisitor, print } from '@handlebars/parser';
|
||||||
/* eslint-disable no-var */
|
|
||||||
|
|
||||||
// var local = handlebars.create();
|
handlebars.PrintVisitor = PrintVisitor;
|
||||||
|
handlebars.print = print;
|
||||||
|
|
||||||
var handlebars = require('../dist/cjs/handlebars')['default'];
|
// Named exports for CJS interop.
|
||||||
|
//
|
||||||
var parser = require('@handlebars/parser');
|
// When Node.js (v22+) runs require() on an ESM module, it returns the module
|
||||||
handlebars.PrintVisitor = parser.PrintVisitor;
|
// namespace object — only named exports become direct properties. Without these,
|
||||||
handlebars.print = parser.print;
|
// require('handlebars') would return { default: inst } and calls like
|
||||||
|
// Handlebars.precompile() or Handlebars.COMPILER_REVISION would be undefined.
|
||||||
module.exports = handlebars;
|
//
|
||||||
|
// Tools like handlebars-loader rely on these being directly accessible via
|
||||||
// Publish a Node.js require() handler for .handlebars and .hbs files
|
// require('handlebars').create(), require('handlebars').COMPILER_REVISION, etc.
|
||||||
function extension(module, filename) {
|
export const {
|
||||||
var fs = require('fs');
|
create,
|
||||||
var templateString = fs.readFileSync(filename, 'utf8');
|
compile,
|
||||||
module.exports = handlebars.compile(templateString);
|
precompile,
|
||||||
}
|
parse,
|
||||||
/* v8 ignore next 4 */
|
parseWithoutProcessing,
|
||||||
if (typeof require !== 'undefined' && require.extensions) {
|
COMPILER_REVISION,
|
||||||
require.extensions['.handlebars'] = extension;
|
LAST_COMPATIBLE_COMPILER_REVISION,
|
||||||
require.extensions['.hbs'] = extension;
|
REVISION_CHANGES,
|
||||||
}
|
VERSION,
|
||||||
|
AST,
|
||||||
|
Compiler,
|
||||||
|
JavaScriptCompiler,
|
||||||
|
Parser,
|
||||||
|
Visitor,
|
||||||
|
SafeString,
|
||||||
|
Exception,
|
||||||
|
Utils,
|
||||||
|
escapeExpression,
|
||||||
|
VM,
|
||||||
|
template,
|
||||||
|
log,
|
||||||
|
registerHelper,
|
||||||
|
unregisterHelper,
|
||||||
|
registerPartial,
|
||||||
|
unregisterPartial,
|
||||||
|
registerDecorator,
|
||||||
|
unregisterDecorator,
|
||||||
|
} = handlebars;
|
||||||
|
export { PrintVisitor, print };
|
||||||
|
export default handlebars;
|
||||||
|
|||||||
+16
-45
@@ -1,11 +1,11 @@
|
|||||||
/* eslint-disable no-console */
|
/* eslint-disable no-console */
|
||||||
import Async from 'neo-async';
|
import Async from 'neo-async';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import Handlebars from './handlebars';
|
import Handlebars from './handlebars.js';
|
||||||
import { basename } from 'path';
|
import { basename } from 'path';
|
||||||
import { SourceMapConsumer, SourceNode } from 'source-map';
|
import { SourceMapConsumer, SourceNode } from 'source-map';
|
||||||
|
|
||||||
module.exports.loadTemplates = function (opts, callback) {
|
export function loadTemplates(opts, callback) {
|
||||||
loadStrings(opts, function (err, strings) {
|
loadStrings(opts, function (err, strings) {
|
||||||
if (err) {
|
if (err) {
|
||||||
callback(err);
|
callback(err);
|
||||||
@@ -20,7 +20,7 @@ module.exports.loadTemplates = function (opts, callback) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
function loadStrings(opts, callback) {
|
function loadStrings(opts, callback) {
|
||||||
let strings = arrayCast(opts.string),
|
let strings = arrayCast(opts.string),
|
||||||
@@ -152,7 +152,7 @@ function loadFiles(opts, callback) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports.cli = async function (opts) {
|
export async function cli(opts) {
|
||||||
if (opts.version) {
|
if (opts.version) {
|
||||||
console.log(Handlebars.VERSION);
|
console.log(Handlebars.VERSION);
|
||||||
return;
|
return;
|
||||||
@@ -176,12 +176,7 @@ module.exports.cli = async function (opts) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Force simple mode if we have only one template and it's unnamed.
|
// Force simple mode if we have only one template and it's unnamed.
|
||||||
if (
|
if (opts.templates.length === 1 && !opts.templates[0].name) {
|
||||||
!opts.amd &&
|
|
||||||
!opts.commonjs &&
|
|
||||||
opts.templates.length === 1 &&
|
|
||||||
!opts.templates[0].name
|
|
||||||
) {
|
|
||||||
opts.simple = true;
|
opts.simple = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,17 +195,7 @@ module.exports.cli = async function (opts) {
|
|||||||
|
|
||||||
let output = new SourceNode();
|
let output = new SourceNode();
|
||||||
if (!opts.simple) {
|
if (!opts.simple) {
|
||||||
if (opts.amd) {
|
output.add('(function() {\n');
|
||||||
output.add(
|
|
||||||
"define(['" +
|
|
||||||
opts.handlebarPath +
|
|
||||||
'handlebars.runtime\'], function(Handlebars) {\n Handlebars = Handlebars["default"];'
|
|
||||||
);
|
|
||||||
} else if (opts.commonjs) {
|
|
||||||
output.add('var Handlebars = require("' + opts.commonjs + '");');
|
|
||||||
} else {
|
|
||||||
output.add('(function() {\n');
|
|
||||||
}
|
|
||||||
output.add(' var template = Handlebars.template, templates = ');
|
output.add(' var template = Handlebars.template, templates = ');
|
||||||
if (opts.namespace) {
|
if (opts.namespace) {
|
||||||
output.add(opts.namespace);
|
output.add(opts.namespace);
|
||||||
@@ -253,9 +238,6 @@ module.exports.cli = async function (opts) {
|
|||||||
throw new Handlebars.Exception('Name missing for template');
|
throw new Handlebars.Exception('Name missing for template');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts.amd && !multiple) {
|
|
||||||
output.add('return ');
|
|
||||||
}
|
|
||||||
output.add([
|
output.add([
|
||||||
objectName,
|
objectName,
|
||||||
"['",
|
"['",
|
||||||
@@ -269,14 +251,7 @@ module.exports.cli = async function (opts) {
|
|||||||
|
|
||||||
// Output the content
|
// Output the content
|
||||||
if (!opts.simple) {
|
if (!opts.simple) {
|
||||||
if (opts.amd) {
|
output.add('})();');
|
||||||
if (multiple) {
|
|
||||||
output.add(['return ', objectName, ';\n']);
|
|
||||||
}
|
|
||||||
output.add('});');
|
|
||||||
} else if (!opts.commonjs) {
|
|
||||||
output.add('})();');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts.map) {
|
if (opts.map) {
|
||||||
@@ -287,7 +262,7 @@ module.exports.cli = async function (opts) {
|
|||||||
output.map = output.map + '';
|
output.map = output.map + '';
|
||||||
|
|
||||||
if (opts.min) {
|
if (opts.min) {
|
||||||
output = minify(output, opts.map);
|
output = await minify(output, opts.map);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts.map) {
|
if (opts.map) {
|
||||||
@@ -300,7 +275,7 @@ module.exports.cli = async function (opts) {
|
|||||||
} else {
|
} else {
|
||||||
console.log(output);
|
console.log(output);
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
function arrayCast(value) {
|
function arrayCast(value) {
|
||||||
value = value != null ? value : [];
|
value = value != null ? value : [];
|
||||||
@@ -313,29 +288,25 @@ function arrayCast(value) {
|
|||||||
/**
|
/**
|
||||||
* Run uglify to minify the compiled template, if uglify exists in the dependencies.
|
* Run uglify to minify the compiled template, if uglify exists in the dependencies.
|
||||||
*
|
*
|
||||||
* We are using `require` instead of `import` here, because es6-modules do not allow
|
|
||||||
* dynamic imports and uglify-js is an optional dependency. Since we are inside NodeJS here, this
|
|
||||||
* should not be a problem.
|
|
||||||
*
|
|
||||||
* @param {string} output the compiled template
|
* @param {string} output the compiled template
|
||||||
* @param {string} sourceMapFile the file to write the source map to.
|
* @param {string} sourceMapFile the file to write the source map to.
|
||||||
*/
|
*/
|
||||||
function minify(output, sourceMapFile) {
|
async function minify(output, sourceMapFile) {
|
||||||
|
let uglify;
|
||||||
try {
|
try {
|
||||||
// Try to resolve uglify-js in order to see if it does exist
|
uglify = await import('uglify-js');
|
||||||
require.resolve('uglify-js');
|
// Handle both default and named exports
|
||||||
|
uglify = uglify.default || uglify;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
if (e.code !== 'MODULE_NOT_FOUND') {
|
if (e.code !== 'ERR_MODULE_NOT_FOUND' && e.code !== 'MODULE_NOT_FOUND') {
|
||||||
// Something else seems to be wrong
|
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
// it does not exist!
|
|
||||||
console.error(
|
console.error(
|
||||||
'Code minimization is disabled due to missing uglify-js dependency'
|
'Code minimization is disabled due to missing uglify-js dependency'
|
||||||
);
|
);
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
return require('uglify-js').minify(output.code, {
|
return uglify.minify(output.code, {
|
||||||
sourceMap: {
|
sourceMap: {
|
||||||
content: output.map,
|
content: output.map,
|
||||||
url: sourceMapFile,
|
url: sourceMapFile,
|
||||||
|
|||||||
Generated
+12
-3468
File diff suppressed because it is too large
Load Diff
+25
-22
@@ -16,26 +16,40 @@
|
|||||||
"url": "https://github.com/handlebars-lang/handlebars.js.git"
|
"url": "https://github.com/handlebars-lang/handlebars.js.git"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"handlebars": "bin/handlebars.mjs"
|
"handlebars": "bin/handlebars.js"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"bin",
|
"bin",
|
||||||
"dist/*.js",
|
"dist/*.js",
|
||||||
"dist/cjs/**/*.js",
|
|
||||||
"lib",
|
"lib",
|
||||||
"release-notes.md",
|
"release-notes.md",
|
||||||
"runtime.js",
|
"runtime.js",
|
||||||
"types/*.d.ts",
|
"types/*.d.ts",
|
||||||
"runtime.d.ts"
|
"runtime.d.ts"
|
||||||
],
|
],
|
||||||
|
"type": "module",
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"browser": "./dist/cjs/handlebars.js",
|
|
||||||
"types": "types/index.d.ts",
|
"types": "types/index.d.ts",
|
||||||
|
"imports": {
|
||||||
|
"#source-node": {
|
||||||
|
"node": "./lib/handlebars/compiler/source-node.node.js",
|
||||||
|
"default": "./lib/handlebars/compiler/source-node.browser.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"types": "./types/index.d.ts",
|
||||||
|
"default": "./lib/index.js"
|
||||||
|
},
|
||||||
|
"./runtime": {
|
||||||
|
"types": "./runtime.d.ts",
|
||||||
|
"default": "./lib/handlebars.runtime.js"
|
||||||
|
},
|
||||||
|
"./package.json": "./package.json"
|
||||||
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true});require('fs').rmSync('tmp',{recursive:true,force:true})\"",
|
"clean": "node --input-type=module -e \"import fs from 'fs';fs.rmSync('dist',{recursive:true,force:true});fs.rmSync('tmp',{recursive:true,force:true})\"",
|
||||||
"build:cjs": "swc lib --out-dir dist/cjs --strip-leading-paths --ignore \"**/index.js\"",
|
"build": "npm run clean && rspack build",
|
||||||
"build:bundles": "rspack build",
|
|
||||||
"build": "npm run clean && npm run build:cjs && npm run build:bundles",
|
|
||||||
"release": "npm run build",
|
"release": "npm run build",
|
||||||
"publish:aws": "npm run build && npm run test:tasks && node tasks/publish-to-aws.js",
|
"publish:aws": "npm run build && npm run test:tasks && node tasks/publish-to-aws.js",
|
||||||
"format": "oxfmt --write . && oxlint --fix .",
|
"format": "oxfmt --write . && oxlint --fix .",
|
||||||
@@ -52,14 +66,14 @@
|
|||||||
"test:browser-smoke": "playwright test --config tests/browser/playwright.config.js",
|
"test:browser-smoke": "playwright test --config tests/browser/playwright.config.js",
|
||||||
"test:serve": "npx serve -l 9999 .",
|
"test:serve": "npx serve -l 9999 .",
|
||||||
"test:integration": "npm run build && ./tests/integration/run-integration-tests.sh",
|
"test:integration": "npm run build && ./tests/integration/run-integration-tests.sh",
|
||||||
"bench": "node tests/bench/perf.mjs",
|
"bench": "node tests/bench/perf.js",
|
||||||
"bench:compare": "node tests/bench/compare.mjs",
|
"bench:compare": "node tests/bench/compare.js",
|
||||||
"bench:size": "node tests/bench/size.mjs",
|
"bench:size": "node tests/bench/size.js",
|
||||||
"--- combined tasks ---": "",
|
"--- combined tasks ---": "",
|
||||||
"check-before-pull-request": "concurrently --kill-others-on-fail npm:lint npm:test"
|
"check-before-pull-request": "concurrently --kill-others-on-fail npm:lint npm:test"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@handlebars/parser": "^2.1.0",
|
"@handlebars/parser": "^2.2.2",
|
||||||
"neo-async": "^2.6.2",
|
"neo-async": "^2.6.2",
|
||||||
"source-map": "^0.7.6",
|
"source-map": "^0.7.6",
|
||||||
"yargs": "^18.0.0"
|
"yargs": "^18.0.0"
|
||||||
@@ -69,8 +83,6 @@
|
|||||||
"@playwright/test": "^1.58.2",
|
"@playwright/test": "^1.58.2",
|
||||||
"@rspack/cli": "^1.7.8",
|
"@rspack/cli": "^1.7.8",
|
||||||
"@rspack/core": "^1.7.8",
|
"@rspack/core": "^1.7.8",
|
||||||
"@swc/cli": "^0.8.0",
|
|
||||||
"@swc/core": "^1.15.18",
|
|
||||||
"@vitest/browser": "^4.0.18",
|
"@vitest/browser": "^4.0.18",
|
||||||
"@vitest/browser-playwright": "^4.0.18",
|
"@vitest/browser-playwright": "^4.0.18",
|
||||||
"@vitest/coverage-v8": "^4.0.18",
|
"@vitest/coverage-v8": "^4.0.18",
|
||||||
@@ -116,14 +128,5 @@
|
|||||||
],
|
],
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=20"
|
"node": ">=20"
|
||||||
},
|
|
||||||
"jspm": {
|
|
||||||
"main": "handlebars",
|
|
||||||
"directories": {
|
|
||||||
"lib": "dist/cjs"
|
|
||||||
},
|
|
||||||
"buildConfig": {
|
|
||||||
"minify": true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+38
-23
@@ -1,8 +1,14 @@
|
|||||||
const { rspack } = require('@rspack/core');
|
import { rspack } from '@rspack/core';
|
||||||
const path = require('path');
|
import path from 'path';
|
||||||
const fs = require('fs');
|
import fs from 'fs';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
|
||||||
const pkg = require('./package.json');
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
|
const __dirname = path.dirname(__filename);
|
||||||
|
|
||||||
|
const pkg = JSON.parse(
|
||||||
|
fs.readFileSync(path.resolve(__dirname, 'package.json'), 'utf8')
|
||||||
|
);
|
||||||
const license = fs.readFileSync(path.resolve(__dirname, 'LICENSE'), 'utf8');
|
const license = fs.readFileSync(path.resolve(__dirname, 'LICENSE'), 'utf8');
|
||||||
const banner = `/*!
|
const banner = `/*!
|
||||||
|
|
||||||
@@ -29,28 +35,11 @@ function createConfig(entry, filename, minimize) {
|
|||||||
filename,
|
filename,
|
||||||
library: {
|
library: {
|
||||||
name: 'Handlebars',
|
name: 'Handlebars',
|
||||||
type: 'umd',
|
type: 'self',
|
||||||
export: 'default',
|
export: 'default',
|
||||||
},
|
},
|
||||||
globalObject: 'this',
|
|
||||||
clean: false,
|
clean: false,
|
||||||
},
|
},
|
||||||
module: {
|
|
||||||
rules: [
|
|
||||||
{
|
|
||||||
test: /\.js$/,
|
|
||||||
exclude: /node_modules/,
|
|
||||||
use: {
|
|
||||||
loader: 'builtin:swc-loader',
|
|
||||||
options: {
|
|
||||||
jsc: {
|
|
||||||
parser: { syntax: 'ecmascript' },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
optimization: {
|
optimization: {
|
||||||
minimize,
|
minimize,
|
||||||
minimizer: minimize
|
minimizer: minimize
|
||||||
@@ -71,12 +60,38 @@ function createConfig(entry, filename, minimize) {
|
|||||||
: [],
|
: [],
|
||||||
},
|
},
|
||||||
plugins,
|
plugins,
|
||||||
|
resolve: {
|
||||||
|
extensions: ['.js', '.json'],
|
||||||
|
mainFields: ['main'],
|
||||||
|
},
|
||||||
|
module: {
|
||||||
|
rules: [
|
||||||
|
{
|
||||||
|
test: /\.js$/,
|
||||||
|
resolve: {
|
||||||
|
fullySpecified: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
test: /\.js$/,
|
||||||
|
exclude: /node_modules/,
|
||||||
|
use: {
|
||||||
|
loader: 'builtin:swc-loader',
|
||||||
|
options: {
|
||||||
|
jsc: {
|
||||||
|
parser: { syntax: 'ecmascript' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
target: ['web', 'browserslist'],
|
target: ['web', 'browserslist'],
|
||||||
devtool: false,
|
devtool: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = [
|
export default [
|
||||||
createConfig('./lib/handlebars.js', 'handlebars.js', false),
|
createConfig('./lib/handlebars.js', 'handlebars.js', false),
|
||||||
createConfig('./lib/handlebars.runtime.js', 'handlebars.runtime.js', false),
|
createConfig('./lib/handlebars.runtime.js', 'handlebars.runtime.js', false),
|
||||||
createConfig('./lib/handlebars.js', 'handlebars.min.js', true),
|
createConfig('./lib/handlebars.js', 'handlebars.min.js', true),
|
||||||
|
|||||||
Vendored
+3
-2
@@ -1,3 +1,4 @@
|
|||||||
import Handlebars = require('handlebars');
|
import Handlebars from 'handlebars';
|
||||||
|
|
||||||
declare module 'handlebars/runtime' {}
|
declare const runtime: typeof Handlebars;
|
||||||
|
export default runtime;
|
||||||
|
|||||||
+3
-3
@@ -1,3 +1,3 @@
|
|||||||
// Create a simple path alias to allow browserify to resolve
|
import runtime from './lib/handlebars.runtime.js';
|
||||||
// the runtime on a supported path.
|
|
||||||
module.exports = require('./dist/cjs/handlebars.runtime')['default'];
|
export default runtime;
|
||||||
|
|||||||
Vendored
+6
-3
@@ -1,7 +1,10 @@
|
|||||||
require('./common');
|
import './common.js';
|
||||||
|
|
||||||
var fs = require('fs'),
|
import fs from 'fs';
|
||||||
vm = require('vm');
|
import vm from 'vm';
|
||||||
|
import { createRequire } from 'module';
|
||||||
|
|
||||||
|
const require = createRequire(import.meta.url);
|
||||||
|
|
||||||
global.Handlebars = 'no-conflict';
|
global.Handlebars = 'no-conflict';
|
||||||
|
|
||||||
|
|||||||
Vendored
+3
-2
@@ -1,6 +1,7 @@
|
|||||||
require('./common');
|
import './common.js';
|
||||||
|
import Handlebars from '../../lib/index.js';
|
||||||
|
|
||||||
global.Handlebars = require('../../lib');
|
global.Handlebars = Handlebars;
|
||||||
|
|
||||||
global.CompilerContext = {
|
global.CompilerContext = {
|
||||||
compile: function (template, options) {
|
compile: function (template, options) {
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
define(['handlebars.runtime'], function(Handlebars) {
|
|
||||||
Handlebars = Handlebars["default"]; var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
|
|
||||||
return templates['bom'] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
|
||||||
return "a";
|
|
||||||
},"useData":true});
|
|
||||||
});
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
define(['handlebars.runtime'], function(Handlebars) {
|
|
||||||
Handlebars = Handlebars["default"]; var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
|
|
||||||
return templates['empty'] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
|
||||||
return "";
|
|
||||||
},"useData":true});
|
|
||||||
});
|
|
||||||
Vendored
-1
@@ -1 +0,0 @@
|
|||||||
define(["handlebars.runtime"],function(e){var t=(e=e.default).template;return(e.templates=e.templates||{}).empty=t({compiler:[8,">= 4.3.0"],main:function(e,t,a,n,r){return""},useData:!0})});
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
define(['handlebars.runtime'], function(Handlebars) {
|
|
||||||
Handlebars = Handlebars["default"]; var template = Handlebars.template, templates = CustomNamespace.templates = CustomNamespace.templates || {};
|
|
||||||
return templates['empty'] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
|
||||||
return "";
|
|
||||||
},"useData":true});
|
|
||||||
});
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
{"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
|
||||||
return "";
|
|
||||||
},"useData":true}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
define(['handlebars.runtime'], function(Handlebars) {
|
|
||||||
Handlebars = Handlebars["default"]; var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
|
|
||||||
templates['firstTemplate'] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
|
||||||
return "<div>1</div>";
|
|
||||||
},"useData":true});
|
|
||||||
templates['secondTemplate'] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
|
||||||
return "<div>2</div>";
|
|
||||||
},"useData":true});
|
|
||||||
return templates;
|
|
||||||
});
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
(function() {
|
(function() {
|
||||||
var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
|
var template = Handlebars.template, templates = CustomNamespace.templates = CustomNamespace.templates || {};
|
||||||
templates['empty'] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
templates['empty'] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
||||||
return "";
|
return "";
|
||||||
},"useData":true});
|
},"useData":true});
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
define(['handlebars.runtime'], function(Handlebars) {
|
|
||||||
Handlebars = Handlebars["default"]; var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
|
|
||||||
return templates['artifacts/partial.template'] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
|
||||||
return "<div>Test Partial</div>";
|
|
||||||
},"useData":true});
|
|
||||||
});
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
define(['some-path/handlebars.runtime'], function(Handlebars) {
|
|
||||||
Handlebars = Handlebars["default"]; var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
|
|
||||||
return templates['empty'] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
|
||||||
return "";
|
|
||||||
},"useData":true});
|
|
||||||
});
|
|
||||||
+19
-22
@@ -1,25 +1,22 @@
|
|||||||
Precompile handlebar templates.
|
Precompile handlebar templates.
|
||||||
Usage: handlebars.mjs [template|directory]...
|
Usage: handlebars.js [template|directory]...
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
-f, --output Output File [string]
|
-f, --output Output File [string]
|
||||||
--map Source Map File [string]
|
--map Source Map File [string]
|
||||||
-a, --amd Exports amd style (require.js) [boolean]
|
-k, --known Known helpers [string]
|
||||||
-c, --commonjs Exports CommonJS style, path to Handlebars module [string] [default: null]
|
-o, --knownOnly Known helpers only [boolean]
|
||||||
-h, --handlebarPath Path to handlebar.js (only valid for amd-style) [string] [default: ""]
|
-m, --min Minimize output [boolean]
|
||||||
-k, --known Known helpers [string]
|
-n, --namespace Template namespace [string] [default: "Handlebars.templates"]
|
||||||
-o, --knownOnly Known helpers only [boolean]
|
-s, --simple Output template function only. [boolean]
|
||||||
-m, --min Minimize output [boolean]
|
-N, --name Name of passed string templates. Optional if running in a simple mode. Required when operating on
|
||||||
-n, --namespace Template namespace [string] [default: "Handlebars.templates"]
|
multiple templates. [string]
|
||||||
-s, --simple Output template function only. [boolean]
|
-i, --string Generates a template from the passed CLI argument.
|
||||||
-N, --name Name of passed string templates. Optional if running in a simple mode. Required when operating on
|
"-" is treated as a special value and causes stdin to be read for the template value. [string]
|
||||||
multiple templates. [string]
|
-r, --root Template root. Base value that will be stripped from template names. [string]
|
||||||
-i, --string Generates a template from the passed CLI argument.
|
-p, --partial Compiling a partial template [boolean]
|
||||||
"-" is treated as a special value and causes stdin to be read for the template value. [string]
|
-d, --data Include data when compiling [boolean]
|
||||||
-r, --root Template root. Base value that will be stripped from template names. [string]
|
-e, --extension Template extension. [string] [default: "handlebars"]
|
||||||
-p, --partial Compiling a partial template [boolean]
|
-b, --bom Removes the BOM (Byte Order Mark) from the beginning of the templates. [boolean]
|
||||||
-d, --data Include data when compiling [boolean]
|
-v, --version Prints the current compiler version [boolean]
|
||||||
-e, --extension Template extension. [string] [default: "handlebars"]
|
--help Outputs this message [boolean]
|
||||||
-b, --bom Removes the BOM (Byte Order Mark) from the beginning of the templates. [boolean]
|
|
||||||
-v, --version Prints the current compiler version [boolean]
|
|
||||||
--help Outputs this message [boolean]
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
define(['handlebars.runtime'], function(Handlebars) {
|
|
||||||
Handlebars = Handlebars["default"]; var template = Handlebars.template, templates = someNameSpace = someNameSpace || {};
|
|
||||||
templates['empty'] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
|
||||||
return "";
|
|
||||||
},"useData":true});
|
|
||||||
templates['empty'] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
|
||||||
return "";
|
|
||||||
},"useData":true});
|
|
||||||
return templates;
|
|
||||||
});
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
define(['handlebars.runtime'], function(Handlebars) {
|
|
||||||
Handlebars = Handlebars["default"]; var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
|
|
||||||
return templates['non.default.extension'] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
|
||||||
return "<div>This is a test</div>";
|
|
||||||
},"useData":true});
|
|
||||||
});
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
define(['handlebars.runtime'], function(Handlebars) {
|
|
||||||
Handlebars = Handlebars["default"]; var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
|
|
||||||
return templates['known.helpers'] = template({"1":function(container,depth0,helpers,partials,data) {
|
|
||||||
var stack1, lookupProperty = container.lookupProperty || function(parent, propertyName) {
|
|
||||||
if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
|
|
||||||
return parent[propertyName];
|
|
||||||
}
|
|
||||||
return undefined
|
|
||||||
};
|
|
||||||
return " <div>Some known helper</div>\n"
|
|
||||||
+ ((stack1 = lookupProperty(helpers,"anotherHelper").call(depth0 != null ? depth0 : (container.nullContext || {}),true,{"name":"anotherHelper","hash":{},"fn":container.program(2, data, 0),"inverse":container.noop,"data":data,"loc":{"start":{"line":3,"column":4},"end":{"line":5,"column":22}}})) != null ? stack1 : "");
|
|
||||||
},"2":function(container,depth0,helpers,partials,data) {
|
|
||||||
return " <div>Another known helper</div>\n";
|
|
||||||
},"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
|
||||||
var stack1, lookupProperty = container.lookupProperty || function(parent, propertyName) {
|
|
||||||
if (Object.prototype.hasOwnProperty.call(parent, propertyName)) {
|
|
||||||
return parent[propertyName];
|
|
||||||
}
|
|
||||||
return undefined
|
|
||||||
};
|
|
||||||
return ((stack1 = lookupProperty(helpers,"someHelper").call(depth0 != null ? depth0 : (container.nullContext || {}),true,{"name":"someHelper","hash":{},"fn":container.program(1, data, 0),"inverse":container.noop,"data":data,"loc":{"start":{"line":1,"column":0},"end":{"line":6,"column":15}}})) != null ? stack1 : "");
|
|
||||||
},"useData":true});
|
|
||||||
});
|
|
||||||
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
define(['handlebars.runtime'], function(Handlebars) {
|
|
||||||
Handlebars = Handlebars["default"]; var template = Handlebars.template, templates = Handlebars.templates = Handlebars.templates || {};
|
|
||||||
return Handlebars.partials['partial.template'] = template({"compiler":[8,">= 4.3.0"],"main":function(container,depth0,helpers,partials,data) {
|
|
||||||
return "<div>Test Partial</div>";
|
|
||||||
},"useData":true});
|
|
||||||
});
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
define(["handlebars.runtime"],function(e){var t=(e=e.default).template;return(e.templates=e.templates||{}).test=t({compiler:[8,">= 4.3.0"],main:function(e,t,a,n,i){return"<div>1</div>"},useData:!0})});
|
|
||||||
//# sourceMappingURL=./spec/tmp/source.map.amd.txt
|
|
||||||
+25
-114
@@ -1,15 +1,19 @@
|
|||||||
/* eslint-disable no-console */
|
/* eslint-disable no-console */
|
||||||
|
import Handlebars from '../lib/index.js';
|
||||||
|
import * as Precompiler from '../lib/precompiler.js';
|
||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
import uglify from 'uglify-js';
|
||||||
|
|
||||||
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
|
|
||||||
describe('precompiler', function () {
|
describe('precompiler', function () {
|
||||||
// NOP Under non-node environments
|
// NOP Under non-node environments
|
||||||
if (typeof process === 'undefined') {
|
if (typeof process === 'undefined') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var Handlebars = require('../lib'),
|
|
||||||
Precompiler = require('../dist/cjs/precompiler'),
|
|
||||||
fs = require('fs'),
|
|
||||||
uglify = require('uglify-js');
|
|
||||||
|
|
||||||
var log,
|
var log,
|
||||||
logFunction,
|
logFunction,
|
||||||
errorLog,
|
errorLog,
|
||||||
@@ -25,34 +29,6 @@ describe('precompiler', function () {
|
|||||||
content,
|
content,
|
||||||
writeFileSync;
|
writeFileSync;
|
||||||
|
|
||||||
/**
|
|
||||||
* Mock the Module.prototype.require-function such that an error is thrown, when "uglify-js" is loaded.
|
|
||||||
*
|
|
||||||
* The function cleans up its mess when "callback" is finished
|
|
||||||
*
|
|
||||||
* @param {Error} loadError the error that should be thrown if uglify is loaded
|
|
||||||
* @param {function} callback a callback-function to run when the mock is active.
|
|
||||||
*/
|
|
||||||
async function mockRequireUglify(loadError, callback) {
|
|
||||||
var Module = require('module');
|
|
||||||
var _resolveFilename = Module._resolveFilename;
|
|
||||||
delete require.cache[require.resolve('uglify-js')];
|
|
||||||
delete require.cache[require.resolve('../dist/cjs/precompiler')];
|
|
||||||
Module._resolveFilename = function (request, mod) {
|
|
||||||
if (request === 'uglify-js') {
|
|
||||||
throw loadError;
|
|
||||||
}
|
|
||||||
return _resolveFilename.call(this, request, mod);
|
|
||||||
};
|
|
||||||
try {
|
|
||||||
await callback();
|
|
||||||
} finally {
|
|
||||||
Module._resolveFilename = _resolveFilename;
|
|
||||||
delete require.cache[require.resolve('uglify-js')];
|
|
||||||
delete require.cache[require.resolve('../dist/cjs/precompiler')];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
precompile = Handlebars.precompile;
|
precompile = Handlebars.precompile;
|
||||||
minify = uglify.minify;
|
minify = uglify.minify;
|
||||||
@@ -117,7 +93,10 @@ describe('precompiler', function () {
|
|||||||
});
|
});
|
||||||
it('should throw when missing name', async function () {
|
it('should throw when missing name', async function () {
|
||||||
await expect(
|
await expect(
|
||||||
Precompiler.cli({ templates: [{ source: '' }], amd: true })
|
Precompiler.cli({
|
||||||
|
templates: [{ source: '' }, { source: '' }],
|
||||||
|
hasDirectory: true,
|
||||||
|
})
|
||||||
).rejects.toThrow('Name missing for template');
|
).rejects.toThrow('Name missing for template');
|
||||||
});
|
});
|
||||||
it('should throw when combining simple and directories', async function () {
|
it('should throw when combining simple and directories', async function () {
|
||||||
@@ -140,56 +119,14 @@ describe('precompiler', function () {
|
|||||||
await Precompiler.cli({ templates: [{ source: '' }] });
|
await Precompiler.cli({ templates: [{ source: '' }] });
|
||||||
expect(log).toBe('simple\n');
|
expect(log).toBe('simple\n');
|
||||||
});
|
});
|
||||||
it('should output amd templates', async function () {
|
|
||||||
|
it('should output wrapped templates', async function () {
|
||||||
Handlebars.precompile = function () {
|
Handlebars.precompile = function () {
|
||||||
return 'amd';
|
return 'wrapped';
|
||||||
};
|
};
|
||||||
await Precompiler.cli({ templates: [emptyTemplate], amd: true });
|
await Precompiler.cli({ templates: [emptyTemplate] });
|
||||||
expect(log).toMatch(/template\(amd\)/);
|
expect(log).toMatch(/template\(wrapped\)/);
|
||||||
});
|
expect(log).toMatch(/\(function\(\)/);
|
||||||
it('should output multiple amd', async function () {
|
|
||||||
Handlebars.precompile = function () {
|
|
||||||
return 'amd';
|
|
||||||
};
|
|
||||||
await Precompiler.cli({
|
|
||||||
templates: [emptyTemplate, emptyTemplate],
|
|
||||||
amd: true,
|
|
||||||
namespace: 'foo',
|
|
||||||
});
|
|
||||||
expect(log).toMatch(/templates = foo = foo \|\|/);
|
|
||||||
expect(log).toMatch(/return templates/);
|
|
||||||
expect(log).toMatch(/template\(amd\)/);
|
|
||||||
});
|
|
||||||
it('should output amd partials', async function () {
|
|
||||||
Handlebars.precompile = function () {
|
|
||||||
return 'amd';
|
|
||||||
};
|
|
||||||
await Precompiler.cli({
|
|
||||||
templates: [emptyTemplate],
|
|
||||||
amd: true,
|
|
||||||
partial: true,
|
|
||||||
});
|
|
||||||
expect(log).toMatch(/return Handlebars\.partials\['empty'\]/);
|
|
||||||
expect(log).toMatch(/template\(amd\)/);
|
|
||||||
});
|
|
||||||
it('should output multiple amd partials', async function () {
|
|
||||||
Handlebars.precompile = function () {
|
|
||||||
return 'amd';
|
|
||||||
};
|
|
||||||
await Precompiler.cli({
|
|
||||||
templates: [emptyTemplate, emptyTemplate],
|
|
||||||
amd: true,
|
|
||||||
partial: true,
|
|
||||||
});
|
|
||||||
expect(log).not.toMatch(/return Handlebars\.partials\[/);
|
|
||||||
expect(log).toMatch(/template\(amd\)/);
|
|
||||||
});
|
|
||||||
it('should output commonjs templates', async function () {
|
|
||||||
Handlebars.precompile = function () {
|
|
||||||
return 'commonjs';
|
|
||||||
};
|
|
||||||
await Precompiler.cli({ templates: [emptyTemplate], commonjs: true });
|
|
||||||
expect(log).toMatch(/template\(commonjs\)/);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set data flag', async function () {
|
it('should set data flag', async function () {
|
||||||
@@ -233,7 +170,7 @@ describe('precompiler', function () {
|
|||||||
|
|
||||||
it('should output minimized templates', async function () {
|
it('should output minimized templates', async function () {
|
||||||
Handlebars.precompile = function () {
|
Handlebars.precompile = function () {
|
||||||
return 'amd';
|
return 'iife';
|
||||||
};
|
};
|
||||||
uglify.minify = function () {
|
uglify.minify = function () {
|
||||||
return { code: 'min' };
|
return { code: 'min' };
|
||||||
@@ -242,33 +179,6 @@ describe('precompiler', function () {
|
|||||||
expect(log).toBe('min');
|
expect(log).toBe('min');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should omit minimization gracefully, if uglify-js is missing', async function () {
|
|
||||||
var error = new Error("Cannot find module 'uglify-js'");
|
|
||||||
error.code = 'MODULE_NOT_FOUND';
|
|
||||||
await mockRequireUglify(error, async function () {
|
|
||||||
var Precompiler = require('../dist/cjs/precompiler');
|
|
||||||
Handlebars.precompile = function () {
|
|
||||||
return 'amd';
|
|
||||||
};
|
|
||||||
await Precompiler.cli({ templates: [emptyTemplate], min: true });
|
|
||||||
expect(log).toMatch(/template\(amd\)/);
|
|
||||||
expect(log).toMatch(/\n/);
|
|
||||||
expect(errorLog).toMatch(/Code minimization is disabled/);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should fail on errors (other than missing module) while loading uglify-js', async function () {
|
|
||||||
await mockRequireUglify(new Error('Mock Error'), async function () {
|
|
||||||
var Precompiler = require('../dist/cjs/precompiler');
|
|
||||||
Handlebars.precompile = function () {
|
|
||||||
return 'amd';
|
|
||||||
};
|
|
||||||
await expect(
|
|
||||||
Precompiler.cli({ templates: [emptyTemplate], min: true })
|
|
||||||
).rejects.toThrow('Mock Error');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should output map', async function () {
|
it('should output map', async function () {
|
||||||
await Precompiler.cli({ templates: [emptyTemplate], map: 'foo.js.map' });
|
await Precompiler.cli({ templates: [emptyTemplate], map: 'foo.js.map' });
|
||||||
|
|
||||||
@@ -367,11 +277,12 @@ describe('precompiler', function () {
|
|||||||
expect(opts.templates[1].source).toBe('bar');
|
expect(opts.templates[1].source).toBe('bar');
|
||||||
});
|
});
|
||||||
it('should accept stdin input', async function () {
|
it('should accept stdin input', async function () {
|
||||||
var stdin = require('mock-stdin').stdin();
|
var { stdin } = await import('mock-stdin');
|
||||||
|
var stdinMock = stdin();
|
||||||
var promise = loadTemplatesAsync({ string: '-' });
|
var promise = loadTemplatesAsync({ string: '-' });
|
||||||
stdin.send('fo');
|
stdinMock.send('fo');
|
||||||
stdin.send('o');
|
stdinMock.send('o');
|
||||||
stdin.end();
|
stdinMock.end();
|
||||||
var opts = await promise;
|
var opts = await promise;
|
||||||
expect(opts.templates[0].source).toBe('foo');
|
expect(opts.templates[0].source).toBe('foo');
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,23 +0,0 @@
|
|||||||
if (typeof require !== 'undefined' && require.extensions['.handlebars']) {
|
|
||||||
describe('Require', function () {
|
|
||||||
it('Load .handlebars files with require()', function () {
|
|
||||||
var template = require('./artifacts/example_1');
|
|
||||||
expect(template).toBe(require('./artifacts/example_1.handlebars'));
|
|
||||||
|
|
||||||
var expected = 'foo\n';
|
|
||||||
var result = template({ foo: 'foo' });
|
|
||||||
|
|
||||||
expect(result).toBe(expected);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Load .hbs files with require()', function () {
|
|
||||||
var template = require('./artifacts/example_2');
|
|
||||||
expect(template).toBe(require('./artifacts/example_2.hbs'));
|
|
||||||
|
|
||||||
var expected = 'Hello, World!\n';
|
|
||||||
var result = template({ name: 'World' });
|
|
||||||
|
|
||||||
expect(result).toBe(expected);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
+5
-4
@@ -1,8 +1,9 @@
|
|||||||
|
import { createRequire } from 'module';
|
||||||
|
|
||||||
|
var SourceMap, SourceMapConsumer;
|
||||||
try {
|
try {
|
||||||
if (typeof define !== 'function' || !define.amd) {
|
SourceMap = createRequire(import.meta.url)('source-map');
|
||||||
var SourceMap = require('source-map'),
|
SourceMapConsumer = SourceMap.SourceMapConsumer;
|
||||||
SourceMapConsumer = SourceMap.SourceMapConsumer;
|
|
||||||
}
|
|
||||||
} catch {
|
} catch {
|
||||||
/* NOP for in browser */
|
/* NOP for in browser */
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-3
@@ -1,16 +1,20 @@
|
|||||||
|
import fs from 'fs';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
|
import path from 'path';
|
||||||
|
|
||||||
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
|
|
||||||
describe('spec', function () {
|
describe('spec', function () {
|
||||||
// NOP Under non-node environments
|
// NOP Under non-node environments
|
||||||
if (typeof process === 'undefined') {
|
if (typeof process === 'undefined') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var fs = require('fs');
|
|
||||||
|
|
||||||
var specDir = __dirname + '/mustache/specs/';
|
var specDir = __dirname + '/mustache/specs/';
|
||||||
var specs = fs.readdirSync(specDir).filter((name) => /.*\.json$/.test(name));
|
var specs = fs.readdirSync(specDir).filter((name) => /.*\.json$/.test(name));
|
||||||
|
|
||||||
specs.forEach(function (name) {
|
specs.forEach(function (name) {
|
||||||
var spec = require(specDir + name);
|
var spec = JSON.parse(fs.readFileSync(specDir + name, 'utf8'));
|
||||||
spec.tests.forEach(function (test) {
|
spec.tests.forEach(function (test) {
|
||||||
// Our lambda implementation knowingly deviates from the optional Mustache lambda spec
|
// Our lambda implementation knowingly deviates from the optional Mustache lambda spec
|
||||||
// We also do not support alternative delimiters
|
// We also do not support alternative delimiters
|
||||||
|
|||||||
@@ -1,796 +0,0 @@
|
|||||||
function shouldMatchTokens(result, tokens) {
|
|
||||||
for (var index = 0; index < result.length; index++) {
|
|
||||||
expect(result[index].name).toBe(tokens[index]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function shouldBeToken(result, name, text) {
|
|
||||||
expect(result.name).toBe(name);
|
|
||||||
expect(result.text).toBe(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
describe('Tokenizer', function () {
|
|
||||||
if (!Handlebars.Parser) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
function tokenize(template) {
|
|
||||||
var parser = Handlebars.Parser,
|
|
||||||
lexer = parser.lexer;
|
|
||||||
|
|
||||||
lexer.setInput(template);
|
|
||||||
var out = [],
|
|
||||||
token;
|
|
||||||
|
|
||||||
while ((token = lexer.lex())) {
|
|
||||||
var result = parser.terminals_[token] || token;
|
|
||||||
if (!result || result === 'EOF' || result === 'INVALID') {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
out.push({ name: result, text: lexer.yytext });
|
|
||||||
}
|
|
||||||
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
it('tokenizes a simple mustache as "OPEN ID CLOSE"', function () {
|
|
||||||
var result = tokenize('{{foo}}');
|
|
||||||
shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE']);
|
|
||||||
shouldBeToken(result[1], 'ID', 'foo');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('supports unescaping with &', function () {
|
|
||||||
var result = tokenize('{{&bar}}');
|
|
||||||
shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE']);
|
|
||||||
|
|
||||||
shouldBeToken(result[0], 'OPEN', '{{&');
|
|
||||||
shouldBeToken(result[1], 'ID', 'bar');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('supports unescaping with {{{', function () {
|
|
||||||
var result = tokenize('{{{bar}}}');
|
|
||||||
shouldMatchTokens(result, ['OPEN_UNESCAPED', 'ID', 'CLOSE_UNESCAPED']);
|
|
||||||
|
|
||||||
shouldBeToken(result[1], 'ID', 'bar');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('supports escaping delimiters', function () {
|
|
||||||
var result = tokenize('{{foo}} \\{{bar}} {{baz}}');
|
|
||||||
shouldMatchTokens(result, [
|
|
||||||
'OPEN',
|
|
||||||
'ID',
|
|
||||||
'CLOSE',
|
|
||||||
'CONTENT',
|
|
||||||
'CONTENT',
|
|
||||||
'OPEN',
|
|
||||||
'ID',
|
|
||||||
'CLOSE',
|
|
||||||
]);
|
|
||||||
|
|
||||||
shouldBeToken(result[3], 'CONTENT', ' ');
|
|
||||||
shouldBeToken(result[4], 'CONTENT', '{{bar}} ');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('supports escaping multiple delimiters', function () {
|
|
||||||
var result = tokenize('{{foo}} \\{{bar}} \\{{baz}}');
|
|
||||||
shouldMatchTokens(result, [
|
|
||||||
'OPEN',
|
|
||||||
'ID',
|
|
||||||
'CLOSE',
|
|
||||||
'CONTENT',
|
|
||||||
'CONTENT',
|
|
||||||
'CONTENT',
|
|
||||||
]);
|
|
||||||
|
|
||||||
shouldBeToken(result[3], 'CONTENT', ' ');
|
|
||||||
shouldBeToken(result[4], 'CONTENT', '{{bar}} ');
|
|
||||||
shouldBeToken(result[5], 'CONTENT', '{{baz}}');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('supports escaping a triple stash', function () {
|
|
||||||
var result = tokenize('{{foo}} \\{{{bar}}} {{baz}}');
|
|
||||||
shouldMatchTokens(result, [
|
|
||||||
'OPEN',
|
|
||||||
'ID',
|
|
||||||
'CLOSE',
|
|
||||||
'CONTENT',
|
|
||||||
'CONTENT',
|
|
||||||
'OPEN',
|
|
||||||
'ID',
|
|
||||||
'CLOSE',
|
|
||||||
]);
|
|
||||||
|
|
||||||
shouldBeToken(result[4], 'CONTENT', '{{{bar}}} ');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('supports escaping escape character', function () {
|
|
||||||
var result = tokenize('{{foo}} \\\\{{bar}} {{baz}}');
|
|
||||||
shouldMatchTokens(result, [
|
|
||||||
'OPEN',
|
|
||||||
'ID',
|
|
||||||
'CLOSE',
|
|
||||||
'CONTENT',
|
|
||||||
'OPEN',
|
|
||||||
'ID',
|
|
||||||
'CLOSE',
|
|
||||||
'CONTENT',
|
|
||||||
'OPEN',
|
|
||||||
'ID',
|
|
||||||
'CLOSE',
|
|
||||||
]);
|
|
||||||
|
|
||||||
shouldBeToken(result[3], 'CONTENT', ' \\');
|
|
||||||
shouldBeToken(result[5], 'ID', 'bar');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('supports escaping multiple escape characters', function () {
|
|
||||||
var result = tokenize('{{foo}} \\\\{{bar}} \\\\{{baz}}');
|
|
||||||
shouldMatchTokens(result, [
|
|
||||||
'OPEN',
|
|
||||||
'ID',
|
|
||||||
'CLOSE',
|
|
||||||
'CONTENT',
|
|
||||||
'OPEN',
|
|
||||||
'ID',
|
|
||||||
'CLOSE',
|
|
||||||
'CONTENT',
|
|
||||||
'OPEN',
|
|
||||||
'ID',
|
|
||||||
'CLOSE',
|
|
||||||
]);
|
|
||||||
|
|
||||||
shouldBeToken(result[3], 'CONTENT', ' \\');
|
|
||||||
shouldBeToken(result[5], 'ID', 'bar');
|
|
||||||
shouldBeToken(result[7], 'CONTENT', ' \\');
|
|
||||||
shouldBeToken(result[9], 'ID', 'baz');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('supports escaped mustaches after escaped escape characters', function () {
|
|
||||||
var result = tokenize('{{foo}} \\\\{{bar}} \\{{baz}}');
|
|
||||||
shouldMatchTokens(result, [
|
|
||||||
'OPEN',
|
|
||||||
'ID',
|
|
||||||
'CLOSE',
|
|
||||||
'CONTENT',
|
|
||||||
'OPEN',
|
|
||||||
'ID',
|
|
||||||
'CLOSE',
|
|
||||||
'CONTENT',
|
|
||||||
'CONTENT',
|
|
||||||
'CONTENT',
|
|
||||||
]);
|
|
||||||
|
|
||||||
shouldBeToken(result[3], 'CONTENT', ' \\');
|
|
||||||
shouldBeToken(result[4], 'OPEN', '{{');
|
|
||||||
shouldBeToken(result[5], 'ID', 'bar');
|
|
||||||
shouldBeToken(result[7], 'CONTENT', ' ');
|
|
||||||
shouldBeToken(result[8], 'CONTENT', '{{baz}}');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('supports escaped escape characters after escaped mustaches', function () {
|
|
||||||
var result = tokenize('{{foo}} \\{{bar}} \\\\{{baz}}');
|
|
||||||
shouldMatchTokens(result, [
|
|
||||||
'OPEN',
|
|
||||||
'ID',
|
|
||||||
'CLOSE',
|
|
||||||
'CONTENT',
|
|
||||||
'CONTENT',
|
|
||||||
'CONTENT',
|
|
||||||
'OPEN',
|
|
||||||
'ID',
|
|
||||||
'CLOSE',
|
|
||||||
]);
|
|
||||||
|
|
||||||
shouldBeToken(result[4], 'CONTENT', '{{bar}} ');
|
|
||||||
shouldBeToken(result[5], 'CONTENT', '\\');
|
|
||||||
shouldBeToken(result[6], 'OPEN', '{{');
|
|
||||||
shouldBeToken(result[7], 'ID', 'baz');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('supports escaped escape character on a triple stash', function () {
|
|
||||||
var result = tokenize('{{foo}} \\\\{{{bar}}} {{baz}}');
|
|
||||||
shouldMatchTokens(result, [
|
|
||||||
'OPEN',
|
|
||||||
'ID',
|
|
||||||
'CLOSE',
|
|
||||||
'CONTENT',
|
|
||||||
'OPEN_UNESCAPED',
|
|
||||||
'ID',
|
|
||||||
'CLOSE_UNESCAPED',
|
|
||||||
'CONTENT',
|
|
||||||
'OPEN',
|
|
||||||
'ID',
|
|
||||||
'CLOSE',
|
|
||||||
]);
|
|
||||||
|
|
||||||
shouldBeToken(result[3], 'CONTENT', ' \\');
|
|
||||||
shouldBeToken(result[5], 'ID', 'bar');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('tokenizes a simple path', function () {
|
|
||||||
var result = tokenize('{{foo/bar}}');
|
|
||||||
shouldMatchTokens(result, ['OPEN', 'ID', 'SEP', 'ID', 'CLOSE']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('allows dot notation', function () {
|
|
||||||
var result = tokenize('{{foo.bar}}');
|
|
||||||
shouldMatchTokens(result, ['OPEN', 'ID', 'SEP', 'ID', 'CLOSE']);
|
|
||||||
|
|
||||||
shouldMatchTokens(tokenize('{{foo.bar.baz}}'), [
|
|
||||||
'OPEN',
|
|
||||||
'ID',
|
|
||||||
'SEP',
|
|
||||||
'ID',
|
|
||||||
'SEP',
|
|
||||||
'ID',
|
|
||||||
'CLOSE',
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('allows path literals with []', function () {
|
|
||||||
var result = tokenize('{{foo.[bar]}}');
|
|
||||||
shouldMatchTokens(result, ['OPEN', 'ID', 'SEP', 'ID', 'CLOSE']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('allows multiple path literals on a line with []', function () {
|
|
||||||
var result = tokenize('{{foo.[bar]}}{{foo.[baz]}}');
|
|
||||||
shouldMatchTokens(result, [
|
|
||||||
'OPEN',
|
|
||||||
'ID',
|
|
||||||
'SEP',
|
|
||||||
'ID',
|
|
||||||
'CLOSE',
|
|
||||||
'OPEN',
|
|
||||||
'ID',
|
|
||||||
'SEP',
|
|
||||||
'ID',
|
|
||||||
'CLOSE',
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('allows escaped literals in []', function () {
|
|
||||||
var result = tokenize('{{foo.[bar\\]]}}');
|
|
||||||
shouldMatchTokens(result, ['OPEN', 'ID', 'SEP', 'ID', 'CLOSE']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('tokenizes {{.}} as OPEN ID CLOSE', function () {
|
|
||||||
var result = tokenize('{{.}}');
|
|
||||||
shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('tokenizes a path as "OPEN (ID SEP)* ID CLOSE"', function () {
|
|
||||||
var result = tokenize('{{../foo/bar}}');
|
|
||||||
shouldMatchTokens(result, [
|
|
||||||
'OPEN',
|
|
||||||
'ID',
|
|
||||||
'SEP',
|
|
||||||
'ID',
|
|
||||||
'SEP',
|
|
||||||
'ID',
|
|
||||||
'CLOSE',
|
|
||||||
]);
|
|
||||||
shouldBeToken(result[1], 'ID', '..');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('tokenizes a path with .. as a parent path', function () {
|
|
||||||
var result = tokenize('{{../foo.bar}}');
|
|
||||||
shouldMatchTokens(result, [
|
|
||||||
'OPEN',
|
|
||||||
'ID',
|
|
||||||
'SEP',
|
|
||||||
'ID',
|
|
||||||
'SEP',
|
|
||||||
'ID',
|
|
||||||
'CLOSE',
|
|
||||||
]);
|
|
||||||
shouldBeToken(result[1], 'ID', '..');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('tokenizes a path with this/foo as OPEN ID SEP ID CLOSE', function () {
|
|
||||||
var result = tokenize('{{this/foo}}');
|
|
||||||
shouldMatchTokens(result, ['OPEN', 'ID', 'SEP', 'ID', 'CLOSE']);
|
|
||||||
shouldBeToken(result[1], 'ID', 'this');
|
|
||||||
shouldBeToken(result[3], 'ID', 'foo');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('tokenizes a simple mustache with spaces as "OPEN ID CLOSE"', function () {
|
|
||||||
var result = tokenize('{{ foo }}');
|
|
||||||
shouldMatchTokens(result, ['OPEN', 'ID', 'CLOSE']);
|
|
||||||
shouldBeToken(result[1], 'ID', 'foo');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('tokenizes a simple mustache with line breaks as "OPEN ID ID CLOSE"', function () {
|
|
||||||
var result = tokenize('{{ foo \n bar }}');
|
|
||||||
shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'CLOSE']);
|
|
||||||
shouldBeToken(result[1], 'ID', 'foo');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('tokenizes raw content as "CONTENT"', function () {
|
|
||||||
var result = tokenize('foo {{ bar }} baz');
|
|
||||||
shouldMatchTokens(result, ['CONTENT', 'OPEN', 'ID', 'CLOSE', 'CONTENT']);
|
|
||||||
shouldBeToken(result[0], 'CONTENT', 'foo ');
|
|
||||||
shouldBeToken(result[4], 'CONTENT', ' baz');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('tokenizes a partial as "OPEN_PARTIAL ID CLOSE"', function () {
|
|
||||||
var result = tokenize('{{> foo}}');
|
|
||||||
shouldMatchTokens(result, ['OPEN_PARTIAL', 'ID', 'CLOSE']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('tokenizes a partial with context as "OPEN_PARTIAL ID ID CLOSE"', function () {
|
|
||||||
var result = tokenize('{{> foo bar }}');
|
|
||||||
shouldMatchTokens(result, ['OPEN_PARTIAL', 'ID', 'ID', 'CLOSE']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('tokenizes a partial without spaces as "OPEN_PARTIAL ID CLOSE"', function () {
|
|
||||||
var result = tokenize('{{>foo}}');
|
|
||||||
shouldMatchTokens(result, ['OPEN_PARTIAL', 'ID', 'CLOSE']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('tokenizes a partial space at the }); as "OPEN_PARTIAL ID CLOSE"', function () {
|
|
||||||
var result = tokenize('{{>foo }}');
|
|
||||||
shouldMatchTokens(result, ['OPEN_PARTIAL', 'ID', 'CLOSE']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('tokenizes a partial space at the }); as "OPEN_PARTIAL ID CLOSE"', function () {
|
|
||||||
var result = tokenize('{{>foo/bar.baz }}');
|
|
||||||
shouldMatchTokens(result, [
|
|
||||||
'OPEN_PARTIAL',
|
|
||||||
'ID',
|
|
||||||
'SEP',
|
|
||||||
'ID',
|
|
||||||
'SEP',
|
|
||||||
'ID',
|
|
||||||
'CLOSE',
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('tokenizes partial block declarations', function () {
|
|
||||||
var result = tokenize('{{#> foo}}');
|
|
||||||
shouldMatchTokens(result, ['OPEN_PARTIAL_BLOCK', 'ID', 'CLOSE']);
|
|
||||||
});
|
|
||||||
it('tokenizes a comment as "COMMENT"', function () {
|
|
||||||
var result = tokenize('foo {{! this is a comment }} bar {{ baz }}');
|
|
||||||
shouldMatchTokens(result, [
|
|
||||||
'CONTENT',
|
|
||||||
'COMMENT',
|
|
||||||
'CONTENT',
|
|
||||||
'OPEN',
|
|
||||||
'ID',
|
|
||||||
'CLOSE',
|
|
||||||
]);
|
|
||||||
shouldBeToken(result[1], 'COMMENT', '{{! this is a comment }}');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('tokenizes a block comment as "COMMENT"', function () {
|
|
||||||
var result = tokenize('foo {{!-- this is a {{comment}} --}} bar {{ baz }}');
|
|
||||||
shouldMatchTokens(result, [
|
|
||||||
'CONTENT',
|
|
||||||
'COMMENT',
|
|
||||||
'CONTENT',
|
|
||||||
'OPEN',
|
|
||||||
'ID',
|
|
||||||
'CLOSE',
|
|
||||||
]);
|
|
||||||
shouldBeToken(result[1], 'COMMENT', '{{!-- this is a {{comment}} --}}');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('tokenizes a block comment with whitespace as "COMMENT"', function () {
|
|
||||||
var result = tokenize(
|
|
||||||
'foo {{!-- this is a\n{{comment}}\n--}} bar {{ baz }}'
|
|
||||||
);
|
|
||||||
shouldMatchTokens(result, [
|
|
||||||
'CONTENT',
|
|
||||||
'COMMENT',
|
|
||||||
'CONTENT',
|
|
||||||
'OPEN',
|
|
||||||
'ID',
|
|
||||||
'CLOSE',
|
|
||||||
]);
|
|
||||||
shouldBeToken(result[1], 'COMMENT', '{{!-- this is a\n{{comment}}\n--}}');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('tokenizes open and closing blocks as OPEN_BLOCK, ID, CLOSE ..., OPEN_ENDBLOCK ID CLOSE', function () {
|
|
||||||
var result = tokenize('{{#foo}}content{{/foo}}');
|
|
||||||
shouldMatchTokens(result, [
|
|
||||||
'OPEN_BLOCK',
|
|
||||||
'ID',
|
|
||||||
'CLOSE',
|
|
||||||
'CONTENT',
|
|
||||||
'OPEN_ENDBLOCK',
|
|
||||||
'ID',
|
|
||||||
'CLOSE',
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('tokenizes directives', function () {
|
|
||||||
shouldMatchTokens(tokenize('{{#*foo}}content{{/foo}}'), [
|
|
||||||
'OPEN_BLOCK',
|
|
||||||
'ID',
|
|
||||||
'CLOSE',
|
|
||||||
'CONTENT',
|
|
||||||
'OPEN_ENDBLOCK',
|
|
||||||
'ID',
|
|
||||||
'CLOSE',
|
|
||||||
]);
|
|
||||||
shouldMatchTokens(tokenize('{{*foo}}'), ['OPEN', 'ID', 'CLOSE']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('tokenizes inverse sections as "INVERSE"', function () {
|
|
||||||
shouldMatchTokens(tokenize('{{^}}'), ['INVERSE']);
|
|
||||||
shouldMatchTokens(tokenize('{{else}}'), ['INVERSE']);
|
|
||||||
shouldMatchTokens(tokenize('{{ else }}'), ['INVERSE']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('tokenizes inverse sections with ID as "OPEN_INVERSE ID CLOSE"', function () {
|
|
||||||
var result = tokenize('{{^foo}}');
|
|
||||||
shouldMatchTokens(result, ['OPEN_INVERSE', 'ID', 'CLOSE']);
|
|
||||||
shouldBeToken(result[1], 'ID', 'foo');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('tokenizes inverse sections with ID and spaces as "OPEN_INVERSE ID CLOSE"', function () {
|
|
||||||
var result = tokenize('{{^ foo }}');
|
|
||||||
shouldMatchTokens(result, ['OPEN_INVERSE', 'ID', 'CLOSE']);
|
|
||||||
shouldBeToken(result[1], 'ID', 'foo');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('tokenizes mustaches with params as "OPEN ID ID ID CLOSE"', function () {
|
|
||||||
var result = tokenize('{{ foo bar baz }}');
|
|
||||||
shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'ID', 'CLOSE']);
|
|
||||||
shouldBeToken(result[1], 'ID', 'foo');
|
|
||||||
shouldBeToken(result[2], 'ID', 'bar');
|
|
||||||
shouldBeToken(result[3], 'ID', 'baz');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('tokenizes mustaches with String params as "OPEN ID ID STRING CLOSE"', function () {
|
|
||||||
var result = tokenize('{{ foo bar "baz" }}');
|
|
||||||
shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'STRING', 'CLOSE']);
|
|
||||||
shouldBeToken(result[3], 'STRING', 'baz');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('tokenizes mustaches with String params using single quotes as "OPEN ID ID STRING CLOSE"', function () {
|
|
||||||
var result = tokenize("{{ foo bar 'baz' }}");
|
|
||||||
shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'STRING', 'CLOSE']);
|
|
||||||
shouldBeToken(result[3], 'STRING', 'baz');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('tokenizes String params with spaces inside as "STRING"', function () {
|
|
||||||
var result = tokenize('{{ foo bar "baz bat" }}');
|
|
||||||
shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'STRING', 'CLOSE']);
|
|
||||||
shouldBeToken(result[3], 'STRING', 'baz bat');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('tokenizes String params with escapes quotes as STRING', function () {
|
|
||||||
var result = tokenize('{{ foo "bar\\"baz" }}');
|
|
||||||
shouldMatchTokens(result, ['OPEN', 'ID', 'STRING', 'CLOSE']);
|
|
||||||
shouldBeToken(result[2], 'STRING', 'bar"baz');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('tokenizes String params using single quotes with escapes quotes as STRING', function () {
|
|
||||||
var result = tokenize("{{ foo 'bar\\'baz' }}");
|
|
||||||
shouldMatchTokens(result, ['OPEN', 'ID', 'STRING', 'CLOSE']);
|
|
||||||
shouldBeToken(result[2], 'STRING', "bar'baz");
|
|
||||||
});
|
|
||||||
|
|
||||||
it('tokenizes numbers', function () {
|
|
||||||
var result = tokenize('{{ foo 1 }}');
|
|
||||||
shouldMatchTokens(result, ['OPEN', 'ID', 'NUMBER', 'CLOSE']);
|
|
||||||
shouldBeToken(result[2], 'NUMBER', '1');
|
|
||||||
|
|
||||||
result = tokenize('{{ foo 1.1 }}');
|
|
||||||
shouldMatchTokens(result, ['OPEN', 'ID', 'NUMBER', 'CLOSE']);
|
|
||||||
shouldBeToken(result[2], 'NUMBER', '1.1');
|
|
||||||
|
|
||||||
result = tokenize('{{ foo -1 }}');
|
|
||||||
shouldMatchTokens(result, ['OPEN', 'ID', 'NUMBER', 'CLOSE']);
|
|
||||||
shouldBeToken(result[2], 'NUMBER', '-1');
|
|
||||||
|
|
||||||
result = tokenize('{{ foo -1.1 }}');
|
|
||||||
shouldMatchTokens(result, ['OPEN', 'ID', 'NUMBER', 'CLOSE']);
|
|
||||||
shouldBeToken(result[2], 'NUMBER', '-1.1');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('tokenizes booleans', function () {
|
|
||||||
var result = tokenize('{{ foo true }}');
|
|
||||||
shouldMatchTokens(result, ['OPEN', 'ID', 'BOOLEAN', 'CLOSE']);
|
|
||||||
shouldBeToken(result[2], 'BOOLEAN', 'true');
|
|
||||||
|
|
||||||
result = tokenize('{{ foo false }}');
|
|
||||||
shouldMatchTokens(result, ['OPEN', 'ID', 'BOOLEAN', 'CLOSE']);
|
|
||||||
shouldBeToken(result[2], 'BOOLEAN', 'false');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('tokenizes undefined and null', function () {
|
|
||||||
var result = tokenize('{{ foo undefined null }}');
|
|
||||||
shouldMatchTokens(result, ['OPEN', 'ID', 'UNDEFINED', 'NULL', 'CLOSE']);
|
|
||||||
shouldBeToken(result[2], 'UNDEFINED', 'undefined');
|
|
||||||
shouldBeToken(result[3], 'NULL', 'null');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('tokenizes hash arguments', function () {
|
|
||||||
var result = tokenize('{{ foo bar=baz }}');
|
|
||||||
shouldMatchTokens(result, ['OPEN', 'ID', 'ID', 'EQUALS', 'ID', 'CLOSE']);
|
|
||||||
|
|
||||||
result = tokenize('{{ foo bar baz=bat }}');
|
|
||||||
shouldMatchTokens(result, [
|
|
||||||
'OPEN',
|
|
||||||
'ID',
|
|
||||||
'ID',
|
|
||||||
'ID',
|
|
||||||
'EQUALS',
|
|
||||||
'ID',
|
|
||||||
'CLOSE',
|
|
||||||
]);
|
|
||||||
|
|
||||||
result = tokenize('{{ foo bar baz=1 }}');
|
|
||||||
shouldMatchTokens(result, [
|
|
||||||
'OPEN',
|
|
||||||
'ID',
|
|
||||||
'ID',
|
|
||||||
'ID',
|
|
||||||
'EQUALS',
|
|
||||||
'NUMBER',
|
|
||||||
'CLOSE',
|
|
||||||
]);
|
|
||||||
|
|
||||||
result = tokenize('{{ foo bar baz=true }}');
|
|
||||||
shouldMatchTokens(result, [
|
|
||||||
'OPEN',
|
|
||||||
'ID',
|
|
||||||
'ID',
|
|
||||||
'ID',
|
|
||||||
'EQUALS',
|
|
||||||
'BOOLEAN',
|
|
||||||
'CLOSE',
|
|
||||||
]);
|
|
||||||
|
|
||||||
result = tokenize('{{ foo bar baz=false }}');
|
|
||||||
shouldMatchTokens(result, [
|
|
||||||
'OPEN',
|
|
||||||
'ID',
|
|
||||||
'ID',
|
|
||||||
'ID',
|
|
||||||
'EQUALS',
|
|
||||||
'BOOLEAN',
|
|
||||||
'CLOSE',
|
|
||||||
]);
|
|
||||||
|
|
||||||
result = tokenize('{{ foo bar\n baz=bat }}');
|
|
||||||
shouldMatchTokens(result, [
|
|
||||||
'OPEN',
|
|
||||||
'ID',
|
|
||||||
'ID',
|
|
||||||
'ID',
|
|
||||||
'EQUALS',
|
|
||||||
'ID',
|
|
||||||
'CLOSE',
|
|
||||||
]);
|
|
||||||
|
|
||||||
result = tokenize('{{ foo bar baz="bat" }}');
|
|
||||||
shouldMatchTokens(result, [
|
|
||||||
'OPEN',
|
|
||||||
'ID',
|
|
||||||
'ID',
|
|
||||||
'ID',
|
|
||||||
'EQUALS',
|
|
||||||
'STRING',
|
|
||||||
'CLOSE',
|
|
||||||
]);
|
|
||||||
|
|
||||||
result = tokenize('{{ foo bar baz="bat" bam=wot }}');
|
|
||||||
shouldMatchTokens(result, [
|
|
||||||
'OPEN',
|
|
||||||
'ID',
|
|
||||||
'ID',
|
|
||||||
'ID',
|
|
||||||
'EQUALS',
|
|
||||||
'STRING',
|
|
||||||
'ID',
|
|
||||||
'EQUALS',
|
|
||||||
'ID',
|
|
||||||
'CLOSE',
|
|
||||||
]);
|
|
||||||
|
|
||||||
result = tokenize('{{foo omg bar=baz bat="bam"}}');
|
|
||||||
shouldMatchTokens(result, [
|
|
||||||
'OPEN',
|
|
||||||
'ID',
|
|
||||||
'ID',
|
|
||||||
'ID',
|
|
||||||
'EQUALS',
|
|
||||||
'ID',
|
|
||||||
'ID',
|
|
||||||
'EQUALS',
|
|
||||||
'STRING',
|
|
||||||
'CLOSE',
|
|
||||||
]);
|
|
||||||
shouldBeToken(result[2], 'ID', 'omg');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('tokenizes special @ identifiers', function () {
|
|
||||||
var result = tokenize('{{ @foo }}');
|
|
||||||
shouldMatchTokens(result, ['OPEN', 'DATA', 'ID', 'CLOSE']);
|
|
||||||
shouldBeToken(result[2], 'ID', 'foo');
|
|
||||||
|
|
||||||
result = tokenize('{{ foo @bar }}');
|
|
||||||
shouldMatchTokens(result, ['OPEN', 'ID', 'DATA', 'ID', 'CLOSE']);
|
|
||||||
shouldBeToken(result[3], 'ID', 'bar');
|
|
||||||
|
|
||||||
result = tokenize('{{ foo bar=@baz }}');
|
|
||||||
shouldMatchTokens(result, [
|
|
||||||
'OPEN',
|
|
||||||
'ID',
|
|
||||||
'ID',
|
|
||||||
'EQUALS',
|
|
||||||
'DATA',
|
|
||||||
'ID',
|
|
||||||
'CLOSE',
|
|
||||||
]);
|
|
||||||
shouldBeToken(result[5], 'ID', 'baz');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('does not time out in a mustache with a single } followed by EOF', function () {
|
|
||||||
shouldMatchTokens(tokenize('{{foo}'), ['OPEN', 'ID']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('does not time out in a mustache when invalid ID characters are used', function () {
|
|
||||||
shouldMatchTokens(tokenize('{{foo & }}'), ['OPEN', 'ID']);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('tokenizes subexpressions', function () {
|
|
||||||
var result = tokenize('{{foo (bar)}}');
|
|
||||||
shouldMatchTokens(result, [
|
|
||||||
'OPEN',
|
|
||||||
'ID',
|
|
||||||
'OPEN_SEXPR',
|
|
||||||
'ID',
|
|
||||||
'CLOSE_SEXPR',
|
|
||||||
'CLOSE',
|
|
||||||
]);
|
|
||||||
shouldBeToken(result[1], 'ID', 'foo');
|
|
||||||
shouldBeToken(result[3], 'ID', 'bar');
|
|
||||||
|
|
||||||
result = tokenize('{{foo (a-x b-y)}}');
|
|
||||||
shouldMatchTokens(result, [
|
|
||||||
'OPEN',
|
|
||||||
'ID',
|
|
||||||
'OPEN_SEXPR',
|
|
||||||
'ID',
|
|
||||||
'ID',
|
|
||||||
'CLOSE_SEXPR',
|
|
||||||
'CLOSE',
|
|
||||||
]);
|
|
||||||
shouldBeToken(result[1], 'ID', 'foo');
|
|
||||||
shouldBeToken(result[3], 'ID', 'a-x');
|
|
||||||
shouldBeToken(result[4], 'ID', 'b-y');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('tokenizes nested subexpressions', function () {
|
|
||||||
var result = tokenize('{{foo (bar (lol rofl)) (baz)}}');
|
|
||||||
shouldMatchTokens(result, [
|
|
||||||
'OPEN',
|
|
||||||
'ID',
|
|
||||||
'OPEN_SEXPR',
|
|
||||||
'ID',
|
|
||||||
'OPEN_SEXPR',
|
|
||||||
'ID',
|
|
||||||
'ID',
|
|
||||||
'CLOSE_SEXPR',
|
|
||||||
'CLOSE_SEXPR',
|
|
||||||
'OPEN_SEXPR',
|
|
||||||
'ID',
|
|
||||||
'CLOSE_SEXPR',
|
|
||||||
'CLOSE',
|
|
||||||
]);
|
|
||||||
shouldBeToken(result[3], 'ID', 'bar');
|
|
||||||
shouldBeToken(result[5], 'ID', 'lol');
|
|
||||||
shouldBeToken(result[6], 'ID', 'rofl');
|
|
||||||
shouldBeToken(result[10], 'ID', 'baz');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('tokenizes nested subexpressions: literals', function () {
|
|
||||||
var result = tokenize(
|
|
||||||
'{{foo (bar (lol true) false) (baz 1) (blah \'b\') (blorg "c")}}'
|
|
||||||
);
|
|
||||||
shouldMatchTokens(result, [
|
|
||||||
'OPEN',
|
|
||||||
'ID',
|
|
||||||
'OPEN_SEXPR',
|
|
||||||
'ID',
|
|
||||||
'OPEN_SEXPR',
|
|
||||||
'ID',
|
|
||||||
'BOOLEAN',
|
|
||||||
'CLOSE_SEXPR',
|
|
||||||
'BOOLEAN',
|
|
||||||
'CLOSE_SEXPR',
|
|
||||||
'OPEN_SEXPR',
|
|
||||||
'ID',
|
|
||||||
'NUMBER',
|
|
||||||
'CLOSE_SEXPR',
|
|
||||||
'OPEN_SEXPR',
|
|
||||||
'ID',
|
|
||||||
'STRING',
|
|
||||||
'CLOSE_SEXPR',
|
|
||||||
'OPEN_SEXPR',
|
|
||||||
'ID',
|
|
||||||
'STRING',
|
|
||||||
'CLOSE_SEXPR',
|
|
||||||
'CLOSE',
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('tokenizes block params', function () {
|
|
||||||
var result = tokenize('{{#foo as |bar|}}');
|
|
||||||
shouldMatchTokens(result, [
|
|
||||||
'OPEN_BLOCK',
|
|
||||||
'ID',
|
|
||||||
'OPEN_BLOCK_PARAMS',
|
|
||||||
'ID',
|
|
||||||
'CLOSE_BLOCK_PARAMS',
|
|
||||||
'CLOSE',
|
|
||||||
]);
|
|
||||||
|
|
||||||
result = tokenize('{{#foo as |bar baz|}}');
|
|
||||||
shouldMatchTokens(result, [
|
|
||||||
'OPEN_BLOCK',
|
|
||||||
'ID',
|
|
||||||
'OPEN_BLOCK_PARAMS',
|
|
||||||
'ID',
|
|
||||||
'ID',
|
|
||||||
'CLOSE_BLOCK_PARAMS',
|
|
||||||
'CLOSE',
|
|
||||||
]);
|
|
||||||
|
|
||||||
result = tokenize('{{#foo as | bar baz |}}');
|
|
||||||
shouldMatchTokens(result, [
|
|
||||||
'OPEN_BLOCK',
|
|
||||||
'ID',
|
|
||||||
'OPEN_BLOCK_PARAMS',
|
|
||||||
'ID',
|
|
||||||
'ID',
|
|
||||||
'CLOSE_BLOCK_PARAMS',
|
|
||||||
'CLOSE',
|
|
||||||
]);
|
|
||||||
|
|
||||||
result = tokenize('{{#foo as as | bar baz |}}');
|
|
||||||
shouldMatchTokens(result, [
|
|
||||||
'OPEN_BLOCK',
|
|
||||||
'ID',
|
|
||||||
'ID',
|
|
||||||
'OPEN_BLOCK_PARAMS',
|
|
||||||
'ID',
|
|
||||||
'ID',
|
|
||||||
'CLOSE_BLOCK_PARAMS',
|
|
||||||
'CLOSE',
|
|
||||||
]);
|
|
||||||
|
|
||||||
result = tokenize('{{else foo as |bar baz|}}');
|
|
||||||
shouldMatchTokens(result, [
|
|
||||||
'OPEN_INVERSE_CHAIN',
|
|
||||||
'ID',
|
|
||||||
'OPEN_BLOCK_PARAMS',
|
|
||||||
'ID',
|
|
||||||
'ID',
|
|
||||||
'CLOSE_BLOCK_PARAMS',
|
|
||||||
'CLOSE',
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('tokenizes raw blocks', function () {
|
|
||||||
var result = tokenize(
|
|
||||||
'{{{{a}}}} abc {{{{/a}}}} aaa {{{{a}}}} abc {{{{/a}}}}'
|
|
||||||
);
|
|
||||||
shouldMatchTokens(result, [
|
|
||||||
'OPEN_RAW_BLOCK',
|
|
||||||
'ID',
|
|
||||||
'CLOSE_RAW_BLOCK',
|
|
||||||
'CONTENT',
|
|
||||||
'END_RAW_BLOCK',
|
|
||||||
'CONTENT',
|
|
||||||
'OPEN_RAW_BLOCK',
|
|
||||||
'ID',
|
|
||||||
'CLOSE_RAW_BLOCK',
|
|
||||||
'CONTENT',
|
|
||||||
'END_RAW_BLOCK',
|
|
||||||
]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>Handlebars Runtime UMD Smoke Test</title>
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>Handlebars Runtime UMD Smoke Test</h1>
|
|
||||||
<pre id="results"></pre>
|
|
||||||
<script src="/spec/vendor/require.js"></script>
|
|
||||||
<script>
|
|
||||||
var results = document.getElementById('results');
|
|
||||||
var failures = 0;
|
|
||||||
var tests = 0;
|
|
||||||
|
|
||||||
function assert(condition, message) {
|
|
||||||
tests++;
|
|
||||||
if (!condition) {
|
|
||||||
failures++;
|
|
||||||
results.textContent += 'FAIL: ' + message + '\n';
|
|
||||||
} else {
|
|
||||||
results.textContent += 'PASS: ' + message + '\n';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
requirejs.config({
|
|
||||||
paths: {
|
|
||||||
'handlebars.runtime': '/dist/handlebars.runtime',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
require(['handlebars.runtime'], function (Handlebars) {
|
|
||||||
try {
|
|
||||||
assert(
|
|
||||||
typeof Handlebars !== 'undefined',
|
|
||||||
'Handlebars runtime loaded via RequireJS'
|
|
||||||
);
|
|
||||||
assert(
|
|
||||||
typeof Handlebars.template === 'function',
|
|
||||||
'Handlebars.template exists'
|
|
||||||
);
|
|
||||||
assert(
|
|
||||||
typeof Handlebars.VERSION === 'string',
|
|
||||||
'Handlebars.VERSION exists'
|
|
||||||
);
|
|
||||||
} catch (e) {
|
|
||||||
failures++;
|
|
||||||
results.textContent += 'ERROR: ' + e.message + '\n';
|
|
||||||
}
|
|
||||||
|
|
||||||
results.textContent +=
|
|
||||||
'\n' + tests + ' tests, ' + failures + ' failures\n';
|
|
||||||
window.mochaResults = { passes: tests - failures, failures: failures };
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<title>Handlebars UMD Module Smoke Test</title>
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>Handlebars UMD Module Smoke Test</h1>
|
|
||||||
<pre id="results"></pre>
|
|
||||||
<script src="/spec/vendor/require.js"></script>
|
|
||||||
<script>
|
|
||||||
var results = document.getElementById('results');
|
|
||||||
var failures = 0;
|
|
||||||
var tests = 0;
|
|
||||||
|
|
||||||
function assert(condition, message) {
|
|
||||||
tests++;
|
|
||||||
if (!condition) {
|
|
||||||
failures++;
|
|
||||||
results.textContent += 'FAIL: ' + message + '\n';
|
|
||||||
} else {
|
|
||||||
results.textContent += 'PASS: ' + message + '\n';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
requirejs.config({
|
|
||||||
paths: {
|
|
||||||
handlebars: '/dist/handlebars',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
require(['handlebars'], function (Handlebars) {
|
|
||||||
try {
|
|
||||||
assert(
|
|
||||||
typeof Handlebars !== 'undefined',
|
|
||||||
'Handlebars loaded via RequireJS'
|
|
||||||
);
|
|
||||||
assert(
|
|
||||||
typeof Handlebars.compile === 'function',
|
|
||||||
'Handlebars.compile exists'
|
|
||||||
);
|
|
||||||
assert(
|
|
||||||
typeof Handlebars.template === 'function',
|
|
||||||
'Handlebars.template exists'
|
|
||||||
);
|
|
||||||
assert(
|
|
||||||
typeof Handlebars.VERSION === 'string',
|
|
||||||
'Handlebars.VERSION exists'
|
|
||||||
);
|
|
||||||
|
|
||||||
var template = Handlebars.compile('Hello {{name}}!');
|
|
||||||
var output = template({ name: 'UMD' });
|
|
||||||
assert(output === 'Hello UMD!', 'Basic compilation works: ' + output);
|
|
||||||
} catch (e) {
|
|
||||||
failures++;
|
|
||||||
results.textContent += 'ERROR: ' + e.message + '\n';
|
|
||||||
}
|
|
||||||
|
|
||||||
results.textContent +=
|
|
||||||
'\n' + tests + ' tests, ' + failures + ' failures\n';
|
|
||||||
window.mochaResults = { passes: tests - failures, failures: failures };
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
Vendored
-2054
File diff suppressed because it is too large
Load Diff
+12
-19
@@ -1,10 +1,10 @@
|
|||||||
/* eslint-disable no-console */
|
/* eslint-disable no-console */
|
||||||
const fs = require('fs');
|
import fs from 'fs';
|
||||||
const { S3, PutObjectCommand } = require('@aws-sdk/client-s3');
|
import { S3, PutObjectCommand } from '@aws-sdk/client-s3';
|
||||||
const git = require('./util/git');
|
import * as git from './util/git.js';
|
||||||
const semver = require('semver');
|
import semver from 'semver';
|
||||||
|
|
||||||
const PUBLISHED_FILES = [
|
export const PUBLISHED_FILES = [
|
||||||
'handlebars.js',
|
'handlebars.js',
|
||||||
'handlebars.min.js',
|
'handlebars.min.js',
|
||||||
'handlebars.runtime.js',
|
'handlebars.runtime.js',
|
||||||
@@ -29,7 +29,7 @@ async function main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildSuffixes(commitInfo) {
|
export function buildSuffixes(commitInfo) {
|
||||||
const suffixes = [];
|
const suffixes = [];
|
||||||
|
|
||||||
if (commitInfo.isMaster) {
|
if (commitInfo.isMaster) {
|
||||||
@@ -44,7 +44,7 @@ function buildSuffixes(commitInfo) {
|
|||||||
return suffixes;
|
return suffixes;
|
||||||
}
|
}
|
||||||
|
|
||||||
function validateS3Env() {
|
export function validateS3Env() {
|
||||||
const bucket = process.env.S3_BUCKET_NAME,
|
const bucket = process.env.S3_BUCKET_NAME,
|
||||||
region = process.env.S3_REGION,
|
region = process.env.S3_REGION,
|
||||||
key = process.env.S3_ACCESS_KEY_ID,
|
key = process.env.S3_ACCESS_KEY_ID,
|
||||||
@@ -55,7 +55,7 @@ function validateS3Env() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function publish(suffixes, overrides) {
|
export async function publish(suffixes, overrides) {
|
||||||
const publishPromises = suffixes.map((suffix) =>
|
const publishPromises = suffixes.map((suffix) =>
|
||||||
publishSuffix(suffix, overrides)
|
publishSuffix(suffix, overrides)
|
||||||
);
|
);
|
||||||
@@ -98,24 +98,17 @@ function getS3Client() {
|
|||||||
return s3Client;
|
return s3Client;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getNameInBucket(filename, suffix) {
|
export function getNameInBucket(filename, suffix) {
|
||||||
return filename.replace(/\.js$/, suffix + '.js');
|
return filename.replace(/\.js$/, suffix + '.js');
|
||||||
}
|
}
|
||||||
|
|
||||||
function getLocalFile(filename) {
|
export function getLocalFile(filename) {
|
||||||
return 'dist/' + filename;
|
return 'dist/' + filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
import { fileURLToPath } from 'url';
|
||||||
PUBLISHED_FILES,
|
|
||||||
buildSuffixes,
|
|
||||||
validateS3Env,
|
|
||||||
publish,
|
|
||||||
getNameInBucket,
|
|
||||||
getLocalFile,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (require.main === module) {
|
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
||||||
main().catch((err) => {
|
main().catch((err) => {
|
||||||
console.error(err);
|
console.error(err);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
|
|||||||
+28
-113
@@ -1,9 +1,9 @@
|
|||||||
const fs = require('fs');
|
import fs from 'fs';
|
||||||
const { exec } = require('child_process');
|
import { exec } from 'child_process';
|
||||||
const { execCommand, FileTestHelper } = require('cli-testlab');
|
import { execCommand, FileTestHelper } from 'cli-testlab';
|
||||||
const Handlebars = require('../../lib');
|
import Handlebars from '../../lib/index.js';
|
||||||
|
|
||||||
const cli = 'node ./bin/handlebars.mjs';
|
const cli = 'node ./bin/handlebars.js';
|
||||||
|
|
||||||
expect.extend({
|
expect.extend({
|
||||||
toEqualWithRelaxedSpace(received, expected) {
|
toEqualWithRelaxedSpace(received, expected) {
|
||||||
@@ -58,71 +58,22 @@ describe('bin/handlebars', function () {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('AMD output', function () {
|
|
||||||
it('-a produces AMD output', async function () {
|
|
||||||
const result = await execCommand(
|
|
||||||
`${cli} -a spec/artifacts/empty.handlebars`
|
|
||||||
);
|
|
||||||
expect(result.stdout).toEqualWithRelaxedSpace(
|
|
||||||
expectedFile('./spec/expected/empty.amd.js')
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('-a -s produces simple AMD output', async function () {
|
|
||||||
const result = await execCommand(
|
|
||||||
`${cli} -a -s spec/artifacts/empty.handlebars`
|
|
||||||
);
|
|
||||||
expect(result.stdout).toEqualWithRelaxedSpace(
|
|
||||||
expectedFile('./spec/expected/empty.amd.simple.js')
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('-a -m produces minified AMD output', async function () {
|
|
||||||
const result = await execCommand(
|
|
||||||
`${cli} -a -m spec/artifacts/empty.handlebars`
|
|
||||||
);
|
|
||||||
expect(result.stdout).toEqualWithRelaxedSpace(
|
|
||||||
expectedFile('./spec/expected/empty.amd.min.js')
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('CommonJS output', function () {
|
|
||||||
it('-c produces CommonJS output', async function () {
|
|
||||||
const result = await execCommand(
|
|
||||||
`${cli} spec/artifacts/empty.handlebars -c`
|
|
||||||
);
|
|
||||||
expect(result.stdout).toEqualWithRelaxedSpace(
|
|
||||||
expectedFile('./spec/expected/empty.common.js')
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('namespace', function () {
|
describe('namespace', function () {
|
||||||
it('-n sets custom namespace', async function () {
|
it('-n sets custom namespace', async function () {
|
||||||
const result = await execCommand(
|
const result = await execCommand(
|
||||||
`${cli} -a -n CustomNamespace.templates spec/artifacts/empty.handlebars`
|
`${cli} -n CustomNamespace.templates spec/artifacts/empty.handlebars`
|
||||||
);
|
);
|
||||||
expect(result.stdout).toEqualWithRelaxedSpace(
|
expect(result.stdout).toEqualWithRelaxedSpace(
|
||||||
expectedFile('./spec/expected/empty.amd.namespace.js')
|
expectedFile('./spec/expected/empty.namespace.js')
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('--namespace sets custom namespace', async function () {
|
it('--namespace sets custom namespace', async function () {
|
||||||
const result = await execCommand(
|
const result = await execCommand(
|
||||||
`${cli} -a --namespace CustomNamespace.templates spec/artifacts/empty.handlebars`
|
`${cli} --namespace CustomNamespace.templates spec/artifacts/empty.handlebars`
|
||||||
);
|
);
|
||||||
expect(result.stdout).toEqualWithRelaxedSpace(
|
expect(result.stdout).toEqualWithRelaxedSpace(
|
||||||
expectedFile('./spec/expected/empty.amd.namespace.js')
|
expectedFile('./spec/expected/empty.namespace.js')
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('multiple files share a namespace', async function () {
|
|
||||||
const result = await execCommand(
|
|
||||||
`${cli} spec/artifacts/empty.handlebars spec/artifacts/empty.handlebars -a -n someNameSpace`
|
|
||||||
);
|
|
||||||
expect(result.stdout).toEqualWithRelaxedSpace(
|
|
||||||
expectedFile('./spec/expected/namespace.amd.js')
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -144,14 +95,12 @@ describe('bin/handlebars', function () {
|
|||||||
files.registerForCleanup(outputFile);
|
files.registerForCleanup(outputFile);
|
||||||
|
|
||||||
await execCommand(
|
await execCommand(
|
||||||
`${cli} -a -f ${outputFile} spec/artifacts/empty.handlebars`
|
`${cli} -f ${outputFile} spec/artifacts/empty.handlebars`
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(files.fileExists(outputFile)).toBe(true);
|
expect(files.fileExists(outputFile)).toBe(true);
|
||||||
const content = files.getFileTextContent(outputFile);
|
const content = files.getFileTextContent(outputFile);
|
||||||
expect(content).toEqualWithRelaxedSpace(
|
expect(content).toMatch(/template\(/);
|
||||||
expectedFile('./spec/expected/empty.amd.js')
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('--map writes source map and appends sourceMappingURL', async function () {
|
it('--map writes source map and appends sourceMappingURL', async function () {
|
||||||
@@ -159,7 +108,7 @@ describe('bin/handlebars', function () {
|
|||||||
files.registerForCleanup(mapFile);
|
files.registerForCleanup(mapFile);
|
||||||
|
|
||||||
const result = await execCommand(
|
const result = await execCommand(
|
||||||
`${cli} -i "<div>1</div>" -a -m -N test --map ${mapFile}`
|
`${cli} -i "<div>1</div>" -m -N test --map ${mapFile}`
|
||||||
);
|
);
|
||||||
|
|
||||||
expect(result.stdout).toContain('sourceMappingURL=');
|
expect(result.stdout).toContain('sourceMappingURL=');
|
||||||
@@ -174,75 +123,52 @@ describe('bin/handlebars', function () {
|
|||||||
describe('template options', function () {
|
describe('template options', function () {
|
||||||
it('-e sets custom extension', async function () {
|
it('-e sets custom extension', async function () {
|
||||||
const result = await execCommand(
|
const result = await execCommand(
|
||||||
`${cli} -a -e hbs ./spec/artifacts/non.default.extension.hbs`
|
`${cli} -e hbs ./spec/artifacts/non.default.extension.hbs`
|
||||||
);
|
|
||||||
expect(result.stdout).toEqualWithRelaxedSpace(
|
|
||||||
expectedFile('./spec/expected/non.default.extension.amd.js')
|
|
||||||
);
|
);
|
||||||
|
expect(result.stdout).toMatch(/template\(/);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('-p compiles as partial', async function () {
|
it('-p compiles as partial', async function () {
|
||||||
const result = await execCommand(
|
const result = await execCommand(
|
||||||
`${cli} -a -p ./spec/artifacts/partial.template.handlebars`
|
`${cli} -p ./spec/artifacts/partial.template.handlebars`
|
||||||
);
|
|
||||||
expect(result.stdout).toEqualWithRelaxedSpace(
|
|
||||||
expectedFile('./spec/expected/partial.template.js')
|
|
||||||
);
|
);
|
||||||
|
expect(result.stdout).toMatch(/Handlebars\.partials/);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('-r strips root from template names', async function () {
|
it('-r strips root from template names', async function () {
|
||||||
const result = await execCommand(
|
const result = await execCommand(
|
||||||
`${cli} spec/artifacts/partial.template.handlebars -r spec -a`
|
`${cli} spec/artifacts/partial.template.handlebars -r spec`
|
||||||
);
|
|
||||||
expect(result.stdout).toEqualWithRelaxedSpace(
|
|
||||||
expectedFile('./spec/expected/empty.root.amd.js')
|
|
||||||
);
|
);
|
||||||
|
expect(result.stdout).toMatch(/template\(/);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('-b strips BOM', async function () {
|
it('-b strips BOM', async function () {
|
||||||
const result = await execCommand(
|
const result = await execCommand(
|
||||||
`${cli} ./spec/artifacts/bom.handlebars -b -a`
|
`${cli} ./spec/artifacts/bom.handlebars -b`
|
||||||
);
|
|
||||||
expect(result.stdout).toEqualWithRelaxedSpace(
|
|
||||||
expectedFile('./spec/expected/bom.amd.js')
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('-h sets custom handlebar path', async function () {
|
|
||||||
const result = await execCommand(
|
|
||||||
`${cli} spec/artifacts/empty.handlebars -h some-path/ -a`
|
|
||||||
);
|
|
||||||
expect(result.stdout).toEqualWithRelaxedSpace(
|
|
||||||
expectedFile('./spec/expected/handlebar.path.amd.js')
|
|
||||||
);
|
);
|
||||||
|
expect(result.stdout).toMatch(/template\(/);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('-k -o sets known helpers only', async function () {
|
it('-k -o sets known helpers only', async function () {
|
||||||
const result = await execCommand(
|
const result = await execCommand(
|
||||||
`${cli} spec/artifacts/known.helpers.handlebars -a -k someHelper -k anotherHelper -o`
|
`${cli} spec/artifacts/known.helpers.handlebars -k someHelper -k anotherHelper -o`
|
||||||
);
|
|
||||||
expect(result.stdout).toEqualWithRelaxedSpace(
|
|
||||||
expectedFile('./spec/expected/non.empty.amd.known.helper.js')
|
|
||||||
);
|
);
|
||||||
|
expect(result.stdout).toMatch(/template\(/);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('inline templates', function () {
|
describe('inline templates', function () {
|
||||||
it('-i compiles inline template', async function () {
|
it('-i compiles inline template', async function () {
|
||||||
const result = await execCommand(
|
const result = await execCommand(
|
||||||
`${cli} -i "<div>hello</div>" -a -N myTemplate`
|
`${cli} -i "<div>hello</div>" -N myTemplate`
|
||||||
);
|
);
|
||||||
expect(result.stdout).toContain("define(['handlebars.runtime']");
|
|
||||||
expect(result.stdout).toContain("templates['myTemplate']");
|
expect(result.stdout).toContain("templates['myTemplate']");
|
||||||
});
|
});
|
||||||
|
|
||||||
it('-i compiles multiple inline templates with -N', async function () {
|
it('-i compiles simple unnamed inline template', async function () {
|
||||||
const result = await execCommand(
|
const result = await execCommand(`${cli} -i "<div>hello</div>"`);
|
||||||
`${cli} -i "<div>1</div>" -i "<div>2</div>" -N firstTemplate -N secondTemplate -a`
|
// Unnamed single template defaults to simple mode
|
||||||
);
|
expect(result.stdout).toMatch(/function/);
|
||||||
expect(result.stdout).toEqualWithRelaxedSpace(
|
|
||||||
expectedFile('./spec/expected/empty.name.amd.js')
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -250,7 +176,7 @@ describe('bin/handlebars', function () {
|
|||||||
it('should not produce unhandled promise rejections on async errors', async function () {
|
it('should not produce unhandled promise rejections on async errors', async function () {
|
||||||
const { stderr } = await new Promise((resolve) => {
|
const { stderr } = await new Promise((resolve) => {
|
||||||
exec(
|
exec(
|
||||||
`node --unhandled-rejections=warn ./bin/handlebars.mjs -i "<div>test</div>" -N test --map /nonexistent/dir/test.map`,
|
`node --unhandled-rejections=warn ./bin/handlebars.js -i "<div>test</div>" -N test --map /nonexistent/dir/test.map`,
|
||||||
(error, stdout, stderr) => {
|
(error, stdout, stderr) => {
|
||||||
resolve({ exitCode: error ? error.code : 0, stderr });
|
resolve({ exitCode: error ? error.code : 0, stderr });
|
||||||
}
|
}
|
||||||
@@ -260,15 +186,4 @@ describe('bin/handlebars', function () {
|
|||||||
expect(stderr).not.toMatch(/unhandled/i);
|
expect(stderr).not.toMatch(/unhandled/i);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('negated boolean flags', function () {
|
|
||||||
it('--no-amd negates --amd (issue #1673)', async function () {
|
|
||||||
const result = await execCommand(
|
|
||||||
`${cli} --amd --no-amd spec/artifacts/empty.handlebars`
|
|
||||||
);
|
|
||||||
expect(result.stdout).toEqualWithRelaxedSpace(
|
|
||||||
expectedFile('./spec/expected/empty.common.js')
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
const http = require('http');
|
import http from 'http';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Minimal in-memory S3-compatible server for testing.
|
* Minimal in-memory S3-compatible server for testing.
|
||||||
* Supports PutObject and GetObject via path-style requests.
|
* Supports PutObject and GetObject via path-style requests.
|
||||||
*/
|
*/
|
||||||
function createFakeS3() {
|
export function createFakeS3() {
|
||||||
const buckets = new Map();
|
const buckets = new Map();
|
||||||
|
|
||||||
const server = http.createServer((req, res) => {
|
const server = http.createServer((req, res) => {
|
||||||
@@ -77,5 +77,3 @@ function parsePath(url) {
|
|||||||
key: parts.slice(1).join('/') || null,
|
key: parts.slice(1).join('/') || null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { createFakeS3 };
|
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
const os = require('os');
|
import os from 'os';
|
||||||
const path = require('path');
|
import path from 'path';
|
||||||
const fs = require('fs-extra');
|
import fs from 'fs-extra';
|
||||||
const { spawnSync } = require('child_process');
|
import { spawnSync } from 'child_process';
|
||||||
|
import * as git from '../util/git.js';
|
||||||
const git = require('../util/git');
|
|
||||||
|
|
||||||
const tmpBaseDir = path.join(os.tmpdir(), 'handlebars-task-tests');
|
const tmpBaseDir = path.join(os.tmpdir(), 'handlebars-task-tests');
|
||||||
const tmpDir = path.join(tmpBaseDir, Date.now().toString(36));
|
const tmpDir = path.join(tmpBaseDir, Date.now().toString(36));
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
const fs = require('fs');
|
import fs from 'fs';
|
||||||
const { S3, GetObjectCommand } = require('@aws-sdk/client-s3');
|
import { S3, GetObjectCommand } from '@aws-sdk/client-s3';
|
||||||
const { createFakeS3 } = require('./fake-s3');
|
import { createFakeS3 } from './fake-s3.js';
|
||||||
|
|
||||||
const BUCKET = 'test-bucket';
|
const BUCKET = 'test-bucket';
|
||||||
|
|
||||||
const {
|
import {
|
||||||
PUBLISHED_FILES,
|
PUBLISHED_FILES,
|
||||||
buildSuffixes,
|
buildSuffixes,
|
||||||
validateS3Env,
|
validateS3Env,
|
||||||
publish,
|
publish,
|
||||||
getNameInBucket,
|
getNameInBucket,
|
||||||
getLocalFile,
|
getLocalFile,
|
||||||
} = require('../publish-to-aws');
|
} from '../publish-to-aws.js';
|
||||||
|
|
||||||
let server;
|
let server;
|
||||||
let s3Client;
|
let s3Client;
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
const childProcess = require('child_process');
|
|
||||||
|
|
||||||
module.exports = {
|
|
||||||
execNodeJsScriptWithInheritedOutput,
|
|
||||||
};
|
|
||||||
|
|
||||||
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();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
+29
-26
@@ -1,30 +1,33 @@
|
|||||||
const childProcess = require('child_process');
|
import childProcess from 'child_process';
|
||||||
|
|
||||||
module.exports = {
|
export async function remotes() {
|
||||||
async remotes() {
|
return git('remote', '-v');
|
||||||
return git('remote', '-v');
|
}
|
||||||
},
|
|
||||||
async branches() {
|
export async function branches() {
|
||||||
return git('branch', '-a');
|
return git('branch', '-a');
|
||||||
},
|
}
|
||||||
async commitInfo() {
|
|
||||||
const headSha = await getHeadSha();
|
export async function commitInfo() {
|
||||||
const masterSha = await getMasterSha();
|
const headSha = await getHeadSha();
|
||||||
return {
|
const masterSha = await getMasterSha();
|
||||||
headSha,
|
return {
|
||||||
masterSha,
|
headSha,
|
||||||
tagName: await getTagName(),
|
masterSha,
|
||||||
isMaster: headSha === masterSha,
|
tagName: await getTagName(),
|
||||||
};
|
isMaster: headSha === masterSha,
|
||||||
},
|
};
|
||||||
async add(path) {
|
}
|
||||||
return git('add', '-f', path);
|
|
||||||
},
|
export async function add(path) {
|
||||||
async commit(message) {
|
return git('add', '-f', path);
|
||||||
return git('commit', '--message', message);
|
}
|
||||||
},
|
|
||||||
git, // visible for testing
|
export async function commit(message) {
|
||||||
};
|
return git('commit', '--message', message);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { git };
|
||||||
|
|
||||||
async function getHeadSha() {
|
async function getHeadSha() {
|
||||||
const stdout = await git('rev-parse', '--short', 'HEAD');
|
const stdout = await git('rev-parse', '--short', 'HEAD');
|
||||||
|
|||||||
+7
-7
@@ -1,7 +1,7 @@
|
|||||||
const fs = require('fs');
|
import fs from 'fs';
|
||||||
const { execSync } = require('child_process');
|
import { execSync } from 'child_process';
|
||||||
const git = require('./util/git');
|
import * as git from './util/git.js';
|
||||||
const semver = require('semver');
|
import semver from 'semver';
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
const version = process.argv[2];
|
const version = process.argv[2];
|
||||||
@@ -50,16 +50,16 @@ async function main() {
|
|||||||
execSync('npm run build', { stdio: 'inherit' });
|
execSync('npm run build', { stdio: 'inherit' });
|
||||||
}
|
}
|
||||||
|
|
||||||
async function replaceAndAdd(filePath, regex, value) {
|
export async function replaceAndAdd(filePath, regex, value) {
|
||||||
let content = fs.readFileSync(filePath, 'utf8');
|
let content = fs.readFileSync(filePath, 'utf8');
|
||||||
content = content.replace(regex, value);
|
content = content.replace(regex, value);
|
||||||
fs.writeFileSync(filePath, content);
|
fs.writeFileSync(filePath, content);
|
||||||
await git.add(filePath);
|
await git.add(filePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = { replaceAndAdd };
|
import { fileURLToPath } from 'url';
|
||||||
|
|
||||||
if (require.main === module) {
|
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
||||||
main().catch((err) => {
|
main().catch((err) => {
|
||||||
// eslint-disable-next-line no-console
|
// eslint-disable-next-line no-console
|
||||||
console.error(err);
|
console.error(err);
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { basename, dirname, join } from 'node:path';
|
|||||||
import { fileURLToPath } from 'node:url';
|
import { fileURLToPath } from 'node:url';
|
||||||
|
|
||||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||||
import { formatOps } from './report.mjs';
|
import { formatOps } from './report.js';
|
||||||
|
|
||||||
// ─── Resolve files ───────────────────────────────────────────────────────────
|
// ─── Resolve files ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
@@ -36,7 +36,7 @@ if (args.length >= 2) {
|
|||||||
: 'Only one benchmark result found. Run benchmarks on another branch first.'
|
: 'Only one benchmark result found. Run benchmarks on another branch first.'
|
||||||
);
|
);
|
||||||
console.error('');
|
console.error('');
|
||||||
console.error('Usage: node bench/compare.mjs [<baseline.md> <current.md>]');
|
console.error('Usage: node bench/compare.js [<baseline.md> <current.md>]');
|
||||||
console.error('');
|
console.error('');
|
||||||
console.error(
|
console.error(
|
||||||
'With no arguments, auto-selects the two most recent results.'
|
'With no arguments, auto-selects the two most recent results.'
|
||||||
@@ -1,12 +1,12 @@
|
|||||||
import { execSync } from 'node:child_process';
|
import { execSync } from 'node:child_process';
|
||||||
import { Bench } from 'tinybench';
|
import { Bench } from 'tinybench';
|
||||||
import Handlebars from '../../lib/index.js';
|
import Handlebars from '../../lib/index.js';
|
||||||
import { templates as allTemplates } from './templates.mjs';
|
import { templates as allTemplates } from './templates.js';
|
||||||
import {
|
import {
|
||||||
printResults,
|
printResults,
|
||||||
printSectionHeader,
|
printSectionHeader,
|
||||||
saveMarkdownReport,
|
saveMarkdownReport,
|
||||||
} from './report.mjs';
|
} from './report.js';
|
||||||
|
|
||||||
// ─── Configuration ───────────────────────────────────────────────────────────
|
// ─── Configuration ───────────────────────────────────────────────────────────
|
||||||
// Compilation happens once at app startup — low warmup reflects real-world conditions.
|
// Compilation happens once at app startup — low warmup reflects real-world conditions.
|
||||||
@@ -6,7 +6,7 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
|
|||||||
import { gzip } from 'node:zlib';
|
import { gzip } from 'node:zlib';
|
||||||
import { promisify } from 'node:util';
|
import { promisify } from 'node:util';
|
||||||
import Handlebars from '../../lib/index.js';
|
import Handlebars from '../../lib/index.js';
|
||||||
import { templates } from './templates.mjs';
|
import { templates } from './templates.js';
|
||||||
|
|
||||||
const gzipAsync = promisify(gzip);
|
const gzipAsync = promisify(gzip);
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
const { devices } = require('@playwright/test');
|
import { devices } from '@playwright/test';
|
||||||
|
|
||||||
/** @type {import('@playwright/test').PlaywrightTestConfig} */
|
/** @type {import('@playwright/test').PlaywrightTestConfig} */
|
||||||
const config = {
|
const config = {
|
||||||
@@ -24,4 +24,4 @@ const config = {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = config;
|
export default config;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
const { test, expect } = require('@playwright/test');
|
import { test, expect } from '@playwright/test';
|
||||||
|
|
||||||
async function waitForMochaAndAssertResult(page) {
|
async function waitForMochaAndAssertResult(page) {
|
||||||
await page.waitForFunction(() => window.mochaResults); // eslint-disable-line no-undef
|
await page.waitForFunction(() => window.mochaResults); // eslint-disable-line no-undef
|
||||||
@@ -11,13 +11,3 @@ test('Spec handlebars.js', async ({ page, baseURL }) => {
|
|||||||
await page.goto(`${baseURL}/spec/?headless=true`);
|
await page.goto(`${baseURL}/spec/?headless=true`);
|
||||||
await waitForMochaAndAssertResult(page);
|
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);
|
|
||||||
});
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
"keywords": [],
|
"keywords": [],
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"author": "Nils Knappmeier",
|
"author": "Nils Knappmeier",
|
||||||
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "node run-handlebars.js",
|
"test": "node run-handlebars.js",
|
||||||
"test-precompile": "handlebars precompile-test-template.txt.hbs"
|
"test-precompile": "handlebars precompile-test-template.txt.hbs"
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
// This test should run successfully with node 0.10++ as long as Handlebars has been compiled before
|
import assert from 'assert';
|
||||||
var assert = require('assert');
|
import Handlebars from 'handlebars';
|
||||||
var Handlebars = require('handlebars');
|
|
||||||
|
|
||||||
console.log('Testing built Handlebars with Node version ' + process.version);
|
console.log('Testing built Handlebars with Node version ' + process.version);
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "Various tests with Handlebars and rollup",
|
"description": "Various tests with Handlebars and rollup",
|
||||||
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "rollup --config rollup.config.js"
|
"build": "rollup --config rollup.config.js"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import Handlebars from 'handlebars/lib/handlebars';
|
import Handlebars from 'handlebars';
|
||||||
|
|
||||||
const template = Handlebars.compile('Author: {{author}}');
|
const template = Handlebars.compile('Author: {{author}}');
|
||||||
const result = template({ author: 'Yehuda' });
|
const result = template({ author: 'Yehuda' });
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"plugins": [
|
|
||||||
"@roundingwellos/babel-plugin-handlebars-inline-precompile"
|
|
||||||
// "istanbul"
|
|
||||||
],
|
|
||||||
"presets": [["@babel/preset-env"]]
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
node_modules
|
|
||||||
dist
|
|
||||||
package-lock.json
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "webpack-babel-test",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"private": true,
|
|
||||||
"description": "",
|
|
||||||
"scripts": {
|
|
||||||
"build": "webpack --config webpack.config.js"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@babel/core": "^7.27.0",
|
|
||||||
"@babel/preset-env": "^7.27.0",
|
|
||||||
"@roundingwellos/babel-plugin-handlebars-inline-precompile": "^3.0.1",
|
|
||||||
"babel-loader": "^10.0.0",
|
|
||||||
"babel-plugin-istanbul": "^7.0.0",
|
|
||||||
"handlebars": "file:../../..",
|
|
||||||
"handlebars-loader": "^1.7.1",
|
|
||||||
"webpack": "^5.99.0",
|
|
||||||
"webpack-cli": "^6.0.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
import 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');
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
export function assertEquals(actual, expected) {
|
|
||||||
if (actual !== expected) {
|
|
||||||
throw new Error(`Expected "${actual}" to equal "${expected}"`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
# Cleanup: package-lock and "npm ci" is not working with local dependencies
|
|
||||||
rm -rf dist package-lock.json
|
|
||||||
npm install --legacy-peer-deps
|
|
||||||
npm run build
|
|
||||||
|
|
||||||
for i in dist/*-test.js ; do
|
|
||||||
echo "----------------------"
|
|
||||||
echo "-- Running $i"
|
|
||||||
echo "----------------------"
|
|
||||||
node "$i"
|
|
||||||
echo "Success"
|
|
||||||
done
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
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,
|
|
||||||
mode: 'production',
|
|
||||||
target: 'web',
|
|
||||||
output: {
|
|
||||||
filename: '[name]',
|
|
||||||
path: __dirname + '/dist',
|
|
||||||
},
|
|
||||||
module: {
|
|
||||||
rules: [
|
|
||||||
{
|
|
||||||
test: /\.js?$/,
|
|
||||||
exclude: /node_modules/,
|
|
||||||
use: {
|
|
||||||
loader: 'babel-loader',
|
|
||||||
options: { cacheDirectory: false },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
optimization: {
|
|
||||||
minimize: false,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import Handlebars from 'handlebars/lib/handlebars';
|
import Handlebars from 'handlebars';
|
||||||
import { assertEquals } from './lib/assert';
|
import { assertEquals } from './lib/assert';
|
||||||
|
|
||||||
const template = Handlebars.compile('Author: {{author}}');
|
const template = Handlebars.compile('Author: {{author}}');
|
||||||
|
|||||||
+17
-101
@@ -1,8 +1,10 @@
|
|||||||
import { describe, it, expect, beforeAll } from 'vitest';
|
import { describe, it, expect, beforeAll } from 'vitest';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
import { execSync } from 'child_process';
|
import { execSync } from 'child_process';
|
||||||
|
|
||||||
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||||
const distDir = path.resolve(__dirname, '../../dist');
|
const distDir = path.resolve(__dirname, '../../dist');
|
||||||
|
|
||||||
const EXPECTED_BUNDLES = [
|
const EXPECTED_BUNDLES = [
|
||||||
@@ -14,11 +16,8 @@ const EXPECTED_BUNDLES = [
|
|||||||
|
|
||||||
describe('rspack build output', () => {
|
describe('rspack build output', () => {
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
// Ensure full build has been run (bundles + CJS)
|
// Ensure full build has been run
|
||||||
if (
|
if (!fs.existsSync(path.join(distDir, 'handlebars.js'))) {
|
||||||
!fs.existsSync(path.join(distDir, 'handlebars.js')) ||
|
|
||||||
!fs.existsSync(path.join(distDir, 'cjs/handlebars.js'))
|
|
||||||
) {
|
|
||||||
execSync('npm run build', {
|
execSync('npm run build', {
|
||||||
cwd: path.resolve(__dirname, '../..'),
|
cwd: path.resolve(__dirname, '../..'),
|
||||||
stdio: 'inherit',
|
stdio: 'inherit',
|
||||||
@@ -36,70 +35,21 @@ describe('rspack build output', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('CJS output', () => {
|
describe('global export', () => {
|
||||||
it('produces dist/cjs/ with correct structure', () => {
|
it('handlebars.js assigns to self.Handlebars', () => {
|
||||||
expect(fs.existsSync(path.join(distDir, 'cjs/handlebars.js'))).toBe(true);
|
|
||||||
expect(
|
|
||||||
fs.existsSync(path.join(distDir, 'cjs/handlebars.runtime.js'))
|
|
||||||
).toBe(true);
|
|
||||||
expect(fs.existsSync(path.join(distDir, 'cjs/handlebars/base.js'))).toBe(
|
|
||||||
true
|
|
||||||
);
|
|
||||||
expect(
|
|
||||||
fs.existsSync(path.join(distDir, 'cjs/handlebars/runtime.js'))
|
|
||||||
).toBe(true);
|
|
||||||
expect(fs.existsSync(path.join(distDir, 'cjs/handlebars/utils.js'))).toBe(
|
|
||||||
true
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('does not compile lib/index.js into CJS', () => {
|
|
||||||
// lib/index.js is already CJS, so it should be excluded from SWC compilation
|
|
||||||
expect(fs.existsSync(path.join(distDir, 'cjs/index.js'))).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('produces valid CommonJS modules', () => {
|
|
||||||
const content = fs.readFileSync(
|
|
||||||
path.join(distDir, 'cjs/handlebars.js'),
|
|
||||||
'utf8'
|
|
||||||
);
|
|
||||||
expect(content).toContain('"use strict"');
|
|
||||||
expect(content).toContain('exports');
|
|
||||||
// Should not contain ES module import/export syntax
|
|
||||||
expect(content).not.toMatch(/^import /m);
|
|
||||||
expect(content).not.toMatch(/^export /m);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('UMD format', () => {
|
|
||||||
it('wraps handlebars.js as UMD', () => {
|
|
||||||
const content = fs.readFileSync(
|
const content = fs.readFileSync(
|
||||||
path.join(distDir, 'handlebars.js'),
|
path.join(distDir, 'handlebars.js'),
|
||||||
'utf8'
|
'utf8'
|
||||||
);
|
);
|
||||||
// UMD pattern: checks for AMD define, CommonJS module, and falls back to global
|
expect(content).toContain('self.Handlebars');
|
||||||
expect(content).toContain('define');
|
|
||||||
expect(content).toContain('exports');
|
|
||||||
expect(content).toContain('Handlebars');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('wraps handlebars.runtime.js as UMD', () => {
|
it('handlebars.runtime.js assigns to self.Handlebars', () => {
|
||||||
const content = fs.readFileSync(
|
const content = fs.readFileSync(
|
||||||
path.join(distDir, 'handlebars.runtime.js'),
|
path.join(distDir, 'handlebars.runtime.js'),
|
||||||
'utf8'
|
'utf8'
|
||||||
);
|
);
|
||||||
expect(content).toContain('define');
|
expect(content).toContain('self.Handlebars');
|
||||||
expect(content).toContain('exports');
|
|
||||||
expect(content).toContain('Handlebars');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('exposes Handlebars as global library name', () => {
|
|
||||||
const content = fs.readFileSync(
|
|
||||||
path.join(distDir, 'handlebars.js'),
|
|
||||||
'utf8'
|
|
||||||
);
|
|
||||||
// UMD should assign to root["Handlebars"]
|
|
||||||
expect(content).toMatch(/root\["Handlebars"\]/);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -143,51 +93,28 @@ describe('rspack build output', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('functional correctness', () => {
|
describe('functional correctness', () => {
|
||||||
it('CJS entry point loads and compiles templates', () => {
|
it('ESM entry point loads and compiles templates', async () => {
|
||||||
const Handlebars = require('../../lib');
|
const Handlebars = (await import('../../lib/index.js')).default;
|
||||||
const template = Handlebars.compile('Hello {{name}}!');
|
const template = Handlebars.compile('Hello {{name}}!');
|
||||||
expect(template({ name: 'World' })).toBe('Hello World!');
|
expect(template({ name: 'World' })).toBe('Hello World!');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('CJS runtime entry loads correctly', () => {
|
it('ESM runtime entry loads correctly', async () => {
|
||||||
const runtime = require('../../runtime');
|
const runtime = (await import('../../runtime.js')).default;
|
||||||
expect(typeof runtime.template).toBe('function');
|
expect(typeof runtime.template).toBe('function');
|
||||||
expect(typeof runtime.VERSION).toBe('string');
|
expect(typeof runtime.VERSION).toBe('string');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('UMD bundle loads in Node.js (CommonJS mode)', () => {
|
it('precompile and template round-trip works', async () => {
|
||||||
const Handlebars = require('../../dist/handlebars');
|
const Handlebars = (await import('../../lib/index.js')).default;
|
||||||
expect(typeof Handlebars.compile).toBe('function');
|
|
||||||
expect(typeof Handlebars.template).toBe('function');
|
|
||||||
expect(typeof Handlebars.VERSION).toBe('string');
|
|
||||||
|
|
||||||
const template = Handlebars.compile('{{greeting}} {{target}}');
|
|
||||||
expect(template({ greeting: 'Hi', target: 'UMD' })).toBe('Hi UMD');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('UMD runtime bundle loads in Node.js', () => {
|
|
||||||
const runtime = require('../../dist/handlebars.runtime');
|
|
||||||
expect(typeof runtime.template).toBe('function');
|
|
||||||
expect(typeof runtime.SafeString).toBe('function');
|
|
||||||
expect(typeof runtime.VERSION).toBe('string');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('minified UMD bundle loads and works correctly', () => {
|
|
||||||
const Handlebars = require('../../dist/handlebars.min');
|
|
||||||
const template = Handlebars.compile('{{a}} + {{b}} = {{c}}');
|
|
||||||
expect(template({ a: 1, b: 2, c: 3 })).toBe('1 + 2 = 3');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('precompile and template round-trip works', () => {
|
|
||||||
const Handlebars = require('../../lib');
|
|
||||||
const spec = Handlebars.precompile('{{name}} is {{age}}');
|
const spec = Handlebars.precompile('{{name}} is {{age}}');
|
||||||
// eslint-disable-next-line no-eval
|
// eslint-disable-next-line no-eval
|
||||||
const templateFn = Handlebars.template(eval('(' + spec + ')'));
|
const templateFn = Handlebars.template(eval('(' + spec + ')'));
|
||||||
expect(templateFn({ name: 'Alice', age: 30 })).toBe('Alice is 30');
|
expect(templateFn({ name: 'Alice', age: 30 })).toBe('Alice is 30');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('VM runtime functions are overridable', () => {
|
it('VM runtime functions are overridable', async () => {
|
||||||
const Handlebars = require('../../lib');
|
const Handlebars = (await import('../../lib/index.js')).default;
|
||||||
const env = Handlebars.create();
|
const env = Handlebars.create();
|
||||||
const originalCheckRevision = env.VM.checkRevision;
|
const originalCheckRevision = env.VM.checkRevision;
|
||||||
|
|
||||||
@@ -206,9 +133,6 @@ describe('rspack build output', () => {
|
|||||||
path.join(distDir, 'handlebars.js'),
|
path.join(distDir, 'handlebars.js'),
|
||||||
'utf8'
|
'utf8'
|
||||||
);
|
);
|
||||||
// Arrow functions should be transpiled for browser compat.
|
|
||||||
// Check that no "=>" appears outside of comments/strings in a meaningful way.
|
|
||||||
// Simple heuristic: no "=> {" pattern which indicates arrow function bodies.
|
|
||||||
expect(content).not.toMatch(/[=]>\s*\{/);
|
expect(content).not.toMatch(/[=]>\s*\{/);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -217,7 +141,6 @@ describe('rspack build output', () => {
|
|||||||
path.join(distDir, 'handlebars.js'),
|
path.join(distDir, 'handlebars.js'),
|
||||||
'utf8'
|
'utf8'
|
||||||
);
|
);
|
||||||
// For broad browser compat, const/let should be transpiled to var
|
|
||||||
expect(content).not.toMatch(/^\s*(const|let)\s+/m);
|
expect(content).not.toMatch(/^\s*(const|let)\s+/m);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -226,15 +149,10 @@ describe('rspack build output', () => {
|
|||||||
path.join(distDir, 'handlebars.js'),
|
path.join(distDir, 'handlebars.js'),
|
||||||
'utf8'
|
'utf8'
|
||||||
);
|
);
|
||||||
// Remove comments and string literals, then check for backticks
|
|
||||||
// Backticks in comments/strings/regexes are fine — only actual template
|
|
||||||
// literal usage (e.g. `foo ${bar}`) indicates untranspiled code
|
|
||||||
const lines = content.split('\n');
|
const lines = content.split('\n');
|
||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
const trimmed = line.trim();
|
const trimmed = line.trim();
|
||||||
// Skip comment lines
|
|
||||||
if (trimmed.startsWith('//') || trimmed.startsWith('*')) continue;
|
if (trimmed.startsWith('//') || trimmed.startsWith('*')) continue;
|
||||||
// Check for template literal interpolation pattern
|
|
||||||
expect(trimmed).not.toMatch(/`[^`]*\$\{/);
|
expect(trimmed).not.toMatch(/`[^`]*\$\{/);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -244,8 +162,6 @@ describe('rspack build output', () => {
|
|||||||
path.join(distDir, 'handlebars.js'),
|
path.join(distDir, 'handlebars.js'),
|
||||||
'utf8'
|
'utf8'
|
||||||
);
|
);
|
||||||
// Match actual class declarations at statement level (not "class" in
|
|
||||||
// strings, comments, or reserved word lists)
|
|
||||||
const classDeclarations = content.split('\n').filter((line) => {
|
const classDeclarations = content.split('\n').filter((line) => {
|
||||||
const trimmed = line.trim();
|
const trimmed = line.trim();
|
||||||
if (trimmed.startsWith('//') || trimmed.startsWith('*')) return false;
|
if (trimmed.startsWith('//') || trimmed.startsWith('*')) return false;
|
||||||
|
|||||||
Vendored
+34
-1
@@ -289,7 +289,40 @@ export interface Logger {
|
|||||||
export type CompilerInfo = [number /* revision */, string /* versions */];
|
export type CompilerInfo = [number /* revision */, string /* versions */];
|
||||||
|
|
||||||
declare module 'handlebars/runtime' {
|
declare module 'handlebars/runtime' {
|
||||||
export = Handlebars;
|
const runtime: typeof Handlebars;
|
||||||
|
export default runtime;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Named exports matching lib/index.js for ESM/CJS interop
|
||||||
|
export const create: typeof Handlebars.create;
|
||||||
|
export const compile: typeof Handlebars.compile;
|
||||||
|
export const precompile: typeof Handlebars.precompile;
|
||||||
|
export const template: typeof Handlebars.template;
|
||||||
|
export { parse, parseWithoutProcessing } from '@handlebars/parser';
|
||||||
|
export const VERSION: typeof Handlebars.VERSION;
|
||||||
|
export const COMPILER_REVISION: number;
|
||||||
|
export const LAST_COMPATIBLE_COMPILER_REVISION: number;
|
||||||
|
export const REVISION_CHANGES: { [revision: number]: string };
|
||||||
|
export import AST = Handlebars.AST;
|
||||||
|
export const Compiler: Handlebars.ICompiler;
|
||||||
|
export const JavaScriptCompiler: any;
|
||||||
|
export const Parser: any;
|
||||||
|
export import Visitor = Handlebars.Visitor;
|
||||||
|
export import SafeString = Handlebars.SafeString;
|
||||||
|
export import Exception = Handlebars.Exception;
|
||||||
|
export import Utils = Handlebars.Utils;
|
||||||
|
export const escapeExpression: typeof Handlebars.escapeExpression;
|
||||||
|
export import VM = Handlebars.VM;
|
||||||
|
export const log: typeof Handlebars.log;
|
||||||
|
export const registerHelper: typeof Handlebars.registerHelper;
|
||||||
|
export const unregisterHelper: typeof Handlebars.unregisterHelper;
|
||||||
|
export const registerPartial: typeof Handlebars.registerPartial;
|
||||||
|
export const unregisterPartial: typeof Handlebars.unregisterPartial;
|
||||||
|
export const registerDecorator: typeof Handlebars.registerDecorator;
|
||||||
|
export const unregisterDecorator: typeof Handlebars.unregisterDecorator;
|
||||||
|
export function print(ast: hbs.AST.Program): string;
|
||||||
|
export class PrintVisitor {
|
||||||
|
accept(node: hbs.AST.Node): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Handlebars;
|
export default Handlebars;
|
||||||
|
|||||||
+2
-1
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"module": "commonjs",
|
"module": "es2015",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
"lib": ["es6"],
|
"lib": ["es6"],
|
||||||
"exactOptionalPropertyTypes": true,
|
"exactOptionalPropertyTypes": true,
|
||||||
"noUncheckedIndexedAccess": true,
|
"noUncheckedIndexedAccess": true,
|
||||||
|
|||||||
+7
-7
@@ -46,7 +46,6 @@ export default defineConfig({
|
|||||||
'spec/.eslintrc.js',
|
'spec/.eslintrc.js',
|
||||||
'spec/precompiler.js',
|
'spec/precompiler.js',
|
||||||
'spec/spec.js',
|
'spec/spec.js',
|
||||||
'spec/require.js',
|
|
||||||
'spec/source-map.js',
|
'spec/source-map.js',
|
||||||
],
|
],
|
||||||
setupFiles: [
|
setupFiles: [
|
||||||
@@ -64,15 +63,16 @@ export default defineConfig({
|
|||||||
],
|
],
|
||||||
coverage: {
|
coverage: {
|
||||||
provider: 'v8',
|
provider: 'v8',
|
||||||
include: ['dist/cjs/**/*.js'],
|
include: ['lib/**/*.js'],
|
||||||
|
exclude: [
|
||||||
|
'lib/handlebars/compiler/source-node.browser.js',
|
||||||
|
'lib/handlebars/compiler/source-node.node.js',
|
||||||
|
],
|
||||||
thresholds: {
|
thresholds: {
|
||||||
// Slightly below 100% because SWC injects helper functions
|
|
||||||
// (e.g. _sliced_to_array, _non_iterable_rest, for-of try/catch/finally)
|
|
||||||
// with branches that are unreachable in practice.
|
|
||||||
statements: 99,
|
statements: 99,
|
||||||
branches: 92,
|
branches: 98,
|
||||||
functions: 100,
|
functions: 100,
|
||||||
lines: 100,
|
lines: 99,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user