Skip to content

Commit

Permalink
fix: can run app in dev mode
Browse files Browse the repository at this point in the history
Ref: eclipse-theia/theia#12793
Signed-off-by: Akos Kitta <[email protected]>
  • Loading branch information
Akos Kitta committed Aug 2, 2023
1 parent 75480bd commit f7cb8aa
Show file tree
Hide file tree
Showing 5 changed files with 181 additions and 99 deletions.
7 changes: 4 additions & 3 deletions electron-app/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"name": "electron-app",
"version": "2.1.2",
"license": "AGPL-3.0-or-later",
"main": "arduino-ide-electron-main.js",
"main": "./src-gen/backend/electron-main.js",
"dependencies": {
"@theia/core": "1.39.0",
"@theia/debug": "1.39.0",
Expand All @@ -25,6 +25,7 @@
"@theia/cli": "1.39.0",
"7zip-min": "^1.4.4",
"chmodr": "^1.2.0",
"compression-webpack-plugin": "^9.0.0",
"copy-webpack-plugin": "^8.1.1",
"dateformat": "^5.0.3",
"electron": "^23.2.4",
Expand All @@ -45,10 +46,10 @@
"prebuild": "rimraf lib",
"build": "theia build",
"prebuild:dev": "yarn prebuild",
"build:dev": "theia build --mode development",
"build:dev": "theia build --config webpack.dev.js --mode development",
"test": "mocha \"./test/**/*.test.js\"",
"start": "theia start --plugins=local-dir:../plugins",
"watch": "theia build --watch --mode development",
"watch": "theia build --config webpack.dev.js --mode development --watch",
"prepackage": "rimraf dist",
"package": "node ./scripts/package.js",
"postpackage": "node ./scripts/post-package.js",
Expand Down
6 changes: 5 additions & 1 deletion electron-app/scripts/package.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ async function run() {
electronVersion.slice(1), // removes the leading ^ from the version. TODO: user `semver` to clean it.
'-c.extraMetadata.version',
version,
// overrides the `name` in the `package.json` to keep the `localStorage` location. (https://github.com/arduino/arduino-ide/pull/2144#pullrequestreview-1554005028)
'-c.extraMetadata.name',
'arduino-ide', // overrides the `name` in the `package.json` to keep the `localStorage` location. (https://github.com/arduino/arduino-ide/pull/2144#pullrequestreview-1554005028)
'arduino-ide',
`-c.${platform}.artifactName`,
artifactName,
'-c.extraMetadata.theia.frontend.config.appVersion',
Expand All @@ -31,6 +32,9 @@ async function run() {
typeof cliVersion === 'string' ? cliVersion : '',
'-c.extraMetadata.theia.frontend.config.buildDate',
new Date().toISOString(),
// when running in development mode, the main entry is a JS module generated by Theia. In the final application it's a custom module with the file logger.
'-c.extraMetadata.main',
'./arduino-ide-electron-main.js',
];
const updateChannel = getChannel();
if (updateChannel) {
Expand Down
138 changes: 138 additions & 0 deletions electron-app/webpack.base.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
// @ts-check
'use strict';

const chmodr = require('chmodr');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const path = require('node:path');
const fs = require('node:fs/promises');

const isWindows = process.platform === 'win32';
const isMacOS = process.platform === 'darwin';

function resolvePackagePath(target, baseDir = __dirname) {
const resolvePackageJsonPath = require('resolve-package-path');
const packageJsonPath = resolvePackageJsonPath(target, baseDir);
if (!packageJsonPath) {
throw new Error(
`Could not resolve package '${target}'. Base dir: ${baseDir}`
);
}
return path.join(packageJsonPath, '..'); // one level up to locate the package folder
}

// restore file permissions after webpack copy
// https://github.com/webpack-contrib/copy-webpack-plugin/issues/35#issuecomment-1407280257
class PermissionsPlugin {
constructor(targetPath, patchTheia12780 = false) {
this.targetPath = targetPath;
this.patchTheia12780 = patchTheia12780;
}

/**
* @param {import('webpack').Compiler} compiler
*/
apply(compiler) {
compiler.hooks.afterEmit.tap('PermissionsPlugin', () => {
return new Promise(async (resolve, reject) => {
if (this.patchTheia12780) {
let trashBinaryFilename = undefined;
if (isWindows) {
trashBinaryFilename = 'windows-trash.exe';
} else if (isMacOS) {
trashBinaryFilename = 'macos-trash';
}
if (trashBinaryFilename) {
await fs.chmod(
path.join(__dirname, 'lib', 'backend', trashBinaryFilename),
0o755
);
}
}
chmodr(this.targetPath, 0o755, (err) =>
err ? reject(err) : resolve(undefined)
);
});
});
}
}

/**
* Creates webpack plugins to copy all required resources (binaries, plotter app, translation files, etc.) to the appropriate location.
* @param {string} targetPath where to copy the resources
* @param {boolean|undefined} [patchTheia12780=true] to apply patch for https://github.com/eclipse-theia/theia/issues/12780. Only required in the production app.
* @param {string|undefined} [baseDir=__dirname] to calculate the modules from. Defaults to `__dirname`
*/
function createCopyArduinoResourcesPlugins(
targetPath,
patchTheia12780 = false,
baseDir = __dirname
) {
const trashBinariesPath = path.join(
resolvePackagePath('trash', baseDir),
'lib'
);
const copyOptions = {
patterns: [
// binaries
{
from: path.join(
resolvePackagePath('arduino-ide-extension', baseDir),
'src',
'node',
'resources'
),
to: targetPath,
globOptions: {
ignore: ['**/i18n/**'],
},
},
// plotter app
{
from: path.join(
resolvePackagePath('arduino-serial-plotter-webapp', baseDir),
'build'
),
to: path.resolve(targetPath, 'arduino-serial-plotter-webapp'),
},
],
};

if (patchTheia12780) {
// workaround for https://github.com/eclipse-theia/theia/issues/12780
// copy the Windows (`windows-trash.exe`) and macOS (`macos-trash`) executables for `trash`
if (isWindows) {
copyOptions.patterns.push({
from: path.join(trashBinariesPath, 'windows-trash.exe'),
to: path.resolve(__dirname, 'lib', 'backend'),
});
} else if (isMacOS) {
copyOptions.patterns.push({
from: path.join(trashBinariesPath, 'macos-trash'),
to: path.resolve(__dirname, 'lib', 'backend'),
});
}
}
return [
new CopyWebpackPlugin(copyOptions),
new PermissionsPlugin(targetPath, patchTheia12780),
];
}

/**
* Removes the compression webpack plugin if it's set in the config. Otherwise, it's NOOP>
* @param {import('webpack').Configuration} config
*/
function removeCompressionPlugin(config) {
const CompressionPlugin = require('compression-webpack-plugin');
for (let i = config.plugins?.length || 0; i >= 0; i--) {
const plugin = config.plugins?.[i];
if (plugin instanceof CompressionPlugin) {
config.plugins?.splice(i, 1);
}
}
}

module.exports = {
createCopyArduinoResourcesPlugins,
removeCompressionPlugin,
};
107 changes: 12 additions & 95 deletions electron-app/webpack.config.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
const chmodr = require('chmodr');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const path = require('node:path');
const fs = require('node:fs/promises');
const resolvePackagePath = require('resolve-package-path');
const webpack = require('webpack');
const frontend = require('./gen-webpack.config');
const backend = require('./gen-webpack.node.config');

const isWindows = process.platform === 'win32';
const isMacOS = process.platform === 'darwin';
const {
createCopyArduinoResourcesPlugins,
removeCompressionPlugin,
} = require('./webpack.base');

// https://github.com/browserify/node-util/issues/57#issuecomment-764436352
const mainWindowConfig = frontend[0];
Expand All @@ -32,97 +29,13 @@ if (process.platform !== 'win32') {
);
}

// restore file permissions after webpack copy
// https://github.com/webpack-contrib/copy-webpack-plugin/issues/35#issuecomment-1407280257
class PermissionsPlugin {
/**
*
* @param {import('webpack').Compiler} compiler
*/
apply(compiler) {
compiler.hooks.afterEmit.tap('PermissionsPlugin', () => {
return new Promise(async (resolve, reject) => {
let trashBinaryFilename = undefined;
if (isWindows) {
trashBinaryFilename = 'windows-trash.exe';
} else if (isMacOS) {
trashBinaryFilename = 'macos-trash';
}
if (trashBinaryFilename) {
await fs.chmod(
path.join(__dirname, 'lib', 'backend', trashBinaryFilename),
0o755
);
}
chmodr(
path.join(__dirname, 'lib', 'backend', 'resources'),
0o755,
(err) => (err ? reject(err) : resolve())
);
});
});
}
}

const trashBinariesPath = path.join(
resolvePackagePath('trash', __dirname),
'..',
'lib'
);

const copyOptions = {
patterns: [
// binaries
{
from: path.join(
resolvePackagePath('arduino-ide-extension', __dirname),
'..',
'src',
'node',
'resources'
),
to: path.resolve(__dirname, 'lib', 'backend', 'resources'),
globOptions: {
ignore: ['**/i18n/**'],
},
},
// plotter app
{
from: path.join(
resolvePackagePath('arduino-serial-plotter-webapp', __dirname),
'..',
'build'
),
to: path.resolve(
__dirname,
'lib',
'backend',
'resources',
'arduino-serial-plotter-webapp'
),
},
],
};

// workaround for https://github.com/eclipse-theia/theia/issues/12780
// copy the Windows (`windows-trash.exe`) and macOS (`macos-trash`) executables for `trash`
if (isWindows) {
copyOptions.patterns.push({
from: path.join(trashBinariesPath, 'windows-trash.exe'),
to: path.resolve(__dirname, 'lib', 'backend'),
});
} else if (isMacOS) {
copyOptions.patterns.push({
from: path.join(trashBinariesPath, 'macos-trash'),
to: path.resolve(__dirname, 'lib', 'backend'),
});
}

// Copy all the IDE2 binaries and the plotter web app.
// XXX: For whatever reason it is important to use `unshift` instead of `push`, and execute the additional webpack plugins before the Theia contributed ones kick in. Otherwise ours do not work.
backend.config.plugins.unshift(
new CopyWebpackPlugin(copyOptions),
new PermissionsPlugin()
...createCopyArduinoResourcesPlugins(
path.resolve(__dirname, 'lib', 'backend', 'resources'),
true
)
);

// Override the default entry from Theia as IDE2 has a customization of the module.
Expand All @@ -141,5 +54,9 @@ backend.config.entry['main'] = require.resolve('./arduino-ide-backend-main.js');
backend.config.optimization.splitChunks = false;
backend.config.optimization.concatenateModules = true;

// Removed GZIP compression: the frontend is on the same machine as the backend.
removeCompressionPlugin(mainWindowConfig);
removeCompressionPlugin(preloadConfig);

// Do not include the `secondary-window` configuration from Theia. It's unused in IDE2, and can save up to ~30MB final app size.
module.exports = [mainWindowConfig, preloadConfig, backend.config];
22 changes: 22 additions & 0 deletions electron-app/webpack.dev.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// When running in development mode, do not webpack the backend and electron main modules.
// It does not work in watch mode: https://github.com/eclipse-theia/theia/issues/12793.
const path = require('node:path');
const configs = require('./webpack.config');
const { createCopyArduinoResourcesPlugins } = require('./webpack.base');
const [mainWindowConfig, preloadConfig] = configs;

// Use the frontend's webpack config to copy the required resources to the `./arduino-ide-extension/lib/node/resources` folder.
mainWindowConfig.plugins?.push(
...createCopyArduinoResourcesPlugins(
path.join(
__dirname,
'..',
'arduino-ide-extension',
'lib',
'node',
'resources'
)
)
);

module.exports = [mainWindowConfig, preloadConfig];

0 comments on commit f7cb8aa

Please sign in to comment.