mirror of
https://github.com/electron/electron.git
synced 2026-05-02 03:00:22 -04:00
* build: add oxfmt for code formatting and import sorting
Adds oxfmt as a devDependency alongside oxlint and wires it into the
lint pipeline. The .oxfmtrc.json config matches Electron's current JS
style (single quotes, semicolons, 2-space indent, trailing commas off,
printWidth 100) and configures sortImports with custom groups that
mirror the import/order pathGroups previously enforced by ESLint:
@electron/internal, @electron/*, and {electron,electron/**} each get
their own ordered group ahead of external modules.
- `yarn lint:fmt` runs `oxfmt --check` over JS/TS sources and is
chained into `yarn lint` so CI enforces it automatically.
- `yarn format` runs `oxfmt --write` for local fix-up.
- lint-staged invokes `oxfmt --write` on staged .js/.ts/.mjs/.cjs
files before oxlint, so formatting is applied at commit time.
The next commit applies the formatter to the existing codebase so the
check actually passes.
* chore: apply oxfmt formatting to JS and TS sources
Runs `yarn format` across lib/, spec/, script/, build/, default_app/,
and npm/ to bring the codebase in line with the .oxfmtrc.json settings
added in the previous commit. This is a pure formatting pass: import
statements are sorted into the groups defined by the config, method
chains longer than printWidth are broken, single-quoted strings
containing apostrophes are switched to double quotes, and a handful of
single-statement `if` bodies are re-wrapped and get braces added by
`oxlint --fix` to satisfy the `curly: multi-line` rule.
No behavior changes.
184 lines
5.0 KiB
TypeScript
184 lines
5.0 KiB
TypeScript
/**
|
|
* Utilities to parse comma-separated key value pairs used in browser APIs.
|
|
* For example: "x=100,y=200,width=500,height=500"
|
|
*/
|
|
import { BrowserWindowConstructorOptions } from 'electron/main';
|
|
|
|
type RequiredBrowserWindowConstructorOptions = Required<BrowserWindowConstructorOptions>;
|
|
type IntegerBrowserWindowOptionKeys = {
|
|
[K in keyof RequiredBrowserWindowConstructorOptions]: RequiredBrowserWindowConstructorOptions[K] extends number
|
|
? K
|
|
: never;
|
|
}[keyof RequiredBrowserWindowConstructorOptions];
|
|
|
|
// This could be an array of keys, but an object allows us to add a compile-time
|
|
// check validating that we haven't added an integer property to
|
|
// BrowserWindowConstructorOptions that this module doesn't know about.
|
|
const keysOfTypeNumberCompileTimeCheck: { [K in IntegerBrowserWindowOptionKeys]: true } = {
|
|
x: true,
|
|
y: true,
|
|
width: true,
|
|
height: true,
|
|
minWidth: true,
|
|
maxWidth: true,
|
|
minHeight: true,
|
|
maxHeight: true,
|
|
opacity: true
|
|
};
|
|
// Note `top` / `left` are special cases from the browser which we later convert
|
|
// to y / x.
|
|
// NOTE(@mlaurencin) `innerWidth` / `innerHeight` are also special cases. The spec
|
|
// states that `width` and `height` represent the window content size and are equivalent
|
|
// to `innerWidth` / `innerHeight`. However, our implementation currently incorrectly maps
|
|
// `width` and `height` to `outerWidth` and `outerHeight`, or the size of the window
|
|
// with all border and related window chrome.
|
|
const keysOfTypeNumber = new Set([
|
|
'top',
|
|
'left',
|
|
'innerWidth',
|
|
'innerHeight',
|
|
...Object.keys(keysOfTypeNumberCompileTimeCheck)
|
|
]);
|
|
|
|
/**
|
|
* Note that we only allow "0" and "1" boolean conversion when the type is known
|
|
* not to be an integer.
|
|
*
|
|
* The coercion of yes/no/1/0 represents best effort accordance with the spec:
|
|
* https://html.spec.whatwg.org/multipage/window-object.html#concept-window-open-features-parse-boolean
|
|
*/
|
|
type CoercedValue = string | number | boolean;
|
|
function coerce(key: string, value: string): CoercedValue {
|
|
if (keysOfTypeNumber.has(key)) {
|
|
return parseInt(value, 10);
|
|
}
|
|
|
|
switch (value) {
|
|
case 'true':
|
|
case '1':
|
|
case 'yes':
|
|
case undefined:
|
|
return true;
|
|
case 'false':
|
|
case '0':
|
|
case 'no':
|
|
return false;
|
|
default:
|
|
return value;
|
|
}
|
|
}
|
|
|
|
export function parseCommaSeparatedKeyValue(source: string) {
|
|
const parsed = {} as { [key: string]: any };
|
|
for (const keyValuePair of source.split(',')) {
|
|
const [key, value] = keyValuePair.split('=').map((str) => str.trim());
|
|
if (key) {
|
|
parsed[key] = coerce(key, value);
|
|
}
|
|
}
|
|
|
|
return parsed;
|
|
}
|
|
|
|
export function parseWebViewWebPreferences(preferences: string) {
|
|
return parseCommaSeparatedKeyValue(preferences);
|
|
}
|
|
|
|
const allowedWebPreferences = [
|
|
'zoomFactor',
|
|
'nodeIntegration',
|
|
'javascript',
|
|
'contextIsolation',
|
|
'webviewTag'
|
|
] as const;
|
|
type AllowedWebPreference = (typeof allowedWebPreferences)[number];
|
|
|
|
// Top-level BrowserWindow options that may be set via the window.open()
|
|
// features string. Options not listed here are silently dropped; apps that
|
|
// need to pass other options should use setWindowOpenHandler in the main
|
|
// process.
|
|
const allowedWindowOptions = new Set<string>([
|
|
// standard window.open() position/size features
|
|
'top',
|
|
'left',
|
|
'innerWidth',
|
|
'innerHeight',
|
|
// numeric
|
|
'x',
|
|
'y',
|
|
'width',
|
|
'height',
|
|
'minWidth',
|
|
'minHeight',
|
|
'maxWidth',
|
|
'maxHeight',
|
|
'opacity',
|
|
// presentational booleans
|
|
'show',
|
|
'center',
|
|
'useContentSize',
|
|
'frame',
|
|
'transparent',
|
|
'hasShadow',
|
|
'movable',
|
|
'closable',
|
|
'focusable',
|
|
'minimizable',
|
|
'maximizable',
|
|
'fullscreenable',
|
|
'alwaysOnTop',
|
|
'skipTaskbar',
|
|
'modal',
|
|
'acceptFirstMouse',
|
|
'autoHideMenuBar',
|
|
'enableLargerThanScreen',
|
|
'paintWhenInitiallyHidden',
|
|
'roundedCorners',
|
|
'thickFrame',
|
|
'disableAutoHideCursor',
|
|
'hiddenInMissionControl',
|
|
// presentational strings (no filesystem/network side effects)
|
|
'title',
|
|
'backgroundColor',
|
|
'tabbingIdentifier',
|
|
'titleBarStyle',
|
|
'vibrancy',
|
|
'visualEffectState',
|
|
'backgroundMaterial'
|
|
]);
|
|
|
|
/**
|
|
* Parses a feature string that has the format used in window.open().
|
|
*/
|
|
export function parseFeatures(features: string) {
|
|
const parsed = parseCommaSeparatedKeyValue(features);
|
|
|
|
const webPreferences: { [K in AllowedWebPreference]?: any } = {};
|
|
for (const key of allowedWebPreferences) {
|
|
if (parsed[key] === undefined) continue;
|
|
webPreferences[key] = parsed[key];
|
|
delete parsed[key];
|
|
}
|
|
|
|
// Per spec - https://html.spec.whatwg.org/multipage/nav-history-apis.html#dom-open-dev
|
|
// windows are always resizable.
|
|
if (parsed.resizable !== undefined) {
|
|
delete parsed.resizable;
|
|
}
|
|
|
|
if (parsed.left !== undefined) parsed.x = parsed.left;
|
|
if (parsed.top !== undefined) parsed.y = parsed.top;
|
|
|
|
const options: { [key: string]: CoercedValue } = {};
|
|
for (const key of Object.keys(parsed)) {
|
|
if (allowedWindowOptions.has(key)) {
|
|
options[key] = parsed[key];
|
|
}
|
|
}
|
|
|
|
return {
|
|
options: options as Omit<BrowserWindowConstructorOptions, 'webPreferences'>,
|
|
webPreferences
|
|
};
|
|
}
|