@borrowdev/docval 0.1.2 → 0.1.4
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/index.mjs.map +1 -1
- package/dist/adapters/javascript/utils.mjs +12 -2
- package/dist/adapters/javascript/utils.mjs.map +1 -1
- package/dist/adapters/rust/index.mjs +2 -2
- package/dist/adapters/rust/index.mjs.map +1 -1
- package/dist/adapters/rust/utils.mjs +28 -6
- 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 +63 -5
- package/dist/remark/plugin.mjs.map +1 -1
- package/dist/utils.mjs +62 -1
- package/dist/utils.mjs.map +1 -1
- package/package.json +2 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["adapterJavaScript","adapterTypeScript","adapterJSX","adapterTSX","adapterRust"],"sources":["../../src/adapters/index.ts"],"sourcesContent":["import adapterJavaScript from \"./javascript\";\nimport adapterJSX from \"./jsx\";\nimport adapterRust from \"./rust\";\nimport adapterTSX from \"./tsx\";\nimport adapterTypeScript from \"./typescript\";\n\nconst map
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["adapterJavaScript","adapterTypeScript","adapterJSX","adapterTSX","adapterRust"],"sources":["../../src/adapters/index.ts"],"sourcesContent":["import adapterJavaScript from \"./javascript\";\nimport adapterJSX from \"./jsx\";\nimport adapterRust from \"./rust\";\nimport adapterTSX from \"./tsx\";\nimport adapterTypeScript from \"./typescript\";\n\nconst map = {\n javascript: adapterJavaScript,\n js: adapterJavaScript,\n typescript: adapterTypeScript,\n ts: adapterTypeScript,\n jsx: adapterJSX,\n tsx: adapterTSX,\n rust: adapterRust,\n rs: adapterRust,\n} as const;\n\ntype Language = keyof typeof map;\n\nexport { Language };\nexport default map;\n"],"mappings":";;;;;;;AAMA,MAAM,MAAM;CACV,YAAYA;CACZ,IAAIA;CACJ,YAAYC;CACZ,IAAIA;CACJ,KAAKC;CACL,KAAKC;CACL,MAAMC;CACN,IAAIA;CACL"}
|
|
@@ -8,6 +8,16 @@ import { join } from "path";
|
|
|
8
8
|
import { isBuiltin } from "module";
|
|
9
9
|
|
|
10
10
|
//#region src/adapters/javascript/utils.ts
|
|
11
|
+
function getTree(code, type, filename) {
|
|
12
|
+
return parse(filename ?? "unknown", code, { lang: type });
|
|
13
|
+
}
|
|
14
|
+
async function getComments(code, type) {
|
|
15
|
+
return (await getTree(code, type)).comments.map((comment) => ({
|
|
16
|
+
value: comment.value,
|
|
17
|
+
start: comment.start,
|
|
18
|
+
end: comment.end
|
|
19
|
+
}));
|
|
20
|
+
}
|
|
11
21
|
function getPackage(importSpec) {
|
|
12
22
|
let packageSpec = importSpec;
|
|
13
23
|
if (packageSpec.startsWith("npm:")) packageSpec = packageSpec.slice(4);
|
|
@@ -15,7 +25,7 @@ function getPackage(importSpec) {
|
|
|
15
25
|
return packageSpec.split("/")[0];
|
|
16
26
|
}
|
|
17
27
|
async function getImports(code, filename, type) {
|
|
18
|
-
const ast = await
|
|
28
|
+
const ast = await getTree(code, type, filename);
|
|
19
29
|
return [...ast.module.dynamicImports.map((d) => {
|
|
20
30
|
const packageSpec = getPackage(code.slice(d.moduleRequest.start, d.moduleRequest.end).replaceAll(/['"]/g, ""));
|
|
21
31
|
return {
|
|
@@ -55,5 +65,5 @@ async function createEnvironment(code, imports, env, options = { installCommand:
|
|
|
55
65
|
}
|
|
56
66
|
|
|
57
67
|
//#endregion
|
|
58
|
-
export { createEnvironment, getEntryPath, getImports };
|
|
68
|
+
export { createEnvironment, getComments, getEntryPath, getImports };
|
|
59
69
|
//# sourceMappingURL=utils.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.mjs","names":["manifest"],"sources":["../../../src/adapters/javascript/utils.ts"],"sourcesContent":["import { parse } from \"oxc-parser\";\nimport { tmpdir } from \"os\";\nimport { mkdir, writeFile } from \"fs/promises\";\nimport manifest from \"~/assets/package.json\";\nimport { join } from \"path\";\nimport { randomUUID } from \"crypto\";\nimport { isBuiltin } from \"module\";\nimport { cleanupEnvironment, execUntilExit, logger } from \"@/utils\";\n\ntype Import = {\n isExternal: boolean;\n package: string;\n};\n\nfunction getPackage(importSpec: string): string {\n let packageSpec = importSpec;\n // Deno environment\n if (packageSpec.startsWith(\"npm:\")) {\n packageSpec = packageSpec.slice(4);\n }\n if (packageSpec.startsWith(\"@\")) {\n return packageSpec.split(\"/\").slice(0, 2).join(\"/\");\n }\n return packageSpec.split(\"/\")[0];\n}\n\nasync function getImports(code: string, filename: string, type: JavaScriptType): Promise<Import[]> {\n const ast = await
|
|
1
|
+
{"version":3,"file":"utils.mjs","names":["manifest"],"sources":["../../../src/adapters/javascript/utils.ts"],"sourcesContent":["import { parse } from \"oxc-parser\";\nimport { tmpdir } from \"os\";\nimport { mkdir, writeFile } from \"fs/promises\";\nimport manifest from \"~/assets/package.json\";\nimport { join } from \"path\";\nimport { randomUUID } from \"crypto\";\nimport { isBuiltin } from \"module\";\nimport { cleanupEnvironment, execUntilExit, logger } from \"@/utils\";\nimport type { Comment } from \"@/utils\";\n\ntype Import = {\n isExternal: boolean;\n package: string;\n};\n\nfunction getTree(code: string, type: JavaScriptType, filename?: string) {\n return parse(filename ?? \"unknown\", code, {\n lang: type,\n });\n}\n\nasync function getComments(code: string, type: JavaScriptType): Promise<Comment[]> {\n const ast = await getTree(code, type);\n return ast.comments.map((comment) => ({\n value: comment.value,\n start: comment.start,\n end: comment.end,\n }));\n}\n\nfunction getPackage(importSpec: string): string {\n let packageSpec = importSpec;\n // Deno environment\n if (packageSpec.startsWith(\"npm:\")) {\n packageSpec = packageSpec.slice(4);\n }\n if (packageSpec.startsWith(\"@\")) {\n return packageSpec.split(\"/\").slice(0, 2).join(\"/\");\n }\n return packageSpec.split(\"/\")[0];\n}\n\nasync function getImports(code: string, filename: string, type: JavaScriptType): Promise<Import[]> {\n const ast = await getTree(code, type, filename);\n return [\n ...ast.module.dynamicImports.map((d) => {\n const packageSpec = getPackage(\n code.slice(d.moduleRequest.start, d.moduleRequest.end).replaceAll(/['\"]/g, \"\"),\n );\n return { package: packageSpec, isExternal: !isBuiltin(packageSpec) };\n }),\n ...ast.module.staticImports.map((s) => ({\n package: getPackage(s.moduleRequest.value),\n isExternal: !isBuiltin(s.moduleRequest.value),\n })),\n ];\n}\n\ntype EnvironmentOptions = {\n environmentPath?: string;\n installCommand: string[];\n};\n\nfunction getEntryPath(environmentPath: string) {\n return `${environmentPath}/dist/index.js`;\n}\n\nasync function createEnvironment(\n code: string,\n imports: Import[],\n env: string | undefined,\n options: EnvironmentOptions = {\n installCommand: [\"npm\", \"install\"],\n },\n) {\n logger.info(\n \"Creating environment with imports\",\n imports.map((i) => i.package),\n );\n if (options.environmentPath) {\n logger.info(\"Using explicit environment at\", options.environmentPath);\n await mkdir(`${options.environmentPath}/dist`, { recursive: true });\n await writeFile(`${options.environmentPath}/dist/index.js`, code);\n return options.environmentPath;\n }\n\n const path = join(tmpdir(), \"docval\", randomUUID());\n await mkdir(`${path}/dist`, { recursive: true });\n await Promise.all([\n writeFile(`${path}/package.json`, JSON.stringify(manifest)),\n writeFile(`${path}/dist/index.js`, code),\n env ? writeFile(`${path}/.env`, env) : Promise.resolve(),\n ]);\n logger.info(\"Created environment at\", path);\n const cmd = options.installCommand\n .concat(imports.filter((i) => i.isExternal).map((i) => i.package))\n .join(\" \");\n if (imports.length > 0) {\n logger.info(\"Installing dependencies with command:\", cmd);\n await execUntilExit(cmd, path);\n }\n return path;\n}\n\ntype JavaScriptType = \"js\" | \"ts\" | \"tsx\" | \"jsx\";\n\nexport type { JavaScriptType };\nexport {\n getImports,\n createEnvironment,\n cleanupEnvironment,\n execUntilExit,\n getEntryPath,\n getComments,\n};\n"],"mappings":";;;;;;;;;;AAeA,SAAS,QAAQ,MAAc,MAAsB,UAAmB;AACtE,QAAO,MAAM,YAAY,WAAW,MAAM,EACxC,MAAM,MACP,CAAC;;AAGJ,eAAe,YAAY,MAAc,MAA0C;AAEjF,SADY,MAAM,QAAQ,MAAM,KAAK,EAC1B,SAAS,KAAK,aAAa;EACpC,OAAO,QAAQ;EACf,OAAO,QAAQ;EACf,KAAK,QAAQ;EACd,EAAE;;AAGL,SAAS,WAAW,YAA4B;CAC9C,IAAI,cAAc;AAElB,KAAI,YAAY,WAAW,OAAO,CAChC,eAAc,YAAY,MAAM,EAAE;AAEpC,KAAI,YAAY,WAAW,IAAI,CAC7B,QAAO,YAAY,MAAM,IAAI,CAAC,MAAM,GAAG,EAAE,CAAC,KAAK,IAAI;AAErD,QAAO,YAAY,MAAM,IAAI,CAAC;;AAGhC,eAAe,WAAW,MAAc,UAAkB,MAAyC;CACjG,MAAM,MAAM,MAAM,QAAQ,MAAM,MAAM,SAAS;AAC/C,QAAO,CACL,GAAG,IAAI,OAAO,eAAe,KAAK,MAAM;EACtC,MAAM,cAAc,WAClB,KAAK,MAAM,EAAE,cAAc,OAAO,EAAE,cAAc,IAAI,CAAC,WAAW,SAAS,GAAG,CAC/E;AACD,SAAO;GAAE,SAAS;GAAa,YAAY,CAAC,UAAU,YAAY;GAAE;GACpE,EACF,GAAG,IAAI,OAAO,cAAc,KAAK,OAAO;EACtC,SAAS,WAAW,EAAE,cAAc,MAAM;EAC1C,YAAY,CAAC,UAAU,EAAE,cAAc,MAAM;EAC9C,EAAE,CACJ;;AAQH,SAAS,aAAa,iBAAyB;AAC7C,QAAO,GAAG,gBAAgB;;AAG5B,eAAe,kBACb,MACA,SACA,KACA,UAA8B,EAC5B,gBAAgB,CAAC,OAAO,UAAU,EACnC,EACD;AACA,QAAO,KACL,qCACA,QAAQ,KAAK,MAAM,EAAE,QAAQ,CAC9B;AACD,KAAI,QAAQ,iBAAiB;AAC3B,SAAO,KAAK,iCAAiC,QAAQ,gBAAgB;AACrE,QAAM,MAAM,GAAG,QAAQ,gBAAgB,QAAQ,EAAE,WAAW,MAAM,CAAC;AACnE,QAAM,UAAU,GAAG,QAAQ,gBAAgB,iBAAiB,KAAK;AACjE,SAAO,QAAQ;;CAGjB,MAAM,OAAO,KAAK,QAAQ,EAAE,UAAU,YAAY,CAAC;AACnD,OAAM,MAAM,GAAG,KAAK,QAAQ,EAAE,WAAW,MAAM,CAAC;AAChD,OAAM,QAAQ,IAAI;EAChB,UAAU,GAAG,KAAK,gBAAgB,KAAK,UAAUA,gBAAS,CAAC;EAC3D,UAAU,GAAG,KAAK,iBAAiB,KAAK;EACxC,MAAM,UAAU,GAAG,KAAK,QAAQ,IAAI,GAAG,QAAQ,SAAS;EACzD,CAAC;AACF,QAAO,KAAK,0BAA0B,KAAK;CAC3C,MAAM,MAAM,QAAQ,eACjB,OAAO,QAAQ,QAAQ,MAAM,EAAE,WAAW,CAAC,KAAK,MAAM,EAAE,QAAQ,CAAC,CACjE,KAAK,IAAI;AACZ,KAAI,QAAQ,SAAS,GAAG;AACtB,SAAO,KAAK,yCAAyC,IAAI;AACzD,QAAM,cAAc,KAAK,KAAK;;AAEhC,QAAO"}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { getOptions } from "../../remark/utils.mjs";
|
|
2
|
-
import { cleanupEnvironment, execUntilExit } from "../../utils.mjs";
|
|
3
2
|
import { createEnvironment, getImports } from "./utils.mjs";
|
|
3
|
+
import { cleanupEnvironment, execUntilExit } from "../../utils.mjs";
|
|
4
4
|
import { ADAPTER_OPTIONS } from "./constants.mjs";
|
|
5
5
|
|
|
6
6
|
//#region src/adapters/rust/index.ts
|
|
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, getDirectives, logger } from "../../utils.mjs";
|
|
2
2
|
import { randomUUID } from "crypto";
|
|
3
3
|
import { tmpdir } from "os";
|
|
4
4
|
import { mkdir, readFile, writeFile } from "fs/promises";
|
|
@@ -27,12 +27,33 @@ const ERROR_CRATES = new Set([
|
|
|
27
27
|
"super",
|
|
28
28
|
"self"
|
|
29
29
|
]);
|
|
30
|
-
async function
|
|
30
|
+
async function getTree(code) {
|
|
31
31
|
const language = await getRustLanguage();
|
|
32
32
|
const parser = new Parser();
|
|
33
33
|
parser.setLanguage(language);
|
|
34
|
-
|
|
34
|
+
return parser.parse(code);
|
|
35
|
+
}
|
|
36
|
+
async function getComments(input) {
|
|
37
|
+
const tree = typeof input === "string" ? await getTree(input) : input;
|
|
38
|
+
return tree ? tree.rootNode.descendantsOfType("line_comment").map((comment) => ({
|
|
39
|
+
value: comment.text,
|
|
40
|
+
start: comment.startIndex,
|
|
41
|
+
end: comment.endIndex
|
|
42
|
+
})) : [];
|
|
43
|
+
}
|
|
44
|
+
async function getImports(code) {
|
|
45
|
+
const tree = await getTree(code);
|
|
46
|
+
const directives = await getDirectives(code, "rust");
|
|
35
47
|
const imports = [];
|
|
48
|
+
for (const directive of directives["cargo-add-options"] ?? []) {
|
|
49
|
+
const [packageSpec, ...options] = directive.args;
|
|
50
|
+
if (!packageSpec) continue;
|
|
51
|
+
imports.push({
|
|
52
|
+
package: packageSpec,
|
|
53
|
+
cargoAddOptions: options.join(" "),
|
|
54
|
+
isExternal: true
|
|
55
|
+
});
|
|
56
|
+
}
|
|
36
57
|
for (const node of tree.rootNode.children) {
|
|
37
58
|
let crateName;
|
|
38
59
|
if (node.type === "use_declaration") {
|
|
@@ -42,6 +63,7 @@ async function getImports(code) {
|
|
|
42
63
|
}
|
|
43
64
|
if (crateName) {
|
|
44
65
|
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.`);
|
|
66
|
+
if (imports.some((i) => i.package === crateName)) continue;
|
|
45
67
|
imports.push({
|
|
46
68
|
package: crateName,
|
|
47
69
|
isExternal: !BUILTIN_CRATES.has(crateName)
|
|
@@ -66,14 +88,14 @@ async function createEnvironment(code, imports, options = {}) {
|
|
|
66
88
|
await mkdir(`${path}/src`, { recursive: true });
|
|
67
89
|
await Promise.all([writeFile(`${path}/Cargo.toml`, manifest), writeFile(`${path}/src/main.rs`, code)]);
|
|
68
90
|
if (imports.length > 0) {
|
|
69
|
-
const crates = imports.filter((i) => i.isExternal)
|
|
91
|
+
const crates = imports.filter((i) => i.isExternal);
|
|
70
92
|
logger.info("Installing crates", crates);
|
|
71
|
-
await execUntilExit(`cargo add ${
|
|
93
|
+
for (const crate of crates) await execUntilExit(`cargo add ${crate.package}${crate.cargoAddOptions ? ` ${crate.cargoAddOptions}` : ""}`, path);
|
|
72
94
|
}
|
|
73
95
|
logger.info("Created Rust environment at", path);
|
|
74
96
|
return path;
|
|
75
97
|
}
|
|
76
98
|
|
|
77
99
|
//#endregion
|
|
78
|
-
export { createEnvironment, getImports };
|
|
100
|
+
export { createEnvironment, getComments, getImports };
|
|
79
101
|
//# sourceMappingURL=utils.mjs.map
|
|
@@ -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
|
|
1
|
+
{"version":3,"file":"utils.mjs","names":[],"sources":["../../../src/adapters/rust/utils.ts"],"sourcesContent":["import { Language, Parser, Tree } 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, getDirectives } from \"@/utils\";\nimport type { Comment } 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 getTree(code: string): Promise<Tree | null> {\n const language = await getRustLanguage();\n const parser = new Parser();\n parser.setLanguage(language);\n return parser.parse(code);\n}\n\nasync function getComments(input: string | Tree): Promise<Comment[]> {\n const tree = typeof input === \"string\" ? await getTree(input) : input;\n return tree\n ? tree.rootNode.descendantsOfType(\"line_comment\").map((comment) => ({\n value: comment.text,\n start: comment.startIndex,\n end: comment.endIndex,\n }))\n : [];\n}\n\nasync function getImports(code: string): Promise<Import[]> {\n const tree = await getTree(code);\n const directives = await getDirectives(code, \"rust\");\n const imports: Import[] = [];\n\n for (const directive of directives[\"cargo-add-options\"] ?? []) {\n const [packageSpec, ...options] = directive.args;\n if (!packageSpec) {\n continue;\n }\n\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\ntype Directive = \"cargo-add-options\";\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, getComments };\nexport type { Import, Directive };\n"],"mappings":"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"}
|
|
@@ -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,22 +1,80 @@
|
|
|
1
|
+
import { filterHiddenCode, 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
|
|
21
|
+
const newTree = structuredClone(tree);
|
|
22
|
+
newTree.children = await Promise.all(tree.children.map(async (node) => {
|
|
8
23
|
if (node.type === "code" && node.lang && node.lang in map) {
|
|
24
|
+
let newNode = node;
|
|
25
|
+
const lang = node.lang;
|
|
26
|
+
const contentHash = hash("sha1", JSON.stringify([
|
|
27
|
+
node.value,
|
|
28
|
+
lang,
|
|
29
|
+
node.meta
|
|
30
|
+
]));
|
|
31
|
+
if (cache) {
|
|
32
|
+
const [success, originalResults] = await readFile(`${CONTENT_CACHE_DIR}/${contentHash}.txt`, { encoding: "utf-8" }).then((content) => {
|
|
33
|
+
const [success, results] = content.split("\n");
|
|
34
|
+
if (!["true", "false"].includes(success)) return [null, null];
|
|
35
|
+
return [success === "true", JSON.parse(results ?? "null")];
|
|
36
|
+
}).catch(() => [null, null]);
|
|
37
|
+
if (typeof success === "boolean") {
|
|
38
|
+
logger.debug("Result found in cache, skipping validation");
|
|
39
|
+
if (!success) throw JSON.stringify({
|
|
40
|
+
message: "One or more code blocks failed validation.",
|
|
41
|
+
originalResults
|
|
42
|
+
});
|
|
43
|
+
else return node;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
9
46
|
const metadata = node.meta?.split(" ") ?? [];
|
|
10
47
|
const docValIndex = metadata.findIndex((item) => item === "docval");
|
|
11
|
-
|
|
48
|
+
const noDocValIndex = metadata.findIndex((item) => item === "no-docval");
|
|
49
|
+
if (include && noDocValIndex === -1 || docValIndex !== -1) promises.push((async () => {
|
|
50
|
+
try {
|
|
51
|
+
return {
|
|
52
|
+
result: await map[lang](node.value, metadata),
|
|
53
|
+
contentHash
|
|
54
|
+
};
|
|
55
|
+
} catch (err) {
|
|
56
|
+
throw {
|
|
57
|
+
result: err,
|
|
58
|
+
contentHash
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
})());
|
|
62
|
+
newNode.value = await filterHiddenCode(node.value, lang);
|
|
63
|
+
return newNode;
|
|
12
64
|
} else if (node.type === "code" && process.env.DOCVAL_TEST_NO_SKIP === "true") throw new Error(`Skipped code block: ${JSON.stringify(node)}`);
|
|
13
|
-
|
|
65
|
+
return node;
|
|
66
|
+
}));
|
|
14
67
|
const res = await Promise.allSettled(promises);
|
|
68
|
+
if (cache) await Promise.all(res.map(async (r) => {
|
|
69
|
+
const path = (hash) => `${CONTENT_CACHE_DIR}/${hash}.txt`;
|
|
70
|
+
if (r.status === "rejected" && !existsSync(path(r.reason.contentHash))) await writeFile(`${CONTENT_CACHE_DIR}/${r.reason.contentHash}.txt`, `false\n${JSON.stringify(r.reason)}`);
|
|
71
|
+
else if (r.status === "fulfilled" && !existsSync(path(r.value.contentHash))) await writeFile(`${CONTENT_CACHE_DIR}/${r.value.contentHash}.txt`, "true");
|
|
72
|
+
}));
|
|
15
73
|
if (res.some((r) => r.status === "rejected")) throw JSON.stringify({
|
|
16
74
|
message: "One or more code blocks failed validation.",
|
|
17
75
|
originalResults: res
|
|
18
76
|
});
|
|
19
|
-
return
|
|
77
|
+
return newTree;
|
|
20
78
|
};
|
|
21
79
|
}
|
|
22
80
|
|
|
@@ -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, { Language } from \"@/adapters\";\nimport { CONTENT_CACHE_DIR } from \"@/constants\";\nimport { filterHiddenCode, 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 const newTree = structuredClone(tree);\n\n newTree.children = await Promise.all(\n tree.children.map(async (node) => {\n if (node.type === \"code\" && node.lang && node.lang in adapters) {\n let newNode = node;\n const lang: Language = node.lang as Language;\n const contentHash = hash(\"sha1\", JSON.stringify([node.value, 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 node;\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[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\n newNode.value = await filterHiddenCode(node.value, lang);\n return newNode;\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 return node;\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 newTree;\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;EACT,MAAM,UAAU,gBAAgB,KAAK;AAErC,UAAQ,WAAW,MAAM,QAAQ,IAC/B,KAAK,SAAS,IAAI,OAAO,SAAS;AAChC,OAAI,KAAK,SAAS,UAAU,KAAK,QAAQ,KAAK,QAAQA,KAAU;IAC9D,IAAI,UAAU;IACd,MAAM,OAAiB,KAAK;IAC5B,MAAM,cAAc,KAAK,QAAQ,KAAK,UAAU;KAAC,KAAK;KAAO;KAAM,KAAK;KAAK,CAAC,CAAC;AAC/E,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,QAAO;;;IAKb,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,MAC5B,KAAK,OACL,SACD;OAGC;OACD;cACM,KAAK;AACZ,YAAM;OACJ,QAAQ;OACR;OACD;;QAED,CACL;AAGH,YAAQ,QAAQ,MAAM,iBAAiB,KAAK,OAAO,KAAK;AACxD,WAAO;cACE,KAAK,SAAS,UAAU,QAAQ,IAAI,wBAAwB,OACrE,OAAM,IAAI,MAAM,uBAAuB,KAAK,UAAU,KAAK,GAAG;AAGhE,UAAO;IACP,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
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { getComments } from "./adapters/rust/utils.mjs";
|
|
2
|
+
import { getComments as getComments$1 } from "./adapters/javascript/utils.mjs";
|
|
1
3
|
import { spawn } from "child_process";
|
|
2
4
|
import consola from "consola";
|
|
3
5
|
|
|
@@ -25,7 +27,66 @@ function execUntilExit(command, cwd) {
|
|
|
25
27
|
async function cleanupEnvironment(path) {
|
|
26
28
|
await execUntilExit(`rm -rf ${path}`, process.cwd());
|
|
27
29
|
}
|
|
30
|
+
function isDirective(comment) {
|
|
31
|
+
return /@docval-.+/.test(comment.trim());
|
|
32
|
+
}
|
|
33
|
+
function isCodeDirective(directive) {
|
|
34
|
+
return directive === "hidden";
|
|
35
|
+
}
|
|
36
|
+
function getAssociatedCode(code, comment) {
|
|
37
|
+
const commentLineStart = code.lastIndexOf("\n", comment.start - 1);
|
|
38
|
+
const start = commentLineStart === -1 ? 0 : commentLineStart + 1;
|
|
39
|
+
const currentLineEnd = code.indexOf("\n", comment.end);
|
|
40
|
+
if (currentLineEnd === -1) return code.slice(start);
|
|
41
|
+
const nextLineStart = currentLineEnd + 1;
|
|
42
|
+
const nextLineEnd = code.indexOf("\n", nextLineStart);
|
|
43
|
+
return code.slice(start, nextLineEnd === -1 ? code.length : nextLineEnd + 1);
|
|
44
|
+
}
|
|
45
|
+
async function getDirectives(code, language) {
|
|
46
|
+
let comments = [];
|
|
47
|
+
switch (language) {
|
|
48
|
+
case "javascript":
|
|
49
|
+
case "js":
|
|
50
|
+
case "jsx":
|
|
51
|
+
case "typescript":
|
|
52
|
+
case "ts":
|
|
53
|
+
case "tsx":
|
|
54
|
+
comments = await getComments$1(code, ["javascript", "js"].includes(language) ? "js" : language === "jsx" ? "jsx" : ["typescript", "ts"].includes(language) ? "ts" : "tsx");
|
|
55
|
+
break;
|
|
56
|
+
case "rust":
|
|
57
|
+
case "rs":
|
|
58
|
+
comments = await getComments(code);
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
const directives = {};
|
|
62
|
+
const prefix = "@docval-";
|
|
63
|
+
for (const comment of comments) {
|
|
64
|
+
if (!isDirective(comment.value)) continue;
|
|
65
|
+
const trimmed = comment.value.trim();
|
|
66
|
+
const prefixIndex = trimmed.indexOf(prefix);
|
|
67
|
+
if (prefixIndex === -1) continue;
|
|
68
|
+
const content = trimmed.slice(prefixIndex + 8);
|
|
69
|
+
const spaceIdx = content.indexOf(" ");
|
|
70
|
+
const directive = spaceIdx === -1 ? content : content.slice(0, spaceIdx);
|
|
71
|
+
const args = spaceIdx === -1 ? [] : content.slice(spaceIdx + 1).split(" ").filter(Boolean);
|
|
72
|
+
const key = directive;
|
|
73
|
+
directives[key] ??= [];
|
|
74
|
+
directives[key].push({
|
|
75
|
+
args,
|
|
76
|
+
code: isCodeDirective(directive) ? getAssociatedCode(code, comment) : void 0
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
return directives;
|
|
80
|
+
}
|
|
81
|
+
async function filterHiddenCode(code, language) {
|
|
82
|
+
const directives = await getDirectives(code, language);
|
|
83
|
+
let filteredCode = code;
|
|
84
|
+
if (directives.hidden) {
|
|
85
|
+
for (const directive of directives.hidden) if (directive.code) filteredCode = filteredCode.replace(directive.code, "");
|
|
86
|
+
}
|
|
87
|
+
return filteredCode;
|
|
88
|
+
}
|
|
28
89
|
|
|
29
90
|
//#endregion
|
|
30
|
-
export { cleanupEnvironment, execUntilExit, logger };
|
|
91
|
+
export { cleanupEnvironment, execUntilExit, filterHiddenCode, getDirectives, logger };
|
|
31
92
|
//# 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":"
|
|
1
|
+
{"version":3,"file":"utils.mjs","names":["getCommentsJavaScript","getCommentsRust"],"sources":["../src/utils.ts"],"sourcesContent":["import { spawn } from \"child_process\";\nimport consola from \"consola\";\nimport { Language } from \"./adapters\";\nimport { Directive as DirectiveRust } from \"./adapters/rust/utils\";\nimport { getComments as getCommentsJavaScript } from \"./adapters/javascript/utils\";\nimport { getComments as getCommentsRust } from \"./adapters/rust/utils\";\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 Directive = \"hidden\";\n\ntype Comment = {\n value: string;\n start: number;\n end: number;\n};\n\ntype DirectiveValue = {\n args: string[];\n /**\n * Code associated with the directive\n */\n code?: string;\n};\n\ntype DirectiveKey = Directive | DirectiveRust;\ntype DirectiveMap = Partial<Record<DirectiveKey, DirectiveValue[]>>;\n\nfunction isDirective(comment: string): boolean {\n return /@docval-.+/.test(comment.trim());\n}\n\nfunction isCodeDirective(directive: string): boolean {\n return directive === \"hidden\";\n}\n\nfunction getAssociatedCode(code: string, comment: Comment): string | undefined {\n const commentLineStart = code.lastIndexOf(\"\\n\", comment.start - 1);\n const start = commentLineStart === -1 ? 0 : commentLineStart + 1;\n const currentLineEnd = code.indexOf(\"\\n\", comment.end);\n if (currentLineEnd === -1) {\n return code.slice(start);\n }\n\n const nextLineStart = currentLineEnd + 1;\n const nextLineEnd = code.indexOf(\"\\n\", nextLineStart);\n\n return code.slice(start, nextLineEnd === -1 ? code.length : nextLineEnd + 1);\n}\n\nasync function getDirectives(code: string, language: Language): Promise<DirectiveMap> {\n let comments: Comment[] = [];\n switch (language) {\n case \"javascript\":\n case \"js\":\n case \"jsx\":\n case \"typescript\":\n case \"ts\":\n case \"tsx\":\n const jsType = [\"javascript\", \"js\"].includes(language)\n ? \"js\"\n : language === \"jsx\"\n ? \"jsx\"\n : [\"typescript\", \"ts\"].includes(language)\n ? \"ts\"\n : \"tsx\";\n comments = await getCommentsJavaScript(code, jsType);\n break;\n case \"rust\":\n case \"rs\":\n comments = await getCommentsRust(code);\n break;\n }\n\n const directives: DirectiveMap = {};\n const prefix = \"@docval-\";\n for (const comment of comments) {\n if (!isDirective(comment.value)) continue;\n const trimmed = comment.value.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 const key = directive as DirectiveKey;\n directives[key] ??= [];\n directives[key].push({\n args,\n code: isCodeDirective(directive) ? getAssociatedCode(code, comment) : undefined,\n });\n }\n\n return directives;\n}\n\nasync function filterHiddenCode(code: string, language: Language): Promise<string> {\n const directives = await getDirectives(code, language);\n let filteredCode = code;\n if (directives.hidden) {\n for (const directive of directives.hidden) {\n if (directive.code) {\n filteredCode = filteredCode.replace(directive.code, \"\");\n }\n }\n }\n\n return filteredCode;\n}\n\nexport { execUntilExit, cleanupEnvironment, logger, getDirectives, filterHiddenCode };\nexport type { Comment };\n"],"mappings":";;;;;;AAOA,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;;AAsBtD,SAAS,YAAY,SAA0B;AAC7C,QAAO,aAAa,KAAK,QAAQ,MAAM,CAAC;;AAG1C,SAAS,gBAAgB,WAA4B;AACnD,QAAO,cAAc;;AAGvB,SAAS,kBAAkB,MAAc,SAAsC;CAC7E,MAAM,mBAAmB,KAAK,YAAY,MAAM,QAAQ,QAAQ,EAAE;CAClE,MAAM,QAAQ,qBAAqB,KAAK,IAAI,mBAAmB;CAC/D,MAAM,iBAAiB,KAAK,QAAQ,MAAM,QAAQ,IAAI;AACtD,KAAI,mBAAmB,GACrB,QAAO,KAAK,MAAM,MAAM;CAG1B,MAAM,gBAAgB,iBAAiB;CACvC,MAAM,cAAc,KAAK,QAAQ,MAAM,cAAc;AAErD,QAAO,KAAK,MAAM,OAAO,gBAAgB,KAAK,KAAK,SAAS,cAAc,EAAE;;AAG9E,eAAe,cAAc,MAAc,UAA2C;CACpF,IAAI,WAAsB,EAAE;AAC5B,SAAQ,UAAR;EACE,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;EACL,KAAK;AAQH,cAAW,MAAMA,cAAsB,MAPxB,CAAC,cAAc,KAAK,CAAC,SAAS,SAAS,GAClD,OACA,aAAa,QACX,QACA,CAAC,cAAc,KAAK,CAAC,SAAS,SAAS,GACrC,OACA,MAC4C;AACpD;EACF,KAAK;EACL,KAAK;AACH,cAAW,MAAMC,YAAgB,KAAK;AACtC;;CAGJ,MAAM,aAA2B,EAAE;CACnC,MAAM,SAAS;AACf,MAAK,MAAM,WAAW,UAAU;AAC9B,MAAI,CAAC,YAAY,QAAQ,MAAM,CAAE;EACjC,MAAM,UAAU,QAAQ,MAAM,MAAM;EACpC,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;EACxB,MAAM,MAAM;AACZ,aAAW,SAAS,EAAE;AACtB,aAAW,KAAK,KAAK;GACnB;GACA,MAAM,gBAAgB,UAAU,GAAG,kBAAkB,MAAM,QAAQ,GAAG;GACvE,CAAC;;AAGJ,QAAO;;AAGT,eAAe,iBAAiB,MAAc,UAAqC;CACjF,MAAM,aAAa,MAAM,cAAc,MAAM,SAAS;CACtD,IAAI,eAAe;AACnB,KAAI,WAAW,QACb;OAAK,MAAM,aAAa,WAAW,OACjC,KAAI,UAAU,KACZ,gBAAe,aAAa,QAAQ,UAAU,MAAM,GAAG;;AAK7D,QAAO"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@borrowdev/docval",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
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
|
],
|