Skip to content

Commit 48584a5

Browse files
fix: remove get-package-info and resolve dependencies (#1906)
Replaces get-package-info with a small async generator that walks up the directory tree yielding parsed package.json files, and reads the needed fields (productName/name, version, author, electron dep) directly with optional chaining instead of dot-path string lookup. Replaces the resolve package with createRequire().resolve() for locating the electron package.json during version inference. Drops ~35 transitive production dependencies including bluebird, lodash.get, read-pkg-up, normalize-package-data, and the spdx-* tree.
1 parent 28ebc46 commit 48584a5

5 files changed

Lines changed: 112 additions & 550 deletions

File tree

CONTRIBUTING.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ feature. Known modules include:
3030
* `electron-notarize*`
3131
* `electron-osx-sign`
3232
* `electron-packager` (always use this one before filing an issue)
33-
* `get-package-info`
3433

3534
We use the [`debug`](https://www.npmjs.com/package/debug#usage) module for this functionality. It
3635
has examples on how to set environment variables if you don't know how.

package.json

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,11 @@
4747
"extract-zip": "^2.0.1",
4848
"filenamify": "^6.0.0",
4949
"galactus": "^2.0.2",
50-
"get-package-info": "^1.0.0",
5150
"graceful-fs": "^4.2.11",
5251
"junk": "^4.0.1",
5352
"parse-author": "^2.0.0",
5453
"plist": "^3.1.0",
5554
"resedit": "^2.0.3",
56-
"resolve": "^1.22.10",
5755
"semver": "^7.7.2",
5856
"yargs-parser": "^22.0.0"
5957
},
@@ -64,7 +62,6 @@
6462
"@types/node": "~22.10.7",
6563
"@types/parse-author": "^2.0.2",
6664
"@types/plist": "^3.0.5",
67-
"@types/resolve": "^1.20.6",
6865
"@types/semver": "^7.7.0",
6966
"@types/yargs-parser": "^21.0.3",
7067
"husky": "^9.1.7",

src/ambient.d.ts

Lines changed: 0 additions & 32 deletions
This file was deleted.

src/infer.ts

Lines changed: 112 additions & 142 deletions
Original file line numberDiff line numberDiff line change
@@ -1,176 +1,146 @@
1-
import getPackageInfo, {
2-
GetPackageInfoError,
3-
GetPackageInfoResult,
4-
GetPackageInfoResultSourceItem,
5-
} from 'get-package-info';
61
import parseAuthor from 'parse-author';
72
import path from 'node:path';
8-
import resolve, { AsyncOpts } from 'resolve';
3+
import { createRequire } from 'node:module';
4+
import { promisifiedGracefulFs } from './util.js';
95
import { debug } from './common.js';
106
import { OfficialPlatform, Options, ProcessedOptions } from './types.js';
117

12-
function isMissingRequiredProperty(props: string[]) {
13-
return props.some((prop) => prop === 'productName' || prop === 'dependencies.electron');
14-
}
15-
16-
function errorMessageForProperty(prop: string) {
17-
let hash, propDescription;
18-
switch (prop) {
19-
case 'productName':
20-
hash = 'name';
21-
propDescription = 'application name';
22-
break;
23-
case 'dependencies.electron':
24-
hash = 'electronversion';
25-
propDescription = 'Electron version';
26-
break;
27-
case 'version':
28-
hash = 'appversion';
29-
propDescription = 'application version';
30-
break;
31-
/* istanbul ignore next */
32-
default:
33-
hash = '';
34-
propDescription = `[Unknown Property (${prop})]`;
8+
type PackageJSON = {
9+
name?: string;
10+
productName?: string;
11+
version?: string;
12+
author?: string | { name?: string };
13+
dependencies?: Record<string, string>;
14+
devDependencies?: Record<string, string>;
15+
};
16+
17+
const ELECTRON_PACKAGES = ['electron', 'electron-nightly'] as const;
18+
19+
async function* walkPackageJSONs(dir: string): AsyncGenerator<{ src: string; pkg: PackageJSON }> {
20+
let prev: string | undefined;
21+
let cur = path.resolve(dir);
22+
while (cur !== prev) {
23+
const src = path.join(cur, 'package.json');
24+
try {
25+
const contents = await promisifiedGracefulFs.readFile(src, 'utf8');
26+
yield { src, pkg: JSON.parse(contents.replace(/^\uFEFF/, '')) };
27+
} catch (err) {
28+
if ((err as NodeJS.ErrnoException).code !== 'ENOENT') throw err;
29+
}
30+
prev = cur;
31+
cur = path.dirname(cur);
3532
}
33+
}
3634

35+
function errorMessageForProperty(propDescription: string, hash: string) {
3736
return (
3837
`Unable to determine ${propDescription}. Please specify an ${propDescription}\n\n` +
3938
'For more information, please see\n' +
4039
`https://electron.github.io/packager/main/interfaces/Options.html#${hash}\n`
4140
);
4241
}
4342

44-
function resolvePromise(id: string, options: AsyncOpts) {
45-
return new Promise<[string | undefined, { version: string }]>(
46-
// eslint-disable-next-line promise/param-names
47-
(accept, reject) => {
48-
resolve(id, options, (err, mainPath, pkg) => {
49-
if (err) {
50-
/* istanbul ignore next */
51-
reject(err);
52-
} else {
53-
accept([mainPath as string | undefined, pkg as { version: string }]);
54-
}
55-
});
56-
},
57-
);
58-
}
59-
60-
async function getVersion(electronProp: GetPackageInfoResultSourceItem) {
61-
const [, packageName] = electronProp.prop.split('.');
62-
const src = electronProp.src;
63-
64-
const pkg = (await resolvePromise(packageName, { basedir: path.dirname(src) }))[1];
65-
debug(`Inferring target Electron version from ${packageName} in ${src}`);
43+
async function resolveElectronVersion(packageName: string, fromSrc: string) {
44+
const pkgJsonPath = createRequire(fromSrc).resolve(`${packageName}/package.json`);
45+
const pkg = JSON.parse(await promisifiedGracefulFs.readFile(pkgJsonPath, 'utf8'));
46+
debug(`Inferring target Electron version from ${packageName} in ${fromSrc}`);
6647
return pkg.version;
6748
}
6849

69-
async function handleMetadata(
70-
opts: Options,
71-
result: GetPackageInfoResult,
72-
): Promise<Partial<ProcessedOptions>> {
73-
const processedValues: Partial<ProcessedOptions> = {};
74-
75-
if (typeof result.values.productName === 'string') {
76-
debug(
77-
`Inferring application name from ${result.source.productName.prop} in ${result.source.productName.src}`,
78-
);
79-
processedValues.name = result.values.productName;
80-
}
81-
82-
if (typeof result.values.version === 'string') {
83-
debug(`Inferring appVersion from version in ${result.source.version.src}`);
84-
processedValues.appVersion = result.values.version;
85-
}
86-
87-
if (result.values.author) {
88-
const author = result.values.author as string | { name: string };
89-
const win32metadata = opts.win32metadata || {};
90-
91-
debug(`Inferring win32metadata.CompanyName from author in ${result.source.author.src}`);
92-
if (typeof author === 'string') {
93-
win32metadata.CompanyName = parseAuthor(author).name;
94-
} else if (author.name) {
95-
win32metadata.CompanyName = author.name;
96-
} else {
97-
debug('Cannot infer win32metadata.CompanyName from author, no name found');
98-
}
99-
processedValues.win32metadata = win32metadata;
100-
}
101-
102-
if (Object.prototype.hasOwnProperty.call(result.values, 'dependencies.electron')) {
103-
processedValues.electronVersion = await getVersion(result.source['dependencies.electron']);
104-
}
105-
106-
return processedValues;
107-
}
108-
109-
function handleMissingProperties(
110-
opts: Options,
111-
err: GetPackageInfoError,
112-
): Promise<Partial<ProcessedOptions>> {
113-
const missingProps = err.missingProps.map((prop) => {
114-
return Array.isArray(prop) ? prop[0] : prop;
115-
});
116-
117-
if (isMissingRequiredProperty(missingProps)) {
118-
const messages = missingProps.map(errorMessageForProperty);
119-
120-
debug(err.message);
121-
err.message = messages.join('\n') + '\n';
122-
throw err;
123-
} else {
124-
// Missing props not required, can continue w/ partial result
125-
return handleMetadata(opts, err.result);
126-
}
127-
}
128-
12950
export async function getMetadataFromPackageJSON(
13051
platforms: OfficialPlatform[],
13152
opts: Options,
13253
dir: string,
13354
): Promise<Partial<ProcessedOptions>> {
134-
const props: Array<string | string[]> = [];
55+
const result: Partial<ProcessedOptions> = {};
13556

136-
if (!opts.name) {
137-
props.push(['productName', 'name']);
138-
}
57+
let needName = !opts.name;
58+
let needVersion = !opts.appVersion;
59+
let needElectron = !opts.electronVersion;
60+
let needAuthor = platforms.includes('win32') && !opts.win32metadata?.CompanyName;
13961

140-
if (!opts.appVersion) {
141-
props.push('version');
62+
if (needAuthor) {
63+
debug('Requiring author in package.json, as CompanyName was not specified for win32metadata');
14264
}
14365

144-
if (!opts.electronVersion) {
145-
props.push([
146-
'dependencies.electron',
147-
'devDependencies.electron',
148-
'dependencies.electron-nightly',
149-
'devDependencies.electron-nightly',
150-
]);
151-
}
66+
const initiallyNeeded = [
67+
needName && 'productName',
68+
needVersion && 'version',
69+
needElectron && 'dependencies.electron',
70+
needAuthor && 'author',
71+
].filter(Boolean) as string[];
72+
73+
if (!initiallyNeeded.length) return result;
74+
75+
for await (const { src, pkg } of walkPackageJSONs(dir)) {
76+
if (needName) {
77+
const name = pkg.productName ?? pkg.name;
78+
if (name !== undefined) {
79+
const field = pkg.productName !== undefined ? 'productName' : 'name';
80+
debug(`Inferring application name from ${field} in ${src}`);
81+
result.name = name;
82+
needName = false;
83+
}
84+
}
15285

153-
if (platforms.includes('win32') && !(opts.win32metadata && opts.win32metadata.CompanyName)) {
154-
debug('Requiring author in package.json, as CompanyName was not specified for win32metadata');
155-
props.push('author');
156-
}
86+
if (needVersion && pkg.version !== undefined) {
87+
debug(`Inferring appVersion from version in ${src}`);
88+
result.appVersion = pkg.version;
89+
needVersion = false;
90+
}
15791

158-
// Search package.json files to infer name and version from
159-
try {
160-
const packageInfo = await getPackageInfo(props, dir);
161-
return handleMetadata(opts, packageInfo);
162-
} catch (e) {
163-
const err = e as GetPackageInfoError;
164-
165-
if (err.missingProps) {
166-
if (err.missingProps.length === props.length) {
167-
debug(err.message);
168-
err.message = `Could not locate a package.json file in "${path.resolve(opts.dir)}" or its parent directories for an Electron app with the following fields: ${err.missingProps.join(', ')}`;
92+
if (needAuthor && pkg.author !== undefined) {
93+
debug(`Inferring win32metadata.CompanyName from author in ${src}`);
94+
const win32metadata = opts.win32metadata || {};
95+
if (typeof pkg.author === 'string') {
96+
win32metadata.CompanyName = parseAuthor(pkg.author).name;
97+
} else if (pkg.author.name) {
98+
win32metadata.CompanyName = pkg.author.name;
16999
} else {
170-
return handleMissingProperties(opts, err);
100+
debug('Cannot infer win32metadata.CompanyName from author, no name found');
101+
}
102+
result.win32metadata = win32metadata;
103+
needAuthor = false;
104+
}
105+
106+
if (needElectron) {
107+
for (const packageName of ELECTRON_PACKAGES) {
108+
if (
109+
pkg.dependencies?.[packageName] !== undefined ||
110+
pkg.devDependencies?.[packageName] !== undefined
111+
) {
112+
result.electronVersion = await resolveElectronVersion(packageName, src);
113+
needElectron = false;
114+
break;
115+
}
171116
}
172117
}
173118

174-
throw err;
119+
if (!needName && !needVersion && !needElectron && !needAuthor) break;
175120
}
121+
122+
const stillMissing = [
123+
needName && 'productName',
124+
needVersion && 'version',
125+
needElectron && 'dependencies.electron',
126+
needAuthor && 'author',
127+
].filter(Boolean) as string[];
128+
129+
if (stillMissing.length) {
130+
if (stillMissing.length === initiallyNeeded.length) {
131+
throw new Error(
132+
`Could not locate a package.json file in "${path.resolve(opts.dir)}" or its parent directories for an Electron app with the following fields: ${initiallyNeeded.join(', ')}`,
133+
);
134+
}
135+
if (needName || needElectron) {
136+
const messages: string[] = [];
137+
if (needName) messages.push(errorMessageForProperty('application name', 'name'));
138+
if (needVersion) messages.push(errorMessageForProperty('application version', 'appversion'));
139+
if (needElectron)
140+
messages.push(errorMessageForProperty('Electron version', 'electronversion'));
141+
throw new Error(messages.join('\n') + '\n');
142+
}
143+
}
144+
145+
return result;
176146
}

0 commit comments

Comments
 (0)