@borrowdev/docval 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/Cargo.toml +6 -0
- package/assets/package.json +6 -0
- package/dist/adapters/rust/index.mjs +1 -1
- package/dist/adapters/rust/index.mjs.map +1 -1
- package/dist/adapters/rust/utils.mjs +13 -3
- package/dist/adapters/rust/utils.mjs.map +1 -1
- package/dist/constants.mjs +9 -0
- package/dist/constants.mjs.map +1 -0
- package/dist/remark/plugin.d.mts +3 -4
- package/dist/remark/plugin.d.mts.map +1 -1
- package/dist/remark/plugin.mjs +56 -4
- package/dist/remark/plugin.mjs.map +1 -1
- package/dist/utils.mjs +17 -1
- package/dist/utils.mjs.map +1 -1
- package/package.json +2 -1
|
@@ -7,7 +7,7 @@ import { ADAPTER_OPTIONS } from "./constants.mjs";
|
|
|
7
7
|
async function adapterRust(code, options) {
|
|
8
8
|
const environmentPath = await createEnvironment(code, await getImports(code), { environmentPath: options.environment });
|
|
9
9
|
try {
|
|
10
|
-
await execUntilExit(
|
|
10
|
+
await execUntilExit(`cargo run`, environmentPath);
|
|
11
11
|
} finally {
|
|
12
12
|
if (!options.environment) await cleanupEnvironment(environmentPath);
|
|
13
13
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../../src/adapters/rust/index.ts"],"sourcesContent":["import { getOptions } from \"@/remark/utils\";\nimport { createEnvironment, getImports } from \"./utils\";\nimport { ADAPTER_OPTIONS } from \"./constants\";\nimport { cleanupEnvironment, execUntilExit } from \"@/utils\";\n\ntype AdapterOptions = {\n environment?: string;\n};\n\nasync function adapterRust(code: string, options: AdapterOptions): Promise<void> {\n const imports = await getImports(code);\n const environmentPath = await createEnvironment(code, imports, {\n environmentPath: options.environment,\n });\n\n try {\n await execUntilExit(
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../../src/adapters/rust/index.ts"],"sourcesContent":["import { getOptions } from \"@/remark/utils\";\nimport { createEnvironment, getImports } from \"./utils\";\nimport { ADAPTER_OPTIONS } from \"./constants\";\nimport { cleanupEnvironment, execUntilExit } from \"@/utils\";\n\ntype AdapterOptions = {\n environment?: string;\n};\n\nasync function adapterRust(code: string, options: AdapterOptions): Promise<void> {\n const imports = await getImports(code);\n const environmentPath = await createEnvironment(code, imports, {\n environmentPath: options.environment,\n });\n\n try {\n await execUntilExit(`cargo run`, environmentPath);\n } finally {\n if (!options.environment) {\n await cleanupEnvironment(environmentPath);\n }\n }\n}\n\nexport default (code: string, metadata: string[]) =>\n adapterRust(code, getOptions(metadata, ADAPTER_OPTIONS));\n"],"mappings":";;;;;;AASA,eAAe,YAAY,MAAc,SAAwC;CAE/E,MAAM,kBAAkB,MAAM,kBAAkB,MADhC,MAAM,WAAW,KAAK,EACyB,EAC7D,iBAAiB,QAAQ,aAC1B,CAAC;AAEF,KAAI;AACF,QAAM,cAAc,aAAa,gBAAgB;WACzC;AACR,MAAI,CAAC,QAAQ,YACX,OAAM,mBAAmB,gBAAgB;;;AAK/C,oBAAgB,MAAc,aAC5B,YAAY,MAAM,WAAW,UAAU,gBAAgB,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { execUntilExit, logger } from "../../utils.mjs";
|
|
1
|
+
import { execUntilExit, logger, parseDirectives } from "../../utils.mjs";
|
|
2
2
|
import { randomUUID } from "crypto";
|
|
3
3
|
import { tmpdir } from "os";
|
|
4
4
|
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
@@ -33,6 +33,15 @@ async function getImports(code) {
|
|
|
33
33
|
parser.setLanguage(language);
|
|
34
34
|
const tree = parser.parse(code);
|
|
35
35
|
const imports = [];
|
|
36
|
+
const directives = parseDirectives(tree?.rootNode.descendantsOfType("line_comment").map((n) => n.text) ?? []);
|
|
37
|
+
for (const directive of directives["cargo-add-options"] ?? []) {
|
|
38
|
+
const [packageSpec, ...options] = directive;
|
|
39
|
+
imports.push({
|
|
40
|
+
package: packageSpec,
|
|
41
|
+
cargoAddOptions: options.join(" "),
|
|
42
|
+
isExternal: true
|
|
43
|
+
});
|
|
44
|
+
}
|
|
36
45
|
for (const node of tree.rootNode.children) {
|
|
37
46
|
let crateName;
|
|
38
47
|
if (node.type === "use_declaration") {
|
|
@@ -42,6 +51,7 @@ async function getImports(code) {
|
|
|
42
51
|
}
|
|
43
52
|
if (crateName) {
|
|
44
53
|
if (ERROR_CRATES.has(crateName)) throw new Error(`Unsupported import of "${crateName}". Imports of ${Array.from(ERROR_CRATES).map((c) => `"${c}"`).join(", ")} are not supported.`);
|
|
54
|
+
if (imports.some((i) => i.package === crateName)) continue;
|
|
45
55
|
imports.push({
|
|
46
56
|
package: crateName,
|
|
47
57
|
isExternal: !BUILTIN_CRATES.has(crateName)
|
|
@@ -66,9 +76,9 @@ async function createEnvironment(code, imports, options = {}) {
|
|
|
66
76
|
await mkdir(`${path}/src`, { recursive: true });
|
|
67
77
|
await Promise.all([writeFile(`${path}/Cargo.toml`, manifest), writeFile(`${path}/src/main.rs`, code)]);
|
|
68
78
|
if (imports.length > 0) {
|
|
69
|
-
const crates = imports.filter((i) => i.isExternal)
|
|
79
|
+
const crates = imports.filter((i) => i.isExternal);
|
|
70
80
|
logger.info("Installing crates", crates);
|
|
71
|
-
await execUntilExit(`cargo add ${
|
|
81
|
+
for (const crate of crates) await execUntilExit(`cargo add ${crate.package}${crate.cargoAddOptions ? ` ${crate.cargoAddOptions}` : ""}`, path);
|
|
72
82
|
}
|
|
73
83
|
logger.info("Created Rust environment at", path);
|
|
74
84
|
return path;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.mjs","names":[],"sources":["../../../src/adapters/rust/utils.ts"],"sourcesContent":["import { Language, Parser } from \"web-tree-sitter\";\nimport { createRequire } from \"module\";\nimport { tmpdir } from \"os\";\nimport { mkdir, readFile, writeFile } from \"fs/promises\";\nimport { join } from \"path\";\nimport { randomUUID } from \"crypto\";\nimport { execUntilExit, logger } from \"@/utils\";\n\nconst require = createRequire(import.meta.url);\nconst RUST_WASM_PATH = require.resolve(\"tree-sitter-rust/tree-sitter-rust.wasm\");\n\nlet rustLanguage: Language | undefined;\n\nasync function getRustLanguage(): Promise<Language> {\n if (!rustLanguage) {\n await Parser.init();\n rustLanguage = await Language.load(RUST_WASM_PATH);\n }\n return rustLanguage!;\n}\n\nconst BUILTIN_CRATES = new Set([\"std\", \"core\", \"alloc\", \"crate\"]);\nconst ERROR_CRATES = new Set([\"crate\", \"super\", \"self\"]);\n\ntype Import = {\n isExternal: boolean;\n package: string;\n};\n\nasync function getImports(code: string): Promise<Import[]> {\n const language = await getRustLanguage();\n const parser = new Parser();\n parser.setLanguage(language);\n const tree = parser.parse(code);\n const imports: Import[] = [];\n\n for (const node of tree!.rootNode.children) {\n let crateName: string | undefined;\n\n if (node.type === \"use_declaration\") {\n const arg = node.child(1);\n if (!arg) continue;\n if (arg.type === \"identifier\" || arg.type === \"scoped_identifier\") {\n crateName = arg.text.split(\"::\")[0];\n }\n }\n\n if (crateName) {\n if (ERROR_CRATES.has(crateName)) {\n throw new Error(\n `Unsupported import of \"${crateName}\". ` +\n `Imports of ${Array.from(ERROR_CRATES)\n .map((c) => `\"${c}\"`)\n .join(\", \")} are not supported.`,\n );\n }\n\n imports.push({\n package: crateName,\n isExternal: !BUILTIN_CRATES.has(crateName),\n });\n }\n }\n
|
|
1
|
+
{"version":3,"file":"utils.mjs","names":[],"sources":["../../../src/adapters/rust/utils.ts"],"sourcesContent":["import { Language, Parser } from \"web-tree-sitter\";\nimport { createRequire } from \"module\";\nimport { tmpdir } from \"os\";\nimport { mkdir, readFile, writeFile } from \"fs/promises\";\nimport { join } from \"path\";\nimport { randomUUID } from \"crypto\";\nimport { execUntilExit, logger, parseDirectives } from \"@/utils\";\n\nconst require = createRequire(import.meta.url);\nconst RUST_WASM_PATH = require.resolve(\"tree-sitter-rust/tree-sitter-rust.wasm\");\n\nlet rustLanguage: Language | undefined;\n\nasync function getRustLanguage(): Promise<Language> {\n if (!rustLanguage) {\n await Parser.init();\n rustLanguage = await Language.load(RUST_WASM_PATH);\n }\n return rustLanguage!;\n}\n\nconst BUILTIN_CRATES = new Set([\"std\", \"core\", \"alloc\", \"crate\"]);\nconst ERROR_CRATES = new Set([\"crate\", \"super\", \"self\"]);\n\ntype Import = {\n isExternal: boolean;\n package: string;\n cargoAddOptions?: string;\n};\n\nasync function getImports(code: string): Promise<Import[]> {\n const language = await getRustLanguage();\n const parser = new Parser();\n parser.setLanguage(language);\n const tree = parser.parse(code);\n const imports: Import[] = [];\n const comments = tree?.rootNode.descendantsOfType(\"line_comment\").map((n) => n.text) ?? [];\n const directives = parseDirectives(comments);\n\n for (const directive of directives[\"cargo-add-options\"] ?? []) {\n const [packageSpec, ...options] = directive;\n imports.push({ package: packageSpec, cargoAddOptions: options.join(\" \"), isExternal: true });\n }\n\n for (const node of tree!.rootNode.children) {\n let crateName: string | undefined;\n\n if (node.type === \"use_declaration\") {\n const arg = node.child(1);\n if (!arg) continue;\n if (arg.type === \"identifier\" || arg.type === \"scoped_identifier\") {\n crateName = arg.text.split(\"::\")[0];\n }\n }\n\n if (crateName) {\n if (ERROR_CRATES.has(crateName)) {\n throw new Error(\n `Unsupported import of \"${crateName}\". ` +\n `Imports of ${Array.from(ERROR_CRATES)\n .map((c) => `\"${c}\"`)\n .join(\", \")} are not supported.`,\n );\n }\n if (imports.some((i) => i.package === crateName)) {\n continue;\n }\n\n imports.push({\n package: crateName,\n isExternal: !BUILTIN_CRATES.has(crateName),\n });\n }\n }\n return imports;\n}\n\ntype EnvironmentOptions = {\n environmentPath?: string;\n};\n\nfunction getEntryPath(environmentPath: string) {\n return `${environmentPath}/src/main.rs`;\n}\n\nasync function createEnvironment(\n code: string,\n imports: Import[],\n options: EnvironmentOptions = {},\n) {\n logger.info(\n \"Creating Rust environment with crates\",\n imports.map((i) => i.package),\n );\n\n if (options.environmentPath) {\n logger.info(\"Using explicit environment at\", options.environmentPath);\n await mkdir(`${options.environmentPath}/src`, { recursive: true });\n await writeFile(getEntryPath(options.environmentPath), code);\n return options.environmentPath;\n }\n\n const manifest = await readFile(new URL(\"../../../assets/Cargo.toml\", import.meta.url), \"utf-8\");\n const path = join(tmpdir(), \"docval\", randomUUID());\n await mkdir(`${path}/src`, { recursive: true });\n await Promise.all([\n writeFile(`${path}/Cargo.toml`, manifest),\n writeFile(`${path}/src/main.rs`, code),\n ]);\n if (imports.length > 0) {\n const crates = imports.filter((i) => i.isExternal);\n logger.info(\"Installing crates\", crates);\n for (const crate of crates) {\n await execUntilExit(\n `cargo add ${crate.package}${crate.cargoAddOptions ? ` ${crate.cargoAddOptions}` : \"\"}`,\n path,\n );\n }\n }\n\n logger.info(\"Created Rust environment at\", path);\n return path;\n}\n\nexport { getImports, createEnvironment, getEntryPath };\nexport type { Import };\n"],"mappings":";;;;;;;;;AASA,MAAM,iBADU,cAAc,OAAO,KAAK,IAAI,CACf,QAAQ,yCAAyC;AAEhF,IAAI;AAEJ,eAAe,kBAAqC;AAClD,KAAI,CAAC,cAAc;AACjB,QAAM,OAAO,MAAM;AACnB,iBAAe,MAAM,SAAS,KAAK,eAAe;;AAEpD,QAAO;;AAGT,MAAM,iBAAiB,IAAI,IAAI;CAAC;CAAO;CAAQ;CAAS;CAAQ,CAAC;AACjE,MAAM,eAAe,IAAI,IAAI;CAAC;CAAS;CAAS;CAAO,CAAC;AAQxD,eAAe,WAAW,MAAiC;CACzD,MAAM,WAAW,MAAM,iBAAiB;CACxC,MAAM,SAAS,IAAI,QAAQ;AAC3B,QAAO,YAAY,SAAS;CAC5B,MAAM,OAAO,OAAO,MAAM,KAAK;CAC/B,MAAM,UAAoB,EAAE;CAE5B,MAAM,aAAa,gBADF,MAAM,SAAS,kBAAkB,eAAe,CAAC,KAAK,MAAM,EAAE,KAAK,IAAI,EAAE,CAC9C;AAE5C,MAAK,MAAM,aAAa,WAAW,wBAAwB,EAAE,EAAE;EAC7D,MAAM,CAAC,aAAa,GAAG,WAAW;AAClC,UAAQ,KAAK;GAAE,SAAS;GAAa,iBAAiB,QAAQ,KAAK,IAAI;GAAE,YAAY;GAAM,CAAC;;AAG9F,MAAK,MAAM,QAAQ,KAAM,SAAS,UAAU;EAC1C,IAAI;AAEJ,MAAI,KAAK,SAAS,mBAAmB;GACnC,MAAM,MAAM,KAAK,MAAM,EAAE;AACzB,OAAI,CAAC,IAAK;AACV,OAAI,IAAI,SAAS,gBAAgB,IAAI,SAAS,oBAC5C,aAAY,IAAI,KAAK,MAAM,KAAK,CAAC;;AAIrC,MAAI,WAAW;AACb,OAAI,aAAa,IAAI,UAAU,CAC7B,OAAM,IAAI,MACR,0BAA0B,UAAU,gBACpB,MAAM,KAAK,aAAa,CACnC,KAAK,MAAM,IAAI,EAAE,GAAG,CACpB,KAAK,KAAK,CAAC,qBACjB;AAEH,OAAI,QAAQ,MAAM,MAAM,EAAE,YAAY,UAAU,CAC9C;AAGF,WAAQ,KAAK;IACX,SAAS;IACT,YAAY,CAAC,eAAe,IAAI,UAAU;IAC3C,CAAC;;;AAGN,QAAO;;AAOT,SAAS,aAAa,iBAAyB;AAC7C,QAAO,GAAG,gBAAgB;;AAG5B,eAAe,kBACb,MACA,SACA,UAA8B,EAAE,EAChC;AACA,QAAO,KACL,yCACA,QAAQ,KAAK,MAAM,EAAE,QAAQ,CAC9B;AAED,KAAI,QAAQ,iBAAiB;AAC3B,SAAO,KAAK,iCAAiC,QAAQ,gBAAgB;AACrE,QAAM,MAAM,GAAG,QAAQ,gBAAgB,OAAO,EAAE,WAAW,MAAM,CAAC;AAClE,QAAM,UAAU,aAAa,QAAQ,gBAAgB,EAAE,KAAK;AAC5D,SAAO,QAAQ;;CAGjB,MAAM,WAAW,MAAM,SAAS,IAAI,IAAI,8BAA8B,OAAO,KAAK,IAAI,EAAE,QAAQ;CAChG,MAAM,OAAO,KAAK,QAAQ,EAAE,UAAU,YAAY,CAAC;AACnD,OAAM,MAAM,GAAG,KAAK,OAAO,EAAE,WAAW,MAAM,CAAC;AAC/C,OAAM,QAAQ,IAAI,CAChB,UAAU,GAAG,KAAK,cAAc,SAAS,EACzC,UAAU,GAAG,KAAK,eAAe,KAAK,CACvC,CAAC;AACF,KAAI,QAAQ,SAAS,GAAG;EACtB,MAAM,SAAS,QAAQ,QAAQ,MAAM,EAAE,WAAW;AAClD,SAAO,KAAK,qBAAqB,OAAO;AACxC,OAAK,MAAM,SAAS,OAClB,OAAM,cACJ,aAAa,MAAM,UAAU,MAAM,kBAAkB,IAAI,MAAM,oBAAoB,MACnF,KACD;;AAIL,QAAO,KAAK,+BAA+B,KAAK;AAChD,QAAO"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { resolve } from "path";
|
|
2
|
+
|
|
3
|
+
//#region src/constants.ts
|
|
4
|
+
const CACHE_DIR = resolve(import.meta.dirname, ".docval", "cache");
|
|
5
|
+
const CONTENT_CACHE_DIR = resolve(CACHE_DIR, "content");
|
|
6
|
+
|
|
7
|
+
//#endregion
|
|
8
|
+
export { CONTENT_CACHE_DIR };
|
|
9
|
+
//# sourceMappingURL=constants.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.mjs","names":[],"sources":["../src/constants.ts"],"sourcesContent":["import { resolve } from \"path\";\n\nconst CACHE_DIR = resolve(import.meta.dirname, \".docval\", \"cache\");\nconst CONTENT_CACHE_DIR = resolve(CACHE_DIR, \"content\");\n\nexport { CACHE_DIR, CONTENT_CACHE_DIR };\n"],"mappings":";;;AAEA,MAAM,YAAY,QAAQ,OAAO,KAAK,SAAS,WAAW,QAAQ;AAClE,MAAM,oBAAoB,QAAQ,WAAW,UAAU"}
|
package/dist/remark/plugin.d.mts
CHANGED
|
@@ -2,11 +2,10 @@ import { Root } from "mdast";
|
|
|
2
2
|
|
|
3
3
|
//#region src/remark/plugin.d.ts
|
|
4
4
|
type DocValOptions = {
|
|
5
|
-
|
|
5
|
+
cache: boolean;
|
|
6
|
+
include: boolean;
|
|
6
7
|
};
|
|
7
|
-
declare function remarkDocval(
|
|
8
|
-
include
|
|
9
|
-
}?: DocValOptions): (tree: Root) => Promise<Root>;
|
|
8
|
+
declare function remarkDocval(options?: Partial<DocValOptions>): (tree: Root) => Promise<Root>;
|
|
10
9
|
//#endregion
|
|
11
10
|
export { remarkDocval };
|
|
12
11
|
//# sourceMappingURL=plugin.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.mts","names":[],"sources":["../../src/remark/plugin.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"plugin.d.mts","names":[],"sources":["../../src/remark/plugin.ts"],"mappings":";;;KAQY,aAAA;EACV,KAAA;EACA,OAAA;AAAA;AAAA,iBAUsB,YAAA,CAAa,OAAA,GAAS,OAAA,CAAQ,aAAA,KAA+B,IAAA,EAGtD,IAAA,KAAI,OAAA,CAAA,IAAA"}
|
package/dist/remark/plugin.mjs
CHANGED
|
@@ -1,17 +1,69 @@
|
|
|
1
|
+
import { logger } from "../utils.mjs";
|
|
1
2
|
import map from "../adapters/index.mjs";
|
|
3
|
+
import { CONTENT_CACHE_DIR } from "../constants.mjs";
|
|
4
|
+
import { hash } from "crypto";
|
|
5
|
+
import { readFile, writeFile } from "fs/promises";
|
|
6
|
+
import { existsSync, mkdirSync } from "fs";
|
|
2
7
|
|
|
3
8
|
//#region src/remark/plugin.ts
|
|
4
|
-
|
|
9
|
+
mkdirSync(CONTENT_CACHE_DIR, { recursive: true });
|
|
10
|
+
const defaultOptions = {
|
|
11
|
+
cache: process.env.CI !== "true" && process.env.NODE_ENV !== "test",
|
|
12
|
+
include: false
|
|
13
|
+
};
|
|
14
|
+
function remarkDocval(options = defaultOptions) {
|
|
15
|
+
const { cache, include } = {
|
|
16
|
+
...defaultOptions,
|
|
17
|
+
...options
|
|
18
|
+
};
|
|
5
19
|
return async function(tree) {
|
|
6
20
|
const promises = [];
|
|
7
|
-
tree.children.
|
|
21
|
+
await Promise.all(tree.children.map(async (node) => {
|
|
8
22
|
if (node.type === "code" && node.lang && node.lang in map) {
|
|
23
|
+
const contentHash = hash("sha1", JSON.stringify([
|
|
24
|
+
node.value,
|
|
25
|
+
node.lang,
|
|
26
|
+
node.meta
|
|
27
|
+
]));
|
|
28
|
+
if (cache) {
|
|
29
|
+
const [success, originalResults] = await readFile(`${CONTENT_CACHE_DIR}/${contentHash}.txt`, { encoding: "utf-8" }).then((content) => {
|
|
30
|
+
const [success, results] = content.split("\n");
|
|
31
|
+
if (!["true", "false"].includes(success)) return [null, null];
|
|
32
|
+
return [success === "true", JSON.parse(results ?? "null")];
|
|
33
|
+
}).catch(() => [null, null]);
|
|
34
|
+
if (typeof success === "boolean") {
|
|
35
|
+
logger.debug("Result found in cache, skipping validation");
|
|
36
|
+
if (!success) throw JSON.stringify({
|
|
37
|
+
message: "One or more code blocks failed validation.",
|
|
38
|
+
originalResults
|
|
39
|
+
});
|
|
40
|
+
else return;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
9
43
|
const metadata = node.meta?.split(" ") ?? [];
|
|
10
44
|
const docValIndex = metadata.findIndex((item) => item === "docval");
|
|
11
|
-
|
|
45
|
+
const noDocValIndex = metadata.findIndex((item) => item === "no-docval");
|
|
46
|
+
if (include && noDocValIndex === -1 || docValIndex !== -1) promises.push((async () => {
|
|
47
|
+
try {
|
|
48
|
+
return {
|
|
49
|
+
result: await map[node.lang](node.value, metadata),
|
|
50
|
+
contentHash
|
|
51
|
+
};
|
|
52
|
+
} catch (err) {
|
|
53
|
+
throw {
|
|
54
|
+
result: err,
|
|
55
|
+
contentHash
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
})());
|
|
12
59
|
} else if (node.type === "code" && process.env.DOCVAL_TEST_NO_SKIP === "true") throw new Error(`Skipped code block: ${JSON.stringify(node)}`);
|
|
13
|
-
});
|
|
60
|
+
}));
|
|
14
61
|
const res = await Promise.allSettled(promises);
|
|
62
|
+
if (cache) await Promise.all(res.map(async (r) => {
|
|
63
|
+
const path = (hash) => `${CONTENT_CACHE_DIR}/${hash}.txt`;
|
|
64
|
+
if (r.status === "rejected" && !existsSync(path(r.reason.contentHash))) await writeFile(`${CONTENT_CACHE_DIR}/${r.reason.contentHash}.txt`, `false\n${JSON.stringify(r.reason)}`);
|
|
65
|
+
else if (r.status === "fulfilled" && !existsSync(path(r.value.contentHash))) await writeFile(`${CONTENT_CACHE_DIR}/${r.value.contentHash}.txt`, "true");
|
|
66
|
+
}));
|
|
15
67
|
if (res.some((r) => r.status === "rejected")) throw JSON.stringify({
|
|
16
68
|
message: "One or more code blocks failed validation.",
|
|
17
69
|
originalResults: res
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.mjs","names":["adapters"],"sources":["../../src/remark/plugin.ts"],"sourcesContent":["import adapters from \"@/adapters\";\nimport type { Root } from \"mdast\";\n\nexport type DocValOptions = {\n include
|
|
1
|
+
{"version":3,"file":"plugin.mjs","names":["adapters"],"sources":["../../src/remark/plugin.ts"],"sourcesContent":["import adapters from \"@/adapters\";\nimport { CONTENT_CACHE_DIR } from \"@/constants\";\nimport { logger } from \"@/utils\";\nimport { hash } from \"crypto\";\nimport { existsSync, mkdirSync } from \"fs\";\nimport { readFile, writeFile } from \"fs/promises\";\nimport type { Root } from \"mdast\";\n\nexport type DocValOptions = {\n cache: boolean;\n include: boolean;\n};\n\nmkdirSync(CONTENT_CACHE_DIR, { recursive: true });\n\nconst defaultOptions: DocValOptions = {\n cache: process.env.CI !== \"true\" && process.env.NODE_ENV !== \"test\",\n include: false,\n};\n\nexport default function remarkDocval(options: Partial<DocValOptions> = defaultOptions) {\n const { cache, include } = { ...defaultOptions, ...options };\n\n return async function (tree: Root) {\n const promises: Promise<{\n result: any;\n contentHash: string;\n }>[] = [];\n await Promise.all(\n tree.children.map(async (node) => {\n if (node.type === \"code\" && node.lang && node.lang in adapters) {\n const contentHash = hash(\"sha1\", JSON.stringify([node.value, node.lang, node.meta]));\n if (cache) {\n const [success, originalResults]: [boolean | null, any] = await readFile(\n `${CONTENT_CACHE_DIR}/${contentHash}.txt`,\n { encoding: \"utf-8\" },\n )\n .then((content) => {\n const [success, results] = content.split(\"\\n\");\n\n if (![\"true\", \"false\"].includes(success)) {\n return [null, null] as [boolean | null, any];\n }\n return [success === \"true\", JSON.parse(results ?? \"null\")] as [boolean, any];\n })\n .catch(() => [null, null]);\n\n if (typeof success === \"boolean\") {\n logger.debug(\"Result found in cache, skipping validation\");\n\n if (!success) {\n throw JSON.stringify({\n message: \"One or more code blocks failed validation.\",\n originalResults: originalResults,\n });\n } else {\n return;\n }\n }\n }\n\n const metadata = node.meta?.split(\" \") ?? [];\n const docValIndex = metadata.findIndex((item) => item === \"docval\");\n const noDocValIndex = metadata.findIndex((item) => item === \"no-docval\");\n if ((include && noDocValIndex === -1) || docValIndex !== -1) {\n promises.push(\n (async () => {\n try {\n const result = await adapters[node.lang as keyof typeof adapters](\n node.value,\n metadata,\n );\n return {\n result,\n contentHash,\n };\n } catch (err) {\n throw {\n result: err,\n contentHash,\n };\n }\n })(),\n );\n }\n } else if (node.type === \"code\" && process.env.DOCVAL_TEST_NO_SKIP === \"true\") {\n throw new Error(`Skipped code block: ${JSON.stringify(node)}`);\n }\n }),\n );\n const res = await Promise.allSettled(promises);\n\n if (cache) {\n await Promise.all(\n res.map(async (r) => {\n const path = (hash: string) => `${CONTENT_CACHE_DIR}/${hash}.txt`;\n if (r.status === \"rejected\" && !existsSync(path(r.reason.contentHash))) {\n await writeFile(\n `${CONTENT_CACHE_DIR}/${r.reason.contentHash}.txt`,\n `false\\n${JSON.stringify(r.reason)}`,\n );\n } else if (r.status === \"fulfilled\" && !existsSync(path(r.value.contentHash))) {\n await writeFile(`${CONTENT_CACHE_DIR}/${r.value.contentHash}.txt`, \"true\");\n }\n }),\n );\n }\n\n if (res.some((r) => r.status === \"rejected\")) {\n throw JSON.stringify({\n message: \"One or more code blocks failed validation.\",\n originalResults: res,\n });\n }\n\n return tree;\n };\n}\n"],"mappings":";;;;;;;;AAaA,UAAU,mBAAmB,EAAE,WAAW,MAAM,CAAC;AAEjD,MAAM,iBAAgC;CACpC,OAAO,QAAQ,IAAI,OAAO,UAAU,QAAQ,IAAI,aAAa;CAC7D,SAAS;CACV;AAED,SAAwB,aAAa,UAAkC,gBAAgB;CACrF,MAAM,EAAE,OAAO,YAAY;EAAE,GAAG;EAAgB,GAAG;EAAS;AAE5D,QAAO,eAAgB,MAAY;EACjC,MAAM,WAGC,EAAE;AACT,QAAM,QAAQ,IACZ,KAAK,SAAS,IAAI,OAAO,SAAS;AAChC,OAAI,KAAK,SAAS,UAAU,KAAK,QAAQ,KAAK,QAAQA,KAAU;IAC9D,MAAM,cAAc,KAAK,QAAQ,KAAK,UAAU;KAAC,KAAK;KAAO,KAAK;KAAM,KAAK;KAAK,CAAC,CAAC;AACpF,QAAI,OAAO;KACT,MAAM,CAAC,SAAS,mBAA0C,MAAM,SAC9D,GAAG,kBAAkB,GAAG,YAAY,OACpC,EAAE,UAAU,SAAS,CACtB,CACE,MAAM,YAAY;MACjB,MAAM,CAAC,SAAS,WAAW,QAAQ,MAAM,KAAK;AAE9C,UAAI,CAAC,CAAC,QAAQ,QAAQ,CAAC,SAAS,QAAQ,CACtC,QAAO,CAAC,MAAM,KAAK;AAErB,aAAO,CAAC,YAAY,QAAQ,KAAK,MAAM,WAAW,OAAO,CAAC;OAC1D,CACD,YAAY,CAAC,MAAM,KAAK,CAAC;AAE5B,SAAI,OAAO,YAAY,WAAW;AAChC,aAAO,MAAM,6CAA6C;AAE1D,UAAI,CAAC,QACH,OAAM,KAAK,UAAU;OACnB,SAAS;OACQ;OAClB,CAAC;UAEF;;;IAKN,MAAM,WAAW,KAAK,MAAM,MAAM,IAAI,IAAI,EAAE;IAC5C,MAAM,cAAc,SAAS,WAAW,SAAS,SAAS,SAAS;IACnE,MAAM,gBAAgB,SAAS,WAAW,SAAS,SAAS,YAAY;AACxE,QAAK,WAAW,kBAAkB,MAAO,gBAAgB,GACvD,UAAS,MACN,YAAY;AACX,SAAI;AAKF,aAAO;OACL,QALa,MAAMA,IAAS,KAAK,MACjC,KAAK,OACL,SACD;OAGC;OACD;cACM,KAAK;AACZ,YAAM;OACJ,QAAQ;OACR;OACD;;QAED,CACL;cAEM,KAAK,SAAS,UAAU,QAAQ,IAAI,wBAAwB,OACrE,OAAM,IAAI,MAAM,uBAAuB,KAAK,UAAU,KAAK,GAAG;IAEhE,CACH;EACD,MAAM,MAAM,MAAM,QAAQ,WAAW,SAAS;AAE9C,MAAI,MACF,OAAM,QAAQ,IACZ,IAAI,IAAI,OAAO,MAAM;GACnB,MAAM,QAAQ,SAAiB,GAAG,kBAAkB,GAAG,KAAK;AAC5D,OAAI,EAAE,WAAW,cAAc,CAAC,WAAW,KAAK,EAAE,OAAO,YAAY,CAAC,CACpE,OAAM,UACJ,GAAG,kBAAkB,GAAG,EAAE,OAAO,YAAY,OAC7C,UAAU,KAAK,UAAU,EAAE,OAAO,GACnC;YACQ,EAAE,WAAW,eAAe,CAAC,WAAW,KAAK,EAAE,MAAM,YAAY,CAAC,CAC3E,OAAM,UAAU,GAAG,kBAAkB,GAAG,EAAE,MAAM,YAAY,OAAO,OAAO;IAE5E,CACH;AAGH,MAAI,IAAI,MAAM,MAAM,EAAE,WAAW,WAAW,CAC1C,OAAM,KAAK,UAAU;GACnB,SAAS;GACT,iBAAiB;GAClB,CAAC;AAGJ,SAAO"}
|
package/dist/utils.mjs
CHANGED
|
@@ -25,7 +25,23 @@ function execUntilExit(command, cwd) {
|
|
|
25
25
|
async function cleanupEnvironment(path) {
|
|
26
26
|
await execUntilExit(`rm -rf ${path}`, process.cwd());
|
|
27
27
|
}
|
|
28
|
+
function parseDirectives(comments) {
|
|
29
|
+
const directives = {};
|
|
30
|
+
const prefix = "@docval-";
|
|
31
|
+
for (const line of comments) {
|
|
32
|
+
const trimmed = line.trim();
|
|
33
|
+
const prefixIndex = trimmed.indexOf(prefix);
|
|
34
|
+
if (prefixIndex === -1) continue;
|
|
35
|
+
const content = trimmed.slice(prefixIndex + 8);
|
|
36
|
+
const spaceIdx = content.indexOf(" ");
|
|
37
|
+
const directive = spaceIdx === -1 ? content : content.slice(0, spaceIdx);
|
|
38
|
+
const args = spaceIdx === -1 ? [] : content.slice(spaceIdx + 1).split(" ").filter(Boolean);
|
|
39
|
+
if (!Array.isArray(directives[directive])) directives[directive] = [];
|
|
40
|
+
directives[directive].push(args);
|
|
41
|
+
}
|
|
42
|
+
return directives;
|
|
43
|
+
}
|
|
28
44
|
|
|
29
45
|
//#endregion
|
|
30
|
-
export { cleanupEnvironment, execUntilExit, logger };
|
|
46
|
+
export { cleanupEnvironment, execUntilExit, logger, parseDirectives };
|
|
31
47
|
//# sourceMappingURL=utils.mjs.map
|
package/dist/utils.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.mjs","names":[],"sources":["../src/utils.ts"],"sourcesContent":["import { spawn } from \"child_process\";\nimport consola from \"consola\";\n\nconst logger = consola.withTag(\"Borrow\").withTag(\"DocVal\");\n\nfunction execUntilExit(command: string, cwd: string): Promise<void> {\n let error = \"\";\n return new Promise((resolve, reject) => {\n const process = spawn(command, { cwd, shell: true });\n\n process.stdout.on(\"data\", (data) => {\n logger.debug(data.toString());\n });\n\n process.stderr.on(\"data\", (data) => {\n error += data;\n });\n\n process.on(\"close\", (code) => {\n if (code === 0) {\n resolve();\n } else {\n reject(`Command failed with exit code ${code}\\n${error})`);\n }\n });\n });\n}\n\nasync function cleanupEnvironment(path: string) {\n await execUntilExit(`rm -rf ${path}`, process.cwd());\n}\n\nexport { execUntilExit, cleanupEnvironment, logger };\n"],"mappings":";;;;AAGA,MAAM,SAAS,QAAQ,QAAQ,SAAS,CAAC,QAAQ,SAAS;AAE1D,SAAS,cAAc,SAAiB,KAA4B;CAClE,IAAI,QAAQ;AACZ,QAAO,IAAI,SAAS,SAAS,WAAW;EACtC,MAAM,UAAU,MAAM,SAAS;GAAE;GAAK,OAAO;GAAM,CAAC;AAEpD,UAAQ,OAAO,GAAG,SAAS,SAAS;AAClC,UAAO,MAAM,KAAK,UAAU,CAAC;IAC7B;AAEF,UAAQ,OAAO,GAAG,SAAS,SAAS;AAClC,YAAS;IACT;AAEF,UAAQ,GAAG,UAAU,SAAS;AAC5B,OAAI,SAAS,EACX,UAAS;OAET,QAAO,iCAAiC,KAAK,IAAI,MAAM,GAAG;IAE5D;GACF;;AAGJ,eAAe,mBAAmB,MAAc;AAC9C,OAAM,cAAc,UAAU,QAAQ,QAAQ,KAAK,CAAC"}
|
|
1
|
+
{"version":3,"file":"utils.mjs","names":[],"sources":["../src/utils.ts"],"sourcesContent":["import { spawn } from \"child_process\";\nimport consola from \"consola\";\n\nconst logger = consola.withTag(\"Borrow\").withTag(\"DocVal\");\n\nfunction execUntilExit(command: string, cwd: string): Promise<void> {\n let error = \"\";\n return new Promise((resolve, reject) => {\n const process = spawn(command, { cwd, shell: true });\n\n process.stdout.on(\"data\", (data) => {\n logger.debug(data.toString());\n });\n\n process.stderr.on(\"data\", (data) => {\n error += data;\n });\n\n process.on(\"close\", (code) => {\n if (code === 0) {\n resolve();\n } else {\n reject(`Command failed with exit code ${code}\\n${error})`);\n }\n });\n });\n}\n\nasync function cleanupEnvironment(path: string) {\n await execUntilExit(`rm -rf ${path}`, process.cwd());\n}\n\ntype DocvalDirectives = Record<string, string[][]> & {\n \"cargo-add-options\"?: string[][];\n};\n\nfunction parseDirectives(comments: string[]): DocvalDirectives {\n const directives: DocvalDirectives = {};\n const prefix = \"@docval-\";\n for (const line of comments) {\n const trimmed = line.trim();\n const prefixIndex = trimmed.indexOf(prefix);\n if (prefixIndex === -1) continue;\n const content = trimmed.slice(prefixIndex + prefix.length);\n const spaceIdx = content.indexOf(\" \");\n const directive = spaceIdx === -1 ? content : content.slice(0, spaceIdx);\n const args =\n spaceIdx === -1\n ? []\n : content\n .slice(spaceIdx + 1)\n .split(\" \")\n .filter(Boolean);\n if (!Array.isArray(directives[directive])) {\n directives[directive] = [];\n }\n directives[directive].push(args);\n }\n return directives;\n}\n\nexport { execUntilExit, cleanupEnvironment, logger, parseDirectives };\nexport type { DocvalDirectives };\n"],"mappings":";;;;AAGA,MAAM,SAAS,QAAQ,QAAQ,SAAS,CAAC,QAAQ,SAAS;AAE1D,SAAS,cAAc,SAAiB,KAA4B;CAClE,IAAI,QAAQ;AACZ,QAAO,IAAI,SAAS,SAAS,WAAW;EACtC,MAAM,UAAU,MAAM,SAAS;GAAE;GAAK,OAAO;GAAM,CAAC;AAEpD,UAAQ,OAAO,GAAG,SAAS,SAAS;AAClC,UAAO,MAAM,KAAK,UAAU,CAAC;IAC7B;AAEF,UAAQ,OAAO,GAAG,SAAS,SAAS;AAClC,YAAS;IACT;AAEF,UAAQ,GAAG,UAAU,SAAS;AAC5B,OAAI,SAAS,EACX,UAAS;OAET,QAAO,iCAAiC,KAAK,IAAI,MAAM,GAAG;IAE5D;GACF;;AAGJ,eAAe,mBAAmB,MAAc;AAC9C,OAAM,cAAc,UAAU,QAAQ,QAAQ,KAAK,CAAC;;AAOtD,SAAS,gBAAgB,UAAsC;CAC7D,MAAM,aAA+B,EAAE;CACvC,MAAM,SAAS;AACf,MAAK,MAAM,QAAQ,UAAU;EAC3B,MAAM,UAAU,KAAK,MAAM;EAC3B,MAAM,cAAc,QAAQ,QAAQ,OAAO;AAC3C,MAAI,gBAAgB,GAAI;EACxB,MAAM,UAAU,QAAQ,MAAM,cAAc,EAAc;EAC1D,MAAM,WAAW,QAAQ,QAAQ,IAAI;EACrC,MAAM,YAAY,aAAa,KAAK,UAAU,QAAQ,MAAM,GAAG,SAAS;EACxE,MAAM,OACJ,aAAa,KACT,EAAE,GACF,QACG,MAAM,WAAW,EAAE,CACnB,MAAM,IAAI,CACV,OAAO,QAAQ;AACxB,MAAI,CAAC,MAAM,QAAQ,WAAW,WAAW,CACvC,YAAW,aAAa,EAAE;AAE5B,aAAW,WAAW,KAAK,KAAK;;AAElC,QAAO"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@borrowdev/docval",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Validate your documentation codeblocks at build-time, find bugs in record-time",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"borrow.dev",
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
},
|
|
17
17
|
"files": [
|
|
18
18
|
"dist",
|
|
19
|
+
"assets",
|
|
19
20
|
"package.json",
|
|
20
21
|
"README.md"
|
|
21
22
|
],
|