Skip to content
Open
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ __dummy.html
!/test/*/.min_frontend
!/test/*/.fail_build
!/test/*/dub.json
!/test/*/package.json
!/test/*/dub.sdl
!/test/*/dub.settings.json
!/test/*/source/
Expand Down
13 changes: 13 additions & 0 deletions changelog/refuse-npm-package-json.dd
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
Refuse to load npm `package.json` files.

For historic reasons Dub treated `package.json` as an alias for `dub.json`.
When a Node/npm `package.json` was present and no `dub.json`/`dub.sdl` was
found, Dub parsed it anyway, emitted confusing unknown-key warnings, and often
failed with an empty-library / `targetType: none` error.

Dub now refuses to load a file named `package.json` when it contains npm-only
top-level keys (`packageManager`, `scripts`, `devDependencies`, `workspaces`,
or `private`). Legacy Dub recipes that still use the `package.json` filename
continue to work. Prefer `dub.json` or `dub.sdl` for new packages.

$(LINK2 https://github.com/dlang/dub/issues/3118, #3118)
72 changes: 72 additions & 0 deletions source/dub/recipe/io.d
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ PackageRecipe parsePackageRecipe(string contents, string filename,
string default_package_name = null, StrictMode mode = StrictMode.Ignore)
{
import std.algorithm : endsWith;
import std.path : baseName;
import dub.compilers.buildsettings : TargetType;
import dub.recipe.sdl : parseSDL;

Expand All @@ -91,6 +92,11 @@ PackageRecipe parsePackageRecipe(string contents, string filename,

if (filename.endsWith(".json"))
{
// Historic alias for dub.json; refuse npm's package.json to avoid
// confusing parse warnings / empty-library errors (see #3118).
if (baseName(filename) == "package.json")
enforceNotNpmPackageJson(contents, filename);

ret = parseConfigString!PackageRecipe(contents, filename, mode);
fixDependenciesNames(ret.name, ret);
}
Expand All @@ -115,6 +121,72 @@ PackageRecipe parsePackageRecipe(string contents, string filename,
return ret;
}

/** Top-level npm keys that Dub recipes never use.

Presence of any of these in a file named `package.json` means the file
belongs to npm (or a similar JS package manager), not Dub.
*/
private static immutable string[] npmPackageJsonSentinelKeys = [
"packageManager",
"scripts",
"devDependencies",
"workspaces",
"private",
];

/// Throws if `contents` looks like an npm `package.json` rather than a Dub recipe.
private void enforceNotNpmPackageJson(string contents, string filename)
{
import std.exception : enforce;
import std.format : format;
import dub.internal.vibecompat.data.json : Json, parseJsonString;

Json json;
try
json = parseJsonString(contents, filename);
catch (Exception)
return; // Let the normal recipe parser report JSON syntax errors.

if (json.type != Json.Type.object)
return;

string[] found;
foreach (key; npmPackageJsonSentinelKeys)
if (key in json)
found ~= key;

enforce(found.length == 0,
format("%s: Refusing to load npm package.json (found non-Dub key%s: %-(%s, %)). " ~
"Use dub.json or dub.sdl for Dub packages.",
filename, found.length == 1 ? "" : "s", found));
}

unittest { // issue #3118 - refuse npm package.json, keep legacy Dub package.json
import std.exception : assertThrown, assertNotThrown;

assertThrown!Exception(parsePackageRecipe(
`{ "name": "web", "private": true, "devDependencies": { "antora": "^3.1.14" }, "packageManager": "pnpm@9.15.0" }`,
"package.json"));

assertThrown!Exception(parsePackageRecipe(
`{ "name": "web", "scripts": { "build": "antora antora-playbook.yml" } }`,
"package.json"));

assertThrown!Exception(parsePackageRecipe(
`{ "name": "web", "workspaces": ["packages/*"] }`,
"package.json"));

// Legacy Dub recipes named package.json remain valid.
assertNotThrown(parsePackageRecipe(
`{ "name": "exec-simple", "targetType": "executable" }`,
"package.json"));

// Same npm-looking content is fine under dub.json (unknown keys handled by StrictMode).
assertNotThrown(parsePackageRecipe(
`{ "name": "web", "private": true }`,
"dub.json"));
}


unittest { // issue #711 - configuration default target type not correct for SDL
import dub.compilers.buildsettings : TargetType;
Expand Down
Empty file.
Empty file.
Empty file.
8 changes: 8 additions & 0 deletions test/issue3118-npm-package-json/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"name": "npm-looking-package",
"private": true,
"devDependencies": {
"antora": "^3.1.14"
},
"packageManager": "pnpm@9.15.0"
}
Loading