Skip to content

Commit

Permalink
feat(core): add version/publish workspace: protocol
Browse files Browse the repository at this point in the history
- add few unit tests under Package/PackageGraph & Publish
  • Loading branch information
ghiscoding committed May 8, 2022
1 parent b855ba8 commit ee57dfb
Show file tree
Hide file tree
Showing 23 changed files with 397 additions and 41 deletions.
74 changes: 73 additions & 1 deletion packages/core/src/__tests__/package.spec.ts
Expand Up @@ -301,7 +301,7 @@ describe('Package', () => {
});

describe(".updateLocalDependency()", () => {
it("works with workspace: protocols", () => {
it("works with `workspace:` protocol range", () => {
const pkg = factory({
dependencies: {
a: "workspace:^1.0.0",
Expand Down Expand Up @@ -329,6 +329,78 @@ describe('Package', () => {
}
`);
});

it("works with star workspace target `workspace:*` and will keep same output target", () => {
const pkg = factory({
dependencies: {
a: "workspace:*",
b: "workspace:^1.0.0",
},
});

const resolved: NpaResolveResult = npa.resolve("a", "^1.0.0", ".");
resolved.explicitWorkspace = true;
resolved.workspaceTarget = 'workspace:*';

pkg.updateLocalDependency(resolved, "2.0.0", "^");

expect(pkg.toJSON()).toMatchInlineSnapshot(`
Object {
"dependencies": Object {
"a": "workspace:*",
"b": "workspace:^1.0.0",
},
}
`);
});

it("works with caret workspace target `workspace:^` and will keep same output target", () => {
const pkg = factory({
dependencies: {
a: "workspace:^",
b: "workspace:^1.0.0",
},
});

const resolved: NpaResolveResult = npa.resolve("a", "^1.0.0", ".");
resolved.explicitWorkspace = true;
resolved.workspaceTarget = 'workspace:^';

pkg.updateLocalDependency(resolved, "2.0.0", "^");

expect(pkg.toJSON()).toMatchInlineSnapshot(`
Object {
"dependencies": Object {
"a": "workspace:^",
"b": "workspace:^1.0.0",
},
}
`);
});

it("works with tilde workspace target `workspace:~` and will keep same output target", () => {
const pkg = factory({
dependencies: {
a: "workspace:~",
b: "workspace:^1.0.0",
},
});

const resolved: NpaResolveResult = npa.resolve("a", "^1.0.0", ".");
resolved.explicitWorkspace = true;
resolved.workspaceTarget = 'workspace:~';

pkg.updateLocalDependency(resolved, "2.0.0", "^");

expect(pkg.toJSON()).toMatchInlineSnapshot(`
Object {
"dependencies": Object {
"a": "workspace:~",
"b": "workspace:^1.0.0",
},
}
`);
});
});
});

Expand Down
16 changes: 8 additions & 8 deletions packages/core/src/command.ts
Expand Up @@ -10,7 +10,7 @@ import { warnIfHanging } from './utils/warn-if-hanging';
import { writeLogFile } from './utils/write-log-file';
import { Project } from './project/project';
import { ValidationError } from './validation-error';
import { ExecOpts } from './models';
import { CommandType, ExecOpts } from './models';
import { PackageGraph } from './package-graph/package-graph';
import { logExecCommand } from './child-process';

Expand All @@ -25,7 +25,7 @@ export class Command {
toposort?: number;

execOpts!: ExecOpts;
name = '';
commandName: CommandType = '';
composed;
logger!: Logger;
options: any;
Expand All @@ -41,10 +41,10 @@ export class Command {
log.silly('argv', argv);

// 'FooCommand' => 'foo'
this.name = this.constructor.name.replace(/Command$/, '').toLowerCase();
this.commandName = (this.constructor.name.replace(/Command$/, '').toLowerCase()) as CommandType;

// composed commands are called from other commands, like publish -> version
this.composed = typeof argv.composed === 'string' && argv.composed !== this.name;
this.composed = typeof argv.composed === 'string' && argv.composed !== this.commandName;

if (!this.composed) {
// composed commands have already logged the lerna version
Expand Down Expand Up @@ -175,7 +175,7 @@ export class Command {
const commandConfig = this.project.config.command || {};

// The current command always overrides otherCommandConfigs
const overrides = [this.name, ...this.otherCommandConfigs].map((key) => (commandConfig as any)[key]);
const overrides = [this.commandName, ...this.otherCommandConfigs].map((key) => (commandConfig as any)[key]);

this.options = defaultOptions(
// CLI flags, which if defined overrule subsequent values
Expand Down Expand Up @@ -213,7 +213,7 @@ export class Command {

// create logger that subclasses use
Object.defineProperty(this, 'logger', {
value: log.newGroup(this.name),
value: log.newGroup(this.commandName),
});

// emit all buffered logs at configured level and higher
Expand Down Expand Up @@ -302,11 +302,11 @@ export class Command {
}

initialize(): any | Promise<any> {
throw new ValidationError(this.name, 'initialize() needs to be implemented.');
throw new ValidationError(this.commandName, 'initialize() needs to be implemented.');
}

execute(): any | Promise<any> {
throw new ValidationError(this.name, 'execute() needs to be implemented.');
throw new ValidationError(this.commandName, 'execute() needs to be implemented.');
}
}

Expand Down
3 changes: 3 additions & 0 deletions packages/core/src/models/index.ts
Expand Up @@ -18,6 +18,8 @@ export interface CommandOptions {
rollVersion?: boolean;
}

export type CommandType = '' | 'exec' | 'info' | 'publish' | 'version' | 'run';

export interface DescribeRefOptions {
/* Defaults to `process.cwd()` */
cwd?: string;
Expand Down Expand Up @@ -141,6 +143,7 @@ export interface GitClient {

export type NpaResolveResult = (npa.FileResult | npa.HostedGitResult | npa.URLResult | npa.AliasResult | npa.RegistryResult) & {
explicitWorkspace?: boolean;
workspaceTarget?: string;
}

/** Passed between concurrent executions */
Expand Down
39 changes: 33 additions & 6 deletions packages/core/src/package-graph/__tests__/package-graph.spec.ts
Expand Up @@ -120,15 +120,42 @@ describe("PackageGraph", () => {
},
"/test/pkg-3"
),
new Package(
{
name: "pkg-4",
version: "1.0.0",
dependencies: {
"pkg-1": "workspace:*",
},
},
"/test/pkg-4"
),
new Package(
{
name: "pkg-5",
version: "1.0.0",
dependencies: {
"pkg-1": "workspace:^",
"pkg-2": "workspace:~",
},
},
"/test/pkg-5"
),
];

const graph = new PackageGraph(pkgs, "allDependencies", "explicit");
const [pkg1, pkg2, pkg3] = graph.values();

expect(pkg1.localDependents.has("pkg-2")).toBe(false);
expect(pkg2.localDependencies.has("pkg-1")).toBe(false);
expect(pkg1.localDependents.has("pkg-3")).toBe(true);
expect(pkg3.localDependencies.has("pkg-1")).toBe(true);
const [pkg1, pkg2, pkg3, pkg4, pkg5] = graph.values();

expect(pkg1.localDependents.has('pkg-2')).toBe(false);
expect(pkg2.localDependencies.has('pkg-1')).toBe(false);
expect(pkg1.localDependents.has('pkg-3')).toBe(true);
expect(pkg3.localDependencies.has('pkg-1')).toBe(true);
expect(pkg4.localDependencies.has('pkg-1')).toBe(true);
expect(pkg4.localDependencies.get('pkg-1').workspaceTarget).toBe('workspace:*');
expect(pkg5.localDependencies.has('pkg-1')).toBe(true);
expect(pkg5.localDependencies.has('pkg-2')).toBe(true);
expect(pkg5.localDependencies.get('pkg-1').workspaceTarget).toBe('workspace:^');
expect(pkg5.localDependencies.get('pkg-2').workspaceTarget).toBe('workspace:~');
});
});

Expand Down
15 changes: 15 additions & 0 deletions packages/core/src/package-graph/package-graph.ts
Expand Up @@ -70,12 +70,27 @@ export class PackageGraph extends Map {
// npa doesn't support the explicit workspace: protocol, supported by
// pnpm and Yarn.
const explicitWorkspace = /^workspace:/.test(spec);
let workspaceTarget: string | undefined;
if (explicitWorkspace) {
workspaceTarget = spec;
spec = spec.replace(/^workspace:/, '');

// when dependency is defined as target workspace, like `workspace:*`,
// we'll have to pull the version from its parent package version property
// example with `1.5.0`, ws:* => "1.5.0", ws:^ => "^1.5.0", ws:~ => "~1.5.0", ws:^1.5.0 => "^1.5.0"
if (spec === '*' || spec === '^' || spec === '~') {
const depPkg = packages.find(pkg => pkg.name === depName);
const version = depPkg?.version;
const specTarget = spec === '*' ? '' : spec;
spec = depPkg ? `${specTarget}${version}` : '';
}
}

const resolved: NpaResolveResult = npa.resolve(depName, spec, currentNode.location);
resolved.explicitWorkspace = explicitWorkspace;
if (resolved.explicitWorkspace) {
resolved.workspaceTarget = workspaceTarget;
}

if (!depNode) {
// it's an external dependency, store the resolution and bail
Expand Down
15 changes: 11 additions & 4 deletions packages/core/src/package.ts
Expand Up @@ -3,7 +3,7 @@ import path from 'path';
import loadJsonFile from 'load-json-file';
import writePkg from 'write-pkg';

import { NpaResolveResult, RawManifest } from './models';
import { CommandType, NpaResolveResult, RawManifest } from './models';

// symbol used to 'hide' internal state
const PKG = Symbol('pkg');
Expand Down Expand Up @@ -244,8 +244,9 @@ export class Package {
* @param {Object} resolved npa metadata
* @param {String} depVersion semver
* @param {String} savePrefix npm_config_save_prefix
* @param {String} updatedByCommand - which command called this update?
*/
updateLocalDependency(resolved: NpaResolveResult, depVersion: string, savePrefix: string) {
updateLocalDependency(resolved: NpaResolveResult, depVersion: string, savePrefix: string, updatedByCommand?: CommandType) {
const depName = resolved.name as string;

// first, try runtime dependencies
Expand All @@ -266,8 +267,14 @@ export class Package {
if (resolved.registry || resolved.type === 'directory') {
// a version (1.2.3) OR range (^1.2.3) OR directory (file:../foo-pkg)
depCollection[depName] = `${savePrefix}${depVersion}`;
if (resolved.explicitWorkspace) {
depCollection[depName] = `workspace:${depCollection[depName]}`;

// when using explicit workspace protocol and we're not doing a Publish
// if we are publishing, we will skip this and so we'll keep regular semver range, e.g.: "workspace:*"" will be converted to "^1.2.3"
if (resolved.explicitWorkspace && updatedByCommand !== 'publish') {
// keep target workspace or bump when it's a workspace semver range (like `workspace:^1.2.3`)
depCollection[depName] = /^workspace:[*|^|~]{1}$/.test(resolved?.workspaceTarget ?? '')
? resolved.workspaceTarget // target like `workspace:*`
: `workspace:${depCollection[depName]}`; // range like `workspace:^1.2.3`
}
} else if (resolved.gitCommittish) {
// a git url with matching committish (#v1.2.3 or #1.2.3)
Expand Down
3 changes: 2 additions & 1 deletion packages/exec/src/exec-command.ts
Expand Up @@ -2,6 +2,7 @@ import 'dotenv/config';
import pMap from 'p-map';
import {
Command,
CommandType,
logOutput,
Package,
runTopologically,
Expand All @@ -19,7 +20,7 @@ export function factory(argv) {

export class ExecCommand extends Command {
/** command name */
name = 'exec';
name = 'exec' as CommandType;

args: string[] = [];
bail = false;
Expand Down
4 changes: 2 additions & 2 deletions packages/info/src/info-command.ts
@@ -1,4 +1,4 @@
import { Command, logOutput, } from '@lerna-lite/core';
import { Command, CommandType, logOutput, } from '@lerna-lite/core';
import envinfo from 'envinfo';

export function factory(argv) {
Expand All @@ -7,7 +7,7 @@ export function factory(argv) {

export class InfoCommand extends Command {
/** command name */
name = 'info';
name = 'info' as CommandType;

constructor(argv: any) {
super(argv);
Expand Down
@@ -0,0 +1 @@
node_modules
@@ -0,0 +1 @@
registry = https://registry.npmjs.org/
@@ -0,0 +1,3 @@
{
"version": "1.0.0"
}
@@ -0,0 +1,10 @@
{
"name": "workspace-protocol-specs",
"description": "workspace protocol specifiers should act just like recognized semver ranges",
"private": true,
"version": "0.0.0-lerna",
"dependencies": {
"package-2": "workspace:*",
"package-5": "workspace:*"
}
}
@@ -0,0 +1,8 @@
{
"name": "package-1",
"version": "1.0.0",
"changed": false,
"dependencies": {
"tiny-tarball": "^1.0.0"
}
}
@@ -0,0 +1,7 @@
{
"name": "package-2",
"version": "1.0.0",
"dependencies": {
"package-1": "workspace:*"
}
}
@@ -0,0 +1,7 @@
{
"name": "package-3",
"version": "1.0.0",
"dependencies": {
"package-2": "workspace:^"
}
}
@@ -0,0 +1,7 @@
{
"name": "package-4",
"version": "1.0.0",
"optionalDependencies": {
"package-3": "workspace:~"
}
}
@@ -0,0 +1,8 @@
{
"name": "package-5",
"version": "1.0.0",
"dependencies": {
"package-4": "workspace:^1.0.0",
"package-6": "workspace:~1.0.0"
}
}
@@ -0,0 +1,7 @@
{
"name": "package-6",
"version": "1.0.0",
"dependencies": {
"tiny-tarball": "^1.0.0"
}
}
@@ -0,0 +1,8 @@
{
"name": "package-7",
"version": "1.0.0",
"private": true,
"dependencies": {
"package-1": "^1.0.0"
}
}

0 comments on commit ee57dfb

Please sign in to comment.