refactor plugin loading

pull/5370/head
filip mertens 2 years ago
parent 76254bafca
commit 3ee250e322
  1. 8
      .circleci/config.yml
  2. 6
      apps/doc-gen/project.json
  3. 0
      apps/doc-gen/src/profile.json
  4. 6
      apps/doc-viewer/project.json
  5. 0
      apps/doc-viewer/src/profile.json
  6. 6
      apps/etherscan/project.json
  7. 16
      apps/etherscan/src/profile.json
  8. 2
      apps/remix-ide-e2e/src/tests/etherscan_api.test.ts
  9. 2
      apps/remix-ide-e2e/src/tests/vyper_api.test.ts
  10. 5
      apps/remix-ide/project.json
  11. 31
      apps/remix-ide/src/remixAppManager.js
  12. 24
      apps/remix-ide/webpack.config.js
  13. 6
      apps/vyper/project.json
  14. 3
      apps/vyper/src/app/app.tsx
  15. 0
      apps/vyper/src/assets/logo.svg
  16. 0
      apps/vyper/src/assets/star.svg
  17. 14
      apps/vyper/src/profile.json

@ -286,7 +286,7 @@ workflows:
- build-plugin: - build-plugin:
matrix: matrix:
parameters: parameters:
plugin: ["etherscan", "vyper", "plugin_api", "doc-gen", "doc-viewer"] plugin: ["plugin_api"]
- lint: - lint:
requires: requires:
- build - build
@ -299,15 +299,11 @@ workflows:
matrix: matrix:
alias: plugins alias: plugins
parameters: parameters:
plugin: ["etherscan", "vyper", "plugin_api"] plugin: ["plugin_api"]
parallelism: [1, 9] parallelism: [1, 9]
exclude: exclude:
- plugin: plugin_api - plugin: plugin_api
parallelism: 1 parallelism: 1
- plugin: etherscan
parallelism: 9
- plugin: vyper
parallelism: 9
- remix-ide-browser: - remix-ide-browser:
requires: requires:

@ -14,11 +14,12 @@
"compiler": "babel", "compiler": "babel",
"outputPath": "dist/apps/doc-gen", "outputPath": "dist/apps/doc-gen",
"index": "apps/doc-gen/src/index.html", "index": "apps/doc-gen/src/index.html",
"baseHref": "/", "baseHref": "./",
"main": "apps/doc-gen/src/main.tsx", "main": "apps/doc-gen/src/main.tsx",
"tsConfig": "apps/doc-gen/tsconfig.app.json", "tsConfig": "apps/doc-gen/tsconfig.app.json",
"assets": [ "assets": [
"apps/doc-gen/src/favicon.ico" "apps/doc-gen/src/favicon.ico",
"apps/doc-gen/src/profile.json"
], ],
"styles": [], "styles": [],
"scripts": [], "scripts": [],
@ -28,7 +29,6 @@
"development": { "development": {
}, },
"production": { "production": {
"baseHref": "./",
"fileReplacements": [ "fileReplacements": [
{ {
"replace": "apps/doc-gen/src/environments/environment.ts", "replace": "apps/doc-gen/src/environments/environment.ts",

@ -14,11 +14,12 @@
"compiler": "babel", "compiler": "babel",
"outputPath": "dist/apps/doc-viewer", "outputPath": "dist/apps/doc-viewer",
"index": "apps/doc-viewer/src/index.html", "index": "apps/doc-viewer/src/index.html",
"baseHref": "/", "baseHref": "./",
"main": "apps/doc-viewer/src/main.tsx", "main": "apps/doc-viewer/src/main.tsx",
"tsConfig": "apps/doc-viewer/tsconfig.app.json", "tsConfig": "apps/doc-viewer/tsconfig.app.json",
"assets": [ "assets": [
"apps/doc-viewer/src/favicon.ico" "apps/doc-viewer/src/favicon.ico",
"apps/doc-viewer/src/profile.json"
], ],
"styles": [], "styles": [],
"scripts": [], "scripts": [],
@ -28,7 +29,6 @@
"development": { "development": {
}, },
"production": { "production": {
"baseHref": "./",
"fileReplacements": [ "fileReplacements": [
{ {
"replace": "apps/doc-viewer/src/environments/environment.ts", "replace": "apps/doc-viewer/src/environments/environment.ts",

@ -18,7 +18,8 @@
"tsConfig": "apps/etherscan/tsconfig.app.json", "tsConfig": "apps/etherscan/tsconfig.app.json",
"assets": [ "assets": [
"apps/etherscan/src/favicon.ico", "apps/etherscan/src/favicon.ico",
"apps/etherscan/src/assets" "apps/etherscan/src/assets",
"apps/etherscan/src/profile.json"
], ],
"styles": ["apps/etherscan/src/styles.css"], "styles": ["apps/etherscan/src/styles.css"],
"scripts": [], "scripts": [],
@ -42,7 +43,8 @@
"defaultConfiguration": "development", "defaultConfiguration": "development",
"options": { "options": {
"buildTarget": "etherscan:build", "buildTarget": "etherscan:build",
"hmr": true "hmr": true,
"baseHref": "/"
}, },
"configurations": { "configurations": {
"development": { "development": {

@ -0,0 +1,16 @@
{
"name": "etherscan",
"displayName": "Etherscan - Contract verification",
"description": "Verify Solidity contract code using Etherscan API",
"version": "0.1.0",
"events": [],
"methods": ["verify", "receiptStatus"],
"kind": "none",
"icon": "data:image/png;base64,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",
"location": "sidePanel",
"url": "https://ipfs-cluster.ethdevops.io/ipfs/QmQsZbBSYCVBVpz2mVRbPRVTrcz59oJEpuuoxiT9otu3mh",
"repo": "https://github.com/ethereum/remix-project/tree/master/apps/etherscan",
"documentation": "https://remix-etherscan-plugin.readthedocs.io/en/latest",
"maintainedBy": "Remix",
"authorContact": "remix@ethereum.org"
}

@ -9,7 +9,7 @@ declare global {
module.exports = { module.exports = {
'@disabled': true, '@disabled': true,
before: function (browser: NightwatchBrowser, done: VoidFunction) { before: function (browser: NightwatchBrowser, done: VoidFunction) {
init(browser, done, null, true, { name: 'etherscan', url: 'http://127.0.0.1:9999'}) init(browser, done, null)
}, },
'Should load etherscan plugin #group1': function (browser: NightwatchBrowser) { 'Should load etherscan plugin #group1': function (browser: NightwatchBrowser) {

@ -9,7 +9,7 @@ declare global {
module.exports = { module.exports = {
'@disabled': true, '@disabled': true,
before: function (browser: NightwatchBrowser, done: VoidFunction) { before: function (browser: NightwatchBrowser, done: VoidFunction) {
init(browser, done, null, true, { name: 'vyper', url: 'http://127.0.0.1:9999'}) init(browser, done)
}, },
'Should connect to vyper plugin #group1': function (browser: NightwatchBrowser) { 'Should connect to vyper plugin #group1': function (browser: NightwatchBrowser) {

@ -3,8 +3,7 @@
"$schema": "../../node_modules/nx/schemas/project-schema.json", "$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "apps/remix-ide/src", "sourceRoot": "apps/remix-ide/src",
"projectType": "application", "projectType": "application",
"implicitDependencies": [ "implicitDependencies": ["doc-gen", "doc-viewer", "etherscan", "vyper"],
],
"targets": { "targets": {
"build": { "build": {
"executor": "@nrwl/webpack:webpack", "executor": "@nrwl/webpack:webpack",
@ -47,7 +46,7 @@
"executor": "@nrwl/webpack:dev-server", "executor": "@nrwl/webpack:dev-server",
"defaultConfiguration": "development", "defaultConfiguration": "development",
"options": { "options": {
"buildTarget": "remix-ide:build", "buildTarget": "remix-ide:build"
}, },
"configurations": { "configurations": {

@ -9,12 +9,14 @@ const requiredModules = [ // services + layout views + system views
'manager', 'config', 'compilerArtefacts', 'compilerMetadata', 'contextualListener', 'editor', 'offsetToLineColumnConverter', 'network', 'theme', 'locale', 'manager', 'config', 'compilerArtefacts', 'compilerMetadata', 'contextualListener', 'editor', 'offsetToLineColumnConverter', 'network', 'theme', 'locale',
'fileManager', 'contentImport', 'blockchain', 'web3Provider', 'scriptRunner', 'fetchAndCompile', 'mainPanel', 'hiddenPanel', 'sidePanel', 'menuicons', 'fileManager', 'contentImport', 'blockchain', 'web3Provider', 'scriptRunner', 'fetchAndCompile', 'mainPanel', 'hiddenPanel', 'sidePanel', 'menuicons',
'filePanel', 'terminal', 'settings', 'pluginManager', 'tabs', 'udapp', 'dGitProvider', 'solidity', 'solidity-logic', 'gistHandler', 'layout', 'filePanel', 'terminal', 'settings', 'pluginManager', 'tabs', 'udapp', 'dGitProvider', 'solidity', 'solidity-logic', 'gistHandler', 'layout',
'notification', 'permissionhandler', 'walkthrough', 'storage', 'restorebackupzip', 'link-libraries', 'deploy-libraries', 'openzeppelin-proxy', 'notification', 'permissionhandler', 'walkthrough', 'storage', 'restorebackupzip', 'link-libraries', 'deploy-libraries', 'openzeppelin-proxy',
'hardhat-provider', 'ganache-provider', 'foundry-provider', 'basic-http-provider', 'injected', 'injected-trustwallet', 'injected-optimism-provider', 'injected-arbitrum-one-provider', 'vm-custom-fork', 'vm-goerli-fork', 'vm-mainnet-fork', 'vm-sepolia-fork', 'vm-merge', 'vm-london', 'vm-berlin', 'hardhat-provider', 'ganache-provider', 'foundry-provider', 'basic-http-provider', 'injected', 'injected-trustwallet', 'injected-optimism-provider', 'injected-arbitrum-one-provider', 'vm-custom-fork', 'vm-goerli-fork', 'vm-mainnet-fork', 'vm-sepolia-fork', 'vm-merge', 'vm-london', 'vm-berlin',
'compileAndRun', 'search', 'recorder', 'fileDecorator', 'codeParser', 'codeFormatter', 'solidityumlgen', 'contractflattener', 'doc-gen', 'doc-viewer', 'solidity-script'] 'compileAndRun', 'search', 'recorder', 'fileDecorator', 'codeParser', 'codeFormatter', 'solidityumlgen', 'contractflattener', 'solidity-script']
// dependentModules shouldn't be manually activated (e.g hardhat is activated by remixd) // dependentModules shouldn't be manually activated (e.g hardhat is activated by remixd)
const dependentModules = ['foundry', 'hardhat', 'truffle', 'slither'] const dependentModules = ['foundry', 'hardhat', 'truffle', 'slither']
const loadLocalPlugins = ["doc-gen", "doc-viewer", "etherscan", "vyper"]
const sensitiveCalls = { const sensitiveCalls = {
'fileManager': ['writeFile', 'copyFile', 'rename', 'copyDir'], 'fileManager': ['writeFile', 'copyFile', 'rename', 'copyDir'],
@ -24,9 +26,9 @@ const sensitiveCalls = {
export function isNative(name) { export function isNative(name) {
// nativePlugin allows to bypass the permission request // nativePlugin allows to bypass the permission request
const nativePlugins = ['vyper', 'workshops', 'debugger', 'remixd', 'menuicons', 'solidity', 'solidity-logic', 'solidityStaticAnalysis', 'solidityUnitTesting', const nativePlugins = ['vyper', 'workshops', 'debugger', 'remixd', 'menuicons', 'solidity', 'solidity-logic', 'solidityStaticAnalysis', 'solidityUnitTesting',
'layout', 'notification', 'hardhat-provider', 'ganache-provider', 'foundry-provider', 'basic-http-provider', 'injected-optimism-provider', 'layout', 'notification', 'hardhat-provider', 'ganache-provider', 'foundry-provider', 'basic-http-provider', 'injected-optimism-provider',
'tabs', 'injected-arbitrum-one-provider', 'injected'] 'tabs', 'injected-arbitrum-one-provider', 'injected', 'doc-gen', 'doc-viewer']
return nativePlugins.includes(name) || requiredModules.includes(name) return nativePlugins.includes(name) || requiredModules.includes(name)
} }
@ -149,7 +151,24 @@ export class RemixAppManager extends PluginManager {
} }
const testPluginName = localStorage.getItem('test-plugin-name') const testPluginName = localStorage.getItem('test-plugin-name')
const testPluginUrl = localStorage.getItem('test-plugin-url') const testPluginUrl = localStorage.getItem('test-plugin-url')
return plugins.map(plugin => {
for (let plugin of loadLocalPlugins) {
// fetch the profile from the local plugin
try {
const profile = await fetch(`plugins/${plugin}/profile.json`)
const profileJson = await profile.json()
// remove duplicates
plugins = plugins.filter((p) => p.name !== profileJson.name && p.displayName !== profileJson.displayName)
// change url
profileJson.url = `plugins/${plugin}/index.html`
// add the local plugin
plugins.push(profileJson)
} catch (e) {
console.log(e)
}
}
return plugins.map(plugin => {
if (plugin.name === testPluginName) plugin.url = testPluginUrl if (plugin.name === testPluginName) plugin.url = testPluginUrl
return new IframePlugin(plugin) return new IframePlugin(plugin)
}) })

@ -15,6 +15,23 @@ const versionData = {
fs.writeFileSync('./apps/remix-ide/src/assets/version.json', JSON.stringify(versionData)) fs.writeFileSync('./apps/remix-ide/src/assets/version.json', JSON.stringify(versionData))
const project = fs.readFileSync('./apps/remix-ide/project.json', 'utf8')
const implicitDependencies = JSON.parse(project).implicitDependencies
const copyPatterns = implicitDependencies.map((dep) => {
try {
fs.statSync(__dirname + `/../../dist/apps/${dep}`).isDirectory()
return { from: `../../dist/apps/${dep}`, to: `plugins/${dep}` }
}
catch (e) {
console.log('error', e)
return false
}
})
console.log('Copying plugins... ', copyPatterns)
// Nx plugins for webpack. // Nx plugins for webpack.
module.exports = composePlugins(withNx(), withReact(), (config) => { module.exports = composePlugins(withNx(), withReact(), (config) => {
// Update the webpack config as needed here. // Update the webpack config as needed here.
@ -41,7 +58,7 @@ module.exports = composePlugins(withNx(), withReact(), (config) => {
"buffer": require.resolve("buffer/"), "buffer": require.resolve("buffer/"),
"vm": require.resolve('vm-browserify'), "vm": require.resolve('vm-browserify'),
} }
// add externals // add externals
config.externals = { config.externals = {
@ -61,8 +78,7 @@ module.exports = composePlugins(withNx(), withReact(), (config) => {
new CopyPlugin({ new CopyPlugin({
patterns: [ patterns: [
{ from: '../../node_modules/monaco-editor/min/vs', to: 'assets/js/monaco-editor/min/vs' }, { from: '../../node_modules/monaco-editor/min/vs', to: 'assets/js/monaco-editor/min/vs' },
{ from: '../../dist/apps/doc-gen', to: 'plugins/doc-gen' }, ...copyPatterns
{ from: '../../dist/apps/doc-gen', to: 'plugins/doc-viewer' },
].filter(Boolean) ].filter(Boolean)
}), }),
new webpack.ProvidePlugin({ new webpack.ProvidePlugin({
@ -79,7 +95,7 @@ module.exports = composePlugins(withNx(), withReact(), (config) => {
enforce: "pre" enforce: "pre"
}) })
config.ignoreWarnings = [/Failed to parse source map/, /require function/ ] // ignore source-map-loader warnings & AST warnings config.ignoreWarnings = [/Failed to parse source map/, /require function/] // ignore source-map-loader warnings & AST warnings
// set minimizer // set minimizer
config.optimization.minimizer = [ config.optimization.minimizer = [

@ -18,7 +18,8 @@
"tsConfig": "apps/vyper/tsconfig.app.json", "tsConfig": "apps/vyper/tsconfig.app.json",
"assets": [ "assets": [
"apps/vyper/src/favicon.ico", "apps/vyper/src/favicon.ico",
"apps/vyper/src/assets" "apps/vyper/src/assets",
"apps/vyper/src/profile.json"
], ],
"styles": ["apps/vyper/src/styles.css"], "styles": ["apps/vyper/src/styles.css"],
"scripts": [], "scripts": [],
@ -42,7 +43,8 @@
"defaultConfiguration": "development", "defaultConfiguration": "development",
"options": { "options": {
"buildTarget": "vyper:build", "buildTarget": "vyper:build",
"hmr": true "hmr": true,
"baseHref": "/"
}, },
"configurations": { "configurations": {
"development": { "development": {

@ -12,7 +12,6 @@ import ToggleButtonGroup from 'react-bootstrap/ToggleButtonGroup'
import ToggleButton from 'react-bootstrap/ToggleButton' import ToggleButton from 'react-bootstrap/ToggleButton'
import Button from 'react-bootstrap/Button' import Button from 'react-bootstrap/Button'
import vyperLogo from './logo.svg'
import './app.css' import './app.css'
interface AppState { interface AppState {
@ -71,7 +70,7 @@ const App: React.FC = () => {
<main id="vyper-plugin"> <main id="vyper-plugin">
<header> <header>
<div className="title"> <div className="title">
<img src={vyperLogo} alt="Vyper logo" /> <img src={'assets/logo.svg'} alt="Vyper logo" />
<h4>yper Compiler</h4> <h4>yper Compiler</h4>
</div> </div>
<a <a

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

Before

Width:  |  Height:  |  Size: 347 B

After

Width:  |  Height:  |  Size: 347 B

@ -0,0 +1,14 @@
{
"name": "vyper",
"displayName": "Vyper",
"methods": ["getCompilationResult", "compile"],
"url": "https://ipfs-cluster.ethdevops.io/ipfs/QmbmPzUg7ghTKcF2eo64zm1k1LKdibYfqYmiqXkHKXks8r",
"documentation": "https://github.com/GrandSchtroumpf/vyper-remix",
"description": "Compile vyper contracts",
"kind": "compiler",
"icon": "data:image/svg+xml;base64,PHN2ZyBpZD0iRmxhdF9Mb2dvIiBkYXRhLW5hbWU9IkZsYXQgTG9nbyIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB2aWV3Qm94PSIwIDAgMjA0OCAxNzczLjYyIj4gIDx0aXRsZT52eXBlci1sb2dvLWZsYXQ8L3RpdGxlPiAgPHBvbHlsaW5lIHBvaW50cz0iMTAyNCA4ODYuODEgNzY4IDEzMzAuMjIgMTAyNCAxNzczLjYyIDEyODAgMTMzMC4yMiAxMDI0IDg4Ni44MSIgc3R5bGU9ImZpbGw6IzMzMyIvPiAgPHBvbHlsaW5lIHBvaW50cz0iMTI4MCA0NDMuNDEgMTAyNCA4ODYuODEgMTI4MCAxMzMwLjIyIDE1MzYgODg2LjgxIDEyODAgNDQzLjQxIiBzdHlsZT0iZmlsbDojNjY2Ii8+ICA8cG9seWxpbmUgcG9pbnRzPSI3NjggNDQzLjQxIDUxMiA4ODYuODEgNzY4IDEzMzAuMjIgMTAyNCA4ODYuODEgNzY4IDQ0My40MSIgc3R5bGU9ImZpbGw6IzY2NiIvPiAgPHBvbHlsaW5lIHBvaW50cz0iMTUzNiAwIDEyODAgNDQzLjQxIDE1MzYgODg2LjgxIDE3OTIgNDQzLjQxIDE1MzYgMCIgc3R5bGU9ImZpbGw6IzhjOGM4YyIvPiAgPHBvbHlsaW5lIHBvaW50cz0iMTE1MiAyMjEuNyA4OTYgMjIxLjcgNzY4IDQ0My40MSAxMDI0IDg4Ni44MSAxMjgwIDQ0My40MSAxMTUyIDIyMS43IiBzdHlsZT0iZmlsbDojOGM4YzhjIi8+ICA8cG9seWxpbmUgcG9pbnRzPSI1MTIgMCAyNTYgNDQzLjQxIDUxMiA4ODYuODEgNzY4IDQ0My40MSA1MTIgMCIgc3R5bGU9ImZpbGw6IzhjOGM4YyIvPiAgPHBvbHlsaW5lIHBvaW50cz0iMjA0OCAwIDE1MzYgMCAxNzkyIDQ0My40IDIwNDggMCIgc3R5bGU9ImZpbGw6I2IyYjJiMiIvPiAgPHBvbHlsaW5lIHBvaW50cz0iNTEyIDAgMCAwIDI1NiA0NDMuNCA1MTIgMCIgc3R5bGU9ImZpbGw6I2IyYjJiMiIvPjwvc3ZnPg==",
"location": "sidePanel",
"repo": "https://github.com/ethereum/remix-project/tree/master/apps/vyper",
"maintainedBy": "Remix",
"authorContact": "remix@ethereum.org"
}
Loading…
Cancel
Save