Skip to content

Commit

Permalink
fix(init): add missing packages/workspaces
Browse files Browse the repository at this point in the history
- add new option `--use-workspaces`
- create `packages` property in "lerna.json" unless `--use-workspaces` is enabled which will create "workspaces" property in "package.json" file
  • Loading branch information
ghiscoding committed May 15, 2022
1 parent 2253e1c commit f78860d
Show file tree
Hide file tree
Showing 13 changed files with 278 additions and 188 deletions.
11 changes: 6 additions & 5 deletions README.md
Expand Up @@ -24,7 +24,7 @@
- [Troubleshooting](https://github.com/ghiscoding/lerna-lite/wiki/Troubleshooting)
- Commands
- included with CLI
- 🏁 [`init`](https://github.com/ghiscoding/lerna-lite/tree/main/packages/init#readme) - create/initialize a new Lerna-Lite repo
- 🧰 [`init`](https://github.com/ghiscoding/lerna-lite/tree/main/packages/init#readme) - create/initialize a new Lerna-Lite repo
- 💻 [`info`](https://github.com/ghiscoding/lerna-lite/tree/main/packages/info#readme) - print local environment information (useful when opening new issue)
- ☁️ [`publish`](https://github.com/ghiscoding/lerna-lite/tree/main/packages/publish#readme) - publish workspace packages
- 📑 [`version`](https://github.com/ghiscoding/lerna-lite/tree/main/packages/version#readme) - create new version for each workspace packages
Expand All @@ -34,8 +34,8 @@

---

### 📢 Lerna-Lite now supports yarn/pnpm `workspace:` protocol
#### _this new feature was introduced with version [1.2.0](https://github.com/ghiscoding/lerna-lite/releases/tag/v1.2.0) of Lerna-Lite, however we recommend using [1.3.0](https://github.com/ghiscoding/lerna-lite/releases/tag/v1.3.0)._
### 📢 Lerna-Lite now supports yarn/pnpm `workspace:` protocol
#### _this new feature was introduced with release [1.2.0](https://github.com/ghiscoding/lerna-lite/releases/tag/v1.2.0) of Lerna-Lite, however we recommend using [1.3.0](https://github.com/ghiscoding/lerna-lite/releases/tag/v1.3.0)._
If you use this new feature, please take 30sec. to fill in this small [poll #156](https://github.com/ghiscoding/lerna-lite/discussions/156) survey just to see which package manager is the most popular to use with this new `workspace:` protocol.

---
Expand Down Expand Up @@ -106,18 +106,19 @@ This will create a `lerna.json` configuration file as well as a `packages` folde
```
lerna-repo/
packages/
package-a
package.json
lerna.json
```

## Installation
Run the following commands to install Lerna-Lite in your project and/or install it globally by adding the `-g` option.
Run the following commands to install Lerna-Lite in your project and/or install it globally by adding the `-g` option.

If you are new to Lerna-Lite, you could also run the [lerna init](https://github.com/ghiscoding/lerna-lite/tree/main/packages/init#readme) command which will create the `lerna.json` for you.

| Command | Install | Description | Included |
|---------|-------------|-------------| ---------|
| 🏁 [init](https://github.com/ghiscoding/lerna-lite/tree/main/packages/init#readme) | `npm i @lerna-lite/cli -D -W` | create/initialize a new Lerna-Lite repo | Yes |
| 🧰 [init](https://github.com/ghiscoding/lerna-lite/tree/main/packages/init#readme) | `npm i @lerna-lite/cli -D -W` | create/initialize a new Lerna-Lite repo | Yes |
| 💻 [info](https://github.com/ghiscoding/lerna-lite/tree/main/packages/info#readme) | `npm i @lerna-lite/cli -D -W` | print local environment information | Yes |
| 📑 [version](https://github.com/ghiscoding/lerna-lite/tree/main/packages/version#readme) | `npm i @lerna-lite/cli -D -W` | create new version for each workspace package | Yes |
| ☁️ [publish](https://github.com/ghiscoding/lerna-lite/tree/main/packages/publish#readme) | `npm i @lerna-lite/cli -D -W` | publish each workspace package | Yes |
Expand Down
2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -9,7 +9,7 @@
"build": "npm run build --ws",
"lint": "eslint packages --ext .ts",
"dist-info-cmd": "node ./packages/cli/dist/cli.js info",
"dist-init-cmd": "node ./packages/cli/dist/cli.js init --independent --exact",
"dist-init-cmd": "node ./packages/cli/dist/cli.js init --independent --exact --use-workspaces",
"dist-roll-version": "node ./packages/cli/dist/cli.js version",
"dist-roll-version-dry-run": "node ./packages/cli/dist/cli.js version --git-dry-run",
"dist-roll-publish": "node ./packages/cli/dist/cli.js publish from-package",
Expand Down
5 changes: 5 additions & 0 deletions packages/cli/src/cli-commands/cli-init-commands.ts
Expand Up @@ -16,6 +16,11 @@ exports.builder = {
alias: 'i',
type: 'boolean',
},
'use-workspaces': {
group: 'Command Options:',
describe: 'Enable integration with Yarn workspaces.',
type: 'boolean',
},
};

exports.handler = function handler(argv) {
Expand Down
36 changes: 18 additions & 18 deletions packages/core/src/__tests__/command.spec.ts
Expand Up @@ -19,7 +19,7 @@ import { updateLernaConfig } from '@lerna-test/update-lerna-config';
import { Command } from '../command';

describe('core-command', () => {
let testDir;
let testDir = '';

beforeAll(async () => {
testDir = await initFixture('basic');
Expand All @@ -38,7 +38,7 @@ describe('core-command', () => {
// swallow errors when passed in argv
const onRejected = () => { };

class OkCommand extends Command {
class OkCommand extends Command<any> {
initialize() {
return true;
}
Expand All @@ -49,7 +49,7 @@ describe('core-command', () => {
}

// convenience to avoid silly 'not implemented errors'
const testFactory = (argv = {}) => new OkCommand(Object.assign({ cwd: testDir }, argv));
const testFactory = (argv = {}) => new OkCommand(Object.assign({ cwd: testDir } as any, argv));

describe('.logger', () => {
it('should be added to the instance', async () => {
Expand Down Expand Up @@ -168,7 +168,7 @@ describe('core-command', () => {
});

it('logs stdout and stderr of error from package', async () => {
class PkgErrorCommand extends Command {
class PkgErrorCommand extends Command<any> {
initialize() {
return true;
}
Expand All @@ -187,7 +187,7 @@ describe('core-command', () => {
}
}

const command = new PkgErrorCommand({ cwd: testDir });
const command = new PkgErrorCommand({ cwd: testDir } as any);

await expect(command).rejects.toThrow(
expect.objectContaining({
Expand All @@ -206,7 +206,7 @@ describe('core-command', () => {
});

it('does not log stdout/stderr after streaming ends', async () => {
class PkgErrorCommand extends Command {
class PkgErrorCommand extends Command<any> {
initialize() {
return true;
}
Expand All @@ -225,7 +225,7 @@ describe('core-command', () => {
}
}

const command = new PkgErrorCommand({ cwd: testDir, stream: true });
const command = new PkgErrorCommand({ cwd: testDir, stream: true } as any);

await expect(command).rejects.toThrow('message');
expect(console.error).not.toHaveBeenCalled();
Expand Down Expand Up @@ -257,46 +257,46 @@ describe('core-command', () => {
});

describe('.options', () => {
class TestACommand extends Command { }
class TestBCommand extends Command { }
class TestCCommand extends Command {
class TestACommand extends Command<any> { }
class TestBCommand extends Command<any> { }
class TestCCommand extends Command<any> {
get otherCommandConfigs() {
return ['testb'];
}
}

it('does not mutate argv parameter', async () => {
const argv = { cwd: testDir, onRejected };
const instance = new TestACommand(argv);
const instance = new TestACommand(argv as any);
await instance;

expect(argv).toEqual({ cwd: testDir, onRejected });
expect(instance.argv).not.toEqual(argv);
});

it('should pick up global options', async () => {
const instance = new TestACommand({ cwd: testDir, onRejected });
const instance = new TestACommand({ cwd: testDir, onRejected } as any);
await instance;

expect(instance.options.testOption).toBe('default');
});

it('should override global options with command-level options', async () => {
const instance = new TestBCommand({ cwd: testDir, onRejected });
const instance = new TestBCommand({ cwd: testDir, onRejected } as any);
await instance;

expect(instance.options.testOption).toBe('b');
});

it('should override global options with inherited command-level options', async () => {
const instance = new TestCCommand({ cwd: testDir, onRejected });
const instance = new TestCCommand({ cwd: testDir, onRejected } as any);
await instance;

expect(instance.options.testOption).toBe('b');
});

it('should override inherited command-level options with local command-level options', async () => {
const instance = new TestCCommand({ cwd: testDir, onRejected });
const instance = new TestCCommand({ cwd: testDir, onRejected } as any);
await instance;

expect(instance.options.testOption2).toBe('c');
Expand All @@ -307,7 +307,7 @@ describe('core-command', () => {
cwd: testDir,
onRejected,
testOption2: 'f',
});
} as any);
await instance;

expect(instance.options.testOption2).toBe('f');
Expand All @@ -318,7 +318,7 @@ describe('core-command', () => {
cwd: testDir,
onRejected,
testOption: undefined, // yargs does this when --test-option is not passed
});
} as any);
await instance;

expect(instance.options.testOption).toBe('b');
Expand All @@ -328,7 +328,7 @@ describe('core-command', () => {
describe('subclass implementation', () => {
['initialize', 'execute'].forEach((method) => {
it(`throws if ${method}() is not overridden`, () => {
const command = new Command({ cwd: testDir, onRejected });
const command = new Command({ cwd: testDir, onRejected } as any);
expect(() => command[method]()).toThrow();
});
});
Expand Down
3 changes: 3 additions & 0 deletions packages/core/src/models/command-options.ts
Expand Up @@ -41,6 +41,9 @@ export interface InitCommandOption {

/** version packages independently */
independent?: boolean;

/** enables integration with Yarn or other package manager that use `workspaces` property in `package.json` */
useWorkspaces?: boolean;
}

export interface PublishCommandOption extends VersionCommandOption {
Expand Down
16 changes: 1 addition & 15 deletions packages/core/src/models/interfaces.ts
Expand Up @@ -191,22 +191,8 @@ export interface ProjectConfig extends LernaConfig, QueryGraphConfig {
}

/** The subset of package.json properties that Lerna-Lite uses */
export interface RawManifest {
name: string;
location: string;
version: string;
private?: boolean;
bin?: Record<string, string> | string;
scripts?: Record<string, string>;
dependencies?: Record<string, string>;
devDependencies?: Record<string, string>;
optionalDependencies?: Record<string, string>;
peerDependencies?: Record<string, string>;
export interface RawManifest extends Package {
publishConfig?: Record<'directory' | 'registry' | 'tag', string>;

/** workspace package globs when `useWorkspaces` is enabled */
workspaces?: string[] | { packages: string[] };
get: (str: string) => { packages?: string[] } | string[];
}

export interface ReleaseClient {
Expand Down
8 changes: 8 additions & 0 deletions packages/core/src/package.ts
Expand Up @@ -149,6 +149,14 @@ export class Package {
this[PKG].version = version;
}

get workspaces(): string[] | { packages: string[] } {
return this[PKG].workspaces;
}

set workspaces(workspaces: string[] | { packages: string[] }) {
this[PKG].workspaces = workspaces;
}

get contents() {
// if modified with setter, use that value
if (this[_contents]) {
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/project/project.ts
Expand Up @@ -121,7 +121,7 @@ export class Project {
return (this.packageConfigs as any).map(globParent).map((parentDir: string) => path.resolve(this.rootPath, parentDir));
}

get manifest(): Package {
get manifest(): RawManifest {
let manifest;

try {
Expand Down
39 changes: 34 additions & 5 deletions packages/init/README.md
Expand Up @@ -4,7 +4,7 @@
[![npm](https://img.shields.io/npm/v/@lerna-lite/init.svg?logo=npm&logoColor=fff&label=npm&color=limegreen)](https://www.npmjs.com/package/@lerna-lite/init)

# @lerna-lite/init
## (`lerna init`) - Init command 🏁
## (`lerna init`) - Init command 🧰

Create/initialize a new Lerna-Lite repo or upgrade an existing repo to the current version of Lerna-Lite CLI

Expand All @@ -27,14 +27,14 @@ npx lerna init
$ lerna init
```

Create/initialize a new Lerna=Lite repo or upgrade an existing repo to the current version of Lerna-Lite.
Create/initialize a new Lerna-Lite repo or upgrade an existing repo to the current version of Lerna-Lite.

> Lerna assumes the repo has already been initialized with `git init`.
When run, this command will:

1. Add `lerna` as a [`devDependency`](https://docs.npmjs.com/files/package.json#devdependencies) in `package.json` if it doesn't already exist.
2. Create a `lerna.json` config file to store the `version` number.
2. Create a `lerna.json` config file to store the `version` number and also add a `packages` property (unless you use the `--use-workspaces` flag)

Example output on a new git repo:

Expand All @@ -54,7 +54,7 @@ lerna success Initialized Lerna files
$ lerna init --independent
```

This flag tells Lerna to use independent versioning mode.
This flag tells Lerna-Lite to use independent versioning mode.

### `--exact`

Expand All @@ -65,7 +65,7 @@ $ lerna init --exact
By default, `lerna init` will use a caret range when adding or updating
the local version of `lerna`, just like `npm install --save-dev lerna`.

To retain the `lerna` 1.x behavior of "exact" comparison, pass this flag.
To retain the `lerna` of "exact" comparison, pass this flag.
It will configure `lerna.json` to enforce exact match for all subsequent executions.

```json
Expand All @@ -78,3 +78,32 @@ It will configure `lerna.json` to enforce exact match for all subsequent executi
"version": "0.0.0"
}
```

### `--use-workspaces`

```sh
$ lerna init --use-workspaces
```

This flag tells Lerna-Lite to add a `workspaces` property in the root `package.json` instead of the default `lerna.json` file.

#### `lerna.json`
```json
{
"version": "0.0.0"
}
```

#### `package.json`
```json
{
"name": "monorepo",
"devDependencies": {
"@lerna-lite/cli": "^1.3.0"
},
"workspaces": [
"./packages/a",
"./packages/a"
]
}
```
@@ -1,6 +1,6 @@
{
"name": "updates",
"devDependencies": {
"dependencies": {
"@lerna-lite/cli": "__OVERWRITTEN__"
}
}

0 comments on commit f78860d

Please sign in to comment.