<commit_before>import assert from 'assert';
import error from 'pug-error';

// Mising "types"
//  - Comment
//  - BlockComment
//  - &attributes
//  - InterpolatedTag
//  - Code(buffer: false + block)
//  - Each(object)

export default function ({babel, parse, helpers, ast, path, code}) {
  const {types: t} = babel;
  const nodes = t.identifier('pug_nodes');
  function withString(node, stringLiteral) {
    t.assertStringLiteral(stringLiteral);
    if (t.isStringLiteral(node)) {
      return t.stringLiteral(node.value + stringLiteral.value);
    }
    if (t.isBinaryExpression(node, {operator: '+'}) && t.isStringLiteral(node.right)) {
      return t.binaryExpression('+', node.left, withString(node.right, stringLiteral));
    }
    return t.binaryExpression('+', node, stringLiteral);
  }
  let staticBlockID = 0;
  const compiler = {
    baseBlock() {
      return {
        _id: 'base',
        getKey(fn) {
          fn(t.stringLiteral('pug'));
        },
        handleAttributes(attrs) {},
        end() {},
      };
    },
    staticBlock(parent) {
      let enabled = false;
      let parentEnabled = false;
      let key = null;
      const pending = [];
      let index = 0;
      parent.getKey(parentKey => {
        parentEnabled = true;
        key = withString(parentKey, t.stringLiteral(':' + (staticBlockID++)));
        onUpdate();
      });
      function onUpdate() {
        if (enabled && parentEnabled) {
          while (pending.length) {
            pending.shift()(withString(key, t.stringLiteral(':' + (index++))));
          }
        }
      }
      return {
        getKey(fn) {
          if (pending.indexOf(fn) === -1) {
            pending.push(fn);
          }
          onUpdate();
        },
        handleAttributes(attrs) {
          for (const attr of attrs) {
            if (
              t.isJSXAttribute(attr) &&
              t.isJSXIdentifier(attr.name, {name: 'key'})
            ) {
              return;
            }
          }
          this.getKey(key => {
            if (t.isStringLiteral(key)) key = key; // t.jSXText(key.value);
            else key = t.jSXExpressionContainer(key);
            attrs.push(t.jSXAttribute(
              t.jSXIdentifier('key'),
              key,
            ));
          });
        },
        end() {
          enabled = true;
          onUpdate();
        },
      };
    },
    dynamicBlock(parent, lineNumber) {
      let enabled = false;
      let localKey = null;
      let parentEnabled = false;
      let parentKey = null;
      const pending = [];
      let index = 0;
      function join(left, right) {
        return t.binaryExpression('+', left, right);
      }
      parent.getKey(_parentKey => {
        parentEnabled = true;
        parentKey = withString(_parentKey, t.stringLiteral(':'));
        onUpdate();
      });
      function onUpdate() {
        if (enabled && parentEnabled && localKey) {
          while (pending.length) {
            pending.shift()(withString(join(parentKey, localKey), t.stringLiteral(':' + (index++))));
          }
        } else if (enabled && parentEnabled) {
          const err = error('MISSING_KEY', 'You must specify a key for the first item in any loops.', {
            line: path.node.loc.start.line + lineNumber,
            filename: 'pug',
            src: code,
          });
          throw err;
        }
      }
      return {
        getKey(fn) {
          if (pending.indexOf(fn) === -1) {
            pending.push(fn);
          }
          onUpdate();
        },
        handleAttributes(attrs) {
          for (const attr of attrs) {
            if (
              t.isJSXAttribute(attr) &&
              t.isJSXIdentifier(attr.name, {name: 'key'})
            ) {
              if (localKey === null && t.isJSXExpressionContainer(attr.value) && attr.value.expression) {
                localKey = attr.value.expression;
                onUpdate();
              }
              return;
            }
          }
          this.getKey(key => {
            if (t.isStringLiteral(key)) key = key; // t.jSXText(key.value);<commit_msg>Handle nested `each` with not very unique keys<commit_after>import assert from 'assert';
import error from 'pug-error';

// Mising "types"
//  - Comment
//  - BlockComment
//  - &attributes
//  - InterpolatedTag
//  - Code(buffer: false + block)
//  - Each(object)

export default function ({babel, parse, helpers, ast, path, code}) {
  const {types: t} = babel;
  const nodes = t.identifier('pug_nodes');
  function withString(node, stringLiteral) {
    t.assertStringLiteral(stringLiteral);
    if (t.isStringLiteral(node)) {
      return t.stringLiteral(node.value + stringLiteral.value);
    }
    if (t.isBinaryExpression(node, {operator: '+'}) && t.isStringLiteral(node.right)) {
      return t.binaryExpression('+', node.left, withString(node.right, stringLiteral));
    }
    return t.binaryExpression('+', node, stringLiteral);
  }
  let staticBlockID = 0;
  const compiler = {
    baseBlock() {
      return {
        _id: 'base',
        getKey(fn) {
          fn(t.stringLiteral('pug'));
        },
        handleAttributes(attrs) {},
        end() {},
      };
    },
    staticBlock(parent) {
      let enabled = false;
      let parentEnabled = false;
      let key = null;
      const pending = [];
      let index = 0;
      parent.getKey(parentKey => {
        parentEnabled = true;
        key = withString(parentKey, t.stringLiteral(':' + (staticBlockID++)));
        onUpdate();
      });
      function onUpdate() {
        if (enabled && parentEnabled) {
          while (pending.length) {
            pending.shift()(withString(key, t.stringLiteral(':' + (index++))));
          }
        }
      }
      return {
        getKey(fn) {
          if (pending.indexOf(fn) === -1) {
            pending.push(fn);
          }
          onUpdate();
        },
        handleAttributes(attrs) {
          for (const attr of attrs) {
            if (
              t.isJSXAttribute(attr) &&
              t.isJSXIdentifier(attr.name, {name: 'key'})
            ) {
              return;
            }
          }
          this.getKey(key => {
            if (t.isStringLiteral(key)) key = key; // t.jSXText(key.value);
            else key = t.jSXExpressionContainer(key);
            attrs.push(t.jSXAttribute(
              t.jSXIdentifier('key'),
              key,
            ));
          });
        },
        end() {
          enabled = true;
          onUpdate();
        },
      };
    },
    dynamicBlock(parent, lineNumber) {
      let enabled = false;
      let localKey = null;
      let parentEnabled = false;
      let parentKey = null;
      const pending = [];
      let index = 0;
      function join(left, right) {
        return t.binaryExpression('+', left, right);
      }
      parent.getKey(_parentKey => {
        parentEnabled = true;
        parentKey = withString(_parentKey, t.stringLiteral(':'));
        onUpdate();
      });
      function onUpdate() {
        if (enabled && parentEnabled && localKey) {
          while (pending.length) {
            pending.shift()(withString(join(parentKey, localKey), t.stringLiteral(':' + (index++))));
          }
        } else if (enabled && parentEnabled) {
          const err = error('MISSING_KEY', 'You must specify a key for the first item in any loops.', {
            line: path.node.loc.start.line + lineNumber,
            filename: 'pug',
            src: code,
          });
          throw err;
        }
      }
      return {
        getKey(fn) {
          if (pending.indexOf(fn) === -1) {
            pending.push(fn);
          }
          onUpdate();
        },
        handleAttributes(attrs) {
          for (const attr of attrs) {
            if (
              t.isJSXAttribute(attr) &&
              t.isJSXIdentifier(attr.name, {name: 'key'})
            ) {
              if (localKey === null && t.isJSXExpressionContainer(attr.value) && attr.value.expression) {
                localKey = attr.value.expression;
                onUpdate();
                // remove the attribute and replace with the properly nested version
                attrs.splice(attrs.indexOf(attr), 1);
              } else {
                return;
              }
            }
          }
          this.getKey(key => {
            if (t.isStringLiteral(key)) key = key; // t.jSXText(key.value);
{'fraction_non_alphanumeric': 0.13892529488859764, 'fraction_numerical': 0.001310615989515072, 'mean_word_length': 1.6852207293666026, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '298125', 'n_tokens_mistral': 2415, 'n_tokens_neox': 2408, 'n_words': 505}