Bun uses yarn lock for it's binary file. Running the binary will produce the content of a yarn lock file (v1) Other option is to use the -y command on add and install. This will create a yarn lock file and then createLockFile can just modify the yarn.lock file instead? This is the PR made from #19113 and pushed due to #22402 being closed. PS Bun feels more stable since the PR was first created! This PR will resolve #22283 and start of #21075
582 lines
15 KiB
TypeScript
582 lines
15 KiB
TypeScript
import {
|
|
checkFilesDoNotExist,
|
|
checkFilesExist,
|
|
cleanupProject,
|
|
e2eCwd,
|
|
expectCodeIsFormatted,
|
|
expectNoAngularDevkit,
|
|
expectNoTsJestInJestConfig,
|
|
getSelectedPackageManager,
|
|
packageManagerLockFile,
|
|
readJson,
|
|
runCommand,
|
|
runCreateWorkspace,
|
|
uniq,
|
|
} from '@nx/e2e/utils';
|
|
import { readFileSync } from 'fs';
|
|
import { existsSync, mkdirSync, rmSync } from 'fs-extra';
|
|
|
|
describe('create-nx-workspace', () => {
|
|
const packageManager = getSelectedPackageManager() || 'pnpm';
|
|
|
|
afterEach(() => cleanupProject());
|
|
|
|
it('should create a workspace with a single angular app at the root without routing', () => {
|
|
const wsName = uniq('angular');
|
|
|
|
runCreateWorkspace(wsName, {
|
|
preset: 'angular-standalone',
|
|
appName: wsName,
|
|
style: 'css',
|
|
packageManager,
|
|
standaloneApi: false,
|
|
routing: false,
|
|
e2eTestRunner: 'none',
|
|
bundler: 'webpack',
|
|
ssr: false,
|
|
});
|
|
|
|
checkFilesExist('package.json');
|
|
checkFilesExist('project.json');
|
|
checkFilesExist('src/app/app.module.ts');
|
|
checkFilesDoNotExist('src/app/app.routes.ts');
|
|
expectCodeIsFormatted();
|
|
});
|
|
|
|
it('should create a workspace with a single angular app at the root using standalone APIs', () => {
|
|
const wsName = uniq('angular');
|
|
|
|
runCreateWorkspace(wsName, {
|
|
preset: 'angular-standalone',
|
|
appName: wsName,
|
|
style: 'css',
|
|
packageManager,
|
|
standaloneApi: true,
|
|
routing: true,
|
|
e2eTestRunner: 'none',
|
|
bundler: 'webpack',
|
|
ssr: false,
|
|
});
|
|
|
|
checkFilesExist('package.json');
|
|
checkFilesExist('project.json');
|
|
checkFilesExist('src/app/app.routes.ts');
|
|
checkFilesDoNotExist('src/app/app.module.ts');
|
|
expectCodeIsFormatted();
|
|
});
|
|
|
|
it('should create a workspace with a single react app with vite at the root', () => {
|
|
const wsName = uniq('react');
|
|
|
|
runCreateWorkspace(wsName, {
|
|
preset: 'react-standalone',
|
|
appName: wsName,
|
|
style: 'css',
|
|
packageManager,
|
|
bundler: 'vite',
|
|
e2eTestRunner: 'none',
|
|
});
|
|
|
|
checkFilesExist('package.json');
|
|
checkFilesExist('project.json');
|
|
checkFilesExist('vite.config.ts');
|
|
checkFilesDoNotExist('tsconfig.base.json');
|
|
expectCodeIsFormatted();
|
|
});
|
|
|
|
it('should create a workspace with a single react app with webpack and playwright at the root', () => {
|
|
const wsName = uniq('react');
|
|
|
|
runCreateWorkspace(wsName, {
|
|
preset: 'react-standalone',
|
|
appName: wsName,
|
|
style: 'css',
|
|
packageManager,
|
|
bundler: 'webpack',
|
|
e2eTestRunner: 'playwright',
|
|
});
|
|
|
|
checkFilesExist('package.json');
|
|
checkFilesExist('project.json');
|
|
checkFilesExist('webpack.config.js');
|
|
checkFilesDoNotExist('tsconfig.base.json');
|
|
expectCodeIsFormatted();
|
|
});
|
|
|
|
it('should be able to create an empty workspace built for apps', () => {
|
|
const wsName = uniq('apps');
|
|
runCreateWorkspace(wsName, {
|
|
preset: 'apps',
|
|
packageManager,
|
|
});
|
|
|
|
checkFilesExist('package.json', packageManagerLockFile[packageManager]);
|
|
|
|
expectNoAngularDevkit();
|
|
});
|
|
|
|
it('should be able to create an empty workspace with npm capabilities', () => {
|
|
const wsName = uniq('npm');
|
|
runCreateWorkspace(wsName, {
|
|
preset: 'npm',
|
|
packageManager,
|
|
});
|
|
|
|
expectNoAngularDevkit();
|
|
checkFilesDoNotExist('tsconfig.base.json');
|
|
});
|
|
|
|
it('should be able to create an empty workspace with ts/js capabilities', () => {
|
|
const wsName = uniq('ts');
|
|
runCreateWorkspace(wsName, {
|
|
preset: 'ts',
|
|
packageManager,
|
|
});
|
|
|
|
expectNoAngularDevkit();
|
|
expectCodeIsFormatted();
|
|
});
|
|
|
|
it('should be able to create an angular workspace', () => {
|
|
const wsName = uniq('angular');
|
|
const appName = uniq('app');
|
|
runCreateWorkspace(wsName, {
|
|
preset: 'angular-monorepo',
|
|
style: 'css',
|
|
appName,
|
|
packageManager,
|
|
standaloneApi: false,
|
|
routing: true,
|
|
e2eTestRunner: 'none',
|
|
bundler: 'webpack',
|
|
ssr: false,
|
|
});
|
|
expectCodeIsFormatted();
|
|
});
|
|
|
|
it('should fail correctly when preset errors', () => {
|
|
// Using Angular Preset as the example here to test
|
|
// It will error when prefix is not valid
|
|
const wsName = uniq('angular-1-test');
|
|
const appName = uniq('app');
|
|
expect(() =>
|
|
runCreateWorkspace(wsName, {
|
|
preset: 'angular-monorepo',
|
|
style: 'css',
|
|
appName,
|
|
packageManager,
|
|
standaloneApi: false,
|
|
routing: false,
|
|
e2eTestRunner: 'none',
|
|
bundler: 'webpack',
|
|
ssr: false,
|
|
prefix: '1-one',
|
|
})
|
|
).toThrow();
|
|
});
|
|
|
|
it('should be able to create a react workspace with webpack', () => {
|
|
const wsName = uniq('react');
|
|
const appName = uniq('app');
|
|
|
|
runCreateWorkspace(wsName, {
|
|
preset: 'react-monorepo',
|
|
style: 'css',
|
|
appName,
|
|
packageManager,
|
|
bundler: 'webpack',
|
|
e2eTestRunner: 'none',
|
|
});
|
|
|
|
expectNoAngularDevkit();
|
|
expectNoTsJestInJestConfig(appName);
|
|
const packageJson = readJson('package.json');
|
|
expect(packageJson.devDependencies['@nx/webpack']).toBeDefined();
|
|
expectCodeIsFormatted();
|
|
});
|
|
|
|
it('should be able to create a react workspace with vite', () => {
|
|
const wsName = uniq('react');
|
|
const appName = uniq('app');
|
|
|
|
runCreateWorkspace(wsName, {
|
|
preset: 'react-monorepo',
|
|
style: 'css',
|
|
appName,
|
|
packageManager,
|
|
bundler: 'vite',
|
|
e2eTestRunner: 'none',
|
|
});
|
|
|
|
expectNoAngularDevkit();
|
|
const packageJson = readJson('package.json');
|
|
expect(packageJson.devDependencies['@nx/webpack']).not.toBeDefined();
|
|
expect(packageJson.devDependencies['@nx/vite']).toBeDefined();
|
|
expectCodeIsFormatted();
|
|
});
|
|
|
|
it('should be able to create an next workspace', () => {
|
|
const wsName = uniq('next');
|
|
const appName = uniq('app');
|
|
runCreateWorkspace(wsName, {
|
|
preset: 'next',
|
|
style: 'css',
|
|
appName,
|
|
nextAppDir: false,
|
|
nextSrcDir: true,
|
|
packageManager,
|
|
e2eTestRunner: 'none',
|
|
});
|
|
|
|
checkFilesExist(`apps/${appName}/src/pages/index.tsx`);
|
|
|
|
expectNoAngularDevkit();
|
|
expectCodeIsFormatted();
|
|
});
|
|
|
|
it('should be able to create a nextjs standalone workspace using app router', () => {
|
|
const wsName = uniq('next');
|
|
const appName = uniq('app');
|
|
runCreateWorkspace(wsName, {
|
|
preset: 'nextjs-standalone',
|
|
style: 'css',
|
|
nextAppDir: true,
|
|
nextSrcDir: true,
|
|
appName,
|
|
packageManager,
|
|
e2eTestRunner: 'none',
|
|
});
|
|
|
|
checkFilesExist('src/app/page.tsx');
|
|
|
|
expectNoAngularDevkit();
|
|
expectCodeIsFormatted();
|
|
});
|
|
|
|
it('should be able to create a nextjs standalone workspace using pages router', () => {
|
|
const wsName = uniq('next');
|
|
const appName = uniq('app');
|
|
runCreateWorkspace(wsName, {
|
|
preset: 'nextjs-standalone',
|
|
style: 'css',
|
|
nextAppDir: false,
|
|
nextSrcDir: true,
|
|
appName,
|
|
packageManager,
|
|
e2eTestRunner: 'none',
|
|
});
|
|
|
|
checkFilesExist('src/pages/index.tsx');
|
|
|
|
expectNoAngularDevkit();
|
|
expectCodeIsFormatted();
|
|
});
|
|
|
|
it('should be able to create an web-components workspace', () => {
|
|
const wsName = uniq('web-components');
|
|
const appName = uniq('app');
|
|
runCreateWorkspace(wsName, {
|
|
preset: 'web-components',
|
|
style: 'css',
|
|
appName,
|
|
packageManager,
|
|
});
|
|
|
|
expectNoAngularDevkit();
|
|
expectCodeIsFormatted();
|
|
});
|
|
|
|
it('should be able to create an express workspace', () => {
|
|
const wsName = uniq('express');
|
|
const appName = uniq('app');
|
|
runCreateWorkspace(wsName, {
|
|
preset: 'express',
|
|
docker: false,
|
|
appName,
|
|
packageManager,
|
|
});
|
|
|
|
expectNoAngularDevkit();
|
|
expectCodeIsFormatted();
|
|
});
|
|
|
|
it('should be able to create react-native workspace', () => {
|
|
const wsName = uniq('react-native');
|
|
const appName = uniq('app');
|
|
runCreateWorkspace(wsName, {
|
|
preset: 'react-native',
|
|
appName,
|
|
packageManager: 'npm',
|
|
});
|
|
|
|
expectNoAngularDevkit();
|
|
expectCodeIsFormatted();
|
|
});
|
|
|
|
it('should be able to create an expo workspace', () => {
|
|
const wsName = uniq('expo');
|
|
const appName = uniq('app');
|
|
runCreateWorkspace(wsName, {
|
|
preset: 'expo',
|
|
appName,
|
|
packageManager: 'npm',
|
|
});
|
|
|
|
expectNoAngularDevkit();
|
|
expectCodeIsFormatted();
|
|
});
|
|
|
|
it('should be able to create a workspace with a custom base branch and HEAD', () => {
|
|
const wsName = uniq('branch');
|
|
runCreateWorkspace(wsName, {
|
|
preset: 'apps',
|
|
base: 'main',
|
|
packageManager,
|
|
});
|
|
});
|
|
|
|
it('should be able to create a workspace with custom commit information', () => {
|
|
const wsName = uniq('branch');
|
|
runCreateWorkspace(wsName, {
|
|
preset: 'apps',
|
|
extraArgs:
|
|
'--commit.name="John Doe" --commit.email="myemail@test.com" --commit.message="Custom commit message!"',
|
|
packageManager,
|
|
});
|
|
});
|
|
|
|
it('should be able to create a nest workspace', () => {
|
|
const wsName = uniq('nest');
|
|
const appName = uniq('app');
|
|
runCreateWorkspace(wsName, {
|
|
preset: 'nest',
|
|
docker: false,
|
|
appName,
|
|
packageManager,
|
|
});
|
|
expectCodeIsFormatted();
|
|
});
|
|
|
|
it('should respect package manager preference', () => {
|
|
const wsName = uniq('pm');
|
|
|
|
process.env.YARN_REGISTRY = `http://localhost:4872`;
|
|
process.env.SELECTED_PM = 'npm';
|
|
|
|
runCreateWorkspace(wsName, {
|
|
preset: 'apps',
|
|
packageManager: 'npm',
|
|
});
|
|
|
|
checkFilesDoNotExist('yarn.lock');
|
|
checkFilesExist('package-lock.json');
|
|
process.env.SELECTED_PM = packageManager;
|
|
});
|
|
|
|
describe('Use detected package manager', () => {
|
|
function setupProject(envPm: 'npm' | 'yarn' | 'pnpm' | 'bun') {
|
|
process.env.SELECTED_PM = envPm;
|
|
runCreateWorkspace(uniq('pm'), {
|
|
preset: 'apps',
|
|
packageManager: envPm,
|
|
useDetectedPm: true,
|
|
});
|
|
}
|
|
|
|
if (packageManager === 'npm') {
|
|
it('should use npm when invoked with npx', () => {
|
|
setupProject('npm');
|
|
checkFilesExist(packageManagerLockFile['npm']);
|
|
checkFilesDoNotExist(
|
|
packageManagerLockFile['yarn'],
|
|
packageManagerLockFile['pnpm'],
|
|
packageManagerLockFile['bun']
|
|
);
|
|
process.env.SELECTED_PM = packageManager;
|
|
}, 90000);
|
|
}
|
|
|
|
if (packageManager === 'pnpm') {
|
|
it('should use pnpm when invoked with pnpx', () => {
|
|
setupProject('pnpm');
|
|
checkFilesExist(packageManagerLockFile['pnpm']);
|
|
checkFilesDoNotExist(
|
|
packageManagerLockFile['yarn'],
|
|
packageManagerLockFile['npm'],
|
|
packageManagerLockFile['bun']
|
|
);
|
|
process.env.SELECTED_PM = packageManager;
|
|
}, 90000);
|
|
}
|
|
|
|
if (packageManager === 'bun') {
|
|
it('should use bun when invoked with bunx', () => {
|
|
setupProject('bun');
|
|
checkFilesExist(packageManagerLockFile['bun']);
|
|
checkFilesDoNotExist(
|
|
packageManagerLockFile['yarn'],
|
|
packageManagerLockFile['npm'],
|
|
packageManagerLockFile['pnpm']
|
|
);
|
|
process.env.SELECTED_PM = packageManager;
|
|
}, 90000);
|
|
}
|
|
|
|
// skipping due to packageManagerCommand for createWorkspace not using yarn create nx-workspace
|
|
if (packageManager === 'yarn') {
|
|
xit('should use yarn when invoked with yarn create', () => {
|
|
setupProject('yarn');
|
|
checkFilesExist(packageManagerLockFile['yarn']);
|
|
checkFilesDoNotExist(
|
|
packageManagerLockFile['pnpm'],
|
|
packageManagerLockFile['npm'],
|
|
packageManagerLockFile['bun']
|
|
);
|
|
process.env.SELECTED_PM = packageManager;
|
|
}, 90000);
|
|
}
|
|
});
|
|
|
|
it('should create a workspace with a single vue app at the root', () => {
|
|
const wsName = uniq('vue');
|
|
|
|
runCreateWorkspace(wsName, {
|
|
preset: 'vue-standalone',
|
|
appName: wsName,
|
|
style: 'css',
|
|
packageManager,
|
|
e2eTestRunner: 'none',
|
|
});
|
|
|
|
checkFilesExist('package.json');
|
|
checkFilesExist('project.json');
|
|
checkFilesExist('index.html');
|
|
checkFilesExist('src/main.ts');
|
|
checkFilesExist('src/app/App.vue');
|
|
expectCodeIsFormatted();
|
|
});
|
|
|
|
it('should be able to create a vue monorepo', () => {
|
|
const wsName = uniq('vue');
|
|
const appName = uniq('app');
|
|
runCreateWorkspace(wsName, {
|
|
preset: 'vue-monorepo',
|
|
appName,
|
|
style: 'css',
|
|
packageManager,
|
|
e2eTestRunner: 'none',
|
|
});
|
|
expectCodeIsFormatted();
|
|
});
|
|
|
|
it('should create a workspace with a single nuxt app at the root', () => {
|
|
const wsName = uniq('nuxt');
|
|
|
|
runCreateWorkspace(wsName, {
|
|
preset: 'nuxt-standalone',
|
|
appName: wsName,
|
|
style: 'css',
|
|
packageManager,
|
|
e2eTestRunner: 'none',
|
|
});
|
|
|
|
checkFilesExist('package.json');
|
|
checkFilesExist('project.json');
|
|
checkFilesExist('nuxt.config.ts');
|
|
checkFilesExist('src/app.vue');
|
|
checkFilesExist('src/pages/index.vue');
|
|
expectCodeIsFormatted();
|
|
});
|
|
|
|
it('should be able to create a nuxt monorepo', () => {
|
|
const wsName = uniq('nuxt');
|
|
const appName = uniq('app');
|
|
runCreateWorkspace(wsName, {
|
|
preset: 'nuxt',
|
|
appName,
|
|
style: 'css',
|
|
packageManager,
|
|
e2eTestRunner: 'none',
|
|
});
|
|
expectCodeIsFormatted();
|
|
});
|
|
});
|
|
|
|
describe('create-nx-workspace parent folder', () => {
|
|
const tmpDir = `${e2eCwd}/${uniq('with space')}`;
|
|
const wsName = uniq('parent');
|
|
const packageManager = getSelectedPackageManager() || 'pnpm';
|
|
|
|
afterEach(() => cleanupProject({ cwd: `${tmpDir}/${wsName}` }));
|
|
|
|
it('should handle spaces in workspace path', () => {
|
|
mkdirSync(tmpDir, { recursive: true });
|
|
|
|
runCreateWorkspace(wsName, {
|
|
preset: 'apps',
|
|
packageManager,
|
|
cwd: tmpDir,
|
|
});
|
|
|
|
expect(existsSync(`${tmpDir}/${wsName}/package.json`)).toBeTruthy();
|
|
});
|
|
});
|
|
|
|
describe('create-nx-workspace yarn berry', () => {
|
|
const tmpDir = `${e2eCwd}/${uniq('yarn-berry')}`;
|
|
let wsName: string;
|
|
let yarnVersion: string;
|
|
|
|
beforeAll(() => {
|
|
mkdirSync(tmpDir, { recursive: true });
|
|
runCommand('corepack prepare yarn@3.6.1 --activate', { cwd: tmpDir });
|
|
runCommand('yarn set version 3.6.1', { cwd: tmpDir });
|
|
yarnVersion = runCommand('yarn --version', { cwd: tmpDir }).trim();
|
|
// previous command creates a package.json file which we don't want
|
|
rmSync(`${tmpDir}/package.json`);
|
|
process.env.YARN_ENABLE_IMMUTABLE_INSTALLS = 'false';
|
|
});
|
|
|
|
afterEach(() => cleanupProject({ cwd: `${tmpDir}/${wsName}` }));
|
|
|
|
it('should create a workspace with yarn berry', () => {
|
|
wsName = uniq('apps');
|
|
|
|
runCreateWorkspace(wsName, {
|
|
preset: 'apps',
|
|
packageManager: 'yarn',
|
|
cwd: tmpDir,
|
|
});
|
|
|
|
expect(existsSync(`${tmpDir}/${wsName}/.yarnrc.yml`)).toBeTruthy();
|
|
expect(
|
|
readFileSync(`${tmpDir}/${wsName}/.yarnrc.yml`, { encoding: 'utf-8' })
|
|
).toMatchInlineSnapshot(`
|
|
"nodeLinker: node-modules
|
|
|
|
yarnPath: .yarn/releases/yarn-${yarnVersion}.cjs
|
|
"
|
|
`);
|
|
});
|
|
|
|
it('should create a js workspace with yarn berry', () => {
|
|
wsName = uniq('ts');
|
|
|
|
runCreateWorkspace(wsName, {
|
|
preset: 'ts',
|
|
packageManager: 'yarn',
|
|
cwd: tmpDir,
|
|
});
|
|
|
|
expect(existsSync(`${tmpDir}/${wsName}/.yarnrc.yml`)).toBeTruthy();
|
|
expect(
|
|
readFileSync(`${tmpDir}/${wsName}/.yarnrc.yml`, { encoding: 'utf-8' })
|
|
).toMatchInlineSnapshot(`
|
|
"nodeLinker: node-modules
|
|
|
|
yarnPath: .yarn/releases/yarn-${yarnVersion}.cjs
|
|
"
|
|
`);
|
|
});
|
|
});
|