Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(publish): remove workspace prefix on publish #3397

Merged
merged 5 commits into from Nov 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
28 changes: 28 additions & 0 deletions commands/publish/index.js
Expand Up @@ -240,6 +240,7 @@ class PublishCommand extends Command {
}

chain = chain.then(() => this.resolveLocalDependencyLinks());
chain = chain.then(() => this.resolveWorkspaceDependencyLinks());
chain = chain.then(() => this.annotateGitHead());
chain = chain.then(() => this.serializeChanges());
chain = chain.then(() => this.packUpdated());
Expand Down Expand Up @@ -551,6 +552,33 @@ class PublishCommand extends Command {
});
}

resolveWorkspaceDependencyLinks() {
// resolve relative workspace: links to their actual version range
const updatesWithWorkspaceLinks = this.updates.filter((node) =>
Array.from(node.localDependencies.values()).some((resolved) => !!resolved.workspaceSpec)
);

return pMap(updatesWithWorkspaceLinks, (node) => {
for (const [depName, resolved] of node.localDependencies) {
let depVersion;
let savePrefix;
if (resolved.workspaceAlias) {
depVersion = this.updatesVersions.get(depName) || this.packageGraph.get(depName).pkg.version;
savePrefix = resolved.workspaceAlias === "*" ? "" : resolved.workspaceAlias;
} else {
const specMatch = resolved.workspaceSpec.match(/^workspace:([~^]?)(.*)/);
savePrefix = specMatch[1];
depVersion = specMatch[2];
}

// it no longer matters if we mutate the shared Package instance
node.pkg.updateLocalDependency(resolved, depVersion, savePrefix, { retainWorkspacePrefix: false });
}

// writing changes to disk handled in serializeChanges()
});
}

annotateGitHead() {
try {
const gitHead = this.options.gitHead || getCurrentSHA(this.execOpts);
Expand Down
6 changes: 3 additions & 3 deletions core/package/index.js
Expand Up @@ -254,7 +254,7 @@ class Package {
* @param {String} depVersion semver
* @param {String} savePrefix npm_config_save_prefix
*/
updateLocalDependency(resolved, depVersion, savePrefix) {
updateLocalDependency(resolved, depVersion, savePrefix, options = { retainWorkspacePrefix: true }) {
const depName = resolved.name;

// first, try runtime dependencies
Expand All @@ -270,10 +270,10 @@ class Package {
depCollection = this.devDependencies;
}

if (resolved.workspaceSpec) {
if (resolved.workspaceSpec && options.retainWorkspacePrefix) {
// do nothing if there is a workspace alias since they don't specify a version number
if (!resolved.workspaceAlias) {
const workspacePrefix = resolved.workspaceSpec.match(/^(workspace:[*|~|^]?)/)[0];
const workspacePrefix = resolved.workspaceSpec.match(/^(workspace:[*~^]?)/)[0];
depCollection[depName] = `${workspacePrefix}${depVersion}`;
}
} else if (resolved.registry || resolved.type === "directory") {
Expand Down
247 changes: 247 additions & 0 deletions e2e/tests/lerna-publish/lerna-publish-npm-workspace-prefix.spec.ts
@@ -0,0 +1,247 @@
import { writeJsonFile } from "@nrwl/devkit";
import { Fixture } from "../../utils/fixture";
import { normalizeCommitSHAs, normalizeEnvironment } from "../../utils/snapshot-serializer-utils";

const randomInt = (min: number, max: number) => Math.floor(Math.random() * (max - min + 1)) + min;
const randomVersion = () => `${randomInt(10, 89)}.${randomInt(10, 89)}.${randomInt(10, 89)}`;

expect.addSnapshotSerializer({
serialize(str: string) {
return normalizeCommitSHAs(normalizeEnvironment(str))
.replaceAll(/integrity:\s*.*/g, "integrity: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")
.replaceAll(/\d*\.?\d+\s?[KMGTkmgt]?B/g, "XXXB");
},
test(val: string) {
return val != null && typeof val === "string";
},
});

describe("lerna-publish-workspace-prefix", () => {
let fixture: Fixture;

beforeEach(async () => {
fixture = await Fixture.create({
name: "lerna-publish-workspace-prefix",
packageManager: "npm",
initializeGit: true,
runLernaInit: true,
installDependencies: true,
});
});
afterEach(() => fixture.destroy());

describe("from-git", () => {
it("should publish to the remote registry, removing workspace: prefix from dependencies", async () => {
await fixture.lerna("create test-workspace-alias-star -y");
await fixture.lerna("create test-workspace-alias-tilde -y");
await fixture.lerna("create test-workspace-alias-caret -y");
await fixture.lerna("create test-workspace-exact -y");
await fixture.lerna("create test-workspace-compat -y");
await fixture.lerna("create test-workspace-approx -y");
await fixture.lerna("create test-main -y");

await fixture.updateJson(`packages/test-main/package.json`, (json) => ({
...json,
dependencies: {
...(json.dependencies as Record<string, string>),
"test-workspace-alias-star": "workspace:*",
"test-workspace-alias-tilde": "workspace:~",
"test-workspace-alias-caret": "workspace:^",
"test-workspace-exact": `workspace:0.0.0`,
"test-workspace-compat": `workspace:^0.0.0`,
"test-workspace-approx": `workspace:~0.0.0`,
},
}));

const version = randomVersion();
await fixture.createInitialGitCommit();
await fixture.exec("git push origin test-main");

await fixture.lerna(`version ${version} -y`);

const output = await fixture.lerna(
"publish from-git --registry=http://localhost:4872 -y --concurrency 1"
);

const replaceVersion = (str: string) => str.replaceAll(version, "XX.XX.XX");

const unpublish = async (packageName: string) => {
const unpublishOutput = await fixture.exec(
`npm unpublish ${packageName}@${version} --force --registry=http://localhost:4872`
);
expect(replaceVersion(unpublishOutput.combinedOutput)).toContain(`${packageName}@XX.XX.XX`);
};

expect(replaceVersion(output.combinedOutput)).toMatchInlineSnapshot(`
lerna notice cli v999.9.9-e2e.0
Found 7 packages to publish:
- test-main => XX.XX.XX
- test-workspace-alias-caret => XX.XX.XX
- test-workspace-alias-star => XX.XX.XX
- test-workspace-alias-tilde => XX.XX.XX
- test-workspace-approx => XX.XX.XX
- test-workspace-compat => XX.XX.XX
- test-workspace-exact => XX.XX.XX
lerna info auto-confirmed
lerna info publish Publishing packages to npm...
lerna notice Skipping all user and access validation due to third-party registry
lerna notice Make sure you're authenticated properly ¯\\_(ツ)_/¯
lerna WARN ENOLICENSE Packages test-main, test-workspace-alias-caret, test-workspace-alias-star, test-workspace-alias-tilde, test-workspace-approx, test-workspace-compat, and test-workspace-exact are missing a license.
lerna WARN ENOLICENSE One way to fix this is to add a LICENSE.md file to the root of this repository.
lerna WARN ENOLICENSE See https://choosealicense.com for additional guidance.
lerna success published test-workspace-alias-caret XX.XX.XX
lerna notice
lerna notice 📦 test-workspace-alias-caret@XX.XX.XX
lerna notice === Tarball Contents ===
lerna notice XXXB lib/test-workspace-alias-caret.js
lerna notice XXXB package.json
lerna notice XXXB README.md
lerna notice === Tarball Details ===
lerna notice name: test-workspace-alias-caret
lerna notice version: XX.XX.XX
lerna notice filename: test-workspace-alias-caret-XX.XX.XX.tgz
lerna notice package size: XXXB
lerna notice unpacked size: XXXB
lerna notice shasum: {FULL_COMMIT_SHA}
lerna notice integrity: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
lerna notice total files: 3
lerna notice
lerna success published test-workspace-alias-star XX.XX.XX
lerna notice
lerna notice 📦 test-workspace-alias-star@XX.XX.XX
lerna notice === Tarball Contents ===
lerna notice XXXB lib/test-workspace-alias-star.js
lerna notice XXXB package.json
lerna notice XXXB README.md
lerna notice === Tarball Details ===
lerna notice name: test-workspace-alias-star
lerna notice version: XX.XX.XX
lerna notice filename: test-workspace-alias-star-XX.XX.XX.tgz
lerna notice package size: XXXB
lerna notice unpacked size: XXXB
lerna notice shasum: {FULL_COMMIT_SHA}
lerna notice integrity: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
lerna notice total files: 3
lerna notice
lerna success published test-workspace-alias-tilde XX.XX.XX
lerna notice
lerna notice 📦 test-workspace-alias-tilde@XX.XX.XX
lerna notice === Tarball Contents ===
lerna notice XXXB lib/test-workspace-alias-tilde.js
lerna notice XXXB package.json
lerna notice XXXB README.md
lerna notice === Tarball Details ===
lerna notice name: test-workspace-alias-tilde
lerna notice version: XX.XX.XX
lerna notice filename: test-workspace-alias-tilde-XX.XX.XX.tgz
lerna notice package size: XXXB
lerna notice unpacked size: XXXB
lerna notice shasum: {FULL_COMMIT_SHA}
lerna notice integrity: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
lerna notice total files: 3
lerna notice
lerna success published test-workspace-approx XX.XX.XX
lerna notice
lerna notice 📦 test-workspace-approx@XX.XX.XX
lerna notice === Tarball Contents ===
lerna notice XXXB lib/test-workspace-approx.js
lerna notice XXXB package.json
lerna notice XXXB README.md
lerna notice === Tarball Details ===
lerna notice name: test-workspace-approx
lerna notice version: XX.XX.XX
lerna notice filename: test-workspace-approx-XX.XX.XX.tgz
lerna notice package size: XXXB
lerna notice unpacked size: XXXB
lerna notice shasum: {FULL_COMMIT_SHA}
lerna notice integrity: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
lerna notice total files: 3
lerna notice
lerna success published test-workspace-compat XX.XX.XX
lerna notice
lerna notice 📦 test-workspace-compat@XX.XX.XX
lerna notice === Tarball Contents ===
lerna notice XXXB lib/test-workspace-compat.js
lerna notice XXXB package.json
lerna notice XXXB README.md
lerna notice === Tarball Details ===
lerna notice name: test-workspace-compat
lerna notice version: XX.XX.XX
lerna notice filename: test-workspace-compat-XX.XX.XX.tgz
lerna notice package size: XXXB
lerna notice unpacked size: XXXB
lerna notice shasum: {FULL_COMMIT_SHA}
lerna notice integrity: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
lerna notice total files: 3
lerna notice
lerna success published test-workspace-exact XX.XX.XX
lerna notice
lerna notice 📦 test-workspace-exact@XX.XX.XX
lerna notice === Tarball Contents ===
lerna notice XXXB lib/test-workspace-exact.js
lerna notice XXXB package.json
lerna notice XXXB README.md
lerna notice === Tarball Details ===
lerna notice name: test-workspace-exact
lerna notice version: XX.XX.XX
lerna notice filename: test-workspace-exact-XX.XX.XX.tgz
lerna notice package size: XXXB
lerna notice unpacked size: XXXB
lerna notice shasum: {FULL_COMMIT_SHA}
lerna notice integrity: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
lerna notice total files: 3
lerna notice
lerna success published test-main XX.XX.XX
lerna notice
lerna notice 📦 test-main@XX.XX.XX
lerna notice === Tarball Contents ===
lerna notice XXXB lib/test-main.js
lerna notice XXXB package.json
lerna notice XXXB README.md
lerna notice === Tarball Details ===
lerna notice name: test-main
lerna notice version: XX.XX.XX
lerna notice filename: test-main-XX.XX.XX.tgz
lerna notice package size: XXXB
lerna notice unpacked size: XXXB
lerna notice shasum: {FULL_COMMIT_SHA}
lerna notice integrity: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
lerna notice total files: 3
lerna notice
Successfully published:
- test-main@XX.XX.XX
- test-workspace-alias-caret@XX.XX.XX
- test-workspace-alias-star@XX.XX.XX
- test-workspace-alias-tilde@XX.XX.XX
- test-workspace-approx@XX.XX.XX
- test-workspace-compat@XX.XX.XX
- test-workspace-exact@XX.XX.XX
lerna success published 7 packages
`);

await fixture.exec("mkdir test-install-published-packages");
writeJsonFile(fixture.getWorkspacePath("test-install-published-packages/package.json"), {
name: "test-install-published-packages",
dependencies: {
"test-main": version,
},
});

// ensure that the published packages can be installed
// this verifies the validity of the updated package.json file that was published by `lerna publish`
await fixture.exec(
"npm --prefix ./test-install-published-packages install --registry=http://localhost:4872"
);

await unpublish("test-workspace-alias-star");
await unpublish("test-workspace-alias-tilde");
await unpublish("test-workspace-alias-caret");
await unpublish("test-workspace-exact");
await unpublish("test-workspace-compat");
await unpublish("test-workspace-approx");
});
});
});