Compare commits
22 Commits
patch-error
...
19.4.2
| Author | SHA1 | Date | |
|---|---|---|---|
| 76f145aff0 | |||
| e2eda2cd5e | |||
| 8462387245 | |||
| 95f851c4e3 | |||
| 3493ef7618 | |||
| 4eae2d20bd | |||
| 49da9b19f4 | |||
| c8c42958e3 | |||
| 8947560166 | |||
| 7dc54ff536 | |||
| 8c84ab1aa8 | |||
| 892c98fe9b | |||
| ae38180934 | |||
| d4b1a7e774 | |||
| 3ea1ecd1b2 | |||
| e4ae1e7b01 | |||
| 314c2fdf88 | |||
| 9f118e846d | |||
| a6d74286a9 | |||
| 23add04f18 | |||
| 8dc97c3ba8 | |||
| ec37b1e5e8 |
@@ -121,9 +121,9 @@ Generate a 'src/' directory for Next.js
|
||||
|
||||
Type: `string`
|
||||
|
||||
Choices: [yes, github, circleci, skip]
|
||||
Choices: [github, circleci, gitlab, azure, bitbucket, skip, yes]
|
||||
|
||||
Do you want Nx Cloud to make your CI fast?
|
||||
Which CI provider would you like to use?
|
||||
|
||||
### packageManager
|
||||
|
||||
|
||||
@@ -28,6 +28,17 @@
|
||||
"path": "/ci/intro/why-nx-cloud",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"id": "connect-to-cloud",
|
||||
"name": "Connect Nx Cloud",
|
||||
"description": "",
|
||||
"mediaImage": "",
|
||||
"file": "nx-cloud/intro/connect-to-cloud",
|
||||
"itemList": [],
|
||||
"isExternal": false,
|
||||
"path": "/ci/intro/connect-to-cloud",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"id": "tutorials",
|
||||
"name": "Tutorials",
|
||||
@@ -89,6 +100,17 @@
|
||||
"path": "/ci/intro/why-nx-cloud",
|
||||
"tags": []
|
||||
},
|
||||
"/ci/intro/connect-to-cloud": {
|
||||
"id": "connect-to-cloud",
|
||||
"name": "Connect Nx Cloud",
|
||||
"description": "",
|
||||
"mediaImage": "",
|
||||
"file": "nx-cloud/intro/connect-to-cloud",
|
||||
"itemList": [],
|
||||
"isExternal": false,
|
||||
"path": "/ci/intro/connect-to-cloud",
|
||||
"tags": []
|
||||
},
|
||||
"/ci/intro/tutorials": {
|
||||
"id": "tutorials",
|
||||
"name": "Tutorials",
|
||||
@@ -217,17 +239,6 @@
|
||||
"isExternal": false,
|
||||
"path": "/ci/features/flaky-tasks",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"id": "on-premise",
|
||||
"name": "Set up Nx Cloud On-Premise",
|
||||
"description": "Set up Nx Cloud on machines that you control",
|
||||
"mediaImage": "",
|
||||
"file": "nx-cloud/features/nx-enterprise-on-prem",
|
||||
"itemList": [],
|
||||
"isExternal": false,
|
||||
"path": "/ci/features/on-premise",
|
||||
"tags": ["on-premise"]
|
||||
}
|
||||
],
|
||||
"isExternal": false,
|
||||
@@ -300,17 +311,6 @@
|
||||
"path": "/ci/features/flaky-tasks",
|
||||
"tags": []
|
||||
},
|
||||
"/ci/features/on-premise": {
|
||||
"id": "on-premise",
|
||||
"name": "Set up Nx Cloud On-Premise",
|
||||
"description": "Set up Nx Cloud on machines that you control",
|
||||
"mediaImage": "",
|
||||
"file": "nx-cloud/features/nx-enterprise-on-prem",
|
||||
"itemList": [],
|
||||
"isExternal": false,
|
||||
"path": "/ci/features/on-premise",
|
||||
"tags": ["on-premise"]
|
||||
},
|
||||
"/ci/concepts": {
|
||||
"id": "concepts",
|
||||
"name": "Concepts",
|
||||
@@ -425,17 +425,6 @@
|
||||
"mediaImage": "",
|
||||
"file": "",
|
||||
"itemList": [
|
||||
{
|
||||
"id": "connect-to-cloud",
|
||||
"name": "Connect Nx Cloud",
|
||||
"description": "",
|
||||
"mediaImage": "",
|
||||
"file": "nx-cloud/recipes/connect-to-cloud",
|
||||
"itemList": [],
|
||||
"isExternal": false,
|
||||
"path": "/ci/recipes/set-up/connect-to-cloud",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"id": "monorepo-ci-azure",
|
||||
"name": "Setting up Azure Pipelines",
|
||||
@@ -622,6 +611,17 @@
|
||||
"mediaImage": "",
|
||||
"file": "",
|
||||
"itemList": [
|
||||
{
|
||||
"id": "on-premise",
|
||||
"name": "Set up Nx Cloud On-Premise",
|
||||
"description": "Set up Nx Cloud on machines that you control",
|
||||
"mediaImage": "",
|
||||
"file": "nx-cloud/features/nx-enterprise-on-prem",
|
||||
"itemList": [],
|
||||
"isExternal": false,
|
||||
"path": "/ci/recipes/enterprise/on-premise/on-premise",
|
||||
"tags": ["on-premise"]
|
||||
},
|
||||
{
|
||||
"id": "auth-single-admin",
|
||||
"name": "Authenticate with a Single Admin",
|
||||
@@ -855,17 +855,6 @@
|
||||
"mediaImage": "",
|
||||
"file": "",
|
||||
"itemList": [
|
||||
{
|
||||
"id": "connect-to-cloud",
|
||||
"name": "Connect Nx Cloud",
|
||||
"description": "",
|
||||
"mediaImage": "",
|
||||
"file": "nx-cloud/recipes/connect-to-cloud",
|
||||
"itemList": [],
|
||||
"isExternal": false,
|
||||
"path": "/ci/recipes/set-up/connect-to-cloud",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"id": "monorepo-ci-azure",
|
||||
"name": "Setting up Azure Pipelines",
|
||||
@@ -937,17 +926,6 @@
|
||||
"path": "/ci/recipes/set-up",
|
||||
"tags": ["distribute-task-execution"]
|
||||
},
|
||||
"/ci/recipes/set-up/connect-to-cloud": {
|
||||
"id": "connect-to-cloud",
|
||||
"name": "Connect Nx Cloud",
|
||||
"description": "",
|
||||
"mediaImage": "",
|
||||
"file": "nx-cloud/recipes/connect-to-cloud",
|
||||
"itemList": [],
|
||||
"isExternal": false,
|
||||
"path": "/ci/recipes/set-up/connect-to-cloud",
|
||||
"tags": []
|
||||
},
|
||||
"/ci/recipes/set-up/monorepo-ci-azure": {
|
||||
"id": "monorepo-ci-azure",
|
||||
"name": "Setting up Azure Pipelines",
|
||||
@@ -1206,6 +1184,17 @@
|
||||
"mediaImage": "",
|
||||
"file": "",
|
||||
"itemList": [
|
||||
{
|
||||
"id": "on-premise",
|
||||
"name": "Set up Nx Cloud On-Premise",
|
||||
"description": "Set up Nx Cloud on machines that you control",
|
||||
"mediaImage": "",
|
||||
"file": "nx-cloud/features/nx-enterprise-on-prem",
|
||||
"itemList": [],
|
||||
"isExternal": false,
|
||||
"path": "/ci/recipes/enterprise/on-premise/on-premise",
|
||||
"tags": ["on-premise"]
|
||||
},
|
||||
{
|
||||
"id": "auth-single-admin",
|
||||
"name": "Authenticate with a Single Admin",
|
||||
@@ -1400,6 +1389,17 @@
|
||||
"mediaImage": "",
|
||||
"file": "",
|
||||
"itemList": [
|
||||
{
|
||||
"id": "on-premise",
|
||||
"name": "Set up Nx Cloud On-Premise",
|
||||
"description": "Set up Nx Cloud on machines that you control",
|
||||
"mediaImage": "",
|
||||
"file": "nx-cloud/features/nx-enterprise-on-prem",
|
||||
"itemList": [],
|
||||
"isExternal": false,
|
||||
"path": "/ci/recipes/enterprise/on-premise/on-premise",
|
||||
"tags": ["on-premise"]
|
||||
},
|
||||
{
|
||||
"id": "auth-single-admin",
|
||||
"name": "Authenticate with a Single Admin",
|
||||
@@ -1504,6 +1504,17 @@
|
||||
"path": "/ci/recipes/enterprise/on-premise",
|
||||
"tags": []
|
||||
},
|
||||
"/ci/recipes/enterprise/on-premise/on-premise": {
|
||||
"id": "on-premise",
|
||||
"name": "Set up Nx Cloud On-Premise",
|
||||
"description": "Set up Nx Cloud on machines that you control",
|
||||
"mediaImage": "",
|
||||
"file": "nx-cloud/features/nx-enterprise-on-prem",
|
||||
"itemList": [],
|
||||
"isExternal": false,
|
||||
"path": "/ci/recipes/enterprise/on-premise/on-premise",
|
||||
"tags": ["on-premise"]
|
||||
},
|
||||
"/ci/recipes/enterprise/on-premise/auth-single-admin": {
|
||||
"id": "auth-single-admin",
|
||||
"name": "Authenticate with a Single Admin",
|
||||
|
||||
@@ -284,7 +284,7 @@
|
||||
"disableCollapsible": false
|
||||
},
|
||||
{
|
||||
"name": "Explore your Workspace",
|
||||
"name": "Explore Your Workspace",
|
||||
"path": "/features/explore-graph",
|
||||
"id": "explore-graph",
|
||||
"isExternal": false,
|
||||
@@ -376,14 +376,6 @@
|
||||
"isExternal": true,
|
||||
"children": [],
|
||||
"disableCollapsible": false
|
||||
},
|
||||
{
|
||||
"name": "Set up Nx Cloud On-Premise",
|
||||
"path": "/ci/features/on-premise",
|
||||
"id": "on-premise",
|
||||
"isExternal": true,
|
||||
"children": [],
|
||||
"disableCollapsible": false
|
||||
}
|
||||
],
|
||||
"disableCollapsible": false
|
||||
@@ -408,7 +400,7 @@
|
||||
"disableCollapsible": false
|
||||
},
|
||||
{
|
||||
"name": "Explore your Workspace",
|
||||
"name": "Explore Your Workspace",
|
||||
"path": "/features/explore-graph",
|
||||
"id": "explore-graph",
|
||||
"isExternal": false,
|
||||
@@ -500,14 +492,6 @@
|
||||
"isExternal": true,
|
||||
"children": [],
|
||||
"disableCollapsible": false
|
||||
},
|
||||
{
|
||||
"name": "Set up Nx Cloud On-Premise",
|
||||
"path": "/ci/features/on-premise",
|
||||
"id": "on-premise",
|
||||
"isExternal": true,
|
||||
"children": [],
|
||||
"disableCollapsible": false
|
||||
}
|
||||
],
|
||||
"disableCollapsible": false
|
||||
@@ -560,14 +544,6 @@
|
||||
"children": [],
|
||||
"disableCollapsible": false
|
||||
},
|
||||
{
|
||||
"name": "Set up Nx Cloud On-Premise",
|
||||
"path": "/ci/features/on-premise",
|
||||
"id": "on-premise",
|
||||
"isExternal": true,
|
||||
"children": [],
|
||||
"disableCollapsible": false
|
||||
},
|
||||
{
|
||||
"name": "Concepts",
|
||||
"path": "/concepts",
|
||||
@@ -5169,6 +5145,14 @@
|
||||
"children": [],
|
||||
"disableCollapsible": false
|
||||
},
|
||||
{
|
||||
"name": "Connect Nx Cloud",
|
||||
"path": "/ci/intro/connect-to-cloud",
|
||||
"id": "connect-to-cloud",
|
||||
"isExternal": false,
|
||||
"children": [],
|
||||
"disableCollapsible": false
|
||||
},
|
||||
{
|
||||
"name": "Tutorials",
|
||||
"path": "/ci/intro/tutorials",
|
||||
@@ -5213,6 +5197,14 @@
|
||||
"children": [],
|
||||
"disableCollapsible": false
|
||||
},
|
||||
{
|
||||
"name": "Connect Nx Cloud",
|
||||
"path": "/ci/intro/connect-to-cloud",
|
||||
"id": "connect-to-cloud",
|
||||
"isExternal": false,
|
||||
"children": [],
|
||||
"disableCollapsible": false
|
||||
},
|
||||
{
|
||||
"name": "Tutorials",
|
||||
"path": "/ci/intro/tutorials",
|
||||
@@ -5307,14 +5299,6 @@
|
||||
"isExternal": false,
|
||||
"children": [],
|
||||
"disableCollapsible": false
|
||||
},
|
||||
{
|
||||
"name": "Set up Nx Cloud On-Premise",
|
||||
"path": "/ci/features/on-premise",
|
||||
"id": "on-premise",
|
||||
"isExternal": false,
|
||||
"children": [],
|
||||
"disableCollapsible": false
|
||||
}
|
||||
],
|
||||
"disableCollapsible": false
|
||||
@@ -5367,14 +5351,6 @@
|
||||
"children": [],
|
||||
"disableCollapsible": false
|
||||
},
|
||||
{
|
||||
"name": "Set up Nx Cloud On-Premise",
|
||||
"path": "/ci/features/on-premise",
|
||||
"id": "on-premise",
|
||||
"isExternal": false,
|
||||
"children": [],
|
||||
"disableCollapsible": false
|
||||
},
|
||||
{
|
||||
"name": "Concepts",
|
||||
"path": "/ci/concepts",
|
||||
@@ -5460,14 +5436,6 @@
|
||||
"id": "set-up",
|
||||
"isExternal": false,
|
||||
"children": [
|
||||
{
|
||||
"name": "Connect Nx Cloud",
|
||||
"path": "/ci/recipes/set-up/connect-to-cloud",
|
||||
"id": "connect-to-cloud",
|
||||
"isExternal": false,
|
||||
"children": [],
|
||||
"disableCollapsible": false
|
||||
},
|
||||
{
|
||||
"name": "Setting up Azure Pipelines",
|
||||
"path": "/ci/recipes/set-up/monorepo-ci-azure",
|
||||
@@ -5605,6 +5573,14 @@
|
||||
"id": "on-premise",
|
||||
"isExternal": false,
|
||||
"children": [
|
||||
{
|
||||
"name": "Set up Nx Cloud On-Premise",
|
||||
"path": "/ci/recipes/enterprise/on-premise/on-premise",
|
||||
"id": "on-premise",
|
||||
"isExternal": false,
|
||||
"children": [],
|
||||
"disableCollapsible": false
|
||||
},
|
||||
{
|
||||
"name": "Authenticate with a Single Admin",
|
||||
"path": "/ci/recipes/enterprise/on-premise/auth-single-admin",
|
||||
@@ -5774,14 +5750,6 @@
|
||||
"id": "set-up",
|
||||
"isExternal": false,
|
||||
"children": [
|
||||
{
|
||||
"name": "Connect Nx Cloud",
|
||||
"path": "/ci/recipes/set-up/connect-to-cloud",
|
||||
"id": "connect-to-cloud",
|
||||
"isExternal": false,
|
||||
"children": [],
|
||||
"disableCollapsible": false
|
||||
},
|
||||
{
|
||||
"name": "Setting up Azure Pipelines",
|
||||
"path": "/ci/recipes/set-up/monorepo-ci-azure",
|
||||
@@ -5833,14 +5801,6 @@
|
||||
],
|
||||
"disableCollapsible": false
|
||||
},
|
||||
{
|
||||
"name": "Connect Nx Cloud",
|
||||
"path": "/ci/recipes/set-up/connect-to-cloud",
|
||||
"id": "connect-to-cloud",
|
||||
"isExternal": false,
|
||||
"children": [],
|
||||
"disableCollapsible": false
|
||||
},
|
||||
{
|
||||
"name": "Setting up Azure Pipelines",
|
||||
"path": "/ci/recipes/set-up/monorepo-ci-azure",
|
||||
@@ -6031,6 +5991,14 @@
|
||||
"id": "on-premise",
|
||||
"isExternal": false,
|
||||
"children": [
|
||||
{
|
||||
"name": "Set up Nx Cloud On-Premise",
|
||||
"path": "/ci/recipes/enterprise/on-premise/on-premise",
|
||||
"id": "on-premise",
|
||||
"isExternal": false,
|
||||
"children": [],
|
||||
"disableCollapsible": false
|
||||
},
|
||||
{
|
||||
"name": "Authenticate with a Single Admin",
|
||||
"path": "/ci/recipes/enterprise/on-premise/auth-single-admin",
|
||||
@@ -6172,6 +6140,14 @@
|
||||
"id": "on-premise",
|
||||
"isExternal": false,
|
||||
"children": [
|
||||
{
|
||||
"name": "Set up Nx Cloud On-Premise",
|
||||
"path": "/ci/recipes/enterprise/on-premise/on-premise",
|
||||
"id": "on-premise",
|
||||
"isExternal": false,
|
||||
"children": [],
|
||||
"disableCollapsible": false
|
||||
},
|
||||
{
|
||||
"name": "Authenticate with a Single Admin",
|
||||
"path": "/ci/recipes/enterprise/on-premise/auth-single-admin",
|
||||
@@ -6247,6 +6223,14 @@
|
||||
],
|
||||
"disableCollapsible": false
|
||||
},
|
||||
{
|
||||
"name": "Set up Nx Cloud On-Premise",
|
||||
"path": "/ci/recipes/enterprise/on-premise/on-premise",
|
||||
"id": "on-premise",
|
||||
"isExternal": false,
|
||||
"children": [],
|
||||
"disableCollapsible": false
|
||||
},
|
||||
{
|
||||
"name": "Authenticate with a Single Admin",
|
||||
"path": "/ci/recipes/enterprise/on-premise/auth-single-admin",
|
||||
|
||||
@@ -385,7 +385,7 @@
|
||||
},
|
||||
{
|
||||
"id": "explore-graph",
|
||||
"name": "Explore your Workspace",
|
||||
"name": "Explore Your Workspace",
|
||||
"description": "",
|
||||
"mediaImage": "",
|
||||
"file": "shared/features/explore-graph",
|
||||
@@ -510,17 +510,6 @@
|
||||
"isExternal": true,
|
||||
"path": "/ci/features/flaky-tasks",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"id": "on-premise",
|
||||
"name": "Set up Nx Cloud On-Premise",
|
||||
"description": "Set up Nx Cloud on machines that you control",
|
||||
"mediaImage": "",
|
||||
"file": "",
|
||||
"itemList": [],
|
||||
"isExternal": true,
|
||||
"path": "/ci/features/on-premise",
|
||||
"tags": []
|
||||
}
|
||||
],
|
||||
"isExternal": false,
|
||||
@@ -556,7 +545,7 @@
|
||||
},
|
||||
"/features/explore-graph": {
|
||||
"id": "explore-graph",
|
||||
"name": "Explore your Workspace",
|
||||
"name": "Explore Your Workspace",
|
||||
"description": "",
|
||||
"mediaImage": "",
|
||||
"file": "shared/features/explore-graph",
|
||||
@@ -681,17 +670,6 @@
|
||||
"isExternal": true,
|
||||
"path": "/ci/features/flaky-tasks",
|
||||
"tags": []
|
||||
},
|
||||
{
|
||||
"id": "on-premise",
|
||||
"name": "Set up Nx Cloud On-Premise",
|
||||
"description": "Set up Nx Cloud on machines that you control",
|
||||
"mediaImage": "",
|
||||
"file": "",
|
||||
"itemList": [],
|
||||
"isExternal": true,
|
||||
"path": "/ci/features/on-premise",
|
||||
"tags": []
|
||||
}
|
||||
],
|
||||
"isExternal": false,
|
||||
@@ -764,17 +742,6 @@
|
||||
"path": "/ci/features/flaky-tasks",
|
||||
"tags": []
|
||||
},
|
||||
"/ci/features/on-premise": {
|
||||
"id": "on-premise",
|
||||
"name": "Set up Nx Cloud On-Premise",
|
||||
"description": "Set up Nx Cloud on machines that you control",
|
||||
"mediaImage": "",
|
||||
"file": "",
|
||||
"itemList": [],
|
||||
"isExternal": true,
|
||||
"path": "/ci/features/on-premise",
|
||||
"tags": []
|
||||
},
|
||||
"/concepts": {
|
||||
"id": "concepts",
|
||||
"name": "Concepts",
|
||||
|
||||
@@ -248,7 +248,7 @@
|
||||
"description": "",
|
||||
"file": "shared/features/explore-graph",
|
||||
"id": "explore-graph",
|
||||
"name": "Explore your Workspace",
|
||||
"name": "Explore Your Workspace",
|
||||
"path": "/features/explore-graph"
|
||||
},
|
||||
{
|
||||
@@ -1134,7 +1134,7 @@
|
||||
"file": "nx-cloud/features/nx-enterprise-on-prem",
|
||||
"id": "on-premise",
|
||||
"name": "Set up Nx Cloud On-Premise",
|
||||
"path": "/ci/features/on-premise"
|
||||
"path": "/ci/recipes/enterprise/on-premise/on-premise"
|
||||
}
|
||||
],
|
||||
"docker": [
|
||||
|
||||
@@ -121,9 +121,9 @@ Generate a 'src/' directory for Next.js
|
||||
|
||||
Type: `string`
|
||||
|
||||
Choices: [yes, github, circleci, skip]
|
||||
Choices: [github, circleci, gitlab, azure, bitbucket, skip, yes]
|
||||
|
||||
Do you want Nx Cloud to make your CI fast?
|
||||
Which CI provider would you like to use?
|
||||
|
||||
### packageManager
|
||||
|
||||
|
||||
+13
-21
@@ -99,7 +99,7 @@
|
||||
"file": "shared/features/cache-task-results"
|
||||
},
|
||||
{
|
||||
"name": "Explore your Workspace",
|
||||
"name": "Explore Your Workspace",
|
||||
"id": "explore-graph",
|
||||
"tags": ["explore-graph"],
|
||||
"file": "shared/features/explore-graph"
|
||||
@@ -179,14 +179,6 @@
|
||||
"file": "",
|
||||
"path": "/ci/features/flaky-tasks",
|
||||
"isExternal": true
|
||||
},
|
||||
{
|
||||
"name": "Set up Nx Cloud On-Premise",
|
||||
"id": "on-premise",
|
||||
"description": "Set up Nx Cloud on machines that you control",
|
||||
"file": "",
|
||||
"path": "/ci/features/on-premise",
|
||||
"isExternal": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1572,6 +1564,11 @@
|
||||
"id": "why-nx-cloud",
|
||||
"file": "nx-cloud/intro/why-nx-cloud"
|
||||
},
|
||||
{
|
||||
"name": "Connect Nx Cloud",
|
||||
"id": "connect-to-cloud",
|
||||
"file": "nx-cloud/intro/connect-to-cloud"
|
||||
},
|
||||
{
|
||||
"name": "Tutorials",
|
||||
"id": "tutorials",
|
||||
@@ -1633,13 +1630,6 @@
|
||||
"id": "flaky-tasks",
|
||||
"description": "Learn how Nx Cloud is able to automatically identify flaky tasks and re-run them for you.",
|
||||
"file": "nx-cloud/features/flaky-tasks"
|
||||
},
|
||||
{
|
||||
"name": "Set up Nx Cloud On-Premise",
|
||||
"description": "Set up Nx Cloud on machines that you control",
|
||||
"id": "on-premise",
|
||||
"tags": ["on-premise"],
|
||||
"file": "nx-cloud/features/nx-enterprise-on-prem"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -1684,11 +1674,6 @@
|
||||
"description": "Learn how to set up Nx Cloud for your workspace.",
|
||||
"tags": ["distribute-task-execution"],
|
||||
"itemList": [
|
||||
{
|
||||
"name": "Connect Nx Cloud",
|
||||
"id": "connect-to-cloud",
|
||||
"file": "nx-cloud/recipes/connect-to-cloud"
|
||||
},
|
||||
{
|
||||
"name": "Setting up Azure Pipelines",
|
||||
"id": "monorepo-ci-azure",
|
||||
@@ -1780,6 +1765,13 @@
|
||||
"id": "on-premise",
|
||||
"description": "Manage an on-premise installation of Nx Cloud",
|
||||
"itemList": [
|
||||
{
|
||||
"name": "Set up Nx Cloud On-Premise",
|
||||
"description": "Set up Nx Cloud on machines that you control",
|
||||
"id": "on-premise",
|
||||
"tags": ["on-premise"],
|
||||
"file": "nx-cloud/features/nx-enterprise-on-prem"
|
||||
},
|
||||
{
|
||||
"name": "Authenticate with a Single Admin",
|
||||
"id": "auth-single-admin",
|
||||
|
||||
@@ -14,6 +14,10 @@ Implementing an efficient CI setup for monorepos - while crucial - can be challe
|
||||
|
||||
[Create an account on Nx Cloud](https://cloud.nx.app) and connect your repository.
|
||||
|
||||
```shell
|
||||
npx nx connect
|
||||
```
|
||||
|
||||
## Learn about Nx on CI
|
||||
|
||||
{% cards cols="2" lgCols="4" mdCols="4" smCols="2" %}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
# Connect to Nx Cloud
|
||||
|
||||
Nx Cloud directly integrates with your existing CI setup.
|
||||
|
||||

|
||||
|
||||
In a nutshell, here's how this works:
|
||||
|
||||
**Step 1: Connect your workspace to Nx Cloud**
|
||||
|
||||
This can be done by signing up on [nx.app](https://nx.app) and then connecting to your git repository.
|
||||
|
||||
```shell
|
||||
npx nx connect
|
||||
```
|
||||
|
||||
**Step 2: Your CI script triggers Nx Cloud**
|
||||
|
||||
```yml
|
||||
- name: Start CI run
|
||||
run: 'npx nx-cloud start-ci-run --distribute-on="8 linux-medium-js"'
|
||||
```
|
||||
|
||||
Let us generate the workflow file for you, if you don't already have one.
|
||||
|
||||
```shell
|
||||
npx nx g ci-workflow
|
||||
```
|
||||
|
||||
Or, check out our [recipes for the various CI providers](/ci/recipes/set-up).
|
||||
|
||||
**Step 3: Run your Nx commands as usual**
|
||||
|
||||
```yml
|
||||
- run: npx nx-cloud record -- nx format:check
|
||||
- run: npx nx affected -t lint test build
|
||||
- run: npx nx affected -t e2e-ci --parallel 1
|
||||
```
|
||||
|
||||
All these commands are automatically picked up by Nx Cloud, split up into smaller tasks and distributed across the specified number of machines.
|
||||
|
||||
**Step 4: All results are played back automatically**
|
||||
|
||||
Nx Cloud automatically plays back all results to your CI system, as if distribution never happened. You can continue doing post-processing on the results, like uploading test reports, deploying artifacts etc.
|
||||
@@ -21,39 +21,9 @@ All this is possible because Nx Cloud directly integrates with Nx and has knowle
|
||||
|
||||
Read more about individual features of Nx Cloud in the [features section](/ci/features).
|
||||
|
||||
## How does Nx Cloud integrate into my existing CI setup
|
||||
## Integrate Nx Cloud into my CI setup
|
||||
|
||||
Nx Cloud directly integrates with your existing CI setup.
|
||||
|
||||

|
||||
|
||||
In a nutshell, here's how this works:
|
||||
|
||||
**Step 1: Connect your workspace to Nx Cloud**
|
||||
|
||||
This can be done by signing up on [nx.app](https://nx.app) and then connecting to your git repository. Read more about all the details [on this doc page](/ci/recipes/set-up/connect-to-cloud).
|
||||
|
||||
**Step 2: Your CI script triggers Nx Cloud**
|
||||
|
||||
```yml
|
||||
- name: Start CI run
|
||||
run: 'npx nx-cloud start-ci-run --distribute-on="8 linux-medium-js"'
|
||||
```
|
||||
|
||||
Check out our [recipes for the various CI providers](/ci/recipes/set-up).
|
||||
|
||||
**Step 3: Run your Nx command as usual**
|
||||
|
||||
```yml
|
||||
- run: npx nx-cloud record -- nx format:check
|
||||
- run: npx nx affected -t lint test build e2e-ci
|
||||
```
|
||||
|
||||
All these commands are automatically picked up by Nx Cloud, split up into smaller tasks and distributed across the specified number of machines.
|
||||
|
||||
**Step 4: All results are played back automatically**
|
||||
|
||||
Nx Cloud automatically plays back all results to your CI system, as if distribution never happened. You can continue doing post-processing on the results, like uploading test reports, deploying artifacts etc.
|
||||
Ready to experience fast CI? Read the [connect to Nx Cloud](/ci/intro/connect-to-cloud) page for more details.
|
||||
|
||||
## Learn more
|
||||
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
# Connect to Nx Cloud
|
||||
|
||||
Create an account on [nx.app](https://nx.app). There are several ways to connect your repository to Nx Cloud.
|
||||
|
||||
#### Connect Directly Through GitHub
|
||||
|
||||
If your repository is hosted on GitHub, we recommend you create an Nx Cloud organization based on your GitHub organization.
|
||||
|
||||

|
||||
|
||||
After that, connect you repository.
|
||||
|
||||

|
||||
|
||||
This will send a pull request to your repository that will add the `nxCloudAccessToken` property to `nx.json`.
|
||||
|
||||

|
||||
|
||||
This wires up all the CI for you and configures access. Folks who can see your repository can see your workspace on nx.app.
|
||||
|
||||
## Manually Connect Your Workspace
|
||||
|
||||
If your repository is hosted on a different source control provider, you can also connect to Nx Cloud manually. You'll need to add a source control integration later to enable [Nx Agents](/ci/features/distribute-task-execution).
|
||||
|
||||
Run the following command in your repository:
|
||||
|
||||
```shell
|
||||
pnpm nx connect
|
||||
```
|
||||
|
||||
Click the link in the terminal to claim your workspace on [nx.app](https://nx.app).
|
||||
|
||||
The command generates an `nxCloudAccessToken` property inside of `nx.json`. This is a read-only token that should be committed to the repository.
|
||||
@@ -10,7 +10,7 @@ In this diagram, Teika runs the build once on his machine, then CI, Kimiko and J
|
||||
|
||||
## Setting Up Nx Cloud
|
||||
|
||||
To use **Nx Replay** you need to connect your workspace to Nx Cloud. See the [connect to Nx Cloud recipe](/ci/recipes/set-up/connect-to-cloud).
|
||||
To use **Nx Replay** you need to connect your workspace to Nx Cloud. See the [connect to Nx Cloud recipe](/ci/intro/connect-to-cloud).
|
||||
|
||||
## See Remote Caching in Action
|
||||
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 458 KiB |
@@ -16,7 +16,7 @@
|
||||
- [Features](/features)
|
||||
- [Run Tasks](/features/run-tasks)
|
||||
- [Cache Task Results](/features/cache-task-results)
|
||||
- [Explore your Workspace](/features/explore-graph)
|
||||
- [Explore Your Workspace](/features/explore-graph)
|
||||
- [Generate Code](/features/generate-code)
|
||||
- [Automate Updating Dependencies](/features/automate-updating-dependencies)
|
||||
- [Enforce Module Boundaries](/features/enforce-module-boundaries)
|
||||
@@ -245,6 +245,7 @@
|
||||
- [Intro](/ci/intro)
|
||||
- [CI with Nx](/ci/intro/ci-with-nx)
|
||||
- [Why Nx Cloud?](/ci/intro/why-nx-cloud)
|
||||
- [Connect Nx Cloud](/ci/intro/connect-to-cloud)
|
||||
- [Tutorials](/ci/intro/tutorials)
|
||||
- [Circle CI with Nx](/ci/intro/tutorials/circle)
|
||||
- [GitHub Actions with Nx](/ci/intro/tutorials/github-actions)
|
||||
@@ -255,7 +256,6 @@
|
||||
- [Dynamically Allocate Agents](/ci/features/dynamic-agents)
|
||||
- [Automatically Split E2E Tasks (Atomizer)](/ci/features/split-e2e-tasks)
|
||||
- [Identify and Re-run Flaky Tasks](/ci/features/flaky-tasks)
|
||||
- [Set up Nx Cloud On-Premise](/ci/features/on-premise)
|
||||
- [Concepts](/ci/concepts)
|
||||
- [The Building Blocks of Fast CI](/ci/concepts/building-blocks-fast-ci)
|
||||
- [Reduce Wasted Time in CI](/ci/concepts/reduce-waste)
|
||||
@@ -263,7 +263,6 @@
|
||||
- [Cache Security](/ci/concepts/cache-security)
|
||||
- [Recipes](/ci/recipes)
|
||||
- [Set Up CI](/ci/recipes/set-up)
|
||||
- [Connect Nx Cloud](/ci/recipes/set-up/connect-to-cloud)
|
||||
- [Setting up Azure Pipelines](/ci/recipes/set-up/monorepo-ci-azure)
|
||||
- [Setting up CircleCI](/ci/recipes/set-up/monorepo-ci-circle-ci)
|
||||
- [Setting up GitHub Actions](/ci/recipes/set-up/monorepo-ci-github-actions)
|
||||
@@ -281,6 +280,7 @@
|
||||
- [Enable Azure DevOps PR Integration](/ci/recipes/source-control-integration/azure-devops)
|
||||
- [Enterprise](/ci/recipes/enterprise)
|
||||
- [On-Premise](/ci/recipes/enterprise/on-premise)
|
||||
- [Set up Nx Cloud On-Premise](/ci/recipes/enterprise/on-premise/on-premise)
|
||||
- [Authenticate with a Single Admin](/ci/recipes/enterprise/on-premise/auth-single-admin)
|
||||
- [Authenticate with GitHub](/ci/recipes/enterprise/on-premise/auth-github)
|
||||
- [On-Prem VM Setup](/ci/recipes/enterprise/on-premise/ami-setup)
|
||||
|
||||
@@ -509,7 +509,7 @@ export default App;
|
||||
|
||||
Serving your app (`nx serve react-store`) and then navigating to `/products` should give you the following result:
|
||||
|
||||

|
||||

|
||||
|
||||
Let's apply the same for our `orders` library.
|
||||
|
||||
@@ -1026,9 +1026,7 @@ NX Ran target lint for 7 projects (2s)
|
||||
- nx run products:lint
|
||||
```
|
||||
|
||||
If you have the ESLint plugin installed in your IDE you should immediately see an error:
|
||||
|
||||

|
||||
If you have the ESLint plugin installed in your IDE you should also immediately see an error.
|
||||
|
||||
Learn more about how to [enforce module boundaries](/features/enforce-module-boundaries).
|
||||
|
||||
|
||||
@@ -0,0 +1,248 @@
|
||||
{
|
||||
"project": {
|
||||
"name": "react-app",
|
||||
"type": "lib",
|
||||
"data": {
|
||||
"root": ".",
|
||||
"targets": {
|
||||
"vite:build": {
|
||||
"options": {
|
||||
"cwd": ".",
|
||||
"command": "vite build"
|
||||
},
|
||||
"cache": true,
|
||||
"dependsOn": ["^vite:build"],
|
||||
"inputs": [
|
||||
"default",
|
||||
"^default",
|
||||
{
|
||||
"externalDependencies": ["vite"]
|
||||
}
|
||||
],
|
||||
"outputs": ["{projectRoot}/dist"],
|
||||
"executor": "nx:run-commands",
|
||||
"configurations": {}
|
||||
},
|
||||
"build": {
|
||||
"executor": "nx:run-script",
|
||||
"metadata": {
|
||||
"scriptContent": "nx vite:build",
|
||||
"runCommand": "npm run build"
|
||||
},
|
||||
"cache": true,
|
||||
"dependsOn": ["typecheck"],
|
||||
"options": {
|
||||
"script": "build"
|
||||
},
|
||||
"configurations": {}
|
||||
}
|
||||
},
|
||||
"sourceRoot": ".",
|
||||
"name": "react-app",
|
||||
"projectType": "library",
|
||||
"metadata": {
|
||||
"targetGroups": {
|
||||
"NPM Scripts": ["build"]
|
||||
}
|
||||
},
|
||||
"implicitDependencies": [],
|
||||
"tags": []
|
||||
}
|
||||
},
|
||||
"sourceMap": {
|
||||
"root": ["package.json", "nx/core/package-json-workspaces"],
|
||||
"targets": ["vite.config.ts", "@nx/vite/plugin"],
|
||||
"targets.vite:build": ["vite.config.ts", "@nx/vite/plugin"],
|
||||
"targets.vite:build.options": ["vite.config.ts", "@nx/vite/plugin"],
|
||||
"targets.vite:build.cache": ["vite.config.ts", "@nx/vite/plugin"],
|
||||
"targets.vite:build.dependsOn": ["vite.config.ts", "@nx/vite/plugin"],
|
||||
"targets.vite:build.inputs": ["vite.config.ts", "@nx/vite/plugin"],
|
||||
"targets.vite:build.outputs": ["vite.config.ts", "@nx/vite/plugin"],
|
||||
"targets.vite:build.executor": ["vite.config.ts", "@nx/vite/plugin"],
|
||||
"targets.vite:build.options.cwd": ["vite.config.ts", "@nx/vite/plugin"],
|
||||
"targets.vite:build.options.command": ["vite.config.ts", "@nx/vite/plugin"],
|
||||
"targets.serve": ["vite.config.ts", "@nx/vite/plugin"],
|
||||
"targets.serve.options": ["vite.config.ts", "@nx/vite/plugin"],
|
||||
"targets.serve.executor": ["vite.config.ts", "@nx/vite/plugin"],
|
||||
"targets.serve.options.cwd": ["vite.config.ts", "@nx/vite/plugin"],
|
||||
"targets.serve.options.command": ["vite.config.ts", "@nx/vite/plugin"],
|
||||
"targets.vite:preview": ["vite.config.ts", "@nx/vite/plugin"],
|
||||
"targets.vite:preview.options": ["vite.config.ts", "@nx/vite/plugin"],
|
||||
"targets.vite:preview.executor": ["vite.config.ts", "@nx/vite/plugin"],
|
||||
"targets.vite:preview.options.cwd": ["vite.config.ts", "@nx/vite/plugin"],
|
||||
"targets.vite:preview.options.command": [
|
||||
"vite.config.ts",
|
||||
"@nx/vite/plugin"
|
||||
],
|
||||
"targets.serve-static": ["vite.config.ts", "@nx/vite/plugin"],
|
||||
"targets.serve-static.executor": ["vite.config.ts", "@nx/vite/plugin"],
|
||||
"targets.serve-static.options": ["vite.config.ts", "@nx/vite/plugin"],
|
||||
"targets.serve-static.options.buildTarget": [
|
||||
"vite.config.ts",
|
||||
"@nx/vite/plugin"
|
||||
],
|
||||
"targets.serve-static.options.spa": ["vite.config.ts", "@nx/vite/plugin"],
|
||||
"targets.eslint:lint": [".eslintrc.cjs", "@nx/eslint/plugin"],
|
||||
"targets.eslint:lint.cache": [".eslintrc.cjs", "@nx/eslint/plugin"],
|
||||
"targets.eslint:lint.options": [".eslintrc.cjs", "@nx/eslint/plugin"],
|
||||
"targets.eslint:lint.inputs": [".eslintrc.cjs", "@nx/eslint/plugin"],
|
||||
"targets.eslint:lint.outputs": [".eslintrc.cjs", "@nx/eslint/plugin"],
|
||||
"targets.eslint:lint.executor": [".eslintrc.cjs", "@nx/eslint/plugin"],
|
||||
"targets.eslint:lint.options.cwd": [".eslintrc.cjs", "@nx/eslint/plugin"],
|
||||
"targets.eslint:lint.options.command": [
|
||||
".eslintrc.cjs",
|
||||
"@nx/eslint/plugin"
|
||||
],
|
||||
"targets.typecheck": ["package.json", "nx/core/package-json-workspaces"],
|
||||
"targets.typecheck.executor": [
|
||||
"package.json",
|
||||
"nx/core/package-json-workspaces"
|
||||
],
|
||||
"targets.typecheck.options": [
|
||||
"package.json",
|
||||
"nx/core/package-json-workspaces"
|
||||
],
|
||||
"targets.typecheck.metadata": [
|
||||
"package.json",
|
||||
"nx/core/package-json-workspaces"
|
||||
],
|
||||
"targets.typecheck.cache": ["nx.json", "nx/core/target-defaults"],
|
||||
"targets.typecheck.options.script": [
|
||||
"package.json",
|
||||
"nx/core/package-json-workspaces"
|
||||
],
|
||||
"targets.typecheck.metadata.scriptContent": [
|
||||
"package.json",
|
||||
"nx/core/package-json-workspaces"
|
||||
],
|
||||
"targets.typecheck.metadata.runCommand": [
|
||||
"package.json",
|
||||
"nx/core/package-json-workspaces"
|
||||
],
|
||||
"targets.build": ["package.json", "nx/core/package-json-workspaces"],
|
||||
"targets.build.executor": [
|
||||
"package.json",
|
||||
"nx/core/package-json-workspaces"
|
||||
],
|
||||
"targets.build.options": [
|
||||
"package.json",
|
||||
"nx/core/package-json-workspaces"
|
||||
],
|
||||
"targets.build.metadata": [
|
||||
"package.json",
|
||||
"nx/core/package-json-workspaces"
|
||||
],
|
||||
"targets.build.cache": ["nx.json", "nx/core/target-defaults"],
|
||||
"targets.build.options.script": [
|
||||
"package.json",
|
||||
"nx/core/package-json-workspaces"
|
||||
],
|
||||
"targets.build.metadata.scriptContent": [
|
||||
"package.json",
|
||||
"nx/core/package-json-workspaces"
|
||||
],
|
||||
"targets.build.metadata.runCommand": [
|
||||
"package.json",
|
||||
"nx/core/package-json-workspaces"
|
||||
],
|
||||
"targets.lint": ["package.json", "nx/core/package-json-workspaces"],
|
||||
"targets.lint.executor": [
|
||||
"package.json",
|
||||
"nx/core/package-json-workspaces"
|
||||
],
|
||||
"targets.lint.options": ["package.json", "nx/core/package-json-workspaces"],
|
||||
"targets.lint.metadata": [
|
||||
"package.json",
|
||||
"nx/core/package-json-workspaces"
|
||||
],
|
||||
"targets.lint.cache": ["nx.json", "nx/core/target-defaults"],
|
||||
"targets.lint.options.script": [
|
||||
"package.json",
|
||||
"nx/core/package-json-workspaces"
|
||||
],
|
||||
"targets.lint.metadata.scriptContent": [
|
||||
"package.json",
|
||||
"nx/core/package-json-workspaces"
|
||||
],
|
||||
"targets.lint.metadata.runCommand": [
|
||||
"package.json",
|
||||
"nx/core/package-json-workspaces"
|
||||
],
|
||||
"sourceRoot": ["package.json", "nx/core/package-json-workspaces"],
|
||||
"name": ["package.json", "nx/core/package-json-workspaces"],
|
||||
"projectType": ["package.json", "nx/core/package-json-workspaces"],
|
||||
"metadata.targetGroups": [
|
||||
"package.json",
|
||||
"nx/core/package-json-workspaces"
|
||||
],
|
||||
"metadata.targetGroups.NPM Scripts": [
|
||||
"package.json",
|
||||
"nx/core/package-json-workspaces"
|
||||
],
|
||||
"metadata.targetGroups.NPM Scripts.0": [
|
||||
"package.json",
|
||||
"nx/core/package-json-workspaces"
|
||||
],
|
||||
"metadata.targetGroups.NPM Scripts.1": [
|
||||
"package.json",
|
||||
"nx/core/package-json-workspaces"
|
||||
],
|
||||
"metadata.targetGroups.NPM Scripts.2": [
|
||||
"package.json",
|
||||
"nx/core/package-json-workspaces"
|
||||
],
|
||||
"metadata.targetGroups.NPM Scripts.3": [
|
||||
"package.json",
|
||||
"nx/core/package-json-workspaces"
|
||||
],
|
||||
"metadata.targetGroups.NPM Scripts.4": [
|
||||
"package.json",
|
||||
"nx/core/package-json-workspaces"
|
||||
],
|
||||
"targets.dev": ["package.json", "nx/core/package-json-workspaces"],
|
||||
"targets.dev.executor": ["package.json", "nx/core/package-json-workspaces"],
|
||||
"targets.dev.options": ["package.json", "nx/core/package-json-workspaces"],
|
||||
"targets.dev.metadata": ["package.json", "nx/core/package-json-workspaces"],
|
||||
"targets.dev.options.script": [
|
||||
"package.json",
|
||||
"nx/core/package-json-workspaces"
|
||||
],
|
||||
"targets.dev.metadata.scriptContent": [
|
||||
"package.json",
|
||||
"nx/core/package-json-workspaces"
|
||||
],
|
||||
"targets.dev.metadata.runCommand": [
|
||||
"package.json",
|
||||
"nx/core/package-json-workspaces"
|
||||
],
|
||||
"targets.build.dependsOn": [
|
||||
"package.json",
|
||||
"nx/core/package-json-workspaces"
|
||||
],
|
||||
"targets.preview": ["package.json", "nx/core/package-json-workspaces"],
|
||||
"targets.preview.executor": [
|
||||
"package.json",
|
||||
"nx/core/package-json-workspaces"
|
||||
],
|
||||
"targets.preview.options": [
|
||||
"package.json",
|
||||
"nx/core/package-json-workspaces"
|
||||
],
|
||||
"targets.preview.metadata": [
|
||||
"package.json",
|
||||
"nx/core/package-json-workspaces"
|
||||
],
|
||||
"targets.preview.options.script": [
|
||||
"package.json",
|
||||
"nx/core/package-json-workspaces"
|
||||
],
|
||||
"targets.preview.metadata.scriptContent": [
|
||||
"package.json",
|
||||
"nx/core/package-json-workspaces"
|
||||
],
|
||||
"targets.preview.metadata.runCommand": [
|
||||
"package.json",
|
||||
"nx/core/package-json-workspaces"
|
||||
]
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 36 KiB |
File diff suppressed because it is too large
Load Diff
|
Before Width: | Height: | Size: 166 KiB After Width: | Height: | Size: 166 KiB |
@@ -0,0 +1,186 @@
|
||||
{
|
||||
"project": {
|
||||
"name": "vue-app",
|
||||
"type": "lib",
|
||||
"data": {
|
||||
"root": ".",
|
||||
"targets": {
|
||||
"vite:build": {
|
||||
"options": {
|
||||
"cwd": ".",
|
||||
"command": "vite build"
|
||||
},
|
||||
"cache": true,
|
||||
"dependsOn": ["^vite:build"],
|
||||
"inputs": [
|
||||
"default",
|
||||
"^default",
|
||||
{
|
||||
"externalDependencies": ["vite"]
|
||||
}
|
||||
],
|
||||
"outputs": ["{projectRoot}/dist"],
|
||||
"executor": "nx:run-commands",
|
||||
"configurations": {}
|
||||
},
|
||||
"build-only": {
|
||||
"executor": "nx:run-script",
|
||||
"metadata": {
|
||||
"scriptContent": "nx vite:build",
|
||||
"runCommand": "npm run build-only"
|
||||
},
|
||||
"outputs": ["{projectRoot}/dist"],
|
||||
"cache": true,
|
||||
"options": {
|
||||
"script": "build-only"
|
||||
},
|
||||
"configurations": {}
|
||||
},
|
||||
"build": {
|
||||
"executor": "nx:run-script",
|
||||
"metadata": {
|
||||
"scriptContent": "nx exec -- echo 'Ran type-check and build-only'",
|
||||
"runCommand": "npm run build"
|
||||
},
|
||||
"dependsOn": [
|
||||
"type-check",
|
||||
{
|
||||
"target": "build-only",
|
||||
"params": "forward"
|
||||
}
|
||||
],
|
||||
"options": {
|
||||
"script": "build"
|
||||
},
|
||||
"configurations": {}
|
||||
}
|
||||
},
|
||||
"sourceRoot": ".",
|
||||
"name": "vue-app",
|
||||
"projectType": "library",
|
||||
"metadata": {
|
||||
"targetGroups": {
|
||||
"NPM Scripts": ["build", "build-only"]
|
||||
}
|
||||
},
|
||||
"implicitDependencies": [],
|
||||
"tags": []
|
||||
}
|
||||
},
|
||||
"sourceMap": {
|
||||
"root": ["package.json", "nx/core/package-json-workspaces"],
|
||||
"targets": ["vite.config.ts", "@nx/vite/plugin"],
|
||||
"targets.vite:build": ["vite.config.ts", "@nx/vite/plugin"],
|
||||
"targets.vite:build.options": ["vite.config.ts", "@nx/vite/plugin"],
|
||||
"targets.vite:build.cache": ["vite.config.ts", "@nx/vite/plugin"],
|
||||
"targets.vite:build.dependsOn": ["vite.config.ts", "@nx/vite/plugin"],
|
||||
"targets.vite:build.inputs": ["vite.config.ts", "@nx/vite/plugin"],
|
||||
"targets.vite:build.outputs": ["vite.config.ts", "@nx/vite/plugin"],
|
||||
"targets.vite:build.executor": ["vite.config.ts", "@nx/vite/plugin"],
|
||||
"targets.vite:build.options.cwd": ["vite.config.ts", "@nx/vite/plugin"],
|
||||
"targets.vite:build.options.command": ["vite.config.ts", "@nx/vite/plugin"],
|
||||
"targets.build-only": ["package.json", "nx/core/package-json-workspaces"],
|
||||
"targets.build-only.executor": [
|
||||
"package.json",
|
||||
"nx/core/package-json-workspaces"
|
||||
],
|
||||
"targets.build-only.options": [
|
||||
"package.json",
|
||||
"nx/core/package-json-workspaces"
|
||||
],
|
||||
"targets.build-only.metadata": [
|
||||
"package.json",
|
||||
"nx/core/package-json-workspaces"
|
||||
],
|
||||
"targets.build-only.outputs": ["nx.json", "nx/core/target-defaults"],
|
||||
"targets.build-only.cache": ["nx.json", "nx/core/target-defaults"],
|
||||
"targets.build-only.options.script": [
|
||||
"package.json",
|
||||
"nx/core/package-json-workspaces"
|
||||
],
|
||||
"targets.build-only.metadata.scriptContent": [
|
||||
"package.json",
|
||||
"nx/core/package-json-workspaces"
|
||||
],
|
||||
"targets.build-only.metadata.runCommand": [
|
||||
"package.json",
|
||||
"nx/core/package-json-workspaces"
|
||||
],
|
||||
"sourceRoot": ["package.json", "nx/core/package-json-workspaces"],
|
||||
"name": ["package.json", "nx/core/package-json-workspaces"],
|
||||
"projectType": ["package.json", "nx/core/package-json-workspaces"],
|
||||
"metadata.targetGroups": [
|
||||
"package.json",
|
||||
"nx/core/package-json-workspaces"
|
||||
],
|
||||
"metadata.targetGroups.NPM Scripts": [
|
||||
"package.json",
|
||||
"nx/core/package-json-workspaces"
|
||||
],
|
||||
"metadata.targetGroups.NPM Scripts.0": [
|
||||
"package.json",
|
||||
"nx/core/package-json-workspaces"
|
||||
],
|
||||
"metadata.targetGroups.NPM Scripts.1": [
|
||||
"package.json",
|
||||
"nx/core/package-json-workspaces"
|
||||
],
|
||||
"metadata.targetGroups.NPM Scripts.2": [
|
||||
"package.json",
|
||||
"nx/core/package-json-workspaces"
|
||||
],
|
||||
"metadata.targetGroups.NPM Scripts.3": [
|
||||
"package.json",
|
||||
"nx/core/package-json-workspaces"
|
||||
],
|
||||
"metadata.targetGroups.NPM Scripts.4": [
|
||||
"package.json",
|
||||
"nx/core/package-json-workspaces"
|
||||
],
|
||||
"metadata.targetGroups.NPM Scripts.5": [
|
||||
"package.json",
|
||||
"nx/core/package-json-workspaces"
|
||||
],
|
||||
"metadata.targetGroups.NPM Scripts.6": [
|
||||
"package.json",
|
||||
"nx/core/package-json-workspaces"
|
||||
],
|
||||
"metadata.targetGroups.NPM Scripts.7": [
|
||||
"package.json",
|
||||
"nx/core/package-json-workspaces"
|
||||
],
|
||||
"metadata.targetGroups.NPM Scripts.8": [
|
||||
"package.json",
|
||||
"nx/core/package-json-workspaces"
|
||||
],
|
||||
"targets.build": ["package.json", "nx/core/package-json-workspaces"],
|
||||
"targets.build.executor": [
|
||||
"package.json",
|
||||
"nx/core/package-json-workspaces"
|
||||
],
|
||||
"targets.build.metadata": [
|
||||
"package.json",
|
||||
"nx/core/package-json-workspaces"
|
||||
],
|
||||
"targets.build.dependsOn": [
|
||||
"package.json",
|
||||
"nx/core/package-json-workspaces"
|
||||
],
|
||||
"targets.build.options": [
|
||||
"package.json",
|
||||
"nx/core/package-json-workspaces"
|
||||
],
|
||||
"targets.build.options.script": [
|
||||
"package.json",
|
||||
"nx/core/package-json-workspaces"
|
||||
],
|
||||
"targets.build.metadata.scriptContent": [
|
||||
"package.json",
|
||||
"nx/core/package-json-workspaces"
|
||||
],
|
||||
"targets.build.metadata.runCommand": [
|
||||
"package.json",
|
||||
"nx/core/package-json-workspaces"
|
||||
]
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 30 KiB |
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,29 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Node Applications + webpack should generate a Dockerfile 1`] = `
|
||||
"# This file is generated by Nx.
|
||||
#
|
||||
# Build the docker image with \`npx nx docker-build docker-express-app\`.
|
||||
# Tip: Modify "docker-build" options in project.json to change docker build args.
|
||||
#
|
||||
# Run the container with \`docker run -p 3000:3000 -t docker-express-app\`.
|
||||
FROM docker.io/node:lts-alpine
|
||||
|
||||
ENV HOST=0.0.0.0
|
||||
ENV PORT=3000
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
RUN addgroup --system docker-express-app && \\
|
||||
adduser --system -G docker-express-app docker-express-app
|
||||
|
||||
COPY dist/apps/docker-express-app docker-express-app/
|
||||
RUN chown -R docker-express-app:docker-express-app .
|
||||
|
||||
# You can remove this install step if you build with \`--bundle\` option.
|
||||
# The bundled output will include external dependencies.
|
||||
RUN npm --prefix docker-express-app --omit=dev -f install
|
||||
|
||||
CMD [ "node", "docker-express-app" ]
|
||||
"
|
||||
`;
|
||||
@@ -2,6 +2,7 @@ import {
|
||||
checkFilesDoNotExist,
|
||||
checkFilesExist,
|
||||
cleanupProject,
|
||||
getSelectedPackageManager,
|
||||
killPort,
|
||||
newProject,
|
||||
promisifiedTreeKill,
|
||||
@@ -14,11 +15,19 @@ import {
|
||||
} from '@nx/e2e/utils';
|
||||
import { join } from 'path';
|
||||
|
||||
xdescribe('Node Applications + webpack', () => {
|
||||
describe('Node Applications + webpack', () => {
|
||||
let proj: string;
|
||||
const selectedPm = getSelectedPackageManager();
|
||||
|
||||
// TODO(nicholas): Look into how this can work with npm instead of forcing pnpm.
|
||||
beforeAll(() => {
|
||||
proj = newProject({
|
||||
packages: ['@nx/node'],
|
||||
// npm has resolution for ajv some packages require ajv6 and some require ajv8 and npm resolves it to ajv6 (Error: Cannot find module 'ajv/dist/compile/codegen')
|
||||
// - ajv@6 (fork-ts-checker-webpack-plugin, terser-webpack-plugin, webpack)
|
||||
// - ajv@8 (babel-loader)
|
||||
// Solution is to use pnpm or run fix it via npm ex.(npm dedupe --force)
|
||||
packageManager: selectedPm === 'npm' ? 'pnpm' : selectedPm,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -47,10 +56,10 @@ xdescribe('Node Applications + webpack', () => {
|
||||
}
|
||||
}
|
||||
|
||||
async function runE2eTests(appName: string) {
|
||||
process.env.PORT = '5000';
|
||||
async function runE2eTests(appName: string, port: number = 5000) {
|
||||
process.env.PORT = `${port}`;
|
||||
const childProcess = await runCommandUntil(`serve ${appName}`, (output) => {
|
||||
return output.includes('http://localhost:5000');
|
||||
return output.includes(`http://localhost:${port}`);
|
||||
});
|
||||
const result = runCLI(`e2e ${appName}-e2e --verbose`);
|
||||
expect(result).toContain('Setting up...');
|
||||
@@ -58,13 +67,11 @@ xdescribe('Node Applications + webpack', () => {
|
||||
expect(result).toContain('Successfully ran target e2e');
|
||||
|
||||
await promisifiedTreeKill(childProcess.pid, 'SIGKILL');
|
||||
await killPort(5000);
|
||||
await killPort(port);
|
||||
process.env.PORT = '';
|
||||
}
|
||||
|
||||
// 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 () => {
|
||||
describe('frameworks', () => {
|
||||
const testLib1 = uniq('test1');
|
||||
const testLib2 = uniq('test2');
|
||||
const expressApp = uniq('expressapp');
|
||||
@@ -72,79 +79,98 @@ xdescribe('Node Applications + webpack', () => {
|
||||
const koaApp = uniq('koaapp');
|
||||
const nestApp = uniq('nest');
|
||||
|
||||
runCLI(`generate @nx/node:lib ${testLib1}`);
|
||||
runCLI(`generate @nx/node:lib ${testLib2} --importPath=@acme/test2`);
|
||||
runCLI(
|
||||
`generate @nx/node:app ${expressApp} --framework=express --no-interactive`
|
||||
);
|
||||
runCLI(
|
||||
`generate @nx/node:app ${fastifyApp} --framework=fastify --no-interactive`
|
||||
);
|
||||
runCLI(`generate @nx/node:app ${koaApp} --framework=koa --no-interactive`);
|
||||
runCLI(
|
||||
`generate @nx/node:app ${nestApp} --framework=nest --bundler=webpack --no-interactive`
|
||||
);
|
||||
beforeAll(() => {
|
||||
runCLI(`generate @nx/node:lib ${testLib1}`);
|
||||
runCLI(`generate @nx/node:lib ${testLib2} --importPath=@acme/test2`);
|
||||
runCLI(
|
||||
`generate @nx/node:app ${expressApp} --framework=express --port=7000 --no-interactive`
|
||||
);
|
||||
runCLI(
|
||||
`generate @nx/node:app ${fastifyApp} --framework=fastify --port=7001 --no-interactive`
|
||||
);
|
||||
runCLI(
|
||||
`generate @nx/node:app ${koaApp} --framework=koa --port=7002 --no-interactive`
|
||||
);
|
||||
runCLI(
|
||||
`generate @nx/node:app ${nestApp} --framework=nest --port=7003 --bundler=webpack --no-interactive`
|
||||
);
|
||||
|
||||
// Use esbuild by default
|
||||
checkFilesDoNotExist(`apps/${expressApp}/webpack.config.js`);
|
||||
checkFilesDoNotExist(`apps/${fastifyApp}/webpack.config.js`);
|
||||
checkFilesDoNotExist(`apps/${koaApp}/webpack.config.js`);
|
||||
addLibImport(expressApp, testLib1);
|
||||
addLibImport(expressApp, testLib2, '@acme/test2');
|
||||
addLibImport(fastifyApp, testLib1);
|
||||
addLibImport(fastifyApp, testLib2, '@acme/test2');
|
||||
addLibImport(koaApp, testLib1);
|
||||
addLibImport(koaApp, testLib2, '@acme/test2');
|
||||
|
||||
// Uses only webpack
|
||||
checkFilesExist(`apps/${nestApp}/webpack.config.js`);
|
||||
addLibImport(nestApp, testLib1);
|
||||
addLibImport(nestApp, testLib2, '@acme/test2');
|
||||
});
|
||||
|
||||
expect(() => runCLI(`lint ${expressApp}`)).not.toThrow();
|
||||
expect(() => runCLI(`lint ${fastifyApp}`)).not.toThrow();
|
||||
expect(() => runCLI(`lint ${koaApp}`)).not.toThrow();
|
||||
expect(() => runCLI(`lint ${nestApp}`)).not.toThrow();
|
||||
it('should generate an app defaults using webpack or esbuild', async () => {
|
||||
// Use esbuild by default
|
||||
checkFilesDoNotExist(`apps/${expressApp}/webpack.config.js`);
|
||||
checkFilesDoNotExist(`apps/${fastifyApp}/webpack.config.js`);
|
||||
checkFilesDoNotExist(`apps/${koaApp}/webpack.config.js`);
|
||||
|
||||
expect(() => runCLI(`lint ${expressApp}-e2e`)).not.toThrow();
|
||||
expect(() => runCLI(`lint ${fastifyApp}-e2e`)).not.toThrow();
|
||||
expect(() => runCLI(`lint ${koaApp}-e2e`)).not.toThrow();
|
||||
expect(() => runCLI(`lint ${nestApp}-e2e`)).not.toThrow();
|
||||
// Uses only webpack
|
||||
checkFilesExist(`apps/${nestApp}/webpack.config.js`);
|
||||
|
||||
// Only Fastify generates with unit tests since it supports them without additional libraries.
|
||||
expect(() => runCLI(`test ${fastifyApp}`)).not.toThrow();
|
||||
expect(() => runCLI(`lint ${expressApp}`)).not.toThrow();
|
||||
expect(() => runCLI(`lint ${fastifyApp}`)).not.toThrow();
|
||||
expect(() => runCLI(`lint ${koaApp}`)).not.toThrow();
|
||||
expect(() => runCLI(`lint ${nestApp}`)).not.toThrow();
|
||||
|
||||
// https://github.com/nrwl/nx/issues/16601
|
||||
const nestMainContent = readFile(`apps/${nestApp}/src/main.ts`);
|
||||
updateFile(
|
||||
`apps/${nestApp}/src/main.ts`,
|
||||
`
|
||||
expect(() => runCLI(`lint ${expressApp}-e2e`)).not.toThrow();
|
||||
expect(() => runCLI(`lint ${fastifyApp}-e2e`)).not.toThrow();
|
||||
expect(() => runCLI(`lint ${koaApp}-e2e`)).not.toThrow();
|
||||
expect(() => runCLI(`lint ${nestApp}-e2e`)).not.toThrow();
|
||||
|
||||
// Only Fastify generates with unit tests since it supports them without additional libraries.
|
||||
expect(() => runCLI(`test ${fastifyApp}`)).not.toThrow();
|
||||
|
||||
// https://github.com/nrwl/nx/issues/16601
|
||||
const nestMainContent = readFile(`apps/${nestApp}/src/main.ts`);
|
||||
updateFile(
|
||||
`apps/${nestApp}/src/main.ts`,
|
||||
`
|
||||
${nestMainContent}
|
||||
// Make sure this is not replaced during build time
|
||||
console.log('env: ' + process.env['NODE_ENV']);
|
||||
`
|
||||
);
|
||||
runCLI(`build ${nestApp}`);
|
||||
expect(readFile(`dist/apps/${nestApp}/main.js`)).toContain(
|
||||
`'env: ' + process.env['NODE_ENV']`
|
||||
);
|
||||
);
|
||||
runCLI(`build ${nestApp}`);
|
||||
expect(readFile(`dist/apps/${nestApp}/main.js`)).toContain(
|
||||
`'env: ' + process.env['NODE_ENV']`
|
||||
);
|
||||
}, 300_000);
|
||||
|
||||
addLibImport(expressApp, testLib1);
|
||||
addLibImport(expressApp, testLib2, '@acme/test2');
|
||||
addLibImport(fastifyApp, testLib1);
|
||||
addLibImport(fastifyApp, testLib2, '@acme/test2');
|
||||
addLibImport(koaApp, testLib1);
|
||||
addLibImport(koaApp, testLib2, '@acme/test2');
|
||||
it('should e2e test express app', async () => {
|
||||
await runE2eTests(expressApp, 7000);
|
||||
});
|
||||
|
||||
addLibImport(nestApp, testLib1);
|
||||
addLibImport(nestApp, testLib2, '@acme/test2');
|
||||
it('should e2e test fastify app', async () => {
|
||||
await runE2eTests(fastifyApp, 7001);
|
||||
});
|
||||
|
||||
await runE2eTests(expressApp);
|
||||
await runE2eTests(fastifyApp);
|
||||
await runE2eTests(koaApp);
|
||||
await runE2eTests(nestApp);
|
||||
}, 900_000);
|
||||
it('should e2e test koa app', async () => {
|
||||
await runE2eTests(koaApp, 7002);
|
||||
});
|
||||
|
||||
it('should e2e test nest app', async () => {
|
||||
await runE2eTests(nestApp, 7003);
|
||||
});
|
||||
});
|
||||
|
||||
it('should generate a Dockerfile', async () => {
|
||||
const expressApp = uniq('expressapp');
|
||||
const expressApp = 'docker-express-app'; // needs to be consistent for the Dockerfile snapshot
|
||||
|
||||
runCLI(
|
||||
`generate @nx/node:app ${expressApp} --framework=express --docker --no-interactive`
|
||||
`generate @nx/node:app ${expressApp} --framework=express --docker --no-interactive`
|
||||
);
|
||||
|
||||
checkFilesExist(`apps/${expressApp}/Dockerfile`);
|
||||
const dockerFile = readFile(`apps/${expressApp}/Dockerfile`);
|
||||
expect(dockerFile).toMatchSnapshot();
|
||||
}, 300_000);
|
||||
|
||||
it('should support waitUntilTargets for serve target', async () => {
|
||||
|
||||
@@ -1,13 +1,8 @@
|
||||
import {
|
||||
ClipboardDocumentCheckIcon,
|
||||
ClipboardDocumentIcon,
|
||||
} from '@heroicons/react/24/outline';
|
||||
// @ts-ignore
|
||||
import { CopyToClipboard } from 'react-copy-to-clipboard';
|
||||
// @ts-ignore
|
||||
import SyntaxHighlighter, { createElement } from 'react-syntax-highlighter';
|
||||
import { JSX, ReactNode, useEffect, useMemo, useState } from 'react';
|
||||
import { JSX, ReactNode, useMemo } from 'react';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
import { CopyToClipboardButton } from '@nx/graph/ui-components';
|
||||
|
||||
export function JsonCodeBlockPreTag({
|
||||
children,
|
||||
@@ -30,45 +25,27 @@ export function JsonCodeBlockPreTag({
|
||||
export interface JsonCodeBlockProps {
|
||||
data: any;
|
||||
renderSource: (propertyName: string) => ReactNode;
|
||||
copyTooltipText: string;
|
||||
}
|
||||
|
||||
export function JsonCodeBlock(props: JsonCodeBlockProps): JSX.Element {
|
||||
const [copied, setCopied] = useState(false);
|
||||
const jsonString = useMemo(
|
||||
() => JSON.stringify(props.data, null, 2),
|
||||
[props.data]
|
||||
);
|
||||
useEffect(() => {
|
||||
if (!copied) return;
|
||||
const t = setTimeout(() => {
|
||||
setCopied(false);
|
||||
}, 3000);
|
||||
return () => clearTimeout(t);
|
||||
}, [copied]);
|
||||
return (
|
||||
<div className="code-block group relative w-full">
|
||||
<div className="absolute right-0 top-0 z-10 flex">
|
||||
<CopyToClipboard
|
||||
<CopyToClipboardButton
|
||||
text={jsonString}
|
||||
onCopy={() => {
|
||||
setCopied(true);
|
||||
}}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
className={twMerge(
|
||||
'not-prose flex',
|
||||
'border border-slate-200 bg-slate-50/50 p-2 dark:border-slate-700 dark:bg-slate-800/60',
|
||||
'opacity-0 transition-opacity group-hover:opacity-100'
|
||||
)}
|
||||
>
|
||||
{copied ? (
|
||||
<ClipboardDocumentCheckIcon className="h-5 w-5 text-blue-500 dark:text-sky-500" />
|
||||
) : (
|
||||
<ClipboardDocumentIcon className="h-5 w-5" />
|
||||
)}
|
||||
</button>
|
||||
</CopyToClipboard>
|
||||
tooltipAlignment="right"
|
||||
tooltipText={props.copyTooltipText}
|
||||
className={twMerge(
|
||||
'not-prose flex',
|
||||
'border border-slate-200 bg-slate-50/50 p-2 dark:border-slate-700 dark:bg-slate-800/60',
|
||||
'opacity-0 transition-opacity group-hover:opacity-100'
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<SyntaxHighlighter
|
||||
language="json"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export * from './lib/copy-to-clipboard-button';
|
||||
export * from './lib/debounced-text-input';
|
||||
export * from './lib/tag';
|
||||
export * from './lib/dropdown';
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
import {
|
||||
CopyToClipboardButton,
|
||||
CopyToClipboardButtonProps,
|
||||
} from './copy-to-clipboard-button';
|
||||
|
||||
const meta: Meta<typeof CopyToClipboardButton> = {
|
||||
component: CopyToClipboardButton,
|
||||
title: 'CopyToClipboardButton',
|
||||
};
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<typeof CopyToClipboardButton>;
|
||||
|
||||
export const Simple: Story = {
|
||||
args: {
|
||||
text: 'Hello, world!',
|
||||
tooltipAlignment: 'left',
|
||||
} as CopyToClipboardButtonProps,
|
||||
};
|
||||
@@ -0,0 +1,61 @@
|
||||
// @ts-ignore
|
||||
import { CopyToClipboard } from 'react-copy-to-clipboard';
|
||||
import { JSX, ReactNode, useEffect, useState } from 'react';
|
||||
import {
|
||||
ClipboardDocumentCheckIcon,
|
||||
ClipboardDocumentIcon,
|
||||
} from '@heroicons/react/24/outline';
|
||||
|
||||
export interface CopyToClipboardButtonProps {
|
||||
text: string;
|
||||
tooltipText?: string;
|
||||
tooltipAlignment?: 'left' | 'right';
|
||||
className?: string;
|
||||
children?: ReactNode;
|
||||
}
|
||||
|
||||
export function CopyToClipboardButton({
|
||||
text,
|
||||
tooltipAlignment,
|
||||
tooltipText,
|
||||
className,
|
||||
children,
|
||||
}: CopyToClipboardButtonProps) {
|
||||
const [copied, setCopied] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!copied) return;
|
||||
const t = setTimeout(() => {
|
||||
setCopied(false);
|
||||
}, 3000);
|
||||
return () => clearTimeout(t);
|
||||
}, [copied]);
|
||||
|
||||
return (
|
||||
<CopyToClipboard
|
||||
text={text}
|
||||
onCopy={() => {
|
||||
setCopied(true);
|
||||
}}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
data-tooltip={tooltipText ? tooltipText : false}
|
||||
data-tooltip-align-right={tooltipAlignment === 'right'}
|
||||
data-tooltip-align-left={tooltipAlignment === 'left'}
|
||||
className={className}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
{copied ? (
|
||||
<ClipboardDocumentCheckIcon className="inline h-5 w-5 text-blue-500 dark:text-sky-500" />
|
||||
) : (
|
||||
<ClipboardDocumentIcon className="inline h-5 w-5" />
|
||||
)}
|
||||
{children}
|
||||
</button>
|
||||
</CopyToClipboard>
|
||||
);
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
import { CopyToClipboard } from './copy-to-clipboard';
|
||||
|
||||
const meta: Meta<typeof CopyToClipboard> = {
|
||||
component: CopyToClipboard,
|
||||
title: 'CopyToClipboard',
|
||||
};
|
||||
export default meta;
|
||||
|
||||
type Story = StoryObj<typeof CopyToClipboard>;
|
||||
|
||||
export const Simple: Story = {
|
||||
args: {
|
||||
onCopy: () => {},
|
||||
tooltipAlignment: 'left',
|
||||
},
|
||||
};
|
||||
@@ -1,36 +0,0 @@
|
||||
import { ClipboardIcon } from '@heroicons/react/24/outline';
|
||||
import { JSX, useEffect, useState } from 'react';
|
||||
|
||||
interface CopyToClipboardProps {
|
||||
onCopy: () => void;
|
||||
tooltipAlignment?: 'left' | 'right';
|
||||
}
|
||||
|
||||
export function CopyToClipboard(props: CopyToClipboardProps): JSX.Element {
|
||||
const [copied, setCopied] = useState(false);
|
||||
useEffect(() => {
|
||||
if (copied) {
|
||||
const timeout = setTimeout(() => {
|
||||
setCopied(false);
|
||||
}, 3000);
|
||||
return () => clearTimeout(timeout);
|
||||
}
|
||||
});
|
||||
return (
|
||||
<span
|
||||
data-tooltip="Copy to clipboard"
|
||||
data-tooltip-align-right={props.tooltipAlignment === 'right'}
|
||||
>
|
||||
<ClipboardIcon
|
||||
className={`inline h-4 w-5 !cursor-pointer ${
|
||||
copied ? 'text-sky-500' : ''
|
||||
}`}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setCopied(true);
|
||||
props.onCopy();
|
||||
}}
|
||||
></ClipboardIcon>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
|
||||
/* eslint-disable @nx/enforce-module-boundaries */
|
||||
// nx-ignore-next-line
|
||||
import type { ProjectGraphProjectNode } from '@nx/devkit';
|
||||
@@ -71,7 +69,7 @@ export const ProjectDetails = ({
|
||||
>
|
||||
<div
|
||||
className={twMerge(
|
||||
`flex items-center justify-between`,
|
||||
`flex flex-wrap items-center justify-between`,
|
||||
isCompact ? `gap-1` : `mb-4 gap-2`
|
||||
)}
|
||||
>
|
||||
@@ -90,17 +88,15 @@ export const ProjectDetails = ({
|
||||
className="h-6 w-6"
|
||||
/>
|
||||
</div>
|
||||
<span>
|
||||
{onViewInProjectGraph && viewInProjectGraphPosition === 'top' && (
|
||||
<ViewInProjectGraphButton
|
||||
callback={() =>
|
||||
onViewInProjectGraph({ projectName: project.name })
|
||||
}
|
||||
/>
|
||||
)}{' '}
|
||||
</span>
|
||||
{onViewInProjectGraph && viewInProjectGraphPosition === 'top' && (
|
||||
<ViewInProjectGraphButton
|
||||
callback={() =>
|
||||
onViewInProjectGraph({ projectName: project.name })
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex justify-between py-2">
|
||||
<div className="flex flex-wrap justify-between py-2">
|
||||
<div>
|
||||
{projectData.metadata?.description ? (
|
||||
<p className="mb-2 text-sm capitalize text-gray-500 dark:text-slate-400">
|
||||
@@ -133,16 +129,14 @@ export const ProjectDetails = ({
|
||||
) : null}
|
||||
</div>
|
||||
<div className="self-end">
|
||||
<span>
|
||||
{onViewInProjectGraph &&
|
||||
viewInProjectGraphPosition === 'bottom' && (
|
||||
<ViewInProjectGraphButton
|
||||
callback={() =>
|
||||
onViewInProjectGraph({ projectName: project.name })
|
||||
}
|
||||
/>
|
||||
)}{' '}
|
||||
</span>
|
||||
{onViewInProjectGraph &&
|
||||
viewInProjectGraphPosition === 'bottom' && (
|
||||
<ViewInProjectGraphButton
|
||||
callback={() =>
|
||||
onViewInProjectGraph({ projectName: project.name })
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
+17
-14
@@ -1,6 +1,7 @@
|
||||
/* eslint-disable @nx/enforce-module-boundaries */
|
||||
// nx-ignore-next-line
|
||||
import type { TargetConfiguration } from '@nx/devkit';
|
||||
import { CopyToClipboardButton } from '@nx/graph/ui-components';
|
||||
import {
|
||||
ChevronDownIcon,
|
||||
ChevronUpIcon,
|
||||
@@ -17,7 +18,6 @@ import { twMerge } from 'tailwind-merge';
|
||||
import { Pill } from '../pill';
|
||||
import { TargetTechnologies } from '../target-technologies/target-technologies';
|
||||
import { SourceInfo } from '../source-info/source-info';
|
||||
import { CopyToClipboard } from '../copy-to-clipboard/copy-to-clipboard';
|
||||
import { getDisplayHeaderFromTargetConfiguration } from '../utils/get-display-header-from-target-configuration';
|
||||
import { TargetExecutor } from '../target-executor/target-executor';
|
||||
|
||||
@@ -53,10 +53,6 @@ export const TargetConfigurationDetailsHeader = ({
|
||||
onViewInTaskGraph,
|
||||
onNxConnect,
|
||||
}: TargetConfigurationDetailsHeaderProps) => {
|
||||
const handleCopyClick = async (copyText: string) => {
|
||||
await window.navigator.clipboard.writeText(copyText);
|
||||
};
|
||||
|
||||
if (!collapsable) {
|
||||
// when collapsable is false, isCollasped should be false
|
||||
isCollasped = false;
|
||||
@@ -156,6 +152,12 @@ export const TargetConfigurationDetailsHeader = ({
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<CopyToClipboardButton
|
||||
text={JSON.stringify(targetConfiguration, null, 2)}
|
||||
tooltipText={!isCollasped ? 'Copy Target' : undefined}
|
||||
tooltipAlignment="right"
|
||||
className="rounded-md bg-inherit p-1 text-sm text-slate-600 ring-1 ring-inset ring-slate-400/40 hover:bg-slate-200 dark:text-slate-300 dark:ring-slate-400/30 dark:hover:bg-slate-700/60"
|
||||
/>
|
||||
{onViewInTaskGraph && (
|
||||
<button
|
||||
className="rounded-md bg-inherit p-1 text-sm text-slate-600 ring-1 ring-inset ring-slate-400/40 hover:bg-slate-200 dark:text-slate-300 dark:ring-slate-400/30 dark:hover:bg-slate-700/60"
|
||||
@@ -194,21 +196,22 @@ export const TargetConfigurationDetailsHeader = ({
|
||||
</div>
|
||||
{!isCollasped && (
|
||||
<div className="ml-5 mt-2 text-sm">
|
||||
<SourceInfo
|
||||
data={sourceMap[`targets.${targetName}`]}
|
||||
propertyKey={`targets.${targetName}`}
|
||||
color="text-gray-500 dark:text-slate-400"
|
||||
/>
|
||||
<div className="flex">
|
||||
<SourceInfo
|
||||
data={sourceMap[`targets.${targetName}`]}
|
||||
propertyKey={`targets.${targetName}`}
|
||||
color="text-gray-500 dark:text-slate-400"
|
||||
/>
|
||||
</div>
|
||||
{targetName !== 'nx-release-publish' && (
|
||||
<div className="mt-2 text-right">
|
||||
<code className="ml-4 rounded bg-gray-100 px-2 py-1 font-mono text-gray-800 dark:bg-gray-700 dark:text-gray-300">
|
||||
nx run {projectName}:{targetName}
|
||||
</code>
|
||||
<span>
|
||||
<CopyToClipboard
|
||||
onCopy={() =>
|
||||
handleCopyClick(`nx run ${projectName}:${targetName}`)
|
||||
}
|
||||
<CopyToClipboardButton
|
||||
text={`nx run ${projectName}:${targetName}`}
|
||||
tooltipText="Copy Command"
|
||||
tooltipAlignment="right"
|
||||
/>
|
||||
</span>
|
||||
|
||||
+21
-39
@@ -1,12 +1,11 @@
|
||||
/* eslint-disable @nx/enforce-module-boundaries */
|
||||
// nx-ignore-next-line
|
||||
import type { TargetConfiguration } from '@nx/devkit';
|
||||
|
||||
import { JsonCodeBlock } from '@nx/graph/ui-code-block';
|
||||
import { CopyToClipboardButton } from '@nx/graph/ui-components';
|
||||
import { useCallback, useContext, useEffect, useState } from 'react';
|
||||
import { FadingCollapsible } from './fading-collapsible';
|
||||
import { TargetConfigurationProperty } from './target-configuration-property';
|
||||
import { CopyToClipboard } from '../copy-to-clipboard/copy-to-clipboard';
|
||||
import { PropertyInfoTooltip, Tooltip } from '@nx/graph/ui-tooltips';
|
||||
import { TooltipTriggerText } from './tooltip-trigger-text';
|
||||
import { Pill } from '../pill';
|
||||
@@ -53,10 +52,6 @@ export default function TargetConfigurationDetails({
|
||||
const [collapsed, setCollapsed] = useState(true);
|
||||
const { expandedTargets, toggleTarget } = useContext(ExpandedTargetsContext);
|
||||
|
||||
const handleCopyClick = async (copyText: string) => {
|
||||
await window.navigator.clipboard.writeText(copyText);
|
||||
};
|
||||
|
||||
const handleCollapseToggle = useCallback(() => {
|
||||
if (toggleTarget) {
|
||||
toggleTarget(targetName);
|
||||
@@ -110,10 +105,7 @@ export default function TargetConfigurationDetails({
|
||||
<div className="p-4 text-base">
|
||||
<div className="group mb-4">
|
||||
<h4 className="mb-4">
|
||||
<TargetExecutorTitle
|
||||
{...displayHeader}
|
||||
handleCopyClick={handleCopyClick}
|
||||
/>
|
||||
<TargetExecutorTitle {...displayHeader} />
|
||||
</h4>
|
||||
<p className="pl-5 font-mono">
|
||||
<TargetExecutor {...displayHeader} link={link}>
|
||||
@@ -131,10 +123,7 @@ export default function TargetConfigurationDetails({
|
||||
{script && (
|
||||
<div className="group mb-4">
|
||||
<h4 className="mb-4">
|
||||
<TargetExecutorTitle
|
||||
script={script}
|
||||
handleCopyClick={handleCopyClick}
|
||||
/>
|
||||
<TargetExecutorTitle script={script} />
|
||||
</h4>
|
||||
<p className="pl-5 font-mono">
|
||||
<TargetExecutor script={script} link={link}>
|
||||
@@ -164,6 +153,7 @@ export default function TargetConfigurationDetails({
|
||||
<FadingCollapsible>
|
||||
<JsonCodeBlock
|
||||
data={options}
|
||||
copyTooltipText="Copy Options"
|
||||
renderSource={(propertyName: string) => (
|
||||
<TargetSourceInfo
|
||||
className="flex min-w-0 pl-4"
|
||||
@@ -198,14 +188,11 @@ export default function TargetConfigurationDetails({
|
||||
</span>
|
||||
</Tooltip>
|
||||
<span className="mb-1 ml-2 hidden group-hover:inline">
|
||||
<CopyToClipboard
|
||||
onCopy={() =>
|
||||
handleCopyClick(
|
||||
`"inputs": ${JSON.stringify(
|
||||
targetConfiguration.inputs
|
||||
)}`
|
||||
)
|
||||
}
|
||||
<CopyToClipboardButton
|
||||
text={`"inputs": ${JSON.stringify(
|
||||
targetConfiguration.inputs
|
||||
)}`}
|
||||
tooltipText="Copy Inputs"
|
||||
/>
|
||||
</span>
|
||||
</h4>
|
||||
@@ -239,14 +226,11 @@ export default function TargetConfigurationDetails({
|
||||
</span>
|
||||
</Tooltip>
|
||||
<span className="mb-1 ml-2 hidden group-hover:inline">
|
||||
<CopyToClipboard
|
||||
onCopy={() =>
|
||||
handleCopyClick(
|
||||
`"outputs": ${JSON.stringify(
|
||||
targetConfiguration.outputs
|
||||
)}`
|
||||
)
|
||||
}
|
||||
<CopyToClipboardButton
|
||||
text={`"outputs": ${JSON.stringify(
|
||||
targetConfiguration.outputs
|
||||
)}`}
|
||||
tooltipText="Copy Outputs"
|
||||
/>
|
||||
</span>
|
||||
</h4>
|
||||
@@ -279,15 +263,12 @@ export default function TargetConfigurationDetails({
|
||||
<TooltipTriggerText>Depends On</TooltipTriggerText>
|
||||
</span>
|
||||
</Tooltip>
|
||||
<span className="inline pl-4 opacity-0 transition-opacity duration-150 ease-in-out group-hover/line:opacity-100">
|
||||
<CopyToClipboard
|
||||
onCopy={() =>
|
||||
handleCopyClick(
|
||||
`"dependsOn": ${JSON.stringify(
|
||||
targetConfiguration.dependsOn
|
||||
)}`
|
||||
)
|
||||
}
|
||||
<span className="mb-1 ml-2 hidden group-hover:inline">
|
||||
<CopyToClipboardButton
|
||||
text={`"dependsOn": ${JSON.stringify(
|
||||
targetConfiguration.dependsOn
|
||||
)}`}
|
||||
tooltipText="Copy Depends On"
|
||||
/>
|
||||
</span>
|
||||
</h4>
|
||||
@@ -336,6 +317,7 @@ export default function TargetConfigurationDetails({
|
||||
<FadingCollapsible>
|
||||
<JsonCodeBlock
|
||||
data={targetConfiguration.configurations}
|
||||
copyTooltipText="Copy Configurations"
|
||||
renderSource={(propertyName: string) => (
|
||||
<TargetSourceInfo
|
||||
className="flex min-w-0 pl-4"
|
||||
|
||||
@@ -1,29 +1,26 @@
|
||||
import { PropertyInfoTooltip, Tooltip } from '@nx/graph/ui-tooltips';
|
||||
import { CopyToClipboard } from '../copy-to-clipboard/copy-to-clipboard';
|
||||
import { CopyToClipboardButton } from '@nx/graph/ui-components';
|
||||
import { TooltipTriggerText } from '../target-configuration-details/tooltip-trigger-text';
|
||||
|
||||
export function TargetExecutorTitle({
|
||||
commands,
|
||||
command,
|
||||
script,
|
||||
handleCopyClick,
|
||||
executor,
|
||||
}: {
|
||||
handleCopyClick: (copyText: string) => void;
|
||||
commands?: string[];
|
||||
command?: string;
|
||||
script?: string;
|
||||
executor?: string;
|
||||
}) {
|
||||
if (commands && commands.length) {
|
||||
return (
|
||||
<span className="font-medium">
|
||||
Commands
|
||||
<span className="mb-1 ml-2 hidden group-hover:inline">
|
||||
<CopyToClipboard
|
||||
onCopy={() =>
|
||||
handleCopyClick(
|
||||
`"commands": [${commands.map((c) => `"${c}"`).join(', ')}]`
|
||||
)
|
||||
}
|
||||
<CopyToClipboardButton
|
||||
text={`"commands": [${commands.map((c) => `"${c}"`).join(', ')}]`}
|
||||
tooltipText="Copy Commands"
|
||||
/>
|
||||
</span>
|
||||
</span>
|
||||
@@ -34,8 +31,9 @@ export function TargetExecutorTitle({
|
||||
<span className="font-medium">
|
||||
Command
|
||||
<span className="mb-1 ml-2 hidden group-hover:inline">
|
||||
<CopyToClipboard
|
||||
onCopy={() => handleCopyClick(`"command": "${command}"`)}
|
||||
<CopyToClipboardButton
|
||||
text={`"command": "${command}"`}
|
||||
tooltipText="Copy Command"
|
||||
/>
|
||||
</span>
|
||||
</span>
|
||||
@@ -46,7 +44,7 @@ export function TargetExecutorTitle({
|
||||
<span className="font-medium">
|
||||
Script
|
||||
<span className="mb-1 ml-2 hidden group-hover:inline">
|
||||
<CopyToClipboard onCopy={() => handleCopyClick(script)} />
|
||||
<CopyToClipboardButton text={script} tooltipText="Copy Script" />
|
||||
</span>
|
||||
</span>
|
||||
);
|
||||
@@ -58,6 +56,12 @@ export function TargetExecutorTitle({
|
||||
>
|
||||
<span className="font-medium">
|
||||
<TooltipTriggerText>Executor</TooltipTriggerText>
|
||||
<span className="mb-1 ml-2 hidden group-hover:inline">
|
||||
<CopyToClipboardButton
|
||||
text={executor ?? ''}
|
||||
tooltipText="Copy Executor"
|
||||
/>
|
||||
</span>
|
||||
</span>
|
||||
</Tooltip>
|
||||
);
|
||||
|
||||
@@ -95,7 +95,8 @@ export function getBasicNxCloudSection(items: MenuItem[]): MenuSection {
|
||||
.map((m) => {
|
||||
return {
|
||||
...m,
|
||||
disableCollapsible: !m.id.endsWith('tutorial'),
|
||||
disableCollapsible:
|
||||
!m.id.endsWith('tutorial') && !m.id.endsWith('concepts'),
|
||||
};
|
||||
}),
|
||||
};
|
||||
|
||||
@@ -54,6 +54,7 @@ function CategoryBox({ category }: { category: RelatedDocumentsCategory }) {
|
||||
<Link
|
||||
href={d.path}
|
||||
className="flex flex-grow items-center justify-between no-underline hover:text-sky-600 hover:underline dark:hover:text-sky-400"
|
||||
prefetch={false}
|
||||
>
|
||||
<span>{d.name}</span>
|
||||
<ArrowRightIcon className="h-4 w-4 text-slate-500 dark:text-slate-400" />
|
||||
|
||||
@@ -93,6 +93,7 @@ export function TableOfContents({
|
||||
'pl-6': item.level === 3,
|
||||
}
|
||||
)}
|
||||
prefetch={false}
|
||||
>
|
||||
{item.level === 1 ? 'Overview' : item.title}
|
||||
</Link>
|
||||
|
||||
@@ -129,6 +129,7 @@ export function Content({
|
||||
className="relative mx-4 inline-flex rounded-md border border-green-100 bg-green-50 px-4 py-2 text-xs font-medium text-green-600 dark:border-green-900 dark:bg-green-900/30 dark:text-green-400"
|
||||
href="/recipes/other/rescope"
|
||||
title="Nx 16 package name changes"
|
||||
prefetch={false}
|
||||
>
|
||||
Rescope @nrwl to @nx
|
||||
</Link>
|
||||
|
||||
@@ -20,7 +20,11 @@ function Hit({
|
||||
hit: InternalDocSearchHit | StoredDocSearchHit;
|
||||
children: ReactNode;
|
||||
}): JSX.Element {
|
||||
return <Link href={hit.url}>{children}</Link>;
|
||||
return (
|
||||
<Link href={hit.url} prefetch={false}>
|
||||
{children}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
export function AlgoliaSearch({
|
||||
|
||||
@@ -32,7 +32,7 @@ const pages: Array<{ title: string; path: string }> = [
|
||||
},
|
||||
{
|
||||
title: 'Running Nx Cloud Enterprise',
|
||||
path: '/ci/features/on-premise',
|
||||
path: '/ci/recipes/enterprise/on-premise/on-premise',
|
||||
},
|
||||
{
|
||||
title: 'Auth (Basic)',
|
||||
|
||||
@@ -240,7 +240,11 @@ export default function Changelog(props: ChangeLogProps): JSX.Element {
|
||||
</h1>
|
||||
<p className="mt-4">
|
||||
All the Nx goodies in one page, sorted by release. See our{' '}
|
||||
<Link className="underline" href="/reference/releases">
|
||||
<Link
|
||||
className="underline"
|
||||
href="/reference/releases"
|
||||
prefetch={false}
|
||||
>
|
||||
release page
|
||||
</Link>{' '}
|
||||
for information about the release cycle and LTS policy.
|
||||
|
||||
@@ -114,7 +114,11 @@ export default function Packages({
|
||||
<p>
|
||||
In version 16, we have rescoped our packages to{' '}
|
||||
<code>@nx/*</code> from <code>@nrwl/*</code>.{' '}
|
||||
<Link href="/recipes/other/rescope" className="underline">
|
||||
<Link
|
||||
href="/recipes/other/rescope"
|
||||
className="underline"
|
||||
prefetch={false}
|
||||
>
|
||||
Read more about the rescope ≫
|
||||
</Link>
|
||||
</p>
|
||||
@@ -129,6 +133,7 @@ export default function Packages({
|
||||
key={'ref-' + pkg.name}
|
||||
href={pkg.path}
|
||||
className="group relative flex items-center gap-3 rounded-md border border-slate-200 bg-slate-50/40 p-4 text-sm capitalize shadow-sm transition hover:bg-slate-50 dark:border-slate-800/40 dark:bg-slate-800/60 dark:hover:bg-slate-800"
|
||||
prefetch={false}
|
||||
>
|
||||
<img
|
||||
className="h-5 w-5 object-cover opacity-75 dark:invert"
|
||||
|
||||
@@ -1,10 +1,14 @@
|
||||
import { getBasicPluginsSection } from '@nx/nx-dev/data-access-menu';
|
||||
import { Menu } from '@nx/nx-dev/models-menu';
|
||||
import { Breadcrumbs, DocumentationHeader, Footer } from '@nx/nx-dev/ui-common';
|
||||
import { getBasicNxSection } from '@nx/nx-dev/data-access-menu';
|
||||
import { MenuItem } from '@nx/nx-dev/models-menu';
|
||||
import {
|
||||
Breadcrumbs,
|
||||
DocumentationHeader,
|
||||
Footer,
|
||||
SidebarContainer,
|
||||
} from '@nx/nx-dev/ui-common';
|
||||
import { PluginDirectory } from '@nx/nx-dev/ui-community';
|
||||
import { NextSeo } from 'next-seo';
|
||||
import { useRouter } from 'next/router';
|
||||
import { useRef } from 'react';
|
||||
import { menusApi } from '../lib/menus.api';
|
||||
import { useNavToggle } from '../lib/navigation-toggle.effect';
|
||||
import { nxPackagesApi } from '../lib/packages.api';
|
||||
@@ -20,7 +24,7 @@ interface PluginInfo {
|
||||
}
|
||||
interface BrowseProps {
|
||||
pluginList: PluginInfo[];
|
||||
// segments: string[];
|
||||
menu: MenuItem[];
|
||||
}
|
||||
|
||||
export async function getStaticProps(): Promise<{ props: BrowseProps }> {
|
||||
@@ -56,7 +60,7 @@ export async function getStaticProps(): Promise<{ props: BrowseProps }> {
|
||||
isOfficial: false,
|
||||
})),
|
||||
],
|
||||
// segments,
|
||||
menu: menusApi.getMenu('nx', ''),
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -64,14 +68,9 @@ export async function getStaticProps(): Promise<{ props: BrowseProps }> {
|
||||
export default function Browse(props: BrowseProps): JSX.Element {
|
||||
const router = useRouter();
|
||||
const { toggleNav, navIsOpen } = useNavToggle();
|
||||
const wrapperElement = useRef(null);
|
||||
|
||||
const vm: {
|
||||
menu: Menu;
|
||||
} = {
|
||||
menu: {
|
||||
sections: [getBasicPluginsSection(menusApi.getMenu('extending-nx', ''))],
|
||||
},
|
||||
const menu = {
|
||||
sections: [getBasicNxSection(props.menu)],
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -106,8 +105,14 @@ export default function Browse(props: BrowseProps): JSX.Element {
|
||||
role="main"
|
||||
className="flex h-full flex-1 overflow-y-hidden"
|
||||
>
|
||||
<div className="hidden">
|
||||
<SidebarContainer
|
||||
menu={menu}
|
||||
navIsOpen={navIsOpen}
|
||||
toggleNav={toggleNav}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
ref={wrapperElement}
|
||||
id="wrapper"
|
||||
data-testid="wrapper"
|
||||
className="relative flex flex-grow flex-col items-stretch justify-start overflow-y-scroll"
|
||||
|
||||
@@ -43,6 +43,7 @@ const tips: NewYearTip[] = [
|
||||
<Link
|
||||
href="/getting-started/intro"
|
||||
className="text-slate-900 underline dark:text-slate-100"
|
||||
prefetch={false}
|
||||
>
|
||||
Intro to Nx
|
||||
</Link>
|
||||
@@ -78,6 +79,7 @@ const tips: NewYearTip[] = [
|
||||
<Link
|
||||
href="/concepts/integrated-vs-package-based"
|
||||
className="text-slate-900 underline dark:text-slate-100"
|
||||
prefetch={false}
|
||||
>
|
||||
Types of Repos Guide
|
||||
</Link>
|
||||
@@ -109,6 +111,7 @@ const tips: NewYearTip[] = [
|
||||
<Link
|
||||
href="/ci/features/distribute-task-execution"
|
||||
className="text-slate-900 underline dark:text-slate-100"
|
||||
prefetch={false}
|
||||
>
|
||||
Nx Agents
|
||||
</Link>
|
||||
@@ -120,6 +123,7 @@ const tips: NewYearTip[] = [
|
||||
<Link
|
||||
href="/ci/intro/ci-with-nx"
|
||||
className="text-slate-900 underline dark:text-slate-100"
|
||||
prefetch={false}
|
||||
>
|
||||
CI with Nx
|
||||
</Link>
|
||||
@@ -153,6 +157,7 @@ const tips: NewYearTip[] = [
|
||||
<Link
|
||||
href="/recipes/adopting-nx/adding-to-existing-project"
|
||||
className="text-slate-900 underline dark:text-slate-100"
|
||||
prefetch={false}
|
||||
>
|
||||
Add Nx to an Existing Project
|
||||
</Link>
|
||||
@@ -187,6 +192,7 @@ const tips: NewYearTip[] = [
|
||||
<Link
|
||||
href="/concepts/module-federation/micro-frontend-architecture"
|
||||
className="text-slate-900 underline dark:text-slate-100"
|
||||
prefetch={false}
|
||||
>
|
||||
Micro Frontends with Nx
|
||||
</Link>
|
||||
@@ -211,18 +217,21 @@ const tips: NewYearTip[] = [
|
||||
<Link
|
||||
href="/getting-started/tutorials/react-standalone-tutorial"
|
||||
className="m-4 w-20"
|
||||
prefetch={false}
|
||||
>
|
||||
{frameworkIcons.react.image}
|
||||
</Link>
|
||||
<Link
|
||||
href="/getting-started/tutorials/angular-standalone-tutorial"
|
||||
className="m-4 w-20"
|
||||
prefetch={false}
|
||||
>
|
||||
{frameworkIcons.angular.image}
|
||||
</Link>
|
||||
<Link
|
||||
href="/getting-started/tutorials/vue-standalone-tutorial"
|
||||
className="m-4 w-20"
|
||||
prefetch={false}
|
||||
>
|
||||
{frameworkIcons.vue.image}
|
||||
</Link>
|
||||
@@ -232,6 +241,7 @@ const tips: NewYearTip[] = [
|
||||
<Link
|
||||
href="/showcase/example-repos"
|
||||
className="text-slate-900 underline dark:text-slate-100"
|
||||
prefetch={false}
|
||||
>
|
||||
Nx with your Favorite Tech
|
||||
</Link>
|
||||
@@ -266,6 +276,7 @@ const tips: NewYearTip[] = [
|
||||
<Link
|
||||
href="/extending-nx/recipes/create-install-package"
|
||||
className="text-slate-900 underline dark:text-slate-100"
|
||||
prefetch={false}
|
||||
>
|
||||
Build Your Own CLI
|
||||
</Link>
|
||||
@@ -304,6 +315,7 @@ const tips: NewYearTip[] = [
|
||||
<Link
|
||||
href="https://go.nx.dev/nx-agents-ea"
|
||||
className="text-slate-900 underline dark:text-slate-100"
|
||||
prefetch={false}
|
||||
>
|
||||
Sign up now for early access.
|
||||
</Link>
|
||||
@@ -313,6 +325,7 @@ const tips: NewYearTip[] = [
|
||||
<Link
|
||||
href="/ci/features/distribute-task-execution"
|
||||
className="text-slate-900 underline dark:text-slate-100"
|
||||
prefetch={false}
|
||||
>
|
||||
Nx Agents
|
||||
</Link>
|
||||
@@ -347,6 +360,7 @@ const tips: NewYearTip[] = [
|
||||
<Link
|
||||
href="/recipes/tips-n-tricks/standalone-to-integrated"
|
||||
className="text-slate-900 underline dark:text-slate-100"
|
||||
prefetch={false}
|
||||
>
|
||||
convert a standalone app repo to a monorepo
|
||||
</Link>
|
||||
@@ -378,6 +392,7 @@ const tips: NewYearTip[] = [
|
||||
<Link
|
||||
href="https://blog.nrwl.io/evergreen-tooling-more-than-just-codemods-fc68f32ce605"
|
||||
className="text-slate-900 underline dark:text-slate-100"
|
||||
prefetch={false}
|
||||
>
|
||||
Evergreen Tooling
|
||||
</Link>
|
||||
@@ -388,6 +403,7 @@ const tips: NewYearTip[] = [
|
||||
<Link
|
||||
href="/features/automate-updating-dependencies"
|
||||
className="text-slate-900 underline dark:text-slate-100"
|
||||
prefetch={false}
|
||||
>
|
||||
Automate Updating Dependencies
|
||||
</Link>
|
||||
|
||||
@@ -426,7 +426,9 @@ const nxCloudUrls = {
|
||||
'/nx-cloud/features/distribute-task-execution',
|
||||
'/concepts/affected': '/ci/features/affected',
|
||||
'/nx-cloud/private-cloud': '/ci/recipes/enterprise/on-premise',
|
||||
'/nx-cloud/private-cloud/get-started': '/ci/features/on-premise',
|
||||
'/nx-cloud/private-cloud/get-started':
|
||||
'/ci/recipes/enterprise/on-premise/on-premise',
|
||||
'/ci/features/on-premise': '/ci/recipes/enterprise/on-premise/on-premise',
|
||||
'/nx-cloud/private-cloud/auth-single-admin':
|
||||
'/nx-cloud/recipes/enterprise/on-premise/auth-single-admin',
|
||||
'/nx-cloud/private-cloud/auth-github':
|
||||
@@ -478,6 +480,7 @@ const nxCloudUrls = {
|
||||
'/ci/concepts/parallelization-distribution',
|
||||
'/nx-cloud/:path*': '/ci/:path*',
|
||||
'/core-features/:path*': '/features/:path*',
|
||||
'/ci/recipes/set-up/connect-to-cloud': '/ci/intro/connect-to-cloud',
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -46,6 +46,7 @@ export function BlogDetails({ post }: BlogDetailsProps) {
|
||||
<Link
|
||||
href="/blog"
|
||||
className="flex w-20 shrink-0 items-center gap-2 text-slate-400 hover:text-slate-800 dark:text-slate-600 dark:hover:text-slate-200"
|
||||
prefetch={false}
|
||||
>
|
||||
<ChevronLeftIcon className="h-3 w-3" />
|
||||
Blog
|
||||
|
||||
@@ -28,6 +28,7 @@ export function BlogEntry({ post }: BlogEntryProps) {
|
||||
href={`/blog/${post.slug}`}
|
||||
title={post.title}
|
||||
className="text-balance text-lg font-semibold text-slate-900 dark:text-white"
|
||||
prefetch={false}
|
||||
>
|
||||
<span className="absolute inset-0" aria-hidden="true" />
|
||||
{post.title}
|
||||
|
||||
@@ -30,6 +30,7 @@ export function MoreBlogs({ blogs }: MoreBlogsProps) {
|
||||
href={`/blog/${post.slug}`}
|
||||
key={post.slug}
|
||||
className="relative flex items-center gap-6 border-b border-slate-200 py-5 text-sm before:absolute before:inset-x-[-16px] before:inset-y-[-2px] before:z-[-1] before:rounded-xl before:bg-slate-200 before:opacity-0 last:border-0 before:hover:opacity-100 dark:border-slate-800 dark:before:bg-slate-800/50"
|
||||
prefetch={false}
|
||||
>
|
||||
<span className="w-1/2 flex-none text-balance font-medium text-slate-500 sm:w-5/12 dark:text-white">
|
||||
{post.title}
|
||||
|
||||
@@ -110,6 +110,7 @@ export const ButtonLink = forwardRef(function (
|
||||
href={href}
|
||||
title={title}
|
||||
className={getLayoutClassName(className)}
|
||||
prefetch={false}
|
||||
{...props}
|
||||
>
|
||||
<ButtonInner variant={variant} size={size}>
|
||||
|
||||
@@ -165,6 +165,7 @@ export function Footer(): JSX.Element {
|
||||
href={item.href}
|
||||
title={item.label}
|
||||
className="text-sm text-slate-500 hover:text-slate-600 dark:hover:text-slate-400"
|
||||
prefetch={false}
|
||||
>
|
||||
<span className="sr-only">{item.name}</span>
|
||||
<item.icon className="h-6 w-6" aria-hidden="true" />
|
||||
@@ -187,6 +188,7 @@ export function Footer(): JSX.Element {
|
||||
<Link
|
||||
href={item.href}
|
||||
className="text-sm text-slate-500 hover:text-slate-600 dark:hover:text-slate-400"
|
||||
prefetch={false}
|
||||
>
|
||||
{item.name}
|
||||
</Link>
|
||||
@@ -204,6 +206,7 @@ export function Footer(): JSX.Element {
|
||||
<Link
|
||||
href={item.href}
|
||||
className="text-sm text-slate-500 hover:text-slate-600 dark:hover:text-slate-400"
|
||||
prefetch={false}
|
||||
>
|
||||
{item.name}
|
||||
</Link>
|
||||
@@ -223,6 +226,7 @@ export function Footer(): JSX.Element {
|
||||
<Link
|
||||
href={item.href}
|
||||
className="text-sm text-slate-500 hover:text-slate-600 dark:hover:text-slate-400"
|
||||
prefetch={false}
|
||||
>
|
||||
{item.name}
|
||||
</Link>
|
||||
@@ -240,6 +244,7 @@ export function Footer(): JSX.Element {
|
||||
<Link
|
||||
href={item.href}
|
||||
className="text-sm text-slate-500 hover:text-slate-600 dark:hover:text-slate-400"
|
||||
prefetch={false}
|
||||
>
|
||||
{item.name}
|
||||
</Link>
|
||||
|
||||
@@ -36,6 +36,7 @@ export function DefaultMenuItem({
|
||||
title={item.name}
|
||||
target={hasExternalLink ? '_blank' : '_self'}
|
||||
className="text-sm font-medium text-slate-900 dark:text-slate-200"
|
||||
prefetch={false}
|
||||
>
|
||||
{item.name}
|
||||
{item.isNew ? (
|
||||
|
||||
@@ -43,6 +43,7 @@ function Menu({ tabs }: { tabs: any[] }): JSX.Element {
|
||||
'whitespace-nowrap border-b-2 py-2 text-sm font-medium'
|
||||
)}
|
||||
aria-current={tab.current ? 'page' : undefined}
|
||||
prefetch={false}
|
||||
>
|
||||
{tab.name}
|
||||
</Link>
|
||||
@@ -203,6 +204,7 @@ export function DocumentationHeader({
|
||||
<Link
|
||||
href="/"
|
||||
className="flex flex-grow items-center px-4 text-slate-900 lg:px-0 dark:text-white"
|
||||
prefetch={false}
|
||||
>
|
||||
<span className="sr-only">Nx</span>
|
||||
<NxIcon aria-hidden="true" className="h-8 w-8" />
|
||||
@@ -210,6 +212,7 @@ export function DocumentationHeader({
|
||||
<Link
|
||||
href="/getting-started/intro"
|
||||
className="ml-2 hidden items-center px-4 text-slate-900 lg:flex lg:px-0 dark:text-white"
|
||||
prefetch={false}
|
||||
>
|
||||
<span className="text-xl font-bold uppercase tracking-wide">
|
||||
Docs
|
||||
@@ -320,13 +323,15 @@ export function DocumentationHeader({
|
||||
href="/getting-started/intro"
|
||||
title="Documentation"
|
||||
className="hidden px-3 py-2 font-medium leading-tight hover:text-blue-500 md:inline-flex dark:text-slate-200 dark:hover:text-sky-500"
|
||||
prefetch={false}
|
||||
>
|
||||
Documentation
|
||||
Docs
|
||||
</Link>
|
||||
<Link
|
||||
href="/blog"
|
||||
title="Blog"
|
||||
className="hidden px-3 py-2 font-medium leading-tight hover:text-blue-500 md:inline-flex dark:text-slate-200 dark:hover:text-sky-500"
|
||||
prefetch={false}
|
||||
>
|
||||
Blog
|
||||
</Link>
|
||||
@@ -391,6 +396,7 @@ export function DocumentationHeader({
|
||||
className="hidden cursor-pointer px-3 py-2 text-sm font-medium leading-tight hover:text-blue-500 md:inline-flex dark:text-slate-200 dark:hover:text-sky-500"
|
||||
title="Contact Us"
|
||||
href="/contact"
|
||||
prefetch={false}
|
||||
>
|
||||
Contact
|
||||
</Link>
|
||||
|
||||
@@ -54,6 +54,7 @@ export function Header(): JSX.Element {
|
||||
<Link
|
||||
href="/"
|
||||
className="mr-4 flex items-center text-slate-900 dark:text-white"
|
||||
prefetch={false}
|
||||
>
|
||||
<span className="sr-only">Nx</span>
|
||||
<NxIcon aria-hidden="true" className="h-8 w-8" />
|
||||
@@ -156,13 +157,15 @@ export function Header(): JSX.Element {
|
||||
href="/getting-started/intro"
|
||||
title="Documentation"
|
||||
className="hidden px-3 py-2 font-medium leading-tight hover:text-blue-500 md:inline-flex dark:text-slate-200 dark:hover:text-sky-500"
|
||||
prefetch={false}
|
||||
>
|
||||
Documentation
|
||||
Docs
|
||||
</Link>
|
||||
<Link
|
||||
href="/blog"
|
||||
title="Blog"
|
||||
className="hidden px-3 py-2 font-medium leading-tight hover:text-blue-500 md:inline-flex dark:text-slate-200 dark:hover:text-sky-500"
|
||||
prefetch={false}
|
||||
>
|
||||
Blog
|
||||
</Link>
|
||||
@@ -227,6 +230,7 @@ export function Header(): JSX.Element {
|
||||
className="hidden cursor-pointer px-3 py-2 font-medium leading-tight hover:text-blue-500 md:inline-flex dark:text-slate-200 dark:hover:text-sky-500"
|
||||
title="Contact Us"
|
||||
href="/contact"
|
||||
prefetch={false}
|
||||
>
|
||||
Contact
|
||||
</Link>
|
||||
@@ -260,6 +264,7 @@ export function Header(): JSX.Element {
|
||||
<Link
|
||||
href="/"
|
||||
className="flex items-center text-slate-900 dark:text-white"
|
||||
prefetch={false}
|
||||
>
|
||||
<span className="sr-only">Nx</span>
|
||||
<NxIcon aria-hidden="true" className="h-8 w-8" />
|
||||
@@ -315,6 +320,7 @@ export function Header(): JSX.Element {
|
||||
<Link
|
||||
href="/"
|
||||
className="flex items-center text-slate-900 dark:text-white"
|
||||
prefetch={false}
|
||||
>
|
||||
<svg
|
||||
role="img"
|
||||
@@ -435,13 +441,15 @@ export function Header(): JSX.Element {
|
||||
href="/getting-started/intro"
|
||||
title="Documentation"
|
||||
className="block py-4 font-medium leading-tight hover:text-blue-500 dark:text-slate-200 dark:hover:text-sky-500"
|
||||
prefetch={false}
|
||||
>
|
||||
Documentation
|
||||
Docs
|
||||
</Link>
|
||||
<Link
|
||||
href="/blog"
|
||||
title="Blog"
|
||||
className="block py-4 font-medium leading-tight hover:text-blue-500 dark:text-slate-200 dark:hover:text-sky-500"
|
||||
prefetch={false}
|
||||
>
|
||||
Blog
|
||||
</Link>
|
||||
@@ -501,6 +509,7 @@ export function Header(): JSX.Element {
|
||||
href="/contact"
|
||||
title="Contact"
|
||||
className="block py-4 font-medium leading-tight hover:text-blue-500 dark:text-slate-200 dark:hover:text-sky-500"
|
||||
prefetch={false}
|
||||
>
|
||||
Contact
|
||||
</Link>
|
||||
|
||||
@@ -9,7 +9,9 @@ import {
|
||||
PlayCircleIcon,
|
||||
ShareIcon,
|
||||
Squares2X2Icon,
|
||||
RectangleGroupIcon,
|
||||
ChatBubbleBottomCenterTextIcon,
|
||||
ArrowUpCircleIcon,
|
||||
} from '@heroicons/react/24/outline';
|
||||
import { FC, SVGProps } from 'react';
|
||||
import { NxAgentsIcon } from '../nx-agents-icon';
|
||||
@@ -27,7 +29,7 @@ export interface MenuItem {
|
||||
|
||||
export const featuresItems: MenuItem[] = [
|
||||
{
|
||||
name: 'Task Running',
|
||||
name: 'Run Tasks',
|
||||
// description: 'Run one or many tasks in parallel.',
|
||||
description: null,
|
||||
href: '/features/run-tasks',
|
||||
@@ -36,7 +38,7 @@ export const featuresItems: MenuItem[] = [
|
||||
isHighlight: false,
|
||||
},
|
||||
{
|
||||
name: 'Local Caching',
|
||||
name: 'Cache Task Results',
|
||||
// description: 'Speeds up your local workflow.',
|
||||
description: null,
|
||||
href: '/features/cache-task-results',
|
||||
@@ -45,7 +47,7 @@ export const featuresItems: MenuItem[] = [
|
||||
isHighlight: false,
|
||||
},
|
||||
{
|
||||
name: 'Nx Graph',
|
||||
name: 'Explore Your Workspace',
|
||||
// description: 'See interactions for tasks and modules.',
|
||||
description: null,
|
||||
href: '/features/explore-graph',
|
||||
@@ -54,16 +56,16 @@ export const featuresItems: MenuItem[] = [
|
||||
isHighlight: false,
|
||||
},
|
||||
{
|
||||
name: 'Automated updates',
|
||||
name: 'Automate Updating Dependencies',
|
||||
// description: 'Keep running on latest without effort.',
|
||||
description: null,
|
||||
href: '/features/automate-updating-dependencies',
|
||||
icon: ArrowPathIcon,
|
||||
icon: ArrowUpCircleIcon,
|
||||
isNew: false,
|
||||
isHighlight: false,
|
||||
},
|
||||
{
|
||||
name: 'Module Boundaries',
|
||||
name: 'Enforce Module Boundaries',
|
||||
// description: 'Partition your code into defined units.',
|
||||
description: null,
|
||||
href: '/features/enforce-module-boundaries',
|
||||
@@ -72,16 +74,16 @@ export const featuresItems: MenuItem[] = [
|
||||
isHighlight: false,
|
||||
},
|
||||
{
|
||||
name: 'Nx Release',
|
||||
name: 'Manage Releases',
|
||||
// description: 'Versioning, changelog, publishing.',
|
||||
description: null,
|
||||
href: '/features/manage-releases',
|
||||
icon: CubeIcon,
|
||||
isNew: true,
|
||||
isNew: false,
|
||||
isHighlight: false,
|
||||
},
|
||||
{
|
||||
name: 'Nx Replay',
|
||||
name: 'Use Remote Caching (Nx Replay)',
|
||||
description: 'Zero-config, fast & secure remote cache solution.',
|
||||
href: '/ci/features/remote-cache',
|
||||
icon: NxReplayIcon,
|
||||
@@ -89,12 +91,12 @@ export const featuresItems: MenuItem[] = [
|
||||
isHighlight: true,
|
||||
},
|
||||
{
|
||||
name: 'Nx Agents',
|
||||
name: 'Distribute Task Execution (Nx Agents)',
|
||||
description:
|
||||
'One-line config for distributing tasks, E2E tests split & flaky tasks rerun.',
|
||||
'One-line config for distributing tasks across multiple machines',
|
||||
href: '/ci/features/distribute-task-execution',
|
||||
icon: NxAgentsIcon,
|
||||
isNew: true,
|
||||
isNew: false,
|
||||
isHighlight: true,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -34,6 +34,7 @@ export function MobileMenuItem({
|
||||
title={item.name}
|
||||
target={hasExternalLink ? '_blank' : '_self'}
|
||||
className="text-sm font-medium text-slate-900 dark:text-slate-200"
|
||||
prefetch={false}
|
||||
>
|
||||
{item.name}
|
||||
{item.isNew ? (
|
||||
|
||||
@@ -47,6 +47,7 @@ export function PluginCard({
|
||||
target={isOfficial ? undefined : '_blank'}
|
||||
rel={isOfficial ? undefined : 'noreferrer'}
|
||||
className="flex grow flex-col focus:outline-none"
|
||||
prefetch={false}
|
||||
>
|
||||
<span className="absolute inset-0" aria-hidden="true" />
|
||||
<p className="mb-2 line-clamp-3 grow text-sm">{description}</p>
|
||||
|
||||
@@ -103,7 +103,11 @@ function SidebarSectionItems({ item }: { item: MenuItem }): JSX.Element {
|
||||
onClick={handleCollapseToggle}
|
||||
>
|
||||
{item.disableCollapsible ? (
|
||||
<Link href={item.path as string} className="hover:underline">
|
||||
<Link
|
||||
href={item.path as string}
|
||||
className="hover:underline"
|
||||
prefetch={false}
|
||||
>
|
||||
{item.name}
|
||||
</Link>
|
||||
) : (
|
||||
@@ -134,6 +138,7 @@ function SidebarSectionItems({ item }: { item: MenuItem }): JSX.Element {
|
||||
className={cx(
|
||||
'relative block py-1 text-slate-500 transition-colors duration-200 hover:text-slate-900 dark:text-slate-400 dark:hover:text-slate-300'
|
||||
)}
|
||||
prefetch={false}
|
||||
>
|
||||
<span
|
||||
className={cx('relative', {
|
||||
@@ -187,11 +192,17 @@ export function SidebarMobile({
|
||||
const router = useRouter();
|
||||
const isCI: boolean = router.asPath.startsWith('/ci');
|
||||
const isAPI: boolean = router.asPath.startsWith('/nx-api');
|
||||
const isPlugins: boolean = router.asPath.startsWith('/extending-nx');
|
||||
const isExtendingNx: boolean = router.asPath.startsWith('/extending-nx');
|
||||
const isPlugins: boolean = router.asPath.startsWith('/plugin-registry');
|
||||
const isChangelog: boolean = router.asPath.startsWith('/changelog');
|
||||
const isAiChat: boolean = router.asPath.startsWith('/ai-chat');
|
||||
const isNx: boolean =
|
||||
!isCI && !isAPI && !isPlugins && !isChangelog && !isAiChat;
|
||||
!isCI &&
|
||||
!isAPI &&
|
||||
!isExtendingNx &&
|
||||
!isPlugins &&
|
||||
!isChangelog &&
|
||||
!isAiChat;
|
||||
|
||||
const sections = {
|
||||
general: [
|
||||
@@ -220,6 +231,11 @@ export function SidebarMobile({
|
||||
{
|
||||
name: 'Extending Nx',
|
||||
href: '/extending-nx/intro/getting-started',
|
||||
current: isExtendingNx,
|
||||
},
|
||||
{
|
||||
name: 'Plugins',
|
||||
href: '/plugin-registry',
|
||||
current: isPlugins,
|
||||
},
|
||||
{
|
||||
@@ -275,6 +291,7 @@ export function SidebarMobile({
|
||||
<Link
|
||||
href="/"
|
||||
className="flex flex-grow items-center px-4 text-slate-900 lg:px-0 dark:text-white"
|
||||
prefetch={false}
|
||||
>
|
||||
<span className="sr-only">Nx</span>
|
||||
<NxIcon aria-hidden="true" className="h-8 w-8" />
|
||||
@@ -296,6 +313,7 @@ export function SidebarMobile({
|
||||
'whitespace-nowrap p-4 text-center text-sm font-medium'
|
||||
)}
|
||||
aria-current={section.current ? 'page' : undefined}
|
||||
prefetch={false}
|
||||
>
|
||||
{section.name}
|
||||
</Link>
|
||||
@@ -313,6 +331,7 @@ export function SidebarMobile({
|
||||
'whitespace-nowrap p-4 text-center text-sm font-medium'
|
||||
)}
|
||||
aria-current={section.current ? 'page' : undefined}
|
||||
prefetch={false}
|
||||
>
|
||||
{section.name}
|
||||
</Link>
|
||||
|
||||
@@ -19,7 +19,11 @@ export function ConnectWithUs(): JSX.Element {
|
||||
</p>
|
||||
<p className="py-4">
|
||||
Looking for community plugins? Find them listed in the{' '}
|
||||
<Link href="/plugin-registry" className="font-semibold underline">
|
||||
<Link
|
||||
href="/plugin-registry"
|
||||
className="font-semibold underline"
|
||||
prefetch={false}
|
||||
>
|
||||
plugin registry
|
||||
</Link>
|
||||
.
|
||||
|
||||
@@ -145,6 +145,7 @@ export function ContactLinks(): JSX.Element {
|
||||
href="/getting-started/intro"
|
||||
title="Nx documentation"
|
||||
className="mt-2 flex items-center gap-2 text-sm text-slate-500 transition hover:text-slate-800 dark:hover:text-slate-400"
|
||||
prefetch={false}
|
||||
>
|
||||
<span>Nx docs</span>
|
||||
<ChevronRightIcon aria-hidden="true" className="h-3 w-3" />
|
||||
|
||||
@@ -60,6 +60,7 @@ export const BentoGridItem = ({
|
||||
href={url}
|
||||
title="Learn more"
|
||||
className="float-right text-sm font-medium transition duration-200 group-hover/bento:text-blue-500 group-hover/bento:dark:text-sky-500"
|
||||
prefetch={false}
|
||||
>
|
||||
<span className="group absolute inset-0" />
|
||||
|
||||
|
||||
@@ -62,6 +62,7 @@ export function CallToAction(): JSX.Element {
|
||||
href="/contact/engineering"
|
||||
title="Talk to the engineering team"
|
||||
className="rounded-md bg-slate-950 px-3.5 py-2.5 text-sm font-semibold text-slate-100 shadow-sm hover:bg-slate-800 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white dark:bg-white dark:text-slate-900 dark:hover:bg-slate-100"
|
||||
prefetch={false}
|
||||
>
|
||||
Talk to engineering
|
||||
</Link>
|
||||
@@ -71,6 +72,7 @@ export function CallToAction(): JSX.Element {
|
||||
href="/contact/sales"
|
||||
title="Talk to the sales team"
|
||||
className="font-semibold underline"
|
||||
prefetch={false}
|
||||
>
|
||||
Speak directly to sales
|
||||
</Link>
|
||||
|
||||
@@ -33,6 +33,7 @@ export function Hero(): JSX.Element {
|
||||
href="/contact/sales"
|
||||
title="Talk to the sales team"
|
||||
className="font-semibold underline"
|
||||
prefetch={false}
|
||||
>
|
||||
Speak directly to sales
|
||||
</Link>
|
||||
|
||||
@@ -37,6 +37,7 @@ export function ExtensibleAndIntegrated(): JSX.Element {
|
||||
href="/getting-started/editor-setup"
|
||||
title="Install VSCode's native extension for Nx"
|
||||
className="font-medium text-blue-500 dark:text-sky-500"
|
||||
prefetch={false}
|
||||
>
|
||||
VSCode
|
||||
</Link>{' '}
|
||||
@@ -45,6 +46,7 @@ export function ExtensibleAndIntegrated(): JSX.Element {
|
||||
href="/getting-started/editor-setup"
|
||||
className="font-medium text-blue-500 dark:text-sky-500"
|
||||
title="Install JetBrains' native plugin for Nx"
|
||||
prefetch={false}
|
||||
>
|
||||
JetBrains
|
||||
</Link>{' '}
|
||||
@@ -52,6 +54,7 @@ export function ExtensibleAndIntegrated(): JSX.Element {
|
||||
<Link
|
||||
href="/getting-started/editor-setup#neovim"
|
||||
className="font-medium text-blue-500 dark:text-sky-500"
|
||||
prefetch={false}
|
||||
>
|
||||
Neovim
|
||||
</Link>
|
||||
@@ -70,6 +73,7 @@ export function ExtensibleAndIntegrated(): JSX.Element {
|
||||
href="https://nx.app/?utm_source=nx.dev"
|
||||
title="Nx Cloud: Distributed Task execution & Caching"
|
||||
className="font-medium text-blue-500 dark:text-sky-500"
|
||||
prefetch={false}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
|
||||
@@ -92,7 +92,7 @@ export function IdeIntegrationTab(): JSX.Element {
|
||||
</div>
|
||||
<div className="mt-3 sm:ml-3 sm:mt-0">
|
||||
<h3 className="text-sm font-medium">{feature.name}</h3>
|
||||
<Link href={feature.link}>
|
||||
<Link href={feature.link} prefetch={false}>
|
||||
<span className="absolute inset-0" aria-hidden="true" />
|
||||
<p className="mt-2 text-sm text-slate-500">
|
||||
{feature.description}
|
||||
|
||||
@@ -33,6 +33,7 @@ export function Hero(): JSX.Element {
|
||||
href="/ci/features/distribute-task-execution"
|
||||
title="Discover Nx Agents"
|
||||
className="font-semibold text-blue-500 dark:text-sky-500"
|
||||
prefetch={false}
|
||||
>
|
||||
<span className="absolute inset-0" aria-hidden="true"></span>Read
|
||||
more <span aria-hidden="true">→</span>
|
||||
|
||||
@@ -116,6 +116,7 @@ export function MigrationsAndCodeGeneration(): JSX.Element {
|
||||
<Link
|
||||
href="https://nx.dev/features/automate-updating-dependencies"
|
||||
className="ml-2 underline"
|
||||
prefetch={false}
|
||||
>
|
||||
https://nx.dev/features/automate-updating-dependencies
|
||||
</Link>
|
||||
|
||||
@@ -30,6 +30,7 @@ export function MonorepoStyles(): JSX.Element {
|
||||
href="/recipes/adopting-nx/adding-to-monorepo"
|
||||
title="Install VSCode's native extension for Nx"
|
||||
className="font-medium text-blue-500 dark:text-sky-500"
|
||||
prefetch={false}
|
||||
>
|
||||
Add Nx on top!
|
||||
</Link>{' '}
|
||||
@@ -41,6 +42,7 @@ export function MonorepoStyles(): JSX.Element {
|
||||
href="/extending-nx"
|
||||
title="Install VSCode's native extension for Nx"
|
||||
className="font-medium text-blue-500 dark:text-sky-500"
|
||||
prefetch={false}
|
||||
>
|
||||
powerful plugins
|
||||
</Link>
|
||||
|
||||
@@ -149,6 +149,7 @@ export function NxWithCi(): JSX.Element {
|
||||
<Link
|
||||
href="/ci/features/remote-cache"
|
||||
title="Discover Nx Replay"
|
||||
prefetch={false}
|
||||
>
|
||||
<span className="absolute inset-0"></span>Nx Replay
|
||||
</Link>
|
||||
|
||||
@@ -6,6 +6,7 @@ export function CustomLink(props: any) {
|
||||
|
||||
return (
|
||||
<Link
|
||||
prefetch={false}
|
||||
{...props}
|
||||
passHref
|
||||
target={target}
|
||||
|
||||
@@ -83,6 +83,7 @@ export function Cards({
|
||||
<Link
|
||||
className="group flex items-center whitespace-nowrap border-transparent px-4 py-0 text-sm font-semibold no-underline transition-all duration-200 ease-in-out hover:text-slate-900 dark:hover:text-sky-400"
|
||||
href={moreLink}
|
||||
prefetch={false}
|
||||
>
|
||||
Browse more
|
||||
<span
|
||||
@@ -117,6 +118,7 @@ export function LinkCard({
|
||||
href={url}
|
||||
className="no-prose relative col-span-1 flex flex-col items-center rounded-md border border-slate-200 bg-slate-50/40 p-4 text-center font-semibold shadow-sm transition focus-within:ring-2 focus-within:ring-blue-500 focus-within:ring-offset-2 hover:bg-slate-100 dark:border-slate-800/40 dark:bg-slate-800/60 dark:hover:bg-slate-800"
|
||||
style={{ textDecorationLine: 'none' }}
|
||||
prefetch={false}
|
||||
>
|
||||
{icon && (
|
||||
<div
|
||||
@@ -177,6 +179,7 @@ export function Card({
|
||||
href={url}
|
||||
title={title}
|
||||
className="group flex flex-col items-stretch rounded-md border border-slate-200 bg-slate-50/40 text-sm no-underline shadow-sm transition focus-within:ring-2 focus-within:ring-blue-500 focus-within:ring-offset-2 hover:bg-slate-50 dark:border-slate-800/40 dark:bg-slate-800/60 dark:hover:bg-slate-800"
|
||||
prefetch={false}
|
||||
>
|
||||
{!!hasYoutubeId && (
|
||||
<div className="max-h-24">
|
||||
|
||||
@@ -207,6 +207,7 @@ export function Persona({
|
||||
title={title}
|
||||
aria-hidden="true"
|
||||
className="absolute inset-0"
|
||||
prefetch={false}
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -14,6 +14,7 @@ export function Pill({
|
||||
<Link
|
||||
href={url}
|
||||
className="flex items-center font-semibold no-underline group-hover:underline"
|
||||
prefetch={false}
|
||||
>
|
||||
<span className="absolute inset-0" aria-hidden="true"></span>
|
||||
{children}
|
||||
|
||||
+2
-2
@@ -332,13 +332,13 @@
|
||||
"json-schema-to-typescript": "^10.1.5",
|
||||
"jsonpointer": "^5.0.0",
|
||||
"license-checker": "^25.0.1",
|
||||
"next": "14.2.3",
|
||||
"next": "14.2.4",
|
||||
"next-seo": "^5.13.0",
|
||||
"node-machine-id": "1.1.12",
|
||||
"npm-run-path": "^4.0.1",
|
||||
"preact": "10.6.4",
|
||||
"react": "18.3.1",
|
||||
"react-copy-to-clipboard": "^5.0.3",
|
||||
"react-copy-to-clipboard": "^5.1.0",
|
||||
"react-dom": "18.3.1",
|
||||
"react-syntax-highlighter": "^15.5.0",
|
||||
"regenerator-runtime": "0.13.7",
|
||||
|
||||
@@ -25,10 +25,19 @@ function shouldSkipInitialTargetRun(
|
||||
project: string,
|
||||
target: string
|
||||
): boolean {
|
||||
const allTargetNames = new Set<string>();
|
||||
for (const projectName in projectGraph.nodes) {
|
||||
const project = projectGraph.nodes[projectName];
|
||||
for (const targetName in project.data.targets ?? {}) {
|
||||
allTargetNames.add(targetName);
|
||||
}
|
||||
}
|
||||
|
||||
const projectDependencyConfigs = getDependencyConfigs(
|
||||
{ project, target },
|
||||
{},
|
||||
projectGraph
|
||||
projectGraph,
|
||||
Array.from(allTargetNames)
|
||||
);
|
||||
|
||||
// if the task runner already ran the target, skip the initial run
|
||||
|
||||
@@ -2,37 +2,51 @@ import { execSync } from 'node:child_process';
|
||||
import { isCI } from '../ci/is-ci';
|
||||
import { getPackageManagerCommand } from '../package-manager';
|
||||
|
||||
export const NxCloudChoices = ['yes', 'github', 'circleci', 'skip'];
|
||||
export const NxCloudChoices = [
|
||||
'github',
|
||||
'circleci',
|
||||
'gitlab',
|
||||
'azure',
|
||||
'bitbucket',
|
||||
'skip',
|
||||
'yes', // Deprecated but still handled
|
||||
];
|
||||
|
||||
const messageOptions = {
|
||||
const messageOptions: Record<string, MessageData[]> = {
|
||||
setupCI: [
|
||||
{
|
||||
code: 'enable-nx-cloud',
|
||||
message: `Do you want Nx Cloud to make your CI fast?`,
|
||||
initial: 1,
|
||||
code: 'which-ci-provider',
|
||||
message: `Which CI provider would you like to use?`,
|
||||
initial: 0,
|
||||
choices: [
|
||||
{ value: 'yes', name: 'Yes, enable Nx Cloud' },
|
||||
{ value: 'github', name: 'Yes, configure Nx Cloud for GitHub Actions' },
|
||||
{ value: 'circleci', name: 'Yes, configure Nx Cloud for Circle CI' },
|
||||
{ value: 'skip', name: 'Skip for now' },
|
||||
{ value: 'github', name: 'GitHub Actions' },
|
||||
{ value: 'circleci', name: 'Circle CI' },
|
||||
{ value: 'gitlab', name: 'Gitlab' },
|
||||
{ value: 'azure', name: 'Azure DevOps' },
|
||||
{ value: 'bitbucket', name: 'BitBucket Pipelines' },
|
||||
{ value: 'skip', name: '\nDo it later' },
|
||||
],
|
||||
footer:
|
||||
'\nWatch a short video on Nx Cloud at https://nx.dev/ci/intro/why-nx-cloud',
|
||||
hint: `\n(it's free and can be disabled any time)`,
|
||||
fallback: undefined,
|
||||
'\nRemote caching, task distribution, and test splitting are provided by Nx Cloud. Read more at https://nx.dev/ci',
|
||||
fallback: { value: 'skip', key: 'setupNxCloud' },
|
||||
},
|
||||
{
|
||||
code: 'set-up-ci',
|
||||
message: `Set up CI with caching, distribution and test deflaking`,
|
||||
initial: 0,
|
||||
choices: [
|
||||
{ value: 'github', name: 'Yes, for GitHub Actions with Nx Cloud' },
|
||||
{ value: 'circleci', name: 'Yes, for CircleCI with Nx Cloud' },
|
||||
{ value: 'skip', name: 'Skip for now' },
|
||||
{ value: 'github', name: 'GitHub Actions' },
|
||||
{ value: 'circleci', name: 'CircleCI' },
|
||||
{ value: 'gitlab', name: 'Gitlab' },
|
||||
{ value: 'azure', name: 'Azure DevOps' },
|
||||
{
|
||||
value: 'bitbucket',
|
||||
name: 'BitBucket Pipelines',
|
||||
},
|
||||
{ value: 'skip', name: '\nDo it later' },
|
||||
],
|
||||
footer:
|
||||
'\nWatch a short video on Nx Cloud at https://nx.dev/ci/intro/why-nx-cloud',
|
||||
hint: `\n(it's free and can be disabled any time)`,
|
||||
'\nRemote caching, task distribution, and test splitting are provided by Nx Cloud. Read more at https://nx.dev/ci',
|
||||
fallback: { value: 'skip', key: 'setupNxCloud' },
|
||||
},
|
||||
],
|
||||
@@ -51,10 +65,18 @@ const messageOptions = {
|
||||
fallback: undefined,
|
||||
},
|
||||
],
|
||||
} as const;
|
||||
};
|
||||
|
||||
export type MessageKey = keyof typeof messageOptions;
|
||||
type MessageData = (typeof messageOptions)[MessageKey][number];
|
||||
interface MessageData {
|
||||
code: string;
|
||||
message: string;
|
||||
initial: number;
|
||||
choices: Array<{ value: string; name: string }>;
|
||||
footer: string;
|
||||
hint?: string;
|
||||
fallback?: { value: string; key: MessageKey };
|
||||
}
|
||||
|
||||
export class PromptMessages {
|
||||
private selectedMessages: { [key in MessageKey]?: number } = {};
|
||||
|
||||
@@ -162,11 +162,11 @@ describe('eslint-utils', () => {
|
||||
});
|
||||
|
||||
describe('ESLint Flat Config', () => {
|
||||
it('should throw if a non eslint.config.js file is used with ESLint Flat Config', async () => {
|
||||
it('should throw if a non eslint.config.js or eslint.config.cjs file is used with ESLint Flat Config', async () => {
|
||||
await expect(
|
||||
resolveAndInstantiateESLint('./.eslintrc.json', {} as any, true)
|
||||
).rejects.toThrowErrorMatchingInlineSnapshot(
|
||||
`"When using the new Flat Config with ESLint, all configs must be named eslint.config.js and .eslintrc files may not be used. See https://eslint.org/docs/latest/use/configure/configuration-files-new"`
|
||||
`"When using the new Flat Config with ESLint, all configs must be named eslint.config.js or eslint.config.cjs and .eslintrc files may not be used. See https://eslint.org/docs/latest/use/configure/configuration-files"`
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -10,7 +10,8 @@ export async function resolveAndInstantiateESLint(
|
||||
) {
|
||||
if (useFlatConfig && eslintConfigPath && !isFlatConfig(eslintConfigPath)) {
|
||||
throw new Error(
|
||||
'When using the new Flat Config with ESLint, all configs must be named eslint.config.js and .eslintrc files may not be used. See https://eslint.org/docs/latest/use/configure/configuration-files-new'
|
||||
// todo: add support for eslint.config.mjs,
|
||||
'When using the new Flat Config with ESLint, all configs must be named eslint.config.js or eslint.config.cjs and .eslintrc files may not be used. See https://eslint.org/docs/latest/use/configure/configuration-files'
|
||||
);
|
||||
}
|
||||
const ESLint = await resolveESLintClass(useFlatConfig);
|
||||
|
||||
@@ -7,7 +7,10 @@ import {
|
||||
} from '@nx/devkit';
|
||||
import type { Tree } from '@nx/devkit';
|
||||
import type { Linter } from 'eslint';
|
||||
import { useFlatConfig } from '../../utils/flat-config';
|
||||
import {
|
||||
flatConfigEslintFilename,
|
||||
useFlatConfig,
|
||||
} from '../../utils/flat-config';
|
||||
import {
|
||||
addBlockToFlatConfigExport,
|
||||
addCompatToFlatConfig,
|
||||
@@ -174,7 +177,7 @@ export function addOverrideToLintConfig(
|
||||
if (useFlatConfig(tree)) {
|
||||
const fileName = joinPathFragments(
|
||||
root,
|
||||
isBase ? baseEsLintFlatConfigFile : 'eslint.config.js'
|
||||
isBase ? baseEsLintFlatConfigFile : flatConfigEslintFilename(tree)
|
||||
);
|
||||
const flatOverride = generateFlatOverride(override);
|
||||
let content = tree.read(fileName, 'utf8');
|
||||
@@ -220,7 +223,7 @@ export function updateOverrideInLintConfig(
|
||||
) => Linter.ConfigOverride<Linter.RulesRecord>
|
||||
) {
|
||||
if (useFlatConfig(tree)) {
|
||||
const fileName = joinPathFragments(root, 'eslint.config.js');
|
||||
const fileName = joinPathFragments(root, flatConfigEslintFilename(tree));
|
||||
let content = tree.read(fileName, 'utf8');
|
||||
content = replaceOverride(content, root, lookup, update);
|
||||
tree.write(fileName, content);
|
||||
@@ -262,7 +265,7 @@ export function lintConfigHasOverride(
|
||||
if (useFlatConfig(tree)) {
|
||||
const fileName = joinPathFragments(
|
||||
root,
|
||||
isBase ? baseEsLintFlatConfigFile : 'eslint.config.js'
|
||||
isBase ? baseEsLintFlatConfigFile : flatConfigEslintFilename(tree)
|
||||
);
|
||||
const content = tree.read(fileName, 'utf8');
|
||||
return hasOverride(content, lookup);
|
||||
@@ -282,7 +285,7 @@ export function replaceOverridesInLintConfig(
|
||||
overrides: Linter.ConfigOverride<Linter.RulesRecord>[]
|
||||
) {
|
||||
if (useFlatConfig(tree)) {
|
||||
const fileName = joinPathFragments(root, 'eslint.config.js');
|
||||
const fileName = joinPathFragments(root, flatConfigEslintFilename(tree));
|
||||
let content = tree.read(fileName, 'utf8');
|
||||
// we will be using compat here so we need to make sure it's added
|
||||
if (overrides.some(overrideNeedsCompat)) {
|
||||
@@ -311,7 +314,7 @@ export function addExtendsToLintConfig(
|
||||
) {
|
||||
const plugins = Array.isArray(plugin) ? plugin : [plugin];
|
||||
if (useFlatConfig(tree)) {
|
||||
const fileName = joinPathFragments(root, 'eslint.config.js');
|
||||
const fileName = joinPathFragments(root, flatConfigEslintFilename(tree));
|
||||
const pluginExtends = generatePluginExtendsElement(plugins);
|
||||
let content = tree.read(fileName, 'utf8');
|
||||
content = addCompatToFlatConfig(content);
|
||||
@@ -341,7 +344,7 @@ export function addPluginsToLintConfig(
|
||||
) {
|
||||
const plugins = Array.isArray(plugin) ? plugin : [plugin];
|
||||
if (useFlatConfig(tree)) {
|
||||
const fileName = joinPathFragments(root, 'eslint.config.js');
|
||||
const fileName = joinPathFragments(root, flatConfigEslintFilename(tree));
|
||||
let content = tree.read(fileName, 'utf8');
|
||||
const mappedPlugins: { name: string; varName: string; imp: string }[] = [];
|
||||
plugins.forEach((name) => {
|
||||
@@ -369,7 +372,7 @@ export function addIgnoresToLintConfig(
|
||||
ignorePatterns: string[]
|
||||
) {
|
||||
if (useFlatConfig(tree)) {
|
||||
const fileName = joinPathFragments(root, 'eslint.config.js');
|
||||
const fileName = joinPathFragments(root, flatConfigEslintFilename(tree));
|
||||
const block = generateAst<ts.ObjectLiteralExpression>({
|
||||
ignores: ignorePatterns.map((path) => mapFilePath(path)),
|
||||
});
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { existsSync, statSync } from 'fs';
|
||||
import { basename, dirname, join, resolve } from 'path';
|
||||
import { eslintFlatConfigFilenames } from './flat-config';
|
||||
|
||||
// TODO(leo): add support for eslint.config.mjs and eslint.config.cjs
|
||||
export const ESLINT_FLAT_CONFIG_FILENAMES = ['eslint.config.js'];
|
||||
export const ESLINT_FLAT_CONFIG_FILENAMES = eslintFlatConfigFilenames;
|
||||
|
||||
export const ESLINT_OLD_CONFIG_FILENAMES = [
|
||||
'.eslintrc',
|
||||
@@ -40,6 +40,7 @@ export function findFlatConfigFile(
|
||||
ESLINT_FLAT_CONFIG_FILENAMES
|
||||
);
|
||||
if (configFilePath) {
|
||||
console.log(`Found eslint flat config file at: ${configFilePath}`);
|
||||
return configFilePath;
|
||||
}
|
||||
if (currentDir === workspaceRoot) {
|
||||
|
||||
@@ -1,5 +1,24 @@
|
||||
import { Tree } from '@nx/devkit';
|
||||
|
||||
export function useFlatConfig(tree: Tree): boolean {
|
||||
return tree.exists('eslint.config.js');
|
||||
// todo: add support for eslint.config.mjs,
|
||||
export const eslintFlatConfigFilenames = [
|
||||
'eslint.config.js',
|
||||
'eslint.config.cjs',
|
||||
];
|
||||
|
||||
export function flatConfigEslintFilename(tree: Tree): string {
|
||||
for (const file of eslintFlatConfigFilenames) {
|
||||
if (tree.exists(file)) {
|
||||
return file;
|
||||
}
|
||||
}
|
||||
throw new Error('Could not find flat config file');
|
||||
}
|
||||
|
||||
export function useFlatConfig(tree: Tree): boolean {
|
||||
try {
|
||||
return !!flatConfigEslintFilename(tree);
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,12 @@
|
||||
"cli": "nx",
|
||||
"description": "Add task projectReportAll to build.gradle file",
|
||||
"factory": "./src/migrations/19-4-0/add-project-report-all"
|
||||
},
|
||||
"change-regex-production-test": {
|
||||
"version": "19.4.1-beta.0",
|
||||
"cli": "nx",
|
||||
"description": "This function changes !{projectRoot}/test/**/* in nx.json for production to !{projectRoot}/src/test/**/*",
|
||||
"factory": "./src/migrations/19-4-1/change-regex-test-production"
|
||||
}
|
||||
},
|
||||
"packageJsonUpdates": {}
|
||||
|
||||
@@ -81,16 +81,16 @@ describe('@nx/gradle:init', () => {
|
||||
});
|
||||
const nxJson = readNxJson(tree);
|
||||
expect(nxJson.namedInputs).toMatchInlineSnapshot(`
|
||||
{
|
||||
"default": [
|
||||
"{projectRoot}/**/*",
|
||||
],
|
||||
"production": [
|
||||
"default",
|
||||
"!{projectRoot}/test/**/*",
|
||||
],
|
||||
}
|
||||
`);
|
||||
{
|
||||
"default": [
|
||||
"{projectRoot}/**/*",
|
||||
],
|
||||
"production": [
|
||||
"default",
|
||||
"!{projectRoot}/src/test/**/*",
|
||||
],
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('should not overwrite existing namedInputs', async () => {
|
||||
@@ -102,7 +102,7 @@ describe('@nx/gradle:init', () => {
|
||||
'!{projectRoot}/cypress/**/*',
|
||||
'!{projectRoot}/**/*.cy.[jt]s?(x)',
|
||||
'!{projectRoot}/cypress.config.[jt]s',
|
||||
'!{projectRoot}/test/**/*',
|
||||
'!{projectRoot}/src/test/**/*',
|
||||
],
|
||||
},
|
||||
});
|
||||
@@ -122,7 +122,7 @@ describe('@nx/gradle:init', () => {
|
||||
"!{projectRoot}/cypress/**/*",
|
||||
"!{projectRoot}/**/*.cy.[jt]s?(x)",
|
||||
"!{projectRoot}/cypress.config.[jt]s",
|
||||
"!{projectRoot}/test/**/*",
|
||||
"!{projectRoot}/src/test/**/*",
|
||||
"default",
|
||||
],
|
||||
}
|
||||
|
||||
@@ -146,7 +146,7 @@ allprojects {
|
||||
}
|
||||
}
|
||||
|
||||
function updateNxJsonConfiguration(tree: Tree) {
|
||||
export function updateNxJsonConfiguration(tree: Tree) {
|
||||
const nxJson = readNxJson(tree);
|
||||
|
||||
if (!nxJson.namedInputs) {
|
||||
@@ -158,7 +158,7 @@ function updateNxJsonConfiguration(tree: Tree) {
|
||||
);
|
||||
const productionFileSet = nxJson.namedInputs.production ?? [];
|
||||
nxJson.namedInputs.production = Array.from(
|
||||
new Set([...productionFileSet, 'default', '!{projectRoot}/test/**/*'])
|
||||
new Set([...productionFileSet, 'default', '!{projectRoot}/src/test/**/*'])
|
||||
);
|
||||
updateNxJson(tree, nxJson);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
import { Tree, readNxJson } from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import update from './change-regex-test-production';
|
||||
|
||||
describe('change-regex-test-production', () => {
|
||||
let tree: Tree;
|
||||
|
||||
beforeAll(() => {
|
||||
tree = createTreeWithEmptyWorkspace();
|
||||
});
|
||||
|
||||
it('should not add to the namedInputs if it does not exist', async () => {
|
||||
tree.write(
|
||||
'nx.json',
|
||||
JSON.stringify({ namedInputs: {}, plugins: ['@nx/gradle'] })
|
||||
);
|
||||
update(tree);
|
||||
expect(readNxJson(tree)).toMatchInlineSnapshot(`
|
||||
{
|
||||
"namedInputs": {},
|
||||
"plugins": [
|
||||
"@nx/gradle",
|
||||
],
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('should not add to the namedInputs production if it is empty', async () => {
|
||||
tree.write(
|
||||
'nx.json',
|
||||
JSON.stringify({
|
||||
namedInputs: { production: [] },
|
||||
plugins: ['@nx/gradle'],
|
||||
})
|
||||
);
|
||||
update(tree);
|
||||
expect(readNxJson(tree)).toMatchInlineSnapshot(`
|
||||
{
|
||||
"namedInputs": {
|
||||
"production": [],
|
||||
},
|
||||
"plugins": [
|
||||
"@nx/gradle",
|
||||
],
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('should remove !{projectRoot}/test/**/* from the namedInputs production', async () => {
|
||||
tree.write(
|
||||
'nx.json',
|
||||
JSON.stringify({
|
||||
namedInputs: { production: ['!{projectRoot}/test/**/*'] },
|
||||
plugins: ['@nx/gradle'],
|
||||
})
|
||||
);
|
||||
update(tree);
|
||||
expect(readNxJson(tree)).toMatchInlineSnapshot(`
|
||||
{
|
||||
"namedInputs": {
|
||||
"production": [
|
||||
"!{projectRoot}/src/test/**/*",
|
||||
],
|
||||
},
|
||||
"plugins": [
|
||||
"@nx/gradle",
|
||||
],
|
||||
}
|
||||
`);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,19 @@
|
||||
import { Tree, readNxJson, updateNxJson } from '@nx/devkit';
|
||||
import { hasGradlePlugin } from '../../utils/has-gradle-plugin';
|
||||
|
||||
// This function changes !{projectRoot}/test/**/* in nx.json for production to !{projectRoot}/src/test/**/*
|
||||
export default function update(tree: Tree) {
|
||||
if (!hasGradlePlugin(tree)) {
|
||||
return;
|
||||
}
|
||||
const nxJson = readNxJson(tree);
|
||||
if (!nxJson) {
|
||||
return;
|
||||
}
|
||||
const production = nxJson.namedInputs?.production;
|
||||
if (production?.includes('!{projectRoot}/test/**/*')) {
|
||||
production[production.indexOf('!{projectRoot}/test/**/*')] =
|
||||
'!{projectRoot}/src/test/**/*';
|
||||
updateNxJson(tree, nxJson);
|
||||
}
|
||||
}
|
||||
@@ -439,14 +439,16 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
|
||||
|
||||
it('should add extended config files', async () => {
|
||||
await applyFilesToTempFsAndContext(tempFs, context, {
|
||||
'libs/my-lib/tsconfig.json': JSON.stringify({
|
||||
extends: '../../tsconfig.foo.json',
|
||||
include: ['src/**/*.ts'],
|
||||
'tsconfig.base.json': JSON.stringify({
|
||||
exclude: ['node_modules', 'tmp'],
|
||||
}),
|
||||
'tsconfig.foo.json': JSON.stringify({
|
||||
extends: './tsconfig.base.json',
|
||||
}),
|
||||
'tsconfig.base.json': '{}',
|
||||
'libs/my-lib/tsconfig.json': JSON.stringify({
|
||||
extends: '../../tsconfig.foo.json',
|
||||
include: ['src/**/*.ts'],
|
||||
}),
|
||||
'libs/my-lib/package.json': `{}`,
|
||||
});
|
||||
expect(await invokeCreateNodesOnMatchingFiles(context, {}))
|
||||
@@ -467,6 +469,8 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
|
||||
"{workspaceRoot}/tsconfig.base.json",
|
||||
"{projectRoot}/tsconfig.json",
|
||||
"{projectRoot}/src/**/*.ts",
|
||||
"!{workspaceRoot}/node_modules",
|
||||
"!{workspaceRoot}/tmp",
|
||||
"^production",
|
||||
{
|
||||
"externalDependencies": [
|
||||
@@ -1455,15 +1459,17 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
|
||||
|
||||
it('should add extended config files', async () => {
|
||||
await applyFilesToTempFsAndContext(tempFs, context, {
|
||||
'tsconfig.base.json': JSON.stringify({
|
||||
exclude: ['node_modules', 'tmp'],
|
||||
}),
|
||||
'tsconfig.foo.json': JSON.stringify({
|
||||
extends: './tsconfig.base.json',
|
||||
}),
|
||||
'libs/my-lib/tsconfig.json': '{}',
|
||||
'libs/my-lib/tsconfig.lib.json': JSON.stringify({
|
||||
extends: '../../tsconfig.foo.json',
|
||||
include: ['src/**/*.ts'],
|
||||
}),
|
||||
'tsconfig.foo.json': JSON.stringify({
|
||||
extends: './tsconfig.base.json',
|
||||
}),
|
||||
'tsconfig.base.json': '{}',
|
||||
'libs/my-lib/package.json': `{}`,
|
||||
});
|
||||
expect(
|
||||
@@ -1488,6 +1494,8 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
|
||||
"{workspaceRoot}/tsconfig.base.json",
|
||||
"{projectRoot}/tsconfig.lib.json",
|
||||
"{projectRoot}/src/**/*.ts",
|
||||
"!{workspaceRoot}/node_modules",
|
||||
"!{workspaceRoot}/tmp",
|
||||
"^production",
|
||||
{
|
||||
"externalDependencies": [
|
||||
|
||||
@@ -282,7 +282,11 @@ function getInputs(
|
||||
pathToInputOrOutput(p, workspaceRoot, projectRoot)
|
||||
),
|
||||
...Array.from(includePaths).map((p: string) =>
|
||||
joinPathFragments('{projectRoot}', p)
|
||||
pathToInputOrOutput(
|
||||
joinPathFragments(projectRoot, p),
|
||||
workspaceRoot,
|
||||
projectRoot
|
||||
)
|
||||
)
|
||||
);
|
||||
} else {
|
||||
@@ -294,7 +298,12 @@ function getInputs(
|
||||
if (excludePaths.size) {
|
||||
inputs.push(
|
||||
...Array.from(excludePaths).map(
|
||||
(p: string) => `!${joinPathFragments('{projectRoot}', p)}`
|
||||
(p: string) =>
|
||||
`!${pathToInputOrOutput(
|
||||
joinPathFragments(projectRoot, p),
|
||||
workspaceRoot,
|
||||
projectRoot
|
||||
)}`
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -27,12 +27,22 @@ function normalizeOptions(
|
||||
|
||||
function addDocker(tree: Tree, options: SetUpDockerOptions) {
|
||||
const projectConfig = readProjectConfiguration(tree, options.project);
|
||||
|
||||
const outputPath =
|
||||
projectConfig.targets[options.buildTarget]?.options['outputPath'];
|
||||
|
||||
if (!projectConfig) {
|
||||
throw new Error(`Cannot find project configuration for ${options.project}`);
|
||||
}
|
||||
|
||||
if (!outputPath && !options.outputPath) {
|
||||
throw new Error(
|
||||
`The output path for the project ${options.project} is not defined. Please provide it as an option to the generator.`
|
||||
);
|
||||
}
|
||||
generateFiles(tree, join(__dirname, './files'), projectConfig.root, {
|
||||
tmpl: '',
|
||||
buildLocation: options.outputPath,
|
||||
buildLocation: options.outputPath ?? outputPath,
|
||||
project: options.project,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ export async function resetHandler(args: ResetCommandOptions) {
|
||||
try {
|
||||
await cleanupNativeFileCache();
|
||||
} catch {
|
||||
errors.push('Failed to clean up the native file cache.');
|
||||
// ignore, deleting the native file cache is not critical and can fail if another process is locking the file
|
||||
}
|
||||
try {
|
||||
await cleanupWorkspaceData();
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
import { source } from '@angular-devkit/schematics';
|
||||
import { createCommandGraph } from './create-command-graph';
|
||||
import { CommandGraph } from './command-graph';
|
||||
|
||||
export { createCommandGraph } from './create-command-graph';
|
||||
|
||||
describe('createCommandGraph', () => {
|
||||
it('should create command graph with empty dependencies', () => {
|
||||
const projectGraph = {
|
||||
dependencies: {},
|
||||
nodes: {},
|
||||
};
|
||||
const projectNames = [];
|
||||
const nxArgs = {};
|
||||
const result = createCommandGraph(projectGraph, projectNames, nxArgs);
|
||||
expect(result).toEqual({ dependencies: {}, roots: [] });
|
||||
});
|
||||
|
||||
it('should create command graph with dependencies', () => {
|
||||
const projectGraph = {
|
||||
dependencies: {
|
||||
dep1: [{ target: 'dep2', type: 'static', source: 'dep1' }],
|
||||
dep2: [],
|
||||
},
|
||||
nodes: {
|
||||
dep1: {
|
||||
type: 'lib' as 'lib',
|
||||
name: 'dep1',
|
||||
data: {
|
||||
files: [],
|
||||
root: 'dep1',
|
||||
},
|
||||
},
|
||||
dep2: {
|
||||
type: 'lib' as 'lib',
|
||||
name: 'dep2',
|
||||
data: {
|
||||
files: [],
|
||||
root: 'dep2',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
const projectNames = ['dep1'];
|
||||
const nxArgs = {};
|
||||
const result = createCommandGraph(projectGraph, projectNames, nxArgs);
|
||||
expect(result).toEqual({
|
||||
dependencies: { dep1: ['dep2'], dep2: [] },
|
||||
roots: ['dep2'],
|
||||
});
|
||||
});
|
||||
|
||||
it('should create command graph with nested dependencies', () => {
|
||||
const projectGraph = {
|
||||
dependencies: {
|
||||
dep1: [{ target: 'dep2', type: 'static', source: 'dep1' }],
|
||||
dep2: [],
|
||||
dep3: [{ target: 'dep1', type: 'static', source: 'dep3' }],
|
||||
},
|
||||
nodes: {
|
||||
dep1: {
|
||||
type: 'lib' as 'lib',
|
||||
name: 'dep1',
|
||||
data: {
|
||||
files: [],
|
||||
root: 'dep1',
|
||||
},
|
||||
},
|
||||
dep2: {
|
||||
type: 'lib' as 'lib',
|
||||
name: 'dep2',
|
||||
data: {
|
||||
files: [],
|
||||
root: 'dep2',
|
||||
},
|
||||
},
|
||||
dep3: {
|
||||
type: 'lib' as 'lib',
|
||||
name: 'dep3',
|
||||
data: {
|
||||
files: [],
|
||||
root: 'dep3',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
const result = createCommandGraph(
|
||||
projectGraph,
|
||||
['dep1', 'dep2', 'dep3'],
|
||||
{}
|
||||
);
|
||||
expect(result).toEqual({
|
||||
dependencies: { dep1: ['dep2'], dep2: [], dep3: ['dep1'] },
|
||||
roots: ['dep2'],
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -4,6 +4,46 @@ import { NxArgs } from '../utils/command-line-utils';
|
||||
import { output } from '../utils/output';
|
||||
import { CommandGraph } from './command-graph';
|
||||
|
||||
/**
|
||||
* Make structure { lib: [dep], dep: [dep1], dep1: [] } from projectName lib and projectGraph
|
||||
* @param projectGraph
|
||||
* @param projectName
|
||||
* @param resolved reference to an object that will contain resolved dependencies
|
||||
* @returns
|
||||
*/
|
||||
const recursiveResolveDeps = (
|
||||
projectGraph: ProjectGraph,
|
||||
projectName: string,
|
||||
resolved: Record<string, string[]>
|
||||
) => {
|
||||
if (projectGraph.dependencies[projectName].length === 0) {
|
||||
// no deps - no resolve
|
||||
resolved[projectName] = [];
|
||||
return;
|
||||
}
|
||||
// if already resolved - just skip
|
||||
if (resolved[projectName]) {
|
||||
return resolved[projectName];
|
||||
}
|
||||
|
||||
// deps string list
|
||||
const projectDeps = [
|
||||
...new Set(
|
||||
projectGraph.dependencies[projectName]
|
||||
.map((projectDep) => projectDep.target)
|
||||
.filter((projectDep) => projectGraph.nodes[projectDep])
|
||||
).values(),
|
||||
];
|
||||
|
||||
// define
|
||||
resolved[projectName] = projectDeps;
|
||||
if (projectDeps.length > 0) {
|
||||
for (const dep of projectDeps) {
|
||||
recursiveResolveDeps(projectGraph, dep, resolved);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export function createCommandGraph(
|
||||
projectGraph: ProjectGraph,
|
||||
projectNames: string[],
|
||||
@@ -11,17 +51,7 @@ export function createCommandGraph(
|
||||
): CommandGraph {
|
||||
const dependencies: Record<string, string[]> = {};
|
||||
for (const projectName of projectNames) {
|
||||
if (projectGraph.dependencies[projectName].length >= 1) {
|
||||
dependencies[projectName] = [
|
||||
...new Set(
|
||||
projectGraph.dependencies[projectName]
|
||||
.map((projectDep) => projectDep.target)
|
||||
.filter((projectDep) => projectGraph.nodes[projectDep])
|
||||
).values(),
|
||||
];
|
||||
} else {
|
||||
dependencies[projectName] = [];
|
||||
}
|
||||
recursiveResolveDeps(projectGraph, projectName, dependencies);
|
||||
}
|
||||
const roots = Object.keys(dependencies).filter(
|
||||
(d) => dependencies[d].length === 0
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { dirname, join, sep } from 'path';
|
||||
import type { TsConfigOptions } from 'ts-node';
|
||||
import type { CompilerOptions } from 'typescript';
|
||||
import { NX_PREFIX, logger, stripIndent } from '../../../utils/logger';
|
||||
import { logger, NX_PREFIX, stripIndent } from '../../../utils/logger';
|
||||
|
||||
const swcNodeInstalled = packageIsInstalled('@swc-node/register');
|
||||
const tsNodeInstalled = packageIsInstalled('ts-node/register');
|
||||
@@ -120,17 +120,9 @@ export function getSwcTranspiler(
|
||||
return typeof cleanupFn === 'function' ? cleanupFn : () => {};
|
||||
}
|
||||
|
||||
const registered = new Set<string>();
|
||||
|
||||
export function getTsNodeTranspiler(
|
||||
compilerOptions: CompilerOptions
|
||||
): (...args: unknown[]) => unknown {
|
||||
// Just return if transpiler was already registered before.
|
||||
const registrationKey = JSON.stringify(compilerOptions);
|
||||
if (registered.has(registrationKey)) {
|
||||
return () => {};
|
||||
}
|
||||
|
||||
const { register } = require('ts-node') as typeof import('ts-node');
|
||||
// ts-node doesn't provide a cleanup method
|
||||
const service = register({
|
||||
@@ -139,7 +131,6 @@ export function getTsNodeTranspiler(
|
||||
// we already read and provide the compiler options, so prevent ts-node from reading them again
|
||||
skipProject: true,
|
||||
});
|
||||
registered.add(registrationKey);
|
||||
|
||||
const { transpiler, swc } = service.options;
|
||||
|
||||
@@ -227,6 +218,11 @@ function filterRecognizedTsConfigTsNodeOptions(jsonObject: any): {
|
||||
return { recognized: filteredTsConfigOptions, unrecognized };
|
||||
}
|
||||
|
||||
const registered = new Map<
|
||||
string,
|
||||
{ refCount: number; cleanup: () => (...args: unknown[]) => unknown }
|
||||
>();
|
||||
|
||||
export function getTranspiler(
|
||||
compilerOptions: CompilerOptions,
|
||||
tsConfigRaw?: unknown
|
||||
@@ -246,15 +242,38 @@ export function getTranspiler(
|
||||
compilerOptions.inlineSourceMap = true;
|
||||
compilerOptions.skipLibCheck = true;
|
||||
|
||||
if (swcNodeInstalled && !preferTsNode) {
|
||||
return () => getSwcTranspiler(compilerOptions);
|
||||
// Just return if transpiler was already registered before.
|
||||
const registrationKey = JSON.stringify(compilerOptions);
|
||||
const registrationEntry = registered.get(registrationKey);
|
||||
if (registered.has(registrationKey)) {
|
||||
registrationEntry.refCount++;
|
||||
return registrationEntry.cleanup;
|
||||
}
|
||||
|
||||
// We can fall back on ts-node if it's available
|
||||
if (tsNodeInstalled) {
|
||||
const tsNodeOptions =
|
||||
filterRecognizedTsConfigTsNodeOptions(tsConfigRaw).recognized;
|
||||
return () => getTsNodeTranspiler(compilerOptions);
|
||||
const _getTranspiler =
|
||||
swcNodeInstalled && !preferTsNode
|
||||
? getSwcTranspiler
|
||||
: tsNodeInstalled
|
||||
? // We can fall back on ts-node if it's available
|
||||
getTsNodeTranspiler
|
||||
: undefined;
|
||||
|
||||
if (_getTranspiler) {
|
||||
const transpilerCleanup = _getTranspiler(compilerOptions);
|
||||
const currRegistrationEntry = {
|
||||
refCount: 1,
|
||||
cleanup: () => {
|
||||
return () => {
|
||||
currRegistrationEntry.refCount--;
|
||||
if (currRegistrationEntry.refCount === 0) {
|
||||
registered.delete(registrationKey);
|
||||
transpilerCleanup();
|
||||
}
|
||||
};
|
||||
},
|
||||
};
|
||||
registered.set(registrationKey, currRegistrationEntry);
|
||||
return currRegistrationEntry.cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -106,7 +106,7 @@ export interface PluginCreateMetadataResult {
|
||||
}
|
||||
| {
|
||||
success: false;
|
||||
error: string;
|
||||
error: Error;
|
||||
tx: string;
|
||||
};
|
||||
}
|
||||
|
||||
@@ -325,7 +325,7 @@ async function startPluginWorker() {
|
||||
ipcPath,
|
||||
],
|
||||
{
|
||||
stdio: process.stdout.isTTY ? 'inherit' : 'ignore',
|
||||
stdio: 'inherit',
|
||||
env,
|
||||
detached: true,
|
||||
shell: false,
|
||||
|
||||
@@ -121,7 +121,11 @@ const server = createServer((socket) => {
|
||||
} catch (e) {
|
||||
return {
|
||||
type: 'createMetadataResult',
|
||||
payload: { success: false, error: e.stack, tx },
|
||||
payload: {
|
||||
success: false,
|
||||
error: createSerializableError(e),
|
||||
tx,
|
||||
},
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
@@ -393,9 +393,11 @@ export async function createProjectConfigurations(
|
||||
} else {
|
||||
errorBodyLines.push(` - ${e.message}`);
|
||||
}
|
||||
const innerStackTrace = ' ' + e.stack.split('\n').join('\n ');
|
||||
errorBodyLines.push(innerStackTrace);
|
||||
}
|
||||
|
||||
error.message = errorBodyLines.join('\n');
|
||||
error.stack = errorBodyLines.join('\n');
|
||||
|
||||
// This represents a single plugin erroring out with a hard error.
|
||||
errors.push(error);
|
||||
|
||||
@@ -7,18 +7,27 @@ import {
|
||||
import { Task, TaskGraph } from '../config/task-graph';
|
||||
import { TargetDefaults, TargetDependencies } from '../config/nx-json';
|
||||
import { TargetDependencyConfig } from '../devkit-exports';
|
||||
import { findMatchingProjects } from '../utils/find-matching-projects';
|
||||
import { output } from '../utils/output';
|
||||
|
||||
export class ProcessTasks {
|
||||
private readonly seen = new Set<string>();
|
||||
readonly tasks: { [id: string]: Task } = {};
|
||||
readonly dependencies: { [k: string]: string[] } = {};
|
||||
private readonly allTargetNames: string[];
|
||||
|
||||
constructor(
|
||||
private readonly extraTargetDependencies: TargetDependencies,
|
||||
private readonly projectGraph: ProjectGraph
|
||||
) {}
|
||||
) {
|
||||
const allTargetNames = new Set<string>();
|
||||
for (const projectName in projectGraph.nodes) {
|
||||
const project = projectGraph.nodes[projectName];
|
||||
for (const targetName in project.data.targets ?? {}) {
|
||||
allTargetNames.add(targetName);
|
||||
}
|
||||
}
|
||||
this.allTargetNames = Array.from(allTargetNames);
|
||||
}
|
||||
|
||||
processTasks(
|
||||
projectNames: string[],
|
||||
@@ -100,7 +109,8 @@ export class ProcessTasks {
|
||||
const dependencyConfigs = getDependencyConfigs(
|
||||
{ project: task.target.project, target: task.target.target },
|
||||
this.extraTargetDependencies,
|
||||
this.projectGraph
|
||||
this.projectGraph,
|
||||
this.allTargetNames
|
||||
);
|
||||
for (const dependencyConfig of dependencyConfigs) {
|
||||
const taskOverrides =
|
||||
@@ -108,40 +118,7 @@ export class ProcessTasks {
|
||||
? overrides
|
||||
: { __overrides_unparsed__: [] };
|
||||
if (dependencyConfig.projects) {
|
||||
/** LERNA SUPPORT START - Remove in v20 */
|
||||
// Lerna uses `dependencies` in `prepNxOptions`, so we need to maintain
|
||||
// support for it until lerna can be updated to use the syntax.
|
||||
//
|
||||
// This should have been removed in v17, but the updates to lerna had not
|
||||
// been made yet.
|
||||
//
|
||||
// TODO(@agentender): Remove this part in v20
|
||||
if (typeof dependencyConfig.projects === 'string') {
|
||||
if (dependencyConfig.projects === 'self') {
|
||||
this.processTasksForSingleProject(
|
||||
task,
|
||||
task.target.project,
|
||||
dependencyConfig,
|
||||
configuration,
|
||||
taskOverrides,
|
||||
overrides
|
||||
);
|
||||
continue;
|
||||
} else if (dependencyConfig.projects === 'dependencies') {
|
||||
this.processTasksForDependencies(
|
||||
projectUsedToDeriveDependencies,
|
||||
dependencyConfig,
|
||||
configuration,
|
||||
task,
|
||||
taskOverrides,
|
||||
overrides
|
||||
);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
/** LERNA SUPPORT END - Remove in v17 */
|
||||
|
||||
this.processTasksForMatchingProjects(
|
||||
this.processTasksForMultipleProjects(
|
||||
dependencyConfig,
|
||||
configuration,
|
||||
task,
|
||||
@@ -170,31 +147,22 @@ export class ProcessTasks {
|
||||
}
|
||||
}
|
||||
|
||||
private processTasksForMatchingProjects(
|
||||
private processTasksForMultipleProjects(
|
||||
dependencyConfig: TargetDependencyConfig,
|
||||
configuration: string,
|
||||
task: Task,
|
||||
taskOverrides: Object | { __overrides_unparsed__: any[] },
|
||||
overrides: Object
|
||||
) {
|
||||
const targetProjectSpecifiers =
|
||||
typeof dependencyConfig.projects === 'string'
|
||||
? [dependencyConfig.projects]
|
||||
: dependencyConfig.projects;
|
||||
const matchingProjects = findMatchingProjects(
|
||||
targetProjectSpecifiers,
|
||||
this.projectGraph.nodes
|
||||
);
|
||||
|
||||
if (matchingProjects.length === 0) {
|
||||
if (dependencyConfig.projects.length === 0) {
|
||||
output.warn({
|
||||
title: `\`dependsOn\` is misconfigured for ${task.target.project}:${task.target.target}`,
|
||||
bodyLines: [
|
||||
`Project patterns "${targetProjectSpecifiers}" does not match any projects.`,
|
||||
`Project patterns "${dependencyConfig.projects}" does not match any projects.`,
|
||||
],
|
||||
});
|
||||
}
|
||||
for (const projectName of matchingProjects) {
|
||||
for (const projectName of dependencyConfig.projects) {
|
||||
this.processTasksForSingleProject(
|
||||
task,
|
||||
projectName,
|
||||
|
||||
@@ -15,16 +15,16 @@ function _findCycle(
|
||||
return null;
|
||||
}
|
||||
|
||||
export function findCycle(taskGraph: {
|
||||
export function findCycle(graph: {
|
||||
dependencies: Record<string, string[]>;
|
||||
}): string[] | null {
|
||||
const visited = {};
|
||||
for (const t of Object.keys(taskGraph.dependencies)) {
|
||||
for (const t of Object.keys(graph.dependencies)) {
|
||||
visited[t] = false;
|
||||
}
|
||||
|
||||
for (const t of Object.keys(taskGraph.dependencies)) {
|
||||
const cycle = _findCycle(taskGraph, t, visited, [t]);
|
||||
for (const t of Object.keys(graph.dependencies)) {
|
||||
const cycle = _findCycle(graph, t, visited, [t]);
|
||||
if (cycle) return cycle;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import {
|
||||
expandDependencyConfigSyntaxSugar,
|
||||
getDependencyConfigs,
|
||||
getOutputsForTargetAndConfiguration,
|
||||
interpolate,
|
||||
transformLegacyOutputs,
|
||||
@@ -408,6 +409,7 @@ describe('utils', () => {
|
||||
const result = transformLegacyOutputs('myapp', e);
|
||||
expect(result).toEqual(['{workspaceRoot}/dist']);
|
||||
}
|
||||
expect.assertions(1);
|
||||
});
|
||||
|
||||
it('should prefix unix-absolute paths with {workspaceRoot}', () => {
|
||||
@@ -418,6 +420,7 @@ describe('utils', () => {
|
||||
const result = transformLegacyOutputs('myapp', e);
|
||||
expect(result).toEqual(['{workspaceRoot}/dist']);
|
||||
}
|
||||
expect.assertions(1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -429,6 +432,7 @@ describe('utils', () => {
|
||||
const result = transformLegacyOutputs('myapp', e);
|
||||
expect(result).toEqual(['{workspaceRoot}/dist']);
|
||||
}
|
||||
expect.assertions(1);
|
||||
});
|
||||
|
||||
it('should prefix paths within the project with {projectRoot}', () => {
|
||||
@@ -439,6 +443,7 @@ describe('utils', () => {
|
||||
const result = transformLegacyOutputs('myapp', e);
|
||||
expect(result).toEqual(['{projectRoot}/dist']);
|
||||
}
|
||||
expect.assertions(1);
|
||||
});
|
||||
|
||||
describe('expandDependencyConfigSyntaxSugar', () => {
|
||||
@@ -509,6 +514,111 @@ describe('utils', () => {
|
||||
target: 'target:with:colons',
|
||||
});
|
||||
});
|
||||
|
||||
it('supports wildcards in targets', () => {
|
||||
const result = getDependencyConfigs(
|
||||
{ project: 'project', target: 'build' },
|
||||
{},
|
||||
{
|
||||
dependencies: {},
|
||||
nodes: {
|
||||
project: {
|
||||
name: 'project',
|
||||
type: 'app',
|
||||
data: {
|
||||
root: 'libs/project',
|
||||
targets: {
|
||||
build: {
|
||||
dependsOn: ['build-*'],
|
||||
},
|
||||
'build-css': {},
|
||||
'build-js': {},
|
||||
'then-build-something-else': {},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
['build', 'build-css', 'build-js', 'then-build-something-else']
|
||||
);
|
||||
expect(result).toEqual([
|
||||
{
|
||||
target: 'build-css',
|
||||
projects: ['project'],
|
||||
},
|
||||
{
|
||||
target: 'build-js',
|
||||
projects: ['project'],
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('should support wildcards with dependencies', () => {
|
||||
const result = getDependencyConfigs(
|
||||
{ project: 'project', target: 'build' },
|
||||
{},
|
||||
{
|
||||
dependencies: {},
|
||||
nodes: {
|
||||
project: {
|
||||
name: 'project',
|
||||
type: 'app',
|
||||
data: {
|
||||
root: 'libs/project',
|
||||
targets: {
|
||||
build: {
|
||||
dependsOn: ['^build-*'],
|
||||
},
|
||||
'then-build-something-else': {},
|
||||
},
|
||||
},
|
||||
},
|
||||
dep1: {
|
||||
name: 'dep1',
|
||||
type: 'lib',
|
||||
data: {
|
||||
root: 'libs/dep1',
|
||||
targets: {
|
||||
'build-css': {},
|
||||
'build-js': {},
|
||||
},
|
||||
},
|
||||
},
|
||||
dep2: {
|
||||
name: 'dep2',
|
||||
type: 'lib',
|
||||
data: {
|
||||
root: 'libs/dep2',
|
||||
targets: {
|
||||
'build-python': {},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
[
|
||||
'build',
|
||||
'build-css',
|
||||
'build-js',
|
||||
'then-build-something-else',
|
||||
'build-python',
|
||||
]
|
||||
);
|
||||
expect(result).toEqual([
|
||||
{
|
||||
target: 'build-css',
|
||||
dependencies: true,
|
||||
},
|
||||
{
|
||||
target: 'build-js',
|
||||
dependencies: true,
|
||||
},
|
||||
{
|
||||
target: 'build-python',
|
||||
dependencies: true,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateOutputs', () => {
|
||||
|
||||
@@ -15,40 +15,81 @@ import { splitByColons } from '../utils/split-target';
|
||||
import { getExecutorInformation } from '../command-line/run/executor-utils';
|
||||
import { CustomHasher, ExecutorConfig } from '../config/misc-interfaces';
|
||||
import { readProjectsConfigurationFromProjectGraph } from '../project-graph/project-graph';
|
||||
import {
|
||||
GLOB_CHARACTERS,
|
||||
findMatchingProjects,
|
||||
} from '../utils/find-matching-projects';
|
||||
import { minimatch } from 'minimatch';
|
||||
|
||||
export type NormalizedTargetDependencyConfig = TargetDependencyConfig & {
|
||||
projects: string[];
|
||||
};
|
||||
|
||||
export function getDependencyConfigs(
|
||||
{ project, target }: { project: string; target: string },
|
||||
extraTargetDependencies: Record<string, (TargetDependencyConfig | string)[]>,
|
||||
projectGraph: ProjectGraph
|
||||
): TargetDependencyConfig[] | undefined {
|
||||
projectGraph: ProjectGraph,
|
||||
allTargetNames: string[]
|
||||
): NormalizedTargetDependencyConfig[] | undefined {
|
||||
const dependencyConfigs = (
|
||||
projectGraph.nodes[project].data?.targets[target]?.dependsOn ??
|
||||
// This is passed into `run-command` from programmatic invocations
|
||||
extraTargetDependencies[target] ??
|
||||
[]
|
||||
).map((config) =>
|
||||
typeof config === 'string'
|
||||
? expandDependencyConfigSyntaxSugar(config, projectGraph)
|
||||
: config
|
||||
).flatMap((config) =>
|
||||
normalizeDependencyConfigDefinition(
|
||||
config,
|
||||
project,
|
||||
projectGraph,
|
||||
allTargetNames
|
||||
)
|
||||
);
|
||||
for (const dependencyConfig of dependencyConfigs) {
|
||||
if (dependencyConfig.projects && dependencyConfig.dependencies) {
|
||||
output.error({
|
||||
title: `dependsOn is improperly configured for ${project}:${target}`,
|
||||
bodyLines: [
|
||||
`dependsOn.projects and dependsOn.dependencies cannot be used together.`,
|
||||
],
|
||||
});
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
return dependencyConfigs;
|
||||
}
|
||||
|
||||
export function normalizeDependencyConfigDefinition(
|
||||
definition: string | TargetDependencyConfig,
|
||||
currentProject: string,
|
||||
graph: ProjectGraph,
|
||||
allTargetNames: string[]
|
||||
): NormalizedTargetDependencyConfig[] {
|
||||
return expandWildcardTargetConfiguration(
|
||||
normalizeDependencyConfigProjects(
|
||||
expandDependencyConfigSyntaxSugar(definition, graph),
|
||||
currentProject,
|
||||
graph
|
||||
),
|
||||
allTargetNames
|
||||
);
|
||||
}
|
||||
|
||||
export function normalizeDependencyConfigProjects(
|
||||
dependencyConfig: TargetDependencyConfig,
|
||||
currentProject: string,
|
||||
graph: ProjectGraph
|
||||
): NormalizedTargetDependencyConfig {
|
||||
const noStringConfig =
|
||||
normalizeTargetDependencyWithStringProjects(dependencyConfig);
|
||||
|
||||
if (noStringConfig.projects) {
|
||||
dependencyConfig.projects = findMatchingProjects(
|
||||
noStringConfig.projects,
|
||||
graph.nodes
|
||||
);
|
||||
} else if (!noStringConfig.dependencies) {
|
||||
dependencyConfig.projects = [currentProject];
|
||||
}
|
||||
return dependencyConfig as NormalizedTargetDependencyConfig;
|
||||
}
|
||||
|
||||
export function expandDependencyConfigSyntaxSugar(
|
||||
dependencyConfigString: string,
|
||||
dependencyConfigString: string | TargetDependencyConfig,
|
||||
graph: ProjectGraph
|
||||
): TargetDependencyConfig {
|
||||
if (typeof dependencyConfigString !== 'string') {
|
||||
return dependencyConfigString;
|
||||
}
|
||||
|
||||
const [dependencies, targetString] = dependencyConfigString.startsWith('^')
|
||||
? [true, dependencyConfigString.substring(1)]
|
||||
: [false, dependencyConfigString];
|
||||
@@ -62,22 +103,69 @@ export function expandDependencyConfigSyntaxSugar(
|
||||
};
|
||||
}
|
||||
|
||||
const { projects, target } = readProjectAndTargetFromTargetString(
|
||||
targetString,
|
||||
graph.nodes
|
||||
);
|
||||
|
||||
return projects ? { projects, target } : { target };
|
||||
}
|
||||
|
||||
// Weakmap let's the cache get cleared by garbage collector if allTargetNames is no longer used
|
||||
const patternResultCache = new WeakMap<
|
||||
string[],
|
||||
// Map< Pattern, Dependency Configs >
|
||||
Map<string, NormalizedTargetDependencyConfig[]>
|
||||
>();
|
||||
|
||||
export function expandWildcardTargetConfiguration(
|
||||
dependencyConfig: NormalizedTargetDependencyConfig,
|
||||
allTargetNames: string[]
|
||||
): NormalizedTargetDependencyConfig[] {
|
||||
if (!GLOB_CHARACTERS.some((char) => dependencyConfig.target.includes(char))) {
|
||||
return [dependencyConfig];
|
||||
}
|
||||
let cache = patternResultCache.get(allTargetNames);
|
||||
if (!cache) {
|
||||
cache = new Map();
|
||||
patternResultCache.set(allTargetNames, cache);
|
||||
}
|
||||
const cachedResult = cache.get(dependencyConfig.target);
|
||||
if (cachedResult) {
|
||||
return cachedResult;
|
||||
}
|
||||
|
||||
const matcher = minimatch.filter(dependencyConfig.target);
|
||||
|
||||
const matchingTargets = allTargetNames.filter((t) => matcher(t));
|
||||
|
||||
const result = matchingTargets.map((t) => ({
|
||||
...dependencyConfig,
|
||||
target: t,
|
||||
}));
|
||||
cache.set(dependencyConfig.target, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
export function readProjectAndTargetFromTargetString(
|
||||
targetString: string,
|
||||
projects: Record<string, ProjectGraphProjectNode>
|
||||
): { projects?: string[]; target: string } {
|
||||
// Support for both `project:target` and `target:with:colons` syntax
|
||||
const [maybeProject, ...segments] = splitByColons(targetString);
|
||||
|
||||
// if no additional segments are provided, then the string references
|
||||
// a target of the same project
|
||||
if (!segments.length) {
|
||||
// if no additional segments are provided, then the string references
|
||||
// a target of the same project
|
||||
return { target: maybeProject };
|
||||
}
|
||||
|
||||
return {
|
||||
} else if (maybeProject in projects) {
|
||||
// Only the first segment could be a project. If it is, the rest is a target.
|
||||
// If its not, then the whole targetString was a target with colons in its name.
|
||||
target: maybeProject in graph.nodes ? segments.join(':') : targetString,
|
||||
return { projects: [maybeProject], target: segments.join(':') };
|
||||
} else {
|
||||
// If the first segment is a project, then we have a specific project. Otherwise, we don't.
|
||||
projects: maybeProject in graph.nodes ? [maybeProject] : undefined,
|
||||
};
|
||||
return { target: targetString };
|
||||
}
|
||||
}
|
||||
|
||||
export function getOutputs(
|
||||
@@ -92,6 +180,34 @@ export function getOutputs(
|
||||
);
|
||||
}
|
||||
|
||||
export function normalizeTargetDependencyWithStringProjects(
|
||||
dependencyConfig: TargetDependencyConfig
|
||||
): Omit<TargetDependencyConfig, 'projects'> & { projects: string[] } {
|
||||
if (typeof dependencyConfig.projects === 'string') {
|
||||
/** LERNA SUPPORT START - Remove in v20 */
|
||||
// Lerna uses `dependencies` in `prepNxOptions`, so we need to maintain
|
||||
// support for it until lerna can be updated to use the syntax.
|
||||
//
|
||||
// This should have been removed in v17, but the updates to lerna had not
|
||||
// been made yet.
|
||||
//
|
||||
// TODO(@agentender): Remove this part in v20
|
||||
if (dependencyConfig.projects === 'self') {
|
||||
delete dependencyConfig.projects;
|
||||
} else if (dependencyConfig.projects === 'dependencies') {
|
||||
dependencyConfig.dependencies = true;
|
||||
delete dependencyConfig.projects;
|
||||
return;
|
||||
/** LERNA SUPPORT END - Remove in v20 */
|
||||
} else {
|
||||
dependencyConfig.projects = [dependencyConfig.projects];
|
||||
}
|
||||
}
|
||||
return dependencyConfig as Omit<TargetDependencyConfig, 'projects'> & {
|
||||
projects: string[];
|
||||
};
|
||||
}
|
||||
|
||||
class InvalidOutputsError extends Error {
|
||||
constructor(public outputs: string[], public invalidOutputs: Set<string>) {
|
||||
super(InvalidOutputsError.createMessage(invalidOutputs));
|
||||
@@ -201,7 +317,7 @@ export function getOutputsForTargetAndConfiguration(
|
||||
const targetConfiguration = node.data.targets[target];
|
||||
|
||||
const options = {
|
||||
...targetConfiguration.options,
|
||||
...targetConfiguration?.options,
|
||||
...targetConfiguration?.configurations?.[configuration],
|
||||
...overrides,
|
||||
};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user