Hook into PUG just before the AST is converted to JS code. eslint-disable-next-line func-name-matching, no-unused-vars
557217
function enforceTrustedTypes(inputAst, options) {
    // PUG provides no way to forward options to plugins, so we piggyback on
    // filterOptions.
    let {
      csrfInputName,
      csrfInputValueExpression, // eslint-disable-line prefer-const
      nonceValueExpression, // eslint-disable-line prefer-const
      report,
    } = (options.filterOptions || {}).trustedTypes || {};

    // eslint-disable-next-line no-console
    report = report || console.warn.bind(console);
    csrfInputName = csrfInputName || 'csrfToken';

    let ast = null;
    let unpredictableSuffix = null;
    {
      // Defensively copy the AST.
      const astJson = JSON.stringify(inputAst);
      ast = JSON.parse(astJson);
      // Produce a stable but unguessable ID so we can get unmaskable access
      // to our runtime support code.
      const unpredictableSuffixHash = crypto.createHash('sha256');
      unpredictableSuffixHash.update(astJson);
      unpredictableSuffix = unpredictableSuffixHash.digest('hex');
    }

    // If we make any changes, we'll inject runtime support.
    let needsRuntime = false;
    let needsScrubber = false;

    // Keep track of the mixin call graph and which might be called in sensitive
    // contexts.
    const deferred = [];
    const calledInSensitiveContext = new Map();
    // Maps each name of a mixin to the names of mixins it calls in a text context.
    const mixinCallGraph = Object.create(null);

    // A sequence of (object, key) pairs that were traversed to reach the
    // current AST node.
    const policyPath = [];

    // Don't convert the output as TrustedHTML if the template uses known unsafe
    // features.
    let mayTrustOutput = true;

    // Logs a message and flips a bit so that we do not bless the output of this template.
    function distrust(msg, optAstNode) {
      const { filename, line } = optAstNode || policyPath[policyPath.length - 2] || {};
      const relfilename = options.basedir ? path.relative(options.basedir, filename) : filename;
      report(`${ relfilename }:${ line }: ${ msg }`);
      mayTrustOutput = false;
    }

    // Walk upwards to find the enclosing tag and mixin if any.
    function getContainerName(skip = 0) {
      let element = null;
      let mixin = null;
      for (let i = policyPath.length - (skip * 2); (i -= 2) >= 0;) {
        const policyPathElement = policyPath[i];
        if (typeof policyPathElement === 'object') {
          if (policyPathElement.type === 'Tag') {
            element = policyPathElement.name.toLowerCase();
            break;
          } else if (policyPathElement.type === 'Mixin' && !policyPathElement.call) {
            mixin = policyPathElement.name;
            break;
          }
        }
      }
      return { element, mixin };
    }

    // Sometimes the type for one attribute depends on another.
    // For example, the sensitivity of <link href> depends on
    // the value of rel.
    function valueGetter({ attrs, attributeBlocks }) {
      let values = null;
      return function getValue(name) {
        if (!values) {
          values = new Map();
          // Look in &attributes(...) style attribute blocks for any known values.
          // eslint-disable-next-line no-unused-vars
          for (const attributeBlock of attributeBlocks) {
            const jsAst = parseExpression(attributeBlock.val);
            if (jsAst.type === 'ObjectExpression' && jsAst.properties) {
              for (const property of jsAst.properties) {
                if (property.type !== 'ObjectProperty' || property.method || property.computed) {
                  // Can't sanitize getters or methods, or spread elements.
                  continue;
                }
                const attrName = property.key.name || property.key.value;
                const { code: valueExpression } = generate(property.value);
                if (constantinople(valueExpression)) {
                  values.set(attrName, constantinople.toConstant(valueExpression));
                }
              }
            }
          }
          // Find any known values among the (attr=expr, ...) style attributes.
          for (const attr of attrs) {
            if (constantinople(attr.val)) {
              values.set(attr.name, constantinople.toConstant(attr.val));
            }
          }
        }

        name = String(name).toLowerCase();
        return values.get(name);
      };
    }

    // Check well-formedness of user expressions.
    // This may be redundant with Pug codegen, but otherwise
    // a guard expression like
    //      rt_abcd.requireFoo(${ expr })
    // could be bypassed by an expr like ` 'ok'), (x ` that combines
    // with that to produce an ineffective guard expression:
    //      rt_abcd.requireFoo('ok'), (x)
    function isWellFormed(expr) {
      try {
        // Should throw if attr.val is not well-formed.
        // eslint-disable-next-line no-new, no-new-func
        new Function(`return () => (${ expr })`);
      } catch (exc) {
        report(`Malformed expression in Pug template: ${ expr }`);
        return false;
      }
      return true;
    }

    // Decorate expression text with a call to a guard function.
    function addGuard(guard, expr) {
      let safeExpr = null;
      if (!isWellFormed(expr)) {
        expr = '{/*Malformed Expression*/}';
      }
      needsRuntime = true;
      safeExpr = ` rt_${ unpredictableSuffix }.${ guard }(${ expr }) `;
      return safeExpr;
    }

    // Decorate expression text with a call to a scrubber function
    // that checks an entire attribute bundle at runtime.
    function addScrubber(scrubber, element, expr) {
      let safeExpr = null;
      if (!isWellFormed(expr)) {
        expr = '{/*Malformed Expression*/}';
      }
      needsScrubber = true;
      safeExpr = ` sc_${ unpredictableSuffix }.${ scrubber }(${ stringify(element || '*') }, ${ expr }) `;
      return safeExpr;
    }

    // Add a guard if necessary.
    // - elementName: string - lower case
    // - attrName: string
    // - getValue(attrName) - returns the value of another attribute on the same element if known
    // - valueExpression: string - a JS expression that computes the result
    // - onNewValue(newValueExpression) - called with any newly guarded value expression.
    //     Not called if no guard required.
    function maybeGuardAttributeValue(
      elementName, attrName, getValue, valueExpression, onNewValue) {
      attrName = String(attrName).toLowerCase();
      const type = typeOfAttribute(elementName || '*', attrName, getValue);
      if (type === null) {
        distrust(`Cannot trust dynamic value for <${ elementName } ${ attrName }>`);
      } else {
        const guard = GUARDS_BY_ATTRIBUTE_TYPE[type];
        if (guard) {
          onNewValue(addGuard(guard, valueExpression));
        }
      }
    }

    // If user expressions have free variables like pug_html then don't bless
    // the output because we'd have to statically analyze the generated JS to
    // preserve output integrity.
    function checkCodeDoesNotInterfere(astNode, exprKey, isExpression) {
      let expr = astNode[exprKey];
      const seen = new Set();

      const type = typeof expr;
      if (type !== 'string') {
        // expr may be true, not "true".
        // This occurs for inferred expressions like valueless attributes.
        expr = `${ expr }`;
        astNode[exprKey] = expr;
      }

      let warnedPug = false;
      let warnedModule = false;
      // Allows check to take into account the context in which a node appears.
      // Flattened pairs of [ancestor, keyInAncestorToDescendent].
      const jsAstPath = [];

      function check(jsAst) {
        if (jsAst && typeof jsAst === 'object' && jsAst.type === 'Identifier') {
          const { name } = jsAst;
          if (/^pug_/.test(name) || name === 'eval') {
            if (!warnedPug) {
              distrust(`Expression (${ expr }) may interfere with PUG internals ${ jsAst.name }`);
              warnedPug = true;
            }
          } else if (name === 'require' &&
                     // Allow trusted plugin code to require modules they need.
                     !(Object.hasOwnProperty.call(astNode, 'mayRequire') && astNode.mayRequire) &&
                     // Allow require.moduleKeys and require.resolve but not require(moduleId).
                     !(jsAstPath.length && jsAstPath[jsAstPath.length - 2].type === 'MemberExpression' &&
                       jsAstPath[jsAstPath.length - 1] === 'object')) {
            // Defang expression.
            astNode[exprKey] = 'null';
            // We trust trusted plugin code and PUG code to use the module's private key
            // but not template code.
            if (!warnedModule) {
              distrust(`Expression (${ expr }) may interfere with module internals ${ jsAst.name }`);
              warnedModule = true;
            }
          }
        }
        checkChildren(jsAst); // eslint-disable-line no-use-before-define
      }

      function checkChildren(jsAst) {
        if (!seen.has(jsAst)) {
          seen.add(jsAst);
          const jsAstPathLength = jsAstPath.length;
          jsAstPath[jsAstPathLength] = jsAst;
          for (const key in jsAst) {
            if (Object.hasOwnProperty.call(jsAst, key)) {
              jsAstPath[jsAstPathLength + 1] = key;
              check(jsAst[key]);
            }
          }
          jsAstPath.length = jsAstPathLength;
        }
      }

      let root = null;
      try {
        root = isExpression ? parseExpression(expr) : parse(expr);
      } catch (exc) {
        distrust(`Malformed expression (${ expr })`);
        return;
      }
      check(root);
    }

    // Add nonce attributes to attribute sets as needed.
    function noncifyAttrs(element, getValue, attrs) {
      if (nonceValueExpression) {
        if (element === 'script' || element === 'style' ||
            (element === 'link' && (getValue('rel') || '').toLowerCase() === 'stylesheet')) {
          if (attrs.findIndex(({ name }) => name === 'nonce') < 0) {
            attrs[attrs.length] = {
              name: 'nonce',
              val: nonceValueExpression,
              mustEscape: true,
            };
          }
        }
      }
    }

    // Add nonce attributes to tags as needed.
    function noncifyTag({ name, block: { nodes } }) {
      if (name === 'form' && csrfInputValueExpression) {
        nodes.unshift({
          type: 'Conditional',
          test: csrfInputValueExpression,
          consequent: {
            type: 'Block',
            nodes: [
              {
                type: 'Tag',
                name: 'input',
                selfClosing: false,
                block: {
                  type: 'Block',
                  nodes: [],
                },
                attrs: [
                  {
                    name: 'name',
                    val: stringify(csrfInputName),
                    mustEscape: true,
                  },
                  {
                    name: 'type',
                    val: '\'hidden\'',
                    mustEscape: true,
                  },
                  {
                    name: 'value',
                    val: csrfInputValueExpression,
                    mustEscape: true,
                  },
                ],
                attributeBlocks: [],
                isInline: false,
              },
            ],
          },
          alternate: null,
        });
      }
    }

    // Keys match keys in the Pug AST.  The input is the referent of policyPath.
    const policy = {
      __proto__: null,
      type: {
        __proto__: null,
        Code(obj) {
          // When we see a code block, sanity check it, and if it specifies text content,
          // add any guards needed.
          // If the code appears in a mixin we might have to defer adding guards until we
          // understand the mixin call graph.
          if (constantinople(obj.val)) {
            return;
          }
          checkCodeDoesNotInterfere(obj, 'val', obj.buffer);
          if (obj.buffer) {
            const { element, mixin } = getContainerName();
            const contentType = contentTypeForElement(element);
            let guard = contentType ? GUARDS_BY_ELEMENT_CONTENT_TYPE[contentType] : null;
            if (!guard) {
              if (!element && mixin) {
                deferred.push({ mixin, code: obj });
                return;
              }
              guard = trustedHTMLGuard;
            }
            obj.val = addGuard(guard, obj.val);
            obj.mustEscape = false;
          }
        },
        Comment(obj) {
          // User comments can lead to confusing tokenization since
          // -->
          // seems like it should run to end of line but does not.
          if (/--|^-?$/.test(obj.val)) {
            distrust(`Invalid comment content ${ JSON.stringify(obj.val) }`);
          }
        },
        Mixin(obj) {
          // Both calls and definitions have type:'Mixin'
          if (obj.call) {
            if (/\S/.test(obj.args)) {
              checkCodeDoesNotInterfere(obj, 'args', true);
            }
            const { element, mixin } = getContainerName();
            const contentType = contentTypeForElement(element);
            const guard = contentType ? GUARDS_BY_ELEMENT_CONTENT_TYPE[contentType] : null;
            if (guard) {
              multiMapSet(calledInSensitiveContext, obj.name, guard);
            } else if (mixin) {
              mixinCallGraph[mixin] = mixinCallGraph[mixin] || [];
              mixinCallGraph[mixin].push(obj.name);
            }
          }
        },
        Tag(obj) {
          // Record information about which contexts tags appear in so we can avoid
          // confusion due to tag-like content in non-tag contexts like
          // <script><div>
          const { element, mixin } = getContainerName(1);
          if (element) {
            if (!mayContainTags(element)) {
              distrust(`HTML tag <${ obj.name }> appears inside <${ element }> which cannot have tag content`);
            }
          } else if (mixin) {
            deferred.push({ mixin, tag: obj });
          }
          noncifyTag(obj);
        },
        Text(obj) {
          // Inline HTML is a token-level integrity risk.
          if (obj.isHtml) {
            const match = MALFORMED_MARKUP.exec(obj.val);
            if (match) {
              distrust(`Malformed HTML markup ${ match[0] }`);
            }
          }
        },
        Each(obj) {
          checkCodeDoesNotInterfere(obj, 'val', true);
        },
      },
      // "attrs"'s value maps HTML attribute names to AST nodes representing value expressions.
      attrs(obj) {
        const parent = policyPath[policyPath.length - 2];
        const getValue = valueGetter(parent);
        // If the attributes appear directly on a call, do not assume any containing
        // element context
        const element = parent.call ? null : getContainerName().element;
        // Iterate over attributes and add checks as necessary.
        for (const attr of obj) {
          if (constantinople(attr.val)) {
            if (!attr.mustEscape) {
              const constant = constantinople.toConstant(attr.val);
              if (/"/.test(constant)) {
                // TODO: upstream a fix to pug_attr so that pug_attr('name', 'val">', false).
                // https://github.com/pugjs/pug/pull/3073
                distrust(`Attribute value ${ constant } breaks attribute quoting`);
              }
            }
          } else {
            checkCodeDoesNotInterfere(attr, 'val', true);
            maybeGuardAttributeValue(
              element, attr.name, getValue, attr.val,
              (guardedExpression) => {
                attr.val = guardedExpression;
              });
            if (!attr.mustEscape) {
              distrust(`The value of the ${ attr.name } attribute must be escaped`);
            }
          }
        }
        noncifyAttrs(element, getValue, obj);
      },
      // "attributeBlocks" allow a single expression to specify a group of attributes.
      attributeBlocks(obj) {
        const parent = policyPath[policyPath.length - 2];
        const getValue = valueGetter(parent);
        // If the attributes appear directly on a call, do not assume any containing
        // element context
        const element = parent.call ? null : getContainerName().element;
        for (const attributeBlock of obj) {
          checkCodeDoesNotInterfere(attributeBlock, 'val', true);
          // Parse the expression and look for constant properties to guard.
          const jsAst = parseExpression(attributeBlock.val);
          let needsDynamicScrubbing = true;
          let changedAst = false;
          if (jsAst.type === 'ObjectExpression' && jsAst.properties) {
            needsDynamicScrubbing = false;
            for (const property of jsAst.properties) {
              if (property.type !== 'ObjectProperty' || property.method || property.computed) {
                // Can't sanitize getters or methods, or spread elements.
                needsDynamicScrubbing = true;
                break;
              }
              const attrName = property.key.name || property.key.value;
              const { code: valueExpression } = generate(property.value);
              if (!constantinople(valueExpression)) {
                maybeGuardAttributeValue(
                  element, attrName, getValue, valueExpression,
                  // eslint-disable-next-line no-loop-func
                  (newValueExpression) => {
                    changedAst = true;
                    property.value = parseExpression(newValueExpression);
                  });
              }
            }
          }
          // If not all the property names are known, then we need to depend on a scrubber
          // library that picks guards at render time.
          if (needsDynamicScrubbing) {
            attributeBlock.val = addScrubber('scrubAttrs', element, attributeBlock.val);
          } else if (changedAst) {
            attributeBlock.val = generate(jsAst).code;
          }
        }
      },
    };

    // Walk the AST applying the policy
    function apply(x) {
      const policyPathLength = policyPath.length;
      policyPath[policyPathLength] = x;
      if (Array.isArray(x)) {
        for (let i = 0, len = x.length; i < len; ++i) {
          policyPath[policyPathLength + 1] = i;
          apply(x[i]);
        }
      } else if (x && typeof x === 'object') {
        for (const key of Object.getOwnPropertyNames(x)) {
          policyPath[policyPathLength + 1] = key;
          const valueType = typeof policy[key];
          if (valueType === 'function') {
            policy[key](x[key]);
          } else if (valueType === 'object') {
            const policyMember = policy[key][x[key]];
            if (typeof policyMember === 'function') {
              policyMember(x);
            }
          }
          if (key === 'test' || key === 'expr') {
            checkCodeDoesNotInterfere(x, key, true);
          }
          apply(x[key]);
        }
      }
      policyPath.length = policyPathLength;
    }

    apply(ast, []);

    // If mixin f calls fp, and f is in calledInScript,
    // then add fp to calledInSensitiveContext.
    transitiveClosure(calledInSensitiveContext, mixinCallGraph);

    // Worst case analysis!  If a code block appears in the
    // top level of a mixin body, and that body is called from the
    // top level of a <script> element, then guard the code.
    // This may affect uses of the mixin from outside script elements.
    function guardTextInMixins() {
      for (const { mixin, code, tag } of deferred) {
        if (calledInSensitiveContext.has(mixin)) {
          const guards = calledInSensitiveContext.get(mixin);
          if (code) {
            for (const guard of guards) {
              code.val = addGuard(guard, code.val);
              code.mustEscape = false;
            }
          } else if (guards.has(trustedScriptGuard)) {
            distrust(
              `HTML tag <${ tag.name
              }> may appear inside <script> which cannot have tag content via call to +${ mixin }`,
              tag);
          }
        } else if (code) {
          code.val = addGuard(trustedHTMLGuard, code.val);
          code.mustEscape = false;
        }
      }
    }

    guardTextInMixins();

    // Inject the equivalent of
    // - var rt_unpredictableId = ...
    // at the top of the template if it turns out we need it.
    if (mayTrustOutput) {
      ast.nodes.push(
        {
          'type': 'Code',
          'val': `pug_html = rt_${ unpredictableSuffix }.getMinter(require.moduleKeys)(pug_html)`,
          'buffer': false,
          'mustEscape': false,
          'isInline': false,
        });
      needsRuntime = true;
    }
    if (needsScrubber) {
      ast.nodes.splice(
        0, 0,
        {
          'type': 'Code',
          // TODO: How does this affect client-side compilation?
          'val': `var sc_${ unpredictableSuffix } = require('pug-scrubber-trusted-types');`,
          'buffer': false,
          'mustEscape': false,
          'isInline': false,
          'mayRequire': true,
        });
    }
    if (needsRuntime) {
      ast.nodes.splice(
        0, 0,
        {
          'type': 'Code',
          // TODO: How does this affect client-side compilation?
          'val': (
            'require(\'module-keys/cjs\').polyfill(module, require);\n' +
            `var rt_${ unpredictableSuffix } = require('pug-runtime-trusted-types');`
          ),
          'buffer': false,
          'mustEscape': false,
          'isInline': false,
          'mayRequire': true,
        });
    }

    return ast;
  }
987572
def lint_javascript(context: Context):
    
    args = [
        '--config', os.path.join(context.app.common_templates_path, 'mtp_common', 'build_tasks', 'eslintrc.json'),
        '--format', 'stylish',
    ]
    if context.verbosity == 0:
        args.append('--quiet')
    if not context.use_colour:
        args.append('--no-color')
    args.append(context.app.javascript_source_path)
    return context.node_tool('eslint', *args)
763833
function jsToAst(js) {
  const ast = acorn.parse(js, {
    plugins: { jsx: true },
  });
  if (ast.body.length === 0) throw new Error('Empty AST input');
  return ast;
}
953684
function transpileJavaScriptToPHP ({ js, variables }) {

    // match all local variables (let, const or var)
    let localVariablesRegex = /[^a-zA-Z0-9_](?:let|const|var)\s+(?:\[([^\]]+)\]|([a-zA-Z0-9_]+))/g // local variables

    let allVariables = (variables || []).map (x => x); // clone the array
    // process the variables created in destructuring assignments as well
    let localVariablesMatches
    while (localVariablesMatches = localVariablesRegex.exec (js)) {
        if (localVariablesMatches[1]) {
            // this is a destructuring assignment like
            // let [ a, b, c ] = 'a-b-c'.split ('-')
            let matches = localVariablesMatches[1].trim ().split (', ') // split the destructuring assignment by comma
            matches.forEach (x => allVariables.push (x.trim ())) // trim each variable name
        } else {
            // this is a single variable assignment
            allVariables.push (localVariablesMatches[2].trim ()) // add it to the list of local variables
        }
    }

    // match all variables instantiated in the catch()-block of a try-catch clause
    let catchClauseRegex = /catch \(([^)]+)\)/g
    let catchClauseMatches
    while (catchClauseMatches = catchClauseRegex.exec (js)) {
        allVariables.push (catchClauseMatches[1])
    }

    // append $ to all variables in the method (PHP syntax demands $ at the beginning of a variable name)
    let phpVariablesRegexes = allVariables.map (x => [ "([^$$a-zA-Z0-9\\.\\>'_/])" + x + "([^a-zA-Z0-9'_/])", '$1$$' + x + '$2' ])

    // support for php syntax for object-pointer dereference
    // convert all $variable.property to $variable->property
    let variablePropertiesRegexes = allVariables.map (x => [ "([^a-zA-Z0-9\\.\\>'_])" + x + '\\.', '$1' + x + '->' ])

    // transpile JS → PHP
    let phpBody = regexAll (js, phpRegexes.concat (phpVariablesRegexes).concat (variablePropertiesRegexes))

    return phpBody
}
146242
function minifyCodeEsprima(code,callback,description) {
    if ((typeof esprima == "undefined") ||
        (typeof esmangle == "undefined") ||
        (typeof escodegen == "undefined")) {
      console.warn("esprima/esmangle/escodegen not defined - not minifying")
      return callback(code);
    }

    var code, syntax, option, str, before, after;
    var options = {};
    options["mangle"] = Espruino.Config.MINIFICATION_Mangle;
    options["remove-unreachable-branch"] = Espruino.Config.MINIFICATION_Unreachable;
    options["remove-unused-vars"] = Espruino.Config.MINIFICATION_Unused;
    options["fold-constant"] = Espruino.Config.MINIFICATION_Literal;
    options["eliminate-dead-code"] = Espruino.Config.MINIFICATION_DeadCode;
    option = {format: {indent: {style: ''},quotes: 'auto',compact: true}};
    str = '';
    try {
        before = code.length;
        syntax = esprima.parse(code, { raw: true, loc: true });
        syntax = obfuscate(syntax,options);
        code = escodegen.generate(syntax, option);
        after = code.length;
        if (before > after) {
          Espruino.Core.Notifications.info('No errors'+description+'. Minified ' + before + ' bytes to ' + after + ' bytes.');
        } else {
          Espruino.Core.Notifications.info('Can not minify further'+description+', code is already optimized.');
        }
        callback(code);
    } catch (e) {
      Espruino.Core.Notifications.error(e.toString()+description);
      console.error(e.stack);
      callback(code);
    } finally { }
  }
48978
function wrap(set) {
    return function (fn, time /* , ...args */) {
      var boundArgs = arguments.length > 2;
      var args = boundArgs ? slice.call(arguments, 2) : false;
      return set(boundArgs ? function () {
        // eslint-disable-next-line no-new-func
        (typeof fn == 'function' ? fn : Function(fn)).apply(this, args);
      } : fn, time);
    };
  }
375977
function captureConfigObject(config) {
  let captured;
  const require = {};
  require.config = function _config(conf) {
    captured = conf;
  };

  let wedConfig;

  // eslint-disable-next-line no-unused-vars
  function define(name, obj) {
    if (wedConfig !== undefined) {
      throw new Error("more than one define");
    }

    switch (arguments.length) {
    case 0:
      throw new Error("no arguments to the define!");
    case 1:
      if (typeof name !== "object") {
        throw new Error("if define has only one argument, it must be an " +
                        "object");
      }
      wedConfig = name;
      break;
    default:
      throw new Error("captureConfigObject is designed to capture a " +
                      "maximum of two arguments.");
    }
  }

  eval(config); // eslint-disable-line no-eval
  return {
    requireConfig: captured,
    wedConfig,
  };
}
195937
function Context(opt_configureForTesting) {
  var closure = this.closure = vm.createContext({
    console: console,
    setTimeout: setTimeout,
    setInterval: setInterval,
    clearTimeout: clearTimeout,
    clearInterval: clearInterval,
    process: process,
    require: require,
    Buffer: Buffer,
    Error: Error,
    CLOSURE_BASE_PATH: path.dirname(CLOSURE_BASE_FILE_PATH) + '/',
    CLOSURE_IMPORT_SCRIPT: function(src) {
      loadScript(src);
      return true;
    },
    CLOSURE_NO_DEPS: !isDevMode(),
    goog: {}
  });
  closure.window = closure.top = closure;

  if (opt_configureForTesting) {
    closure.document = {
      body: {},
      createElement: function() { return {}; },
      getElementsByTagName: function() { return []; }
    };
    closure.document.body.ownerDocument = closure.document;
  }

  loadScript(CLOSURE_BASE_FILE_PATH);
  loadScript(DEPS_FILE_PATH);

  /**
   * Synchronously loads a script into the protected Closure context.
   * @param {string} src Path to the file to load.
   */
  function loadScript(src) {
    src = path.normalize(src);
    var contents = fs.readFileSync(src, 'utf8');
    vm.runInContext(contents, closure, src);
  }
}
761472
function(node, parent){
    var debug = this.debug;

    if (!node) {
      var msg;
      if (parent) {
        msg = 'A child of ' + parent.type + ' (' + (parent.filename || 'Pug') + ':' + parent.line + ')';
      } else {
        msg = 'A top-level node';
      }
      msg += ' is ' + node + ', expected a Pug AST Node.';
      throw new TypeError(msg);
    }

    if (debug && node.debug !== false && node.type !== 'Block') {
      if (node.line) {
        var js = ';pug_debug_line = ' + node.line;
        if (node.filename) js += ';pug_debug_filename = ' + stringify(node.filename);
        this.buf.push(js + ';');
      }
    }

    if (!this['visit' + node.type]) {
      var msg;
      if (parent) {
        msg = 'A child of ' + parent.type
      } else {
        msg = 'A top-level node';
      }
      msg += ' (' + (node.filename || 'Pug') + ':' + node.line + ')'
           + ' is of type ' + node.type + ','
           + ' which is not supported by pug-code-gen.'
      switch (node.type) {
      case 'Filter':
        msg += ' Please use pug-filters to preprocess this AST.'
        break;
      case 'Extends':
      case 'Include':
      case 'NamedBlock':
      case 'FileReference': // unlikely but for the sake of completeness
        msg += ' Please use pug-linker to preprocess this AST.'
        break;
      }
      throw new TypeError(msg);
    }

    this.visitNode(node);
  }
10887
async function parseReport() { // eslint-disable-line no-unused-vars
  try {
    const coverage = await promises.readFile('lcov.info', 'utf8');
    const report = Report.fromCoverage(coverage);
    console.log(`The coverage report contains ${report.records.length} records:`);
    console.log(report.toJSON());
  }

  catch (error) {
    console.log(`An error occurred: ${error.message}`);
  }
}
1351340
function _genericLint( src ) {
  // Pass all command line flags to EsLint.
  const options = minimist( process.argv.slice( 2 ) );

  return gulp.src( src, { base: './' } )
    .pipe( gulpEslint( options ) )
    .pipe( gulpEslint.format() )
    .pipe( ( () => {
      if ( options.travis ) {
        return gulpEslint.failAfterError();
      }

      return gulpUtil.noop();
    } )( ) )
    .pipe( gulp.dest( './' ) )
    .on( 'error', handleErrors );
}
1323677
function scopify (code, unescapeSingleQuotes) {
      // Interpolations got escaped as HTML and must be unescaped to be JS.
      if (unescapeSingleQuotes) {
        code = code.replace(/\\'/g, "'")
      }
      var tokens = code.split(/\b/)
      var isProperty = false
      var isLoopVar = false
      var isInString = false
      for (var i = 0; i < tokens.length; i++) {
        var token = tokens[i]
        var stringTokens = token.match(/['"]/g)
        if (stringTokens) {
          isInString = ((isInString ? 1 : 0) + stringTokens.length) % 2
        } else if (!isInString) {
          if (/^[a-z_]/i.test(token)) {
            if (!ltl.jsPattern.test(token)) {
              for (var j = 0; j < loopVars.length; j++) {
                for (var k = 0; k < loopVars[j].length; k++) {
                  if (token === loopVars[j][k][0]) {
                    isLoopVar = true
                    tokens[i] = loopVars[j][k][1]
                  }
                }
              }
              if (!isProperty && !isLoopVar) {
                tokens[i] = 'scope.' + token
              }
            }
          }
          isProperty = token[token.length - 1] === '.'
          isLoopVar = false
        }
      }
      code = tokens.join('')
      return code
    }
425316
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);
      }

      var str = applyPlugins(contents, {filename: filename}, plugins, 'preLex');
      debug_sources[filename] = str;
      return str;
    }
  });
  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};
}
1101670
function compile (format) {
  if (typeof format !== 'string') {
    throw new TypeError('argument format must be a string')
  }

  var fmt = String(JSON.stringify(format))
  var js = '  "use strict"\n  return ' + fmt.replace(/:([-\w]{2,})(?:\[([^\]]+)\])?/g, function (_, name, arg) {
    var tokenArguments = 'req, res'
    var tokenFunction = 'tokens[' + String(JSON.stringify(name)) + ']'

    if (arg !== undefined) {
      tokenArguments += ', ' + String(JSON.stringify(arg))
    }

    return '" +\n    (' + tokenFunction + '(' + tokenArguments + ') || "-") + "'
  })

  // eslint-disable-next-line no-new-func
  return new Function('tokens, req, res', js)
}
751406
function gulpHtml2pug(options = {}) {
  // creating a stream through which each file will pass
  return through.obj(function (file, enc, cb) {
    const newFile = file.clone();

    vinylToString(file, enc)
      .then(html => html2pug(html, options))
      .then((pug) => {
        if (file.isBuffer()) {
          newFile.contents = Buffer.from(pug);
        } else if (file.isStream()) {
          // start the transformation
          newFile.contents = through();
          newFile.contents.end(pug);
        } else {
          throw new PluginError(PLUGIN_NAME, 'Invalid file');
        }

        const dirname = path.dirname(file.path);
        const basename = path.basename(file.path, path.extname(file.path));
        newFile.path = path.join(dirname, `${basename}.pug`);
        // make sure the file goes through the next gulp plugin
        this.push(newFile);
        cb();
      });
  });
}
37759
function _genericLintJs( src ) {
  // Pass all command line flags to ESLint.
  const options = minimist( process.argv.slice( 2 ) );
  let errorHandler = through2.obj();

  if ( options.travis ) {
    options.quiet = true;
    errorHandler = gulpEslint.failAfterError();
  }

  return gulp.src( src.concat( '!**/node_modules/**' ), { base: './' } )
    .pipe( gulpEslint( options ) )
    .pipe( gulpEslint.format() )
    .pipe( errorHandler )
    .pipe( gulp.dest( './' ) )
    .on( 'error', handleErrors );
}
1368944
function es3 ({ remove, sourceMap, sourcemap } = {}) {
  const removeHash = {
    'defineProperty': [/^\s*Object\.defineProperty\(\s*exports,\s*'__esModule'.*\n$/mg, ''],
    'freeze': [/Object.freeze\s*\(\s*([^)]*)\)/g, '$1']
  }
  if (!Array.isArray(remove)) remove = Object.keys(removeHash)

  return {
    name: 'es3',
    transformBundle: function (code) {
      const magicString = new MagicString(code);
      for (let k in removeHash) {
        if (remove.indexOf(k) > -1) {
          const [pattern, value = ''] = removeHash[k];
          let match, start, end, replacement;

          while ((match = pattern.exec(code))) {
            start       = match.index;
            end         = start + match[0].length;
            replacement = value.toString();
            for (let i = 1; i < match.length; i++) {
              replacement = replacement.replace(`$${i}`, match[i]);
            }
            magicString.overwrite(start, end, replacement);
          }
        }
      }
      return {
        code: magicString.toString(),
        map:  (sourceMap !== false && sourcemap !== false) ?
          magicString.generateMap({ hires: true }) : { mappings: '' }
      }
    }
  }
}