123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659 |
- 'use strict';
-
- function _inheritsLoose(subClass, superClass) { subClass.prototype = Object.create(superClass.prototype); subClass.prototype.constructor = subClass; _setPrototypeOf(subClass, superClass); }
-
- function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
-
- var asap = require('asap');
-
- var _waterfall = require('a-sync-waterfall');
-
- var lib = require('./lib');
-
- var compiler = require('./compiler');
-
- var filters = require('./filters');
-
- var _require = require('./loaders'),
- FileSystemLoader = _require.FileSystemLoader,
- WebLoader = _require.WebLoader,
- PrecompiledLoader = _require.PrecompiledLoader;
-
- var tests = require('./tests');
-
- var globals = require('./globals');
-
- var _require2 = require('./object'),
- Obj = _require2.Obj,
- EmitterObj = _require2.EmitterObj;
-
- var globalRuntime = require('./runtime');
-
- var handleError = globalRuntime.handleError,
- Frame = globalRuntime.Frame;
-
- var expressApp = require('./express-app'); // If the user is using the async API, *always* call it
- // asynchronously even if the template was synchronous.
-
-
- function callbackAsap(cb, err, res) {
- asap(function () {
- cb(err, res);
- });
- }
- /**
- * A no-op template, for use with {% include ignore missing %}
- */
-
-
- var noopTmplSrc = {
- type: 'code',
- obj: {
- root: function root(env, context, frame, runtime, cb) {
- try {
- cb(null, '');
- } catch (e) {
- cb(handleError(e, null, null));
- }
- }
- }
- };
-
- var Environment = /*#__PURE__*/function (_EmitterObj) {
- _inheritsLoose(Environment, _EmitterObj);
-
- function Environment() {
- return _EmitterObj.apply(this, arguments) || this;
- }
-
- var _proto = Environment.prototype;
-
- _proto.init = function init(loaders, opts) {
- var _this = this;
-
- // The dev flag determines the trace that'll be shown on errors.
- // If set to true, returns the full trace from the error point,
- // otherwise will return trace starting from Template.render
- // (the full trace from within nunjucks may confuse developers using
- // the library)
- // defaults to false
- opts = this.opts = opts || {};
- this.opts.dev = !!opts.dev; // The autoescape flag sets global autoescaping. If true,
- // every string variable will be escaped by default.
- // If false, strings can be manually escaped using the `escape` filter.
- // defaults to true
-
- this.opts.autoescape = opts.autoescape != null ? opts.autoescape : true; // If true, this will make the system throw errors if trying
- // to output a null or undefined value
-
- this.opts.throwOnUndefined = !!opts.throwOnUndefined;
- this.opts.trimBlocks = !!opts.trimBlocks;
- this.opts.lstripBlocks = !!opts.lstripBlocks;
- this.loaders = [];
-
- if (!loaders) {
- // The filesystem loader is only available server-side
- if (FileSystemLoader) {
- this.loaders = [new FileSystemLoader('views')];
- } else if (WebLoader) {
- this.loaders = [new WebLoader('/views')];
- }
- } else {
- this.loaders = lib.isArray(loaders) ? loaders : [loaders];
- } // It's easy to use precompiled templates: just include them
- // before you configure nunjucks and this will automatically
- // pick it up and use it
-
-
- if (typeof window !== 'undefined' && window.nunjucksPrecompiled) {
- this.loaders.unshift(new PrecompiledLoader(window.nunjucksPrecompiled));
- }
-
- this._initLoaders();
-
- this.globals = globals();
- this.filters = {};
- this.tests = {};
- this.asyncFilters = [];
- this.extensions = {};
- this.extensionsList = [];
-
- lib._entries(filters).forEach(function (_ref) {
- var name = _ref[0],
- filter = _ref[1];
- return _this.addFilter(name, filter);
- });
-
- lib._entries(tests).forEach(function (_ref2) {
- var name = _ref2[0],
- test = _ref2[1];
- return _this.addTest(name, test);
- });
- };
-
- _proto._initLoaders = function _initLoaders() {
- var _this2 = this;
-
- this.loaders.forEach(function (loader) {
- // Caching and cache busting
- loader.cache = {};
-
- if (typeof loader.on === 'function') {
- loader.on('update', function (name, fullname) {
- loader.cache[name] = null;
-
- _this2.emit('update', name, fullname, loader);
- });
- loader.on('load', function (name, source) {
- _this2.emit('load', name, source, loader);
- });
- }
- });
- };
-
- _proto.invalidateCache = function invalidateCache() {
- this.loaders.forEach(function (loader) {
- loader.cache = {};
- });
- };
-
- _proto.addExtension = function addExtension(name, extension) {
- extension.__name = name;
- this.extensions[name] = extension;
- this.extensionsList.push(extension);
- return this;
- };
-
- _proto.removeExtension = function removeExtension(name) {
- var extension = this.getExtension(name);
-
- if (!extension) {
- return;
- }
-
- this.extensionsList = lib.without(this.extensionsList, extension);
- delete this.extensions[name];
- };
-
- _proto.getExtension = function getExtension(name) {
- return this.extensions[name];
- };
-
- _proto.hasExtension = function hasExtension(name) {
- return !!this.extensions[name];
- };
-
- _proto.addGlobal = function addGlobal(name, value) {
- this.globals[name] = value;
- return this;
- };
-
- _proto.getGlobal = function getGlobal(name) {
- if (typeof this.globals[name] === 'undefined') {
- throw new Error('global not found: ' + name);
- }
-
- return this.globals[name];
- };
-
- _proto.addFilter = function addFilter(name, func, async) {
- var wrapped = func;
-
- if (async) {
- this.asyncFilters.push(name);
- }
-
- this.filters[name] = wrapped;
- return this;
- };
-
- _proto.getFilter = function getFilter(name) {
- if (!this.filters[name]) {
- throw new Error('filter not found: ' + name);
- }
-
- return this.filters[name];
- };
-
- _proto.addTest = function addTest(name, func) {
- this.tests[name] = func;
- return this;
- };
-
- _proto.getTest = function getTest(name) {
- if (!this.tests[name]) {
- throw new Error('test not found: ' + name);
- }
-
- return this.tests[name];
- };
-
- _proto.resolveTemplate = function resolveTemplate(loader, parentName, filename) {
- var isRelative = loader.isRelative && parentName ? loader.isRelative(filename) : false;
- return isRelative && loader.resolve ? loader.resolve(parentName, filename) : filename;
- };
-
- _proto.getTemplate = function getTemplate(name, eagerCompile, parentName, ignoreMissing, cb) {
- var _this3 = this;
-
- var that = this;
- var tmpl = null;
-
- if (name && name.raw) {
- // this fixes autoescape for templates referenced in symbols
- name = name.raw;
- }
-
- if (lib.isFunction(parentName)) {
- cb = parentName;
- parentName = null;
- eagerCompile = eagerCompile || false;
- }
-
- if (lib.isFunction(eagerCompile)) {
- cb = eagerCompile;
- eagerCompile = false;
- }
-
- if (name instanceof Template) {
- tmpl = name;
- } else if (typeof name !== 'string') {
- throw new Error('template names must be a string: ' + name);
- } else {
- for (var i = 0; i < this.loaders.length; i++) {
- var loader = this.loaders[i];
- tmpl = loader.cache[this.resolveTemplate(loader, parentName, name)];
-
- if (tmpl) {
- break;
- }
- }
- }
-
- if (tmpl) {
- if (eagerCompile) {
- tmpl.compile();
- }
-
- if (cb) {
- cb(null, tmpl);
- return undefined;
- } else {
- return tmpl;
- }
- }
-
- var syncResult;
-
- var createTemplate = function createTemplate(err, info) {
- if (!info && !err && !ignoreMissing) {
- err = new Error('template not found: ' + name);
- }
-
- if (err) {
- if (cb) {
- cb(err);
- return;
- } else {
- throw err;
- }
- }
-
- var newTmpl;
-
- if (!info) {
- newTmpl = new Template(noopTmplSrc, _this3, '', eagerCompile);
- } else {
- newTmpl = new Template(info.src, _this3, info.path, eagerCompile);
-
- if (!info.noCache) {
- info.loader.cache[name] = newTmpl;
- }
- }
-
- if (cb) {
- cb(null, newTmpl);
- } else {
- syncResult = newTmpl;
- }
- };
-
- lib.asyncIter(this.loaders, function (loader, i, next, done) {
- function handle(err, src) {
- if (err) {
- done(err);
- } else if (src) {
- src.loader = loader;
- done(null, src);
- } else {
- next();
- }
- } // Resolve name relative to parentName
-
-
- name = that.resolveTemplate(loader, parentName, name);
-
- if (loader.async) {
- loader.getSource(name, handle);
- } else {
- handle(null, loader.getSource(name));
- }
- }, createTemplate);
- return syncResult;
- };
-
- _proto.express = function express(app) {
- return expressApp(this, app);
- };
-
- _proto.render = function render(name, ctx, cb) {
- if (lib.isFunction(ctx)) {
- cb = ctx;
- ctx = null;
- } // We support a synchronous API to make it easier to migrate
- // existing code to async. This works because if you don't do
- // anything async work, the whole thing is actually run
- // synchronously.
-
-
- var syncResult = null;
- this.getTemplate(name, function (err, tmpl) {
- if (err && cb) {
- callbackAsap(cb, err);
- } else if (err) {
- throw err;
- } else {
- syncResult = tmpl.render(ctx, cb);
- }
- });
- return syncResult;
- };
-
- _proto.renderString = function renderString(src, ctx, opts, cb) {
- if (lib.isFunction(opts)) {
- cb = opts;
- opts = {};
- }
-
- opts = opts || {};
- var tmpl = new Template(src, this, opts.path);
- return tmpl.render(ctx, cb);
- };
-
- _proto.waterfall = function waterfall(tasks, callback, forceAsync) {
- return _waterfall(tasks, callback, forceAsync);
- };
-
- return Environment;
- }(EmitterObj);
-
- var Context = /*#__PURE__*/function (_Obj) {
- _inheritsLoose(Context, _Obj);
-
- function Context() {
- return _Obj.apply(this, arguments) || this;
- }
-
- var _proto2 = Context.prototype;
-
- _proto2.init = function init(ctx, blocks, env) {
- var _this4 = this;
-
- // Has to be tied to an environment so we can tap into its globals.
- this.env = env || new Environment(); // Make a duplicate of ctx
-
- this.ctx = lib.extend({}, ctx);
- this.blocks = {};
- this.exported = [];
- lib.keys(blocks).forEach(function (name) {
- _this4.addBlock(name, blocks[name]);
- });
- };
-
- _proto2.lookup = function lookup(name) {
- // This is one of the most called functions, so optimize for
- // the typical case where the name isn't in the globals
- if (name in this.env.globals && !(name in this.ctx)) {
- return this.env.globals[name];
- } else {
- return this.ctx[name];
- }
- };
-
- _proto2.setVariable = function setVariable(name, val) {
- this.ctx[name] = val;
- };
-
- _proto2.getVariables = function getVariables() {
- return this.ctx;
- };
-
- _proto2.addBlock = function addBlock(name, block) {
- this.blocks[name] = this.blocks[name] || [];
- this.blocks[name].push(block);
- return this;
- };
-
- _proto2.getBlock = function getBlock(name) {
- if (!this.blocks[name]) {
- throw new Error('unknown block "' + name + '"');
- }
-
- return this.blocks[name][0];
- };
-
- _proto2.getSuper = function getSuper(env, name, block, frame, runtime, cb) {
- var idx = lib.indexOf(this.blocks[name] || [], block);
- var blk = this.blocks[name][idx + 1];
- var context = this;
-
- if (idx === -1 || !blk) {
- throw new Error('no super block available for "' + name + '"');
- }
-
- blk(env, context, frame, runtime, cb);
- };
-
- _proto2.addExport = function addExport(name) {
- this.exported.push(name);
- };
-
- _proto2.getExported = function getExported() {
- var _this5 = this;
-
- var exported = {};
- this.exported.forEach(function (name) {
- exported[name] = _this5.ctx[name];
- });
- return exported;
- };
-
- return Context;
- }(Obj);
-
- var Template = /*#__PURE__*/function (_Obj2) {
- _inheritsLoose(Template, _Obj2);
-
- function Template() {
- return _Obj2.apply(this, arguments) || this;
- }
-
- var _proto3 = Template.prototype;
-
- _proto3.init = function init(src, env, path, eagerCompile) {
- this.env = env || new Environment();
-
- if (lib.isObject(src)) {
- switch (src.type) {
- case 'code':
- this.tmplProps = src.obj;
- break;
-
- case 'string':
- this.tmplStr = src.obj;
- break;
-
- default:
- throw new Error("Unexpected template object type " + src.type + "; expected 'code', or 'string'");
- }
- } else if (lib.isString(src)) {
- this.tmplStr = src;
- } else {
- throw new Error('src must be a string or an object describing the source');
- }
-
- this.path = path;
-
- if (eagerCompile) {
- try {
- this._compile();
- } catch (err) {
- throw lib._prettifyError(this.path, this.env.opts.dev, err);
- }
- } else {
- this.compiled = false;
- }
- };
-
- _proto3.render = function render(ctx, parentFrame, cb) {
- var _this6 = this;
-
- if (typeof ctx === 'function') {
- cb = ctx;
- ctx = {};
- } else if (typeof parentFrame === 'function') {
- cb = parentFrame;
- parentFrame = null;
- } // If there is a parent frame, we are being called from internal
- // code of another template, and the internal system
- // depends on the sync/async nature of the parent template
- // to be inherited, so force an async callback
-
-
- var forceAsync = !parentFrame; // Catch compile errors for async rendering
-
- try {
- this.compile();
- } catch (e) {
- var err = lib._prettifyError(this.path, this.env.opts.dev, e);
-
- if (cb) {
- return callbackAsap(cb, err);
- } else {
- throw err;
- }
- }
-
- var context = new Context(ctx || {}, this.blocks, this.env);
- var frame = parentFrame ? parentFrame.push(true) : new Frame();
- frame.topLevel = true;
- var syncResult = null;
- var didError = false;
- this.rootRenderFunc(this.env, context, frame, globalRuntime, function (err, res) {
- // TODO: this is actually a bug in the compiled template (because waterfall
- // tasks are both not passing errors up the chain of callbacks AND are not
- // causing a return from the top-most render function). But fixing that
- // will require a more substantial change to the compiler.
- if (didError && cb && typeof res !== 'undefined') {
- // prevent multiple calls to cb
- return;
- }
-
- if (err) {
- err = lib._prettifyError(_this6.path, _this6.env.opts.dev, err);
- didError = true;
- }
-
- if (cb) {
- if (forceAsync) {
- callbackAsap(cb, err, res);
- } else {
- cb(err, res);
- }
- } else {
- if (err) {
- throw err;
- }
-
- syncResult = res;
- }
- });
- return syncResult;
- };
-
- _proto3.getExported = function getExported(ctx, parentFrame, cb) {
- // eslint-disable-line consistent-return
- if (typeof ctx === 'function') {
- cb = ctx;
- ctx = {};
- }
-
- if (typeof parentFrame === 'function') {
- cb = parentFrame;
- parentFrame = null;
- } // Catch compile errors for async rendering
-
-
- try {
- this.compile();
- } catch (e) {
- if (cb) {
- return cb(e);
- } else {
- throw e;
- }
- }
-
- var frame = parentFrame ? parentFrame.push() : new Frame();
- frame.topLevel = true; // Run the rootRenderFunc to populate the context with exported vars
-
- var context = new Context(ctx || {}, this.blocks, this.env);
- this.rootRenderFunc(this.env, context, frame, globalRuntime, function (err) {
- if (err) {
- cb(err, null);
- } else {
- cb(null, context.getExported());
- }
- });
- };
-
- _proto3.compile = function compile() {
- if (!this.compiled) {
- this._compile();
- }
- };
-
- _proto3._compile = function _compile() {
- var props;
-
- if (this.tmplProps) {
- props = this.tmplProps;
- } else {
- var source = compiler.compile(this.tmplStr, this.env.asyncFilters, this.env.extensionsList, this.path, this.env.opts);
- var func = new Function(source); // eslint-disable-line no-new-func
-
- props = func();
- }
-
- this.blocks = this._getBlocks(props);
- this.rootRenderFunc = props.root;
- this.compiled = true;
- };
-
- _proto3._getBlocks = function _getBlocks(props) {
- var blocks = {};
- lib.keys(props).forEach(function (k) {
- if (k.slice(0, 2) === 'b_') {
- blocks[k.slice(2)] = props[k];
- }
- });
- return blocks;
- };
-
- return Template;
- }(Obj);
-
- module.exports = {
- Environment: Environment,
- Template: Template
- };
|