Use let or const to avoid scope issues and hoisting
var debug = require('debug')('express:view');
1/*!2 * express3 * Copyright(c) 2009-2013 TJ Holowaychuk4 * Copyright(c) 2013 Roman Shtylman5 * Copyright(c) 2014-2015 Douglas Christopher Wilson6 * MIT Licensed7 */89'use strict';1011/**12 * Module dependencies.13 * @private14 */1516var debug = require('debug')('express:view');17var path = require('node:path');18var fs = require('node:fs');1920/**21 * Module variables.22 * @private23 */2425var dirname = path.dirname;26var basename = path.basename;27var extname = path.extname;28var join = path.join;29var resolve = path.resolve;3031/**32 * Module exports.33 * @public34 */3536module.exports = View;3738/**39 * Initialize a new `View` with the given `name`.40 *41 * Options:42 *43 * - `defaultEngine` the default template engine name44 * - `engines` template engine require() cache45 * - `root` root path for view lookup46 *47 * @param {string} name48 * @param {object} options49 * @public50 */5152function View(name, options) {53 var opts = options || {};5455 this.defaultEngine = opts.defaultEngine;56 this.ext = extname(name);57 this.name = name;58 this.root = opts.root;5960 if (!this.ext && !this.defaultEngine) {61 throw new Error('No default engine was specified and no extension was provided.');62 }6364 var fileName = name;6566 if (!this.ext) {67 // get extension from default engine name68 this.ext = this.defaultEngine[0] !== '.'69 ? '.' + this.defaultEngine70 : this.defaultEngine;7172 fileName += this.ext;73 }7475 if (!opts.engines[this.ext]) {76 // load engine77 var mod = this.ext.slice(1)78 debug('require "%s"', mod)7980 // default engine export81 var fn = require(mod).__express8283 if (typeof fn !== 'function') {84 throw new Error('Module "' + mod + '" does not provide a view engine.')85 }8687 opts.engines[this.ext] = fn88 }8990 // store loaded engine91 this.engine = opts.engines[this.ext];9293 // lookup path94 this.path = this.lookup(fileName);95}9697/**98 * Lookup view by the given `name`99 *100 * @param {string} name101 * @private102 */103104View.prototype.lookup = function lookup(name) {105 var path;106 var roots = [].concat(this.root);107108 debug('lookup "%s"', name);109110 for (var i = 0; i < roots.length && !path; i++) {111 var root = roots[i];112113 // resolve the path114 var loc = resolve(root, name);115 var dir = dirname(loc);116 var file = basename(loc);117118 // resolve the file119 path = this.resolve(dir, file);120 }121122 return path;123};124125/**126 * Render with the given options.127 *128 * @param {object} options129 * @param {function} callback130 * @private131 */132133View.prototype.render = function render(options, callback) {134 var sync = true;135136 debug('render "%s"', this.path);137138 // render, normalizing sync callbacks139 this.engine(this.path, options, function onRender() {140 if (!sync) {141 return callback.apply(this, arguments);142 }143144 // copy arguments145 var args = new Array(arguments.length);146 var cntx = this;147148 for (var i = 0; i < arguments.length; i++) {149 args[i] = arguments[i];150 }151152 // force callback to be async153 return process.nextTick(function renderTick() {154 return callback.apply(cntx, args);155 });156 });157158 sync = false;159};160161/**162 * Resolve the file within the given directory.163 *164 * @param {string} dir165 * @param {string} file166 * @private167 */168169View.prototype.resolve = function resolve(dir, file) {170 var ext = this.ext;171172 // <path>.<ext>173 var path = join(dir, file);174 var stat = tryStat(path);175176 if (stat && stat.isFile()) {177 return path;178 }179180 // <path>/index.<ext>181 path = join(dir, basename(file, ext), 'index' + ext);182 stat = tryStat(path);183184 if (stat && stat.isFile()) {185 return path;186 }187};188189/**190 * Return a stat, maybe.191 *192 * @param {string} path193 * @return {fs.Stats}194 * @private195 */196197function tryStat(path) {198 debug('stat "%s"', path);199200 try {201 return fs.statSync(path);202 } catch (e) {203 return undefined;204 }205}
Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.