Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
850646b
move the manual node module traversal to the root abstract class. Add…
mmaietta Dec 10, 2025
9e83327
clear env for spawn
mmaietta Dec 12, 2025
76edef5
bun back to npm collector
mmaietta Dec 12, 2025
3edc5a7
Merge commit 'e70da99e34c1cc913b237c5a58456bf752a378bc' into fix/is-d…
mmaietta Dec 14, 2025
be94b53
fix merge conflict res
mmaietta Dec 14, 2025
ed507da
whoops
mmaietta Dec 14, 2025
86e5d7a
woah
mmaietta Dec 15, 2025
dfce2a2
Merge branch 'master' into fix/is-dep-prodpath
mmaietta Dec 15, 2025
75d8c10
add snapshot tests for traversal tests, add `packageManager` override…
mmaietta Dec 15, 2025
42cb47f
Merge remote-tracking branch 'origin/master' into fix/is-dep-prodpath
mmaietta Dec 18, 2025
5551da3
force traversal for Bun
mmaietta Dec 18, 2025
39395b6
update changeset
mmaietta Dec 18, 2025
650ec00
Merge remote-tracking branch 'origin/master' into fix/is-dep-prodpath
mmaietta Dec 21, 2025
fe1ef15
temp save
mmaietta Dec 21, 2025
17d0d77
tmp save
mmaietta Dec 21, 2025
e0fe3e6
cleanup
mmaietta Dec 21, 2025
053c1b0
cleanup
mmaietta Dec 21, 2025
b7661b5
rename
mmaietta Dec 21, 2025
40a096c
tmp save
mmaietta Dec 21, 2025
0cea36d
simplify
mmaietta Dec 21, 2025
c81dd37
reset snapshot
mmaietta Dec 21, 2025
cebf281
lint
mmaietta Dec 21, 2025
a80612e
Merge branch 'master' into fix/is-dep-prodpath
mmaietta Dec 21, 2025
20b49b8
properly declare null return values in moduleManager
mmaietta Dec 22, 2025
2e8ceea
update changeset
mmaietta Dec 22, 2025
dfc17c0
Merge branch 'master' into fix/is-dep-prodpath
mmaietta Jan 12, 2026
cdaa816
cleanup
mmaietta Jan 12, 2026
a427c8a
Merge branch 'master' into fix/is-dep-prodpath
mmaietta Jan 12, 2026
44907f6
changeset
mmaietta Jan 12, 2026
c5fc00a
Merge commit 'a427c8a74cf98a2d520b1b83d0acc9c57258246c' into fix/is-d…
mmaietta Jan 12, 2026
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
5 changes: 5 additions & 0 deletions .changeset/some-taxis-double.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"app-builder-lib": patch
---

chore: move the manual node module traversal to the separate class. Add `env: { COREPACK_ENABLE_STRICT: "0", ...process.env },` to allow `npm list` to work across environments.
21 changes: 21 additions & 0 deletions packages/app-builder-lib/scheme.json
Original file line number Diff line number Diff line change
Expand Up @@ -7731,6 +7731,27 @@
}
]
},
"packageManager": {
"anyOf": [
{
"enum": [
"auto",
"bun",
"npm",
"pnpm",
"traversal",
"yarn",
"yarn-berry"
],
"type": "string"
},
{
"type": "null"
}
],
"default": "auto",
"description": "The package manager to use for installing dependencies and node module collections."
},
"pacman": {
"anyOf": [
{
Expand Down
7 changes: 7 additions & 0 deletions packages/app-builder-lib/src/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { BuildResult } from "./packager"
import { ArtifactBuildStarted, ArtifactCreated } from "./packagerApi"
import { PlatformPackager } from "./platformPackager"
import { NsisOptions, NsisWebOptions, PortableOptions } from "./targets/nsis/nsisOptions"
import { PM } from "./node-module-collector"

// duplicate appId here because it is important
/**
Expand Down Expand Up @@ -134,6 +135,12 @@ export interface CommonConfiguration {
*/
readonly nativeRebuilder?: "legacy" | "sequential" | "parallel" | null

/**
* The package manager to use for installing dependencies and node module collections.
* @default auto
*/
readonly packageManager?: "auto" | PM | null

/**
* The build number. Maps to the `--iteration` flag for builds using FPM on Linux.
* If not defined, then it will fallback to `BUILD_NUMBER` or `TRAVIS_BUILD_NUMBER` or `APPVEYOR_BUILD_NUMBER` or `CIRCLE_BUILD_NUM` or `BUILD_BUILDNUMBER` or `CI_PIPELINE_IID` env.
Expand Down
2 changes: 1 addition & 1 deletion packages/app-builder-lib/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ export { WindowsAzureSigningConfiguration, WindowsConfiguration, WindowsSigntool
export { BuildResult, Packager } from "./packager"
export { ArtifactBuildStarted, ArtifactCreated, PackagerOptions } from "./packagerApi"
export { CommonNsisOptions, CustomNsisBinary, NsisOptions, NsisWebOptions, PortableOptions } from "./targets/nsis/nsisOptions"

export { PM } from "./node-module-collector/packageManager"
export { CancellationToken, ProgressInfo } from "builder-util-runtime"
export { PublishOptions, UploadTask } from "electron-publish"
export { WindowsSignOptions } from "./codeSign/windowsCodeSign"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
import { log } from "builder-util"
import { NpmNodeModulesCollector } from "./npmNodeModulesCollector"
import { PM } from "./packageManager"
import { NpmDependency } from "./types"
import { TraversalNodeModulesCollector } from "./traversalNodeModulesCollector"

export class BunNodeModulesCollector extends NpmNodeModulesCollector {
export class BunNodeModulesCollector extends TraversalNodeModulesCollector {
public readonly installOptions = { manager: PM.BUN, lockfile: "bun.lock" }

protected async getDependenciesTree(_pm: PM): Promise<NpmDependency> {
log.info(null, "bun does not support any CLI for dependency tree extraction, falling back to NPM node module collector")
log.info(null, "note: bun does not support any CLI for dependency tree extraction, utilizing NPM node module collector instead")
return super.getDependenciesTree(PM.NPM)
}

protected isProdDependency(packageName: string, tree: NpmDependency): boolean {
return tree.dependencies?.[packageName] != null || tree.optionalDependencies?.[packageName] != null
}
}
45 changes: 34 additions & 11 deletions packages/app-builder-lib/src/node-module-collector/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,26 @@ import { Lazy } from "lazy-val"
import { spawn, log, exists } from "builder-util"
import * as fs from "fs-extra"
import * as path from "path"
import { TraversalNodeModulesCollector } from "./traversalNodeModulesCollector"
import { Configuration } from "../configuration"

export { getPackageManagerCommand, PM }

export function getCollectorByPackageManager(pm: PM, rootDir: string, tempDirManager: TmpDir) {
export function getCollectorByPackageManager(pm: PM, rootDir: string, tempDirManager: TmpDir, cancellationToken: CancellationToken) {
switch (pm) {
case PM.PNPM:
return new PnpmNodeModulesCollector(rootDir, tempDirManager)
return new PnpmNodeModulesCollector(rootDir, tempDirManager, cancellationToken)
case PM.YARN:
return new YarnNodeModulesCollector(rootDir, tempDirManager)
return new YarnNodeModulesCollector(rootDir, tempDirManager, cancellationToken)
case PM.YARN_BERRY:
return new YarnBerryNodeModulesCollector(rootDir, tempDirManager)
return new YarnBerryNodeModulesCollector(rootDir, tempDirManager, cancellationToken)
case PM.BUN:
return new BunNodeModulesCollector(rootDir, tempDirManager)
return new BunNodeModulesCollector(rootDir, tempDirManager, cancellationToken)
case PM.NPM:
return new NpmNodeModulesCollector(rootDir, tempDirManager)
return new NpmNodeModulesCollector(rootDir, tempDirManager, cancellationToken)
// should never access this case (as it's internally a fallback), but TS needs a default and we need to satisfy it
case PM.TRAVERSAL:
return new TraversalNodeModulesCollector(rootDir, tempDirManager, cancellationToken)
}
}

Expand All @@ -43,14 +48,28 @@ export function getNodeModules(
packageName: string
}
): Promise<NodeModuleInfo[]> {
const collector = getCollectorByPackageManager(pm, rootDir, tempDirManager)
return collector.getNodeModules({ cancellationToken, packageName })
const collector = getCollectorByPackageManager(pm, rootDir, tempDirManager, cancellationToken)
return collector.getNodeModules({ packageName })
}

export const determinePackageManagerEnv = ({ projectDir, appDir, workspaceRoot }: { projectDir: string; appDir: string; workspaceRoot: string | Nullish }) =>
export const determinePackageManagerEnv = ({
projectDir,
appDir,
workspaceRoot,
packageManagerOverride,
}: {
projectDir: string
appDir: string
workspaceRoot: string | Nullish
packageManagerOverride: Configuration["packageManager"]
}) =>
new Lazy(async () => {
const availableDirs = [projectDir, appDir, workspaceRoot].filter((it): it is string => it != null)
const pm = await detectPackageManager(availableDirs)
const override =
packageManagerOverride != null && packageManagerOverride !== "auto"
? { pm: packageManagerOverride, resolvedDirectory: availableDirs[0], corepackConfig: undefined, detectionMethod: "override" }
: null
const pm = override ?? (await detectPackageManager(availableDirs))
const root = await findWorkspaceRoot(pm.pm, projectDir)
if (root != null) {
// re-detect package manager from workspace root, this seems particularly necessary for pnpm workspaces
Expand Down Expand Up @@ -93,7 +112,11 @@ async function findWorkspaceRoot(pm: PM, cwd: string): Promise<string | undefine
break
}

const output = await spawn(command.command, command.args, { cwd, stdio: ["ignore", "pipe", "ignore"] })
const output = await spawn(command.command, command.args, {
cwd,
env: { COREPACK_ENABLE_STRICT: "0", ...process.env }, // allow `process.env` overrides
stdio: ["ignore", "pipe", "ignore"],
})
.then(async it => {
const out: string | undefined = it?.trim()
if (!out) {
Expand Down
Loading