Compare commits

...

34 Commits

Author SHA1 Message Date
FrozenPandaz 68db3e032c chore(react): fix failing unit test 2024-12-10 17:34:03 -05:00
Jason Jean 506f206d2d fix(repo): cleanup old plugins promise (#29295)
<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
<!-- This is the behavior we have today -->

When plugins in `nx.json` are changed, the daemon can no longer
communicate with plugin workers and create the graph. It will just hang
waiting for messages to go through.

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

Proper cleanup is done when plugins in `nx.json` are changed and the
daemon continues responding properly with project graphs.

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

Fixes #

(cherry picked from commit a2670639db)
2024-12-10 16:37:06 -05:00
James Henry 8158edd9f3 chore(repo): update reference to upload-artifact from v3 to v4 (#29296)
(cherry picked from commit f415595508)
2024-12-10 16:37:05 -05:00
Colum Ferry e1d787f662 fix(core): ensure @nx/module-federation is listed in package group (#29292)
## Current Behavior
`@nx/module-federation` is not listed in the `nx` package's
`packageGroup`.
This reports it incorrectly in `nx report` and can impact `nx migrate`.

## Expected Behavior
`@nx/module-federation` should be listed in the `packageGroup`

(cherry picked from commit be2eab926b)
2024-12-10 16:37:03 -05:00
Tine Kondo 59a1345832 feat(testing): allow custom address for local registry (#29050)
… output<!-- Please make sure you have read the submission guidelines
before posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
<!-- This is the behavior we have today -->
The `startLocalRegistry()` always assumed that the local registry was
started on `localhost` , which is not necessary the case, if for example
user has set the `listenAddress` option in the underlying `verdaccio
executor` or via the `verdaccio config
file`https://github.com/nrwl/nx/blob/128778e7d1a8d7f2b078b2401d47270c0c15a2ce/packages/js/src/executors/verdaccio/verdaccio.impl.ts#L130

As a result the `startLocalregistry()`, will never return as, it is
waiting forever to detect the local registry URL in the process output:
https://github.com/nrwl/nx/blob/128778e7d1a8d7f2b078b2401d47270c0c15a2ce/packages/js/src/plugins/jest/start-local-registry.ts#L40

This customization of `verdaccio` listen address is something required,
namely when running within a **devcontainer**. In that case, the address
must be set to `0.0.0.0`, so that the registry can be accessed from
`http://localhost:4873` from host machine.

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

Allow customizing the `listenAddress` when starting the local registry,
via an additional option that can be provided when calling the method.
It default value will be `localhost`.

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

Fixes https://github.com/nrwl/nx/issues/28238

(cherry picked from commit cfb67cf124)
2024-12-10 16:37:02 -05:00
Leosvel Pérez Espinosa 7226c13580 fix(angular): normalize prerender and appShell options of the application executor correctly (#29281)
<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
<!-- This is the behavior we have today -->

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

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

Fixes #29276

(cherry picked from commit 7fca6e236a)
2024-12-10 16:37:01 -05:00
Leosvel Pérez Espinosa ae07748701 fix(angular): handle removed angular-eslint rules in root eslint config files and update package (#29262)
<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
<!-- This is the behavior we have today -->

The migration to remove Angular ESLint rules that were removed in v19
does not handle these rules in the root eslint config files.

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

The migration to remove Angular ESLint rules that were removed in v19
should handle these rules in the root eslint config files.

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

Fixes #

(cherry picked from commit df77fde81f)
2024-12-10 16:37:00 -05:00
Jack Hsu 8f521c82de fix(react): add files entry for publishable libraries (#29277)
<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
<!-- This is the behavior we have today -->

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

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

Fixes #

(cherry picked from commit 7e388243e0)
2024-12-10 16:36:58 -05:00
James Henry c305dceca2 chore(repo): use extra-large resource class for memory intensive nx-dev build (#29285)
(cherry picked from commit d2564c6292)
2024-12-10 16:36:57 -05:00
James Henry 0381681e4b docs(release): cover version reference updates in greater detail (#29259)
(cherry picked from commit 0af50a9f9d)
2024-12-10 16:36:56 -05:00
Leosvel Pérez Espinosa 8f58cbcde9 fix(vite)!: generate config with esm by default (#29270)
BREAKING CHANGE

When generating projects that use Vite, the Vite configuration will be
set to use the ESM format only. Previously, the configuration was set to
produce both ESM and CJS, but the dual format was not correctly
configured in the libraries' `package.json` files, nor was it producing
the correct declaration files.

<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
<!-- This is the behavior we have today -->

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

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

Fixes #

(cherry picked from commit 6684fc0688)
2024-12-10 16:36:56 -05:00
Ben McCann 6f50e2f0e3 fix(js): switch from fast-glob to tinyglobby (#29141)
(cherry picked from commit e3f8c813d6)
2024-12-10 16:36:11 -05:00
master96 9f28983fa0 fix(react-native): typescript lib schema (#27955)
<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
Currently when react native generators are used. They won't exclude
`.test` extension files.

## Expected Behavior
React native generators should exclude `.test` files in tsconfig lib

## Related Issue(s)

Fixes #

Co-authored-by: vivek ajage <vajage@signifyhealth.com>
(cherry picked from commit 2d135f952b)
2024-12-10 16:36:09 -05:00
Maciej Stosio 58d822e659 fix(react-native): default template fails when envs are not set (#28931)
## Current Behavior
When envs are not set metro fails in `.babelrc.js` on
`process.env.NX_TASK_TARGET_TARGET.includes('storybook')`

## Expected Behavior
When envs are not set don't fail on the if and just precede.

(cherry picked from commit aaa9cced76)
2024-12-10 16:36:08 -05:00
Juri Strumpflohner bb58839805 fix(nx-dev): update Nx Cloud proj created (#29272)
(cherry picked from commit ecba861be3)
2024-12-10 16:36:07 -05:00
James Henry 4050dfc0f7 fix(release): make commits separator in git log command more unique (#29261)
(cherry picked from commit 2c07bf0f73)
2024-12-10 16:36:05 -05:00
Steven Nance b9f1bbcdc1 docs(core): remove nx-cloud agent count (#29271)
(cherry picked from commit 5114f97912)
2024-12-10 16:36:04 -05:00
Nicholas Cunningham e56f231795 fix(core): Update bundlers to not typecheck if using new TS solution setup (#29227)
If we are using the new TS setup we should opt out of doing type
checking during build since we already have a typecheck target and it
may lead to doing type checking twice.

(cherry picked from commit 89ab8874b0)
2024-12-10 16:35:58 -05:00
Isaac Mann d084241cd8 docs(core): remove nx-cloud agent count (#29265)
Remove references to `NX_CLOUD_DISTRIBUTED_EXECUTION_AGENT_COUNT`

(cherry picked from commit 04151ca5dc)
2024-12-10 16:35:57 -05:00
Isaac Mann 6ad1af3044 docs(core): remove quoted terms in content (#29266)
Remove quoted terms in the form \`"term"\` from the docs

(cherry picked from commit fdc09fbf68)
2024-12-10 16:35:55 -05:00
Jason Jean 223a14344b fix(core): hashing fixes (#29247)
<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
<!-- This is the behavior we have today -->

Hashing files via the workspace context sometimes resulted in a falsely
identical hashes when files are renamed.. but remain in the same order
alphabetically. This should be somewhat rare and most likely other
changes would accompany the rename which would change the overall hash.
This hashing was not used in task hashing though so it would not affect
that.

Debugging hashing inconsistencies is currently difficult as there is an
overload of logging:

<details>

```sh
~/p/nx-test (master↓24|✔) [1]$ NX_NATIVE_LOGGING='nx::native::tasks::hashers::hash_workspace_files=trace' NX_DAEMON=false nx lint nx
TRACE nx::native::tasks::hashers::hash_workspace_files: Workspace file set: {workspaceRoot}/nx.json
TRACE nx::native::tasks::hashers::hash_workspace_files: Workspace file set: {workspaceRoot}/.gitignore
TRACE nx::native::tasks::hashers::hash_workspace_files: Workspace file set: {workspaceRoot}/.nxignore
TRACE nx::native::tasks::hashers::hash_workspace_files: Workspace file set: {workspaceRoot}/nx.json
TRACE nx::native::tasks::hashers::hash_workspace_files: Workspace file set: {workspaceRoot}/.gitignore
TRACE nx::native::tasks::hashers::hash_workspace_files: Workspace file set: {workspaceRoot}/.nxignore
TRACE nx::native::tasks::hashers::hash_workspace_files: Workspace file set: {workspaceRoot}/babel.config.json
TRACE nx::native::tasks::hashers::hash_workspace_files: Workspace file set: {workspaceRoot}/.nx/workflows/agents.yaml
TRACE nx::native::tasks::hashers::hash_workspace_files: Workspace file set: {workspaceRoot}/.circleci/config.yml
TRACE nx::native::tasks::hashers::hash_workspace_files: ".circleci/config.yml" was found with glob ["babel.config.json", ".nx/workflows/agents.yaml", ".circleci/config.yml"]
TRACE nx::native::tasks::hashers::hash_workspace_files: "babel.config.json" was found with glob ["babel.config.json", ".nx/workflows/agents.yaml", ".circleci/config.yml"]
TRACE nx::native::tasks::hashers::hash_workspace_files: ".gitignore" was found with glob ["nx.json", ".gitignore", ".nxignore"]
TRACE nx::native::tasks::hashers::hash_workspace_files: ".nxignore" was found with glob ["nx.json", ".gitignore", ".nxignore"]
TRACE nx::native::tasks::hashers::hash_workspace_files: ".gitignore" was found with glob ["nx.json", ".gitignore", ".nxignore"]
TRACE nx::native::tasks::hashers::hash_workspace_files: ".nxignore" was found with glob ["nx.json", ".gitignore", ".nxignore"]
TRACE nx::native::tasks::hashers::hash_workspace_files: "nx.json" was found with glob ["nx.json", ".gitignore", ".nxignore"]
TRACE nx::native::tasks::hashers::hash_workspace_files: "nx.json" was found with glob ["nx.json", ".gitignore", ".nxignore"]
TRACE nx::native::tasks::hashers::hash_workspace_files: Workspace file set: {workspaceRoot}/babel.config.json
TRACE nx::native::tasks::hashers::hash_workspace_files: Workspace file set: {workspaceRoot}/.nx/workflows/agents.yaml
TRACE nx::native::tasks::hashers::hash_workspace_files: Workspace file set: {workspaceRoot}/.circleci/config.yml
TRACE nx::native::tasks::hashers::hash_workspace_files: Workspace file set: {workspaceRoot}/.eslintrc.json
TRACE nx::native::tasks::hashers::hash_workspace_files: Workspace file set: {workspaceRoot}/tools/eslint-rules/**/*
TRACE nx::native::tasks::hashers::hash_workspace_files: Workspace file set: {workspaceRoot}/babel.config.json
TRACE nx::native::tasks::hashers::hash_workspace_files: Workspace file set: {workspaceRoot}/.nx/workflows/agents.yaml
TRACE nx::native::tasks::hashers::hash_workspace_files: Workspace file set: {workspaceRoot}/.circleci/config.yml
TRACE nx::native::tasks::hashers::hash_workspace_files: Workspace file set: {workspaceRoot}/nx.json
TRACE nx::native::tasks::hashers::hash_workspace_files: Workspace file set: {workspaceRoot}/.gitignore
TRACE nx::native::tasks::hashers::hash_workspace_files: Workspace file set: {workspaceRoot}/.nxignore
TRACE nx::native::tasks::hashers::hash_workspace_files: ".circleci/config.yml" was found with glob ["babel.config.json", ".nx/workflows/agents.yaml", ".circleci/config.yml", ".eslintrc.json", "tools/eslint-rules/**/*"]
TRACE nx::native::tasks::hashers::hash_workspace_files: ".eslintrc.json" was found with glob ["babel.config.json", ".nx/workflows/agents.yaml", ".circleci/config.yml", ".eslintrc.json", "tools/eslint-rules/**/*"]
TRACE nx::native::tasks::hashers::hash_workspace_files: "babel.config.json" was found with glob ["babel.config.json", ".nx/workflows/agents.yaml", ".circleci/config.yml", ".eslintrc.json", "tools/eslint-rules/**/*"]
TRACE nx::native::tasks::hashers::hash_workspace_files: "tools/eslint-rules/index.ts" was found with glob ["babel.config.json", ".nx/workflows/agents.yaml", ".circleci/config.yml", ".eslintrc.json", "tools/eslint-rules/**/*"]
TRACE nx::native::tasks::hashers::hash_workspace_files: "tools/eslint-rules/jest.config.ts" was found with glob ["babel.config.json", ".nx/workflows/agents.yaml", ".circleci/config.yml", ".eslintrc.json", "tools/eslint-rules/**/*"]
TRACE nx::native::tasks::hashers::hash_workspace_files: "tools/eslint-rules/project.json" was found with glob ["babel.config.json", ".nx/workflows/agents.yaml", ".circleci/config.yml", ".eslintrc.json", "tools/eslint-rules/**/*"]
TRACE nx::native::tasks::hashers::hash_workspace_files: "tools/eslint-rules/raw-file-parser.js" was found with glob ["babel.config.json", ".nx/workflows/agents.yaml", ".circleci/config.yml", ".eslintrc.json", "tools/eslint-rules/**/*"]
TRACE nx::native::tasks::hashers::hash_workspace_files: "tools/eslint-rules/rules/ensure-pnpm-lock-version.ts" was found with glob ["babel.config.json", ".nx/workflows/agents.yaml", ".circleci/config.yml", ".eslintrc.json", "tools/eslint-rules/**/*"]
TRACE nx::native::tasks::hashers::hash_workspace_files: "tools/eslint-rules/rules/valid-command-object.spec.ts" was found with glob ["babel.config.json", ".nx/workflows/agents.yaml", ".circleci/config.yml", ".eslintrc.json", "tools/eslint-rules/**/*"]
TRACE nx::native::tasks::hashers::hash_workspace_files: "tools/eslint-rules/rules/valid-command-object.ts" was found with glob ["babel.config.json", ".nx/workflows/agents.yaml", ".circleci/config.yml", ".eslintrc.json", "tools/eslint-rules/**/*"]
TRACE nx::native::tasks::hashers::hash_workspace_files: "tools/eslint-rules/rules/valid-schema-description.spec.ts" was found with glob ["babel.config.json", ".nx/workflows/agents.yaml", ".circleci/config.yml", ".eslintrc.json", "tools/eslint-rules/**/*"]
TRACE nx::native::tasks::hashers::hash_workspace_files: "tools/eslint-rules/rules/valid-schema-description.ts" was found with glob ["babel.config.json", ".nx/workflows/agents.yaml", ".circleci/config.yml", ".eslintrc.json", "tools/eslint-rules/**/*"]
TRACE nx::native::tasks::hashers::hash_workspace_files: "tools/eslint-rules/tsconfig.json" was found with glob ["babel.config.json", ".nx/workflows/agents.yaml", ".circleci/config.yml", ".eslintrc.json", "tools/eslint-rules/**/*"]
TRACE nx::native::tasks::hashers::hash_workspace_files: "tools/eslint-rules/tsconfig.lint.json" was found with glob ["babel.config.json", ".nx/workflows/agents.yaml", ".circleci/config.yml", ".eslintrc.json", "tools/eslint-rules/**/*"]
TRACE nx::native::tasks::hashers::hash_workspace_files: "tools/eslint-rules/tsconfig.spec.json" was found with glob ["babel.config.json", ".nx/workflows/agents.yaml", ".circleci/config.yml", ".eslintrc.json", "tools/eslint-rules/**/*"]

```
</details>

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

Hashing files via the workspace context no longer produces falsely
identical hashes when files are named.

Debugging hashing inconsistencies is better now. It produces the hash
and the file it is adding to the workspace file hashset in the order
which it is added. Different filesets are still handled in parallel with
one another so the logging is best filtered through `grep` for a
particular fileset:
<details>

```sh
~/p/nx (hash-logging|✔) $ NX_NATIVE_LOGGING='nx::native::tasks::hashers::hash_workspace_files=debug' NX_DAEMON=false nx lint nx | grep "nx.json"
DEBUG nx::native::tasks::hashers::hash_workspace_files: Hashing workspace fileset{cache_key="nx.json,.gitignore,.nxignore"}: Adding "5069375034190792089" (".gitignore") to hash
DEBUG nx::native::tasks::hashers::hash_workspace_files: Hashing workspace fileset{cache_key="nx.json,.gitignore,.nxignore"}: Adding "10752854809791558346" (".nxignore") to hash
DEBUG nx::native::tasks::hashers::hash_workspace_files: Hashing workspace fileset{cache_key="nx.json,.gitignore,.nxignore"}: Adding "9876981562233255395" ("nx.json") to hash
DEBUG nx::native::tasks::hashers::hash_workspace_files: Hashing workspace fileset{cache_key="nx.json,.gitignore,.nxignore"}: Hash Value: "12458994942476116599"
```

</details>

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

Fixes #

(cherry picked from commit 192bfe72d9)
2024-12-10 16:35:52 -05:00
Leosvel Pérez Espinosa 2d366847bb feat(misc): handle artifact generators' path options including file extensions (#29111)
<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
<!-- This is the behavior we have today -->

Artifact generators don't handle consistently receiving a file extension
in the `path` option.

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

Artifact generators should handle receiving a file extension in the
`path` option. If the file extension is passed, the file path will be
treated as "complete" and used fully as provided. If the `path` provided
doesn't contain a file extension, the default extension will be appended
to it (or the one provided in a related option, e.g. `--language`,
`--js`, etc) together with the suffix for generators that use it.

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

Fixes #

(cherry picked from commit 28c53f942b)
2024-12-10 16:35:49 -05:00
Fábio Correia ce53dd2a98 fix(release): use prepatch version for pre-release dependent package updates (#29123)
(cherry picked from commit 3474d7c607)
2024-12-10 16:35:48 -05:00
Jacob Ley 3850e95870 fix(core): defer loading package manager until necessary (#29248)
(cherry picked from commit 5d61191999)
2024-12-10 16:35:48 -05:00
Jason Jean 41d0df6587 fix(core): fix process being prevented from exiting (#29240)
<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
<!-- This is the behavior we have today -->

The db cleanup handler does not exit the process and thus does not
handle `SIGINT` properly.

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

Nx processes handle SIGINT properly.

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

Fixes #

(cherry picked from commit de8b18980c)
2024-12-06 16:30:02 -05:00
Benjamin Cabanes c071f847ab docs(nx-dev): simplify Hero component by removing ShaderGradient (#29241)
This commit removes the ShaderGradientElement and its associated
functionality from the Hero component, reducing complexity and
dependencies. The return type of the Hero component is also updated for
consistency. These changes lead to a more streamlined codebase with
potentially fewer runtime considerations and increase client
compatibility.

(cherry picked from commit d8d74da9fb)
2024-12-06 16:30:01 -05:00
Jason Jean d1f296513e chore(repo): fix docs release (#29233)
<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
<!-- This is the behavior we have today -->

Docs fail to release with the new typescript version

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

Docs can be released and release flow can be tested via `--dryRun`

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

Fixes #

(cherry picked from commit d23350fbc0)
2024-12-06 16:30:00 -05:00
Benjamin Cabanes 20dbd45eac docs(nx-dev): update contact form sales (#29238)
Corrected a typo in a job title and removed redundant GDPR information
across multiple components. Enhanced the sales team section with new
imports and testimonials to improve visual appeal and clarity.

---------

Co-authored-by: Isaac Mann <isaacplmann@users.noreply.github.com>
(cherry picked from commit c3b4bf6c43)
2024-12-06 16:29:59 -05:00
Mike Hartington ab56d97690 docs(misc): update console blog post release date (#29239)
<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
<!-- This is the behavior we have today -->

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

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

Fixes #

(cherry picked from commit 9769c2986c)
2024-12-06 16:29:58 -05:00
Colum Ferry 6234d98fd2 fix(storybook): ensure 'storybook' dep is installed for non-crystal workspaces (#29235)
## Current Behavior
The `storybook` package is required to be installed to correctly find
paths correctly.
It acts almost like a shim.
It was not being installed for non-crystal workspaces

## Expected Behavior
Ensure `storybook` is installed

(cherry picked from commit 288193b761)
2024-12-06 16:29:56 -05:00
Emily Xiong 046686fbb7 fix(core): add workspaces path if package path is not included (#28824)
<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
<!-- This is the behavior we have today -->
- when import to a path where is not in the workspaces, it currently
just shows a warning. however, it will cause an error like "module not
found" because there are packages not installed.

## Expected Behavior
<!-- This is the behavior we should expect with the changes in this PR
-->
- automatically add to the workspaces
![Screenshot 2024-11-08 at 12 58
42 AM](https://github.com/user-attachments/assets/d36dd093-a0ce-45c3-a783-97244741971f)

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

Fixes #

(cherry picked from commit 902eef5320)
2024-12-06 16:29:55 -05:00
Colum Ferry 6ed78d29bd fix(storybook): only install react deps for storybook 7 #29213 (#29231)
## Current Behavior
Storybook 7 requires `react` and `react-dom` to be installed as they are
peer deps of the storybook package.
Storybook 8 does not require these deps to be installed as they are now
bundled correctly into storybook

## Expected Behavior
When user's version of storybook < 8, install `react` and `react-dom`,
otherwise, do not.

## Related Issue(s)

Fixes #29213

(cherry picked from commit 51cf34fc81)
2024-12-06 16:29:54 -05:00
Zachary DeRose 62632a92f7 docs(misc): Nx Console CIPE Panel blogpost (#29064)
<!-- Please make sure you have read the submission guidelines before
posting an PR -->
<!--
https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr
-->

<!-- Please make sure that your commit message follows our format -->
<!-- Example: `fix(nx): must begin with lowercase` -->

<!-- If this is a particularly complex change or feature addition, you
can request a dedicated Nx release for this pull request branch. Mention
someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they
will confirm if the PR warrants its own release for testing purposes,
and generate it for you if appropriate. -->

## Current Behavior
<!-- This is the behavior we have today -->

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

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

Fixes #

(cherry picked from commit e3df75b1e5)
2024-12-06 16:29:53 -05:00
Benjamin Cabanes 383bc3081c docs(nx-dev): revamp the Nx Enterprise page on nx.dev (#29209)
This update introduces a revamp to the Nx Enterprise page. The code
changes involved the addition of new image files, amending several
components for improved UI/UX. This refactor also includes an alteration
to the Call-to-Action section and an introduction of a new Carousel
component for better navigation. These adjustments aim to provide an
improved user experience and enhanced readability.

---------

Co-authored-by: Isaac Mann <isaacplmann@users.noreply.github.com>
(cherry picked from commit cc1441170a)
2024-12-06 16:29:52 -05:00
303 changed files with 5423 additions and 3425 deletions
+1 -1
View File
@@ -54,7 +54,7 @@ jobs:
env:
GITHUB_TOKEN: ${{ github.token }}
- uses: actions/upload-artifact@v3
- uses: actions/upload-artifact@v4
with:
name: cached-issue-data
path: ./scripts/issues-scraper/cached/data.json
+1 -1
View File
@@ -419,7 +419,7 @@ jobs:
- name: (Stable Release Only) Trigger Docs Release
# Publish docs only on a full release
if: ${{ !github.event.release.prerelease && github.event_name == 'release' }}
run: npx ts-node ./scripts/release-docs.ts
run: npx ts-node -P ./scripts/tsconfig.scripts.json ./scripts/release-docs.ts
- name: (PR Release Only) Create comment for successful PR release
if: success() && github.event.inputs.pr
+2 -2
View File
@@ -56,8 +56,8 @@ launch-templates:
- name: Install zip and unzip
script: sudo apt-get -yqq install zip unzip
linux-large:
resource-class: 'docker_linux_amd64/large'
linux-extra-large:
resource-class: 'docker_linux_amd64/extra_large'
image: 'ubuntu22.04-node20.11-v10'
env:
GIT_AUTHOR_EMAIL: test@test.com
+2 -2
View File
@@ -1,10 +1,10 @@
distribute-on:
default: auto linux-medium, 1 linux-large
default: auto linux-medium, 1 linux-extra-large
assignment-rules:
- project: nx-dev
target: build-base
runs-on:
- linux-large
- linux-extra-large
- target: test
runs-on:
- linux-medium
@@ -0,0 +1,41 @@
---
title: Nx Cloud Pipelines Come To Nx Console
slug: nx-cloud-pipelines-come-to-nx-console
authors: [Zack DeRose]
tags: [nx, nx-cloud, nx-console, enterprise]
cover_image: '/blog/images/2024-11-25/thumbnail.png'
---
## Your CI Pipelines, Now At-A-Glance In Your IDE
We're in the process of adding a new integration between [Nx Cloud](/nx-cloud), our CI product, and [Nx Console](/getting-started/editor-setup) our official plugin for the VSCode and JetBrains IDEs!
Now, once you've [connected your Nx Console to Nx Cloud](https://blog.nrwl.io/nx-console-meets-nx-cloud-d45dc099dc5d), you will have access to a new panel in the console that shows all of your recent CI Pipelines, including those in progress now.
![Nx Cloud Pipeline Panel](/blog/images/2024-11-25/pipeline-panel.png)
This way, you can keep an eye on the status of the pipeline of your latest PR, and always just 1 click away from seeing detailed your results on Nx Cloud.
## Notifications On What You Care About Most
In addition to a panel where you can see all of your recent pipelines, you can also receive pop-up notifications when a pipeline completes
![Pipeline Result Notification](/blog/images/2024-11-25/result-notification.png)
Head to the Nx Console settings to adjust controls on these notifications. This will allow you to adjust notifications to only show for failed runs if you prefer, or to turn off the notifications altogether.
![Notification Settings](/blog/images/2024-11-25/notification-settings.png)
## Coming Soon!
We're in the process of landing this on Nx Console for VSCode now - and we're already dog-fooding it on the `nx` and `nx-console` repos. It will be landing in the next few week for all Nx Cloud projects, with the feature landing on Nx Console for JetBrains IDEs to follow soon!
For more on Nx Cloud, check out the Solutions tab above! Nx Cloud is free to get started, so you can [create your free account today](https://cloud.nx.app)!
[![Learn More about Nx Cloud](/blog/images/2024-11-25/learn-more-about-nx-cloud.gif)](/nx-cloud)
And if you're not using Nx Console yet, you can get started now with Nx Console available for VSCode and JetBrains IDEs:
{% install-nx-console /%}
![Nx Console screenshot](/shared/images/nx-console/nx-console-screenshot.webp)
Binary file not shown.

After

Width:  |  Height:  |  Size: 13 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 395 KiB

+24 -24
View File
@@ -1268,6 +1268,14 @@
"children": [],
"disableCollapsible": false
},
{
"name": "Updating Version References in Manifest Files (e.g. package.json)",
"path": "/recipes/nx-release/updating-version-references",
"id": "updating-version-references",
"isExternal": false,
"children": [],
"disableCollapsible": false
},
{
"name": "Automatically Version with Conventional Commits",
"path": "/recipes/nx-release/automatically-version-with-conventional-commits",
@@ -1340,14 +1348,6 @@
"children": [],
"disableCollapsible": false
},
{
"name": "Publish a Custom Dist Directory",
"path": "/recipes/nx-release/publish-custom-dist-directory",
"id": "publish-custom-dist-directory",
"isExternal": false,
"children": [],
"disableCollapsible": false
},
{
"name": "Build Before Versioning",
"path": "/recipes/nx-release/build-before-versioning",
@@ -2417,6 +2417,14 @@
"children": [],
"disableCollapsible": false
},
{
"name": "Updating Version References in Manifest Files (e.g. package.json)",
"path": "/recipes/nx-release/updating-version-references",
"id": "updating-version-references",
"isExternal": false,
"children": [],
"disableCollapsible": false
},
{
"name": "Automatically Version with Conventional Commits",
"path": "/recipes/nx-release/automatically-version-with-conventional-commits",
@@ -2489,14 +2497,6 @@
"children": [],
"disableCollapsible": false
},
{
"name": "Publish a Custom Dist Directory",
"path": "/recipes/nx-release/publish-custom-dist-directory",
"id": "publish-custom-dist-directory",
"isExternal": false,
"children": [],
"disableCollapsible": false
},
{
"name": "Build Before Versioning",
"path": "/recipes/nx-release/build-before-versioning",
@@ -2532,6 +2532,14 @@
"children": [],
"disableCollapsible": false
},
{
"name": "Updating Version References in Manifest Files (e.g. package.json)",
"path": "/recipes/nx-release/updating-version-references",
"id": "updating-version-references",
"isExternal": false,
"children": [],
"disableCollapsible": false
},
{
"name": "Automatically Version with Conventional Commits",
"path": "/recipes/nx-release/automatically-version-with-conventional-commits",
@@ -2604,14 +2612,6 @@
"children": [],
"disableCollapsible": false
},
{
"name": "Publish a Custom Dist Directory",
"path": "/recipes/nx-release/publish-custom-dist-directory",
"id": "publish-custom-dist-directory",
"isExternal": false,
"children": [],
"disableCollapsible": false
},
{
"name": "Build Before Versioning",
"path": "/recipes/nx-release/build-before-versioning",
+33 -33
View File
@@ -1737,6 +1737,17 @@
"path": "/recipes/nx-release/release-projects-independently",
"tags": ["nx-release"]
},
{
"id": "updating-version-references",
"name": "Updating Version References in Manifest Files (e.g. package.json)",
"description": "",
"mediaImage": "",
"file": "shared/recipes/nx-release/updating-version-references",
"itemList": [],
"isExternal": false,
"path": "/recipes/nx-release/updating-version-references",
"tags": ["nx-release"]
},
{
"id": "automatically-version-with-conventional-commits",
"name": "Automatically Version with Conventional Commits",
@@ -1836,17 +1847,6 @@
"path": "/recipes/nx-release/configure-changelog-format",
"tags": ["nx-release"]
},
{
"id": "publish-custom-dist-directory",
"name": "Publish a Custom Dist Directory",
"description": "",
"mediaImage": "",
"file": "shared/recipes/nx-release/publish-custom-dist-directory",
"itemList": [],
"isExternal": false,
"path": "/recipes/nx-release/publish-custom-dist-directory",
"tags": ["nx-release"]
},
{
"id": "build-before-versioning",
"name": "Build Before Versioning",
@@ -3311,6 +3311,17 @@
"path": "/recipes/nx-release/release-projects-independently",
"tags": ["nx-release"]
},
{
"id": "updating-version-references",
"name": "Updating Version References in Manifest Files (e.g. package.json)",
"description": "",
"mediaImage": "",
"file": "shared/recipes/nx-release/updating-version-references",
"itemList": [],
"isExternal": false,
"path": "/recipes/nx-release/updating-version-references",
"tags": ["nx-release"]
},
{
"id": "automatically-version-with-conventional-commits",
"name": "Automatically Version with Conventional Commits",
@@ -3410,17 +3421,6 @@
"path": "/recipes/nx-release/configure-changelog-format",
"tags": ["nx-release"]
},
{
"id": "publish-custom-dist-directory",
"name": "Publish a Custom Dist Directory",
"description": "",
"mediaImage": "",
"file": "shared/recipes/nx-release/publish-custom-dist-directory",
"itemList": [],
"isExternal": false,
"path": "/recipes/nx-release/publish-custom-dist-directory",
"tags": ["nx-release"]
},
{
"id": "build-before-versioning",
"name": "Build Before Versioning",
@@ -3470,6 +3470,17 @@
"path": "/recipes/nx-release/release-projects-independently",
"tags": ["nx-release"]
},
"/recipes/nx-release/updating-version-references": {
"id": "updating-version-references",
"name": "Updating Version References in Manifest Files (e.g. package.json)",
"description": "",
"mediaImage": "",
"file": "shared/recipes/nx-release/updating-version-references",
"itemList": [],
"isExternal": false,
"path": "/recipes/nx-release/updating-version-references",
"tags": ["nx-release"]
},
"/recipes/nx-release/automatically-version-with-conventional-commits": {
"id": "automatically-version-with-conventional-commits",
"name": "Automatically Version with Conventional Commits",
@@ -3569,17 +3580,6 @@
"path": "/recipes/nx-release/configure-changelog-format",
"tags": ["nx-release"]
},
"/recipes/nx-release/publish-custom-dist-directory": {
"id": "publish-custom-dist-directory",
"name": "Publish a Custom Dist Directory",
"description": "",
"mediaImage": "",
"file": "shared/recipes/nx-release/publish-custom-dist-directory",
"itemList": [],
"isExternal": false,
"path": "/recipes/nx-release/publish-custom-dist-directory",
"tags": ["nx-release"]
},
"/recipes/nx-release/build-before-versioning": {
"id": "build-before-versioning",
"name": "Build Before Versioning",
+7 -7
View File
@@ -497,6 +497,13 @@
"name": "Release Projects Independently",
"path": "/recipes/nx-release/release-projects-independently"
},
{
"description": "",
"file": "shared/recipes/nx-release/updating-version-references",
"id": "updating-version-references",
"name": "Updating Version References in Manifest Files (e.g. package.json)",
"path": "/recipes/nx-release/updating-version-references"
},
{
"description": "",
"file": "shared/recipes/nx-release/automatically-version-with-conventional-commits",
@@ -560,13 +567,6 @@
"name": "Configure Changelog Format",
"path": "/recipes/nx-release/configure-changelog-format"
},
{
"description": "",
"file": "shared/recipes/nx-release/publish-custom-dist-directory",
"id": "publish-custom-dist-directory",
"name": "Publish a Custom Dist Directory",
"path": "/recipes/nx-release/publish-custom-dist-directory"
},
{
"description": "",
"file": "shared/recipes/nx-release/build-before-versioning",
@@ -12,7 +12,7 @@
"properties": {
"path": {
"type": "string",
"description": "The file path to the component without the file extension and suffix. Relative to the current working directory.",
"description": "The file path to the component. Relative to the current working directory.",
"$default": { "$source": "argv", "index": 0 },
"x-prompt": "What is the component file path?"
},
@@ -117,7 +117,7 @@
}
},
"required": ["path"],
"examplesFile": "## Examples\n\n{% tabs %}\n{% tab label=\"Simple Component\" %}\n\nGenerate a component named `MyComponent` at `apps/my-app/src/lib/my-component/my-component.component.ts`:\n\n```bash\nnx g @nx/angular:component apps/my-app/src/lib/my-component/my-component\n```\n\n{% /tab %}\n\n{% tab label=\"With Different Symbol Name\" %}\n\nGenerate a component named `CustomComponent` at `apps/my-app/src/lib/my-component/my-component.component.ts`:\n\n```bash\nnx g @nx/angular:component apps/my-app/src/lib/my-component/my-component --name=custom\n```\n\n{% /tab %}\n\n{% tab label=\"Single File Component\" %}\n\nCreate a component named `my-component` with inline styles and inline template:\n\n```bash\nnx g @nx/angular:component apps/my-app/src/lib/my-component/my-component --inlineStyle --inlineTemplate\n```\n\n{% /tab %}\n\n{% tab label=\"Component with OnPush Change Detection Strategy\" %}\n\nCreate a component named `my-component` with OnPush Change Detection Strategy:\n\n```bash\nnx g @nx/angular:component apps/my-app/src/lib/my-component/my-component --changeDetection=OnPush\n```\n\n{% /tab %}\n",
"examplesFile": "## Examples\n\n{% tabs %}\n{% tab label=\"Simple Component\" %}\n\nGenerate a component named `MyComponent` at `apps/my-app/src/lib/my-component/my-component.component.ts`:\n\n```bash\nnx g @nx/angular:component apps/my-app/src/lib/my-component/my-component.ts\n```\n\n{% /tab %}\n\n{% tab label=\"Without Providing the File Extension\" %}\n\nGenerate a component named `MyComponent` at `apps/my-app/src/lib/my-component/my-component.component.ts`:\n\n```bash\nnx g @nx/angular:component apps/my-app/src/lib/my-component/my-component\n```\n\n{% /tab %}\n\n{% tab label=\"With Different Symbol Name\" %}\n\nGenerate a component named `CustomComponent` at `apps/my-app/src/lib/my-component/my-component.component.ts`:\n\n```bash\nnx g @nx/angular:component apps/my-app/src/lib/my-component/my-component --name=custom\n```\n\n{% /tab %}\n\n{% tab label=\"Single File Component\" %}\n\nCreate a component named `my-component` with inline styles and inline template:\n\n```bash\nnx g @nx/angular:component apps/my-app/src/lib/my-component/my-component --inlineStyle --inlineTemplate\n```\n\n{% /tab %}\n\n{% tab label=\"Component with OnPush Change Detection Strategy\" %}\n\nCreate a component named `my-component` with OnPush Change Detection Strategy:\n\n```bash\nnx g @nx/angular:component apps/my-app/src/lib/my-component/my-component --changeDetection=OnPush\n```\n\n{% /tab %}\n",
"presets": []
},
"aliases": ["c"],
@@ -12,6 +12,10 @@
"examples": [
{
"description": "Generate a directive with the exported symbol matching the file name. It results in the directive `FooDirective` at `mylib/src/lib/foo.directive.ts`",
"command": "nx g @nx/angular:directive mylib/src/lib/foo.directive.ts"
},
{
"description": "Generate a directive without providing the file extension. It results in the directive `FooDirective` at `mylib/src/lib/foo.directive.ts`",
"command": "nx g @nx/angular:directive mylib/src/lib/foo"
},
{
@@ -22,7 +26,7 @@
"properties": {
"path": {
"type": "string",
"description": "The file path to the directive without the file extension and suffix. Relative to the current working directory.",
"description": "The file path to the directive. Relative to the current working directory.",
"$default": { "$source": "argv", "index": 0 },
"x-prompt": "What is the directive file path?"
},
@@ -12,6 +12,10 @@
"examples": [
{
"description": "Generate a pipe with the exported symbol matching the file name. It results in the pipe `FooPipe` at `mylib/src/lib/foo.pipe.ts`",
"command": "nx g @nx/angular:pipe mylib/src/lib/foo.pipe.ts"
},
{
"description": "Generate a pipe without providing the file extension. It results in the pipe `FooPipe` at `mylib/src/lib/foo.pipe.ts`",
"command": "nx g @nx/angular:pipe mylib/src/lib/foo"
},
{
@@ -22,7 +26,7 @@
"properties": {
"path": {
"type": "string",
"description": "The file path to the pipe without the file extension and suffix. Relative to the current working directory.",
"description": "The file path to the pipe. Relative to the current working directory.",
"$default": { "$source": "argv", "index": 0 },
"x-prompt": "What is the pipe file path?"
},
@@ -10,6 +10,10 @@
"examples": [
{
"description": "Generate a directive with the exported symbol matching the file name. It results in the directive `FooDirective` at `mylib/src/lib/foo.directive.ts`",
"command": "nx g @nx/angular:scam-directive mylib/src/lib/foo.directive.ts"
},
{
"description": "Generate a directive without providing the file extension. It results in the directive `FooDirective` at `mylib/src/lib/foo.directive.ts`",
"command": "nx g @nx/angular:scam-directive mylib/src/lib/foo"
},
{
@@ -22,7 +26,7 @@
"properties": {
"path": {
"type": "string",
"description": "The file path to the SCAM directive without the file extension and suffix. Relative to the current working directory.",
"description": "The file path to the SCAM directive. Relative to the current working directory.",
"$default": { "$source": "argv", "index": 0 },
"x-prompt": "What is the SCAM directive file path?"
},
@@ -10,6 +10,10 @@
"examples": [
{
"description": "Generate a pipe with the exported symbol matching the file name. It results in the pipe `FooPipe` at `mylib/src/lib/foo.pipe.ts`",
"command": "nx g @nx/angular:scam-pipe mylib/src/lib/foo.pipe.ts"
},
{
"description": "Generate a pipe without providing the file extension. It results in the pipe `FooPipe` at `mylib/src/lib/foo.pipe.ts`",
"command": "nx g @nx/angular:scam-pipe mylib/src/lib/foo"
},
{
@@ -22,7 +26,7 @@
"properties": {
"path": {
"type": "string",
"description": "The file path to the SCAM pipe without the file extension and suffix. Relative to the current working directory.",
"description": "The file path to the SCAM pipe. Relative to the current working directory.",
"$default": { "$source": "argv", "index": 0 },
"x-prompt": "What is the SCAM pipe file path?"
},
@@ -10,6 +10,10 @@
"examples": [
{
"description": "Generate a component with the exported symbol matching the file name. It results in the component `FooComponent` at `mylib/src/lib/foo.component.ts`",
"command": "nx g @nx/angular:scam mylib/src/lib/foo.component.ts"
},
{
"description": "Generate a component without providing the file extension. It results in the component `FooComponent` at `mylib/src/lib/foo.component.ts`",
"command": "nx g @nx/angular:scam mylib/src/lib/foo"
},
{
@@ -22,7 +26,7 @@
"properties": {
"path": {
"type": "string",
"description": "The file path to the SCAM without the file extension and suffix. Relative to the current working directory.",
"description": "The file path to the SCAM. Relative to the current working directory.",
"$default": { "$source": "argv", "index": 0 },
"x-prompt": "What is the SCAM file path?"
},
@@ -10,11 +10,15 @@
"examples": [
{
"description": "Generate a component with the exported symbol matching the file name. It results in the component `Foo` at `mylib/src/foo.tsx`",
"command": "nx g @nx/expo:component mylib/src/foo"
"command": "nx g @nx/expo:component mylib/src/foo.tsx"
},
{
"description": "Generate a component with the exported symbol different from the file name. It results in the component `Custom` at `mylib/src/foo.tsx`",
"command": "nx g @nx/expo:component mylib/src/foo --name=custom"
"command": "nx g @nx/expo:component mylib/src/foo.tsx --name=custom"
},
{
"description": "Generate a component without the providing the file extension. It results in the component `Foo` at `mylib/src/foo.tsx`",
"command": "nx g @nx/expo:component mylib/src/foo"
},
{
"description": "Generate a class component at `mylib/src/foo.tsx`",
@@ -24,7 +28,7 @@
"properties": {
"path": {
"type": "string",
"description": "The file path to the component without the file extension. Relative to the current working directory.",
"description": "The file path to the component. Relative to the current working directory.",
"$default": { "$source": "argv", "index": 0 },
"x-prompt": "What is the component file path?"
},
@@ -35,7 +39,7 @@
"js": {
"type": "boolean",
"description": "Generate JavaScript files rather than TypeScript files.",
"default": false
"x-deprecated": "Provide the full file path including the file extension in the `path` option. This option will be removed in Nx v21."
},
"skipFormat": {
"description": "Skip formatting files.",
@@ -11,12 +11,16 @@
"examples": [
{
"description": "Generate the class `Foo` at `myapp/src/app/foo.ts`",
"command": "nx g @nx/nest:class myapp/src/app/foo.ts"
},
{
"description": "Generate the class without providing the file extension. It results in the class `Foo` at `myapp/src/app/foo.ts`",
"command": "nx g @nx/nest:class myapp/src/app/foo"
}
],
"properties": {
"path": {
"description": "The file path to the class without the file extension. Relative to the current working directory.",
"description": "The file path to the class. Relative to the current working directory.",
"type": "string",
"$default": { "$source": "argv", "index": 0 },
"x-prompt": "What is the class file path?"
@@ -11,12 +11,16 @@
"examples": [
{
"description": "Generate the controller `FooController` at `myapp/src/app/foo.controller.ts`",
"command": "nx g @nx/nest:controller myapp/src/app/foo.controller.ts"
},
{
"description": "Generate the controller without providing the file extension. It results in the controller `FooController` at `myapp/src/app/foo.controller.ts`",
"command": "nx g @nx/nest:controller myapp/src/app/foo"
}
],
"properties": {
"path": {
"description": "The file path to the controller without the file extension and suffix. Relative to the current working directory.",
"description": "The file path to the controller. Relative to the current working directory.",
"type": "string",
"$default": { "$source": "argv", "index": 0 },
"x-prompt": "What is the controller file path?"
@@ -11,12 +11,16 @@
"examples": [
{
"description": "Generate the decorator `Foo` at `myapp/src/app/foo.decorator.ts`",
"command": "nx g @nx/nest:decorator myapp/src/app/foo.decorator.ts"
},
{
"description": "Generate the decorator without providing the file extension. It results in the decorator `Foo` at `myapp/src/app/foo.decorator.ts`",
"command": "nx g @nx/nest:decorator myapp/src/app/foo"
}
],
"properties": {
"path": {
"description": "The file path to the decorator without the file extension and suffix. Relative to the current working directory.",
"description": "The file path to the decorator. Relative to the current working directory.",
"type": "string",
"$default": { "$source": "argv", "index": 0 },
"x-prompt": "What is the decorator file path?"
@@ -11,12 +11,16 @@
"examples": [
{
"description": "Generate the filter `FooFilter` at `myapp/src/app/foo.filter.ts`",
"command": "nx g @nx/nest:filter myapp/src/app/foo.filter.ts"
},
{
"description": "Generate the filter without providing the file extension. It results in the filter `FooFilter` at `myapp/src/app/foo.filter.ts`",
"command": "nx g @nx/nest:filter myapp/src/app/foo"
}
],
"properties": {
"path": {
"description": "The file path to the filter without the file extension and suffix. Relative to the current working directory.",
"description": "The file path to the filter. Relative to the current working directory.",
"type": "string",
"$default": { "$source": "argv", "index": 0 },
"x-prompt": "What is the filter file path?"
@@ -11,12 +11,16 @@
"examples": [
{
"description": "Generate the gateway `FooGateway` at `myapp/src/app/foo.gateway.ts`",
"command": "nx g @nx/nest:gateway myapp/src/app/foo.gateway.ts"
},
{
"description": "Generate the gateway without providing the file extension. It results in the gateway `FooGateway` at `myapp/src/app/foo.gateway.ts`",
"command": "nx g @nx/nest:gateway myapp/src/app/foo"
}
],
"properties": {
"path": {
"description": "The file path to the gateway without the file extension and suffix. Relative to the current working directory.",
"description": "The file path to the gateway. Relative to the current working directory.",
"type": "string",
"$default": { "$source": "argv", "index": 0 },
"x-prompt": "What is the gateway file path?"
@@ -11,12 +11,16 @@
"examples": [
{
"description": "Generate the guard `FooGuard` at `myapp/src/app/foo.guard.ts`",
"command": "nx g @nx/nest:guard myapp/src/app/foo.guard.ts"
},
{
"description": "Generate the guard without providing the file extension. It results in the guard `FooGuard` at `myapp/src/app/foo.guard.ts`",
"command": "nx g @nx/nest:guard myapp/src/app/foo"
}
],
"properties": {
"path": {
"description": "The file path to the guard without the file extension and suffix. Relative to the current working directory.",
"description": "The file path to the guard. Relative to the current working directory.",
"type": "string",
"$default": { "$source": "argv", "index": 0 },
"x-prompt": "What is the guard file path?"
@@ -11,12 +11,16 @@
"examples": [
{
"description": "Generate the interceptor `FooInterceptor` at `myapp/src/app/foo.interceptor.ts`",
"command": "nx g @nx/nest:interceptor myapp/src/app/foo.interceptor.ts"
},
{
"description": "Generate the interceptor without providing the file extension. It results in the interceptor `FooInterceptor` at `myapp/src/app/foo.interceptor.ts`",
"command": "nx g @nx/nest:interceptor myapp/src/app/foo"
}
],
"properties": {
"path": {
"description": "The file path to the interceptor without the file extension and suffix. Relative to the current working directory.",
"description": "The file path to the interceptor. Relative to the current working directory.",
"type": "string",
"$default": { "$source": "argv", "index": 0 },
"x-prompt": "What is the interceptor file path?"
@@ -11,12 +11,16 @@
"examples": [
{
"description": "Generate the interface `Foo` at `myapp/src/app/foo.interface.ts`",
"command": "nx g @nx/nest:interface myapp/src/app/foo.interface.ts"
},
{
"description": "Generate the interface without providing the file extension. It results in the interface `Foo` at `myapp/src/app/foo.interface.ts`",
"command": "nx g @nx/nest:interface myapp/src/app/foo"
}
],
"properties": {
"path": {
"description": "The file path to the interface without the file extension and suffix. Relative to the current working directory.",
"description": "The file path to the interface. Relative to the current working directory.",
"type": "string",
"$default": { "$source": "argv", "index": 0 },
"x-prompt": "What is the interface file path?"
@@ -11,12 +11,16 @@
"examples": [
{
"description": "Generate the middleware `FooMiddleware` at `myapp/src/app/foo.middleware.ts`",
"command": "nx g @nx/nest:middleware myapp/src/app/foo.middleware.ts"
},
{
"description": "Generate the middleware without providing the file extension. It results in the middleware `FooMiddleware` at `myapp/src/app/foo.middleware.ts`",
"command": "nx g @nx/nest:middleware myapp/src/app/foo"
}
],
"properties": {
"path": {
"description": "The file path to the middleware without the file extension and suffix. Relative to the current working directory.",
"description": "The file path to the middleware. Relative to the current working directory.",
"type": "string",
"$default": { "$source": "argv", "index": 0 },
"x-prompt": "What is the middleware file path?"
@@ -11,12 +11,16 @@
"examples": [
{
"description": "Generate the module `FooModule` at `myapp/src/app/foo.module.ts`",
"command": "nx g @nx/nest:module myapp/src/app/foo.module.ts"
},
{
"description": "Generate the module without providing the file extension. It results in the module `FooModule` at `myapp/src/app/foo.module.ts`",
"command": "nx g @nx/nest:module myapp/src/app/foo"
}
],
"properties": {
"path": {
"description": "The file path to the module without the file extension and suffix. Relative to the current working directory.",
"description": "The file path to the module. Relative to the current working directory.",
"type": "string",
"$default": { "$source": "argv", "index": 0 },
"x-prompt": "What is the module file path?"
@@ -11,12 +11,16 @@
"examples": [
{
"description": "Generate the pipe `FooPipe` at `myapp/src/app/foo.pipe.ts`",
"command": "nx g @nx/nest:pipe myapp/src/app/foo.pipe.ts"
},
{
"description": "Generate the pipe without providing the file extension. It results in the pipe `FooPipe` at `myapp/src/app/foo.pipe.ts`",
"command": "nx g @nx/nest:pipe myapp/src/app/foo"
}
],
"properties": {
"path": {
"description": "The file path to the pipe without the file extension and suffix. Relative to the current working directory.",
"description": "The file path to the pipe. Relative to the current working directory.",
"type": "string",
"$default": { "$source": "argv", "index": 0 },
"x-prompt": "What is the pipe file path?"
@@ -11,12 +11,16 @@
"examples": [
{
"description": "Generate the provider `Foo` at `myapp/src/app/foo.ts`",
"command": "nx g @nx/nest:provider myapp/src/app/foo.ts"
},
{
"description": "Generate the provider without providing the file extension. It results in the provider `Foo` at `myapp/src/app/foo.ts`",
"command": "nx g @nx/nest:provider myapp/src/app/foo"
}
],
"properties": {
"path": {
"description": "The file path to the provider without the file extension and suffix. Relative to the current working directory.",
"description": "The file path to the provider. Relative to the current working directory.",
"type": "string",
"$default": { "$source": "argv", "index": 0 },
"x-prompt": "What is the provider file path?"
@@ -11,12 +11,16 @@
"examples": [
{
"description": "Generate the resolver `FooResolver` at `myapp/src/app/foo.resolver.ts`",
"command": "nx g @nx/nest:resolver myapp/src/app/foo.resolver.ts"
},
{
"description": "Generate the resolver without providing the file extension. It results in the resolver `FooResolver` at `myapp/src/app/foo.resolver.ts`",
"command": "nx g @nx/nest:resolver myapp/src/app/foo"
}
],
"properties": {
"path": {
"description": "The file path to the resolver without the file extension and suffix. Relative to the current working directory.",
"description": "The file path to the resolver. Relative to the current working directory.",
"type": "string",
"$default": { "$source": "argv", "index": 0 },
"x-prompt": "What is the resolver file path?"
@@ -17,7 +17,7 @@
"properties": {
"path": {
"type": "string",
"description": "The file path to the resource without the file extension and suffix. Relative to the current working directory.",
"description": "The file path to the resource. Relative to the current working directory.",
"$default": { "$source": "argv", "index": 0 },
"x-prompt": "What is the resource file path?"
},
@@ -33,11 +33,6 @@
"enum": ["jest", "none"],
"default": "jest"
},
"language": {
"description": "Nest class language.",
"type": "string",
"enum": ["js", "ts"]
},
"type": {
"type": "string",
"description": "The transport layer.",
@@ -11,12 +11,16 @@
"examples": [
{
"description": "Generate the service `FooService` at `myapp/src/app/foo.service.ts`",
"command": "nx g @nx/nest:service myapp/src/app/foo.service.ts"
},
{
"description": "Generate the service without providing the file extension. It results in the service `FooService` at `myapp/src/app/foo.service.ts`",
"command": "nx g @nx/nest:service myapp/src/app/foo"
}
],
"properties": {
"path": {
"description": "The file path to the service without the file extension and suffix. Relative to the current working directory.",
"description": "The file path to the service. Relative to the current working directory.",
"type": "string",
"$default": { "$source": "argv", "index": 0 },
"x-prompt": "What is the service file path?"
@@ -11,7 +11,7 @@
"properties": {
"path": {
"type": "string",
"description": "The file path to the component without the file extension. Relative to the current working directory.",
"description": "The file path to the component. Relative to the current working directory.",
"$default": { "$source": "argv", "index": 0 },
"x-prompt": "What is the component file path?",
"x-priority": "important"
@@ -69,7 +69,7 @@
"js": {
"type": "boolean",
"description": "Generate JavaScript files rather than TypeScript files.",
"default": false
"x-deprecated": "Provide the full file path including the file extension in the `path` option. This option will be removed in Nx v21."
},
"skipFormat": {
"description": "Skip formatting files.",
@@ -79,7 +79,7 @@
}
},
"required": ["path"],
"examplesFile": "## Examples\n\n{% tabs %}\n{% tab label=\"Create a Component\" %}\n\nGenerate a component named `MyComponent` at `apps/my-app/src/app/my-component/my-component.tsx`:\n\n```shell\nnx g component apps/my-app/src/app/my-component/my-component\n```\n\n{% /tab %}\n{% tab label=\"Create a Component with a Different Symbol Name\" %}\n\nGenerate a component named `Custom` at `apps/my-app/src/app/my-component/my-component.tsx`:\n\n```shell\nnx g component apps/my-app/src/app/my-component/my-component --name=custom\n```\n\n{% /tab %}\n{% /tabs %}\n",
"examplesFile": "## Examples\n\n{% tabs %}\n{% tab label=\"Create a Component\" %}\n\nGenerate a component named `MyComponent` at `apps/my-app/src/app/my-component/my-component.tsx`:\n\n```shell\nnx g component apps/my-app/src/app/my-component/my-component.tsx\n```\n\n{% /tab %}\n{% tab label=\"Create a Component with a Different Symbol Name\" %}\n\nGenerate a component named `Custom` at `apps/my-app/src/app/my-component/my-component.tsx`:\n\n```shell\nnx g component apps/my-app/src/app/my-component/my-component.tsx --name=custom\n```\n\n{% /tab %}\n{% tab label=\"Create a Component Omitting the File Extension\" %}\n\nGenerate a component named `MyComponent` at `apps/my-app/src/app/my-component/my-component.tsx` without specifying the file extension:\n\n```shell\nnx g component apps/my-app/src/app/my-component/my-component\n```\n\n{% /tab %}\n{% /tabs %}\n",
"presets": []
},
"description": "Create a component.",
@@ -7,12 +7,12 @@
"$id": "NxPluginExecutor",
"title": "Create an Executor for an Nx Plugin",
"description": "Create an Executor for an Nx Plugin.",
"examplesFile": "## Examples\n\n{% tabs %}\n{% tab label=\"Basic executor\" %}\n\nCreate a new executor called `build` at `tools/my-plugin/src/executors/build.ts`:\n\n```bash\nnx g @nx/plugin:executor tools/my-plugin/src/executors/build\n```\n\n{% /tab %}\n{% tab label=\"With different exported name\" %}\n\nCreate a new executor called `custom` at `tools/my-plugin/src/executors/build.ts`:\n\n```bash\nnx g @nx/plugin:executor tools/my-plugin/src/executors/build --name=custom\n```\n\n{% /tab %}\n{% tab label=\"With custom hashing\" %}\n\nCreate a new executor called `build` at `tools/my-plugin/src/executors/build.ts`, that uses a custom hashing function:\n\n```bash\nnx g @nx/plugin:executor tools/my-plugin/src/executors/build --includeHasher\n```\n\n{% /tab %}\n{% /tabs %}\n",
"examplesFile": "## Examples\n\n{% tabs %}\n{% tab label=\"Basic executor\" %}\n\nCreate a new executor called `build` at `tools/my-plugin/src/executors/build.ts`:\n\n```bash\nnx g @nx/plugin:executor tools/my-plugin/src/executors/build.ts\n```\n\n{% /tab %}\n{% tab label=\"Without providing the file extension\" %}\n\nCreate a new executor called `build` at `tools/my-plugin/src/executors/build.ts`:\n\n```bash\nnx g @nx/plugin:executor tools/my-plugin/src/executors/build\n```\n\n{% /tab %}\n{% tab label=\"With different exported name\" %}\n\nCreate a new executor called `custom` at `tools/my-plugin/src/executors/build.ts`:\n\n```bash\nnx g @nx/plugin:executor tools/my-plugin/src/executors/build.ts --name=custom\n```\n\n{% /tab %}\n{% tab label=\"With custom hashing\" %}\n\nCreate a new executor called `build` at `tools/my-plugin/src/executors/build.ts`, that uses a custom hashing function:\n\n```bash\nnx g @nx/plugin:executor tools/my-plugin/src/executors/build --includeHasher\n```\n\n{% /tab %}\n{% /tabs %}\n",
"type": "object",
"properties": {
"path": {
"type": "string",
"description": "The file path to the executor without the file extension. Relative to the current working directory.",
"description": "The file path to the executor. Relative to the current working directory.",
"x-prompt": "What is the executor file path?",
"$default": { "$source": "argv", "index": 0 },
"x-priority": "important"
@@ -11,6 +11,10 @@
"examples": [
{
"description": "Generate a generator exported with the name matching the file name. It results in the generator `foo` at `mylib/src/generators/foo.ts`",
"command": "nx g @nx/plugin:generator mylib/src/generators/foo.ts"
},
{
"description": "Generate a generator without providing the file extension. It results in the generator `foo` at `mylib/src/generators/foo.ts`",
"command": "nx g @nx/plugin:generator mylib/src/generators/foo"
},
{
@@ -21,7 +25,7 @@
"properties": {
"path": {
"type": "string",
"description": "The file path to the generator without the file extension. Relative to the current working directory.",
"description": "The file path to the generator. Relative to the current working directory.",
"$default": { "$source": "argv", "index": 0 },
"x-prompt": "What is the generator file path?",
"x-priority": "important"
@@ -11,6 +11,10 @@
"examples": [
{
"description": "Generate a migration exported with the name matching the file name, which will be triggered when migrating to version 1.0.0 or above from a previous version. It results in the migration `foo` at `mylib/src/migrations/foo.ts`",
"command": "nx g @nx/plugin:migration mylib/src/migrations/foo.ts -v=1.0.0"
},
{
"description": "Generate a migration without providing the file extension, which will be triggered when migrating to version 1.0.0 or above from a previous version. It results in the migration `foo` at `mylib/src/migrations/foo.ts`",
"command": "nx g @nx/plugin:migration mylib/src/migrations/foo -v=1.0.0"
},
{
@@ -11,11 +11,15 @@
"examples": [
{
"description": "Generate a component with the exported symbol matching the file name. It results in the component `Foo` at `mylib/src/lib/foo.tsx`",
"command": "nx g @nx/react-native:component mylib/src/lib/foo"
"command": "nx g @nx/react-native:component mylib/src/lib/foo.tsx"
},
{
"description": "Generate a component with the exported symbol different from the file name. It results in the component `Custom` at `mylib/src/lib/foo.tsx`",
"command": "nx g @nx/react-native:component mylib/src/lib/foo --name=custom"
"command": "nx g @nx/react-native:component mylib/src/lib/foo.tsx --name=custom"
},
{
"description": "Generate a component without providing the file extension. It results in the component `Foo` at `mylib/src/lib/foo.tsx`",
"command": "nx g @nx/react-native:component mylib/src/lib/foo"
},
{
"description": "Generate a class component at `mylib/src/lib/foo.tsx`",
@@ -25,7 +29,7 @@
"properties": {
"path": {
"type": "string",
"description": "The file path to the component without the file extension. Relative to the current working directory.",
"description": "The file path to the component. Relative to the current working directory.",
"$default": { "$source": "argv", "index": 0 },
"x-prompt": "What is the component file path?"
},
@@ -36,7 +40,7 @@
"js": {
"type": "boolean",
"description": "Generate JavaScript files rather than TypeScript files.",
"default": false
"x-deprecated": "Provide the full file path including the file extension in the `path` option. This option will be removed in Nx v21."
},
"skipTests": {
"type": "boolean",
@@ -11,7 +11,7 @@
"properties": {
"path": {
"type": "string",
"description": "The file path to the component without the file extension. Relative to the current working directory.",
"description": "The file path to the component. Relative to the current working directory.",
"$default": { "$source": "argv", "index": 0 },
"x-prompt": "What is the component file path?",
"x-priority": "important"
@@ -57,7 +57,7 @@
"js": {
"type": "boolean",
"description": "Generate JavaScript files rather than TypeScript files.",
"default": false
"x-deprecated": "Provide the full file path including the file extension in the `path` option. This option will be removed in Nx v21."
},
"skipTests": {
"type": "boolean",
@@ -87,10 +87,6 @@
"description": "Default is `false`. When `true`, the component is generated with `*.css`/`*.scss` instead of `*.module.css`/`*.module.scss`.",
"default": false
},
"fileName": {
"type": "string",
"description": "Create a component with this file name."
},
"inSourceTests": {
"type": "boolean",
"default": false,
@@ -104,7 +100,7 @@
}
},
"required": ["path"],
"examplesFile": "## Examples\n\n{% tabs %}\n{% tab label=\"Simple Component\" %}\n\nCreate a component named `MyComponent` at `libs/ui/src/my-component.tsx`:\n\n```shell\nnx g @nx/react:component libs/ui/src/my-component\n```\n\n{% /tab %}\n\n{% tab label=\"With a Different Symbol Name\" %}\n\nCreate a component named `Custom` at `libs/ui/src/my-component.tsx`:\n\n```shell\nnx g @nx/react:component libs/ui/src/my-component --name=custom\n```\n\n{% /tab %}\n\n{% tab label=\"Class Component\" %}\n\nCreate a class component named `MyComponent` at `libs/ui/src/my-component.tsx`:\n\n```shell\nnx g @nx/react:component libs/ui/src/my-component --classComponent\n```\n\n{% /tab %}\n",
"examplesFile": "## Examples\n\n{% tabs %}\n{% tab label=\"Simple Component\" %}\n\nCreate a component named `MyComponent` at `libs/ui/src/my-component.tsx`:\n\n```shell\nnx g @nx/react:component libs/ui/src/my-component.tsx\n```\n\n{% /tab %}\n\n{% tab label=\"With a Different Symbol Name\" %}\n\nCreate a component named `Custom` at `libs/ui/src/my-component.tsx`:\n\n```shell\nnx g @nx/react:component libs/ui/src/my-component.tsx --name=custom\n```\n\n{% /tab %}\n\n{% tab label=\"Omitting the File Extension\" %}\n\nCreate a component named `MyComponent` at `libs/ui/src/my-component.tsx` without specifying the file extension:\n\n```shell\nnx g @nx/react:component libs/ui/src/my-component\n```\n\n{% /tab %}\n\n{% tab label=\"Class Component\" %}\n\nCreate a class component named `MyComponent` at `libs/ui/src/my-component.tsx`:\n\n```shell\nnx g @nx/react:component libs/ui/src/my-component --classComponent\n```\n\n{% /tab %}\n",
"presets": []
},
"description": "Create a React component.",
@@ -11,17 +11,21 @@
"examples": [
{
"description": "Generate a hook with the exported symbol matching the file name. It results in the hook `useFoo` at `mylib/src/lib/foo.ts`",
"command": "nx g @nx/react:hook mylib/src/lib/foo"
"command": "nx g @nx/react:hook mylib/src/lib/foo.ts"
},
{
"description": "Generate a hook with the exported symbol different from the file name. It results in the hook `useCustom` at `mylib/src/lib/foo.ts`",
"command": "nx g @nx/react:hook mylib/src/lib/foo --name=useCustom"
"command": "nx g @nx/react:hook mylib/src/lib/foo.ts --name=useCustom"
},
{
"description": "Generate a hook without providing the file extension. It results in the hook `useFoo` at `mylib/src/lib/foo.ts`",
"command": "nx g @nx/react:hook mylib/src/lib/foo"
}
],
"properties": {
"path": {
"type": "string",
"description": "The file path to the hook without the file extension. Relative to the current working directory.",
"description": "The file path to the hook. Relative to the current working directory.",
"$default": { "$source": "argv", "index": 0 },
"x-prompt": "What is the hook file path?",
"x-priority": "important"
@@ -33,7 +37,7 @@
"js": {
"type": "boolean",
"description": "Generate JavaScript files rather than TypeScript files.",
"default": false
"x-deprecated": "Provide the full file path including the file extension in the `path` option. This option will be removed in Nx v21."
},
"skipTests": {
"type": "boolean",
@@ -11,17 +11,21 @@
"examples": [
{
"description": "Generate a Redux state slice with the exported symbol matching the file name. It results in the slice `fooSlice` at `mylib/src/lib/foo.slice.ts`",
"command": "nx g @nx/react:redux mylib/src/lib/foo"
"command": "nx g @nx/react:redux mylib/src/lib/foo.slice.ts"
},
{
"description": "Generate a Redux state slice with the exported symbol different from the file name. It results in the slice `customSlice` at `mylib/src/lib/foo.slice.ts`",
"command": "nx g @nx/react:redux mylib/src/lib/foo --name=custom"
"command": "nx g @nx/react:redux mylib/src/lib/foo.slice.ts --name=custom"
},
{
"description": "Generate a Redux state slice without providing the \"slice\" suffix and the file extension. It results in the slice `fooSlice` at `mylib/src/lib/foo.slice.ts`",
"command": "nx g @nx/react:redux mylib/src/lib/foo"
}
],
"properties": {
"path": {
"type": "string",
"description": "The file path to the Redux state slice without the file extension. Relative to the current working directory.",
"description": "The file path to the Redux state slice. Relative to the current working directory.",
"$default": { "$source": "argv", "index": 0 },
"x-prompt": "What is the Redux stateslice file path?",
"x-priority": "important"
@@ -38,7 +42,7 @@
"js": {
"type": "boolean",
"description": "Generate JavaScript files rather than TypeScript files.",
"default": false
"x-deprecated": "Provide the full file path including the file extension in the `path` option. This option will be removed in Nx v21."
}
},
"required": ["path"],
@@ -9,16 +9,20 @@
"description": "Generate a resource route.",
"examples": [
{
"command": "g resource-route 'path/to/page'",
"description": "Generate resource route at /path/to/page"
"description": "Generate a resource route at `myapp/app/routes/foo.ts`",
"command": "nx g resource-route myapp/app/routes/foo.ts"
},
{
"description": "Generate a resource route without providing the file extension at `myapp/app/routes/foo.tsx`",
"command": "nx g resource-route myapp/app/routes/foo"
}
],
"properties": {
"path": {
"type": "string",
"description": "The route path or path to the filename of the route.",
"description": "The file path to the route. Relative to the current working directory.",
"$default": { "$source": "argv", "index": 0 },
"x-prompt": "What is the path of the route? (e.g. 'apps/demo/app/routes/foo/bar')"
"x-prompt": "What is the route file path?"
},
"action": {
"type": "boolean",
@@ -9,16 +9,20 @@
"type": "object",
"examples": [
{
"command": "g route 'path/to/page'",
"description": "Generate route at /path/to/page"
"description": "Generate a route at `myapp/app/routes/foo.tsx`",
"command": "nx g resource-route myapp/app/routes/foo.tsx"
},
{
"description": "Generate a route without providing the file extension at `myapp/app/routes/foo.tsx`",
"command": "nx g resource-route myapp/app/routes/foo"
}
],
"properties": {
"path": {
"type": "string",
"description": "The route path or path to the filename of the route.",
"description": "The file path to the route. Relative to the current working directory.",
"$default": { "$source": "argv", "index": 0 },
"x-prompt": "What is the path of the route? (e.g. 'apps/demo/app/routes/foo/bar')"
"x-prompt": "What is the route file path?"
},
"style": {
"type": "string",
@@ -9,16 +9,16 @@
"type": "object",
"examples": [
{
"command": "g style --path='apps/demo/app/routes/path/to/page.tsx'",
"description": "Generate route at apps/demo/app/routes/path/to/page.tsx"
"description": "Generate a stylesheet at `myapp/app/styles/foo.css`",
"command": "nx g style myapp/app/routes/foo.tsx"
}
],
"properties": {
"path": {
"type": "string",
"description": "Route path",
"description": "The file path to the route. Relative to the current working directory.",
"$default": { "$source": "argv", "index": 0 },
"x-prompt": "What is the path of the route? (e.g. 'apps/demo/app/routes/foo/bar.tsx')"
"x-prompt": "What is the route file path?"
}
},
"required": ["path"],
@@ -9,6 +9,14 @@
"description": "Create a Vue Component for Nx.",
"type": "object",
"examples": [
{
"description": "Generate a component at `mylib/src/lib/foo.vue`",
"command": "nx g @nx/vue:component mylib/src/lib/foo.vue"
},
{
"description": "Generate a component without providing the file extension at `mylib/src/lib/foo.vue`",
"command": "nx g @nx/vue:component mylib/src/lib/foo"
},
{
"description": "Generate a component at `mylib/src/lib/foo.vue` with `vitest` as the unit test runner",
"command": "nx g @nx/vue:component mylib/src/lib/foo --unitTestRunner=vitest"
@@ -17,7 +25,7 @@
"properties": {
"path": {
"type": "string",
"description": "The file path to the component without the file extension. Relative to the current working directory.",
"description": "The file path to the component. Relative to the current working directory.",
"$default": { "$source": "argv", "index": 0 },
"x-prompt": "What is the component file path?"
},
+6 -6
View File
@@ -499,6 +499,12 @@
"tags": ["nx-release"],
"file": "shared/recipes/nx-release/release-projects-independently"
},
{
"name": "Updating Version References in Manifest Files (e.g. package.json)",
"id": "updating-version-references",
"tags": ["nx-release"],
"file": "shared/recipes/nx-release/updating-version-references"
},
{
"name": "Automatically Version with Conventional Commits",
"id": "automatically-version-with-conventional-commits",
@@ -553,12 +559,6 @@
"tags": ["nx-release"],
"file": "shared/recipes/nx-release/configure-changelog-format"
},
{
"name": "Publish a Custom Dist Directory",
"id": "publish-custom-dist-directory",
"tags": ["nx-release"],
"file": "shared/recipes/nx-release/publish-custom-dist-directory"
},
{
"name": "Build Before Versioning",
"id": "build-before-versioning",
@@ -14,7 +14,6 @@ pr:
variables:
CI: 'true'
NX_CLOUD_DISTRIBUTED_EXECUTION_AGENT_COUNT: 3 # expected number of agents
${{ if eq(variables['Build.Reason'], 'PullRequest') }}:
NX_BRANCH: $(System.PullRequest.PullRequestNumber)
TARGET_BRANCH: $[replace(variables['System.PullRequest.TargetBranch'],'refs/heads/','origin/')]
@@ -30,10 +30,9 @@ pipelines:
name: CI
script:
- export NX_BRANCH=$BITBUCKET_PR_ID
- export NX_CLOUD_DISTRIBUTED_EXECUTION_AGENT_COUNT=3
- npm ci
- npx nx-cloud start-ci-run --distribute-on="manual" --stop-agents-after="e2e-ci" --agent-count=3
- npx nx-cloud start-ci-run --distribute-on="manual" --stop-agents-after="e2e-ci"
- npx nx-cloud record -- nx format:check
- npx nx affected --target=lint,test,build,e2e-ci --parallel=2
- step: *agent
@@ -14,8 +14,6 @@ jobs:
main:
docker:
- image: cimg/node:lts-browsers
environment:
NX_CLOUD_DISTRIBUTED_EXECUTION_AGENT_COUNT: 3 # expected number of agents
steps:
- checkout
- run: npm ci
@@ -23,7 +23,6 @@ permissions:
env:
NX_CLOUD_DISTRIBUTED_EXECUTION: true # this enables DTE
NX_CLOUD_DISTRIBUTED_EXECUTION_AGENT_COUNT: 3 # expected number of agents
NX_BRANCH: ${{ github.event.number || github.ref_name }}
NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }} # this is needed if our pipeline publishes to npm
@@ -38,7 +38,6 @@ image: node:18
- yarn install --cache-folder .yarn-cache --prefer-offline --frozen-lockfile
- NX_HEAD=$CI_COMMIT_SHA
- NX_BASE=${CI_MERGE_REQUEST_DIFF_BASE_SHA:-$CI_COMMIT_BEFORE_SHA}
- NX_CLOUD_DISTRIBUTED_EXECUTION_AGENT_COUNT=3 # expected number of agents
artifacts:
expire_in: 5 days
@@ -11,7 +11,6 @@ pipeline {
agent none
environment {
NX_BRANCH = env.BRANCH_NAME.replace('PR-', '')
NX_CLOUD_DISTRIBUTED_EXECUTION_AGENT_COUNT = 3
}
stages {
stage('Pipeline') {
@@ -101,7 +101,7 @@ First, use your project configuration (in `project.json` or `package.json`) to a
Next, you should update your root lint configuration:
- If you are using **ESLint** you should look for an existing rule entry in your root `.eslintrc.json` called `"@nx/enforce-module-boundaries"` and you should update the `"depConstraints"`:
- If you are using **ESLint** you should look for an existing rule entry in your root `.eslintrc.json` called `@nx/enforce-module-boundaries` and you should update the `depConstraints`:
```jsonc {% fileName=".eslintrc.json" %}
{
+2 -2
View File
@@ -20,7 +20,7 @@ nx release --dry-run
A release can be thought about in three main phases:
1. **Versioning** - The process of determining the next version of your projects, and updating any projects that depend on them to use the new version.
2. **Changelog** - The process of deriving a changelog from your commit messages, which can be used to communicate the changes to your users.
2. **Changelog** - The process of deriving a changelog from your commit messages or [version plan](/recipes/nx-release/file-based-versioning-version-plans) files, which can be used to communicate the changes to your users.
3. **Publishing** - The process of publishing your projects to a registry, such as npm for TypeScript/JavaScript libraries.
## Running releases
@@ -43,7 +43,7 @@ If you are working with a brand new workspace, or one that has never been releas
The `nx release` command is highly customizable. You can customize the versioning, changelog, and publishing phases of the release process independently through a mixture of configuration and CLI arguments.
The configuration lives in your `nx.json` file under the `"release"` section.
The configuration lives in your `nx.json` file under the `release` section.
```jsonc {% fileName="nx.json" %}
{
@@ -192,7 +192,7 @@ serve" target executor to `@nx/angular:dev-server` as shown below.
### Add Executor to Target Defaults
If you'd like to avoid adding `"dependsOn": ["^build"]` to every application in your workspace that uses one of the
required executors you can add it to the `"targetDefaults"` section of the `nx.json`:
required executors you can add it to the `targetDefaults` section of the `nx.json`:
{% tabs %}
{% tab label="@nx/angular:application" %}
+1 -1
View File
@@ -218,7 +218,7 @@ export default defineConfig({
fileName: 'index',
// Change this to the formats you want to support.
// Don't forget to update your package.json as well.
formats: ['es', 'cjs'],
formats: ['es'],
},
rollupOptions: {
// External packages that should not be bundled into your library.
@@ -72,7 +72,7 @@ That's is! Our server is now deployed for the world to use.
You can also automate the deployment by adding a target to your project. In addition, that allows us to leverage the Nx [task pipeline](/concepts/task-pipeline-configuration) to make sure we first run the `build` and then the `deploy`.
By using [Nx run-commands](/recipes/running-tasks/run-commands-executor), you can add a `deploy` target to the project. Go to the project's `project.json` file (under `"targets"`) and add the following:
By using [Nx run-commands](/recipes/running-tasks/run-commands-executor), you can add a `deploy` target to the project. Go to the project's `project.json` file (under `targets`) and add the following:
```json {% fileName="project.json" %}
"deploy": {
@@ -12,7 +12,7 @@ In order to ensure that projects are built before the new version is applied to
}
```
This command will run the `build` target for all projects before the version step of Nx Release. Any command can be specified, including non-nx commands. This step is often required when [publishing from a custom dist directory](/recipes/nx-release/publish-custom-dist-directory), as the dist directory must be built before the version is applied to the dist directory's package manifest.
This command will run the `build` target for all projects before the version step of Nx Release. Any command can be specified, including non-nx commands. This step is often required when [publishing from a custom dist directory](/recipes/nx-release/updating-version-references#scenario-2-i-want-to-publish-from-a-custom-dist-directory-and-not-update-references-in-my-source-packagejson-files), as the dist directory must be built before the version is applied to the dist directory's package manifest.
When using release groups in which the member projects are versioned together, you can use `groupPreVersionCommand` and it will be executed before the versioning step for that release group.
@@ -151,7 +151,7 @@ You can read more about these utilities and why they are needed on their respect
Nx release will compare the touched projects to the projects and release groups that are specified in the version plan files in the `.nx/version-plans/` directory. If a version plan file does not exist, the command will print an error message and return a non-zero exit code, which can be used to fail CI builds or other automation.
By default, all files that have changed are considered, but you may not want all files under a project to require a version plan be created for them. For example, you may wish to ignore test only files from consideration from this check. The way you can achieve this is by setting version plans to be a configuration object instead of a boolean, and set the `"ignorePatternsForPlanCheck"` property to an array of glob patterns that should be ignored when checking for version plans. For example:
By default, all files that have changed are considered, but you may not want all files under a project to require a version plan be created for them. For example, you may wish to ignore test only files from consideration from this check. The way you can achieve this is by setting version plans to be a configuration object instead of a boolean, and set the `ignorePatternsForPlanCheck` property to an array of glob patterns that should be ignored when checking for version plans. For example:
```jsonc
{
@@ -1,79 +0,0 @@
# Publish a Custom Dist Directory
This recipe guides you through configuring Nx Release to version and publish packages that are built to a custom `dist` directory instead of their source project root. This is often the case when using Nx generators or custom builders that output to a centralized `dist` directory at the workspace root.
## The Package Root
Nx Release has the concept of a "package root", which is different than the project root. The package root is the directory from which the package is versioned and published. By default, the package root is the project root detected by Nx. Due to the modular nature of Nx Release, the package root can be configured independently for the version and publish steps.
## Strategies for Tracking Dependencies
Nx Release supports two out-of-the-box methods of tracking dependencies for js packages.
### Use Explicit Version Numbers and Track Versions in Source Control
Keep all version numbers in the source package.json file and use semantic versions when specifying dependencies. This is the default behavior. In this case, a package.json file will look like this:
```json
{
"name": "my-package",
"version": "0.1.1",
"dependencies": {
"my-other-package": "^0.1.1",
"my-other-package-2": "1.2.0"
}
}
```
Nx Release will read the current version (0.1.1) from the source package.json file, update it to the new version, and update all dependencies in this package.json file. Changes to the package.json file will be staged and committed unless git operations are disabled. In this case, the `packageRoot` is the same as the Nx project root.
### Use File or Workspace References and Do Not Track Versions in Source Control
Keep all version numbers in the dist package.json file and use file or workspace references for specifying dependencies. This is done by specifying an alternate `packageRoot` for the version and publish commands. In this case, the source package.json file looks like this:
```json
{
"name": "my-package",
"dependencies": {
"my-other-package": "file:../my-other-package",
"my-other-package-2": "workspace:*"
}
}
```
and the dist package.json file, which will actually be published in the publish step, looks like this:
```json
{
"name": "my-package",
"version": "0.1.1",
"dependencies": {
"my-other-package": "0.1.1",
"my-other-package-2": "1.2.0"
}
}
```
Note that the version number is not present in the source package.json file. This is because the source package.json file is never updated in the versioning process; its data is instead written to the dist package.json file. To ensure the correct version number is used in the dist package.json file, `"release.version.generatorOptions.currentVersionResolver"` should be set to something other than the default value of `"disk"`. To pick the current version from git tags, set it to `"git-tag"`. To look up the current version from the remote registry, set it to `"registry"`.
Configure this behavior by adding the following configuration to the `nx.json` file, or the `project.json` file of relevant projects:
```jsonc {% fileName="nx.json" %}
{
"release": {
"version": {
"generatorOptions": {
"packageRoot": "dist/packages/{projectName}", // path structure for your dist directory
"currentVersionResolver": "git-tag" // or "registry"
}
}
},
"targetDefaults": {
"nx-release-publish": {
"options": {
"packageRoot": "dist/packages/{projectName}" // path structure for your dist directory
}
}
}
}
```
@@ -0,0 +1,193 @@
# Updating Version References in Manifest Files (e.g. package.json)
The versioning stage of Nx Release is customizable and programming language agnostic, but some of its capabilities are dictated by the tooling you are using. This is particularly true when it comes to updating version references in manifest files, such as `package.json`.
Nx provides the TypeScript/JavaScript (and therefore `package.json`) functionality out of the box, so that is what will be covered in more detail in this recipe. For other ecosystems, please see the documentation of the respective plugins.
An important characteristic of Nx release is that it does not manipulate your packages in memory before releasing them. This maintains complete transparency between you and the tooling being leveraged to publish your packages, such as `npm publish` or `pnpm publish`, which are leveraged automatically by Nx Release during its publishing phase. The relevance of this will become clear for [Scenario 3 below](#scenario-3-i-want-to-update-package-versions-directly-in-my-source-files-but-use-local-dependency-references-via-fileworkspace).
## Scenario 1: I want to update semantic version numbers directly in my source package.json files
This is the simplest scenario, and default behavior of Nx Release. If you have a TypeScript/JavaScript project which lives at e.g. `packages/my-project` with its package.json at the root of the project, you can run `nx release` or use the programmatic API and it will update the version number and all relevant intra-workspace dependency references in `packages/my-project/package.json` to the new version(s).
For example, with the following project structure:
```
packages/
my-project/
package.json
my-other-project-in-the-monorepo/
package.json
```
And starting point for package.json sources:
```json {% fileName="packages/my-project/package.json" %}
{
"name": "my-project",
"version": "0.1.1",
"dependencies": {
"my-other-project-in-the-monorepo": "0.1.1"
}
}
```
```json {% fileName="packages/my-other-project-in-the-monorepo/package.json" %}
{
"name": "my-other-project-in-the-monorepo",
"version": "0.1.1"
}
```
When running `nx release` and applying a patch release, the following changes will be made to the source package.json files:
```json {% fileName="packages/my-project/package.json" %}
{
"name": "my-project",
"version": "0.1.2",
"dependencies": {
"my-other-project-in-the-monorepo": "0.1.2"
}
}
```
```json {% fileName="packages/my-other-project-in-the-monorepo/package.json" %}
{
"name": "my-other-project-in-the-monorepo",
"version": "0.1.2"
}
```
By default, the changes will be staged and committed unless git operations are disabled.
## Scenario 2: I want to publish from a custom dist directory and not update references in my source package.json files
Nx Release has the concept of a "package root", which is different than the project root. The package root is the directory from which the package is versioned and published. By default, the package root is the project root detected by Nx as we have seen in Scenario 1 above, but the package root can be configured independently for the versioning and publishing steps.
If we want to build our projects to a centralized `dist/` directory in the Nx workspace, we can tell Nx Release to discover it for the versioning and publishing steps by adding the following configuration to the `nx.json` file, or the `project.json` file of relevant projects:
{% callout type="warning" title="The source control tracked package.json files are no longer the source of truth for the package version" %}
Because we are no longer updating the version references in the source package.json files, the source control tracked package.json files are no longer the source of truth for the package version. We need to reference git tags or the latest value in the registry as the source of truth for the package version instead. We will also need to handle intra-workspace dependency references in the source package.json files differently using file/workspace references, which will be covered below.
{% /callout %}
```jsonc {% fileName="nx.json" %}
{
"release": {
// Ensure that versioning works from the dist directory
"version": {
"generatorOptions": {
"packageRoot": "dist/packages/{projectName}", // path structure for your dist directory, where {projectRoot} and {projectName} are available placeholders that will be interpolated by Nx
"currentVersionResolver": "git-tag" // or "registry", because we are no longer referencing our source package.json as the source of truth for the current version
}
}
},
"targetDefaults": {
// Ensure that publishing works from the dist directory
// The nx-release-publish target is added implicitly behind the scenes by Nx Release, and we can therefore configure it in targetDefaults
"nx-release-publish": {
"options": {
"packageRoot": "dist/packages/{projectName}" // path structure for your dist directory, where {projectRoot} and {projectName} are available placeholders that will be interpolated by Nx
}
}
}
}
```
Because our source package.json files are no longer updated during versioning, we will need to handle intra-workspace dependency references in the source package.json files differently. The way to achieve this is by using local `file:` or `workspace:` references in the source package.json files.
For example, using our packages from Scenario 1 above, if we want to reference the `my-other-project-in-the-monorepo` project from `my-project`, we can update the source package.json file as follows:
```jsonc {% fileName="packages/my-project/package.json" %}
{
"name": "my-project", // note there is no version number in the source package.json file because it will never be updated
"dependencies": {
"my-other-project-in-the-monorepo": "workspace:*" // or "file:../my-other-project-in-the-monorepo", depending on your preference and which package manager you are using
}
}
```
This reference will never need to change when versioning is carried out and the package manager will reliably link the two packages together (and tools like TypeScript can follow these references for import resolution etc).
Our dist package.json will therefore ultimately look like this:
```jsonc {% fileName="dist/packages/my-project/package.json" %}
{
"name": "my-project",
"version": "0.1.2", // the version number is applied
"dependencies": {
"my-other-project-in-the-monorepo": "0.1.2" // the dependency reference is updated from the workspace reference to the actual version number
}
}
```
This package.json is now valid and ready to be published to the registry.
## Scenario 3: I want to update package versions directly in my source files, but use local dependency references via file/workspace
{% callout type="caution" title="This scenario is only supported when your package manager is pnpm" %}
pnpm is the only package manager that provides a publish command that supports dynamically swapping the `file:` and `workspace:*` references with the actual version number at publish time.
{% /callout %}
This is a more advanced scenario because it removes the clean separation of concerns between versioning and publishing. The reason for this is that the `file:` and `workspace:` references simply have to be replaced with actual version numbers before they are written to the registry, otherwise they will break when a user tries to install the package. If versioning does not replace them, publishing needs to.
As mentioned at the start of this recipe, Nx Release intentionally does not manipulate your packages in memory during publishing, so this scenario is only supported when your package manager provides publishing functionality which dynamically swaps the local references. **Currently this is only supported by pnpm.**
Let's first look at the default behavior of Nx Release, which is to update the all version references in the source package.json files with the new version number.
For example, using this source package.json file, when applying a patch release:
```jsonc {% fileName="packages/my-project/package.json" %}
{
"name": "my-project",
"version": "0.1.2",
"dependencies": {
"my-other-project-in-the-monorepo": "workspace:*"
}
}
```
Nx release will see this and update the "version" number to `0.1.3`, and want to replace the `workspace:*` reference with the actual version number of the dependency - also `0.1.3`. This cleanly prepares the package.json ready to be published:
```jsonc {% fileName="packages/my-project/package.json" %}
{
"name": "my-project",
"version": "0.1.3",
"dependencies": {
// whilst this is now ready to publish, it is not really what we wanted to happen,
// because we have now hardcoded the version number in the source package.json file
// and we have lost the evergreen "workspace:*" reference
"my-other-project-in-the-monorepo": "0.1.3"
}
}
```
Now Nx release can publish this and your package will work for all its consumers, but you have the side-effect of having hardcoded the version number in the source package.json file. This is exactly what we covered in [Scenario 1 above](#scenario-1-i-want-to-update-semantic-version-numbers-directly-in-my-source-packagejson-files).
To instead configure Nx Release to not update the dependency references in the source package.json files, you can set the "preserveLocalDependencyProtocols" version generator option to `true`:
```jsonc {% fileName="nx.json" %}
{
"release": {
"version": {
"generatorOptions": {
"preserveLocalDependencyProtocols": true
}
}
}
}
```
Now, that same patch release to the source package.json file will result in the following:
```jsonc {% fileName="packages/my-project/package.json" %}
{
"name": "my-project",
"version": "0.1.3", // our version number is updated as expected
"dependencies": {
// our workspace dependency reference is preserved
"my-other-project-in-the-monorepo": "workspace:*"
}
}
```
Again, this is not in a valid state to be published to the registry, and so the publishing step will need to handle this. **This is only supported by pnpm**, in which case Nx Release invokes `pnpm publish` instead of `npm publish` behind the scenes during publishing, and you will receive a clear error if you attempt to use such a package.json with another package manager.
@@ -144,7 +144,7 @@ In the `options` you will see `"compodoc": false`. Change that to `true`.
Storybook has [preconfigured `compodoc`](https://github.com/storybookjs/storybook/blob/next/code/frameworks/angular/src/builders/utils/run-compodoc.ts#L25) to generate a `documentation.json` file at the root of your workspace by default. We want to change that, and keep the documentation file project-specific. Of course, you can change that later, or as you see fit for your use case. But let's keep it project-specific for now.
In your project's `project.json` file (eg. `apps/my-app/project.json`), find the `storybook` and the `build-storybook` targets. Below the `"compodoc"` option, create a new option called `"compodocArgs` which contains the following: `["-e", "json", "-d", "apps/my-app"]`. This means that the `exportFormat` (`-e`) will be `json` and the `output` directory (`-d`) will be `apps/my-app` (change that, of course, to the directory of your project).
In your project's `project.json` file (eg. `apps/my-app/project.json`), find the `storybook` and the `build-storybook` targets. Below the `compodoc` option, create a new option called `"compodocArgs` which contains the following: `["-e", "json", "-d", "apps/my-app"]`. This means that the `exportFormat` (`-e`) will be `json` and the `output` directory (`-d`) will be `apps/my-app` (change that, of course, to the directory of your project).
Let's see the result for our `my-app` app `storybook` target, for example (in `apps/my-app/project.json`):
+7 -1
View File
@@ -1,4 +1,10 @@
# Releases
# Releases of the nx and @nx/\* packages
{% callout type="info" title="Looking for guidance on releasing your projects with Nx Release?" %}
This page covers the releases of the Nx tooling itself. If you are looking for guidance on releasing your project with Nx Release, [see the documentation on managing releases](/features/manage-releases).
{% /callout %}
The `nx` package, and all packages under the `@nx` namespace which live alongside each other in the [https://github.com/nrwl/nx](https://github.com/nrwl/nx) repository, are released together in lockstep. You should always use matching versions of the `nx` package and the `@nx` packages, e.g. `nx@19.2.0` and `@nx/js@20.2.0` should be used together.
Major Nx versions are released as the _latest_ every six months, typically around April and October.
After each major version release, the _previous_ major version moves to long-term support (LTS) for 12 months, after
+1 -1
View File
@@ -75,6 +75,7 @@
- [Nx Release](/recipes/nx-release)
- [Get Started with Nx Release](/recipes/nx-release/get-started-with-nx-release)
- [Release Projects Independently](/recipes/nx-release/release-projects-independently)
- [Updating Version References in Manifest Files (e.g. package.json)](/recipes/nx-release/updating-version-references)
- [Automatically Version with Conventional Commits](/recipes/nx-release/automatically-version-with-conventional-commits)
- [Customize Conventional Commit Types](/recipes/nx-release/customize-conventional-commit-types)
- [File Based Versioning (Version Plans)](/recipes/nx-release/file-based-versioning-version-plans)
@@ -84,7 +85,6 @@
- [Publish Rust Crates](/recipes/nx-release/publish-rust-crates)
- [Update Your Local Registry Setup to use Nx Release](/recipes/nx-release/update-local-registry-setup)
- [Configure Changelog Format](/recipes/nx-release/configure-changelog-format)
- [Publish a Custom Dist Directory](/recipes/nx-release/publish-custom-dist-directory)
- [Build Before Versioning](/recipes/nx-release/build-before-versioning)
- [Configuring Version Prefix for Dependency Versions](/recipes/nx-release/configuration-version-prefix)
- [Module Federation](/recipes/module-federation)
+24 -5
View File
@@ -5,9 +5,11 @@ import {
newProject,
runCLI,
runCommand,
updateJson,
updateFile,
e2eCwd,
readJson,
readFile,
updateFile,
updateJson,
} from '@nx/e2e/utils';
import { writeFileSync, mkdirSync, rmdirSync } from 'fs';
import { execSync } from 'node:child_process';
@@ -38,7 +40,10 @@ describe('Nx Import', () => {
try {
rmdirSync(join(tempImportE2ERoot));
} catch {}
});
beforeEach(() => {
// Clean up the temp import directory before each test to not have any uncommited changes
runCommand(`git add .`);
runCommand(`git commit -am "Update" --allow-empty`);
});
@@ -110,9 +115,13 @@ describe('Nx Import', () => {
execSync(`git commit -am "initial commit"`, {
cwd: repoPath,
});
execSync(`git checkout -b main`, {
cwd: repoPath,
});
try {
execSync(`git checkout -b main`, {
cwd: repoPath,
});
} catch {
// This fails if git is already configured to have `main` branch, but that's OK
}
mkdirSync(join(repoPath, 'packages/a'), { recursive: true });
writeFileSync(join(repoPath, 'packages/a/README.md'), `# A`);
execSync(`git add .`, {
@@ -143,6 +152,16 @@ describe('Nx Import', () => {
}
);
if (getSelectedPackageManager() === 'pnpm') {
const workspaceYaml = readFile('pnpm-workspace.yaml');
expect(workspaceYaml).toMatch(/(packages\/a)/);
expect(workspaceYaml).toMatch(/(packages\/b)/);
} else {
const packageJson = readJson('package.json');
expect(packageJson.workspaces).toContain('packages/a');
expect(packageJson.workspaces).toContain('packages/b');
}
checkFilesExist('packages/a/README.md', 'packages/b/README.md');
});
});
-3
View File
@@ -116,7 +116,6 @@ describe('Build React applications and libraries with Vite', () => {
await runCLIAsync(`build ${viteLib}`);
checkFilesExist(
`dist/libs/${viteLib}/index.d.ts`,
`dist/libs/${viteLib}/index.js`,
`dist/libs/${viteLib}/index.mjs`
);
}, 300_000);
@@ -132,7 +131,6 @@ describe('Build React applications and libraries with Vite', () => {
checkFilesExist(
`dist/libs/${viteLib}/index.d.ts`,
`dist/libs/${viteLib}/index.js`,
`dist/libs/${viteLib}/index.mjs`
);
@@ -147,7 +145,6 @@ describe('Build React applications and libraries with Vite', () => {
await runCLIAsync(`build ${nonBuildableLib}`);
checkFilesExist(
`dist/libs/${nonBuildableLib}/index.d.ts`,
`dist/libs/${nonBuildableLib}/index.js`,
`dist/libs/${nonBuildableLib}/index.mjs`,
`dist/libs/${nonBuildableLib}/README.md`
);
+25 -26
View File
@@ -3,29 +3,31 @@ import { NextSeo } from 'next-seo';
import { DefaultLayout } from '@nx/nx-dev/ui-common';
import {
CallToAction,
DownloadEbook,
EnterpriseAddons,
CustomerLogos,
CustomerMetrics,
Hero,
MetricsAndCustomers,
ScaleYourPeople,
HetznerCloudTestimonial,
MakeYourCiFast,
ScaleOrganizationIntro,
ScaleYourOrganization,
Security,
TrustedBy,
SolveYourCi,
TestimonialCarousel,
VmwareTestimonial,
} from '@nx/nx-dev/ui-enterprise';
import { TrialCallout } from '@nx/nx-dev/ui-pricing';
import { requestFreeTrial } from '../lib/components/headerCtaConfigs';
import { ReactElement } from 'react';
export function Enterprise(): JSX.Element {
export function Enterprise(): ReactElement {
const router = useRouter();
return (
<>
<NextSeo
title="Nx Enterprise"
title="Solving the Performance Paradox, get speed and scale"
description="Accelerate your organization's journey to tighter collaboration, better developer experience, and speed…lots of speed."
openGraph={{
url: 'https://nx.dev' + router.asPath,
title: 'Nx Enterprise',
title: 'Solving the Performance Paradox, get speed and scale',
description:
"Accelerate your organization's journey to tighter collaboration, better developer experience, and speed…lots of speed.",
images: [
@@ -41,35 +43,32 @@ export function Enterprise(): JSX.Element {
type: 'website',
}}
/>
<DefaultLayout headerCTAConfig={[requestFreeTrial]}>
<DefaultLayout headerCTAConfig={[requestFreeTrial]} isHome={true}>
<div>
<Hero />
<CustomerLogos />
</div>
<CustomerMetrics />
<div className="mt-32 lg:mt-40">
<MakeYourCiFast />
</div>
<div className="mt-32 lg:mt-40">
<MetricsAndCustomers />
<TestimonialCarousel />
</div>
<div className="mt-32 lg:mt-40">
<TrialCallout pageId="enterprise" />
<ScaleOrganizationIntro />
<ScaleYourOrganization />
</div>
<div className="mt-32 lg:mt-56">
<ScaleYourPeople />
<div className="mt-32 lg:mt-40">
<HetznerCloudTestimonial />
</div>
<div className="mt-32 lg:mt-56">
<Security />
</div>
<div className="mt-32 lg:mt-56">
<SolveYourCi />
<VmwareTestimonial />
</div>
<div className="mt-32 lg:mt-56">
<DownloadEbook />
</div>
<div className="mt-32 lg:mt-56">
<EnterpriseAddons />
</div>
<div className="mt-32">
<TrustedBy />
</div>
<div className="mt-32 lg:mt-56">
<div className="mt-32 lg:mt-40">
<CallToAction />
</div>
</DefaultLayout>
Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

+3
View File
@@ -383,6 +383,9 @@ const recipesUrls = {
'/recipes/nx-console/console-project-pane': '/getting-started/editor-setup',
'/recipes/nx-console/console-add-dependency-command':
'/getting-started/editor-setup',
// This one was folded into a more holistic recipe around managing version reference updates
'/recipes/nx-release/publish-custom-dist-directory':
'/recipes/nx-release/updating-version-references#scenario-2-i-want-to-publish-from-a-custom-dist-directory-and-not-update-references-in-my-source-packagejson-files',
};
/**
+10 -7
View File
@@ -53,13 +53,16 @@ export function Marquee({
.map((_, i) => (
<div
key={i}
className={cx('flex shrink-0 justify-around [gap:var(--gap)]', {
'animate-marquee flex-row': !vertical,
'animate-marquee-vertical flex-col': vertical,
'[animation-play-state:paused]': shouldReduceMotion,
'group-hover:[animation-play-state:paused]': pauseOnHover,
'[animation-direction:reverse]': reverse,
})}
className={cx(
'flex shrink-0 items-center justify-around [gap:var(--gap)]',
{
'animate-marquee flex-row': !vertical,
'animate-marquee-vertical flex-col': vertical,
'[animation-play-state:paused]': shouldReduceMotion,
'group-hover:[animation-play-state:paused]': pauseOnHover,
'[animation-direction:reverse]': reverse,
}
)}
>
{children}
</div>
+1 -1
View File
@@ -24,7 +24,7 @@ export function DefaultLayout({
aria-hidden="true"
>
<div
className="relative left-[calc(50%-11rem)] aspect-[1155/678] w-[46.125rem] -translate-x-1/2 rotate-[35deg] bg-gradient-to-tr from-[#9333ea] to-[#3b82f6] opacity-25 sm:left-[calc(70%-30rem)] sm:w-[92.1875rem] dark:from-[#3b82f6] dark:to-[#9333ea] dark:opacity-15"
className="relative left-[calc(50%-11rem)] aspect-[1155/678] w-[46.125rem] -translate-x-1/2 rotate-[35deg] bg-gradient-to-tr from-[#9333ea] to-[#3b82f6] opacity-10 sm:left-[calc(70%-30rem)] sm:w-[92.1875rem] dark:from-[#3b82f6] dark:to-[#9333ea] dark:opacity-15"
style={{
clipPath:
'polygon(74.1% 44.1%, 100% 61.6%, 97.5% 26.9%, 95.5% 0.1%, 80.7% 2%, 72.5% 32.5%, 60.2% 62.4%, 52.4% 68.1%, 47.5% 58.3%, 45.2% 34.5%, 67.5% 76.7%, 0.1% 64.9%, 77.9% 100%, 27.6% 76.8%, 76.1% 97.7%, 84.1% 44.1%)',
+5 -15
View File
@@ -7,29 +7,19 @@ import {
} from '@heroicons/react/24/outline';
import cx from 'classnames';
import Link from 'next/link';
import { Fragment, useEffect, useState } from 'react';
import { Fragment, ReactElement, useEffect, useState } from 'react';
import { ButtonLink, ButtonLinkProps } from '../button';
import {
companyItems,
eventItems,
featuresItems,
learnItems,
ossProducts,
resourceMenuItems,
productsMenuItems,
enterpriseResourcesMenuItems,
} from './menu-items';
import { resourceMenuItems } from './menu-items';
import { MobileMenuItem } from './mobile-menu-item';
import { SectionsMenu } from './sections-menu';
import { TwoColumnsMenu } from './two-columns-menu';
import { AlgoliaSearch } from '@nx/nx-dev/feature-search';
import { GitHubIcon, NxCloudAnimatedIcon, NxIcon } from '@nx/nx-dev/ui-icons';
import { GitHubIcon, NxIcon } from '@nx/nx-dev/ui-icons';
interface HeaderProps {
ctaButtons?: ButtonLinkProps[];
}
export function Header({ ctaButtons }: HeaderProps): JSX.Element {
export function Header({ ctaButtons }: HeaderProps): ReactElement {
let [isOpen, setIsOpen] = useState(false);
// We need to close the popover if the route changes or the window is resized to prevent the popover from being stuck open.
@@ -70,7 +60,7 @@ export function Header({ ctaButtons }: HeaderProps): JSX.Element {
}}
/>
{/*DESKTOP*/}
<div className="mx-auto mt-2 hidden w-full max-w-7xl items-center justify-between space-x-10 rounded-xl border border-slate-200/40 bg-white/70 px-4 py-2 shadow-lg backdrop-blur-xl backdrop-saturate-150 lg:flex dark:border-slate-800/60 dark:bg-slate-950/40">
<div className="mx-auto mt-2 hidden w-full max-w-7xl items-center justify-between space-x-10 rounded-xl border border-slate-200/40 bg-white/70 px-4 py-2 shadow-sm backdrop-blur-xl backdrop-saturate-150 lg:flex dark:border-slate-800/60 dark:bg-slate-950/40">
{/*PRIMARY NAVIGATION*/}
<div className="flex flex-shrink-0 text-sm">
{/*LOGO*/}
@@ -1,7 +1,19 @@
import { SectionHeading } from '@nx/nx-dev/ui-common';
import { HubspotForm } from './hubspot-form';
import { ReactElement } from 'react';
import {
CapitalOneIcon,
CaterpillarIcon,
ManIcon,
RedwoodJsIcon,
RoyalBankOfCanadaIcon,
ShopifyIcon,
SiriusxmAlternateIcon,
StorybookIcon,
VmwareIcon,
} from '@nx/nx-dev/ui-icons';
export function TalkToOurEngineeringTeam(): JSX.Element {
export function TalkToOurEngineeringTeam(): ReactElement {
return (
<section id="contact-sales">
<div className="mx-auto max-w-7xl px-6 lg:px-8">
@@ -26,116 +38,84 @@ export function TalkToOurEngineeringTeam(): JSX.Element {
Fill out the form, and well get back to you to schedule a call or
start the discussion on a shared Slack.
</p>
<div className="mx-auto mt-6 grid w-full grid-cols-4 gap-2 md:grid-cols-2 lg:mt-12">
<div className="col-span-1 flex h-14 items-center justify-center lg:h-28">
<svg
<figure className="mt-12 border-l border-slate-200 pl-8 dark:border-slate-800">
<blockquote className="text-base/7">
<p>
“The decision to jump to Nx Cloud was really something we
wanted from the beginning. There's nothing but benefits from
it. Nx means tooling and efficiency around our software
development lifecycle that empowers us to move a lot faster,
ship code faster and more reliably.
</p>
</blockquote>
<figcaption className="mt-6 flex items-center gap-x-4 text-sm/6">
<img
alt="Justin Schwartzenberger"
src="https://avatars.githubusercontent.com/u/1243236?v=4"
className="size-8 flex-none rounded-full"
/>
<div>
<div className="font-semibold">Justin Schwartzenberger</div>
<div className="text-slate-500">
Principal Software Engineer, SiriusXM
</div>
</div>
<SiriusxmAlternateIcon
aria-hidden="true"
role="img"
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
viewBox="0 0 24 24"
className="h-14 w-14 text-black dark:text-white"
>
<title>Royal Bank of Canada</title>
<path d="M3 0v21.047c0 .805.318 1.235.953 1.504C6.388 23.517 9.247 24 12 24c2.753 0 5.612-.483 8.047-1.45.635-.268.953-.698.953-1.503V0H3zm3.793 1.092h1.361v.914h5.233v1.4c0 .216 0 .537-.053.752 0 .162-.053.379-.314.594-.157.108-.417.16-.784.16-.314 0-.629.001-.943-.053-.576-.053-.785.215-.785.43v.162c.261-.215.68-.376 1.1-.215.784.27.993.269 1.412-.054l.052.107a.622.622 0 0 0-.047.057c-.043.057-.102.135-.214.213h.367V7.71l1.203-.752h3.35l1.62.861c.21.108.263.269.315.43 0 .161 0 .377-.105.484-.053.108-.418.754-.627 1.077.627.7.941 1.561.941 2.476s-.315 2.045-1.361 2.906c.418.108.732.377.994.754l-.26 1.184h-.158c0-.969-.837-1.184-1.727-1.184H14.59c.105.485.105 1.024.053 1.508h-.157c-.261-2.1-1.413-3.176-3.035-4.307v.97h-.158c-.419-1.024-1.359-1.939-2.3-2.585l-.735-.537v1.022h-.156c-.367-1.023-1.1-1.83-2.041-2.745-1.047-.968-1.518-1.562-1.727-2.261a3.143 3.143 0 0 1-.209-1.184c0-.807.365-1.56.889-1.83v.377c-.262.323-.366.753-.366 1.184 0 .376.105.861.262 1.238.628 1.184 2.616 2.637 3.924 3.498l.531.361c3.57 2.43 4.314 2.939 4.963 4.377.016.05.036.102.059.16.052.136.114.297.15.485.576-.108 2.67-.43 3.088-.485a.618.618 0 0 0 .107-.02c.066-.014.146-.032.207-.032.367-.216.628-.54.89-.916l-1.518-.108c-.037.113-.099.226-.15.32-.023.041-.044.077-.06.11h-.103c.026-.08.038-.162.05-.242.014-.081.027-.162.054-.242l-.418-.053v-.108l.418-.054c.104-.43.105-.862.105-1.239l-1.937-.107v.592h-.104c-.026-.108-.04-.201-.053-.295a2.869 2.869 0 0 0-.052-.297h-.418v-.107h.418a13.494 13.494 0 0 1-.053-1.454l-1.15-.054.156.324c.261.7.21 1.506.105 1.883h-.156c0-.43-.158-.968-.315-1.291-.314-.754-.993-1.454-2.197-2.207v.968h-.158c-.366-1.291-1.36-1.99-2.824-2.744-1.204-.646-1.57-1.4-1.518-2.584l.418.268c.105 1.076.942 1.723 1.78 1.723a6.44 6.44 0 0 0 1.464-.16c.68-.162 1.256-.27 1.78-.055v-.647h-.418c-.157.108-.42.27-.838.27-.471 0-1.362-.269-1.362-1.291 0-.915.733-1.024 1.309-1.024h1.047a.437.437 0 0 0 .254-.074l.06-.033a.41.41 0 0 0 .104-.215v-1.13H9.775v1.13L8.73 3.566v-.27h.522v-.429h-1.83v-.914h-.629c-.366 0-.733.215-.785.7 0 .484.158.86.629.86h.105v.538h-.105c-.785 0-1.412-.592-1.412-1.453 0-.862.678-1.506 1.568-1.506zm4.584 4.521-.012.002c-.214.013-.38.098-.543.266.157.054.314.107.471.107.262-.054.419-.214.523-.322a1.685 1.685 0 0 0-.427-.053h-.012zm.545 2.098c-.312 0-.739.094-1.098.174-.183.04-.349.077-.472.095.05.035.15.098.275.176.26.164.627.396.875.578l1.309-.806c-.262-.162-.575-.217-.89-.217zm2.826 0L12.34 9.166c.47.323.838.645 1.152 1.021.628-.968 1.778-1.56 2.877-1.56.262 0 .68.054 1.047.162l.209-.27-.262-.376.053-.108.785.43-.158.539c.105.054.263.162.42.27l.523-.862-1.308-.701h-2.93zM4.439 8.789l.21.16a4.281 4.281 0 0 0-.106.809c.052 1.076.89 1.775 3.35 3.336 2.616 1.668 3.347 2.692 3.609 3.984h-.156c-.576-1.292-1.727-1.992-3.035-2.746v1.023h-.157c-.366-.915-1.046-1.775-1.988-2.529-.89-.646-1.257-1.024-1.623-1.562a2.585 2.585 0 0 1-.418-1.399c0-.377.105-.753.314-1.076zm11.512.537c-.157.377-.262.808-.367 1.238l1.361-.052a5.594 5.594 0 0 0-.47-1.186h-.524zm1.412.215c.105.323.21.647.262.916l.941-.055c-.314-.376-.732-.646-1.203-.861zm-2.25.11c-.523.269-.89.591-1.256 1.076l1.1-.055c.052-.538.104-.86.156-1.022zm.367 1.29c-.093.432-.146.863-.193 1.256l-.017.145 1.937-.11c0-.43-.053-.805-.158-1.236l-1.569-.055zm2.25.108c.053.43.051.807.051 1.183l1.57-.107a3.306 3.306 0 0 0-.314-1.021l-1.307-.055zm-.052 1.668c-.053.43-.158.809-.315 1.185l1.727-.214c.105-.27.209-.593.209-.862l-1.621-.11zM5.25 17.455h2.4c1.1 0 1.55.351 1.55 1.104 0 .652-.5 1.001-1.1 1.152l1.55 2.008h-1.2l-1.4-1.907H6.7v1.907h-1v-3.31c0-.352-.05-.604-.2-.755-.05-.05-.15-.1-.25-.15v-.049zm4.85 0h2.25c1.35 0 1.55.502 1.55.953 0 .502-.4.852-.85.903.6.05 1.15.35 1.1 1.103 0 .903-.7 1.305-1.75 1.305h-1.85v-3.412c0-.301-.05-.502-.2-.653-.05-.05-.2-.15-.25-.15v-.049zm7.35 0c.9 0 1.3.25 1.3.25l-.2.703h-.05s-.1-.2-.35-.351c-.15-.1-.35-.2-.75-.1-.75 0-1.3.401-1.3 1.705 0 1.405.9 1.705 1.55 1.705.75 0 1.1-.351 1.1-.351v.502s-.4.3-1.4.3c-1.05 0-2.3-.45-2.3-2.156 0-1.505 1-2.207 2.4-2.207zm-10.7.4v1.606h.5c.6 0 .95-.352.95-.854 0-.601-.3-.752-.85-.752h-.6zm4.75 0v1.305h.45c.5 0 .95-.151.95-.703 0-.502-.3-.602-.8-.602h-.6zm.05 1.706v1.755h.6c.65 0 1.05-.3 1.1-.902 0-.602-.3-.853-1.1-.853h-.6z" />
</svg>
className="ml-auto size-10 text-[#0000EB]"
/>
</figcaption>
</figure>
<div className="mx-auto mt-12 grid w-full grid-cols-4 gap-2 md:grid-cols-2 lg:mt-12">
<div className="col-span-1 flex h-14 items-center justify-center lg:h-28">
<RoyalBankOfCanadaIcon
aria-hidden="true"
className="size-14 text-black dark:text-white"
/>
</div>
<div className="col-span-1 flex h-14 items-center justify-center lg:h-28">
<svg
<ManIcon
aria-hidden="true"
role="img"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
className="h-14 w-14 text-[#E40045]"
>
<title>MAN</title>
<path d="M10.979 14.943h2.05L15.46 18.7h-2.054l-.263-.409h-2.278l-.264.41H8.548zm1.025 1.568l-.458.711h.916l-.458-.712zM0 17.372C0 10.704 5.372 5.3 12 5.3s12 5.405 12 12.073c0 .449-.024.892-.072 1.328H22.58c.054-.435.082-.878.082-1.328 0-5.924-4.774-10.726-10.662-10.726-5.889 0-10.661 4.802-10.661 10.726 0 .45.027.893.08 1.328H.073A12.254 12.274 0 0 1 0 17.372zm2.237-2.43h1.83l1.22 1.228 1.22-1.227h1.831V18.7H6.363v-1.38l-1.075 1.082-1.076-1.082v1.38H2.237v-3.757zm13.42 0h1.927l2.17 1.62v-1.62h1.975V18.7h-1.942l-2.156-1.605V18.7h-1.975Z" />
</svg>
className="size-14 text-[#E40045]"
/>
</div>
<div className="col-span-1 flex h-14 items-center justify-center lg:h-28">
<svg
<ShopifyIcon
aria-hidden="true"
role="img"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
className="h-12 w-12 text-[#7AB55C]"
>
<title>Shopify</title>
<path d="M15.337 23.979l7.216-1.561s-2.604-17.613-2.625-17.73c-.018-.116-.114-.192-.211-.192s-1.929-.136-1.929-.136-1.275-1.274-1.439-1.411c-.045-.037-.075-.057-.121-.074l-.914 21.104h.023zM11.71 11.305s-.81-.424-1.774-.424c-1.447 0-1.504.906-1.504 1.141 0 1.232 3.24 1.715 3.24 4.629 0 2.295-1.44 3.76-3.406 3.76-2.354 0-3.54-1.465-3.54-1.465l.646-2.086s1.245 1.066 2.28 1.066c.675 0 .975-.545.975-.932 0-1.619-2.654-1.694-2.654-4.359-.034-2.237 1.571-4.416 4.827-4.416 1.257 0 1.875.361 1.875.361l-.945 2.715-.02.01zM11.17.83c.136 0 .271.038.405.135-.984.465-2.064 1.639-2.508 3.992-.656.213-1.293.405-1.889.578C7.697 3.75 8.951.84 11.17.84V.83zm1.235 2.949v.135c-.754.232-1.583.484-2.394.736.466-1.777 1.333-2.645 2.085-2.971.193.501.309 1.176.309 2.1zm.539-2.234c.694.074 1.141.867 1.429 1.755-.349.114-.735.231-1.158.366v-.252c0-.752-.096-1.371-.271-1.871v.002zm2.992 1.289c-.02 0-.06.021-.078.021s-.289.075-.714.21c-.423-1.233-1.176-2.37-2.508-2.37h-.115C12.135.209 11.669 0 11.265 0 8.159 0 6.675 3.877 6.21 5.846c-1.194.365-2.063.636-2.16.674-.675.213-.694.232-.772.87-.075.462-1.83 14.063-1.83 14.063L15.009 24l.927-21.166z" />
</svg>
className="size-12 text-[#7AB55C]"
/>
</div>
<div className="col-span-1 flex h-14 items-center justify-center lg:h-28">
<svg
<CapitalOneIcon
aria-hidden="true"
role="img"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
className="h-28 w-28 text-black dark:text-white"
>
<title>Capital One</title>
<path
fillRule="evenodd"
d="M15.037 14.072c-.798.567-1.741 1.16-2.755 1.777l-.04.025a.027.027 0 0 0-.005.04c.01.016.03.016.04.006l.035-.02c.858-.447 1.847-.984 2.84-1.531l.016-.01a1.414 1.414 0 0 1-.13-.287Zm5.225-4.23c-1.255-1.386-9.325-.142-12.758.611l-.08.016a.038.038 0 0 0-.026.035c.006.015.02.025.036.025l.08-.015c2.845-.507 8.758-1.21 10.033.055.386.387.296.883-.156 1.466.24.155.417.402.492.707 1.757-1.17 2.941-2.283 2.379-2.9Z"
clipRule="evenodd"
/>
<path d="M15.55 13.706c.015.487.326.883.728.883.778 0 1.109-.954 1.089-1.596-.015-.487-.331-.888-.733-.888-.678.01-1.11.958-1.084 1.6Zm-.411-.025c-.03-.834.622-1.682 1.566-1.682.632 0 1.044.422 1.069 1.07.03.877-.578 1.68-1.566 1.68-.633 0-1.05-.42-1.07-1.069Zm-8.96-.221a.6.6 0 0 1-.13.035c-.055.01-.15.03-.296.05a.47.47 0 0 0-.206.065c-.035.03-.06.06-.065.1-.01.045.005.076.045.106.04.025.1.04.19.04.066 0 .131-.01.197-.03.065-.02.12-.05.16-.085.03-.025.05-.06.07-.1.005-.026.02-.086.035-.181Zm.824-.507a2.388 2.388 0 0 1-.035.26l-.141.784c-.01.07.015.125.08.16l-.005.03h-.798l-.005-.2a1.59 1.59 0 0 1-.382.165 1.47 1.47 0 0 1-.381.045c-.256 0-.407-.025-.507-.115-.105-.09-.156-.15-.15-.281a.44.44 0 0 1 .095-.231.567.567 0 0 1 .211-.146c.08-.04.18-.065.321-.09a6.58 6.58 0 0 1 .598-.065c.135-.01.22-.045.26-.06.056-.02.076-.046.086-.091.015-.08-.01-.126-.115-.146-.286-.055-.823.035-1.104.106l.15-.407a7.014 7.014 0 0 1 1.07-.085c.57-.01.757.156.752.367Zm2.308 1.23.276-1.562h.763l-.276 1.561h-.763Zm.317-1.923c.025-.13.226-.23.451-.23.226 0 .392.105.372.23-.025.13-.226.231-.452.231-.23.005-.391-.1-.371-.23Zm3.427 1.205c-.035.01-.08.025-.13.035-.055.015-.15.03-.296.05a.47.47 0 0 0-.206.065.149.149 0 0 0-.065.1c-.01.046.01.08.045.106.04.025.1.04.19.04.066 0 .131-.01.197-.03.065-.02.12-.05.16-.085.03-.025.05-.06.07-.1.005-.026.02-.086.035-.181Zm.824-.512a1.652 1.652 0 0 1-.04.26l-.141.784c-.01.07.015.125.075.16l-.005.026h-.798l-.005-.2c-.115.07-.256.13-.381.165a1.307 1.307 0 0 1-.382.045c-.256 0-.401-.026-.507-.116-.105-.09-.155-.15-.15-.28a.425.425 0 0 1 .095-.232.635.635 0 0 1 .211-.15c.08-.04.181-.065.316-.09.141-.02.337-.046.603-.066a.9.9 0 0 0 .26-.055c.056-.02.076-.045.086-.09.015-.08-.01-.126-.115-.146-.287-.056-.824.035-1.104.105l.15-.407c.366-.055.708-.09 1.069-.09.582.006.768.166.763.377Zm-9.305.668a2.73 2.73 0 0 1-.633.07c-.366 0-.652-.181-.637-.512.01-.221.27-.688.938-.688.211 0 .372.035.593.161l.095-.542a1.763 1.763 0 0 0-.753-.126c-.873.01-1.706.407-1.812 1.21-.105.818.894 1.023 1.335 1.023.256 0 .522-.01.773-.035l.1-.562Zm6.193-1.004.055-.317.798-.175-.085.492h.391l-.075.316h-.377l-.23 1.255s-.769-.005-.773 0l.23-1.25h-.29l.06-.316h.296v-.005Zm3.885 1.565h-.778l.391-2.173.758.02-.371 2.153Zm-6.35-1.194c-.1 0-.19.03-.27.09-.08.06-.13.15-.15.267-.026.135-.01.235.044.296.056.06.13.09.236.09.07 0 .14-.01.196-.04a.38.38 0 0 0 .15-.13.54.54 0 0 0 .086-.206c.02-.126.005-.216-.056-.276a.322.322 0 0 0-.235-.091Zm-1.39 1.731.372-2.088h.668l-.05.267c.06-.08.15-.146.28-.201.13-.055.277-.09.432-.09.176 0 .281.01.412.075.13.07.22.165.27.296a.8.8 0 0 1 .036.427.934.934 0 0 1-.377.612c-.205.15-.386.19-.652.19-.096 0-.17-.01-.236-.025a.421.421 0 0 1-.15-.06.823.823 0 0 1-.126-.115l-.125.718h-.753v-.006Zm13.843-1.54c-.005-.126-.065-.211-.196-.211-.311 0-.648.612-.703.858.512.005.909-.301.899-.647Zm.065.963.06.04c-.15.312-.461.563-.828.563-.296 0-.542-.2-.557-.572-.025-.668.602-1.29 1.114-1.29.221 0 .422.096.432.331.02.532-.728.693-1.22.703a.714.714 0 0 0-.02.196c.01.22.14.407.417.407.246-.001.481-.192.602-.377Zm-3.057-.978c.056-.005.14-.015.176-.015.055 0 .11.01.11.065 0 .035-.06.27-.07.326l-.12.522c-.05.216-.1.442-.146.617h.306l.17-.833c.523-.547.728-.723.849-.723.055 0 .095.03.095.096.006.096-.055.316-.075.376l-.176.607c-.04.136-.07.261-.065.347.005.135.085.19.206.19.22 0 .386-.23.527-.461l-.04-.075c-.056.09-.191.29-.296.29-.036 0-.066-.02-.066-.075-.005-.07.026-.176.045-.246l.196-.708c.05-.195.076-.32.076-.386-.005-.13-.08-.196-.2-.196-.201 0-.468.146-1.035.798h-.01l.07-.316c.041-.176.076-.356.116-.482-.196.07-.477.15-.647.181l.004.1Zm3.328-.567a.27.27 0 0 1 .276-.271c.146 0 .276.115.276.27a.27.27 0 0 1-.276.271.265.265 0 0 1-.276-.27Zm.276.23a.22.22 0 0 0 .22-.226.218.218 0 0 0-.22-.226.218.218 0 0 0-.22.226c0 .131.095.226.22.226Zm-.055-.07h-.05v-.311h.12c.076 0 .11.025.11.09 0 .056-.034.08-.08.085l.091.136h-.055l-.08-.136h-.056v.136Zm.055-.175c.04 0 .076-.006.076-.05 0-.04-.036-.046-.07-.046h-.066v.096h.06Z" />
</svg>
className="size-28 text-black dark:text-white"
/>
</div>
<div className="col-span-1 flex h-14 items-center justify-center lg:h-28">
<svg
<VmwareIcon
aria-hidden="true"
role="img"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
className="h-28 w-28 text-black dark:text-white"
>
<title>VMware</title>
<path d="M.5 10.1a.505.505 0 00-.197.048.497.497 0 00-.25.68l1.138 2.475c.179.38.38.592.721.592.342 0 .542-.22.72-.592l1.003-2.186a.144.144 0 01.144-.092.16.16 0 01.157.16v2.118a.535.535 0 101.066 0v-1.73a.531.531 0 01.566-.552.52.52 0 01.541.551v1.73a.531.531 0 00.53.593.539.539 0 00.535-.592v-1.73a.531.531 0 01.564-.552.52.52 0 01.543.551v1.73a.531.531 0 00.528.593.535.535 0 00.535-.592v-1.969a1.234 1.234 0 00-1.283-1.23 1.647 1.647 0 00-1.14.486 1.26 1.26 0 00-1.095-.483 1.807 1.807 0 00-1.074.483 1.287 1.287 0 00-.961-.483 1.177 1.177 0 00-1.158.786l-.729 1.716-.933-2.203.011-.004A.505.505 0 00.5 10.1zm18.43.06a.27.27 0 00-.266.274h.002v3.142a.27.27 0 10.535 0v-1.222c0-1.037.571-1.56 1.27-1.643a.266.266 0 00.238-.274.258.258 0 00-.266-.269 1.465 1.465 0 00-1.242.88v-.614a.266.266 0 00-.271-.274zm-6.735.008a.273.273 0 00-.25.217l-.912 2.627-.902-2.62a.28.28 0 00-.274-.22.266.266 0 00-.27.258.493.493 0 00.034.144l1.09 3.037.02-.007a.319.319 0 00.298.242.3.3 0 00.293-.242l.903-2.583.896 2.583a.3.3 0 00.293.242h.018a.319.319 0 00.293-.242l1.097-3.038a.512.512 0 00.033-.144.258.258 0 00-.265-.25.262.262 0 00-.258.209l-.918 2.63-.904-2.626a.285.285 0 00-.278-.217h-.025a.273.273 0 00-.012 0zm10.168.008a1.75 1.75 0 00-1.691 1.851 1.765 1.765 0 001.76 1.858l-.008.013a1.784 1.784 0 001.33-.539.228.228 0 00.082-.17.228.228 0 00-.379-.168 1.435 1.435 0 01-1.018.415 1.237 1.237 0 01-1.24-1.207h2.555a.247.247 0 00.246-.247c0-.945-.593-1.806-1.637-1.806zm-5.744.002a1.571 1.571 0 00-.158.006 2.384 2.384 0 00-1.078.205.22.22 0 00-.143.222.24.24 0 00.235.229.266.266 0 00.095-.024 1.822 1.822 0 01.834-.162c.691 0 1.07.334 1.07.979v.125a3.796 3.796 0 00-1.103-.15c-.892 0-1.52.4-1.52 1.16l-.003-.004c0 .736.671 1.117 1.34 1.117a1.575 1.575 0 001.298-.62v.343a.247.247 0 00.254.25.254.254 0 00.258-.262v-1.983a1.416 1.416 0 00-.379-1.046 1.571 1.571 0 00-1-.385zm5.719.43c.714 0 1.085.565 1.139 1.214h-2.278a1.222 1.222 0 011.139-1.215zm-5.885 1.382a3.75 3.75 0 011.057.153V12.49c0 .57-.539.973-1.2.973-.485 0-.904-.261-.904-.713 0-.467.375-.76 1.047-.76Z" />
</svg>
className="size-28 text-black dark:text-white"
/>
</div>
<div className="col-span-1 flex h-14 items-center justify-center lg:h-28">
<svg
<StorybookIcon
aria-hidden="true"
role="img"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
className="h-12 w-12 text-[#FF4785]"
>
<title>Storybook</title>
<path d="M16.71.243l-.12 2.71a.18.18 0 00.29.15l1.06-.8.9.7a.18.18 0 00.28-.14l-.1-2.76 1.33-.1a1.2 1.2 0 011.279 1.2v21.596a1.2 1.2 0 01-1.26 1.2l-16.096-.72a1.2 1.2 0 01-1.15-1.16l-.75-19.797a1.2 1.2 0 011.13-1.27L16.7.222zM13.64 9.3c0 .47 3.16.24 3.59-.08 0-3.2-1.72-4.89-4.859-4.89-3.15 0-4.899 1.72-4.899 4.29 0 4.45 5.999 4.53 5.999 6.959 0 .7-.32 1.1-1.05 1.1-.96 0-1.35-.49-1.3-2.16 0-.36-3.649-.48-3.769 0-.27 4.03 2.23 5.2 5.099 5.2 2.79 0 4.969-1.49 4.969-4.18 0-4.77-6.099-4.64-6.099-6.999 0-.97.72-1.1 1.13-1.1.45 0 1.25.07 1.19 1.87z" />
</svg>
className="size-12 text-[#FF4785]"
/>
</div>
<div className="col-span-1 flex h-14 items-center justify-center lg:h-28">
<svg
<RedwoodJsIcon
aria-hidden="true"
role="img"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
className="h-12 w-12 text-[#BF4722]"
>
<title>RedwoodJS</title>
<path d="M6.989 3.47l4.66 3.181c.105.07.228.108.354.111a.634.634 0 0 0 .354-.111l4.664-3.192a.637.637 0 0 0-.089-1.087L12.272.065a.64.64 0 0 0-.56 0L7.067 2.372a.636.636 0 0 0-.078 1.098zm6.597 4.179c0 .211.104.408.276.528l3.736 2.553a.628.628 0 0 0 .776-.05l3.134-2.803a.637.637 0 0 0-.028-.973l-2.992-2.393a.635.635 0 0 0-.751-.029l-3.874 2.65a.644.644 0 0 0-.277.517zm-9.291 3.474a.64.64 0 0 1 .209.538.625.625 0 0 1-.315.485l-2.231 1.337a.63.63 0 0 1-.718-.049.64.64 0 0 1-.21-.693l.825-2.596a.63.63 0 0 1 1.023-.281l1.417 1.259zm12.1.271l-4.033-2.76a.634.634 0 0 0-.708 0l-4.033 2.76a.646.646 0 0 0-.276.485.664.664 0 0 0 .212.521l4.03 3.605a.635.635 0 0 0 .842 0l4.03-3.605a.647.647 0 0 0-.064-1.006zm-10.758-.713l-3.13-2.803a.648.648 0 0 1-.213-.503.626.626 0 0 1 .237-.481l2.992-2.407a.641.641 0 0 1 .754-.029l3.87 2.65a.639.639 0 0 1 0 1.07l-3.732 2.553a.633.633 0 0 1-.778-.05zm16.073 4.026l-3.187-1.908a.627.627 0 0 0-.744.071l-3.895 3.477a.644.644 0 0 0-.204.587.636.636 0 0 0 .388.483l5.404 2.19a.634.634 0 0 0 .815-.332l1.675-3.752a.64.64 0 0 0-.252-.816zm.442-4.561l.825 2.596h-.007a.635.635 0 0 1-.927.742l-2.234-1.337a.623.623 0 0 1-.305-.485.631.631 0 0 1 .209-.538l1.416-1.262a.63.63 0 0 1 1.023.284zm-11.82 6.786a.637.637 0 0 0-.202-.585L6.235 12.87a.627.627 0 0 0-.744-.071l-3.187 1.908a.648.648 0 0 0-.255.813l1.678 3.752a.632.632 0 0 0 .814.332l5.4-2.19a.637.637 0 0 0 .391-.482zm1.912 1.07l4.334 1.755c.212.091.358.29.382.521a.635.635 0 0 1-.269.596l-4.338 3.013A.625.625 0 0 1 12 24a.64.64 0 0 1-.354-.114l-4.334-3.013a.635.635 0 0 1 .124-1.117l4.334-1.755a.642.642 0 0 1 .474.001z" />
</svg>
className="size-12 text-[#BF4722]"
/>
</div>
<div className="col-span-1 flex h-14 items-center justify-center lg:h-28">
<svg
<CaterpillarIcon
aria-hidden="true"
role="img"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
className="h-14 w-14 text-[#FFCD11]"
>
<title>Caterpillar</title>
<path d="M11.901 11.554l.802-4.1.798 4.1zm2.869-6.52h-4.15L8.2 15.884l4.503-3.635 4.695 3.934zm-2.067 8.156l-7.509 6.072H19.95zM24 5.02v2.77h-2.066v11.45h-.882l-2.436-2.04V7.79h-2.057V5.02zM6.872 16.864c.548-.458.642-1.024.642-1.532V13.2h-2.98v2.894a.75.75 0 0 1-.748.751c-.414 0-.722-.336-.722-.75V7.893c0-.414.308-.75.722-.75a.75.75 0 0 1 .749.75v2.913H7.51V7.785c0-1.67-1.092-3.044-3.75-3.047-2.728 0-3.76 1.38-3.76 3.05v8.563c0 1.655 1.314 2.907 2.995 2.907h.922Z" />
</svg>
className="size-14 text-[#FFCD11]"
/>
</div>
</div>
</section>
@@ -1,7 +1,19 @@
import { SectionHeading } from '@nx/nx-dev/ui-common';
import { HubspotForm } from './hubspot-form';
import { ReactElement } from 'react';
import {
CapitalOneIcon,
CaterpillarIcon,
ManIcon,
RedwoodJsIcon,
RoyalBankOfCanadaIcon,
ShopifyIcon,
SiriusxmAlternateIcon,
StorybookIcon,
VmwareIcon,
} from '@nx/nx-dev/ui-icons';
export function TalkToOurSalesTeam(): JSX.Element {
export function TalkToOurSalesTeam(): ReactElement {
return (
<section id="contact-sales">
<div className="mx-auto max-w-7xl px-6 lg:px-8">
@@ -25,116 +37,84 @@ export function TalkToOurSalesTeam(): JSX.Element {
Fill out the form, and well get back to you to schedule a call or
start the discussion on a shared Slack.
</p>
<div className="mx-auto mt-6 grid w-full grid-cols-4 gap-2 md:grid-cols-2 lg:mt-12">
<div className="col-span-1 flex h-14 items-center justify-center lg:h-28">
<svg
<figure className="mt-12 border-l border-slate-200 pl-8 dark:border-slate-800">
<blockquote className="text-base/7">
<p>
The decision to jump to Nx Cloud was really something we
wanted from the beginning. There's nothing but benefits from
it. Nx means tooling and efficiency around our software
development lifecycle that empowers us to move a lot faster,
ship code faster and more reliably.
</p>
</blockquote>
<figcaption className="mt-6 flex items-center gap-x-4 text-sm/6">
<img
alt="Justin Schwartzenberger"
src="https://avatars.githubusercontent.com/u/1243236?v=4"
className="size-8 flex-none rounded-full"
/>
<div>
<div className="font-semibold">Justin Schwartzenberger</div>
<div className="text-slate-500">
Principal Software Engineer, SiriusXM
</div>
</div>
<SiriusxmAlternateIcon
aria-hidden="true"
role="img"
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
viewBox="0 0 24 24"
className="h-14 w-14 text-black dark:text-white"
>
<title>Royal Bank of Canada</title>
<path d="M3 0v21.047c0 .805.318 1.235.953 1.504C6.388 23.517 9.247 24 12 24c2.753 0 5.612-.483 8.047-1.45.635-.268.953-.698.953-1.503V0H3zm3.793 1.092h1.361v.914h5.233v1.4c0 .216 0 .537-.053.752 0 .162-.053.379-.314.594-.157.108-.417.16-.784.16-.314 0-.629.001-.943-.053-.576-.053-.785.215-.785.43v.162c.261-.215.68-.376 1.1-.215.784.27.993.269 1.412-.054l.052.107a.622.622 0 0 0-.047.057c-.043.057-.102.135-.214.213h.367V7.71l1.203-.752h3.35l1.62.861c.21.108.263.269.315.43 0 .161 0 .377-.105.484-.053.108-.418.754-.627 1.077.627.7.941 1.561.941 2.476s-.315 2.045-1.361 2.906c.418.108.732.377.994.754l-.26 1.184h-.158c0-.969-.837-1.184-1.727-1.184H14.59c.105.485.105 1.024.053 1.508h-.157c-.261-2.1-1.413-3.176-3.035-4.307v.97h-.158c-.419-1.024-1.359-1.939-2.3-2.585l-.735-.537v1.022h-.156c-.367-1.023-1.1-1.83-2.041-2.745-1.047-.968-1.518-1.562-1.727-2.261a3.143 3.143 0 0 1-.209-1.184c0-.807.365-1.56.889-1.83v.377c-.262.323-.366.753-.366 1.184 0 .376.105.861.262 1.238.628 1.184 2.616 2.637 3.924 3.498l.531.361c3.57 2.43 4.314 2.939 4.963 4.377.016.05.036.102.059.16.052.136.114.297.15.485.576-.108 2.67-.43 3.088-.485a.618.618 0 0 0 .107-.02c.066-.014.146-.032.207-.032.367-.216.628-.54.89-.916l-1.518-.108c-.037.113-.099.226-.15.32-.023.041-.044.077-.06.11h-.103c.026-.08.038-.162.05-.242.014-.081.027-.162.054-.242l-.418-.053v-.108l.418-.054c.104-.43.105-.862.105-1.239l-1.937-.107v.592h-.104c-.026-.108-.04-.201-.053-.295a2.869 2.869 0 0 0-.052-.297h-.418v-.107h.418a13.494 13.494 0 0 1-.053-1.454l-1.15-.054.156.324c.261.7.21 1.506.105 1.883h-.156c0-.43-.158-.968-.315-1.291-.314-.754-.993-1.454-2.197-2.207v.968h-.158c-.366-1.291-1.36-1.99-2.824-2.744-1.204-.646-1.57-1.4-1.518-2.584l.418.268c.105 1.076.942 1.723 1.78 1.723a6.44 6.44 0 0 0 1.464-.16c.68-.162 1.256-.27 1.78-.055v-.647h-.418c-.157.108-.42.27-.838.27-.471 0-1.362-.269-1.362-1.291 0-.915.733-1.024 1.309-1.024h1.047a.437.437 0 0 0 .254-.074l.06-.033a.41.41 0 0 0 .104-.215v-1.13H9.775v1.13L8.73 3.566v-.27h.522v-.429h-1.83v-.914h-.629c-.366 0-.733.215-.785.7 0 .484.158.86.629.86h.105v.538h-.105c-.785 0-1.412-.592-1.412-1.453 0-.862.678-1.506 1.568-1.506zm4.584 4.521-.012.002c-.214.013-.38.098-.543.266.157.054.314.107.471.107.262-.054.419-.214.523-.322a1.685 1.685 0 0 0-.427-.053h-.012zm.545 2.098c-.312 0-.739.094-1.098.174-.183.04-.349.077-.472.095.05.035.15.098.275.176.26.164.627.396.875.578l1.309-.806c-.262-.162-.575-.217-.89-.217zm2.826 0L12.34 9.166c.47.323.838.645 1.152 1.021.628-.968 1.778-1.56 2.877-1.56.262 0 .68.054 1.047.162l.209-.27-.262-.376.053-.108.785.43-.158.539c.105.054.263.162.42.27l.523-.862-1.308-.701h-2.93zM4.439 8.789l.21.16a4.281 4.281 0 0 0-.106.809c.052 1.076.89 1.775 3.35 3.336 2.616 1.668 3.347 2.692 3.609 3.984h-.156c-.576-1.292-1.727-1.992-3.035-2.746v1.023h-.157c-.366-.915-1.046-1.775-1.988-2.529-.89-.646-1.257-1.024-1.623-1.562a2.585 2.585 0 0 1-.418-1.399c0-.377.105-.753.314-1.076zm11.512.537c-.157.377-.262.808-.367 1.238l1.361-.052a5.594 5.594 0 0 0-.47-1.186h-.524zm1.412.215c.105.323.21.647.262.916l.941-.055c-.314-.376-.732-.646-1.203-.861zm-2.25.11c-.523.269-.89.591-1.256 1.076l1.1-.055c.052-.538.104-.86.156-1.022zm.367 1.29c-.093.432-.146.863-.193 1.256l-.017.145 1.937-.11c0-.43-.053-.805-.158-1.236l-1.569-.055zm2.25.108c.053.43.051.807.051 1.183l1.57-.107a3.306 3.306 0 0 0-.314-1.021l-1.307-.055zm-.052 1.668c-.053.43-.158.809-.315 1.185l1.727-.214c.105-.27.209-.593.209-.862l-1.621-.11zM5.25 17.455h2.4c1.1 0 1.55.351 1.55 1.104 0 .652-.5 1.001-1.1 1.152l1.55 2.008h-1.2l-1.4-1.907H6.7v1.907h-1v-3.31c0-.352-.05-.604-.2-.755-.05-.05-.15-.1-.25-.15v-.049zm4.85 0h2.25c1.35 0 1.55.502 1.55.953 0 .502-.4.852-.85.903.6.05 1.15.35 1.1 1.103 0 .903-.7 1.305-1.75 1.305h-1.85v-3.412c0-.301-.05-.502-.2-.653-.05-.05-.2-.15-.25-.15v-.049zm7.35 0c.9 0 1.3.25 1.3.25l-.2.703h-.05s-.1-.2-.35-.351c-.15-.1-.35-.2-.75-.1-.75 0-1.3.401-1.3 1.705 0 1.405.9 1.705 1.55 1.705.75 0 1.1-.351 1.1-.351v.502s-.4.3-1.4.3c-1.05 0-2.3-.45-2.3-2.156 0-1.505 1-2.207 2.4-2.207zm-10.7.4v1.606h.5c.6 0 .95-.352.95-.854 0-.601-.3-.752-.85-.752h-.6zm4.75 0v1.305h.45c.5 0 .95-.151.95-.703 0-.502-.3-.602-.8-.602h-.6zm.05 1.706v1.755h.6c.65 0 1.05-.3 1.1-.902 0-.602-.3-.853-1.1-.853h-.6z" />
</svg>
className="ml-auto size-10 text-[#0000EB]"
/>
</figcaption>
</figure>
<div className="mx-auto mt-12 grid w-full grid-cols-4 gap-2 md:grid-cols-2 lg:mt-12">
<div className="col-span-1 flex h-14 items-center justify-center lg:h-28">
<RoyalBankOfCanadaIcon
aria-hidden="true"
className="size-14 text-black dark:text-white"
/>
</div>
<div className="col-span-1 flex h-14 items-center justify-center lg:h-28">
<svg
<ManIcon
aria-hidden="true"
role="img"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
className="h-14 w-14 text-[#E40045]"
>
<title>MAN</title>
<path d="M10.979 14.943h2.05L15.46 18.7h-2.054l-.263-.409h-2.278l-.264.41H8.548zm1.025 1.568l-.458.711h.916l-.458-.712zM0 17.372C0 10.704 5.372 5.3 12 5.3s12 5.405 12 12.073c0 .449-.024.892-.072 1.328H22.58c.054-.435.082-.878.082-1.328 0-5.924-4.774-10.726-10.662-10.726-5.889 0-10.661 4.802-10.661 10.726 0 .45.027.893.08 1.328H.073A12.254 12.274 0 0 1 0 17.372zm2.237-2.43h1.83l1.22 1.228 1.22-1.227h1.831V18.7H6.363v-1.38l-1.075 1.082-1.076-1.082v1.38H2.237v-3.757zm13.42 0h1.927l2.17 1.62v-1.62h1.975V18.7h-1.942l-2.156-1.605V18.7h-1.975Z" />
</svg>
className="size-14 text-[#E40045]"
/>
</div>
<div className="col-span-1 flex h-14 items-center justify-center lg:h-28">
<svg
<ShopifyIcon
aria-hidden="true"
role="img"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
className="h-12 w-12 text-[#7AB55C]"
>
<title>Shopify</title>
<path d="M15.337 23.979l7.216-1.561s-2.604-17.613-2.625-17.73c-.018-.116-.114-.192-.211-.192s-1.929-.136-1.929-.136-1.275-1.274-1.439-1.411c-.045-.037-.075-.057-.121-.074l-.914 21.104h.023zM11.71 11.305s-.81-.424-1.774-.424c-1.447 0-1.504.906-1.504 1.141 0 1.232 3.24 1.715 3.24 4.629 0 2.295-1.44 3.76-3.406 3.76-2.354 0-3.54-1.465-3.54-1.465l.646-2.086s1.245 1.066 2.28 1.066c.675 0 .975-.545.975-.932 0-1.619-2.654-1.694-2.654-4.359-.034-2.237 1.571-4.416 4.827-4.416 1.257 0 1.875.361 1.875.361l-.945 2.715-.02.01zM11.17.83c.136 0 .271.038.405.135-.984.465-2.064 1.639-2.508 3.992-.656.213-1.293.405-1.889.578C7.697 3.75 8.951.84 11.17.84V.83zm1.235 2.949v.135c-.754.232-1.583.484-2.394.736.466-1.777 1.333-2.645 2.085-2.971.193.501.309 1.176.309 2.1zm.539-2.234c.694.074 1.141.867 1.429 1.755-.349.114-.735.231-1.158.366v-.252c0-.752-.096-1.371-.271-1.871v.002zm2.992 1.289c-.02 0-.06.021-.078.021s-.289.075-.714.21c-.423-1.233-1.176-2.37-2.508-2.37h-.115C12.135.209 11.669 0 11.265 0 8.159 0 6.675 3.877 6.21 5.846c-1.194.365-2.063.636-2.16.674-.675.213-.694.232-.772.87-.075.462-1.83 14.063-1.83 14.063L15.009 24l.927-21.166z" />
</svg>
className="size-12 text-[#7AB55C]"
/>
</div>
<div className="col-span-1 flex h-14 items-center justify-center lg:h-28">
<svg
<CapitalOneIcon
aria-hidden="true"
role="img"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
className="h-28 w-28 text-black dark:text-white"
>
<title>Capital One</title>
<path
fillRule="evenodd"
d="M15.037 14.072c-.798.567-1.741 1.16-2.755 1.777l-.04.025a.027.027 0 0 0-.005.04c.01.016.03.016.04.006l.035-.02c.858-.447 1.847-.984 2.84-1.531l.016-.01a1.414 1.414 0 0 1-.13-.287Zm5.225-4.23c-1.255-1.386-9.325-.142-12.758.611l-.08.016a.038.038 0 0 0-.026.035c.006.015.02.025.036.025l.08-.015c2.845-.507 8.758-1.21 10.033.055.386.387.296.883-.156 1.466.24.155.417.402.492.707 1.757-1.17 2.941-2.283 2.379-2.9Z"
clipRule="evenodd"
/>
<path d="M15.55 13.706c.015.487.326.883.728.883.778 0 1.109-.954 1.089-1.596-.015-.487-.331-.888-.733-.888-.678.01-1.11.958-1.084 1.6Zm-.411-.025c-.03-.834.622-1.682 1.566-1.682.632 0 1.044.422 1.069 1.07.03.877-.578 1.68-1.566 1.68-.633 0-1.05-.42-1.07-1.069Zm-8.96-.221a.6.6 0 0 1-.13.035c-.055.01-.15.03-.296.05a.47.47 0 0 0-.206.065c-.035.03-.06.06-.065.1-.01.045.005.076.045.106.04.025.1.04.19.04.066 0 .131-.01.197-.03.065-.02.12-.05.16-.085.03-.025.05-.06.07-.1.005-.026.02-.086.035-.181Zm.824-.507a2.388 2.388 0 0 1-.035.26l-.141.784c-.01.07.015.125.08.16l-.005.03h-.798l-.005-.2a1.59 1.59 0 0 1-.382.165 1.47 1.47 0 0 1-.381.045c-.256 0-.407-.025-.507-.115-.105-.09-.156-.15-.15-.281a.44.44 0 0 1 .095-.231.567.567 0 0 1 .211-.146c.08-.04.18-.065.321-.09a6.58 6.58 0 0 1 .598-.065c.135-.01.22-.045.26-.06.056-.02.076-.046.086-.091.015-.08-.01-.126-.115-.146-.286-.055-.823.035-1.104.106l.15-.407a7.014 7.014 0 0 1 1.07-.085c.57-.01.757.156.752.367Zm2.308 1.23.276-1.562h.763l-.276 1.561h-.763Zm.317-1.923c.025-.13.226-.23.451-.23.226 0 .392.105.372.23-.025.13-.226.231-.452.231-.23.005-.391-.1-.371-.23Zm3.427 1.205c-.035.01-.08.025-.13.035-.055.015-.15.03-.296.05a.47.47 0 0 0-.206.065.149.149 0 0 0-.065.1c-.01.046.01.08.045.106.04.025.1.04.19.04.066 0 .131-.01.197-.03.065-.02.12-.05.16-.085.03-.025.05-.06.07-.1.005-.026.02-.086.035-.181Zm.824-.512a1.652 1.652 0 0 1-.04.26l-.141.784c-.01.07.015.125.075.16l-.005.026h-.798l-.005-.2c-.115.07-.256.13-.381.165a1.307 1.307 0 0 1-.382.045c-.256 0-.401-.026-.507-.116-.105-.09-.155-.15-.15-.28a.425.425 0 0 1 .095-.232.635.635 0 0 1 .211-.15c.08-.04.181-.065.316-.09.141-.02.337-.046.603-.066a.9.9 0 0 0 .26-.055c.056-.02.076-.045.086-.09.015-.08-.01-.126-.115-.146-.287-.056-.824.035-1.104.105l.15-.407c.366-.055.708-.09 1.069-.09.582.006.768.166.763.377Zm-9.305.668a2.73 2.73 0 0 1-.633.07c-.366 0-.652-.181-.637-.512.01-.221.27-.688.938-.688.211 0 .372.035.593.161l.095-.542a1.763 1.763 0 0 0-.753-.126c-.873.01-1.706.407-1.812 1.21-.105.818.894 1.023 1.335 1.023.256 0 .522-.01.773-.035l.1-.562Zm6.193-1.004.055-.317.798-.175-.085.492h.391l-.075.316h-.377l-.23 1.255s-.769-.005-.773 0l.23-1.25h-.29l.06-.316h.296v-.005Zm3.885 1.565h-.778l.391-2.173.758.02-.371 2.153Zm-6.35-1.194c-.1 0-.19.03-.27.09-.08.06-.13.15-.15.267-.026.135-.01.235.044.296.056.06.13.09.236.09.07 0 .14-.01.196-.04a.38.38 0 0 0 .15-.13.54.54 0 0 0 .086-.206c.02-.126.005-.216-.056-.276a.322.322 0 0 0-.235-.091Zm-1.39 1.731.372-2.088h.668l-.05.267c.06-.08.15-.146.28-.201.13-.055.277-.09.432-.09.176 0 .281.01.412.075.13.07.22.165.27.296a.8.8 0 0 1 .036.427.934.934 0 0 1-.377.612c-.205.15-.386.19-.652.19-.096 0-.17-.01-.236-.025a.421.421 0 0 1-.15-.06.823.823 0 0 1-.126-.115l-.125.718h-.753v-.006Zm13.843-1.54c-.005-.126-.065-.211-.196-.211-.311 0-.648.612-.703.858.512.005.909-.301.899-.647Zm.065.963.06.04c-.15.312-.461.563-.828.563-.296 0-.542-.2-.557-.572-.025-.668.602-1.29 1.114-1.29.221 0 .422.096.432.331.02.532-.728.693-1.22.703a.714.714 0 0 0-.02.196c.01.22.14.407.417.407.246-.001.481-.192.602-.377Zm-3.057-.978c.056-.005.14-.015.176-.015.055 0 .11.01.11.065 0 .035-.06.27-.07.326l-.12.522c-.05.216-.1.442-.146.617h.306l.17-.833c.523-.547.728-.723.849-.723.055 0 .095.03.095.096.006.096-.055.316-.075.376l-.176.607c-.04.136-.07.261-.065.347.005.135.085.19.206.19.22 0 .386-.23.527-.461l-.04-.075c-.056.09-.191.29-.296.29-.036 0-.066-.02-.066-.075-.005-.07.026-.176.045-.246l.196-.708c.05-.195.076-.32.076-.386-.005-.13-.08-.196-.2-.196-.201 0-.468.146-1.035.798h-.01l.07-.316c.041-.176.076-.356.116-.482-.196.07-.477.15-.647.181l.004.1Zm3.328-.567a.27.27 0 0 1 .276-.271c.146 0 .276.115.276.27a.27.27 0 0 1-.276.271.265.265 0 0 1-.276-.27Zm.276.23a.22.22 0 0 0 .22-.226.218.218 0 0 0-.22-.226.218.218 0 0 0-.22.226c0 .131.095.226.22.226Zm-.055-.07h-.05v-.311h.12c.076 0 .11.025.11.09 0 .056-.034.08-.08.085l.091.136h-.055l-.08-.136h-.056v.136Zm.055-.175c.04 0 .076-.006.076-.05 0-.04-.036-.046-.07-.046h-.066v.096h.06Z" />
</svg>
className="size-28 text-black dark:text-white"
/>
</div>
<div className="col-span-1 flex h-14 items-center justify-center lg:h-28">
<svg
<VmwareIcon
aria-hidden="true"
role="img"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
className="h-28 w-28 text-black dark:text-white"
>
<title>VMware</title>
<path d="M.5 10.1a.505.505 0 00-.197.048.497.497 0 00-.25.68l1.138 2.475c.179.38.38.592.721.592.342 0 .542-.22.72-.592l1.003-2.186a.144.144 0 01.144-.092.16.16 0 01.157.16v2.118a.535.535 0 101.066 0v-1.73a.531.531 0 01.566-.552.52.52 0 01.541.551v1.73a.531.531 0 00.53.593.539.539 0 00.535-.592v-1.73a.531.531 0 01.564-.552.52.52 0 01.543.551v1.73a.531.531 0 00.528.593.535.535 0 00.535-.592v-1.969a1.234 1.234 0 00-1.283-1.23 1.647 1.647 0 00-1.14.486 1.26 1.26 0 00-1.095-.483 1.807 1.807 0 00-1.074.483 1.287 1.287 0 00-.961-.483 1.177 1.177 0 00-1.158.786l-.729 1.716-.933-2.203.011-.004A.505.505 0 00.5 10.1zm18.43.06a.27.27 0 00-.266.274h.002v3.142a.27.27 0 10.535 0v-1.222c0-1.037.571-1.56 1.27-1.643a.266.266 0 00.238-.274.258.258 0 00-.266-.269 1.465 1.465 0 00-1.242.88v-.614a.266.266 0 00-.271-.274zm-6.735.008a.273.273 0 00-.25.217l-.912 2.627-.902-2.62a.28.28 0 00-.274-.22.266.266 0 00-.27.258.493.493 0 00.034.144l1.09 3.037.02-.007a.319.319 0 00.298.242.3.3 0 00.293-.242l.903-2.583.896 2.583a.3.3 0 00.293.242h.018a.319.319 0 00.293-.242l1.097-3.038a.512.512 0 00.033-.144.258.258 0 00-.265-.25.262.262 0 00-.258.209l-.918 2.63-.904-2.626a.285.285 0 00-.278-.217h-.025a.273.273 0 00-.012 0zm10.168.008a1.75 1.75 0 00-1.691 1.851 1.765 1.765 0 001.76 1.858l-.008.013a1.784 1.784 0 001.33-.539.228.228 0 00.082-.17.228.228 0 00-.379-.168 1.435 1.435 0 01-1.018.415 1.237 1.237 0 01-1.24-1.207h2.555a.247.247 0 00.246-.247c0-.945-.593-1.806-1.637-1.806zm-5.744.002a1.571 1.571 0 00-.158.006 2.384 2.384 0 00-1.078.205.22.22 0 00-.143.222.24.24 0 00.235.229.266.266 0 00.095-.024 1.822 1.822 0 01.834-.162c.691 0 1.07.334 1.07.979v.125a3.796 3.796 0 00-1.103-.15c-.892 0-1.52.4-1.52 1.16l-.003-.004c0 .736.671 1.117 1.34 1.117a1.575 1.575 0 001.298-.62v.343a.247.247 0 00.254.25.254.254 0 00.258-.262v-1.983a1.416 1.416 0 00-.379-1.046 1.571 1.571 0 00-1-.385zm5.719.43c.714 0 1.085.565 1.139 1.214h-2.278a1.222 1.222 0 011.139-1.215zm-5.885 1.382a3.75 3.75 0 011.057.153V12.49c0 .57-.539.973-1.2.973-.485 0-.904-.261-.904-.713 0-.467.375-.76 1.047-.76Z" />
</svg>
className="size-28 text-black dark:text-white"
/>
</div>
<div className="col-span-1 flex h-14 items-center justify-center lg:h-28">
<svg
<StorybookIcon
aria-hidden="true"
role="img"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
className="h-12 w-12 text-[#FF4785]"
>
<title>Storybook</title>
<path d="M16.71.243l-.12 2.71a.18.18 0 00.29.15l1.06-.8.9.7a.18.18 0 00.28-.14l-.1-2.76 1.33-.1a1.2 1.2 0 011.279 1.2v21.596a1.2 1.2 0 01-1.26 1.2l-16.096-.72a1.2 1.2 0 01-1.15-1.16l-.75-19.797a1.2 1.2 0 011.13-1.27L16.7.222zM13.64 9.3c0 .47 3.16.24 3.59-.08 0-3.2-1.72-4.89-4.859-4.89-3.15 0-4.899 1.72-4.899 4.29 0 4.45 5.999 4.53 5.999 6.959 0 .7-.32 1.1-1.05 1.1-.96 0-1.35-.49-1.3-2.16 0-.36-3.649-.48-3.769 0-.27 4.03 2.23 5.2 5.099 5.2 2.79 0 4.969-1.49 4.969-4.18 0-4.77-6.099-4.64-6.099-6.999 0-.97.72-1.1 1.13-1.1.45 0 1.25.07 1.19 1.87z" />
</svg>
className="size-12 text-[#FF4785]"
/>
</div>
<div className="col-span-1 flex h-14 items-center justify-center lg:h-28">
<svg
<RedwoodJsIcon
aria-hidden="true"
role="img"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
className="h-12 w-12 text-[#BF4722]"
>
<title>RedwoodJS</title>
<path d="M6.989 3.47l4.66 3.181c.105.07.228.108.354.111a.634.634 0 0 0 .354-.111l4.664-3.192a.637.637 0 0 0-.089-1.087L12.272.065a.64.64 0 0 0-.56 0L7.067 2.372a.636.636 0 0 0-.078 1.098zm6.597 4.179c0 .211.104.408.276.528l3.736 2.553a.628.628 0 0 0 .776-.05l3.134-2.803a.637.637 0 0 0-.028-.973l-2.992-2.393a.635.635 0 0 0-.751-.029l-3.874 2.65a.644.644 0 0 0-.277.517zm-9.291 3.474a.64.64 0 0 1 .209.538.625.625 0 0 1-.315.485l-2.231 1.337a.63.63 0 0 1-.718-.049.64.64 0 0 1-.21-.693l.825-2.596a.63.63 0 0 1 1.023-.281l1.417 1.259zm12.1.271l-4.033-2.76a.634.634 0 0 0-.708 0l-4.033 2.76a.646.646 0 0 0-.276.485.664.664 0 0 0 .212.521l4.03 3.605a.635.635 0 0 0 .842 0l4.03-3.605a.647.647 0 0 0-.064-1.006zm-10.758-.713l-3.13-2.803a.648.648 0 0 1-.213-.503.626.626 0 0 1 .237-.481l2.992-2.407a.641.641 0 0 1 .754-.029l3.87 2.65a.639.639 0 0 1 0 1.07l-3.732 2.553a.633.633 0 0 1-.778-.05zm16.073 4.026l-3.187-1.908a.627.627 0 0 0-.744.071l-3.895 3.477a.644.644 0 0 0-.204.587.636.636 0 0 0 .388.483l5.404 2.19a.634.634 0 0 0 .815-.332l1.675-3.752a.64.64 0 0 0-.252-.816zm.442-4.561l.825 2.596h-.007a.635.635 0 0 1-.927.742l-2.234-1.337a.623.623 0 0 1-.305-.485.631.631 0 0 1 .209-.538l1.416-1.262a.63.63 0 0 1 1.023.284zm-11.82 6.786a.637.637 0 0 0-.202-.585L6.235 12.87a.627.627 0 0 0-.744-.071l-3.187 1.908a.648.648 0 0 0-.255.813l1.678 3.752a.632.632 0 0 0 .814.332l5.4-2.19a.637.637 0 0 0 .391-.482zm1.912 1.07l4.334 1.755c.212.091.358.29.382.521a.635.635 0 0 1-.269.596l-4.338 3.013A.625.625 0 0 1 12 24a.64.64 0 0 1-.354-.114l-4.334-3.013a.635.635 0 0 1 .124-1.117l4.334-1.755a.642.642 0 0 1 .474.001z" />
</svg>
className="size-12 text-[#BF4722]"
/>
</div>
<div className="col-span-1 flex h-14 items-center justify-center lg:h-28">
<svg
<CaterpillarIcon
aria-hidden="true"
role="img"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
fill="currentColor"
className="h-14 w-14 text-[#FFCD11]"
>
<title>Caterpillar</title>
<path d="M11.901 11.554l.802-4.1.798 4.1zm2.869-6.52h-4.15L8.2 15.884l4.503-3.635 4.695 3.934zm-2.067 8.156l-7.509 6.072H19.95zM24 5.02v2.77h-2.066v11.45h-.882l-2.436-2.04V7.79h-2.057V5.02zM6.872 16.864c.548-.458.642-1.024.642-1.532V13.2h-2.98v2.894a.75.75 0 0 1-.748.751c-.414 0-.722-.336-.722-.75V7.893c0-.414.308-.75.722-.75a.75.75 0 0 1 .749.75v2.913H7.51V7.785c0-1.67-1.092-3.044-3.75-3.047-2.728 0-3.76 1.38-3.76 3.05v8.563c0 1.655 1.314 2.907 2.995 2.907h.922Z" />
</svg>
className="size-14 text-[#FFCD11]"
/>
</div>
</div>
</section>
+8 -7
View File
@@ -1,10 +1,11 @@
export * from './lib/call-to-action';
export * from './lib/download-case-study';
export * from './lib/download-ebook';
export * from './lib/enterprise-addons';
export * from './lib/hero';
export * from './lib/metrics-and-customers';
export * from './lib/scale-your-people';
export * from './lib/security';
export * from './lib/trusted-by';
export * from './lib/solve-your-ci';
export * from './lib/customer-logos';
export * from './lib/customer-metrics';
export * from './lib/make-your-ci-fast';
export * from './lib/hetzner-cloud-testimonial';
export * from './lib/vmware-testimonial';
export * from './lib/scale-your-organization';
export * from './lib/testimonial-carousel';
export * from './lib/download-case-study';
@@ -1,78 +0,0 @@
import Link from 'next/link';
import { ReactNode } from 'react';
import { cx } from '@nx/nx-dev/ui-primitives';
export const BentoGrid = ({
className,
children,
}: {
className?: string;
children?: ReactNode;
}) => {
return (
<div
className={cx(
'mx-auto grid max-w-7xl grid-cols-1 gap-4 md:auto-rows-[24rem] md:grid-cols-2 lg:grid-cols-3 ',
className
)}
>
{children}
</div>
);
};
export const BentoGridItem = ({
className,
title = null,
description = null,
header,
url = null,
icon,
}: {
className?: string;
title?: string | ReactNode | null;
description?: string | ReactNode | null;
header: ReactNode;
icon?: ReactNode;
url?: string | null;
}) => {
return (
<div
className={cx(
'group/bento shadow-input relative row-span-1 flex flex-col justify-between space-y-4 overflow-hidden rounded-xl border border-slate-200 bg-white p-4 transition duration-200 dark:border-slate-800 dark:bg-slate-950 dark:shadow-none',
className
)}
>
{header}
<div className="mt-2 flex items-start">
<div className="grow">
<div className="flex items-center gap-2 font-sans font-bold text-slate-800 transition duration-200 group-hover/bento:text-blue-500 dark:text-slate-200 group-hover/bento:dark:text-sky-500">
{icon} {title}
</div>
{description && (
<div className="mt-2 font-sans text-sm font-normal text-slate-600 transition duration-200 group-hover/bento:text-blue-500 dark:text-slate-400 group-hover/bento:dark:text-sky-500">
{description}
</div>
)}
</div>
{url ? (
<Link
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" />
<span
className="inline-block transition duration-200 group-hover/bento:translate-x-2"
aria-hidden="true"
>
</span>
</Link>
) : null}
</div>
</div>
);
};
+34 -15
View File
@@ -1,11 +1,17 @@
import Link from 'next/link';
import { ReactElement } from 'react';
import { sendCustomEvent } from '@nx/nx-dev/feature-analytics';
export function CallToAction(): JSX.Element {
export function CallToAction(): ReactElement {
return (
<section className="relative isolate px-6 py-32 sm:py-40 lg:px-8">
<section
className="relative isolate px-6 py-32 sm:py-40 lg:px-8"
aria-labelledby="section-cta-heading"
>
<svg
className="absolute inset-0 -z-10 h-full w-full stroke-black/10 [mask-image:radial-gradient(100%_100%_at_top_right,white,transparent)] dark:stroke-white/10"
className="absolute inset-0 -z-10 h-full w-full rotate-180 stroke-black/10 [mask-image:radial-gradient(100%_100%_at_top_right,white,transparent)] dark:stroke-white/10"
aria-hidden="true"
focusable="false"
>
<defs>
<pattern
@@ -41,7 +47,7 @@ export function CallToAction(): JSX.Element {
aria-hidden="true"
>
<div
className="aspect-[1108/632] w-[69.25rem] flex-none bg-gradient-to-r from-[#80caff] to-[#4f46e5] opacity-20"
className="aspect-[1108/632] w-[69.25rem] flex-none bg-gradient-to-r from-[#80caff] to-[#4f46e5] opacity-10"
style={{
clipPath:
'polygon(73.6% 51.7%, 91.7% 11.8%, 100% 46.4%, 97.4% 82.2%, 92.5% 84.9%, 75.7% 64%, 55.3% 47.5%, 46.5% 49.4%, 45% 62.9%, 50.3% 87.2%, 21.3% 64.1%, 0.1% 100%, 5.4% 51.1%, 21.4% 63.9%, 58.9% 0.2%, 73.6% 51.7%)',
@@ -50,31 +56,44 @@ export function CallToAction(): JSX.Element {
</div>
<div className="mx-auto max-w-2xl text-center">
<h2
id="cta"
id="section-cta-heading"
className="text-3xl font-medium tracking-tight text-slate-950 sm:text-5xl dark:text-white"
>
Your organization's transformation
<br />
starts now
Get your hands dirty and try it out for yourself.
</h2>
<div className="mt-10">
<Link
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"
aria-label="Request a free trial"
href="/contact/sales"
prefetch={false}
title="Request a free trial"
onClick={() =>
sendCustomEvent(
'request-trial-click',
'enterprise-bottom-cta',
'enterprise'
)
}
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"
>
Talk to engineering
Request a free trial
</Link>
<p className="mt-6 italic">
Ready to talk terms?{' '}
Want to talk terms or see a demo?{' '}
<Link
href="/contact/sales"
title="Talk to the sales team"
title="Talk to the team"
className="font-semibold underline"
prefetch={false}
onClick={() =>
sendCustomEvent(
'contact-team',
'enterprise-bottom-cta',
'enterprise'
)
}
>
Speak directly to sales
Reach out to our team
</Link>
.
</p>
+309
View File
@@ -0,0 +1,309 @@
import {
createContext,
forwardRef,
ReactElement,
ReactNode,
Ref,
useCallback,
useContext,
useEffect,
useImperativeHandle,
useRef,
useState,
} from 'react';
import { motion, MotionConfig, useAnimation } from 'framer-motion';
import { cx } from '@nx/nx-dev/ui-primitives';
export interface CarouselHandle {
/**
* Navigate to a specific slide index
* @param index The zero-based index of the target slide
*/
goToSlide: (index: number) => void;
/**
* Move to the next slide
*/
goToNext: () => void;
/**
* Move to the previous slide
*/
goToPrevious: () => void;
/**
* Get the current slide index
*/
getCurrentIndex: () => number;
/**
* Get the total number of slides
*/
getTotalSlides: () => number;
/**
* Pause the autoplay if it's enabled
*/
pauseAutoPlay: () => void;
/**
* Resume the autoplay if it's enabled
*/
resumeAutoPlay: () => void;
}
interface CarouselContextValue {
currentIndex: number;
totalSlides: number;
goToSlide: (index: number) => void;
goToNext: () => void;
goToPrevious: () => void;
pauseAutoPlay: () => void;
resumeAutoPlay: () => void;
registerSlide: (id: string) => void;
unregisterSlide: (id: string) => void;
}
interface CarouselRootProps {
children: ReactNode;
className?: string;
autoPlayInterval?: number;
animationDuration?: number;
onSlideChange?: (index: number) => void;
enableKeyboardNavigation?: boolean;
ref?: Ref<CarouselHandle>;
}
const SPRING_CONFIG = {
type: 'spring' as const,
damping: 30,
stiffness: 300,
mass: 0.2,
};
const CarouselContext = createContext<CarouselContextValue | null>(null);
export const useCarousel = (): CarouselContextValue => {
const context = useContext(CarouselContext);
if (!context) {
throw new Error('Carousel components must be used within a CarouselRoot');
}
return context;
};
export const CarouselRoot = forwardRef<CarouselHandle, CarouselRootProps>(
(
{
children,
className = '',
autoPlayInterval,
animationDuration = 0.5,
onSlideChange,
enableKeyboardNavigation = true,
},
ref
): ReactElement => {
const [currentIndex, setCurrentIndex] = useState(0);
const [slideIds, setSlideIds] = useState<string[]>([]);
const autoPlayTimeoutRef = useRef<NodeJS.Timeout>();
const containerRef = useRef<HTMLDivElement>(null);
const goToSlide = useCallback(
(index: number) => {
// Ensure the index is within bounds
const safeIndex = Math.max(0, Math.min(index, slideIds.length - 1));
setCurrentIndex(safeIndex);
// Call the onSlideChange callback if provided
onSlideChange?.(safeIndex);
},
[slideIds.length, onSlideChange]
);
const goToNext = useCallback(() => {
goToSlide((currentIndex + 1) % slideIds.length);
}, [currentIndex, slideIds.length, goToSlide]);
const goToPrevious = useCallback(() => {
goToSlide((currentIndex - 1 + slideIds.length) % slideIds.length);
}, [currentIndex, slideIds.length, goToSlide]);
// Implement keyboard navigation
useEffect(() => {
if (!enableKeyboardNavigation) return;
const handleKeyDown = (event: KeyboardEvent) => {
// Ignore keyboard navigation when user is typing in an input
if (
event.target instanceof HTMLInputElement ||
event.ctrlKey ||
event.altKey ||
event.shiftKey ||
event.metaKey
) {
return;
}
switch (event.key) {
case 'ArrowLeft':
event.preventDefault();
goToPrevious();
break;
case 'ArrowRight':
event.preventDefault();
goToNext();
break;
case 'Home':
event.preventDefault();
goToSlide(0);
break;
case 'End':
event.preventDefault();
goToSlide(slideIds.length - 1);
break;
default:
break;
}
};
window.addEventListener('keydown', handleKeyDown);
return () => window.removeEventListener('keydown', handleKeyDown);
}, [
enableKeyboardNavigation,
goToNext,
goToPrevious,
goToSlide,
slideIds.length,
]);
// Autoplay functionality
const pauseAutoPlay = useCallback(() => {
if (autoPlayTimeoutRef.current) {
clearInterval(autoPlayTimeoutRef.current);
}
}, []);
const resumeAutoPlay = useCallback(() => {
if (autoPlayInterval) {
pauseAutoPlay();
autoPlayTimeoutRef.current = setInterval(goToNext, autoPlayInterval);
}
}, [autoPlayInterval, goToNext, pauseAutoPlay]);
// Set up and clean up autoplay
useEffect(() => {
if (autoPlayInterval) {
resumeAutoPlay();
}
return pauseAutoPlay;
}, [autoPlayInterval, resumeAutoPlay, pauseAutoPlay]);
// Register and unregister slides
const registerSlide = useCallback((id: string) => {
setSlideIds((prev) => [...prev, id]);
}, []);
const unregisterSlide = useCallback((id: string) => {
setSlideIds((prev) => prev.filter((slideId) => slideId !== id));
}, []);
const contextValue = {
currentIndex,
totalSlides: slideIds.length,
goToSlide,
goToNext,
goToPrevious,
pauseAutoPlay,
resumeAutoPlay,
registerSlide,
unregisterSlide,
};
// Expose methods through the ref
useImperativeHandle(
ref,
() => ({
goToSlide,
goToNext,
goToPrevious,
getCurrentIndex: () => currentIndex,
getTotalSlides: () => slideIds.length,
pauseAutoPlay,
resumeAutoPlay,
}),
[
goToSlide,
goToNext,
goToPrevious,
currentIndex,
slideIds.length,
pauseAutoPlay,
resumeAutoPlay,
]
);
return (
<CarouselContext.Provider value={contextValue}>
<MotionConfig transition={{ duration: animationDuration }}>
<div
ref={containerRef}
role="region"
aria-roledescription="carousel"
aria-label={`Carousel with ${slideIds.length} slides`}
tabIndex={enableKeyboardNavigation ? 0 : undefined}
className={cx('relative overflow-hidden', className)}
>
{children}
</div>
</MotionConfig>
</CarouselContext.Provider>
);
}
);
export function CarouselViewport({
className = '',
children,
}: {
className?: string;
children: ReactNode;
}): ReactElement {
const { currentIndex } = useCarousel();
const controls = useAnimation();
useEffect(() => {
controls.start({
x: -currentIndex * 100 + '%',
transition: SPRING_CONFIG,
});
}, [currentIndex, controls]);
return (
<motion.div animate={controls} className={cx('flex h-full', className)}>
{children}
</motion.div>
);
}
export function CarouselSlide({
children,
className = '',
}: {
children: ReactNode;
className?: string;
}): ReactElement {
const { registerSlide, unregisterSlide } = useCarousel();
const slideId = useRef(`slide-${Math.random()}`);
useEffect(() => {
registerSlide(slideId.current);
return () => unregisterSlide(slideId.current);
}, [registerSlide, unregisterSlide]);
return (
<div
role="group"
aria-roledescription="slide"
className={cx('w-full flex-shrink-0', className)}
>
{children}
</div>
);
}
@@ -0,0 +1,114 @@
import {
AmericanAirlinesIcon,
AwsIcon,
BillIcon,
CapitalOneIcon,
CaterpillarIcon,
CiscoIcon,
FicoIcon,
HiltonIcon,
ManIcon,
RoyalBankOfCanadaIcon,
SevenElevenIcon,
ShopifyIcon,
StorybookIcon,
VmwareIcon,
} from '@nx/nx-dev/ui-icons';
import { ReactElement } from 'react';
import { Marquee } from '@nx/nx-dev/ui-animations';
import { cx } from '@nx/nx-dev/ui-primitives';
import Link from 'next/link';
import { ChevronRightIcon } from '@heroicons/react/20/solid';
export function CustomerLogos(): ReactElement {
const icons = [
{
Icon: AwsIcon,
className: 'size-14',
},
{
Icon: ManIcon,
className: 'size-14',
},
{
Icon: CapitalOneIcon,
className: 'size-28',
},
{
Icon: ShopifyIcon,
className: null,
},
{
Icon: RoyalBankOfCanadaIcon,
className: null,
},
{
Icon: VmwareIcon,
className: 'size-24',
},
{
Icon: StorybookIcon,
className: null,
},
{
Icon: FicoIcon,
className: 'size-20',
},
{
Icon: CaterpillarIcon,
className: null,
},
{
Icon: CiscoIcon,
className: 'size-16',
},
{
Icon: BillIcon,
className: null,
},
{
Icon: SevenElevenIcon,
className: null,
},
{
Icon: HiltonIcon,
className: 'size-24',
},
{
Icon: AmericanAirlinesIcon,
className: null,
},
];
return (
<section
id="customer-logos"
className="group/canvas relative mx-auto flex max-w-7xl overflow-hidden text-slate-950 dark:text-slate-100"
>
<Marquee className="w-full justify-center overflow-hidden [--duration:240s] [--gap:6rem]">
{icons.map((e, idx) => (
<e.Icon
key={'icon-' + idx}
aria-hidden="true"
className={cx('mx-auto size-12', e.className)}
/>
))}
</Marquee>
<div className="absolute bottom-0 left-0 top-0 w-1/3 max-w-60 bg-gradient-to-r from-white to-white/0 dark:from-slate-950 dark:to-slate-950/0"></div>
<div className="absolute bottom-0 right-0 top-0 w-1/3 max-w-60 bg-gradient-to-r from-white/0 to-white dark:from-slate-950/0 dark:to-slate-950"></div>
<div className="absolute inset-0 grid grid-cols-1 place-items-center bg-white/60 opacity-0 backdrop-blur-sm transition-all duration-500 group-hover/canvas:opacity-100 dark:bg-slate-950/60">
<Link
href="/customers"
title="See our customers"
className="group/link relative flex items-center gap-2 text-base font-semibold drop-shadow"
>
<span>See our customers</span>{' '}
<ChevronRightIcon
aria-hidden="true"
className="size-5 transform transition-all group-hover/link:translate-x-1"
/>
</Link>
</div>
</section>
);
}
@@ -0,0 +1,90 @@
import { ReactElement } from 'react';
import { HetznerCloudIcon } from '@nx/nx-dev/ui-icons';
import { SectionHeading } from '@nx/nx-dev/ui-common';
import Link from 'next/link';
import { sendCustomEvent } from '@nx/nx-dev/feature-analytics';
export function CustomerMetrics(): ReactElement {
return (
<section className="border-b border-t border-slate-200 bg-slate-50 py-24 sm:py-32 dark:border-slate-800 dark:bg-slate-900">
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div className="mx-auto max-w-2xl lg:mx-0 lg:max-w-none">
<div className="mt-6 flex flex-col-reverse gap-x-8 gap-y-20 lg:flex-row lg:items-center">
<div className="lg:w-full lg:max-w-2xl lg:flex-auto">
<figure className="rounded-2xl bg-white shadow-lg ring-1 ring-slate-900/5 sm:col-span-2 xl:col-start-2 xl:row-end-1 dark:bg-slate-800">
<blockquote className="p-6 text-lg font-semibold tracking-tight text-black dark:text-white">
<p>
Engineers will run a test command and expect it to run for
20 mins, they start it and see it finishes in a few seconds,
then they ask Did I start it wrong? Why is it so fast?
</p>
</blockquote>
<figcaption className="flex flex-wrap items-center gap-x-4 gap-y-4 border-t border-slate-900/10 px-6 py-4 sm:flex-nowrap dark:border-slate-100/10">
<img
alt="pavlo grosse"
src="https://avatars.githubusercontent.com/u/2219064?v=4"
className="size-10 flex-none rounded-full bg-slate-50"
/>
<div className="flex-auto">
<div className="font-semibold">Pavlo Grosse</div>
<div className="text-slate-600 dark:text-slate-500">
Senior Software Engineer, Hetzner Cloud
</div>
</div>
<HetznerCloudIcon
aria-hidden="true"
className="mx-auto size-10 flex-none bg-white text-[#D50C2D]"
/>
</figcaption>
</figure>
<SectionHeading
as="p"
variant="subtitle"
className="mt-8 text-center italic"
>
Want to see it in action?{' '}
<Link
href="/contact/sales"
title="Request Nx Enterprise demo"
prefetch={false}
onClick={() =>
sendCustomEvent(
'request-trial-click',
'enterprise-customer-metrics',
'enterprise'
)
}
className="font-semibold underline"
>
Request a demo now <span aria-hidden="true"></span>
</Link>
</SectionHeading>
</div>
<div className="lg:flex lg:flex-auto lg:justify-center">
<dl className="flex flex-col justify-center gap-8 sm:flex-row lg:flex-col xl:w-80">
<div className="flex flex-col-reverse gap-y-1 border-slate-200 pl-4 text-center sm:border-l sm:text-left dark:border-slate-800">
<dt className="text-base/7">faster CI</dt>
<dd className="[text-shadow:1px 1px 0 white;] te text-5xl font-semibold tracking-tight text-black dark:text-white">
70%
</dd>
</div>
<div className="flex flex-col-reverse gap-y-1 border-slate-200 pl-4 text-center sm:border-l sm:text-left dark:border-slate-800">
<dt className="text-base/7">less compute used</dt>
<dd className="text-5xl font-semibold tracking-tight text-black dark:text-white">
60%
</dd>
</div>
<div className="flex flex-col-reverse gap-y-1 border-slate-200 pl-4 text-center sm:border-l sm:text-left dark:border-slate-800">
<dt className="text-base/7">reduction in infra costs</dt>
<dd className="text-5xl font-semibold tracking-tight text-black dark:text-white">
75%
</dd>
</div>
</dl>
</div>
</div>
</div>
</div>
</section>
);
}
@@ -1,4 +1,5 @@
import { ButtonLink } from '@nx/nx-dev/ui-common';
import { ReactElement } from 'react';
export interface DownloadCaseStudyProps {
title: string;
@@ -12,7 +13,7 @@ export function DownloadCaseStudy({
description,
buttonHref,
buttonText = 'Download (pdf)',
}: DownloadCaseStudyProps): JSX.Element {
}: DownloadCaseStudyProps): ReactElement {
return (
<div className="border border-slate-100 bg-white shadow-lg sm:rounded-lg dark:border-slate-800/60 dark:bg-slate-950">
<div className="px-4 py-5 sm:p-6">
@@ -1,62 +0,0 @@
import { ButtonLink } from '@nx/nx-dev/ui-common';
import { ReactElement } from 'react';
export function DownloadEbook(): ReactElement {
return (
<div className="mx-auto max-w-7xl sm:px-6 lg:px-8">
<div className="relative isolate overflow-hidden bg-slate-50/70 px-6 pb-4 pt-16 shadow-2xl ring-1 ring-slate-200 sm:rounded-3xl sm:px-16 sm:pb-0 md:pt-24 lg:flex lg:gap-x-20 lg:px-24 lg:pt-0 dark:border dark:border-slate-800/60 dark:bg-slate-950 dark:ring-slate-800/60">
<svg
viewBox="0 0 1024 1024"
className="absolute left-1/2 top-1/2 -z-10 h-[64rem] w-[64rem] -translate-y-1/2 [mask-image:radial-gradient(closest-side,white,transparent)] sm:left-full sm:-ml-80 lg:left-1/2 lg:ml-0 lg:-translate-x-1/2 lg:translate-y-0"
aria-hidden="true"
>
<circle
cx={512}
cy={512}
r={512}
fill="url(#759c1415-0410-454c-8f7c-9a820de03641)"
fillOpacity="0.7"
/>
<defs>
<radialGradient id="759c1415-0410-454c-8f7c-9a820de03641">
<stop stopColor="#7775D6" />
<stop offset={1} stopColor="#a855f7" />
</radialGradient>
</defs>
</svg>
<div className="mx-auto max-w-md text-center lg:mx-0 lg:flex-auto lg:py-12 lg:text-left">
<h2 className="text-3xl font-bold tracking-tight text-slate-950 sm:text-4xl dark:text-slate-50">
Download our ebook
</h2>
<p className="mt-6 text-lg leading-8">
Discover how to scale your organization without feeling the pain of
CI, while having a better developer experience and fitting your
requirements.
</p>
<div className="mt-10 flex items-center justify-center gap-x-6 lg:justify-start">
<ButtonLink
href="https://go.nx.dev/ci-ebook"
title="Download Fast CI whitepaper"
target="_blank"
>
<span>Download</span>{' '}
<span className="text-xs italic">(pdf)</span>
</ButtonLink>
</div>
</div>
<div className="relative mt-16 hidden h-72 sm:block lg:mt-8">
<img
className="absolute left-0 top-0 w-[42rem] max-w-none rounded-md bg-white/5 ring-1 ring-white/10"
src="images/white-paper-ebook.avif"
alt="App screenshot"
width={1200}
height={675}
/>
<span className="absolute left-4 top-4 inline-flex items-center rounded-md bg-blue-50 px-2 py-1 text-xs font-medium text-blue-700 ring-1 ring-inset ring-blue-700/10">
ebook
</span>
</div>
</div>
</div>
);
}
@@ -1,78 +0,0 @@
import {
BoltIcon,
ChevronDoubleRightIcon,
UsersIcon,
} from '@heroicons/react/24/outline';
import { SectionHeading } from '@nx/nx-dev/ui-common';
export function EnterpriseAddons(): JSX.Element {
return (
<section id="partner-with-the-nx-team">
<div className="mx-auto max-w-7xl px-6 lg:px-8">
<div className="mx-auto max-w-2xl text-center">
<SectionHeading as="h2" variant="title">
Partner with the Nx team
</SectionHeading>
</div>
<div className="mx-auto mt-16 max-w-2xl sm:mt-20 lg:mt-24 lg:max-w-none">
<dl className="grid max-w-xl grid-cols-1 gap-x-8 gap-y-16 lg:max-w-none lg:grid-cols-3">
<div className="flex flex-col">
<dt className="text-base font-semibold leading-7 text-black dark:text-white">
<div className="mb-6 flex h-10 w-10 items-center justify-center rounded-lg bg-black dark:bg-white">
<BoltIcon
className="h-6 w-6 text-white dark:text-black"
aria-hidden="true"
/>
</div>
Move fast, move together
</dt>
<dd className="mt-1 flex flex-auto flex-col text-base leading-7">
<p className="flex-auto">
We know Nx, you know your code. Together, we can build your
ultimate developer platform, tailored to your team.
</p>
</dd>
</div>
<div className="flex flex-col">
<dt className="text-base font-semibold leading-7 text-black dark:text-white">
<div className="mb-6 flex h-10 w-10 items-center justify-center rounded-lg bg-black dark:bg-white">
<UsersIcon
className="h-6 w-6 text-white dark:text-black"
aria-hidden="true"
/>
</div>
No trial and error necessary
</dt>
<dd className="mt-1 flex flex-auto flex-col text-base leading-7">
<p className="flex-auto">
With the help of the developers of Nx, you'll use Nx and Nx
Cloud to its full potential, the first time. No matter how
long you've been using Nx, we'll find ways to make it more
powerful.
</p>
</dd>
</div>
<div className="flex flex-col">
<dt className="text-base font-semibold leading-7 text-black dark:text-white">
<div className="mb-6 flex h-10 w-10 items-center justify-center rounded-lg bg-black dark:bg-white">
<ChevronDoubleRightIcon
className="h-6 w-6 text-white dark:text-black"
aria-hidden="true"
/>
</div>
Migrate to Nx & Nx Cloud
</dt>
<dd className="mt-1 flex flex-auto flex-col text-base leading-7">
<p className="flex-auto">
Wherever your team is at in their Nx journey, our experts will
make it easy to move ahead and capture the full value of Nx
and Nx Cloud.
</p>
</dd>
</div>
</dl>
</div>
</div>
</section>
);
}
+82 -20
View File
@@ -1,42 +1,104 @@
import { ButtonLink, SectionHeading } from '@nx/nx-dev/ui-common';
import Link from 'next/link';
import { ReactElement } from 'react';
import { ChevronRightIcon } from '@heroicons/react/20/solid';
import { sendCustomEvent } from '@nx/nx-dev/feature-analytics';
export function Hero(): JSX.Element {
export function Hero(): ReactElement {
return (
<section>
<div className="relative mx-auto max-w-7xl px-6 lg:px-8">
<div className="mx-auto max-w-3xl text-center">
<SectionHeading id="fast-ci-for-monorepo" as="h1" variant="display">
Nx Enterprise
<section className="relative isolate overflow-hidden">
<svg
focusable="false"
aria-hidden="true"
role="presentation"
className="absolute inset-0 -z-10 size-full stroke-slate-200 [mask-image:radial-gradient(100%_100%_at_top_right,white,transparent)] dark:stroke-slate-800"
>
<defs>
<pattern
x="50%"
y={-1}
id="0787a7c5-978c-4f66-83c7-11c213f99cb7"
width={200}
height={200}
patternUnits="userSpaceOnUse"
>
<path d="M.5 200V.5H200" fill="none" />
</pattern>
</defs>
<rect
fill="url(#0787a7c5-978c-4f66-83c7-11c213f99cb7)"
width="100%"
height="100%"
strokeWidth={0}
/>
</svg>
<div className="mx-auto max-w-7xl px-6 pb-24 pt-32 lg:flex lg:px-8 lg:pt-56">
<div className="mx-auto max-w-2xl lg:mx-0 lg:-mt-12 lg:shrink-0">
<p>
<a
href="https://bit.ly/3B0Ebfe"
title="See live event in details"
className="group/event-link inline-flex space-x-6"
>
<span className="rounded-full bg-blue-600/10 px-3 py-1 text-sm/6 font-semibold text-blue-600 ring-1 ring-inset ring-blue-600/10 dark:bg-cyan-600/10 dark:text-cyan-600 dark:ring-cyan-600/10">
Live event
</span>
<span className="inline-flex items-center space-x-2 text-sm/6 font-medium">
<span>Webinar + live Q&A on Dec 10th</span>
<ChevronRightIcon
aria-hidden="true"
className="size-5 transform transition-all group-hover/event-link:translate-x-1"
/>
</span>
</a>
</p>
<SectionHeading
id="get-speed-and-scale"
as="h1"
variant="display"
className="mt-8 text-pretty tracking-tight"
>
Solving the Performance Paradox,{' '}
<span className="rounded-lg bg-gradient-to-r from-pink-500 to-fuchsia-500 bg-clip-text text-transparent">
get speed and scale
</span>
</SectionHeading>
<SectionHeading
as="p"
variant="subtitle"
className="mx-auto mt-6 max-w-3xl "
className="mx-auto mt-6 max-w-3xl lg:pr-20"
>
Accelerate your organization's journey to tighter collaboration,
better developer experience, and speed…lots of speed.
</SectionHeading>
<div className="mt-10 items-center justify-center gap-x-6">
<div className="mt-8 flex items-center gap-x-6">
<ButtonLink
href="/contact/sales"
title="Request a free trial"
variant="primary"
size="default"
onClick={() =>
sendCustomEvent(
'request-trial-click',
'enterprise-hero',
'enterprise'
)
}
>
Request a free trial
</ButtonLink>
<p className="mt-6 italic">
Got questions?{' '}
<Link
href="/contact/engineering"
title="Talk to the sales team"
className="font-semibold underline"
prefetch={false}
>
Talk to an engineer.
</Link>
</p>
</div>
</div>
<div className="mx-auto mt-16 flex max-w-2xl sm:mt-24 lg:ml-10 lg:mr-0 lg:mt-8 lg:max-w-none lg:flex-none xl:ml-32">
<div className="max-w-3xl flex-none sm:max-w-5xl lg:max-w-none">
<div className="-m-2 rounded-xl bg-slate-900/5 p-2 ring-1 ring-inset ring-slate-900/10 lg:-m-4 lg:rounded-2xl lg:p-4 dark:bg-slate-100/5 dark:ring-slate-100/10">
<img
alt="nx cloud application dashboard screenshot"
src="/images/home/nx-app-dashboard.avif"
width={2500}
height={1616}
className="w-[764px] rounded-md shadow-2xl ring-1 ring-slate-900/10"
/>
</div>
</div>
</div>
</div>
@@ -0,0 +1,246 @@
import { ComponentProps, ReactElement } from 'react';
import { Button, ButtonLink, SectionHeading } from '@nx/nx-dev/ui-common';
import { HetznerCloudIcon } from '@nx/nx-dev/ui-icons';
import Link from 'next/link';
import { cx } from '@nx/nx-dev/ui-primitives';
import { MovingBorder } from '@nx/nx-dev/ui-animations';
import { motion } from 'framer-motion';
import {
ArrowDownTrayIcon,
PlayIcon,
VideoCameraIcon,
} from '@heroicons/react/24/outline';
function PlayButton({
className,
...props
}: ComponentProps<'div'>): ReactElement {
const parent = {
initial: {
width: 82,
transition: {
when: 'afterChildren',
},
},
hover: {
width: 296,
transition: {
duration: 0.125,
type: 'tween',
ease: 'easeOut',
},
},
};
const child = {
initial: {
opacity: 0,
x: -6,
},
hover: {
x: 0,
opacity: 1,
transition: {
duration: 0.015,
type: 'tween',
ease: 'easeOut',
},
},
};
return (
<div
className={cx(
'group relative overflow-hidden rounded-full bg-transparent p-[1px] shadow-md',
className
)}
{...props}
>
<div className="absolute inset-0">
<MovingBorder duration={5000} rx="5%" ry="5%">
<div className="size-20 bg-[radial-gradient(var(--blue-500)_40%,transparent_60%)] opacity-[0.8] dark:bg-[radial-gradient(var(--pink-500)_40%,transparent_60%)]" />
</MovingBorder>
</div>
<motion.div
initial="initial"
whileHover="hover"
variants={parent}
className="relative isolate flex size-20 cursor-pointer items-center justify-center gap-6 rounded-full border-2 border-slate-100 bg-white/10 p-6 text-sm text-white antialiased backdrop-blur-xl"
>
<PlayIcon aria-hidden="true" className="absolute left-6 top-6 size-8" />
<motion.div variants={child} className="absolute left-20 top-4 w-48">
<p className="text-base font-medium">Watch the interview</p>
<p># customer story</p>
</motion.div>
</motion.div>
</div>
);
}
export function HetznerCloudTestimonial(): ReactElement {
return (
<div className="border-b border-t border-slate-200 bg-slate-50 py-24 sm:py-32 dark:border-slate-800 dark:bg-slate-900">
<section
id="hetzner-cloud-testimonial"
className="z-0 mx-auto max-w-7xl px-4 sm:px-6 lg:px-8"
>
<SectionHeading as="h2" variant="title" id="hetzner-cloud-testimonial">
Nx Enterprise{' '}
<span className="rounded-lg bg-gradient-to-r from-cyan-500 to-blue-500 bg-clip-text text-transparent">
speeds build and test times
</span>{' '}
<br />
as Hetzner Cloud{' '}
<span className="rounded-lg bg-gradient-to-r from-cyan-500 to-blue-500 bg-clip-text text-transparent">
scales up
</span>{' '}
product offering
</SectionHeading>
<div className="mt-8 md:grid md:grid-cols-2 md:items-center md:gap-10 lg:gap-12">
<div className="mb-24 hidden sm:px-6 md:mb-0 md:block">
<div className="relative">
<div className="absolute bottom-0 start-0 -translate-x-14 translate-y-10">
<svg
className="h-auto max-w-40 text-slate-200 dark:text-slate-800"
width="696"
height="653"
viewBox="0 0 696 653"
fill="none"
xmlns="http://www.w3.org/2000/svg"
>
<circle cx="72.5" cy="29.5" r="29.5" fill="currentColor" />
<circle cx="171.5" cy="29.5" r="29.5" fill="currentColor" />
<circle cx="270.5" cy="29.5" r="29.5" fill="currentColor" />
<circle cx="369.5" cy="29.5" r="29.5" fill="currentColor" />
<circle cx="468.5" cy="29.5" r="29.5" fill="currentColor" />
<circle cx="567.5" cy="29.5" r="29.5" fill="currentColor" />
<circle cx="666.5" cy="29.5" r="29.5" fill="currentColor" />
<circle cx="29.5" cy="128.5" r="29.5" fill="currentColor" />
<circle cx="128.5" cy="128.5" r="29.5" fill="currentColor" />
<circle cx="227.5" cy="128.5" r="29.5" fill="currentColor" />
<circle cx="326.5" cy="128.5" r="29.5" fill="currentColor" />
<circle cx="425.5" cy="128.5" r="29.5" fill="currentColor" />
<circle cx="524.5" cy="128.5" r="29.5" fill="currentColor" />
<circle cx="623.5" cy="128.5" r="29.5" fill="currentColor" />
<circle cx="72.5" cy="227.5" r="29.5" fill="currentColor" />
<circle cx="171.5" cy="227.5" r="29.5" fill="currentColor" />
<circle cx="270.5" cy="227.5" r="29.5" fill="currentColor" />
<circle cx="369.5" cy="227.5" r="29.5" fill="currentColor" />
<circle cx="468.5" cy="227.5" r="29.5" fill="currentColor" />
<circle cx="567.5" cy="227.5" r="29.5" fill="currentColor" />
<circle cx="666.5" cy="227.5" r="29.5" fill="currentColor" />
<circle cx="29.5" cy="326.5" r="29.5" fill="currentColor" />
<circle cx="128.5" cy="326.5" r="29.5" fill="currentColor" />
<circle cx="227.5" cy="326.5" r="29.5" fill="currentColor" />
<circle cx="326.5" cy="326.5" r="29.5" fill="currentColor" />
<circle cx="425.5" cy="326.5" r="29.5" fill="currentColor" />
<circle cx="524.5" cy="326.5" r="29.5" fill="currentColor" />
<circle cx="623.5" cy="326.5" r="29.5" fill="currentColor" />
<circle cx="72.5" cy="425.5" r="29.5" fill="currentColor" />
<circle cx="171.5" cy="425.5" r="29.5" fill="currentColor" />
<circle cx="270.5" cy="425.5" r="29.5" fill="currentColor" />
<circle cx="369.5" cy="425.5" r="29.5" fill="currentColor" />
<circle cx="468.5" cy="425.5" r="29.5" fill="currentColor" />
<circle cx="567.5" cy="425.5" r="29.5" fill="currentColor" />
<circle cx="666.5" cy="425.5" r="29.5" fill="currentColor" />
<circle cx="29.5" cy="524.5" r="29.5" fill="currentColor" />
<circle cx="128.5" cy="524.5" r="29.5" fill="currentColor" />
<circle cx="227.5" cy="524.5" r="29.5" fill="currentColor" />
<circle cx="326.5" cy="524.5" r="29.5" fill="currentColor" />
<circle cx="425.5" cy="524.5" r="29.5" fill="currentColor" />
<circle cx="524.5" cy="524.5" r="29.5" fill="currentColor" />
<circle cx="623.5" cy="524.5" r="29.5" fill="currentColor" />
<circle cx="72.5" cy="623.5" r="29.5" fill="currentColor" />
<circle cx="171.5" cy="623.5" r="29.5" fill="currentColor" />
<circle cx="270.5" cy="623.5" r="29.5" fill="currentColor" />
<circle cx="369.5" cy="623.5" r="29.5" fill="currentColor" />
<circle cx="468.5" cy="623.5" r="29.5" fill="currentColor" />
<circle cx="567.5" cy="623.5" r="29.5" fill="currentColor" />
<circle cx="666.5" cy="623.5" r="29.5" fill="currentColor" />
</svg>
</div>
<img
src="/images/enterprise/video-story-pavlo-grosse.avif"
alt="Avatar"
className="relative rounded-xl"
/>
<div className="absolute inset-0 grid h-full w-full items-center justify-center">
{/*<PlayButton*/}
{/*onClick={() => setIsOpen(true)}*/}
{/*/>*/}
</div>
</div>
</div>
<figure>
<blockquote className="relative">
<svg
className="absolute start-0 top-0 size-24 -translate-x-8 -translate-y-4 transform text-slate-200 dark:text-slate-800"
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
aria-hidden="true"
>
<path
d="M7.39762 10.3C7.39762 11.0733 7.14888 11.7 6.6514 12.18C6.15392 12.6333 5.52552 12.86 4.76621 12.86C3.84979 12.86 3.09047 12.5533 2.48825 11.94C1.91222 11.3266 1.62421 10.4467 1.62421 9.29999C1.62421 8.07332 1.96459 6.87332 2.64535 5.69999C3.35231 4.49999 4.33418 3.55332 5.59098 2.85999L6.4943 4.25999C5.81354 4.73999 5.26369 5.27332 4.84476 5.85999C4.45201 6.44666 4.19017 7.12666 4.05926 7.89999C4.29491 7.79332 4.56983 7.73999 4.88403 7.73999C5.61716 7.73999 6.21938 7.97999 6.69067 8.45999C7.16197 8.93999 7.39762 9.55333 7.39762 10.3ZM14.6242 10.3C14.6242 11.0733 14.3755 11.7 13.878 12.18C13.3805 12.6333 12.7521 12.86 11.9928 12.86C11.0764 12.86 10.3171 12.5533 9.71484 11.94C9.13881 11.3266 8.85079 10.4467 8.85079 9.29999C8.85079 8.07332 9.19117 6.87332 9.87194 5.69999C10.5789 4.49999 11.5608 3.55332 12.8176 2.85999L13.7209 4.25999C13.0401 4.73999 12.4903 5.27332 12.0713 5.85999C11.6786 6.44666 11.4168 7.12666 11.2858 7.89999C11.5215 7.79332 11.7964 7.73999 12.1106 7.73999C12.8437 7.73999 13.446 7.97999 13.9173 8.45999C14.3886 8.93999 14.6242 9.55333 14.6242 10.3Z"
fill="currentColor"
/>
</svg>
<div className="relative z-10">
<p className="mb-3 text-xs font-semibold uppercase tracking-wide text-slate-500 dark:text-neutral-200">
Featured client
</p>
<p className="text-xl font-medium italic text-slate-800 md:text-2xl md:leading-normal xl:text-3xl xl:leading-normal dark:text-neutral-200">
Nx is speed and scalability. Before we only had a few features
and CI was slow and now its fast with way more features.
Thats a huge win for us..
</p>
</div>
<figcaption className="mt-6 flex flex-wrap items-center gap-4 sm:flex-nowrap">
<img
alt="pavlo grosse"
src="https://avatars.githubusercontent.com/u/2219064?v=4"
className="size-12 flex-none rounded-full bg-gray-50"
/>
<div className="flex-auto">
<div className="text-base font-semibold">Pavlo Grosse</div>
<div className="text-xs text-slate-600 dark:text-slate-500">
Senior Software Engineer, Hetzner Cloud
</div>
</div>
<HetznerCloudIcon
aria-hidden="true"
className="mx-auto size-10 flex-none bg-white text-[#D50C2D]"
/>
</figcaption>
<footer className="mt-8 flex items-center gap-6">
{/*<Button*/}
{/* title="Watch the customer story"*/}
{/* variant="secondary"*/}
{/* size="small"*/}
{/*>*/}
{/* <PlayIcon aria-hidden="true" className="size-5 shrink-0" />*/}
{/* <span>Watch the customer story</span>*/}
{/*</Button>*/}
<Link
href="/customers"
prefetch={false}
className="text-sm/6 font-semibold"
>
See our customers <span aria-hidden="true"></span>
</Link>
</footer>
</blockquote>
</figure>
</div>
</section>
</div>
);
}
@@ -0,0 +1,143 @@
import { ReactElement } from 'react';
import { SectionHeading } from '@nx/nx-dev/ui-common';
export function MakeYourCiFast(): ReactElement {
return (
<section className="make-your-ci-fast-section">
<div className="mx-auto max-w-2xl px-6 lg:max-w-7xl lg:px-8">
<SectionHeading id="make-your-ci-fast" as="h1" variant="title">
Make your CI fast,{' '}
<span className="rounded-lg bg-gradient-to-r from-cyan-500 to-blue-500 bg-clip-text text-transparent">
really fast
</span>
</SectionHeading>
<div className="mt-10 grid grid-cols-1 gap-4 sm:mt-16 lg:grid-cols-6">
<div className="relative lg:col-span-2">
<div className="relative flex h-full flex-col overflow-hidden rounded-[calc(theme(borderRadius.lg)+1px)] bg-white shadow ring-1 ring-black/5 lg:rounded-tl-[calc(2rem+1px)] dark:bg-slate-950 dark:ring-white/10">
<img
alt="Nx Replay: remote caching"
src="/images/enterprise/nx-replay.avif"
className="h-80 object-cover object-left"
/>
<div className="relative p-10 pt-4">
<div className="absolute -top-10 left-10">
<span className="inline-flex items-center rounded-md bg-slate-400/10 px-2 py-1 text-xs font-medium text-slate-400 ring-1 ring-inset ring-slate-400/20">
Nx Replay
</span>
</div>
<h3 className="text-lg font-medium tracking-tight text-slate-950 dark:text-white">
Never build the same code twice
</h3>
<p className="mt-2 max-w-lg text-sm/6">
Remote caching ensures that tasks are never rebuilt twice,
significantly reducing build times and resource usage. Share
cached results across your team and CI pipelines.
</p>
</div>
</div>
</div>
<div className="relative lg:col-span-2">
<div className="relative flex h-full flex-col overflow-hidden rounded-[calc(theme(borderRadius.lg)+1px)] bg-white shadow ring-1 ring-black/5 dark:bg-slate-950 dark:ring-white/10">
<img
alt="Nx flaky task detection & rerun"
src="/images/enterprise/nx-flaky-tasks-detection.avif"
className="h-80 object-cover"
/>
<div className="relative p-10 pt-4">
<div className="absolute -top-10 left-10">
<span className="inline-flex items-center rounded-md bg-slate-400/10 px-2 py-1 text-xs font-medium text-slate-400 ring-1 ring-inset ring-slate-400/20">
Flaky task retries
</span>
</div>
<h3 className="text-lg font-medium tracking-tight text-slate-950 dark:text-white">
One less thing to debug
</h3>
<p className="mt-2 max-w-lg text-sm/6">
Automatically detect and re-run flaky tasks, enhancing the
reliability of your CI processes and minimizing time spent
debugging.
</p>
</div>
</div>
</div>
<div className="relative lg:col-span-2">
<div className="relative flex h-full flex-col overflow-hidden rounded-[calc(theme(borderRadius.lg)+1px)] bg-white shadow ring-1 ring-black/5 max-lg:rounded-t-[calc(2rem+1px)] lg:rounded-tr-[calc(2rem+1px)] dark:bg-slate-950 dark:ring-white/10">
<img
alt="Nx Atomizer: split large tasks & E2E in chunks"
src="/images/enterprise/nx-atomizer.avif"
className="h-80 object-cover object-left"
/>
<div className="relative p-10 pt-4">
<div className="absolute -top-10 left-10">
<span className="inline-flex items-center rounded-md bg-slate-400/10 px-2 py-1 text-xs font-medium text-slate-400 ring-1 ring-inset ring-slate-400/20">
Atomizer
</span>
</div>
<h3 className="text-lg font-medium tracking-tight text-slate-950 dark:text-white">
Break tasks down, to speed tests up
</h3>
<p className="mt-2 max-w-lg text-sm/6">
<span className="font-semibold">Atomizer</span> automatically
splits large e2e tests into smaller, atomized tasks, enabling
lightning fast testing. Parallelize your tests to reduce
bottlenecks and keep your pipelines fast.
</p>
</div>
</div>
</div>
<div className="relative lg:col-span-3">
<div className="relative flex h-full flex-col overflow-hidden rounded-[calc(theme(borderRadius.lg)+1px)] bg-white shadow ring-1 ring-black/5 max-lg:rounded-b-[calc(2rem+1px)] lg:rounded-bl-[calc(2rem+1px)] dark:bg-slate-950 dark:ring-white/10">
<img
alt="Nx Agents: simple & fast task distribution"
src="/images/enterprise/nx-agents.avif"
className="h-80 object-cover object-left"
/>
<div className="relative p-10 pt-4">
<div className="absolute -top-10 left-10">
<span className="inline-flex items-center rounded-md bg-slate-400/10 px-2 py-1 text-xs font-medium text-slate-400 ring-1 ring-inset ring-slate-400/20">
Nx Agents
</span>
</div>
<h3 className="text-lg font-medium tracking-tight text-slate-950 dark:text-white">
Machines, make it fast!
</h3>
<p className="mt-2 max-w-lg text-sm/6">
<span className="font-semibold">Nx Agents</span> intelligently
distribute tasks across multiple machines, significantly
reducing build times. Dynamically allocate agents based on PR
size to balance speed and cost.
</p>
</div>
</div>
</div>
<div className="relative lg:col-span-3">
<div className="relative flex h-full flex-col overflow-hidden rounded-[calc(theme(borderRadius.lg)+1px)] bg-white shadow ring-1 ring-black/5 max-lg:rounded-b-[calc(2rem+1px)] lg:rounded-br-[calc(2rem+1px)] dark:bg-slate-950 dark:ring-white/10">
<img
alt="Partner with the Nx Team for guidances"
src="/images/enterprise/nx-partnership.avif"
className="h-80 object-cover object-left lg:object-right"
/>
<div className="relative p-10 pt-4">
<div className="absolute -top-10 left-10">
<span className="inline-flex items-center rounded-md bg-slate-400/10 px-2 py-1 text-xs font-medium text-slate-400 ring-1 ring-inset ring-slate-400/20">
Partnership
</span>
</div>
<h3 className="text-lg font-medium tracking-tight text-slate-950 dark:text-white">
Always thinking a step ahead for you
</h3>
<p className="mt-2 max-w-lg text-sm/6">
With Nx, you receive expert guidance from day one, ensuring
your setup is optimized for maximum efficiency. Whether you're
starting fresh, migrating, or scaling your developer platform,
we'll work with you to tailor the perfect solution for your
team.
</p>
</div>
</div>
</div>
</div>
</div>
</section>
);
}
@@ -1,185 +0,0 @@
import { motion, Variants } from 'framer-motion';
import { DownloadCaseStudy } from './download-case-study';
import {
BillColoredIcon,
CapitalOneIcon,
CaterpillarIcon,
CiscoIcon,
FicoIcon,
HiltonIcon,
ManIcon,
RoyalBankOfCanadaColoredIcon,
SevenElevenColoredIcon,
ShopifyIcon,
StorybookIcon,
VmwareIcon,
} from '@nx/nx-dev/ui-icons';
export function MetricsAndCustomers(): JSX.Element {
const downloadElement: Variants = {
hidden: {
opacity: 0,
translateY: 90,
},
visible: {
opacity: 1,
translateY: 0,
transition: {
duration: 1,
ease: 'easeInOut',
type: 'tween',
},
},
};
return (
<div className="relative isolate pb-24 pt-16">
<svg
className="absolute inset-0 -z-10 h-full w-full rotate-180 transform stroke-slate-100 [mask-image:radial-gradient(100%_100%_at_top,white,transparent)] dark:stroke-slate-800/60 dark:[mask-image:radial-gradient(100%_100%_at_top,black,transparent)]"
aria-hidden="true"
>
<defs>
<pattern
id="83dwp7e5a-9d52-45fc-17c6-718e5d7fe918"
width={200}
height={200}
x="50%"
y={-1}
patternUnits="userSpaceOnUse"
>
<path d="M100 200V.5M.5 .5H200" fill="none" />
</pattern>
</defs>
<svg
x="50%"
y={-1}
className="overflow-visible fill-slate-50/15 dark:fill-slate-900/10"
>
<path
d="M-100.5 0h201v201h-201Z M699.5 0h201v201h-201Z M499.5 400h201v201h-201Z M-300.5 600h201v201h-201Z"
strokeWidth={0}
/>
</svg>
<rect
width="100%"
height="100%"
strokeWidth={0}
fill="url(#83dwp7e5a-9d52-45fc-17c6-718e5d7fe918)"
/>
</svg>
<div className="mx-auto max-w-7xl px-6 lg:px-8">
<dl className="grid grid-cols-1 gap-4 text-center md:grid-cols-3">
<div className="p-4 text-xl">
<dt className="text-center text-xs uppercase">Speed</dt>
<dd className="mt-2 ">
<span className="text-3xl font-semibold text-slate-950 dark:text-slate-50">
30-70% Faster
</span>
</dd>
</div>
<div className="p-4 text-xl">
<dt className="text-center text-xs uppercase">Infra Cost</dt>
<dd className="mt-2 ">
<span className="text-3xl font-semibold text-slate-950 dark:text-slate-50">
40-75% Cheaper
</span>
</dd>
</div>
<div className="p-4 text-xl">
<dt className="text-center text-xs uppercase">Compute</dt>
<dd className="mt-2 ">
<span className="text-3xl font-semibold text-slate-950 dark:text-slate-50">
30-60% Less
</span>
</dd>
</div>
</dl>
<div className="mt-12 grid grid-cols-2 justify-between gap-2 md:mt-0 md:flex">
<div className="col-span-1 hidden h-14 items-center justify-center lg:flex lg:h-28">
<ManIcon aria-hidden="true" className="h-14 w-14 text-[#E40045]" />
</div>
<div className="col-span-1 flex h-14 items-center justify-center lg:h-28">
<CapitalOneIcon
aria-hidden="true"
className="h-28 w-28 text-black dark:text-white"
/>
</div>
<div className="col-span-1 hidden h-14 items-center justify-center lg:flex lg:h-28">
<ShopifyIcon
aria-hidden="true"
className="h-12 w-12 text-[#7AB55C]"
/>
</div>
<div className="col-span-1 flex h-14 items-center justify-center lg:h-28">
<RoyalBankOfCanadaColoredIcon
aria-hidden="true"
className="h-14 w-14"
/>
</div>
<div className="col-span-1 flex h-14 items-center justify-center lg:h-28">
<VmwareIcon
aria-hidden="true"
className="h-28 w-28 text-black dark:text-white"
/>
</div>
<div className="col-span-1 hidden h-14 items-center justify-center lg:flex lg:h-28">
<StorybookIcon
aria-hidden="true"
className="h-12 w-12 text-[#FF4785]"
/>
</div>
<div className="col-span-1 flex h-14 items-center justify-center lg:h-28">
<FicoIcon aria-hidden="true" className="h-28 w-28 text-[#0A6DE6]" />
</div>
<div className="col-span-1 hidden h-14 items-center justify-center lg:flex lg:h-28">
<CaterpillarIcon
aria-hidden="true"
className="h-14 w-14 text-[#FFCD11]"
/>
</div>
</div>
<div className="relative mt-12 flex">
<div className="hidden w-1/4 items-center gap-20 lg:flex">
<div className="col-span-1 hidden h-14 items-center justify-center lg:flex lg:h-28">
<CiscoIcon
aria-hidden="true"
className="h-16 w-16 text-[#1BA0D7]"
/>
</div>
<div className="col-span-1 hidden h-14 items-center justify-center lg:flex lg:h-28">
<BillColoredIcon aria-hidden="true" className="h-14 w-14" />
</div>
</div>
<div className="grow lg:w-1/2">
<motion.div
initial="hidden"
animate="visible"
variants={downloadElement}
whileInView="visible"
className="mx-auto max-w-xl"
>
<DownloadCaseStudy
title="Banking Case Study"
description="See how a $7B bank saved money, reduced CI times by 62% and improved developer productivity."
buttonHref="https://go.nx.dev/banking-case-study"
/>
</motion.div>
</div>
<div className="hidden w-1/4 items-center justify-end gap-20 lg:flex">
<div className="col-span-1 hidden h-14 items-center justify-center lg:flex lg:h-28">
<SevenElevenColoredIcon
aria-hidden="true"
className="h-14 w-14"
/>
</div>
<div className="col-span-1 hidden h-14 items-center justify-center lg:flex lg:h-28">
<HiltonIcon
aria-hidden="true"
className="h-20 w-20 text-black dark:text-white"
/>
</div>
</div>
</div>
</div>
</div>
);
}
@@ -0,0 +1,319 @@
import { ReactElement } from 'react';
import { SectionHeading, Strong } from '@nx/nx-dev/ui-common';
import {
AcademicCapIcon,
ArrowPathIcon,
CheckBadgeIcon,
CodeBracketSquareIcon,
DocumentMagnifyingGlassIcon,
DocumentTextIcon,
EyeIcon,
ShieldExclamationIcon,
UsersIcon,
} from '@heroicons/react/24/outline';
import Link from 'next/link';
export function ScaleYourOrganization(): ReactElement {
return (
<section className="overflow-hidden">
<div className="mx-auto max-w-7xl px-6 lg:px-8">
<div className="mx-auto grid max-w-2xl grid-cols-1 gap-x-8 gap-y-16 sm:gap-y-20 lg:mx-0 lg:max-w-none lg:grid-cols-2 lg:items-start">
<div className="flex items-start justify-end lg:order-first">
<img
alt="Nx Polygraph"
src="/images/enterprise/graphs.jpg"
width={3405}
height={1493}
className="w-[48rem] max-w-none rounded-xl shadow-xl ring-1 ring-slate-400/10 sm:w-[57rem] md:-mr-4 lg:mr-0"
/>
</div>
<div className="lg:ml-auto lg:pl-4 lg:pt-4">
<div className="lg:max-w-lg">
<SectionHeading as="p" variant="subtitle" className="mt-6">
Get to market faster by avoiding the most common pitfall of
growing teams: silos. Silos create waste, have poorly defined
ownership, lack visibility - and slow everything down.
</SectionHeading>
<SectionHeading as="p" variant="subtitle" className="mt-4">
Polygraph is a set of Nx Enterprise features built to help large
organizations{' '}
<Strong>
see across workspaces and take action to accelerate
time-to-market
</Strong>
.
</SectionHeading>
<figure className="mt-12 border-l border-slate-200 pl-8 dark:border-slate-800">
<blockquote className="text-base/7">
<p>
Nx means tooling and efficiency around our software
development lifecycle that empowers us to move a lot faster,
ship code faster and more reliably.
</p>
</blockquote>
<figcaption className="mt-6 flex items-center gap-x-4 text-sm/6">
<img
alt="Justin Schwartzenberger"
src="https://avatars.githubusercontent.com/u/1243236?v=4"
className="size-8 flex-none rounded-full"
/>
<div>
<div className="font-semibold">Justin Schwartzenberger</div>
<div className="text-slate-500">
Principal Software Engineer, SiriusXM
</div>
</div>
</figcaption>
</figure>
</div>
</div>
</div>
<div className="mx-auto mt-16 max-w-4xl sm:mt-20 lg:mt-24">
<dl className="mx-auto mt-10 flex flex-col">
<div className="group/section flex items-center gap-16">
<div className="hidden shrink-0 p-6 sm:p-12 lg:block">
<div className="relative grid size-12 place-items-center">
<EyeIcon
aria-hidden="true"
className="size-8 transition-all group-hover/section:-translate-x-2"
/>
<DocumentMagnifyingGlassIcon
aria-hidden="true"
className="absolute inset-0 size-8 opacity-0 transition-all group-hover/section:-translate-y-2 group-hover/section:translate-x-8 group-hover/section:opacity-100"
/>
<CodeBracketSquareIcon
aria-hidden="true"
className="absolute inset-0 size-8 opacity-0 transition-all group-hover/section:translate-x-7 group-hover/section:translate-y-6 group-hover/section:opacity-100"
/>
</div>
</div>
<div className="relative flex gap-x-4">
<div className="absolute -bottom-6 left-0 top-0 flex w-6 justify-center">
<div className="w-px bg-slate-200 dark:bg-slate-800" />
</div>
<div className="relative mt-1 flex size-6 flex-none items-center justify-center bg-white dark:bg-slate-950">
<div className="size-1.5 rounded-full bg-slate-100 ring-1 ring-slate-300 dark:bg-slate-800 dark:ring-slate-600" />
</div>
<div className="flex flex-col pb-10">
<dt className="text-base font-semibold leading-7 text-black dark:text-white">
<div className="mb-6 flex">
<div className="rounded-lg bg-blue-500 px-2 py-0.5 font-semibold text-white dark:bg-white dark:text-black">
Visibility
</div>
</div>
See dependencies across teams and repos
</dt>
<dd className="mt-1 flex flex-auto flex-col text-base leading-7">
<p className="flex-auto">
In an organization with hundreds of repos it can be hard
to understand the relationships between different parts of
your ever-growing codebases. With the Workspace Graph, see
dependencies across all your repos to understand which
projects and teams are affected by changes in a single
repo.
</p>
</dd>
</div>
</div>
</div>
<div className="group/section flex items-center gap-16">
<div className="hidden shrink-0 p-6 sm:p-12 lg:block">
<div className="relative grid size-12 place-items-center">
<CheckBadgeIcon
aria-hidden="true"
className="size-8 transition-all group-hover/section:-translate-x-2"
/>
<DocumentTextIcon
aria-hidden="true"
className="absolute inset-0 size-8 opacity-0 transition-all group-hover/section:-translate-y-2 group-hover/section:translate-x-8 group-hover/section:opacity-100"
/>
<ArrowPathIcon
aria-hidden="true"
className="absolute inset-0 size-8 opacity-0 transition-all group-hover/section:translate-x-7 group-hover/section:translate-y-6 group-hover/section:opacity-100"
/>
</div>
</div>
<div className="group/section relative flex gap-x-4">
<div className="absolute -bottom-6 left-0 top-0 flex w-6 justify-center">
<div className="w-px bg-slate-200 dark:bg-slate-800" />
</div>
<div className="relative mt-1 flex size-6 flex-none items-center justify-center bg-white dark:bg-slate-950">
<div className="size-1.5 rounded-full bg-slate-100 ring-1 ring-slate-300 dark:bg-slate-800 dark:ring-slate-600" />
</div>
<div className="flex flex-col pb-10">
<dt className="text-base font-semibold leading-7 text-black dark:text-white">
<div className="mb-6 flex">
<div className="rounded-lg bg-blue-500 px-2 py-0.5 font-semibold text-white dark:bg-white dark:text-black">
Conformance
</div>
</div>
Streamline onboarding and governance
</dt>
<dd className="mt-1 flex flex-auto flex-col text-base leading-7">
<p className="flex-auto">
Platform teams can easily publish and enforce coding
standards across an entire organization. Write and publish
rules, refactorings, and generators, then quickly deploy
them across all repos without involving individual teams.
Ensure security vulnerabilities are always addressed and
third-party dependencies stay up to date.{' '}
<i>
Included in{' '}
<Link
href="/powerpack"
title="Nx Powerpack"
prefetch={false}
className="underline"
>
Nx Powerpack
</Link>
, and available complimentary to all Enterprise
customers.
</i>
</p>
<ul className="mt-6 space-y-2 pl-4">
<li>
<Link
href="/ci/recipes/enterprise/conformance/configure-conformance-rules-in-nx-cloud"
prefetch={false}
title="Configure Conformance Rules"
className="text-sm/6 font-semibold"
>
Configure Conformance Rules{' '}
<span aria-hidden="true"></span>
</Link>
</li>
<li>
<Link
href="/ci/recipes/enterprise/conformance/publish-conformance-rules-to-nx-cloud"
prefetch={false}
title="Publish Conformance Rules"
className="text-sm/6 font-semibold"
>
Publish Conformance Rules{' '}
<span aria-hidden="true"></span>
</Link>
</li>
</ul>
</dd>
</div>
</div>
</div>
<div className="group/section flex items-center gap-16">
<div className="hidden shrink-0 p-6 sm:p-12 lg:block">
<div className="relative grid size-12 place-items-center">
<UsersIcon
aria-hidden="true"
className="size-8 transition-all group-hover/section:-translate-x-2"
/>
<AcademicCapIcon
aria-hidden="true"
className="absolute inset-0 size-8 opacity-0 transition-all group-hover/section:-translate-y-2 group-hover/section:translate-x-8 group-hover/section:opacity-100"
/>
<ShieldExclamationIcon
aria-hidden="true"
className="absolute inset-0 size-8 opacity-0 transition-all group-hover/section:translate-x-8 group-hover/section:translate-y-6 group-hover/section:opacity-100"
/>
</div>
</div>
<div className="relative flex gap-x-4">
<div className="absolute -bottom-6 left-0 top-0 flex w-6 justify-center">
<div className="w-px bg-slate-200 dark:bg-slate-800" />
</div>
<div className="relative mt-1 flex size-6 flex-none items-center justify-center bg-white dark:bg-slate-950">
<div className="size-1.5 rounded-full bg-slate-100 ring-1 ring-slate-300 dark:bg-slate-800 dark:ring-slate-600" />
</div>
<div className="flex flex-col pb-10">
<dt className="text-base font-semibold leading-7 text-black dark:text-white">
<div className="mb-6 flex">
<div className="rounded-lg bg-blue-500 px-2 py-0.5 font-semibold text-white dark:bg-white dark:text-black">
Ownership
</div>
</div>
Clear, expressive ownership
</dt>
<dd className="mt-1 flex flex-auto flex-col text-base leading-7">
<p className="flex-auto">
Unlike the code ownership tools from VCS providers, Nx
Owners is built for monorepos. Define ownership at the
project level. Nx will compile it back to the file-based
rules your VCS providers understand.
</p>
<p>
Polygraph lets you see owners across all your repositories
and plan out multi repo refactorings and migrations.{' '}
<i>
Included in{' '}
<Link
href="/powerpack"
title="Nx Powerpack"
className="underline"
>
Nx Powerpack
</Link>
, and available complimentary to all Enterprise
customers.
</i>
</p>
</dd>
</div>
</div>
</div>
</dl>
</div>
</div>
</section>
);
}
export function ScaleOrganizationIntro(): ReactElement {
return (
<section className="relative isolate px-6 py-24 sm:py-32 lg:px-8">
<svg
focusable={false}
aria-hidden="true"
className="absolute inset-0 -z-10 h-full w-full stroke-black/10 [mask-image:radial-gradient(100%_100%_at_top_right,white,transparent)] dark:stroke-white/20"
>
<defs>
<pattern
id="1d4240dd-898f-445f-932d-e2872fd12de3"
width={200}
height={200}
x="50%"
y={0}
patternUnits="userSpaceOnUse"
>
<path d="M.5 200V.5H200" fill="none" />
</pattern>
</defs>
<svg
x="50%"
y={0}
className="overflow-visible fill-slate-200/20 dark:fill-slate-800/60"
>
<path
d="M-200 0h201v201h-201Z M600 0h201v201h-201Z M-400 600h201v201h-201Z M200 800h201v201h-201Z"
strokeWidth={0}
/>
</svg>
<rect
width="100%"
height="100%"
strokeWidth={0}
fill="url(#1d4240dd-898f-445f-932d-e2872fd12de3)"
/>
</svg>
<div className="mx-auto max-w-5xl text-center">
<SectionHeading as="h2" variant="title" id="scale-your-organization">
Dont lose velocity as your organization grows{' '}
<br className="xl:block" />
<span className="line-through">10x developer</span>{' '}
<span className="rounded-lg bg-gradient-to-r from-cyan-500 to-blue-500 bg-clip-text text-transparent">
Nx developers are even more efficient at scale
</span>
</SectionHeading>
</div>
</section>
);
}
@@ -1,255 +0,0 @@
import {
BuildingOffice2Icon,
Cog6ToothIcon,
CubeTransparentIcon,
IdentificationIcon,
SquaresPlusIcon,
UserGroupIcon,
} from '@heroicons/react/24/outline';
import { SectionHeading } from '@nx/nx-dev/ui-common';
export function ScaleYourPeople(): JSX.Element {
return (
<section id="scale-your-people">
<div className="mx-auto max-w-7xl px-6 lg:px-8">
<div className="mx-auto max-w-3xl text-center">
<SectionHeading as="h2" variant="title">
Scale your people
</SectionHeading>
</div>
<SectionHeading as="p" variant="subtitle" className="mt-6">
Build big things with the efficiency of a small team by increasing
collaboration and developer mobility, reducing wait time and
duplication, and establishing clear ownership.
</SectionHeading>
<div className="mx-auto mt-12 grid max-w-2xl grid-cols-1 gap-2 md:gap-y-16 lg:max-w-none lg:grid-cols-4">
<div className="relative rounded-md bg-slate-100 px-4 py-3 text-slate-900 dark:bg-slate-800 dark:text-slate-100">
<div className="flex items-center gap-3 text-lg font-medium leading-6">
<CubeTransparentIcon
aria-hidden="true"
className="h-5 w-5 flex-none"
/>
Visibility
</div>
<svg
aria-hidden="true"
viewBox="0 0 4 12"
fill="currentColor"
className="absolute right-0 top-1/2 hidden h-6 w-2 -translate-y-1/2 translate-x-full transform text-slate-100 lg:block dark:text-slate-800"
>
<path
d="M3.26 4.9a2 2 0 0 1 0 2.2L0 12V0l3.26 4.9z"
fillRule="evenodd"
/>
</svg>
</div>
<div className="relative rounded-md bg-slate-100 px-4 py-3 text-slate-900 dark:bg-slate-800 dark:text-slate-100">
<svg
aria-hidden="true"
viewBox="0 0 4 12"
fill="currentColor"
className="absolute left-0 top-1/2 hidden h-6 w-2 -translate-y-1/2 transform text-white lg:block dark:text-slate-950"
>
<path
d="M3.26 4.9a2 2 0 0 1 0 2.2L0 12V0l3.26 4.9z"
fillRule="evenodd"
/>
</svg>
<div className="flex items-center gap-3 text-lg font-medium leading-6">
<IdentificationIcon
aria-hidden="true"
className="h-5 w-5 flex-none"
/>
Ownership
</div>
<svg
aria-hidden="true"
viewBox="0 0 4 12"
fill="currentColor"
className="absolute right-0 top-1/2 hidden h-6 w-2 -translate-y-1/2 translate-x-full transform text-slate-100 lg:block dark:text-slate-800"
>
<path
d="M3.26 4.9a2 2 0 0 1 0 2.2L0 12V0l3.26 4.9z"
fillRule="evenodd"
/>
</svg>
</div>
<div className="relative rounded-md bg-slate-100 px-4 py-3 text-slate-900 dark:bg-slate-800 dark:text-slate-100">
<svg
aria-hidden="true"
viewBox="0 0 4 12"
fill="currentColor"
className="absolute left-0 top-1/2 hidden h-6 w-2 -translate-y-1/2 transform text-white lg:block dark:text-slate-950"
>
<path
d="M3.26 4.9a2 2 0 0 1 0 2.2L0 12V0l3.26 4.9z"
fillRule="evenodd"
/>
</svg>
<div className="flex items-center gap-3 text-lg font-medium leading-6">
<UserGroupIcon aria-hidden="true" className="h-5 w-5 flex-none" />
Control
</div>
<svg
aria-hidden="true"
viewBox="0 0 4 12"
fill="currentColor"
className="absolute right-0 top-1/2 hidden h-6 w-2 -translate-y-1/2 translate-x-full transform text-slate-100 lg:block dark:text-slate-800"
>
<path
d="M3.26 4.9a2 2 0 0 1 0 2.2L0 12V0l3.26 4.9z"
fillRule="evenodd"
/>
</svg>
</div>
<div className="relative rounded-md bg-slate-100 px-4 py-3 text-slate-900 dark:bg-slate-800 dark:text-slate-100">
<svg
aria-hidden="true"
viewBox="0 0 4 12"
fill="currentColor"
className="absolute left-0 top-1/2 hidden h-6 w-2 -translate-y-1/2 transform text-white lg:block dark:text-slate-950"
>
<path
d="M3.26 4.9a2 2 0 0 1 0 2.2L0 12V0l3.26 4.9z"
fillRule="evenodd"
/>
</svg>
<div className="flex items-center gap-3 text-lg font-medium leading-6">
<Cog6ToothIcon aria-hidden="true" className="h-5 w-5 flex-none" />
Automation
</div>
</div>
</div>
<picture className="block py-12">
<img
src="/images/enterprise/graphs.jpg"
alt="Product screenshot"
className="mx-auto max-w-full rounded-xl shadow-xl ring-1 ring-slate-400/10"
width={2500}
height={1616}
/>
</picture>
<div className="relative mx-auto mt-16 max-w-2xl space-y-12 sm:mt-20">
<svg
className="absolute left-0 top-0 -z-10 -ml-20 hidden -translate-x-full -translate-y-1/2 transform lg:block"
width={200}
height={400}
fill="none"
viewBox="0 0 200 400"
aria-hidden="true"
>
<defs>
<pattern
id="de316486-4a29-4312-bdfc-fbce2132a2c1"
x={0}
y={0}
width={20}
height={20}
patternUnits="userSpaceOnUse"
>
<rect
x={0}
y={0}
width={4}
height={4}
className="text-slate-100 dark:text-slate-800/60"
fill="currentColor"
/>
</pattern>
</defs>
<rect
width={200}
height={400}
fill="url(#de316486-4a29-4312-bdfc-fbce2132a2c1)"
/>
</svg>
<svg
className="absolute bottom-0 right-0 -z-10 -mr-20 hidden translate-x-full translate-y-1/2 transform lg:block"
width={200}
height={400}
fill="none"
viewBox="0 0 200 400"
aria-hidden="true"
>
<defs>
<pattern
id="de316486-4a29-4312-bdfc-fbce2132a2c1"
x={0}
y={0}
width={20}
height={20}
patternUnits="userSpaceOnUse"
>
<rect
x={0}
y={0}
width={4}
height={4}
className="text-slate-100 dark:text-slate-800/60"
fill="currentColor"
/>
</pattern>
</defs>
<rect
width={200}
height={400}
fill="url(#de316486-4a29-4312-bdfc-fbce2132a2c1)"
/>
</svg>
<div className="space-y-6 lg:space-y-12">
<div className="flex justify-center">
<span className="inline-flex items-center rounded-md bg-blue-50 px-2 py-1 text-xs font-medium text-blue-700 ring-1 ring-inset ring-blue-700/10 dark:bg-blue-400/10 dark:text-blue-400 dark:ring-blue-400/30">
coming soon
</span>
</div>
<div className="flex items-start gap-6">
<div className="rounded-full p-3 shadow-sm ring-1 ring-slate-200 dark:ring-slate-800/60">
<SquaresPlusIcon className="h-5 w-5 text-slate-900 dark:text-slate-100" />
</div>
<div>
<h4 className="relative text-base font-medium leading-6 text-slate-900 dark:text-slate-100">
Monorepo, polyrepo, multi-monorepo?
</h4>
<p className="mt-2">
Whatever youre working with, Nx Enterprise will give you the
visibility you need to understand what they have in common,
how they relate, and how they differ.
</p>
</div>
</div>
<div className="flex items-start gap-6">
<div className="rounded-full p-3 shadow-sm ring-1 ring-slate-200 dark:ring-slate-800/60">
<Cog6ToothIcon className="h-5 w-5 text-slate-900 dark:text-slate-100" />
</div>
<div>
<h4 className="relative text-base font-medium leading-6 text-slate-900 dark:text-slate-100">
Monorepo experience in a polyrepo environment
</h4>
<p className="mt-2">
Nx Enterprise will support optional monorepo-like constraints
to be applied across Nx Workspace boundaries in a seamless and
flexible way. Move fast with confidence.
</p>
</div>
</div>
<div className="flex items-start gap-6">
<div className="rounded-full p-3 shadow-sm ring-1 ring-slate-200 dark:ring-slate-800/60">
<BuildingOffice2Icon className="h-5 w-5 text-slate-900 dark:text-slate-100" />
</div>
<div>
<h4 className="relative text-base font-medium leading-6 text-slate-900 dark:text-slate-100">
Automation over coordination
</h4>
<p className="mt-2">
Testing and cross-repo coordination can be left to Nx
Enterprise tooling instead of manual human intervention to
test and enforce cross-repo dependencies & constraints.
</p>
</div>
</div>
</div>
</div>
</div>
</section>
);
}
File diff suppressed because one or more lines are too long
@@ -1,849 +0,0 @@
import {
ArrowsRightLeftIcon,
BanknotesIcon,
BoltIcon,
ChartBarSquareIcon,
ChevronRightIcon,
ClipboardDocumentIcon,
CloudArrowDownIcon,
CursorArrowRaysIcon,
LightBulbIcon,
SparklesIcon,
Square3Stack3DIcon,
} from '@heroicons/react/24/outline';
import { animate, motion, useMotionValue, useTransform } from 'framer-motion';
import { useEffect } from 'react';
import { BentoGrid, BentoGridItem } from './bento-grid';
import { cx } from '@nx/nx-dev/ui-primitives';
import { SectionHeading } from '@nx/nx-dev/ui-common';
import Link from 'next/link';
export function SolveYourCi(): JSX.Element {
return (
<section id="solve-your-ci">
<div className="mx-auto max-w-7xl px-6 lg:px-8">
<div className="mx-auto max-w-2xl text-center">
<SectionHeading as="h2" variant="title">
Solve your CI
</SectionHeading>
</div>
<SectionHeading as="p" variant="subtitle" className="mt-6">
Monorepos help you scale your people but they can also make CI a
challenge. Nx Enterprise solves it by providing efficient, fast and
reliable CI that can handle workflows of any size.
</SectionHeading>
{/*FEATURES CONTAINER*/}
<BentoGrid className="mx-auto mt-20 w-full md:auto-rows-[22rem]">
{items.map((item, i) => (
<BentoGridItem
key={i}
title={item.title}
description={item.description}
header={item.header}
className={cx('[&>p:text-lg]', item.className)}
icon={item.icon}
url={item.url}
/>
))}
</BentoGrid>
{/*TEXT*/}
<div className="relative mx-auto mt-16 max-w-2xl space-y-12 sm:my-32">
<svg
className="absolute left-0 top-0 -z-10 -ml-20 hidden -translate-x-full -translate-y-1/2 transform lg:block"
width={200}
height={400}
fill="none"
viewBox="0 0 200 400"
aria-hidden="true"
>
<defs>
<pattern
id="de316486-4a29-4312-bdfc-fbce2132a2c1"
x={0}
y={0}
width={20}
height={20}
patternUnits="userSpaceOnUse"
>
<rect
x={0}
y={0}
width={4}
height={4}
className="text-slate-100 dark:text-slate-800/60"
fill="currentColor"
/>
</pattern>
<pattern
id="de316486-4a29-4312-bdfc-fbce2132a2c1"
x={0}
y={0}
width={20}
height={20}
patternUnits="userSpaceOnUse"
>
<rect
x={0}
y={0}
width={4}
height={4}
className="text-pink-200 dark:text-slate-800/60"
fill="currentColor"
/>
</pattern>
</defs>
<rect
width={200}
height={400}
fill="url(#de316486-4a29-4312-bdfc-fbce2132a2c1)"
/>
</svg>
<svg
className="absolute bottom-0 right-0 -z-10 -mr-20 hidden translate-x-full translate-y-1/2 transform lg:block"
width={200}
height={400}
fill="none"
viewBox="0 0 200 400"
aria-hidden="true"
>
<defs>
<pattern
id="de316486-4a29-4312-bdfc-fbce2132a2c1"
x={0}
y={0}
width={20}
height={20}
patternUnits="userSpaceOnUse"
>
<rect
x={0}
y={0}
width={4}
height={4}
className="text-slate-100 dark:text-slate-800/60"
fill="currentColor"
/>
</pattern>
</defs>
<rect
width={200}
height={400}
fill="url(#de316486-4a29-4312-bdfc-fbce2132a2c1)"
/>
</svg>
<div className="space-y-6 lg:space-y-12">
<div className="flex items-start gap-6">
<div className="rounded-full p-3 shadow-sm ring-1 ring-slate-200 dark:ring-slate-800/60">
<BanknotesIcon className="h-5 w-5 text-slate-900 dark:text-slate-100" />
</div>
<div>
<h4 className="relative text-base font-medium leading-6 text-slate-900 dark:text-slate-100">
Both faster and cheaper
</h4>
<p className="mt-2">
Current CI providers arent made for monorepos. Theyre
low-level and static in their configuration. The combination
of Nx Agents and Nx Replay lets you reuse computation from
other runs and utilizes the allocated VMs in the most optimal
way.
</p>
</div>
</div>
<div className="flex items-start gap-6">
<div className="rounded-full p-3 shadow-sm ring-1 ring-slate-200 dark:ring-slate-800/60">
<CursorArrowRaysIcon className="h-5 w-5 text-slate-900 dark:text-slate-100" />
</div>
<div>
<h4 className="relative text-base font-medium leading-6 text-slate-900 dark:text-slate-100">
Solving E2E tests
</h4>
<p className="mt-2">
Atomizer splits large e2e projects into fine-grained test
runs, enabling more efficient distribution and dramatically
reducing CI times. It also identifies flaky e2e tests at the
file level and re-runs those specific tests.
</p>
</div>
</div>
<div className="flex items-start gap-6">
<div className="rounded-full p-3 shadow-sm ring-1 ring-slate-200 dark:ring-slate-800/60">
<LightBulbIcon className="h-5 w-5 text-slate-900 dark:text-slate-100" />
</div>
<div>
<h4 className="relative text-base font-medium leading-6 text-slate-900 dark:text-slate-100">
What, not how
</h4>
<p className="mt-2">
Nx Enterprise simplifies CI configuration, emphasizing which
tasks to execute over how with no need to tweak your CI
scripts as your monorepo evolves. This simplified
configuration cuts down on CI maintenance and increases
stability.
</p>
</div>
</div>
<div className="flex items-start gap-6">
<div className="rounded-full p-3 shadow-sm ring-1 ring-slate-200 dark:ring-slate-800/60">
<BoltIcon className="h-5 w-5 text-slate-900 dark:text-slate-100" />
</div>
<div>
<h4 className="relative text-base font-medium leading-6 text-slate-900 dark:text-slate-100">
Nx Powerpack included
</h4>
<p className="mt-2">
A suite of paid extensions for the Nx CLI specifically
designed for enterprises, built and supported by the Nx core
team.{' '}
<Link
href="/powerpack"
title="Learn more about Nx Powerpack"
className="font-semibold underline"
>
Learn more about Nx Powerpack
</Link>
</p>
</div>
</div>
</div>
</div>
</div>
</section>
);
}
const Caching = () => {
const variants = {
hidden: {
opacity: 0,
transition: {
when: 'afterChildren',
},
},
visible: {
opacity: 1,
},
};
const items = [
{
cache: 'remote',
target: 'build',
project: 'website',
},
{
cache: 'remote',
target: 'test',
project: 'express',
},
{
cache: 'remote',
target: 'build',
project: 'eslint',
},
{
cache: 'remote',
target: 'lint',
project: 'autoloan',
},
{
cache: 'remote',
target: 'test',
project: 'website',
},
{
cache: 'remote',
target: 'lint',
project: 'website',
},
{
cache: 'remote',
target: 'build-base',
project: 'express',
},
{
cache: 'remote',
target: 'build',
project: 'express',
},
{
cache: 'remote',
target: 'lint',
project: 'express',
},
{
cache: 'remote',
target: 'test',
project: 'autoloan',
},
];
return (
<motion.div
initial="hidden"
variants={variants}
whileInView="visible"
viewport={{ once: true }}
className="flex h-full min-h-[12rem] w-full flex-1 flex-col gap-2"
>
<motion.div className="flex items-center gap-1.5 text-center text-sm italic">
<SparklesIcon aria-hidden="true" className="h-4 w-4" />
<span className="font-semibold">
<Counter value={items.length + 123} duration={5} />
</span>
tasks replayed instantly with cache
</motion.div>
<div className="flex flex-1 flex-col divide-y divide-slate-100 overflow-hidden rounded-lg border border-slate-100 dark:divide-slate-700 dark:border-slate-700 dark:bg-slate-950">
{items.map((i, idx) => (
<div
key={`project-${i}-${idx}`}
className="flex w-full flex-row items-center gap-2 p-2 transition-colors ease-out hover:bg-slate-50/40 dark:hover:bg-slate-900/40"
>
<div className="m-1 h-2.5 w-2.5 flex-none rounded-full bg-emerald-500" />
<div className="flex min-w-[4rem]">
<span className="inline-flex cursor-default items-center rounded-md bg-slate-50 px-2 py-1 text-xs font-medium text-slate-600 ring-1 ring-inset ring-slate-500/10 dark:bg-slate-400/10 dark:text-slate-400 dark:ring-slate-400/20">
{i.cache}
</span>
</div>
<div
className="grow truncate text-left text-sm font-medium"
data-testid="task-target"
>
{i.project}:{i.target}
</div>
<div className="text-xs">&lt; 1s</div>
</div>
))}
</div>
</motion.div>
);
};
const Atomizer = () => {
const variants = {
hidden: {
opacity: 0,
transition: {
when: 'afterChildren',
},
},
visible: {
opacity: 1,
},
};
const itemVariants = {
visible: (i: number) => ({
opacity: 1,
x: 0,
transition: {
delay: i * 0.2,
duration: 0.275,
ease: 'easeOut',
when: 'beforeChildren',
staggerChildren: 0.3,
},
}),
hidden: {
opacity: 0,
x: -100,
transition: {
when: 'afterChildren',
},
},
};
const uiDialogsTests = ['e2e-ui-cdk:e2e', 'e2e-ui-layout:e2e'];
const loansTests = [
'loans-front-store:e2e',
'loans-loans:e2e',
'loans-credit-card:e2e',
'loans-workflows:e2e',
'loans-mortgage:e2e',
'loans-submission:e2e',
];
const DefaultConnector = () => (
<>
<span
className="absolute left-4 top-0 -ml-px -mt-px h-[105%] w-0.5 bg-slate-100 dark:bg-slate-700"
aria-hidden="true"
/>
<span
className="absolute left-4 top-1/2 -ml-px h-0.5 w-3 bg-slate-100 dark:bg-slate-700"
aria-hidden="true"
/>
</>
);
const BottomConnector = () => (
<>
<span
className="absolute left-4 top-0 -ml-px -mt-px h-[55%] w-0.5 bg-slate-100 dark:bg-slate-700"
aria-hidden="true"
/>
<span
className="absolute left-4 top-1/2 -ml-px h-0.5 w-3 bg-slate-100 dark:bg-slate-700"
aria-hidden="true"
/>
</>
);
return (
<motion.div
initial="hidden"
variants={variants}
whileInView="visible"
viewport={{ once: true }}
className="flex h-full min-h-[12rem] w-full flex-1 flex-col gap-2"
>
<motion.div className="flex items-center gap-1.5 text-center text-sm">
Running
<ChevronRightIcon aria-hidden="true" className="h-3 w-3" />
<code className="text-xs font-medium">nx affected --targets=e2e</code>
</motion.div>
<div className="flex flex-1 flex-col divide-y divide-slate-100 overflow-hidden rounded-lg border border-slate-100 bg-white dark:divide-slate-700 dark:border-slate-700 dark:bg-slate-950">
<div className="flex w-full flex-row items-center gap-2 p-2 transition-colors ease-out hover:bg-slate-50/40 dark:hover:bg-slate-900/40">
<div className="flex items-center gap-2">
<div className="flex-none animate-pulse rounded-full bg-yellow-500/20 p-1">
<div className="h-2 w-2 rounded-full bg-yellow-500" />
</div>
<div className="flex min-w-[5rem]">
<span className="whitespace-nowrap text-sm font-medium">
In progress
</span>
</div>
</div>
<div className="grow truncate text-left text-sm font-medium">
E2E {'>'} CI
</div>
</div>
{uiDialogsTests.map((i, idx) => (
<div
key={`${i}-${idx}`}
className="relative flex w-full flex-row items-center gap-2 p-2 transition-colors ease-out hover:bg-slate-50/40 dark:hover:bg-slate-900/40"
>
{uiDialogsTests.length === idx + 1 ? (
<BottomConnector />
) : (
<DefaultConnector />
)}
<span aria-hidden="true" className="h-4 w-4" />
<div className="flex-none animate-pulse rounded-full bg-yellow-500/20 p-1">
<div className="h-2 w-2 rounded-full bg-yellow-500" />
</div>
<div className="grow truncate text-left text-sm">{i}</div>
</div>
))}
<motion.div
custom={1}
variants={itemVariants}
className="flex w-full flex-row items-center gap-2 p-2 transition-colors ease-out hover:bg-slate-50/40 dark:hover:bg-slate-900/40"
>
<div className="m-1 h-2.5 w-2.5 flex-none rounded-full bg-emerald-500" />
<div className="flex min-w-[5rem]">
<span className="inline-flex cursor-default items-center rounded-md bg-slate-50 px-2 py-1 text-xs font-medium text-slate-600 ring-1 ring-inset ring-slate-500/10 dark:bg-slate-400/10 dark:text-slate-400 dark:ring-slate-400/20">
miss
</span>
</div>
<div className="grow truncate text-left text-sm font-medium">
website:e2e
</div>
<motion.span
custom={3}
variants={itemVariants}
className="inline-flex cursor-default items-center rounded-md bg-yellow-400/10 px-2 py-1 text-xs font-medium text-yellow-500 ring-1 ring-inset ring-yellow-400/20 dark:bg-yellow-400/10 dark:text-yellow-500 dark:ring-yellow-400/20"
>
flaky
</motion.span>
<motion.span
custom={2}
variants={itemVariants}
className="inline-flex cursor-default items-center rounded-md bg-slate-50 px-2 py-1 text-xs font-medium text-slate-600 ring-1 ring-inset ring-slate-500/10 dark:bg-slate-400/10 dark:text-slate-400 dark:ring-slate-400/20"
>
1 retry
</motion.span>
</motion.div>
<div className="flex w-full flex-row items-center gap-2 p-2 transition-colors ease-out hover:bg-slate-50/40 dark:hover:bg-slate-900/40">
<div className="flex items-center gap-2">
<div className="flex-none animate-pulse rounded-full bg-yellow-500/20 p-1">
<div className="h-2 w-2 rounded-full bg-yellow-500" />
</div>
<div className="flex min-w-[5rem]">
<span className="whitespace-nowrap text-sm font-medium">
In progress
</span>
</div>
</div>
<div className="grow truncate text-left text-sm font-medium">
loans {'>'} e2e
</div>
</div>
{loansTests.map((i, idx) => (
<div
key={`${i}-${idx}`}
className="relative flex w-full flex-row items-center gap-2 p-2 transition-colors ease-out hover:bg-slate-50/40 dark:hover:bg-slate-900/40"
>
{loansTests.length === idx + 1 ? (
<BottomConnector />
) : (
<DefaultConnector />
)}
<span aria-hidden="true" className="h-4 w-4" />
<div className="flex-none animate-pulse rounded-full bg-yellow-500/20 p-1">
<div className="h-2 w-2 rounded-full bg-yellow-500" />
</div>
<div className="grow truncate text-left text-sm">{i}</div>
</div>
))}
</div>
</motion.div>
);
};
const TaskDistribution = () => {
const variants = {
hidden: {
opacity: 0,
transition: {
when: 'afterChildren',
},
},
visible: {
opacity: 1,
},
};
const itemVariants = {
visible: (i: number) => ({
opacity: 1,
x: 0,
transition: {
delay: i * 0.2,
duration: 0.275,
ease: 'easeOut',
when: 'beforeChildren',
staggerChildren: 0.3,
},
}),
hidden: {
opacity: 0,
x: -100,
transition: {
when: 'afterChildren',
},
},
};
const agent1Items = ['website:build-base', 'website:build'];
const agent2Items = ['docs:lint', 'express:test', 'website:lint'];
const agent3Items = ['graph-client:build', 'plugin:test'];
const nxReplayItems = [
'graph-client:lint',
'plugin:lint',
'website:test',
'vite:test',
'vite:build',
];
const notStartedTasks = ['js:build', 'js:lint'];
return (
<motion.div
initial="hidden"
variants={variants}
whileInView="visible"
viewport={{ once: true }}
className="relative flex h-full min-h-[12rem] w-full flex-1 flex-col gap-2"
>
<div className="grid max-h-full grid-cols-2 items-stretch gap-8 overflow-hidden p-1 lg:grid-cols-3">
<motion.div
custom={2}
variants={itemVariants}
className="relative overflow-x-hidden rounded-lg bg-white p-2 ring-1 ring-slate-100 dark:bg-slate-950 dark:ring-slate-700"
>
<p className="text-sm font-medium text-slate-700 dark:text-slate-400">
Main Workflow
</p>
<div className="mt-2 flex flex-col gap-1">
<p className="overflow-x-auto truncate py-2 font-mono text-xs font-medium text-slate-900 dark:text-slate-400">
nx affected --target=build,lint,test
</p>
<div className="flex h-1.5 w-full flex-row rounded-full">
<div
title="2 tasks in completed"
className="cursor-pointer rounded-l-full bg-green-400 dark:bg-green-600"
style={{ flexGrow: 8 }}
/>
<div
title="12 tasks in progress"
className="cursor-pointer bg-yellow-400 dark:bg-yellow-600"
style={{ flexGrow: 12 }}
/>
<div
title="24 tasks not started"
className="cursor-pointer rounded-r-full bg-slate-100 dark:bg-slate-600"
style={{ flexGrow: 24 }}
/>
</div>
<div className="mt-2 flex flex-1 flex-col divide-y divide-slate-100 overflow-auto rounded-lg border border-slate-100 dark:divide-slate-700 dark:border-slate-700">
{notStartedTasks.map((i, idx) => (
<motion.div
key={`${i}-${idx}`}
custom={idx + 3}
variants={itemVariants}
className="flex w-full flex-row items-center gap-2 p-2 transition-colors ease-out hover:bg-slate-50/40 dark:hover:bg-slate-800/40"
>
<div className="text-xs">{i}</div>
</motion.div>
))}
</div>
</div>
</motion.div>
<motion.div
custom={3}
variants={itemVariants}
className="relative flex flex-col gap-2 overflow-x-hidden rounded-lg bg-white p-2 ring-1 ring-slate-100 dark:bg-slate-950 dark:ring-slate-700"
>
<p className="text-sm font-medium text-slate-700 dark:text-slate-400">
Nx Agents
</p>
<div className="flex flex-1 flex-col overflow-hidden">
<motion.div custom={4} variants={itemVariants} className="relative">
<div className="sticky top-0 z-10 mt-1 rounded-md border border-slate-100 bg-slate-50 p-2 transition hover:bg-slate-100 dark:border-slate-800 dark:bg-slate-900 dark:hover:bg-slate-800">
<div className="flex items-center gap-x-2 text-xs font-medium">
<div className="flex-none animate-pulse rounded-full bg-yellow-500/20 p-1">
<div className="h-2 w-2 rounded-full bg-yellow-500"></div>
</div>
Agent 1<span className="flex-grow"></span>
<span className="mr-1 opacity-80">{agent1Items.length}</span>
</div>
</div>
<ul className="my-2 overflow-y-auto overflow-x-hidden">
{agent1Items.map((i, idx) => (
<motion.li
key={`agent-${i}-${idx}`}
custom={idx + 5}
variants={itemVariants}
className="truncate p-1 pl-4 text-xs"
>
{i}
</motion.li>
))}
</ul>
</motion.div>
<motion.div custom={6} variants={itemVariants} className="relative">
<div className="sticky top-0 z-10 mt-1 rounded-md border border-slate-100 bg-slate-50 p-2 transition hover:bg-slate-100 dark:border-slate-800 dark:bg-slate-900 dark:hover:bg-slate-800">
<div className="flex items-center gap-x-2 text-xs font-medium">
<div className="flex-none animate-pulse rounded-full bg-yellow-500/20 p-1">
<div className="h-2 w-2 rounded-full bg-yellow-500"></div>
</div>
Agent 2<span className="flex-grow"></span>
<span className="mr-1 opacity-80">{agent2Items.length}</span>
</div>
</div>
<ul className="my-2 overflow-y-auto overflow-x-hidden">
{agent2Items.map((i, idx) => (
<motion.li
key={`agent-${i}-${idx}`}
custom={idx + 7}
variants={itemVariants}
className="truncate p-1 pl-4 text-xs"
>
{i}
</motion.li>
))}
</ul>
</motion.div>
<motion.div
custom={8}
variants={itemVariants}
className="relative hidden lg:block"
>
<div className="sticky top-0 z-10 mt-1 rounded-md border border-slate-100 bg-slate-50 p-2 transition hover:bg-slate-100 dark:border-slate-800 dark:bg-slate-900 dark:hover:bg-slate-800">
<div className="flex items-center gap-x-2 text-xs font-medium">
<div className="flex-none animate-pulse rounded-full bg-yellow-500/20 p-1">
<div className="h-2 w-2 rounded-full bg-yellow-500"></div>
</div>
Agent 3<span className="flex-grow"></span>
<span className="mr-1 opacity-80">{agent3Items.length}</span>
</div>
</div>
<ul className="my-2 overflow-y-auto overflow-x-hidden">
{agent3Items.map((i, idx) => (
<motion.li
key={`agent-${i}-${idx}`}
custom={idx + 9}
variants={itemVariants}
className="truncate p-1 pl-4 text-xs"
>
{i}
</motion.li>
))}
</ul>
</motion.div>
</div>
</motion.div>
<motion.div
custom={4}
variants={itemVariants}
className="relative hidden flex-col gap-2 overflow-x-hidden rounded-lg bg-white p-2 ring-1 ring-slate-100 lg:flex dark:bg-slate-950 dark:ring-slate-700"
>
<p className="text-sm font-medium text-slate-700 dark:text-slate-400">
Nx Replay
</p>
<div className="flex flex-1 flex-col divide-y divide-slate-100 overflow-auto rounded-lg border border-slate-100 dark:divide-slate-800 dark:border-slate-800">
{nxReplayItems.map((i, idx) => (
<motion.div
key={`replay-${i}-${idx}`}
custom={idx + 10}
variants={itemVariants}
className="flex w-full flex-row items-center gap-2 p-2 text-xs transition-colors ease-out hover:bg-slate-50/40 dark:hover:bg-slate-800/40"
>
{i}
</motion.div>
))}
</div>
</motion.div>
</div>
</motion.div>
);
};
const IncreasedVisibility = () => {
const variants = {
hidden: {
opacity: 0,
transition: {
when: 'afterChildren',
},
},
visible: {
opacity: 1,
},
};
return (
<motion.div
initial="hidden"
variants={variants}
whileInView="visible"
viewport={{ once: true }}
className="flex h-full min-h-[12rem] w-full flex-1 flex-col gap-4"
>
<div className="flex items-center gap-1.5">
{/*STATUS*/}
<div className="flex items-center gap-2">
<div className="m-1 h-2.5 w-2.5 flex-none rounded-full bg-red-500"></div>
<span
className="whitespace-nowrap text-lg font-medium"
data-testid="status-label"
>
Failed
</span>
with code: 1
</div>
</div>
{/*COMPARE & FLAKY*/}
{/*<div className="flex items-center justify-end gap-1.5">
<button
type="button"
className="cursor-default rounded bg-white px-2 py-1 text-xs font-semibold text-slate-900 shadow-sm ring-1 ring-inset ring-slate-300 dark:bg-white/10 dark:text-slate-400 dark:ring-slate-700"
>
Compare to similar tasks
</button>
<button
type="button"
className="cursor-default rounded bg-white px-2 py-1 text-xs font-semibold text-slate-900 shadow-sm ring-1 ring-inset ring-slate-300 dark:bg-white/10 dark:text-slate-400 dark:ring-slate-700"
>
Set as "not flaky"
</button>
</div>*/}
<div>
<div className="border-b border-slate-100 dark:border-slate-700">
<div className="-mb-px flex space-x-4">
<span className="cursor-default whitespace-nowrap border-b-2 border-transparent px-0.5 py-2 text-xs font-medium text-slate-500 dark:text-slate-400">
Attempt 1
</span>
<span className="cursor-default whitespace-nowrap border-b-2 border-blue-500 px-0.5 py-2 text-xs font-medium text-blue-500 dark:border-sky-600 dark:text-sky-600">
Attempt 2
</span>
</div>
</div>
</div>
<p className="text-xs">Feb 23, 2024 08:57:49 - 08:57:54 (4s)</p>
<motion.div className="terminal-output flex max-h-full min-h-[2rem] flex-col overflow-visible rounded-lg border border-slate-200 bg-slate-50 font-mono text-xs leading-normal text-slate-800 subpixel-antialiased dark:border-slate-700 dark:bg-slate-900 dark:text-slate-200">
<div className="flex items-center justify-between gap-4 rounded-t-lg border-b border-slate-200 bg-slate-100 px-2 py-1 dark:border-slate-700 dark:bg-slate-800">
<div className="font-sans text-sm font-medium">
<span className="group relative flex cursor-pointer items-center overflow-hidden whitespace-nowrap subpixel-antialiased dark:text-slate-300">
<span>nx run nx-dev:build</span>
<span className="transform opacity-0 transition-all">
<ClipboardDocumentIcon className="h-4 w-4" />
</span>
</span>
</div>
</div>
<div className="overflow-hidden">
<pre className="overflow-x-hidden p-1 dark:text-slate-400">
{`nx run nx-dev:sitemap ✨ [next-sitemap]
Loading next-sitemap config:file:///home/workflows/workspace/nx-dev/nx-dev/next-sitemap.config.js
❌ [next-sitemap] Unable to find export-maker.
Make sure to build the project using "next build" command
node:internal/process/promises:289
triggerUncaughtException(err, true /* fromPromise */); ^
[Error:ENOENT: no such file or directory, stat '/home/workflows/workspace/dist/nx-dev/nx-dev/.next/export-marker.json']
errno: -2, code: 'ENOENT', syscall: 'stat', path: '/home/workflows/workspace/dist/nx-dev/nx-dev/.next/export-marker.json'
Node.js v20.9.0 Warning: command "pnpm next-sitemap --config
./nx-dev/nx-dev/next-sitemap.config.js" exited with non-zero status code`}
</pre>
</div>
</motion.div>
</motion.div>
);
};
export function Counter({
value,
duration = 2,
}: {
value: number;
duration?: number;
}) {
const count = useMotionValue(0);
const rounded = useTransform(count, Math.round);
useEffect(() => {
const animation = animate(count, value, {
type: 'tween',
ease: 'easeOut',
duration,
});
return animation.stop;
}, []);
return <motion.span>{rounded}</motion.span>;
}
const items = [
{
title: 'Nx Agents: seamless task distribution',
header: <TaskDistribution />,
className: 'md:col-span-2',
icon: <ArrowsRightLeftIcon className="h-4 w-4 text-slate-500" />,
url: '/ci/features/distribute-task-execution',
},
{
title: 'Nx Replay: secure computation cache',
header: <Caching />,
className: 'md:col-span-1',
icon: <CloudArrowDownIcon className="h-4 w-4 text-slate-500" />,
url: '/ci/features/remote-cache',
},
{
title: 'Increased visibility',
header: <IncreasedVisibility />,
className: 'md:col-span-1',
icon: <ChartBarSquareIcon className="h-4 w-4 text-slate-500" />,
},
{
title: 'Atomizer: task splitting & flaky rerun',
description: null,
header: <Atomizer />,
className: 'md:col-span-2',
icon: <Square3Stack3DIcon className="h-4 w-4 text-slate-500" />,
url: '/ci/features/split-e2e-tasks',
},
];
@@ -0,0 +1,196 @@
import { ReactElement, ReactNode, useRef, useState } from 'react';
import {
CarouselHandle,
CarouselRoot,
CarouselSlide,
CarouselViewport,
} from './carousel';
import { PayfitIcon, UkgIcon } from '@nx/nx-dev/ui-icons';
export function Carousel({
items,
}: {
items: { element: ReactNode; innerButtonElement: ReactNode }[];
}): ReactElement {
const carouselRef = useRef<CarouselHandle>(null);
const iterableItems = items.map((item, index) => ({
...item,
id: crypto.randomUUID(),
}));
const [currentIndex, setCurrentIndex] = useState(0);
const handleSlideChange = (index: number) => setCurrentIndex(index);
return (
<div className="w-full">
{/* Main carousel section */}
<CarouselRoot
className="overflow-hidden rounded-lg xl:[box-shadow:0_50px_100px_-20px_rgba(50,50,93,0.25),_0_30px_60px_-30px_rgba(0,0,0,0.3)]"
onSlideChange={handleSlideChange}
enableKeyboardNavigation={true}
autoPlayInterval={6000}
ref={carouselRef}
>
<CarouselViewport>
{iterableItems.map((item, index) => (
<CarouselSlide key={item.id}>{item.element}</CarouselSlide>
))}
</CarouselViewport>
{/* Custom line-style indicators */}
<div className="absolute bottom-8 left-1/2 flex -translate-x-1/2 gap-2">
{iterableItems.map((_, index) => (
<div
key={index}
className={`h-1 w-8 rounded-full transition-colors duration-300 ${
index === currentIndex ? 'bg-white' : 'bg-white/30'
}`}
/>
))}
</div>
</CarouselRoot>
{/* Partner logos section - now linked to carousel items */}
<div className="mt-12 flex items-center justify-center divide-x divide-slate-200 dark:divide-slate-700">
{iterableItems.map((item, index) => (
<button
key={item.id + '-logo'}
onClick={() => carouselRef.current?.goToSlide(index)}
className={`px-8 py-4 transition-all duration-300 ${
index === currentIndex
? 'underline opacity-100 grayscale-0'
: 'opacity-50 grayscale'
}`}
>
{item.innerButtonElement}
</button>
))}
</div>
</div>
);
}
export function TestimonialCarousel(): ReactElement {
return (
<section className="">
<div className="mx-auto max-w-7xl px-6 lg:px-8">
<Carousel
items={[
{
element: (
<div className="relative overflow-hidden">
<div
className="absolute inset-0 bg-opacity-75 bg-contain bg-right bg-no-repeat"
style={{
backgroundImage:
"url('https://images.unsplash.com/photo-1511376868136-742c0de8c9a8?q=80&w=2670&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D')",
}}
/>
<div className="absolute inset-0 bg-gradient-to-r from-[#0F6FDE] via-[#0F6FDE] via-70% to-[#0F6FDE]/40" />
<div className="relative mx-auto grid max-w-2xl grid-cols-1 px-12 py-16 text-white lg:mx-0 lg:max-w-none lg:grid-cols-4">
<div className="col-span-3 flex flex-col">
<figure className="flex flex-auto flex-col justify-between">
<blockquote className="text-pretty text-xl/8">
<p>
The number of hours we spent trying to manage CI
before, trying to load balance in CircleCI, the
number of agents that we run ourselves by hand and
try to distribute ourselves manually - it was
painful, wed spend hours and days trying to do
that.{' '}
<span className="font-semibold">
With Nx Cloud we dont need to think about that,
here is my task, deal with it and make it fast
</span>
.
</p>
</blockquote>
<figcaption className="mt-10 flex items-center gap-x-6">
<img
alt="avatar"
src="https://avatars.githubusercontent.com/u/7281023?v=4"
className="size-14 rounded-full bg-slate-50"
/>
<div className="text-base">
<div className="font-semibold">
Nicolas Beaussart
</div>
<div className="mt-1 ">
Staff Platform Engineer, Payfit
</div>
</div>
</figcaption>
</figure>
</div>
<div className="grid-col-1 grid place-items-center p-4">
<PayfitIcon
aria-hidden="true"
className="size-12 lg:size-20"
/>
</div>
</div>
</div>
),
// innerButtonElement: (
// <PayfitIcon
// aria-hidden="true"
// className="h-10 self-start text-[#0F6FDE]"
// />
// ),
innerButtonElement: (
<span className="text-2xl">Increase speed</span>
),
},
{
element: (
<div className="relative">
<div
className="absolute inset-0 bg-opacity-75 bg-contain bg-right bg-no-repeat"
style={{
backgroundImage:
"url('https://images.unsplash.com/photo-1552664730-d307ca884978?q=80&w=2670&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D')",
}}
/>
<div className="absolute inset-0 bg-gradient-to-r from-[#005151] via-[#005151] via-55% to-[#005151]/40" />
<div className="relative mx-auto grid max-w-2xl grid-cols-1 px-12 py-16 text-white lg:mx-0 lg:max-w-none lg:grid-cols-4">
<div className="col-span-2 flex flex-col">
<figure className="flex flex-auto flex-col justify-between">
<blockquote className="text-pretty text-xl/8">
<p>
I really like the Nx check-ins - Nx people are very
well prepared for how to help their team grow and
scale and to help us spot some of our challenges. I
cant see a future where we dont have Nx.
</p>
</blockquote>
<figcaption className="mt-10 flex items-center gap-x-6">
<img
alt=""
src="https://avatars.githubusercontent.com/u/6657673?v=4"
className="size-14 rounded-full bg-slate-50"
/>
<div className="text-base">
<div className="font-semibold">Sid Govindaraju</div>
<div className="mt-1">Engineering Manager, UKG</div>
</div>
</figcaption>
</figure>
</div>
<div className="grid-col-1 grid place-items-center p-4">
<UkgIcon aria-hidden="true" className="h-6 lg:h-12" />
</div>
</div>
</div>
),
// innerButtonElement: (
// <UkgIcon aria-hidden="true" className="h-8 text-[#005151]" />
// ),
innerButtonElement: (
<span className="text-2xl">Proactive partnership</span>
),
},
]}
/>
</div>
</section>
);
}
-220
View File
@@ -1,220 +0,0 @@
import {
AwsIcon,
BillColoredIcon,
CapitalOneIcon,
CaterpillarIcon,
CiscoIcon,
FicoIcon,
HiltonIcon,
ManIcon,
ReactQueryIcon,
RedwoodJsIcon,
RoyalBankOfCanadaColoredIcon,
SevenElevenColoredIcon,
ShopifyIcon,
StorybookIcon,
VmwareIcon,
} from '@nx/nx-dev/ui-icons';
import { motion } from 'framer-motion';
export function TrustedBy(): JSX.Element {
const variants = {
hidden: {
opacity: 0,
transition: {
when: 'afterChildren',
},
},
visible: (i: number) => ({
opacity: 1,
transition: {
delay: i || 0,
},
}),
};
const itemVariants = {
visible: (i: number) => ({
opacity: 1,
y: 0,
transition: {
delay: i * 0.25,
duration: 0.65,
ease: 'easeOut',
when: 'beforeChildren',
staggerChildren: 0.3,
},
}),
hidden: {
opacity: 0,
y: 4,
transition: {
when: 'afterChildren',
},
},
};
return (
<section className="">
<div className="mx-auto max-w-7xl px-4 pb-12 sm:px-6 lg:px-8 lg:pb-16">
{/*<div className="mx-auto max-w-2xl lg:mx-0 lg:max-w-xl">*/}
{/* <SectionHeading as="h2" variant="title" id="trusted-by">*/}
{/* Trusted by startups and Fortune 500 companies*/}
{/* </SectionHeading>*/}
{/*</div>*/}
<motion.dl
initial="hidden"
variants={variants}
whileInView="visible"
viewport={{ once: true }}
className="mt-4 grid grid-cols-2 gap-0.5 md:grid-cols-5"
>
<motion.div
custom={1}
variants={itemVariants}
className="col-span-1 flex items-center justify-center"
>
<AwsIcon className="h-14 w-14 text-black dark:text-white" />
</motion.div>
<motion.div
custom={2}
variants={itemVariants}
className="col-span-1 flex h-14 items-center justify-center lg:h-28"
>
<ManIcon aria-hidden="true" className="h-14 w-14 text-[#E40045]" />
</motion.div>
<motion.div
custom={3}
variants={itemVariants}
className="col-span-1 flex h-14 items-center justify-center lg:h-28"
>
<CapitalOneIcon
aria-hidden="true"
className="h-28 w-28 text-black dark:text-white"
/>
</motion.div>
<motion.div
custom={4}
variants={itemVariants}
className="col-span-1 flex h-14 items-center justify-center lg:h-28"
>
<ShopifyIcon
aria-hidden="true"
className="h-12 w-12 text-[#7AB55C]"
/>
</motion.div>
<motion.div
custom={5}
variants={itemVariants}
className="col-span-1 flex h-14 items-center justify-center lg:h-28"
>
<RoyalBankOfCanadaColoredIcon
aria-hidden="true"
className="h-14 w-14"
/>
</motion.div>
<motion.div
custom={6}
variants={itemVariants}
className="col-span-1 flex h-14 items-center justify-center lg:h-28"
>
<VmwareIcon
aria-hidden="true"
className="h-28 w-28 text-black dark:text-white"
/>
</motion.div>
<motion.div
custom={7}
variants={itemVariants}
className="col-span-1 flex h-14 items-center justify-center lg:h-28"
>
<StorybookIcon
aria-hidden="true"
className="h-12 w-12 text-[#FF4785]"
/>
</motion.div>
<motion.div
custom={8}
variants={itemVariants}
className="col-span-1 flex h-14 items-center justify-center lg:h-28"
>
<FicoIcon aria-hidden="true" className="h-28 w-28 text-[#0A6DE6]" />
</motion.div>
<motion.div
custom={9}
variants={itemVariants}
className="col-span-1 flex h-14 items-center justify-center lg:h-28"
>
<CaterpillarIcon
aria-hidden="true"
className="h-14 w-14 text-[#FFCD11]"
/>
</motion.div>
<motion.div
custom={10}
variants={itemVariants}
className="col-span-1 flex items-center justify-center"
>
<CiscoIcon
aria-hidden="true"
className="h-20 w-20 text-[#1BA0D7]"
/>
</motion.div>
<motion.div
custom={11}
variants={itemVariants}
className="col-span-1 flex items-center justify-center"
>
<BillColoredIcon aria-hidden="true" className="h-14 w-14" />
</motion.div>
<motion.div
custom={12}
variants={itemVariants}
className="col-span-1 flex items-center justify-center"
>
<SevenElevenColoredIcon aria-hidden="true" className="h-14 w-14" />
</motion.div>
<motion.div
custom={13}
variants={itemVariants}
className="col-span-1 flex items-center justify-center"
>
<HiltonIcon
aria-hidden="true"
className="h-20 w-20 text-black dark:text-white"
/>
</motion.div>
<motion.div
custom={14}
variants={itemVariants}
className="col-span-1 flex items-center justify-center"
>
<RedwoodJsIcon
aria-hidden="true"
className="h-14 w-14 text-[#BF4722]"
/>
</motion.div>
<motion.div
custom={14}
variants={itemVariants}
className="col-span-1 flex items-center justify-center"
>
<ReactQueryIcon
aria-hidden="true"
className="h-14 w-14 text-[#FF4154]"
/>
</motion.div>
{/*<motion.div*/}
{/* custom={11}*/}
{/* variants={itemVariants}*/}
{/* className="col-span-1 flex items-center justify-center"*/}
{/*>*/}
{/* <AmericanAirlinesIcon*/}
{/* aria-hidden="true"*/}
{/* className="h-12 w-12 text-[#0071CE]"*/}
{/* />*/}
{/*</motion.div>*/}
</motion.dl>
</div>
</section>
);
}

Some files were not shown because too many files have changed in this diff Show More