Compare commits
26 Commits
docs/tutorial
...
18.0.2
| Author | SHA1 | Date | |
|---|---|---|---|
| 7b0c86924f | |||
| fde900b607 | |||
| 2ebfee5781 | |||
| 7ee4137638 | |||
| 5a8a56dc15 | |||
| f7ed73d088 | |||
| cb8e02bf88 | |||
| fbb24615b2 | |||
| df446f7b1e | |||
| 2a7743c543 | |||
| 563ebc2494 | |||
| 31f61f8527 | |||
| 8764424e9b | |||
| 81d73e3512 | |||
| 4056645034 | |||
| 66f3bcb633 | |||
| bbe8e6678d | |||
| c5a44ef4ae | |||
| 8dd1db291e | |||
| 1fe809f6d6 | |||
| 489aa8f2e0 | |||
| 7a44f6ea22 | |||
| 577061c5a1 | |||
| 984edacd41 | |||
| 7163769b39 | |||
| 01831e3e67 |
@@ -21,7 +21,10 @@ launch-templates:
|
||||
~/.cache/Cypress
|
||||
~/.pnpm-store
|
||||
BASE_BRANCH: 'master'
|
||||
|
||||
- name: Install e2e deps
|
||||
script: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y ca-certificates lsof
|
||||
- name: Install Pnpm
|
||||
script: |
|
||||
npm install -g @pnpm/exe@8.7.4
|
||||
|
||||
@@ -458,5 +458,10 @@
|
||||
"name": "@gnuechtel/nx-cucumber",
|
||||
"description": "Plugin to use Cucumber within Nx workspaces",
|
||||
"url": "https://gitlab.com/gnuechtel/open-source/-/tree/main/libs/nx-cucumber"
|
||||
},
|
||||
{
|
||||
"name": "@analogjs/platform",
|
||||
"description": "Official plugin to add Analog to your Nx monorepo.",
|
||||
"url": "https://analogjs.org/docs/integrations/nx"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -675,6 +675,29 @@
|
||||
"path": "/ci/recipes/on-premise",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"id": "troubleshooting",
|
||||
"name": "Troubleshooting",
|
||||
"description": "Learn how to solve common issues in Nx Cloud.",
|
||||
"mediaImage": "",
|
||||
"file": "",
|
||||
"itemList": [
|
||||
{
|
||||
"id": "ci-execution-failed",
|
||||
"name": "CI Execution Failed",
|
||||
"description": "",
|
||||
"mediaImage": "",
|
||||
"file": "nx-cloud/troubleshooting/ci-execution-failed",
|
||||
"itemList": [],
|
||||
"isExternal": false,
|
||||
"path": "/ci/recipes/troubleshooting/ci-execution-failed",
|
||||
"tags": []
|
||||
}
|
||||
],
|
||||
"isExternal": false,
|
||||
"path": "/ci/recipes/troubleshooting",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"id": "other",
|
||||
"name": "Other",
|
||||
@@ -1224,6 +1247,40 @@
|
||||
"path": "/ci/recipes/on-premise/advanced-config",
|
||||
"tags": []
|
||||
},
|
||||
"/ci/recipes/troubleshooting": {
|
||||
"id": "troubleshooting",
|
||||
"name": "Troubleshooting",
|
||||
"description": "Learn how to solve common issues in Nx Cloud.",
|
||||
"mediaImage": "",
|
||||
"file": "",
|
||||
"itemList": [
|
||||
{
|
||||
"id": "ci-execution-failed",
|
||||
"name": "CI Execution Failed",
|
||||
"description": "",
|
||||
"mediaImage": "",
|
||||
"file": "nx-cloud/troubleshooting/ci-execution-failed",
|
||||
"itemList": [],
|
||||
"isExternal": false,
|
||||
"path": "/ci/recipes/troubleshooting/ci-execution-failed",
|
||||
"tags": []
|
||||
}
|
||||
],
|
||||
"isExternal": false,
|
||||
"path": "/ci/recipes/troubleshooting",
|
||||
"tags": []
|
||||
},
|
||||
"/ci/recipes/troubleshooting/ci-execution-failed": {
|
||||
"id": "ci-execution-failed",
|
||||
"name": "CI Execution Failed",
|
||||
"description": "",
|
||||
"mediaImage": "",
|
||||
"file": "nx-cloud/troubleshooting/ci-execution-failed",
|
||||
"itemList": [],
|
||||
"isExternal": false,
|
||||
"path": "/ci/recipes/troubleshooting/ci-execution-failed",
|
||||
"tags": []
|
||||
},
|
||||
"/ci/recipes/other": {
|
||||
"id": "other",
|
||||
"name": "Other",
|
||||
|
||||
@@ -6144,6 +6144,23 @@
|
||||
],
|
||||
"disableCollapsible": false
|
||||
},
|
||||
{
|
||||
"name": "Troubleshooting",
|
||||
"path": "/ci/recipes/troubleshooting",
|
||||
"id": "troubleshooting",
|
||||
"isExternal": false,
|
||||
"children": [
|
||||
{
|
||||
"name": "CI Execution Failed",
|
||||
"path": "/ci/recipes/troubleshooting/ci-execution-failed",
|
||||
"id": "ci-execution-failed",
|
||||
"isExternal": false,
|
||||
"children": [],
|
||||
"disableCollapsible": false
|
||||
}
|
||||
],
|
||||
"disableCollapsible": false
|
||||
},
|
||||
{
|
||||
"name": "Other",
|
||||
"path": "/ci/recipes/other",
|
||||
@@ -6544,6 +6561,31 @@
|
||||
"children": [],
|
||||
"disableCollapsible": false
|
||||
},
|
||||
{
|
||||
"name": "Troubleshooting",
|
||||
"path": "/ci/recipes/troubleshooting",
|
||||
"id": "troubleshooting",
|
||||
"isExternal": false,
|
||||
"children": [
|
||||
{
|
||||
"name": "CI Execution Failed",
|
||||
"path": "/ci/recipes/troubleshooting/ci-execution-failed",
|
||||
"id": "ci-execution-failed",
|
||||
"isExternal": false,
|
||||
"children": [],
|
||||
"disableCollapsible": false
|
||||
}
|
||||
],
|
||||
"disableCollapsible": false
|
||||
},
|
||||
{
|
||||
"name": "CI Execution Failed",
|
||||
"path": "/ci/recipes/troubleshooting/ci-execution-failed",
|
||||
"id": "ci-execution-failed",
|
||||
"isExternal": false,
|
||||
"children": [],
|
||||
"disableCollapsible": false
|
||||
},
|
||||
{
|
||||
"name": "Other",
|
||||
"path": "/ci/recipes/other",
|
||||
|
||||
@@ -238,7 +238,7 @@ The library in `dist` is publishable to npm or a private registry.
|
||||
|
||||
### Static HTML Export
|
||||
|
||||
Next.js applications can be statically exported by changing th eoutput inside your Next.js configuration file.
|
||||
Next.js applications can be statically exported by changing the output inside your Next.js configuration file.
|
||||
|
||||
```js {% fileName="apps/my-next-app/next.config.js" %}
|
||||
const nextConfig = {
|
||||
|
||||
@@ -11,5 +11,6 @@ and the version of NodeJS that we tested it against.
|
||||
| 13.x | 10.x, 12.x, 14.x | ~4.6.2 |
|
||||
| 14.x | 12.x, 14.x, 16.x | ~4.7.2 |
|
||||
| 15.x | 14.x, 16.x, 18.x | ~5.0.0 |
|
||||
| 16.x (previous) | 16.x, 18.x, 20.x | ~5.1.0 |
|
||||
| 17.x (latest) | 18.x, 20.x | ~5.1.0 |
|
||||
| 16.x | 16.x, 18.x, 20.x | ~5.1.0 |
|
||||
| 17.x (previous) | 18.x, 20.x | ~5.1.0 |
|
||||
| 18.x (latest) | 18.x, 20.x | ~5.1.0 |
|
||||
|
||||
@@ -1899,6 +1899,18 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Troubleshooting",
|
||||
"id": "troubleshooting",
|
||||
"description": "Learn how to solve common issues in Nx Cloud.",
|
||||
"itemList": [
|
||||
{
|
||||
"name": "CI Execution Failed",
|
||||
"id": "ci-execution-failed",
|
||||
"file": "nx-cloud/troubleshooting/ci-execution-failed"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Other",
|
||||
"id": "other",
|
||||
|
||||
@@ -86,7 +86,7 @@ individual commands as follows:
|
||||
|
||||
## npx nx-cloud stop-all-agents
|
||||
|
||||
Same as `npx nx-cloud complete-ci-run` and `npx nx-cloud complete-run-group`.
|
||||
Same as `npx nx-cloud complete-ci-run`.
|
||||
|
||||
This command tells Nx Cloud to terminate all agents associated with this CI pipeline execution.
|
||||
Invoking this command is not needed anymore. New versions of Nx Cloud can track when the main job terminates
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
# Understanding 'CI Execution Failed'
|
||||
|
||||
## Task Runner-Related
|
||||
|
||||
### No additional tasks detected
|
||||
|
||||
Nx Cloud is not aware of any more tasks to distribute. This can occur if Nx Cloud thinks it is done receiving tasks to distribute and all existing tasks have been completed.
|
||||
|
||||
If you are receiving this error before your full pipeline has completed, consider using [--stop-agents-after](https://nx.dev/ci/reference/nx-cloud-cli#stopagentsafter) with the target set to the last target run in your pipeline.
|
||||
|
||||
### The Nx Cloud heartbeat process failed to report its status in time
|
||||
|
||||
While running in CI environments, Nx Cloud spawns a background process called the "heartbeat" to help maintain status synchronization between itself and external platforms. When the heartbeat process does not report to Nx Cloud for 30 seconds or longer, Nx Cloud assumes something has gone wrong and terminates the current CI Pipeline Execution.
|
||||
|
||||
This behavior can be disabled by setting the [--require-explicit-completion](https://nx.dev/ci/reference/nx-cloud-cli#requireexplicitcompletion) flag to `true` on your `nx-cloud start-ci-run` command.
|
||||
|
||||
### A command was issued to stop all Nx Cloud agents
|
||||
|
||||
Nx Cloud provides two commands to forcibly stop agents, [stop-all-agents and complete-ci-run](https://nx.dev/ci/reference/nx-cloud-cli#npx-nxcloud-stopallagents).
|
||||
|
||||
Once these commands are invoked, the current CI Pipeline Execution is closed and can no longer receive new work.
|
||||
|
||||
### Nx Cloud agents were stopped due to an error
|
||||
|
||||
Nx Cloud detected a failed task in the current CI Pipeline Execution and has halted further execution.
|
||||
|
||||
This behavior can be disabled by setting the [--stop-agents-on-failure](https://nx.dev/ci/reference/nx-cloud-cli#stopagentsonfailure) flag to `false` on your `nx-cloud start-ci-run` command.
|
||||
|
||||
## Nx Agents-Related
|
||||
|
||||
### Failed to start Nx Agents workflow
|
||||
|
||||
Nx Cloud was unable to start the agents workflow with the configuration provided to `nx-cloud start-ci-run`. View the CI Pipeline Execution in the Nx Cloud UI for additional details.
|
||||
|
||||
### Unable to get workflow status from Nx Agents
|
||||
|
||||
Nx Cloud was unable to communicate with the Nx Agents assigned to a workflow for the current CI Pipeline Execution. View the CI Pipeline Execution in the Nx Cloud UI for additional details.
|
||||
|
||||
## Status Reconciliation-Related
|
||||
|
||||
### One or more workflows were cancelled
|
||||
|
||||
The current CI Pipeline Execution had a workflow cancelled due to either:
|
||||
|
||||
- a manual request in the Nx Cloud UI, or
|
||||
- a push to the same branch that already had a running workflow.
|
||||
|
||||
### One or more workflows encountered a critical error
|
||||
|
||||
The current CI Pipeline Execution encountered a critical error in a child execution environment. View the CI Pipeline Execution in the Nx Cloud UI for additional details.
|
||||
|
||||
### One or more workflows failed
|
||||
|
||||
The current CI Pipeline Execution had at least one workflow with failed steps.
|
||||
|
||||
### One or more workflows encountered an error
|
||||
|
||||
The current CI Pipeline Execution had at least one workflow that executed tasks which failed. See also: [Nx Cloud agents were stopped due to an error](#nx-cloud-agents-were-stopped-due-to-an-error)
|
||||
|
||||
### One or more workflows timed out
|
||||
|
||||
The current CI Pipeline Execution had at least one workflow that exceeded the timeout duration. View the CI Pipeline Execution in the Nx Cloud UI for additional details.
|
||||
@@ -1,9 +1,14 @@
|
||||
# Inferred Tasks
|
||||
# Inferred Tasks (Project Crystal)
|
||||
|
||||
In Nx version 18, many Nx plugins will automatically infer tasks for your projects based on the configuration of different tools. Many tools have configuration files which determine what a tool does. Nx is able to cache the results of running the tool. Nx plugins use the same configuration files to infer how Nx should [run the task](/features/run-tasks). This includes [fine-tuned cache settings](/features/cache-task-results) and automatic [task dependencies](/concepts/task-pipeline-configuration).
|
||||
|
||||
For example, the `@nx/webpack` plugin infers tasks to run webpack through Nx based on your repository's webpack configuration. This configuration already defines the destination of your build files, so Nx reads that value and caches the correct output files.
|
||||
|
||||
{% youtube
|
||||
src="https://youtu.be/wADNsVItnsM"
|
||||
title="Project Crystal"
|
||||
/%}
|
||||
|
||||
## How Does a Plugin Infer Tasks?
|
||||
|
||||
Every plugin has its own custom logic, but in order to infer tasks, they all go through the following steps.
|
||||
|
||||
@@ -238,7 +238,7 @@ The library in `dist` is publishable to npm or a private registry.
|
||||
|
||||
### Static HTML Export
|
||||
|
||||
Next.js applications can be statically exported by changing th eoutput inside your Next.js configuration file.
|
||||
Next.js applications can be statically exported by changing the output inside your Next.js configuration file.
|
||||
|
||||
```js {% fileName="apps/my-next-app/next.config.js" %}
|
||||
const nextConfig = {
|
||||
|
||||
@@ -11,5 +11,6 @@ and the version of NodeJS that we tested it against.
|
||||
| 13.x | 10.x, 12.x, 14.x | ~4.6.2 |
|
||||
| 14.x | 12.x, 14.x, 16.x | ~4.7.2 |
|
||||
| 15.x | 14.x, 16.x, 18.x | ~5.0.0 |
|
||||
| 16.x (previous) | 16.x, 18.x, 20.x | ~5.1.0 |
|
||||
| 17.x (latest) | 18.x, 20.x | ~5.1.0 |
|
||||
| 16.x | 16.x, 18.x, 20.x | ~5.1.0 |
|
||||
| 17.x (previous) | 18.x, 20.x | ~5.1.0 |
|
||||
| 18.x (latest) | 18.x, 20.x | ~5.1.0 |
|
||||
|
||||
@@ -4,6 +4,7 @@ The following environment variables are ones that you can set to change the beha
|
||||
|
||||
| Property | Type | Description |
|
||||
| -------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| NX_ADD_PLUGINS | boolean | If set to false, Nx will not add plugins to infer tasks. This is true by default. Workspaces created before Nx 18 will have this disabled via a migration for backwards compatibility |
|
||||
| NX_BASE | string | The default base branch to use when calculating the affected projects. Can be overridden on the command line with `--base`. |
|
||||
| NX_CACHE_DIRECTORY | string | The cache for task outputs is stored in `node_modules/.cache/nx` by default. Set this variable to use a different directory. |
|
||||
| NX_CACHE_PROJECT_GRAPH | boolean | If set to `false`, disables the project graph cache. Most useful when developing a plugin that modifies the project graph. |
|
||||
|
||||
@@ -305,6 +305,8 @@
|
||||
- [Authenticate via SAML](/ci/recipes/on-premise/auth-saml)
|
||||
- [Authenticate via SAML on Managed Version](/ci/recipes/on-premise/auth-saml-managed)
|
||||
- [Advanced Configuration](/ci/recipes/on-premise/advanced-config)
|
||||
- [Troubleshooting](/ci/recipes/troubleshooting)
|
||||
- [CI Execution Failed](/ci/recipes/troubleshooting/ci-execution-failed)
|
||||
- [Other](/ci/recipes/other)
|
||||
- [Record Non-Nx Commands](/ci/recipes/other/record-commands)
|
||||
- [Prepare applications for deployment via CI](/ci/recipes/other/ci-deployment)
|
||||
|
||||
@@ -126,22 +126,13 @@ describe('Angular Projects', () => {
|
||||
);
|
||||
|
||||
// check e2e tests
|
||||
let appPort = 4958;
|
||||
updateJson(join(app1, 'project.json'), (config) => {
|
||||
config.targets.serve.options ??= {};
|
||||
config.targets.serve.options.port = appPort;
|
||||
return config;
|
||||
});
|
||||
if (runE2ETests()) {
|
||||
const e2eResults = runCLI(
|
||||
`e2e ${app1}-e2e --config baseUrl=http://localhost:${appPort}`
|
||||
);
|
||||
const e2eResults = runCLI(`e2e ${app1}-e2e`);
|
||||
expect(e2eResults).toContain('All specs passed!');
|
||||
// TODO(leo): check why the port is not being killed and add assertion after fixing it
|
||||
await killPort(appPort);
|
||||
expect(await killPort(4200)).toBeTruthy();
|
||||
}
|
||||
|
||||
appPort = 4207;
|
||||
const appPort = 4207;
|
||||
const process = await runCommandUntil(
|
||||
`serve ${app1} -- --port=${appPort}`,
|
||||
(output) => output.includes(`listening on localhost:${appPort}`)
|
||||
|
||||
@@ -371,21 +371,17 @@ describe('Angular Module Federation', () => {
|
||||
);
|
||||
|
||||
// Build host and remote
|
||||
const buildOutput = await runCommandUntil(`build ${host}`, (output) =>
|
||||
output.includes('Successfully ran target build')
|
||||
);
|
||||
await killProcessAndPorts(buildOutput.pid);
|
||||
const remoteOutput = await runCommandUntil(`build ${remote}`, (output) =>
|
||||
output.includes('Successfully ran target build')
|
||||
);
|
||||
await killProcessAndPorts(remoteOutput.pid);
|
||||
const buildHostOutput = runCLI(`build ${host}`);
|
||||
expect(buildHostOutput).toContain('Successfully ran target build');
|
||||
const buildRemoteOutput = runCLI(`build ${remote}`);
|
||||
expect(buildRemoteOutput).toContain('Successfully ran target build');
|
||||
|
||||
if (runE2ETests()) {
|
||||
const hostE2eResults = await runCommandUntil(
|
||||
const e2eProcess = await runCommandUntil(
|
||||
`e2e ${host}-e2e --no-watch --verbose`,
|
||||
(output) => output.includes('All specs passed!')
|
||||
);
|
||||
await killProcessAndPorts(hostE2eResults.pid, hostPort, hostPort + 1);
|
||||
await killProcessAndPorts(e2eProcess.pid, hostPort, hostPort + 1);
|
||||
}
|
||||
}, 500_000);
|
||||
|
||||
@@ -467,21 +463,17 @@ describe('Angular Module Federation', () => {
|
||||
);
|
||||
|
||||
// Build host and remote
|
||||
const buildOutput = await runCommandUntil(`build ${host}`, (output) =>
|
||||
output.includes('Successfully ran target build')
|
||||
);
|
||||
await killProcessAndPorts(buildOutput.pid);
|
||||
const remoteOutput = await runCommandUntil(`build ${remote}`, (output) =>
|
||||
output.includes('Successfully ran target build')
|
||||
);
|
||||
await killProcessAndPorts(remoteOutput.pid);
|
||||
const buildHostOutput = runCLI(`build ${host}`);
|
||||
expect(buildHostOutput).toContain('Successfully ran target build');
|
||||
const buildRemoteOutput = runCLI(`build ${remote}`);
|
||||
expect(buildRemoteOutput).toContain('Successfully ran target build');
|
||||
|
||||
if (runE2ETests()) {
|
||||
const hostE2eResults = await runCommandUntil(
|
||||
const e2eProcess = await runCommandUntil(
|
||||
`e2e ${host}-e2e --no-watch --verbose`,
|
||||
(output) => output.includes('All specs passed!')
|
||||
);
|
||||
await killProcessAndPorts(hostE2eResults.pid, hostPort, hostPort + 1);
|
||||
await killProcessAndPorts(e2eProcess.pid, hostPort, hostPort + 1);
|
||||
}
|
||||
}, 500_000);
|
||||
});
|
||||
|
||||
@@ -9,8 +9,7 @@ import {
|
||||
|
||||
const TEN_MINS_MS = 600_000;
|
||||
|
||||
// TODO(crystal, @leosvelperez): Still need to investigate why this is failing on CI
|
||||
xdescribe('Cypress E2E Test runner (legacy)', () => {
|
||||
describe('Cypress E2E Test runner (legacy)', () => {
|
||||
beforeAll(() => {
|
||||
newProject({ packages: ['@nx/angular', '@nx/react'] });
|
||||
});
|
||||
|
||||
@@ -12,8 +12,8 @@ import {
|
||||
} from '@nx/e2e/utils';
|
||||
|
||||
const TEN_MINS_MS = 600_000;
|
||||
// TODO(crystal, @leosvelperez): Still need to investigate why this is failing on CI
|
||||
xdescribe('Cypress E2E Test runner', () => {
|
||||
|
||||
describe('Cypress E2E Test runner', () => {
|
||||
const myapp = uniq('myapp');
|
||||
|
||||
beforeAll(() => {
|
||||
|
||||
@@ -248,9 +248,10 @@ describe('Linter', () => {
|
||||
const libC = uniq('tslib-c');
|
||||
|
||||
beforeAll(() => {
|
||||
runCLI(`generate @nx/js:lib ${libA}`);
|
||||
runCLI(`generate @nx/js:lib ${libB}`);
|
||||
runCLI(`generate @nx/js:lib ${libC}`);
|
||||
// make these libs non-buildable to avoid dep-checks triggering lint errors
|
||||
runCLI(`generate @nx/js:lib ${libA} --bundler=none`);
|
||||
runCLI(`generate @nx/js:lib ${libB} --bundler=none`);
|
||||
runCLI(`generate @nx/js:lib ${libC} --bundler=none`);
|
||||
|
||||
/**
|
||||
* create tslib-a structure
|
||||
@@ -402,8 +403,7 @@ describe('Linter', () => {
|
||||
);
|
||||
});
|
||||
|
||||
// TODO(crystal, @meeroslav): Investigate why this is failing
|
||||
xit('should fix noRelativeOrAbsoluteImportsAcrossLibraries', () => {
|
||||
it('should fix noRelativeOrAbsoluteImportsAcrossLibraries', () => {
|
||||
const stdout = runCLI(`lint ${libB}`, {
|
||||
silenceError: true,
|
||||
});
|
||||
@@ -435,16 +435,29 @@ describe('Linter', () => {
|
||||
describe('dependency checks', () => {
|
||||
beforeAll(() => {
|
||||
updateJson(`libs/${mylib}/.eslintrc.json`, (json) => {
|
||||
json.overrides = [
|
||||
...json.overrides,
|
||||
{
|
||||
files: ['*.json'],
|
||||
parser: 'jsonc-eslint-parser',
|
||||
rules: {
|
||||
'@nx/dependency-checks': 'error',
|
||||
if (!json.overrides.some((o) => o.rules?.['@nx/dependency-checks'])) {
|
||||
json.overrides = [
|
||||
...json.overrides,
|
||||
{
|
||||
files: ['*.json'],
|
||||
parser: 'jsonc-eslint-parser',
|
||||
rules: {
|
||||
'@nx/dependency-checks': 'error',
|
||||
},
|
||||
},
|
||||
},
|
||||
];
|
||||
];
|
||||
}
|
||||
return json;
|
||||
});
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
// ensure the rule for dependency checks is removed
|
||||
// so that it does not affect other tests
|
||||
updateJson(`libs/${mylib}/.eslintrc.json`, (json) => {
|
||||
json.overrides = json.overrides.filter(
|
||||
(o) => !o.rules?.['@nx/dependency-checks']
|
||||
);
|
||||
return json;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import {
|
||||
cleanupProject,
|
||||
isNotWindows,
|
||||
killPorts,
|
||||
newProject,
|
||||
runCLI,
|
||||
runE2ETests,
|
||||
uniq,
|
||||
updateFile,
|
||||
} from '@nx/e2e/utils';
|
||||
import { checkApp } from './utils';
|
||||
|
||||
describe('Next.js App Router', () => {
|
||||
let proj: string;
|
||||
@@ -20,8 +20,7 @@ describe('Next.js App Router', () => {
|
||||
|
||||
afterAll(() => cleanupProject());
|
||||
|
||||
// TODO: enable this when tests are passing again
|
||||
xit('should be able to generate and build app with default App Router', async () => {
|
||||
it('should be able to generate and build app with default App Router', async () => {
|
||||
const appName = uniq('app');
|
||||
const jsLib = uniq('tslib');
|
||||
|
||||
@@ -31,7 +30,7 @@ describe('Next.js App Router', () => {
|
||||
runCLI(`generate @nx/js:lib ${jsLib} --no-interactive`);
|
||||
|
||||
updateFile(
|
||||
`apps/${appName}/app/page.tsx`,
|
||||
`apps/${appName}/src/app/page.tsx`,
|
||||
`
|
||||
import React from 'react';
|
||||
import { ${jsLib} } from '@${proj}/${jsLib}';
|
||||
@@ -40,7 +39,7 @@ describe('Next.js App Router', () => {
|
||||
return (
|
||||
<p>{${jsLib}()}</p>
|
||||
);
|
||||
};
|
||||
}
|
||||
`
|
||||
);
|
||||
|
||||
@@ -58,11 +57,15 @@ describe('Next.js App Router', () => {
|
||||
`
|
||||
);
|
||||
|
||||
await checkApp(appName, {
|
||||
checkUnitTest: false,
|
||||
checkLint: true,
|
||||
checkE2E: isNotWindows(),
|
||||
checkExport: false,
|
||||
});
|
||||
const lintResults = runCLI(`lint ${appName}`);
|
||||
expect(lintResults).toContain('Successfully ran target lint');
|
||||
|
||||
if (runE2ETests()) {
|
||||
const e2eResults = runCLI(
|
||||
`e2e ${appName}-e2e --configuration=production`
|
||||
);
|
||||
expect(e2eResults).toContain('Successfully ran target e2e for project');
|
||||
expect(await killPorts()).toBeTruthy();
|
||||
}
|
||||
}, 300_000);
|
||||
});
|
||||
|
||||
@@ -22,16 +22,11 @@ import { mkdirSync, removeSync } from 'fs-extra';
|
||||
import { join } from 'path';
|
||||
import { checkApp } from './utils';
|
||||
|
||||
// TODO(crystal, @ndcunningham): Investigate why these tests are failing
|
||||
xdescribe('@nx/next (legacy)', () => {
|
||||
describe('@nx/next (legacy)', () => {
|
||||
let proj: string;
|
||||
let originalEnv: string;
|
||||
let packageManager;
|
||||
|
||||
afterEach(() => {
|
||||
cleanupProject();
|
||||
});
|
||||
|
||||
beforeAll(() => {
|
||||
proj = newProject({
|
||||
packages: ['@nx/next'],
|
||||
@@ -99,7 +94,8 @@ xdescribe('@nx/next (legacy)', () => {
|
||||
});
|
||||
}, 1_000_000);
|
||||
|
||||
it('should produce a self-contained artifact in dist', async () => {
|
||||
// Reenable this test once we fix Error: Cannot find module 'ajv/dist/compile/codegen'
|
||||
xit('should produce a self-contained artifact in dist', async () => {
|
||||
// Remove apps/libs folder and use packages.
|
||||
// Allows us to test other integrated monorepo setup that had a regression.
|
||||
// See: https://github.com/nrwl/nx/issues/16658
|
||||
|
||||
@@ -34,12 +34,6 @@ export async function checkApp(
|
||||
expect(buildResult).toContain(`Successfully ran target build`);
|
||||
checkFilesExist(`${appsDir}/${appName}/.next/build-manifest.json`);
|
||||
|
||||
// TODO(crystal, @ndcunningham): Investigate if this file is correct
|
||||
// const packageJson = readJson(`${appsDir}/${appName}/.next/package.json`);
|
||||
// expect(packageJson.dependencies.react).toBeDefined();
|
||||
// expect(packageJson.dependencies['react-dom']).toBeDefined();
|
||||
// expect(packageJson.dependencies.next).toBeDefined();
|
||||
|
||||
if (opts.checkE2E && runE2ETests()) {
|
||||
const e2eResults = runCLI(
|
||||
`e2e ${appName}-e2e --no-watch --configuration=production`
|
||||
|
||||
@@ -10,7 +10,6 @@ import {
|
||||
uniq,
|
||||
updateFile,
|
||||
} from '@nx/e2e/utils';
|
||||
import { join } from 'path';
|
||||
|
||||
describe('Node Applications + esbuild', () => {
|
||||
beforeAll(() =>
|
||||
|
||||
@@ -62,7 +62,9 @@ describe('Node Applications + webpack', () => {
|
||||
process.env.PORT = '';
|
||||
}
|
||||
|
||||
it('should generate an app using webpack', async () => {
|
||||
// Disabled due to flakiness of ajv disabled (Error: Cannot find module 'ajv/dist/compile/codegen')
|
||||
// TODO: (nicholas) Re-enable when the flakiness is resolved
|
||||
xit('should generate an app using webpack', async () => {
|
||||
const testLib1 = uniq('test1');
|
||||
const testLib2 = uniq('test2');
|
||||
const expressApp = uniq('expressapp');
|
||||
|
||||
+74
-26
@@ -29,6 +29,12 @@ import { getLockFileName } from '@nx/js';
|
||||
import { satisfies } from 'semver';
|
||||
import { join } from 'path';
|
||||
|
||||
let originalEnvPort;
|
||||
|
||||
function getRandomPort() {
|
||||
return Math.floor(1000 + Math.random() * 9000);
|
||||
}
|
||||
|
||||
function getData(port, path = '/api'): Promise<any> {
|
||||
return new Promise((resolve) => {
|
||||
http.get(`http://localhost:${port}${path}`, (res) => {
|
||||
@@ -49,18 +55,23 @@ function getData(port, path = '/api'): Promise<any> {
|
||||
}
|
||||
|
||||
describe('Node Applications', () => {
|
||||
beforeAll(() =>
|
||||
beforeAll(() => {
|
||||
originalEnvPort = process.env.PORT;
|
||||
newProject({
|
||||
packages: ['@nx/node', '@nx/express', '@nx/nest'],
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
afterAll(() => cleanupProject());
|
||||
afterAll(() => {
|
||||
process.env.PORT = originalEnvPort;
|
||||
cleanupProject();
|
||||
});
|
||||
|
||||
it('should be able to generate an empty application', async () => {
|
||||
const nodeapp = uniq('nodeapp');
|
||||
|
||||
runCLI(`generate @nx/node:app ${nodeapp} --linter=eslint`);
|
||||
const port = getRandomPort();
|
||||
process.env.PORT = `${port}`;
|
||||
runCLI(`generate @nx/node:app ${nodeapp} --port=${port} --linter=eslint`);
|
||||
|
||||
const lintResults = runCLI(`lint ${nodeapp}`);
|
||||
expect(lintResults).toContain('Successfully ran target lint');
|
||||
@@ -73,9 +84,10 @@ describe('Node Applications', () => {
|
||||
cwd: tmpProjPath(),
|
||||
}).toString();
|
||||
expect(result).toContain('Hello World!');
|
||||
await killPorts(port);
|
||||
}, 300000);
|
||||
|
||||
// TODO(crystal, @ndcunningham): What is the alternative here?
|
||||
// TODO(crystal, @ndcunningham): This does not work because NxWebpackPlugin({}) outputFilename does not work.
|
||||
xit('should be able to generate the correct outputFileName in options', async () => {
|
||||
const nodeapp = uniq('nodeapp');
|
||||
runCLI(`generate @nx/node:app ${nodeapp} --linter=eslint`);
|
||||
@@ -89,26 +101,47 @@ describe('Node Applications', () => {
|
||||
checkFilesExist(`dist/apps/${nodeapp}/index.js`);
|
||||
}, 300000);
|
||||
|
||||
// TODO(crystal, @ndcunningham): What is the alternative here?
|
||||
xit('should be able to generate an empty application with additional entries', async () => {
|
||||
it('should be able to generate an empty application with additional entries', async () => {
|
||||
const nodeapp = uniq('nodeapp');
|
||||
|
||||
const port = getRandomPort();
|
||||
process.env.PORT = `${port}`;
|
||||
runCLI(
|
||||
`generate @nx/node:app ${nodeapp} --linter=eslint --bundler=webpack`
|
||||
`generate @nx/node:app ${nodeapp} --port=${port} --linter=eslint --bundler=webpack`
|
||||
);
|
||||
|
||||
const lintResults = runCLI(`lint ${nodeapp}`);
|
||||
expect(lintResults).toContain('Successfully ran target lint');
|
||||
|
||||
updateJson(join('apps', nodeapp, 'project.json'), (config) => {
|
||||
config.targets.build.options.additionalEntryPoints = [
|
||||
updateFile(
|
||||
`apps/${nodeapp}/webpack.config.js`,
|
||||
`
|
||||
const { NxWebpackPlugin } = require('@nx/webpack');
|
||||
const { join } = require('path');
|
||||
|
||||
module.exports = {
|
||||
output: {
|
||||
path: join(__dirname, '../../dist/apps/${nodeapp}'),
|
||||
},
|
||||
plugins: [
|
||||
new NxWebpackPlugin({
|
||||
target: 'node',
|
||||
compiler: 'tsc',
|
||||
main: './src/main.ts',
|
||||
tsConfig: './tsconfig.app.json',
|
||||
assets: ['./src/assets'],
|
||||
additionalEntryPoints: [
|
||||
{
|
||||
entryPath: 'apps/${nodeapp}/src/additional-main.ts',
|
||||
entryName: 'additional-main',
|
||||
entryPath: `apps/${nodeapp}/src/additional-main.ts`,
|
||||
},
|
||||
];
|
||||
return config;
|
||||
});
|
||||
}
|
||||
],
|
||||
optimization: false,
|
||||
outputHashing: 'none',
|
||||
}),
|
||||
],
|
||||
};
|
||||
`
|
||||
);
|
||||
|
||||
updateFile(
|
||||
`apps/${nodeapp}/src/additional-main.ts`,
|
||||
@@ -144,6 +177,8 @@ describe('Node Applications', () => {
|
||||
}
|
||||
).toString();
|
||||
expect(additionalResult).toContain('Hello Additional World!');
|
||||
|
||||
await killPorts(port);
|
||||
}, 300_000);
|
||||
|
||||
it('should be able to generate an empty application with variable in .env file', async () => {
|
||||
@@ -185,10 +220,12 @@ describe('Node Applications', () => {
|
||||
it('should be able to generate an express application', async () => {
|
||||
const nodeapp = uniq('nodeapp');
|
||||
const originalEnvPort = process.env.PORT;
|
||||
const port = 3456;
|
||||
const port = 3499;
|
||||
process.env.PORT = `${port}`;
|
||||
|
||||
runCLI(`generate @nx/express:app ${nodeapp} --linter=eslint`);
|
||||
runCLI(
|
||||
`generate @nx/express:app ${nodeapp} --port=${port} --linter=eslint`
|
||||
);
|
||||
|
||||
const lintResults = runCLI(`lint ${nodeapp}`);
|
||||
expect(lintResults).toContain('Successfully ran target lint');
|
||||
@@ -221,9 +258,9 @@ describe('Node Applications', () => {
|
||||
|
||||
try {
|
||||
await promisifiedTreeKill(p.pid, 'SIGKILL');
|
||||
await killPorts(port);
|
||||
} finally {
|
||||
process.env.port = originalEnvPort;
|
||||
expect(await killPorts(port)).toBeTruthy();
|
||||
} catch (err) {
|
||||
expect(err).toBeFalsy();
|
||||
}
|
||||
}, 120_000);
|
||||
|
||||
@@ -287,6 +324,7 @@ describe('Node Applications', () => {
|
||||
}, 120000);
|
||||
|
||||
// TODO(crystal, @ndcunningham): how do we handle this now?
|
||||
// Revisit when NxWebpackPlugin({}) outputFilename is working.
|
||||
xit('should be able to run ESM applications', async () => {
|
||||
const esmapp = uniq('esmapp');
|
||||
|
||||
@@ -333,12 +371,16 @@ describe('Node Applications', () => {
|
||||
describe('Build Node apps', () => {
|
||||
let scope: string;
|
||||
beforeAll(() => {
|
||||
originalEnvPort = process.env.PORT;
|
||||
scope = newProject({
|
||||
packages: ['@nx/node', '@nx/express', '@nx/nest'],
|
||||
});
|
||||
});
|
||||
|
||||
afterAll(() => cleanupProject());
|
||||
afterAll(() => {
|
||||
process.env.PORT = originalEnvPort;
|
||||
cleanupProject();
|
||||
});
|
||||
|
||||
// TODO(crystal, @ndcunningham): What is the alternative here?
|
||||
xit('should generate a package.json with the `--generatePackageJson` flag', async () => {
|
||||
@@ -445,7 +487,10 @@ ${jslib}();
|
||||
it('should remove previous output before building with the --deleteOutputPath option set', async () => {
|
||||
const appName = uniq('app');
|
||||
|
||||
runCLI(`generate @nx/node:app ${appName} --no-interactive`);
|
||||
const port = getRandomPort();
|
||||
process.env.PORT = `${port}`;
|
||||
|
||||
runCLI(`generate @nx/node:app ${appName} --port=${port} --no-interactive`);
|
||||
|
||||
// deleteOutputPath should default to true
|
||||
createFile(`dist/apps/${appName}/_should_remove.txt`);
|
||||
@@ -481,8 +526,11 @@ ${jslib}();
|
||||
const appName = uniq('app1');
|
||||
const libName = uniq('@my-org/lib1');
|
||||
|
||||
const port = getRandomPort();
|
||||
process.env.PORT = `${port}`;
|
||||
|
||||
runCLI(
|
||||
`generate @nx/node:app ${appName} --project-name-and-root-format=as-provided --no-interactive`
|
||||
`generate @nx/node:app ${appName} --project-name-and-root-format=as-provided --port=${port} --no-interactive`
|
||||
);
|
||||
|
||||
// check files are generated without the layout directory ("apps/") and
|
||||
|
||||
@@ -125,7 +125,7 @@ describe('React Applications', () => {
|
||||
}, 500000);
|
||||
|
||||
// TODO(crystal, @jaysoo): Investigate why this is failing.
|
||||
xit('should be able to use Vite to build and test apps', async () => {
|
||||
it('should be able to use Vite to build and test apps', async () => {
|
||||
const appName = uniq('app');
|
||||
const libName = uniq('lib');
|
||||
|
||||
@@ -153,7 +153,11 @@ describe('React Applications', () => {
|
||||
checkFilesExist(`dist/apps/${appName}/index.html`);
|
||||
|
||||
if (runE2ETests()) {
|
||||
const e2eResults = runCLI(`e2e ${appName}-e2e`);
|
||||
const e2eResults = runCLI(`e2e ${appName}-e2e`, {
|
||||
env: {
|
||||
DEBUG: 'cypress:server:*',
|
||||
},
|
||||
});
|
||||
expect(e2eResults).toContain('All specs passed!');
|
||||
expect(await killPorts()).toBeTruthy();
|
||||
}
|
||||
|
||||
@@ -13,14 +13,15 @@ export const promisifiedTreeKill: (
|
||||
|
||||
export async function killPort(port: number): Promise<boolean> {
|
||||
if (await portCheck(port)) {
|
||||
let killPortResult;
|
||||
try {
|
||||
logInfo(`Attempting to close port ${port}`);
|
||||
await kill(port);
|
||||
killPortResult = await kill(port);
|
||||
await new Promise<void>((resolve) =>
|
||||
setTimeout(() => resolve(), KILL_PORT_DELAY)
|
||||
);
|
||||
if (await portCheck(port)) {
|
||||
logError(`Port ${port} still open`);
|
||||
logError(`Port ${port} still open`, JSON.stringify(killPortResult));
|
||||
} else {
|
||||
logSuccess(`Port ${port} successfully closed`);
|
||||
return true;
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 41 KiB |
@@ -452,9 +452,12 @@ const nxCloudUrls = {
|
||||
'/nx-cloud/concepts/scenarios': '/ci/concepts/cache-security',
|
||||
'/nx-cloud/account/encryption': '/ci/recipes/security/encryption',
|
||||
'/nx-cloud/concepts/encryption': '/ci/recipes/security/encryption',
|
||||
'/nx-cloud/features/nx-cloud-workflows': '/ci/features/nx-agents',
|
||||
'/nx-cloud/features/nx-cloud-workflows':
|
||||
'/ci/features/distribute-task-execution',
|
||||
'/ci/features/nx-agents': '/ci/features/distribute-task-execution',
|
||||
'/ci': '/ci/intro/ci-with-nx',
|
||||
'/nx-cloud/:path*': '/ci/:path*',
|
||||
'/core-features/:path*': '/features/:path*',
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -45,7 +45,7 @@ export function LaunchWeekAgenda(): JSX.Element {
|
||||
{
|
||||
type: 'event',
|
||||
time: '3:35pm',
|
||||
title: 'Nx Release',
|
||||
title: 'Releasing Nx Release',
|
||||
description: ``,
|
||||
speakers: ['James Henry'],
|
||||
videoUrl: '',
|
||||
|
||||
@@ -1,15 +1,75 @@
|
||||
import { ButtonLink, SectionHeading } from '@nx/nx-dev/ui-common';
|
||||
|
||||
export function LaunchWeekAnnouncements(): JSX.Element {
|
||||
return (
|
||||
<div className="border-t border-slate-200 dark:border-slate-700">
|
||||
<section className="w-full divide-y divide-slate-200 border-t border-b border-slate-200 dark:divide-slate-700 dark:border-slate-700">
|
||||
<div className="mx-auto max-w-screen-lg xl:max-w-screen-xl">
|
||||
<article className="md:divide-x md:divide-slate-200 md:dark:divide-slate-700">
|
||||
<div className="px-5 py-12 md:pr-12">
|
||||
<p>
|
||||
We’ll be sharing new features and content daily during launch
|
||||
week, so be sure to keep an eye on this space for all the latest
|
||||
info!
|
||||
</p>
|
||||
<div className="border-y border-slate-200 dark:border-slate-700">
|
||||
<section className="w-full divide-y divide-slate-200 dark:divide-slate-700">
|
||||
<article className="mx-auto max-w-screen-lg xl:max-w-screen-xl">
|
||||
<div className="px-5 py-12 md:pr-12">
|
||||
<p>
|
||||
We’ll be sharing new features and content daily during launch
|
||||
week, so be sure to keep an eye on this space for all the latest
|
||||
info!
|
||||
</p>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
{/* MONDAY */}
|
||||
<div>
|
||||
<article className="relative overflow-hidden pt-4 mx-auto max-w-screen-lg xl:max-w-screen-xl">
|
||||
<div className="px-5 py-12 grid sm:grid-cols-2 sm:gap-8 lg:py-16">
|
||||
<div>
|
||||
<header>
|
||||
<SectionHeading as="h2" variant="title" id="monday">
|
||||
Monday
|
||||
</SectionHeading>
|
||||
<SectionHeading as="p" variant="display" className="mt-4">
|
||||
Announcing Project Crystal
|
||||
</SectionHeading>
|
||||
</header>
|
||||
<div className="mt-8 flex gap-16 font-normal">
|
||||
<p className="max-w-xl text-lg text-slate-700 dark:text-slate-400">
|
||||
When working on the next iteration of Nx, one idea
|
||||
consistently emerged: Nx Plugins are powerful and have
|
||||
proven to help large enterprises adopt monorepos,
|
||||
successfully maintaining and scaling them. However, there's
|
||||
definitely a barrier to entry. So, what if Nx Plugins
|
||||
functioned more like VSCode extensions? You simply add them,
|
||||
and they instantly enhance the experience of working with a
|
||||
given tool or technology.
|
||||
<br />
|
||||
This is what Nx Project Crystal is all about.
|
||||
</p>
|
||||
</div>
|
||||
<div className="action my-6 flex space-x-2">
|
||||
<ButtonLink
|
||||
variant="primary"
|
||||
size="default"
|
||||
href="https://blog.nrwl.io/what-if-nx-plugins-were-more-like-vscode-extensions-dcdad140ae09?source=friends_link&sk=ade76fe8d50d44aafb4d4d89ab882e24"
|
||||
title="Unveiling Project Crystal"
|
||||
>
|
||||
Read the blog post
|
||||
</ButtonLink>
|
||||
<ButtonLink
|
||||
variant="primary"
|
||||
size="default"
|
||||
href="https://youtu.be/wADNsVItnsM"
|
||||
title="Nx - Project Crystal - Youtube Video"
|
||||
>
|
||||
Watch the video
|
||||
</ButtonLink>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
aria-hidden="true"
|
||||
className="order-first sm:order-last pb-8 relative flex flex-col items-center"
|
||||
>
|
||||
<img
|
||||
className="rounded-lg"
|
||||
src="/images/launch-nx/proj-crystal-launch.jpg"
|
||||
alt="Nx Project Crystal"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
@@ -6,7 +6,7 @@ exports[`Init MF --federationType=dynamic should create a host with the correct
|
||||
fetch('/assets/module-federation.manifest.json')
|
||||
.then((res) => res.json())
|
||||
.then(definitions => setRemoteDefinitions(definitions))
|
||||
.then(() => import('./bootstrap').catch(err => console.error(err));)"
|
||||
.then(() => import('./bootstrap').catch(err => console.error(err)));"
|
||||
`;
|
||||
|
||||
exports[`Init MF --federationType=dynamic should create a host with the correct configurations when --typescriptConfiguration=true 1`] = `
|
||||
@@ -15,7 +15,7 @@ exports[`Init MF --federationType=dynamic should create a host with the correct
|
||||
fetch('/assets/module-federation.manifest.json')
|
||||
.then((res) => res.json())
|
||||
.then(definitions => setRemoteDefinitions(definitions))
|
||||
.then(() => import('./bootstrap').catch(err => console.error(err));)"
|
||||
.then(() => import('./bootstrap').catch(err => console.error(err)));"
|
||||
`;
|
||||
|
||||
exports[`Init MF should add a remote application and add it to a specified host applications router config 1`] = `
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
// as Angular attempt to figure out how to fix HMR when styles.js
|
||||
// is attached to the index.html with type=module
|
||||
|
||||
import { CYPRESS_CONFIG_FILE_NAME_PATTERN } from '@nx/cypress/src/utils/config';
|
||||
import type { ProjectConfiguration, Tree } from '@nx/devkit';
|
||||
import {
|
||||
glob,
|
||||
joinPathFragments,
|
||||
logger,
|
||||
readProjectConfiguration,
|
||||
@@ -31,8 +33,9 @@ export function addCypressOnErrorWorkaround(tree: Tree, schema: Schema) {
|
||||
}
|
||||
|
||||
if (
|
||||
e2eProject.targets.e2e.executor !== '@nx/cypress:cypress' &&
|
||||
e2eProject.targets.e2e.executor !== '@nx/cypress:cypress'
|
||||
e2eProject.targets?.e2e?.executor !== '@nx/cypress:cypress' &&
|
||||
!glob(tree, [`${e2eProject.root}/${CYPRESS_CONFIG_FILE_NAME_PATTERN}`])
|
||||
.length
|
||||
) {
|
||||
// Not a cypress e2e project, skip
|
||||
return;
|
||||
|
||||
@@ -11,20 +11,20 @@ export function fixBootstrap(tree: Tree, appRoot: string, options: Schema) {
|
||||
tree.write(joinPathFragments(appRoot, 'src/bootstrap.ts'), bootstrapCode);
|
||||
}
|
||||
|
||||
const bootstrapImportCode = `import('./bootstrap').catch(err => console.error(err));`;
|
||||
const bootstrapImportCode = `import('./bootstrap').catch(err => console.error(err))`;
|
||||
|
||||
const fetchMFManifestCode = `import { setRemoteDefinitions } from '@nx/angular/mf';
|
||||
|
||||
fetch('/assets/module-federation.manifest.json')
|
||||
.then((res) => res.json())
|
||||
.then(definitions => setRemoteDefinitions(definitions))
|
||||
.then(() => ${bootstrapImportCode})`;
|
||||
.then(() => ${bootstrapImportCode});`;
|
||||
|
||||
tree.write(
|
||||
mainFilePath,
|
||||
options.mfType === 'host' && options.federationType === 'dynamic'
|
||||
? fetchMFManifestCode
|
||||
: bootstrapImportCode
|
||||
: `${bootstrapImportCode};`
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,6 @@ import { existsSync, readdirSync } from 'fs';
|
||||
|
||||
import { projectGraphCacheDirectory } from 'nx/src/utils/cache-directory';
|
||||
import { calculateHashForCreateNodes } from '@nx/devkit/src/utils/calculate-hash-for-create-nodes';
|
||||
import { PHASE_PRODUCTION_BUILD } from 'next/constants';
|
||||
import { getLockFileName } from '@nx/js';
|
||||
|
||||
export interface NextPluginOptions {
|
||||
@@ -155,6 +154,7 @@ function getStartTargetConfig(options: NextPluginOptions, projectRoot: string) {
|
||||
|
||||
async function getOutputs(projectRoot, nextConfig) {
|
||||
let dir = '.next';
|
||||
const { PHASE_PRODUCTION_BUILD } = require('next/constants');
|
||||
|
||||
if (typeof nextConfig === 'function') {
|
||||
// Works for both async and sync functions.
|
||||
|
||||
@@ -13,27 +13,6 @@ import * as Mod from 'module';
|
||||
*/
|
||||
|
||||
export function initLocal(workspace: WorkspaceTypeAndRoot) {
|
||||
// If module.register is not available, we need to restart the process with the experimental ESM loader.
|
||||
// Otherwise, usage of `registerTsProject` will not work for `.ts` files using ESM.
|
||||
// TODO: Remove this once Node 18 is out of LTS (March 2024).
|
||||
if (shouldRestartWithExperimentalTsEsmLoader()) {
|
||||
const child = require('child_process').fork(
|
||||
require.resolve('./nx'),
|
||||
process.argv.slice(2),
|
||||
{
|
||||
env: {
|
||||
...process.env,
|
||||
RESTARTED_WITH_EXPERIMENTAL_TS_ESM_LOADER: '1',
|
||||
},
|
||||
execArgv: execArgvWithExperimentalLoaderOptions(),
|
||||
}
|
||||
);
|
||||
child.on('close', (code: number | null) => {
|
||||
if (code !== 0 && code !== null) process.exit(code);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
process.env.NX_CLI_SET = 'true';
|
||||
|
||||
try {
|
||||
@@ -215,36 +194,3 @@ function monkeyPatchRequire() {
|
||||
// do some side-effect of your own
|
||||
};
|
||||
}
|
||||
|
||||
function shouldRestartWithExperimentalTsEsmLoader(): boolean {
|
||||
// Already restarted with experimental loader
|
||||
if (process.env.RESTARTED_WITH_EXPERIMENTAL_TS_ESM_LOADER === '1')
|
||||
return false;
|
||||
const nodeVersion = parseInt(process.versions.node.split('.')[0]);
|
||||
// `--experimental-loader` is only supported in Nodejs >= 16 so there is no point restarting for older versions
|
||||
if (nodeVersion < 16) return false;
|
||||
// Node 20.6.0 adds `module.register`, otherwise we need to restart process with "--experimental-loader ts-node/esm".
|
||||
return (
|
||||
!require('node:module').register &&
|
||||
moduleResolves('ts-node/esm') &&
|
||||
moduleResolves('typescript')
|
||||
);
|
||||
}
|
||||
|
||||
function execArgvWithExperimentalLoaderOptions() {
|
||||
return [
|
||||
...process.execArgv,
|
||||
'--no-warnings',
|
||||
'--experimental-loader',
|
||||
'ts-node/esm',
|
||||
];
|
||||
}
|
||||
|
||||
function moduleResolves(packageName: string) {
|
||||
try {
|
||||
require.resolve(packageName);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
} from '../../../utils/package-manager';
|
||||
import { joinPathFragments } from '../../../utils/path';
|
||||
import { nxVersion } from '../../../utils/versions';
|
||||
import { readFileSync, writeFileSync } from 'fs';
|
||||
import { existsSync, readFileSync, writeFileSync } from 'fs';
|
||||
|
||||
export function createNxJsonFile(
|
||||
repoRoot: string,
|
||||
@@ -223,3 +223,14 @@ export function printFinalMessage({
|
||||
].filter(Boolean),
|
||||
});
|
||||
}
|
||||
|
||||
export function isMonorepo(packageJson: PackageJson) {
|
||||
if (!!packageJson.workspaces) return true;
|
||||
|
||||
if (existsSync('pnpm-workspace.yaml') || existsSync('pnpm-workspace.yml'))
|
||||
return true;
|
||||
|
||||
if (existsSync('lerna.json')) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import { runNxSync } from '../../utils/child-process';
|
||||
import { directoryExists, readJsonFile } from '../../utils/fileutils';
|
||||
import { PackageJson } from '../../utils/package-json';
|
||||
import { nxVersion } from '../../utils/versions';
|
||||
import { isMonorepo } from './implementation/utils';
|
||||
|
||||
export interface InitArgs {
|
||||
addE2e: boolean;
|
||||
@@ -106,17 +107,6 @@ function isNestCLI(packageJson: PackageJson) {
|
||||
);
|
||||
}
|
||||
|
||||
function isMonorepo(packageJson: PackageJson) {
|
||||
if (!!packageJson.workspaces) return true;
|
||||
|
||||
if (existsSync('pnpm-workspace.yaml') || existsSync('pnpm-workspace.yml'))
|
||||
return true;
|
||||
|
||||
if (existsSync('lerna.json')) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function setupDotNxInstallation(version: string) {
|
||||
if (process.platform !== 'win32') {
|
||||
console.log(
|
||||
|
||||
@@ -10,6 +10,7 @@ import { nxVersion } from '../../utils/versions';
|
||||
import {
|
||||
addDepsToPackageJson,
|
||||
createNxJsonFile,
|
||||
isMonorepo,
|
||||
runInstall,
|
||||
updateGitIgnore,
|
||||
} from './implementation/utils';
|
||||
@@ -19,6 +20,7 @@ import { addNxToAngularCliRepo } from './implementation/angular';
|
||||
import { globWithWorkspaceContext } from '../../utils/workspace-context';
|
||||
import { connectExistingRepoToNxCloudPrompt } from '../connect/connect-to-nx-cloud';
|
||||
import { addNxToNpmRepo } from './implementation/add-nx-to-npm-repo';
|
||||
import { addNxToMonorepo } from './implementation/add-nx-to-monorepo';
|
||||
|
||||
export interface InitArgs {
|
||||
interactive: boolean;
|
||||
@@ -59,22 +61,17 @@ export async function initHandler(options: InitArgs): Promise<void> {
|
||||
return;
|
||||
}
|
||||
|
||||
const repoRoot = process.cwd();
|
||||
const cacheableOperations: string[] = [];
|
||||
createNxJsonFile(repoRoot, [], cacheableOperations, {});
|
||||
|
||||
const pmc = getPackageManagerCommand();
|
||||
|
||||
updateGitIgnore(repoRoot);
|
||||
|
||||
const detectPluginsResponse = await detectPlugins();
|
||||
|
||||
if (!detectPluginsResponse?.plugins.length) {
|
||||
// If no plugins are detected/chosen, guide users to setup
|
||||
// their targetDefaults correctly so their package scripts will work.
|
||||
await addNxToNpmRepo({
|
||||
interactive: options.interactive,
|
||||
});
|
||||
const packageJson: PackageJson = readJsonFile('package.json');
|
||||
if (isMonorepo(packageJson)) {
|
||||
await addNxToMonorepo({ interactive: options.interactive });
|
||||
} else {
|
||||
await addNxToNpmRepo({ interactive: options.interactive });
|
||||
}
|
||||
} else {
|
||||
const useNxCloud =
|
||||
options.nxCloud ??
|
||||
@@ -82,6 +79,12 @@ export async function initHandler(options: InitArgs): Promise<void> {
|
||||
? await connectExistingRepoToNxCloudPrompt()
|
||||
: false);
|
||||
|
||||
const repoRoot = process.cwd();
|
||||
const pmc = getPackageManagerCommand();
|
||||
|
||||
createNxJsonFile(repoRoot, [], [], {});
|
||||
updateGitIgnore(repoRoot);
|
||||
|
||||
addDepsToPackageJson(repoRoot, detectPluginsResponse?.plugins ?? []);
|
||||
|
||||
output.log({ title: '📦 Installing Nx' });
|
||||
|
||||
@@ -2,7 +2,7 @@ import * as memfs from 'memfs';
|
||||
|
||||
import '../../../src/internal-testing-utils/mock-fs';
|
||||
|
||||
import { TargetDefaultsPlugin } from './target-defaults-plugin';
|
||||
import { getTargetInfo, TargetDefaultsPlugin } from './target-defaults-plugin';
|
||||
import { CreateNodesContext } from '../../utils/nx-plugin';
|
||||
const {
|
||||
createNodes: [, createNodesFn],
|
||||
@@ -74,6 +74,7 @@ describe('target-defaults plugin', () => {
|
||||
"dependsOn": [
|
||||
"^build",
|
||||
],
|
||||
"executor": "nx:run-commands",
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -114,6 +115,10 @@ describe('target-defaults plugin', () => {
|
||||
"targets": {
|
||||
"test": {
|
||||
"command": "jest",
|
||||
"executor": "nx:run-script",
|
||||
"options": {
|
||||
"script": "test",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -156,6 +161,10 @@ describe('target-defaults plugin', () => {
|
||||
"targets": {
|
||||
"test": {
|
||||
"command": "jest",
|
||||
"executor": "nx:run-script",
|
||||
"options": {
|
||||
"script": "test",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -225,6 +234,10 @@ describe('target-defaults plugin', () => {
|
||||
"targets": {
|
||||
"test": {
|
||||
"command": "jest",
|
||||
"executor": "nx:run-script",
|
||||
"options": {
|
||||
"script": "test",
|
||||
},
|
||||
Symbol(ONLY_MODIFIES_EXISTING_TARGET): true,
|
||||
},
|
||||
},
|
||||
@@ -274,11 +287,13 @@ describe('target-defaults plugin', () => {
|
||||
".": {
|
||||
"targets": {
|
||||
"echo": {
|
||||
"executor": "nx:run-commands",
|
||||
"options": {
|
||||
"cwd": "{projectRoot}",
|
||||
},
|
||||
},
|
||||
"echo2": {
|
||||
"executor": "nx:run-commands",
|
||||
"options": {
|
||||
"cwd": "{projectRoot}",
|
||||
},
|
||||
@@ -336,11 +351,13 @@ describe('target-defaults plugin', () => {
|
||||
".": {
|
||||
"targets": {
|
||||
"echo": {
|
||||
"executor": "nx:run-commands",
|
||||
"options": {
|
||||
"cwd": "{projectRoot}",
|
||||
},
|
||||
},
|
||||
"echo2": {
|
||||
"executor": "nx:run-commands",
|
||||
"options": {
|
||||
"cwd": "{projectRoot}",
|
||||
},
|
||||
@@ -358,4 +375,93 @@ describe('target-defaults plugin', () => {
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('get target info', () => {
|
||||
it('should include command for single command', () => {
|
||||
const result = getTargetInfo(
|
||||
'echo',
|
||||
{
|
||||
targets: {
|
||||
echo: {
|
||||
command: 'echo hi',
|
||||
},
|
||||
},
|
||||
},
|
||||
null
|
||||
);
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
{
|
||||
"command": "echo hi",
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('should include command for run-commands', () => {
|
||||
const result = getTargetInfo(
|
||||
'echo',
|
||||
{
|
||||
targets: {
|
||||
echo: {
|
||||
executor: 'nx:run-commands',
|
||||
options: {
|
||||
command: 'echo hi',
|
||||
cwd: '{projectRoot}',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
null
|
||||
);
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
{
|
||||
"executor": "nx:run-commands",
|
||||
"options": {
|
||||
"command": "echo hi",
|
||||
},
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('should include script for run-script', () => {
|
||||
expect(
|
||||
getTargetInfo('build', null, {
|
||||
scripts: {
|
||||
build: 'echo hi',
|
||||
},
|
||||
})
|
||||
).toMatchInlineSnapshot(`
|
||||
{
|
||||
"executor": "nx:run-script",
|
||||
"options": {
|
||||
"script": "build",
|
||||
},
|
||||
}
|
||||
`);
|
||||
|
||||
expect(
|
||||
getTargetInfo('echo', null, {
|
||||
scripts: {
|
||||
build: 'echo hi',
|
||||
},
|
||||
nx: {
|
||||
targets: {
|
||||
echo: {
|
||||
executor: 'nx:run-script',
|
||||
options: {
|
||||
script: 'build',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
).toMatchInlineSnapshot(`
|
||||
{
|
||||
"executor": "nx:run-script",
|
||||
"options": {
|
||||
"script": "build",
|
||||
},
|
||||
}
|
||||
`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -88,9 +88,13 @@ export const TargetDefaultsPlugin: NxPluginV2 = {
|
||||
// Prevents `build` from overwriting `@nx/js:tsc` if both are present
|
||||
// and build is specified later in the ordering.
|
||||
if (!modifiedTargets[targetName] || targetName !== defaultSpecifier) {
|
||||
modifiedTargets[targetName] = JSON.parse(
|
||||
const defaults = JSON.parse(
|
||||
JSON.stringify(targetDefaults[defaultSpecifier])
|
||||
);
|
||||
modifiedTargets[targetName] = {
|
||||
...getTargetInfo(targetName, projectJson, packageJson),
|
||||
...defaults,
|
||||
};
|
||||
}
|
||||
// TODO: Remove this after we figure out a way to define new targets
|
||||
// in target defaults
|
||||
@@ -140,3 +144,93 @@ function readJsonOrNull<T extends Object = any>(path: string) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This fn gets target info that would make a target uniquely compatible
|
||||
* with what is described by project.json or package.json. As the merge process
|
||||
* for config happens, without this, the target defaults may be compatible
|
||||
* with a config from a plugin and then that combined target be incompatible
|
||||
* with the project json configuration resulting in the target default values
|
||||
* being scrapped. By adding enough information from the project.json / package.json,
|
||||
* we can make sure that the target after merging is compatible with the defined target.
|
||||
*/
|
||||
export function getTargetInfo(
|
||||
target: string,
|
||||
projectJson: Pick<ProjectConfiguration, 'targets'>,
|
||||
packageJson: Pick<PackageJson, 'scripts' | 'nx'>
|
||||
) {
|
||||
const projectJsonTarget = projectJson?.targets?.[target];
|
||||
const packageJsonTarget = packageJson?.nx?.targets?.[target];
|
||||
|
||||
const executor = getTargetExecutor(target, projectJson, packageJson);
|
||||
const targetOptions = {
|
||||
...packageJsonTarget?.options,
|
||||
...projectJsonTarget?.options,
|
||||
};
|
||||
|
||||
if (projectJsonTarget?.command) {
|
||||
return {
|
||||
command: projectJsonTarget?.command,
|
||||
};
|
||||
}
|
||||
|
||||
if (executor === 'nx:run-commands') {
|
||||
if (targetOptions?.command) {
|
||||
return {
|
||||
executor: 'nx:run-commands',
|
||||
options: {
|
||||
command: projectJsonTarget.options?.command,
|
||||
},
|
||||
};
|
||||
} else if (targetOptions?.commands) {
|
||||
return {
|
||||
executor: 'nx:run-commands',
|
||||
options: {
|
||||
commands: targetOptions.commands,
|
||||
},
|
||||
};
|
||||
}
|
||||
return {
|
||||
executor: 'nx:run-commands',
|
||||
};
|
||||
}
|
||||
|
||||
if (executor === 'nx:run-script') {
|
||||
return {
|
||||
executor: 'nx:run-script',
|
||||
options: {
|
||||
script: targetOptions?.script ?? target,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (executor) {
|
||||
return { executor };
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
function getTargetExecutor(
|
||||
target: string,
|
||||
projectJson: Pick<ProjectConfiguration, 'targets'>,
|
||||
packageJson: Pick<PackageJson, 'scripts' | 'nx'>
|
||||
) {
|
||||
const projectJsonTarget = projectJson?.targets?.[target];
|
||||
const packageJsonTarget = packageJson?.nx?.targets?.[target];
|
||||
const packageJsonScript = packageJson?.scripts?.[target];
|
||||
|
||||
if (projectJsonTarget?.command) {
|
||||
return 'nx:run-commands';
|
||||
}
|
||||
|
||||
if (
|
||||
!projectJsonTarget?.executor &&
|
||||
!packageJsonTarget?.executor &&
|
||||
packageJsonScript
|
||||
) {
|
||||
return 'nx:run-script';
|
||||
}
|
||||
|
||||
return projectJsonTarget?.executor ?? packageJsonTarget?.executor;
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ import { createLoaderFromCompiler } from './compiler-loaders';
|
||||
import { NormalizedNxWebpackPluginOptions } from '../nx-webpack-plugin-options';
|
||||
import TerserPlugin = require('terser-webpack-plugin');
|
||||
import nodeExternals = require('webpack-node-externals');
|
||||
import ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
|
||||
|
||||
const IGNORED_WEBPACK_WARNINGS = [
|
||||
/The comment file/i,
|
||||
@@ -223,6 +222,7 @@ function applyNxDependentConfig(
|
||||
};
|
||||
|
||||
if (!options?.skipTypeChecking) {
|
||||
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
|
||||
plugins.push(
|
||||
new ForkTsCheckerWebpackPlugin({
|
||||
typescript: {
|
||||
|
||||
Reference in New Issue
Block a user