|  | 
|  | 1 | +// @ts-check | 
|  | 2 | + | 
|  | 3 | +// Loop through all files switching a reference like: | 
|  | 4 | +// | 
|  | 5 | +//  [13]: <src/compiler/checker.ts - function checkIfStatement> | 
|  | 6 | +// | 
|  | 7 | +// to | 
|  | 8 | +// | 
|  | 9 | +//  [13]: https://github.com/microsoft/TypeScript/blob/8d986554/src/compiler/checker.ts#L30308 | 
|  | 10 | +//  [13]: <src/compiler/checker.ts - function checkIfStatement> | 
|  | 11 | +//  | 
|  | 12 | +// GitHub uses the first link and ignores the 2nd. If the dupe is already there, | 
|  | 13 | +// then it updates to a new url. | 
|  | 14 | + | 
|  | 15 | +// Validate: | 
|  | 16 | +// node scripts/updateFindLinks.js scripts/fixtures/input.md | 
|  | 17 | + | 
|  | 18 | +// Write: | 
|  | 19 | +// node scripts/updateFindLinks.js scripts/fixtures/input.md --write | 
|  | 20 | + | 
|  | 21 | +const glob = require("glob"); | 
|  | 22 | +const { readFileSync, writeFileSync, existsSync } = require("fs"); | 
|  | 23 | +const { join } = require("path"); | 
|  | 24 | +const { execSync } = require("child_process"); | 
|  | 25 | +const escapeRegex = require("escape-regex-string"); | 
|  | 26 | + | 
|  | 27 | +if (!process.argv[2]) throw new Error("Did not include a glob for markdown files to change"); | 
|  | 28 | + | 
|  | 29 | +// This can be anything | 
|  | 30 | +const write = process.argv[3] !== undefined; | 
|  | 31 | + | 
|  | 32 | +const possibleTSRepo = ["../typescript-compiler", "../TypeScript", "TypeScript"]; | 
|  | 33 | +let repoPath = possibleTSRepo.find(f => existsSync(join(f, "package.json"))); | 
|  | 34 | +if (!repoPath) throw new Error("Could not find a TypeScript repo"); | 
|  | 35 | + | 
|  | 36 | +const repoHead = execSync(`git rev-parse HEAD | cut -c 1-8`, { cwd: repoPath, encoding: "utf8" }); | 
|  | 37 | +if (!repoHead) throw new Error("Could not get the git info from the sibling TypeScript repo"); | 
|  | 38 | + | 
|  | 39 | +const files = glob.sync(process.argv[2]); | 
|  | 40 | +if (!files.length) throw new Error("Did not get any files with that glob"); | 
|  | 41 | + | 
|  | 42 | +let failed = []; | 
|  | 43 | + | 
|  | 44 | +files.forEach(file => { | 
|  | 45 | +  if (file === "README.md") return; | 
|  | 46 | + | 
|  | 47 | +  let content = readFileSync(file, "utf8"); | 
|  | 48 | +  // https://regex101.com/r/w1dEG1/1 | 
|  | 49 | +  const regex = new RegExp(/\[.*]: <(.*) - (.*)>/g); | 
|  | 50 | + | 
|  | 51 | +  const matches = content.matchAll(regex) | 
|  | 52 | +  for (const result of matches) { | 
|  | 53 | +    const fileRef = result[1]; | 
|  | 54 | +    const searchTerm = result[2]; | 
|  | 55 | +    const original = `: <${fileRef} - ${searchTerm}>`; | 
|  | 56 | +    const originalLine = getLineOfStr(content, original)[0].number | 
|  | 57 | +    const reference = lineOfStr(content, originalLine-1).slice(1).split("]")[0] | 
|  | 58 | +    const lineToChange = lineOfStr(content, originalLine-2) | 
|  | 59 | +    const shouldReplace = lineToChange.includes("/blob/") | 
|  | 60 | +    try { | 
|  | 61 | +      const originalFile = readFileSync(join(repoPath, fileRef), "utf8"); | 
|  | 62 | +      const line = getLineNo(originalFile, new RegExp(escapeRegex(searchTerm))); | 
|  | 63 | +      const lineRef = line && line[0] && line[0].number ? `#L${line[0].number}` : ""; | 
|  | 64 | +      const replacement = `[${reference}]: https://github.com/microsoft/TypeScript/blob/${repoHead.trim()}/${fileRef}${lineRef}`; | 
|  | 65 | +      content = content.replace(lineToChange, shouldReplace ? replacement : lineToChange + "\n" + replacement); | 
|  | 66 | +    } catch (e) { | 
|  | 67 | +      console.log(e) | 
|  | 68 | +      failed.push([file, fileRef]); | 
|  | 69 | +    } | 
|  | 70 | +  } | 
|  | 71 | + | 
|  | 72 | +  if (write) { | 
|  | 73 | +    writeFileSync(file, content); | 
|  | 74 | +  } else { | 
|  | 75 | +    console.log(content); | 
|  | 76 | +  } | 
|  | 77 | +}); | 
|  | 78 | + | 
|  | 79 | +if (failed.length) { | 
|  | 80 | +  console.error("Could not find the following references to update:"); | 
|  | 81 | +  console.error(failed); | 
|  | 82 | + | 
|  | 83 | +  console.error("Either ping @orta if confused about this failure, or update the filepaths please. It's likely they've moved in the TypeScript repo."); | 
|  | 84 | +  process.exit(1); | 
|  | 85 | +} | 
|  | 86 | + | 
|  | 87 | +function lineOfStr(str, idx) { | 
|  | 88 | +  return str.split(/\r?\n/)[idx] | 
|  | 89 | +} | 
|  | 90 | + | 
|  | 91 | +function getLineOfStr(str, substr) { | 
|  | 92 | +  return str | 
|  | 93 | +    .split(/\r?\n/) | 
|  | 94 | +    .map(function(line, i) { | 
|  | 95 | +      if (line.includes(substr)) { | 
|  | 96 | +        return { | 
|  | 97 | +          number: i + 1, | 
|  | 98 | +          line | 
|  | 99 | +        }; | 
|  | 100 | +      } | 
|  | 101 | +    }) | 
|  | 102 | +    .filter(Boolean); | 
|  | 103 | +} | 
|  | 104 | + | 
|  | 105 | + | 
|  | 106 | +/*! | 
|  | 107 | + * line-number <https://github.com/jonschlinkert/line-number> | 
|  | 108 | + * | 
|  | 109 | + * Copyright (c) 2014 Jon Schlinkert, contributors. | 
|  | 110 | + * Licensed under the MIT License | 
|  | 111 | + */ | 
|  | 112 | +function getLineNo(str, re) { | 
|  | 113 | +  return str | 
|  | 114 | +    .split(/\r?\n/) | 
|  | 115 | +    .map(function(line, i) { | 
|  | 116 | +      if (re.test(line)) { | 
|  | 117 | +        return { | 
|  | 118 | +          line: line, | 
|  | 119 | +          number: i + 1, | 
|  | 120 | +          match: line.match(re)[0] | 
|  | 121 | +        }; | 
|  | 122 | +      } | 
|  | 123 | +    }) | 
|  | 124 | +    .filter(Boolean); | 
|  | 125 | +} | 
0 commit comments