@moonbit/markdown-linter
Advanced tools
Comparing version 0.1.13 to 0.1.14
#!/usr/bin/env node | ||
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
// @ts-check | ||
@@ -7,182 +9,167 @@ /* | ||
*/ | ||
const { parseArgs } = require("node:util"); | ||
const cli = parseArgs({ | ||
options: { | ||
version: { | ||
type: "boolean", | ||
short: "v", | ||
var MarkdownIt = require("markdown-it"); | ||
var node_child_process_1 = require("node:child_process"); | ||
var node_fs_1 = require("node:fs"); | ||
var node_path_1 = require("node:path"); | ||
var node_util_1 = require("node:util"); | ||
var temp_1 = require("temp"); | ||
var cli = (0, node_util_1.parseArgs)({ | ||
options: { | ||
version: { | ||
type: "boolean", | ||
short: "v", | ||
}, | ||
}, | ||
}, | ||
allowPositionals: true, | ||
allowPositionals: true, | ||
}); | ||
if (cli.values.version) { | ||
console.log(`Markdown linter ${require("./package.json").version}`); | ||
globalThis.process.exit(0); | ||
console.log("Markdown linter ".concat(require("./package.json").version)); | ||
globalThis.process.exit(0); | ||
} | ||
const files = cli.positionals; | ||
const MarkdownIt = require("markdown-it"); | ||
const fs = require("node:fs"); | ||
const path = require("node:path"); | ||
const { execSync } = require("node:child_process"); | ||
const temp = require("temp").track(); | ||
const md = new MarkdownIt(); | ||
var files = cli.positionals; | ||
var temp = (0, temp_1.track)(); | ||
var md = new MarkdownIt(); | ||
var hasErrors = false; | ||
for (const inputFile of files) { | ||
processMarkdown(inputFile); | ||
for (var _i = 0, files_1 = files; _i < files_1.length; _i++) { | ||
var inputFile = files_1[_i]; | ||
processMarkdown(inputFile); | ||
} | ||
function executeCommandLine(workingDir, command) { | ||
try { | ||
const output = execSync(command, { encoding: "utf-8", stdio: "pipe", cwd: workingDir }); | ||
return output.trim(); | ||
} catch (error) { | ||
return error.stdout.trim() + error.stderr.trim(); | ||
} | ||
try { | ||
var output = (0, node_child_process_1.execSync)(command, { encoding: "utf-8", stdio: "pipe", cwd: workingDir }); | ||
return output.trim(); | ||
} | ||
catch (error) { | ||
return error.stdout.trim() + error.stderr.trim(); | ||
} | ||
} | ||
function makeTempProject(projectName) { | ||
const projectPath = temp.mkdirSync(); | ||
fs.writeFileSync(path.join(projectPath, "/moon.mod.json"), `{ "name": "${projectName}" }`, "utf-8"); | ||
fs.writeFileSync(path.join(projectPath, "/moon.pkg.json"), `{}`, "utf-8"); | ||
return projectPath; | ||
var projectPath = temp.mkdirSync(); | ||
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(projectPath, "/moon.mod.json"), "{ \"name\": \"".concat(projectName, "\" }"), "utf-8"); | ||
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(projectPath, "/moon.pkg.json"), "{}", "utf-8"); | ||
return projectPath; | ||
} | ||
function processMarkdown(inputFile) { | ||
const readmeFilename = path.basename(inputFile); | ||
const readme = fs.readFileSync(inputFile, "utf-8"); | ||
// parse readme and find codeblocks | ||
const tokens = md.parse(readme, {}); | ||
var codeBlocks = []; | ||
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; | ||
if (info.length > 1) { | ||
switch (info[1].toLowerCase()) { | ||
case "expr": | ||
kind = "expr"; | ||
break; | ||
case "no-check": | ||
kind = "no-check"; | ||
break; | ||
default: | ||
kind = "normal"; | ||
var readmeFilename = (0, node_path_1.basename)(inputFile); | ||
var readme = (0, node_fs_1.readFileSync)(inputFile, "utf-8"); | ||
// parse readme and find codeblocks | ||
var tokens = md.parse(readme, {}); | ||
var codeBlocks = []; | ||
tokens.forEach(function (token, index) { | ||
var codeInfo = token.info.trim(); | ||
if (codeInfo.toLowerCase().startsWith("mbt") || codeInfo.toLowerCase().startsWith("moonbit")) { | ||
var info = codeInfo.split(" ").map(function (s) { return s.trim(); }); | ||
var kind; | ||
if (info.length > 1) { | ||
switch (info[1].toLowerCase()) { | ||
case "expr": | ||
kind = "expr"; | ||
break; | ||
case "no-check": | ||
kind = "no-check"; | ||
break; | ||
default: | ||
kind = "normal"; | ||
} | ||
} | ||
else { | ||
kind = "normal"; | ||
} | ||
var content = token.content, map = token.map; | ||
if (map) { | ||
codeBlocks.push({ | ||
content: content, | ||
kind: kind, | ||
beginLine: map[0] + 1, | ||
endLine: map[1] + 1 | ||
}); | ||
} | ||
} | ||
} else { | ||
kind = "normal"; | ||
} | ||
const { content, map } = token; | ||
if (map) { | ||
codeBlocks.push({ content, kind, beginLine: map[0] + 1, endLine: map[1] + 1 }); | ||
} | ||
}); | ||
// generate source map | ||
var sourceMap = []; | ||
var line = 1; | ||
function countLines(str) { | ||
return str.split("\n").length - 1; | ||
} | ||
}); | ||
// generate source map | ||
var sourceMap = []; | ||
var line = 1; | ||
function countLines(str) { | ||
return str.split("\n").length - 1; | ||
} | ||
var processedCodeBlocks = [] | ||
codeBlocks.forEach(block => { | ||
var wrapper; | ||
switch (block.kind) { | ||
case "expr": | ||
wrapper = { leading: "fn init {debug({\n", trailing: "\n})}\n" }; | ||
break; | ||
case "no-check": | ||
return; | ||
default: | ||
wrapper = { leading: "", trailing: "" }; | ||
break; | ||
} | ||
const leadingLines = countLines(wrapper.leading); | ||
const contentLines = countLines(block.content); | ||
const trailingLines = countLines(wrapper.trailing); | ||
sourceMap.push({ | ||
original: block.beginLine + 1, // 1 based line number in markdown | ||
generated: line + leadingLines, // 1 based line number in the generated mbt source | ||
var processedCodeBlocks = []; | ||
codeBlocks.forEach(function (block) { | ||
var wrapper; | ||
switch (block.kind) { | ||
case "expr": | ||
wrapper = { leading: "fn init {debug({\n", trailing: "\n})}\n" }; | ||
break; | ||
case "no-check": | ||
return; | ||
default: | ||
wrapper = { leading: "", trailing: "" }; | ||
break; | ||
} | ||
var leadingLines = countLines(wrapper.leading); | ||
var contentLines = countLines(block.content); | ||
var trailingLines = countLines(wrapper.trailing); | ||
sourceMap.push({ | ||
originalLine: block.beginLine + 1, // 1 based line number in markdown | ||
generatedLine: line + leadingLines, // 1 based line number in the generated mbt source | ||
}); | ||
sourceMap.push({ | ||
originalLine: block.endLine - 1, | ||
generatedLine: line + leadingLines + contentLines | ||
}); | ||
line += leadingLines + contentLines + trailingLines; | ||
block.content = wrapper.leading + block.content + wrapper.trailing; | ||
processedCodeBlocks.push(block); | ||
}); | ||
sourceMap.push({ | ||
original: block.endLine - 1, | ||
generated: line + leadingLines + contentLines | ||
}); | ||
line += leadingLines + contentLines + trailingLines; | ||
block.content = wrapper.leading + block.content + wrapper.trailing; | ||
processedCodeBlocks.push(block); | ||
}); | ||
// map location to real location in markdown | ||
function getRealLine(sourceMap, line) { | ||
function find(line, l, r) { | ||
if (l > r) return sourceMap[l]; | ||
var m = Math.floor((l + r) / 2); | ||
const currentLine = sourceMap[m].generated; | ||
if (currentLine > line) return find(line, l, m - 1); | ||
if (currentLine < line) return find(line, m + 1, r); | ||
return sourceMap[m]; | ||
console.log(sourceMap); | ||
// map location to real location in markdown | ||
function getRealLine(sourceMap, line) { | ||
function find(line, l, r) { | ||
if (l >= r) | ||
return sourceMap[l]; | ||
var m = Math.floor((l + r) / 2); | ||
console.log("m:", m, "len:", sourceMap.length, "item:", sourceMap[m]); | ||
var currentLine = sourceMap[m].generatedLine; | ||
if (currentLine > line) | ||
return find(line, l, m - 1); | ||
if (currentLine < line) | ||
return find(line, m + 1, r); | ||
return sourceMap[m]; | ||
} | ||
var _a = find(line, 0, sourceMap.length - 1), originalLine = _a.originalLine, generatedLine = _a.generatedLine; | ||
return originalLine + (line - generatedLine); | ||
} | ||
const { original, generated } = find(line, 0, sourceMap.length); | ||
return original + (line - generated); | ||
} | ||
const source = processedCodeBlocks.reduce((acc, { content }) => acc + content, ""); | ||
// create a temporary project to run type checking and testing | ||
const projectPath = makeTempProject(path.basename(inputFile, ".md")); | ||
fs.writeFileSync(path.join(projectPath, "main.mbt"), source, "utf-8"); | ||
// run moon test | ||
const checkOutput = executeCommandLine(projectPath, `moon test`); | ||
// cleanup the temporary project | ||
temp.cleanupSync(); | ||
// process the diagnostics | ||
const diagnosticPattern = /^(.+\.mbt):(\d+):(\d+)-(\d+):(\d+)/gm; | ||
const moonFailedPattern = /failed: moonc .+\n/g; | ||
const diagnostics = checkOutput | ||
.replace( // replace location with real location in markdown | ||
diagnosticPattern, | ||
(_, file, beginLine, beginColumn, endLine, endColumn) => { | ||
const realBeginLine = getRealLine(sourceMap, parseInt(beginLine)); | ||
const realEndLine = getRealLine(sourceMap, parseInt(endLine)); | ||
return `${readmeFilename}:${realBeginLine}:${beginColumn}-${realEndLine}:${endColumn}`; | ||
} | ||
) | ||
.replace( // remove unused output | ||
moonFailedPattern, | ||
_ => { | ||
var source = processedCodeBlocks.reduce(function (acc, _a) { | ||
var content = _a.content; | ||
return acc + content; | ||
}, ""); | ||
// create a temporary project to run type checking and testing | ||
var projectPath = makeTempProject((0, node_path_1.basename)(inputFile, ".md")); | ||
(0, node_fs_1.writeFileSync)((0, node_path_1.join)(projectPath, "main.mbt"), source, "utf-8"); | ||
// run moon test | ||
var checkOutput = executeCommandLine(projectPath, "moon test"); | ||
// cleanup the temporary project | ||
temp.cleanupSync(); | ||
// process the diagnostics | ||
var diagnosticPattern = /^(.+\.mbt):(\d+):(\d+)-(\d+):(\d+)/gm; | ||
var moonFailedPattern = /failed: moonc .+\n/g; | ||
var diagnostics = checkOutput | ||
.replace(// replace location with real location in markdown | ||
diagnosticPattern, function (_, file, beginLine, beginColumn, endLine, endColumn) { | ||
var realBeginLine = getRealLine(sourceMap, parseInt(beginLine)); | ||
var realEndLine = getRealLine(sourceMap, parseInt(endLine)); | ||
var fullPath = (0, node_path_1.join)(process.cwd(), inputFile); | ||
return "".concat(fullPath, ":").concat(realBeginLine, ":").concat(beginColumn, "-").concat(realEndLine, ":").concat(endColumn); | ||
}) | ||
.replace(// remove unused output | ||
moonFailedPattern, function (_) { | ||
hasErrors = true; | ||
return "" | ||
} | ||
) | ||
console.log(diagnostics); | ||
return ""; | ||
}); | ||
console.log(diagnostics); | ||
} | ||
if (hasErrors) { | ||
process.exit(1) | ||
} else { | ||
process.exit(0) | ||
process.exit(1); | ||
} | ||
else { | ||
process.exit(0); | ||
} |
{ | ||
"name": "@moonbit/markdown-linter", | ||
"version": "0.1.13", | ||
"version": "0.1.14", | ||
"description": "markdown linter for MoonBit", | ||
@@ -14,3 +14,13 @@ "main": "./markdown-linter.js", | ||
"temp": "^0.9.4" | ||
}, | ||
"devDependencies": { | ||
"@types/markdown-it": "^13.0.7", | ||
"@types/node": "^20.11.28", | ||
"@types/temp": "^0.9.4", | ||
"typescript": "^5.4.2" | ||
}, | ||
"scripts": { | ||
"dev": "tsc markdown-linter.ts && node markdown-linter.js", | ||
"prepublishOnly": "tsc markdown-linter.ts" | ||
} | ||
} |
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
26056
5
350
4
1