Skip to content

feat: add "preferPackageJson" deno.json setting - #35392

Merged
bartlomieju merged 4 commits into
mainfrom
feat/prefer-package-json
Jun 23, 2026
Merged

bartlomieju merged 4 commits into
mainfrom
feat/prefer-package-json

Conversation

@bartlomieju

Copy link
Copy Markdown
Member

Summary

Builds on the --package-json flag (added in Deno 2.8) by introducing a persistent "preferPackageJson": true setting in deno.json. When enabled, dependency management commands always target package.json:

  • deno add / deno install <pkg> / deno remove write to package.json instead of deno.json
  • a package.json is created automatically if one doesn't exist
  • it's equivalent to passing --package-json to those commands on every invocation

It also prints a warning when imports/scopes are still present in deno.json while preferPackageJson is enabled, nudging users to move those dependencies to package.json.

// deno.json
{
  "preferPackageJson": true
}
$ deno add npm:chalk
Created package.json configuration file.
Add npm:chalk@5.x.x

Implementation

  • preferPackageJson added to the ConfigFileJson schema and the JSON schema (config-file.v1.json) for editor autocompletion.
  • Workspace::prefer_package_json() reads the setting from the root deno.json.
  • cli/tools/pm/mod.rs folds the setting into the existing force_package_json path that already powers --package-json, so add/install/remove all honor it. link/unlink explicitly opt out, since the "links" field lives in deno.json.
  • The imports warning rides the existing workspace-diagnostics path (new WorkspaceDiagnosticKind::PreferPackageJsonWithImports).

Tests

  • tests/specs/add/prefer_package_json β€” routes deps into package.json; creates it when missing.
  • tests/specs/add/prefer_package_json_imports_warning β€” warning fires when imports is present.

Existing package_json_flag, link, remove, and deno_config tests pass.

Adds a "preferPackageJson": true field to deno.json that always manages
dependencies via package.json, equivalent to passing --package-json to
deno add/install/remove. A package.json is created if one doesn't exist.

Also emits a warning when "imports"/"scopes" are present in deno.json
while "preferPackageJson" is enabled.
The "preferPackageJson" imports warning fired whenever deno.json had any
"imports"/"scopes", but path-alias imports (e.g. "#utils" -> "./utils.ts")
can't be moved to package.json, so warning about them is misleading.
Narrow the trigger to entries that are actual npm:/jsr: dependency
specifiers.

Add a unit test covering the warning's presence (imports + scopes deps)
and absence (path aliases, setting off), and a spec test that `deno
remove` honors preferPackageJson by removing from package.json.
@@ -0,0 +1,2 @@
Created package.json configuration file.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would prefer we don't print this message

A workspace member setting "preferPackageJson" was silently ignored
(the setting is read from the root deno.json only), and yet the member
could still receive the lingering-imports warning. Emit a RootOnlyOption
diagnostic for members that set it, and only emit the
PreferPackageJsonWithImports warning for the root config where the
setting actually takes effect.
@bartlomieju bartlomieju added this to the 2.9.0 milestone Jun 20, 2026
@bartlomieju

Copy link
Copy Markdown
Member Author

Reviewed β€” solid and well-tested. A few notes:

Confirmed correct:

  • deno install <pkg> is genuinely covered even though the diff only touches add/remove: installer/local.rs routes InstallFlagsLocal::Add β†’ pm::add(.., AddCommandName::Install), which now passes honor_prefer_package_json = true. Bare deno install (TopLevel) bypasses add() and is correctly unaffected.
  • link/unlink opt-out is correct (the "links" field stays in deno.json).
  • In the force path load_configs always returns npm_config = Some(..), so the .unwrap() calls in add/remove can't panic.
  • has_dependency_imports uses value_to_dep_req, which matches only npm:/jsr: specifiers, so path-alias imports ("#utils": "./utils.ts") correctly don't warn. Nicely covered by the unit tests.

One design point on the new PreferPackageJsonWithImports warning: it rides workspace.diagnostics(), which is drained in CliOptions::from_flags (cli/args/mod.rs) for essentially every command that loads the workspace β€” deno run, deno test, deno check, etc., not just add/install/remove. That's a more aggressive cadence than its siblings on that path (which are hard misconfigurations vs. this advisory migration nudge). Global emission is a reasonable choice if intended β€” just flagging it as deliberate. It correctly honors --quiet/--log-level error for free since it's a log::warn! and init_logging runs before the diagnostics are drained.

Minor: the diff adds a third spec (prefer_package_json_remove) the PR description doesn't mention; and under preferPackageJson the importMap-field bail in add() is skipped (deno_config is None), which is arguably fine but a slight asymmetry vs. plain deno add.

@bartlomieju
bartlomieju merged commit bc6e3cb into main Jun 23, 2026
136 checks passed
@bartlomieju
bartlomieju deleted the feat/prefer-package-json branch June 23, 2026 11:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant