From f77da3b20be3d8340977e50fd457ae754d1c2d60 Mon Sep 17 00:00:00 2001 From: Isaac Mann Date: Mon, 9 Oct 2023 12:19:17 -0400 Subject: [PATCH 1/7] docs(core): vue standalone tutorial --- docs/generated/manifests/menus.json | 24 + docs/generated/manifests/nx.json | 30 + docs/map.json | 5 + docs/shared/reference/sitemap.md | 1 + .../vue-standalone-tutorial/vue-standalone.md | 845 ++++++++++++++++++ 5 files changed, 905 insertions(+) create mode 100644 docs/shared/vue-standalone-tutorial/vue-standalone.md diff --git a/docs/generated/manifests/menus.json b/docs/generated/manifests/menus.json index b721948787923..4e39eea543019 100644 --- a/docs/generated/manifests/menus.json +++ b/docs/generated/manifests/menus.json @@ -86,6 +86,14 @@ "children": [], "disableCollapsible": false }, + { + "name": "Vue Standalone", + "path": "/getting-started/tutorials/vue-standalone-tutorial", + "id": "vue-standalone-tutorial", + "isExternal": false, + "children": [], + "disableCollapsible": false + }, { "name": "Node Standalone", "path": "/getting-started/tutorials/node-server-tutorial", @@ -178,6 +186,14 @@ "children": [], "disableCollapsible": false }, + { + "name": "Vue Standalone", + "path": "/getting-started/tutorials/vue-standalone-tutorial", + "id": "vue-standalone-tutorial", + "isExternal": false, + "children": [], + "disableCollapsible": false + }, { "name": "Node Standalone", "path": "/getting-started/tutorials/node-server-tutorial", @@ -237,6 +253,14 @@ "children": [], "disableCollapsible": false }, + { + "name": "Vue Standalone", + "path": "/getting-started/tutorials/vue-standalone-tutorial", + "id": "vue-standalone-tutorial", + "isExternal": false, + "children": [], + "disableCollapsible": false + }, { "name": "Node Standalone", "path": "/getting-started/tutorials/node-server-tutorial", diff --git a/docs/generated/manifests/nx.json b/docs/generated/manifests/nx.json index 55407694836ad..b0eb760e170d5 100644 --- a/docs/generated/manifests/nx.json +++ b/docs/generated/manifests/nx.json @@ -101,6 +101,16 @@ "path": "/getting-started/tutorials/angular-monorepo-tutorial", "tags": [] }, + { + "id": "vue-standalone-tutorial", + "name": "Vue Standalone", + "description": "", + "file": "shared/vue-standalone-tutorial/vue-standalone", + "itemList": [], + "isExternal": false, + "path": "/getting-started/tutorials/vue-standalone-tutorial", + "tags": [] + }, { "id": "node-server-tutorial", "name": "Node Standalone", @@ -217,6 +227,16 @@ "path": "/getting-started/tutorials/angular-monorepo-tutorial", "tags": [] }, + { + "id": "vue-standalone-tutorial", + "name": "Vue Standalone", + "description": "", + "file": "shared/vue-standalone-tutorial/vue-standalone", + "itemList": [], + "isExternal": false, + "path": "/getting-started/tutorials/vue-standalone-tutorial", + "tags": [] + }, { "id": "node-server-tutorial", "name": "Node Standalone", @@ -292,6 +312,16 @@ "path": "/getting-started/tutorials/angular-monorepo-tutorial", "tags": [] }, + "/getting-started/tutorials/vue-standalone-tutorial": { + "id": "vue-standalone-tutorial", + "name": "Vue Standalone", + "description": "", + "file": "shared/vue-standalone-tutorial/vue-standalone", + "itemList": [], + "isExternal": false, + "path": "/getting-started/tutorials/vue-standalone-tutorial", + "tags": [] + }, "/getting-started/tutorials/node-server-tutorial": { "id": "node-server-tutorial", "name": "Node Standalone", diff --git a/docs/map.json b/docs/map.json index 29b0303373c84..8fd259ccabaf2 100644 --- a/docs/map.json +++ b/docs/map.json @@ -63,6 +63,11 @@ "id": "angular-monorepo-tutorial", "file": "shared/angular-tutorial/angular-monorepo" }, + { + "name": "Vue Standalone", + "id": "vue-standalone-tutorial", + "file": "shared/vue-standalone-tutorial/vue-standalone" + }, { "name": "Node Standalone", "id": "node-server-tutorial", diff --git a/docs/shared/reference/sitemap.md b/docs/shared/reference/sitemap.md index e1978fc7f7507..12bcf0a116b68 100644 --- a/docs/shared/reference/sitemap.md +++ b/docs/shared/reference/sitemap.md @@ -11,6 +11,7 @@ - [React Monorepo](/getting-started/tutorials/react-monorepo-tutorial) - [Angular Standalone](/getting-started/tutorials/angular-standalone-tutorial) - [Angular Monorepo](/getting-started/tutorials/angular-monorepo-tutorial) + - [Vue Standalone](/getting-started/tutorials/vue-standalone-tutorial) - [Node Standalone](/getting-started/tutorials/node-server-tutorial) - [Core Tutorial](/core-tutorial) - [1 - Create Blog](/core-tutorial/01-create-blog) diff --git a/docs/shared/vue-standalone-tutorial/vue-standalone.md b/docs/shared/vue-standalone-tutorial/vue-standalone.md new file mode 100644 index 0000000000000..9a7729c1f9961 --- /dev/null +++ b/docs/shared/vue-standalone-tutorial/vue-standalone.md @@ -0,0 +1,845 @@ +# Building Vue Apps with the Nx Standalone Projects Setup + +In this tutorial you'll learn how to use Vue with Nx in a ["standalone" (non-monorepo) setup](/concepts/integrated-vs-package-based#standalone-applications). Not to be confused with the "Vue Standalone API", a standalone project in Nx is a non-monorepo setup where you have a single application at the root level. This setup is very similar to what the Vue CLI gives you. + +What are you going to learn? + +- how to create a new standalone (single-project) Nx workspace setup for Vue +- how to run a single task (i.e. serve your app) or run multiple tasks in parallel +- how to leverage code generators to scaffold components +- how to modularize your codebase and impose architectural constraints for better maintainability + +{% callout type="info" title="Looking for Vue monorepos?" %} +Note, this tutorial sets up a repo with a single application at the root level that breaks out its code into libraries to add structure. If you are looking for a Vue monorepo setup then check out our [Vue monorepo tutorial](/getting-started/tutorials/vue-monorepo-tutorial). + +{% /callout %} + +Note, while you could easily use Nx together with your manually set up Vue application, we're going to use the `@nx/vue` plugin for this tutorial which provides some nice enhancements when working with Vue. [Visit our "Why Nx" page](/getting-started/why-nx) to learn more about plugins and what role they play in the Nx architecture. + +## Warm Up + +Here's the source code of the final result for this tutorial. + +{% github-repository url="https://github.com/nrwl/nx-recipes/tree/main/vue-standalone" /%} + + + +## Creating a new Vue App + + + +Create a new Vue application with the following command: + +```{% command="npx create-nx-workspace@latest myvueapp --preset=vue-standalone" path="~" %} + > NX Let's create a new workspace [https://nx.dev/getting-started/intro] + +✔ Test runner to use for end to end (E2E) tests · cypress +✔ Default stylesheet format · css +✔ Enable distributed caching to make your CI faster · Yes + + > NX Creating your v17.0.0 workspace. + + To make sure the command works reliably in all environments, and that the preset is applied correctly, + Nx will run "npm install" several times. Please wait. +``` + +You can choose any test runner or stylesheet format you like. In this tutorial we're going to use Cypress and css. The above command generates the following structure: + +``` +└─ myvueapp + ├─ .vscode + │ └─ extensions.json + ├─ e2e + │ ├─ ... + │ ├─ project.json + │ ├─ src + │ │ ├─ e2e + │ │ │ └─ app.cy.ts + │ │ ├─ ... + │ └─ tsconfig.json + ├─ src + │ ├─ app + │ │ ├─ App.spec.ts + │ │ ├─ App.vue + │ │ └─ NxWelcome.vue + │ ├─ main.ts + │ └─ styles.css + ├─ index.html + ├─ jest.config.ts + ├─ jest.preset.js + ├─ nx.json + ├─ package-lock.json + ├─ package.json + ├─ project.json + ├─ README.md + ├─ tsconfig.app.json + ├─ tsconfig.base.json + ├─ tsconfig.json + ├─ tsconfig.spec.json + └─ vite.config.ts +``` + +The setup includes.. + +- a new Vue application at the root of the Nx workspace (`src`) +- a Cypress based set of e2e tests (`e2e/`) +- Prettier preconfigured +- ESLint preconfigured +- Vitest preconfigured + +Let me explain a couple of things that might be new to you. + +| File | Description | +| -------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `nx.json` | This is where we fine-tune how Nx works. We define what [cacheable operations](/core-features/cache-task-results) there are, and configure our [task pipeline](/concepts/task-pipeline-configuration). More on that soon. | +| `project.json` | This file contains the targets that can be invoked for the `myreactapp` project. It is like a more evolved version of simple `package.json` scripts with more metadata attached. You can read more about it [here](/reference/project-configuration). | + +## Serving the App + + + +The most common tasks are already defined in the `package.json` file: + +```json {% fileName="package.json" %} +{ + "name": "myvueapp", + "scripts": { + "start": "nx serve", + "build": "nx build", + "test": "nx test" + } + ... +} +``` + +To serve your new Vue application, just run: `npm start`. Alternatively you can directly use Nx by using + +```shell +nx serve +``` + +Your application should be served at [http://localhost:4200](http://localhost:4200). + +Nx uses the following syntax to run tasks: + +![Syntax for Running Tasks in Nx](/shared/images/run-target-syntax.svg) + +All targets, such as `serve`, `build`, `test` or your custom ones, are defined in the `project.json` file. + +```json {% fileName="project.json"} +{ + "name": "myvueapp", + ... + "targets": { + "lint": { ... }, + "build": { ... }, + "serve": { ... }, + "preview": { ... }, + "test": { ... }, + "serve-static": { ... }, + }, +} +``` + +Each target contains a configuration object that tells Nx how to run that target. + +```json {% fileName="project.json"} +{ + "name": "myvueapp", + ... + "targets": { + "serve": { + "executor": "@nx/vite:dev-server", + "defaultConfiguration": "development", + "options": { + "buildTarget": "myvueapp:build" + }, + "configurations": { + "development": { + "buildTarget": "myvueapp:build:development", + "hmr": true + }, + "production": { + "buildTarget": "myvueapp:build:production", + "hmr": false + } + } + }, + ... + }, +} +``` + +The most critical parts are: + +- `executor` - this is of the syntax `:`, where the `plugin` is an NPM package containing an [Nx Plugin](/extending-nx/intro/getting-started) and `` points to a function that runs the task. In this case, the `@nx/vite` plugin contains the `dev-server` executor which serves the React app using Vite. +- `options` - these are additional properties and flags passed to the executor function to customize it + +Learn more about how to [run tasks with Nx](/core-features/run-tasks). + +## Testing and Linting - Running Multiple Tasks + + + +Our current setup not only has targets for serving and building the Vue application, but also has targets for unit testing, e2e testing and linting. Again, these are defined in the `project.json` file. We can use the same syntax as before to run these tasks: + +```bash +nx test # runs tests using Jest +nx lint # runs linting with ESLint +nx e2e e2e # runs e2e tests with Cypress +``` + +More conveniently, we can also run them in parallel using the following syntax: + +```{% command="nx run-many -t test lint e2e" path="myvueapp" %} + + ✔ nx run e2e:lint (1s) + ✔ nx run myvueapp:lint (1s) + ✔ nx run myvueapp:test (2s) + ✔ nx run e2e:e2e (6s) + + —————————————————————————————————————————————————————— + + > NX Successfully ran targets test, lint, e2e for 2 projects (8s) +``` + +### Caching + + + +One thing to highlight is that Nx is able to [cache the tasks you run](/core-features/cache-task-results). + +Note that all of these targets are automatically cached by Nx. If you re-run a single one or all of them again, you'll see that the task completes immediately. In addition, (as can be seen in the output example below) there will be a note that a matching cache result was found and therefore the task was not run again. + +```{% command="nx run-many -t test lint e2e" path="myvueapp" %} + + ✔ nx run myvueapp:lint [existing outputs match the cache, left as is] + ✔ nx run e2e:lint [existing outputs match the cache, left as is] + ✔ nx run myvueapp:test [existing outputs match the cache, left as is] + ✔ nx run e2e:e2e [existing outputs match the cache, left as is] + + ——————————————————————————————————————————————————————— + + > NX Successfully ran targets test, lint, e2e for 2 projects (143ms) + + Nx read the output from the cache instead of running the command for 4 out of 4 tasks. +``` + +Not all tasks might be cacheable though. You can configure `cacheableOperations` in the `nx.json` file. You can also [learn more about how caching works](/core-features/cache-task-results). + +## Nx Plugins? Why? + + + +One thing you might be curious about is the project.json. You may wonder why we define tasks inside the `project.json` file instead of using the `package.json` file with scripts that directly launch Vite. + +Nx understands and supports both approaches, allowing you to define targets either in your `package.json` or `project.json` files. While both serve a similar purpose, the `project.json` file can be seen as an advanced form of `package.json` scripts, providing additional metadata and capabilities. In this tutorial, we utilize the `project.json` approach primarily because we take advantage of Nx Plugins. + +So, what are Nx Plugins? Nx Plugins are optional packages that extend the capabilities of Nx, catering to various specific technologies. For instance, we have plugins tailored to Vue (e.g., `@nx/vue`), Vite (`@nx/vite`), Cypress (`@nx/cypress`), and more. These plugins offer additional features, making your development experience more efficient and enjoyable when working with specific tech stacks. + +[visit our "Why Nx" page](/getting-started/why-nx) for more deails. + +## Creating New Components + + + +You can just create new React components as you normally would. However, Nx plugins usually also ship [generators](/core-features/plugin-features/use-code-generators). They allow you to easily scaffold code, configuration or entire projects. To see what capabilities the `@nx/vue` plugin ships, run the following command and inspect the output: + +```{% command="npx nx list @nx/vue" path="myvueapp" %} + +> NX Capabilities in @nx/vue: + + GENERATORS + + init : Initialize the `@nx/vue` plugin. + application : Create a Vue application. + library : Create a Vue library. + component : Create a Vue component. + setup-tailwind : Set up Tailwind configuration for a project. + storybook-configuration : Set up storybook for a Vue app or library. + stories : Create stories for all components declared in an app or library. +``` + +{% callout type="info" title="Prefer a more visual UI?" %} + +If you prefer a more integrated experience, you can install the "Nx Console" extension for your code editor. It has support for VSCode, IntelliJ and ships a LSP for Vim. Nx Console provides autocompletion support in Nx configuration files and has UIs for browsing and running generators. + +More info can be found in [the integrate with editors article](/core-features/integrate-with-editors). + +{% /callout %} + +Run the following command to generate a new "hello-world" component. Note how we append `--dry-run` to first check the output. + +```{% command="npx nx g @nx/vue:component hello-world --no-export --unit-test-runner=vitest --dry-run" path="myvueapp" %} +> NX Generating @nx/vue:component + +CREATE src/components/hello-world/hello-world.vue +CREATE src/components/hello-world/__tests__/hello-world.spec.ts + +NOTE: The "dryRun" flag means no changes were made. +``` + +As you can see it generates a new component in the `app/hello-world/` folder. If you want to actually run the generator, remove the `--dry-run` flag. + +```ts {% fileName="src/app/hello-world/hello-world.vue" %} + + + + + +``` + +## Building the App for Deployment + + + +If you're ready and want to ship your application, you can build it using + +```{% command="npx nx build" path="myvueapp" %} +> nx run myvueapp:build:production + +vite v4.3.9 building for production... +✓ 15 modules transformed. +dist/myvueapp/index.html 0.43 kB │ gzip: 0.29 kB +dist/myvueapp/assets/index-5056d525.css 7.90 kB │ gzip: 1.78 kB +dist/myvueapp/assets/index-a94ce881.js 62.46 kB │ gzip: 24.36 kB +✓ built in 534ms + + ——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— + + > NX Successfully ran target build for project myvueapp (1s) +``` + +All the required files will be placed in the `dist/myvueapp` folder and can be deployed to your favorite hosting provider. + +## You're ready to go! + + + +In the previous sections you learned about the basics of using Nx, running tasks and navigating an Nx workspace. You're ready to ship features now! + +But there's more to learn. You have two possibilities here: + +- [Jump to the next steps section](#next-steps) to find where to go from here or +- keep reading and learn some more about what makes Nx unique when working with Vue. + +## Modularizing your Vue App with Local Libraries + + + +When you develop your Vue application, usually all your logic sits in the `app` folder. Ideally separated by various folder names which represent your "domains". As your app grows, this becomes more and more monolithic though. + +The following structure is a common example of this kind of monolithic code organization: + +``` +└─ myvueapp + ├─ ... + ├─ src + │ ├─ app + │ │ ├─ products + │ │ ├─ cart + │ │ ├─ ui + │ │ ├─ ... + │ │ └─ App.vue + │ ├─ ... + │ └─ main.ts + ├─ ... + ├─ package.json + ├─ ... +``` + +Nx allows you to separate this logic into "local libraries". The main benefits include + +- better separation of concerns +- better reusability +- more explicit "APIs" between your "domain areas" +- better scalability in CI by enabling independent test/lint/build commands for each library +- better scalability in your teams by allowing different teams to work on separate libraries + +### Creating Local Libraries + + + +Let's assume our domain areas include `products`, `orders` and some more generic design system components, called `ui`. We can generate a new library for each of these areas using the Vue library generator: + +``` +nx g @nx/vue:library products --directory=modules/products --unit-test-runner=vitest --bundler=vite +nx g @nx/vue:library orders --directory=modules/orders --unit-test-runner=vitest --bundler=vite +nx g @nx/vue:library shared-ui --directory=modules/shared/ui --unit-test-runner=vitest --bundler=vite +``` + +Note how we use the `--directory` flag to place the libraries into a subfolder. You can choose whatever folder structure you like, even keep all of them at the root-level. + +Running the above commands should lead to the following directory structure: + +``` +└─ myvueapp + ├─ ... + ├─ e2e/ + ├─ modules + │ ├─ products + │ │ ├─ .eslintrc.json + │ │ ├─ README.md + │ │ ├─ vite.config.ts + │ │ ├─ project.json + │ │ ├─ src + │ │ │ ├─ index.ts + │ │ │ ├─ components + │ │ │ │ ├─ products.spec.ts + │ │ │ │ └─ products.vue + │ │ │ └─ vue-shims.d.ts + │ │ ├─ tsconfig.json + │ │ ├─ tsconfig.lib.json + │ │ └─ tsconfig.spec.json + │ ├─ orders + │ │ ├─ ... + │ │ ├─ src + │ │ │ ├─ index.ts + │ │ │ ├─ components + │ │ │ │ ├─ ... + │ │ │ │ └─ orders.vue + │ │ ├─ ... + │ └─ shared + │ └─ ui + │ ├─ ... + │ ├─ src + │ │ ├─ index.ts + │ │ └─ components + │ │ └─ shared-ui.vue + │ └─ ... + ├─ ... + ├─ src + │ ├─ app + │ │ ├─ ... + │ │ ├─ App.vue + │ ├─ ... + ├─ ... +``` + +Each of these libraries + +- has its own `project.json` file with corresponding targets you can run (e.g. running tests for just orders: `nx test orders`) +- has a dedicated `index.ts` file which is the "public API" of the library +- is mapped in the `tsconfig.base.json` at the root of the workspace + +### Importing Libraries into the Vue Application + + + +All libraries that we generate automatically have aliases created in the root-level `tsconfig.base.json`. + +```json {% fileName="tsconfig.base.json" %} +{ + "compilerOptions": { + ... + "paths": { + "products": ["modules/products/src/index.ts"], + "orders": ["modules/orders/src/index.ts"], + "shared-ui": ["modules/shared/ui/src/index.ts"] + }, + ... + }, +} +``` + +Hence we can easily import them into other libraries and our Vue application. For example: let's use our existing `Products` component in `modules/products/src/components/products.vue`: + +```vue {% fileName="modules/products/src/components/products.vue" %} + + + + + +``` + +Make sure the `Products` component is exported via the `index.ts` file of our `products` library (which it should already be). The `modules/products/src/index.ts` file is the public API for the `products` library with the rest of the workspace. Only export what's really necessary to be usable outside the library itself. + +```ts {% fileName="modules/products/src/index.ts" %} +export { default as Products } from './components/products.vue'; +``` + +We're ready to import it into our main application now. First, let's set up the Vue Router. + +```shell +npm install vue-router --legacy-peer-deps +``` + +Configure it in the `main.ts` file. + +```ts {% fileName="src/main.ts" %} +import './styles.css'; + +import { createApp } from 'vue'; +import App from './app/App.vue'; +import NxWelcome from './app/NxWelcome.vue'; +import * as VueRouter from 'vue-router'; + +const routes = [ + { path: '/', component: NxWelcome }, + { + path: '/products', + component: () => import('products').then((m) => m.Products), + }, +]; + +const router = VueRouter.createRouter({ + history: VueRouter.createWebHashHistory(), + routes, +}); + +const app = createApp(App); + +app.use(router); +app.mount('#root'); +``` + +Then we can set up navigation links and the `RouterView` in the main `App` component. + +```vue {% fileName="src/App.vue" %} + + + +``` + +If you now navigate to [http://localhost:4200/products](http://localhost:4200/products) you should see the `Products` component being rendered. + +![Browser screenshot of navigating to the products route](/shared/images/tutorial-vue-standalone/app-products-route.png) + +Let's do the same process for our `orders` library. Import the `Orders` component into the `main.ts` routes: + +```ts {% fileName="src/app/main.ts" %} +import './styles.css'; + +import { createApp } from 'vue'; +import App from './app/App.vue'; +import NxWelcome from './app/NxWelcome.vue'; +import * as VueRouter from 'vue-router'; + +const routes = [ + { path: '/', component: NxWelcome }, + { + path: '/products', + component: () => import('products').then((m) => m.Products), + }, + { path: '/orders', component: () => import('orders').then((m) => m.Orders) }, +]; + +const router = VueRouter.createRouter({ + history: VueRouter.createWebHashHistory(), + routes, +}); + +const app = createApp(App); + +app.use(router); +app.mount('#root'); +``` + +And update the navigation links: + +```vue {% fileName="src/App.vue" %} + + + +``` + +Similarly, navigating to [http://localhost:4200/orders](http://localhost:4200/orders) should now render the `Orders` component. + +Note that both the `Products` component and `Orders` component are lazy loaded so the initial bundle size will be smaller. + +## Visualizing your Project Structure + + + +Nx automatically detects the dependencies between the various parts of your workspace and builds a [project graph](/core-features/explore-graph). This graph is used by Nx to perform various optimizations such as determining the correct order of execution when running tasks like `nx build`, identifying [affected projects](/core-features/run-tasks#run-tasks-affected-by-a-pr) and more. Interestingly you can also visualize it. + +Just run: + +```shell +nx graph +``` + +You should be able to see something similar to the following in your browser (hint: click the "Show all projects" button). + +{% graph height="450px" %} + +```json +{ + "projects": [ + { + "name": "myvueapp", + "type": "app", + "data": { + "tags": [] + } + }, + { + "name": "e2e", + "type": "e2e", + "data": { + "tags": [] + } + }, + { + "name": "shared-ui", + "type": "lib", + "data": { + "tags": [] + } + }, + { + "name": "orders", + "type": "lib", + "data": { + "tags": [] + } + }, + + { + "name": "products", + "type": "lib", + "data": { + "tags": [] + } + } + ], + "dependencies": { + "myvueapp": [ + { "source": "myvueapp", "target": "orders", "type": "dynamic" }, + { "source": "myvueapp", "target": "products", "type": "dynamic" } + ], + "e2e": [{ "source": "e2e", "target": "myvueapp", "type": "implicit" }], + "shared-ui": [], + "orders": [], + "products": [] + }, + "workspaceLayout": { "appsDir": "", "libsDir": "" }, + "affectedProjectIds": [], + "focus": null, + "groupByFolder": false +} +``` + +{% /graph %} + +Notice how `shared-ui` is not yet connected to anything because we didn't import it in any of our projects. Also the arrows to `orders` and `products` are dashed because we're using lazy imports. + +Exercise for you: change the codebase so that `shared-ui` is used by `orders` and `products`. Note: you need to restart the `nx graph` command to update the graph visualization or run the CLI command with the `--watch` flag. + +## Imposing Constraints with Module Boundary Rules + + + +Once you modularize your codebase you want to make sure that the modules are not coupled to each other in an uncontrolled way. Here are some examples of how we might want to guard our small demo workspace: + +- we might want to allow `orders` to import from `shared-ui` but not the other way around +- we might want to allow `orders` to import from `products` but not the other way around +- we might want to allow all libraries to import the `shared-ui` components, but not the other way around + +When building these kinds of constraints you usually have two dimensions: + +- **type of project:** what is the type of your library. Example: "feature" library, "utility" library, "data-access" library, "ui" library (see [library types](/concepts/more-concepts/library-types)) +- **scope (domain) of the project:** what domain area is covered by the project. Example: "orders", "products", "shared" ... this really depends on the type of product you're developing + +Nx comes with a generic mechanism that allows you to assign "tags" to projects. "tags" are arbitrary strings you can assign to a project that can be used later when defining boundaries between projects. For example, go to the `project.json` of your `orders` library and assign the tags `type:feature` and `scope:orders` to it. + +```json {% fileName="modules/orders/project.json" %} +{ + ... + "tags": ["type:feature", "scope:orders"], + ... +} +``` + +Then go to the `project.json` of your `products` library and assign the tags `type:feature` and `scope:products` to it. + +```json {% fileName="modules/products/project.json" %} +{ + ... + "tags": ["type:feature", "scope:products"], + ... +} +``` + +Finally, go to the `project.json` of the `shared-ui` library and assign the tags `type:ui` and `scope:shared` to it. + +```json {% fileName="modules/shared/ui/project.json" %} +{ + ... + "tags": ["type:ui", "scope:shared"], + ... +} +``` + +Notice how we assign `scope:shared` to our UI library because it is intended to be used throughout the workspace. + +Next, let's come up with a set of rules based on these tags: + +- `type:feature` should be able to import from `type:feature` and `type:ui` +- `type:ui` should only be able to import from `type:ui` +- `scope:orders` should be able to import from `scope:orders`, `scope:shared` and `scope:products` +- `scope:products` should be able to import from `scope:products` and `scope:shared` + +To enforce the rules, Nx ships with a custom ESLint rule. Open the `.eslintrc.base.json` at the root of the workspace and add the following `depConstraints` in the `@nx/enforce-module-boundaries` rule configuration: + +```json {% fileName=".eslintrc.base.json" %} +{ + ... + "overrides": [ + { + ... + "rules": { + "@nx/enforce-module-boundaries": [ + "error", + { + "enforceBuildableLibDependency": true, + "allow": [], + "depConstraints": [ + { + "sourceTag": "*", + "onlyDependOnLibsWithTags": ["*"] + }, + { + "sourceTag": "type:feature", + "onlyDependOnLibsWithTags": ["type:feature", "type:ui"] + }, + { + "sourceTag": "type:ui", + "onlyDependOnLibsWithTags": ["type:ui"] + }, + { + "sourceTag": "scope:orders", + "onlyDependOnLibsWithTags": [ + "scope:orders", + "scope:products", + "scope:shared" + ] + }, + { + "sourceTag": "scope:products", + "onlyDependOnLibsWithTags": ["scope:products", "scope:shared"] + }, + { + "sourceTag": "scope:shared", + "onlyDependOnLibsWithTags": ["scope:shared"] + } + ] + } + ] + } + }, + ... + ] +} +``` + +To test it, go to your `modules/products/src/components/products.vue` file and import the `Orders` component from the `orders` project: + +```tsx {% fileName="modules/products/src/components/products.vue" %} + + + + + +``` + +If you lint your workspace you'll get an error now: + +```{% command="nx run-many -t lint" %} + ✖ nx run products:lint + Linting "products"... + + /Users/isaac/Documents/code/nx-recipes/vue-standalone/modules/products/src/components/products.vue + 5:1 error A project tagged with "scope:products" can only depend on libs tagged with "scope:products", "scope:shared" @nx/enforce-module-boundaries + + ✖ 1 problem (1 error, 0 warnings) + + Lint errors found in the listed files. + + ✔ nx run orders:lint (1s) + ✔ nx run myvueapp:lint (1s) + ✔ nx run e2e:lint (682ms) + ✔ nx run shared-ui:lint (797ms) + + ————————————————————————————————————————————————————————————————————— + + > NX Ran target lint for 5 projects (2s) + + ✔ 4/5 succeeded [0 read from cache] + + ✖ 1/5 targets failed, including the following: + - nx run products:lint + +``` + +If you have the ESLint plugin installed in your IDE you should immediately see an error: + +![ESLint module boundary error](/shared/images/tutorial-vue-standalone/boundary-rule-violation-vscode.png) + +Learn more about how to [enforce module boundaries](/core-features/enforce-module-boundaries). + +## Next Steps + +Congrats, you made it!! You now know how to leverage the Nx standalone applications preset to build modular Vue applications. + +Here's some more things you can dive into next: + +- Learn more about the [underlying mental model of Nx](/concepts/mental-model) +- [Speed up CI: Run only tasks for project that got changed](/core-features/run-tasks#run-tasks-affected-by-a-pr) +- [Speed up CI: Share your cache](/core-features/remote-cache) +- [Speed up CI: Distribute your tasks across machines](/core-features/distribute-task-execution) + +Also, make sure you + +- [Join the Official Nx Discord Server](https://go.nx.dev/community) to ask questions and find out the latest news about Nx. +- [Follow Nx on Twitter](https://twitter.com/nxdevtools) to stay up to date with Nx news +- [Read our Nx blog](https://blog.nrwl.io/) +- [Subscribe to our Youtube channel](https://www.youtube.com/@nxdevtools) for demos and Nx insights From 941b1c446ebe2d69b0d925db7598fe141d1d47b7 Mon Sep 17 00:00:00 2001 From: Isaac Mann Date: Mon, 9 Oct 2023 12:28:17 -0400 Subject: [PATCH 2/7] docs(core): remove reference to vue monorepo tutorial --- docs/shared/vue-standalone-tutorial/vue-standalone.md | 5 ----- 1 file changed, 5 deletions(-) diff --git a/docs/shared/vue-standalone-tutorial/vue-standalone.md b/docs/shared/vue-standalone-tutorial/vue-standalone.md index 9a7729c1f9961..5d9225710a602 100644 --- a/docs/shared/vue-standalone-tutorial/vue-standalone.md +++ b/docs/shared/vue-standalone-tutorial/vue-standalone.md @@ -9,11 +9,6 @@ What are you going to learn? - how to leverage code generators to scaffold components - how to modularize your codebase and impose architectural constraints for better maintainability -{% callout type="info" title="Looking for Vue monorepos?" %} -Note, this tutorial sets up a repo with a single application at the root level that breaks out its code into libraries to add structure. If you are looking for a Vue monorepo setup then check out our [Vue monorepo tutorial](/getting-started/tutorials/vue-monorepo-tutorial). - -{% /callout %} - Note, while you could easily use Nx together with your manually set up Vue application, we're going to use the `@nx/vue` plugin for this tutorial which provides some nice enhancements when working with Vue. [Visit our "Why Nx" page](/getting-started/why-nx) to learn more about plugins and what role they play in the Nx architecture. ## Warm Up From 4dbf182ccc07c8b55c6f8e0fbfa3b30e4ad7f43c Mon Sep 17 00:00:00 2001 From: Isaac Mann Date: Mon, 9 Oct 2023 15:40:24 -0400 Subject: [PATCH 3/7] docs(core): fixes --- docs/shared/react-standalone-tutorial/react-standalone.md | 2 +- docs/shared/vue-standalone-tutorial/vue-standalone.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/shared/react-standalone-tutorial/react-standalone.md b/docs/shared/react-standalone-tutorial/react-standalone.md index 39291c482f1be..9cfa58514a3a5 100644 --- a/docs/shared/react-standalone-tutorial/react-standalone.md +++ b/docs/shared/react-standalone-tutorial/react-standalone.md @@ -237,7 +237,7 @@ Nx understands and supports both approaches, allowing you to define targets eith So, what are Nx Plugins? Nx Plugins are optional packages that extend the capabilities of Nx, catering to various specific technologies. For instance, we have plugins tailored to React (e.g., `@nx/react`), Vite (`@nx/vite`), Cypress (`@nx/cypress`), and more. These plugins offer additional features, making your development experience more efficient and enjoyable when working with specific tech stacks. -[visit our "Why Nx" page](/getting-started/why-nx) for more deails. +[Visit our "Why Nx" page](/getting-started/why-nx) for more details. ## Creating New Components diff --git a/docs/shared/vue-standalone-tutorial/vue-standalone.md b/docs/shared/vue-standalone-tutorial/vue-standalone.md index 5d9225710a602..51116222ffa33 100644 --- a/docs/shared/vue-standalone-tutorial/vue-standalone.md +++ b/docs/shared/vue-standalone-tutorial/vue-standalone.md @@ -232,7 +232,7 @@ Nx understands and supports both approaches, allowing you to define targets eith So, what are Nx Plugins? Nx Plugins are optional packages that extend the capabilities of Nx, catering to various specific technologies. For instance, we have plugins tailored to Vue (e.g., `@nx/vue`), Vite (`@nx/vite`), Cypress (`@nx/cypress`), and more. These plugins offer additional features, making your development experience more efficient and enjoyable when working with specific tech stacks. -[visit our "Why Nx" page](/getting-started/why-nx) for more deails. +[Visit our "Why Nx" page](/getting-started/why-nx) for more details. ## Creating New Components @@ -521,7 +521,7 @@ import { RouterLink, RouterView } from 'vue-router'; If you now navigate to [http://localhost:4200/products](http://localhost:4200/products) you should see the `Products` component being rendered. -![Browser screenshot of navigating to the products route](/shared/images/tutorial-vue-standalone/app-products-route.png) +![Browser screenshot of navigating to the products route](/shared/images/tutorial-react-standalone/react-tutorial-products-route.png) Let's do the same process for our `orders` library. Import the `Orders` component into the `main.ts` routes: From cc6722d21c16ec96667d97c2232b5a2c16981774 Mon Sep 17 00:00:00 2001 From: Isaac Mann Date: Mon, 16 Oct 2023 15:46:52 -0400 Subject: [PATCH 4/7] docs(core): vue standalone tutorial --- .../vue-tutorial-products-route.png | Bin 0 -> 128158 bytes .../vue-standalone-tutorial/vue-standalone.md | 54 +++++++++--------- 2 files changed, 27 insertions(+), 27 deletions(-) create mode 100644 docs/shared/images/tutorial-vue-standalone/vue-tutorial-products-route.png diff --git a/docs/shared/images/tutorial-vue-standalone/vue-tutorial-products-route.png b/docs/shared/images/tutorial-vue-standalone/vue-tutorial-products-route.png new file mode 100644 index 0000000000000000000000000000000000000000..0137837eca698db29ad5e0ca2de168135db6ed13 GIT binary patch literal 128158 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%0DaPxCfsr(+>ipOIKv7bzb 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 NX Generating @nx/vue:component +CREATE src/components/hello-world/hello-world.spec.ts CREATE src/components/hello-world/hello-world.vue -CREATE src/components/hello-world/__tests__/hello-world.spec.ts NOTE: The "dryRun" flag means no changes were made. ``` @@ -282,7 +282,7 @@ defineProps<{}>(); @@ -300,13 +300,13 @@ If you're ready and want to ship your application, you can build it using vite v4.3.9 building for production... ✓ 15 modules transformed. dist/myvueapp/index.html 0.43 kB │ gzip: 0.29 kB -dist/myvueapp/assets/index-5056d525.css 7.90 kB │ gzip: 1.78 kB -dist/myvueapp/assets/index-a94ce881.js 62.46 kB │ gzip: 24.36 kB -✓ built in 534ms +dist/myvueapp/assets/index-a0201bbf.css 7.90 kB │ gzip: 1.78 kB +dist/myvueapp/assets/index-46a11b5f.js 62.39 kB │ gzip: 24.35 kB +✓ built in 502ms - ——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— + ————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— - > NX Successfully ran target build for project myvueapp (1s) + > NX Successfully ran target build for project myvueapp (957ms) ``` All the required files will be placed in the `dist/myvueapp` folder and can be deployed to your favorite hosting provider. @@ -380,6 +380,7 @@ Running the above commands should lead to the following directory structure: │ │ ├─ .eslintrc.json │ │ ├─ README.md │ │ ├─ vite.config.ts + │ │ ├─ package.json │ │ ├─ project.json │ │ ├─ src │ │ │ ├─ index.ts @@ -432,8 +433,8 @@ All libraries that we generate automatically have aliases created in the root-le "compilerOptions": { ... "paths": { - "products": ["modules/products/src/index.ts"], "orders": ["modules/orders/src/index.ts"], + "products": ["modules/products/src/index.ts"], "shared-ui": ["modules/shared/ui/src/index.ts"] }, ... @@ -441,7 +442,7 @@ All libraries that we generate automatically have aliases created in the root-le } ``` -Hence we can easily import them into other libraries and our Vue application. For example: let's use our existing `Products` component in `modules/products/src/components/products.vue`: +Hence we can easily import them into other libraries and our Vue application. For example: let's use our existing `ProductsProducts` component in `modules/products/src/components/products.vue`: ```vue {% fileName="modules/products/src/components/products.vue" %} @@ -815,10 +819,6 @@ If you lint your workspace you'll get an error now: ``` -If you have the ESLint plugin installed in your IDE you should immediately see an error: - -![ESLint module boundary error](/shared/images/tutorial-vue-standalone/boundary-rule-violation-vscode.png) - Learn more about how to [enforce module boundaries](/core-features/enforce-module-boundaries). ## Next Steps From f1cad12493eaef075e0d82727d8f16f524984aa3 Mon Sep 17 00:00:00 2001 From: Isaac Mann Date: Tue, 17 Oct 2023 14:43:09 -0400 Subject: [PATCH 5/7] docs(core): updates --- .../vue-standalone-tutorial/vue-standalone.md | 61 ++++++++++--------- 1 file changed, 33 insertions(+), 28 deletions(-) diff --git a/docs/shared/vue-standalone-tutorial/vue-standalone.md b/docs/shared/vue-standalone-tutorial/vue-standalone.md index f5386e77f865d..561530df74ecf 100644 --- a/docs/shared/vue-standalone-tutorial/vue-standalone.md +++ b/docs/shared/vue-standalone-tutorial/vue-standalone.md @@ -276,13 +276,13 @@ NOTE: The "dryRun" flag means no changes were made. As you can see it generates a new component in the `app/hello-world/` folder. If you want to actually run the generator, remove the `--dry-run` flag. -```ts {% fileName="src/app/hello-world/hello-world.vue" %} +```ts {% fileName="src/components/hello-world/hello-world.vue" %} @@ -442,7 +442,7 @@ All libraries that we generate automatically have aliases created in the root-le } ``` -Hence we can easily import them into other libraries and our Vue application. For example: let's use our existing `ProductsProducts` component in `modules/products/src/components/products.vue`: +Hence we can easily import them into other libraries and our Vue application. For example: let's use our existing `Products` component in `modules/products/src/components/products.vue`: ```vue {% fileName="modules/products/src/components/products.vue" %} @@ -520,13 +520,13 @@ import { RouterLink, RouterView } from 'vue-router'; ``` -If you now navigate to [http://localhost:4200/products](http://localhost:4200/products) you should see the `ProductsProducts` component being rendered. +If you now navigate to [http://localhost:4200/products](http://localhost:4200/products) you should see the `Products` component being rendered. ![Browser screenshot of navigating to the products route](/shared/images/tutorial-vue-standalone/vue-tutorial-products-route.png) -Let's do the same process for our `orders` library. Import the `OrdersOrders` component into the `main.ts` routes: +Let's do the same process for our `orders` library. Import the `Orders` component into the `main.ts` routes: -```ts {% fileName="src/app/main.ts" %} +```ts {% fileName="src/main.ts" %} import './styles.css'; import { createApp } from 'vue'; @@ -538,11 +538,11 @@ const routes = [ { path: '/', component: NxWelcome }, { path: '/products', - component: () => import('products').then((m) => m.ProductsProducts), + component: () => import('products').then((m) => m.Products), }, { path: '/orders', - component: () => import('orders').then((m) => m.OrdersOrders), + component: () => import('orders').then((m) => m.Orders), }, ]; @@ -559,7 +559,7 @@ app.mount('#root'); And update the navigation links: -```vue {% fileName="src/App.vue" %} +```vue {% fileName="src/app/App.vue" %} @@ -583,9 +583,9 @@ import { RouterLink, RouterView } from 'vue-router'; ``` -Similarly, navigating to [http://localhost:4200/orders](http://localhost:4200/orders) should now render the `OrdersOrders` component. +Similarly, navigating to [http://localhost:4200/orders](http://localhost:4200/orders) should now render the `Orders` component. -Note that both the `ProductsProducts` component and `OrdersOrders` component are lazy loaded so the initial bundle size will be smaller. +Note that both the `Products` component and `Orders` component are lazy loaded so the initial bundle size will be smaller. ## Visualizing your Project Structure @@ -645,8 +645,8 @@ You should be able to see something similar to the following in your browser (hi ], "dependencies": { "myvueapp": [ - { "source": "myvueapp", "target": "orders", "type": "dynamic" }, - { "source": "myvueapp", "target": "products", "type": "dynamic" } + { "source": "myvueapp", "target": "orders" }, + { "source": "myvueapp", "target": "products" } ], "e2e": [{ "source": "e2e", "target": "myvueapp", "type": "implicit" }], "shared-ui": [], @@ -773,18 +773,18 @@ To enforce the rules, Nx ships with a custom ESLint rule. Open the `.eslintrc.ba } ``` -To test it, go to your `modules/products/src/components/products.vue` file and import the `OrdersOrders` component from the `orders` project: +To test it, go to your `modules/products/src/components/products.vue` file and import the `Orders` component from the `orders` project: ```tsx {% fileName="modules/products/src/components/products.vue" %} @@ -793,26 +793,31 @@ import { OrdersOrders } from 'orders'; If you lint your workspace you'll get an error now: ```{% command="nx run-many -t lint" %} + > NX Running target lint for 5 projects ✖ nx run products:lint Linting "products"... /Users/isaac/Documents/code/nx-recipes/vue-standalone/modules/products/src/components/products.vue - 5:1 error A project tagged with "scope:products" can only depend on libs tagged with "scope:products", "scope:shared" @nx/enforce-module-boundaries + 5:1 error A project tagged with "scope:products" can only depend on libs tagged with "scope:products", "scope:shared" @nx/enforce-module-boundaries + 5:10 warning 'Orders' is defined but never used @typescript-eslint/no-unused-vars + + ✖ 2 problems (1 error, 1 warning) - ✖ 1 problem (1 error, 0 warnings) + Lint warnings found in the listed files. Lint errors found in the listed files. - ✔ nx run orders:lint (1s) - ✔ nx run myvueapp:lint (1s) - ✔ nx run e2e:lint (682ms) - ✔ nx run shared-ui:lint (797ms) - ————————————————————————————————————————————————————————————————————— + ✔ nx run orders:lint (913ms) + ✔ nx run e2e:lint [existing outputs match the cache, left as is] + ✔ nx run myvueapp:lint (870ms) + ✔ nx run shared-ui:lint (688ms) + + —————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— > NX Ran target lint for 5 projects (2s) - ✔ 4/5 succeeded [0 read from cache] + ✔ 4/5 succeeded [1 read from cache] ✖ 1/5 targets failed, including the following: - nx run products:lint From 003d551c49b74a5ec104f595baca4c0ed769d8ed Mon Sep 17 00:00:00 2001 From: Isaac Mann Date: Tue, 17 Oct 2023 15:43:37 -0400 Subject: [PATCH 6/7] docs(core): changes --- .../vue-standalone-tutorial/vue-standalone.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/docs/shared/vue-standalone-tutorial/vue-standalone.md b/docs/shared/vue-standalone-tutorial/vue-standalone.md index 561530df74ecf..17cba2669d010 100644 --- a/docs/shared/vue-standalone-tutorial/vue-standalone.md +++ b/docs/shared/vue-standalone-tutorial/vue-standalone.md @@ -38,7 +38,7 @@ Create a new Vue application with the following command: Nx will run "npm install" several times. Please wait. ``` -You can choose any test runner or stylesheet format you like. In this tutorial we're going to use Cypress and css. The above command generates the following structure: +You can also choose [Playwright](/nx-api/playwright) for your e2e tests or a different stylesheet format. In this tutorial we're going to use Cypress and css. The above command generates the following structure: ``` └─ myvueapp @@ -60,10 +60,7 @@ You can choose any test runner or stylesheet format you like. In this tutorial w │ ├─ main.ts │ └─ styles.css ├─ index.html - ├─ jest.config.ts - ├─ jest.preset.js ├─ nx.json - ├─ package-lock.json ├─ package.json ├─ project.json ├─ README.md @@ -520,7 +517,7 @@ import { RouterLink, RouterView } from 'vue-router'; ``` -If you now navigate to [http://localhost:4200/products](http://localhost:4200/products) you should see the `Products` component being rendered. +If you now navigate to [http://localhost:4200/#/products](http://localhost:4200/#/products) you should see the `Products` component being rendered. ![Browser screenshot of navigating to the products route](/shared/images/tutorial-vue-standalone/vue-tutorial-products-route.png) @@ -583,7 +580,7 @@ import { RouterLink, RouterView } from 'vue-router'; ``` -Similarly, navigating to [http://localhost:4200/orders](http://localhost:4200/orders) should now render the `Orders` component. +Similarly, navigating to [http://localhost:4200/#/orders](http://localhost:4200/#/orders) should now render the `Orders` component. Note that both the `Products` component and `Orders` component are lazy loaded so the initial bundle size will be smaller. @@ -826,6 +823,10 @@ If you lint your workspace you'll get an error now: Learn more about how to [enforce module boundaries](/core-features/enforce-module-boundaries). +## Migrating to a Monorepo + +When you are ready to add another application to the repo, you'll probably want to move `myvueapp` to its own folder. To do this, you can run the [`convert-to-monorepo` generator](/nx-api/workspace/generators/convert-to-monorepo) or [manually move the configuration files](/recipes/tips-n-tricks/standalone-to-integrated). + ## Next Steps Congrats, you made it!! You now know how to leverage the Nx standalone applications preset to build modular Vue applications. From e45afa18d8d99a534309096c0350960b1961ca64 Mon Sep 17 00:00:00 2001 From: Isaac Mann Date: Fri, 20 Oct 2023 09:53:16 -0400 Subject: [PATCH 7/7] docs(core): update recipe --- .../vue-standalone-tutorial/vue-standalone.md | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/docs/shared/vue-standalone-tutorial/vue-standalone.md b/docs/shared/vue-standalone-tutorial/vue-standalone.md index 17cba2669d010..cbc93b650017b 100644 --- a/docs/shared/vue-standalone-tutorial/vue-standalone.md +++ b/docs/shared/vue-standalone-tutorial/vue-standalone.md @@ -262,20 +262,20 @@ More info can be found in [the integrate with editors article](/core-features/in Run the following command to generate a new "hello-world" component. Note how we append `--dry-run` to first check the output. -```{% command="npx nx g @nx/vue:component hello-world --no-export --unit-test-runner=vitest --dry-run" path="myvueapp" %} +```{% command="npx nx g @nx/vue:component hello-world --no-export --unit-test-runner=vitest --directory=src/components --dry-run" path="myvueapp" %} > NX Generating @nx/vue:component -CREATE src/components/hello-world/hello-world.spec.ts -CREATE src/components/hello-world/hello-world.vue +CREATE src/components/hello-world.spec.ts +CREATE src/components/hello-world.vue NOTE: The "dryRun" flag means no changes were made. ``` -As you can see it generates a new component in the `app/hello-world/` folder. If you want to actually run the generator, remove the `--dry-run` flag. +As you can see it generates a new component in the `src/components/` folder. If you want to actually run the generator, remove the `--dry-run` flag. -```ts {% fileName="src/components/hello-world/hello-world.vue" %} +```ts {% fileName="src/components/hello-world.vue" %}