feat: add sanitizePackageJson option to strip dev-only package.json fields#1931
Open
claude[bot] wants to merge 2 commits into
Open
feat: add sanitizePackageJson option to strip dev-only package.json fields#1931claude[bot] wants to merge 2 commits into
claude[bot] wants to merge 2 commits into
Conversation
5 tasks
…ields Packaged apps currently ship the developer's entire package.json, including devDependencies, scripts, and tooling configuration. The bundled app's package.json is now rewritten after the afterCopy hooks run and before the afterPrune hooks: a default sanitizer strips dev-only fields while keeping runtime-relevant ones such as main, dependencies, and exports. Consistent with the other hook options, sanitizePackageJson is an array of functions. They run serially, each receiving the package.json object returned by the previous one plus the Electron version and the target platform and arch. A non-empty array replaces the default sanitizer entirely; the default is exported as defaultSanitizePackageJson so custom arrays can include it. Sanitizing the bundled copy cannot affect pruning, since the Pruner reads the source directory's package.json. The expected ElectronAsarIntegrity hash in the macOS packaging test changes because the default sanitizer changes the bundled package.json, and therefore the app.asar header, for the test fixture. Fixes #1184
30b15bd to
514c023
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Requested by Samuel Attard · Slack thread
Fixes #1184
Summarize your changes:
Before: the entire developer
package.jsonis copied into every packaged app verbatim — includingdevDependencies,scripts, workspace/package-manager settings, publishing config, and tooling configuration. Anyone unpacking the app can read all of it.After: the bundled app's
package.jsonis sanitized by default. Dev-only fields are stripped while runtime-relevant fields (main,name,productName,version,type,imports,exports,dependencies,optionalDependencies,peerDependencies, and everything else not on the denylist) are kept. Users can pass a newsanitizePackageJsonoption — an array of functions, consistent with the other hook options — to fully replace the default. The functions run serially, each receiving the object returned by the previous one, anddefaultSanitizePackageJsonis exported so a custom array can extend the default instead:Default denylist:
devDependencies,scripts,workspaces,packageManager,resolutions,overrides,pnpm,private,publishConfig,devEngines,jest,eslintConfig,prettier,browserslist,lint-staged,nano-staged,husky,commitlint,mocha,ava,nyc,c8,tap,xo,standard.How:
src/sanitize-package-json.ts(new):defaultSanitizePackageJson()plussanitizeAppPackageJson(), which reads/package.json, runs the user-provided sanitizer functions (or the default when the option is unset or an empty array) serially — each function receives the object returned by the previous one — and writes the final result back with 2-space indentation. If nopackage.jsonwas copied, it does nothing so that app validation can report its friendlier error.src/platform.ts:App.copyTemplate()runs the sanitize step after all userafterCopyhooks complete and before theafterPrunehooks, soafterCopyhooks still see the original manifest and nothing after that phase ever sees the unsanitized one.sanitizePackageJsonis also added to thecopyPrebuiltAsar()incompatibility list, matching the other copy-phase hooks.src/types.ts: newSanitizePackageJsonFunctiontype andsanitizePackageJson?: SanitizePackageJsonFunction[]option. Each function receives(packageJson, electronVersion, platform, arch)— the same context args as the other hooks — and returns (or resolves to) the object to pass on / write back; it transforms a value instead of observing a directory, hence nobuildPath. A non-empty array replaces the default sanitizer entirely; an empty or unset array runs the default. Full TSDoc on the option (denylist, replace-not-extend semantics, serial chaining, ordering, prune interaction) for the generated API docs.src/index.ts: exportsdefaultSanitizePackageJson.test/packager.spec.ts: the expectedElectronAsarIntegrityhash in the macOS packaging test is updated, since sanitizing the fixture's bundledpackage.jsondeterministically changes theapp.asarheader (verified locally by reproducing both the old and new header hashes byte-for-byte).test/sanitize-package-json.spec.ts(new): covers denylist stripping, keep-list preservation, on-disk write format, array-replaces-default semantics, empty-array-runs-default, serial chaining of two hooks (ordering, chained results, and context args), missing-manifest no-op,afterCopy→ sanitize →afterPruneordering throughApp.copyTemplate(), and theprebuiltAsarincompatibility error.Prune safety: pruning is unaffected by design — the
Prunerwalks the source directory'spackage.json(userPathFilterconstructs it fromopts.dir), not the bundled copy, so strippingdevDependenciesfrom the bundled manifest cannot change which modules are pruned.validateElectronAppruns after sanitization and still passes, since the default keepsmain.Behavior change (please weigh in on semver): the default sanitizer is intentionally on by default, so this changes the packaged output for every consumer that doesn't set the new option. Apps that read stripped fields from their own bundled
package.jsonat runtime (e.g.scriptsordevDependencies) would need to opt out with a pass-throughsanitizePackageJson: [(pkg) => pkg]. Flagging this clearly so maintainers can decide whether this ships as a major.Local test note:
yarn lintandyarn testpass locally except for pre-existing failures intest/packager.spec.ts/test/unzip.spec.tsthat require downloading Electron binaries, which is blocked in my environment (HTTP 403 from the network proxy). Those failures are identical onmain; relying on CI for full coverage.Generated by Claude Code