@tevm/base-bundler
Version:
Internal bundler for Tevm
1,265 lines (1,195 loc) • 36.1 kB
text/typescript
import { tmpdir } from 'node:os'
import { createCache } from '@tevm/bundler-cache'
import { type ModuleInfo, resolveArtifacts, resolveArtifactsSync } from '@tevm/compiler'
import type { SolcInputDescription, SolcOutput } from '@tevm/solc'
import type { Node } from 'solidity-ast/node.js'
import { afterEach, beforeEach, describe, expect, it, type Mock, vi } from 'vitest'
import { bundler } from './bundler.js'
import type { Bundler, FileAccessObject, Logger } from './types.js'
// Mock @tevm/compiler at the module level
vi.mock('@tevm/compiler', () => {
return {
resolveArtifacts: vi.fn(),
resolveArtifactsSync: vi.fn(),
}
})
// We'll mock getContractPath inside our test
const fao: FileAccessObject = {
existsSync: vi.fn() as any,
readFile: vi.fn() as any,
readFileSync: vi.fn() as any,
writeFileSync: vi.fn() as any,
statSync: vi.fn() as any,
stat: vi.fn() as any,
mkdirSync: vi.fn() as any,
exists: vi.fn() as any,
mkdir: vi.fn() as any,
writeFile: vi.fn() as any,
}
const mockModules: Record<string, ModuleInfo> = {
module1: {
id: 'id',
rawCode: `import { TestContract } from 'module2'
contract TestContract {}`,
code: `import { TestContract } from 'module2'
contract TestContract {}`,
importedIds: ['module2'],
},
}
const contractPackage = '@tevm/contract'
describe(bundler.name, () => {
it('should fall back to getContractPath when contractPackage is not a string', async () => {
// Import the actual bundler function from ./bundler.js
const { bundler: actualBundler } = await import('./bundler.js')
// Mock the getContractPath module
vi.mock('./getContractPath.js', () => ({
getContractPath: vi.fn().mockReturnValue('@tevm/mocked-contract-path'),
}))
// Save the original process.cwd
const originalCwd = process.cwd
// Mock process.cwd
process.cwd = vi.fn().mockReturnValue('/test/cwd')
// Prepare test data
const mockLogger = { error: vi.fn() }
const mockConfig = {}
const mockFao = {}
const mockSolc = { version: () => '0.8.10' }
const mockCache = {}
// Test with string contractPackage (should use the provided string directly)
const bundlerWithString = actualBundler(
mockConfig as any,
mockLogger as any,
mockFao as any,
mockSolc as any,
mockCache as any,
'custom/package/path' as any,
)
// Ensure bundler is working by checking for expected properties
expect(bundlerWithString).toHaveProperty('name', 'TevmBaseBundler')
expect(bundlerWithString).toHaveProperty('config', mockConfig)
expect(bundlerWithString).toHaveProperty('resolveDts')
// Now test with non-string values
// For each test, we need to create a fresh import to refresh the module scope
vi.resetModules()
vi.mock('./getContractPath.js', () => ({
getContractPath: vi.fn().mockReturnValue('@tevm/mocked-contract-path'),
}))
const { bundler: bundlerForUndefined } = await import('./bundler.js')
const { getContractPath: getContractPathForUndefined } = await import('./getContractPath.js')
// Test with undefined
bundlerForUndefined(
mockConfig as any,
mockLogger as any,
mockFao as any,
mockSolc as any,
mockCache as any,
undefined as any,
)
expect(getContractPathForUndefined).toHaveBeenCalled()
// Test with null
vi.resetModules()
vi.mock('./getContractPath.js', () => ({
getContractPath: vi.fn().mockReturnValue('@tevm/mocked-contract-path'),
}))
const { bundler: bundlerForNull } = await import('./bundler.js')
const { getContractPath: getContractPathForNull } = await import('./getContractPath.js')
bundlerForNull(mockConfig as any, mockLogger as any, mockFao as any, mockSolc as any, mockCache as any, null as any)
expect(getContractPathForNull).toHaveBeenCalled()
// Test with object
vi.resetModules()
vi.mock('./getContractPath.js', () => ({
getContractPath: vi.fn().mockReturnValue('@tevm/mocked-contract-path'),
}))
const { bundler: bundlerForObject } = await import('./bundler.js')
const { getContractPath: getContractPathForObject } = await import('./getContractPath.js')
bundlerForObject(mockConfig as any, mockLogger as any, mockFao as any, mockSolc as any, mockCache as any, {} as any)
expect(getContractPathForObject).toHaveBeenCalled()
// Restore original process.cwd
process.cwd = originalCwd
// Restore original modules
vi.restoreAllMocks()
vi.resetModules()
})
let resolver: ReturnType<Bundler>
let logger: Logger
let config: any
const mockAddresses = {
10: '0x123',
}
beforeEach(() => {
logger = { ...console, error: vi.fn() }
config = {
compiler: 'compiler config',
localContracts: {
contracts: [{ name: 'TestContract', addresses: mockAddresses }],
},
}
resolver = bundler(
config as any,
logger,
fao,
require('solc'),
createCache(tmpdir(), fao, tmpdir()),
contractPackage,
)
})
afterEach(() => {
vi.clearAllMocks()
})
describe('error cases', () => {
describe('resolveDts', () => {
it('should throw an error if there is an issue in resolveArtifacts', async () => {
mockResolveArtifacts.mockRejectedValueOnce(new Error('Test error'))
await expect(resolver.resolveDts('module', 'basedir', false, false)).rejects.toThrow('Test error')
expect((logger.error as Mock).mock.calls).toMatchInlineSnapshot(`
[
[
"there was an error in tevm plugin resolving .dts",
],
[
[Error: Test error],
],
]
`)
})
})
describe('resolveDtsSync', () => {
it('should throw an error if there is an issue in resolveArtifactsSync', () => {
mockResolveArtifactsSync.mockImplementation(() => {
throw new Error('Test error sync')
})
expect(() => resolver.resolveDtsSync('module', 'basedir', false, false)).toThrow('Test error sync')
expect((logger.error as Mock).mock.calls).toMatchInlineSnapshot(`
[
[
"there was an error in tevm plugin resolving .dts",
],
[
[Error: Test error sync],
],
]
`)
})
})
describe('resolveTsModuleSync', () => {
it('should throw an error if there is an issue in resolveArtifactsSync', () => {
mockResolveArtifactsSync.mockImplementation(() => {
throw new Error('Test error sync')
})
expect(() => resolver.resolveTsModuleSync('module', 'basedir', false, false)).toThrow('Test error sync')
expect((logger.error as Mock).mock.calls).toMatchInlineSnapshot(`
[
[
"there was an error in tevm plugin resolving .ts",
],
[
[Error: Test error sync],
],
]
`)
})
})
describe('resolveTsModule', () => {
it('should throw an error if there is an issue in resolveArtifacts', async () => {
mockResolveArtifacts.mockRejectedValueOnce(new Error('Test error'))
await expect(resolver.resolveTsModule('module', 'basedir', false, false)).rejects.toThrow('Test error')
expect((logger.error as Mock).mock.calls).toMatchInlineSnapshot(`
[
[
"there was an error in tevm plugin resolving .ts",
],
[
[Error: Test error],
],
]
`)
})
})
describe('resolveCjsModuleSync', () => {
it('should throw an error if there is an issue in resolveArtifactsSync', () => {
mockResolveArtifactsSync.mockImplementation(() => {
throw new Error('Test error sync')
})
expect(() => resolver.resolveCjsModuleSync('module', 'basedir', false, false)).toThrow('Test error sync')
expect((logger.error as Mock).mock.calls).toMatchInlineSnapshot(`
[
[
"there was an error in tevm plugin resolving .cjs",
],
[
[Error: Test error sync],
],
]
`)
})
})
describe('resolveCjsModule', () => {
it('should throw an error if there is an issue in resolveArtifacts', async () => {
mockResolveArtifacts.mockRejectedValueOnce(new Error('Test error'))
await expect(resolver.resolveCjsModule('module', 'basedir', false, false)).rejects.toThrow('Test error')
expect((logger.error as Mock).mock.calls).toMatchInlineSnapshot(`
[
[
"there was an error in tevm plugin resolving .cjs",
],
[
[Error: Test error],
],
]
`)
})
})
describe('resolveEsmModuleSync', () => {
it('should throw an error if there is an issue in resolveArtifactsSync', () => {
mockResolveArtifactsSync.mockImplementation(() => {
throw new Error('Test error sync')
})
expect(() => resolver.resolveEsmModuleSync('module', 'basedir', false, false)).toThrow('Test error sync')
expect((logger.error as Mock).mock.calls).toMatchInlineSnapshot(`
[
[
"there was an error in tevm plugin resolving .mjs",
],
[
[Error: Test error sync],
],
]
`)
})
})
describe('resolveEsmModule', () => {
it('should throw an error if there is an issue in resolveArtifacts', async () => {
mockResolveArtifacts.mockRejectedValueOnce(new Error('Test error'))
await expect(resolver.resolveEsmModule('module', 'basedir', false, false)).rejects.toThrow('Test error')
expect((logger.error as Mock).mock.calls).toMatchInlineSnapshot(`
[
[
"there was an error in tevm plugin resolving .mjs",
],
[
[Error: Test error],
],
]
`)
})
})
describe('edge cases and features', () => {
it('should handle contracts with complex ABIs correctly', async () => {
const complexAbi = [
{
inputs: [{ internalType: 'uint256', name: 'initialValue', type: 'uint256' }],
stateMutability: 'nonpayable',
type: 'constructor',
},
{
inputs: [],
name: 'getValue',
outputs: [{ internalType: 'uint256', name: '', type: 'uint256' }],
stateMutability: 'view',
type: 'function',
},
{
inputs: [{ internalType: 'uint256', name: 'newValue', type: 'uint256' }],
name: 'setValue',
outputs: [],
stateMutability: 'nonpayable',
type: 'function',
},
{
anonymous: false,
inputs: [
{ indexed: true, internalType: 'address', name: 'sender', type: 'address' },
{ indexed: false, internalType: 'uint256', name: 'oldValue', type: 'uint256' },
{ indexed: false, internalType: 'uint256', name: 'newValue', type: 'uint256' },
],
name: 'ValueChanged',
type: 'event',
},
]
const artifacts = {
ComplexContract: {
contractName: 'ComplexContract',
abi: complexAbi,
userdoc: { methods: {} },
evm: { deployedBytecode: { object: '0xABCDEF' } },
},
}
mockResolveArtifacts.mockResolvedValueOnce({
artifacts,
modules: mockModules,
asts: { 'ComplexContract.sol': {} },
solcInput: { language: 'Solidity', settings: {}, sources: {} },
solcOutput: { contracts: {}, sources: {} },
})
const result = await resolver.resolveEsmModule('module', 'basedir', false, true)
// Check that ABI is correctly included
expect(result.code).toContain('"name": "ComplexContract"')
expect(result.code).toContain('function getValue()')
expect(result.code).toContain('function setValue(uint256')
expect(result.code).toContain('event ValueChanged')
})
it('should handle multiple contracts in a single file', async () => {
const artifacts = {
Contract1: {
contractName: 'Contract1',
abi: [
{
name: 'method1',
type: 'function',
inputs: [],
outputs: [],
stateMutability: 'nonpayable',
},
],
userdoc: { methods: {} },
evm: { deployedBytecode: { object: '0x111' } },
},
Contract2: {
contractName: 'Contract2',
abi: [
{
name: 'method2',
type: 'function',
inputs: [],
outputs: [],
stateMutability: 'nonpayable',
},
],
userdoc: { methods: {} },
evm: { deployedBytecode: { object: '0x222' } },
},
}
mockResolveArtifacts.mockResolvedValueOnce({
artifacts,
modules: mockModules,
asts: { 'MultiContract.sol': {} },
solcInput: { language: 'Solidity', settings: {}, sources: {} },
solcOutput: { contracts: {}, sources: {} },
})
const result = await resolver.resolveEsmModule('module', 'basedir', false, true)
// Check that both contracts are included
expect(result.code).toContain('export const Contract1')
expect(result.code).toContain('export const Contract2')
expect(result.code).toContain('"name": "Contract1"')
expect(result.code).toContain('"name": "Contract2"')
expect(result.code).toContain('method1')
expect(result.code).toContain('method2')
})
it('should handle empty artifacts gracefully', async () => {
mockResolveArtifacts.mockResolvedValueOnce({
artifacts: {},
modules: {},
asts: {},
solcInput: { language: 'Solidity', settings: {}, sources: {} },
solcOutput: { contracts: {}, sources: {} },
})
const result = await resolver.resolveEsmModule('module', 'basedir', false, false)
// Check that we get error comment but not a crash
expect(result.code).toContain('there were no artifacts for module')
// asts can be {} not undefined, adjust expectation
expect(Object.keys(result.asts || {}).length).toBe(0)
})
it('should handle bytecode correctly when includeBytecode is true', async () => {
const artifacts = {
TestContract: {
contractName: 'TestContract',
abi: [],
evm: {
bytecode: {
object: '0x1234567890',
},
deployedBytecode: {
object: '0xabcdef123456',
},
},
},
}
mockResolveArtifacts.mockResolvedValueOnce({
artifacts,
modules: mockModules,
asts: { 'TestContract.sol': {} },
solcInput: { language: 'Solidity', settings: {}, sources: {} },
solcOutput: { contracts: {}, sources: {} },
})
const result = await resolver.resolveEsmModule('module', 'basedir', false, true)
// Check that bytecode is included in the generated code
expect(result.code).toContain('"bytecode"')
expect(result.code).toContain('"deployedBytecode"')
})
it('should handle contracts with abstract contracts and libraries', async () => {
const artifacts = {
Implementor: {
contractName: 'Implementor',
abi: [
{
name: 'implementedFunction',
type: 'function',
inputs: [],
outputs: [],
stateMutability: 'nonpayable',
},
{
name: 'abstractFunction',
type: 'function',
inputs: [],
outputs: [],
stateMutability: 'nonpayable',
},
],
userdoc: { methods: {} },
},
MyLib: {
contractName: 'MyLib',
abi: [
{
name: 'libFunction',
type: 'function',
inputs: [],
outputs: [],
stateMutability: 'nonpayable',
},
],
userdoc: { methods: {} },
},
}
mockResolveArtifacts.mockResolvedValueOnce({
artifacts,
modules: mockModules,
asts: { 'Inheritance.sol': {} },
solcInput: { language: 'Solidity', settings: {}, sources: {} },
solcOutput: { contracts: {}, sources: {} },
})
const result = await resolver.resolveDts('module', 'basedir', false, false)
// Check that both the concrete contract and the library are included
expect(result.code).toContain('const _nameImplementor = "Implementor"')
expect(result.code).toContain('const _nameMyLib = "MyLib"')
})
})
})
const mockResolveArtifacts = resolveArtifacts as Mock
describe('resolveDts', () => {
it('should return an empty string if no artifacts are found', async () => {
mockResolveArtifacts.mockResolvedValueOnce({})
const result = await resolver.resolveDts('module', 'basedir', false, false)
expect(result).toMatchInlineSnapshot(`
{
"asts": undefined,
"code": "// there were no artifacts for module. This is likely a bug in tevm",
"modules": undefined,
"solcInput": undefined,
"solcOutput": undefined,
}
`)
})
it('should generate proper dts if artifacts are found', async () => {
const artifacts = {
TestContract: { contractName: 'TestContract', abi: [] },
}
mockResolveArtifacts.mockResolvedValueOnce({
artifacts,
modules: mockModules,
solcInput: {
language: 'Solidity',
settings: { outputSelection: { sources: {} } },
sources: {},
} satisfies SolcInputDescription,
solcOutput: {
contracts: {},
sources: {},
} satisfies SolcOutput,
asts: {
'TestContract.sol': {
absolutePath: '/absolute/path',
evmVersion: 'homestead',
},
} as any as Record<string, Node>,
})
const result = await resolver.resolveDts('module', 'basedir', false, false)
expect(result).toMatchInlineSnapshot(`
{
"asts": {
"TestContract.sol": {
"absolutePath": "/absolute/path",
"evmVersion": "homestead",
},
},
"code": "import type { Contract } from '@tevm/contract'
const _abiTestContract = [] as const;
const _nameTestContract = "TestContract" as const;
/**
* TestContract Contract (no bytecode)
* change file name or add file that ends in '.s.sol' extension if you wish to compile the bytecode
* @see [contract docs](https://tevm.sh/learn/contracts/) for more documentation
*/
export const TestContract: Contract<typeof _nameTestContract, typeof _abiTestContract, undefined, undefined, undefined, undefined>;
// solc artifacts of compilation
export const artifacts = {
"TestContract": {
"contractName": "TestContract",
"abi": []
}
};
",
"modules": {
"module1": {
"code": "import { TestContract } from 'module2'
contract TestContract {}",
"id": "id",
"importedIds": [
"module2",
],
"rawCode": "import { TestContract } from 'module2'
contract TestContract {}",
},
},
"solcInput": {
"language": "Solidity",
"settings": {
"outputSelection": {
"sources": {},
},
},
"sources": {},
},
"solcOutput": {
"contracts": {},
"sources": {},
},
}
`)
})
})
const mockResolveArtifactsSync = resolveArtifactsSync as Mock
describe('resolveDtsSync', () => {
it('should return an empty string if no artifacts are found', () => {
mockResolveArtifactsSync.mockReturnValueOnce({})
const result = resolver.resolveDtsSync('module', 'basedir', false, false)
expect(result).toMatchInlineSnapshot(`
{
"asts": undefined,
"code": "// there were no artifacts for module. This is likely a bug in tevm",
"modules": undefined,
"solcInput": undefined,
"solcOutput": undefined,
}
`)
})
it('should generate proper dts if artifacts are found', () => {
const artifacts = {
TestContract: { contractName: 'TestContract', abi: [] },
}
mockResolveArtifactsSync.mockReturnValueOnce({
artifacts,
modules: mockModules,
asts: {
'TestContract.sol': {
absolutePath: '/absolute/path',
evmVersion: 'homestead',
},
},
solcInput: {
language: 'Solidity',
settings: { outputSelection: { sources: {} } },
sources: {},
} satisfies SolcInputDescription,
solcOutput: {
contracts: {},
sources: {},
} satisfies SolcOutput,
})
const result = resolver.resolveDtsSync('module', 'basedir', false, false)
expect(result).toMatchInlineSnapshot(`
{
"asts": {
"TestContract.sol": {
"absolutePath": "/absolute/path",
"evmVersion": "homestead",
},
},
"code": "import type { Contract } from '@tevm/contract'
const _abiTestContract = [] as const;
const _nameTestContract = "TestContract" as const;
/**
* TestContract Contract (no bytecode)
* change file name or add file that ends in '.s.sol' extension if you wish to compile the bytecode
* @see [contract docs](https://tevm.sh/learn/contracts/) for more documentation
*/
export const TestContract: Contract<typeof _nameTestContract, typeof _abiTestContract, undefined, undefined, undefined, undefined>;
// solc artifacts of compilation
export const artifacts = {
"TestContract": {
"contractName": "TestContract",
"abi": []
}
};
",
"modules": {
"module1": {
"code": "import { TestContract } from 'module2'
contract TestContract {}",
"id": "id",
"importedIds": [
"module2",
],
"rawCode": "import { TestContract } from 'module2'
contract TestContract {}",
},
},
"solcInput": {
"language": "Solidity",
"settings": {
"outputSelection": {
"sources": {},
},
},
"sources": {},
},
"solcOutput": {
"contracts": {},
"sources": {},
},
}
`)
})
})
describe('resolveTsModuleSync', () => {
it('should return an empty string if no artifacts are found', () => {
mockResolveArtifactsSync.mockReturnValueOnce({})
const result = resolver.resolveTsModuleSync('module', 'basedir', false, false)
expect(result).toMatchInlineSnapshot(`
{
"asts": undefined,
"code": "// there were no artifacts for module. This is likely a bug in tevm",
"modules": undefined,
"solcInput": undefined,
"solcOutput": undefined,
}
`)
})
it('should generate proper dts if artifacts are found', () => {
const artifacts = {
TestContract: { contractName: 'TestContract', abi: [] },
}
;(resolveArtifactsSync as Mock).mockReturnValueOnce({
artifacts,
modules: mockModules,
asts: {
'TestContract.sol': {
absolutePath: '/absolute/path',
evmVersion: 'homestead',
},
},
solcInput: {
language: 'Solidity',
settings: { outputSelection: { sources: {} } },
sources: {},
} satisfies SolcInputDescription,
solcOutput: {
contracts: {},
sources: {},
} satisfies SolcOutput,
})
const result = resolver.resolveTsModuleSync('module', 'basedir', false, false)
expect(result).toMatchInlineSnapshot(`
{
"asts": {
"TestContract.sol": {
"absolutePath": "/absolute/path",
"evmVersion": "homestead",
},
},
"code": "import { createContract } from '@tevm/contract'
const _TestContract = {
"name": "TestContract",
"humanReadableAbi": []
} as const
/**
* @see [contract docs](https://tevm.sh/learn/contracts/) for more documentation
*/
export const TestContract = createContract(_TestContract);
export const artifacts = {
"TestContract": {
"contractName": "TestContract",
"abi": []
}
};",
"modules": {
"module1": {
"code": "import { TestContract } from 'module2'
contract TestContract {}",
"id": "id",
"importedIds": [
"module2",
],
"rawCode": "import { TestContract } from 'module2'
contract TestContract {}",
},
},
"solcInput": {
"language": "Solidity",
"settings": {
"outputSelection": {
"sources": {},
},
},
"sources": {},
},
"solcOutput": {
"contracts": {},
"sources": {},
},
}
`)
})
})
describe('resolveTsModule', () => {
it('should return an empty string if no artifacts are found', async () => {
mockResolveArtifacts.mockResolvedValueOnce({})
const result = await resolver.resolveTsModule('module', 'basedir', false, false)
expect(result).toMatchInlineSnapshot(`
{
"asts": undefined,
"code": "// there were no artifacts for module. This is likely a bug in tevm",
"modules": undefined,
"solcInput": undefined,
"solcOutput": undefined,
}
`)
})
it('should generate proper dts if artifacts are found', async () => {
const artifacts = {
TestContract: { contractName: 'TestContract', abi: [] },
}
mockResolveArtifacts.mockResolvedValueOnce({
artifacts,
modules: mockModules,
asts: {
'TestContract.sol': {
absolutePath: '/absolute/path',
evmVersion: 'homestead',
},
},
solcInput: {
language: 'Solidity',
settings: { outputSelection: { sources: {} } },
sources: {},
} satisfies SolcInputDescription,
solcOutput: {
contracts: {},
sources: {},
} satisfies SolcOutput,
})
const result = await resolver.resolveTsModule('module', 'basedir', false, false)
expect(result).toMatchInlineSnapshot(`
{
"asts": {
"TestContract.sol": {
"absolutePath": "/absolute/path",
"evmVersion": "homestead",
},
},
"code": "import { createContract } from '@tevm/contract'
const _TestContract = {
"name": "TestContract",
"humanReadableAbi": []
} as const
/**
* @see [contract docs](https://tevm.sh/learn/contracts/) for more documentation
*/
export const TestContract = createContract(_TestContract);
export const artifacts = {
"TestContract": {
"contractName": "TestContract",
"abi": []
}
};",
"modules": {
"module1": {
"code": "import { TestContract } from 'module2'
contract TestContract {}",
"id": "id",
"importedIds": [
"module2",
],
"rawCode": "import { TestContract } from 'module2'
contract TestContract {}",
},
},
"solcInput": {
"language": "Solidity",
"settings": {
"outputSelection": {
"sources": {},
},
},
"sources": {},
},
"solcOutput": {
"contracts": {},
"sources": {},
},
}
`)
})
})
describe('resolveCjsModuleSync', () => {
it('should return an empty string if no artifacts are found', () => {
mockResolveArtifactsSync.mockReturnValueOnce({})
const result = resolver.resolveCjsModuleSync('module', 'basedir', false, false)
expect(result).toMatchInlineSnapshot(`
{
"asts": undefined,
"code": "// there were no artifacts for module. This is likely a bug in tevm",
"modules": undefined,
"solcInput": undefined,
"solcOutput": undefined,
}
`)
})
it('should generate proper CommonJS module if artifacts are found', () => {
const artifacts = {
TestContract: { contractName: 'TestContract', abi: [] },
}
mockResolveArtifactsSync.mockReturnValueOnce({
artifacts,
modules: mockModules,
asts: {
'TestContract.sol': {
absolutePath: '/absolute/path',
evmVersion: 'homestead',
},
},
solcInput: {
language: 'Solidity',
settings: { outputSelection: { sources: {} } },
sources: {},
} satisfies SolcInputDescription,
solcOutput: {
contracts: {},
sources: {},
} satisfies SolcOutput,
})
const result = resolver.resolveCjsModuleSync('module', 'basedir', false, false)
expect(result).toMatchInlineSnapshot(`
{
"asts": {
"TestContract.sol": {
"absolutePath": "/absolute/path",
"evmVersion": "homestead",
},
},
"code": "const { createContract } = require('@tevm/contract')
const _TestContract = {
"name": "TestContract",
"humanReadableAbi": []
};
/**
* @see [contract docs](https://tevm.sh/learn/contracts/) for more documentation
*/
module.exports.TestContract = createContract(_TestContract);
module.exports.artifacts = {
"TestContract": {
"contractName": "TestContract",
"abi": []
}
};",
"modules": {
"module1": {
"code": "import { TestContract } from 'module2'
contract TestContract {}",
"id": "id",
"importedIds": [
"module2",
],
"rawCode": "import { TestContract } from 'module2'
contract TestContract {}",
},
},
"solcInput": {
"language": "Solidity",
"settings": {
"outputSelection": {
"sources": {},
},
},
"sources": {},
},
"solcOutput": {
"contracts": {},
"sources": {},
},
}
`)
})
})
describe('resolveCjsModule', () => {
it('should return an empty string if no artifacts are found', async () => {
mockResolveArtifacts.mockResolvedValueOnce({})
const result = await resolver.resolveCjsModule('module', 'basedir', false, false)
expect(result).toMatchInlineSnapshot(`
{
"asts": undefined,
"code": "// there were no artifacts for module. This is likely a bug in tevm",
"modules": undefined,
"solcInput": undefined,
"solcOutput": undefined,
}
`)
})
it('should generate proper CommonJS module if artifacts are found', async () => {
const artifacts = {
TestContract: { contractName: 'TestContract', abi: [] },
}
mockResolveArtifacts.mockResolvedValueOnce({
artifacts,
modules: mockModules,
asts: {
'TestContract.sol': {
absolutePath: '/absolute/path',
evmVersion: 'homestead',
},
},
solcInput: {
language: 'Solidity',
settings: { outputSelection: { sources: {} } },
sources: {},
} satisfies SolcInputDescription,
solcOutput: {
contracts: {},
sources: {},
} satisfies SolcOutput,
})
const result = await resolver.resolveCjsModule('module', 'basedir', false, false)
expect(result).toMatchInlineSnapshot(`
{
"asts": {
"TestContract.sol": {
"absolutePath": "/absolute/path",
"evmVersion": "homestead",
},
},
"code": "const { createContract } = require('@tevm/contract')
const _TestContract = {
"name": "TestContract",
"humanReadableAbi": []
};
/**
* @see [contract docs](https://tevm.sh/learn/contracts/) for more documentation
*/
module.exports.TestContract = createContract(_TestContract);
module.exports.artifacts = {
"TestContract": {
"contractName": "TestContract",
"abi": []
}
};",
"modules": {
"module1": {
"code": "import { TestContract } from 'module2'
contract TestContract {}",
"id": "id",
"importedIds": [
"module2",
],
"rawCode": "import { TestContract } from 'module2'
contract TestContract {}",
},
},
"solcInput": {
"language": "Solidity",
"settings": {
"outputSelection": {
"sources": {},
},
},
"sources": {},
},
"solcOutput": {
"contracts": {},
"sources": {},
},
}
`)
})
})
describe('resolveEsmModuleSync', () => {
it('should return an empty string if no artifacts are found', () => {
mockResolveArtifactsSync.mockReturnValueOnce({})
const result = resolver.resolveEsmModuleSync('module', 'basedir', false, false)
expect(result).toMatchInlineSnapshot(`
{
"asts": undefined,
"code": "// there were no artifacts for module. This is likely a bug in tevm",
"modules": undefined,
"solcInput": undefined,
"solcOutput": undefined,
}
`)
})
it('should generate proper ESM module if artifacts are found', () => {
const artifacts = {
TestContract: { contractName: 'TestContract', abi: [] },
}
mockResolveArtifactsSync.mockReturnValueOnce({
artifacts,
modules: mockModules,
asts: {
'TestContract.sol': {
absolutePath: '/absolute/path',
evmVersion: 'homestead',
},
},
solcInput: {
language: 'Solidity',
settings: { outputSelection: { sources: {} } },
sources: {},
} satisfies SolcInputDescription,
solcOutput: {
contracts: {},
sources: {},
} satisfies SolcOutput,
})
const result = resolver.resolveEsmModuleSync('module', 'basedir', false, false)
expect(result).toMatchInlineSnapshot(`
{
"asts": {
"TestContract.sol": {
"absolutePath": "/absolute/path",
"evmVersion": "homestead",
},
},
"code": "import { createContract } from '@tevm/contract'
const _TestContract = {
"name": "TestContract",
"humanReadableAbi": []
};
/**
* @see [contract docs](https://tevm.sh/learn/contracts/) for more documentation
*/
export const TestContract = createContract(_TestContract);
export const artifacts = {
"TestContract": {
"contractName": "TestContract",
"abi": []
}
};",
"modules": {
"module1": {
"code": "import { TestContract } from 'module2'
contract TestContract {}",
"id": "id",
"importedIds": [
"module2",
],
"rawCode": "import { TestContract } from 'module2'
contract TestContract {}",
},
},
"solcInput": {
"language": "Solidity",
"settings": {
"outputSelection": {
"sources": {},
},
},
"sources": {},
},
"solcOutput": {
"contracts": {},
"sources": {},
},
}
`)
})
})
describe('resolveEsmModule', () => {
it('should return an empty string if no artifacts are found', async () => {
mockResolveArtifacts.mockResolvedValueOnce({})
const result = await resolver.resolveEsmModule('module', 'basedir', false, false)
expect(result).toMatchInlineSnapshot(`
{
"asts": undefined,
"code": "// there were no artifacts for module. This is likely a bug in tevm",
"modules": undefined,
"solcInput": undefined,
"solcOutput": undefined,
}
`)
})
it('should generate proper ESM module if artifacts are found', async () => {
const artifacts = {
TestContract: { contractName: 'TestContract', abi: [] },
}
mockResolveArtifacts.mockResolvedValueOnce({
artifacts,
modules: mockModules,
asts: {
'TestContract.sol': {
absolutePath: '/absolute/path',
evmVersion: 'homestead',
},
},
solcInput: {
language: 'Solidity',
settings: { outputSelection: { sources: {} } },
sources: {},
} satisfies SolcInputDescription,
solcOutput: {
contracts: {},
sources: {},
} satisfies SolcOutput,
})
const result = await resolver.resolveEsmModule('module', 'basedir', false, false)
expect(result).toMatchInlineSnapshot(`
{
"asts": {
"TestContract.sol": {
"absolutePath": "/absolute/path",
"evmVersion": "homestead",
},
},
"code": "import { createContract } from '@tevm/contract'
const _TestContract = {
"name": "TestContract",
"humanReadableAbi": []
};
/**
* @see [contract docs](https://tevm.sh/learn/contracts/) for more documentation
*/
export const TestContract = createContract(_TestContract);
export const artifacts = {
"TestContract": {
"contractName": "TestContract",
"abi": []
}
};",
"modules": {
"module1": {
"code": "import { TestContract } from 'module2'
contract TestContract {}",
"id": "id",
"importedIds": [
"module2",
],
"rawCode": "import { TestContract } from 'module2'
contract TestContract {}",
},
},
"solcInput": {
"language": "Solidity",
"settings": {
"outputSelection": {
"sources": {},
},
},
"sources": {},
},
"solcOutput": {
"contracts": {},
"sources": {},
},
}
`)
})
})
})