mirror of
https://github.com/electron/electron.git
synced 2026-04-10 03:01:51 -04:00
Adds a GN typescript_check template that runs tsgo --noEmit over a
tsconfig and writes a stamp file on success, wired into BUILD.gn as
electron_lib_typecheck. electron_js2c depends on it so a broken type
in lib/ fails 'e build'.
tsgo (@typescript/native-preview, the Go-based TypeScript compiler
preview) runs the full lib/ typecheck in ~400ms, about 6x faster than
tsc 6.0.2 (~2.3s). ts-loader previously typechecked implicitly inside
webpack and was removed in the esbuild migration, so this restores
typecheck coverage that was briefly absent on the bundle build path.
Because tsgo has no 'ignoreDiagnostics' option like ts-loader's, the
previously-silenced TS6059 and TS1111 errors needed to be fixed
properly:
- tsconfig.electron.json drops 'rootDir: "lib"'. It was only
meaningful for emit, and the TS 6 implicit rootDir inference plus
the @node/* path alias was pulling Node's internal .js files
(specifically internal/url.js with its #searchParams brand check)
into the program.
- tsconfig.json drops the @node/* path alias entirely. Every call
site that used 'as typeof import("@node/lib/...")' is replaced
with narrow structural types declared once in an ambient
NodeInternalModules interface in typings/internal-ambient.d.ts.
__non_webpack_require__ becomes an overloaded function that picks
the right return type from a string-literal id argument, so call
sites no longer need 'as' casts.
- tsconfig.default_app.json: moduleResolution 'node' -> 'bundler'
(TS 6 deprecates 'node').
- typings/internal-ambient.d.ts: 'declare module NodeJS { }' ->
'declare namespace NodeJS { }' (TS 6 rejects the module keyword
for non-external declarations).
spec/ts-smoke/runner.js now invokes tsgo's bin instead of resolving
the 'typescript' package. The 'tsc' npm script is repointed from
'tsc' to 'tsgo' so the existing CI step
'node script/yarn.js tsc -p tsconfig.script.json' continues to run.
A small script/typecheck.js wrapper runs tsgo and writes the stamp
file for GN; the typescript_check template invokes it via a new
'tsc-check' npm script.
205 lines
7.1 KiB
TypeScript
205 lines
7.1 KiB
TypeScript
import type * as defaultMenuModule from '@electron/internal/browser/default-menu';
|
|
|
|
import { EventEmitter } from 'events';
|
|
import * as fs from 'fs';
|
|
import * as path from 'path';
|
|
|
|
import type * as url from 'url';
|
|
import type * as v8 from 'v8';
|
|
|
|
const Module = require('module') as NodeJS.ModuleInternal;
|
|
|
|
// We modified the original process.argv to let node.js load the init.js,
|
|
// we need to restore it here.
|
|
process.argv.splice(1, 1);
|
|
|
|
// Import common settings.
|
|
require('@electron/internal/common/init');
|
|
|
|
process._linkedBinding('electron_browser_event_emitter').setEventEmitterPrototype(EventEmitter.prototype);
|
|
|
|
// Don't quit on fatal error.
|
|
process.on('uncaughtException', function (error) {
|
|
// Do nothing if the user has a custom uncaught exception handler.
|
|
if (process.listenerCount('uncaughtException') > 1) {
|
|
return;
|
|
}
|
|
|
|
// Show error in GUI.
|
|
// We can't import { dialog } at the top of this file as this file is
|
|
// responsible for setting up the require hook for the "electron" module
|
|
// so we import it inside the handler down here
|
|
import('electron')
|
|
.then(({ dialog }) => {
|
|
const stack = error.stack ? error.stack : `${error.name}: ${error.message}`;
|
|
const message = 'Uncaught Exception:\n' + stack;
|
|
dialog.showErrorBox('A JavaScript error occurred in the main process', message);
|
|
});
|
|
});
|
|
|
|
// Emit 'exit' event on quit.
|
|
const { app } = require('electron');
|
|
|
|
app.on('quit', (_event: any, exitCode: number) => {
|
|
process.emit('exit', exitCode);
|
|
});
|
|
|
|
if (process.platform === 'win32') {
|
|
// If we are a Squirrel.Windows-installed app, set app user model ID
|
|
// so that users don't have to do this.
|
|
//
|
|
// Squirrel packages are always of the form:
|
|
//
|
|
// PACKAGE-NAME
|
|
// - Update.exe
|
|
// - app-VERSION
|
|
// - OUREXE.exe
|
|
//
|
|
// Squirrel itself will always set the shortcut's App User Model ID to the
|
|
// form `com.squirrel.PACKAGE-NAME.OUREXE`. We need to call
|
|
// app.setAppUserModelId with a matching identifier so that renderer processes
|
|
// will inherit this value.
|
|
const updateDotExe = path.join(path.dirname(process.execPath), '..', 'update.exe');
|
|
|
|
if (fs.existsSync(updateDotExe)) {
|
|
const packageDir = path.dirname(path.resolve(updateDotExe));
|
|
const packageName = path.basename(packageDir).replaceAll(/\s/g, '');
|
|
const exeName = path.basename(process.execPath).replace(/\.exe$/i, '').replaceAll(/\s/g, '');
|
|
|
|
app.setAppUserModelId(`com.squirrel.${packageName}.${exeName}`);
|
|
}
|
|
}
|
|
|
|
// Map process.exit to app.exit, which quits gracefully.
|
|
process.exit = app.exit as () => never;
|
|
|
|
// Load the RPC server.
|
|
require('@electron/internal/browser/rpc-server');
|
|
|
|
// Load the guest view manager.
|
|
require('@electron/internal/browser/guest-view-manager');
|
|
|
|
// Now we try to load app's package.json.
|
|
const v8Util = process._linkedBinding('electron_common_v8_util');
|
|
let packagePath = null;
|
|
let packageJson = null;
|
|
const searchPaths: string[] = v8Util.getHiddenValue(global, 'appSearchPaths');
|
|
const searchPathsOnlyLoadASAR: boolean = v8Util.getHiddenValue(global, 'appSearchPathsOnlyLoadASAR');
|
|
// Borrow the _getOrCreateArchive asar helper
|
|
const getOrCreateArchive = process._getOrCreateArchive;
|
|
delete process._getOrCreateArchive;
|
|
|
|
if (process.resourcesPath) {
|
|
for (packagePath of searchPaths) {
|
|
try {
|
|
packagePath = path.join(process.resourcesPath, packagePath);
|
|
if (searchPathsOnlyLoadASAR) {
|
|
if (!getOrCreateArchive?.(packagePath)) {
|
|
continue;
|
|
}
|
|
}
|
|
packageJson = Module._load(path.join(packagePath, 'package.json'));
|
|
break;
|
|
} catch {
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (packageJson == null) {
|
|
process.nextTick(function () {
|
|
return process.exit(1);
|
|
});
|
|
throw new Error('Unable to find a valid app');
|
|
}
|
|
|
|
// Set application's version.
|
|
if (packageJson.version != null) {
|
|
app.setVersion(packageJson.version);
|
|
}
|
|
|
|
// Set application's name.
|
|
if (packageJson.productName != null) {
|
|
app.name = `${packageJson.productName}`.trim();
|
|
} else if (packageJson.name != null) {
|
|
app.name = `${packageJson.name}`.trim();
|
|
}
|
|
|
|
// Set application's desktop name (Linux). These usually match the executable name,
|
|
// so use it as the default to ensure the app gets the correct icon in the taskbar and application switcher.
|
|
const desktopName = packageJson.desktopName || `${path.basename(process.execPath)}.desktop`;
|
|
app.setDesktopName(desktopName);
|
|
|
|
// Set v8 flags, deliberately lazy load so that apps that do not use this
|
|
// feature do not pay the price
|
|
if (packageJson.v8Flags != null) {
|
|
(require('v8') as typeof v8).setFlagsFromString(packageJson.v8Flags);
|
|
}
|
|
|
|
app.setAppPath(packagePath);
|
|
|
|
// Load the chrome devtools support.
|
|
require('@electron/internal/browser/devtools');
|
|
|
|
// Load protocol module to ensure it is populated on app ready
|
|
require('@electron/internal/browser/api/protocol');
|
|
|
|
// Load service-worker-main module to ensure it is populated on app ready
|
|
require('@electron/internal/browser/api/service-worker-main');
|
|
|
|
// Load web-contents module to ensure it is populated on app ready
|
|
require('@electron/internal/browser/api/web-contents');
|
|
|
|
// Load web-frame-main module to ensure it is populated on app ready
|
|
require('@electron/internal/browser/api/web-frame-main');
|
|
|
|
// Required because `new BrowserWindow` calls some WebContentsView stuff, so
|
|
// the inheritance needs to be set up before that happens.
|
|
require('@electron/internal/browser/api/web-contents-view');
|
|
|
|
// Set main startup script of the app.
|
|
const mainStartupScript = packageJson.main || 'index.js';
|
|
|
|
// Quit when all windows are closed and no other one is listening to this.
|
|
app.on('window-all-closed', () => {
|
|
if (app.listenerCount('window-all-closed') === 1) {
|
|
app.quit();
|
|
}
|
|
});
|
|
|
|
const { setDefaultApplicationMenu } = require('@electron/internal/browser/default-menu') as typeof defaultMenuModule;
|
|
|
|
// Create default menu.
|
|
//
|
|
// The |will-finish-launching| event is emitted before |ready| event, so default
|
|
// menu is set before any user window is created.
|
|
app.once('will-finish-launching', setDefaultApplicationMenu);
|
|
|
|
const { appCodeLoaded } = process;
|
|
delete process.appCodeLoaded;
|
|
|
|
if (packagePath) {
|
|
// Finally load app's main.js and transfer control to C++.
|
|
if ((packageJson.type === 'module' && !mainStartupScript.endsWith('.cjs')) || mainStartupScript.endsWith('.mjs')) {
|
|
const { runEntryPointWithESMLoader } = __non_webpack_require__('internal/modules/run_main');
|
|
const main = (require('url') as typeof url).pathToFileURL(path.join(packagePath, mainStartupScript));
|
|
runEntryPointWithESMLoader(async (cascadedLoader: any) => {
|
|
try {
|
|
await cascadedLoader.import(main.toString(), undefined, Object.create(null));
|
|
appCodeLoaded!();
|
|
} catch (err) {
|
|
appCodeLoaded!();
|
|
process.emit('uncaughtException', err as Error);
|
|
}
|
|
});
|
|
} else {
|
|
// Call appCodeLoaded before just for safety, it doesn't matter here as _load is synchronous
|
|
appCodeLoaded!();
|
|
Module._load(path.join(packagePath, mainStartupScript), Module, true);
|
|
}
|
|
} else {
|
|
console.error('Failed to locate a valid package to load (app, app.asar or default_app.asar)');
|
|
console.error('This normally means you\'ve damaged the Electron package somehow');
|
|
appCodeLoaded!();
|
|
}
|