diff --git a/apps/remix-ide-e2e/src/tests/solidityImport.test.ts b/apps/remix-ide-e2e/src/tests/solidityImport.test.ts index 665c47379a..f7c42851e1 100644 --- a/apps/remix-ide-e2e/src/tests/solidityImport.test.ts +++ b/apps/remix-ide-e2e/src/tests/solidityImport.test.ts @@ -79,6 +79,16 @@ module.exports = { browser.assert.ok(content.indexOf('contract ERC20 is Context, IERC20') !== -1, 'current displayed content should be from the ERC20 source code') }) + }, + + 'Test NPM Import (with unpkg.com)': function (browser: NightwatchBrowser) { + browser + // .setSolidityCompilerVersion('soljson-v0.8.0+commit.c7dfd78e.js') + .clickLaunchIcon('fileExplorers') + .click('li[data-id="treeViewLitreeViewItembrowser/README.txt"') + .addFile('Untitled9.sol', sources[8]['browser/Untitled9.sol']) + .clickLaunchIcon('fileExplorers') + .verifyContracts(['test13', 'ERC20', 'SafeMath'], { wait: 30000 }) .end() }, tearDown: sauce @@ -109,5 +119,8 @@ const sources = [ }, { 'browser/Untitled8.sol': { content: 'import "https://raw.githubusercontent.com/OpenZeppelin/openzeppelin-contracts/master/contracts/token/ERC20/ERC20.sol"; contract test12 {}' } + }, + { + 'browser/Untitled9.sol': { content: 'pragma solidity >=0.6.0 <0.8.0; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; contract test13 {}' } } ] diff --git a/apps/remix-ide/src/app/compiler/compiler-imports.js b/apps/remix-ide/src/app/compiler/compiler-imports.js index 39ab3e9f7a..18e05ebf2e 100644 --- a/apps/remix-ide/src/app/compiler/compiler-imports.js +++ b/apps/remix-ide/src/app/compiler/compiler-imports.js @@ -6,7 +6,6 @@ const remixTests = require('@remix-project/remix-tests') const globalRegistry = require('../../global/registry') const addTooltip = require('../ui/tooltip') const async = require('async') -var resolver = require('@resolver-engine/imports').ImportsEngine() const profile = { name: 'contentImport', @@ -50,7 +49,7 @@ module.exports = class CompilerImports extends Plugin { }) } - import (url, force, loadingCb, cb) { + async import (url, force, loadingCb, cb) { if (typeof force !== 'boolean') { const temp = loadingCb loadingCb = force @@ -66,44 +65,20 @@ module.exports = class CompilerImports extends Plugin { if (imported) { return cb(null, imported.content, imported.cleanUrl, imported.type, url) } - var handlers = this.urlResolver.getHandlers() - var found = false - handlers.forEach(function (handler) { - if (found) return - var match = handler.match(url) - if (match) { - found = true - loadingCb('Loading ' + url + ' ...') - handler.handle(match).then(function (result) { - const { content, cleanUrl } = result - self.previouslyHandled[url] = { - content, - cleanUrl, - type: handler.type - } - cb(null, content, cleanUrl, handler.type, url) - }).catch(function (error) { - cb('Unable to import url : ' + error) - }) - } - }) - if (found) return - resolver - .resolve(url) - .then(result => { - return resolver.require(url) - }) - .then(result => { - if (url.indexOf(result.provider + ':') === 0) { - url = url.substring(result.provider.length + 1) // remove the github prefix - } - cb(null, result.source, url, result.provider, result.url) - }) - .catch(err => { - console.error(err) - cb('Unable to import "' + url + '": File not found') - }) + let resolved + try { + resolved = await this.urlResolver.resolve(url) + const { content, cleanUrl, type } = resolved + self.previouslyHandled[url] = { + content, + cleanUrl, + type + } + cb(null, content, cleanUrl, type, url) + } catch (e) { + return cb('Unable to import url : ' + e.message) + } } importExternal (url, targetPath, cb) { @@ -182,13 +157,12 @@ module.exports = class CompilerImports extends Plugin { } resolve(result) }) - } else { - // try to resolve external content - this.importExternal(url, targetPath, (error, content) => { - if (error) return reject(error) - resolve(content) - }) } + + this.importExternal(url, targetPath, (error, content) => { + if (error) return reject(error) + resolve(content) + }) }) } }) diff --git a/libs/remix-url-resolver/src/resolve.ts b/libs/remix-url-resolver/src/resolve.ts index 1e3f92cbc6..7aa4646bf4 100644 --- a/libs/remix-url-resolver/src/resolve.ts +++ b/libs/remix-url-resolver/src/resolve.ts @@ -121,6 +121,21 @@ export class RemixURLResolver { } } + /** + * Handle an import statement based on NPM + * @param url The url of the NPM import statement + */ + async handleNpmImport(url: string): Promise { + // eslint-disable-next-line no-useless-catch + try { + const req = 'https://unpkg.com/' + url + const response: AxiosResponse = await axios.get(req) + return { content: response.data, cleanUrl: url } + } catch (e) { + throw e + } + } + getHandlers(): Handler[] { return [ { @@ -147,6 +162,11 @@ export class RemixURLResolver { type: 'ipfs', match: (url) => { return /^(ipfs:\/\/?.+)/.exec(url) }, handle: (match) => this.handleIPFS(match[1]) + }, + { + type: 'npm', + match: (url) => { return /^[^/][^\n"?:*<>|]*$/g.exec(url) }, // match a typical relative path + handle: (match) => this.handleNpmImport(match[0]) } ] }