Commit 74f4be16 authored by tom's avatar tom

handle aliased imports

parent 6f1128a1
...@@ -30,7 +30,10 @@ export interface SmartContract { ...@@ -30,7 +30,10 @@ export interface SmartContract {
file_path: string; file_path: string;
additional_sources: Array<{ file_path: string; source_code: string }>; additional_sources: Array<{ file_path: string; source_code: string }>;
external_libraries: Array<SmartContractExternalLibrary> | null; external_libraries: Array<SmartContractExternalLibrary> | null;
compiler_settings: unknown; compiler_settings?: {
evmVersion?: string;
remappings?: Array<string>;
};
verified_twin_address_hash: string | null; verified_twin_address_hash: string | null;
minimal_proxy_address_hash: string | null; minimal_proxy_address_hash: string | null;
} }
......
...@@ -177,6 +177,7 @@ const ContractCode = ({ addressHash }: Props) => { ...@@ -177,6 +177,7 @@ const ContractCode = ({ addressHash }: Props) => {
isViper={ Boolean(data.is_vyper_contract) } isViper={ Boolean(data.is_vyper_contract) }
filePath={ data.file_path } filePath={ data.file_path }
additionalSource={ data.additional_sources } additionalSource={ data.additional_sources }
remappings={ data.compiler_settings?.remappings }
/> />
) } ) }
{ Boolean(data.compiler_settings) && ( { Boolean(data.compiler_settings) && (
......
...@@ -16,9 +16,10 @@ interface Props { ...@@ -16,9 +16,10 @@ interface Props {
isViper: boolean; isViper: boolean;
filePath?: string; filePath?: string;
additionalSource?: SmartContract['additional_sources']; additionalSource?: SmartContract['additional_sources'];
remappings?: Array<string>;
} }
const ContractSourceCode = ({ data, hasSol2Yml, address, isViper, filePath, additionalSource }: Props) => { const ContractSourceCode = ({ data, hasSol2Yml, address, isViper, filePath, additionalSource, remappings }: Props) => {
const heading = ( const heading = (
<Text fontWeight={ 500 }> <Text fontWeight={ 500 }>
<span>Contract source code</span> <span>Contract source code</span>
...@@ -56,7 +57,7 @@ const ContractSourceCode = ({ data, hasSol2Yml, address, isViper, filePath, addi ...@@ -56,7 +57,7 @@ const ContractSourceCode = ({ data, hasSol2Yml, address, isViper, filePath, addi
{ diagramLink } { diagramLink }
{ copyToClipboard } { copyToClipboard }
</Flex> </Flex>
<CodeEditor data={ editorData }/> <CodeEditor data={ editorData } remappings={ remappings }/>
</section> </section>
); );
}; };
......
...@@ -35,9 +35,10 @@ const EDITOR_HEIGHT = 500; ...@@ -35,9 +35,10 @@ const EDITOR_HEIGHT = 500;
interface Props { interface Props {
data: Array<File>; data: Array<File>;
remappings?: Array<string>;
} }
const CodeEditor = ({ data }: Props) => { const CodeEditor = ({ data, remappings }: Props) => {
const [ instance, setInstance ] = React.useState<Monaco | undefined>(); const [ instance, setInstance ] = React.useState<Monaco | undefined>();
const [ editor, setEditor ] = React.useState<monaco.editor.IStandaloneCodeEditor | undefined>(); const [ editor, setEditor ] = React.useState<monaco.editor.IStandaloneCodeEditor | undefined>();
const [ index, setIndex ] = React.useState(0); const [ index, setIndex ] = React.useState(0);
...@@ -145,14 +146,14 @@ const CodeEditor = ({ data }: Props) => { ...@@ -145,14 +146,14 @@ const CodeEditor = ({ data }: Props) => {
.map((element: HTMLSpanElement) => element.innerText) .map((element: HTMLSpanElement) => element.innerText)
.join(''); .join('');
const fullPath = getFullPathOfImportedFile(data[index].file_path, path); const fullPath = getFullPathOfImportedFile(data[index].file_path, path, remappings);
const fileIndex = data.findIndex((file) => file.file_path === fullPath); const fileIndex = data.findIndex((file) => file.file_path === fullPath);
if (fileIndex > -1) { if (fileIndex > -1) {
event.stopPropagation(); event.stopPropagation();
handleSelectFile(fileIndex); handleSelectFile(fileIndex);
} }
} }
}, [ data, handleSelectFile, index, isMetaPressed, isMobile ]); }, [ data, handleSelectFile, index, isMetaPressed, isMobile, remappings ]);
const handleKeyDown = React.useCallback((event: React.KeyboardEvent) => { const handleKeyDown = React.useCallback((event: React.KeyboardEvent) => {
isMetaKey(event) && setIsMetaPressed(true); isMetaKey(event) && setIsMetaPressed(true);
......
...@@ -18,20 +18,32 @@ it('returns undefined if imported file is outside the base file folder', () => { ...@@ -18,20 +18,32 @@ it('returns undefined if imported file is outside the base file folder', () => {
expect(result).toBeUndefined(); expect(result).toBeUndefined();
}); });
it('returns unmodified path if it is already absolute', () => { describe('returns unmodified path if it is already absolute', () => {
const result = getFullPathOfImportedFile( it('with prefix', () => {
'/index.sol', const result = getFullPathOfImportedFile(
'/abc/contract.sol', '/index.sol',
); '/abc/contract.sol',
);
expect(result).toBe('/abc/contract.sol');
expect(result).toBe('/abc/contract.sol');
});
it('without prefix', () => {
const result = getFullPathOfImportedFile(
'/index.sol',
'abc/contract.sol',
);
expect(result).toBe('/abc/contract.sol');
});
}); });
it('returns undefined for external path', () => { it('correctly manages remappings', () => {
const result = getFullPathOfImportedFile( const result = getFullPathOfImportedFile(
'/index.sol', '/index.sol',
'https://github.com/ethereum/dapp/contract.sol', 'node_modules/@openzeppelin/contracts/access/AccessControl.sol',
[ '@ensdomains/=node_modules/@ensdomains/', '@openzeppelin/=node_modules/@openzeppelin/' ],
); );
expect(result).toBeUndefined(); expect(result).toBe('/node_modules/@openzeppelin/contracts/access/AccessControl.sol');
}); });
import stripLeadingSlash from 'lib/stripLeadingSlash'; import stripLeadingSlash from 'lib/stripLeadingSlash';
export default function getFullPathOfImportedFile(baseFilePath: string, importedFilePath: string) { export default function getFullPathOfImportedFile(baseFilePath: string, importedFilePath: string, remappings?: Array<string>) {
if (importedFilePath[0] !== '.') { if (importedFilePath[0] !== '.') {
return importedFilePath[0] === '/' ? importedFilePath : '/' + importedFilePath; let result = importedFilePath;
if (remappings && remappings.length > 0) {
const [ alias, target ] = remappings.map((item) => item.split('=')).find(([ key ]) => importedFilePath.startsWith(key)) || [];
if (alias) {
result = importedFilePath.replace(alias, target);
}
}
return result[0] === '/' ? result : '/' + result;
} }
const baseFileChunks = stripLeadingSlash(baseFilePath).split('/'); const baseFileChunks = stripLeadingSlash(baseFilePath).split('/');
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment