Merge branch 'master' into errormarkers

pull/2774/head
bunsenstraat 2 years ago committed by GitHub
commit 408b44d1df
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 54
      .circleci/config.yml
  2. 2
      README.md
  3. 17
      apps/remix-ide-e2e/src/helpers/init.ts
  4. 32
      apps/remix-ide-e2e/src/tests/ballot.test.ts
  5. 42
      apps/remix-ide-e2e/src/tests/search.test.ts
  6. 4
      apps/remix-ide-e2e/src/tests/terminal.test.ts
  7. 97
      apps/remix-ide-e2e/src/tests/url.test.ts
  8. 96
      apps/remix-ide-e2e/src/tests/vyper_api.ts
  9. 25
      apps/remix-ide/ci/browser_tests_vyper_plugin.sh
  10. 23
      apps/remix-ide/src/app.js
  11. 3
      apps/remix-ide/src/app/tabs/abstract-provider.tsx
  12. 6
      apps/remix-ide/src/remixAppManager.js
  13. 13
      apps/vyper/.babelrc
  14. 16
      apps/vyper/.browserslistrc
  15. 18
      apps/vyper/.eslintrc
  16. 124
      apps/vyper/src/app/app.css
  17. 118
      apps/vyper/src/app/app.tsx
  18. 75
      apps/vyper/src/app/components/CompilerButton.tsx
  19. 36
      apps/vyper/src/app/components/LocalUrl.tsx
  20. 70
      apps/vyper/src/app/components/VyperResult.tsx
  21. 19
      apps/vyper/src/app/components/WarnRemote.tsx
  22. 161
      apps/vyper/src/app/examples/ballot.tsx
  23. 14
      apps/vyper/src/app/logo.svg
  24. 11
      apps/vyper/src/app/star.svg
  25. 139
      apps/vyper/src/app/utils/compiler.tsx
  26. 12
      apps/vyper/src/app/utils/index.ts
  27. 71
      apps/vyper/src/app/utils/remix-client.tsx
  28. 0
      apps/vyper/src/assets/.gitkeep
  29. 3
      apps/vyper/src/environments/environment.prod.ts
  30. 6
      apps/vyper/src/environments/environment.ts
  31. BIN
      apps/vyper/src/favicon.ico
  32. 14
      apps/vyper/src/index.html
  33. 7
      apps/vyper/src/main.tsx
  34. 7
      apps/vyper/src/polyfills.ts
  35. 1
      apps/vyper/src/styles.css
  36. 14
      apps/vyper/tsconfig.app.json
  37. 20
      apps/vyper/tsconfig.json
  38. 2
      libs/remix-core-plugin/src/lib/compiler-metadata.ts
  39. 4
      libs/remix-core-plugin/src/lib/constants/uups.ts
  40. 2
      libs/remix-core-plugin/src/lib/editor-context-listener.ts
  41. 53
      libs/remix-core-plugin/src/lib/openzeppelin-proxy.ts
  42. 1
      libs/remix-tests/package.json
  43. 20
      libs/remix-tests/src/compiler.ts
  44. 6
      libs/remix-tests/src/run.ts
  45. 56
      libs/remix-tests/src/runTestFiles.ts
  46. 4
      libs/remix-tests/tests/examples_0/assert_ok_test.sol
  47. 12
      libs/remix-tests/tests/examples_0/assert_ok_without_console_test.sol
  48. 1532
      libs/remix-tests/tests/examples_0/hardhat/console.sol
  49. 46
      libs/remix-tests/tests/testRunner.cli.spec.ts
  50. 2
      libs/remix-tests/tests/testRunner.spec.ts
  51. 2
      libs/remix-ui/clipboard/src/lib/copy-to-clipboard/copy-to-clipboard.tsx
  52. 29
      libs/remix-ui/editor/src/lib/remix-ui-editor.tsx
  53. 13
      libs/remix-ui/run-tab/src/lib/actions/events.ts
  54. 25
      libs/remix-ui/run-tab/src/lib/components/contractGUI.tsx
  55. 2
      libs/remix-ui/settings/src/lib/constants.ts
  56. 26
      libs/remix-ui/solidity-compiler/src/lib/compiler-container.tsx
  57. 10
      libs/remix-ui/solidity-compiler/src/lib/contract-selection.tsx
  58. 2
      libs/remix-ui/solidity-compiler/src/lib/solidity-compiler.tsx
  59. 1
      libs/remix-ui/solidity-compiler/src/lib/types/index.ts
  60. 2
      libs/remix-ui/terminal/src/lib/utils/wrapScript.ts
  61. 5
      libs/remix-ui/workspace/src/lib/actions/workspace.ts
  62. 69
      libs/remixd/README.md
  63. 2
      libs/remixd/package.json
  64. 44
      nx.json
  65. 1
      package.json
  66. 498
      workspace.json
  67. 137
      yarn.lock

@ -35,10 +35,10 @@ jobs:
key: v1-deps-{{ checksum "yarn.lock" }} key: v1-deps-{{ checksum "yarn.lock" }}
paths: paths:
- node_modules - node_modules
- run: yarn run downloadsolc_assets - run: yarn run downloadsolc_assets
- run: npx nx build remix-ide - run: npx nx build remix-ide
- run: npx nx build remix-ide-e2e-src-local-plugin - run: npx nx build remix-ide-e2e-src-local-plugin
- run: yarn run build:libs - run: yarn run build:libs
- run: mkdir persist && zip -r persist/dist.zip dist - run: mkdir persist && zip -r persist/dist.zip dist
- persist_to_workspace: - persist_to_workspace:
@ -266,6 +266,50 @@ jobs:
path: ./reports/tests path: ./reports/tests
- store_artifacts: - store_artifacts:
path: ./reports/screenshots path: ./reports/screenshots
remix-ide-vyper-plugin:
docker:
# specify the version you desire here
- image: cimg/node:14.17.6-browsers
# Specify service dependencies here if necessary
# CircleCI maintains a library of pre-built images
# documented at https://circleci.com/docs/2.0/circleci-images/
resource_class: xlarge
# - image: circleci/mongo:3.4.4
environment:
- COMMIT_AUTHOR_EMAIL: "yann@ethereum.org"
- COMMIT_AUTHOR: "Circle CI"
working_directory: ~/remix-project
parallelism: 10
steps:
- browser-tools/install-chrome
- browser-tools/install-chromedriver
- run:
command: |
google-chrome --version
chromedriver --version
java -jar /usr/local/bin/selenium.jar --version
name: Check install
- checkout
- checkout
- attach_workspace:
at: .
- run: unzip ./persist/dist.zip
- restore_cache:
keys:
- v1-deps-{{ checksum "yarn.lock" }}
- run: yarn install
- run:
name: Start Selenium
command: java -jar /usr/local/bin/selenium.jar
background: true
- run: npx nx build vyper
- run: ./apps/remix-ide/ci/browser_tests_vyper_plugin.sh
- store_test_results:
path: ./reports/tests
- store_artifacts:
path: ./reports/screenshots
remix-ide-plugin-api: remix-ide-plugin-api:
docker: docker:
@ -420,6 +464,9 @@ workflows:
- remix-ide-plugin-api: - remix-ide-plugin-api:
requires: requires:
- build - build
- remix-ide-vyper-plugin:
requires:
- build
- remix-ide-chrome: - remix-ide-chrome:
requires: requires:
- build - build
@ -433,6 +480,7 @@ workflows:
- remix-ide-chrome - remix-ide-chrome
- remix-ide-firefox - remix-ide-firefox
- remix-ide-plugin-api - remix-ide-plugin-api
- remix-ide-vyper-plugin
filters: filters:
branches: branches:
only: remix_live only: remix_live
@ -443,6 +491,7 @@ workflows:
- remix-ide-chrome - remix-ide-chrome
- remix-ide-firefox - remix-ide-firefox
- remix-ide-plugin-api - remix-ide-plugin-api
- remix-ide-vyper-plugin
filters: filters:
branches: branches:
only: master only: master
@ -453,6 +502,7 @@ workflows:
- remix-ide-chrome - remix-ide-chrome
- remix-ide-firefox - remix-ide-firefox
- remix-ide-plugin-api - remix-ide-plugin-api
- remix-ide-vyper-plugin
filters: filters:
branches: branches:
only: remix_beta only: remix_beta

@ -213,7 +213,7 @@ To do this you need to:
browser.waitForElementPresent('*[data-id="verticalIconsKindfilePanel"]') browser.waitForElementPresent('*[data-id="verticalIconsKindfilePanel"]')
``` ```
- add '@disable': true to the test file you want to split: - add '@disabled': true to the test file you want to split:
``` ```
module.exports = { module.exports = {

@ -2,13 +2,28 @@ import { NightwatchBrowser } from 'nightwatch'
require('dotenv').config() require('dotenv').config()
export default function (browser: NightwatchBrowser, callback: VoidFunction, url?: string, preloadPlugins = true): void { type LoadPlugin = {
name: string
url: string
}
export default function (browser: NightwatchBrowser, callback: VoidFunction, url?: string, preloadPlugins = true, loadPlugin?: LoadPlugin): void {
browser browser
.url(url || 'http://127.0.0.1:8080') .url(url || 'http://127.0.0.1:8080')
.pause(6000) .pause(6000)
.switchBrowserTab(0) .switchBrowserTab(0)
.waitForElementVisible('[id="remixTourSkipbtn"]') .waitForElementVisible('[id="remixTourSkipbtn"]')
.click('[id="remixTourSkipbtn"]') .click('[id="remixTourSkipbtn"]')
.perform((done) => {
if (!loadPlugin) return done()
browser.execute(function (loadPlugin) { // override a plugin url for testing purpose
localStorage.setItem('test-plugin-name', loadPlugin.name)
localStorage.setItem('test-plugin-url', loadPlugin.url)
}, [loadPlugin])
.refresh()
.pause(6000)
.perform(done)
})
.maximizeWindow() .maximizeWindow()
.fullscreenWindow(() => { .fullscreenWindow(() => {
if (preloadPlugins) { if (preloadPlugins) {

@ -134,6 +134,23 @@ module.exports = {
.sendKeys('*[data-id$="scConfigFilePathInput"]', browser.Keys.ENTER) .sendKeys('*[data-id$="scConfigFilePathInput"]', browser.Keys.ENTER)
.openFile('Untitled.sol') .openFile('Untitled.sol')
.verifyContracts(['Ballot'], {wait: 2000, runs: '300'}) .verifyContracts(['Ballot'], {wait: 2000, runs: '300'})
},
'Compile and deploy sample yul file': function (browser: NightwatchBrowser) {
browser
.addFile('sample.yul', {content: yulSample})
.clickLaunchIcon('solidity')
.waitForElementVisible('*[data-id="scConfigExpander"]')
.click('*[data-id="scManualConfiguration"]')
.waitForElementVisible('select[id="compilierLanguageSelector"]', 10000)
.click('select[id="compilierLanguageSelector"]')
.click('select[id="compilierLanguageSelector"] option[value=Yul]')
.waitForElementContainsText('[data-id="compiledContracts"]', 'Contract', 60000)
.clickLaunchIcon('udapp')
.click('*[data-id="Deploy - transact (not payable)"]')
.waitForElementPresent('*[data-id="universalDappUiContractActionWrapper"]', 60000)
.journalLastChildIncludes('Contract.(constructor)')
.journalLastChildIncludes('data: 0x602...0565b')
.end() .end()
} }
} }
@ -387,4 +404,19 @@ const configFile = `
"evmVersion": "byzantium" "evmVersion": "byzantium"
} }
} }
`
const yulSample = `
object "Contract" {
code {
function power(base, exponent) -> result
{
result := 1
for { let i := 0 } lt(i, exponent) { i := add(i, 1) }
{
result := mul(result, base)
}
}
}
}
` `

@ -4,10 +4,11 @@ import { NightwatchBrowser } from 'nightwatch'
import init from '../helpers/init' import init from '../helpers/init'
module.exports = { module.exports = {
'@disabled': true,
before: function (browser: NightwatchBrowser, done: VoidFunction) { before: function (browser: NightwatchBrowser, done: VoidFunction) {
init(browser, done, 'http://127.0.0.1:8080', true) init(browser, done, 'http://127.0.0.1:8080', true)
}, },
'Should find text': function (browser: NightwatchBrowser) { 'Should find text #group1': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('*[data-id="remixIdeSidePanel"]') browser.waitForElementVisible('*[data-id="remixIdeSidePanel"]')
.click('*[plugin="search"]').waitForElementVisible('*[id="search_input"]') .click('*[plugin="search"]').waitForElementVisible('*[id="search_input"]')
.waitForElementVisible('*[id="search_include"]') .waitForElementVisible('*[id="search_include"]')
@ -26,7 +27,7 @@ module.exports = {
Array.isArray(res.value) && browser.assert.equal(res.value.length, 6) Array.isArray(res.value) && browser.assert.equal(res.value.length, 6)
}) })
}, },
'Should find text with exclude': function (browser: NightwatchBrowser) { 'Should find text with exclude #group1': function (browser: NightwatchBrowser) {
browser browser
.clearValue('*[id="search_input"]') .clearValue('*[id="search_input"]')
.setValue('*[id="search_input"]', 'contract').pause(1000) .setValue('*[id="search_input"]', 'contract').pause(1000)
@ -42,7 +43,7 @@ module.exports = {
.clearValue('*[id="search_include"]').setValue('*[id="search_include"]', '*.sol, *.js, *.txt') .clearValue('*[id="search_include"]').setValue('*[id="search_include"]', '*.sol, *.js, *.txt')
.clearValue('*[id="search_exclude"]').setValue('*[id="search_exclude"]', '.*/**/*') .clearValue('*[id="search_exclude"]').setValue('*[id="search_exclude"]', '.*/**/*')
}, },
'Should find regex': function (browser: NightwatchBrowser) { 'Should find regex #group1': function (browser: NightwatchBrowser) {
browser browser
.waitForElementVisible('*[id="search_input"]') .waitForElementVisible('*[id="search_input"]')
.clearValue('*[id="search_input"]').pause(2000) .clearValue('*[id="search_input"]').pause(2000)
@ -57,7 +58,7 @@ module.exports = {
Array.isArray(res.value) && browser.assert.equal(res.value.length, 4) Array.isArray(res.value) && browser.assert.equal(res.value.length, 4)
}) })
}, },
'Should find matchcase': function (browser: NightwatchBrowser) { 'Should find matchcase #group1': function (browser: NightwatchBrowser) {
browser browser
.waitForElementVisible('*[data-id="search_use_regex"]').click('*[data-id="search_use_regex"]') .waitForElementVisible('*[data-id="search_use_regex"]').click('*[data-id="search_use_regex"]')
.waitForElementVisible('*[data-id="search_case_sensitive"]').click('*[data-id="search_case_sensitive"]').pause(4000) .waitForElementVisible('*[data-id="search_case_sensitive"]').click('*[data-id="search_case_sensitive"]').pause(4000)
@ -71,7 +72,7 @@ module.exports = {
}) })
.waitForElementContainsText('*[data-id="search_results"]', 'STORAGE.TEST.JS', 60000) .waitForElementContainsText('*[data-id="search_results"]', 'STORAGE.TEST.JS', 60000)
}, },
'Should find matchword': function (browser: NightwatchBrowser) { 'Should find matchword #group1': function (browser: NightwatchBrowser) {
browser browser
.waitForElementVisible('*[data-id="search_case_sensitive"]').click('*[data-id="search_case_sensitive"]') .waitForElementVisible('*[data-id="search_case_sensitive"]').click('*[data-id="search_case_sensitive"]')
.waitForElementVisible('*[data-id="search_whole_word"]').click('*[data-id="search_whole_word"]').pause(2000) .waitForElementVisible('*[data-id="search_whole_word"]').click('*[data-id="search_whole_word"]').pause(2000)
@ -81,7 +82,7 @@ module.exports = {
Array.isArray(res.value) && browser.assert.equal(res.value.length, 15) Array.isArray(res.value) && browser.assert.equal(res.value.length, 15)
}) })
}, },
'Should replace text': function (browser: NightwatchBrowser) { 'Should replace text #group1': function (browser: NightwatchBrowser) {
browser browser
.waitForElementVisible('*[data-id="toggle_replace"]').click('*[data-id="toggle_replace"]') .waitForElementVisible('*[data-id="toggle_replace"]').click('*[data-id="toggle_replace"]')
.waitForElementVisible('*[id="search_replace"]') .waitForElementVisible('*[id="search_replace"]')
@ -95,7 +96,7 @@ module.exports = {
browser.assert.ok(content.includes('replacing deployer for a constructor'), 'should replace text ok') browser.assert.ok(content.includes('replacing deployer for a constructor'), 'should replace text ok')
}) })
}, },
'Should replace text without confirmation': function (browser: NightwatchBrowser) { 'Should replace text without confirmation #group1': function (browser: NightwatchBrowser) {
browser.click('*[data-id="confirm_replace_label"]').pause(500) browser.click('*[data-id="confirm_replace_label"]').pause(500)
.clearValue('*[id="search_input"]') .clearValue('*[id="search_input"]')
.setValue('*[id="search_input"]', 'replacing').sendKeys('*[id="search_input"]', browser.Keys.ENTER).pause(1000) .setValue('*[id="search_input"]', 'replacing').sendKeys('*[id="search_input"]', browser.Keys.ENTER).pause(1000)
@ -108,7 +109,7 @@ module.exports = {
browser.assert.ok(content.includes('replacing2 deployer for a constructor'), 'should replace text ok') browser.assert.ok(content.includes('replacing2 deployer for a constructor'), 'should replace text ok')
}) })
}, },
'Should replace all & undo': function (browser: NightwatchBrowser) { 'Should replace all & undo #group1': function (browser: NightwatchBrowser) {
browser browser
.clearValue('*[id="search_input"]') .clearValue('*[id="search_input"]')
.setValue('*[id="search_input"]', 'storage').sendKeys('*[id="search_input"]', browser.Keys.ENTER) .setValue('*[id="search_input"]', 'storage').sendKeys('*[id="search_input"]', browser.Keys.ENTER)
@ -127,7 +128,7 @@ module.exports = {
browser.assert.ok(content.includes('title Storage'), 'should undo text ok') browser.assert.ok(content.includes('title Storage'), 'should undo text ok')
}) })
}, },
'Should replace all & undo & switch between files': function (browser: NightwatchBrowser) { 'Should replace all & undo & switch between files #group1': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('*[id="search_input"]') browser.waitForElementVisible('*[id="search_input"]')
.clearValue('*[id="search_input"]') .clearValue('*[id="search_input"]')
.setValue('*[id="search_input"]', 'storage').sendKeys('*[id="search_input"]', browser.Keys.ENTER) .setValue('*[id="search_input"]', 'storage').sendKeys('*[id="search_input"]', browser.Keys.ENTER)
@ -163,14 +164,20 @@ module.exports = {
browser.assert.ok(content.includes("Storage' contract"), 'should replace text ok') browser.assert.ok(content.includes("Storage' contract"), 'should replace text ok')
}) })
}, },
'Should hide button when edited content is the same': function (browser: NightwatchBrowser) { 'Should hide button when edited content is the same #group2': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('*[data-id="remixIdeSidePanel"]') browser.waitForElementVisible('*[data-id="remixIdeSidePanel"]')
.addFile('test.sol', { content: '123' }) .addFile('test.sol', { content: '123' })
.click('*[plugin="search"]').waitForElementVisible('*[id="search_input"]') .click('*[plugin="search"]')
.waitForElementVisible('*[id="search_input"]')
.waitForElementVisible('*[data-id="toggle_replace"]')
.click('*[data-id="toggle_replace"]')
.clearValue('*[id="search_input"]') .clearValue('*[id="search_input"]')
.setValue('*[id="search_input"]', '123').sendKeys('*[id="search_input"]', browser.Keys.ENTER) .setValue('*[id="search_input"]', '123')
.sendKeys('*[id="search_input"]', browser.Keys.ENTER)
.waitForElementVisible('*[id="search_replace"]')
.clearValue('*[id="search_replace"]') .clearValue('*[id="search_replace"]')
.setValue('*[id="search_replace"]', '456').pause(1000) .setValue('*[id="search_replace"]', '456').pause(1000)
.click('*[data-id="confirm_replace_label"]').pause(500)
.waitForElementVisible('*[data-id="replace-all-test.sol"]') .waitForElementVisible('*[data-id="replace-all-test.sol"]')
.click('*[data-id="replace-all-test.sol"]').pause(2000) .click('*[data-id="replace-all-test.sol"]').pause(2000)
.getEditorValue((content) => { .getEditorValue((content) => {
@ -181,13 +188,14 @@ module.exports = {
.getEditorValue((content) => { .getEditorValue((content) => {
browser.assert.ok(content.includes('123'), 'should have text ok') browser.assert.ok(content.includes('123'), 'should have text ok')
} }
).pause(1000) ).pause(5000)
.waitForElementNotPresent('*[data-id="undo-replace-test.sol"]') .waitForElementNotPresent('*[data-id="undo-replace-test.sol"]')
}, },
'Should disable/enable button when edited content changed': function (browser: NightwatchBrowser) { 'Should disable/enable button when edited content changed #group2': function (browser: NightwatchBrowser) {
browser browser
.waitForElementVisible('*[id="search_input"]') .waitForElementVisible('*[id="search_input"]')
.clearValue('*[id="search_input"]') .clearValue('*[id="search_input"]')
.clearValue('*[id="search_input"]')
.setValue('*[id="search_input"]', '123').sendKeys('*[id="search_input"]', browser.Keys.ENTER) .setValue('*[id="search_input"]', '123').sendKeys('*[id="search_input"]', browser.Keys.ENTER)
.clearValue('*[id="search_replace"]') .clearValue('*[id="search_replace"]')
.setValue('*[id="search_replace"]', 'replaced').pause(1000) .setValue('*[id="search_replace"]', 'replaced').pause(1000)
@ -201,7 +209,7 @@ module.exports = {
.getEditorValue((content) => { .getEditorValue((content) => {
browser.assert.ok(content.includes('changed'), 'should have text ok') browser.assert.ok(content.includes('changed'), 'should have text ok')
} }
).pause(1000) ).pause(5000)
.waitForElementVisible('*[data-id="undo-replace-test.sol"]') .waitForElementVisible('*[data-id="undo-replace-test.sol"]')
.getAttribute('[data-id="undo-replace-test.sol"]', 'disabled', (result) => { .getAttribute('[data-id="undo-replace-test.sol"]', 'disabled', (result) => {
browser.assert.equal(result.value, 'true', 'should be disabled') browser.assert.equal(result.value, 'true', 'should be disabled')
@ -222,7 +230,7 @@ module.exports = {
.waitForElementNotPresent('*[data-id="undo-replace-test.sol"]') .waitForElementNotPresent('*[data-id="undo-replace-test.sol"]')
}, },
'should clear search': function (browser: NightwatchBrowser) { 'should clear search #group2': function (browser: NightwatchBrowser) {
browser browser
.waitForElementVisible('*[id="search_input"]') .waitForElementVisible('*[id="search_input"]')
.setValue('*[id="search_input"]', 'nodata').sendKeys('*[id="search_input"]', browser.Keys.ENTER).pause(1000) .setValue('*[id="search_input"]', 'nodata').sendKeys('*[id="search_input"]', browser.Keys.ENTER).pause(1000)
@ -230,4 +238,4 @@ module.exports = {
Array.isArray(res.value) && browser.assert.equal(res.value.length, 0) Array.isArray(res.value) && browser.assert.equal(res.value.length, 0)
}) })
} }
} }

@ -7,7 +7,6 @@ module.exports = {
before: function (browser: NightwatchBrowser, done: VoidFunction) { before: function (browser: NightwatchBrowser, done: VoidFunction) {
init(browser, done, 'http://127.0.0.1:8080?plugins=solidity,udapp', false) init(browser, done, 'http://127.0.0.1:8080?plugins=solidity,udapp', false)
}, },
'Should execution a simple console command #group1 #group999': function (browser: NightwatchBrowser) { 'Should execution a simple console command #group1 #group999': function (browser: NightwatchBrowser) {
browser browser
.waitForElementVisible('*[data-id="terminalCli"]', 10000) .waitForElementVisible('*[data-id="terminalCli"]', 10000)
@ -164,10 +163,9 @@ module.exports = {
'Should print hardhat logs #group4': function (browser: NightwatchBrowser) { 'Should print hardhat logs #group4': function (browser: NightwatchBrowser) {
browser browser
.click('*[data-id="terminalClearConsole"]') // clear the terminal .click('*[data-id="terminalClearConsole"]') // clear the terminal
.addFile('printHardhatlog.sol', { content: hardhatLog })
.clickLaunchIcon('solidity')
.waitForElementVisible('[for="autoCompile"]') .waitForElementVisible('[for="autoCompile"]')
.click('[for="autoCompile"]') .click('[for="autoCompile"]')
.clickLaunchIcon('udapp')
.testContracts('printHardhatlog.sol', { content: hardhatLog }, ['OwnerTest']) .testContracts('printHardhatlog.sol', { content: hardhatLog }, ['OwnerTest'])
.clickLaunchIcon('udapp') .clickLaunchIcon('udapp')
.click('*[data-id="deployAndRunClearInstances"]') .click('*[data-id="deployAndRunClearInstances"]')

@ -5,7 +5,39 @@ import init from '../helpers/init'
import examples from '../examples/example-contracts' import examples from '../examples/example-contracts'
const sources = [ const sources = [
{ 'Untitled.sol': { content: examples.ballot.content } } { 'Untitled.sol': { content: examples.ballot.content } },
{
'myTokenV1.sol': {
content: `
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;
import "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
contract MyToken is Initializable, ERC721Upgradeable, OwnableUpgradeable, UUPSUpgradeable {
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
function initialize() initializer public {
__ERC721_init("MyToken", "MTK");
__Ownable_init();
__UUPSUpgradeable_init();
}
function _authorizeUpgrade(address newImplementation)
internal
onlyOwner
override
{}
}
`
}
}
] ]
module.exports = { module.exports = {
@ -76,7 +108,58 @@ module.exports = {
.openFile('ethereum/remix-project/apps/remix-ide/contracts/app/solidity/mode.sol') .openFile('ethereum/remix-project/apps/remix-ide/contracts/app/solidity/mode.sol')
}, },
'Should load using URL compiler params': function (browser: NightwatchBrowser) { 'Should load the code from language & code params': function (browser: NightwatchBrowser) {
browser
.pause(5000)
.url('http://127.0.0.1:8080/#language=yul&version=soljson-v0.8.7+commit.e28d00a7.js&code=Ly8gQSBjb250cmFjdCBjb25zaXN0cyBvZiBhIHNpbmdsZSBvYmplY3Qgd2l0aCBzdWItb2JqZWN0cyByZXByZXNlbnRpbmcKLy8gdGhlIGNvZGUgdG8gYmUgZGVwbG95ZWQgb3Igb3RoZXIgY29udHJhY3RzIGl0IGNhbiBjcmVhdGUuCi8vIFRoZSBzaW5nbGUgImNvZGUiIG5vZGUgaXMgdGhlIGV4ZWN1dGFibGUgY29kZSBvZiB0aGUgb2JqZWN0LgovLyBFdmVyeSAob3RoZXIpIG5hbWVkIG9iamVjdCBvciBkYXRhIHNlY3Rpb24gaXMgc2VyaWFsaXplZCBhbmQKLy8gbWFkZSBhY2Nlc3NpYmxlIHRvIHRoZSBzcGVjaWFsIGJ1aWx0LWluIGZ1bmN0aW9ucyBkYXRhY29weSAvIGRhdGFvZmZzZXQgLyBkYXRhc2l6ZQovLyBUaGUgY3VycmVudCBvYmplY3QsIHN1Yi1vYmplY3RzIGFuZCBkYXRhIGl0ZW1zIGluc2lkZSB0aGUgY3VycmVudCBvYmplY3QKLy8gYXJlIGluIHNjb3BlLgpvYmplY3QgIkNvbnRyYWN0MSIgewogICAgLy8gVGhpcyBpcyB0aGUgY29uc3RydWN0b3IgY29kZSBvZiB0aGUgY29udHJhY3QuCiAgICBjb2RlIHsKICAgICAgICBmdW5jdGlvbiBhbGxvY2F0ZShzaXplKSAtPiBwdHIgewogICAgICAgICAgICBwdHIgOj0gbWxvYWQoMHg0MCkKICAgICAgICAgICAgaWYgaXN6ZXJvKHB0cikgeyBwdHIgOj0gMHg2MCB9CiAgICAgICAgICAgIG1zdG9yZSgweDQwLCBhZGQocHRyLCBzaXplKSkKICAgICAgICB9CgogICAgICAgIC8vIGZpcnN0IGNyZWF0ZSAiQ29udHJhY3QyIgogICAgICAgIGxldCBzaXplIDo9IGRhdGFzaXplKCJDb250cmFjdDIiKQogICAgICAgIGxldCBvZmZzZXQgOj0gYWxsb2NhdGUoc2l6ZSkKICAgICAgICAvLyBUaGlzIHdpbGwgdHVybiBpbnRvIGNvZGVjb3B5IGZvciBFVk0KICAgICAgICBkYXRhY29weShvZmZzZXQsIGRhdGFvZmZzZXQoIkNvbnRyYWN0MiIpLCBzaXplKQogICAgICAgIC8vIGNvbnN0cnVjdG9yIHBhcmFtZXRlciBpcyBhIHNpbmdsZSBudW1iZXIgMHgxMjM0CiAgICAgICAgbXN0b3JlKGFkZChvZmZzZXQsIHNpemUpLCAweDEyMzQpCiAgICAgICAgcG9wKGNyZWF0ZShvZmZzZXQsIGFkZChzaXplLCAzMiksIDApKQoKICAgICAgICAvLyBub3cgcmV0dXJuIHRoZSBydW50aW1lIG9iamVjdCAodGhlIGN1cnJlbnRseQogICAgICAgIC8vIGV4ZWN1dGluZyBjb2RlIGlzIHRoZSBjb25zdHJ1Y3RvciBjb2RlKQogICAgICAgIHNpemUgOj0gZGF0YXNpemUoIkNvbnRyYWN0MV9kZXBsb3llZCIpCiAgICAgICAgb2Zmc2V0IDo9IGFsbG9jYXRlKHNpemUpCiAgICAgICAgLy8gVGhpcyB3aWxsIHR1cm4gaW50byBhIG1lbW9yeS0+bWVtb3J5IGNvcHkgZm9yIEV3YXNtIGFuZAogICAgICAgIC8vIGEgY29kZWNvcHkgZm9yIEVWTQogICAgICAgIGRhdGFjb3B5KG9mZnNldCwgZGF0YW9mZnNldCgiQ29udHJhY3QxX2RlcGxveWVkIiksIHNpemUpCiAgICAgICAgcmV0dXJuKG9mZnNldCwgc2l6ZSkKICAgIH0KCiAgICBkYXRhICJUYWJsZTIiIGhleCI0MTIzIgoKICAgIG9iamVjdCAiQ29udHJhY3QxX2RlcGxveWVkIiB7CiAgICAgICAgY29kZSB7CiAgICAgICAgICAgIGZ1bmN0aW9uIGFsbG9jYXRlKHNpemUpIC0+IHB0ciB7CiAgICAgICAgICAgICAgICBwdHIgOj0gbWxvYWQoMHg0MCkKICAgICAgICAgICAgICAgIGlmIGlzemVybyhwdHIpIHsgcHRyIDo9IDB4NjAgfQogICAgICAgICAgICAgICAgbXN0b3JlKDB4NDAsIGFkZChwdHIsIHNpemUpKQogICAgICAgICAgICB9CgogICAgICAgICAgICAvLyBydW50aW1lIGNvZGUKCiAgICAgICAgICAgIG1zdG9yZSgwLCAiSGVsbG8sIFdvcmxkISIpCiAgICAgICAgICAgIHJldHVybigwLCAweDIwKQogICAgICAgIH0KICAgIH0KCiAgICAvLyBFbWJlZGRlZCBvYmplY3QuIFVzZSBjYXNlIGlzIHRoYXQgdGhlIG91dHNpZGUgaXMgYSBmYWN0b3J5IGNvbnRyYWN0LAogICAgLy8gYW5kIENvbnRyYWN0MiBpcyB0aGUgY29kZSB0byBiZSBjcmVhdGVkIGJ5IHRoZSBmYWN0b3J5CiAgICBvYmplY3QgIkNvbnRyYWN0MiIgewogICAgICAgIGNvZGUgewogICAgICAgICAgICAvLyBjb2RlIGhlcmUgLi4uCiAgICAgICAgfQoKICAgICAgICBvYmplY3QgIkNvbnRyYWN0Ml9kZXBsb3llZCIgewogICAgICAgICAgICBjb2RlIHsKICAgICAgICAgICAgICAgIC8vIGNvZGUgaGVyZSAuLi4KICAgICAgICAgICAgfQogICAgICAgIH0KCiAgICAgICAgZGF0YSAiVGFibGUxIiBoZXgiNDEyMyIKICAgIH0KfQ&optimize=false&runs=200&evmVersion=null')
.refresh()
.pause(5000)
.clickLaunchIcon('filePanel')
.currentWorkspaceIs('code-sample')
.waitForElementVisible('*[data-id="treeViewLitreeViewItemcontract-eaa022e37e.yul"]', 6000)
.openFile('contract-eaa022e37e.yul')
.getEditorValue((content) => {
browser.assert.ok(content && content.indexOf(
'object "Contract1" {') !== -1)
})
},
'Should select deploy with proxy option from URL params': function (browser: NightwatchBrowser) {
browser
.url('http://127.0.0.1:8080/#optimize=false&runs=200&deployProxy=true')
.refresh()
.pause(5000)
.switchWorkspace('default_workspace')
.addFile('myTokenV1.sol', sources[1]['myTokenV1.sol'])
.clickLaunchIcon('solidity')
.pause(2000)
.click('[data-id="compilerContainerCompileBtn"]')
.waitForElementPresent('select[id="compiledContracts"] option[value=MyToken]', 60000)
.clickLaunchIcon('udapp')
.click('select.udapp_contractNames')
.click('select.udapp_contractNames option[value=MyToken]')
.waitForElementPresent('[data-id="contractGUIDeployWithProxyLabel"]')
.expect.element('[data-id="contractGUIDeployWithProxy"]').to.be.selected
},
'Should select upgrade with proxy option from URL params': function (browser: NightwatchBrowser) {
browser
.url('http://127.0.0.1:8080/#optimize=false&runs=200&upgradeProxy=true')
.refresh()
.pause(5000)
.openFile('myTokenV1.sol')
.clickLaunchIcon('solidity')
.pause(2000)
.click('[data-id="compilerContainerCompileBtn"]')
.waitForElementPresent('select[id="compiledContracts"] option[value=MyToken]', 60000)
.clickLaunchIcon('udapp')
.click('select.udapp_contractNames')
.click('select.udapp_contractNames option[value=MyToken]')
.waitForElementPresent('[data-id="contractGUIUpgradeImplementationLabel"]')
.expect.element('[data-id="contractGUIUpgradeImplementation"]').to.be.selected
},
'Should load using various URL compiler params': function (browser: NightwatchBrowser) {
browser browser
.pause(5000) .pause(5000)
.url('http://127.0.0.1:8080/#optimize=true&runs=300&autoCompile=true&evmVersion=istanbul&version=soljson-v0.7.4+commit.3f05b770.js&language=Yul') .url('http://127.0.0.1:8080/#optimize=true&runs=300&autoCompile=true&evmVersion=istanbul&version=soljson-v0.7.4+commit.3f05b770.js&language=Yul')
@ -90,6 +173,16 @@ module.exports = {
.verify.elementPresent('#optimize:checked') .verify.elementPresent('#optimize:checked')
.verify.elementPresent('#autoCompile:checked') .verify.elementPresent('#autoCompile:checked')
.verify.attributeEquals('#runs', 'value', '300') .verify.attributeEquals('#runs', 'value', '300')
.url('http://127.0.0.1:8080/#version=0.8.7')
.refresh()
.pause(5000)
.clickLaunchIcon('solidity')
.assert.containsText('#versionSelector option[data-id="selected"]', '0.8.7+commit.e28d00a7')
.url('http://127.0.0.1:8080/#version=0.8.15+commit.e14f2714')
.refresh()
.pause(5000)
.clickLaunchIcon('solidity')
.assert.containsText('#versionSelector option[data-id="selected"]', '0.8.15+commit.e14f2714')
}, },
'Should load using compiler from link passed in remix URL': function (browser: NightwatchBrowser) { 'Should load using compiler from link passed in remix URL': function (browser: NightwatchBrowser) {

@ -0,0 +1,96 @@
'use strict'
import { NightwatchBrowser } from 'nightwatch'
import init from '../helpers/init'
declare global {
interface Window { testplugin: { name: string, url: string }; }
}
module.exports = {
'@disabled': true,
before: function (browser: NightwatchBrowser, done: VoidFunction) {
init(browser, done, null, true, { name: 'vyper', url: 'http://127.0.0.1:5002'})
},
'Should connect to vyper plugin #group1': function (browser: NightwatchBrowser) {
browser.clickLaunchIcon('pluginManager')
.scrollAndClick('[data-id="pluginManagerComponentActivateButtonvyper"]')
.clickLaunchIcon('vyper')
.pause(5000)
// @ts-ignore
.frame(0)
},
'Should add the Ballot.vy #group1': function (browser: NightwatchBrowser) {
browser.click('button[data-id="add-ballot"]')
.frameParent()
.openFile('ballot.vy')
},
'Compile ballot.vy should error #group1': function (browser: NightwatchBrowser) {
browser.clickLaunchIcon('vyper')
// @ts-ignore
.frame(0)
.click('[data-id="remote-compiler"]')
.click('[data-id="compile"]')
.assert.containsText('[data-id="error-message"]', 'unexpected indent')
},
'Compile test contract should success #group1': function (browser: NightwatchBrowser) {
let contractAddress
browser
.frameParent()
.addFile('test.vy', { content: testContract })
.clickLaunchIcon('vyper')
// @ts-ignore
.frame(0)
.click('[data-id="compile"]')
.frameParent()
.clickLaunchIcon('udapp')
.createContract('')
.clickInstance(0)
.clickFunction('totalPokemonCount - call')
.getAddressAtPosition(0, (address) => {
console.log('Vyper contract ' + address)
contractAddress = address
})
.perform((done) => {
browser.verifyCallReturnValue(contractAddress, ['0:uint256: 0'])
.perform(() => done())
})
}
}
const testContract = `
# @version >=0.2.4 <0.3.0
DNA_DIGITS: constant(uint256) = 16
DNA_MODULUS: constant(uint256) = 10 ** DNA_DIGITS
# add HP_LIMIT
struct Pokemon:
name: String[32]
dna: uint256
HP: uint256
matches: uint256
wins: uint256
totalPokemonCount: public(uint256)
pokemonList: HashMap[uint256, Pokemon]
@pure
@internal
def _generateRandomDNA(_name: String[32]) -> uint256:
random: uint256 = convert(keccak256(_name), uint256)
return random % DNA_MODULUS
# modify _createPokemon
@internal
def _createPokemon(_name: String[32], _dna: uint256, _HP: uint256):
self.pokemonList[self.totalPokemonCount] = Pokemon({
name: _name,
dna: _dna,
HP: _HP,
matches: 0,
wins: 0
})
self.totalPokemonCount += 1`

@ -0,0 +1,25 @@
#!/usr/bin/env bash
set -e
BUILD_ID=${CIRCLE_BUILD_NUM:-${TRAVIS_JOB_NUMBER}}
echo "$BUILD_ID"
TEST_EXITCODE=0
yarn run serve:production &
npx nx serve vyper &
sleep 5
yarn run build:e2e
TESTFILES=$(grep -IRiL "\'@disabled\': \?true" "dist/apps/remix-ide-e2e/src/tests" | grep "vyper_api" | sort | circleci tests split )
for TESTFILE in $TESTFILES; do
npx nightwatch --config dist/apps/remix-ide-e2e/nightwatch.js $TESTFILE --env=chrome || TEST_EXITCODE=1
done
echo "$TEST_EXITCODE"
if [ "$TEST_EXITCODE" -eq 1 ]
then
exit 1
fi

@ -410,6 +410,29 @@ class AppComponent {
this.appManager.call(...callDetails).catch(console.error) this.appManager.call(...callDetails).catch(console.error)
} }
} }
if (params.calls) {
const calls = params.calls.split("///");
// call all functions in the list, one after the other
for (const call of calls) {
const callDetails = call.split("//");
if (callDetails.length > 1) {
this.appManager.call(
"notification",
"toast",
`initiating ${callDetails[0]} ...`
);
// @todo(remove the timeout when activatePlugin is on 0.3.0)
try {
await this.appManager.call(...callDetails)
} catch (e) {
console.error(e)
}
}
}
}
}) })
.catch(console.error) .catch(console.error)
} }

@ -101,7 +101,7 @@ export abstract class AbstractProvider extends Plugin {
reject('Unable to connect') reject('Unable to connect')
} }
}, 2000) }, 2000)
await this.provider.ready await this.provider.detectNetwork() // this throws if the network cannot be detected
this.connected = true this.connected = true
} catch (e) { } catch (e) {
this.switchAway(true) this.switchAway(true)
@ -116,6 +116,7 @@ export abstract class AbstractProvider extends Plugin {
} }
private async switchAway (showError) { private async switchAway (showError) {
if (!this.provider) return
this.provider = null this.provider = null
this.blocked = true this.blocked = true
this.connected = false this.connected = false

@ -146,9 +146,11 @@ export class RemixAppManager extends PluginManager {
} }
} }
} }
return plugins.map(plugin => { const testPluginName = localStorage.getItem('test-plugin-name')
const testPluginUrl = localStorage.getItem('test-plugin-url')
return plugins.map(plugin => {
if (plugin.name === testPluginName) plugin.url = testPluginUrl
return new IframePlugin(plugin) return new IframePlugin(plugin)
// return new IframeReactPlugin(plugin)
}) })
} }

@ -0,0 +1,13 @@
{
"presets": [
[
"@nrwl/react/babel", {
"runtime": "automatic"
}
]
],
"plugins": [
]
}

@ -0,0 +1,16 @@
# This file is used by:
# 1. autoprefixer to adjust CSS to support the below specified browsers
# 2. babel preset-env to adjust included polyfills
#
# For additional information regarding the format and rule options, please see:
# https://github.com/browserslist/browserslist#queries
#
# If you need to support different browsers in production, you may tweak the list below.
last 1 Chrome version
last 1 Firefox version
last 2 Edge major versions
last 2 Safari major version
last 2 iOS major versions
Firefox ESR
not IE 9-11 # For IE 9-11 support, remove 'not'.

@ -0,0 +1,18 @@
{
"env": {
"browser": true,
"es6": true
},
"extends": "../../.eslintrc.json",
"globals": {
"Atomics": "readonly",
"SharedArrayBuffer": "readonly"
},
"parserOptions": {
"ecmaVersion": 11,
"sourceType": "module"
},
"rules": {
"standard/no-callback-literal": "off"
}
}

@ -0,0 +1,124 @@
* {
box-sizing: border-box;
}
html, body, #root, main {
height: 100%;
margin: 0;
}
#vyper-plugin header {
display: flex;
justify-content: space-between;
align-items: center;
height: 50px;
padding: 5px 15px;
}
#vyper-plugin header a,
#vyper-plugin header .title {
display: flex;
height: 60%;
}
#vyper-plugin header svg,
#vyper-plugin header img {
width: auto;
height: 100%;
}
#vyper-plugin section {
display: flex;
flex-direction: column;
align-items: center;
height: calc(100% - 50px);
}
.btn-group-toggle {
width: 90%;
text-transform: uppercase;
margin: 10px 0;
}
.btn-group-toggle .btn {
cursor: pointer;
font-size: 0.8rem;
}
#local-url {
width: 90%;
}
#compile-btn {
width: 90%;
}
#compile-btn * {
width: 100%;
}
#result {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
width: 100%;
padding-top: 10px;
overflow: hidden;
}
#result.error {
justify-content: flex-start;
}
#result.error svg {
width: 40px;
height: 40px;
margin: 10px;
}
#result nav {
width: 100%;
flex-wrap: nowrap;
justify-content: space-evenly;
}
#result nav a {
padding: 0.5rem 1rem;
font-size: 0.8rem;
}
#result .tab-content {
flex: 1;
width: 100%;
padding: 15px;
overflow: auto;
}
#result .tab-pane.active {
height: 100%;
display: flex;
flex-direction: column;
align-items: stretch;
}
#result .copy {
padding: 5x;
font-size: 0.8rem;
margin: 10px;
}
#result p {
text-align: center;
width: 100% !important;
}
#result .react-json-view {
overflow-y: auto;
}
#result textarea {
border: none;
height: 100%;
width: 100%;
}

@ -0,0 +1,118 @@
import React, { useState, useEffect } from 'react'
import { VyperCompilationOutput, remixClient } from './utils'
import { CompilationResult } from '@remixproject/plugin-api'
// Components
import CompilerButton from './components/CompilerButton'
import WarnRemote from './components/WarnRemote'
import VyperResult from './components/VyperResult'
import LocalUrlInput from './components/LocalUrl'
import ToggleButtonGroup from 'react-bootstrap/ToggleButtonGroup'
import ToggleButton from 'react-bootstrap/ToggleButton'
import vyperLogo from './logo.svg'
import './app.css'
interface AppState {
status: 'idle' | 'inProgress'
environment: 'remote' | 'local'
compilationResult?: CompilationResult
localUrl: string
}
interface OutputMap {
[fileName: string]: VyperCompilationOutput
}
const App: React.FC = () => {
const [contract, setContract] = useState<string>()
const [output, setOutput] = useState<OutputMap>({})
const [state, setState] = useState<AppState>({
status: 'idle',
environment: 'local',
localUrl: 'http://localhost:8000/compile'
})
useEffect(() => {
async function start() {
try {
await remixClient.loaded()
remixClient.onFileChange(name => setContract(name))
const name = await remixClient.getContractName()
setContract(name)
} catch (err) {
console.log(err)
}
}
start()
}, [])
/** Update the environment state value */
function setEnvironment(environment: 'local' | 'remote') {
setState({ ...state, environment })
}
function setLocalUrl(url: string) {
setState({ ...state, localUrl: url })
}
function compilerUrl() {
return state.environment === 'remote'
? 'https://vyper.live/compile'
: state.localUrl
}
return (
<main id="vyper-plugin">
<header className="bg-light">
<div className="title">
<img src={vyperLogo} alt="Vyper logo" />
<h4>yper Compiler</h4>
</div>
<a
rel="noopener noreferrer"
href="https://github.com/GrandSchtroumpf/vyper-remix"
target="_blank"
>
<i className="fab fa-github"></i>
</a>
</header>
<section>
<ToggleButtonGroup
name="remote"
onChange={setEnvironment}
type="radio"
value={state.environment}
>
<ToggleButton data-id="remote-compiler" variant="secondary" name="remote" value="remote">
Remote Compiler
</ToggleButton>
<ToggleButton data-id="local-compiler" variant="secondary" name="local" value="local">
Local Compiler
</ToggleButton>
</ToggleButtonGroup>
<LocalUrlInput
url={state.localUrl}
setUrl={setLocalUrl}
environment={state.environment}
/>
<WarnRemote environment={state.environment} />
<div id="compile-btn">
<CompilerButton
compilerUrl={compilerUrl()}
contract={contract}
setOutput={(name, update) =>
setOutput({ ...output, [name]: update })
}
/>
</div>
<article id="result">
<VyperResult output={contract ? output[contract] : undefined} />
</article>
</section>
</main>
)
}
export default App

@ -0,0 +1,75 @@
import React from 'react'
import {
isVyper,
compile,
toStandardOutput,
VyperCompilationOutput,
isCompilationError,
remixClient
} from '../utils'
import Button from 'react-bootstrap/Button'
interface Props {
compilerUrl: string
contract?: string,
setOutput: (name: string, output: VyperCompilationOutput) => void
}
function CompilerButton({ contract, setOutput, compilerUrl }: Props) {
if (!contract || !contract) {
return <Button disabled>No contract selected</Button>
}
if (!isVyper(contract)) {
return <Button disabled>Not a vyper contract</Button>
}
/** Compile a Contract */
async function compileContract() {
try {
const _contract = await remixClient.getContract()
remixClient.changeStatus({
key: 'loading',
type: 'info',
title: 'Compiling'
})
const output = await compile(compilerUrl, _contract)
setOutput(_contract.name, output)
// ERROR
if (isCompilationError(output)) {
const line = output.line
const lineColumnPos = {
start: { line: line - 1 },
end: { line: line - 1 }
}
remixClient.highlight(lineColumnPos as any, _contract.name, '#e0b4b4')
throw new Error(output.message)
}
// SUCCESS
remixClient.discardHighlight()
remixClient.changeStatus({
key: 'succeed',
type: 'success',
title: 'succeed'
})
const data = toStandardOutput(_contract.name, output)
remixClient.compilationFinish(_contract.name, _contract.content, data)
} catch (err: any) {
remixClient.changeStatus({
key: 'failed',
type: 'error',
title: err.message
})
console.error(err)
}
}
return (
<Button data-id="compile" onClick={compileContract} variant="primary">
Compile {contract}
</Button>
)
}
export default CompilerButton

@ -0,0 +1,36 @@
import React from 'react'
import Form from 'react-bootstrap/Form'
interface Props {
url: string
setUrl: (url: string) => void,
environment: 'remote' | 'local'
}
function LocalUrlInput({ url, setUrl, environment }: Props) {
if (environment === 'remote') {
return <></>
}
function updateUrl(event: React.FocusEvent<HTMLInputElement>) {
setUrl(event.target.value)
}
return (
<Form id="local-url">
<Form.Group controlId="localUrl">
<Form.Label>Local Compiler Url</Form.Label>
<Form.Control onBlur={updateUrl}
defaultValue={url}
type="email"
placeholder="eg http://localhost:8000/compile" />
<Form.Text className="text-muted">
The url to your local compiler
</Form.Text>
</Form.Group>
</Form>
)
}
export default LocalUrlInput;

@ -0,0 +1,70 @@
import React, { useState } from 'react';
import {
VyperCompilationResult,
VyperCompilationOutput,
isCompilationError,
remixClient
} from '../utils';
import Tabs from 'react-bootstrap/Tabs'
import Tab from 'react-bootstrap/Tab'
import { Ballot } from '../examples/ballot';
import Button from 'react-bootstrap/Button';
import JSONTree from 'react-json-view'
import { CopyToClipboard } from '@remix-ui/clipboard'
interface VyperResultProps {
output?: VyperCompilationOutput;
}
function VyperResult({ output }: VyperResultProps) {
const [ active, setActive ] = useState<keyof VyperCompilationResult>('abi');
if (!output) return (
<div id="result">
<p>No contract compiled yet.</p>
<Button data-id="add-ballot" variant="info" onClick={() => remixClient.loadContract(Ballot)}>
Create Ballot.vy example
</Button>
</div>
)
if (isCompilationError(output)) {
return (
<div id="result" className="error">
<i className="fas fa-exclamation-circle text-danger"></i>
<p data-id="error-message" className="alert alert-danger">{output.message}</p>
</div>)
}
return (
<Tabs id="result" activeKey={active} onSelect={(key: any) => setActive(key)}>
<Tab eventKey="abi" title="ABI">
<CopyToClipboard getContent={() => JSON.stringify(output.abi)}>
<Button variant="info" className="copy">Copy ABI</Button>
</CopyToClipboard>
<JSONTree src={output.abi} />
</Tab>
<Tab eventKey="bytecode" title="Bytecode">
<CopyToClipboard getContent={() => output.bytecode}>
<Button variant="info" className="copy">Copy Bytecode</Button>
</CopyToClipboard>
<textarea defaultValue={output.bytecode}></textarea>
</Tab>
<Tab eventKey="bytecode_runtime" title="Runtime Bytecode">
<CopyToClipboard getContent={() => output.bytecode_runtime}>
<Button variant="info" className="copy">Copy Runtime Bytecode</Button>
</CopyToClipboard>
<textarea defaultValue={output.bytecode_runtime}></textarea>
</Tab>
<Tab eventKey="ir" title="LLL">
<CopyToClipboard getContent={() => output.ir}>
<Button variant="info" className="copy">Copy LLL Code</Button>
</CopyToClipboard>
<textarea defaultValue={output.ir}></textarea>
</Tab>
</Tabs>
);
}
export default VyperResult;

@ -0,0 +1,19 @@
import React from 'react'
interface Props {
environment: 'remote' | 'local'
}
function WarnRemoteLabel({ environment }: Props) {
if (environment === 'local') {
return <></>
}
return (
<div className="alert alert-warning">It is really important to not use the remote compiler for production environment.
Please only use it for testing purpose and prefer to using a local compiler for production like environment.</div>
)
}
export default WarnRemoteLabel;

@ -0,0 +1,161 @@
export const Ballot = {
name: 'browser/ballot.vy',
content: `# Voting with delegation.
# Information about voters
struct Voter:
# weight is accumulated by delegation
weight: int128
# if true, that person already voted (which includes voting by delegating)
voted: bool
# person delegated to
delegate: address
# index of the voted proposal, which is not meaningful unless 'voted' is True.
vote: int128
# Users can create proposals
struct Proposal:
# short name (up to 32 bytes)
name: bytes32
# number of accumulated votes
voteCount: int128
voters: public(map(address, Voter))
proposals: public(map(int128, Proposal))
voterCount: public(int128)
chairperson: public(address)
int128Proposals: public(int128)
@public
@constant
def delegated(addr: address) -> bool:
return self.voters[addr].delegate != ZERO_ADDRESS
@public
@constant
def directlyVoted(addr: address) -> bool:
return self.voters[addr].voted and (self.voters[addr].delegate == ZERO_ADDRESS)
# Setup global variables
@public
def __init__(_proposalNames: bytes32[2]):
self.chairperson = msg.sender
self.voterCount = 0
for i in range(2):
self.proposals[i] = Proposal({
name: _proposalNames[i],
voteCount: 0
})
self.int128Proposals += 1
# Give a 'voter' the right to vote on this ballot.
# This may only be called by the 'chairperson'.
@public
def giveRightToVote(voter: address):
# Throws if the sender is not the chairperson.
assert msg.sender == self.chairperson
# Throws if the voter has already voted.
assert not self.voters[voter].voted
# Throws if the voter's voting weight isn't 0.
assert self.voters[voter].weight == 0
self.voters[voter].weight = 1
self.voterCount += 1
# Used by 'delegate' below, and can be called by anyone.
@public
def forwardWeight(delegate_with_weight_to_forward: address):
assert self.delegated(delegate_with_weight_to_forward)
# Throw if there is nothing to do:
assert self.voters[delegate_with_weight_to_forward].weight > 0
target: address = self.voters[delegate_with_weight_to_forward].delegate
for i in range(4):
if self.delegated(target):
target = self.voters[target].delegate
# The following effectively detects cycles of length <= 5,
# in which the delegation is given back to the delegator.
# This could be done for any int128ber of loops,
# or even infinitely with a while loop.
# However, cycles aren't actually problematic for correctness;
# they just result in spoiled votes.
# So, in the production version, this should instead be
# the responsibility of the contract's client, and this
# check should be removed.
assert target != delegate_with_weight_to_forward
else:
# Weight will be moved to someone who directly voted or
# hasn't voted.
break
weight_to_forward: int128 = self.voters[delegate_with_weight_to_forward].weight
self.voters[delegate_with_weight_to_forward].weight = 0
self.voters[target].weight += weight_to_forward
if self.directlyVoted(target):
self.proposals[self.voters[target].vote].voteCount += weight_to_forward
self.voters[target].weight = 0
# To reiterate: if target is also a delegate, this function will need
# to be called again, similarly to as above.
# Delegate your vote to the voter 'to'.
@public
def delegate(to: address):
# Throws if the sender has already voted
assert not self.voters[msg.sender].voted
# Throws if the sender tries to delegate their vote to themselves or to
# the default address value of 0x0000000000000000000000000000000000000000
# (the latter might not be problematic, but I don't want to think about it).
assert to != msg.sender
assert to != ZERO_ADDRESS
self.voters[msg.sender].voted = True
self.voters[msg.sender].delegate = to
# This call will throw if and only if this delegation would cause a loop
# of length <= 5 that ends up delegating back to the delegator.
self.forwardWeight(msg.sender)
# Give your vote (including votes delegated to you)
# to proposal 'proposals[proposal].name'.
@public
def vote(proposal: int128):
# can't vote twice
assert not self.voters[msg.sender].voted
# can only vote on legitimate proposals
assert proposal < self.int128Proposals
self.voters[msg.sender].vote = proposal
self.voters[msg.sender].voted = True
# transfer msg.sender's weight to proposal
self.proposals[proposal].voteCount += self.voters[msg.sender].weight
self.voters[msg.sender].weight = 0
# Computes the winning proposal taking all
# previous votes into account.
@public
@constant
def winningProposal() -> int128:
winning_vote_count: int128 = 0
winning_proposal: int128 = 0
for i in range(2):
if self.proposals[i].voteCount > winning_vote_count:
winning_vote_count = self.proposals[i].voteCount
winning_proposal = i
return winning_proposal
# Calls winningProposal() function to get the index
# of the winner contained in the proposals array and then
# returns the name of the winner
@public
@constant
def winnerName() -> bytes32:
return self.proposals[self.winningProposal()].name
`
}

@ -0,0 +1,14 @@
<svg id="Flat_Logo" data-name="Flat Logo" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2048 1773.62">
<title>vyper-logo-flat</title>
/*add logo description for screen readers*/
<desc>The vyper logo, consists of a letter V superimposed on the letter A, in shades of gray, black and white.</desc>
<polyline points="1024 886.81 768 1330.22 1024 1773.62 1280 1330.22 1024 886.81" style="fill:#333"/>
<polyline points="1280 443.41 1024 886.81 1280 1330.22 1536 886.81 1280 443.41" style="fill:#666"/>
<polyline points="768 443.41 512 886.81 768 1330.22 1024 886.81 768 443.41" style="fill:#666"/>
<polyline points="1536 0 1280 443.41 1536 886.81 1792 443.41 1536 0" style="fill:#8c8c8c"/>
<polyline points="1152 221.7 896 221.7 768 443.41 1024 886.81 1280 443.41 1152 221.7" style="fill:#8c8c8c"/>
<polyline points="512 0 256 443.41 512 886.81 768 443.41 512 0" style="fill:#8c8c8c"/>
<polyline points="2048 0 1536 0 1792 443.4 2048 0" style="fill:#b2b2b2"/>
<polyline points="512 0 0 0 256 443.4 512 0" style="fill:#b2b2b2"/>
/*Author: @yudetamago, last update: @evertonmelo*/
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg
className="material-icons"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
viewBox="0 0 24 24"
>
<path d="M0 0h24v24H0z" fill="none" />
<path d="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-.61L12 2 9.19 8.63 2 9.24l5.46 4.73L5.82 21z" />
</svg>

After

Width:  |  Height:  |  Size: 347 B

@ -0,0 +1,139 @@
import { CompilationResult, ABIDescription } from "@remixproject/plugin-api";
export interface Contract {
name: string;
content: string;
}
export interface VyperCompilationResult {
status: 'success',
bytecode: string,
bytecode_runtime: string,
abi: ABIDescription[],
ir: string,
method_identifiers: {
[method: string]: string
}
}
export interface VyperCompilationError {
status: 'failed'
column: number
line: number
message: string
}
export type VyperCompilationOutput = VyperCompilationResult | VyperCompilationError
/** Check if the output is an error */
export function isCompilationError(output: VyperCompilationOutput): output is VyperCompilationError {
return output.status === 'failed'
}
/**
* Compile the a contract
* @param url The url of the compiler
* @param contract The name and content of the contract
*/
export async function compile(url: string, contract: Contract): Promise<VyperCompilationOutput> {
if (!contract.name) {
throw new Error('Set your Vyper contract file.')
}
const extension = contract.name.split('.')[1]
if (extension !== 'vy') {
throw new Error('Use extension .vy for Vyper.')
}
const response = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ code: contract.content })
})
if (response.status === 404) {
throw new Error(`Vyper compiler not found at "${url}".`)
}
/*if (response.status === 400) {
throw new Error(`Vyper compilation failed: ${response.statusText}`)
}*/
return response.json()
}
/**
* Transform Vyper Output to Solidity-like Compiler output
* @param name Name of the contract file
* @param compilationResult Result returned by the compiler
*/
export function toStandardOutput(fileName: string, compilationResult: VyperCompilationResult): CompilationResult {
const contractName = fileName.split('/').slice(-1)[0].split('.')[0];
const methodIdentifiers = JSON.parse(JSON.stringify(compilationResult['method_identifiers']).replace(/0x/g,''));
return {
sources: {
[fileName]: {
id: 1,
ast: {} as any,
legacyAST: {} as any
}
},
contracts: {
[fileName]: {
// If the language used has no contract names, this field should equal to an empty string
[contractName]: {
// The Ethereum Contract ABI. If empty, it is represented as an empty array.
// See https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI
abi: compilationResult['abi'],
evm: {
bytecode: {
linkReferences: {},
object: compilationResult['bytecode'].replace('0x',''),
opcodes: ""
},
deployedBytecode: {
linkReferences: {},
object: compilationResult['bytecode_runtime'].replace('0x',''),
opcodes: ""
},
methodIdentifiers: methodIdentifiers
}
}
} as any
}
};
}
/*
export function createCompilationResultMessage(name: string, result: any) {
if(result.status == 'success') {
return {
bytecode: this.state.compilationResult['bytecode'],
bytecode_runtime: this.state.compilationResult['bytecode_runtime'],
abi: JSON.stringify(this.state.compilationResult['abi'], null , "\t"),
ir: this.state.compilationResult['ir']
}
} else if(result.status == 'failed' && result.column && result.line) {
const header = `${name}:${result.line}:${result.column}`
const body = this.state.compilationResult.message.split(/\r\n|\r|\n/)
const arr = [header].concat(body).join("\n")
return {
bytecode: arr,
bytecode_runtime: arr,
abi: arr,
ir: arr
}
} else if(result.status == 'failed') {
const message = this.state.compilationResult.message
return {
bytecode: message,
bytecode_runtime: message,
abi: message,
ir: message
}
}
return {
bytecode: "",
bytecode_runtime: "",
abi: "",
ir: ""
}
}
*/

@ -0,0 +1,12 @@
export * from './compiler'
export * from './remix-client'
export function contractName(fileName: string): string {
const parts = fileName.split('/')
return parts[parts.length - 1]
}
export function isVyper(name: string): boolean {
const parts = name.split('.')
return parts[parts.length - 1] === 'vy'
}

@ -0,0 +1,71 @@
import { HighlightPosition, CompilationResult, RemixApi } from '@remixproject/plugin-api';
import { Api, Status } from '@remixproject/plugin-utils';
import { createClient } from '@remixproject/plugin-webview'
import { PluginClient } from '@remixproject/plugin';
import { Contract } from './compiler';
export class RemixClient extends PluginClient {
private client = createClient<Api, Readonly<RemixApi>>(this);
loaded() {
return this.client.onload()
}
/** Emit an event when file changed */
async onFileChange(cb: (contract: string) => any) {
this.client.on('fileManager', 'currentFileChanged', async (name: string) => {
if (!name) return
cb(name)
})
}
/** Load Ballot contract example into the file manager */
async loadContract({name, content}: Contract) {
try {
await this.client.call('fileManager', 'setFile', name, content)
await this.client.call('fileManager', 'switchFile', name)
} catch (err) {
console.log(err)
}
}
/** Update the status of the plugin in remix */
changeStatus(status: Status) {
this.client.emit('statusChanged', status);
}
/** Highlight a part of the editor */
highlight(lineColumnPos: HighlightPosition, name: string, color: string) {
return this.client.call('editor', 'highlight', lineColumnPos, name, color)
}
/** Remove current Hightlight */
discardHighlight() {
return this.client.call('editor', 'discardHighlight')
}
/** Get the name of the current contract */
async getContractName(): Promise<string> {
await this.client.onload()
return this.client.call('fileManager', 'getCurrentFile')
}
/** Get the current contract file */
async getContract(): Promise<Contract> {
const name = await this.getContractName()
if (!name) throw new Error('No contract selected yet')
const content = await this.client.call('fileManager', 'getFile', name)
return {
name,
content,
}
}
/** Emit an event to Remix with compilation result */
compilationFinish(title: string, content: string, data: CompilationResult) {
this.client.emit('compilationFinished', title, content, 'vyper', data);
}
}
export const remixClient = new RemixClient()
// export const RemixClientContext = React.createContext(new RemixClient())

@ -0,0 +1,3 @@
export const environment = {
production: true
};

@ -0,0 +1,6 @@
// This file can be replaced during build by using the `fileReplacements` array.
// When building for production, this file is replaced with `environment.prod.ts`.
export const environment = {
production: false
};

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

@ -0,0 +1,14 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Vyper</title>
<base href="/" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" type="image/x-icon" href="favicon.ico" />
</head>
<body>
<div id="root"></div>
</body>
</html>

@ -0,0 +1,7 @@
import { StrictMode } from 'react';
import * as ReactDOM from 'react-dom';
import App from './app/app';
ReactDOM.render(<StrictMode><App /></StrictMode>, document.getElementById('root'));

@ -0,0 +1,7 @@
/**
* Polyfill stable language features. These imports will be optimized by `@babel/preset-env`.
*
* See: https://github.com/zloirock/core-js#babel
*/
import 'core-js/stable';
import 'regenerator-runtime/runtime';

@ -0,0 +1 @@
/* You can add global styles to this file, and also import other style files */

@ -0,0 +1,14 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "../../dist/out-tsc",
"types": ["node"]
},
"files": [
"../../node_modules/@nrwl/react/typings/cssmodule.d.ts",
"../../node_modules/@nrwl/react/typings/image.d.ts"
],
"exclude": ["**/*.spec.ts", "**/*.spec.tsx"],
"include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"]
}

@ -0,0 +1,20 @@
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"jsx": "react-jsx",
"allowJs": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true
},
"files": [],
"include": [],
"references": [
{
"path": "./tsconfig.app.json"
}
]
}

@ -87,7 +87,7 @@ export class CompilerMetadata extends Plugin {
let parsedMetadata let parsedMetadata
try { try {
parsedMetadata = JSON.parse(contract.object.metadata) parsedMetadata = contract.object && contract.object.metadata ? JSON.parse(contract.object.metadata) : null
} catch (e) { } catch (e) {
console.log(e) console.log(e)
} }

@ -102,4 +102,6 @@ export const UUPSupgradeAbi = {
"outputs": [], "outputs": [],
"stateMutability": "nonpayable", "stateMutability": "nonpayable",
"type": "function" "type": "function"
} }
export const EnableProxyURLParam = 'deployProxy'
export const EnableUpgradeURLParam = 'upgradeProxy'

@ -92,7 +92,7 @@ export class EditorContextListener extends Plugin {
this._stopHighlighting() this._stopHighlighting()
this.currentPosition = cursorPosition this.currentPosition = cursorPosition
this.currentFile = file this.currentFile = file
if (compilationResult && compilationResult.data && compilationResult.data.sources[file]) { if (compilationResult && compilationResult.data && compilationResult.data.sources && compilationResult.data.sources[file]) {
const nodes = sourceMappingDecoder.nodesAtPosition(null, cursorPosition, compilationResult.data.sources[file]) const nodes = sourceMappingDecoder.nodesAtPosition(null, cursorPosition, compilationResult.data.sources[file])
this.nodes = nodes this.nodes = nodes
if (nodes && nodes.length && nodes[nodes.length - 1]) { if (nodes && nodes.length && nodes[nodes.length - 1]) {

@ -1,6 +1,6 @@
import { Plugin } from '@remixproject/engine' import { Plugin } from '@remixproject/engine'
import { ContractAST, ContractSources, DeployOptions } from '../types/contract' import { ContractAST, ContractSources, DeployOptions } from '../types/contract'
import { UUPS, UUPSABI, UUPSBytecode, UUPSfunAbi, UUPSupgradeAbi } from './constants/uups' import { EnableProxyURLParam, EnableUpgradeURLParam, UUPS, UUPSABI, UUPSBytecode, UUPSfunAbi, UUPSupgradeAbi } from './constants/uups'
const proxyProfile = { const proxyProfile = {
name: 'openzeppelin-proxy', name: 'openzeppelin-proxy',
@ -33,31 +33,40 @@ export class OpenZeppelinProxy extends Plugin {
async getProxyOptions (data: ContractSources, file: string): Promise<{ [name: string]: DeployOptions }> { async getProxyOptions (data: ContractSources, file: string): Promise<{ [name: string]: DeployOptions }> {
const contracts = data.contracts[file] const contracts = data.contracts[file]
const ast = data.sources[file].ast const ast = data.sources[file].ast
const inputs = {}
if (this.kind === 'UUPS') { if (this.kind === 'UUPS') {
Object.keys(contracts).map(name => { const options = await (this.getUUPSContractOptions(contracts, ast, file))
if (ast) {
const UUPSSymbol = ast.exportedSymbols[UUPS] ? ast.exportedSymbols[UUPS][0] : null return options
}
ast.absolutePath === file && ast.nodes.map((node) => { }
if (node.name === name && node.linearizedBaseContracts.includes(UUPSSymbol)) {
const abi = contracts[name].abi async getUUPSContractOptions (contracts, ast, file) {
const initializeInput = abi.find(node => node.name === 'initialize') const options = {}
inputs[name] = { await Promise.all(Object.keys(contracts).map(async (name) => {
options: [{ title: 'Deploy with Proxy', active: false }, { title: 'Upgrade with Proxy', active: false }], if (ast) {
initializeOptions: { const UUPSSymbol = ast.exportedSymbols[UUPS] ? ast.exportedSymbols[UUPS][0] : null
inputs: initializeInput,
initializeInputs: initializeInput ? this.blockchain.getInputs(initializeInput) : null await Promise.all(ast.absolutePath === file && ast.nodes.map(async (node) => {
} if (node.name === name && node.linearizedBaseContracts.includes(UUPSSymbol)) {
const abi = contracts[name].abi
const initializeInput = abi.find(node => node.name === 'initialize')
const isDeployWithProxyEnabled: boolean = await this.call('config', 'getAppParameter', EnableProxyURLParam) || false
const isDeployWithUpgradeEnabled: boolean = await this.call('config', 'getAppParameter', EnableUpgradeURLParam) || false
options[name] = {
options: [{ title: 'Deploy with Proxy', active: isDeployWithProxyEnabled }, { title: 'Upgrade with Proxy', active: isDeployWithUpgradeEnabled }],
initializeOptions: {
inputs: initializeInput,
initializeInputs: initializeInput ? this.blockchain.getInputs(initializeInput) : null
} }
} }
}) }
} }))
}) }
} }))
return inputs return options
} }
async executeUUPSProxy(implAddress: string, args: string | string [] = '', initializeABI, implementationContractObject): Promise<void> { async executeUUPSProxy(implAddress: string, args: string | string [] = '', initializeABI, implementationContractObject): Promise<void> {

@ -42,6 +42,7 @@
"@remix-project/remix-lib": "^0.5.15", "@remix-project/remix-lib": "^0.5.15",
"@remix-project/remix-simulator": "^0.2.15", "@remix-project/remix-simulator": "^0.2.15",
"@remix-project/remix-solidity": "^0.5.1", "@remix-project/remix-solidity": "^0.5.1",
"@remix-project/remix-url-resolver": "^0.0.36",
"ansi-gray": "^0.1.1", "ansi-gray": "^0.1.1",
"async": "^2.6.0", "async": "^2.6.0",
"axios": ">=0.21.1", "axios": ">=0.21.1",

@ -4,6 +4,7 @@ import path from 'path'
import deepequal from 'deep-equal' import deepequal from 'deep-equal'
import Log from './logger' import Log from './logger'
import { Compiler as RemixCompiler } from '@remix-project/remix-solidity' import { Compiler as RemixCompiler } from '@remix-project/remix-solidity'
import { RemixURLResolver } from '@remix-project/remix-url-resolver'
import { SrcIfc, CompilerConfiguration, CompilationErrors } from './types' import { SrcIfc, CompilerConfiguration, CompilationErrors } from './types'
const logger = new Log() const logger = new Log()
const log = logger.logger const log = logger.logger
@ -85,6 +86,17 @@ export function compileFileOrFiles (filename: string, isDirectory: boolean, opts
'remix_accounts.sol': { content: writeTestAccountsContract(accounts) } 'remix_accounts.sol': { content: writeTestAccountsContract(accounts) }
} }
const filepath: string = (isDirectory ? filename : path.dirname(filename)) const filepath: string = (isDirectory ? filename : path.dirname(filename))
const importsCallback = (url, cb) => {
try {
if(fs.existsSync(url)) cb(null, fs.readFileSync(url, 'utf-8'))
else {
const urlResolver = new RemixURLResolver()
urlResolver.resolve(url).then((result) => cb(null, result.content)).catch((error) => cb(error.message))
}
} catch (e) {
cb(e.message)
}
}
try { try {
if (!isDirectory && fs.existsSync(filename)) { if (!isDirectory && fs.existsSync(filename)) {
if (filename.split('.').pop() === 'sol') { if (filename.split('.').pop() === 'sol') {
@ -114,13 +126,7 @@ export function compileFileOrFiles (filename: string, isDirectory: boolean, opts
} finally { } finally {
async.waterfall([ async.waterfall([
function loadCompiler (next) { function loadCompiler (next) {
compiler = new RemixCompiler((url, cb) => { compiler = new RemixCompiler(importsCallback)
try {
cb(null, fs.readFileSync(url, 'utf-8'))
} catch (e) {
cb(e.message)
}
})
if (compilerConfig) { if (compilerConfig) {
const { currentCompilerUrl, evmVersion, optimize, runs } = compilerConfig const { currentCompilerUrl, evmVersion, optimize, runs } = compilerConfig
if (evmVersion) compiler.set('evmVersion', evmVersion) if (evmVersion) compiler.set('evmVersion', evmVersion)

@ -4,7 +4,7 @@ import path from 'path'
import axios, { AxiosResponse } from 'axios' import axios, { AxiosResponse } from 'axios'
import { runTestFiles } from './runTestFiles' import { runTestFiles } from './runTestFiles'
import fs from './fileSystem' import fs from './fileSystem'
import { Provider } from '@remix-project/remix-simulator' import { Provider, extend } from '@remix-project/remix-simulator'
import { CompilerConfiguration } from './types' import { CompilerConfiguration } from './types'
import Log from './logger' import Log from './logger'
import colors from 'colors' import colors from 'colors'
@ -68,7 +68,7 @@ commander
} }
// Console message // Console message
console.log(colors.white('\n\t👁\t:: Running remix-tests - Unit testing for solidity ::\t👁\n')) console.log(colors.bold('\n\t👁\t:: Running tests using remix-tests ::\t👁\n'))
// Set logger verbosity // Set logger verbosity
if (commander.verbose) { if (commander.verbose) {
@ -115,7 +115,7 @@ commander
const provider: any = new Provider() const provider: any = new Provider()
await provider.init() await provider.init()
web3.setProvider(provider) web3.setProvider(provider)
extend(web3)
runTestFiles(path.resolve(testsPath), isDirectory, web3, compilerConfig) runTestFiles(path.resolve(testsPath), isDirectory, web3, compilerConfig)
}) })

@ -4,7 +4,7 @@ import { runTest } from './testRunner'
import { TestResultInterface, ResultsInterface, CompilerConfiguration, compilationInterface, ASTInterface, Options, AstNode } from './types' import { TestResultInterface, ResultsInterface, CompilerConfiguration, compilationInterface, ASTInterface, Options, AstNode } from './types'
import colors from 'colors' import colors from 'colors'
import Web3 from 'web3' import Web3 from 'web3'
import { format } from 'util'
import { compileFileOrFiles } from './compiler' import { compileFileOrFiles } from './compiler'
import { deployAll } from './deployer' import { deployAll } from './deployer'
@ -22,6 +22,15 @@ export function runTestFiles (filepath: string, isDirectory: boolean, web3: Web3
opts = opts || {} opts = opts || {}
compilerConfig = compilerConfig || {} as CompilerConfiguration compilerConfig = compilerConfig || {} as CompilerConfiguration
const sourceASTs: any = {} const sourceASTs: any = {}
const printLog = (log: string[]) => {
let formattedLog
if (typeof log[0] === 'string' && (log[0].includes('%s') || log[0].includes('%d'))) {
formattedLog = format(log[0], ...log.slice(1))
} else {
formattedLog = log.join(' ')
}
signale.log(formattedLog)
}
const { Signale } = require('signale') // eslint-disable-line const { Signale } = require('signale') // eslint-disable-line
// signale configuration // signale configuration
const options = { const options = {
@ -34,6 +43,11 @@ export function runTestFiles (filepath: string, isDirectory: boolean, web3: Web3
name: { name: {
badge: '\n\t◼', badge: '\n\t◼',
label: '', label: '',
color: 'whiteBright'
},
log: {
badge: '\t',
label: '',
color: 'white' color: 'white'
}, },
error: { error: {
@ -104,17 +118,24 @@ export function runTestFiles (filepath: string, isDirectory: boolean, web3: Web3
let totalPassing = 0 let totalPassing = 0
let totalFailing = 0 let totalFailing = 0
let totalTime = 0 let totalTime = 0
const errors: any[] = []
const _testCallback = function (err: Error | null | undefined, result: TestResultInterface) { const _testCallback = function (err: Error | null | undefined, result: TestResultInterface) {
if (err) throw err if (err) throw err
if (result.type === 'contract') { if (result.type === 'contract') {
signale.name(result.value.white) signale.name(result.value)
console.log('\n')
} else if (result.type === 'testPass') { } else if (result.type === 'testPass') {
signale.result(result.value) if (result?.hhLogs?.length) result.hhLogs.forEach(printLog)
signale.result(result.value.white)
} else if (result.type === 'testFailure') { } else if (result.type === 'testFailure') {
signale.error(result.value.red) if (result?.hhLogs?.length) result.hhLogs.forEach(printLog)
errors.push(result) signale.error(result.value.white)
if (result.assertMethod) {
console.log(colors.green('\t Expected value should be ' + result.assertMethod + ' to: ' + result.expected))
console.log(colors.red('\t Received: ' + result.returned))
}
console.log(colors.red('\t Message: ' + result.errMsg))
console.log('\n')
} }
} }
const _resultsCallback = (_err: Error | null | undefined, result: ResultsInterface, cb) => { const _resultsCallback = (_err: Error | null | undefined, result: ResultsInterface, cb) => {
@ -127,7 +148,7 @@ export function runTestFiles (filepath: string, isDirectory: boolean, web3: Web3
async.eachOfLimit(contractsToTest, 1, (contractName: string, index, cb) => { async.eachOfLimit(contractsToTest, 1, (contractName: string, index, cb) => {
try { try {
const fileAST: AstNode = sourceASTs[contracts[contractName]['filename']] const fileAST: AstNode = sourceASTs[contracts[contractName]['filename']]
runTest(contractName, contracts[contractName], contractsToTestDetails[index], fileAST, { accounts }, _testCallback, (err, result) => { runTest(contractName, contracts[contractName], contractsToTestDetails[index], fileAST, { accounts, web3 }, _testCallback, (err, result) => {
if (err) { if (err) {
console.log(err) console.log(err)
return cb(err) return cb(err)
@ -141,23 +162,16 @@ export function runTestFiles (filepath: string, isDirectory: boolean, web3: Web3
if (err) { if (err) {
return next(err) return next(err)
} }
console.log('\n') console.log('\n')
if (totalPassing > 0) { console.log(colors.bold.underline('Tests Summary: '))
console.log(colors.green(totalPassing + ' passing ') + colors.grey('(' + totalTime + 's)'))
if (totalPassing >= 0) {
console.log(colors.green('Passed: ' + totalPassing))
} }
if (totalFailing > 0) { if (totalFailing >= 0) {
console.log(colors.red(totalFailing + ' failing')) console.log(colors.red('Failed: ' + totalFailing))
} }
console.log('') console.log(colors.white('Time Taken: ' + totalTime + 's'))
errors.forEach((error, index) => {
console.log(' ' + (index + 1) + ') ' + colors.bold(error.context + ': ') + error.value)
console.log('')
console.log(colors.red('\t error: ' + error.errMsg))
console.log(colors.green('\t expected value to be ' + error.assertMethod + ' to: ' + error.expected))
console.log(colors.red('\t returned: ' + error.returned))
})
console.log('') console.log('')
next() next()

@ -1,7 +1,5 @@
import "remix_tests.sol"; // this import is automatically injected by Remix. import "remix_tests.sol"; // this import is automatically injected by Remix.
import "hardhat/console.sol";
import "./hardhat/console.sol";
contract AssertOkTest { contract AssertOkTest {

@ -1,12 +0,0 @@
import "remix_tests.sol"; // this import is automatically injected by Remix.
contract AssertOkTest {
function okPassTest() public {
Assert.ok(true, "okPassTest passes");
}
function okFailTest() public {
Assert.ok(false, "okFailTest fails");
}
}

File diff suppressed because it is too large Load Diff

@ -42,101 +42,103 @@ Commands:
}) })
test('remix-tests running a test file', () => { test('remix-tests running a test file', () => {
const res = spawnSync(executablePath, [resolve(__dirname + '/examples_0/assert_ok_without_console_test.sol')]) const res = spawnSync(executablePath, [resolve(__dirname + '/examples_0/assert_ok_test.sol')])
// match initial lines // match initial lines
expect(res.stdout.toString().trim()).toMatch(/:: Running remix-tests - Unit testing for solidity ::/) expect(res.stdout.toString().trim()).toMatch(/:: Running tests using remix-tests ::/)
expect(res.stdout.toString().trim()).toMatch(/creation of library remix_tests.sol:Assert pending.../) expect(res.stdout.toString().trim()).toMatch(/creation of library remix_tests.sol:Assert pending.../)
// match test result // match test result
expect(res.stdout.toString().trim()).toMatch(/AssertOkTest/) expect(res.stdout.toString().trim()).toMatch(/AssertOkTest/)
expect(res.stdout.toString().trim()).toMatch(/AssertOkTest okPassTest/) // check if console.log is printed
expect(res.stdout.toString().trim()).toMatch(/Ok pass test/) expect(res.stdout.toString().trim()).toMatch(/Ok pass test/)
expect(res.stdout.toString().trim()).toMatch(/AssertOkTest okFailTest/) // check if console.log is printed
expect(res.stdout.toString().trim()).toMatch(/Ok fail test/) expect(res.stdout.toString().trim()).toMatch(/Ok fail test/)
// match fail test details // match fail test details
expect(res.stdout.toString().trim()).toMatch(/error: okFailTest fails/) expect(res.stdout.toString().trim()).toMatch(/Expected value should be ok to: true/)
expect(res.stdout.toString().trim()).toMatch(/expected value to be ok to: true/) expect(res.stdout.toString().trim()).toMatch(/Received: false/)
expect(res.stdout.toString().trim()).toMatch(/returned: false/) expect(res.stdout.toString().trim()).toMatch(/Message: okFailTest fails/)
}) })
test('remix-tests running a test file with custom compiler version', () => { test('remix-tests running a test file with custom compiler version', () => {
const res = spawnSync(executablePath, ['--compiler', '0.7.4', resolve(__dirname + '/examples_0/assert_ok_without_console_test.sol')]) const res = spawnSync(executablePath, ['--compiler', '0.7.4', resolve(__dirname + '/examples_0/assert_ok_test.sol')])
// match initial lines // match initial lines
expect(res.stdout.toString().trim().includes('Compiler version set to 0.7.4. Latest version is')).toBeTruthy() expect(res.stdout.toString().trim().includes('Compiler version set to 0.7.4. Latest version is')).toBeTruthy()
expect(res.stdout.toString().trim().includes('Loading remote solc version v0.7.4+commit.3f05b770 ...')).toBeTruthy() expect(res.stdout.toString().trim().includes('Loading remote solc version v0.7.4+commit.3f05b770 ...')).toBeTruthy()
expect(res.stdout.toString().trim()).toMatch(/:: Running remix-tests - Unit testing for solidity ::/) expect(res.stdout.toString().trim()).toMatch(/:: Running tests using remix-tests ::/)
expect(res.stdout.toString().trim()).toMatch(/creation of library remix_tests.sol:Assert pending.../) expect(res.stdout.toString().trim()).toMatch(/creation of library remix_tests.sol:Assert pending.../)
// match test result // match test result
expect(res.stdout.toString().trim()).toMatch(/Ok pass test/) expect(res.stdout.toString().trim()).toMatch(/Ok pass test/)
expect(res.stdout.toString().trim()).toMatch(/Ok fail test/) expect(res.stdout.toString().trim()).toMatch(/Ok fail test/)
// match fail test details // match fail test details
expect(res.stdout.toString().trim()).toMatch(/error: okFailTest fails/) expect(res.stdout.toString().trim()).toMatch(/Message: okFailTest fails/)
}) })
test('remix-tests running a test file with unavailable custom compiler version (should fail)', () => { test('remix-tests running a test file with unavailable custom compiler version (should fail)', () => {
const res = spawnSync(executablePath, ['--compiler', '1.10.4', resolve(__dirname + '/examples_0/assert_ok_without_console_test.sol')]) const res = spawnSync(executablePath, ['--compiler', '1.10.4', resolve(__dirname + '/examples_0/assert_ok_test.sol')])
// match initial lines // match initial lines
expect(res.stdout.toString().trim().includes('No compiler found in releases with version 1.10.4')).toBeTruthy() expect(res.stdout.toString().trim().includes('No compiler found in releases with version 1.10.4')).toBeTruthy()
}) })
test('remix-tests running a test file with custom EVM', () => { test('remix-tests running a test file with custom EVM', () => {
const res = spawnSync(executablePath, ['--evm', 'petersburg', resolve(__dirname + '/examples_0/assert_ok_without_console_test.sol')]) const res = spawnSync(executablePath, ['--evm', 'petersburg', resolve(__dirname + '/examples_0/assert_ok_test.sol')])
// match initial lines // match initial lines
expect(res.stdout.toString().trim().includes('EVM set to petersburg')).toBeTruthy() expect(res.stdout.toString().trim().includes('EVM set to petersburg')).toBeTruthy()
expect(res.stdout.toString().trim()).toMatch(/:: Running remix-tests - Unit testing for solidity ::/) expect(res.stdout.toString().trim()).toMatch(/:: Running tests using remix-tests ::/)
expect(res.stdout.toString().trim()).toMatch(/creation of library remix_tests.sol:Assert pending.../) expect(res.stdout.toString().trim()).toMatch(/creation of library remix_tests.sol:Assert pending.../)
// match test result // match test result
expect(res.stdout.toString().trim()).toMatch(/Ok pass test/) expect(res.stdout.toString().trim()).toMatch(/Ok pass test/)
expect(res.stdout.toString().trim()).toMatch(/Ok fail test/) expect(res.stdout.toString().trim()).toMatch(/Ok fail test/)
// match fail test details // match fail test details
expect(res.stdout.toString().trim()).toMatch(/error: okFailTest fails/) expect(res.stdout.toString().trim()).toMatch(/Message: okFailTest fails/)
}) })
test('remix-tests running a test file by enabling optimization', () => { test('remix-tests running a test file by enabling optimization', () => {
const res = spawnSync(executablePath, ['--optimize', 'true', resolve(__dirname + '/examples_0/assert_ok_without_console_test.sol')]) const res = spawnSync(executablePath, ['--optimize', 'true', resolve(__dirname + '/examples_0/assert_ok_test.sol')])
// match initial lines // match initial lines
expect(res.stdout.toString().trim().includes('Optimization is enabled')).toBeTruthy() expect(res.stdout.toString().trim().includes('Optimization is enabled')).toBeTruthy()
expect(res.stdout.toString().trim()).toMatch(/:: Running remix-tests - Unit testing for solidity ::/) expect(res.stdout.toString().trim()).toMatch(/:: Running tests using remix-tests ::/)
expect(res.stdout.toString().trim()).toMatch(/creation of library remix_tests.sol:Assert pending.../) expect(res.stdout.toString().trim()).toMatch(/creation of library remix_tests.sol:Assert pending.../)
// match test result // match test result
expect(res.stdout.toString().trim()).toMatch(/Ok pass test/) expect(res.stdout.toString().trim()).toMatch(/Ok pass test/)
expect(res.stdout.toString().trim()).toMatch(/Ok fail test/) expect(res.stdout.toString().trim()).toMatch(/Ok fail test/)
// match fail test details // match fail test details
expect(res.stdout.toString().trim()).toMatch(/error: okFailTest fails/) expect(res.stdout.toString().trim()).toMatch(/Message: okFailTest fails/)
}) })
test('remix-tests running a test file by enabling optimization and setting runs', () => { test('remix-tests running a test file by enabling optimization and setting runs', () => {
const res = spawnSync(executablePath, ['--optimize', 'true', '--runs', '300', resolve(__dirname + '/examples_0/assert_ok_without_console_test.sol')]) const res = spawnSync(executablePath, ['--optimize', 'true', '--runs', '300', resolve(__dirname + '/examples_0/assert_ok_test.sol')])
// match initial lines // match initial lines
expect(res.stdout.toString().trim().includes('Optimization is enabled')).toBeTruthy() expect(res.stdout.toString().trim().includes('Optimization is enabled')).toBeTruthy()
expect(res.stdout.toString().trim().includes('Runs set to 300')).toBeTruthy() expect(res.stdout.toString().trim().includes('Runs set to 300')).toBeTruthy()
expect(res.stdout.toString().trim()).toMatch(/:: Running remix-tests - Unit testing for solidity ::/) expect(res.stdout.toString().trim()).toMatch(/:: Running tests using remix-tests ::/)
expect(res.stdout.toString().trim()).toMatch(/creation of library remix_tests.sol:Assert pending.../) expect(res.stdout.toString().trim()).toMatch(/creation of library remix_tests.sol:Assert pending.../)
// match test result // match test result
expect(res.stdout.toString().trim()).toMatch(/Ok pass test/) expect(res.stdout.toString().trim()).toMatch(/Ok pass test/)
expect(res.stdout.toString().trim()).toMatch(/Ok fail test/) expect(res.stdout.toString().trim()).toMatch(/Ok fail test/)
// match fail test details // match fail test details
expect(res.stdout.toString().trim()).toMatch(/error: okFailTest fails/) expect(res.stdout.toString().trim()).toMatch(/Message: okFailTest fails/)
}) })
test('remix-tests running a test file without enabling optimization and setting runs (should fail)', () => { test('remix-tests running a test file without enabling optimization and setting runs (should fail)', () => {
const res = spawnSync(executablePath, ['--runs', '300', resolve(__dirname + '/examples_0/assert_ok_without_console_test.sol')]) const res = spawnSync(executablePath, ['--runs', '300', resolve(__dirname + '/examples_0/assert_ok_test.sol')])
// match initial lines // match initial lines
expect(res.stdout.toString().trim().includes('Optimization should be enabled for runs')).toBeTruthy() expect(res.stdout.toString().trim().includes('Optimization should be enabled for runs')).toBeTruthy()
}) })
test('remix-tests running a test file with all options', () => { test('remix-tests running a test file with all options', () => {
const res = spawnSync(executablePath, ['--compiler', '0.7.5', '--evm', 'istanbul', '--optimize', 'true', '--runs', '250', resolve(__dirname + '/examples_0/assert_ok_without_console_test.sol')]) const res = spawnSync(executablePath, ['--compiler', '0.7.5', '--evm', 'istanbul', '--optimize', 'true', '--runs', '250', resolve(__dirname + '/examples_0/assert_ok_test.sol')])
// match initial lines // match initial lines
expect(res.stdout.toString().trim().includes('Compiler version set to 0.7.5. Latest version is')).toBeTruthy() expect(res.stdout.toString().trim().includes('Compiler version set to 0.7.5. Latest version is')).toBeTruthy()
expect(res.stdout.toString().trim().includes('Loading remote solc version v0.7.5+commit.eb77ed08 ...')).toBeTruthy() expect(res.stdout.toString().trim().includes('Loading remote solc version v0.7.5+commit.eb77ed08 ...')).toBeTruthy()
expect(res.stdout.toString().trim().includes('EVM set to istanbul')).toBeTruthy() expect(res.stdout.toString().trim().includes('EVM set to istanbul')).toBeTruthy()
expect(res.stdout.toString().trim().includes('Optimization is enabled')).toBeTruthy() expect(res.stdout.toString().trim().includes('Optimization is enabled')).toBeTruthy()
expect(res.stdout.toString().trim().includes('Runs set to 250')).toBeTruthy() expect(res.stdout.toString().trim().includes('Runs set to 250')).toBeTruthy()
expect(res.stdout.toString().trim()).toMatch(/:: Running remix-tests - Unit testing for solidity ::/) expect(res.stdout.toString().trim()).toMatch(/:: Running tests using remix-tests ::/)
expect(res.stdout.toString().trim()).toMatch(/creation of library remix_tests.sol:Assert pending.../) expect(res.stdout.toString().trim()).toMatch(/creation of library remix_tests.sol:Assert pending.../)
// match test result // match test result
expect(res.stdout.toString().trim()).toMatch(/Ok pass test/) expect(res.stdout.toString().trim()).toMatch(/Ok pass test/)
expect(res.stdout.toString().trim()).toMatch(/Ok fail test/) expect(res.stdout.toString().trim()).toMatch(/Ok fail test/)
// match fail test details // match fail test details
expect(res.stdout.toString().trim()).toMatch(/error: okFailTest fails/) expect(res.stdout.toString().trim()).toMatch(/Message: okFailTest fails/)
}) })
}) })
}) })

@ -129,7 +129,7 @@ describe('testRunner', () => {
{ type: 'accountList', value: accounts }, { type: 'accountList', value: accounts },
{ type: 'contract', value: 'AssertOkTest', filename: __dirname + '/examples_0/assert_ok_test.sol' }, { type: 'contract', value: 'AssertOkTest', filename: __dirname + '/examples_0/assert_ok_test.sol' },
{ type: 'testPass', debugTxHash: '0x5b665752a4faf83229259b9b2811d3295be0af633b0051d4b90042283ef55707', value: 'Ok pass test', filename: __dirname + '/examples_0/assert_ok_test.sol', context: 'AssertOkTest', hhLogs: hhLogs1 }, { type: 'testPass', debugTxHash: '0x5b665752a4faf83229259b9b2811d3295be0af633b0051d4b90042283ef55707', value: 'Ok pass test', filename: __dirname + '/examples_0/assert_ok_test.sol', context: 'AssertOkTest', hhLogs: hhLogs1 },
{ type: 'testFailure', debugTxHash: '0xa0a30ad042a7fc3495f72be7ba788d705888ffbbec7173f60bb27e07721510f2',value: 'Ok fail test', filename: __dirname + '/examples_0/assert_ok_test.sol', errMsg: 'okFailTest fails', context: 'AssertOkTest', hhLogs: hhLogs2, assertMethod: 'ok', location: '370:36:0', expected: 'true', returned: 'false'}, { type: 'testFailure', debugTxHash: '0xa0a30ad042a7fc3495f72be7ba788d705888ffbbec7173f60bb27e07721510f2',value: 'Ok fail test', filename: __dirname + '/examples_0/assert_ok_test.sol', errMsg: 'okFailTest fails', context: 'AssertOkTest', hhLogs: hhLogs2, assertMethod: 'ok', location: '366:36:0', expected: 'true', returned: 'false'},
], ['time', 'web3']) ], ['time', 'web3'])
}) })

@ -36,7 +36,7 @@ export const CopyToClipboard = (props: ICopyToClipboard) => {
} }
} }
const handleClick = (e) => { const handleClick = (e: any) => {
if (content) { // module `copy` keeps last copied thing in the memory, so don't show tooltip if nothing is copied, because nothing was added to memory if (content) { // module `copy` keeps last copied thing in the memory, so don't show tooltip if nothing is copied, because nothing was added to memory
copyData() copyData()
} else { } else {

@ -249,7 +249,6 @@ export const EditorUI = (props: EditorUIProps) => {
defineAndSetTheme(monacoRef.current) defineAndSetTheme(monacoRef.current)
}) })
useEffect(() => { useEffect(() => {
if (!editorRef.current || !props.currentFile) return if (!editorRef.current || !props.currentFile) return
currentFileRef.current = props.currentFile currentFileRef.current = props.currentFile
@ -471,6 +470,8 @@ export const EditorUI = (props: EditorUIProps) => {
(window as any).addRemixBreakpoint(e.target.position) (window as any).addRemixBreakpoint(e.target.position)
} }
}) })
// zoomin zoomout
editor.addCommand(monacoRef.current.KeyMod.CtrlCmd | monacoRef.current.KeyCode.US_EQUAL, () => { editor.addCommand(monacoRef.current.KeyMod.CtrlCmd | monacoRef.current.KeyCode.US_EQUAL, () => {
editor.updateOptions({ fontSize: editor.getOption(43).fontSize + 1 }) editor.updateOptions({ fontSize: editor.getOption(43).fontSize + 1 })
}) })
@ -478,6 +479,32 @@ export const EditorUI = (props: EditorUIProps) => {
editor.updateOptions({ fontSize: editor.getOption(43).fontSize - 1 }) editor.updateOptions({ fontSize: editor.getOption(43).fontSize - 1 })
}) })
// add context menu items
const zoominAction = {
id: "zoomIn",
label: "Zoom In",
contextMenuOrder: 0, // choose the order
contextMenuGroupId: "zooming", // create a new grouping
keybindings: [
// eslint-disable-next-line no-bitwise
monacoRef.current.KeyMod.CtrlCmd | monacoRef.current.KeyCode.Equal,
],
run: () => { editor.updateOptions({ fontSize: editor.getOption(43).fontSize + 1 }) },
}
const zoomOutAction = {
id: "zoomOut",
label: "Zoom Out",
contextMenuOrder: 0, // choose the order
contextMenuGroupId: "zooming", // create a new grouping
keybindings: [
// eslint-disable-next-line no-bitwise
monacoRef.current.KeyMod.CtrlCmd | monacoRef.current.KeyCode.Minus,
],
run: () => { editor.updateOptions({ fontSize: editor.getOption(43).fontSize - 1 }) },
}
editor.addAction(zoomOutAction)
editor.addAction(zoominAction)
const editorService = editor._codeEditorService; const editorService = editor._codeEditorService;
const openEditorBase = editorService.openCodeEditor.bind(editorService); const openEditorBase = editorService.openCodeEditor.bind(editorService);
editorService.openCodeEditor = async (input, source) => { editorService.openCodeEditor = async (input, source) => {

@ -95,17 +95,20 @@ export const setupEvents = (plugin: RunTab, dispatch: React.Dispatch<any>) => {
const broadcastCompilationResult = async (plugin: RunTab, dispatch: React.Dispatch<any>, file, source, languageVersion, data, input?) => { const broadcastCompilationResult = async (plugin: RunTab, dispatch: React.Dispatch<any>, file, source, languageVersion, data, input?) => {
// TODO check whether the tab is configured // TODO check whether the tab is configured
const compiler = new CompilerAbstract(languageVersion, data, source, input) const compiler = new CompilerAbstract(languageVersion, data, source, input)
plugin.compilersArtefacts[languageVersion] = compiler plugin.compilersArtefacts[languageVersion] = compiler
plugin.compilersArtefacts.__last = compiler plugin.compilersArtefacts.__last = compiler
const contracts = getCompiledContracts(compiler).map((contract) => { const contracts = getCompiledContracts(compiler).map((contract) => {
return { name: languageVersion, alias: contract.name, file: contract.file, compiler } return { name: languageVersion, alias: contract.name, file: contract.file, compiler }
}) })
const index = contracts.findIndex(contract => contract.alias === plugin.REACT_API.contracts.currentContract) if ((contracts.length > 0)) {
const contractsInCompiledFile = contracts.filter(obj => obj.file === file)
if ((index < 0) && (contracts.length > 0)) dispatch(setCurrentContract(contracts[0].alias)) let currentContract
const isUpgradeable = await plugin.call('openzeppelin-proxy', 'isConcerned', data.sources[file] ? data.sources[file].ast : {}) if (contractsInCompiledFile.length) currentContract = contractsInCompiledFile[0].alias
else currentContract = contracts[0].alias
dispatch(setCurrentContract(currentContract))
}
const isUpgradeable = await plugin.call('openzeppelin-proxy', 'isConcerned', data.sources && data.sources[file] ? data.sources[file].ast : {})
if (isUpgradeable) { if (isUpgradeable) {
const options = await plugin.call('openzeppelin-proxy', 'getProxyOptions', data, file) const options = await plugin.call('openzeppelin-proxy', 'getProxyOptions', data, file)

@ -24,6 +24,13 @@ export function ContractGUI (props: ContractGUIProps) {
const initializeFields = useRef<Array<HTMLInputElement | null>>([]) const initializeFields = useRef<Array<HTMLInputElement | null>>([])
const basicInputRef = useRef<HTMLInputElement>() const basicInputRef = useRef<HTMLInputElement>()
useEffect(() => {
if (props.deployOption && Array.isArray(props.deployOption)) {
if (props.deployOption[0] && props.deployOption[0].title === 'Deploy with Proxy' && props.deployOption[0].active) handleDeployProxySelect(true)
else if (props.deployOption[1] && props.deployOption[1].title === 'Upgrade with Proxy' && props.deployOption[1].active) handleUpgradeImpSelect(true)
}
}, [props.deployOption])
useEffect(() => { useEffect(() => {
if (props.title) { if (props.title) {
setTitle(props.title) setTitle(props.title)
@ -131,12 +138,10 @@ export function ContractGUI (props: ContractGUIProps) {
} }
const makeMultiVal = () => { const makeMultiVal = () => {
let inputString = basicInput const inputString = basicInput
if (inputString) { if (inputString) {
inputString = inputString.replace(/(^|,\s+|,)(\d+)(\s+,|,|$)/g, '$1"$2"$3') // replace non quoted number by quoted number const inputJSON = remixLib.execution.txFormat.parseFunctionParams(inputString)
inputString = inputString.replace(/(^|,\s+|,)(0[xX][0-9a-fA-F]+)(\s+,|,|$)/g, '$1"$2"$3') // replace non quoted hex string by quoted hex string
const inputJSON = JSON.parse('[' + inputString + ']')
const multiInputs = multiFields.current const multiInputs = multiFields.current
for (let k = 0; k < multiInputs.length; k++) { for (let k = 0; k < multiInputs.length; k++) {
@ -179,9 +184,7 @@ export function ContractGUI (props: ContractGUIProps) {
setToggleDeployProxy(!toggleDeployProxy) setToggleDeployProxy(!toggleDeployProxy)
} }
const handleDeployProxySelect = (e) => { const handleDeployProxySelect = (value: boolean) => {
const value = e.target.checked
if (value) setToggleUpgradeImp(false) if (value) setToggleUpgradeImp(false)
setToggleDeployProxy(value) setToggleDeployProxy(value)
setDeployState({ upgrade: false, deploy: value }) setDeployState({ upgrade: false, deploy: value })
@ -191,9 +194,7 @@ export function ContractGUI (props: ContractGUIProps) {
setToggleUpgradeImp(!toggleUpgradeImp) setToggleUpgradeImp(!toggleUpgradeImp)
} }
const handleUpgradeImpSelect = (e) => { const handleUpgradeImpSelect = (value: boolean) => {
const value = e.target.checked
setToggleUpgradeImp(value) setToggleUpgradeImp(value)
if (value) { if (value) {
setToggleDeployProxy(false) setToggleDeployProxy(false)
@ -264,7 +265,7 @@ export function ContractGUI (props: ContractGUIProps) {
data-id="contractGUIDeployWithProxy" data-id="contractGUIDeployWithProxy"
className="form-check-input custom-control-input" className="form-check-input custom-control-input"
type="checkbox" type="checkbox"
onChange={handleDeployProxySelect} onChange={(e) => handleDeployProxySelect(e.target.checked)}
checked={deployState.deploy} checked={deployState.deploy}
/> />
<label <label
@ -307,7 +308,7 @@ export function ContractGUI (props: ContractGUIProps) {
data-id="contractGUIUpgradeImplementation" data-id="contractGUIUpgradeImplementation"
className="form-check-input custom-control-input" className="form-check-input custom-control-input"
type="checkbox" type="checkbox"
onChange={handleUpgradeImpSelect} onChange={(e) => handleUpgradeImpSelect(e.target.checked)}
checked={deployState.upgrade} checked={deployState.upgrade}
/> />
<label <label

@ -6,7 +6,7 @@ export const warnText = 'Be sure the endpoint is opened before enabling it. \nTh
export const gitAccessTokenTitle = 'GitHub Access Token' export const gitAccessTokenTitle = 'GitHub Access Token'
export const gitAccessTokenText = 'Manage the access token used to publish to Gist and retrieve GitHub contents.' export const gitAccessTokenText = 'Manage the access token used to publish to Gist and retrieve GitHub contents.'
export const gitAccessTokenText2 = 'Go to github token page (link below) to create a new token and save it in Remix. Make sure this token has only \'create gist\' permission.' export const gitAccessTokenText2 = 'Go to github token page (link below) to create a new token and save it in Remix. Make sure this token has only \'create gist\' permission.'
export const gitAccessTokenLink = 'https://github.com/settings/tokens' export const gitAccessTokenLink = 'https://github.com/settings/tokens/new?scopes=gist,repo&description=Remix%20IDE%20Token'
export const etherscanTokenTitle = 'EtherScan Access Token' export const etherscanTokenTitle = 'EtherScan Access Token'
export const etherscanTokenLink = 'https://etherscan.io/myapikey' export const etherscanTokenLink = 'https://etherscan.io/myapikey'
export const etherscanAccessTokenText = 'Manage the api key used to interact with Etherscan.' export const etherscanAccessTokenText = 'Manage the api key used to interact with Etherscan.'

@ -309,12 +309,22 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
allVersions = [...allVersions, ...versions] allVersions = [...allVersions, ...versions]
selectedVersion = state.defaultVersion selectedVersion = state.defaultVersion
if (api.getCompilerParameters().version) selectedVersion = api.getCompilerParameters().version if (api.getCompilerParameters().version) {
// Check if version is a URL and corresponding filename starts with 'soljson' const versionFromURL = api.getCompilerParameters().version
if (selectedVersion.startsWith('https://')) { // Check if version is a URL and corresponding filename starts with 'soljson'
const urlArr = selectedVersion.split('/') if (versionFromURL.startsWith('https://')) {
const urlArr = versionFromURL.split('/')
if (urlArr[urlArr.length - 1].startsWith('soljson')) isURL = true if (urlArr[urlArr.length - 1].startsWith('soljson')) {
isURL = true
selectedVersion = versionFromURL
}
} else {
// URL version can be like 0.8.7+commit.e28d00a7, 0.8.7 or soljson-v0.8.7+commit.e28d00a7.js
const selectedVersionArr = versions.filter(obj => obj.path === versionFromURL || obj.longVersion === versionFromURL || obj.version === versionFromURL)
// for version like 0.8.15, there will be more than one elements in the array
// In that case too, index 0 will have non-nightly version object
if (selectedVersionArr.length) selectedVersion = selectedVersionArr[0].path
}
} }
if (wasmRes.event.type !== 'error') { if (wasmRes.event.type !== 'error') {
allVersionsWasm = JSON.parse(wasmRes.json).builds.slice().reverse() allVersionsWasm = JSON.parse(wasmRes.json).builds.slice().reverse()
@ -765,7 +775,7 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
<div className={`px-4 pb-4 border-bottom flex-column ${toggleExpander ? "d-flex" : "d-none"}`}> <div className={`px-4 pb-4 border-bottom flex-column ${toggleExpander ? "d-flex" : "d-none"}`}>
<div className="d-flex pb-1 remixui_compilerConfig custom-control custom-radio"> <div className="d-flex pb-1 remixui_compilerConfig custom-control custom-radio">
<input className="custom-control-input" type="radio" name="configradio" value="manual" onChange={toggleConfigType} checked={!state.useFileConfiguration} id="scManualConfig" /> <input className="custom-control-input" type="radio" name="configradio" value="manual" onChange={toggleConfigType} checked={!state.useFileConfiguration} id="scManualConfig" />
<label className="form-check-label custom-control-label" htmlFor="scManualConfig">Compiler configuration</label> <label className="form-check-label custom-control-label" htmlFor="scManualConfig" data-id="scManualConfiguration">Compiler configuration</label>
</div> </div>
<div className={`flex-column 'd-flex'}`}> <div className={`flex-column 'd-flex'}`}>
<div className="mb-2 ml-4"> <div className="mb-2 ml-4">
@ -813,7 +823,7 @@ export const CompilerContainer = (props: CompilerContainerProps) => {
<input <input
ref={configFilePathInput} ref={configFilePathInput}
className={`py-0 my-0 form-control ${showFilePathInput ? "d-flex" : "d-none"}`} className={`py-0 my-0 form-control ${showFilePathInput ? "d-flex" : "d-none"}`}
placeholder={"Enter the new path"} placeholder={"/folder_path/file_name.json"}
title="If the file you entered does not exist you will be able to create one in the next step." title="If the file you entered does not exist you will be able to create one in the next step."
disabled={!state.useFileConfiguration} disabled={!state.useFileConfiguration}
data-id="scConfigFilePathInput" data-id="scConfigFilePathInput"

@ -7,12 +7,18 @@ import { CopyToClipboard } from '@remix-ui/clipboard' // eslint-disable-line
import './css/style.css' import './css/style.css'
export const ContractSelection = (props: ContractSelectionProps) => { export const ContractSelection = (props: ContractSelectionProps) => {
const { api, contractsDetails, contractList, modal } = props const { api, compiledFileName, contractsDetails, contractList, modal } = props
const [selectedContract, setSelectedContract] = useState('') const [selectedContract, setSelectedContract] = useState('')
const [storage, setStorage] = useState(null) const [storage, setStorage] = useState(null)
useEffect(() => { useEffect(() => {
if (contractList.length) setSelectedContract(contractList[0].name) if (contractList.length) {
const compiledPathArr = compiledFileName.split('/')
const compiledFile = compiledPathArr[compiledPathArr.length - 1]
const contractsInCompiledFile = contractList.filter(obj => obj.file === compiledFile)
if (contractsInCompiledFile.length) setSelectedContract(contractsInCompiledFile[0].name)
else setSelectedContract(contractList[0].name)
}
}, [contractList]) }, [contractList])
const resetStorage = () => { const resetStorage = () => {

@ -183,7 +183,7 @@ export const SolidityCompiler = (props: SolidityCompilerProps) => {
configFilePath={state.configFilePath} configFilePath={state.configFilePath}
setConfigFilePath={setConfigFilePath} setConfigFilePath={setConfigFilePath}
/> />
{ contractsFile[currentFile] && contractsFile[currentFile].contractsDetails && <ContractSelection api={api} contractsDetails={contractsFile[currentFile].contractsDetails} contractList={contractsFile[currentFile].contractList} modal={modal} /> } { contractsFile[currentFile] && contractsFile[currentFile].contractsDetails && <ContractSelection api={api} compiledFileName={currentFile} contractsDetails={contractsFile[currentFile].contractsDetails} contractList={contractsFile[currentFile].contractList} modal={modal} /> }
{ compileErrors[currentFile] && { compileErrors[currentFile] &&
<div className="remixui_errorBlobs p-4" data-id="compiledErrors"> <div className="remixui_errorBlobs p-4" data-id="compiledErrors">
<span data-id={`compilationFinishedWith_${currentVersion}`}></span> <span data-id={`compilationFinishedWith_${currentVersion}`}></span>

@ -23,6 +23,7 @@ export interface CompilerContainerProps {
} }
export interface ContractSelectionProps { export interface ContractSelectionProps {
api: ICompilerApi, api: ICompilerApi,
compiledFileName: string,
contractList: { file: string, name: string }[], contractList: { file: string, name: string }[],
modal: (title: string, message: string | JSX.Element, okLabel: string, okFn: () => void, cancelLabel?: string, cancelFn?: () => void) => void, modal: (title: string, message: string | JSX.Element, okLabel: string, okFn: () => void, cancelLabel?: string, cancelFn?: () => void) => void,
contractsDetails: Record<string, any> contractsDetails: Record<string, any>

@ -1,5 +1,5 @@
export const wrapScript = (script) => { export const wrapScript = (script) => {
const isKnownScript = ['remix.', 'git'].some(prefix => script.trim().startsWith(prefix)) const isKnownScript = ['remix.', 'console.', 'git'].some(prefix => script.trim().startsWith(prefix))
if (isKnownScript) return script if (isKnownScript) return script
return ` return `
try { try {

@ -75,7 +75,8 @@ export const createWorkspaceTemplate = async (workspaceName: string, template: W
export type UrlParametersType = { export type UrlParametersType = {
gist: string, gist: string,
code: string, code: string,
url: string url: string,
language: string
} }
export const loadWorkspacePreset = async (template: WorkspaceTemplate = 'remixDefault') => { export const loadWorkspacePreset = async (template: WorkspaceTemplate = 'remixDefault') => {
@ -91,7 +92,7 @@ export const loadWorkspacePreset = async (template: WorkspaceTemplate = 'remixDe
if (params.code) { if (params.code) {
const hash = bufferToHex(keccakFromString(params.code)) const hash = bufferToHex(keccakFromString(params.code))
path = 'contract-' + hash.replace('0x', '').substring(0, 10) + '.sol' path = 'contract-' + hash.replace('0x', '').substring(0, 10) + (params.language && params.language.toLowerCase() === 'yul' ? '.yul': '.sol')
content = atob(params.code) content = atob(params.code)
await workspaceProvider.set(path, content) await workspaceProvider.set(path, content)
} }

@ -1,59 +1,88 @@
# Remixd # Remixd
`remixd` is a tool that intend to be used with [Remix IDE](https://github.com/ethereum/remix-project) (aka. Browser-Solidity). It allows a websocket connection between [![npm version](https://badge.fury.io/js/%40remix-project%2Fremixd.svg)](https://www.npmjs.com/package/@remix-project/remixd)
`Remix IDE` (web application) and the local computer. [![npm](https://img.shields.io/npm/dt/@remix-project/remixd.svg?label=Total%20Downloads&logo=npm)](https://www.npmjs.com/package/@remix-project/remixd)
[![npm](https://img.shields.io/npm/dw/@remix-project/remixd.svg?logo=npm)](https://www.npmjs.com/package/@remix-project/remixd)
Practically Remix IDE makes available a folder shared by `remixd`.
More details are explained in this [tutorial](https://remix-ide.readthedocs.io/en/latest/remixd.html). `@remix-project/remixd` is an NPM module that intends to be used with [Remix IDE](https://remix.ethereum.org/) web and desktop applications. It establishes a two-way websocket connection between the local computer and Remix IDE for a particular project directory.
Alternatively `remixd` can be used to setup a development environment that can be used with other popular frameworks like Embark, Truffle, Ganache, etc.. `remixd` can be used to setup a development environment with other popular frameworks like Hardhat, Truffle, Slither etc.
`remixd` needs `npm` and `node` More details are explained in the [documentation](https://remix-ide.readthedocs.io/en/latest/remixd.html).
## INSTALLATION ## Installation
`yarn global add @remix-project/remixd` `npm install -g @remix-project/remixd`
### Warning for old users NOTE: When the remixd NPM module is installed, it also installs [Slither](https://github.com/crytic/slither), [solc-select](https://github.com/crytic/solc-select#quickstart) and sets [solc](https://docs.soliditylang.org/en/latest/installing-solidity.html) to latest version i.e. 0.8.15 currently.
ALSO NOTE: Python3.6+ (pip3) needs to already be installed on the System. In case of any discrepany, Slither can also installed along with other dependencies using command:
```
> remixd -i slither
```
_(This packaging of Slither with the remixd module is supported since Remixd v0.6.3)_
### Warning for quite old users
There is a new version of remixd with a new npm address: https://npmjs.com/package/@remix-project/remixd There is a new version of remixd with a new npm address: https://npmjs.com/package/@remix-project/remixd
If you were using the old one you need to: If you were using the old one you need to:
1. uninstall the old one: `npm uninstall -g remixd` 1. uninstall the old one: `npm uninstall -g remixd`
2. install the new: `yarn global add @remix-project/remixd` 2. install the new: `npm install -g @remix-project/remixd`
## remixd command
## HELP SECTION The remixd command without options shares present working directory and the shared Remix domain will be https://remix.ethereum.org, https://remix-alpha.ethereum.org, or https://remix-beta.ethereum.org
The remixd command is:
```
> remixd
``` ```
Usage: remixd -s <shared folder>
Provide a two-way connection between the local computer and Remix IDE If you are using Remix from localhost or you are not running the command from your working directory, you’ll need to use the command with flags.
```
> remixd -h
Usage: remixd [options]
Establish a two-way websocket connection between the local computer and Remix IDE for a folder
Options: Options:
-v, --version output the version number -v, --version output the version number
-u, --remix-ide <url> URL of remix instance allowed to connect to this web sockect connection -u, --remix-ide <url> URL of remix instance allowed to connect
-s, --shared-folder <path> Folder to share with Remix IDE -s, --shared-folder <path> Folder to share with Remix IDE (Default: CWD)
-i, --install <name> Module name to install locally (Supported: ["slither"])
-r, --read-only Treat shared folder as read-only (experimental) -r, --read-only Treat shared folder as read-only (experimental)
-h, --help output usage information -h, --help output usage information
Example: Example:
remixd -s ./ -u http://localhost:8080 remixd -s ./shared_project -u http://localhost:8080
``` ```
## SHARE A FOLDER ## Share a project directory
`remixd -s <absolute-path> --remix-ide https://remix.ethereum.org` `remixd -s ./shared_project -u https://remix.ethereum.org`
The current user should have `read/write` access to the folder (at least `read` access). The current user should have `read/write` access to the folder (at least `read` access).
It is important to notice that changes made to the current file in `Remix IDE` are automatically saved to the local computer every 5000 ms. There is no `Save` action. But the `Ctrl-Z` (undo) can be used. It is important to notice that changes made to the current file in `Remix IDE` are automatically saved to the local computer every 5000 ms. There is no `Save` action. But the `Ctrl-Z` (undo) can be used.
Furthermore : Furthermore:
- No copy of the shared folder are kept in the browser storage. - No copy of the shared folder are kept in the browser storage.
- It is not possible to create a file from `Remix IDE` (that might change). - Clicking on the new folder or new file icon under localhost will create a new file or folder in the shared folder.
- If a folder does not contain any file, the folder will not be displayed in the explorer (that might change). - If a folder does not contain any file, the folder will not be displayed in the explorer (that might change).
- Symbolic links are not forwarded to Remix IDE. - Symbolic links are not forwarded to Remix IDE.
## Ports Usage
remixd creates a websocket connections with Remix IDE on different ports. Ports are defined according to specific purpose. Port usage details are as:
- **65520** : For `remixd` websocket listener, to share a project from local device with Remix IDE. Shared folder will be loaded in the Remix IDE File Explorer workspace named localhost [See more](https://remix-ide.readthedocs.io/en/latest/remixd.html)
- **65522** : For `Hardhat` websocket listener, to enable the Hardhat Compilation using Remix IDE Solidity Compiler plugin, if shared folder is a Hardhat project [See more](https://remix-ide.readthedocs.io/en/latest/hardhat.html)
- **65523** : For `Slither` websocket listener, to enable the Slither Analysis using Remix IDE Solidity Static Analysis plugin [See more](https://remix-ide.readthedocs.io/en/latest/slither.html)
- **65524** : For `Truffle` websocket listener, to enable the Truffle Compilation using Remix IDE Solidity Compiler plugin, if shared folder is a Truffle project [See more](https://remix-ide.readthedocs.io/en/latest/truffle.html)
Note: Please make sure your system is secured enough and these ports are not opened nor forwarded.

@ -1,6 +1,6 @@
{ {
"name": "@remix-project/remixd", "name": "@remix-project/remixd",
"version": "0.6.4", "version": "0.6.5",
"description": "remix server: allow accessing file system from remix.ethereum.org and start a dev environment (see help section)", "description": "remix server: allow accessing file system from remix.ethereum.org and start a dev environment (see help section)",
"main": "index.js", "main": "index.js",
"types": "./index.d.ts", "types": "./index.d.ts",

@ -15,7 +15,12 @@
"default": { "default": {
"runner": "@nrwl/workspace/tasks-runners/default", "runner": "@nrwl/workspace/tasks-runners/default",
"options": { "options": {
"cacheableOperations": ["build", "lint", "test", "e2e"] "cacheableOperations": [
"build",
"lint",
"test",
"e2e"
]
} }
} }
}, },
@ -25,27 +30,43 @@
}, },
"remix-astwalker": { "remix-astwalker": {
"tags": [], "tags": [],
"implicitDependencies": ["remix-lib"] "implicitDependencies": [
"remix-lib"
]
}, },
"remix-analyzer": { "remix-analyzer": {
"tags": [], "tags": [],
"implicitDependencies": ["remix-astwalker", "remix-lib"] "implicitDependencies": [
"remix-astwalker",
"remix-lib"
]
}, },
"remix-debug": { "remix-debug": {
"tags": [], "tags": [],
"implicitDependencies": ["remix-astwalker", "remix-lib"] "implicitDependencies": [
"remix-astwalker",
"remix-lib"
]
}, },
"remix-simulator": { "remix-simulator": {
"tags": [], "tags": [],
"implicitDependencies": ["remix-lib"] "implicitDependencies": [
"remix-lib"
]
}, },
"remix-solidity": { "remix-solidity": {
"tags": [], "tags": [],
"implicitDependencies": ["remix-lib"] "implicitDependencies": [
"remix-lib"
]
}, },
"remix-tests": { "remix-tests": {
"tags": [], "tags": [],
"implicitDependencies": ["remix-lib", "remix-simulator", "remix-solidity"] "implicitDependencies": [
"remix-lib",
"remix-simulator",
"remix-solidity"
]
}, },
"remix-url-resolver": { "remix-url-resolver": {
"tags": [] "tags": []
@ -69,7 +90,9 @@
}, },
"remix-ide-e2e": { "remix-ide-e2e": {
"tags": [], "tags": [],
"implicitDependencies": ["remix-ide"] "implicitDependencies": [
"remix-ide"
]
}, },
"remixd": { "remixd": {
"tags": [] "tags": []
@ -172,6 +195,9 @@
}, },
"remix-ui-tooltip-popup": { "remix-ui-tooltip-popup": {
"tags": [] "tags": []
},
"vyper": {
"tags": []
} }
}, },
"targetDependencies": { "targetDependencies": {
@ -182,4 +208,4 @@
} }
] ]
} }
} }

@ -199,6 +199,7 @@
"react-bootstrap": "^1.6.4", "react-bootstrap": "^1.6.4",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"react-draggable": "^4.4.4", "react-draggable": "^4.4.4",
"react-json-view": "^1.21.3",
"react-tabs": "^3.2.2", "react-tabs": "^3.2.2",
"regenerator-runtime": "0.13.7", "regenerator-runtime": "0.13.7",
"rss-parser": "^3.12.0", "rss-parser": "^3.12.0",

@ -1,5 +1,37 @@
{ {
"version": 1, "version": 1,
"cli": {
"defaultCollection": "@nrwl/react"
},
"defaultProject": "remix-ide",
"schematics": {
"@nrwl/workspace": {
"library": {
"linter": "eslint"
}
},
"@nrwl/react": {
"application": {
"style": "css",
"linter": "eslint",
"strict": true,
"babel": true
},
"component": {
"style": "css"
},
"library": {
"style": "css",
"linter": "eslint",
"strict": true
}
},
"@nrwl/nx-plugin": {
"plugin": {
"linter": "eslint"
}
}
},
"projects": { "projects": {
"remix-ide": { "remix-ide": {
"root": "apps/remix-ide", "root": "apps/remix-ide",
@ -75,7 +107,9 @@
"options": { "options": {
"linter": "eslint", "linter": "eslint",
"config": "apps/remix-ide/.eslintrc", "config": "apps/remix-ide/.eslintrc",
"files": ["apps/remix-ide/src/**/*.js"], "files": [
"apps/remix-ide/src/**/*.js"
],
"exclude": [ "exclude": [
"**/node_modules/**", "**/node_modules/**",
"apps/remix-ide/src/app/editor/mode-solidity.js", "apps/remix-ide/src/app/editor/mode-solidity.js",
@ -94,8 +128,13 @@
"builder": "@nrwl/linter:lint", "builder": "@nrwl/linter:lint",
"options": { "options": {
"linter": "eslint", "linter": "eslint",
"tsConfig": ["apps/remix-ide-e2e/tsconfig.e2e.json"], "tsConfig": [
"exclude": ["**/node_modules/**", "!apps/remix-ide-e2e/**/*"] "apps/remix-ide-e2e/tsconfig.e2e.json"
],
"exclude": [
"**/node_modules/**",
"!apps/remix-ide-e2e/**/*"
]
} }
} }
} }
@ -111,14 +150,21 @@
"options": { "options": {
"linter": "eslint", "linter": "eslint",
"config": "libs/remix-analyzer/.eslintrc", "config": "libs/remix-analyzer/.eslintrc",
"tsConfig": ["libs/remix-analyzer/tsconfig.lib.json"], "tsConfig": [
"exclude": ["**/node_modules/**", "libs/remix-analyzer/test/**/*"] "libs/remix-analyzer/tsconfig.lib.json"
],
"exclude": [
"**/node_modules/**",
"libs/remix-analyzer/test/**/*"
]
} }
}, },
"test": { "test": {
"builder": "@nrwl/workspace:run-commands", "builder": "@nrwl/workspace:run-commands",
"options": { "options": {
"commands": ["./../../node_modules/.bin/npm-run-all test"], "commands": [
"./../../node_modules/.bin/npm-run-all test"
],
"cwd": "libs/remix-analyzer" "cwd": "libs/remix-analyzer"
} }
}, },
@ -129,7 +175,9 @@
"tsConfig": "libs/remix-analyzer/tsconfig.lib.json", "tsConfig": "libs/remix-analyzer/tsconfig.lib.json",
"packageJson": "libs/remix-analyzer/package.json", "packageJson": "libs/remix-analyzer/package.json",
"main": "libs/remix-analyzer/src/index.ts", "main": "libs/remix-analyzer/src/index.ts",
"assets": ["libs/remix-analyzer/*.md"] "assets": [
"libs/remix-analyzer/*.md"
]
} }
} }
} }
@ -145,14 +193,21 @@
"options": { "options": {
"linter": "eslint", "linter": "eslint",
"config": "libs/remix-astwalker/.eslintrc", "config": "libs/remix-astwalker/.eslintrc",
"tsConfig": ["libs/remix-astwalker/tsconfig.lib.json"], "tsConfig": [
"exclude": ["**/node_modules/**", "libs/remix-astwalker/tests/**/*"] "libs/remix-astwalker/tsconfig.lib.json"
],
"exclude": [
"**/node_modules/**",
"libs/remix-astwalker/tests/**/*"
]
} }
}, },
"test": { "test": {
"builder": "@nrwl/workspace:run-commands", "builder": "@nrwl/workspace:run-commands",
"options": { "options": {
"commands": ["./../../node_modules/.bin/npm-run-all test"], "commands": [
"./../../node_modules/.bin/npm-run-all test"
],
"cwd": "libs/remix-astwalker" "cwd": "libs/remix-astwalker"
} }
}, },
@ -163,7 +218,9 @@
"tsConfig": "libs/remix-astwalker/tsconfig.lib.json", "tsConfig": "libs/remix-astwalker/tsconfig.lib.json",
"packageJson": "libs/remix-astwalker/package.json", "packageJson": "libs/remix-astwalker/package.json",
"main": "libs/remix-astwalker/src/index.ts", "main": "libs/remix-astwalker/src/index.ts",
"assets": ["libs/remix-astwalker/*.md"] "assets": [
"libs/remix-astwalker/*.md"
]
} }
} }
} }
@ -179,14 +236,21 @@
"options": { "options": {
"linter": "eslint", "linter": "eslint",
"config": "libs/remix-debug/.eslintrc", "config": "libs/remix-debug/.eslintrc",
"tsConfig": ["libs/remix-debug/tsconfig.lib.json"], "tsConfig": [
"exclude": ["**/node_modules/**", "libs/remix-debug/test/**/*"] "libs/remix-debug/tsconfig.lib.json"
],
"exclude": [
"**/node_modules/**",
"libs/remix-debug/test/**/*"
]
} }
}, },
"test": { "test": {
"builder": "@nrwl/workspace:run-commands", "builder": "@nrwl/workspace:run-commands",
"options": { "options": {
"commands": ["./../../node_modules/.bin/npm-run-all test"], "commands": [
"./../../node_modules/.bin/npm-run-all test"
],
"cwd": "libs/remix-debug" "cwd": "libs/remix-debug"
} }
}, },
@ -224,14 +288,21 @@
"options": { "options": {
"linter": "eslint", "linter": "eslint",
"config": "libs/remix-lib/.eslintrc", "config": "libs/remix-lib/.eslintrc",
"tsConfig": ["libs/remix-lib/tsconfig.lib.json"], "tsConfig": [
"exclude": ["**/node_modules/**", "libs/remix-lib/test/**/*"] "libs/remix-lib/tsconfig.lib.json"
],
"exclude": [
"**/node_modules/**",
"libs/remix-lib/test/**/*"
]
} }
}, },
"test": { "test": {
"builder": "@nrwl/workspace:run-commands", "builder": "@nrwl/workspace:run-commands",
"options": { "options": {
"commands": ["./../../node_modules/.bin/npm-run-all test"], "commands": [
"./../../node_modules/.bin/npm-run-all test"
],
"cwd": "libs/remix-lib" "cwd": "libs/remix-lib"
} }
}, },
@ -242,7 +313,9 @@
"tsConfig": "libs/remix-lib/tsconfig.lib.json", "tsConfig": "libs/remix-lib/tsconfig.lib.json",
"packageJson": "libs/remix-lib/package.json", "packageJson": "libs/remix-lib/package.json",
"main": "libs/remix-lib/src/index.ts", "main": "libs/remix-lib/src/index.ts",
"assets": ["libs/remix-lib/*.md"] "assets": [
"libs/remix-lib/*.md"
]
} }
} }
} }
@ -258,14 +331,21 @@
"options": { "options": {
"linter": "eslint", "linter": "eslint",
"config": "libs/remix-simulator/.eslintrc", "config": "libs/remix-simulator/.eslintrc",
"tsConfig": ["libs/remix-simulator/tsconfig.lib.json"], "tsConfig": [
"exclude": ["**/node_modules/**", "libs/remix-simulator/test/**/*"] "libs/remix-simulator/tsconfig.lib.json"
],
"exclude": [
"**/node_modules/**",
"libs/remix-simulator/test/**/*"
]
} }
}, },
"test": { "test": {
"builder": "@nrwl/workspace:run-commands", "builder": "@nrwl/workspace:run-commands",
"options": { "options": {
"commands": ["./../../node_modules/.bin/npm-run-all test"], "commands": [
"./../../node_modules/.bin/npm-run-all test"
],
"cwd": "libs/remix-simulator" "cwd": "libs/remix-simulator"
} }
}, },
@ -303,8 +383,12 @@
"options": { "options": {
"linter": "eslint", "linter": "eslint",
"config": "libs/remix-solidity/.eslintrc", "config": "libs/remix-solidity/.eslintrc",
"tsConfig": ["libs/remix-solidity/tsconfig.lib.json"], "tsConfig": [
"exclude": ["**/node_modules/**"] "libs/remix-solidity/tsconfig.lib.json"
],
"exclude": [
"**/node_modules/**"
]
} }
}, },
"test": { "test": {
@ -321,7 +405,9 @@
"tsConfig": "libs/remix-solidity/tsconfig.lib.json", "tsConfig": "libs/remix-solidity/tsconfig.lib.json",
"packageJson": "libs/remix-solidity/package.json", "packageJson": "libs/remix-solidity/package.json",
"main": "libs/remix-solidity/src/index.ts", "main": "libs/remix-solidity/src/index.ts",
"assets": ["libs/remix-solidity/*.md"] "assets": [
"libs/remix-solidity/*.md"
]
} }
} }
} }
@ -337,7 +423,9 @@
"options": { "options": {
"linter": "eslint", "linter": "eslint",
"config": "libs/remix-tests/.eslintrc", "config": "libs/remix-tests/.eslintrc",
"tsConfig": ["libs/remix-tests/tsconfig.lib.json"], "tsConfig": [
"libs/remix-tests/tsconfig.lib.json"
],
"exclude": [ "exclude": [
"**/node_modules/**", "**/node_modules/**",
"libs/remix-tests/tests/**/*", "libs/remix-tests/tests/**/*",
@ -386,7 +474,9 @@
"options": { "options": {
"linter": "eslint", "linter": "eslint",
"config": "libs/remix-url-resolver/.eslintrc", "config": "libs/remix-url-resolver/.eslintrc",
"tsConfig": ["libs/remix-url-resolver/tsconfig.lib.json"], "tsConfig": [
"libs/remix-url-resolver/tsconfig.lib.json"
],
"exclude": [ "exclude": [
"**/node_modules/**", "**/node_modules/**",
"libs/remix-url-resolver/tests/**/*" "libs/remix-url-resolver/tests/**/*"
@ -396,7 +486,9 @@
"test": { "test": {
"builder": "@nrwl/workspace:run-commands", "builder": "@nrwl/workspace:run-commands",
"options": { "options": {
"commands": ["./../../node_modules/.bin/npm-run-all test"], "commands": [
"./../../node_modules/.bin/npm-run-all test"
],
"cwd": "libs/remix-url-resolver" "cwd": "libs/remix-url-resolver"
} }
}, },
@ -407,7 +499,9 @@
"tsConfig": "libs/remix-url-resolver/tsconfig.lib.json", "tsConfig": "libs/remix-url-resolver/tsconfig.lib.json",
"packageJson": "libs/remix-url-resolver/package.json", "packageJson": "libs/remix-url-resolver/package.json",
"main": "libs/remix-url-resolver/src/index.ts", "main": "libs/remix-url-resolver/src/index.ts",
"assets": ["libs/remix-url-resolver/*.md"] "assets": [
"libs/remix-url-resolver/*.md"
]
} }
} }
} }
@ -426,7 +520,10 @@
"libs/remixd/tsconfig.lib.json", "libs/remixd/tsconfig.lib.json",
"libs/remixd/tsconfig.spec.json" "libs/remixd/tsconfig.spec.json"
], ],
"exclude": ["**/node_modules/**", "!libs/remixd/**/*"] "exclude": [
"**/node_modules/**",
"!libs/remixd/**/*"
]
} }
}, },
"test": { "test": {
@ -444,7 +541,10 @@
"tsConfig": "libs/remixd/tsconfig.lib.json", "tsConfig": "libs/remixd/tsconfig.lib.json",
"packageJson": "libs/remixd/package.json", "packageJson": "libs/remixd/package.json",
"main": "libs/remixd/src/index.ts", "main": "libs/remixd/src/index.ts",
"assets": ["libs/remixd/*.md", "libs/remixd/src/origins.json"] "assets": [
"libs/remixd/*.md",
"libs/remixd/src/origins.json"
]
} }
} }
} }
@ -463,7 +563,10 @@
"libs/remix-ui/tree-view/tsconfig.lib.json", "libs/remix-ui/tree-view/tsconfig.lib.json",
"libs/remix-ui/tree-view/tsconfig.spec.json" "libs/remix-ui/tree-view/tsconfig.spec.json"
], ],
"exclude": ["**/node_modules/**", "!libs/remix-ui/tree-view/**/*"] "exclude": [
"**/node_modules/**",
"!libs/remix-ui/tree-view/**/*"
]
} }
}, },
"test": { "test": {
@ -490,7 +593,10 @@
"libs/remix-ui/debugger-ui/tsconfig.lib.json", "libs/remix-ui/debugger-ui/tsconfig.lib.json",
"libs/remix-ui/debugger-ui/tsconfig.spec.json" "libs/remix-ui/debugger-ui/tsconfig.spec.json"
], ],
"exclude": ["**/node_modules/**", "!libs/remix-ui/debugger-ui/**/*"] "exclude": [
"**/node_modules/**",
"!libs/remix-ui/debugger-ui/**/*"
]
} }
}, },
"test": { "test": {
@ -517,7 +623,10 @@
"libs/remix-ui/utils/tsconfig.lib.json", "libs/remix-ui/utils/tsconfig.lib.json",
"libs/remix-ui/utils/tsconfig.spec.json" "libs/remix-ui/utils/tsconfig.spec.json"
], ],
"exclude": ["**/node_modules/**", "!libs/remix-ui/utils/**/*"] "exclude": [
"**/node_modules/**",
"!libs/remix-ui/utils/**/*"
]
} }
}, },
"test": { "test": {
@ -544,7 +653,10 @@
"libs/remix-ui/clipboard/tsconfig.lib.json", "libs/remix-ui/clipboard/tsconfig.lib.json",
"libs/remix-ui/clipboard/tsconfig.spec.json" "libs/remix-ui/clipboard/tsconfig.spec.json"
], ],
"exclude": ["**/node_modules/**", "!libs/remix-ui/clipboard/**/*"] "exclude": [
"**/node_modules/**",
"!libs/remix-ui/clipboard/**/*"
]
} }
}, },
"test": { "test": {
@ -597,8 +709,13 @@
"builder": "@nrwl/linter:lint", "builder": "@nrwl/linter:lint",
"options": { "options": {
"linter": "eslint", "linter": "eslint",
"tsConfig": ["libs/remix-ui/toaster/tsconfig.lib.json"], "tsConfig": [
"exclude": ["**/node_modules/**", "!libs/remix-ui/toaster/**/*"] "libs/remix-ui/toaster/tsconfig.lib.json"
],
"exclude": [
"**/node_modules/**",
"!libs/remix-ui/toaster/**/*"
]
} }
} }
} }
@ -666,8 +783,13 @@
"lint": { "lint": {
"builder": "@angular-devkit/build-angular:tslint", "builder": "@angular-devkit/build-angular:tslint",
"options": { "options": {
"tsConfig": ["apps/debugger/tsconfig.app.json"], "tsConfig": [
"exclude": ["**/node_modules/**", "!apps/debugger/**/*"] "apps/debugger/tsconfig.app.json"
],
"exclude": [
"**/node_modules/**",
"!apps/debugger/**/*"
]
} }
} }
} }
@ -682,8 +804,13 @@
"builder": "@nrwl/linter:lint", "builder": "@nrwl/linter:lint",
"options": { "options": {
"linter": "eslint", "linter": "eslint",
"tsConfig": ["libs/remix-ui/workspace/tsconfig.lib.json"], "tsConfig": [
"exclude": ["**/node_modules/**", "!libs/remix-ui/workspace/**/*"] "libs/remix-ui/workspace/tsconfig.lib.json"
],
"exclude": [
"**/node_modules/**",
"!libs/remix-ui/workspace/**/*"
]
} }
} }
} }
@ -698,8 +825,13 @@
"builder": "@nrwl/linter:lint", "builder": "@nrwl/linter:lint",
"options": { "options": {
"linter": "eslint", "linter": "eslint",
"tsConfig": ["libs/remix-ui/settings/tsconfig.lib.json"], "tsConfig": [
"exclude": ["**/node_modules/**", "!libs/remix-ui/settings/**/*"] "libs/remix-ui/settings/tsconfig.lib.json"
],
"exclude": [
"**/node_modules/**",
"!libs/remix-ui/settings/**/*"
]
} }
} }
} }
@ -714,7 +846,9 @@
"builder": "@nrwl/linter:lint", "builder": "@nrwl/linter:lint",
"options": { "options": {
"linter": "eslint", "linter": "eslint",
"tsConfig": ["libs/remix-ui/static-analyser/tsconfig.lib.json"], "tsConfig": [
"libs/remix-ui/static-analyser/tsconfig.lib.json"
],
"exclude": [ "exclude": [
"**/node_modules/**", "**/node_modules/**",
"!libs/remix-ui/static-analyser/**/*" "!libs/remix-ui/static-analyser/**/*"
@ -733,8 +867,13 @@
"builder": "@nrwl/linter:lint", "builder": "@nrwl/linter:lint",
"options": { "options": {
"linter": "eslint", "linter": "eslint",
"tsConfig": ["libs/remix-ui/checkbox/tsconfig.lib.json"], "tsConfig": [
"exclude": ["**/node_modules/**", "!libs/remix-ui/checkbox/**/*"] "libs/remix-ui/checkbox/tsconfig.lib.json"
],
"exclude": [
"**/node_modules/**",
"!libs/remix-ui/checkbox/**/*"
]
} }
} }
} }
@ -749,8 +888,13 @@
"builder": "@nrwl/linter:lint", "builder": "@nrwl/linter:lint",
"options": { "options": {
"linter": "eslint", "linter": "eslint",
"tsConfig": ["libs/remix-ui/terminal/tsconfig.lib.json"], "tsConfig": [
"exclude": ["**/node_modules/**", "!libs/remix-ui/terminal/**/*"] "libs/remix-ui/terminal/tsconfig.lib.json"
],
"exclude": [
"**/node_modules/**",
"!libs/remix-ui/terminal/**/*"
]
} }
} }
} }
@ -765,7 +909,9 @@
"builder": "@nrwl/linter:lint", "builder": "@nrwl/linter:lint",
"options": { "options": {
"linter": "eslint", "linter": "eslint",
"tsConfig": ["libs/remix-ui/plugin-manager/tsconfig.lib.json"], "tsConfig": [
"libs/remix-ui/plugin-manager/tsconfig.lib.json"
],
"exclude": [ "exclude": [
"**/node_modules/**", "**/node_modules/**",
"!libs/remix-ui/plugin-manager/**/*" "!libs/remix-ui/plugin-manager/**/*"
@ -784,8 +930,13 @@
"builder": "@nrwl/linter:lint", "builder": "@nrwl/linter:lint",
"options": { "options": {
"linter": "eslint", "linter": "eslint",
"tsConfig": ["libs/remix-core-plugin/tsconfig.lib.json"], "tsConfig": [
"exclude": ["**/node_modules/**", "!libs/remix-core-plugin/**/*"] "libs/remix-core-plugin/tsconfig.lib.json"
],
"exclude": [
"**/node_modules/**",
"!libs/remix-core-plugin/**/*"
]
} }
}, },
"build": { "build": {
@ -809,7 +960,9 @@
"builder": "@nrwl/linter:lint", "builder": "@nrwl/linter:lint",
"options": { "options": {
"linter": "eslint", "linter": "eslint",
"tsConfig": ["libs/remix-ui/solidity-compiler/tsconfig.lib.json"], "tsConfig": [
"libs/remix-ui/solidity-compiler/tsconfig.lib.json"
],
"exclude": [ "exclude": [
"**/node_modules/**", "**/node_modules/**",
"!libs/remix-ui/solidity-compiler/**/*" "!libs/remix-ui/solidity-compiler/**/*"
@ -828,7 +981,9 @@
"builder": "@nrwl/linter:lint", "builder": "@nrwl/linter:lint",
"options": { "options": {
"linter": "eslint", "linter": "eslint",
"tsConfig": ["libs/remix-ui/publish-to-storage/tsconfig.lib.json"], "tsConfig": [
"libs/remix-ui/publish-to-storage/tsconfig.lib.json"
],
"exclude": [ "exclude": [
"**/node_modules/**", "**/node_modules/**",
"!libs/remix-ui/publish-to-storage/**/*" "!libs/remix-ui/publish-to-storage/**/*"
@ -847,8 +1002,13 @@
"builder": "@nrwl/linter:lint", "builder": "@nrwl/linter:lint",
"options": { "options": {
"linter": "eslint", "linter": "eslint",
"tsConfig": ["libs/remix-ui/renderer/tsconfig.lib.json"], "tsConfig": [
"exclude": ["**/node_modules/**", "!libs/remix-ui/renderer/**/*"] "libs/remix-ui/renderer/tsconfig.lib.json"
],
"exclude": [
"**/node_modules/**",
"!libs/remix-ui/renderer/**/*"
]
} }
} }
} }
@ -872,7 +1032,9 @@
"apps/solidity-compiler/src/assets", "apps/solidity-compiler/src/assets",
"apps/solidity-compiler/src/index.html" "apps/solidity-compiler/src/index.html"
], ],
"styles": ["apps/solidity-compiler/src/styles.css"], "styles": [
"apps/solidity-compiler/src/styles.css"
],
"scripts": [], "scripts": [],
"webpackConfig": "apps/solidity-compiler/webpack.config.js", "webpackConfig": "apps/solidity-compiler/webpack.config.js",
"maxWorkers": 2 "maxWorkers": 2
@ -917,8 +1079,13 @@
"builder": "@nrwl/linter:lint", "builder": "@nrwl/linter:lint",
"options": { "options": {
"linter": "eslint", "linter": "eslint",
"tsConfig": ["apps/solidity-compiler/tsconfig.app.json"], "tsConfig": [
"exclude": ["**/node_modules/**", "!apps/solidity-compiler/**/*"] "apps/solidity-compiler/tsconfig.app.json"
],
"exclude": [
"**/node_modules/**",
"!apps/solidity-compiler/**/*"
]
} }
} }
} }
@ -941,7 +1108,9 @@
"apps/remix-ide-e2e/src/local-plugin/src/favicon.ico", "apps/remix-ide-e2e/src/local-plugin/src/favicon.ico",
"apps/remix-ide-e2e/src/local-plugin/src/assets" "apps/remix-ide-e2e/src/local-plugin/src/assets"
], ],
"styles": ["apps/remix-ide-e2e/src/local-plugin/src/styles.css"], "styles": [
"apps/remix-ide-e2e/src/local-plugin/src/styles.css"
],
"scripts": [], "scripts": [],
"webpackConfig": "@nrwl/react/plugins/webpack" "webpackConfig": "@nrwl/react/plugins/webpack"
}, },
@ -1007,8 +1176,13 @@
"builder": "@nrwl/linter:lint", "builder": "@nrwl/linter:lint",
"options": { "options": {
"linter": "eslint", "linter": "eslint",
"tsConfig": ["libs/remix-ui/home-tab/tsconfig.lib.json"], "tsConfig": [
"exclude": ["**/node_modules/**", "!libs/remix-ui/home-tab/**/*"] "libs/remix-ui/home-tab/tsconfig.lib.json"
],
"exclude": [
"**/node_modules/**",
"!libs/remix-ui/home-tab/**/*"
]
} }
} }
} }
@ -1023,8 +1197,13 @@
"builder": "@nrwl/linter:lint", "builder": "@nrwl/linter:lint",
"options": { "options": {
"linter": "eslint", "linter": "eslint",
"tsConfig": ["libs/remix-ui/editor/tsconfig.lib.json"], "tsConfig": [
"exclude": ["**/node_modules/**", "!libs/remix-ui/editor/**/*"] "libs/remix-ui/editor/tsconfig.lib.json"
],
"exclude": [
"**/node_modules/**",
"!libs/remix-ui/editor/**/*"
]
} }
} }
} }
@ -1038,8 +1217,13 @@
"builder": "@nrwl/linter:lint", "builder": "@nrwl/linter:lint",
"options": { "options": {
"linter": "eslint", "linter": "eslint",
"tsConfig": ["libs/remix-ui/editor/tsconfig.lib.json"], "tsConfig": [
"exclude": ["**/node_modules/**", "!libs/remix-ui/editor/**/*"] "libs/remix-ui/editor/tsconfig.lib.json"
],
"exclude": [
"**/node_modules/**",
"!libs/remix-ui/editor/**/*"
]
} }
} }
} }
@ -1054,8 +1238,13 @@
"builder": "@nrwl/linter:lint", "builder": "@nrwl/linter:lint",
"options": { "options": {
"linter": "eslint", "linter": "eslint",
"tsConfig": ["libs/remix-ui/app/tsconfig.lib.json"], "tsConfig": [
"exclude": ["**/node_modules/**", "!libs/remix-ui/app/**/*"] "libs/remix-ui/app/tsconfig.lib.json"
],
"exclude": [
"**/node_modules/**",
"!libs/remix-ui/app/**/*"
]
} }
} }
} }
@ -1070,8 +1259,13 @@
"builder": "@nrwl/linter:lint", "builder": "@nrwl/linter:lint",
"options": { "options": {
"linter": "eslint", "linter": "eslint",
"tsConfig": ["libs/remix-ui/helper/tsconfig.lib.json"], "tsConfig": [
"exclude": ["**/node_modules/**", "!libs/remix-ui/helper/**/*"] "libs/remix-ui/helper/tsconfig.lib.json"
],
"exclude": [
"**/node_modules/**",
"!libs/remix-ui/helper/**/*"
]
} }
} }
} }
@ -1085,8 +1279,13 @@
"builder": "@nrwl/linter:lint", "builder": "@nrwl/linter:lint",
"options": { "options": {
"linter": "eslint", "linter": "eslint",
"tsConfig": ["libs/remix-ui/tabs/tsconfig.lib.json"], "tsConfig": [
"exclude": ["**/node_modules/**", "!libs/remix-ui/tabs/**/*"] "libs/remix-ui/tabs/tsconfig.lib.json"
],
"exclude": [
"**/node_modules/**",
"!libs/remix-ui/tabs/**/*"
]
} }
} }
} }
@ -1120,8 +1319,13 @@
"builder": "@nrwl/linter:lint", "builder": "@nrwl/linter:lint",
"options": { "options": {
"linter": "eslint", "linter": "eslint",
"tsConfig": ["libs/remix-ui/search/tsconfig.lib.json"], "tsConfig": [
"exclude": ["**/node_modules/**", "!libs/remix-ui/search/**/*"] "libs/remix-ui/search/tsconfig.lib.json"
],
"exclude": [
"**/node_modules/**",
"!libs/remix-ui/search/**/*"
]
} }
} }
} }
@ -1135,8 +1339,13 @@
"builder": "@nrwl/linter:lint", "builder": "@nrwl/linter:lint",
"options": { "options": {
"linter": "eslint", "linter": "eslint",
"tsConfig": ["libs/remix-ui/panel/tsconfig.lib.json"], "tsConfig": [
"exclude": ["**/node_modules/**", "!libs/remix-ui/panel/**/*"] "libs/remix-ui/panel/tsconfig.lib.json"
],
"exclude": [
"**/node_modules/**",
"!libs/remix-ui/panel/**/*"
]
} }
} }
} }
@ -1170,7 +1379,9 @@
"builder": "@nrwl/linter:lint", "builder": "@nrwl/linter:lint",
"options": { "options": {
"linter": "eslint", "linter": "eslint",
"tsConfig": ["libs/remix-ui/editor-context-view/tsconfig.lib.json"], "tsConfig": [
"libs/remix-ui/editor-context-view/tsconfig.lib.json"
],
"exclude": [ "exclude": [
"**/node_modules/**", "**/node_modules/**",
"!libs/remix-ui/editor-context-view/**/*" "!libs/remix-ui/editor-context-view/**/*"
@ -1188,7 +1399,9 @@
"builder": "@nrwl/linter:lint", "builder": "@nrwl/linter:lint",
"options": { "options": {
"linter": "eslint", "linter": "eslint",
"tsConfig": ["libs/remix-ui/run-tab/tsconfig.lib.json"], "tsConfig": [
"libs/remix-ui/run-tab/tsconfig.lib.json"
],
"exclude": [ "exclude": [
"**/node_modules/**", "**/node_modules/**",
"libs/remix-ui/run-tab/**/*.d.ts", "libs/remix-ui/run-tab/**/*.d.ts",
@ -1207,7 +1420,9 @@
"builder": "@nrwl/linter:lint", "builder": "@nrwl/linter:lint",
"options": { "options": {
"linter": "eslint", "linter": "eslint",
"tsConfig": ["libs/remix-ui/permission-handler/tsconfig.lib.json"], "tsConfig": [
"libs/remix-ui/permission-handler/tsconfig.lib.json"
],
"exclude": [ "exclude": [
"**/node_modules/**", "**/node_modules/**",
"libs/remix-ui/permission-handler/**/*.d.ts", "libs/remix-ui/permission-handler/**/*.d.ts",
@ -1227,8 +1442,12 @@
"options": { "options": {
"linter": "eslint", "linter": "eslint",
"config": "libs/remix-ws-templates/.eslintrc", "config": "libs/remix-ws-templates/.eslintrc",
"tsConfig": ["libs/remix-ws-templates/tsconfig.lib.json"], "tsConfig": [
"exclude": ["**/node_modules/**"] "libs/remix-ws-templates/tsconfig.lib.json"
],
"exclude": [
"**/node_modules/**"
]
} }
}, },
"build": { "build": {
@ -1241,7 +1460,9 @@
"assets": [ "assets": [
{ {
"glob": "templates/**/*", "glob": "templates/**/*",
"ignore": ["templates/**/*/index.ts"], "ignore": [
"templates/**/*/index.ts"
],
"input": "libs/remix-ws-templates/src/", "input": "libs/remix-ws-templates/src/",
"output": "src/" "output": "src/"
}, },
@ -1265,41 +1486,94 @@
"options": { "options": {
"linter": "eslint", "linter": "eslint",
"config": "libs/remix-ui/tooltip-popup/.eslintrc.json", "config": "libs/remix-ui/tooltip-popup/.eslintrc.json",
"tsConfig": ["libs/remix-ui/tooltip-popup/tsconfig.lib.json"], "tsConfig": [
"exclude": ["**/node_modules/**"] "libs/remix-ui/tooltip-popup/tsconfig.lib.json"
],
"exclude": [
"**/node_modules/**"
]
} }
} }
} }
}
},
"cli": {
"defaultCollection": "@nrwl/react"
},
"schematics": {
"@nrwl/workspace": {
"library": {
"linter": "eslint"
}
},
"@nrwl/react": {
"application": {
"style": "css",
"linter": "eslint",
"babel": true
},
"component": {
"style": "css"
},
"library": {
"style": "css",
"linter": "eslint"
}
}, },
"@nrwl/nx-plugin": { "vyper": {
"plugin": { "root": "apps/vyper",
"linter": "eslint" "sourceRoot": "apps/vyper/src",
"projectType": "application",
"architect": {
"build": {
"builder": "@nrwl/web:build",
"outputs": [
"{options.outputPath}"
],
"options": {
"outputPath": "dist/apps/vyper",
"index": "apps/vyper/src/index.html",
"main": "apps/vyper/src/main.tsx",
"polyfills": "apps/vyper/src/polyfills.ts",
"tsConfig": "apps/vyper/tsconfig.app.json",
"assets": [
"apps/vyper/src/favicon.ico",
"apps/vyper/src/assets"
],
"styles": [
"apps/vyper/src/styles.css"
],
"scripts": [],
"webpackConfig": "@nrwl/react/plugins/webpack"
},
"configurations": {
"production": {
"fileReplacements": [
{
"replace": "apps/vyper/src/environments/environment.ts",
"with": "apps/vyper/src/environments/environment.prod.ts"
}
],
"optimization": true,
"outputHashing": "all",
"sourceMap": false,
"extractCss": true,
"namedChunks": false,
"extractLicenses": true,
"vendorChunk": false,
"budgets": [
{
"type": "initial",
"maximumWarning": "500kb",
"maximumError": "1mb"
}
]
}
}
},
"serve": {
"builder": "@nrwl/web:dev-server",
"options": {
"buildTarget": "vyper:build",
"port": 5002
},
"configurations": {
"production": {
"buildTarget": "vyper:build:production",
"hmr": false
}
}
},
"lint": {
"builder": "@nrwl/linter:lint",
"options": {
"linter": "eslint",
"config": "apps/vyper/.eslintrc",
"files": [
"apps/vyper/src/**/*.js", "apps/vyper/src/**/*.ts"
],
"exclude": [
"**/node_modules/**"
]
}
}
} }
} }
}, }
"defaultProject": "remix-ide" }
}

@ -5359,6 +5359,11 @@ asap@^2.0.0, asap@~2.0.5:
resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.5.tgz#522765b50c3510490e52d7dcfe085ef9ba96958f" resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.5.tgz#522765b50c3510490e52d7dcfe085ef9ba96958f"
integrity sha512-7Ql0Lz9sffzP1jDkhjeju5/6z0LnwZAMZdlJoTe2GghKnYNA+H1rZOD8rWx4b9EBjux0kJq66igvQkANmbWnKg== integrity sha512-7Ql0Lz9sffzP1jDkhjeju5/6z0LnwZAMZdlJoTe2GghKnYNA+H1rZOD8rWx4b9EBjux0kJq66igvQkANmbWnKg==
asap@~2.0.3:
version "2.0.6"
resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
integrity sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==
asn1.js@^5.2.0: asn1.js@^5.2.0:
version "5.4.1" version "5.4.1"
resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07" resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07"
@ -6325,6 +6330,11 @@ base-x@^3.0.2, base-x@^3.0.8:
dependencies: dependencies:
safe-buffer "^5.0.1" safe-buffer "^5.0.1"
base16@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/base16/-/base16-1.0.0.tgz#e297f60d7ec1014a7a971a39ebc8a98c0b681e70"
integrity sha512-pNdYkNPiJUnEhnfXV56+sQy8+AaPcG3POZAUnwr4EeqCUZFz4u2PePbo3e5Gj4ziYPCWGUZT9RHisvJKnwFuBQ==
base64-js@^1.0.2, base64-js@^1.3.1: base64-js@^1.0.2, base64-js@^1.3.1:
version "1.5.1" version "1.5.1"
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
@ -8385,6 +8395,13 @@ cross-blob@^2.0.1:
blob-polyfill "^5.0.20210201" blob-polyfill "^5.0.20210201"
fetch-blob "^2.1.2" fetch-blob "^2.1.2"
cross-fetch@^3.1.5:
version "3.1.5"
resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f"
integrity sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw==
dependencies:
node-fetch "2.6.7"
cross-spawn-async@^2.1.1: cross-spawn-async@^2.1.1:
version "2.2.5" version "2.2.5"
resolved "https://registry.yarnpkg.com/cross-spawn-async/-/cross-spawn-async-2.2.5.tgz#845ff0c0834a3ded9d160daca6d390906bb288cc" resolved "https://registry.yarnpkg.com/cross-spawn-async/-/cross-spawn-async-2.2.5.tgz#845ff0c0834a3ded9d160daca6d390906bb288cc"
@ -10577,6 +10594,31 @@ fb-watchman@^2.0.0:
dependencies: dependencies:
bser "2.1.1" bser "2.1.1"
fbemitter@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/fbemitter/-/fbemitter-3.0.0.tgz#00b2a1af5411254aab416cd75f9e6289bee4bff3"
integrity sha512-KWKaceCwKQU0+HPoop6gn4eOHk50bBv/VxjJtGMfwmJt3D29JpN4H4eisCtIPA+a8GVBam+ldMMpMjJUvpDyHw==
dependencies:
fbjs "^3.0.0"
fbjs-css-vars@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/fbjs-css-vars/-/fbjs-css-vars-1.0.2.tgz#216551136ae02fe255932c3ec8775f18e2c078b8"
integrity sha512-b2XGFAFdWZWg0phtAWLHCk836A1Xann+I+Dgd3Gk64MHKZO44FfoD1KxyvbSh0qZsIoXQGGlVztIY+oitJPpRQ==
fbjs@^3.0.0, fbjs@^3.0.1:
version "3.0.4"
resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-3.0.4.tgz#e1871c6bd3083bac71ff2da868ad5067d37716c6"
integrity sha512-ucV0tDODnGV3JCnnkmoszb5lf4bNpzjv80K41wd4k798Etq+UYD0y0TIfalLjZoKgjive6/adkRnszwapiDgBQ==
dependencies:
cross-fetch "^3.1.5"
fbjs-css-vars "^1.0.0"
loose-envify "^1.0.0"
object-assign "^4.1.0"
promise "^7.1.1"
setimmediate "^1.0.5"
ua-parser-js "^0.7.30"
fd-slicer@~1.1.0: fd-slicer@~1.1.0:
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e"
@ -10864,6 +10906,14 @@ flush-write-stream@^1.0.0, flush-write-stream@^1.0.2:
inherits "^2.0.3" inherits "^2.0.3"
readable-stream "^2.3.6" readable-stream "^2.3.6"
flux@^4.0.1:
version "4.0.3"
resolved "https://registry.yarnpkg.com/flux/-/flux-4.0.3.tgz#573b504a24982c4768fdfb59d8d2ea5637d72ee7"
integrity sha512-yKAbrp7JhZhj6uiT1FTuVMlIAT1J4jqEyBpFApi1kxpGZCvacMVc/t1pMQyotqHhAgvoE3bNvAykhCo2CLjnYw==
dependencies:
fbemitter "^3.0.0"
fbjs "^3.0.1"
fn.name@1.x.x: fn.name@1.x.x:
version "1.1.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/fn.name/-/fn.name-1.1.0.tgz#26cad8017967aea8731bc42961d04a3d5988accc" resolved "https://registry.yarnpkg.com/fn.name/-/fn.name-1.1.0.tgz#26cad8017967aea8731bc42961d04a3d5988accc"
@ -15082,6 +15132,11 @@ lodash.clonedeep@^4.5.0, lodash.clonedeep@~4.5.0:
resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef"
integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= integrity sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=
lodash.curry@^4.0.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/lodash.curry/-/lodash.curry-4.1.1.tgz#248e36072ede906501d75966200a86dab8b23170"
integrity sha512-/u14pXGviLaweY5JI0IUzgzF2J6Ne8INyzAZjImcryjgkZ+ebruBxy2/JaOOkTqScddcYtakjhSaeemV8lR0tA==
lodash.debounce@^4.0.8: lodash.debounce@^4.0.8:
version "4.0.8" version "4.0.8"
resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
@ -15097,6 +15152,11 @@ lodash.flattendeep@^4.4.0:
resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2" resolved "https://registry.yarnpkg.com/lodash.flattendeep/-/lodash.flattendeep-4.4.0.tgz#fb030917f86a3134e5bc9bec0d69e0013ddfedb2"
integrity sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI= integrity sha1-+wMJF/hqMTTlvJvsDWngAT3f7bI=
lodash.flow@^3.3.0:
version "3.5.0"
resolved "https://registry.yarnpkg.com/lodash.flow/-/lodash.flow-3.5.0.tgz#87bf40292b8cf83e4e8ce1a3ae4209e20071675a"
integrity sha512-ff3BX/tSioo+XojX4MOsOMhJw0nZoUEF011LX8g8d3gvjVbxd89cCio4BCXronjxcTUIJUoqKEUA+n4CqvvRPw==
lodash.get@^4.4.2: lodash.get@^4.4.2:
version "4.4.2" version "4.4.2"
resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99"
@ -16600,6 +16660,13 @@ node-fetch-npm@^2.0.2:
json-parse-better-errors "^1.0.0" json-parse-better-errors "^1.0.0"
safe-buffer "^5.1.1" safe-buffer "^5.1.1"
node-fetch@2.6.7, node-fetch@^2.5.0, node-fetch@^2.6.0, node-fetch@^2.6.1:
version "2.6.7"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad"
integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==
dependencies:
whatwg-url "^5.0.0"
node-fetch@3.0.0-beta.9: node-fetch@3.0.0-beta.9:
version "3.0.0-beta.9" version "3.0.0-beta.9"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.0.0-beta.9.tgz#0a7554cfb824380dd6812864389923c783c80d9b" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.0.0-beta.9.tgz#0a7554cfb824380dd6812864389923c783c80d9b"
@ -16608,13 +16675,6 @@ node-fetch@3.0.0-beta.9:
data-uri-to-buffer "^3.0.1" data-uri-to-buffer "^3.0.1"
fetch-blob "^2.1.1" fetch-blob "^2.1.1"
node-fetch@^2.5.0, node-fetch@^2.6.0, node-fetch@^2.6.1:
version "2.6.7"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad"
integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==
dependencies:
whatwg-url "^5.0.0"
node-forge@^0.10.0: node-forge@^0.10.0:
version "0.10.0" version "0.10.0"
resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3" resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3"
@ -18833,6 +18893,13 @@ promise.series@^0.2.0:
resolved "https://registry.yarnpkg.com/promise.series/-/promise.series-0.2.0.tgz#2cc7ebe959fc3a6619c04ab4dbdc9e452d864bbd" resolved "https://registry.yarnpkg.com/promise.series/-/promise.series-0.2.0.tgz#2cc7ebe959fc3a6619c04ab4dbdc9e452d864bbd"
integrity sha1-LMfr6Vn8OmYZwEq029yeRS2GS70= integrity sha1-LMfr6Vn8OmYZwEq029yeRS2GS70=
promise@^7.1.1:
version "7.3.1"
resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf"
integrity sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==
dependencies:
asap "~2.0.3"
prompts@^2.0.1: prompts@^2.0.1:
version "2.4.2" version "2.4.2"
resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069"
@ -19021,6 +19088,11 @@ pupa@^2.1.1:
dependencies: dependencies:
escape-goat "^2.0.0" escape-goat "^2.0.0"
pure-color@^1.2.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/pure-color/-/pure-color-1.3.0.tgz#1fe064fb0ac851f0de61320a8bf796836422f33e"
integrity sha512-QFADYnsVoBMw1srW7OVKEYjG+MbIa49s54w1MA1EDY6r2r/sTcKKYqRX1f4GYvnXP7eN/Pe9HFcX+hwzmrXRHA==
q@^1.1.2, q@^1.5.1: q@^1.1.2, q@^1.5.1:
version "1.5.1" version "1.5.1"
resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7"
@ -19202,6 +19274,16 @@ re-emitter@1.1.3:
resolved "https://registry.yarnpkg.com/re-emitter/-/re-emitter-1.1.3.tgz#fa9e319ffdeeeb35b27296ef0f3d374dac2f52a7" resolved "https://registry.yarnpkg.com/re-emitter/-/re-emitter-1.1.3.tgz#fa9e319ffdeeeb35b27296ef0f3d374dac2f52a7"
integrity sha1-+p4xn/3u6zWycpbvDz03TawvUqc= integrity sha1-+p4xn/3u6zWycpbvDz03TawvUqc=
react-base16-styling@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/react-base16-styling/-/react-base16-styling-0.6.0.tgz#ef2156d66cf4139695c8a167886cb69ea660792c"
integrity sha512-yvh/7CArceR/jNATXOKDlvTnPKPmGZz7zsenQ3jUwLzHkNUR0CvY3yGYJbWJ/nnxsL8Sgmt5cO3/SILVuPO6TQ==
dependencies:
base16 "^1.0.0"
lodash.curry "^4.0.1"
lodash.flow "^3.3.0"
pure-color "^1.2.0"
react-beautiful-dnd@^13.1.0: react-beautiful-dnd@^13.1.0:
version "13.1.0" version "13.1.0"
resolved "https://registry.yarnpkg.com/react-beautiful-dnd/-/react-beautiful-dnd-13.1.0.tgz#ec97c81093593526454b0de69852ae433783844d" resolved "https://registry.yarnpkg.com/react-beautiful-dnd/-/react-beautiful-dnd-13.1.0.tgz#ec97c81093593526454b0de69852ae433783844d"
@ -19265,6 +19347,16 @@ react-is@^17.0.1, react-is@^17.0.2:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
react-json-view@^1.21.3:
version "1.21.3"
resolved "https://registry.yarnpkg.com/react-json-view/-/react-json-view-1.21.3.tgz#f184209ee8f1bf374fb0c41b0813cff54549c475"
integrity sha512-13p8IREj9/x/Ye4WI/JpjhoIwuzEgUAtgJZNBJckfzJt1qyh24BdTm6UQNGnyTq9dapQdrqvquZTo3dz1X6Cjw==
dependencies:
flux "^4.0.1"
react-base16-styling "^0.6.0"
react-lifecycles-compat "^3.0.4"
react-textarea-autosize "^8.3.2"
react-lifecycles-compat@^3.0.4: react-lifecycles-compat@^3.0.4:
version "3.0.4" version "3.0.4"
resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362"
@ -19314,6 +19406,15 @@ react-tabs@^3.2.2:
clsx "^1.1.0" clsx "^1.1.0"
prop-types "^15.5.0" prop-types "^15.5.0"
react-textarea-autosize@^8.3.2:
version "8.3.4"
resolved "https://registry.yarnpkg.com/react-textarea-autosize/-/react-textarea-autosize-8.3.4.tgz#270a343de7ad350534141b02c9cb78903e553524"
integrity sha512-CdtmP8Dc19xL8/R6sWvtknD/eCXkQr30dtvC4VmGInhRsfF8X/ihXCq6+9l9qbxmKRiq407/7z5fxE7cVWQNgQ==
dependencies:
"@babel/runtime" "^7.10.2"
use-composed-ref "^1.3.0"
use-latest "^1.2.1"
react-transition-group@^4.4.1: react-transition-group@^4.4.1:
version "4.4.2" version "4.4.2"
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.2.tgz#8b59a56f09ced7b55cbd53c36768b922890d5470" resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.2.tgz#8b59a56f09ced7b55cbd53c36768b922890d5470"
@ -22580,6 +22681,11 @@ typescript@^4.4.3:
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.4.4.tgz#2cd01a1a1f160704d3101fd5a58ff0f9fcb8030c" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.4.4.tgz#2cd01a1a1f160704d3101fd5a58ff0f9fcb8030c"
integrity sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA== integrity sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA==
ua-parser-js@^0.7.30:
version "0.7.31"
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.31.tgz#649a656b191dffab4f21d5e053e27ca17cbff5c6"
integrity sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ==
uglify-js@^2.8.16: uglify-js@^2.8.16:
version "2.8.29" version "2.8.29"
resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd" resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd"
@ -22966,6 +23072,23 @@ url@^0.11.0, url@~0.11.0:
punycode "1.3.2" punycode "1.3.2"
querystring "0.2.0" querystring "0.2.0"
use-composed-ref@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/use-composed-ref/-/use-composed-ref-1.3.0.tgz#3d8104db34b7b264030a9d916c5e94fbe280dbda"
integrity sha512-GLMG0Jc/jiKov/3Ulid1wbv3r54K9HlMW29IWcDFPEqFkSO2nS0MuefWgMJpeHQ9YJeXDL3ZUF+P3jdXlZX/cQ==
use-isomorphic-layout-effect@^1.1.1:
version "1.1.2"
resolved "https://registry.yarnpkg.com/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.2.tgz#497cefb13d863d687b08477d9e5a164ad8c1a6fb"
integrity sha512-49L8yCO3iGT/ZF9QttjwLF/ZD9Iwto5LnH5LmEdk/6cFmXddqi2ulF0edxTwjj+7mqvpVVGQWvbXZdn32wRSHA==
use-latest@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/use-latest/-/use-latest-1.2.1.tgz#d13dfb4b08c28e3e33991546a2cee53e14038cf2"
integrity sha512-xA+AVm/Wlg3e2P/JiItTziwS7FK92LWrDB0p+hgXloIMuVCeJJ8v6f0eeHyPZaJrM+usM1FkFfbNCrJGs8A/zw==
dependencies:
use-isomorphic-layout-effect "^1.1.1"
use-memo-one@^1.1.1: use-memo-one@^1.1.1:
version "1.1.2" version "1.1.2"
resolved "https://registry.yarnpkg.com/use-memo-one/-/use-memo-one-1.1.2.tgz#0c8203a329f76e040047a35a1197defe342fab20" resolved "https://registry.yarnpkg.com/use-memo-one/-/use-memo-one-1.1.2.tgz#0c8203a329f76e040047a35a1197defe342fab20"

Loading…
Cancel
Save