chore: setup codeblock extraction

This commit is contained in:
Hunter Lovell
2025-07-28 02:08:18 -07:00
parent a688d287a1
commit 176eaafbab
12 changed files with 4103 additions and 98 deletions
-51
View File
@@ -1,51 +0,0 @@
import * as path from "node:path";
import * as fs from "node:fs/promises";
import * as url from "node:url";
const mdPath = url.fileURLToPath(
new URL("./add_translation_js_ref_updated.md", import.meta.url)
);
const extractedDir = url.fileURLToPath(
new URL(
"../../../oap-langgraphjs-tools-agent/src/add_transaction_js",
import.meta.url
)
);
const files = (await fs.readdir(extractedDir, { withFileTypes: true })).sort(
(a, b) => {
const aInt = Number.parseInt(a.name.split(".")[0], 10);
const bInt = Number.parseInt(b.name.split(".")[0], 10);
return aInt - bInt;
}
);
let count = 0;
let lines = [];
for (let file of files) {
if (file.isDirectory() || !file.name.endsWith(".mts")) continue;
count += 1;
const content = await fs.readFile(path.resolve(extractedDir, file.name), {
encoding: "utf-8",
});
lines = lines.concat(
content
.split("\n")
.reduce((acc, line) => {
if (line.trimStart().startsWith("// ```")) acc.push([]);
acc.at(-1)?.push(line);
return acc;
}, [])
.map((i) => {
const tag = i[0].trimStart().slice("// ```".length);
return ["```" + tag, ...i.slice(1), "```"].join("\n");
})
);
}
await fs.writeFile(mdPath, lines.join("\n\n"));
-46
View File
@@ -1,46 +0,0 @@
import * as fs from "node:fs/promises";
import * as path from "node:path";
import * as url from "node:url";
const mdPath = url.fileURLToPath(
new URL("./add_translation_js_ref.md", import.meta.url)
);
const extractedDir = url.fileURLToPath(
new URL(
"../../../oap-langgraphjs-tools-agent/src/add_transaction_js",
import.meta.url
)
);
await fs.mkdir(extractedDir, { recursive: true });
const md = (await fs.readFile(mdPath, { encoding: "utf-8" })).split("\n");
const chunks = [];
let current = [];
for (let line of md) {
if (line.trimStart().startsWith("```")) {
if (current.length > 0) {
chunks.push(current.join("\n"));
current = [];
} else {
current.push("// " + line.trimStart());
}
} else if (current.length > 0) {
current.push(line);
}
}
if (current.length > 0) {
chunks.push(current.join("\n"));
}
for (let i = 0; i < chunks.length; i += 1) {
await fs.writeFile(path.resolve(extractedDir, `${i}.mts`), chunks[i], {
encoding: "utf-8",
});
}
console.log("finished");
@@ -0,0 +1,6 @@
.prettierrc
.eslint.config.mjs
package.json
README.md
tsconfig.json
yarn.lock
@@ -0,0 +1,19 @@
{
"$schema": "https://json.schemastore.org/prettierrc",
"printWidth": 80,
"tabWidth": 2,
"useTabs": false,
"semi": true,
"singleQuote": false,
"quoteProps": "as-needed",
"jsxSingleQuote": false,
"trailingComma": "es5",
"bracketSpacing": true,
"arrowParens": "always",
"requirePragma": false,
"insertPragma": false,
"proseWrap": "preserve",
"htmlWhitespaceSensitivity": "css",
"vueIndentScriptAndStyle": false,
"endOfLine": "lf"
}
@@ -0,0 +1 @@
# \_codeblocks
@@ -0,0 +1,14 @@
import js from "@eslint/js";
import globals from "globals";
import tseslint from "typescript-eslint";
import { defineConfig } from "eslint/config";
export default defineConfig([
{
files: ["**/*.{js,mjs,cjs,ts,mts,cts}"],
plugins: { js },
extends: ["js/recommended"],
languageOptions: { globals: globals.browser },
},
tseslint.configs.recommended,
]);
@@ -0,0 +1,27 @@
{
"name": "_codeblocks",
"packageManager": "yarn@4.6.0",
"scripts": {
"lint": "eslint .",
"lint:fix": "eslint . --fix",
"format": "prettier --write .",
"format:fix": "prettier --write . --fix"
},
"dependencies": {
"@langchain/anthropic": "^0.3.24",
"@langchain/core": "^0.3.66",
"@langchain/langgraph": "^0.3.11",
"@langchain/langgraph-api": "^0.0.52",
"@langchain/langgraph-sdk": "^0.0.102",
"@langchain/openai": "^0.6.3",
"zod": "^4.0.10"
},
"devDependencies": {
"@eslint/js": "^9.32.0",
"eslint": "^9.32.0",
"globals": "^16.3.0",
"jiti": "^2.5.1",
"typescript": "^5.8.3",
"typescript-eslint": "^8.38.0"
}
}
@@ -0,0 +1,114 @@
{
"compilerOptions": {
/* Visit https://aka.ms/tsconfig to read more about this file */
/* Projects */
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
/* Language and Environment */
"target": "esnext", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
// "jsx": "preserve", /* Specify what JSX code is generated. */
// "libReplacement": true, /* Enable lib replacement. */
// "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
/* Modules */
"module": "nodenext", /* Specify what module code is generated. */
// "rootDir": "./", /* Specify the root folder within your source files. */
"moduleResolution": "nodenext", /* Specify how TypeScript looks up a file from a given module specifier. */
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
// "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
// "types": [], /* Specify type package names to be included without being referenced in a source file. */
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
// "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
// "rewriteRelativeImportExtensions": true, /* Rewrite '.ts', '.tsx', '.mts', and '.cts' file extensions in relative import paths to their JavaScript equivalent in output files. */
// "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
// "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
// "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
// "noUncheckedSideEffectImports": true, /* Check side effect imports. */
// "resolveJsonModule": true, /* Enable importing .json files. */
// "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
/* JavaScript Support */
// "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
/* Emit */
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
// "noEmit": true, /* Disable emitting files from a compilation. */
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
// "outDir": "./", /* Specify an output folder for all emitted files. */
// "removeComments": true, /* Disable emitting comments. */
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
// "newLine": "crlf", /* Set the newline character for emitting files. */
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
/* Interop Constraints */
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
// "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
// "isolatedDeclarations": true, /* Require sufficient annotation on exports so other tools can trivially generate declaration files. */
// "erasableSyntaxOnly": true, /* Do not allow runtime constructs that are not part of ECMAScript. */
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
/* Type Checking */
"strict": false, /* Enable all strict type-checking options. */
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
// "strictBuiltinIteratorReturn": true, /* Built-in iterators are instantiated with a 'TReturn' type of 'undefined' instead of 'any'. */
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
/* Completeness */
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
"skipLibCheck": true /* Skip type checking all .d.ts files. */
""
}
}
File diff suppressed because it is too large Load Diff
+150
View File
@@ -0,0 +1,150 @@
#!/usr/bin/env python
"""Extracts typescript code blocks from a markdown file."""
import argparse
import json
import re
import os
from typing import List, TypedDict, Literal
class CodeBlock(TypedDict):
"""A code block extracted from a markdown file."""
starting_line: int
"""The line number where the code block starts in the source file"""
ending_line: int
"""The line number where the code block ends in the source file"""
indentation: int
"""Number of spaces/tabs used for indentation of the code block"""
source_file: str
"""Path to the markdown file containing this code block"""
frontmatter: str
"""Any metadata or frontmatter specified after the opening code fence"""
code: str
"""The actual code content within the code block"""
language: str
"""The language of the code block (e.g. typescript, javascript)"""
def extract_code_blocks(markdown_content: str, source_file: str) -> List[CodeBlock]:
"""Extracts code blocks from a markdown file.
Args:
markdown_content: The content of the markdown file.
source_file: The path to the markdown file.
Returns:
A list of TypedDicts, where each dict represents a code block.
"""
# Regex to find code blocks with specified languages, capturing indentation
# and frontmatter.
pattern = re.compile(
r"^(?P<indentation>\s*)```(?P<language>typescript|javascript|ts|js)(?P<frontmatter>[^\n]*)\n(?P<code>.*?)\n^(?P=indentation)```\s*$",
re.DOTALL | re.MULTILINE,
)
code_blocks: List[CodeBlock] = []
for match in pattern.finditer(markdown_content):
start_pos = match.start()
# Calculate line numbers
starting_line = markdown_content.count("\n", 0, start_pos) + 1
ending_line = starting_line + match.group(0).count("\n")
indentation_str = match.group("indentation")
code_block: CodeBlock = {
"starting_line": starting_line,
"ending_line": ending_line,
"indentation": len(indentation_str),
"source_file": source_file,
"frontmatter": match.group("frontmatter").strip(),
"code": match.group("code"),
"language": match.group("language"),
}
code_blocks.append(code_block)
return code_blocks
def dump_code_blocks(input_file: str, output_file: str, format: Literal["json", "inline"]) -> None:
"""Function to extract and save code blocks from a markdown file.
Args:
input_file: Path to the input markdown file.
output_file: Path to the output JSON file for the extracted code blocks.
format: Output format - either "json" or "inline"
"""
with open(input_file, "r", encoding="utf-8") as f:
markdown_content = f.read()
extracted_code = extract_code_blocks(markdown_content, input_file)
if len(extracted_code) == 0:
print(f"No code blocks found in {input_file}")
return
if format == "json":
with open(output_file, "w", encoding="utf-8") as f:
json.dump(extracted_code, f, indent=2)
elif format == "inline":
with open(output_file, "w", encoding="utf-8") as f:
for code_block in extracted_code:
f.write(f"// {json.dumps({k:v for k,v in code_block.items() if k != 'code'})}\n")
f.write("\n")
f.write(code_block["code"])
f.write("\n")
print(f"Extracted {len(extracted_code)} code blocks from {input_file} to {output_file}")
def main(input_path: str, output_path: str, format: Literal["json", "inline"]) -> None:
"""Main function to extract code blocks from a markdown file.
Args:
input_file: Path to the input markdown file.
output_file: Path to the output JSON file for the extracted code blocks.
format: Output format - either "json" or "inline"
"""
# Check if input path is a directory
if os.path.isdir(input_path):
if os.path.isfile(output_path):
raise ValueError("If input_path is a directory, output_path must also be a directory")
if not os.path.isdir(output_path):
os.makedirs(output_path, exist_ok=True)
# Process each markdown file in the directory recursively
for root, _, files in os.walk(input_path):
for filename in files:
if filename.endswith(".md"):
# Get relative path to maintain directory structure
rel_path = os.path.relpath(root, input_path)
input_file = os.path.join(root, filename)
# Create output directory if it doesn't exist
output_dir = os.path.join(output_path, rel_path)
os.makedirs(output_dir, exist_ok=True)
output_file = os.path.join(output_dir, filename.replace(".md", ".ts"))
dump_code_blocks(input_file, output_file, format)
else:
# Process single file
dump_code_blocks(input_path, output_path, format)
if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="Extract typescript code blocks from a markdown file."
)
parser.add_argument(
"input_file",
help="Path to the input markdown file.",
)
parser.add_argument(
"output_file",
help="Path to the output JSON file for the extracted code blocks.",
)
parser.add_argument(
"--format",
choices=["json", "inline"],
default="json",
help="Output format - either 'json' or 'inline'",
)
args = parser.parse_args()
main(args.input_file, args.output_file, args.format)