@tevm/bun-plugin
Version:
A bun plugin for tevm
1 lines • 15.3 kB
Source Map (JSON)
{"version":3,"sources":["../src/bunFile.js","../src/bunFileAccessObject.js","../src/bunPluginTevm.js"],"names":["existsSync","readFileSync","statSync","stat","mkdirSync","mkdir","writeFile","solc","defaultSolc","config","runSync","loadConfig","catchTag","logWarning","map","defaultConfig","createCache","bundler","createSolc"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Re-exports the Bun file API for working with files in the file system.\n * The Bun file API provides an optimized interface for file operations with\n * methods for reading, writing, and checking file existence.\n *\n * @type {typeof import('bun').file}\n * @see {@link https://bun.sh/docs/api/file-io | Bun File I/O Documentation}\n *\n * @example\n * ```javascript\n * import { file } from '@tevm/bun'\n *\n * // Create a file reference\n * const myFile = file('path/to/file.txt')\n *\n * // Check if the file exists\n * const exists = await myFile.exists()\n *\n * // Read file as text\n * const content = await myFile.text()\n *\n * // Write to file\n * await myFile.write('Hello, world!')\n * ```\n */\nexport const file = require('bun').file\n","import { existsSync, mkdirSync, readFileSync, statSync } from 'node:fs'\nimport { mkdir, stat, writeFile } from 'node:fs/promises'\nimport { file } from './bunFile.js'\n\n/**\n * An adapter around the Bun file API that implements the FileAccessObject interface\n * required by @tevm/base-bundler.\n *\n * This object combines Node.js file system functions with Bun's optimized file API\n * to provide a complete implementation of the FileAccessObject interface, which is\n * used by Tevm bundlers to read and write files during the Solidity compilation process.\n *\n * @type {import(\"@tevm/base-bundler\").FileAccessObject}\n *\n * @example\n * ```javascript\n * import { bunFileAccesObject } from '@tevm/bun'\n * import { bundler } from '@tevm/base-bundler'\n *\n * // Use in Tevm bundler\n * const tevmBundler = bundler(\n * config,\n * console,\n * bunFileAccesObject, // Pass the file access object\n * solcCompiler,\n * cacheInstance\n * )\n *\n * // Or use directly\n * const fileExists = await bunFileAccesObject.exists('./contracts/Token.sol')\n * if (fileExists) {\n * const content = await bunFileAccesObject.readFile('./contracts/Token.sol', 'utf8')\n * console.log(content)\n * }\n * ```\n *\n * @see {@link https://bun.sh/docs/api/file-io | Bun File I/O Documentation}\n */\nexport const bunFileAccesObject = {\n\t/**\n\t * Synchronously checks if a file exists\n\t * @param {string} path - Path to the file\n\t * @returns {boolean} - True if the file exists, false otherwise\n\t */\n\texistsSync,\n\n\t/**\n\t * Asynchronously checks if a file exists using Bun's optimized file API\n\t * @param {string} filePath - Path to the file\n\t * @returns {Promise<boolean>} - Promise resolving to true if the file exists, false otherwise\n\t */\n\texists: (filePath) => {\n\t\tconst bunFile = file(filePath)\n\t\treturn bunFile.exists()\n\t},\n\n\t/**\n\t * Asynchronously reads a file as text using Bun's optimized file API\n\t * @param {string} filePath - Path to the file\n\t * @param {BufferEncoding} _encoding - Encoding (ignored, Bun defaults to utf8)\n\t * @returns {Promise<string>} - Promise resolving to the file contents\n\t */\n\treadFile: (filePath, _encoding) => {\n\t\tconst bunFile = file(filePath)\n\t\treturn bunFile.text()\n\t},\n\n\t/**\n\t * Synchronously reads a file as text\n\t * @param {string} path - Path to the file\n\t * @param {BufferEncoding} encoding - Encoding to use\n\t * @returns {string} - File contents\n\t */\n\treadFileSync,\n\n\t/**\n\t * Synchronously writes data to a file using Bun's optimized file API\n\t * @param {string} filePath - Path to the file\n\t * @param {string} data - Data to write\n\t * @returns {number} - Non 0 if successful\n\t */\n\twriteFileSync: (filePath, data) => {\n\t\tconst bunFile = file(filePath)\n\t\treturn bunFile.writer().write(data)\n\t},\n\n\t/**\n\t * Synchronously gets file stats\n\t * @param {string} path - Path to the file\n\t * @returns {import('fs').Stats} - File stats\n\t */\n\tstatSync,\n\n\t/**\n\t * Asynchronously gets file stats\n\t * @param {string} path - Path to the file\n\t * @returns {Promise<import('fs').Stats>} - Promise resolving to file stats\n\t */\n\tstat,\n\n\t/**\n\t * Synchronously creates a directory\n\t * @param {string} path - Path to create\n\t * @param {import('fs').MakeDirectoryOptions} [options] - Options\n\t * @returns {string|undefined} - The first directory path created, or undefined\n\t */\n\tmkdirSync,\n\n\t/**\n\t * Asynchronously creates a directory\n\t * @param {string} path - Path to create\n\t * @param {import('fs').MakeDirectoryOptions} [options] - Options\n\t * @returns {Promise<string|undefined>} - Promise resolving to the first directory path created, or undefined\n\t */\n\tmkdir,\n\n\t/**\n\t * Asynchronously writes data to a file\n\t * @param {string} path - Path to the file\n\t * @param {string|NodeJS.ArrayBufferView} data - Data to write\n\t * @param {import('fs').WriteFileOptions} [options] - Options\n\t * @returns {Promise<void>} - Promise resolving when write is complete\n\t */\n\twriteFile,\n}\n","import { bundler } from '@tevm/base-bundler'\nimport { createCache } from '@tevm/bundler-cache'\nimport { defaultConfig, loadConfig } from '@tevm/config'\nimport { createSolc } from '@tevm/solc'\nimport { catchTag, logWarning, map, runSync } from 'effect/Effect'\nimport defaultSolc from 'solc'\nimport { bunFileAccesObject } from './bunFileAccessObject.js'\n\n/**\n * Creates a Bun plugin for Tevm that enables direct Solidity imports in JavaScript and TypeScript.\n *\n * This plugin allows you to import Solidity contracts directly in your JavaScript/TypeScript code,\n * where they are automatically compiled and transformed into Tevm `Contract` instances with\n * fully typed interfaces. It integrates with the Bun build system to provide seamless handling\n * of .sol files.\n *\n * @param {Object} options - Plugin configuration options\n * @param {import(\"@tevm/solc\").SolcVersions} [options.solc] - Solidity compiler version to use\n * @returns {import(\"bun\").BunPlugin} - A configured Bun plugin\n *\n * @example\n * #### Setup in a plugin.ts file\n * ```typescript\n * // plugins.ts\n * import { bunPluginTevm } from '@tevm/bun'\n * import { plugin } from 'bun'\n *\n * // Initialize with default options\n * plugin(bunPluginTevm({}))\n *\n * // Or with a specific Solidity compiler version\n * plugin(bunPluginTevm({ solc: '0.8.20' }))\n * ```\n *\n * #### Configure in bunfig.toml\n * ```toml\n * # bunfig.toml\n * preload = [\"./plugins.ts\"]\n * ```\n *\n * #### Configure TypeScript support in tsconfig.json\n * For editor integration with LSP (code completion, type checking):\n * ```json\n * {\n * \"compilerOptions\": {\n * \"plugins\": [{ \"name\": \"tevm/ts-plugin\" }]\n * }\n * }\n * ```\n *\n * #### Using imported Solidity contracts\n * ```typescript\n * // Import Solidity contracts directly\n * import { ERC20 } from '@openzeppelin/contracts/token/ERC20/ERC20.sol'\n * import { createMemoryClient } from 'tevm'\n *\n * // Access contract metadata\n * console.log('ABI:', ERC20.abi)\n * console.log('Human-readable ABI:', ERC20.humanReadableAbi)\n * console.log('Bytecode:', ERC20.bytecode)\n *\n * // Deploy and interact with the contract\n * const client = createMemoryClient()\n *\n * // Deploy the contract\n * const deployed = await client.deployContract(ERC20)\n *\n * // Call contract methods\n * const name = await deployed.read.name()\n * const tx = await deployed.write.transfer(\n * \"0x70997970C51812dc3A010C7d01b50e0d17dc79C8\",\n * 1000n\n * )\n * ```\n *\n * ### How it works\n *\n * Under the hood, the plugin processes Solidity files and generates JavaScript modules\n * that create Tevm Contract instances. For example, importing ERC20.sol results in code\n * like:\n *\n * ```typescript\n * import { createContract } from '@tevm/contract'\n *\n * export const ERC20 = createContract({\n * name: 'ERC20',\n * humanReadableAbi: [ 'function balanceOf(address): uint256', ... ],\n * bytecode: '0x...',\n * deployedBytecode: '0x...',\n * })\n * ```\n *\n * ### Custom Configuration\n *\n * For custom configuration of the Tevm compiler, add a `tevm.config.json` file\n * to your project root:\n *\n * ```json\n * {\n * \"foundryProject\": true, // Is this a Foundry project? (or path to project)\n * \"libs\": [\"lib\"], // Library directories\n * \"remappings\": { // Import remappings (like in Foundry)\n * \"foo\": \"vendored/foo\"\n * },\n * \"debug\": true, // Enable debug logging\n * \"cacheDir\": \".tevm\" // Cache directory for compiled contracts\n * }\n * ```\n *\n * @throws {Error} If there's an issue loading or processing Solidity files\n *\n * @see {@link https://tevm.sh/learn/solidity-imports/ | Tevm Solidity Import Documentation}\n */\nexport const bunPluginTevm = ({ solc = defaultSolc.version }) => {\n\treturn {\n\t\tname: '@tevm/bun-plugin',\n\t\tasync setup(build) {\n\t\t\t// Load configuration from tevm.config.json or use defaults\n\t\t\tconst config = runSync(\n\t\t\t\tloadConfig(process.cwd()).pipe(\n\t\t\t\t\tcatchTag('FailedToReadConfigError', () =>\n\t\t\t\t\t\tlogWarning('Unable to find tevm.config.json. Using default config.').pipe(map(() => defaultConfig)),\n\t\t\t\t\t),\n\t\t\t\t),\n\t\t\t)\n\n\t\t\t// Initialize cache and bundler\n\t\t\tconst solcCache = createCache(config.cacheDir, bunFileAccesObject, process.cwd())\n\t\t\tconst moduleResolver = bundler(\n\t\t\t\tconfig,\n\t\t\t\tconsole,\n\t\t\t\tbunFileAccesObject,\n\t\t\t\tsolc === defaultSolc.version ? defaultSolc : await createSolc(solc),\n\t\t\t\tsolcCache,\n\t\t\t)\n\n\t\t\t/**\n\t\t\t * Resolver for @tevm/contract imports\n\t\t\t *\n\t\t\t * This resolver ensures that @tevm/contract is correctly resolved\n\t\t\t * when imported from compiled Solidity files that might be in different\n\t\t\t * locations (node_modules, monorepo packages, etc.)\n\t\t\t */\n\t\t\tbuild.onResolve({ filter: /^@tevm\\/contract/ }, ({ path, importer }) => {\n\t\t\t\tif (\n\t\t\t\t\tpath.startsWith('@tevm/contract') &&\n\t\t\t\t\t!importer.startsWith(process.cwd()) &&\n\t\t\t\t\t!importer.includes('node_modules')\n\t\t\t\t) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tpath: require.resolve('@tevm/contract'),\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn {\n\t\t\t\t\tpath: require.resolve(path),\n\t\t\t\t}\n\t\t\t})\n\n\t\t\t/**\n\t\t\t * Loader for Solidity (.sol) files\n\t\t\t *\n\t\t\t * This loader:\n\t\t\t * 1. Checks if pre-generated JavaScript/TypeScript files exist (.ts, .js, .mjs, .cjs)\n\t\t\t * 2. If they exist, uses those directly\n\t\t\t * 3. Otherwise, compiles the Solidity file using the bundler\n\t\t\t * 4. Sets up file watching for live reload\n\t\t\t */\n\t\t\tbuild.onLoad({ filter: /\\.sol$/ }, async ({ path }) => {\n\t\t\t\t// Check for pre-generated files\n\t\t\t\tconst filePaths = [`${path}.ts`, `${path}.js`, `${path}.mjs`, `${path}.cjs`]\n\t\t\t\tconst existsArr = await Promise.all(filePaths.map((filePath) => bunFileAccesObject.exists(filePath)))\n\t\t\t\tfor (const [i, exists] of existsArr.entries()) {\n\t\t\t\t\tif (exists) {\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\tcontents: await bunFileAccesObject.readFile(/** @type {any} */ (filePaths[i]), 'utf8'),\n\t\t\t\t\t\t\twatchFiles: [filePaths[i]],\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\t// Determine if this is a script (.s.sol) file, which needs bytecode\n\t\t\t\tconst resolveBytecode = path.endsWith('.s.sol')\n\n\t\t\t\t// Compile the Solidity file\n\t\t\t\tconst { code: contents, modules } = await moduleResolver.resolveEsmModule(\n\t\t\t\t\tpath,\n\t\t\t\t\tprocess.cwd(),\n\t\t\t\t\tfalse, // Don't include AST\n\t\t\t\t\tresolveBytecode, // Include bytecode for script files\n\t\t\t\t)\n\n\t\t\t\t// Set up file watching for all non-node_modules dependencies\n\t\t\t\tconst watchFiles = Object.values(modules)\n\t\t\t\t\t.filter(({ id }) => !id.includes('node_modules'))\n\t\t\t\t\t.map(({ id }) => id)\n\n\t\t\t\treturn { contents, watchFiles }\n\t\t\t})\n\t\t},\n\t}\n}\n"]}