Skip to content

Commit

Permalink
fix: handle trailing commas and comments in lerna.json files (#3603)
Browse files Browse the repository at this point in the history
  • Loading branch information
JamesHenry committed Mar 23, 2023
1 parent 43afbf8 commit b826398
Show file tree
Hide file tree
Showing 8 changed files with 89 additions and 8 deletions.
8 changes: 6 additions & 2 deletions .github/actions/install-node-and-dependencies/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,13 @@ runs:
using: "composite"
steps:
- name: Install node and npm based on the given values (or the volta config in package.json)
uses: volta-cli/action@v4
uses: actions/setup-node@v3
with:
node-version: ${{ inputs.node-version }}
node-version-file: "package.json"
# The volta action seems to failing to install/unpack a lot recently https://github.com/volta-cli/action/issues/77#issuecomment-1481045178
# uses: volta-cli/action@v4
# with:
# node-version: ${{ inputs.node-version }}

# Github actions/cache now recommends _not_ caching node_modules when using npm ci
# https://github.com/actions/cache/blob/940f3d7cf195ba83374c77632d1e2cbb2f24ae68/examples.md#node---npm
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{
233434
"version": "1.0.0",
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "test",
"devDependencies": {
"lerna": "500.0.0",
"lerna": "500.0.0"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
// Trailing commas and comments are recoverable
"version": "1.0.0",
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"name": "test",
"devDependencies": {
"lerna": "500.0.0"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"name": "test",
"devDependencies": {
"lerna": "500.0.0",
},
"lerna": {
"version": "1.0.0"
}
}
40 changes: 37 additions & 3 deletions libs/core/src/lib/project/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,18 @@ import fs from "fs-extra";
import { dump } from "js-yaml";
import path from "path";

// Serialize the JSONError output to be more human readable
expect.addSnapshotSerializer({
serialize(str: string) {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const stripAnsi = require("strip-ansi");
return stripAnsi(str).replace(/Error in: .*lerna\.json/, "Error in: normalized/path/to/lerna.json");
},
test(val: string) {
return val != null && typeof val === "string" && val.includes("Error in: ");
},
});

// helpers
const initFixture = initFixtureFactory(__dirname);

Expand Down Expand Up @@ -59,15 +71,37 @@ describe("Project", () => {
expect(new Project().config).toEqual({});
});

it("errors when lerna.json is not valid JSON", async () => {
const cwd = await initFixture("invalid-json");
it("does not error when lerna.json contains trailing commas and/or comments", async () => {
const cwd = await initFixture("invalid-lerna-json-recoverable");

expect(new Project(cwd).config).toMatchInlineSnapshot(`
Object {
"version": "1.0.0",
}
`);
});

it("errors when lerna.json is irrecoverably invalid JSON", async () => {
const cwd = await initFixture("invalid-lerna-json-irrecoverable");

expect(() => new Project(cwd)).toThrow(
expect.objectContaining({
name: "ValidationError",
prefix: "JSONError",
})
);

expect(() => new Project(cwd)).toThrowErrorMatchingInlineSnapshot(`
Error in: normalized/path/to/lerna.json
PropertyNameExpected in JSON at 2:3
1 | {
> 2 | 233434
| ^^^^^^
3 | "version": "1.0.0",
4 | }
5 |
`);
});

it("returns parsed rootPkg.lerna", async () => {
Expand Down Expand Up @@ -447,7 +481,7 @@ describe("Project", () => {
});

it("errors when root package.json is not valid JSON", async () => {
const cwd = await initFixture("invalid-json");
const cwd = await initFixture("invalid-package-json");

expect(() => new Project(cwd)).toThrow(
expect.objectContaining({
Expand Down
27 changes: 25 additions & 2 deletions libs/core/src/lib/project/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { writeJsonFile } from "@nrwl/devkit";
import { cosmiconfigSync } from "cosmiconfig";
import { parseJson, writeJsonFile } from "@nrwl/devkit";
import { cosmiconfigSync, defaultLoaders } from "cosmiconfig";
import dedent from "dedent";
import fs from "fs";
import globParent from "glob-parent";
Expand Down Expand Up @@ -75,6 +75,29 @@ export class Project {

constructor(cwd?: string) {
const explorer = cosmiconfigSync("lerna", {
loaders: {
...defaultLoaders,
".json": (filepath, content) => {
if (!filepath.endsWith("lerna.json")) {
return defaultLoaders[".json"](filepath, content);
}
/**
* This prevents lerna from blowing up on trailing commas and comments in lerna configs,
* however it should be noted that we will not be able to respect those things whenever
* we perform an automated config migration, e.g. via `lerna repair` and they will be lost.
* (Although that will be easy enough for the user to see and updated in their `git diff`)
*/
try {
return parseJson(content);
} catch (err: unknown) {
if (err instanceof Error) {
err.name = "JSONError";
err.message = `Error in: ${filepath}\n${err.message}`;
}
throw err;
}
},
},
searchPlaces: ["lerna.json", "package.json"],
transform(obj) {
// cosmiconfig returns null when nothing is found
Expand Down

0 comments on commit b826398

Please sign in to comment.