Skip to content

Commit

Permalink
fix(core): add workspaces path if package path is not included
Browse files Browse the repository at this point in the history
  • Loading branch information
xiongemi committed Nov 14, 2024
1 parent 3c6c387 commit 49bef64
Show file tree
Hide file tree
Showing 7 changed files with 572 additions and 270 deletions.
39 changes: 20 additions & 19 deletions e2e/nx/src/import.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import {
newProject,
runCLI,
runCommand,
updateJson,
updateFile,
e2eCwd,
readJson,
readFile,
} from '@nx/e2e/utils';
import { writeFileSync, mkdirSync, rmdirSync } from 'fs';
import { execSync } from 'node:child_process';
Expand All @@ -21,24 +21,13 @@ describe('Nx Import', () => {
packages: ['@nx/js'],
});

if (getSelectedPackageManager() === 'pnpm') {
updateFile(
'pnpm-workspace.yaml',
`packages:
- 'projects/*'
`
);
} else {
updateJson('package.json', (json) => {
json.workspaces = ['projects/*'];
return json;
});
}

try {
rmdirSync(join(tempImportE2ERoot));
} catch {}
});

beforeEach(() => {
// Clean up the temp import directory before each test to not have any uncommited changes
runCommand(`git add .`);
runCommand(`git commit -am "Update" --allow-empty`);
});
Expand Down Expand Up @@ -85,6 +74,14 @@ describe('Nx Import', () => {
}
);

if (getSelectedPackageManager() === 'pnpm') {
const workspaceYaml = readFile('pnpm-workspace.yaml');
expect(workspaceYaml).toMatch(/(projects\/vite-app)/);
} else {
const packageJson = readJson('package.json');
expect(packageJson.workspaces).toContain('projects/vite-app');
}

checkFilesExist(
'projects/vite-app/.gitignore',
'projects/vite-app/package.json',
Expand All @@ -110,9 +107,13 @@ describe('Nx Import', () => {
execSync(`git commit -am "initial commit"`, {
cwd: repoPath,
});
execSync(`git checkout -b main`, {
cwd: repoPath,
});
try {
execSync(`git checkout -b main`, {
cwd: repoPath,
});
} catch {
// This fails if git is already configured to have `main` branch, but that's OK
}
mkdirSync(join(repoPath, 'packages/a'), { recursive: true });
writeFileSync(join(repoPath, 'packages/a/README.md'), `# A`);
execSync(`git add .`, {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,6 @@
"@types/jasmine": "~2.8.6",
"@types/jasminewd2": "~2.0.3",
"@types/jest": "29.5.12",
"@types/js-yaml": "^4.0.5",
"@types/marked": "^2.0.0",
"@types/node": "20.16.10",
"@types/npm-package-arg": "6.1.1",
Expand Down Expand Up @@ -314,6 +313,7 @@
"webpack-sources": "^3.2.3",
"webpack-subresource-integrity": "^5.1.0",
"xstate": "4.34.0",
"yaml": "^2.6.0",
"yargs": "17.6.2",
"yargs-parser": "21.1.1"
},
Expand Down
90 changes: 42 additions & 48 deletions packages/nx/src/command-line/import/import.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { dirname, isAbsolute, join, relative, resolve } from 'path';
import { isAbsolute, join, relative, resolve } from 'path';
import { minimatch } from 'minimatch';
import { existsSync, promises as fsp } from 'node:fs';
import * as chalk from 'chalk';
import { load as yamlLoad } from '@zkochan/js-yaml';
import { cloneFromUpstream, GitRepository } from '../../utils/git-utils';
import { stat, mkdir, rm } from 'node:fs/promises';
import { tmpdir } from 'tmp';
Expand All @@ -13,8 +12,10 @@ import { detectPlugins, installPlugins } from '../init/init-v2';
import { readNxJson } from '../../config/nx-json';
import { workspaceRoot } from '../../utils/workspace-root';
import {
addPackagePathToWorkspaces,
detectPackageManager,
getPackageManagerCommand,
getPackageWorkspaces,
isWorkspacesEnabled,
PackageManager,
PackageManagerCommands,
Expand All @@ -28,7 +29,6 @@ import {
getPackagesInPackageManagerWorkspace,
needsInstall,
} from './utils/needs-install';
import { readPackageJson } from '../../project-graph/file-utils';

const importRemoteName = '__tmp_nx_import__';

Expand Down Expand Up @@ -280,6 +280,12 @@ export async function importHandler(options: ImportOptions) {
});
}

await handleMissingWorkspacesEntry(
packageManager,
relativeDestination,
destinationGitClient
);

// If install fails, we should continue since the errors could be resolved later.
let installFailed = false;
if (plugins.length > 0) {
Expand Down Expand Up @@ -325,8 +331,6 @@ export async function importHandler(options: ImportOptions) {
});
}

await warnOnMissingWorkspacesEntry(packageManager, pmc, relativeDestination);

if (source != destination) {
output.warn({
title: `Check configuration files`,
Expand Down Expand Up @@ -391,76 +395,66 @@ async function createTemporaryRemote(
await destinationGitClient.fetch(remoteName);
}

// If the user imports a project that isn't in NPM/Yarn/PNPM workspaces, then its dependencies
// will not be installed. We should warn users and provide instructions on how to fix this.
async function warnOnMissingWorkspacesEntry(
/**
* If the user imports a project that isn't in the workspaces entry, we should add that path to the workspaces entry.
*/
async function handleMissingWorkspacesEntry(
pm: PackageManager,
pmc: PackageManagerCommands,
pkgPath: string
pkgPath: string,
destinationGitClient: GitRepository
) {
let added = false;
try {
added = addPackagePathToWorkspaces(pm, workspaceRoot, pkgPath);
} catch {
return;
}
if (!added) {
return;
}
await destinationGitClient.amendCommit();
if (!isWorkspacesEnabled(pm, workspaceRoot)) {
output.warn({
title: `Missing workspaces in package.json`,
bodyLines:
pm === 'npm'
? [
`We recommend enabling NPM workspaces to install dependencies for the imported project.`,
`Add \`"workspaces": ["${pkgPath}"]\` to package.json and run "${pmc.install}".`,
`Added \`"workspaces": ["${pkgPath}"]\` to package.json.`,
`See: https://docs.npmjs.com/cli/using-npm/workspaces`,
]
: pm === 'yarn'
? [
`We recommend enabling Yarn workspaces to install dependencies for the imported project.`,
`Add \`"workspaces": ["${pkgPath}"]\` to package.json and run "${pmc.install}".`,
`Added \`"workspaces": ["${pkgPath}"]\` to package.json.`,
`See: https://yarnpkg.com/features/workspaces`,
]
: pm === 'bun'
? [
`We recommend enabling Bun workspaces to install dependencies for the imported project.`,
`Add \`"workspaces": ["${pkgPath}"]\` to package.json and run "${pmc.install}".`,
`Added \`"workspaces": ["${pkgPath}"]\` to package.json.`,
`See: https://bun.sh/docs/install/workspaces`,
]
: [
`We recommend enabling PNPM workspaces to install dependencies for the imported project.`,
`Add the following entry to to pnpm-workspace.yaml and run "${pmc.install}":`,
`Added the following entry to to pnpm-workspace.yaml:`,
chalk.bold(`packages:\n - '${pkgPath}'`),
`See: https://pnpm.io/workspaces`,
],
});
} else {
// Check if the new package is included in existing workspaces entries. If not, warn the user.
let workspaces: string[] | null = null;

if (pm === 'npm' || pm === 'yarn' || pm === 'bun') {
const packageJson = readPackageJson();
workspaces = packageJson.workspaces;
} else if (pm === 'pnpm') {
const yamlPath = join(workspaceRoot, 'pnpm-workspace.yaml');
if (existsSync(yamlPath)) {
const yamlContent = await fsp.readFile(yamlPath, 'utf-8');
const yaml = yamlLoad(yamlContent);
workspaces = yaml.packages;
}
}

if (workspaces) {
const isPkgIncluded = workspaces.some((w) => minimatch(pkgPath, w));
if (!isPkgIncluded) {
const pkgsDir = dirname(pkgPath);
output.warn({
title: `Project missing in workspaces`,
bodyLines:
pm === 'npm' || pm === 'yarn' || pm === 'bun'
? [
`The imported project (${pkgPath}) is missing the "workspaces" field in package.json.`,
`Add "${pkgsDir}/*" to workspaces run "${pmc.install}".`,
]
: [
`The imported project (${pkgPath}) is missing the "packages" field in pnpm-workspaces.yaml.`,
`Add "${pkgsDir}/*" to packages run "${pmc.install}".`,
],
});
}
}
output.warn({
title: `Project missing in workspaces`,
bodyLines:
pm === 'npm' || pm === 'yarn' || pm === 'bun'
? [
`The imported project (${pkgPath}) is missing the "workspaces" field in package.json.`,
`Added "${pkgPath}" to workspaces.`,
]
: [
`The imported project (${pkgPath}) is missing the "packages" field in pnpm-workspaces.yaml.`,
`Added "${pkgPath}" to packages.`,
],
});
}
}
4 changes: 2 additions & 2 deletions packages/nx/src/project-graph/file-utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,9 +168,9 @@ export function defaultFileRead(filePath: string): string | null {
return readFileSync(join(workspaceRoot, filePath), 'utf-8');
}

export function readPackageJson(): any {
export function readPackageJson(root: string = workspaceRoot): any {
try {
return readJsonFile(`${workspaceRoot}/package.json`);
return readJsonFile(`${root}/package.json`);
} catch {
return {}; // if package.json doesn't exist
}
Expand Down
Loading

0 comments on commit 49bef64

Please sign in to comment.