UNPKG

@truffle/compile-solidity

Version:
81 lines (68 loc) 3.08 kB
import debugModule from "debug"; const debug = debugModule("compile:parser"); // Warning issued by a pre-release compiler version, ignored by this component. const preReleaseCompilerWarning = "This is a pre-release compiler version, please do not use it in production."; export const Parser = { // This needs to be fast! It is fast (as of this writing). Keep it fast! parseImports(body, solc) { // WARNING: Kind of a hack (an expedient one). // So we don't have to maintain a separate parser, we'll get all the imports // in a file by sending the file to solc and evaluating the error messages // to see what import statements couldn't be resolved. To prevent full-on // compilation when a file has no import statements, we inject an import // statement right on the end; just to ensure it will error and we can parse // the imports speedily without doing extra work. // Inject failing import. const failingImportFileName = "__Truffle__NotFound.sol"; body = `${body}\n\nimport '${failingImportFileName}';\n`; const solcStandardInput = { language: "Solidity", sources: { "ParsedContract.sol": { content: body } }, settings: { outputSelection: { "ParsedContract.sol": { "*": [] // We don't need any output. } } } }; // By compiling only with ParsedContract.sol as the source, solc.compile returns file import errors for each import path. let output = solc.compile(JSON.stringify(solcStandardInput)); output = JSON.parse(output); // Filter out the "pre-release compiler" warning, if present. const errors = output.errors.filter( ({ message }) => !message.includes(preReleaseCompilerWarning) ); debug("errors: %O", errors); // Filter out our forced import, then get the import paths of the rest. const imports = errors .map(({ formattedMessage, message }) => { // Multiline import check which works for solcjs and solc // solcjs: ^ (Relevant source part starts here and spans across multiple lines) // solc: Spanning multiple lines. if (formattedMessage.includes("multiple lines")) { // Parse the import filename from the error message, this does not include the full path to the import const matches = message.match(/Source[^'"]?.*?("|')([^'"]+)("|')/); if (matches) { // Extract the full path by matching against body with the import filename const fullPathRegex = new RegExp(`("|')(.*${matches[2]})("|')`); const importMatches = body.match(fullPathRegex); if (importMatches) return importMatches[2]; } } else { const matches = formattedMessage.match( /import[^'"]?.*("|')([^'"]+)("|')/ ); // Return the item between the quotes. if (matches) return matches[2]; } }) .filter(match => match !== undefined && match !== failingImportFileName); return imports; } };