From 5075eff9bcf1e0691416eaf83cd587b413a123c5 Mon Sep 17 00:00:00 2001 From: Evan Gao Date: Thu, 1 Aug 2024 16:33:13 +0800 Subject: [PATCH] Adds basic LP utility (#3) --- README.md | 146 +++++++++++++++++++++++++-------------- markdown-linter.ts | 165 ++++++++++++++++++++++++++++++++------------- package-lock.json | 103 ++++++++++++++++------------ package.json | 2 +- 4 files changed, 278 insertions(+), 138 deletions(-) diff --git a/README.md b/README.md index 68874d6..1cc529b 100644 --- a/README.md +++ b/README.md @@ -2,89 +2,135 @@ This is a Markdown linter for MoonBit. It gathers all MoonBit codes in Markdown, checks them using MoonBit's compiler, and reports any diagnostics. -# Prerequisites +## Prerequisites To use the MoonBit Markdown Linter, you need to install the [MoonBit compiler](https://www.moonbitlang.com/download/). -# Install +## Install ``` npm install -g @moonbit/markdown-linter ``` -# Usage +## Usage -## Check Syntax and Type Errors +### Examples -Create a markdown file `fib.md`, write some MoonBit code in code block: +`mdlint` checks for possible errors in your moonbit code inside markdown documents: - # Fibonacci +````markdown +// test.md - Calculate the nth Fibonacci number using recursion and pattern matching. - - ```moonbit - fn fib(n : Int) -> Int { - match n { - 0 => 0 - 1 => true // type error here - _ => fib(n - 1) + fib(n - 2) - } +```moonbit // defaults to normal +fn id[T : Eq](x : T) -> T { + x +} +``` + +```moonbit expr +id(5) +``` + +```moonbit no-check +// this is not moonbit at all +unsafe def gcd_unsafe: ℕ -> ℕ -> ℕ + | a, 0 => a + | a, b + 1 => gcd_unsafe (b + 1) (a % (b + 1)) +``` + +```moonbit enclosed +fn fib(n : Int) -> Int { + match n { + 0 => 0 + 1 => true // type error here + _ => fib(n - 1) + fib(n - 2) } - ``` +} +``` +```` -Check it by MoonBit markdown linter. +Use `mdlint test.md` to check (the actual output is colored): ``` -$ mdlint fib.md -fib.md:6:4-6:7 Warning 001: Unused function 'fib' -fib.md:9:14-9:18 Expr Type Mismatch +./fib.md:19:4 // correctly mapped! +[E1001] Warning: Warning: Unused function 'fib' +./fib.md:22:14 +[E4014] Error: Expr Type Mismatch has type : Bool wanted : Int ``` -## Run Inline Test or Evaluate Expression +The line number is correctly mapped to that in the markdown document, this is especially useful for debugging. Note that for multiple markdown files, each of them results in a independent project. -In file identity.md: +### Environments - # Identity +As seen above, `mdlint` supports several code environments including - ```moonbit - fn id[T : Eq](x : T) -> T { - x - } - ``` +- `normal`: codeblocks marked w/ `normal` are copied as-is to generate moonbit project. Default behavior. Linting is performed. +- `expr`: codeblocks marked w/ `expr` are wrapped inside a `println()`, this environment only supports [expressions](https://www.moonbitlang.com/docs/syntax#expressions-and-statements). Linting is performed. +- `no-check`: codeblocks marked w/ `no-check` are ignored, not included in the generated project at all. Linting is NOT performed, suitable for writing pcode. +- `enclose`: codeblocks marked w/ `enclose` are enclosed inside a `init()`. Mainly used to avoid naming conflicts. Linting is performed. - You can also write expression directly. +### Flags - ```moonbit expr - id(5) - ``` +#### --suppress - Test function `id`. +It's common to write not-so-well-formed code (e.g. Unused variables) for demonstration purposes, not production. It would be annoying if the compiler keeps complaining about those trivial problems. `mdlint` acknowledges that and provides `--suppress,-s` to suppress specific compiler error/warning in the output. It can be parsed inline: - ```moonbit - test "id" { - if id(5) != 5 { return Result::Err("test failed") } +```` +```moonbit enclosed -e1001 -e1002 +fn fib(n : Int) -> Int { + match n { + 0 => 0 + 1 => true // type error here + _ => fib(n - 1) + fib(n - 2) } - ``` +} +``` +```` -Run test and evaluate the expression by `mdlint`: +or provided through cli: +```bash +mdlint -s=e1001,e1002 test.md # use comma-separated list ``` -$ mdlint identity.md -5 -running 1 tests in package identity -test identity::id ... ok -test result: 1 passed; 0 failed +- the special `-s=all-warnings` suppresses all warnings from `moon`. +- the `--ignore,-i` specifically ignores all error codes from codeblocks, useful for temporarily enabling error/warnings suppressed before. + +#### --dump + +`--dump,-d` dumps the project generated from the corresponding markdown document in the same directory with the extension `.proj`. e.g. `test.md` results in `test.md.proj`. + +#### -f + +A project generated by `mdlint` typically has the structure + ``` +. +├── README.md +├── lib +│ ├── fib.mbt +│ └── moon.pkg.json +├── moon.mod.json +├── moon.pkg.json +└── top.mbt +``` + +the `-f` flag is used to specify which moonbit source file the code should be copied to. This flag can only be parsed inline: -## Disable check +````markdown +// fib() now goes into lib/fib.mbt -You can also disable checking for some code block by sepcifying `no-check`. +```moonbit -e1001 -f=fib.mbt +fn fib(n : Int) -> Int { + match n { + 0 => 0 + 1 => true // type error here + _ => fib(n - 1) + fib(n - 2) + } +} +``` +```` - ```moonbit no-check - // some pseudo code - fn id(x : T) -> T - fn succ(x : T) -> T - ``` \ No newline at end of file +By default, codeblocks are copied to `top.mbt`. If `-f` is provided with a value other than `top.mbt` (such as `fib.mbt`), `mdlint` would place that codeblock inside `lib/*.mbt`. This hopefully provides very basic literate programming utility. diff --git a/markdown-linter.ts b/markdown-linter.ts index 42a7a30..921f829 100755 --- a/markdown-linter.ts +++ b/markdown-linter.ts @@ -2,11 +2,17 @@ // @ts-check /* * Markdown linter for MoonBit. - * Usage: node markdown_linter.js [args] + * Usage: node markdown_linter.js [args] */ import * as MarkdownIt from "markdown-it"; import { execSync } from "node:child_process"; -import { readFileSync, writeFileSync } from "node:fs"; +import { + cpSync, + readFileSync, + rmSync, + writeFile, + writeFileSync, +} from "node:fs"; import { join, basename } from "node:path"; import { parseArgs } from "node:util"; import { track } from "temp"; @@ -36,6 +42,7 @@ const cli = parseArgs({ ignore: { type: "boolean", short: "i", + default: false, description: "Ignore error codes from codeblocks", }, }, @@ -44,18 +51,18 @@ const cli = parseArgs({ if (cli.values.help) { console.log(` -Usage: mdlint [args] +Usage: mdlint [args] Options: -h, --help Display this help message and exit -v, --version Display version information and exit - -d, --dump Dump generated moon source code + -d, --dump Dump generated moon source project -s, --suppress | Suppress warnings from given comma-separated list | all-warnings Suppress all warnings -i, --ignore Ignore error codes from codeblocks Example: - mdlint README.md -g -s=e1001,e1002 + mdlint README.md -d -s=e1001,e1002 `); process.exit(0); } @@ -73,7 +80,10 @@ if (cli.values.suppress) { globalWarningsSuppressed = true; } else { errSet = new Set( - cli.values.suppress.split(",").map((s) => parseInt(s.substring(1))) + cli.values.suppress + .replace("=", "") + .split(",") + .map((s) => parseInt(s.substring(1))) ); } } @@ -119,19 +129,39 @@ function executeCommandLine(workingDir, command) { return output.trim(); } catch (error) { hasErrors = true; - return error.stderr.trim() + " " + error.stdout.trim(); + return error.stdout.trim(); + } +} + +function removeFiles(...paths: string[]) { + try { + paths.forEach((path) => { + rmSync(path); + }); + } catch (error) { + console.log("Error: " + error.message); } } function makeTempProject(projectName) { const projectPath = temp.mkdirSync(); - writeFileSync( - join(projectPath, "/moon.mod.json"), - `{ "name": "${projectName}" }`, - "utf-8" - ); - writeFileSync(join(projectPath, "/moon.pkg.json"), `{}`, "utf-8"); - return projectPath; + // writeFileSync( + // join(projectPath, "/moon.mod.json"), + // `{ "name": "${projectName}" }`, + // "utf-8" + // ); + // writeFileSync(join(projectPath, "/moon.pkg.json"), `{}`, "utf-8"); + executeCommandLine(projectPath, `moon new ${projectName} --no-license --lib`); + try { + removeFiles( + join(projectPath, projectName, "top.mbt"), + join(projectPath, projectName, "lib", "hello.mbt"), + join(projectPath, projectName, "lib", "hello_test.mbt") + ); + } catch (error) { + console.log("Error: " + error.message); + } + return join(projectPath, projectName); } type LocationMapping = { @@ -144,6 +174,7 @@ type CodeBlock = { kind: "normal" | "expr" | "no-check" | "enclose"; beginLine: number; endLine: number; + fileBelonged: string; }; function processMarkdown(inputFile) { @@ -152,17 +183,17 @@ function processMarkdown(inputFile) { // parse readme and find codeblocks const tokens = md.parse(readme, {}); - var codeBlocks: Array = []; - + const codeBlocks: Array = []; + const projectPath = makeTempProject(basename(inputFile, ".md")); tokens.forEach((token, index) => { const codeInfo = token.info.trim(); - if ( codeInfo.toLowerCase().startsWith("mbt") || codeInfo.toLowerCase().startsWith("moonbit") ) { const info = codeInfo.split(" ").map((s) => s.trim()); var kind; + var fileBelonged: string = "top.mbt"; if (info.length > 1) { switch (info[1].toLowerCase()) { case "expr": @@ -178,12 +209,17 @@ function processMarkdown(inputFile) { kind = "normal"; } // parse error codes from codeblocks - if (cli.values.ignore == false) { - info.slice(1).forEach((arg) => { - const errCodes = arg.match(/(?<=-e)\d+/gi); - errCodes?.forEach((errCode) => errSet.add(parseInt(errCode))); - }); + { + if (cli.values.ignore == false) + info.forEach((arg) => { + const errCodes = arg.match(/(?<=-e)\d+/gi); + errCodes?.forEach((errCode) => errSet.add(parseInt(errCode))); + }); } + info.forEach((arg) => { + const fileNames = arg.match(/(?<=-f=).*mbt/gi); + fileBelonged = fileNames ? basename(fileNames[0]) : fileBelonged; + }); } else { kind = "normal"; } @@ -194,20 +230,21 @@ function processMarkdown(inputFile) { kind, beginLine: map[0] + 1, endLine: map[1] + 1, + fileBelonged: fileBelonged, }); } } }); // generate source map - var sourceMap: Array = []; - var line = 1; + const sourceMap: Map> = new Map(); + const line: Map = new Map(); function countLines(str: string) { return str.split("\n").length - 1; } - var processedCodeBlocks: Array = []; + const processedCodeBlocks: Map> = new Map(); codeBlocks.forEach((block) => { var wrapper: { leading: string; trailing: string }; @@ -225,28 +262,47 @@ function processMarkdown(inputFile) { break; } + line.set(block.fileBelonged, line.get(block.fileBelonged) || 1); // set default line number + + // initialize source map and processed code blocks + sourceMap.set( + block.fileBelonged, + sourceMap.get(block.fileBelonged) || new Array() + ); + processedCodeBlocks.set( + block.fileBelonged, + processedCodeBlocks.get(block.fileBelonged) || new Array() + ); + const leadingLines = countLines(wrapper.leading); const contentLines = countLines(block.content); const trailingLines = countLines(wrapper.trailing); - sourceMap.push({ + sourceMap.get(block.fileBelonged)!.push({ originalLine: block.beginLine + 1, // 1 based line number in markdown - generatedLine: line + leadingLines, // 1 based line number in the generated mbt source + generatedLine: line.get(block.fileBelonged)! + leadingLines, // 1 based line number in the generated mbt source }); - sourceMap.push({ + sourceMap.get(block.fileBelonged)!.push({ originalLine: block.endLine - 1, - generatedLine: line + leadingLines + contentLines, + generatedLine: + line.get(block.fileBelonged)! + leadingLines + contentLines, }); - line += leadingLines + contentLines + trailingLines; + line.set( + block.fileBelonged, + line.get(block.fileBelonged)! + + leadingLines + + contentLines + + trailingLines + ); block.content = wrapper.leading + (block.kind == "expr" || block.kind == "enclose" ? block.content.replace(/^/gm, " ") : block.content) + wrapper.trailing; - processedCodeBlocks.push(block); + processedCodeBlocks.get(block.fileBelonged)!.push(block); }); // map location to real location in markdown @@ -263,28 +319,43 @@ function processMarkdown(inputFile) { return originalLine + (line - generatedLine); } - const source = processedCodeBlocks.reduce( - (acc, { content }) => acc + content, - "" - ); + const source: Map = new Map(); + + for (const [fileName, CodeBlocks] of processedCodeBlocks) { + const sourceCode = CodeBlocks.reduce( + (acc, { content }) => acc + content, + "" + ); + source.set(fileName, sourceCode); + } // create a temporary project to run type checking and testing - const projectPath = makeTempProject(basename(inputFile, ".md")); - writeFileSync(join(projectPath, "main.mbt"), source, "utf-8"); + source.forEach((s, fileName) => { + if (fileName == "top.mbt") { + writeFileSync(join(projectPath, fileName), s, "utf-8"); + } else { + writeFileSync(join(projectPath, "lib", fileName), s, "utf-8"); + } + }); // run moon test const checkOutput = executeCommandLine( projectPath, - `moon check --output-json -q` + `moon check --output-json` ); const errList = checkOutput - .replace(/error: failed when checking.*\n/, "") .split("\n") .map((err) => { - return JSON.parse(err) as Diagnostic; - }); + try { + return JSON.parse(err) as Diagnostic; + } catch (error) { + return null; + } + }) + .filter((err) => err != null); + console.log("Supressed: " + Array.from(errSet)); for (const err of errList) { const { level, loc, message, error_code } = err; const { path, start, end } = loc; @@ -301,13 +372,16 @@ function processMarkdown(inputFile) { ? "\x1b[1;33mWarning\x1b[0m" : level; const errCode = `\x1b[31m[E${error_code}]\x1b[0m`; - const realBeginLine = getRealLine(sourceMap, start.line); - const realEndLine = getRealLine(sourceMap, end.line); + const realBeginLine = getRealLine( + sourceMap.get(basename(path))!, + start.line + ); + const realEndLine = getRealLine(sourceMap.get(basename(path))!, end.line); const errMsg = message.replace( new RegExp(path + ":(\\d+):(\\d+)"), (_, l, c) => `\x1b[4;37m${inputFile}:${getRealLine( - sourceMap, + sourceMap.get(basename(path))!, parseInt(l) )}:${c}\x1b[0m` ); @@ -323,7 +397,8 @@ function processMarkdown(inputFile) { // dump generated code if (cli.values.dump) { - writeFileSync(inputFile + ".mbt", source, "utf-8"); + // writeFileSync(inputFile + ".mbt", source, "utf-8"); + cpSync(projectPath, inputFile + ".proj", { recursive: true }); } // cleanup the temporary project diff --git a/package-lock.json b/package-lock.json index d4226f9..3f1e8bc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,24 +1,25 @@ { "name": "@moonbit/markdown-linter", - "version": "0.1.17", + "version": "0.1.18", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@moonbit/markdown-linter", - "version": "0.1.17", + "version": "0.1.18", "license": "ISC", "dependencies": { - "markdown-it": "^14.0.0", + "markdown-it": "^14.1.0", "temp": "^0.9.4" }, "bin": { "mdlint": "markdown-linter.js" }, "devDependencies": { - "@types/markdown-it": "^13.0.7", - "@types/node": "^20.11.28", - "typescript": "^5.4.2" + "@types/markdown-it": "^13.0.8", + "@types/node": "^20.14.1", + "@types/temp": "^0.9.4", + "typescript": "^5.4.5" } }, "node_modules/@types/linkify-it": { @@ -28,13 +29,13 @@ "dev": true }, "node_modules/@types/markdown-it": { - "version": "13.0.7", - "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-13.0.7.tgz", - "integrity": "sha512-U/CBi2YUUcTHBt5tjO2r5QV/x0Po6nsYwQU4Y04fBS6vfoImaiZ6f8bi3CjTCxBPQSO1LMyUqkByzi8AidyxfA==", + "version": "13.0.9", + "resolved": "https://registry.npmmirror.com/@types/markdown-it/-/markdown-it-13.0.9.tgz", + "integrity": "sha512-1XPwR0+MgXLWfTn9gCsZ55AHOKW1WN+P9vr0PaQh5aerR9LLQXUbjfEAFhjmEmyoYFWAyuN2Mqkn40MZ4ukjBw==", "dev": true, "dependencies": { - "@types/linkify-it": "*", - "@types/mdurl": "*" + "@types/linkify-it": "^3", + "@types/mdurl": "^1" } }, "node_modules/@types/mdurl": { @@ -44,14 +45,23 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.11.28", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.28.tgz", - "integrity": "sha512-M/GPWVS2wLkSkNHVeLkrF2fD5Lx5UC4PxA0uZcKc6QqbIQUJyW1jVjueJYi1z8n0I5PxYrtpnPnWglE+y9A0KA==", + "version": "20.14.13", + "resolved": "https://registry.npmmirror.com/@types/node/-/node-20.14.13.tgz", + "integrity": "sha512-+bHoGiZb8UiQ0+WEtmph2IWQCjIqg8MDZMAV+ppRRhUZnquF5mQkP/9vpSwJClEiSM/C7fZZExPzfU0vJTyp8w==", "dev": true, "dependencies": { "undici-types": "~5.26.4" } }, + "node_modules/@types/temp": { + "version": "0.9.4", + "resolved": "https://registry.npmmirror.com/@types/temp/-/temp-0.9.4.tgz", + "integrity": "sha512-+VfWIwrlept2VBTj7Y2wQnI/Xfscy1u8Pyj/puYwss6V1IblXn1x7S0S9eFh6KyBolgLCm+rUFzhFAbdkR691g==", + "dev": true, + "dependencies": { + "@types/node": "*" + } + }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -134,16 +144,16 @@ } }, "node_modules/markdown-it": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.0.0.tgz", - "integrity": "sha512-seFjF0FIcPt4P9U39Bq1JYblX0KZCjDLFFQPHpL5AzHpqPEKtosxmdq/LTVZnjfH7tjt9BxStm+wXcDBNuYmzw==", + "version": "14.1.0", + "resolved": "https://registry.npmmirror.com/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", "dependencies": { "argparse": "^2.0.1", "entities": "^4.4.0", "linkify-it": "^5.0.0", "mdurl": "^2.0.0", "punycode.js": "^2.3.1", - "uc.micro": "^2.0.0" + "uc.micro": "^2.1.0" }, "bin": { "markdown-it": "bin/markdown-it.mjs" @@ -232,9 +242,9 @@ } }, "node_modules/typescript": { - "version": "5.4.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.2.tgz", - "integrity": "sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==", + "version": "5.5.4", + "resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.5.4.tgz", + "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -245,9 +255,9 @@ } }, "node_modules/uc.micro": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.0.0.tgz", - "integrity": "sha512-DffL94LsNOccVn4hyfRe5rdKa273swqeA5DJpMOeFmEn1wCDc7nAbbB0gXlgBCL7TNzeTv6G7XVWzan7iJtfig==" + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==" }, "node_modules/undici-types": { "version": "5.26.5", @@ -269,13 +279,13 @@ "dev": true }, "@types/markdown-it": { - "version": "13.0.7", - "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-13.0.7.tgz", - "integrity": "sha512-U/CBi2YUUcTHBt5tjO2r5QV/x0Po6nsYwQU4Y04fBS6vfoImaiZ6f8bi3CjTCxBPQSO1LMyUqkByzi8AidyxfA==", + "version": "13.0.9", + "resolved": "https://registry.npmmirror.com/@types/markdown-it/-/markdown-it-13.0.9.tgz", + "integrity": "sha512-1XPwR0+MgXLWfTn9gCsZ55AHOKW1WN+P9vr0PaQh5aerR9LLQXUbjfEAFhjmEmyoYFWAyuN2Mqkn40MZ4ukjBw==", "dev": true, "requires": { - "@types/linkify-it": "*", - "@types/mdurl": "*" + "@types/linkify-it": "^3", + "@types/mdurl": "^1" } }, "@types/mdurl": { @@ -285,14 +295,23 @@ "dev": true }, "@types/node": { - "version": "20.11.28", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.28.tgz", - "integrity": "sha512-M/GPWVS2wLkSkNHVeLkrF2fD5Lx5UC4PxA0uZcKc6QqbIQUJyW1jVjueJYi1z8n0I5PxYrtpnPnWglE+y9A0KA==", + "version": "20.14.13", + "resolved": "https://registry.npmmirror.com/@types/node/-/node-20.14.13.tgz", + "integrity": "sha512-+bHoGiZb8UiQ0+WEtmph2IWQCjIqg8MDZMAV+ppRRhUZnquF5mQkP/9vpSwJClEiSM/C7fZZExPzfU0vJTyp8w==", "dev": true, "requires": { "undici-types": "~5.26.4" } }, + "@types/temp": { + "version": "0.9.4", + "resolved": "https://registry.npmmirror.com/@types/temp/-/temp-0.9.4.tgz", + "integrity": "sha512-+VfWIwrlept2VBTj7Y2wQnI/Xfscy1u8Pyj/puYwss6V1IblXn1x7S0S9eFh6KyBolgLCm+rUFzhFAbdkR691g==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -363,16 +382,16 @@ } }, "markdown-it": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.0.0.tgz", - "integrity": "sha512-seFjF0FIcPt4P9U39Bq1JYblX0KZCjDLFFQPHpL5AzHpqPEKtosxmdq/LTVZnjfH7tjt9BxStm+wXcDBNuYmzw==", + "version": "14.1.0", + "resolved": "https://registry.npmmirror.com/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", "requires": { "argparse": "^2.0.1", "entities": "^4.4.0", "linkify-it": "^5.0.0", "mdurl": "^2.0.0", "punycode.js": "^2.3.1", - "uc.micro": "^2.0.0" + "uc.micro": "^2.1.0" } }, "mdurl": { @@ -437,15 +456,15 @@ } }, "typescript": { - "version": "5.4.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.2.tgz", - "integrity": "sha512-+2/g0Fds1ERlP6JsakQQDXjZdZMM+rqpamFZJEKh4kwTIn3iDkgKtby0CeNd5ATNZ4Ry1ax15TMx0W2V+miizQ==", + "version": "5.5.4", + "resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.5.4.tgz", + "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", "dev": true }, "uc.micro": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.0.0.tgz", - "integrity": "sha512-DffL94LsNOccVn4hyfRe5rdKa273swqeA5DJpMOeFmEn1wCDc7nAbbB0gXlgBCL7TNzeTv6G7XVWzan7iJtfig==" + "version": "2.1.0", + "resolved": "https://registry.npmmirror.com/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==" }, "undici-types": { "version": "5.26.5", diff --git a/package.json b/package.json index 999b0da..a18312c 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,7 @@ "typescript": "^5.4.5" }, "scripts": { - "dev": "tsc markdown-linter.ts && node markdown-linter.js", + "dev": "tsc markdown-linter.ts --downlevelIteration && node markdown-linter.js", "prepublishOnly": "tsc markdown-linter.ts" }, "packageManager": "pnpm@9.1.4+sha256.30a1801ac4e723779efed13a21f4c39f9eb6c9fbb4ced101bce06b422593d7c9"