Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
"devDependencies": {
"@electron/docs-parser": "^2.0.0",
"@types/debug": "^4.1.12",
"@types/lodash": "^4.17.7",
"husky": "^9.1.6",
"lint-staged": "^16.2.7",
"prettier": "^3.3.3",
Expand All @@ -40,7 +39,6 @@
"@types/node": "^20.11.25",
"chalk": "^5.3.0",
"debug": "^4.3.7",
"lodash": "^4.17.11",
"ora": "^8.1.0",
"pretty-ms": "^9.1.0"
},
Expand Down
46 changes: 25 additions & 21 deletions src/dynamic-param-interfaces.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { isDeepStrictEqual } from 'node:util';

import {
EventParameterDocumentation,
DetailedObjectType,
Expand All @@ -7,9 +9,9 @@ import {
} from '@electron/docs-parser';
import chalk from 'chalk';
import d from 'debug';
import _ from 'lodash';

import * as utils from './utils.js';
import { upperFirst, lowerFirst, camelCase } from './utils.js';

const debug = d('dynamic-param');

Expand Down Expand Up @@ -39,11 +41,13 @@ const polite = (s: string): string => {
const ignoreDescriptions = <T extends EventParameterDocumentation>(
props: T[],
): Pick<T, Exclude<keyof T, 'description'>>[] =>
_.map(props, (p) => {
const { description, ...toReturn } = p;
props
.map((p) => {
const { description, ...toReturn } = p;

return toReturn;
}).sort((a, b) => a.name.localeCompare(b.name));
return toReturn;
})
.sort((a, b) => a.name.localeCompare(b.name));

const noDescriptionCache = new WeakMap();
const unsetDescriptions = (o: any): any => {
Expand Down Expand Up @@ -71,48 +75,48 @@ const createParamInterface = (
): string => {
const maybeArray = (type: string) => (param.collection ? `Array<${type}>` : type);
const potentialExistingArgType = polite(IName);
const potentialExistingArgName = _.lowerFirst(polite(IName));
let argType = polite(IName) + _.upperFirst(_.camelCase(param.name));
const potentialExistingArgName = lowerFirst(polite(IName));
let argType = polite(IName) + upperFirst(camelCase(param.name));
let argName = param.name;
// TODO: Note. It is still possible for even backupIName to be already used
let usingExistingParamInterface = false;
_.forIn(paramInterfacesToDeclare, (value, key) => {
for (const [key, value] of Object.entries(paramInterfacesToDeclare)) {
const test = unsetDescriptions(
_.assign({}, param, {
Object.assign({}, param, {
name: argName,
tName: argType,
required: value.required,
additionalTags: (param as any).additionalTags || [],
}),
);
const potentialTest = unsetDescriptions(
_.assign({}, param, {
Object.assign({}, param, {
name: potentialExistingArgName,
tName: potentialExistingArgType,
required: value.required,
additionalTags: (param as any).additionalTags || [],
}),
);
const unsetValue = unsetDescriptions(value);
if (_.isEqual(test, unsetValue) || _.isEqual(potentialTest, unsetValue)) {
if (isDeepStrictEqual(test, unsetValue) || isDeepStrictEqual(potentialTest, unsetValue)) {
usingExistingParamInterface = true;
debug(
chalk.cyan(
`Using existing type for param name ${argType} --> ${key} in Interface: ${_.upperFirst(
`Using existing type for param name ${argType} --> ${key} in Interface: ${upperFirst(
param.tName,
)} --- This is because their structure is identical`,
),
);
argType = key;
return false;
break;
}
});
}
if (usingExistingParamInterface) {
return maybeArray(argType);
}
if (
paramInterfacesToDeclare[argType] &&
!_.isEqual(
!isDeepStrictEqual(
ignoreDescriptions(paramInterfacesToDeclare[argType].properties),
ignoreDescriptions(param.properties),
)
Expand Down Expand Up @@ -167,7 +171,7 @@ const flushParamInterfaces = (
delete toDeclareCheck[prop];
delete declaredCheck[prop];
}
if (!_.isEqual(toDeclareCheck, declaredCheck)) {
if (!isDeepStrictEqual(toDeclareCheck, declaredCheck)) {
throw new Error('Ruh roh, "' + paramKey + '" is already declared');
}
delete paramInterfacesToDeclare[paramKey];
Expand All @@ -177,7 +181,7 @@ const flushParamInterfaces = (
const param = paramInterfacesToDeclare[paramKey];
const paramAPI: string[] = [];
paramAPI.push(
`interface ${_.upperFirst(param.tName)}${
`interface ${upperFirst(param.tName)}${
param.extends ? ` extends ${param.extends}` : ''
} {`,
);
Expand All @@ -193,12 +197,12 @@ const flushParamInterfaces = (

if (!Array.isArray(paramProperty.type) && paramProperty.type.toLowerCase() === 'object') {
let argType =
(paramProperty as any).__type || _.upperFirst(_.camelCase(paramProperty.name));
(paramProperty as any).__type || upperFirst(camelCase(paramProperty.name));
if (API.some((a) => a.name === argType)) {
paramProperty.type = argType;
debug(
chalk.red(
`Auto-correcting type from Object --> ${argType} in Interface: ${_.upperFirst(
`Auto-correcting type from Object --> ${argType} in Interface: ${upperFirst(
param.tName,
)} --- This should be fixed in the docs`,
),
Expand Down Expand Up @@ -230,12 +234,12 @@ const flushParamInterfaces = (
paramPropertyType.type.toLowerCase() === 'object'
) {
let argType =
(paramProperty as any).__type || _.upperFirst(_.camelCase(paramProperty.name));
(paramProperty as any).__type || upperFirst(camelCase(paramProperty.name));
if (API.some((a) => a.name === argType)) {
paramPropertyType.type = argType;
debug(
chalk.red(
`Auto-correcting type from Object --> ${argType} in Interface: ${_.upperFirst(
`Auto-correcting type from Object --> ${argType} in Interface: ${upperFirst(
param.tName,
)} --- This should be fixed in the docs`,
),
Expand Down
3 changes: 1 addition & 2 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import fs from 'node:fs';
import path from 'node:path';

import { ParsedDocumentationResult } from '@electron/docs-parser';
import _ from 'lodash';

import * as utils from './utils.js';
import { getModuleDeclarations, generateModuleDeclaration } from './module-declaration.js';
Expand Down Expand Up @@ -30,7 +29,7 @@ const wrapWithHeaderAndFooter = (outputLines: string[], electronVersion: string)
.split(/\r?\n/),
);

outputLines.slice(0).forEach((l) => newOutputLines.push(`${_.trimEnd(` ${l}`)}`));
outputLines.slice(0).forEach((l) => newOutputLines.push(` ${l}`.trimEnd()));
utils.extendArray(newOutputLines, ['}', '']);

utils.extendArray(
Expand Down
47 changes: 21 additions & 26 deletions src/module-declaration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ import {
DetailedEventType,
DetailedEventReferenceType,
} from '@electron/docs-parser';
import _ from 'lodash';

import { DynamicParamInterfaces } from './dynamic-param-interfaces.js';
import * as utils from './utils.js';
import { upperFirst, camelCase } from './utils.js';

const modules: Record<string, string[]> = {};

Expand All @@ -30,8 +30,8 @@ export const generateModuleDeclaration = (
index: number,
API: ParsedDocumentationResult,
) => {
const moduleAPI = modules[_.upperFirst(module.name)] || [];
const newModule = !modules[_.upperFirst(module.name)];
const moduleAPI = modules[upperFirst(module.name)] || [];
const newModule = !modules[upperFirst(module.name)];
const instanceModuleForStaticVersion = API.find(
(tModule, tIndex) =>
index !== tIndex && tModule.name.toLowerCase() === module.name.toLowerCase(),
Expand Down Expand Up @@ -66,16 +66,16 @@ export const generateModuleDeclaration = (
}
if (extendsInfo) {
moduleAPI.push(
`${isClass ? 'class' : 'interface'} ${_.upperFirst(module.name)}${extendsInfo} {`,
`${isClass ? 'class' : 'interface'} ${upperFirst(module.name)}${extendsInfo} {`,
);
moduleAPI.push('', `// Docs: ${module.websiteUrl}`, '');
} else {
moduleAPI.push(`${isClass ? 'class' : 'interface'} ${_.upperFirst(module.name)} {`);
moduleAPI.push(`${isClass ? 'class' : 'interface'} ${upperFirst(module.name)} {`);
moduleAPI.push('', `// Docs: ${module.websiteUrl}`, '');
}
} else {
moduleAPI.push(
`interface ${_.upperFirst(module.name)}${
`interface ${upperFirst(module.name)}${
module.extends ? ` extends ${module.extends}` : ''
} {`,
);
Expand All @@ -86,13 +86,12 @@ export const generateModuleDeclaration = (
// Event Declaration
if (module.type !== 'Element') {
// To assist with declaration merging we define all parent events in this class too
_.concat(
[],
module.instanceEvents || [],
module.events || [],
...parentModules.map((m) => m.events || []),
...parentModules.map((m) => m.instanceEvents || []),
)
[
...(module.instanceEvents || []),
...(module.events || []),
...parentModules.flatMap((m) => m.events || []),
...parentModules.flatMap((m) => m.instanceEvents || []),
]
.sort((a, b) => a.name.localeCompare(b.name))
.forEach((moduleEvent, i, events) => {
utils.extendArray(
Expand All @@ -103,7 +102,7 @@ export const generateModuleDeclaration = (

if (moduleEvent.parameters) {
const args: string[] = [];
const indent = _.repeat(' ', moduleEvent.name.length + 29);
const indent = ' '.repeat(moduleEvent.name.length + 29);

moduleEvent.parameters.forEach((eventListenerArg, index) => {
let argString = '';
Expand All @@ -129,9 +128,9 @@ export const generateModuleDeclaration = (
argType = DynamicParamInterfaces.createParamInterface(
objectListenerArg,
eventListenerArg.name === 'params'
? _.upperFirst(_.camelCase(moduleEvent.name))
? upperFirst(camelCase(moduleEvent.name))
: undefined,
_.upperFirst(_.camelCase(moduleEvent.name)),
upperFirst(camelCase(moduleEvent.name)),
);
}

Expand All @@ -156,9 +155,7 @@ export const generateModuleDeclaration = (
};
eventParamsType = DynamicParamInterfaces.createParamInterface(
fakeObject,
`${_.upperFirst(_.camelCase(module.name))}${_.upperFirst(
_.camelCase(moduleEvent.name),
)}`,
`${upperFirst(camelCase(module.name))}${upperFirst(camelCase(moduleEvent.name))}`,
);
}
if (eventReferenceListenerArg.eventPropertiesReference) {
Expand Down Expand Up @@ -229,16 +226,14 @@ export const generateModuleDeclaration = (

domEvent.parameters.forEach((eventListenerProp, index) => {
if (eventListenerProp.name === 'result') {
(eventListenerProp as any).__type = `${_.upperFirst(
_.camelCase(domEvent.name),
)}Result`;
(eventListenerProp as any).__type = `${upperFirst(camelCase(domEvent.name))}Result`;
}
fakeObject.properties.push(eventListenerProp);
});

eventType = DynamicParamInterfaces.createParamInterface(
fakeObject,
_.upperFirst(_.camelCase(domEvent.name)),
upperFirst(camelCase(domEvent.name)),
);
}

Expand Down Expand Up @@ -300,7 +295,7 @@ export const generateModuleDeclaration = (
if (returnType === 'Object' || (returnType as TypeInformation).type === 'Object') {
returnType = DynamicParamInterfaces.createParamInterface(
moduleMethod.returns! as any,
_.upperFirst(moduleMethod.name),
upperFirst(moduleMethod.name),
);

// The process module is not in the Electron namespace so we need to reference the Electron namespace to use these types
Expand All @@ -323,7 +318,7 @@ export const generateModuleDeclaration = (
? ''
: `: ${utils.typify(
returnType as TypeInformation,
`${_.upperFirst(moduleMethod.name)}ReturnValue`,
`${upperFirst(moduleMethod.name)}ReturnValue`,
)}`
};`,
);
Expand Down Expand Up @@ -448,7 +443,7 @@ export const generateModuleDeclaration = (
}

// Save moduleAPI for later reuse
modules[_.upperFirst(module.name)] = moduleAPI;
modules[upperFirst(module.name)] = moduleAPI;
};

export const getModuleDeclarations = () => modules;
23 changes: 10 additions & 13 deletions src/primary-interfaces.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { ParsedDocumentationResult } from '@electron/docs-parser';
import d from 'debug';
import _ from 'lodash';

import { upperFirst } from './utils.js';

const debug = d('primary-interface');

Expand Down Expand Up @@ -46,8 +47,8 @@ export const generatePrimaryInterfaces = (
}
const moduleString = isClass
? module.process.exported
? ` class ${_.upperFirst(module.name)} extends Electron.${_.upperFirst(module.name)} {}`
: ` type ${_.upperFirst(module.name)} = Electron.${_.upperFirst(module.name)}`
? ` class ${upperFirst(module.name)} extends Electron.${upperFirst(module.name)} {}`
: ` type ${upperFirst(module.name)} = Electron.${upperFirst(module.name)}`
: '';
const newConstDeclarations: string[] = [];
const newTypeAliases: string[] = [];
Expand All @@ -61,28 +62,24 @@ export const generatePrimaryInterfaces = (
if ((!isClass || module.name !== classify(module.name)) && module.process.exported) {
if (isClass) {
newConstDeclarations.push(
`type ${classify(module.name)} = ${_.upperFirst(module.name)};`,
`const ${classify(module.name)}: typeof ${_.upperFirst(module.name)};`,
`type ${classify(module.name)} = ${upperFirst(module.name)};`,
`const ${classify(module.name)}: typeof ${upperFirst(module.name)};`,
);
} else {
if (isModuleButActuallyStaticClass && !isClass) {
newConstDeclarations.push(
`const ${classify(module.name)}: typeof ${_.upperFirst(module.name)};`,
`const ${classify(module.name)}: typeof ${upperFirst(module.name)};`,
);
} else {
newConstDeclarations.push(
`const ${classify(module.name)}: ${_.upperFirst(module.name)};`,
);
newConstDeclarations.push(`const ${classify(module.name)}: ${upperFirst(module.name)};`);
}
newTypeAliases.push(
`type ${_.upperFirst(module.name)} = Electron.${_.upperFirst(module.name)};`,
`type ${upperFirst(module.name)} = Electron.${upperFirst(module.name)};`,
);
}
}
if (module.type === 'Element') {
newTypeAliases.push(
`type ${_.upperFirst(module.name)} = Electron.${_.upperFirst(module.name)};`,
);
newTypeAliases.push(`type ${upperFirst(module.name)} = Electron.${upperFirst(module.name)};`);
}
constDeclarations.push(...newConstDeclarations);
if (module.process.main && module.process.renderer) {
Expand Down
7 changes: 3 additions & 4 deletions src/remap-optionals.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { ParsedDocumentationResult, MethodDocumentationBlock } from '@electron/docs-parser';
import chalk from 'chalk';
import d from 'debug';
import _ from 'lodash';

import * as utils from './utils.js';

Expand All @@ -17,16 +16,16 @@ export const remapOptionals = (API: ParsedDocumentationResult) => {
if (!method.parameters) return;
if ((method as any).__handled) return;
let optionalFound = false;
_.concat([], method.parameters).forEach((param, index) => {
[...method.parameters].forEach((param, index) => {
if (optionalFound && !utils.isOptional(param)) {
debug(
chalk.cyan(
`Duplicating method due to prefixed optional: ${method.name} Slicing at: ${index}`,
),
);
moreMethods.push(
Object.assign({}, _.cloneDeep(method), {
parameters: [..._.cloneDeep(method.parameters)].filter((tParam, pIndex) => {
Object.assign({}, structuredClone(method), {
parameters: structuredClone(method.parameters).filter((tParam, pIndex) => {
if (pIndex >= index) return true;
return !utils.isOptional(tParam);
}),
Expand Down
Loading