diff --git a/libs/langchain-scripts/bin/build b/libs/langchain-scripts/bin/build new file mode 100755 index 000000000000..b960d786f4df --- /dev/null +++ b/libs/langchain-scripts/bin/build @@ -0,0 +1,874 @@ +#!/usr/bin/env node + +import { Command } from "commander"; +import * as fs from "node:fs"; +import path from "node:path"; +import ts from "typescript"; +import { rollup } from "rollup"; + +/** + * @typedef {Object} ExtraImportMapEntry + * @property {Array} modules + * @property {Array} alias + * @property {string} path + */ + +/** + * @typedef {Object} ImportData + * @property {Record} exportedAliases + * @property {Record} imports + */ + +/** + * @param {any} obj + * @returns {obj is LangChainConfig} + */ +function _verifyObjectIsLangChainConfig( + obj +) { + if (typeof obj !== "object") { + return false; + } + if ( + !("entrypoints" in obj) || + !("tsConfigPath" in obj) || + !("cjsSource" in obj) || + !("cjsDestination" in obj) || + !("abs" in obj) + ) { + return false; + } + if (typeof obj.entrypoints !== "object") { + return false; + } + if (Object.values(obj.entrypoints).some((v) => typeof v !== "string")) { + return false; + } + if ( + typeof obj.tsConfigPath !== "string" || + typeof obj.cjsSource !== "string" || + typeof obj.cjsDestination !== "string" + ) { + return false; + } + if (typeof obj.abs !== "function") { + return false; + } + + // Optional fields + if ( + "requiresOptionalDependency" in obj && + (!Array.isArray(obj.requiresOptionalDependency) || + obj.requiresOptionalDependency.some((v) => typeof v !== "string")) + ) { + return false; + } + if ( + "deprecatedNodeOnly" in obj && + (!Array.isArray(obj.deprecatedNodeOnly) || + obj.deprecatedNodeOnly.some((v) => typeof v !== "string")) + ) { + return false; + } + if ( + "deprecatedOmitFromImportMap" in obj && + (!Array.isArray(obj.deprecatedOmitFromImportMap) || + obj.deprecatedOmitFromImportMap.some((v) => typeof v !== "string")) + ) { + return false; + } + if ("packageSuffix" in obj && typeof obj.packageSuffix !== "string") { + return false; + } + if ( + "shouldTestExports" in obj && + typeof obj.shouldTestExports !== "boolean" + ) { + return false; + } + if ( + "extraImportMapEntries" in obj && + !Array.isArray(obj.deprecatedOmitFromImportMap) + ) { + return false; + } + if ( + "gitignorePaths" in obj && + (!Array.isArray(obj.gitignorePaths) || + obj.gitignorePaths.some((v) => typeof v !== "string")) + ) { + return false; + } + if ("internals" in obj && !Array.isArray(obj.internals)) { + return false; + } + return true; +} + + +async function moveAndRename({ + /** @type {string} */ + source, + /** @type {string} */ + dest, + /** @type {(p: string) => string} */ + abs, +}) { + try { + for (const file of await fs.promises.readdir(abs(source), { withFileTypes: true })) { + if (file.isDirectory()) { + await moveAndRename({ + source: `${source}/${file.name}`, + dest: `${dest}/${file.name}`, + abs, + }); + } else if (file.isFile()) { + const parsed = path.parse(file.name); + + // Ignore anything that's not a .js file + if (parsed.ext !== ".js") { + continue; + } + + // Rewrite any require statements to use .cjs + const content = await fs.promises.readFile(abs(`${source}/${file.name}`), "utf8"); + const rewritten = content.replace( + /require\("(\..+?).js"\)/g, + (_, p1) => `require("${p1}.cjs")` + ); + + // Rename the file to .cjs + const renamed = path.format({ name: parsed.name, ext: ".cjs" }); + + await fs.promises.writeFile(abs(`${dest}/${renamed}`), rewritten, "utf8"); + } + } + } catch (err) { + console.error(err); + process.exit(1); + } +} + +/** @returns {Promise} */ +async function getPackageJson() { + return JSON.parse(await fs.promises.readFile("package.json", "utf-8")); +} + +async function listEntrypoints() { + const { exports } = await getPackageJson(); + /** @type {Record | null} */ + const exportsWithoutPackageJSON = exports + ? Object.entries(exports) + .filter(([k]) => k !== "./package.json") + .reduce((acc, [k, v]) => ({ ...acc, [k]: v }), {}) + : null; + + if (!exportsWithoutPackageJSON) { + throw new Error("No exports found in package.json"); + } + /** @type {string[]} */ + const entrypoints = []; + + for (const [key, value] of Object.entries(exportsWithoutPackageJSON)) { + if (key === "./package.json") { + continue; + } + if (typeof value === "string") { + entrypoints.push(value); + } else if ( + "import" in value && + value.import && + typeof value.import === "string" + ) { + entrypoints.push(value.import); + } + } + + return entrypoints; +} + +/** + * + * @param {Array} extraInternals + * @default [...Object.keys(packageJson.dependencies ?? {}), ...Object.keys(packageJson.peerDependencies ?? {})] + * @returns {Promise>} + */ +async function listExternals( + extraInternals +) { + const packageJson = await getPackageJson(); + return [ + ...Object.keys(packageJson.dependencies ?? {}), + ...Object.keys(packageJson.peerDependencies ?? {}), + ...extraInternals, + ]; +} + +/** + * + * @param {undefined | TreeShakingArgs} options + */ +export async function checkTreeShaking(options) { + const externals = await listExternals(options?.extraInternals ?? []); + const entrypoints = await listEntrypoints(); + const consoleLog = console.log; + /** @type {Map} */ + const reportMap = new Map(); + + for (const entrypoint of entrypoints) { + let sideEffects = ""; + + console.log = function (...args) { + const line = args.length ? args.join(" ") : ""; + if (line.trim().startsWith("First side effect in")) { + sideEffects += `${line}\n`; + } + }; + + await rollup({ + external: externals, + input: entrypoint, + experimentalLogSideEffects: true, + }); + + reportMap.set(entrypoint, { + log: sideEffects, + hasSideEffects: sideEffects.length > 0, + }); + } + + console.log = consoleLog; + + let failed = false; + for (const [entrypoint, report] of reportMap) { + if (report.hasSideEffects) { + failed = true; + console.log("---------------------------------"); + console.log(`Tree shaking failed for ${entrypoint}`); + console.log(report.log); + } + } + + if (failed) { + process.exit(1); + } else { + console.log("Tree shaking checks passed!"); + } +} + + +function identifySecrets(absTsConfigPath) { + const secrets = new Set(); + + const tsConfig = ts.parseJsonConfigFileContent( + ts.readJsonConfigFile(absTsConfigPath, (p) => fs.readFileSync(p, "utf-8")), + ts.sys, + "./src/" + ); + + // `tsConfig.options.target` is not always defined when running this + // via the `@langchain/scripts` package. Instead, fallback to the raw + // tsConfig.json file contents. + const tsConfigFileContentsText = + "text" in tsConfig.raw + ? JSON.parse(tsConfig.raw.text) + : { compilerOptions: {} }; + + const tsConfigTarget = + tsConfig.options.target || tsConfigFileContentsText.compilerOptions.target; + + for (const fileName of tsConfig.fileNames.filter( + (fn) => !fn.endsWith("test.ts") + )) { + if (!tsConfigTarget) { + continue; + } + + const sourceFile = ts.createSourceFile( + fileName, + fs.readFileSync(fileName, "utf-8"), + tsConfigTarget, + true + ); + + sourceFile.forEachChild((node) => { + switch (node.kind) { + case ts.SyntaxKind.ClassDeclaration: + case ts.SyntaxKind.ClassExpression: { + node.forEachChild((node) => { + // look for get lc_secrets() + switch (node.kind) { + case ts.SyntaxKind.GetAccessor: { + const property = node; + if ( + ts.isGetAccessor(property) && + property.name.getText() === "lc_secrets" + ) { + // look for return { ... } + property.body?.statements.forEach((stmt) => { + if ( + ts.isReturnStatement(stmt) && + stmt.expression && + ts.isObjectLiteralExpression(stmt.expression) + ) { + stmt.expression.properties.forEach((element) => { + if (ts.isPropertyAssignment(element)) { + // Type guard for PropertyAssignment + if ( + element.initializer && + ts.isStringLiteral(element.initializer) + ) { + const secret = element.initializer.text; + + if (secret.toUpperCase() !== secret) { + throw new Error( + `Secret identifier must be uppercase: ${secret} at ${fileName}` + ); + } + if (/\s/.test(secret)) { + throw new Error( + `Secret identifier must not contain whitespace: ${secret} at ${fileName}` + ); + } + + secrets.add(secret); + } + } + }); + } + }); + } + break; + } + default: + break; + } + }); + break; + } + default: + break; + } + }); + } + + return secrets; +} + +// .gitignore +const DEFAULT_GITIGNORE_PATHS = ["node_modules", "dist", ".yarn"]; + +/** + * List of test-exports-* packages which we use to test that the exports field + * works correctly across different JS environments. + * Each entry is a tuple of [package name, import statement]. + * @type {Array<[string, (p: string) => string]>} + */ +const testExports = [ + [ + "test-exports-esm", + (p) => + `import * as ${p.replace(/\//g, "_")} from "langchain/${p}";`, + ], + [ + "test-exports-esbuild", + (p) => + `import * as ${p.replace(/\//g, "_")} from "langchain/${p}";`, + ], + [ + "test-exports-cjs", + (p) => + `const ${p.replace(/\//g, "_")} = require("langchain/${p}");`, + ], + ["test-exports-cf", (p) => `export * from "langchain/${p}";`], + ["test-exports-vercel", (p) => `export * from "langchain/${p}";`], + ["test-exports-vite", (p) => `export * from "langchain/${p}";`], + ["test-exports-bun", (p) => `export * from "langchain/${p}";`], +]; + +/** + * + * @param {string} relativePath + * @param {(json: Record) => Record} updateFunction + */ +const updateJsonFile = ( + relativePath, + updateFunction +) => { + const contents = fs.readFileSync(relativePath).toString(); + const res = updateFunction(JSON.parse(contents)); + fs.writeFileSync(relativePath, `${JSON.stringify(res, null, 2)}\n`); +}; + +/** + * @param {Record} entrypoints + * @returns {Record} + */ +const generateFiles = ( + entrypoints +) => { + const files = [...Object.entries(entrypoints)].flatMap(([key, value]) => { + const nrOfDots = key.split("/").length - 1; + const relativePath = "../".repeat(nrOfDots) || "./"; + const compiledPath = `${relativePath}dist/${value}.js`; + return [ + [ + `${key}.cjs`, + `module.exports = require('${relativePath}dist/${value}.cjs');`, + ], + [`${key}.js`, `export * from '${compiledPath}'`], + [`${key}.d.ts`, `export * from '${compiledPath}'`], + [`${key}.d.cts`, `export * from '${compiledPath}'`], + ]; + }); + + return Object.fromEntries(files); +}; + +const updateConfig = ({ + /** @type {Record} */ + entrypoints, + /** @type {Array} */ + deprecatedNodeOnly, + /** @type {Array} */ + requiresOptionalDependency, + /** @type {boolean} */ + shouldTestExports, +}) => { + const generatedFiles = generateFiles(entrypoints); + const filenames = Object.keys(generatedFiles); + + // Update package.json `exports` and `files` fields + updateJsonFile("./package.json", (json) => ({ + ...json, + exports: Object.assign( + Object.fromEntries( + [...Object.keys(entrypoints)].map((key) => { + const entryPoint = { + types: { + import: `./${key}.d.ts`, + require: `./${key}.d.cts`, + default: `./${key}.d.ts`, + }, + import: `./${key}.js`, + require: `./${key}.cjs`, + }; + + return [key === "index" ? "." : `./${key}`, entryPoint]; + }) + ), + { "./package.json": "./package.json" } + ), + files: ["dist/", ...filenames], + })); + + // Write generated files + Object.entries(generatedFiles).forEach(([filename, content]) => { + fs.mkdirSync(path.dirname(filename), { recursive: true }); + fs.writeFileSync(filename, content); + }); + + // Update .gitignore + fs.writeFileSync( + "./.gitignore", + `${filenames.join("\n")}\n${DEFAULT_GITIGNORE_PATHS.join("\n")}\n` + ); + + if (shouldTestExports) { + // Update test-exports-*/entrypoints.js + const entrypointsToTest = Object.keys(entrypoints) + .filter((key) => !deprecatedNodeOnly.includes(key)) + .filter((key) => !requiresOptionalDependency.includes(key)); + testExports.forEach(([pkg, importStatement]) => { + const contents = `${entrypointsToTest + .map((key) => importStatement(key)) + .join("\n")}\n`; + fs.writeFileSync( + `../environment_tests/${pkg}/src/entrypoints.js`, + contents + ); + }); + } +}; + +const cleanGenerated = ({ + /** @type {Record} */ + entrypoints, +}) => { + const filenames = Object.keys(generateFiles(entrypoints)); + filenames.forEach((fname) => { + try { + fs.unlinkSync(fname); + } catch { + // ignore error + } + }); +}; + +// Tuple describing the auto-generated import map (used by langchain/load) +// [package name, import statement, import map path] +// This will not include entrypoints deprecated or requiring optional deps. +/** + * + * @param {string | null} packageSuffix + * @returns {[string, (k: string, p: string) => string, string]} + */ +const importMap = ( + packageSuffix +) => [ + `langchain${packageSuffix ? `-${packageSuffix}` : ""}`, + (k, p) => + `export * as ${k.replace(/\//g, "__")} from "../${p}.js";`, + "src/load/import_map.ts", + ]; + +const generateImportMap = ({ + /** @type {Record} */ + entrypoints, + /** @type {Array} */ + requiresOptionalDependency, + /** @type {Array} */ + deprecatedNodeOnly, + /** @type {Array} */ + deprecatedOmitFromImportMap, + /** @type {string | null} */ + packageSuffix, + /** @type {Array} */ + extraImportMapEntries, +}) => { + // Generate import map + const entrypointsToInclude = Object.keys(entrypoints) + .filter((key) => key !== "load") + .filter((key) => !deprecatedNodeOnly.includes(key)) + .filter((key) => !requiresOptionalDependency.includes(key)) + .filter((key) => !deprecatedOmitFromImportMap.includes(key)); + const [pkg, importStatement, importMapPath] = importMap(packageSuffix); + const contents = `${entrypointsToInclude + .map((key) => importStatement(key, entrypoints[key])) + .join("\n")}\n`; + const extraImportData = extraImportMapEntries.reduce( + (data, { modules, alias, path }) => { + const newData = { ...data }; + if (!newData.imports[path]) { + newData.imports[path] = []; + } + newData.imports[path] = [ + ...new Set(newData.imports[path].concat(modules)), + ]; + const exportAlias = alias.join("__"); + if (!newData.exportedAliases[exportAlias]) { + newData.exportedAliases[exportAlias] = []; + } + newData.exportedAliases[exportAlias] = + newData.exportedAliases[exportAlias].concat(modules); + return newData; + }, + { + imports: {}, + exportedAliases: {}, + } + ); + const extraImportStatements = Object.entries(extraImportData.imports).map( + ([path, modules]) => + `import {\n ${modules.join(",\n ")}\n} from "${path}";` + ); + const extraDeclarations = Object.entries(extraImportData.exportedAliases).map( + ([exportAlias, modules]) => + [ + `const ${exportAlias} = {\n ${modules.join(",\n ")}\n};`, + `export { ${exportAlias} };`, + ].join("\n") + ); + const extraContent = `${extraImportStatements.join( + "\n" + )}\n${extraDeclarations.join("\n")}\n`; + fs.writeFileSync( + `../${pkg}/${importMapPath}`, + `// Auto-generated by \`scripts/create-entrypoints.js\`. Do not edit manually.\n\n${contents}${extraContent}` + ); +}; + +/** + * + * @param {string | null} packageSuffix + * @returns {[string, string]} + */ +const importTypes = (packageSuffix) => [ + `langchain${packageSuffix ? `-${packageSuffix}` : ""}`, + "src/load/import_type.ts", +]; + +const generateImportTypes = ({ + /** @type {string} */ + absTsConfigPath, + /** @type {string | null} */ + packageSuffix, +}) => { + // Generate import types + const [pkg, importTypesPath] = importTypes(packageSuffix); + + fs.writeFileSync( + `../${pkg}/${importTypesPath}`, + `// Auto-generated by \`scripts/create-entrypoints.js\`. Do not edit manually. + +export interface OptionalImportMap {} + +export interface SecretMap { +${[...identifySecrets(absTsConfigPath)] + .sort() + .map((secret) => ` ${secret}?: string;`) + .join("\n")} +} +` + ); +}; + +/** + * @param {string | null} packageSuffix + * @returns {[string, (k: string) => string, string]} + */ +const importConstants = ( + packageSuffix +) => [ + `langchain${packageSuffix ? `-${packageSuffix}` : ""}`, + (k) => + ` "langchain${packageSuffix ? `_${packageSuffix}` : ""}/${k}"`, + "src/load/import_constants.ts", + ]; + +const generateImportConstants = ({ + /** @type {Record} */ + entrypoints, + /** @type {Array} */ + requiresOptionalDependency, + /** @type {Array} */ + deprecatedNodeOnly, + /** @type {string | null} */ + packageSuffix, +}) => { + // Generate import constants + const entrypointsToInclude = Object.keys(entrypoints) + .filter((key) => !deprecatedNodeOnly.includes(key)) + .filter((key) => requiresOptionalDependency.includes(key)); + const [pkg, importStatement, importConstantsPath] = + importConstants(packageSuffix); + const contents = + entrypointsToInclude.length > 0 + ? `\n${entrypointsToInclude + .map((key) => importStatement(key)) + .join(",\n")},\n];\n` + : "];\n"; + fs.writeFileSync( + `../${pkg}/${importConstantsPath}`, + `// Auto-generated by \`scripts/create-entrypoints.js\`. Do not edit manually.\n\nexport const optionalImportEntrypoints: string[] = [${contents}` + ); +}; + +export function createEntrypoints({ + /** + * This lists all the entrypoints for the library. Each key corresponds to an + * importable path, eg. `import { AgentExecutor } from "langchain/agents"`. + * The value is the path to the file in `src/` that exports the entrypoint. + * This is used to generate the `exports` field in package.json. + * Order is not important. + * @type {Record} + */ + entrypoints, + /** + * Entrypoints in this list require an optional dependency to be installed. + * Therefore they are not tested in the generated test-exports-* packages. + * @type {undefined | string[]} + */ + requiresOptionalDependency = [], + /** + * Entrypoints in this list will + * 1. Be excluded from the documentation + * 2. Be only available in Node.js environments (for backwards compatibility) + * @type {undefined | string[]} + */ + deprecatedNodeOnly = [], + /** + * Endpoints that are deprecated due to redundancy. Will not appear in the import map. + * @type {string[]} + */ + deprecatedOmitFromImportMap = [], + /** + * The suffix of the package. Eg. `community` for `@langchain/community`. + * Used in the generated import map. + * @type {undefined | string} + */ + packageSuffix, + /** + * Whether or not to write to the test exports files. At the moment this only + * applies to the `langchain` package. + * @type {undefined | boolean} + */ + shouldTestExports = false, + /** + * Extra entries to add to the import map. + * @type {undefined | Array} + */ + extraImportMapEntries = [], + /** + * The absolute path to the tsconfig.json file. + * @type {string} + */ + absTsConfigPath, + /** + * Whether or not the pre command was passed. + * @type {boolean} + */ + isPre, + /** + * Whether or not to generate import maps + * @type {boolean} + */ + shouldGenMaps, +}) { + if (isPre) { + cleanGenerated({ entrypoints }); + if (shouldGenMaps) { + generateImportMap({ + entrypoints, + requiresOptionalDependency, + deprecatedNodeOnly, + deprecatedOmitFromImportMap, + packageSuffix: packageSuffix ?? null, + extraImportMapEntries, + }); + generateImportTypes({ + absTsConfigPath, + packageSuffix: packageSuffix ?? null, + }); + generateImportConstants({ + entrypoints, + requiresOptionalDependency, + deprecatedNodeOnly, + packageSuffix: packageSuffix ?? null, + }); + } + } else { + updateConfig({ + entrypoints, + deprecatedNodeOnly, + requiresOptionalDependency, + shouldTestExports, + }); + } +} + + +// --------SCRIPT CONTENT-------- + +async function main() { + const program = new Command(); + program + .description("Run a build script for a LangChain package.") + .option( + "--config ", + "Path to the config file, defaults to ./langchain.config.js" + ) + .option( + "--create-entrypoints", + "Pass only if you want to create entrypoints" + ) + .option("--tree-shaking", "Pass only if you want to check tree shaking") + .option("--move-cjs-dist", "Pass only if you want to move cjs to dist") + .option("--pre") + .option("--gen-maps"); + + program.parse(); + + const options = program.opts(); + + const shouldCreateEntrypoints = options.createEntrypoints; + const shouldCheckTreeShaking = options.treeShaking; + const shouldMoveCjsDist = options.moveCjsDist; + const isPre = options.pre; + const shouldGenMaps = options.genMaps; + const configFilePath = options.config ?? "./langchain.config.js"; + const resolvedConfigPath = path.resolve(process.cwd(), configFilePath); + + /** @type {LangChainConfig} */ + let config; + try { + const { config: lcConfig } = await import(resolvedConfigPath); + if (!_verifyObjectIsLangChainConfig(lcConfig)) { + throw new Error("Invalid config object."); + } + config = lcConfig; + } catch (e) { + console.error( + `Failed to read config file at path: ${configFilePath}.\n\nError: ${JSON.stringify( + e + )}` + ); + process.exit(1); + } + + if ( + [shouldCreateEntrypoints, shouldCheckTreeShaking, shouldMoveCjsDist].filter( + Boolean + ).length > 1 + ) { + console.error( + "Can only run one script at a time. Please pass only one of --create-entrypoints, --tree-shaking, --move-cjs-dist" + ); + process.exit(1); + } + + if ( + [shouldCreateEntrypoints, shouldCheckTreeShaking, shouldMoveCjsDist].filter( + Boolean + ).length === 0 + ) { + console.error( + "No script specified. Please pass one of --create-entrypoints, --tree-shaking, --move-cjs-dist" + ); + process.exit(1); + } + + if ( + (isPre || shouldGenMaps) && + [shouldCheckTreeShaking, shouldMoveCjsDist].filter(Boolean).length >= 1 + ) { + console.error( + "Can not pass --pre or --gen-maps with --tree-shaking or --move-cjs-dist" + ); + process.exit(1); + } + + if (shouldCreateEntrypoints) { + createEntrypoints({ + entrypoints: config.entrypoints, + requiresOptionalDependency: config.requiresOptionalDependency, + deprecatedNodeOnly: config.deprecatedNodeOnly, + deprecatedOmitFromImportMap: config.deprecatedOmitFromImportMap, + packageSuffix: config.packageSuffix, + shouldTestExports: config.shouldTestExports, + extraImportMapEntries: config.extraImportMapEntries, + absTsConfigPath: config.tsConfigPath, + isPre, + shouldGenMaps, + }); + } + + if (shouldCheckTreeShaking) { + await checkTreeShaking({ + extraInternals: config.internals, + }); + } + + if (shouldMoveCjsDist) { + await moveAndRename({ + source: config.cjsSource, + dest: config.cjsDestination, + abs: config.abs, + }); + } +} + +/* #__PURE__ */ main().catch((e) => { + console.error(e); + process.exit(1); +}); diff --git a/libs/langchain-scripts/package.json b/libs/langchain-scripts/package.json index 0c940dc608a2..fa1c66b2d95f 100644 --- a/libs/langchain-scripts/package.json +++ b/libs/langchain-scripts/package.json @@ -14,19 +14,19 @@ }, "homepage": "https://github.com/langchain-ai/langchainjs/tree/main/libs/langchain-scripts/", "bin": { - "lc-build": "./build.js" + "lc-build": "bin/build" }, "scripts": { "build": "yarn clean && yarn build:esm && yarn build:cjs && yarn build:scripts", - "build:esm": "NODE_OPTIONS=--max-old-space-size=4096 tsc --outDir dist/", - "build:cjs": "NODE_OPTIONS=--max-old-space-size=4096 tsc --outDir dist-cjs/ -p tsconfig.cjs.json && yarn move-cjs-to-dist && rimraf dist-cjs", + "build:esm": "NODE_OPTIONS=--max-old-space-size=4096 rm -f src/package.json && tsc --outDir dist/", + "build:cjs": "NODE_OPTIONS=--max-old-space-size=4096 echo '{}' > src/package.json && tsc --outDir dist-cjs/ -p tsconfig.cjs.json && yarn move-cjs-to-dist && rimraf dist-cjs src/package.json", "build:watch": "yarn create-entrypoints && tsc --outDir dist/ --watch", "build:scripts": "yarn create-entrypoints && yarn check-tree-shaking", "lint:eslint": "NODE_OPTIONS=--max-old-space-size=4096 eslint --cache --ext .ts,.js src/", "lint:dpdm": "dpdm --exit-code circular:1 --no-warning --no-tree src/*.ts src/**/*.ts", "lint": "yarn lint:eslint && yarn lint:dpdm", "lint:fix": "yarn lint:eslint --fix && yarn lint:dpdm", - "clean": "rimraf .turbo/ dist/ && NODE_OPTIONS=--max-old-space-size=4096 npx tsx ./src/build.ts --config ./langchain.config.js --create-entrypoints --pre", + "clean": "rimraf .turbo/ dist/ && NODE_OPTIONS=--max-old-space-size=4096 node ./bin/build --config ./langchain.config.js --create-entrypoints --pre", "prepack": "yarn build", "test": "NODE_OPTIONS=--experimental-vm-modules jest --testPathIgnorePatterns=\\.int\\.test.ts --testTimeout 30000 --maxWorkers=50%", "test:watch": "NODE_OPTIONS=--experimental-vm-modules jest --watch --testPathIgnorePatterns=\\.int\\.test.ts", @@ -34,9 +34,9 @@ "test:int": "NODE_OPTIONS=--experimental-vm-modules jest --testPathPattern=\\.int\\.test.ts --testTimeout 100000 --maxWorkers=50%", "format": "prettier --write \"src\"", "format:check": "prettier --check \"src\"", - "move-cjs-to-dist": "npx tsx ./src/build.ts --config ./langchain.config.js --move-cjs-dist", - "create-entrypoints": "npx tsx ./src/build.ts --config ./langchain.config.js --create-entrypoints", - "check-tree-shaking": "npx tsx ./src/build.ts --config ./langchain.config.js --tree-shaking" + "move-cjs-to-dist": "node ./bin/build --config ./langchain.config.js --move-cjs-dist", + "create-entrypoints": "node ./bin/build --config ./langchain.config.js --create-entrypoints", + "check-tree-shaking": "node ./bin/build --config ./langchain.config.js --tree-shaking" }, "author": "LangChain", "license": "MIT", @@ -46,7 +46,7 @@ "glob": "^10.3.10", "rollup": "^4.5.2", "ts-morph": "^21.0.1", - "typescript": "<5.2.0" + "typescript": "^5.4.5" }, "devDependencies": { "@jest/globals": "^29.5.0", diff --git a/libs/langchain-scripts/src/move-cjs-to-dist.ts b/libs/langchain-scripts/src/move-cjs-to-dist.ts index a3487aeebd34..b809cb206ef6 100644 --- a/libs/langchain-scripts/src/move-cjs-to-dist.ts +++ b/libs/langchain-scripts/src/move-cjs-to-dist.ts @@ -1,5 +1,5 @@ -import { parse, format } from "node:path"; -import { readdir, readFile, writeFile } from "node:fs/promises"; +import path from "node:path"; +import fs from "node:fs"; export async function moveAndRename({ source, @@ -11,7 +11,9 @@ export async function moveAndRename({ abs: (p: string) => string; }) { try { - for (const file of await readdir(abs(source), { withFileTypes: true })) { + for (const file of await fs.promises.readdir(abs(source), { + withFileTypes: true, + })) { if (file.isDirectory()) { await moveAndRename({ source: `${source}/${file.name}`, @@ -19,7 +21,7 @@ export async function moveAndRename({ abs, }); } else if (file.isFile()) { - const parsed = parse(file.name); + const parsed = path.parse(file.name); // Ignore anything that's not a .js file if (parsed.ext !== ".js") { @@ -27,16 +29,23 @@ export async function moveAndRename({ } // Rewrite any require statements to use .cjs - const content = await readFile(abs(`${source}/${file.name}`), "utf8"); + const content = await fs.promises.readFile( + abs(`${source}/${file.name}`), + "utf8" + ); const rewritten = content.replace( /require\("(\..+?).js"\)/g, (_, p1) => `require("${p1}.cjs")` ); // Rename the file to .cjs - const renamed = format({ name: parsed.name, ext: ".cjs" }); + const renamed = path.format({ name: parsed.name, ext: ".cjs" }); - await writeFile(abs(`${dest}/${renamed}`), rewritten, "utf8"); + await fs.promises.writeFile( + abs(`${dest}/${renamed}`), + rewritten, + "utf8" + ); } } } catch (err) { diff --git a/libs/langchain-scripts/tsconfig.cjs.json b/libs/langchain-scripts/tsconfig.cjs.json index 83f5d513ef0f..64152eb97732 100644 --- a/libs/langchain-scripts/tsconfig.cjs.json +++ b/libs/langchain-scripts/tsconfig.cjs.json @@ -1,7 +1,6 @@ { "extends": "./tsconfig.json", "compilerOptions": { - "module": "commonjs", "declaration": false }, "exclude": [ diff --git a/libs/langchain-scripts/tsconfig.json b/libs/langchain-scripts/tsconfig.json index ffc49dde54a6..c046a2f005f8 100644 --- a/libs/langchain-scripts/tsconfig.json +++ b/libs/langchain-scripts/tsconfig.json @@ -9,7 +9,7 @@ "ES2022.Object", "DOM" ], - "module": "ES2020", + "module": "NodeNext", "moduleResolution": "nodenext", "esModuleInterop": true, "declaration": true, @@ -23,11 +23,12 @@ "strict": true }, "include": [ - "src/**/*" + "src/**/*", + "bin" ], "exclude": [ "node_modules", "dist", "docs" ] -} +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 5cdb54cfd950..b928baa1a751 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9887,9 +9887,9 @@ __metadata: rollup: ^4.5.2 ts-jest: ^29.1.0 ts-morph: ^21.0.1 - typescript: <5.2.0 + typescript: ^5.4.5 bin: - lc-build: ./build.js + lc-build: bin/build languageName: unknown linkType: soft @@ -35303,6 +35303,16 @@ __metadata: languageName: node linkType: hard +"typescript@npm:^5.4.5": + version: 5.4.5 + resolution: "typescript@npm:5.4.5" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 53c879c6fa1e3bcb194b274d4501ba1985894b2c2692fa079db03c5a5a7140587a1e04e1ba03184605d35f439b40192d9e138eb3279ca8eee313c081c8bcd9b0 + languageName: node + linkType: hard + "typescript@patch:typescript@<5.2.0#~builtin, typescript@patch:typescript@~5.1.6#~builtin": version: 5.1.6 resolution: "typescript@patch:typescript@npm%3A5.1.6#~builtin::version=5.1.6&hash=1f5320" @@ -35333,6 +35343,16 @@ __metadata: languageName: node linkType: hard +"typescript@patch:typescript@^5.4.5#~builtin": + version: 5.4.5 + resolution: "typescript@patch:typescript@npm%3A5.4.5#~builtin::version=5.4.5&hash=1f5320" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 2373c693f3b328f3b2387c3efafe6d257b057a142f9a79291854b14ff4d5367d3d730810aee981726b677ae0fd8329b23309da3b6aaab8263dbdccf1da07a3ba + languageName: node + linkType: hard + "typesense@npm:^1.5.3": version: 1.5.3 resolution: "typesense@npm:1.5.3"