chore(deps): Updated to Theia 1.39.0 (#2144)

- update Theia to `1.39.0`,
 - remove the application packager and fix the security vulnerabilities,
 - bundle the backed application with `webpack`, and
 - enhance the developer docs.

Co-authored-by: Akos Kitta <a.kitta@arduino.cc>
Co-authored-by: per1234 <accounts@perglass.com>

Signed-off-by: Akos Kitta <a.kitta@arduino.cc>
This commit is contained in:
Akos Kitta 2023-08-14 12:12:05 +02:00 committed by GitHub
parent 144df893d0
commit 9a6a457bc4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
143 changed files with 5011 additions and 8095 deletions

View File

@ -15,7 +15,9 @@ module.exports = {
'.browser_modules/*',
'docs/*',
'scripts/*',
'electron-app/*',
'electron-app/lib/*',
'electron-app/src-gen/*',
'electron-app/gen-webpack*.js',
'!electron-app/webpack.config.js',
'plugins/*',
'arduino-ide-extension/src/node/cli-protocol',

View File

@ -92,6 +92,7 @@ jobs:
with:
node-version: '16.14'
registry-url: 'https://registry.npmjs.org'
cache: 'yarn'
- name: Install Python 3.x
uses: actions/setup-python@v4
@ -135,15 +136,24 @@ jobs:
if [ "${{ runner.OS }}" = "Windows" ]; then
npm config set msvs_version 2017 --global
fi
npx node-gyp install
yarn --cwd ./electron/packager/
yarn --cwd ./electron/packager/ package
yarn install --immutable
yarn --cwd arduino-ide-extension build
yarn test
yarn --cwd arduino-ide-extension test:slow
yarn --cwd arduino-ide-extension lint
yarn --cwd electron-app rebuild
yarn --cwd electron-app build
yarn --cwd electron-app package
- name: Upload [GitHub Actions]
uses: actions/upload-artifact@v3
with:
name: ${{ env.JOB_TRANSFER_ARTIFACT }}
path: electron/build/dist/build-artifacts/
path: electron-app/dist/build-artifacts
artifacts:
name: ${{ matrix.artifact.name }} artifact

View File

@ -63,6 +63,7 @@ jobs:
with:
node-version: '16.14'
registry-url: 'https://registry.npmjs.org'
cache: 'yarn'
- name: Install Go
uses: actions/setup-go@v4
@ -76,7 +77,7 @@ jobs:
version: 3.x
- name: Install dependencies
run: yarn
run: yarn install --immutable
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -21,6 +21,7 @@ jobs:
with:
node-version: '16.14'
registry-url: 'https://registry.npmjs.org'
cache: 'yarn'
- name: Install Go
uses: actions/setup-go@v4
@ -34,7 +35,7 @@ jobs:
version: 3.x
- name: Install dependencies
run: yarn
run: yarn install --immutable
- name: Run i18n:push script
run: yarn run i18n:push

View File

@ -21,6 +21,7 @@ jobs:
with:
node-version: '16.14'
registry-url: 'https://registry.npmjs.org'
cache: 'yarn'
- name: Install Go
uses: actions/setup-go@v4
@ -34,7 +35,7 @@ jobs:
version: 3.x
- name: Install dependencies
run: yarn
run: yarn install --immutable
- name: Run i18n:pull script
run: yarn run i18n:pull

View File

@ -23,6 +23,7 @@ jobs:
with:
node-version: ${{ env.NODE_VERSION }}
registry-url: 'https://registry.npmjs.org'
cache: 'yarn'
- name: Install Go
uses: actions/setup-go@v4
@ -36,7 +37,7 @@ jobs:
version: 3.x
- name: Install dependencies
run: yarn
run: yarn install --immutable
- name: Run themes:pull script
run: yarn run themes:pull

13
.gitignore vendored
View File

@ -1,25 +1,22 @@
node_modules/
# .node_modules is a hack for the electron builder.
.node_modules/
lib/
downloads/
build/
resources/
arduino-ide-extension/Examples/
!electron/build/
src-gen/
electron/build/webpack.config.js
gen-webpack.config.js
gen-webpack.node.config.js
.DS_Store
# switching from `electron` to `browser` in dev mode.
.browser_modules
yarn*.log
# For the VS Code extensions used by Theia.
plugins
electron-app/plugins
# the tokens folder for the themes
scripts/themes/tokens
# environment variables
.env
# content trace files for electron
electron-app/traces
# any Arduino LS generated log files
inols*.log
# The electron-builder output.
electron-app/dist

104
.vscode/launch.json vendored
View File

@ -4,20 +4,55 @@
{
"type": "node",
"request": "launch",
"name": "App (Electron) [Dev]",
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron",
"name": "App",
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron",
"windows": {
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron.cmd",
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron.cmd",
},
"cwd": "${workspaceFolder}/electron-app",
"args": [
".",
"--log-level=debug",
"--hostname=localhost",
"--app-project-path=${workspaceRoot}/electron-app",
"--app-project-path=${workspaceFolder}/electron-app",
"--remote-debugging-port=9222",
"--no-app-auto-install",
"--plugins=local-dir:../plugins",
"--plugins=local-dir:./plugins",
"--hosted-plugin-inspect=9339",
"--no-ping-timeout",
],
"env": {
"NODE_ENV": "development"
},
"sourceMaps": true,
"outFiles": [
"${workspaceFolder}/electron-app/lib/backend/electron-main.js",
"${workspaceFolder}/electron-app/lib/backend/main.js",
"${workspaceFolder}/electron-app/lib/**/*.js",
"${workspaceFolder}/arduino-ide-extension/lib/**/*.js",
"${workspaceFolder}/node_modules/@theia/**/*.js"
],
"smartStep": true,
"internalConsoleOptions": "openOnSessionStart",
"outputCapture": "std"
},
{
"type": "node",
"request": "launch",
"name": "App [Dev]",
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron",
"windows": {
"runtimeExecutable": "${workspaceFolder}/node_modules/.bin/electron.cmd",
},
"cwd": "${workspaceFolder}/electron-app",
"args": [
".",
"--log-level=debug",
"--hostname=localhost",
"--app-project-path=${workspaceFolder}/electron-app",
"--remote-debugging-port=9222",
"--no-app-auto-install",
"--plugins=local-dir:./plugins",
"--hosted-plugin-inspect=9339",
"--content-trace",
"--open-devtools",
@ -28,46 +63,11 @@
},
"sourceMaps": true,
"outFiles": [
"${workspaceRoot}/electron-app/src-gen/backend/*.js",
"${workspaceRoot}/electron-app/src-gen/frontend/*.js",
"${workspaceRoot}/electron-app/lib/**/*.js",
"${workspaceRoot}/arduino-ide-extension/lib/**/*.js",
"${workspaceRoot}/node_modules/@theia/**/*.js"
],
"smartStep": true,
"internalConsoleOptions": "openOnSessionStart",
"outputCapture": "std"
},
{
"type": "node",
"request": "launch",
"name": "App (Electron)",
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron",
"windows": {
"runtimeExecutable": "${workspaceRoot}/node_modules/.bin/electron.cmd",
},
"cwd": "${workspaceFolder}/electron-app",
"args": [
".",
"--log-level=debug",
"--hostname=localhost",
"--app-project-path=${workspaceRoot}/electron-app",
"--remote-debugging-port=9222",
"--no-app-auto-install",
"--plugins=local-dir:../plugins",
"--hosted-plugin-inspect=9339",
"--no-ping-timeout",
],
"env": {
"NODE_ENV": "development"
},
"sourceMaps": true,
"outFiles": [
"${workspaceRoot}/electron-app/src-gen/backend/*.js",
"${workspaceRoot}/electron-app/src-gen/frontend/*.js",
"${workspaceRoot}/electron-app/lib/**/*.js",
"${workspaceRoot}/arduino-ide-extension/lib/**/*.js",
"${workspaceRoot}/node_modules/@theia/**/*.js"
"${workspaceFolder}/electron-app/lib/backend/electron-main.js",
"${workspaceFolder}/electron-app/lib/backend/main.js",
"${workspaceFolder}/electron-app/lib/**/*.js",
"${workspaceFolder}/arduino-ide-extension/lib/**/*.js",
"${workspaceFolder}/node_modules/@theia/**/*.js"
],
"smartStep": true,
"internalConsoleOptions": "openOnSessionStart",
@ -84,7 +84,7 @@
"type": "node",
"request": "launch",
"name": "Run Test [current]",
"program": "${workspaceRoot}/node_modules/mocha/bin/_mocha",
"program": "${workspaceFolder}/node_modules/mocha/bin/_mocha",
"args": [
"--require",
"reflect-metadata/Reflect",
@ -95,7 +95,8 @@
"**/${fileBasenameNoExtension}.js"
],
"env": {
"TS_NODE_PROJECT": "${workspaceRoot}/tsconfig.json"
"TS_NODE_PROJECT": "${workspaceFolder}/tsconfig.json",
"IDE2_TEST": "true"
},
"sourceMaps": true,
"smartStep": true,
@ -108,19 +109,12 @@
"name": "Attach by Process ID",
"processId": "${command:PickProcess}"
},
{
"type": "node",
"request": "launch",
"name": "Electron Packager",
"program": "${workspaceRoot}/electron/packager/index.js",
"cwd": "${workspaceFolder}/electron/packager"
}
],
"compounds": [
{
"name": "Launch Electron Backend & Frontend",
"configurations": [
"App (Electron)",
"App",
"Attach to Electron Frontend"
]
}

12
.vscode/tasks.json vendored
View File

@ -2,7 +2,7 @@
"version": "2.0.0",
"tasks": [
{
"label": "Arduino IDE - Rebuild Electron App",
"label": "Rebuild App",
"type": "shell",
"command": "yarn rebuild:browser && yarn rebuild:electron",
"group": "build",
@ -13,7 +13,7 @@
}
},
{
"label": "Arduino IDE - Watch IDE Extension",
"label": "Watch Extension",
"type": "shell",
"command": "yarn --cwd ./arduino-ide-extension watch",
"group": "build",
@ -24,7 +24,7 @@
}
},
{
"label": "Arduino IDE - Watch Electron App",
"label": "Watch App",
"type": "shell",
"command": "yarn --cwd ./electron-app watch",
"group": "build",
@ -35,11 +35,11 @@
}
},
{
"label": "Arduino IDE - Watch All [Electron]",
"label": "Watch All",
"type": "shell",
"dependsOn": [
"Arduino IDE - Watch IDE Extension",
"Arduino IDE - Watch Electron App"
"Watch Extension",
"Watch App"
]
}
]

View File

@ -4,63 +4,60 @@
"description": "An extension for Theia building the Arduino IDE",
"license": "AGPL-3.0-or-later",
"scripts": {
"prepare": "yarn download-cli && yarn download-fwuploader && yarn download-ls && yarn copy-i18n && yarn clean && yarn download-examples && yarn build && yarn test",
"prepare": "yarn download-cli && yarn download-fwuploader && yarn download-ls && yarn copy-i18n && yarn download-examples",
"clean": "rimraf lib",
"compose-changelog": "node ./scripts/compose-changelog.js",
"download-cli": "node ./scripts/download-cli.js",
"download-fwuploader": "node ./scripts/download-fwuploader.js",
"copy-i18n": "ncp ../i18n ./build/i18n",
"copy-i18n": "ncp ../i18n ./src/node/resources/i18n",
"download-ls": "node ./scripts/download-ls.js",
"download-examples": "node ./scripts/download-examples.js",
"generate-protocol": "node ./scripts/generate-protocol.js",
"lint": "eslint",
"build": "tsc && ncp ./src/node/cli-protocol/ ./lib/node/cli-protocol/ && yarn lint",
"prebuild": "rimraf lib",
"build": "tsc",
"build:dev": "yarn build",
"postbuild": "ncp ./src/node/cli-protocol/ ./lib/node/cli-protocol/",
"watch": "tsc -w",
"test": "mocha \"./lib/test/**/*.test.js\"",
"test:slow": "mocha \"./lib/test/**/*.slow-test.js\" --slow 5000",
"test:watch": "mocha --watch --watch-files lib \"./lib/test/**/*.test.js\""
"test": "cross-env IDE2_TEST=true mocha \"./lib/test/**/*.test.js\"",
"test:slow": "cross-env IDE2_TEST=true mocha \"./lib/test/**/*.slow-test.js\" --slow 5000"
},
"dependencies": {
"@grpc/grpc-js": "^1.8.14",
"@theia/application-package": "1.37.0",
"@theia/core": "1.37.0",
"@theia/debug": "1.37.0",
"@theia/editor": "1.37.0",
"@theia/electron": "1.37.0",
"@theia/filesystem": "1.37.0",
"@theia/keymaps": "1.37.0",
"@theia/markers": "1.37.0",
"@theia/messages": "1.37.0",
"@theia/monaco": "1.37.0",
"@theia/application-package": "1.39.0",
"@theia/core": "1.39.0",
"@theia/debug": "1.39.0",
"@theia/editor": "1.39.0",
"@theia/electron": "1.39.0",
"@theia/filesystem": "1.39.0",
"@theia/keymaps": "1.39.0",
"@theia/markers": "1.39.0",
"@theia/messages": "1.39.0",
"@theia/monaco": "1.39.0",
"@theia/monaco-editor-core": "1.72.3",
"@theia/navigator": "1.37.0",
"@theia/outline-view": "1.37.0",
"@theia/output": "1.37.0",
"@theia/plugin-ext": "1.37.0",
"@theia/preferences": "1.37.0",
"@theia/scm": "1.37.0",
"@theia/search-in-workspace": "1.37.0",
"@theia/terminal": "1.37.0",
"@theia/typehierarchy": "1.37.0",
"@theia/workspace": "1.37.0",
"@theia/navigator": "1.39.0",
"@theia/outline-view": "1.39.0",
"@theia/output": "1.39.0",
"@theia/plugin-ext": "1.39.0",
"@theia/preferences": "1.39.0",
"@theia/scm": "1.39.0",
"@theia/search-in-workspace": "1.39.0",
"@theia/terminal": "1.39.0",
"@theia/typehierarchy": "1.39.0",
"@theia/workspace": "1.39.0",
"@tippyjs/react": "^4.2.5",
"@types/auth0-js": "^9.14.0",
"@types/btoa": "^1.2.3",
"@types/dateformat": "^3.0.1",
"@types/deepmerge": "^2.2.0",
"@types/glob": "^7.2.0",
"@types/google-protobuf": "^3.7.2",
"@types/js-yaml": "^3.12.2",
"@types/jsdom": "^21.1.1",
"@types/keytar": "^4.4.0",
"@types/lodash.debounce": "^4.0.6",
"@types/node-fetch": "^2.5.7",
"@types/p-queue": "^2.3.1",
"@types/ps-tree": "^1.1.0",
"@types/react-tabs": "^2.3.2",
"@types/temp": "^0.8.34",
"@types/which": "^1.3.1",
"@vscode/debugprotocol": "^1.51.0",
"arduino-serial-plotter-webapp": "0.2.0",
"async-mutex": "^0.3.0",
"auth0-js": "^9.14.0",
@ -70,6 +67,7 @@
"cross-fetch": "^3.1.5",
"dateformat": "^3.0.3",
"deepmerge": "^4.2.2",
"drivelist": "^9.2.4",
"electron-updater": "^4.6.5",
"fast-json-stable-stringify": "^2.1.0",
"fast-safe-stringify": "^2.1.1",
@ -87,6 +85,7 @@
"lodash.debounce": "^4.0.8",
"minimatch": "^3.1.2",
"node-fetch": "^2.6.1",
"node-log-rotate": "^0.1.5",
"open": "^8.0.6",
"p-debounce": "^2.1.0",
"p-queue": "^2.4.2",
@ -112,18 +111,20 @@
"@types/chai": "^4.2.7",
"@types/mocha": "^5.2.7",
"@types/react-window": "^1.8.5",
"@xhmikosr/downloader": "^13.0.1",
"chai": "^4.2.0",
"cross-env": "^7.0.3",
"decompress": "^4.2.0",
"decompress-tarbz2": "^4.1.1",
"decompress-targz": "^4.1.1",
"decompress-unzip": "^4.0.1",
"download": "^7.1.0",
"grpc_tools_node_protoc_ts": "^4.1.0",
"mocha": "^7.0.0",
"mockdate": "^3.0.5",
"moment": "^2.24.0",
"ncp": "^2.0.0",
"protoc": "^1.0.4",
"rimraf": "^2.6.1",
"shelljs": "^0.8.3",
"uuid": "^3.2.1",
"yargs": "^11.1.0"
@ -170,17 +171,17 @@
}
],
"arduino": {
"cli": {
"arduino-cli": {
"version": "0.33.1"
},
"fwuploader": {
"arduino-fwuploader": {
"version": "2.2.2"
},
"arduino-language-server": {
"version": "0.7.4"
},
"clangd": {
"version": "14.0.0"
},
"languageServer": {
"version": "0.7.4"
}
}
}

View File

@ -19,7 +19,7 @@
return undefined;
}
const { cli } = arduino;
const cli = arduino['arduino-cli'];
if (!cli) {
return undefined;
}
@ -34,9 +34,15 @@
}
const { platform, arch } = process;
const buildFolder = path.join(__dirname, '..', 'build');
const resourcesFolder = path.join(
__dirname,
'..',
'src',
'node',
'resources'
);
const cliName = `arduino-cli${platform === 'win32' ? '.exe' : ''}`;
const destinationPath = path.join(buildFolder, cliName);
const destinationPath = path.join(resourcesFolder, cliName);
if (typeof version === 'string') {
const suffix = (() => {

View File

@ -5,12 +5,27 @@ const version = '1.10.0';
(async () => {
const os = require('node:os');
const { promises: fs } = require('node:fs');
const { existsSync, promises: fs } = require('node:fs');
const path = require('node:path');
const shell = require('shelljs');
const { v4 } = require('uuid');
const { exec } = require('./utils');
const destination = path.join(
__dirname,
'..',
'src',
'node',
'resources',
'Examples'
);
if (existsSync(destination)) {
shell.echo(
`Skipping Git checkout of the examples because the repository already exists: ${destination}`
);
return;
}
const repository = path.join(os.tmpdir(), `${v4()}-arduino-examples`);
if (shell.mkdir('-p', repository).code !== 0) {
shell.exit(1);
@ -28,7 +43,6 @@ const version = '1.10.0';
shell
);
const destination = path.join(__dirname, '..', 'Examples');
shell.mkdir('-p', destination);
shell.cp('-fR', path.join(repository, 'examples', '*'), destination);

View File

@ -18,7 +18,7 @@
return undefined;
}
const { fwuploader } = arduino;
const fwuploader = arduino['arduino-fwuploader'];
if (!fwuploader) {
return undefined;
}
@ -35,11 +35,17 @@
}
const { platform, arch } = process;
const buildFolder = path.join(__dirname, '..', 'build');
const resourcesFolder = path.join(
__dirname,
'..',
'src',
'node',
'resources'
);
const fwuploderName = `arduino-fwuploader${
platform === 'win32' ? '.exe' : ''
}`;
const destinationPath = path.join(buildFolder, fwuploderName);
const destinationPath = path.join(resourcesFolder, fwuploderName);
if (typeof version === 'string') {
const suffix = (() => {

View File

@ -16,7 +16,8 @@
const { arduino } = pkg;
if (!arduino) return [undefined, undefined];
const { languageServer, clangd } = arduino;
const { clangd } = arduino;
const languageServer = arduino['arduino-language-server'];
if (!languageServer) return [undefined, undefined];
if (!clangd) return [undefined, undefined];
@ -62,41 +63,50 @@
const force = yargs['force-download'];
const { platform, arch } = process;
const platformArch = platform + '-' + arch;
const build = path.join(__dirname, '..', 'build');
const resourcesFolder = path.join(
__dirname,
'..',
'src',
'node',
'resources'
);
const lsExecutablePath = path.join(
build,
resourcesFolder,
`arduino-language-server${platform === 'win32' ? '.exe' : ''}`
);
let clangdExecutablePath, clangFormatExecutablePath, lsSuffix, clangdSuffix;
switch (platformArch) {
case 'darwin-x64':
clangdExecutablePath = path.join(build, 'clangd');
clangFormatExecutablePath = path.join(build, 'clang-format');
clangdExecutablePath = path.join(resourcesFolder, 'clangd');
clangFormatExecutablePath = path.join(resourcesFolder, 'clang-format');
lsSuffix = 'macOS_64bit.tar.gz';
clangdSuffix = 'macOS_64bit';
break;
case 'darwin-arm64':
clangdExecutablePath = path.join(build, 'clangd');
clangFormatExecutablePath = path.join(build, 'clang-format');
clangdExecutablePath = path.join(resourcesFolder, 'clangd');
clangFormatExecutablePath = path.join(resourcesFolder, 'clang-format');
lsSuffix = 'macOS_ARM64.tar.gz';
clangdSuffix = 'macOS_ARM64';
break;
case 'linux-x64':
clangdExecutablePath = path.join(build, 'clangd');
clangFormatExecutablePath = path.join(build, 'clang-format');
clangdExecutablePath = path.join(resourcesFolder, 'clangd');
clangFormatExecutablePath = path.join(resourcesFolder, 'clang-format');
lsSuffix = 'Linux_64bit.tar.gz';
clangdSuffix = 'Linux_64bit';
break;
case 'linux-arm64':
clangdExecutablePath = path.join(build, 'clangd');
clangFormatExecutablePath = path.join(build, 'clang-format');
clangdExecutablePath = path.join(resourcesFolder, 'clangd');
clangFormatExecutablePath = path.join(resourcesFolder, 'clang-format');
lsSuffix = 'Linux_ARM64.tar.gz';
clangdSuffix = 'Linux_ARM64';
break;
case 'win32-x64':
clangdExecutablePath = path.join(build, 'clangd.exe');
clangFormatExecutablePath = path.join(build, 'clang-format.exe');
clangdExecutablePath = path.join(resourcesFolder, 'clangd.exe');
clangFormatExecutablePath = path.join(
resourcesFolder,
'clang-format.exe'
);
lsSuffix = 'Windows_64bit.zip';
clangdSuffix = 'Windows_64bit';
break;
@ -116,20 +126,31 @@
? 'nightly/arduino-language-server'
: 'arduino-language-server_' + lsVersion
}_${lsSuffix}`;
downloader.downloadUnzipAll(lsUrl, build, lsExecutablePath, force);
downloader.downloadUnzipAll(
lsUrl,
resourcesFolder,
lsExecutablePath,
force
);
} else {
goBuildFromGit(lsVersion, lsExecutablePath, 'language-server');
}
const clangdUrl = `https://downloads.arduino.cc/tools/clangd_${clangdVersion}_${clangdSuffix}.tar.bz2`;
downloader.downloadUnzipAll(clangdUrl, build, clangdExecutablePath, force, {
strip: 1,
}); // `strip`: the new clangd (12.x) is zipped into a folder, so we have to strip the outmost folder.
downloader.downloadUnzipAll(
clangdUrl,
resourcesFolder,
clangdExecutablePath,
force,
{
strip: 1,
}
); // `strip`: the new clangd (12.x) is zipped into a folder, so we have to strip the outmost folder.
const clangdFormatUrl = `https://downloads.arduino.cc/tools/clang-format_${clangdVersion}_${clangdSuffix}.tar.bz2`;
downloader.downloadUnzipAll(
clangdFormatUrl,
build,
resourcesFolder,
clangFormatExecutablePath,
force,
{

View File

@ -1,7 +1,6 @@
const fs = require('fs');
const path = require('path');
const shell = require('shelljs');
const download = require('download');
const decompress = require('decompress');
const unzip = require('decompress-unzip');
const untargz = require('decompress-targz');
@ -47,6 +46,7 @@ exports.downloadUnzipFile = async (
}
shell.echo(`>>> Downloading from '${url}'...`);
const { default: download } = await import('@xhmikosr/downloader');
const data = await download(url);
shell.echo(`<<< Download succeeded.`);
@ -107,6 +107,7 @@ exports.downloadUnzipAll = async (
}
shell.echo(`>>> Downloading from '${url}'...`);
const { default: download } = await import('@xhmikosr/downloader');
const data = await download(url);
shell.echo(`<<< Download succeeded.`);

View File

@ -31,7 +31,7 @@
return defaultVersion;
}
const { cli } = arduino;
const cli = arduino['arduino-cli'];
if (!cli) {
return defaultVersion;
}
@ -65,9 +65,15 @@
shell.echo(`<<< Repository cloned.`);
const { platform } = process;
const build = path.join(__dirname, '..', 'build');
const resourcesFolder = path.join(
__dirname,
'..',
'src',
'node',
'resources'
);
const cli = path.join(
build,
resourcesFolder,
`arduino-cli${platform === 'win32' ? '.exe' : ''}`
);
const versionJson = exec(cli, ['version', '--format', 'json'], shell).trim();

View File

@ -90,9 +90,15 @@ function buildFromGit(command, version, destinationPath, taskName) {
return;
}
const buildFolder = path.join(__dirname, '..', 'build');
if (shell.mkdir('-p', buildFolder).code !== 0) {
shell.echo('Could not create build folder.');
const resourcesFolder = path.join(
__dirname,
'..',
'src',
'node',
'resources'
);
if (shell.mkdir('-p', resourcesFolder).code !== 0) {
shell.echo('Could not create resources folder.');
shell.exit(1);
}

View File

@ -1,11 +1,14 @@
import type { Disposable } from '@theia/core/lib/common/disposable';
import type { AppInfo } from '../electron-common/electron-arduino';
import type { StartupTasks } from '../electron-common/startup-task';
import type { Sketch } from './contributions/contribution';
export type { AppInfo };
export const AppService = Symbol('AppService');
export interface AppService {
quit(): void;
version(): Promise<string>;
info(): Promise<AppInfo>;
registerStartupTasksHandler(
handler: (tasks: StartupTasks) => void
): Disposable;

View File

@ -30,7 +30,7 @@ import {
injectable,
postConstruct,
} from '@theia/core/shared/inversify';
import * as React from '@theia/core/shared/react';
import React from '@theia/core/shared/react';
import { EditorCommands } from '@theia/editor/lib/browser/editor-command';
import { EditorMainMenu } from '@theia/editor/lib/browser/editor-menu';
import { MonacoMenus } from '@theia/monaco/lib/browser/monaco-menu';
@ -69,7 +69,7 @@ export class ArduinoFrontendContribution
private readonly appStateService: FrontendApplicationStateService;
@postConstruct()
protected async init(): Promise<void> {
protected init(): void {
if (!window.navigator.onLine) {
// tslint:disable-next-line:max-line-length
this.messageService.warn(

View File

@ -355,10 +355,8 @@ import { StylingParticipant } from '@theia/core/lib/browser/styling-service';
import { MonacoEditorMenuContribution } from './theia/monaco/monaco-menu';
import { MonacoEditorMenuContribution as TheiaMonacoEditorMenuContribution } from '@theia/monaco/lib/browser/monaco-menu';
import { UpdateArduinoState } from './contributions/update-arduino-state';
import { TerminalWidgetImpl } from './theia/terminal/terminal-widget-impl';
import { TerminalWidget } from '@theia/terminal/lib/browser/base/terminal-widget';
import { TerminalFrontendContribution } from './theia/terminal/terminal-frontend-contribution';
import { TerminalFrontendContribution as TheiaTerminalFrontendContribution } from '@theia/terminal/lib/browser/terminal-frontend-contribution'
import { TerminalFrontendContribution as TheiaTerminalFrontendContribution } from '@theia/terminal/lib/browser/terminal-frontend-contribution';
// Hack to fix copy/cut/paste issue after electron version update in Theia.
// https://github.com/eclipse-theia/theia/issues/12487
@ -1032,7 +1030,8 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
);
// Patch terminal issues.
rebind(TerminalWidget).to(TerminalWidgetImpl).inTransientScope();
bind(TerminalFrontendContribution).toSelf().inSingletonScope();
rebind(TheiaTerminalFrontendContribution).toService(TerminalFrontendContribution);
rebind(TheiaTerminalFrontendContribution).toService(
TerminalFrontendContribution
);
});

View File

@ -1,4 +1,4 @@
import * as React from '@theia/core/shared/react';
import React from '@theia/core/shared/react';
import { injectable, inject } from '@theia/core/shared/inversify';
import { Emitter } from '@theia/core/lib/common/event';
import { ReactWidget, Message } from '@theia/core/lib/browser';

View File

@ -1,4 +1,4 @@
import * as React from '@theia/core/shared/react';
import React from '@theia/core/shared/react';
import { Event } from '@theia/core/lib/common/event';
import { notEmpty } from '@theia/core/lib/common/objects';
import { MaybePromise } from '@theia/core/lib/common/types';

View File

@ -1,4 +1,4 @@
import * as React from '@theia/core/shared/react';
import React from '@theia/core/shared/react';
import * as ReactDOM from '@theia/core/shared/react-dom';
import { CommandRegistry } from '@theia/core/lib/common/command';
import { DisposableCollection } from '@theia/core/lib/common/disposable';

View File

@ -1,4 +1,4 @@
import * as React from '@theia/core/shared/react';
import React from '@theia/core/shared/react';
export type ProgressBarProps = {
percent?: number;

View File

@ -4,7 +4,6 @@ import { nls } from '@theia/core/lib/common/nls';
import { isOSX, isWindows } from '@theia/core/lib/common/os';
import { inject, injectable } from '@theia/core/shared/inversify';
import moment from 'moment';
import { ConfigService } from '../../common/protocol';
import { AppService } from '../app-service';
import { ArduinoMenus } from '../menu/arduino-menus';
import {
@ -18,8 +17,6 @@ import {
export class About extends Contribution {
@inject(ClipboardService)
private readonly clipboardService: ClipboardService;
@inject(ConfigService)
private readonly configService: ConfigService;
@inject(AppService)
private readonly appService: AppService;
@ -42,11 +39,9 @@ export class About extends Contribution {
}
private async showAbout(): Promise<void> {
const [appVersion, cliVersion] = await Promise.all([
this.appService.version(),
this.configService.getVersion(),
]);
const buildDate = this.buildDate;
const appInfo = await this.appService.info();
const { appVersion, cliVersion, buildDate } = appInfo;
const detail = (showAll: boolean) =>
nls.localize(
'arduino/about/detail',
@ -84,10 +79,6 @@ export class About extends Contribution {
return FrontendApplicationConfigProvider.get().applicationName;
}
private get buildDate(): string | undefined {
return FrontendApplicationConfigProvider.get().buildDate;
}
private ago(isoTime: string): string {
const now = moment(Date.now());
const other = moment(isoTime);

View File

@ -1,5 +1,5 @@
import { nls } from '@theia/core/lib/common';
import * as React from '@theia/core/shared/react';
import React from '@theia/core/shared/react';
export const CertificateAddComponent = ({
addCertificate,

View File

@ -1,4 +1,4 @@
import * as React from '@theia/core/shared/react';
import React from '@theia/core/shared/react';
export const CertificateListComponent = ({
certificates,

View File

@ -1,4 +1,4 @@
import * as React from '@theia/core/shared/react';
import React from '@theia/core/shared/react';
import Tippy from '@tippyjs/react';
import { AvailableBoard } from '../../boards/boards-service-provider';
import { CertificateListComponent } from './certificate-list';

View File

@ -1,4 +1,4 @@
import * as React from '@theia/core/shared/react';
import React from '@theia/core/shared/react';
import {
inject,
injectable,

View File

@ -1,5 +1,5 @@
import { nls } from '@theia/core/lib/common';
import * as React from '@theia/core/shared/react';
import React from '@theia/core/shared/react';
import { AvailableBoard } from '../../boards/boards-service-provider';
import { ArduinoSelect } from '../../widgets/arduino-select';

View File

@ -6,7 +6,7 @@ import { nls } from '@theia/core/lib/common/nls';
import { MaybePromise } from '@theia/core/lib/common/types';
import { Message } from '@theia/core/shared/@phosphor/messaging';
import { Widget } from '@theia/core/shared/@phosphor/widgets';
import * as React from '@theia/core/shared/react';
import React from '@theia/core/shared/react';
import { CreateApi } from '../create/create-api';
import { AbstractDialog } from '../theia/dialogs/dialogs';

View File

@ -1,5 +1,5 @@
import { nls } from '@theia/core/lib/common';
import * as React from '@theia/core/shared/react';
import React from '@theia/core/shared/react';
import { Port } from '../../../common/protocol';
import {
ArduinoFirmwareUploader,

View File

@ -1,4 +1,4 @@
import * as React from '@theia/core/shared/react';
import React from '@theia/core/shared/react';
import {
inject,
injectable,

View File

@ -1,5 +1,5 @@
import { nls } from '@theia/core/lib/common/nls';
import * as React from '@theia/core/shared/react';
import React from '@theia/core/shared/react';
// @ts-expect-error see https://github.com/microsoft/TypeScript/issues/49721#issuecomment-1319854183
import type { Options } from 'react-markdown';
import { ProgressInfo, UpdateInfo } from '../../../common/protocol/ide-updater';
@ -111,7 +111,6 @@ export const IDEUpdaterComponent = ({
>
<ReactMarkdown
components={{
// @ts-expect-error see imports. There is no ESM type-only import in CommonJS modules.
a: ({ href, children, ...props }) => (
<a onClick={() => href && openExternal(href)} {...props}>
{children}

View File

@ -1,4 +1,4 @@
import * as React from '@theia/core/shared/react';
import React from '@theia/core/shared/react';
import {
inject,
injectable,

View File

@ -1,4 +1,4 @@
import * as React from '@theia/core/shared/react';
import React from '@theia/core/shared/react';
import { Tab, Tabs, TabList, TabPanel } from 'react-tabs';
import 'react-tabs/style/react-tabs.css';
import { Disable } from 'react-disable';

View File

@ -1,4 +1,4 @@
import * as React from '@theia/core/shared/react';
import React from '@theia/core/shared/react';
import {
injectable,
inject,

View File

@ -1,4 +1,4 @@
import * as React from '@theia/core/shared/react';
import React from '@theia/core/shared/react';
import classnames from 'classnames';
interface SettingsStepInputProps {

View File

@ -117,7 +117,7 @@ export class SettingsService {
protected _settings: Settings;
@postConstruct()
protected async init(): Promise<void> {
protected init(): void {
this.appStateService.reachedState('ready').then(async () => {
const settings = await this.loadSettings();
this._settings = deepClone(settings);

View File

@ -1,4 +1,4 @@
import * as React from '@theia/core/shared/react';
import React from '@theia/core/shared/react';
import { BoardUserField } from '../../../common/protocol';
import { nls } from '@theia/core/lib/common';

View File

@ -1,4 +1,4 @@
import * as React from '@theia/core/shared/react';
import React from '@theia/core/shared/react';
import { inject, injectable } from '@theia/core/shared/inversify';
import { DialogProps } from '@theia/core/lib/browser/dialogs';
import { Message } from '@theia/core/shared/@phosphor/messaging';

View File

@ -1,9 +1,13 @@
import * as React from '@theia/core/shared/react';
import { injectable, inject, postConstruct } from '@theia/core/shared/inversify';
import React from '@theia/core/shared/react';
import {
injectable,
inject,
postConstruct,
} from '@theia/core/shared/inversify';
import {
AbstractViewContribution,
ApplicationShell,
codicon
codicon,
} from '@theia/core/lib/browser';
import { MonitorWidget } from './monitor-widget';
import { MenuModelRegistry, Command, CommandRegistry } from '@theia/core';
@ -20,7 +24,7 @@ import { MonitorManagerProxyClient } from '../../../common/protocol';
import {
ArduinoPreferences,
defaultMonitorWidgetDockPanel,
isMonitorWidgetDockPanel
isMonitorWidgetDockPanel,
} from '../../arduino-preferences';
import { serialMonitorWidgetLabel } from '../../../common/nls';
@ -60,7 +64,7 @@ export class MonitorViewContribution
static readonly TOGGLE_SERIAL_MONITOR_TOOLBAR =
MonitorWidget.ID + ':toggle-toolbar';
static readonly RESET_SERIAL_MONITOR = MonitorWidget.ID + ':reset';
@inject(MonitorModel)
private readonly model: MonitorModel;
@inject(MonitorManagerProxyClient)
@ -85,10 +89,16 @@ export class MonitorViewContribution
@postConstruct()
protected init(): void {
this._panel = this.arduinoPreferences['arduino.monitor.dockPanel'] ?? defaultMonitorWidgetDockPanel;
this._panel =
this.arduinoPreferences['arduino.monitor.dockPanel'] ??
defaultMonitorWidgetDockPanel;
this.monitorManagerProxy.onMonitorShouldReset(() => this.reset());
this.arduinoPreferences.onPreferenceChanged((event) => {
if (event.preferenceName === 'arduino.monitor.dockPanel' && isMonitorWidgetDockPanel(event.newValue) && event.newValue !== this._panel) {
if (
event.preferenceName === 'arduino.monitor.dockPanel' &&
isMonitorWidgetDockPanel(event.newValue) &&
event.newValue !== this._panel
) {
this._panel = event.newValue;
const widget = this.tryGetWidget();
// reopen at the new position if opened
@ -97,14 +107,14 @@ export class MonitorViewContribution
this.openView({ activate: true, reveal: true });
}
}
})
});
}
override get defaultViewOptions(): ApplicationShell.WidgetOptions {
const viewOptions = super.defaultViewOptions;
return {
...viewOptions,
area: this._panel
area: this._panel,
};
}

View File

@ -1,4 +1,4 @@
import * as React from '@theia/core/shared/react';
import React from '@theia/core/shared/react';
import {
injectable,
inject,

View File

@ -1,4 +1,4 @@
import * as React from '@theia/core/shared/react';
import React from '@theia/core/shared/react';
import { Key, KeyCode } from '@theia/core/lib/browser/keys';
import { Board } from '../../../common/protocol/boards-service';
import { DisposableCollection, nls } from '@theia/core/lib/common';

View File

@ -1,4 +1,4 @@
import * as React from '@theia/core/shared/react';
import React from '@theia/core/shared/react';
import { Event } from '@theia/core/lib/common/event';
import { DisposableCollection } from '@theia/core/lib/common/disposable';
import { areEqual, FixedSizeList as List } from 'react-window';

View File

@ -1,7 +1,7 @@
import { AboutDialog as TheiaAboutDialog } from '@theia/core/lib/browser/about-dialog';
export class AboutDialog extends TheiaAboutDialog {
protected override async init(): Promise<void> {
protected override init(): void {
// NOOP
// IDE2 has a custom about dialog, so it does not make sense to collect Theia extensions at startup time.
}

View File

@ -118,7 +118,7 @@ export class FrontendConnectionStatusService extends TheiaFrontendConnectionStat
private readonly connectionProvider: WebSocketConnectionProvider;
@postConstruct()
protected override async init(): Promise<void> {
protected override init(): void {
this.schedulePing();
const refresh = debounce(() => {
this.updateStatus(Boolean(this.daemonPort.port) && this.isOnline.online);

View File

@ -7,7 +7,7 @@ import {
injectable,
postConstruct,
} from '@theia/core/shared/inversify';
import * as React from '@theia/core/shared/react';
import React from '@theia/core/shared/react';
import { accountMenu } from '../../contributions/account';
import { CreateFeatures } from '../../create/create-features';
import { ApplicationConnectionStatusContribution } from './connection-status-service';

View File

@ -1,9 +1,5 @@
import debounce from 'p-debounce';
import {
inject,
injectable,
postConstruct,
} from '@theia/core/shared/inversify';
import { inject, injectable } from '@theia/core/shared/inversify';
import URI from '@theia/core/lib/common/uri';
import { Event, Emitter } from '@theia/core/lib/common/event';
import { FrontendApplicationStateService } from '@theia/core/lib/browser/frontend-application-state';
@ -42,9 +38,7 @@ export class DebugConfigurationManager extends TheiaDebugConfigurationManager {
return this.onTempContentDidChangeEmitter.event;
}
@postConstruct()
protected override async init(): Promise<void> {
super.init();
protected override async doInit(): Promise<void> {
this.appStateService.reachedState('ready').then(async () => {
const tempContent = await this.getTempLaunchJsonContent();
if (!tempContent) {
@ -78,6 +72,7 @@ export class DebugConfigurationManager extends TheiaDebugConfigurationManager {
});
this.updateModels();
});
return super.doInit();
}
protected override updateModels = debounce(async () => {

View File

@ -9,7 +9,7 @@ import {
DisposableCollection,
} from '@theia/core/lib/common/disposable';
import { inject, injectable } from '@theia/core/shared/inversify';
import * as React from '@theia/core/shared/react';
import React from '@theia/core/shared/react';
import { createRoot } from '@theia/core/shared/react-dom/client';
@injectable()

View File

@ -1,4 +1,4 @@
import * as React from '@theia/core/shared/react';
import React from '@theia/core/shared/react';
import { NotificationComponent } from './notification-component';
import { NotificationCenterComponent as TheiaNotificationCenterComponent } from '@theia/messages/lib/browser/notification-center-component';
import { nls } from '@theia/core/lib/common';

View File

@ -1,4 +1,4 @@
import * as React from '@theia/core/shared/react';
import React from '@theia/core/shared/react';
import { NotificationComponent as TheiaNotificationComponent } from '@theia/messages/lib/browser/notification-component';
import { nls } from '@theia/core/lib/common';
import { codicon } from '@theia/core/lib/browser';

View File

@ -1,4 +1,4 @@
import * as React from '@theia/core/shared/react';
import React from '@theia/core/shared/react';
import { NotificationComponent } from './notification-component';
import { NotificationToastsComponent as TheiaNotificationToastsComponent } from '@theia/messages/lib/browser/notification-toasts-component';

View File

@ -1,4 +1,4 @@
import * as React from '@theia/core/shared/react';
import React from '@theia/core/shared/react';
import {
inject,
injectable,

View File

@ -14,7 +14,7 @@ export class PreferenceTreeGenerator extends TheiaPreferenceTreeGenerator {
@inject(FrontendApplicationStateService)
private readonly appStateService: FrontendApplicationStateService;
protected override async init(): Promise<void> {
protected override init(): void {
this.appStateService.onStateChanged((state) => {
this.state = state;
// manually trigger a model (and UI) refresh if it was requested during the startup phase.

View File

@ -1,38 +1,16 @@
import { TabBarToolbarRegistry } from '@theia/core/lib/browser/shell/tab-bar-toolbar';
import { CommandRegistry } from '@theia/core/lib/common/command';
import { Widget } from '@theia/core/shared/@phosphor/widgets';
import { injectable } from '@theia/core/shared/inversify';
import { TerminalWidget } from '@theia/terminal/lib/browser/base/terminal-widget';
import {
TerminalCommands,
TerminalFrontendContribution as TheiaTerminalFrontendContribution,
} from '@theia/terminal/lib/browser/terminal-frontend-contribution';
// Patch for https://github.com/eclipse-theia/theia/pull/12626
@injectable()
export class TerminalFrontendContribution extends TheiaTerminalFrontendContribution {
override registerCommands(commands: CommandRegistry): void {
super.registerCommands(commands);
commands.unregisterCommand(TerminalCommands.SPLIT);
commands.registerCommand(TerminalCommands.SPLIT, {
execute: () => this.splitTerminal(),
isEnabled: (w) => this.withWidget(w, () => true),
isVisible: (w) => this.withWidget(w, () => true),
});
}
override registerToolbarItems(toolbar: TabBarToolbarRegistry): void {
super.registerToolbarItems(toolbar);
// removes the `split-terminal` command from the tabbar toolbar
// https://github.com/dankeboy36/esp-exception-decoder/pull/1#pullrequestreview-1500146673
toolbar.unregisterItem(TerminalCommands.SPLIT.id);
}
private withWidget<T>(
widget: Widget | undefined,
fn: (widget: TerminalWidget) => T
): T | false {
if (widget instanceof TerminalWidget) {
return fn(widget);
}
return false;
}
}

View File

@ -1,23 +0,0 @@
import { injectable } from '@theia/core/shared/inversify';
import { TerminalWidgetImpl as TheiaTerminalWidgetImpl } from '@theia/terminal/lib/browser/terminal-widget-impl';
import debounce from 'p-debounce';
// Patch for https://github.com/eclipse-theia/theia/pull/12587
@injectable()
export class TerminalWidgetImpl extends TheiaTerminalWidgetImpl {
private readonly debouncedResizeTerminal = debounce(
() => this.doResizeTerminal(),
50
);
protected override resizeTerminal(): void {
this.debouncedResizeTerminal();
}
private doResizeTerminal(): void {
const geo = this.fitAddon.proposeDimensions();
const cols = geo.cols;
const rows = geo.rows - 1; // subtract one row for margin
this.term.resize(cols, rows);
}
}

View File

@ -28,6 +28,11 @@ export class WorkspaceInputDialog extends TheiaWorkspaceInputDialog {
protected override readonly labelProvider: LabelProvider
) {
super(props, labelProvider);
if (this.contentNode.contains(this.errorMessageNode)) {
// Reverts https://github.com/eclipse-theia/theia/pull/12585/files#diff-068570364d86f936ca72dfc52f8bfa93f14f6d971e2e6fa19216f33cb322244bR533-R534
this.contentNode.removeChild(this.errorMessageNode);
this.controlPanel.prepend(this.errorMessageNode);
}
this.node.classList.add('workspace-input-dialog');
this.appendCloseButton(Dialog.CANCEL);
}

View File

@ -1,4 +1,4 @@
import * as React from '@theia/core/shared/react';
import React from '@theia/core/shared/react';
import {
TabBarToolbar,
TabBarToolbarRegistry,

View File

@ -1,4 +1,4 @@
import * as React from '@theia/core/shared/react';
import React from '@theia/core/shared/react';
import type { Props, StylesConfig, ThemeConfig } from 'react-select';
import Select from 'react-select';

View File

@ -1,4 +1,4 @@
import * as React from '@theia/core/shared/react';
import React from '@theia/core/shared/react';
import type { Root } from '@theia/core/shared/react-dom/client';
import {
inject,

View File

@ -1,4 +1,4 @@
import * as React from '@theia/core/shared/react';
import React from '@theia/core/shared/react';
import { inject, injectable } from '@theia/core/shared/inversify';
import { TreeModel } from '@theia/core/lib/browser/tree/tree-model';
import { CloudSketchbookTreeModel } from './cloud-sketchbook-tree-model';

View File

@ -1,4 +1,4 @@
import * as React from '@theia/core/shared/react';
import React from '@theia/core/shared/react';
import { DisposableCollection } from '@theia/core/lib/common/disposable';
import { CloudSketchbookTreeModel } from './cloud-sketchbook-tree-model';
import { AuthenticationClientService } from '../../auth/authentication-client-service';

View File

@ -1,4 +1,4 @@
import * as React from '@theia/core/shared/react';
import React from '@theia/core/shared/react';
import type { ArduinoComponent } from '../../../common/protocol/arduino-component';
import { Installable } from '../../../common/protocol/installable';
import type { ListItemRenderer } from './list-item-renderer';

View File

@ -1,4 +1,4 @@
import * as React from '@theia/core/shared/react';
import React from '@theia/core/shared/react';
import { Virtuoso } from '@theia/core/shared/react-virtuoso';
import { ArduinoComponent } from '../../../common/protocol/arduino-component';
import { Installable } from '../../../common/protocol/installable';

View File

@ -1,5 +1,5 @@
import { injectable } from '@theia/core/shared/inversify';
import * as React from '@theia/core/shared/react';
import React from '@theia/core/shared/react';
import {
BoardSearch,
LibrarySearch,

View File

@ -1,4 +1,4 @@
import * as React from '@theia/core/shared/react';
import React from '@theia/core/shared/react';
import debounce from 'lodash.debounce';
import { Event } from '@theia/core/lib/common/event';
import { CommandService } from '@theia/core/lib/common/command';

View File

@ -23,7 +23,7 @@ import {
import { MessageService } from '@theia/core/lib/common/message-service';
import { nls } from '@theia/core/lib/common/nls';
import { inject, injectable } from '@theia/core/shared/inversify';
import * as React from '@theia/core/shared/react';
import React from '@theia/core/shared/react';
import { Unknown } from '../../../common/nls';
import {
CoreService,

View File

@ -1,4 +1,4 @@
import * as React from '@theia/core/shared/react';
import React from '@theia/core/shared/react';
import {
injectable,
postConstruct,

View File

@ -1,5 +1,5 @@
import { nls } from '@theia/core/lib/common';
import * as React from '@theia/core/shared/react';
import React from '@theia/core/shared/react';
export class SearchBar extends React.Component<SearchBar.Props> {
constructor(props: Readonly<SearchBar.Props>) {

View File

@ -1,4 +1,4 @@
import * as React from '@theia/core/shared/react';
import React from '@theia/core/shared/react';
export class CreateNew extends React.Component<CreateNew.Props> {
override render(): React.ReactNode {

View File

@ -1,4 +1,4 @@
import * as React from '@theia/core/shared/react';
import React from '@theia/core/shared/react';
import { createRoot, Root } from '@theia/core/shared/react-dom/client';
import { inject, injectable } from '@theia/core/shared/inversify';
import { nls } from '@theia/core/lib/common/nls';

View File

@ -1,4 +1,4 @@
import * as React from '@theia/core/shared/react';
import React from '@theia/core/shared/react';
import {
inject,
injectable,
@ -62,12 +62,16 @@ export class SketchbookTreeWidget extends FileTreeWidget {
}
@postConstruct()
protected override async init(): Promise<void> {
protected override init(): void {
super.init();
// cache the current open sketch uri
const currentSketch = await this.sketchServiceClient.currentSketch();
this.currentSketchUri =
(CurrentSketch.isValid(currentSketch) && currentSketch.uri) || '';
this.sketchServiceClient
.currentSketch()
.then(
(currentSketch) =>
(this.currentSketchUri =
(CurrentSketch.isValid(currentSketch) && currentSketch.uri) || '')
);
}
protected override createNodeClassNames(

View File

@ -3,7 +3,6 @@ import { RecursivePartial } from '@theia/core/lib/common/types';
export const ConfigServicePath = '/services/config-service';
export const ConfigService = Symbol('ConfigService');
export interface ConfigService {
getVersion(): Promise<Readonly<string>>;
getConfiguration(): Promise<ConfigState>;
setConfiguration(config: Config): Promise<void>;
}

View File

@ -1,6 +1,6 @@
import type { Disposable } from '@theia/core/lib/common/disposable';
import { injectable } from '@theia/core/shared/inversify';
import type { AppService } from '../browser/app-service';
import type { AppInfo, AppService } from '../browser/app-service';
import type { Sketch } from '../common/protocol/sketches-service';
import type { StartupTasks } from '../electron-common/startup-task';
@ -10,8 +10,8 @@ export class ElectronAppService implements AppService {
window.electronArduino.quitApp();
}
version(): Promise<string> {
return window.electronArduino.appVersion();
info(): Promise<AppInfo> {
return window.electronArduino.appInfo();
}
registerStartupTasksHandler(

View File

@ -10,7 +10,7 @@ import {
import { v4 } from 'uuid';
import type { Sketch } from '../common/protocol/sketches-service';
import {
CHANNEL_APP_VERSION,
CHANNEL_APP_INFO,
CHANNEL_IS_FIRST_WINDOW,
CHANNEL_MAIN_MENU_ITEM_DID_CLICK,
CHANNEL_OPEN_PATH,
@ -76,7 +76,7 @@ const api: ElectronArduino = {
ipcRenderer.invoke(CHANNEL_SHOW_OPEN_DIALOG, options),
showSaveDialog: (options: SaveDialogOptions) =>
ipcRenderer.invoke(CHANNEL_SHOW_SAVE_DIALOG, options),
appVersion: () => ipcRenderer.invoke(CHANNEL_APP_VERSION),
appInfo: () => ipcRenderer.invoke(CHANNEL_APP_INFO),
quitApp: () => ipcRenderer.send(CHANNEL_QUIT_APP),
isFirstWindow: () => ipcRenderer.invoke(CHANNEL_IS_FIRST_WINDOW),
requestReload: (options: StartupTasks) =>

View File

@ -11,6 +11,17 @@ import type {
InternalMenuDto as TheiaInternalMenuDto,
MenuDto,
} from '@theia/core/lib/electron-common/electron-api';
export const appInfoPropertyLiterals = [
'appVersion',
'cliVersion',
'buildDate',
] as const;
export type AppInfoProperty = (typeof appInfoPropertyLiterals)[number];
export type AppInfo = {
readonly [P in AppInfoProperty]: string;
};
import type { Sketch } from '../common/protocol/sketches-service';
import type { StartupTasks } from './startup-task';
@ -50,7 +61,7 @@ export interface ElectronArduino {
showMessageBox(options: MessageBoxOptions): Promise<MessageBoxReturnValue>;
showOpenDialog(options: OpenDialogOptions): Promise<OpenDialogReturnValue>;
showSaveDialog(options: SaveDialogOptions): Promise<SaveDialogReturnValue>;
appVersion(): Promise<string>;
appInfo(): Promise<AppInfo>;
quitApp(): void;
isFirstWindow(): Promise<boolean>;
requestReload(tasks: StartupTasks): void;
@ -77,7 +88,7 @@ declare global {
export const CHANNEL_SHOW_MESSAGE_BOX = 'Arduino:ShowMessageBox';
export const CHANNEL_SHOW_OPEN_DIALOG = 'Arduino:ShowOpenDialog';
export const CHANNEL_SHOW_SAVE_DIALOG = 'Arduino:ShowSaveDialog';
export const CHANNEL_APP_VERSION = 'Arduino:AppVersion';
export const CHANNEL_APP_INFO = 'Arduino:AppInfo';
export const CHANNEL_QUIT_APP = 'Arduino:QuitApp';
export const CHANNEL_IS_FIRST_WINDOW = 'Arduino:IsFirstWindow';
export const CHANNEL_SCHEDULE_DELETION = 'Arduino:ScheduleDeletion';

View File

@ -18,7 +18,8 @@ import { createDisposableListener } from '@theia/core/lib/electron-main/event-ut
import { injectable } from '@theia/core/shared/inversify';
import { WebContents } from '@theia/electron/shared/electron';
import {
CHANNEL_APP_VERSION,
AppInfo,
CHANNEL_APP_INFO,
CHANNEL_IS_FIRST_WINDOW,
CHANNEL_MAIN_MENU_ITEM_DID_CLICK,
CHANNEL_OPEN_PATH,
@ -85,8 +86,8 @@ export class ElectronArduino implements ElectronMainApplicationContribution {
return result;
}
);
ipcMain.handle(CHANNEL_APP_VERSION, async () => {
return app.appVersion;
ipcMain.handle(CHANNEL_APP_INFO, async (): Promise<AppInfo> => {
return app.appInfo;
});
ipcMain.on(CHANNEL_QUIT_APP, () => app.requestStop());
ipcMain.handle(CHANNEL_IS_FIRST_WINDOW, async (event) => {

View File

@ -10,7 +10,7 @@ import { fork } from 'node:child_process';
import { AddressInfo } from 'node:net';
import { join, isAbsolute, resolve } from 'node:path';
import { promises as fs, rm, rmSync } from 'node:fs';
import { MaybePromise } from '@theia/core/lib/common/types';
import type { MaybePromise, Mutable } from '@theia/core/lib/common/types';
import { ElectronSecurityToken } from '@theia/core/lib/electron-common/electron-token';
import { FrontendApplicationConfig } from '@theia/application-package/lib/application-props';
import {
@ -31,6 +31,8 @@ import {
} from '@theia/core/lib/common/disposable';
import { Sketch } from '../../common/protocol';
import {
AppInfo,
appInfoPropertyLiterals,
CHANNEL_PLOTTER_WINDOW_DID_CLOSE,
CHANNEL_SCHEDULE_DELETION,
CHANNEL_SHOW_PLOTTER_WINDOW,
@ -72,6 +74,11 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
private readonly isTempSketch: IsTempSketch;
private startup = false;
private _firstWindowId: number | undefined;
private _appInfo: AppInfo = {
appVersion: '',
cliVersion: '',
buildDate: '',
};
private openFilePromise = new Deferred();
/**
* It contains all things the IDE2 must clean up before a normal stop.
@ -111,7 +118,8 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
const cwd = process.cwd();
this.attachFileAssociations(cwd);
this.useNativeWindowFrame = this.getTitleBarStyle(config) === 'native';
this._config = config;
this._config = await updateFrontendApplicationConfigFromPackageJson(config);
this._appInfo = updateAppInfo(this._appInfo, this._config);
this.hookApplicationEvents();
const [port] = await Promise.all([this.startBackend(), app.whenReady()]);
this.startContentTracing();
@ -615,8 +623,8 @@ export class ElectronMainApplication extends TheiaElectronMainApplication {
return this._firstWindowId;
}
get appVersion(): string {
return app.getVersion();
get appInfo(): AppInfo {
return this._appInfo;
}
private async delete(sketch: Sketch): Promise<void> {
@ -681,3 +689,84 @@ class InterruptWorkspaceRestoreError extends Error {
Object.setPrototypeOf(this, InterruptWorkspaceRestoreError.prototype);
}
}
// This is a workaround for a limitation with the Theia CLI and `electron-builder`.
// It is possible to run the `electron-builder` with `-c.extraMetadata.foo.bar=36` option.
// On the fly, a `package.json` file will be generated for the final bundled application with the additional `{ "foo": { "bar": 36 } }` metadata.
// The Theia build (via the CLI) requires the extra `foo.bar=36` metadata to be in the `package.json` at build time (before `electron-builder` time).
// See the generated `./electron-app/src-gen/backend/electron-main.js` and how this works.
// This method merges in any additional required properties defined in the current! `package.json` of the application. For example, the `buildDate`.
// The current package.json is the package.json of the `electron-app` if running from the source code,
// but it's the `package.json` inside the `resources/app/` folder if it's the final bundled app.
// See https://github.com/arduino/arduino-ide/pull/2144#pullrequestreview-1556343430.
async function updateFrontendApplicationConfigFromPackageJson(
config: FrontendApplicationConfig
): Promise<FrontendApplicationConfig> {
try {
const modulePath = __filename;
// must go from `./lib/backend/electron-main.js` to `./package.json` when the app is webpacked.
const packageJsonPath = join(modulePath, '..', '..', '..', 'package.json');
console.debug(
`Checking for frontend application configuration customizations. Module path: ${modulePath}, destination 'package.json': ${packageJsonPath}`
);
const rawPackageJson = await fs.readFile(packageJsonPath, {
encoding: 'utf8',
});
const packageJson = JSON.parse(rawPackageJson);
if (packageJson?.theia?.frontend?.config) {
const packageJsonConfig: Record<string, string> =
packageJson?.theia?.frontend?.config;
for (const property of appInfoPropertyLiterals) {
const value = packageJsonConfig[property];
if (value && !config[property]) {
if (!config[property]) {
console.debug(
`Setting 'theia.frontend.config.${property}' application configuration value to: ${JSON.stringify(
value
)} (type of ${typeof value})`
);
} else {
console.warn(
`Overriding 'theia.frontend.config.${property}' application configuration value with: ${JSON.stringify(
value
)} (type of ${typeof value}). Original value: ${JSON.stringify(
config[property]
)}`
);
}
config[property] = value;
}
}
console.debug(
`Frontend application configuration after modifications: ${JSON.stringify(
config
)}`
);
return config;
}
} catch (err) {
console.error(
`Could not read the frontend application configuration from the 'package.json' file. Falling back to (the Theia CLI) generated default config: ${JSON.stringify(
config
)}`,
err
);
}
return config;
}
/**
* Mutates the `toUpdate` argument and returns with it.
*/
function updateAppInfo(
toUpdate: Mutable<AppInfo>,
updateWith: Record<string, unknown>
): AppInfo {
appInfoPropertyLiterals.forEach((property) => {
const newValue = updateWith[property];
if (typeof newValue === 'string') {
toUpdate[property] = newValue;
}
});
return toUpdate;
}

View File

@ -15,9 +15,9 @@ import { EnvVariablesServer } from '@theia/core/lib/common/env-variables';
import { BackendApplicationContribution } from '@theia/core/lib/node/backend-application';
import { ArduinoDaemon, NotificationServiceServer } from '../common/protocol';
import { CLI_CONFIG } from './cli-config';
import { getExecPath } from './exec-util';
import { SettingsReader } from './settings-reader';
import { ProcessUtils } from '@theia/core/lib/node/process-utils';
import { arduinoCliPath } from './resources';
@injectable()
export class ArduinoDaemonImpl
@ -133,7 +133,7 @@ export class ArduinoDaemonImpl
}
getExecPath(): string {
return getExecPath('arduino-cli');
return arduinoCliPath;
}
protected async getSpawnArgs(): Promise<string[]> {

View File

@ -5,8 +5,9 @@ import {
ArduinoFirmwareUploader,
FirmwareInfo,
} from '../common/protocol/arduino-firmware-uploader';
import { getExecPath, spawnCommand } from './exec-util';
import { spawnCommand } from './exec-util';
import { MonitorManager } from './monitor-manager';
import { arduinoFirmwareUploaderPath } from './resources';
@injectable()
export class ArduinoFirmwareUploaderImpl implements ArduinoFirmwareUploader {
@ -74,7 +75,10 @@ export class ArduinoFirmwareUploaderImpl implements ArduinoFirmwareUploader {
}
private async runCommand(args: string[]): Promise<string> {
const execPath = getExecPath('arduino-fwuploader');
return await spawnCommand(execPath, args, this.onError.bind(this));
return await spawnCommand(
arduinoFirmwareUploaderPath,
args,
this.onError.bind(this)
);
}
}

View File

@ -111,7 +111,7 @@ import {
SurveyNotificationServicePath,
} from '../common/protocol/survey-service';
import { IsTempSketch } from './is-temp-sketch';
import { rebindNsfwFileSystemWatcher } from './theia/filesystem/nsfw-watcher/nsfw-bindings';
import { rebindNsfwFileSystemWatcher } from './theia/filesystem/nsfw-bindings';
import { MessagingContribution } from './theia/core/messaging-contribution';
import { MessagingService } from '@theia/core/lib/node/messaging/messaging-service';
import { HostedPluginReader } from './theia/plugin-ext/plugin-reader';

View File

@ -1,5 +1,5 @@
import * as http from 'node:http';
import * as url from 'node:url';
import http from 'node:http';
import url from 'node:url';
import { body } from './body';
import { authServerPort } from '../../common/protocol/authentication-service';

View File

@ -1,4 +1,4 @@
import type * as keytarType from 'keytar';
import type keytarType from 'keytar';
export type KeychainConfig = {
credentialsSection: string;
@ -6,9 +6,9 @@ export type KeychainConfig = {
};
type Keytar = {
getPassword: typeof keytarType['getPassword'];
setPassword: typeof keytarType['setPassword'];
deletePassword: typeof keytarType['deletePassword'];
getPassword: (typeof keytarType)['getPassword'];
setPassword: (typeof keytarType)['setPassword'];
deletePassword: (typeof keytarType)['deletePassword'];
};
export class Keychain {

View File

@ -6,7 +6,9 @@ import { constants, promises as fs } from 'node:fs';
import { join } from 'node:path';
import { ConfigService } from '../common/protocol';
import { Formatter, FormatterOptions } from '../common/protocol/formatter';
import { getExecPath, spawnCommand } from './exec-util';
import { spawnCommand } from './exec-util';
import { clangFormatPath } from './resources';
import defaultClangFormat from './default-formatter-config.json';
@injectable()
export class ClangFormatter implements Formatter {
@ -37,7 +39,7 @@ export class ClangFormatter implements Formatter {
}
private execPath(): string {
return getExecPath('clang-format');
return clangFormatPath;
}
/**
@ -129,10 +131,9 @@ function styleJson({
TabWidth,
UseTab,
}: ClangFormatOptions): Record<string, unknown> {
// Source: https://github.com/arduino/tooling-project-assets/tree/main/other/clang-format-configuration
const defaultConfig = require('../../src/node/default-formatter-config.json');
return {
...defaultConfig,
// Source: https://github.com/arduino/tooling-project-assets/tree/main/other/clang-format-configuration
...defaultClangFormat,
TabWidth,
UseTab,
};

View File

@ -1,6 +1,6 @@
import { promises as fs } from 'node:fs';
import { dirname } from 'node:path';
import * as yaml from 'js-yaml';
import yaml from 'js-yaml';
import * as grpc from '@grpc/grpc-js';
import { injectable, inject, named } from '@theia/core/shared/inversify';
import URI from '@theia/core/lib/common/uri';
@ -131,10 +131,6 @@ export class ConfigServiceImpl
return this.configChangeEmitter.event;
}
async getVersion(): Promise<string> {
return require('../../package.json').arduino?.cli?.version || '';
}
private async initConfig(): Promise<void> {
this.logger.info('>>> Initializing CLI configuration...');
try {

View File

@ -4,7 +4,7 @@ import {
postConstruct,
} from '@theia/core/shared/inversify';
import { join } from 'node:path';
import * as fs from 'node:fs';
import fs from 'node:fs';
import { FileUri } from '@theia/core/lib/node/file-uri';
import {
SketchRef,
@ -15,6 +15,7 @@ import { LibraryLocation, LibraryPackage } from '../common/protocol';
import { URI } from '@theia/core/lib/common/uri';
import { Path } from '@theia/core/lib/common/path';
import { LibraryServiceImpl } from './library-service-impl';
import { examplesPath } from './resources';
interface BuiltInSketchRef {
readonly name: string;
@ -64,7 +65,7 @@ export class BuiltInExamplesServiceImpl {
if (this._builtIns) {
return this._builtIns;
}
const examplesRootPath = join(__dirname, '..', '..', 'Examples');
const examplesRootPath = examplesPath;
const examplesRootUri = FileUri.create(examplesRootPath);
const rawJson = await fs.promises.readFile(
join(examplesRootPath, 'examples.json'),

View File

@ -1,18 +1,4 @@
import { spawn } from 'node:child_process';
import os from 'node:os';
import { join } from 'node:path';
export type ArduinoBinaryName =
| 'arduino-cli'
| 'arduino-fwuploader'
| 'arduino-language-server';
export type ClangBinaryName = 'clangd' | 'clang-format';
export type BinaryName = ArduinoBinaryName | ClangBinaryName;
export function getExecPath(binaryName: BinaryName): string {
const filename = `${binaryName}${os.platform() === 'win32' ? '.exe' : ''}`;
return join(__dirname, '..', '..', 'build', filename);
}
export function spawnCommand(
command: string,

View File

@ -1,7 +1,11 @@
import { FileUri } from '@theia/core/lib/node/file-uri';
import { injectable } from '@theia/core/shared/inversify';
import { ExecutableService } from '../common/protocol/executable-service';
import { getExecPath } from './exec-util';
import {
arduinoCliPath,
arduinoLanguageServerPath,
clangdPath,
} from './resources';
@injectable()
export class ExecutableServiceImpl implements ExecutableService {
@ -11,9 +15,9 @@ export class ExecutableServiceImpl implements ExecutableService {
lsUri: string;
}> {
return {
clangdUri: FileUri.create(getExecPath('clangd')).toString(),
cliUri: FileUri.create(getExecPath('arduino-cli')).toString(),
lsUri: FileUri.create(getExecPath('arduino-language-server')).toString(),
clangdUri: FileUri.create(clangdPath).toString(),
cliUri: FileUri.create(arduinoCliPath).toString(),
lsUri: FileUri.create(arduinoLanguageServerPath).toString(),
};
}
}

View File

@ -3,46 +3,56 @@ import {
LocalizationRegistry,
} from '@theia/core/lib/node/i18n/localization-contribution';
import { injectable } from '@theia/core/shared/inversify';
import { join } from 'node:path';
import bgJson from '../resources/i18n/bg.json';
import csJson from '../resources/i18n/cs.json';
import deJson from '../resources/i18n/de.json';
import esJson from '../resources/i18n/es.json';
import frJson from '../resources/i18n/fr.json';
import huJson from '../resources/i18n/hu.json';
import itJson from '../resources/i18n/it.json';
import jaJson from '../resources/i18n/ja.json';
import koJson from '../resources/i18n/ko.json';
import nlJson from '../resources/i18n/nl.json';
import plJson from '../resources/i18n/pl.json';
import ptJson from '../resources/i18n/pt.json';
import ruJson from '../resources/i18n/ru.json';
import trJson from '../resources/i18n/tr.json';
import uk_UAJson from '../resources/i18n/uk_UA.json';
import zhJson from '../resources/i18n/zh.json';
import zh_HantJson from '../resources/i18n/zh-Hant.json';
@injectable()
export class ArduinoLocalizationContribution
implements LocalizationContribution
{
// 0. index: locale
// 1. index: optional JSON file to `require` (if differs from the locale)
// keys: locales
// values: the required JSON modules
// If you touch the locales, please keep the alphabetical order. Also in the `package.json` for the VS Code language packs. Thank you! ❤️
// Note that IDE2 has more translations than available VS Code language packs. (https://github.com/arduino/arduino-ide/issues/1447)
private readonly locales: ReadonlyArray<[string, string?]> = [
['bg'],
['cs'],
['de'],
['es'],
['fr'],
['hu'],
// ['id'], Does not have Transifex translations, but has a VS Code language pack available on Open VSX.
['it'],
['ja'],
['ko'],
['nl'],
['pl'],
['pt-br', 'pt'],
['ru'],
['tr'],
['uk', 'uk_UA'],
['zh-cn', 'zh'],
['zh-tw', 'zh-Hant'],
];
private readonly locales: Readonly<Record<string, unknown>> = {
bg: bgJson,
cs: csJson,
de: deJson,
es: esJson,
fr: frJson,
hu: huJson,
// id: Does not have Transifex translations, but has a VS Code language pack available on Open VSX.
it: itJson,
ja: jaJson,
ko: koJson,
nl: nlJson,
pl: plJson,
'pt-br': ptJson,
ru: [ruJson],
tr: [trJson],
uk: uk_UAJson,
'zh-cn': zhJson,
'zh-tw': zh_HantJson,
};
async registerLocalizations(registry: LocalizationRegistry): Promise<void> {
for (const [locale, jsonFilename] of this.locales) {
registry.registerLocalizationFromRequire(
locale,
require(join(
__dirname,
`../../../build/i18n/${jsonFilename ?? locale}.json`
))
);
for (const [locale, module] of Object.entries(this.locales)) {
registry.registerLocalizationFromRequire(locale, module);
}
}
}

View File

@ -1,4 +1,4 @@
import * as fs from 'node:fs';
import fs from 'node:fs';
import tempDir from 'temp-dir';
import { isWindows, isOSX } from '@theia/core/lib/common/os';
import { injectable } from '@theia/core/shared/inversify';

View File

@ -1,4 +1,4 @@
import * as fs from 'node:fs';
import fs from 'node:fs';
import { join } from 'node:path';
import {
injectable,
@ -38,19 +38,21 @@ export class MonitorSettingsProviderImpl implements MonitorSettingsProvider {
private pluggableMonitorSettingsPath: string;
@postConstruct()
protected async init(): Promise<void> {
// get the monitor settings file path
const configDirUri = await this.envVariablesServer.getConfigDirUri();
this.pluggableMonitorSettingsPath = join(
FileUri.fsPath(configDirUri),
MONITOR_SETTINGS_FILE
);
protected init(): void {
(async () => {
// get the monitor settings file path
const configDirUri = await this.envVariablesServer.getConfigDirUri();
this.pluggableMonitorSettingsPath = join(
FileUri.fsPath(configDirUri),
MONITOR_SETTINGS_FILE
);
// read existing settings
await this.readSettingsFromFS();
// read existing settings
await this.readSettingsFromFS();
// init is done, resolve the deferred and unblock any call that was waiting for it
this.ready.resolve();
// init is done, resolve the deferred and unblock any call that was waiting for it
this.ready.resolve();
})();
}
async getSettings(

View File

@ -1,22 +1,20 @@
import * as path from 'node:path';
import * as express from '@theia/core/shared/express';
import { injectable } from '@theia/core/shared/inversify';
import { BackendApplicationContribution } from '@theia/core/lib/node/backend-application';
import express from '@theia/core/shared/express';
import { injectable } from '@theia/core/shared/inversify';
import path from 'node:path';
import { arduinoPlotterWebAppPath } from '../resources';
@injectable()
export class PlotterBackendContribution
implements BackendApplicationContribution
{
configure(app: express.Application): void {
const index = require.resolve(
'arduino-serial-plotter-webapp/build/index.html'
);
app.use(express.static(path.join(index, '..')));
app.use(express.static(arduinoPlotterWebAppPath));
app.get('/plotter', (req, res) => {
console.log(
`Serving serial plotter on http://${req.headers.host}${req.url}`
);
res.sendFile(index);
res.sendFile(path.join(arduinoPlotterWebAppPath, 'index.html'));
});
}
}

View File

@ -0,0 +1,32 @@
import path from 'node:path';
// When running the tests, the JS files are not yet bundled by webpack.
// Hence, the `resources` folder lookup is different.
const testEnv = process.env.IDE2_TEST === 'true';
const resourcesPath = path.join(
__dirname,
...(testEnv ? ['..', '..', 'src', 'node', 'resources'] : ['resources'])
);
const exe = process.platform === 'win32' ? '.exe' : '';
// binaries
export const arduinoCliPath = path.join(resourcesPath, 'arduino-cli' + exe);
export const arduinoFirmwareUploaderPath = path.join(
resourcesPath,
'arduino-fwuploader' + exe
);
export const arduinoLanguageServerPath = path.join(
resourcesPath,
'arduino-language-server' + exe
);
export const clangdPath = path.join(resourcesPath, 'clangd' + exe);
export const clangFormatPath = path.join(resourcesPath, 'clang-format' + exe);
// plotter
export const arduinoPlotterWebAppPath = path.join(
resourcesPath,
'arduino-serial-plotter-webapp'
);
// examples
export const examplesPath = path.join(resourcesPath, 'Examples');

View File

@ -8,13 +8,13 @@ import {
import { FileSystemWatcherService } from '@theia/filesystem/lib/common/filesystem-watcher-protocol';
import { NsfwFileSystemWatcherServerOptions } from '@theia/filesystem/lib/node/nsfw-watcher/nsfw-filesystem-service';
import { FileSystemWatcherServiceDispatcher } from '@theia/filesystem/lib/node/filesystem-watcher-dispatcher';
import { NoDelayDisposalTimeoutNsfwFileSystemWatcherService } from './nsfw-filesystem-service';
import { NoDelayDisposalTimeoutNsfwFileSystemWatcherService } from './nsfw-watcher/nsfw-filesystem-service';
export function rebindNsfwFileSystemWatcher(rebind: interfaces.Rebind): void {
rebind<NsfwFileSystemWatcherServiceProcessOptions>(
NsfwFileSystemWatcherServiceProcessOptions
).toConstantValue({
entryPoint: join(__dirname, 'index.js'),
entryPoint: join(__dirname, 'nsfw-watcher'),
});
rebind<FileSystemWatcherService>(FileSystemWatcherService)
.toDynamicValue((context) =>

View File

@ -1,5 +1,5 @@
import * as yargs from '@theia/core/shared/yargs';
import { JsonRpcProxyFactory } from '@theia/core';
import { JsonRpcProxyFactory } from '@theia/core/lib/common/messaging/proxy-factory';
import { NoDelayDisposalTimeoutNsfwFileSystemWatcherService } from './nsfw-filesystem-service';
import type { IPCEntryPoint } from '@theia/core/lib/node/messaging/ipc-protocol';
import type { FileSystemWatcherServiceClient } from '@theia/filesystem/lib/common/filesystem-watcher-protocol';

View File

@ -1,6 +1,6 @@
import { Emitter } from '@theia/core';
import { injectable } from '@theia/core/shared/inversify';
import * as WebSocket from '@theia/core/shared/ws';
import WebSocket from '@theia/core/shared/ws';
import { WebSocketProvider } from './web-socket-provider';
@injectable()

View File

@ -1,9 +1,9 @@
import { Event } from '@theia/core/lib/common/event';
import * as WebSocket from '@theia/core/shared/ws';
import type { AddressInfo } from '@theia/core/shared/ws';
export const WebSocketProvider = Symbol('WebSocketProvider');
export interface WebSocketProvider {
getAddress(): WebSocket.AddressInfo;
getAddress(): AddressInfo;
sendMessage(message: string): void;
onMessageReceived: Event<string>;
onClientsNumberChanged: Event<number>;

View File

@ -1,6 +1,6 @@
import * as fs from 'node:fs';
import * as path from 'node:path';
import * as temp from 'temp';
import fs from 'node:fs';
import path from 'node:path';
import temp from 'temp';
import { expect } from 'chai';
import { ChildProcess } from 'node:child_process';
import { safeLoad, safeDump } from 'js-yaml';

View File

@ -1,14 +1,15 @@
import { assert, expect } from 'chai';
import fs from 'node:fs';
import path from 'node:path';
import {
ArduinoBinaryName,
BinaryName,
ClangBinaryName,
getExecPath,
spawnCommand,
} from '../../node/exec-util';
import { spawnCommand } from '../../node/exec-util';
import temp from 'temp';
import {
arduinoCliPath,
arduinoFirmwareUploaderPath,
arduinoLanguageServerPath,
clangdPath,
clangFormatPath,
} from '../../node/resources';
describe('exec-utils', () => {
describe('spawnCommand', () => {
@ -26,7 +27,7 @@ describe('exec-utils', () => {
it("should execute the command without 'shell:true' even if the path contains spaces but is not escaped", async () => {
const segment = 'with some spaces';
const cliPath = getExecPath('arduino-cli');
const cliPath = arduinoCliPath;
const filename = path.basename(cliPath);
const tempPath = tracked.mkdirSync();
const tempPathWitSpaces = path.join(tempPath, segment);
@ -44,7 +45,7 @@ describe('exec-utils', () => {
type AssertOutput = (stdout: string) => void;
interface GetExecPathTestSuite {
readonly name: BinaryName;
readonly binaryName: string;
readonly flags?: string[];
readonly assertOutput: AssertOutput;
/**
@ -54,29 +55,29 @@ describe('exec-utils', () => {
readonly expectNonZeroExit?: boolean;
}
const binaryNameToVersionMapping: Record<BinaryName, string> = {
'arduino-cli': 'cli',
'arduino-language-server': 'languageServer',
'arduino-fwuploader': 'fwuploader',
clangd: 'clangd',
'clang-format': 'clangd',
const binaryNameToPathMapping: Record<string, string> = {
'arduino-cli': arduinoCliPath,
'arduino-language-server': arduinoLanguageServerPath,
'arduino-fwuploader': arduinoFirmwareUploaderPath,
clangd: clangdPath,
'clang-format': clangFormatPath,
};
function readVersionFromPackageJson(name: BinaryName): string {
function readVersionFromPackageJson(binaryName: string): string {
const raw = fs.readFileSync(
path.join(__dirname, '..', '..', '..', 'package.json'),
{ encoding: 'utf8' }
);
const json = JSON.parse(raw);
expect(json.arduino).to.be.not.undefined;
const mappedName = binaryNameToVersionMapping[name];
expect(mappedName).to.be.not.undefined;
const version = json.arduino[mappedName].version;
const version =
json.arduino[binaryName === 'clang-format' ? 'clangd' : binaryName]
.version;
expect(version).to.be.not.undefined;
return version;
}
function createTaskAssert(name: ArduinoBinaryName): AssertOutput {
function createTaskAssert(name: string): AssertOutput {
const version = readVersionFromPackageJson(name);
if (typeof version === 'string') {
return (stdout: string) => {
@ -91,7 +92,7 @@ describe('exec-utils', () => {
};
}
function createClangdAssert(name: ClangBinaryName): AssertOutput {
function createClangdAssert(name: string): AssertOutput {
const version = readVersionFromPackageJson(name);
return (stdout: string) => {
expect(stdout.includes(name)).to.be.true;
@ -101,17 +102,17 @@ describe('exec-utils', () => {
const suites: GetExecPathTestSuite[] = [
{
name: 'arduino-cli',
binaryName: 'arduino-cli',
flags: ['version'],
assertOutput: createTaskAssert('arduino-cli'),
},
{
name: 'arduino-fwuploader',
binaryName: 'arduino-fwuploader',
flags: ['version'],
assertOutput: createTaskAssert('arduino-fwuploader'),
},
{
name: 'arduino-language-server',
binaryName: 'arduino-language-server',
assertOutput: (stderr: string) => {
expect(stderr.includes('Path to ArduinoCLI config file must be set.'))
.to.be.true;
@ -119,12 +120,12 @@ describe('exec-utils', () => {
expectNonZeroExit: true,
},
{
name: 'clangd',
binaryName: 'clangd',
flags: ['--version'],
assertOutput: createClangdAssert('clangd'),
},
{
name: 'clang-format',
binaryName: 'clang-format',
flags: ['--version'],
assertOutput: createClangdAssert('clang-format'),
},
@ -133,13 +134,13 @@ describe('exec-utils', () => {
// This is not a functional test but it ensures all executables provided by IDE2 are tested.
it('should cover all provided executables', () => {
expect(suites.length).to.be.equal(
Object.keys(binaryNameToVersionMapping).length
Object.keys(binaryNameToPathMapping).length
);
});
suites.map((suite) =>
it(`should resolve '${suite.name}'`, async () => {
const execPath = getExecPath(suite.name);
it(`should resolve '${suite.binaryName}'`, async () => {
const execPath = binaryNameToPathMapping[suite.binaryName];
expect(execPath).to.be.not.undefined;
expect(execPath).to.be.not.empty;
expect(fs.accessSync(execPath, fs.constants.X_OK)).to.be.undefined;

Some files were not shown because too many files have changed in this diff Show More