Skip to content

Commit

Permalink
feat(core): introduce nx import (#26847)
Browse files Browse the repository at this point in the history
## Current Behavior
<!-- This is the behavior we have today -->

Importing other projects/ repositories into an Nx workspace is a natural
part of the Nx adoption story. However, there is no easy built in way of
handling this while maintaining `git` history.

## Expected Behavior
<!-- This is the behavior we should expect with the changes in this PR
-->

`nx import` is a new command which allows teams to merge code from other
repositories into a Nx workspace.

https://asciinema.org/a/oQiA9qOvA2z85AQvVJ5QRVTp1

## Related Issue(s)
<!-- Please link the issue being fixed so it gets closed when this is
merged. -->

Fixes #
  • Loading branch information
FrozenPandaz authored Aug 12, 2024
1 parent 903c460 commit c72ba9b
Show file tree
Hide file tree
Showing 15 changed files with 962 additions and 29 deletions.
89 changes: 89 additions & 0 deletions e2e/nx/src/import.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import {
checkFilesExist,
cleanupProject,
getSelectedPackageManager,
newProject,
runCLI,
updateJson,
updateFile,
e2eCwd,
} from '@nx/e2e/utils';
import { mkdirSync, rmdirSync } from 'fs';
import { execSync } from 'node:child_process';
import { join } from 'path';

describe('Nx Import', () => {
let proj: string;
const tempImportE2ERoot = join(e2eCwd, 'nx-import');
beforeAll(() => {
proj = newProject({
packages: ['@nx/js'],
unsetProjectNameAndRootFormat: false,
});

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

try {
rmdirSync(join(tempImportE2ERoot));
} catch {}
});
afterAll(() => cleanupProject());

it('should be able to import a vite app', () => {
mkdirSync(join(tempImportE2ERoot), { recursive: true });
const tempViteProjectName = 'created-vite-app';
execSync(
`npx create-vite@latest ${tempViteProjectName} --template react-ts`,
{
cwd: tempImportE2ERoot,
}
);
const tempViteProjectPath = join(tempImportE2ERoot, tempViteProjectName);
execSync(`git init`, {
cwd: tempViteProjectPath,
});
execSync(`git add .`, {
cwd: tempViteProjectPath,
});
execSync(`git commit -am "initial commit"`, {
cwd: tempViteProjectPath,
});
execSync(`git checkout -b main`, {
cwd: tempViteProjectPath,
});

const remote = tempViteProjectPath;
const ref = 'main';
const source = '.';
const directory = 'projects/vite-app';

runCLI(
`import ${remote} ${directory} --ref ${ref} --source ${source} --no-interactive`,
{
verbose: true,
}
);

checkFilesExist(
'projects/vite-app/.gitignore',
'projects/vite-app/package.json',
'projects/vite-app/index.html',
'projects/vite-app/vite.config.ts',
'projects/vite-app/src/main.tsx',
'projects/vite-app/src/App.tsx'
);
runCLI(`vite:build created-vite-app`);
});
});
48 changes: 48 additions & 0 deletions packages/nx/src/command-line/import/command-object.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { CommandModule } from 'yargs';
import { linkToNxDevAndExamples } from '../yargs-utils/documentation';
import { withVerbose } from '../yargs-utils/shared-options';
import { handleErrors } from '../../utils/params';

export const yargsImportCommand: CommandModule = {
command: 'import [sourceRemoteUrl] [destination]',
describe: false,
builder: (yargs) =>
linkToNxDevAndExamples(
withVerbose(
yargs
.positional('sourceRemoteUrl', {
type: 'string',
description: 'The remote URL of the source to import',
})
.positional('destination', {
type: 'string',
description:
'The directory in the current workspace to import into',
})
.option('source', {
type: 'string',
description:
'The directory in the source repository to import from',
})
.option('ref', {
type: 'string',
description: 'The branch from the source repository to import',
})
.option('interactive', {
type: 'boolean',
description: 'Interactive mode',
default: true,
})
),
'import'
),
handler: async (args) => {
const exitCode = await handleErrors(
(args.verbose as boolean) ?? process.env.NX_VERBOSE_LOGGING === 'true',
async () => {
return (await import('./import')).importHandler(args as any);
}
);
process.exit(exitCode);
},
};
Loading

0 comments on commit c72ba9b

Please sign in to comment.