Merge pull request #4010 from ethereum/monacoverUpdate

Monaco editor version update
pull/4036/head
Aniket 1 year ago committed by GitHub
commit 52e5252987
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      apps/remix-ide-e2e/src/commands/checkAnnotations.ts
  2. 6
      apps/remix-ide-e2e/src/commands/checkAnnotationsNotPresent.ts
  3. 132
      apps/remix-ide-e2e/src/tests/editor.test.ts
  4. 90
      apps/remix-ide-e2e/src/tests/editorReferences.test.ts
  5. 186
      apps/remix-ide-e2e/src/types/index.d.ts
  6. 85
      libs/remix-ui/editor/src/lib/providers/codeActionProvider.ts
  7. 4
      libs/remix-ui/editor/src/lib/remix-ui-editor.css
  8. 16
      libs/remix-ui/editor/src/lib/remix-ui-editor.tsx
  9. 16352
      libs/remix-ui/editor/src/types/monaco.ts
  10. 4
      package.json
  11. 27
      yarn.lock

@ -1,9 +1,9 @@
import EventEmitter from 'events' import EventEmitter from 'events'
import { NightwatchBrowser } from 'nightwatch' import {NightwatchBrowser} from 'nightwatch'
class checkAnnotations extends EventEmitter { class checkAnnotations extends EventEmitter {
command (this: NightwatchBrowser, type: string, line: number): NightwatchBrowser { command(this: NightwatchBrowser, type: string): NightwatchBrowser {
this.api.assert.containsText(`.margin-view-overlays .${type} + div`, line.toString()).perform(() => this.emit('complete')) this.api.waitForElementPresent(`.glyph-margin-widgets .${type}`).perform(() => this.emit('complete'))
return this return this
} }
} }

@ -1,9 +1,9 @@
import EventEmitter from 'events' import EventEmitter from 'events'
import { NightwatchBrowser } from 'nightwatch' import {NightwatchBrowser} from 'nightwatch'
class checkAnnotationsNotPresent extends EventEmitter { class checkAnnotationsNotPresent extends EventEmitter {
command (this: NightwatchBrowser, type: string): NightwatchBrowser { command(this: NightwatchBrowser, type: string): NightwatchBrowser {
this.api.waitForElementNotPresent(`.margin-view-overlays .${type}`).perform(() => this.emit('complete')) this.api.waitForElementNotPresent(`.glyph-margin-widgets .${type}`).perform(() => this.emit('complete'))
return this return this
} }
} }

@ -1,16 +1,17 @@
'use strict' 'use strict'
import { NightwatchBrowser } from 'nightwatch' import {NightwatchBrowser} from 'nightwatch'
import init from '../helpers/init' import init from '../helpers/init'
module.exports = { module.exports = {
'@disabled': true, '@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 zoom in editor #group1': function (browser: NightwatchBrowser) { 'Should zoom in editor #group1': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('div[data-id="mainPanelPluginsContainer"]') browser
.waitForElementVisible('div[data-id="mainPanelPluginsContainer"]')
.clickLaunchIcon('filePanel') .clickLaunchIcon('filePanel')
.waitForElementVisible('div[data-id="filePanelFileExplorerTree"]') .waitForElementVisible('div[data-id="filePanelFileExplorerTree"]')
.openFile('contracts') .openFile('contracts')
@ -23,7 +24,8 @@ module.exports = {
}, },
'Should zoom out editor #group1': function (browser: NightwatchBrowser) { 'Should zoom out editor #group1': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('#editorView') browser
.waitForElementVisible('#editorView')
.checkElementStyle('.view-lines', 'font-size', '16px') .checkElementStyle('.view-lines', 'font-size', '16px')
.click('*[data-id="tabProxyZoomOut"]') .click('*[data-id="tabProxyZoomOut"]')
.click('*[data-id="tabProxyZoomOut"]') .click('*[data-id="tabProxyZoomOut"]')
@ -31,57 +33,72 @@ module.exports = {
}, },
'Should display compile error in editor #group1': function (browser: NightwatchBrowser) { 'Should display compile error in editor #group1': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('#editorView') browser
.waitForElementVisible('#editorView')
.setEditorValue(storageContractWithError + 'error') .setEditorValue(storageContractWithError + 'error')
.pause(2000) .pause(2000)
.waitForElementVisible('.margin-view-overlays .fa-exclamation-square', 120000) .waitForElementVisible('.glyph-margin-widgets .fa-exclamation-square', 120000)
.checkAnnotations('fa-exclamation-square', 29) // error .checkAnnotations('fa-exclamation-square') // error
.clickLaunchIcon('udapp') .clickLaunchIcon('udapp')
.checkAnnotationsNotPresent('fa-exclamation-square') // error .checkAnnotationsNotPresent('fa-exclamation-square') // error
.clickLaunchIcon('solidity') .clickLaunchIcon('solidity')
.checkAnnotations('fa-exclamation-square', 29) // error .checkAnnotations('fa-exclamation-square') // error
}, },
'Should minimize and maximize codeblock in editor #group1': '' + function (browser: NightwatchBrowser) { 'Should minimize and maximize codeblock in editor #group1':
browser.waitForElementVisible('#editorView') '' +
.waitForElementVisible('.ace_open') function (browser: NightwatchBrowser) {
.click('.ace_start:nth-of-type(1)') browser
.waitForElementVisible('.ace_closed') .waitForElementVisible('#editorView')
.click('.ace_start:nth-of-type(1)') .waitForElementVisible('.ace_open')
.waitForElementVisible('.ace_open') .click('.ace_start:nth-of-type(1)')
}, .waitForElementVisible('.ace_closed')
.click('.ace_start:nth-of-type(1)')
.waitForElementVisible('.ace_open')
},
'Should add breakpoint to editor #group1': function (browser: NightwatchBrowser) { 'Should add breakpoint to editor #group1': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('#editorView') browser
.waitForElementNotPresent('.margin-view-overlays .fa-circle') .waitForElementVisible('#editorView')
.execute(() => { .waitForElementNotPresent('.glyph-margin-widgets .fa-circle')
(window as any).addRemixBreakpoint(1) .execute(
}, [], () => {}) () => {
.waitForElementVisible('.margin-view-overlays .fa-circle') ;(window as any).addRemixBreakpoint(1)
}, },
[],
'Should load syntax highlighter for ace light theme #group1': '' + function (browser: NightwatchBrowser) { () => {}
browser.waitForElementVisible('#editorView') )
.checkElementStyle('.ace_keyword', 'color', aceThemes.light.keyword) .waitForElementVisible('.glyph-margin-widgets .fa-circle')
.checkElementStyle('.ace_comment.ace_doc', 'color', aceThemes.light.comment)
.checkElementStyle('.ace_function', 'color', aceThemes.light.function)
.checkElementStyle('.ace_variable', 'color', aceThemes.light.variable)
}, },
'Should load syntax highlighter for ace dark theme #group1': '' + function (browser: NightwatchBrowser) { 'Should load syntax highlighter for ace light theme #group1':
browser.waitForElementVisible('*[data-id="verticalIconsKindsettings"]') '' +
.click('*[data-id="verticalIconsKindsettings"]') function (browser: NightwatchBrowser) {
.waitForElementVisible('*[data-id="settingsTabThemeLabelDark"]') browser
.click('*[data-id="settingsTabThemeLabelDark"]') .waitForElementVisible('#editorView')
.pause(2000) .checkElementStyle('.ace_keyword', 'color', aceThemes.light.keyword)
.waitForElementVisible('#editorView') .checkElementStyle('.ace_comment.ace_doc', 'color', aceThemes.light.comment)
/* @todo(#2863) ch for class and not colors .checkElementStyle('.ace_function', 'color', aceThemes.light.function)
.checkElementStyle('.ace_variable', 'color', aceThemes.light.variable)
},
'Should load syntax highlighter for ace dark theme #group1':
'' +
function (browser: NightwatchBrowser) {
browser
.waitForElementVisible('*[data-id="verticalIconsKindsettings"]')
.click('*[data-id="verticalIconsKindsettings"]')
.waitForElementVisible('*[data-id="settingsTabThemeLabelDark"]')
.click('*[data-id="settingsTabThemeLabelDark"]')
.pause(2000)
.waitForElementVisible('#editorView')
/* @todo(#2863) ch for class and not colors
.checkElementStyle('.ace_keyword', 'color', aceThemes.dark.keyword) .checkElementStyle('.ace_keyword', 'color', aceThemes.dark.keyword)
.checkElementStyle('.ace_comment.ace_doc', 'color', aceThemes.dark.comment) .checkElementStyle('.ace_comment.ace_doc', 'color', aceThemes.dark.comment)
.checkElementStyle('.ace_function', 'color', aceThemes.dark.function) .checkElementStyle('.ace_function', 'color', aceThemes.dark.function)
.checkElementStyle('.ace_variable', 'color', aceThemes.dark.variable) .checkElementStyle('.ace_variable', 'color', aceThemes.dark.variable)
*/ */
}, },
'Should highlight source code #group1': function (browser: NightwatchBrowser) { 'Should highlight source code #group1': function (browser: NightwatchBrowser) {
// include all files here because switching between plugins in side-panel removes highlight // include all files here because switching between plugins in side-panel removes highlight
@ -101,22 +118,26 @@ module.exports = {
.checkElementStyle('.highlightLine51', 'background-color', 'rgb(52, 152, 219)') .checkElementStyle('.highlightLine51', 'background-color', 'rgb(52, 152, 219)')
}, },
'Should remove 1 highlight from source code #group1': '' + function (browser: NightwatchBrowser) { 'Should remove 1 highlight from source code #group1':
browser.waitForElementVisible('li[data-id="treeViewLitreeViewItemremoveSourcehighlightScript.js"]') '' +
.click('li[data-id="treeViewLitreeViewItemremoveSourcehighlightScript.js"]') function (browser: NightwatchBrowser) {
.pause(2000) browser
.executeScriptInTerminal('remix.exeCurrent()') .waitForElementVisible('li[data-id="treeViewLitreeViewItemremoveSourcehighlightScript.js"]')
.waitForElementVisible('li[data-id="treeViewLitreeViewItemcontracts"]') .click('li[data-id="treeViewLitreeViewItemremoveSourcehighlightScript.js"]')
.click('li[data-id="treeViewLitreeViewItemcontracts"]') .pause(2000)
.waitForElementVisible('li[data-id="treeViewLitreeViewItemcontracts/3_Ballot.sol"]') .executeScriptInTerminal('remix.exeCurrent()')
.click('li[data-id="treeViewLitreeViewItemcontracts/3_Ballot.sol"]') .waitForElementVisible('li[data-id="treeViewLitreeViewItemcontracts"]')
.waitForElementNotPresent('.highlightLine33', 60000) .click('li[data-id="treeViewLitreeViewItemcontracts"]')
.checkElementStyle('.highlightLine41', 'background-color', 'rgb(52, 152, 219)') .waitForElementVisible('li[data-id="treeViewLitreeViewItemcontracts/3_Ballot.sol"]')
.checkElementStyle('.highlightLine51', 'background-color', 'rgb(52, 152, 219)') .click('li[data-id="treeViewLitreeViewItemcontracts/3_Ballot.sol"]')
}, .waitForElementNotPresent('.highlightLine33', 60000)
.checkElementStyle('.highlightLine41', 'background-color', 'rgb(52, 152, 219)')
.checkElementStyle('.highlightLine51', 'background-color', 'rgb(52, 152, 219)')
},
'Should remove all highlights from source code #group1': function (browser: NightwatchBrowser) { 'Should remove all highlights from source code #group1': function (browser: NightwatchBrowser) {
browser.waitForElementVisible('li[data-id="treeViewLitreeViewItemremoveAllSourcehighlightScript.js"]') browser
.waitForElementVisible('li[data-id="treeViewLitreeViewItemremoveAllSourcehighlightScript.js"]')
.click('li[data-id="treeViewLitreeViewItemremoveAllSourcehighlightScript.js"]') .click('li[data-id="treeViewLitreeViewItemremoveAllSourcehighlightScript.js"]')
.pause(2000) .pause(2000)
.executeScriptInTerminal('remix.exeCurrent()') .executeScriptInTerminal('remix.exeCurrent()')
@ -126,8 +147,7 @@ module.exports = {
.waitForElementNotPresent('.highlightLine33', 60000) .waitForElementNotPresent('.highlightLine33', 60000)
.waitForElementNotPresent('.highlightLine41', 60000) .waitForElementNotPresent('.highlightLine41', 60000)
.waitForElementNotPresent('.highlightLine51', 60000) .waitForElementNotPresent('.highlightLine51', 60000)
}, }
} }
const aceThemes = { const aceThemes = {
@ -233,5 +253,3 @@ contract Storage {
return number; return number;
} }
}` }`

@ -1,53 +1,55 @@
'use strict' 'use strict'
import { NightwatchBrowser } from 'nightwatch' import {NightwatchBrowser} from 'nightwatch'
import init from '../helpers/init' import init from '../helpers/init'
const openReferences = (browser: NightwatchBrowser, path: string) => { const openReferences = (browser: NightwatchBrowser, path: string) => {
(browser as any).useXpath() ;(browser as any)
.useXpath() .useXpath()
.waitForElementVisible(path) .useXpath()
.click(path) .waitForElementVisible(path)
.perform(function () { .click(path)
const actions = this.actions({ async: true }); .perform(function () {
return actions. const actions = this.actions({async: true})
keyDown(this.Keys.SHIFT). return actions.keyDown(this.Keys.SHIFT).sendKeys(this.Keys.F12)
sendKeys(this.Keys.F12) })
})
} }
module.exports = { module.exports = {
before: function (browser: NightwatchBrowser, done: VoidFunction) { 'before': function (browser: NightwatchBrowser, done: VoidFunction) {
init(browser, done, 'http://127.0.0.1:8080', false) init(browser, done, 'http://127.0.0.1:8080', false)
}, },
'Should load the test file': function (browser: NightwatchBrowser) { 'Should load the test file': function (browser: NightwatchBrowser) {
browser.openFile('contracts') browser
.openFile('contracts/3_Ballot.sol') .openFile('contracts')
.waitForElementVisible('#editorView') .openFile('contracts/3_Ballot.sol')
.setEditorValue(BallotWithARefToOwner) .waitForElementVisible('#editorView')
.pause(10000) // wait for the compiler to finish .setEditorValue(BallotWithARefToOwner)
.scrollToLine(37) .pause(10000) // wait for the compiler to finish
}, .scrollToLine(37)
'Should show local references': function (browser: NightwatchBrowser) { },
browser.scrollToLine(48) 'Should show local references': function (browser: NightwatchBrowser) {
const path = "//*[@class='view-line' and contains(.,'length') and contains(.,'proposalNames')]//span//span[contains(.,'proposalNames')]" browser.scrollToLine(48)
openReferences(browser, path) const path = "//*[@class='view-line' and contains(.,'length') and contains(.,'proposalNames')]//span//span[contains(.,'proposalNames')]"
browser.waitForElementVisible("//*[@class='monaco-highlighted-label referenceMatch']//span[contains(.,'length; i++')]") openReferences(browser, path)
.waitForElementVisible("//*[@class='monaco-highlighted-label referenceMatch']//span[contains(.,'name:')]") browser
.waitForElementVisible("//*[@class='monaco-highlighted-label referenceMatch']//span[contains(.,'constructor')]") .waitForElementVisible("//*[@class='monaco-highlighted-label referenceMatch'][contains(.,'length; i++')]")
.keys(browser.Keys.ESCAPE) .waitForElementVisible("//*[@class='monaco-highlighted-label referenceMatch'][contains(.,'name:')]")
}, .waitForElementVisible("//*[@class='monaco-highlighted-label referenceMatch'][contains(.,'constructor')]")
'Should show references of getOwner': function (browser: NightwatchBrowser) { .keys(browser.Keys.ESCAPE)
browser.scrollToLine(39) },
const path = "//*[@class='view-line' and contains(.,'getOwner') and contains(.,'cowner')]//span//span[contains(.,'getOwner')]" 'Should show references of getOwner': function (browser: NightwatchBrowser) {
openReferences(browser, path) browser.scrollToLine(39)
browser.useXpath() const path = "//*[@class='view-line' and contains(.,'getOwner') and contains(.,'cowner')]//span//span[contains(.,'getOwner')]"
.waitForElementVisible("//*[@class='monaco-highlighted-label']//span[contains(.,'2_Owner.sol')]") openReferences(browser, path)
.waitForElementVisible("//*[@class='monaco-highlighted-label']//span[contains(.,'3_Ballot.sol')]") browser
.waitForElementVisible("//*[@class='monaco-highlighted-label referenceMatch']//span[contains(.,'cowner.getOwner')]") .useXpath()
.waitForElementVisible("//*[contains(@class, 'results-loaded') and contains(., 'References (2)')]") .waitForElementVisible("//*[@class='monaco-highlighted-label'][contains(.,'2_Owner.sol')]")
.keys(browser.Keys.ESCAPE) .waitForElementVisible("//*[@class='monaco-highlighted-label'][contains(.,'3_Ballot.sol')]")
} .waitForElementVisible("//*[@class='monaco-highlighted-label referenceMatch'][contains(.,'cowner.getOwner')]")
.waitForElementVisible("//*[contains(@class, 'results-loaded') and contains(., 'References (2)')]")
.keys(browser.Keys.ESCAPE)
}
} }
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
@ -193,4 +195,4 @@ contract BallotHoverTest {
winnerName_ = proposals[winningProposal()].name; winnerName_ = proposals[winningProposal()].name;
} }
} }
` `

@ -1,105 +1,105 @@
// Merge custom command types with nightwatch types // Merge custom command types with nightwatch types
/* eslint-disable no-use-before-define */ /* eslint-disable no-use-before-define */
import { NightwatchBrowser } from 'nightwatch' // eslint-disable-line @typescript-eslint/no-unused-vars import {NightwatchBrowser} from 'nightwatch' // eslint-disable-line @typescript-eslint/no-unused-vars
export type callbackCheckVerifyCallReturnValue = (values: string[]) => { message: string, pass: boolean } export type callbackCheckVerifyCallReturnValue = (values: string[]) => {message: string; pass: boolean}
declare module 'nightwatch' { declare module 'nightwatch' {
export interface NightwatchCustomCommands { export interface NightwatchCustomCommands {
clickLaunchIcon(icon: string): NightwatchBrowser, clickLaunchIcon(icon: string): NightwatchBrowser
switchBrowserTab(index: number): NightwatchBrowser, switchBrowserTab(index: number): NightwatchBrowser
scrollAndClick(target: string): NightwatchBrowser, scrollAndClick(target: string): NightwatchBrowser
scrollInto(target: string): NightwatchBrowser, scrollInto(target: string): NightwatchBrowser
testContracts(fileName: string, contractCode: NightwatchContractContent, compiledContractNames: string[]): NightwatchBrowser, testContracts(fileName: string, contractCode: NightwatchContractContent, compiledContractNames: string[]): NightwatchBrowser
setEditorValue(value: string, callback?: () => void): NightwatchBrowser, setEditorValue(value: string, callback?: () => void): NightwatchBrowser
addFile(name: string, content: NightwatchContractContent): NightwatchBrowser, addFile(name: string, content: NightwatchContractContent): NightwatchBrowser
verifyContracts(compiledContractNames: string[], opts?: { wait: number, version?: string, runs?: string }): NightwatchBrowser, verifyContracts(compiledContractNames: string[], opts?: {wait: number; version?: string; runs?: string}): NightwatchBrowser
selectAccount(account?: string): NightwatchBrowser, selectAccount(account?: string): NightwatchBrowser
clickFunction(fnFullName: string, expectedInput?: NightwatchClickFunctionExpectedInput): NightwatchBrowser, clickFunction(fnFullName: string, expectedInput?: NightwatchClickFunctionExpectedInput): NightwatchBrowser
testFunction(txHash: string, expectedInput: NightwatchTestFunctionExpectedInput): NightwatchBrowser, testFunction(txHash: string, expectedInput: NightwatchTestFunctionExpectedInput): NightwatchBrowser
goToVMTraceStep(step: number, incr?: number): NightwatchBrowser, goToVMTraceStep(step: number, incr?: number): NightwatchBrowser
checkVariableDebug(id: string, debugValue: NightwatchCheckVariableDebugValue): NightwatchBrowser, checkVariableDebug(id: string, debugValue: NightwatchCheckVariableDebugValue): NightwatchBrowser
addAtAddressInstance(address: string, isValidFormat: boolean, isValidChecksum: boolean, isAbi?: boolean): NightwatchBrowser, addAtAddressInstance(address: string, isValidFormat: boolean, isValidChecksum: boolean, isAbi?: boolean): NightwatchBrowser
modalFooterOKClick(id?: string): NightwatchBrowser, modalFooterOKClick(id?: string): NightwatchBrowser
clickInstance(index: number): NightwatchBrowser, clickInstance(index: number): NightwatchBrowser
journalLastChildIncludes(val: string): NightwatchBrowser, journalLastChildIncludes(val: string): NightwatchBrowser
executeScriptInTerminal(script: string): NightwatchBrowser, executeScriptInTerminal(script: string): NightwatchBrowser
clearEditableContent(cssSelector: string): NightwatchBrowser, clearEditableContent(cssSelector: string): NightwatchBrowser
journalChildIncludes(val: string, opts = { shouldHaveOnlyOneOccurence: boolean }): NightwatchBrowser, journalChildIncludes(val: string, opts = {shouldHaveOnlyOneOccurence: boolean}): NightwatchBrowser
debugTransaction(index: number): NightwatchBrowser, debugTransaction(index: number): NightwatchBrowser
checkElementStyle(cssSelector: string, styleProperty: string, expectedResult: string): NightwatchBrowser, checkElementStyle(cssSelector: string, styleProperty: string, expectedResult: string): NightwatchBrowser
openFile(name: string): NightwatchBrowser, openFile(name: string): NightwatchBrowser
refreshPage(): NightwatchBrowser, refreshPage(): NightwatchBrowser
verifyLoad(): NightwatchBrowser, verifyLoad(): NightwatchBrowser
renamePath(path: string, newFileName: string, renamedPath: string): NightwatchBrowser, renamePath(path: string, newFileName: string, renamedPath: string): NightwatchBrowser
rightClickCustom(cssSelector: string): NightwatchBrowser, rightClickCustom(cssSelector: string): NightwatchBrowser
scrollToLine(line: number): NightwatchBrowser, scrollToLine(line: number): NightwatchBrowser
waitForElementContainsText(id: string, value: string, timeout?: number): NightwatchBrowser, waitForElementContainsText(id: string, value: string, timeout?: number): NightwatchBrowser
getModalBody(callback: (value: string, cb: VoidFunction) => void): NightwatchBrowser, getModalBody(callback: (value: string, cb: VoidFunction) => void): NightwatchBrowser
modalFooterCancelClick(id?: string): NightwatchBrowser, modalFooterCancelClick(id?: string): NightwatchBrowser
selectContract(contractName: string): NightwatchBrowser, selectContract(contractName: string): NightwatchBrowser
createContract(inputParams: string): NightwatchBrowser, createContract(inputParams: string): NightwatchBrowser
getAddressAtPosition(index: number, cb: (pos: string) => void): NightwatchBrowser, getAddressAtPosition(index: number, cb: (pos: string) => void): NightwatchBrowser
testConstantFunction(address: string, fnFullName: string, expectedInput: NightwatchTestConstantFunctionExpectedInput | null, expectedOutput: string): NightwatchBrowser, testConstantFunction(address: string, fnFullName: string, expectedInput: NightwatchTestConstantFunctionExpectedInput | null, expectedOutput: string): NightwatchBrowser
getEditorValue(callback: (content: string) => void): NightwatchBrowser, getEditorValue(callback: (content: string) => void): NightwatchBrowser
getInstalledPlugins(cb: (plugins: string[]) => void): NightwatchBrowser, getInstalledPlugins(cb: (plugins: string[]) => void): NightwatchBrowser
verifyCallReturnValue(address: string, checks: string[] | callbackCheckVerifyCallReturnValue): NightwatchBrowser, verifyCallReturnValue(address: string, checks: string[] | callbackCheckVerifyCallReturnValue): NightwatchBrowser
testEditorValue(testvalue: string): NightwatchBrowser, testEditorValue(testvalue: string): NightwatchBrowser
removeFile(path: string, workspace: string): NightwatchBrowser, removeFile(path: string, workspace: string): NightwatchBrowser
switchBrowserWindow(url: string, windowName: string, cb: (browser: NightwatchBrowser, window?: NightwatchCallbackResult<Window>) => void): NightwatchBrowser, switchBrowserWindow(url: string, windowName: string, cb: (browser: NightwatchBrowser, window?: NightwatchCallbackResult<Window>) => void): NightwatchBrowser
setupMetamask(passphrase: string, password: string): NightwatchBrowser, setupMetamask(passphrase: string, password: string): NightwatchBrowser
signMessage(msg: string, callback: (hash: { value: string }, signature: { value: string }) => void): NightwatchBrowser, signMessage(msg: string, callback: (hash: {value: string}, signature: {value: string}) => void): NightwatchBrowser
setSolidityCompilerVersion(version: string): NightwatchBrowser, setSolidityCompilerVersion(version: string): NightwatchBrowser
clickElementAtPosition(cssSelector: string, index: number, opt?: { forceSelectIfUnselected: boolean }): NightwatchBrowser, clickElementAtPosition(cssSelector: string, index: number, opt?: {forceSelectIfUnselected: boolean}): NightwatchBrowser
notContainsText(cssSelector: string, text: string): NightwatchBrowser, notContainsText(cssSelector: string, text: string): NightwatchBrowser
sendLowLevelTx(address: string, value: string, callData: string): NightwatchBrowser, sendLowLevelTx(address: string, value: string, callData: string): NightwatchBrowser
journalLastChild(val: string): NightwatchBrowser, journalLastChild(val: string): NightwatchBrowser
checkTerminalFilter(filter: string, test: string): NightwatchBrowser, checkTerminalFilter(filter: string, test: string): NightwatchBrowser
noWorkerErrorFor(version: string): NightwatchBrowser, noWorkerErrorFor(version: string): NightwatchBrowser
validateValueInput(selector: string, valueTosSet: string, expectedValue: string): NightwatchBrowser validateValueInput(selector: string, valueTosSet: string, expectedValue: string): NightwatchBrowser
checkAnnotations(type: string, line: number): NightwatchBrowser checkAnnotations(type: string): NightwatchBrowser
checkAnnotationsNotPresent(type: string): NightwatchBrowser checkAnnotationsNotPresent(type: string): NightwatchBrowser
getLastTransactionHash(callback: (hash: string) => void) getLastTransactionHash(callback: (hash: string) => void)
currentWorkspaceIs(name: string): NightwatchBrowser currentWorkspaceIs(name: string): NightwatchBrowser
addLocalPlugin(this: NightwatchBrowser, profile: Profile & LocationProfile & ExternalProfile): NightwatchBrowser addLocalPlugin(this: NightwatchBrowser, profile: Profile & LocationProfile & ExternalProfile): NightwatchBrowser
acceptAndRemember (this: NightwatchBrowser, remember: boolean, accept: boolean): NightwatchBrowser acceptAndRemember(this: NightwatchBrowser, remember: boolean, accept: boolean): NightwatchBrowser
clearConsole (this: NightwatchBrowser): NightwatchBrowser clearConsole(this: NightwatchBrowser): NightwatchBrowser
clearTransactions (this: NightwatchBrowser): NightwatchBrowser clearTransactions(this: NightwatchBrowser): NightwatchBrowser
getBrowserLogs (this: NightwatchBrowser): NightwatchBrowser getBrowserLogs(this: NightwatchBrowser): NightwatchBrowser
currentSelectedFileIs (name: string): NightwatchBrowser, currentSelectedFileIs(name: string): NightwatchBrowser
switchWorkspace: (workspaceName: string) => NightwatchBrowser switchWorkspace: (workspaceName: string) => NightwatchBrowser
switchEnvironment: (provider: string) => NightwatchBrowser switchEnvironment: (provider: string) => NightwatchBrowser
connectToExternalHttpProvider: (url: string, identifier: string) => NightwatchBrowser connectToExternalHttpProvider: (url: string, identifier: string) => NightwatchBrowser
} }
export interface NightwatchBrowser { export interface NightwatchBrowser {
api: this, api: this
emit: (status: string) => void, emit: (status: string) => void
fullscreenWindow: (result?: any) => this, fullscreenWindow: (result?: any) => this
keys(keysToSend: string, callback?: (this: NightwatchAPI, result: NightwatchCallbackResult<void>) => void): NightwatchBrowser, keys(keysToSend: string, callback?: (this: NightwatchAPI, result: NightwatchCallbackResult<void>) => void): NightwatchBrowser
sendKeys: (selector: string, inputValue: string | string[], callback?: (this: NightwatchAPI, result: NightwatchCallbackResult<void>) => void) => NightwatchBrowser sendKeys: (selector: string, inputValue: string | string[], callback?: (this: NightwatchAPI, result: NightwatchCallbackResult<void>) => void) => NightwatchBrowser
} }
export interface NightwatchAPI { export interface NightwatchAPI {
keys(keysToSend: string, callback?: (this: NightwatchAPI, result: NightwatchCallbackResult<void>) => void): NightwatchAPI keys(keysToSend: string, callback?: (this: NightwatchAPI, result: NightwatchCallbackResult<void>) => void): NightwatchAPI
} }
export interface NightwatchContractContent { export interface NightwatchContractContent {
content: string; content: string
} }
export interface NightwatchClickFunctionExpectedInput { export interface NightwatchClickFunctionExpectedInput {
types: string, types: string
values: string values: string
} }
export interface NightwatchTestFunctionExpectedInput { export interface NightwatchTestFunctionExpectedInput {
[key: string]: any [key: string]: any
} }
export interface NightwatchTestConstantFunctionExpectedInput { export interface NightwatchTestConstantFunctionExpectedInput {
types: string, types: string
values: string values: string
} }
export type NightwatchCheckVariableDebugValue = NightwatchTestFunctionExpectedInput export type NightwatchCheckVariableDebugValue = NightwatchTestFunctionExpectedInput
} }

@ -1,7 +1,7 @@
import { Monaco } from "@monaco-editor/react" import {Monaco} from '@monaco-editor/react'
import monaco from "../../types/monaco" import monaco from '../../types/monaco'
import { EditorUIProps } from "../remix-ui-editor" import {EditorUIProps} from '../remix-ui-editor'
import { default as fixesList } from "./quickfixes" import {default as fixesList} from './quickfixes'
export class RemixCodeActionProvider implements monaco.languages.CodeActionProvider { export class RemixCodeActionProvider implements monaco.languages.CodeActionProvider {
props: EditorUIProps props: EditorUIProps
@ -11,7 +11,7 @@ export class RemixCodeActionProvider implements monaco.languages.CodeActionProvi
this.monaco = monaco this.monaco = monaco
} }
async provideCodeActions ( async provideCodeActions(
model: monaco.editor.ITextModel, model: monaco.editor.ITextModel,
range: monaco.Range, range: monaco.Range,
context: monaco.languages.CodeActionContext, context: monaco.languages.CodeActionContext,
@ -22,27 +22,39 @@ export class RemixCodeActionProvider implements monaco.languages.CodeActionProvi
let fixes: Record<string, any>[], msg: string let fixes: Record<string, any>[], msg: string
let isOldAST: boolean = false let isOldAST: boolean = false
const errStrings: string[] = Object.keys(fixesList) const errStrings: string[] = Object.keys(fixesList)
const errStr:string = errStrings.find(es => error.message.includes(es)) const errStr: string = errStrings.find((es) => error.message.includes(es))
if (errStr) { if (errStr) {
fixes = fixesList[errStr] fixes = fixesList[errStr]
const cursorPosition: number = this.props.editorAPI.getHoverPosition({lineNumber: error.startLineNumber, column: error.startColumn}) const cursorPosition: number = this.props.editorAPI.getHoverPosition({
lineNumber: error.startLineNumber,
column: error.startColumn
})
const nodeAtPosition = await this.props.plugin.call('codeParser', 'definitionAtPosition', cursorPosition) const nodeAtPosition = await this.props.plugin.call('codeParser', 'definitionAtPosition', cursorPosition)
// Check if a function is hovered // Check if a function is hovered
if (nodeAtPosition && nodeAtPosition.nodeType === "FunctionDefinition") { if (nodeAtPosition && nodeAtPosition.nodeType === 'FunctionDefinition') {
// Identify type of AST node // Identify type of AST node
if (nodeAtPosition.parameters && !Array.isArray(nodeAtPosition.parameters) && Array.isArray(nodeAtPosition.parameters.parameters)) if (nodeAtPosition.parameters && !Array.isArray(nodeAtPosition.parameters) && Array.isArray(nodeAtPosition.parameters.parameters)) isOldAST = true
isOldAST = true
const paramNodes = isOldAST ? nodeAtPosition.parameters.parameters : nodeAtPosition.parameters const paramNodes = isOldAST ? nodeAtPosition.parameters.parameters : nodeAtPosition.parameters
for (const fix of fixes) { for (const fix of fixes) {
msg = paramNodes.length msg = paramNodes.length
? await this.fixForMethodWithParams(model, paramNodes, fix, error, isOldAST) ? await this.fixForMethodWithParams(model, paramNodes, fix, error, isOldAST)
: await this.fixForMethodWithoutParams(model, nodeAtPosition, fix, error, isOldAST) : await this.fixForMethodWithoutParams(model, nodeAtPosition, fix, error, isOldAST)
this.addQuickFix(actions, error, model.uri, {title: fix.title, range: fix.range, text: msg}) this.addQuickFix(actions, error, model.uri, {
id: fix.id,
title: fix.title,
range: fix.range,
text: msg
})
} }
} else { } else {
for (const fix of fixes) { for (const fix of fixes) {
if (fix && nodeAtPosition && fix.nodeType !== nodeAtPosition.nodeType) continue if (fix && nodeAtPosition && fix.nodeType !== nodeAtPosition.nodeType) continue
else this.addQuickFix(actions, error, model.uri, {title: fix.title, range: fix.range || error, text: fix.message}) else
this.addQuickFix(actions, error, model.uri, {
title: fix.title,
range: fix.range || error,
text: fix.message
})
} }
} }
} }
@ -62,16 +74,17 @@ export class RemixCodeActionProvider implements monaco.languages.CodeActionProvi
* @param fix details of quick fix to apply * @param fix details of quick fix to apply
*/ */
addQuickFix(actions: monaco.languages.CodeAction[], error: monaco.editor.IMarkerData, uri: monaco.Uri, fix: Record<string, any>) { addQuickFix(actions: monaco.languages.CodeAction[], error: monaco.editor.IMarkerData, uri: monaco.Uri, fix: Record<string, any>) {
const {title, range, text} = fix const {id, title, range, text} = fix
actions.push({ actions.push({
title, title,
diagnostics: [error], diagnostics: [error],
kind: "quickfix", kind: 'quickfix',
edit: { edit: {
edits: [ edits: [
{ {
resource: uri, resource: uri,
edit: { range, text } textEdit: {range, text},
versionId: undefined
} }
] ]
}, },
@ -88,7 +101,13 @@ export class RemixCodeActionProvider implements monaco.languages.CodeActionProvi
* @param isOldAST true, if AST node contains legacy fields * @param isOldAST true, if AST node contains legacy fields
* @returns message to be placed as quick fix * @returns message to be placed as quick fix
*/ */
async fixForMethodWithParams(model: monaco.editor.ITextModel, paramNodes: Record<string, any>[], fix: Record<string, any>, error: monaco.editor.IMarkerData, isOldAST: boolean): Promise<string> { async fixForMethodWithParams(
model: monaco.editor.ITextModel,
paramNodes: Record<string, any>[],
fix: Record<string, any>,
error: monaco.editor.IMarkerData,
isOldAST: boolean
): Promise<string> {
let lastParamEndLoc: Record<string, any>, fixLineNumber: number, msg: string let lastParamEndLoc: Record<string, any>, fixLineNumber: number, msg: string
// Get last function parameter node // Get last function parameter node
const lastParamNode: Record<string, any> = paramNodes[paramNodes.length - 1] const lastParamNode: Record<string, any> = paramNodes[paramNodes.length - 1]
@ -103,12 +122,13 @@ export class RemixCodeActionProvider implements monaco.languages.CodeActionProvi
fixLineNumber = lastParamEndLoc.line fixLineNumber = lastParamEndLoc.line
} }
const lineContent: string = model.getLineContent(fixLineNumber) const lineContent: string = model.getLineContent(fixLineNumber)
if (fix.id === 5 && lineContent.includes(' view ')) if (fix.id === 5 && lineContent.includes(' view ')) msg = lineContent.replace('view', 'pure')
msg = lineContent.replace('view', 'pure') else if (isOldAST) msg = lineContent.substring(0, lastParamEndLoc.column + 2) + fix.message + lineContent.substring(lastParamEndLoc.column + 1, lineContent.length)
else if (isOldAST) else
msg = lineContent.substring(0, lastParamEndLoc.column + 2) + fix.message + lineContent.substring(lastParamEndLoc.column + 1, lineContent.length) msg =
else lineContent.substring(0, lastParamEndLoc.column + lastParamNode.name.length + 2) +
msg = lineContent.substring(0, lastParamEndLoc.column + lastParamNode.name.length + 2) + fix.message + lineContent.substring(lastParamEndLoc.column + lastParamNode.name.length + 1, lineContent.length) fix.message +
lineContent.substring(lastParamEndLoc.column + lastParamNode.name.length + 1, lineContent.length)
fix.range = { fix.range = {
startLineNumber: fixLineNumber, startLineNumber: fixLineNumber,
@ -128,7 +148,13 @@ export class RemixCodeActionProvider implements monaco.languages.CodeActionProvi
* @param isOldAST true, if AST node contains legacy fields * @param isOldAST true, if AST node contains legacy fields
* @returns message to be placed as quick fix * @returns message to be placed as quick fix
*/ */
async fixForMethodWithoutParams(model: monaco.editor.ITextModel, nodeAtPosition: Record<string, any>, fix: Record<string, any>, error: monaco.editor.IMarkerData, isOldAST: boolean): Promise<string> { async fixForMethodWithoutParams(
model: monaco.editor.ITextModel,
nodeAtPosition: Record<string, any>,
fix: Record<string, any>,
error: monaco.editor.IMarkerData,
isOldAST: boolean
): Promise<string> {
let fixLineNumber: number, msg: string let fixLineNumber: number, msg: string
if (isOldAST) { if (isOldAST) {
const location: Record<string, any> = await this.props.plugin.call('codeParser', 'getLineColumnOfNode', nodeAtPosition) const location: Record<string, any> = await this.props.plugin.call('codeParser', 'getLineColumnOfNode', nodeAtPosition)
@ -137,12 +163,11 @@ export class RemixCodeActionProvider implements monaco.languages.CodeActionProvi
const lineContent: string = model.getLineContent(fixLineNumber) const lineContent: string = model.getLineContent(fixLineNumber)
const i: number = lineContent.indexOf('()') const i: number = lineContent.indexOf('()')
if (fix.id === 5 && lineContent.includes(' view ')) { if (fix.id === 5 && lineContent.includes(' view ')) {
msg = lineContent.replace('view', 'pure') msg = lineContent.replace('view', 'pure')
} else } else msg = lineContent.substring(0, i + 3) + fix.message + lineContent.substring(i + 3, lineContent.length)
msg = lineContent.substring(0, i + 3) + fix.message + lineContent.substring(i + 3, lineContent.length)
fix.range = { fix.range = {
startLineNumber: fixLineNumber, startLineNumber: fixLineNumber,
endLineNumber: fixLineNumber, endLineNumber: fixLineNumber,
@ -151,4 +176,4 @@ export class RemixCodeActionProvider implements monaco.languages.CodeActionProvi
} }
return msg return msg
} }
} }

@ -26,3 +26,7 @@
filter: opacity(0.5); filter: opacity(0.5);
font-weight: bolder; font-weight: bolder;
} }
div.monaco-list-row > span.title {
margin-top: 12px;
}

@ -109,7 +109,6 @@ export type EditorAPIType = {
keepDecorationsFor: (filePath: string, plugin: string, typeOfDecoration: string, registeredDecorations: any, currentDecorations: any) => DecorationsReturn keepDecorationsFor: (filePath: string, plugin: string, typeOfDecoration: string, registeredDecorations: any, currentDecorations: any) => DecorationsReturn
addErrorMarker: (errors: errorMarker[], from: string) => void addErrorMarker: (errors: errorMarker[], from: string) => void
clearErrorMarkers: (sources: string[] | {[fileName: string]: any}, from: string) => void clearErrorMarkers: (sources: string[] | {[fileName: string]: any}, from: string) => void
getPositionAt: (offset: number) => monacoTypes.IPosition
} }
/* eslint-disable-next-line */ /* eslint-disable-next-line */
@ -286,7 +285,7 @@ export const EditorUI = (props: EditorUIProps) => {
'editorSuggestWidget.selectedForeground': textColor, 'editorSuggestWidget.selectedForeground': textColor,
'editorSuggestWidget.highlightForeground': primaryColor, 'editorSuggestWidget.highlightForeground': primaryColor,
'editorSuggestWidget.focusHighlightForeground': infoColor, 'editorSuggestWidget.focusHighlightForeground': infoColor,
'editor.lineHighlightBorder': secondaryColor, 'editor.lineHighlightBorder': textbackground,
'editor.lineHighlightBackground': textbackground === darkColor ? lightColor : secondaryColor, 'editor.lineHighlightBackground': textbackground === darkColor ? lightColor : secondaryColor,
'editorGutter.background': lightColor, 'editorGutter.background': lightColor,
//'editor.selectionHighlightBackground': secondaryColor, //'editor.selectionHighlightBackground': secondaryColor,
@ -550,10 +549,7 @@ export const EditorUI = (props: EditorUIProps) => {
props.editorAPI.getFontSize = () => { props.editorAPI.getFontSize = () => {
if (!editorRef.current) return if (!editorRef.current) return
return editorRef.current.getOption(43).fontSize return editorRef.current.getOption(51)
}
props.editorAPI.getPositionAt = (offset: number): IPosition => {
return editorRef.current.getModel().getPositionAt(offset)
} }
;(window as any).addRemixBreakpoint = (position) => { ;(window as any).addRemixBreakpoint = (position) => {
// make it available from e2e testing... // make it available from e2e testing...
@ -639,10 +635,10 @@ export const EditorUI = (props: EditorUIProps) => {
// zoomin zoomout // zoomin zoomout
editor.addCommand(monacoRef.current.KeyMod.CtrlCmd | (monacoRef.current.KeyCode as any).US_EQUAL, () => { editor.addCommand(monacoRef.current.KeyMod.CtrlCmd | (monacoRef.current.KeyCode as any).US_EQUAL, () => {
editor.updateOptions({fontSize: editor.getOption(43).fontSize + 1}) editor.updateOptions({fontSize: editor.getOption(51) + 1})
}) })
editor.addCommand(monacoRef.current.KeyMod.CtrlCmd | (monacoRef.current.KeyCode as any).US_MINUS, () => { editor.addCommand(monacoRef.current.KeyMod.CtrlCmd | (monacoRef.current.KeyCode as any).US_MINUS, () => {
editor.updateOptions({fontSize: editor.getOption(43).fontSize - 1}) editor.updateOptions({fontSize: editor.getOption(51) - 1})
}) })
// add context menu items // add context menu items
@ -656,7 +652,7 @@ export const EditorUI = (props: EditorUIProps) => {
monacoRef.current.KeyMod.CtrlCmd | monacoRef.current.KeyCode.Equal monacoRef.current.KeyMod.CtrlCmd | monacoRef.current.KeyCode.Equal
], ],
run: () => { run: () => {
editor.updateOptions({fontSize: editor.getOption(43).fontSize + 1}) editor.updateOptions({fontSize: editor.getOption(51) + 1})
} }
} }
const zoomOutAction = { const zoomOutAction = {
@ -669,7 +665,7 @@ export const EditorUI = (props: EditorUIProps) => {
monacoRef.current.KeyMod.CtrlCmd | monacoRef.current.KeyCode.Minus monacoRef.current.KeyMod.CtrlCmd | monacoRef.current.KeyCode.Minus
], ],
run: () => { run: () => {
editor.updateOptions({fontSize: editor.getOption(43).fontSize - 1}) editor.updateOptions({fontSize: editor.getOption(51) - 1})
} }
} }
const formatAction = { const formatAction = {

File diff suppressed because it is too large Load Diff

@ -229,7 +229,7 @@
"@babel/preset-typescript": "^7.18.6", "@babel/preset-typescript": "^7.18.6",
"@babel/register": "^7.4.4", "@babel/register": "^7.4.4",
"@fortawesome/fontawesome-free": "^5.8.1", "@fortawesome/fontawesome-free": "^5.8.1",
"@monaco-editor/react": "4.4.5", "@monaco-editor/react": "4.5.1",
"@nrwl/cli": "^15.7.1", "@nrwl/cli": "^15.7.1",
"@nrwl/eslint-plugin-nx": "^15.7.1", "@nrwl/eslint-plugin-nx": "^15.7.1",
"@nrwl/js": "15.7.1", "@nrwl/js": "15.7.1",
@ -328,7 +328,7 @@
"minixhr": "^4.0.0", "minixhr": "^4.0.0",
"mkdirp": "^0.5.1", "mkdirp": "^0.5.1",
"mocha": "^8.0.1", "mocha": "^8.0.1",
"monaco-editor": "^0.30.1", "monaco-editor": "0.41.0",
"nanohtml": "^1.6.3", "nanohtml": "^1.6.3",
"nightwatch": "^2.3", "nightwatch": "^2.3",
"nodemon": "^2.0.4", "nodemon": "^2.0.4",

@ -3838,20 +3838,19 @@
semver "^7.3.8" semver "^7.3.8"
superstruct "^1.0.3" superstruct "^1.0.3"
"@monaco-editor/loader@^1.3.2": "@monaco-editor/loader@^1.3.3":
version "1.3.2" version "1.3.3"
resolved "https://registry.yarnpkg.com/@monaco-editor/loader/-/loader-1.3.2.tgz#04effbb87052d19cd7d3c9d81c0635490f9bb6d8" resolved "https://registry.yarnpkg.com/@monaco-editor/loader/-/loader-1.3.3.tgz#7f1742bd3cc21c0362a46a4056317f6e5215cfca"
integrity sha512-BTDbpHl3e47r3AAtpfVFTlAi7WXv4UQ/xZmz8atKl4q7epQV5e7+JbigFDViWF71VBi4IIBdcWP57Hj+OWuc9g== integrity sha512-6KKF4CTzcJiS8BJwtxtfyYt9shBiEv32ateQ9T4UVogwn4HM/uPo9iJd2Dmbkpz8CM6Y0PDUpjnZzCwC+eYo2Q==
dependencies: dependencies:
state-local "^1.0.6" state-local "^1.0.6"
"@monaco-editor/react@4.4.5": "@monaco-editor/react@4.5.1":
version "4.4.5" version "4.5.1"
resolved "https://registry.yarnpkg.com/@monaco-editor/react/-/react-4.4.5.tgz#beabe491efeb2457441a00d1c7651c653697f65b" resolved "https://registry.yarnpkg.com/@monaco-editor/react/-/react-4.5.1.tgz#fbc76c692aee9a33b9ab24ae0c5f219b8f002fdb"
integrity sha512-IImtzU7sRc66OOaQVCG+5PFHkSWnnhrUWGBuH6zNmH2h0YgmAhcjHZQc/6MY9JWEbUtVF1WPBMJ9u1XuFbRrVA== integrity sha512-NNDFdP+2HojtNhCkRfE6/D6ro6pBNihaOzMbGK84lNWzRu+CfBjwzGt4jmnqimLuqp5yE5viHS2vi+QOAnD5FQ==
dependencies: dependencies:
"@monaco-editor/loader" "^1.3.2" "@monaco-editor/loader" "^1.3.3"
prop-types "^15.7.2"
"@motionone/animation@^10.15.1": "@motionone/animation@^10.15.1":
version "10.15.1" version "10.15.1"
@ -19718,10 +19717,10 @@ mold-source-map@~0.4.0:
convert-source-map "^1.1.0" convert-source-map "^1.1.0"
through "~2.2.7" through "~2.2.7"
monaco-editor@^0.30.1: monaco-editor@0.41.0:
version "0.30.1" version "0.41.0"
resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.30.1.tgz#47f8d18a0aa2264fc5654581741ab8d7bec01689" resolved "https://registry.yarnpkg.com/monaco-editor/-/monaco-editor-0.41.0.tgz#2ba31e5af7e3ae93ac5d7467ec2772ef9b3d967f"
integrity sha512-B/y4+b2O5G2gjuxIFtCE2EkM17R2NM7/3F8x0qcPsqy4V83bitJTIO4TIeZpYlzu/xy6INiY/+84BEm6+7Cmzg== integrity sha512-1o4olnZJsiLmv5pwLEAmzHTE/5geLKQ07BrGxlF4Ri/AXAc2yyDGZwHjiTqD8D/ROKUZmwMA28A+yEowLNOEcA==
motion@10.16.2: motion@10.16.2:
version "10.16.2" version "10.16.2"

Loading…
Cancel
Save