123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465 |
- 'use strict';
- /*!
- * Pug
- * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
- * MIT Licensed
- */
- /**
- * Module dependencies.
- */
- var fs = require('fs');
- var path = require('path');
- var lex = require('pug-lexer');
- var stripComments = require('pug-strip-comments');
- var parse = require('pug-parser');
- var load = require('pug-load');
- var filters = require('pug-filters');
- var link = require('pug-linker');
- var generateCode = require('pug-code-gen');
- var runtime = require('pug-runtime');
- var runtimeWrap = require('pug-runtime/wrap');
- /**
- * Name for detection
- */
- exports.name = 'Pug';
- /**
- * Pug runtime helpers.
- */
- exports.runtime = runtime;
- /**
- * Template function cache.
- */
- exports.cache = {};
- function applyPlugins(value, options, plugins, name) {
- return plugins.reduce(function (value, plugin) {
- return (
- plugin[name]
- ? plugin[name](value, options)
- : value
- );
- }, value);
- }
- function findReplacementFunc(plugins, name) {
- var eligiblePlugins = plugins.filter(function (plugin) {
- return plugin[name];
- });
- if (eligiblePlugins.length > 1) {
- throw new Error('Two or more plugins all implement ' + name + ' method.');
- } else if (eligiblePlugins.length) {
- return eligiblePlugins[0][name].bind(eligiblePlugins[0]);
- }
- return null;
- }
- /**
- * Object for global custom filters. Note that you can also just pass a `filters`
- * option to any other method.
- */
- exports.filters = {};
- /**
- * Compile the given `str` of pug and return a function body.
- *
- * @param {String} str
- * @param {Object} options
- * @return {Object}
- * @api private
- */
- function compileBody(str, options){
- var debug_sources = {};
- debug_sources[options.filename] = str;
- var dependencies = [];
- var plugins = options.plugins || [];
- var ast = load.string(str, {
- filename: options.filename,
- basedir: options.basedir,
- lex: function (str, options) {
- var lexOptions = {};
- Object.keys(options).forEach(function (key) {
- lexOptions[key] = options[key];
- });
- lexOptions.plugins = plugins.filter(function (plugin) {
- return !!plugin.lex;
- }).map(function (plugin) {
- return plugin.lex;
- });
- var contents = applyPlugins(str, {filename: options.filename}, plugins, 'preLex');
- return applyPlugins(lex(contents, lexOptions), options, plugins, 'postLex');
- },
- parse: function (tokens, options) {
- tokens = tokens.map(function (token) {
- if (token.type === 'path' && path.extname(token.val) === '') {
- return {
- type: 'path',
- loc: token.loc,
- val: token.val + '.pug'
- };
- }
- return token;
- });
- tokens = stripComments(tokens, options);
- tokens = applyPlugins(tokens, options, plugins, 'preParse');
- var parseOptions = {};
- Object.keys(options).forEach(function (key) {
- parseOptions[key] = options[key];
- });
- parseOptions.plugins = plugins.filter(function (plugin) {
- return !!plugin.parse;
- }).map(function (plugin) {
- return plugin.parse;
- });
- return applyPlugins(
- applyPlugins(parse(tokens, parseOptions), options, plugins, 'postParse'),
- options, plugins, 'preLoad'
- );
- },
- resolve: function (filename, source, loadOptions) {
- var replacementFunc = findReplacementFunc(plugins, 'resolve');
- if (replacementFunc) {
- return replacementFunc(filename, source, options);
- }
- return load.resolve(filename, source, loadOptions);
- },
- read: function (filename, loadOptions) {
- dependencies.push(filename);
- var contents;
- var replacementFunc = findReplacementFunc(plugins, 'read');
- if (replacementFunc) {
- contents = replacementFunc(filename, options);
- } else {
- contents = load.read(filename, loadOptions);
- }
- debug_sources[filename] = contents;
- return contents;
- }
- });
- ast = applyPlugins(ast, options, plugins, 'postLoad');
- ast = applyPlugins(ast, options, plugins, 'preFilters');
- var filtersSet = {};
- Object.keys(exports.filters).forEach(function (key) {
- filtersSet[key] = exports.filters[key];
- });
- if (options.filters) {
- Object.keys(options.filters).forEach(function (key) {
- filtersSet[key] = options.filters[key];
- });
- }
- ast = filters.handleFilters(ast, filtersSet, options.filterOptions, options.filterAliases);
- ast = applyPlugins(ast, options, plugins, 'postFilters');
- ast = applyPlugins(ast, options, plugins, 'preLink');
- ast = link(ast);
- ast = applyPlugins(ast, options, plugins, 'postLink');
- // Compile
- ast = applyPlugins(ast, options, plugins, 'preCodeGen');
- var js = generateCode(ast, {
- pretty: options.pretty,
- compileDebug: options.compileDebug,
- doctype: options.doctype,
- inlineRuntimeFunctions: options.inlineRuntimeFunctions,
- globals: options.globals,
- self: options.self,
- includeSources: options.includeSources ? debug_sources : false,
- templateName: options.templateName
- });
- js = applyPlugins(js, options, plugins, 'postCodeGen');
- // Debug compiler
- if (options.debug) {
- console.error('\nCompiled Function:\n\n\u001b[90m%s\u001b[0m', js.replace(/^/gm, ' '));
- }
- return {body: js, dependencies: dependencies};
- }
- /**
- * Get the template from a string or a file, either compiled on-the-fly or
- * read from cache (if enabled), and cache the template if needed.
- *
- * If `str` is not set, the file specified in `options.filename` will be read.
- *
- * If `options.cache` is true, this function reads the file from
- * `options.filename` so it must be set prior to calling this function.
- *
- * @param {Object} options
- * @param {String=} str
- * @return {Function}
- * @api private
- */
- function handleTemplateCache (options, str) {
- var key = options.filename;
- if (options.cache && exports.cache[key]) {
- return exports.cache[key];
- } else {
- if (str === undefined) str = fs.readFileSync(options.filename, 'utf8');
- var templ = exports.compile(str, options);
- if (options.cache) exports.cache[key] = templ;
- return templ;
- }
- }
- /**
- * Compile a `Function` representation of the given pug `str`.
- *
- * Options:
- *
- * - `compileDebug` when `false` debugging code is stripped from the compiled
- template, when it is explicitly `true`, the source code is included in
- the compiled template for better accuracy.
- * - `filename` used to improve errors when `compileDebug` is not `false` and to resolve imports/extends
- *
- * @param {String} str
- * @param {Options} options
- * @return {Function}
- * @api public
- */
- exports.compile = function(str, options){
- var options = options || {}
- str = String(str);
- var parsed = compileBody(str, {
- compileDebug: options.compileDebug !== false,
- filename: options.filename,
- basedir: options.basedir,
- pretty: options.pretty,
- doctype: options.doctype,
- inlineRuntimeFunctions: options.inlineRuntimeFunctions,
- globals: options.globals,
- self: options.self,
- includeSources: options.compileDebug === true,
- debug: options.debug,
- templateName: 'template',
- filters: options.filters,
- filterOptions: options.filterOptions,
- filterAliases: options.filterAliases,
- plugins: options.plugins,
- });
- var res = options.inlineRuntimeFunctions
- ? new Function('', parsed.body + ';return template;')()
- : runtimeWrap(parsed.body);
- res.dependencies = parsed.dependencies;
- return res;
- };
- /**
- * Compile a JavaScript source representation of the given pug `str`.
- *
- * Options:
- *
- * - `compileDebug` When it is `true`, the source code is included in
- * the compiled template for better error messages.
- * - `filename` used to improve errors when `compileDebug` is not `true` and to resolve imports/extends
- * - `name` the name of the resulting function (defaults to "template")
- * - `module` when it is explicitly `true`, the source code include export module syntax
- *
- * @param {String} str
- * @param {Options} options
- * @return {Object}
- * @api public
- */
- exports.compileClientWithDependenciesTracked = function(str, options){
- var options = options || {};
- str = String(str);
- var parsed = compileBody(str, {
- compileDebug: options.compileDebug,
- filename: options.filename,
- basedir: options.basedir,
- pretty: options.pretty,
- doctype: options.doctype,
- inlineRuntimeFunctions: options.inlineRuntimeFunctions !== false,
- globals: options.globals,
- self: options.self,
- includeSources: options.compileDebug,
- debug: options.debug,
- templateName: options.name || 'template',
- filters: options.filters,
- filterOptions: options.filterOptions,
- filterAliases: options.filterAliases,
- plugins: options.plugins
- });
- var body = parsed.body;
- if(options.module) {
- if(options.inlineRuntimeFunctions === false) {
- body = 'var pug = require("pug-runtime");' + body;
- }
- body += ' module.exports = ' + (options.name || 'template') + ';';
- }
- return {body: body, dependencies: parsed.dependencies};
- };
- /**
- * Compile a JavaScript source representation of the given pug `str`.
- *
- * Options:
- *
- * - `compileDebug` When it is `true`, the source code is included in
- * the compiled template for better error messages.
- * - `filename` used to improve errors when `compileDebug` is not `true` and to resolve imports/extends
- * - `name` the name of the resulting function (defaults to "template")
- *
- * @param {String} str
- * @param {Options} options
- * @return {String}
- * @api public
- */
- exports.compileClient = function (str, options) {
- return exports.compileClientWithDependenciesTracked(str, options).body;
- };
- /**
- * Compile a `Function` representation of the given pug file.
- *
- * Options:
- *
- * - `compileDebug` when `false` debugging code is stripped from the compiled
- template, when it is explicitly `true`, the source code is included in
- the compiled template for better accuracy.
- *
- * @param {String} path
- * @param {Options} options
- * @return {Function}
- * @api public
- */
- exports.compileFile = function (path, options) {
- options = options || {};
- options.filename = path;
- return handleTemplateCache(options);
- };
- /**
- * Render the given `str` of pug.
- *
- * Options:
- *
- * - `cache` enable template caching
- * - `filename` filename required for `include` / `extends` and caching
- *
- * @param {String} str
- * @param {Object|Function} options or fn
- * @param {Function|undefined} fn
- * @returns {String}
- * @api public
- */
- exports.render = function(str, options, fn){
- // support callback API
- if ('function' == typeof options) {
- fn = options, options = undefined;
- }
- if (typeof fn === 'function') {
- var res;
- try {
- res = exports.render(str, options);
- } catch (ex) {
- return fn(ex);
- }
- return fn(null, res);
- }
- options = options || {};
- // cache requires .filename
- if (options.cache && !options.filename) {
- throw new Error('the "filename" option is required for caching');
- }
- return handleTemplateCache(options, str)(options);
- };
- /**
- * Render a Pug file at the given `path`.
- *
- * @param {String} path
- * @param {Object|Function} options or callback
- * @param {Function|undefined} fn
- * @returns {String}
- * @api public
- */
- exports.renderFile = function(path, options, fn){
- // support callback API
- if ('function' == typeof options) {
- fn = options, options = undefined;
- }
- if (typeof fn === 'function') {
- var res;
- try {
- res = exports.renderFile(path, options);
- } catch (ex) {
- return fn(ex);
- }
- return fn(null, res);
- }
- options = options || {};
- options.filename = path;
- return handleTemplateCache(options)(options);
- };
- /**
- * Compile a Pug file at the given `path` for use on the client.
- *
- * @param {String} path
- * @param {Object} options
- * @returns {String}
- * @api public
- */
- exports.compileFileClient = function(path, options){
- var key = path + ':client';
- options = options || {};
- options.filename = path;
- if (options.cache && exports.cache[key]) {
- return exports.cache[key];
- }
- var str = fs.readFileSync(options.filename, 'utf8');
- var out = exports.compileClient(str, options);
- if (options.cache) exports.cache[key] = out;
- return out;
- };
- /**
- * Express support.
- */
- exports.__express = function(path, options, fn) {
- if(options.compileDebug == undefined && process.env.NODE_ENV === 'production') {
- options.compileDebug = false;
- }
- exports.renderFile(path, options, fn);
- }
|