diff --git a/.nx/workflows/dynamic-changesets.yaml b/.nx/workflows/dynamic-changesets.yaml
index 8580c122f8c9c..3d14e9ab1acd2 100644
--- a/.nx/workflows/dynamic-changesets.yaml
+++ b/.nx/workflows/dynamic-changesets.yaml
@@ -26,16 +26,7 @@ assignment-rules:
- e2e-web
- e2e-eslint
targets:
- - e2e-ci**react-package**
- - e2e-ci**react.test**
- - e2e-ci**react-router-ts-solution**
- - e2e-ci**next-e2e-and-snapshots**
- - e2e-ci**next-generation**
- - e2e-ci**next-ts-solutions**
- - e2e-ci**next-webpack**
- - e2e-ci**web**
- - e2e-ci**remix-ts-solution**
- - e2e-ci**linter**
+ - e2e-ci**
run-on:
- agent: linux-large
parallelism: 1
diff --git a/e2e/react/src/module-federation/independent-deployability-different-lib-versions.test.ts b/e2e/react/src/module-federation/independent-deployability-different-lib-versions.test.ts
new file mode 100644
index 0000000000000..69ce3beb6d892
--- /dev/null
+++ b/e2e/react/src/module-federation/independent-deployability-different-lib-versions.test.ts
@@ -0,0 +1,178 @@
+import {
+ getAvailablePort,
+ killProcessAndPorts,
+ runCommandUntil,
+ runE2ETests,
+ uniq,
+ updateFile,
+ updateJson,
+} from '@nx/e2e-utils';
+import { stripIndents } from 'nx/src/utils/strip-indents';
+import { readPort, runCLI } from './utils';
+import {
+ setupIndependentDeployabilityTest,
+ cleanupIndependentDeployabilityTest,
+} from './independent-deployability-setup';
+
+describe('Independent Deployability', () => {
+ let proj: string;
+ beforeAll(() => {
+ proj = setupIndependentDeployabilityTest();
+ });
+
+ afterAll(() => {
+ cleanupIndependentDeployabilityTest();
+ });
+
+ it('should support different versions workspace libs for host and remote', async () => {
+ const shell = uniq('shell');
+ const remote = uniq('remote');
+ const lib = uniq('lib');
+
+ const shellPort = await getAvailablePort();
+
+ runCLI(
+ `generate @nx/react:host ${shell} --remotes=${remote} --devServerPort=${shellPort} --bundler=webpack --e2eTestRunner=cypress --no-interactive --skipFormat`
+ );
+
+ runCLI(
+ `generate @nx/js:lib ${lib} --importPath=@acme/${lib} --publishable=true --no-interactive --skipFormat`
+ );
+
+ const remotePort = readPort(remote);
+
+ updateFile(
+ `${lib}/src/lib/${lib}.ts`,
+ stripIndents`
+ export const version = '0.0.1';
+ `
+ );
+
+ updateJson(`${lib}/package.json`, (json) => {
+ return {
+ ...json,
+ version: '0.0.1',
+ };
+ });
+
+ // Update host to use the lib
+ updateFile(
+ `${shell}/src/app/app.tsx`,
+ `
+ import * as React from 'react';
+
+ import NxWelcome from './nx-welcome';
+ import { version } from '@acme/${lib}';
+ import { Link, Route, Routes } from 'react-router-dom';
+
+ const About = React.lazy(() => import('${remote}/Module'));
+
+ export function App() {
+ return (
+
+
+ Lib version: { version }
+
+
+ -
+ Home
+
+
+ -
+ About
+
+
+
+ } />
+
+ } />
+
+
+ );
+ }
+
+ export default App;`
+ );
+
+ // Update remote to use the lib
+ updateFile(
+ `${remote}/src/app/app.tsx`,
+ `// eslint-disable-next-line @typescript-eslint/no-unused-vars
+
+ import styles from './app.module.css';
+ import { version } from '@acme/${lib}';
+
+ import NxWelcome from './nx-welcome';
+
+ export function App() {
+ return (
+
+
+ Lib version: { version }
+
+
+ );
+ }
+
+ export default App;`
+ );
+
+ // update remote e2e test to check the version
+ updateFile(
+ `${remote}-e2e/src/e2e/app.cy.ts`,
+ `describe('${remote}', () => {
+ beforeEach(() => cy.visit('/'));
+
+ it('should check the lib version', () => {
+ cy.get('div.remote').contains('Lib version: 0.0.1');
+ });
+ });
+ `
+ );
+
+ // update shell e2e test to check the version
+ updateFile(
+ `${shell}-e2e/src/e2e/app.cy.ts`,
+ `
+ describe('${shell}', () => {
+ beforeEach(() => cy.visit('/'));
+
+ it('should check the lib version', () => {
+ cy.get('div.home').contains('Lib version: 0.0.1');
+ });
+ });
+ `
+ );
+
+ if (runE2ETests()) {
+ // test remote e2e
+ const remoteE2eResults = await runCommandUntil(
+ `e2e ${remote}-e2e --no-watch --verbose`,
+ (output) => output.includes('All specs passed!')
+ );
+ await killProcessAndPorts(remoteE2eResults.pid, remotePort);
+
+ // test shell e2e
+ // serve remote first
+ const remoteProcess = await runCommandUntil(
+ `serve ${remote} --no-watch --verbose`,
+ (output) => {
+ return output.includes(
+ `Web Development Server is listening at http://localhost:${remotePort}/`
+ );
+ }
+ );
+ await killProcessAndPorts(remoteProcess.pid, remotePort);
+ const shellE2eResults = await runCommandUntil(
+ `e2e ${shell}-e2e --no-watch --verbose`,
+ (output) => output.includes('All specs passed!')
+ );
+ await killProcessAndPorts(
+ shellE2eResults.pid,
+ shellPort,
+ shellPort + 1,
+ remotePort
+ );
+ }
+ }, 500_000);
+});
diff --git a/e2e/react/src/module-federation/independent-deployability-library-type-var.test.ts b/e2e/react/src/module-federation/independent-deployability-library-type-var.test.ts
new file mode 100644
index 0000000000000..b179b36904b06
--- /dev/null
+++ b/e2e/react/src/module-federation/independent-deployability-library-type-var.test.ts
@@ -0,0 +1,153 @@
+import {
+ getAvailablePort,
+ killProcessAndPorts,
+ runCommandUntil,
+ runE2ETests,
+ uniq,
+ updateFile,
+} from '@nx/e2e-utils';
+import { stripIndents } from 'nx/src/utils/strip-indents';
+import { readPort, runCLI } from './utils';
+import {
+ setupIndependentDeployabilityTest,
+ cleanupIndependentDeployabilityTest,
+} from './independent-deployability-setup';
+
+describe('Independent Deployability', () => {
+ let proj: string;
+ beforeAll(() => {
+ proj = setupIndependentDeployabilityTest();
+ });
+
+ afterAll(() => {
+ cleanupIndependentDeployabilityTest();
+ });
+
+ it('should support host and remote with library type var', async () => {
+ const shell = uniq('shell');
+ const remote = uniq('remote');
+ const shellPort = await getAvailablePort();
+
+ runCLI(
+ `generate @nx/react:host ${shell} --devServerPort=${shellPort} --remotes=${remote} --bundler=webpack --e2eTestRunner=cypress --no-interactive --skipFormat`
+ );
+
+ const remotePort = readPort(remote);
+
+ // update host and remote to use library type var
+ updateFile(
+ `${shell}/module-federation.config.ts`,
+ stripIndents`
+ import { ModuleFederationConfig } from '@nx/webpack';
+
+ const config: ModuleFederationConfig = {
+ name: '${shell}',
+ library: { type: 'var', name: '${shell}' },
+ remotes: ['${remote}'],
+ };
+
+ export default config;
+ `
+ );
+
+ updateFile(
+ `${shell}/webpack.config.prod.ts`,
+ `export { default } from './webpack.config';`
+ );
+
+ updateFile(
+ `${remote}/module-federation.config.ts`,
+ stripIndents`
+ import { ModuleFederationConfig } from '@nx/webpack';
+
+ const config: ModuleFederationConfig = {
+ name: '${remote}',
+ library: { type: 'var', name: '${remote}' },
+ exposes: {
+ './Module': './src/remote-entry.ts',
+ },
+ };
+
+ export default config;
+ `
+ );
+
+ updateFile(
+ `${remote}/webpack.config.prod.ts`,
+ `export { default } from './webpack.config';`
+ );
+
+ // Update host e2e test to check that the remote works with library type var via navigation
+ updateFile(
+ `${shell}-e2e/src/e2e/app.cy.ts`,
+ `
+ import { getGreeting } from '../support/app.po';
+
+ describe('${shell}', () => {
+ beforeEach(() => cy.visit('/'));
+
+ it('should display welcome message', () => {
+ getGreeting().contains('Welcome ${shell}');
+
+ });
+
+ it('should navigate to /about from /', () => {
+ cy.get('a').contains('${remote[0].toUpperCase()}${remote.slice(
+ 1
+ )}').click();
+ cy.url().should('include', '/${remote}');
+ getGreeting().contains('Welcome ${remote}');
+ });
+ });
+ `
+ );
+
+ // Build host and remote
+ const buildOutput = runCLI(`build ${shell}`);
+ const remoteOutput = runCLI(`build ${remote}`);
+
+ expect(buildOutput).toContain('Successfully ran target build');
+ expect(remoteOutput).toContain('Successfully ran target build');
+
+ if (runE2ETests()) {
+ const hostE2eResultsSwc = await runCommandUntil(
+ `e2e ${shell}-e2e --no-watch --verbose`,
+ (output) => output.includes('All specs passed!')
+ );
+ await killProcessAndPorts(
+ hostE2eResultsSwc.pid,
+ shellPort,
+ shellPort + 1,
+ remotePort
+ );
+
+ const remoteE2eResultsSwc = await runCommandUntil(
+ `e2e ${remote}-e2e --no-watch --verbose`,
+ (output) => output.includes('All specs passed!')
+ );
+
+ await killProcessAndPorts(remoteE2eResultsSwc.pid, remotePort);
+
+ const hostE2eResultsTsNode = await runCommandUntil(
+ `e2e ${shell}-e2e --no-watch --verbose`,
+ (output) => output.includes('All specs passed!'),
+ { env: { NX_PREFER_TS_NODE: 'true' } }
+ );
+
+ await killProcessAndPorts(
+ hostE2eResultsTsNode.pid,
+ shellPort,
+ shellPort + 1,
+ remotePort
+ );
+
+ const remoteE2eResultsTsNode = await runCommandUntil(
+ `e2e ${remote}-e2e --no-watch --verbose`,
+ (output) => output.includes('All specs passed!'),
+ { env: { NX_PREFER_TS_NODE: 'true' } }
+ );
+
+ await killProcessAndPorts(remoteE2eResultsTsNode.pid, remotePort);
+ }
+ }, 500_000);
+});
diff --git a/e2e/react/src/module-federation/independent-deployability-promise-based-remotes.test.ts b/e2e/react/src/module-federation/independent-deployability-promise-based-remotes.test.ts
new file mode 100644
index 0000000000000..c83d49e446e2c
--- /dev/null
+++ b/e2e/react/src/module-federation/independent-deployability-promise-based-remotes.test.ts
@@ -0,0 +1,162 @@
+import {
+ getAvailablePort,
+ killProcessAndPorts,
+ runCommandUntil,
+ runE2ETests,
+ uniq,
+ updateFile,
+ updateJson,
+} from '@nx/e2e-utils';
+import { stripIndents } from 'nx/src/utils/strip-indents';
+import { readPort, runCLI } from './utils';
+import {
+ setupIndependentDeployabilityTest,
+ cleanupIndependentDeployabilityTest,
+} from './independent-deployability-setup';
+
+describe('Independent Deployability', () => {
+ let proj: string;
+ beforeAll(() => {
+ proj = setupIndependentDeployabilityTest();
+ });
+
+ afterAll(() => {
+ cleanupIndependentDeployabilityTest();
+ });
+
+ it('should support promised based remotes', async () => {
+ const remote = uniq('remote');
+ const host = uniq('host');
+
+ const shellPort = await getAvailablePort();
+
+ runCLI(
+ `generate @nx/react:host ${host} --remotes=${remote} --devServerPort=${shellPort} --bundler=webpack --e2eTestRunner=cypress --no-interactive --typescriptConfiguration=false --skipFormat`
+ );
+
+ const remotePort = readPort(remote);
+
+ // Update remote to be loaded via script
+ updateFile(
+ `${remote}/module-federation.config.js`,
+ stripIndents`
+ module.exports = {
+ name: '${remote}',
+ library: { type: 'var', name: '${remote}' },
+ exposes: {
+ './Module': './src/remote-entry.ts',
+ },
+ };
+ `
+ );
+
+ updateFile(
+ `${remote}/webpack.config.prod.js`,
+ `module.exports = require('./webpack.config');`
+ );
+
+ // Update host to use promise based remote
+ updateFile(
+ `${host}/module-federation.config.js`,
+ `module.exports = {
+ name: '${host}',
+ library: { type: 'var', name: '${host}' },
+ remotes: [
+ [
+ '${remote}',
+ \`promise new Promise(resolve => {
+ const remoteUrl = 'http://localhost:${remotePort}/remoteEntry.js';
+ const script = document.createElement('script');
+ script.src = remoteUrl;
+ script.onload = () => {
+ const proxy = {
+ get: (request) => window.${remote}.get(request),
+ init: (arg) => {
+ try {
+ window.${remote}.init(arg);
+ } catch (e) {
+ console.log('Remote container already initialized');
+ }
+ }
+ };
+ resolve(proxy);
+ }
+ document.head.appendChild(script);
+ })\`,
+ ],
+ ],
+ };
+ `
+ );
+
+ updateFile(
+ `${host}/webpack.config.prod.js`,
+ `module.exports = require('./webpack.config');`
+ );
+
+ // Update e2e project.json
+ updateJson(`${host}-e2e/project.json`, (json) => {
+ return {
+ ...json,
+ targets: {
+ ...json.targets,
+ e2e: {
+ ...json.targets.e2e,
+ options: {
+ ...json.targets.e2e.options,
+ devServerTarget: `${host}:serve-static:production`,
+ },
+ },
+ },
+ };
+ });
+
+ // update e2e
+ updateFile(
+ `${host}-e2e/src/e2e/app.cy.ts`,
+ `
+ import { getGreeting } from '../support/app.po';
+
+ describe('${host}', () => {
+ beforeEach(() => cy.visit('/'));
+
+ it('should display welcome message', () => {
+ getGreeting().contains('Welcome ${host}');
+ });
+
+ it('should navigate to /${remote} from /', () => {
+ cy.get('a').contains('${remote[0].toUpperCase()}${remote.slice(
+ 1
+ )}').click();
+ cy.url().should('include', '/${remote}');
+ getGreeting().contains('Welcome ${remote}');
+ });
+ });
+ `
+ );
+
+ const hostPort = readPort(host);
+
+ // Build host and remote
+ const buildOutput = runCLI(`build ${host}`);
+ const remoteOutput = runCLI(`build ${remote}`);
+
+ expect(buildOutput).toContain('Successfully ran target build');
+ expect(remoteOutput).toContain('Successfully ran target build');
+
+ if (runE2ETests()) {
+ const remoteProcess = await runCommandUntil(
+ `serve-static ${remote} --no-watch --verbose`,
+ () => {
+ return true;
+ }
+ );
+ const hostE2eResults = await runCommandUntil(
+ `e2e ${host}-e2e --no-watch --verbose`,
+ (output) => output.includes('All specs passed!')
+ );
+ await killProcessAndPorts(hostE2eResults.pid, hostPort, hostPort + 1);
+ await killProcessAndPorts(remoteProcess.pid, remotePort);
+ }
+ }, 500_000);
+});
diff --git a/e2e/react/src/module-federation/independent-deployability-setup.ts b/e2e/react/src/module-federation/independent-deployability-setup.ts
new file mode 100644
index 0000000000000..d41c09f8396d8
--- /dev/null
+++ b/e2e/react/src/module-federation/independent-deployability-setup.ts
@@ -0,0 +1,13 @@
+import { cleanupProject, newProject } from '@nx/e2e-utils';
+
+export function setupIndependentDeployabilityTest() {
+ let proj: string;
+ process.env.NX_ADD_PLUGINS = 'false';
+ proj = newProject();
+ return proj;
+}
+
+export function cleanupIndependentDeployabilityTest() {
+ cleanupProject();
+ delete process.env.NX_ADD_PLUGINS;
+}
diff --git a/e2e/react/src/module-federation/independent-deployability.webpack.test.ts b/e2e/react/src/module-federation/independent-deployability.webpack.test.ts
deleted file mode 100644
index f2a2afb63412e..0000000000000
--- a/e2e/react/src/module-federation/independent-deployability.webpack.test.ts
+++ /dev/null
@@ -1,442 +0,0 @@
-import {
- cleanupProject,
- getAvailablePort,
- killProcessAndPorts,
- newProject,
- runCommandUntil,
- runE2ETests,
- uniq,
- updateFile,
- updateJson,
-} from '@nx/e2e-utils';
-import { stripIndents } from 'nx/src/utils/strip-indents';
-import { readPort, runCLI } from './utils';
-
-describe('Independent Deployability', () => {
- let proj: string;
- beforeAll(() => {
- process.env.NX_ADD_PLUGINS = 'false';
- proj = newProject();
- });
-
- afterAll(() => {
- cleanupProject();
- delete process.env.NX_ADD_PLUGINS;
- });
-
- it('should support promised based remotes', async () => {
- const remote = uniq('remote');
- const host = uniq('host');
-
- const shellPort = await getAvailablePort();
-
- runCLI(
- `generate @nx/react:host ${host} --remotes=${remote} --devServerPort=${shellPort} --bundler=webpack --e2eTestRunner=cypress --no-interactive --typescriptConfiguration=false --skipFormat`
- );
-
- const remotePort = readPort(remote);
-
- // Update remote to be loaded via script
- updateFile(
- `${remote}/module-federation.config.js`,
- stripIndents`
- module.exports = {
- name: '${remote}',
- library: { type: 'var', name: '${remote}' },
- exposes: {
- './Module': './src/remote-entry.ts',
- },
- };
- `
- );
-
- updateFile(
- `${remote}/webpack.config.prod.js`,
- `module.exports = require('./webpack.config');`
- );
-
- // Update host to use promise based remote
- updateFile(
- `${host}/module-federation.config.js`,
- `module.exports = {
- name: '${host}',
- library: { type: 'var', name: '${host}' },
- remotes: [
- [
- '${remote}',
- \`promise new Promise(resolve => {
- const remoteUrl = 'http://localhost:${remotePort}/remoteEntry.js';
- const script = document.createElement('script');
- script.src = remoteUrl;
- script.onload = () => {
- const proxy = {
- get: (request) => window.${remote}.get(request),
- init: (arg) => {
- try {
- window.${remote}.init(arg);
- } catch (e) {
- console.log('Remote container already initialized');
- }
- }
- };
- resolve(proxy);
- }
- document.head.appendChild(script);
- })\`,
- ],
- ],
- };
- `
- );
-
- updateFile(
- `${host}/webpack.config.prod.js`,
- `module.exports = require('./webpack.config');`
- );
-
- // Update e2e project.json
- updateJson(`${host}-e2e/project.json`, (json) => {
- return {
- ...json,
- targets: {
- ...json.targets,
- e2e: {
- ...json.targets.e2e,
- options: {
- ...json.targets.e2e.options,
- devServerTarget: `${host}:serve-static:production`,
- },
- },
- },
- };
- });
-
- // update e2e
- updateFile(
- `${host}-e2e/src/e2e/app.cy.ts`,
- `
- import { getGreeting } from '../support/app.po';
-
- describe('${host}', () => {
- beforeEach(() => cy.visit('/'));
-
- it('should display welcome message', () => {
- getGreeting().contains('Welcome ${host}');
- });
-
- it('should navigate to /${remote} from /', () => {
- cy.get('a').contains('${remote[0].toUpperCase()}${remote.slice(
- 1
- )}').click();
- cy.url().should('include', '/${remote}');
- getGreeting().contains('Welcome ${remote}');
- });
- });
- `
- );
-
- const hostPort = readPort(host);
-
- // Build host and remote
- const buildOutput = runCLI(`build ${host}`);
- const remoteOutput = runCLI(`build ${remote}`);
-
- expect(buildOutput).toContain('Successfully ran target build');
- expect(remoteOutput).toContain('Successfully ran target build');
-
- if (runE2ETests()) {
- const remoteProcess = await runCommandUntil(
- `serve-static ${remote} --no-watch --verbose`,
- () => {
- return true;
- }
- );
- const hostE2eResults = await runCommandUntil(
- `e2e ${host}-e2e --no-watch --verbose`,
- (output) => output.includes('All specs passed!')
- );
- await killProcessAndPorts(hostE2eResults.pid, hostPort, hostPort + 1);
- await killProcessAndPorts(remoteProcess.pid, remotePort);
- }
- }, 500_000);
-
- it('should support different versions workspace libs for host and remote', async () => {
- const shell = uniq('shell');
- const remote = uniq('remote');
- const lib = uniq('lib');
-
- const shellPort = await getAvailablePort();
-
- runCLI(
- `generate @nx/react:host ${shell} --remotes=${remote} --devServerPort=${shellPort} --bundler=webpack --e2eTestRunner=cypress --no-interactive --skipFormat`
- );
-
- runCLI(
- `generate @nx/js:lib ${lib} --importPath=@acme/${lib} --publishable=true --no-interactive --skipFormat`
- );
-
- const remotePort = readPort(remote);
-
- updateFile(
- `${lib}/src/lib/${lib}.ts`,
- stripIndents`
- export const version = '0.0.1';
- `
- );
-
- updateJson(`${lib}/package.json`, (json) => {
- return {
- ...json,
- version: '0.0.1',
- };
- });
-
- // Update host to use the lib
- updateFile(
- `${shell}/src/app/app.tsx`,
- `
- import * as React from 'react';
-
- import NxWelcome from './nx-welcome';
- import { version } from '@acme/${lib}';
- import { Link, Route, Routes } from 'react-router-dom';
-
- const About = React.lazy(() => import('${remote}/Module'));
-
- export function App() {
- return (
-
-
- Lib version: { version }
-
-
- -
- Home
-
-
- -
- About
-
-
-
- } />
-
- } />
-
-
- );
- }
-
- export default App;`
- );
-
- // Update remote to use the lib
- updateFile(
- `${remote}/src/app/app.tsx`,
- `// eslint-disable-next-line @typescript-eslint/no-unused-vars
-
- import styles from './app.module.css';
- import { version } from '@acme/${lib}';
-
- import NxWelcome from './nx-welcome';
-
- export function App() {
- return (
-
-
- Lib version: { version }
-
-
- );
- }
-
- export default App;`
- );
-
- // update remote e2e test to check the version
- updateFile(
- `${remote}-e2e/src/e2e/app.cy.ts`,
- `describe('${remote}', () => {
- beforeEach(() => cy.visit('/'));
-
- it('should check the lib version', () => {
- cy.get('div.remote').contains('Lib version: 0.0.1');
- });
- });
- `
- );
-
- // update shell e2e test to check the version
- updateFile(
- `${shell}-e2e/src/e2e/app.cy.ts`,
- `
- describe('${shell}', () => {
- beforeEach(() => cy.visit('/'));
-
- it('should check the lib version', () => {
- cy.get('div.home').contains('Lib version: 0.0.1');
- });
- });
- `
- );
-
- if (runE2ETests()) {
- // test remote e2e
- const remoteE2eResults = await runCommandUntil(
- `e2e ${remote}-e2e --no-watch --verbose`,
- (output) => output.includes('All specs passed!')
- );
- await killProcessAndPorts(remoteE2eResults.pid, remotePort);
-
- // test shell e2e
- // serve remote first
- const remoteProcess = await runCommandUntil(
- `serve ${remote} --no-watch --verbose`,
- (output) => {
- return output.includes(
- `Web Development Server is listening at http://localhost:${remotePort}/`
- );
- }
- );
- await killProcessAndPorts(remoteProcess.pid, remotePort);
- const shellE2eResults = await runCommandUntil(
- `e2e ${shell}-e2e --no-watch --verbose`,
- (output) => output.includes('All specs passed!')
- );
- await killProcessAndPorts(
- shellE2eResults.pid,
- shellPort,
- shellPort + 1,
- remotePort
- );
- }
- }, 500_000);
-
- it('should support host and remote with library type var', async () => {
- const shell = uniq('shell');
- const remote = uniq('remote');
- const shellPort = await getAvailablePort();
-
- runCLI(
- `generate @nx/react:host ${shell} --devServerPort=${shellPort} --remotes=${remote} --bundler=webpack --e2eTestRunner=cypress --no-interactive --skipFormat`
- );
-
- const remotePort = readPort(remote);
-
- // update host and remote to use library type var
- updateFile(
- `${shell}/module-federation.config.ts`,
- stripIndents`
- import { ModuleFederationConfig } from '@nx/webpack';
-
- const config: ModuleFederationConfig = {
- name: '${shell}',
- library: { type: 'var', name: '${shell}' },
- remotes: ['${remote}'],
- };
-
- export default config;
- `
- );
-
- updateFile(
- `${shell}/webpack.config.prod.ts`,
- `export { default } from './webpack.config';`
- );
-
- updateFile(
- `${remote}/module-federation.config.ts`,
- stripIndents`
- import { ModuleFederationConfig } from '@nx/webpack';
-
- const config: ModuleFederationConfig = {
- name: '${remote}',
- library: { type: 'var', name: '${remote}' },
- exposes: {
- './Module': './src/remote-entry.ts',
- },
- };
-
- export default config;
- `
- );
-
- updateFile(
- `${remote}/webpack.config.prod.ts`,
- `export { default } from './webpack.config';`
- );
-
- // Update host e2e test to check that the remote works with library type var via navigation
- updateFile(
- `${shell}-e2e/src/e2e/app.cy.ts`,
- `
- import { getGreeting } from '../support/app.po';
-
- describe('${shell}', () => {
- beforeEach(() => cy.visit('/'));
-
- it('should display welcome message', () => {
- getGreeting().contains('Welcome ${shell}');
-
- });
-
- it('should navigate to /about from /', () => {
- cy.get('a').contains('${remote[0].toUpperCase()}${remote.slice(
- 1
- )}').click();
- cy.url().should('include', '/${remote}');
- getGreeting().contains('Welcome ${remote}');
- });
- });
- `
- );
-
- // Build host and remote
- const buildOutput = runCLI(`build ${shell}`);
- const remoteOutput = runCLI(`build ${remote}`);
-
- expect(buildOutput).toContain('Successfully ran target build');
- expect(remoteOutput).toContain('Successfully ran target build');
-
- if (runE2ETests()) {
- const hostE2eResultsSwc = await runCommandUntil(
- `e2e ${shell}-e2e --no-watch --verbose`,
- (output) => output.includes('All specs passed!')
- );
- await killProcessAndPorts(
- hostE2eResultsSwc.pid,
- shellPort,
- shellPort + 1,
- remotePort
- );
-
- const remoteE2eResultsSwc = await runCommandUntil(
- `e2e ${remote}-e2e --no-watch --verbose`,
- (output) => output.includes('All specs passed!')
- );
-
- await killProcessAndPorts(remoteE2eResultsSwc.pid, remotePort);
-
- const hostE2eResultsTsNode = await runCommandUntil(
- `e2e ${shell}-e2e --no-watch --verbose`,
- (output) => output.includes('All specs passed!'),
- { env: { NX_PREFER_TS_NODE: 'true' } }
- );
-
- await killProcessAndPorts(
- hostE2eResultsTsNode.pid,
- shellPort,
- shellPort + 1,
- remotePort
- );
-
- const remoteE2eResultsTsNode = await runCommandUntil(
- `e2e ${remote}-e2e --no-watch --verbose`,
- (output) => output.includes('All specs passed!'),
- { env: { NX_PREFER_TS_NODE: 'true' } }
- );
-
- await killProcessAndPorts(remoteE2eResultsTsNode.pid, remotePort);
- }
- }, 500_000);
-});
diff --git a/e2e/remix/src/nx-remix-npm.test.ts b/e2e/remix/src/nx-remix-npm.test.ts
new file mode 100644
index 0000000000000..128106ab24ad7
--- /dev/null
+++ b/e2e/remix/src/nx-remix-npm.test.ts
@@ -0,0 +1,23 @@
+import { runCLI, uniq, runCommandAsync } from '@nx/e2e-utils';
+import { setupNxRemixTestNpm, cleanupNxRemixTest } from './nx-remix-setup-npm';
+
+describe('Remix E2E Tests', () => {
+ describe('--integrated (npm)', () => {
+ beforeAll(() => {
+ setupNxRemixTestNpm();
+ });
+
+ afterAll(() => {
+ cleanupNxRemixTest();
+ });
+
+ it('should not cause peer dependency conflicts', async () => {
+ const plugin = uniq('remix');
+ runCLI(
+ `generate @nx/remix:app ${plugin} --linter=eslint --unitTestRunner=vitest`
+ );
+
+ await runCommandAsync('npm install');
+ }, 120000);
+ });
+});
diff --git a/e2e/remix/src/nx-remix-setup-npm.ts b/e2e/remix/src/nx-remix-setup-npm.ts
new file mode 100644
index 0000000000000..cf0a1b7c11826
--- /dev/null
+++ b/e2e/remix/src/nx-remix-setup-npm.ts
@@ -0,0 +1,13 @@
+import { cleanupProject, killPorts, newProject } from '@nx/e2e-utils';
+
+export function setupNxRemixTestNpm() {
+ newProject({
+ packages: ['@nx/remix', '@nx/react'],
+ packageManager: 'npm',
+ });
+}
+
+export function cleanupNxRemixTest() {
+ killPorts();
+ cleanupProject();
+}
diff --git a/e2e/remix/src/nx-remix-setup-standalone.ts b/e2e/remix/src/nx-remix-setup-standalone.ts
new file mode 100644
index 0000000000000..41ce544eac10d
--- /dev/null
+++ b/e2e/remix/src/nx-remix-setup-standalone.ts
@@ -0,0 +1,11 @@
+import { cleanupProject, killPorts, newProject } from '@nx/e2e-utils';
+
+export function setupNxRemixTestStandalone() {
+ const proj = newProject({ packages: ['@nx/remix'] });
+ return proj;
+}
+
+export function cleanupNxRemixTest() {
+ killPorts();
+ cleanupProject();
+}
diff --git a/e2e/remix/src/nx-remix-standalone.test.ts b/e2e/remix/src/nx-remix-standalone.test.ts
new file mode 100644
index 0000000000000..5320df1600572
--- /dev/null
+++ b/e2e/remix/src/nx-remix-standalone.test.ts
@@ -0,0 +1,43 @@
+import { runCLI, uniq, updateFile } from '@nx/e2e-utils';
+import {
+ setupNxRemixTestStandalone,
+ cleanupNxRemixTest,
+} from './nx-remix-setup-standalone';
+
+describe('Remix E2E Tests', () => {
+ describe('--standalone', () => {
+ let proj: string;
+
+ beforeAll(() => {
+ proj = setupNxRemixTestStandalone();
+ });
+
+ afterAll(() => {
+ cleanupNxRemixTest();
+ });
+
+ it('should create a standalone remix app', async () => {
+ const appName = uniq('remix');
+ runCLI(
+ `generate @nx/remix:preset --name ${appName} --directory=apps/${appName} --verbose`
+ );
+
+ // Can import using ~ alias like a normal Remix setup.
+ updateFile(`app/foo.ts`, `export const foo = 'foo';`);
+ updateFile(
+ `app/routes/index.tsx`,
+ `
+ import { foo } from '~/foo';
+ export default function Index() {
+ return (
+ {foo}
+ );
+ }
+ `
+ );
+
+ const result = runCLI(`build ${appName}`);
+ expect(result).toContain('Successfully ran target build');
+ }, 120_000);
+ });
+});
diff --git a/e2e/remix/src/nx-remix.test.ts b/e2e/remix/src/nx-remix.test.ts
index 2368859b9c360..6d4e957fffd70 100644
--- a/e2e/remix/src/nx-remix.test.ts
+++ b/e2e/remix/src/nx-remix.test.ts
@@ -11,28 +11,6 @@ import {
} from '@nx/e2e-utils';
describe('Remix E2E Tests', () => {
- describe('--integrated (npm)', () => {
- beforeAll(() => {
- newProject({
- packages: ['@nx/remix', '@nx/react'],
- packageManager: 'npm',
- });
- });
-
- afterAll(() => {
- killPorts();
- cleanupProject();
- });
-
- it('should not cause peer dependency conflicts', async () => {
- const plugin = uniq('remix');
- runCLI(
- `generate @nx/remix:app ${plugin} --linter=eslint --unitTestRunner=vitest`
- );
-
- await runCommandAsync('npm install');
- }, 120000);
- });
describe('--integrated (yarn)', () => {
beforeAll(async () => {
newProject({
@@ -188,41 +166,4 @@ describe('Remix E2E Tests', () => {
}, 120000);
});
});
-
- describe('--standalone', () => {
- let proj: string;
-
- beforeAll(() => {
- proj = newProject({ packages: ['@nx/remix'] });
- });
-
- afterAll(() => {
- killPorts();
- cleanupProject();
- });
-
- it('should create a standalone remix app', async () => {
- const appName = uniq('remix');
- runCLI(
- `generate @nx/remix:preset --name ${appName} --directory=apps/${appName} --verbose`
- );
-
- // Can import using ~ alias like a normal Remix setup.
- updateFile(`app/foo.ts`, `export const foo = 'foo';`);
- updateFile(
- `app/routes/index.tsx`,
- `
- import { foo } from '~/foo';
- export default function Index() {
- return (
- {foo}
- );
- }
- `
- );
-
- const result = runCLI(`build ${appName}`);
- expect(result).toContain('Successfully ran target build');
- }, 120_000);
- });
});
diff --git a/e2e/remix/src/remix-ts-solution-import-path.test.ts b/e2e/remix/src/remix-ts-solution-import-path.test.ts
new file mode 100644
index 0000000000000..8a8e554038103
--- /dev/null
+++ b/e2e/remix/src/remix-ts-solution-import-path.test.ts
@@ -0,0 +1,39 @@
+import { runCLI, readJson, uniq } from '@nx/e2e-utils';
+import {
+ setupRemixTsSolutionTest,
+ cleanupRemixTsSolutionTest,
+} from './remix-ts-solution-setup';
+
+describe('Remix - TS solution setup', () => {
+ beforeEach(() => {
+ setupRemixTsSolutionTest();
+ });
+
+ afterEach(() => {
+ cleanupRemixTsSolutionTest();
+ });
+
+ it('should respect and support generating libraries with a name different than the import path', async () => {
+ const lib = uniq('lib');
+
+ runCLI(
+ `generate @nx/remix:library packages/${lib} --name=${lib} --linter=eslint --unitTestRunner=vitest --buildable`
+ );
+
+ const packageJson = readJson(`packages/${lib}/package.json`);
+ expect(packageJson.nx.name).toBe(lib);
+
+ expect(runCLI(`build ${lib}`)).toContain(
+ `Successfully ran target build for project ${lib}`
+ );
+ expect(runCLI(`typecheck ${lib}`)).toContain(
+ `Successfully ran target typecheck for project ${lib}`
+ );
+ expect(runCLI(`lint ${lib}`)).toContain(
+ `Successfully ran target lint for project ${lib}`
+ );
+ expect(runCLI(`test ${lib}`)).toContain(
+ `Successfully ran target test for project ${lib}`
+ );
+ }, 120_000);
+});
diff --git a/e2e/remix/src/remix-ts-solution.test.ts b/e2e/remix/src/remix-ts-solution-jest-vitest.test.ts
similarity index 80%
rename from e2e/remix/src/remix-ts-solution.test.ts
rename to e2e/remix/src/remix-ts-solution-jest-vitest.test.ts
index 3921c126b1a4e..dac63fd91378b 100644
--- a/e2e/remix/src/remix-ts-solution.test.ts
+++ b/e2e/remix/src/remix-ts-solution-jest-vitest.test.ts
@@ -1,21 +1,16 @@
+import { runCLI, uniq } from '@nx/e2e-utils';
import {
- cleanupProject,
- newProject,
- readJson,
- runCLI,
- uniq,
-} from '@nx/e2e-utils';
+ setupRemixTsSolutionTest,
+ cleanupRemixTsSolutionTest,
+} from './remix-ts-solution-setup';
describe('Remix - TS solution setup', () => {
beforeEach(() => {
- newProject({
- packages: ['@nx/remix'],
- preset: 'ts',
- });
+ setupRemixTsSolutionTest();
});
afterEach(() => {
- cleanupProject();
+ cleanupRemixTsSolutionTest();
});
it('should generate apps and libraries with jest and vitest and work correctly', async () => {
@@ -119,28 +114,4 @@ describe('Remix - TS solution setup', () => {
`Successfully ran target test for project @proj/${buildableLibJest}`
);
}, 120_000);
-
- it('should respect and support generating libraries with a name different than the import path', async () => {
- const lib = uniq('lib');
-
- runCLI(
- `generate @nx/remix:library packages/${lib} --name=${lib} --linter=eslint --unitTestRunner=vitest --buildable`
- );
-
- const packageJson = readJson(`packages/${lib}/package.json`);
- expect(packageJson.nx.name).toBe(lib);
-
- expect(runCLI(`build ${lib}`)).toContain(
- `Successfully ran target build for project ${lib}`
- );
- expect(runCLI(`typecheck ${lib}`)).toContain(
- `Successfully ran target typecheck for project ${lib}`
- );
- expect(runCLI(`lint ${lib}`)).toContain(
- `Successfully ran target lint for project ${lib}`
- );
- expect(runCLI(`test ${lib}`)).toContain(
- `Successfully ran target test for project ${lib}`
- );
- }, 120_000);
});
diff --git a/e2e/remix/src/remix-ts-solution-setup.ts b/e2e/remix/src/remix-ts-solution-setup.ts
new file mode 100644
index 0000000000000..eaeddbd73a3a1
--- /dev/null
+++ b/e2e/remix/src/remix-ts-solution-setup.ts
@@ -0,0 +1,12 @@
+import { cleanupProject, newProject } from '@nx/e2e-utils';
+
+export function setupRemixTsSolutionTest() {
+ newProject({
+ packages: ['@nx/remix'],
+ preset: 'ts',
+ });
+}
+
+export function cleanupRemixTsSolutionTest() {
+ cleanupProject();
+}
diff --git a/e2e/vite/src/vite-legacy-esm-only.test.ts b/e2e/vite/src/vite-legacy-esm-only.test.ts
new file mode 100644
index 0000000000000..34cbe26146063
--- /dev/null
+++ b/e2e/vite/src/vite-legacy-esm-only.test.ts
@@ -0,0 +1,97 @@
+import {
+ checkFilesExist,
+ cleanupProject,
+ getPackageManagerCommand,
+ newProject,
+ runCLI,
+ runCommand,
+ uniq,
+ updateFile,
+ updateJson,
+} from '@nx/e2e-utils';
+
+describe('Vite Plugin', () => {
+ let proj: string;
+ let originalEnv: string;
+ beforeAll(() => {
+ originalEnv = process.env.NX_ADD_PLUGINS;
+ process.env.NX_ADD_PLUGINS = 'false';
+ proj = newProject({
+ packages: ['@nx/react', '@nx/web'],
+ });
+ });
+
+ afterAll(() => {
+ process.env.NX_ADD_PLUGINS = originalEnv;
+ cleanupProject();
+ });
+
+ describe('ESM-only apps', () => {
+ beforeAll(() => {
+ newProject({
+ packages: ['@nx/react'],
+ });
+ });
+
+ it('should support ESM-only plugins in vite.config.ts for root apps (#NXP-168)', () => {
+ // ESM-only plugin to test with
+ updateFile(
+ 'foo/package.json',
+ JSON.stringify({
+ name: '@acme/foo',
+ type: 'module',
+ version: '1.0.0',
+ main: 'index.js',
+ })
+ );
+ updateFile(
+ 'foo/index.js',
+ `
+ export default function fooPlugin() {
+ return {
+ name: 'foo-plugin',
+ configResolved() {
+ console.log('Foo plugin');
+ }
+ }
+ }`
+ );
+ updateJson('package.json', (json) => {
+ json.devDependencies['@acme/foo'] = 'file:./foo';
+ return json;
+ });
+ runCommand(getPackageManagerCommand().install);
+
+ const rootApp = uniq('root');
+ runCLI(
+ `generate @nx/react:app ${rootApp} --rootProject --bundler=vite --unitTestRunner=none --e2eTestRunner=none --style=css --no-interactive`
+ );
+ updateJson(`package.json`, (json) => {
+ // This allows us to use ESM-only packages in vite.config.ts.
+ json.type = 'module';
+ return json;
+ });
+ updateFile(
+ `vite.config.ts`,
+ `
+ import fooPlugin from '@acme/foo';
+ import { defineConfig } from 'vite';
+ import react from '@vitejs/plugin-react';
+ import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
+
+ export default defineConfig({
+ cacheDir: '../../node_modules/.vite/root-app',
+ server: {
+ port: 4200,
+ host: 'localhost',
+ },
+ plugins: [react(), nxViteTsPaths(), fooPlugin()],
+ });`
+ );
+
+ runCLI(`build ${rootApp}`);
+
+ checkFilesExist(`dist/${rootApp}/index.html`);
+ });
+ });
+});
diff --git a/e2e/vite/src/vite-legacy-incremental-building.test.ts b/e2e/vite/src/vite-legacy-incremental-building.test.ts
new file mode 100644
index 0000000000000..6d59fcfcf61f4
--- /dev/null
+++ b/e2e/vite/src/vite-legacy-incremental-building.test.ts
@@ -0,0 +1,116 @@
+import { names } from '@nx/devkit';
+import {
+ cleanupProject,
+ newProject,
+ removeFile,
+ runCLI,
+ uniq,
+ updateFile,
+} from '@nx/e2e-utils';
+
+describe('Vite Plugin', () => {
+ let proj: string;
+ let originalEnv: string;
+ beforeAll(() => {
+ originalEnv = process.env.NX_ADD_PLUGINS;
+ process.env.NX_ADD_PLUGINS = 'false';
+ proj = newProject({
+ packages: ['@nx/react', '@nx/web'],
+ });
+ });
+
+ afterAll(() => {
+ process.env.NX_ADD_PLUGINS = originalEnv;
+ cleanupProject();
+ });
+
+ describe('incremental building', () => {
+ const app = uniq('demo');
+ const lib = uniq('my-lib');
+ beforeAll(() => {
+ proj = newProject({
+ name: uniq('vite-incr-build'),
+ packages: ['@nx/react'],
+ });
+ runCLI(
+ `generate @nx/react:app ${app} --bundler=vite --unitTestRunner=vitest --no-interactive --directory=${app}`
+ );
+
+ // only this project will be directly used from dist
+ runCLI(
+ `generate @nx/react:lib ${lib}-buildable --unitTestRunner=none --bundler=vite --importPath="@acme/buildable" --no-interactive --directory=${lib}-buildable`
+ );
+
+ runCLI(
+ `generate @nx/react:lib ${lib} --unitTestRunner=none --bundler=none --importPath="@acme/non-buildable" --no-interactive --directory=${lib}`
+ );
+
+ // because the default js lib builds as cjs it cannot be loaded from dist
+ // so the paths plugin should always resolve to the libs source
+ runCLI(
+ `generate @nx/js:lib ${lib}-js --bundler=tsc --importPath="@acme/js-lib" --no-interactive --directory=${lib}-js`
+ );
+ const buildableLibCmp = names(`${lib}-buildable`).className;
+ const nonBuildableLibCmp = names(lib).className;
+ const buildableJsLibFn = names(`${lib}-js`).propertyName;
+
+ updateFile(`${app}/src/app/app.tsx`, () => {
+ return `
+import styles from './app.module.css';
+import NxWelcome from './nx-welcome';
+import { ${buildableLibCmp} } from '@acme/buildable';
+import { ${buildableJsLibFn} } from '@acme/js-lib';
+import { ${nonBuildableLibCmp} } from '@acme/non-buildable';
+
+export function App() {
+ return (
+
+ <${buildableLibCmp} />
+ <${nonBuildableLibCmp} />
+
{${buildableJsLibFn}()}
+
+
+ );
+}
+export default App;
+`;
+ });
+ });
+
+ afterAll(() => {
+ cleanupProject();
+ });
+
+ it('should build app from libs source', () => {
+ const results = runCLI(`build ${app} --buildLibsFromSource=true`);
+ expect(results).toContain('Successfully ran target build for project');
+ // this should be more modules than build from dist
+ expect(results).toContain('38 modules transformed');
+ });
+
+ it('should build app from libs dist', () => {
+ const results = runCLI(`build ${app} --buildLibsFromSource=false`);
+ expect(results).toContain('Successfully ran target build for project');
+ // this should be less modules than building from source
+ expect(results).toContain('36 modules transformed');
+ });
+
+ it('should build app from libs without package.json in lib', () => {
+ removeFile(`${lib}-buildable/package.json`);
+
+ const buildFromSourceResults = runCLI(
+ `build ${app} --buildLibsFromSource=true`
+ );
+ expect(buildFromSourceResults).toContain(
+ 'Successfully ran target build for project'
+ );
+
+ const noBuildFromSourceResults = runCLI(
+ `build ${app} --buildLibsFromSource=false`
+ );
+ expect(noBuildFromSourceResults).toContain(
+ 'Successfully ran target build for project'
+ );
+ });
+ });
+});
diff --git a/e2e/vite/src/vite-legacy-libs-vitest-custom.test.ts b/e2e/vite/src/vite-legacy-libs-vitest-custom.test.ts
new file mode 100644
index 0000000000000..31032b2a79faf
--- /dev/null
+++ b/e2e/vite/src/vite-legacy-libs-vitest-custom.test.ts
@@ -0,0 +1,190 @@
+import {
+ cleanupProject,
+ directoryExists,
+ exists,
+ newProject,
+ runCLI,
+ runCLIAsync,
+ tmpProjPath,
+ uniq,
+ updateFile,
+ updateJson,
+} from '@nx/e2e-utils';
+import { join } from 'path';
+
+describe('Vite Plugin', () => {
+ let proj: string;
+ let originalEnv: string;
+ beforeAll(() => {
+ originalEnv = process.env.NX_ADD_PLUGINS;
+ process.env.NX_ADD_PLUGINS = 'false';
+ proj = newProject({
+ packages: ['@nx/react', '@nx/web'],
+ });
+ });
+
+ afterAll(() => {
+ process.env.NX_ADD_PLUGINS = originalEnv;
+ cleanupProject();
+ });
+
+ describe('should be able to create libs that use vitest', () => {
+ describe('using custom project configuration', () => {
+ const lib = uniq('my-custom-lib');
+ beforeEach(() => {
+ proj = newProject({ name: uniq('vite-proj'), packages: ['@nx/react'] });
+ });
+
+ it('should be able to run tests', async () => {
+ runCLI(
+ `generate @nx/react:lib ${lib} --directory=libs/${lib} --unitTestRunner=vitest`
+ );
+ expect(exists(tmpProjPath(`libs/${lib}/vite.config.ts`))).toBeTruthy();
+
+ const result = await runCLIAsync(`test ${lib}`);
+ expect(result.combinedOutput).toContain(
+ `Successfully ran target test for project ${lib}`
+ );
+
+ const nestedResults = await runCLIAsync(`test ${lib} --skip-nx-cache`, {
+ cwd: `${tmpProjPath()}/libs/${lib}`,
+ });
+ expect(nestedResults.combinedOutput).toContain(
+ `Successfully ran target test for project ${lib}`
+ );
+ }, 100_000);
+
+ it('should collect coverage', () => {
+ runCLI(
+ `generate @nx/react:lib ${lib} --directory=libs/${lib} --unitTestRunner=vitest`
+ );
+ updateFile(`libs/${lib}/vite.config.ts`, () => {
+ return `///
+ import { defineConfig } from 'vite';
+ import react from '@vitejs/plugin-react';
+ import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
+
+ export default defineConfig({
+ root: __dirname,
+ cacheDir: '../../node_modules/.vite/libs/${lib}',
+ plugins: [react(), nxViteTsPaths()],
+ test: {
+ globals: true,
+ cache: {
+ dir: '../../node_modules/.vitest',
+ },
+ environment: 'jsdom',
+ include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
+ reporters: ['default'],
+ coverage: {
+ reportsDirectory: '../../coverage/libs/${lib}',
+ provider: 'v8',
+ enabled: true,
+ thresholds: {
+ lines: 100,
+ statements: 100,
+ functions: 100,
+ branches: 1000,
+ }
+ },
+ },
+ });
+ `;
+ });
+
+ const coverageDir = `${tmpProjPath()}/coverage/libs/${lib}`;
+
+ const results = runCLI(`test ${lib} --coverage`, {
+ silenceError: true,
+ });
+ expect(results).toContain(
+ `Running target test for project ${lib} failed`
+ );
+ expect(results).toContain(`ERROR: Coverage`);
+ expect(directoryExists(coverageDir)).toBeTruthy();
+ }, 100_000);
+
+ it('should not delete the project directory when coverage is enabled', async () => {
+ // when coverage is enabled in the vite.config.ts but reportsDirectory is removed
+ // from the @nx/vite:test executor options, vite will delete the project root directory
+ runCLI(
+ `generate @nx/react:lib ${lib} --directory=libs/${lib} --unitTestRunner=vitest`
+ );
+ updateFile(`libs/${lib}/vite.config.ts`, () => {
+ return `import { defineConfig } from 'vite';
+import react from '@vitejs/plugin-react';
+import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
+
+
+export default defineConfig({
+ server: {
+ port: 4200,
+ host: 'localhost',
+ },
+ plugins: [
+ react(),
+ nxViteTsPaths()
+ ],
+ test: {
+ globals: true,
+ cache: {
+ dir: './node_modules/.vitest',
+ },
+ environment: 'jsdom',
+ include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
+ reporters: ['junit'],
+ outputFile: 'junit.xml',
+ coverage: {
+ enabled: true,
+ reportsDirectory: 'coverage',
+ }
+ },
+});
+`;
+ });
+ updateJson(join('libs', lib, 'project.json'), (config) => {
+ delete config.targets.test.options.reportsDirectory;
+ return config;
+ });
+
+ const projectRoot = `${tmpProjPath()}/libs/${lib}`;
+
+ const results = runCLI(`test ${lib}`, {
+ env: {
+ CI: 'true', // prevent vitest from watching for file changes and making the process hang
+ },
+ });
+
+ expect(directoryExists(projectRoot)).toBeTruthy();
+ expect(results).toContain(
+ `Successfully ran target test for project ${lib}`
+ );
+ expect(results).toContain(`JUNIT report written`);
+ }, 100_000);
+
+ it('should be able to run tests with inSourceTests set to true', async () => {
+ runCLI(
+ `generate @nx/react:lib ${lib} --directory=libs/${lib} --unitTestRunner=vitest --inSourceTests`
+ );
+ expect(
+ exists(tmpProjPath(`libs/${lib}/src/lib/${lib}.spec.tsx`))
+ ).toBeFalsy();
+
+ updateFile(`libs/${lib}/src/lib/${lib}.tsx`, (content) => {
+ content += `
+ if (import.meta.vitest) {
+ const { expect, it } = import.meta.vitest;
+ it('should be successful', () => {
+ expect(1 + 1).toBe(2);
+ });
+ }
+ `;
+ return content;
+ });
+
+ const result = await runCLIAsync(`test ${lib}`);
+ expect(result.combinedOutput).toContain(`1 passed`);
+ }, 100_000);
+ });
+ });
+});
diff --git a/e2e/vite/src/vite-legacy-libs-vitest-default.test.ts b/e2e/vite/src/vite-legacy-libs-vitest-default.test.ts
new file mode 100644
index 0000000000000..1de2e949496b0
--- /dev/null
+++ b/e2e/vite/src/vite-legacy-libs-vitest-default.test.ts
@@ -0,0 +1,68 @@
+import {
+ cleanupProject,
+ killProcessAndPorts,
+ newProject,
+ runCLI,
+ runCommandUntil,
+ uniq,
+} from '@nx/e2e-utils';
+import { ChildProcess } from 'child_process';
+
+describe('Vite Plugin', () => {
+ let proj: string;
+ let originalEnv: string;
+ beforeAll(() => {
+ originalEnv = process.env.NX_ADD_PLUGINS;
+ process.env.NX_ADD_PLUGINS = 'false';
+ proj = newProject({
+ packages: ['@nx/react', '@nx/web'],
+ });
+ });
+
+ afterAll(() => {
+ process.env.NX_ADD_PLUGINS = originalEnv;
+ cleanupProject();
+ });
+
+ describe('should be able to create libs that use vitest', () => {
+ describe('using default project configuration', () => {
+ const lib = uniq('my-default-lib');
+ beforeAll(() => {
+ proj = newProject({ name: uniq('vite-proj'), packages: ['@nx/react'] });
+ runCLI(
+ `generate @nx/react:lib ${lib} --directory=libs/${lib} --unitTestRunner=vitest`
+ );
+ });
+
+ it('should collect coverage when --coverage is set', () => {
+ const results = runCLI(`test ${lib} --coverage`);
+ expect(results).toContain(`Coverage report`);
+ }, 100_000);
+
+ it('should be able to watch tests', async () => {
+ let cp: ChildProcess;
+ try {
+ cp = await runCommandUntil(`test ${lib} --watch`, (output) => {
+ return output.includes('Waiting for file changes...');
+ });
+ } catch (error) {
+ console.error(error);
+ }
+
+ if (cp && cp.pid) {
+ await killProcessAndPorts(cp.pid);
+ }
+ }, 100_000);
+
+ it('should not watch tests when --watch is not set', async () => {
+ const results = runCLI(`test ${lib}`);
+
+ expect(results).not.toContain('Waiting for file changes...');
+
+ expect(results).toContain(
+ `Successfully ran target test for project ${lib}`
+ );
+ }, 100_000);
+ });
+ });
+});
diff --git a/e2e/vite/src/vite-legacy-react-apps.test.ts b/e2e/vite/src/vite-legacy-react-apps.test.ts
new file mode 100644
index 0000000000000..c625bf6563579
--- /dev/null
+++ b/e2e/vite/src/vite-legacy-react-apps.test.ts
@@ -0,0 +1,188 @@
+import {
+ cleanupProject,
+ createFile,
+ newProject,
+ readFile,
+ removeFile,
+ rmDist,
+ runCLI,
+ uniq,
+ updateFile,
+ updateJson,
+ checkFilesExist,
+} from '@nx/e2e-utils';
+
+describe('Vite Plugin', () => {
+ let proj: string;
+ let originalEnv: string;
+ beforeAll(() => {
+ originalEnv = process.env.NX_ADD_PLUGINS;
+ process.env.NX_ADD_PLUGINS = 'false';
+ proj = newProject({
+ packages: ['@nx/react', '@nx/web'],
+ });
+ });
+
+ afterAll(() => {
+ process.env.NX_ADD_PLUGINS = originalEnv;
+ cleanupProject();
+ });
+
+ describe('Vite on React apps', () => {
+ describe('set up new React app with --bundler=vite option', () => {
+ let myApp;
+
+ beforeAll(() => {
+ myApp = uniq('my-app');
+ runCLI(
+ `generate @nx/react:app ${myApp} --bundler=vite --unitTestRunner=vitest`
+ );
+ });
+
+ afterEach(() => {
+ rmDist();
+ });
+
+ describe('build the app', () => {
+ it('should build application', async () => {
+ runCLI(`build ${myApp}`);
+ expect(readFile(`dist/${myApp}/favicon.ico`)).toBeDefined();
+ expect(readFile(`dist/${myApp}/index.html`)).toBeDefined();
+ }, 200_000);
+
+ describe('when the app has static assets', () => {
+ beforeAll(() => {
+ createFile(`${myApp}/public/hello.md`, `# Hello World`);
+ });
+
+ afterAll(() => {
+ removeFile(`${myApp}/public/hello.md`);
+ });
+
+ it('should copy the assets to the output path', async () => {
+ runCLI(`build ${myApp}`);
+ expect(readFile(`dist/${myApp}/favicon.ico`)).toBeDefined();
+ expect(readFile(`dist/${myApp}/hello.md`)).toBeDefined();
+ expect(readFile(`dist/${myApp}/index.html`)).toBeDefined();
+ }, 200_000);
+ });
+ });
+
+ describe('test the app', () => {
+ it('should test application', async () => {
+ const result = runCLI(`test ${myApp}`);
+ expect(result).toContain('Successfully ran target test');
+ }, 200_000);
+
+ it('should generate a coverage file specified by the executor', async () => {
+ updateJson(`${myApp}/project.json`, (json) => {
+ json.targets.test.options.reportsDirectory = '../coverage/test-dir';
+ return json;
+ });
+
+ const result = runCLI(`test ${myApp} --coverage`);
+
+ checkFilesExist(`coverage/test-dir/index.html`);
+ expect(result).toContain('Coverage report');
+ }, 200_000);
+ });
+ });
+
+ describe('set up new React app with --bundler=vite option and use environments api', () => {
+ let myApp;
+
+ beforeAll(() => {
+ myApp = uniq('my-app');
+ runCLI(
+ `generate @nx/react:app ${myApp} --bundler=vite --unitTestRunner=vitest`
+ );
+ updateJson(`${myApp}/project.json`, (json) => {
+ json.targets.build.options.useEnvironmentsApi = true;
+ return json;
+ });
+ updateFile(
+ `${myApp}/vite.config.ts`,
+ `///
+import { defineConfig } from 'vite';
+import react from '@vitejs/plugin-react';
+import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
+import { nxCopyAssetsPlugin } from '@nx/vite/plugins/nx-copy-assets.plugin';
+
+export default defineConfig({
+ root: __dirname,
+ cacheDir: './node_modules/.vite/${myApp}',
+ server: {
+ port: 4200,
+ host: 'localhost',
+ },
+ preview: {
+ port: 4300,
+ host: 'localhost',
+ },
+ plugins: [react(), nxViteTsPaths(), nxCopyAssetsPlugin(['*.md'])],
+ // Uncomment this if you are using workers.
+ // worker: {
+ // plugins: [ nxViteTsPaths() ],
+ // },
+ builder: {},
+ environments: {
+ ssr: {
+ build: {
+ rollupOptions: {
+ input: '${myApp}/src/main.server.tsx'
+ }
+ }
+ }
+ },
+ build: {
+ outDir: './dist/${myApp}',
+ emptyOutDir: false,
+ reportCompressedSize: true,
+ commonjsOptions: {
+ transformMixedEsModules: true,
+ },
+ },
+ test: {
+ watch: false,
+ globals: true,
+ environment: 'jsdom',
+ include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
+ reporters: ['default'],
+ coverage: {
+ reportsDirectory: './coverage/${myApp}',
+ provider: 'v8',
+ },
+ },
+});
+`
+ );
+ updateFile(
+ `${myApp}/src/main.server.tsx`,
+ `import React from 'react'
+import ReactDOMServer from 'react-dom/server'
+import App from './app/app';
+
+export default async function render(_url: string, document: string) {
+ const html = ReactDOMServer.renderToString(
+
+
+
+ )
+ return document.replace('', html);
+}`
+ );
+ });
+
+ afterEach(() => {
+ rmDist();
+ });
+
+ it('should build application', async () => {
+ runCLI(`build ${myApp}`);
+ expect(readFile(`dist/${myApp}/favicon.ico`)).toBeDefined();
+ expect(readFile(`dist/${myApp}/index.html`)).toBeDefined();
+ expect(readFile(`dist/${myApp}/main.server.mjs`)).toBeDefined();
+ }, 200_000);
+ });
+ });
+});
diff --git a/e2e/vite/src/vite-legacy-setup.ts b/e2e/vite/src/vite-legacy-setup.ts
new file mode 100644
index 0000000000000..c644cf5027cd7
--- /dev/null
+++ b/e2e/vite/src/vite-legacy-setup.ts
@@ -0,0 +1,15 @@
+import { cleanupProject, newProject } from '@nx/e2e-utils';
+
+export function setupViteLegacyTest() {
+ const originalEnv = process.env.NX_ADD_PLUGINS;
+ process.env.NX_ADD_PLUGINS = 'false';
+ const proj = newProject({
+ packages: ['@nx/react', '@nx/web'],
+ });
+ return { proj, originalEnv };
+}
+
+export function cleanupViteLegacyTest(originalEnv: string) {
+ process.env.NX_ADD_PLUGINS = originalEnv;
+ cleanupProject();
+}
diff --git a/e2e/vite/src/vite-legacy-web-apps.test.ts b/e2e/vite/src/vite-legacy-web-apps.test.ts
new file mode 100644
index 0000000000000..e0acd2ea10487
--- /dev/null
+++ b/e2e/vite/src/vite-legacy-web-apps.test.ts
@@ -0,0 +1,114 @@
+import {
+ cleanupProject,
+ createFile,
+ newProject,
+ readFile,
+ readJson,
+ rmDist,
+ runCLI,
+ uniq,
+ listFiles,
+ fileExists,
+} from '@nx/e2e-utils';
+
+describe('Vite Plugin', () => {
+ let proj: string;
+ let originalEnv: string;
+ beforeAll(() => {
+ originalEnv = process.env.NX_ADD_PLUGINS;
+ process.env.NX_ADD_PLUGINS = 'false';
+ proj = newProject({
+ packages: ['@nx/react', '@nx/web'],
+ });
+ });
+
+ afterAll(() => {
+ process.env.NX_ADD_PLUGINS = originalEnv;
+ cleanupProject();
+ });
+
+ describe('Vite on Web apps', () => {
+ describe('set up new @nx/web app with --bundler=vite option', () => {
+ let myApp;
+ beforeEach(() => {
+ myApp = uniq('my-app');
+ runCLI(
+ `generate @nx/web:app ${myApp} --bundler=vite --unitTestRunner=vitest --directory=${myApp}`
+ );
+ });
+ it('should build application', async () => {
+ runCLI(`build ${myApp}`);
+ expect(readFile(`dist/${myApp}/index.html`)).toBeDefined();
+ const fileArray = listFiles(`dist/${myApp}/assets`);
+ const mainBundle = fileArray.find((file) => file.endsWith('.js'));
+ expect(readFile(`dist/${myApp}/assets/${mainBundle}`)).toBeDefined();
+ expect(fileExists(`dist/${myApp}/package.json`)).toBeFalsy();
+ rmDist();
+ }, 200_000);
+
+ it('should build application with new package json generation', async () => {
+ runCLI(`build ${myApp} --generatePackageJson`);
+ expect(readFile(`dist/${myApp}/index.html`)).toBeDefined();
+ const fileArray = listFiles(`dist/${myApp}/assets`);
+ const mainBundle = fileArray.find((file) => file.endsWith('.js'));
+ expect(readFile(`dist/${myApp}/assets/${mainBundle}`)).toBeDefined();
+
+ const packageJson = readJson(`dist/${myApp}/package.json`);
+ expect(packageJson.name).toEqual(myApp);
+ expect(packageJson.version).toEqual('0.0.1');
+ expect(packageJson.type).toEqual('module');
+ rmDist();
+ }, 200_000);
+
+ it('should build application with existing package json generation', async () => {
+ createFile(
+ `${myApp}/package.json`,
+ JSON.stringify({
+ name: 'my-existing-app',
+ version: '1.0.1',
+ scripts: {
+ start: 'node server.js',
+ },
+ })
+ );
+ runCLI(`build ${myApp} --generatePackageJson`);
+ expect(readFile(`dist/${myApp}/index.html`)).toBeDefined();
+ const fileArray = listFiles(`dist/${myApp}/assets`);
+ const mainBundle = fileArray.find((file) => file.endsWith('.js'));
+ expect(readFile(`dist/${myApp}/assets/${mainBundle}`)).toBeDefined();
+
+ const packageJson = readJson(`dist/${myApp}/package.json`);
+ expect(packageJson.name).toEqual('my-existing-app');
+ expect(packageJson.version).toEqual('1.0.1');
+ expect(packageJson.type).toEqual('module');
+ expect(packageJson.scripts).toEqual({
+ start: 'node server.js',
+ });
+ rmDist();
+ }, 200_000);
+
+ it('should build application without copying exisiting package json when generatePackageJson=false', async () => {
+ createFile(
+ `${myApp}/package.json`,
+ JSON.stringify({
+ name: 'my-existing-app',
+ version: '1.0.1',
+ scripts: {
+ start: 'node server.js',
+ },
+ })
+ );
+ runCLI(`build ${myApp} --generatePackageJson=false`);
+ expect(readFile(`dist/${myApp}/index.html`)).toBeDefined();
+ const fileArray = listFiles(`dist/${myApp}/assets`);
+ const mainBundle = fileArray.find((file) => file.endsWith('.js'));
+ expect(readFile(`dist/${myApp}/assets/${mainBundle}`)).toBeDefined();
+
+ expect(fileExists(`dist/${myApp}/package.json`)).toBe(false);
+ rmDist();
+ }, 200_000);
+ });
+
+ 100_000;
+ });
+});
diff --git a/e2e/vite/src/vite-legacy.test.ts b/e2e/vite/src/vite-legacy.test.ts
deleted file mode 100644
index 80d8c511a26ff..0000000000000
--- a/e2e/vite/src/vite-legacy.test.ts
+++ /dev/null
@@ -1,646 +0,0 @@
-import { names } from '@nx/devkit';
-import {
- cleanupProject,
- createFile,
- directoryExists,
- exists,
- fileExists,
- getPackageManagerCommand,
- listFiles,
- newProject,
- readFile,
- readJson,
- removeFile,
- rmDist,
- runCLI,
- runCommand,
- runCommandUntil,
- runCLIAsync,
- tmpProjPath,
- uniq,
- updateFile,
- updateJson,
- checkFilesExist,
- killProcessAndPorts,
-} from '@nx/e2e-utils';
-import { join } from 'path';
-import { ChildProcess } from 'child_process';
-
-describe('Vite Plugin', () => {
- let proj: string;
- let originalEnv: string;
- beforeAll(() => {
- originalEnv = process.env.NX_ADD_PLUGINS;
- process.env.NX_ADD_PLUGINS = 'false';
- proj = newProject({
- packages: ['@nx/react', '@nx/web'],
- });
- });
-
- afterAll(() => {
- process.env.NX_ADD_PLUGINS = originalEnv;
- cleanupProject();
- });
-
- describe('Vite on React apps', () => {
- describe('set up new React app with --bundler=vite option', () => {
- let myApp;
-
- beforeAll(() => {
- myApp = uniq('my-app');
- runCLI(
- `generate @nx/react:app ${myApp} --bundler=vite --unitTestRunner=vitest`
- );
- });
-
- afterEach(() => {
- rmDist();
- });
-
- describe('build the app', () => {
- it('should build application', async () => {
- runCLI(`build ${myApp}`);
- expect(readFile(`dist/${myApp}/favicon.ico`)).toBeDefined();
- expect(readFile(`dist/${myApp}/index.html`)).toBeDefined();
- }, 200_000);
-
- describe('when the app has static assets', () => {
- beforeAll(() => {
- createFile(`${myApp}/public/hello.md`, `# Hello World`);
- });
-
- afterAll(() => {
- removeFile(`${myApp}/public/hello.md`);
- });
-
- it('should copy the assets to the output path', async () => {
- runCLI(`build ${myApp}`);
- expect(readFile(`dist/${myApp}/favicon.ico`)).toBeDefined();
- expect(readFile(`dist/${myApp}/hello.md`)).toBeDefined();
- expect(readFile(`dist/${myApp}/index.html`)).toBeDefined();
- }, 200_000);
- });
- });
-
- describe('test the app', () => {
- it('should test application', async () => {
- const result = runCLI(`test ${myApp}`);
- expect(result).toContain('Successfully ran target test');
- }, 200_000);
-
- it('should generate a coverage file specified by the executor', async () => {
- updateJson(`${myApp}/project.json`, (json) => {
- json.targets.test.options.reportsDirectory = '../coverage/test-dir';
- return json;
- });
-
- const result = runCLI(`test ${myApp} --coverage`);
-
- checkFilesExist(`coverage/test-dir/index.html`);
- expect(result).toContain('Coverage report');
- }, 200_000);
- });
- });
-
- describe('set up new React app with --bundler=vite option and use environments api', () => {
- let myApp;
-
- beforeAll(() => {
- myApp = uniq('my-app');
- runCLI(
- `generate @nx/react:app ${myApp} --bundler=vite --unitTestRunner=vitest`
- );
- updateJson(`${myApp}/project.json`, (json) => {
- json.targets.build.options.useEnvironmentsApi = true;
- return json;
- });
- updateFile(
- `${myApp}/vite.config.ts`,
- `///
-import { defineConfig } from 'vite';
-import react from '@vitejs/plugin-react';
-import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
-import { nxCopyAssetsPlugin } from '@nx/vite/plugins/nx-copy-assets.plugin';
-
-export default defineConfig({
- root: __dirname,
- cacheDir: './node_modules/.vite/${myApp}',
- server: {
- port: 4200,
- host: 'localhost',
- },
- preview: {
- port: 4300,
- host: 'localhost',
- },
- plugins: [react(), nxViteTsPaths(), nxCopyAssetsPlugin(['*.md'])],
- // Uncomment this if you are using workers.
- // worker: {
- // plugins: [ nxViteTsPaths() ],
- // },
- builder: {},
- environments: {
- ssr: {
- build: {
- rollupOptions: {
- input: '${myApp}/src/main.server.tsx'
- }
- }
- }
- },
- build: {
- outDir: './dist/${myApp}',
- emptyOutDir: false,
- reportCompressedSize: true,
- commonjsOptions: {
- transformMixedEsModules: true,
- },
- },
- test: {
- watch: false,
- globals: true,
- environment: 'jsdom',
- include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
- reporters: ['default'],
- coverage: {
- reportsDirectory: './coverage/${myApp}',
- provider: 'v8',
- },
- },
-});
-`
- );
- updateFile(
- `${myApp}/src/main.server.tsx`,
- `import React from 'react'
-import ReactDOMServer from 'react-dom/server'
-import App from './app/app';
-
-export default async function render(_url: string, document: string) {
- const html = ReactDOMServer.renderToString(
-
-
-
- )
- return document.replace('', html);
-}`
- );
- });
-
- afterEach(() => {
- rmDist();
- });
-
- it('should build application', async () => {
- runCLI(`build ${myApp}`);
- expect(readFile(`dist/${myApp}/favicon.ico`)).toBeDefined();
- expect(readFile(`dist/${myApp}/index.html`)).toBeDefined();
- expect(readFile(`dist/${myApp}/main.server.mjs`)).toBeDefined();
- }, 200_000);
- });
- });
-
- describe('Vite on Web apps', () => {
- describe('set up new @nx/web app with --bundler=vite option', () => {
- let myApp;
- beforeEach(() => {
- myApp = uniq('my-app');
- runCLI(
- `generate @nx/web:app ${myApp} --bundler=vite --unitTestRunner=vitest --directory=${myApp}`
- );
- });
- it('should build application', async () => {
- runCLI(`build ${myApp}`);
- expect(readFile(`dist/${myApp}/index.html`)).toBeDefined();
- const fileArray = listFiles(`dist/${myApp}/assets`);
- const mainBundle = fileArray.find((file) => file.endsWith('.js'));
- expect(readFile(`dist/${myApp}/assets/${mainBundle}`)).toBeDefined();
- expect(fileExists(`dist/${myApp}/package.json`)).toBeFalsy();
- rmDist();
- }, 200_000);
-
- it('should build application with new package json generation', async () => {
- runCLI(`build ${myApp} --generatePackageJson`);
- expect(readFile(`dist/${myApp}/index.html`)).toBeDefined();
- const fileArray = listFiles(`dist/${myApp}/assets`);
- const mainBundle = fileArray.find((file) => file.endsWith('.js'));
- expect(readFile(`dist/${myApp}/assets/${mainBundle}`)).toBeDefined();
-
- const packageJson = readJson(`dist/${myApp}/package.json`);
- expect(packageJson.name).toEqual(myApp);
- expect(packageJson.version).toEqual('0.0.1');
- expect(packageJson.type).toEqual('module');
- rmDist();
- }, 200_000);
-
- it('should build application with existing package json generation', async () => {
- createFile(
- `${myApp}/package.json`,
- JSON.stringify({
- name: 'my-existing-app',
- version: '1.0.1',
- scripts: {
- start: 'node server.js',
- },
- })
- );
- runCLI(`build ${myApp} --generatePackageJson`);
- expect(readFile(`dist/${myApp}/index.html`)).toBeDefined();
- const fileArray = listFiles(`dist/${myApp}/assets`);
- const mainBundle = fileArray.find((file) => file.endsWith('.js'));
- expect(readFile(`dist/${myApp}/assets/${mainBundle}`)).toBeDefined();
-
- const packageJson = readJson(`dist/${myApp}/package.json`);
- expect(packageJson.name).toEqual('my-existing-app');
- expect(packageJson.version).toEqual('1.0.1');
- expect(packageJson.type).toEqual('module');
- expect(packageJson.scripts).toEqual({
- start: 'node server.js',
- });
- rmDist();
- }, 200_000);
-
- it('should build application without copying exisiting package json when generatePackageJson=false', async () => {
- createFile(
- `${myApp}/package.json`,
- JSON.stringify({
- name: 'my-existing-app',
- version: '1.0.1',
- scripts: {
- start: 'node server.js',
- },
- })
- );
- runCLI(`build ${myApp} --generatePackageJson=false`);
- expect(readFile(`dist/${myApp}/index.html`)).toBeDefined();
- const fileArray = listFiles(`dist/${myApp}/assets`);
- const mainBundle = fileArray.find((file) => file.endsWith('.js'));
- expect(readFile(`dist/${myApp}/assets/${mainBundle}`)).toBeDefined();
-
- expect(fileExists(`dist/${myApp}/package.json`)).toBe(false);
- rmDist();
- }, 200_000);
- });
-
- 100_000;
- });
-
- describe('incremental building', () => {
- const app = uniq('demo');
- const lib = uniq('my-lib');
- beforeAll(() => {
- proj = newProject({
- name: uniq('vite-incr-build'),
- packages: ['@nx/react'],
- });
- runCLI(
- `generate @nx/react:app ${app} --bundler=vite --unitTestRunner=vitest --no-interactive --directory=${app}`
- );
-
- // only this project will be directly used from dist
- runCLI(
- `generate @nx/react:lib ${lib}-buildable --unitTestRunner=none --bundler=vite --importPath="@acme/buildable" --no-interactive --directory=${lib}-buildable`
- );
-
- runCLI(
- `generate @nx/react:lib ${lib} --unitTestRunner=none --bundler=none --importPath="@acme/non-buildable" --no-interactive --directory=${lib}`
- );
-
- // because the default js lib builds as cjs it cannot be loaded from dist
- // so the paths plugin should always resolve to the libs source
- runCLI(
- `generate @nx/js:lib ${lib}-js --bundler=tsc --importPath="@acme/js-lib" --no-interactive --directory=${lib}-js`
- );
- const buildableLibCmp = names(`${lib}-buildable`).className;
- const nonBuildableLibCmp = names(lib).className;
- const buildableJsLibFn = names(`${lib}-js`).propertyName;
-
- updateFile(`${app}/src/app/app.tsx`, () => {
- return `
-import styles from './app.module.css';
-import NxWelcome from './nx-welcome';
-import { ${buildableLibCmp} } from '@acme/buildable';
-import { ${buildableJsLibFn} } from '@acme/js-lib';
-import { ${nonBuildableLibCmp} } from '@acme/non-buildable';
-
-export function App() {
- return (
-
- <${buildableLibCmp} />
- <${nonBuildableLibCmp} />
-
{${buildableJsLibFn}()}
-
-
- );
-}
-export default App;
-`;
- });
- });
-
- afterAll(() => {
- cleanupProject();
- });
-
- it('should build app from libs source', () => {
- const results = runCLI(`build ${app} --buildLibsFromSource=true`);
- expect(results).toContain('Successfully ran target build for project');
- // this should be more modules than build from dist
- expect(results).toContain('38 modules transformed');
- });
-
- it('should build app from libs dist', () => {
- const results = runCLI(`build ${app} --buildLibsFromSource=false`);
- expect(results).toContain('Successfully ran target build for project');
- // this should be less modules than building from source
- expect(results).toContain('36 modules transformed');
- });
-
- it('should build app from libs without package.json in lib', () => {
- removeFile(`${lib}-buildable/package.json`);
-
- const buildFromSourceResults = runCLI(
- `build ${app} --buildLibsFromSource=true`
- );
- expect(buildFromSourceResults).toContain(
- 'Successfully ran target build for project'
- );
-
- const noBuildFromSourceResults = runCLI(
- `build ${app} --buildLibsFromSource=false`
- );
- expect(noBuildFromSourceResults).toContain(
- 'Successfully ran target build for project'
- );
- });
- });
-
- describe('should be able to create libs that use vitest', () => {
- describe('using default project configuration', () => {
- const lib = uniq('my-default-lib');
- beforeAll(() => {
- proj = newProject({ name: uniq('vite-proj'), packages: ['@nx/react'] });
- runCLI(
- `generate @nx/react:lib ${lib} --directory=libs/${lib} --unitTestRunner=vitest`
- );
- });
-
- it('should collect coverage when --coverage is set', () => {
- const results = runCLI(`test ${lib} --coverage`);
- expect(results).toContain(`Coverage report`);
- }, 100_000);
-
- it('should be able to watch tests', async () => {
- let cp: ChildProcess;
- try {
- cp = await runCommandUntil(`test ${lib} --watch`, (output) => {
- return output.includes('Waiting for file changes...');
- });
- } catch (error) {
- console.error(error);
- }
-
- if (cp && cp.pid) {
- await killProcessAndPorts(cp.pid);
- }
- }, 100_000);
-
- it('should not watch tests when --watch is not set', async () => {
- const results = runCLI(`test ${lib}`);
-
- expect(results).not.toContain('Waiting for file changes...');
-
- expect(results).toContain(
- `Successfully ran target test for project ${lib}`
- );
- }, 100_000);
- });
-
- describe('using custom project configuration', () => {
- const lib = uniq('my-custom-lib');
- beforeEach(() => {
- proj = newProject({ name: uniq('vite-proj'), packages: ['@nx/react'] });
- });
-
- it('should be able to run tests', async () => {
- runCLI(
- `generate @nx/react:lib ${lib} --directory=libs/${lib} --unitTestRunner=vitest`
- );
- expect(exists(tmpProjPath(`libs/${lib}/vite.config.ts`))).toBeTruthy();
-
- const result = await runCLIAsync(`test ${lib}`);
- expect(result.combinedOutput).toContain(
- `Successfully ran target test for project ${lib}`
- );
-
- const nestedResults = await runCLIAsync(`test ${lib} --skip-nx-cache`, {
- cwd: `${tmpProjPath()}/libs/${lib}`,
- });
- expect(nestedResults.combinedOutput).toContain(
- `Successfully ran target test for project ${lib}`
- );
- }, 100_000);
-
- it('should collect coverage', () => {
- runCLI(
- `generate @nx/react:lib ${lib} --directory=libs/${lib} --unitTestRunner=vitest`
- );
- updateFile(`libs/${lib}/vite.config.ts`, () => {
- return `///
- import { defineConfig } from 'vite';
- import react from '@vitejs/plugin-react';
- import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
-
- export default defineConfig({
- root: __dirname,
- cacheDir: '../../node_modules/.vite/libs/${lib}',
- plugins: [react(), nxViteTsPaths()],
- test: {
- globals: true,
- cache: {
- dir: '../../node_modules/.vitest',
- },
- environment: 'jsdom',
- include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
- reporters: ['default'],
- coverage: {
- reportsDirectory: '../../coverage/libs/${lib}',
- provider: 'v8',
- enabled: true,
- thresholds: {
- lines: 100,
- statements: 100,
- functions: 100,
- branches: 1000,
- }
- },
- },
- });
- `;
- });
-
- const coverageDir = `${tmpProjPath()}/coverage/libs/${lib}`;
-
- const results = runCLI(`test ${lib} --coverage`, {
- silenceError: true,
- });
- expect(results).toContain(
- `Running target test for project ${lib} failed`
- );
- expect(results).toContain(`ERROR: Coverage`);
- expect(directoryExists(coverageDir)).toBeTruthy();
- }, 100_000);
-
- it('should not delete the project directory when coverage is enabled', async () => {
- // when coverage is enabled in the vite.config.ts but reportsDirectory is removed
- // from the @nx/vite:test executor options, vite will delete the project root directory
- runCLI(
- `generate @nx/react:lib ${lib} --directory=libs/${lib} --unitTestRunner=vitest`
- );
- updateFile(`libs/${lib}/vite.config.ts`, () => {
- return `import { defineConfig } from 'vite';
-import react from '@vitejs/plugin-react';
-import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
-
-
-export default defineConfig({
- server: {
- port: 4200,
- host: 'localhost',
- },
- plugins: [
- react(),
- nxViteTsPaths()
- ],
- test: {
- globals: true,
- cache: {
- dir: './node_modules/.vitest',
- },
- environment: 'jsdom',
- include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
- reporters: ['junit'],
- outputFile: 'junit.xml',
- coverage: {
- enabled: true,
- reportsDirectory: 'coverage',
- }
- },
-});
-`;
- });
- updateJson(join('libs', lib, 'project.json'), (config) => {
- delete config.targets.test.options.reportsDirectory;
- return config;
- });
-
- const projectRoot = `${tmpProjPath()}/libs/${lib}`;
-
- const results = runCLI(`test ${lib}`, {
- env: {
- CI: 'true', // prevent vitest from watching for file changes and making the process hang
- },
- });
-
- expect(directoryExists(projectRoot)).toBeTruthy();
- expect(results).toContain(
- `Successfully ran target test for project ${lib}`
- );
- expect(results).toContain(`JUNIT report written`);
- }, 100_000);
-
- it('should be able to run tests with inSourceTests set to true', async () => {
- runCLI(
- `generate @nx/react:lib ${lib} --directory=libs/${lib} --unitTestRunner=vitest --inSourceTests`
- );
- expect(
- exists(tmpProjPath(`libs/${lib}/src/lib/${lib}.spec.tsx`))
- ).toBeFalsy();
-
- updateFile(`libs/${lib}/src/lib/${lib}.tsx`, (content) => {
- content += `
- if (import.meta.vitest) {
- const { expect, it } = import.meta.vitest;
- it('should be successful', () => {
- expect(1 + 1).toBe(2);
- });
- }
- `;
- return content;
- });
-
- const result = await runCLIAsync(`test ${lib}`);
- expect(result.combinedOutput).toContain(`1 passed`);
- }, 100_000);
- });
- });
-
- describe('ESM-only apps', () => {
- beforeAll(() => {
- newProject({
- packages: ['@nx/react'],
- });
- });
-
- it('should support ESM-only plugins in vite.config.ts for root apps (#NXP-168)', () => {
- // ESM-only plugin to test with
- updateFile(
- 'foo/package.json',
- JSON.stringify({
- name: '@acme/foo',
- type: 'module',
- version: '1.0.0',
- main: 'index.js',
- })
- );
- updateFile(
- 'foo/index.js',
- `
- export default function fooPlugin() {
- return {
- name: 'foo-plugin',
- configResolved() {
- console.log('Foo plugin');
- }
- }
- }`
- );
- updateJson('package.json', (json) => {
- json.devDependencies['@acme/foo'] = 'file:./foo';
- return json;
- });
- runCommand(getPackageManagerCommand().install);
-
- const rootApp = uniq('root');
- runCLI(
- `generate @nx/react:app ${rootApp} --rootProject --bundler=vite --unitTestRunner=none --e2eTestRunner=none --style=css --no-interactive`
- );
- updateJson(`package.json`, (json) => {
- // This allows us to use ESM-only packages in vite.config.ts.
- json.type = 'module';
- return json;
- });
- updateFile(
- `vite.config.ts`,
- `
- import fooPlugin from '@acme/foo';
- import { defineConfig } from 'vite';
- import react from '@vitejs/plugin-react';
- import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
-
- export default defineConfig({
- cacheDir: '../../node_modules/.vite/root-app',
- server: {
- port: 4200,
- host: 'localhost',
- },
- plugins: [react(), nxViteTsPaths(), fooPlugin()],
- });`
- );
-
- runCLI(`build ${rootApp}`);
-
- checkFilesExist(`dist/${rootApp}/index.html`);
- });
- });
-});
diff --git a/e2e/web/src/web-legacy-build-options.test.ts b/e2e/web/src/web-legacy-build-options.test.ts
new file mode 100644
index 0000000000000..e01603113f1c9
--- /dev/null
+++ b/e2e/web/src/web-legacy-build-options.test.ts
@@ -0,0 +1,96 @@
+import {
+ createFile,
+ newProject,
+ readFile,
+ runCLI,
+ uniq,
+ updateFile,
+ updateJson,
+} from '@nx/e2e-utils';
+import { join } from 'path';
+
+describe('Build Options (legacy) ', () => {
+ it('should inject/bundle external scripts and styles', async () => {
+ newProject();
+
+ const appName = uniq('app');
+
+ runCLI(
+ `generate @nx/web:app apps/${appName} --bundler=webpack --no-interactive`,
+ {
+ env: {
+ NX_ADD_PLUGINS: 'false',
+ },
+ }
+ );
+
+ const srcPath = `apps/${appName}/src`;
+ const fooCss = `${srcPath}/foo.css`;
+ const barCss = `${srcPath}/bar.css`;
+ const fooJs = `${srcPath}/foo.js`;
+ const barJs = `${srcPath}/bar.js`;
+ const fooCssContent = `/* ${uniq('foo')} */`;
+ const barCssContent = `/* ${uniq('bar')} */`;
+ const fooJsContent = `/* ${uniq('foo')} */`;
+ const barJsContent = `/* ${uniq('bar')} */`;
+
+ createFile(fooCss);
+ createFile(barCss);
+ createFile(fooJs);
+ createFile(barJs);
+
+ // createFile could not create a file with content
+ updateFile(fooCss, fooCssContent);
+ updateFile(barCss, barCssContent);
+ updateFile(fooJs, fooJsContent);
+ updateFile(barJs, barJsContent);
+
+ const barScriptsBundleName = 'bar-scripts';
+ const barStylesBundleName = 'bar-styles';
+
+ updateJson(join('apps', appName, 'project.json'), (config) => {
+ const buildOptions = config.targets.build.options;
+
+ buildOptions.scripts = [
+ {
+ input: fooJs,
+ inject: true,
+ },
+ {
+ input: barJs,
+ inject: false,
+ bundleName: barScriptsBundleName,
+ },
+ ];
+
+ buildOptions.styles = [
+ {
+ input: fooCss,
+ inject: true,
+ },
+ {
+ input: barCss,
+ inject: false,
+ bundleName: barStylesBundleName,
+ },
+ ];
+ return config;
+ });
+
+ runCLI(`build ${appName} --optimization=false --outputHashing=none`);
+
+ const distPath = `dist/apps/${appName}`;
+ const scripts = readFile(`${distPath}/scripts.js`);
+ const styles = readFile(`${distPath}/styles.css`);
+ const barScripts = readFile(`${distPath}/${barScriptsBundleName}.js`);
+ const barStyles = readFile(`${distPath}/${barStylesBundleName}.css`);
+
+ expect(scripts).toContain(fooJsContent);
+ expect(scripts).not.toContain(barJsContent);
+ expect(barScripts).toContain(barJsContent);
+
+ expect(styles).toContain(fooCssContent);
+ expect(styles).not.toContain(barCssContent);
+ expect(barStyles).toContain(barCssContent);
+ });
+});
diff --git a/e2e/web/src/web-legacy-components.test.ts b/e2e/web/src/web-legacy-components.test.ts
new file mode 100644
index 0000000000000..059fe48673074
--- /dev/null
+++ b/e2e/web/src/web-legacy-components.test.ts
@@ -0,0 +1,119 @@
+import {
+ checkFilesDoNotExist,
+ checkFilesExist,
+ createFile,
+ readFile,
+ rmDist,
+ runCLI,
+ uniq,
+ updateFile,
+ updateJson,
+} from '@nx/e2e-utils';
+import { join } from 'path';
+import { setupWebLegacyTest, cleanupWebLegacyTest } from './web-legacy-setup';
+
+describe('Web Components Applications (legacy)', () => {
+ beforeEach(() => setupWebLegacyTest());
+ afterEach(() => cleanupWebLegacyTest());
+
+ it('should remove previous output before building', async () => {
+ const appName = uniq('app');
+ const libName = uniq('lib');
+
+ runCLI(
+ `generate @nx/web:app apps/${appName} --bundler=webpack --no-interactive --compiler swc`,
+ {
+ env: {
+ NX_ADD_PLUGINS: 'false',
+ },
+ }
+ );
+ runCLI(
+ `generate @nx/react:lib libs/${libName} --bundler=rollup --no-interactive --compiler swc --unitTestRunner=jest`,
+ {
+ env: {
+ NX_ADD_PLUGINS: 'false',
+ },
+ }
+ );
+
+ createFile(`dist/apps/${appName}/_should_remove.txt`);
+ createFile(`dist/libs/${libName}/_should_remove.txt`);
+ createFile(`dist/apps/_should_not_remove.txt`);
+ checkFilesExist(
+ `dist/apps/${appName}/_should_remove.txt`,
+ `dist/apps/_should_not_remove.txt`
+ );
+ runCLI(`build ${appName} --outputHashing none`);
+ runCLI(`build ${libName}`);
+ checkFilesDoNotExist(
+ `dist/apps/${appName}/_should_remove.txt`,
+ `dist/libs/${libName}/_should_remove.txt`
+ );
+
+ // Asset that React runtime is imported
+ expect(readFile(`dist/libs/${libName}/index.esm.js`)).toMatch(
+ /react\/jsx-runtime/
+ );
+ }, 120000);
+
+ it('should support custom webpackConfig option', async () => {
+ const appName = uniq('app');
+ runCLI(
+ `generate @nx/web:app apps/${appName} --bundler=webpack --no-interactive`,
+ {
+ env: {
+ NX_ADD_PLUGINS: 'false',
+ },
+ }
+ );
+
+ updateJson(join('apps', appName, 'project.json'), (config) => {
+ config.targets.build.options.webpackConfig = `apps/${appName}/webpack.config.js`;
+ return config;
+ });
+
+ // Return sync function
+ updateFile(
+ `apps/${appName}/webpack.config.js`,
+ `
+ const { composePlugins, withNx, withWeb } = require('@nx/webpack');
+ module.exports = composePlugins(withNx(), withWeb(), (config, context) => {
+ return config;
+ });
+ `
+ );
+ runCLI(`build ${appName} --outputHashing=none`);
+ checkFilesExist(`dist/apps/${appName}/main.js`);
+
+ rmDist();
+
+ // Return async function
+ updateFile(
+ `apps/${appName}/webpack.config.js`,
+ `
+ const { composePlugins, withNx, withWeb } = require('@nx/webpack');
+ module.exports = composePlugins(withNx(), withWeb(), async (config, context) => {
+ return config;
+ });
+ `
+ );
+ runCLI(`build ${appName} --outputHashing=none`);
+ checkFilesExist(`dist/apps/${appName}/main.js`);
+
+ rmDist();
+
+ // Return promise of function
+ updateFile(
+ `apps/${appName}/webpack.config.js`,
+ `
+ const { composePlugins, withNx, withWeb } = require('@nx/webpack');
+ module.exports = composePlugins(withNx(), withWeb(), Promise.resolve((config, context) => {
+ return config;
+ }));
+ `
+ );
+ runCLI(`build ${appName} --outputHashing=none`);
+ checkFilesExist(`dist/apps/${appName}/main.js`);
+ }, 100000);
+});
diff --git a/e2e/web/src/web-legacy-index-html-interpolation.test.ts b/e2e/web/src/web-legacy-index-html-interpolation.test.ts
new file mode 100644
index 0000000000000..5738667cd8828
--- /dev/null
+++ b/e2e/web/src/web-legacy-index-html-interpolation.test.ts
@@ -0,0 +1,73 @@
+import {
+ createFile,
+ newProject,
+ cleanupProject,
+ readFile,
+ runCLI,
+ uniq,
+ updateFile,
+ updateJson,
+} from '@nx/e2e-utils';
+import { join } from 'path';
+
+describe('index.html interpolation (legacy)', () => {
+ beforeAll(() => newProject());
+ afterAll(() => cleanupProject());
+
+ test('should interpolate environment variables', async () => {
+ const appName = uniq('app');
+
+ runCLI(
+ `generate @nx/web:app apps/${appName} --bundler=webpack --no-interactive`,
+ {
+ env: {
+ NX_ADD_PLUGINS: 'false',
+ },
+ }
+ );
+
+ const srcPath = `apps/${appName}/src`;
+ const indexPath = `${srcPath}/index.html`;
+ const indexContent = `
+
+
+
+ BestReactApp
+
+
+
+
+
+
+ Nx Variable: %NX_PUBLIC_VARIABLE%
+ Some other variable: %SOME_OTHER_VARIABLE%
+ Deploy Url: %DEPLOY_URL%
+
+
+`;
+ const envFilePath = `apps/${appName}/.env`;
+ const envFileContents = `
+ NX_PUBLIC_VARIABLE=foo
+ SOME_OTHER_VARIABLE=bar
+ }`;
+
+ createFile(envFilePath);
+
+ // createFile could not create a file with content
+ updateFile(envFilePath, envFileContents);
+ updateFile(indexPath, indexContent);
+
+ updateJson(join('apps', appName, 'project.json'), (config) => {
+ const buildOptions = config.targets.build.options;
+ buildOptions.deployUrl = 'baz';
+ return config;
+ });
+
+ runCLI(`build ${appName}`);
+
+ const distPath = `dist/apps/${appName}`;
+ const resultIndexContents = readFile(`${distPath}/index.html`);
+
+ expect(resultIndexContents).toMatch(/Nx Variable: foo/);
+ });
+});
diff --git a/e2e/web/src/web-legacy-setup.ts b/e2e/web/src/web-legacy-setup.ts
new file mode 100644
index 0000000000000..6ca8422bbe3f6
--- /dev/null
+++ b/e2e/web/src/web-legacy-setup.ts
@@ -0,0 +1,9 @@
+import { cleanupProject, newProject } from '@nx/e2e-utils';
+
+export function setupWebLegacyTest() {
+ newProject({ packages: ['@nx/web', '@nx/react'] });
+}
+
+export function cleanupWebLegacyTest() {
+ cleanupProject();
+}
diff --git a/e2e/web/src/web-legacy.test.ts b/e2e/web/src/web-legacy.test.ts
deleted file mode 100644
index 2299567c26120..0000000000000
--- a/e2e/web/src/web-legacy.test.ts
+++ /dev/null
@@ -1,268 +0,0 @@
-import {
- checkFilesDoNotExist,
- checkFilesExist,
- cleanupProject,
- createFile,
- newProject,
- readFile,
- rmDist,
- runCLI,
- uniq,
- updateFile,
- updateJson,
-} from '@nx/e2e-utils';
-import { join } from 'path';
-
-describe('Web Components Applications (legacy)', () => {
- beforeEach(() => newProject({ packages: ['@nx/web', '@nx/react'] }));
- afterEach(() => cleanupProject());
-
- it('should remove previous output before building', async () => {
- const appName = uniq('app');
- const libName = uniq('lib');
-
- runCLI(
- `generate @nx/web:app apps/${appName} --bundler=webpack --no-interactive --compiler swc`,
- {
- env: {
- NX_ADD_PLUGINS: 'false',
- },
- }
- );
- runCLI(
- `generate @nx/react:lib libs/${libName} --bundler=rollup --no-interactive --compiler swc --unitTestRunner=jest`,
- {
- env: {
- NX_ADD_PLUGINS: 'false',
- },
- }
- );
-
- createFile(`dist/apps/${appName}/_should_remove.txt`);
- createFile(`dist/libs/${libName}/_should_remove.txt`);
- createFile(`dist/apps/_should_not_remove.txt`);
- checkFilesExist(
- `dist/apps/${appName}/_should_remove.txt`,
- `dist/apps/_should_not_remove.txt`
- );
- runCLI(`build ${appName} --outputHashing none`);
- runCLI(`build ${libName}`);
- checkFilesDoNotExist(
- `dist/apps/${appName}/_should_remove.txt`,
- `dist/libs/${libName}/_should_remove.txt`
- );
-
- // Asset that React runtime is imported
- expect(readFile(`dist/libs/${libName}/index.esm.js`)).toMatch(
- /react\/jsx-runtime/
- );
- }, 120000);
-
- it('should support custom webpackConfig option', async () => {
- const appName = uniq('app');
- runCLI(
- `generate @nx/web:app apps/${appName} --bundler=webpack --no-interactive`,
- {
- env: {
- NX_ADD_PLUGINS: 'false',
- },
- }
- );
-
- updateJson(join('apps', appName, 'project.json'), (config) => {
- config.targets.build.options.webpackConfig = `apps/${appName}/webpack.config.js`;
- return config;
- });
-
- // Return sync function
- updateFile(
- `apps/${appName}/webpack.config.js`,
- `
- const { composePlugins, withNx, withWeb } = require('@nx/webpack');
- module.exports = composePlugins(withNx(), withWeb(), (config, context) => {
- return config;
- });
- `
- );
- runCLI(`build ${appName} --outputHashing=none`);
- checkFilesExist(`dist/apps/${appName}/main.js`);
-
- rmDist();
-
- // Return async function
- updateFile(
- `apps/${appName}/webpack.config.js`,
- `
- const { composePlugins, withNx, withWeb } = require('@nx/webpack');
- module.exports = composePlugins(withNx(), withWeb(), async (config, context) => {
- return config;
- });
- `
- );
- runCLI(`build ${appName} --outputHashing=none`);
- checkFilesExist(`dist/apps/${appName}/main.js`);
-
- rmDist();
-
- // Return promise of function
- updateFile(
- `apps/${appName}/webpack.config.js`,
- `
- const { composePlugins, withNx, withWeb } = require('@nx/webpack');
- module.exports = composePlugins(withNx(), withWeb(), Promise.resolve((config, context) => {
- return config;
- }));
- `
- );
- runCLI(`build ${appName} --outputHashing=none`);
- checkFilesExist(`dist/apps/${appName}/main.js`);
- }, 100000);
-});
-
-describe('Build Options (legacy) ', () => {
- it('should inject/bundle external scripts and styles', async () => {
- newProject();
-
- const appName = uniq('app');
-
- runCLI(
- `generate @nx/web:app apps/${appName} --bundler=webpack --no-interactive`,
- {
- env: {
- NX_ADD_PLUGINS: 'false',
- },
- }
- );
-
- const srcPath = `apps/${appName}/src`;
- const fooCss = `${srcPath}/foo.css`;
- const barCss = `${srcPath}/bar.css`;
- const fooJs = `${srcPath}/foo.js`;
- const barJs = `${srcPath}/bar.js`;
- const fooCssContent = `/* ${uniq('foo')} */`;
- const barCssContent = `/* ${uniq('bar')} */`;
- const fooJsContent = `/* ${uniq('foo')} */`;
- const barJsContent = `/* ${uniq('bar')} */`;
-
- createFile(fooCss);
- createFile(barCss);
- createFile(fooJs);
- createFile(barJs);
-
- // createFile could not create a file with content
- updateFile(fooCss, fooCssContent);
- updateFile(barCss, barCssContent);
- updateFile(fooJs, fooJsContent);
- updateFile(barJs, barJsContent);
-
- const barScriptsBundleName = 'bar-scripts';
- const barStylesBundleName = 'bar-styles';
-
- updateJson(join('apps', appName, 'project.json'), (config) => {
- const buildOptions = config.targets.build.options;
-
- buildOptions.scripts = [
- {
- input: fooJs,
- inject: true,
- },
- {
- input: barJs,
- inject: false,
- bundleName: barScriptsBundleName,
- },
- ];
-
- buildOptions.styles = [
- {
- input: fooCss,
- inject: true,
- },
- {
- input: barCss,
- inject: false,
- bundleName: barStylesBundleName,
- },
- ];
- return config;
- });
-
- runCLI(`build ${appName} --optimization=false --outputHashing=none`);
-
- const distPath = `dist/apps/${appName}`;
- const scripts = readFile(`${distPath}/scripts.js`);
- const styles = readFile(`${distPath}/styles.css`);
- const barScripts = readFile(`${distPath}/${barScriptsBundleName}.js`);
- const barStyles = readFile(`${distPath}/${barStylesBundleName}.css`);
-
- expect(scripts).toContain(fooJsContent);
- expect(scripts).not.toContain(barJsContent);
- expect(barScripts).toContain(barJsContent);
-
- expect(styles).toContain(fooCssContent);
- expect(styles).not.toContain(barCssContent);
- expect(barStyles).toContain(barCssContent);
- });
-});
-
-describe('index.html interpolation (legacy)', () => {
- beforeAll(() => newProject());
- afterAll(() => cleanupProject());
-
- test('should interpolate environment variables', async () => {
- const appName = uniq('app');
-
- runCLI(
- `generate @nx/web:app apps/${appName} --bundler=webpack --no-interactive`,
- {
- env: {
- NX_ADD_PLUGINS: 'false',
- },
- }
- );
-
- const srcPath = `apps/${appName}/src`;
- const indexPath = `${srcPath}/index.html`;
- const indexContent = `
-
-
-
- BestReactApp
-
-
-
-
-
-
- Nx Variable: %NX_PUBLIC_VARIABLE%
- Some other variable: %SOME_OTHER_VARIABLE%
- Deploy Url: %DEPLOY_URL%
-
-
-`;
- const envFilePath = `apps/${appName}/.env`;
- const envFileContents = `
- NX_PUBLIC_VARIABLE=foo
- SOME_OTHER_VARIABLE=bar
- }`;
-
- createFile(envFilePath);
-
- // createFile could not create a file with content
- updateFile(envFilePath, envFileContents);
- updateFile(indexPath, indexContent);
-
- updateJson(join('apps', appName, 'project.json'), (config) => {
- const buildOptions = config.targets.build.options;
- buildOptions.deployUrl = 'baz';
- return config;
- });
-
- runCLI(`build ${appName}`);
-
- const distPath = `dist/apps/${appName}`;
- const resultIndexContents = readFile(`${distPath}/index.html`);
-
- expect(resultIndexContents).toMatch(/Nx Variable: foo/);
- });
-});
diff --git a/nx.json b/nx.json
index 0c6ca02ce7bf2..f7f2298df0769 100644
--- a/nx.json
+++ b/nx.json
@@ -330,7 +330,7 @@
}
],
"parallel": 1,
- "bust": 3,
+ "bust": 12,
"defaultBase": "master",
"sync": {
"applyChanges": true