All files / public/admin tag-operations.js

85.89% Statements 67/78
78.12% Branches 50/64
93.33% Functions 14/15
93.93% Lines 62/66

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 1296x       9x       22x 22x 22x 21x 1x 1x 1x         20x     20x 20x 20x 22x 28x   22x       9x 9x 9x 9x 9x 9x 9x 9x 6x 18x 6x         6x 9x 6x       6x     6x 6x       4x     4x 3x 9x                     3x 3x 3x 3x       5x 5x 1x   4x 3x 3x 9x   3x 3x 9x 9x 9x       3x       3x 1x 1x 3x 1x 1x         1x           6x              
(function () {
  "use strict";
 
  function rawOf(entry) {
    return entry && typeof entry.data === "string" ? entry.data : "";
  }
 
  function readTags(raw) {
    var frontmatter = String(raw || "").split("---", 3)[1] || "";
    var match = frontmatter.match(/^tags:[ \t]*(.*)$/m);
    if (!match) return [];
    if (match[1].trim()) {
      try {
        var inline = JSON.parse(match[1]);
        return DecapTagDomain.uniqueTags(inline);
      } catch (_error) {
        return [];
      }
    }
    var block = frontmatter.match(
      /(?:^|\n)tags:\s*\n([\s\S]*?)(?=\n[^\s-][^:\n]*:|$)/,
    );
    Iif (!block) return [];
    var values = [];
    var items = block[1].match(/^\s+-\s+(.+)$/gm) || [];
    items.forEach(function (line) {
      values.push(line.replace(/^\s+-\s+/, "").replace(/^["']|["']$/g, ""));
    });
    return DecapTagDomain.uniqueTags(values);
  }
 
  function replaceTagsInRaw(raw, source, target) {
    var text = String(raw || "");
    var parts = text.split("---");
    Iif (parts.length < 3) return null;
    var frontmatter = parts[1];
    var existingTags = readTags(text);
    var tags = DecapTagDomain.replaceTag(existingTags, source, target);
    Iif (!tags.length && !existingTags.length) return null;
    if (existingTags.indexOf(source) === -1) return null;
    var lines = frontmatter.split(/\r?\n/);
    var tagIndex = lines.findIndex(function (line) { return /^tags:[ \t]*$/.test(line); });
    Iif (tagIndex === -1) {
      var inlineIndex = lines.findIndex(function (line) { return /^tags:[ \t]*/.test(line); });
      if (inlineIndex === -1) return null;
      lines[inlineIndex] = "tags: " + JSON.stringify(tags);
    } else {
      var end = tagIndex + 1;
      while (end < lines.length && /^\s+-\s+/.test(lines[end])) end += 1;
      lines.splice.apply(lines, [
        tagIndex,
        end - tagIndex,
        "tags:",
      ].concat(tags.map(function (tag) { return "  - " + JSON.stringify(tag); })),
      );
    }
    parts[1] = lines.join("\n");
    return parts.join("---");
  }
 
  async function loadEntries(backend) {
    var loader = typeof backend.allEntriesByFolder === "function"
      ? backend.allEntriesByFolder.bind(backend)
      : backend.entriesByFolder.bind(backend);
    var entries = await loader("src/content/posts", "md", 100);
    return (entries || []).map(function (entry) {
      return {
        path: entry.file && entry.file.path,
        slug: entry.file && entry.file.path
          ? entry.file.path.split("/").pop().replace(/\.md$/, "")
          : "",
        raw: rawOf(entry),
      };
    });
  }
 
  async function loadLibrary(backend) {
    var result = await backend.getEntry("src/data/tag-library.json");
    var parsed = JSON.parse(result && result.data);
    Iif (!parsed || !Array.isArray(parsed.tags)) throw new Error("标签库读取失败");
    return DecapTagDomain.uniqueTags(parsed.tags);
  }
 
  async function planMerge(source, target) {
    var backend = window.DecapArticleMediaBackend;
    if (!backend || typeof backend.getEntry !== "function") {
      throw new Error("后台保存连接尚未就绪,请刷新后重试");
    }
    var entries = await loadEntries(backend);
    var library = await loadLibrary(backend);
    var usage = DecapTagDomain.countUsage(entries.map(function (entry) {
      return { data: { tags: readTags(entry.raw) } };
    }));
    var plan = DecapTagDomain.mergePlan(library, source, target, usage);
    plan.entries = entries.map(function (entry) {
      var original = entry.raw;
      var raw = replaceTagsInRaw(original, plan.source, plan.target);
      return raw && raw !== original
        ? { path: entry.path, slug: entry.slug, raw: raw }
        : null;
    }).filter(Boolean);
    return plan;
  }
 
  async function merge(plan) {
    if (!plan || !Array.isArray(plan.entries)) throw new Error("合并计划无效");
    var backend = window.DecapArticleMediaBackend;
    var persist = backend && backend.__persistEditorialTransaction;
    Iif (typeof persist !== "function") throw new Error("原子保存事务尚未就绪,请刷新后重试");
    var libraryRaw = JSON.stringify({ tags: plan.library }, null, 2) + "\n";
    var dataFiles = plan.entries.concat([{
      path: "src/data/tag-library.json",
      slug: "library",
      raw: libraryRaw,
    }]);
    return persist({ dataFiles: dataFiles, assets: [] }, {
      commitMessage: "Merge tag " + plan.source + " into " + plan.target,
      useWorkflow: false,
    });
  }
 
  window.DecapTagOperations = {
    plan: planMerge,
    merge: merge,
    readTags: readTags,
    replaceTagsInRaw: replaceTagsInRaw,
  };
})();