Skip to content

Commit

Permalink
fix(js): allow publishable library to run release command
Browse files Browse the repository at this point in the history
  • Loading branch information
xiongemi committed Feb 6, 2025
1 parent 8bd0bcd commit 3d0d1bd
Show file tree
Hide file tree
Showing 6 changed files with 994 additions and 155 deletions.
123 changes: 123 additions & 0 deletions e2e/release/src/publishable-libraries-release.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import {
cleanupProject,
newProject,
runCLI,
runCommandAsync,
uniq,
} from '@nx/e2e/utils';
import { execSync } from 'node:child_process';

expect.addSnapshotSerializer({
serialize(str: string) {
return (
str
// Remove all output unique to specific projects to ensure deterministic snapshots
.replaceAll(/my-pkg-\d+/g, '{project-name}')
.replaceAll(
/integrity:\s*.*/g,
'integrity: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
)
.replaceAll(/\b[0-9a-f]{40}\b/g, '{SHASUM}')
.replaceAll(/\d*B index\.js/g, 'XXB index.js')
.replaceAll(/\d*B project\.json/g, 'XXB project.json')
.replaceAll(/\d*B package\.json/g, 'XXXB package.json')
.replaceAll(/size:\s*\d*\s?B/g, 'size: XXXB')
.replaceAll(/\d*\.\d*\s?kB/g, 'XXX.XXX kb')
.replaceAll(/[a-fA-F0-9]{7}/g, '{COMMIT_SHA}')
.replaceAll(/Test @[\w\d]+/g, 'Test @{COMMIT_AUTHOR}')
// Normalize the version title date.
.replaceAll(/\(\d{4}-\d{2}-\d{2}\)/g, '(YYYY-MM-DD)')
// We trim each line to reduce the chances of snapshot flakiness
.split('\n')
.map((r) => r.trim())
.filter(Boolean)
.join('\n')
);
},
test(val: string) {
return val != null && typeof val === 'string';
},
});

describe('publishable libraries release', () => {
let e2eRegistryUrl: string;

beforeAll(async () => {
newProject({
packages: ['@nx/js'],
});

// Normalize git committer information so it is deterministic in snapshots
await runCommandAsync(`git config user.email "[email protected]"`);
await runCommandAsync(`git config user.name "Test"`);
// Create a baseline version tag
await runCommandAsync(`git tag v0.0.0`);

// We need a valid git origin to exist for the commit references to work (and later the test for createRelease)
await runCommandAsync(
`git remote add origin https://github.com/nrwl/fake-repo.git`
);

// This is the verdaccio instance that the e2e tests themselves are working from
e2eRegistryUrl = execSync('npm config get registry').toString().trim();
});
afterAll(() => cleanupProject());

it('should be able to release publishable js library', async () => {
const jsLib = uniq('my-pkg-');
runCLI(
`generate @nx/js:lib ${jsLib} --publishable --importPath=@proj/${jsLib}`
);

let releaseOutput = runCLI(`release --first-release`);
expect(releaseOutput).toContain('Executing pre-version command');
releaseOutput = runCLI(`release --specifier 0.0.2 --yes`);
expect(releaseOutput).toMatchInlineSnapshot(`
NX Executing pre-version command
NX Running release version for project: {project-name}
{project-name} 🔍 Reading data for package "@proj/{project-name}" from dist/{project-name}/package.json
{project-name} 📄 Resolved the current version as 0.0.0 from git tag "v0.0.0".
{project-name} 📄 Using the provided version specifier "0.0.2".
{project-name} ✍️ New version 0.0.2 written to dist/{project-name}/package.json
"name": "@proj/{project-name}",
- "version": "0.0.1",
+ "version": "0.0.2",
"type": "commonjs",
}
+
NX Staging changed files with git
No files to stage. Skipping git add.
NX Generating an entry in CHANGELOG.md for v0.0.2
+ ## 0.0.2 (YYYY-MM-DD)
+
+ This was a version bump only, there were no code changes.
NX Staging changed files with git
NX Committing changes with git
NX Tagging commit with git
NX Running target nx-release-publish for project {project-name}:
- {project-name}
> nx run {project-name}:nx-release-publish
📦 @proj/{project-name}@0.0.2
=== Tarball Contents ===
248B README.md
XXXB package.json
38B src/index.d.ts
208B src/index.js
137B src/index.js.map
48B src/lib/{project-name}.d.ts
213B src/lib/{project-name}.js
210B src/lib/{project-name}.js.map
=== Tarball Details ===
name: @proj/{project-name}
version: 0.0.2
filename: proj-{project-name}-0.0.2.tgz
package size: XXXB
unpacked size: XXX.XXX kb
shasum: {SHASUM}
integrity: XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
total files: 8
Published to ${e2eRegistryUrl} with tag "latest"
NX Successfully ran target nx-release-publish for project {project-name}
`);
});
});
1 change: 0 additions & 1 deletion packages/js/src/generators/library/library.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ describe('lib', () => {
beforeEach(() => {
tree = createTreeWithEmptyWorkspace();
tree.write('/.gitignore', '');
tree.write('/.gitignore', '');
});

it.each`
Expand Down
166 changes: 18 additions & 148 deletions packages/js/src/generators/library/library.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,11 @@ import {
formatFiles,
generateFiles,
GeneratorCallback,
getPackageManagerCommand,
installPackagesTask,
joinPathFragments,
names,
offsetFromRoot,
output,
ProjectConfiguration,
ProjectGraphProjectNode,
readNxJson,
readProjectConfiguration,
runTasksInSerial,
Expand Down Expand Up @@ -69,6 +66,11 @@ import type {
} from './schema';
import { sortPackageJsonFields } from '../../utils/package-json/sort-fields';
import { getImportPath } from '../../utils/get-import-path';
import {
addReleaseConfigForNonTsSolutionConfig,
addReleaseConfigForTsSolutionConfig,
releaseTasks,
} from './utils/add-release-config';

const defaultOutputDirectory = 'dist';

Expand Down Expand Up @@ -115,10 +117,6 @@ export async function libraryGeneratorInternal(
tasks.push(addProjectDependencies(tree, options));
}

if (options.publishable) {
tasks.push(await setupVerdaccio(tree, { ...options, skipFormat: true }));
}

if (options.bundler === 'rollup') {
const { configurationGenerator } = ensurePackage('@nx/rollup', nxVersion);
await configurationGenerator(tree, {
Expand Down Expand Up @@ -249,9 +247,7 @@ export async function libraryGeneratorInternal(
}

if (options.publishable) {
tasks.push(() => {
logNxReleaseDocsInfo();
});
tasks.push(await releaseTasks(tree));
}

// Always run install to link packages.
Expand Down Expand Up @@ -337,32 +333,20 @@ async function configureProject(
}

if (options.publishable) {
if (!options.isUsingTsSolutionConfig) {
const packageRoot = joinPathFragments(
defaultOutputDirectory,
'{projectRoot}'
if (options.isUsingTsSolutionConfig) {
await addReleaseConfigForTsSolutionConfig(
tree,
options.name,
projectConfiguration
);
} else {
await addReleaseConfigForNonTsSolutionConfig(
tree,
options.name,
projectConfiguration,
defaultOutputDirectory
);

projectConfiguration.targets ??= {};
projectConfiguration.targets['nx-release-publish'] = {
options: {
packageRoot,
},
};

projectConfiguration.release = {
version: {
generatorOptions: {
packageRoot,
// using git tags to determine the current version is required here because
// the version in the package root is overridden with every build
currentVersionResolver: 'git-tag',
},
},
};
}

await addProjectToNxReleaseConfig(tree, options, projectConfiguration);
}

if (!options.useProjectJson) {
Expand Down Expand Up @@ -1259,120 +1243,6 @@ function determineEntryFields(
}
}

function projectsConfigMatchesProject(
projectsConfig: string | string[] | undefined,
project: ProjectGraphProjectNode
): boolean {
if (!projectsConfig) {
return false;
}

if (typeof projectsConfig === 'string') {
projectsConfig = [projectsConfig];
}

const graph: Record<string, ProjectGraphProjectNode> = {
[project.name]: project,
};

const matchingProjects = findMatchingProjects(projectsConfig, graph);

return matchingProjects.includes(project.name);
}

async function addProjectToNxReleaseConfig(
tree: Tree,
options: NormalizedLibraryGeneratorOptions,
projectConfiguration: ProjectConfiguration
) {
const nxJson = readNxJson(tree);

const addPreVersionCommand = () => {
const pmc = getPackageManagerCommand();

nxJson.release = {
...nxJson.release,
version: {
preVersionCommand: `${pmc.dlx} nx run-many -t build`,
...nxJson.release?.version,
},
};
};

if (!nxJson.release || (!nxJson.release.projects && !nxJson.release.groups)) {
// skip adding any projects configuration since the new project should be
// automatically included by nx release's default project detection logic
addPreVersionCommand();
writeJson(tree, 'nx.json', nxJson);
return;
}

const project: ProjectGraphProjectNode = {
name: options.name,
type: 'lib' as const,
data: {
root: projectConfiguration.root,
tags: projectConfiguration.tags,
},
};

if (projectsConfigMatchesProject(nxJson.release.projects, project)) {
output.log({
title: `Project already included in existing release configuration`,
});
addPreVersionCommand();
writeJson(tree, 'nx.json', nxJson);
return;
}

if (Array.isArray(nxJson.release.projects)) {
nxJson.release.projects.push(options.name);
addPreVersionCommand();
writeJson(tree, 'nx.json', nxJson);
output.log({
title: `Added project to existing release configuration`,
});
}

if (nxJson.release.groups) {
const allGroups = Object.entries(nxJson.release.groups);

for (const [name, group] of allGroups) {
if (projectsConfigMatchesProject(group.projects, project)) {
addPreVersionCommand();
writeJson(tree, 'nx.json', nxJson);
return `Project already included in existing release configuration for group ${name}`;
}
}

output.warn({
title: `Could not find a release group that includes ${options.name}`,
bodyLines: [
`Ensure that ${options.name} is included in a release group's "projects" list in nx.json so it can be published with "nx release"`,
],
});
addPreVersionCommand();
writeJson(tree, 'nx.json', nxJson);
return;
}

if (typeof nxJson.release.projects === 'string') {
nxJson.release.projects = [nxJson.release.projects, options.name];
addPreVersionCommand();
writeJson(tree, 'nx.json', nxJson);
output.log({
title: `Added project to existing release configuration`,
});
return;
}
}

function logNxReleaseDocsInfo() {
output.log({
title: `📦 To learn how to publish this library, see https://nx.dev/core-features/manage-releases.`,
});
}

function findRootJestPreset(tree: Tree): string | null {
const ext = ['js', 'cjs', 'mjs'].find((ext) =>
tree.exists(`jest.preset.${ext}`)
Expand Down
Loading

0 comments on commit 3d0d1bd

Please sign in to comment.