Skip to content

Commit

Permalink
feat(core): add (.jsonc, .json5) support to Lerna-Lite config (#760)
Browse files Browse the repository at this point in the history
* feat(core): add (`.jsonc`, `.json5) support to Lerna-Lite config
  • Loading branch information
ghiscoding committed Nov 15, 2023
1 parent 1d73413 commit 311c297
Show file tree
Hide file tree
Showing 10 changed files with 106 additions and 36 deletions.
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,8 @@ lerna-repo/
lerna.json
```

**Note** Lerna-Lite now supports 3 file extension types (`.json`, `.jsonc` and `.json5`), the last 2 can accept comments but most code editors (like VSCode) only support [JSON Schema](https://json-schema.org/) and intellisense via `.json` and `.jsonc` extensions but not with `.json5`. Also our parser is very relaxed since we parse all 3 types with `JSON5.parse` so you could in theory add comments to all 3 types, but `.jsonc` or `.json5` are still prefered when adding comments.

Note that `package-a` will not be created, it is only shown here to help clarify the structure. For more info and full details about the `lerna.json` file, you can read the [lerna.json](https://github.com/lerna-lite/lerna-lite/wiki/lerna.json) Wiki. Also note that you can optionally add comments to your `lerna.json` config file since it is also able to parse JSON5 file format.

Finally install the commands that are of interest to you (`publish`, `version`, `run`, `exec`, ...)
Expand All @@ -199,6 +201,7 @@ If you are new to Lerna-Lite, you could also run the [lerna init](https://github
### JSON Schema
You can add the `$schema` property into your `lerna.json` to take advantage of Lerna-Lite [JSON Schema](https://json-schema.org/) (`lerna init` can help setting it up for you). This will help with the developer experience, users will be able to see what properties are valid with their types and a brief description of what each option does (descriptions are pulled from their associated lerna command options documentation).


##### `lerna.json`
```js
{
Expand All @@ -210,6 +213,8 @@ You can add the `$schema` property into your `lerna.json` to take advantage of L
}
```

> **Note** we support 3 config file extensions (`.json`, `.jsonc` and `.json5`), however most code editors (like VSCode) do not support JSON Schema with `.json5` extension, so `.jsonc` might be preffered if you wish to add comments.
### Separate / Optional Installs

| Command | Install | Description |
Expand Down Expand Up @@ -287,7 +292,7 @@ npm install @lerna-lite/publish -D

You want to see a project demo? Sure... you're looking at it 😉

Yes indeed, this project was originally created as an NPM Workspace and later migrated to a [pnpm workspaces](https://pnpm.io/workspaces) for the sole purpose of demoing and testing its own code. All changelogs and versions are created and published by the lib itself, how sweet is that? You can also see that this project has its own [`lerna.json`](https://github.com/lerna-lite/lerna-lite/blob/main/lerna.json) config file as well to run properly (take a look to see how it works).
Yes indeed, this project was originally created as an NPM Workspace and later migrated to a [pnpm workspaces](https://pnpm.io/workspaces) for the sole purpose of demoing and testing its own code. All changelogs and versions are created and published by the lib itself, how sweet is that? You can also see that this project has its own [`lerna.jsonc`](https://github.com/lerna-lite/lerna-lite/blob/main/lerna.jsonc) config file as well to run properly (take a look to see how it works).

### See it in Action 🎦

Expand Down
17 changes: 4 additions & 13 deletions lerna.json → lerna.jsonc
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
{
"$schema": "node_modules/@lerna-lite/cli/schemas/lerna-schema.json",
// or from a CDN:: "$schema": "https://raw.githubusercontent.com/lerna-lite/lerna-lite/main/packages/cli/schemas/lerna-schema.json",
"version": "2.6.0",
"loglevel": "verbose",
"npmClient": "pnpm",
"command": {
"publish": {
"cleanupTempFiles": true,
"removePackageFields": [
"devDependencies",
"scripts"
]
"removePackageFields": ["devDependencies", "scripts"]
},
"version": {
"conventionalCommits": true,
Expand All @@ -21,13 +19,6 @@
}
},
"changelogPreset": "conventional-changelog-conventionalcommits",
"ignoreChanges": [
"**/__fixtures__/**",
"**/__tests__/**",
"**/helpers/**",
"**/*.md"
],
"packages": [
"packages/*"
]
"ignoreChanges": ["**/__fixtures__/**", "**/__tests__/**", "**/helpers/**", "**/*.md"],
"packages": ["packages/*"]
}
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,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,4 @@
{
// any comments are valid in .jsonc
"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"
}
}
38 changes: 38 additions & 0 deletions packages/core/src/project/__tests__/project.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,44 @@ describe('Project', () => {
`);
});

it('does not error when lerna.jsonc contains trailing commas and/or comments', async () => {
const cwd = await initFixture('invalid-lerna-jsonc-recoverable');

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

it('can load lerna.jsonc config with comments', async () => {
const cwd = await initFixture('lerna-jsonc-config');
const project = new Project(cwd);

expect(project.config).toEqual({
version: '1.0.0',
});
expect(project.config).toMatchInlineSnapshot(`
{
"version": "1.0.0",
}
`);
});

it('can load lerna.json5 config with trailing commas and/or comments', async () => {
const cwd = await initFixture('lerna-json5-config');
const project = new Project(cwd);

expect(project.config).toEqual({
version: '1.0.0',
});
expect(project.config).toMatchInlineSnapshot(`
{
"version": "1.0.0",
}
`);
});

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

Expand Down
50 changes: 28 additions & 22 deletions packages/core/src/project/project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,29 +38,11 @@ export class Project {
explorer = cosmiconfigSync('lerna', {
loaders: {
...defaultLoaders,
'.json': (filepath, content) => {
/* c8 ignore next 3 */
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 JSON5.parse(content);
} catch (err: unknown) {
if (err instanceof Error) {
err.name = 'JSONError';
err.message = `Error in: ${filepath}\n${err.message}`;
}
throw err;
}
},
'.json': this.json5Loader,
'.jsonc': this.json5Loader,
'.json5': this.json5Loader,
},
searchPlaces: ['lerna.json', 'package.json'],
searchPlaces: ['lerna.json', 'lerna.jsonc', 'lerna.json5', 'package.json'],
transform(obj) {
// cosmiconfig returns null when nothing is found
if (!obj) {
Expand Down Expand Up @@ -264,6 +246,30 @@ export class Project {
return this.version === 'independent';
}

json5Loader(filepath: string, content: any) {
/* c8 ignore next 4 */
if (!/.*lerna\.json[c|5]?$/gi.test(filepath)) {
// when none of the 3x lerna config file type is found `lerna.{json,jsonc,json5}`, return default cosmiconfig json loader
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 JSON5.parse(content);
} catch (err: unknown) {
if (err instanceof Error) {
err.name = 'JSONError';
err.message = `Error in: ${filepath}\n${err.message}`;
}
throw err;
}
}

serializeConfig() {
// TODO: might be package.json prop
return writeJsonFile(this.rootConfigLocation, this.config, { indent: 2, detectIndent: true }).then(
Expand Down

0 comments on commit 311c297

Please sign in to comment.