|
| 1 | +import marko as md |
| 2 | +import os |
| 3 | +import argparse |
| 4 | +import sys |
| 5 | + |
| 6 | +def extract_inline_code(file_path, languages): |
| 7 | + """extract inline code, language from markdown""" |
| 8 | + |
| 9 | + with open(file_path, "r") as f: |
| 10 | + content = f.read() |
| 11 | + |
| 12 | + parser = md.parser.Parser() |
| 13 | + ast = parser.parse(content) |
| 14 | + |
| 15 | + code_snippet_count = 0 |
| 16 | + for child in ast.children: |
| 17 | + # TODO: add a way to exclude a code snippet |
| 18 | + if isinstance(child, md.block.FencedCode) and child.lang in languages: |
| 19 | + code_snippet_count+=1 |
| 20 | + yield (code_snippet_count, child.lang, child.children[0].children) |
| 21 | + |
| 22 | +ignored_dirs = [".git"] |
| 23 | + |
| 24 | +def get_markdown_files(start, languages): |
| 25 | + """locate all markdown files and call check_code_syntax on them""" |
| 26 | + |
| 27 | + if os.path.isfile(start): |
| 28 | + check_code_syntax(start, languages) |
| 29 | + |
| 30 | + for root, dirs, files in os.walk(start): |
| 31 | + dirs[:] = [d for d in dirs if d not in ignored_dirs] |
| 32 | + |
| 33 | + for f in files: |
| 34 | + if f.endswith('.markdown') or f.endswith('.md'): |
| 35 | + path = os.path.join(root, f) |
| 36 | + check_code_syntax(path, languages) |
| 37 | + |
| 38 | + |
| 39 | +def check_code_syntax(path, languages): |
| 40 | + """check syntax of every code snippet of one specific markdown file""" |
| 41 | + |
| 42 | + iter = extract_inline_code(path, languages) |
| 43 | + for i, language, code_snippet in iter: |
| 44 | + |
| 45 | + file_name = f"{path}.snippet-{i}" |
| 46 | + match language: |
| 47 | + case "cf3": |
| 48 | + write_file(file_name, "cf", code_snippet) |
| 49 | + # TODO: run cf-promises on the file |
| 50 | + # maybe also check run cf-agent and check if output is correct |
| 51 | + break |
| 52 | + case "json": |
| 53 | + write_file(file_name, "json", code_snippet) |
| 54 | + # TODO: check json syntax and run cfbs pretty |
| 55 | + break |
| 56 | + case "yaml": |
| 57 | + write_file(file_name, "yml", code_snippet) |
| 58 | + # TODO: check yaml syntax |
| 59 | + break |
| 60 | + |
| 61 | + |
| 62 | +def write_file(file_name, extension, code_snippet): |
| 63 | + with open(f"{file_name}.{extension}", 'w') as f: |
| 64 | + f.write(code_snippet) |
| 65 | + |
| 66 | + |
| 67 | +def parse_args(): |
| 68 | + parser = argparse.ArgumentParser(prog="Markdown inline code syntax checker", |
| 69 | + description="checks the syntax of documentation inline code" |
| 70 | + ) |
| 71 | + parser.add_argument("--path", "-p", help="path of file or directory to check syntax on", default=".", required=False) |
| 72 | + parser.add_argument("--languages", "-l", nargs='+', help="languages to check syntax of", default=["cf3", "json", "yaml"], required=False) |
| 73 | + |
| 74 | + return parser.parse_args() |
| 75 | + |
| 76 | + |
| 77 | +if __name__ == "__main__": |
| 78 | + supported_languages = ["cf3", "json", "yaml"] |
| 79 | + args = parse_args() |
| 80 | + |
| 81 | + if not os.path.exists(args.path): |
| 82 | + print("[error] This path doesn't exist") |
| 83 | + sys.exit(-1) |
| 84 | + |
| 85 | + for language in args.languages: |
| 86 | + if language not in supported_languages: |
| 87 | + print(f"[error] Unsupported language '{language}'. The supported languages are: {", ".join(supported_languages)}") |
| 88 | + sys.exit(-1) |
| 89 | + |
| 90 | + get_markdown_files(args.path, args.languages) |
0 commit comments