diff --git a/apps/remix-ide/ci/makeMockCompiler.js b/apps/remix-ide/ci/makeMockCompiler.js index a45e91e007..5e69758505 100644 --- a/apps/remix-ide/ci/makeMockCompiler.js +++ b/apps/remix-ide/ci/makeMockCompiler.js @@ -2,7 +2,7 @@ var fs = require('fs') var compiler = require('solc') -var compilerInput = require('@remix-project/remix-solidity').CompilerInput +var compilerInput = require('@remix-project/remix-solidity').compilerInputFactory var defaultVersion = 'soljson-v0.8.24+commit.e11b9ed9.js' const path = require('path') diff --git a/apps/remix-ide/src/app/plugins/contractFlattener.tsx b/apps/remix-ide/src/app/plugins/contractFlattener.tsx index 2c3640bcd9..a5f02719ca 100644 --- a/apps/remix-ide/src/app/plugins/contractFlattener.tsx +++ b/apps/remix-ide/src/app/plugins/contractFlattener.tsx @@ -2,6 +2,7 @@ import React from 'react' import {Plugin} from '@remixproject/engine' import {customAction} from '@remixproject/plugin-api' import {concatSourceFiles, getDependencyGraph, normalizeContractPath} from '@remix-ui/solidity-compiler' +import type {CompilerInput, CompilationSource } from '@remix-project/remix-solidity' const _paq = (window._paq = window._paq || []) @@ -25,7 +26,7 @@ export class ContractFlattener extends Plugin { if (data.sources && Object.keys(data.sources).length > 1) { if (this.triggerFlattenContract) { this.triggerFlattenContract = false - await this.flattenContract(source, file, data) + await this.flattenContract(source, file, data, JSON.parse(input)) } } }) @@ -47,17 +48,17 @@ export class ContractFlattener extends Plugin { * Takes the flattened result, writes it to a file and returns the result. * @returns {Promise} */ - async flattenContract(source: {sources: any; target: string}, filePath: string, data: {contracts: any; sources: any}): Promise { + async flattenContract(source: {sources: any; target: string}, filePath: string, data: {contracts: any; sources: any}, input: CompilerInput): Promise { const appendage = '_flattened.sol' const normalized = normalizeContractPath(filePath) const path = `${normalized[normalized.length - 2]}${appendage}` - const ast = data.sources + const ast: { [contractName: string]: CompilationSource } = data.sources let dependencyGraph let sorted let result let sources try { - dependencyGraph = getDependencyGraph(ast, filePath) + dependencyGraph = getDependencyGraph(ast, filePath, input.settings.remappings) sorted = dependencyGraph.isEmpty() ? [filePath] : dependencyGraph.sort().reverse() sources = source.sources result = concatSourceFiles(sorted, sources) diff --git a/libs/remix-solidity/src/index.ts b/libs/remix-solidity/src/index.ts index b9db8be3be..eba07bb04e 100644 --- a/libs/remix-solidity/src/index.ts +++ b/libs/remix-solidity/src/index.ts @@ -1,6 +1,6 @@ export { Compiler } from './compiler/compiler' export { compile } from './compiler/compiler-helpers' -export { default as CompilerInput, getValidLanguage } from './compiler/compiler-input' +export { default as compilerInputFactory, getValidLanguage } from './compiler/compiler-input' export { CompilerAbstract } from './compiler/compiler-abstract' export * from './compiler/types' export { pathToURL, baseURLBin, baseURLWasm, canUseWorker, urlFromVersion } from './compiler/compiler-utils' diff --git a/libs/remix-ui/solidity-compiler/src/lib/logic/flattenerUtilities.ts b/libs/remix-ui/solidity-compiler/src/lib/logic/flattenerUtilities.ts index b4b0824ebb..7dadf6fa54 100644 --- a/libs/remix-ui/solidity-compiler/src/lib/logic/flattenerUtilities.ts +++ b/libs/remix-ui/solidity-compiler/src/lib/logic/flattenerUtilities.ts @@ -1,11 +1,14 @@ +import type {CompilationSource, AstNode} from '@remix-project/remix-solidity' + const IMPORT_SOLIDITY_REGEX = /^\s*import(\s+).*$/gm; const SPDX_SOLIDITY_REGEX = /^\s*\/\/ SPDX-License-Identifier:.*$/gm; -export function getDependencyGraph(ast, target) { +type Visited = { [key: string]: number } +export function getDependencyGraph(ast: { [name: string]: CompilationSource }, target: string, remappings: string[]) { const graph = tsort(); const visited = {}; visited[target] = 1; - _traverse(graph, visited, ast, target); + _traverse(graph, visited, ast, target, remappings); return graph; } @@ -21,32 +24,31 @@ export function concatSourceFiles(files: any[], sources: any) { return concat; } -function _traverse(graph, visited, ast, name) { +function _traverse(graph: Graph, visited: Visited, ast: { [name: string]: CompilationSource }, name: string, remappings: string[]) { let currentAst = null currentAst = ast[name].ast const dependencies = _getDependencies(currentAst); for (const dependency of dependencies) { - const path = resolve(name, dependency); + const path = resolve(name, dependency, remappings); if (path in visited) { // continue; // fixes wrong ordering of source in flattened file } visited[path] = 1; graph.add(name, path); - _traverse(graph, visited, ast, path); + _traverse(graph, visited, ast, path, remappings); } } -function _getDependencies(ast) { +function _getDependencies(ast: AstNode) { const dependencies = ast?.nodes .filter(node => node?.nodeType === 'ImportDirective') .map(node => node?.file); return dependencies; } - // TSORT -function tsort(initial?: any) { +function tsort(initial?: any): Graph { const graph = new Graph(); if (initial) { @@ -58,78 +60,88 @@ function tsort(initial?: any) { return graph; } +class Graph { + nodes: { [key: string]: any} + constructor() { + this.nodes = {} + } -function Graph() { - this.nodes = {}; -} - -// Add sorted items to the graph -Graph.prototype.add = function () { - const self = this; - // eslint-disable-next-line prefer-rest-params - let items = [].slice.call(arguments); - - if (items.length === 1 && Array.isArray(items[0])) - items = items[0]; - - items.forEach(function (item) { - if (!self.nodes[item]) { - self.nodes[item] = []; + // Add sorted items to the graph + add (name, path) { + const self = this; + // eslint-disable-next-line prefer-rest-params + let items = [].slice.call(arguments); + + if (items.length === 1 && Array.isArray(items[0])) + items = items[0]; + + items.forEach(function (item) { + if (!self.nodes[item]) { + self.nodes[item] = []; + } + }); + + for (let i = 1; i < items.length; i++) { + const from = items[i]; + const to = items[i - 1]; + + self.nodes[from].push(to); } - }); - - for (let i = 1; i < items.length; i++) { - const from = items[i]; - const to = items[i - 1]; - - self.nodes[from].push(to); + + return self; } - return self; -}; - -// Depth first search -// As given in http://en.wikipedia.org/wiki/Topological_sorting -Graph.prototype.sort = function () { - const self = this; - const nodes = Object.keys(this.nodes); - - const sorted = []; - const marks = {}; - - for (let i = 0; i < nodes.length; i++) { - const node = nodes[i]; - - if (!marks[node]) { - visit(node); + // Depth first search + // As given in http://en.wikipedia.org/wiki/Topological_sorting + sort () { + const self = this; + const nodes = Object.keys(this.nodes); + + const sorted = []; + const marks = {}; + + for (let i = 0; i < nodes.length; i++) { + const node = nodes[i]; + + if (!marks[node]) { + visit(node); + } + } + + return sorted; + + function visit(node) { + if (marks[node] === 'temp') + throw new Error("There is a cycle in the graph. It is not possible to derive a topological sort."); + else if (marks[node]) + return; + + marks[node] = 'temp'; + self.nodes[node].forEach(visit); + marks[node] = 'perm'; + + sorted.push(node); } } - return sorted; - - function visit(node) { - if (marks[node] === 'temp') - throw new Error("There is a cycle in the graph. It is not possible to derive a topological sort."); - else if (marks[node]) - return; - - marks[node] = 'temp'; - self.nodes[node].forEach(visit); - marks[node] = 'perm'; - - sorted.push(node); + isEmpty () { + const nodes = Object.keys(this.nodes); + return nodes.length === 0; } -}; - -Graph.prototype.isEmpty = function () { - const nodes = Object.keys(this.nodes); - return nodes.length === 0; } - // PATH -function resolve(parentPath, childPath) { +function resolve(parentPath, childPath, remappings: string[]) { + if (remappings && remappings.length) { + for (const mapping of remappings) { + if (mapping.indexOf('=') !== -1) { + const split = mapping.split('=') + childPath = childPath.replace(split[0].trim(), split[1].trim()) + } + } + } + if (_isAbsolute(childPath)) { return childPath; }