Welcome to BaseButton!
``` ## 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. ## Modularize your Vue App with Local Libraries When you develop your Vue application, usually all your logic sits in the `src` folder. Ideally separated by various folder names which represent your "domains". As your app grows, this becomes more and more monolithic though. ``` └─ vue-app ├─ ... ├─ src │ ├─ views │ │ ├─ products │ │ └─ cart │ ├─ components │ │ ├─ 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 ### Create a Local Library 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 these areas using the Vue library generator: ``` nx g @nx/vue:library modules/products --unitTestRunner=vitest --bundler=vite --component ``` Note how we use the `--directory` flag to place the library into a subfolder. You can choose whatever folder structure you like to organize your libraries. Nx tries to set up your workspace to work with the modular library architecture, but depending on your existing configuration, you may need to tweak some settings. In this repo, you'll need to do a few things in order to prepare for future steps. #### Lint Settings We want the `lint` task for the root `vue-app` project to only lint the files for that project, so we'll change the `lint` command in `package.json`: ```json {% fileName="package.json" %} { "scripts": { "lint": "eslint src --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore" } } ``` Now we need to update the `.eslintrc.cjs` file to extend the `.eslintrc.base.json` file: ```js {% fileName=".eslintrc.cjs" highlightLines=[11,13] %} /* eslint-env node */ require('@rushstack/eslint-patch/modern-module-resolution'); module.exports = { root: true, extends: [ 'plugin:vue/vue3-essential', 'eslint:recommended', '@vue/eslint-config-typescript', '@vue/eslint-config-prettier/skip-formatting', './.eslintrc.base.json', ], ignorePatterns: ['!**/*'], overrides: [ { files: ['e2e/**/*.{test,spec}.{js,ts,jsx,tsx}'], extends: ['plugin:playwright/recommended'], }, ], parserOptions: { ecmaVersion: 'latest', }, }; ``` #### Build Settings To make sure that the build can correctly pull in code from libraries, we need to move the typescript `paths` from the `tsconfig.app.json` file to the newly created `tsconfig.base.json` and extend that base file. ```json {% fileName="tsconfig.app.json" highlightLines=[2] %} { "extends": ["@vue/tsconfig/tsconfig.dom.json", "./tsconfig.base.json"], "include": ["env.d.ts", "src/**/*", "src/**/*.vue"], "exclude": ["src/**/__tests__/*"] } ``` ```json {% fileName="tsconfig.vitest.json" highlightLines=[2] %} { "extends": "./tsconfig.app.json", "exclude": [], "compilerOptions": { "lib": [], "types": ["node", "jsdom"] } } ``` ```json {% fileName="tsconfig.base.json" highlightLines=["5-8"] %} { "compileOnSave": false, "compilerOptions": { "baseUrl": ".", "paths": { "@/*": ["./src/*"], "products": ["modules/products/src/index.ts"] } } } ``` We also need to update `vite.config.ts` to account for typescript aliases. Run the following generator to automatically update your configuration file. ```shell npx nx g @nx/vite:setup-paths-plugin ``` This will update the `vite.config.ts` file to include the `nxViteTsPaths` plugin in the `plugins` array. ```ts {% fileName="vite.config.ts" highlightLines=[6,10] %} import { fileURLToPath, URL } from 'node:url'; import { defineConfig } from 'vite'; import vue from '@vitejs/plugin-vue'; import vueJsx from '@vitejs/plugin-vue-jsx'; import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin'; // https://vitejs.dev/config/ export default defineConfig({ plugins: [vue(), vueJsx(), nxViteTsPaths()], build: { outDir: 'dist/vue-app', }, resolve: { alias: { '@': fileURLToPath(new URL('./src', import.meta.url)), }, }, }); ``` ### Create More Libraries Now that the repository is set up, let's generate the `orders` and `ui` libraries. ``` nx g @nx/vue:library modules/orders --unitTestRunner=vitest --bundler=vite --component nx g @nx/vue:library modules/ui --unitTestRunner=vitest --bundler=vite --component ``` Running the above commands should lead to the following directory structure: ``` └─ vue-app ├─ ... ├─ e2e/ ├─ modules │ ├─ products │ │ ├─ .eslintrc.json │ │ ├─ README.md │ │ ├─ vite.config.ts │ │ ├─ package.json │ │ ├─ project.json │ │ ├─ src │ │ │ ├─ index.ts │ │ │ └─ lib │ │ │ ├─ products.spec.ts │ │ │ └─ products.vue │ │ ├─ tsconfig.json │ │ ├─ tsconfig.lib.json │ │ ├─ tsconfig.spec.json │ │ └─ vite.config.ts │ ├─ orders │ │ ├─ ... │ │ ├─ project.json │ │ ├─ src │ │ │ ├─ index.ts │ │ │ └─ ... │ │ └─ ... │ └─ shared │ └─ ui │ ├─ ... │ ├─ project.json │ ├─ src │ │ ├─ index.ts │ │ └─ ... │ └─ ... ├─ src │ ├─ components │ ├─ ... │ ├─ App.vue │ └─ main.tsx ├─ ... ``` Each of these libraries - has a project details view where you can see the available tasks (e.g. running tests for just orders: `nx test orders`) - has its own `project.json` file where you can customize targets - 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": { "@/*": ["./src/*"], "orders": ["modules/orders/src/index.ts"], "products": ["modules/products/src/index.ts"], "ui": ["modules/shared/ui/src/index.ts"] }, ... }, } ``` That way we can easily import them into other libraries and our Vue application. As an example, let's import the `Products` component from the `products` project into our main application. First, configure the router in the `src/router/index.ts`. ```tsx {% fileName="src/router/index.ts" %} import { createRouter, createWebHistory } from 'vue-router'; import HomeView from '../views/HomeView.vue'; const router = createRouter({ history: createWebHistory(import.meta.env.BASE_URL), routes: [ { path: '/', name: 'home', component: HomeView, }, { path: '/about', name: 'about', // route level code-splitting // this generates a separate chunk (About.[hash].js) for this route // which is lazy-loaded when the route is visited. component: () => import('../views/AboutView.vue'), }, { path: '/products', name: 'products', component: () => import('products').then((m) => m.Products), }, { path: '/orders', name: 'orders', component: () => import('orders').then((m) => m.Orders), }, ], }); export default router; ``` Then we can add links to the routes in `App.vue`. ```tsx {% fileName="src/App.vue" %}Welcome to Products!
``` If you lint your workspace you'll get an error now: ```{% command="nx run-many -t lint" %} ✔ nx run ui:lint [existing outputs match the cache, left as is] ✔ nx run orders:lint [existing outputs match the cache, left as is] —————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— ✖ nx run products:lint > eslint . ~/vue-app/modules/products/src/lib/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) ✔ nx run vue-app:lint (892ms) ✔ nx run vue-app:lint (1s) —————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————— NX Ran targets lint for 4 projects (1s) ✔ 4/5 succeeded [2 read from cache] ✖ 1/5 targets failed, including the following: - nx run products:lint ``` Learn more about how to [enforce module boundaries](/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-monorepo). ## Fast CI ⚡ {% highlightColor="green" %} {% callout type="check" title="Repository with Nx" %} Make sure you have completed the previous sections of this tutorial before starting this one. If you want a clean starting point, you can check out the [reference code](https://github.com/nrwl/nx-recipes/tree/main/vue-app) as a starting point. {% /callout %} This tutorial walked you through how Nx can improve the local development experience, but the biggest difference Nx makes is in CI. As repositories get bigger, making sure that the CI is fast, reliable and maintainable can get very challenging. Nx provides a solution. - Nx reduces wasted time in CI with the [`affected` command](/ci/features/affected). - Nx Replay's [remote caching](/ci/features/remote-cache) will reuse task artifacts from different CI executions making sure you will never run the same computation twice. - Nx Agents [efficiently distribute tasks across machines](/ci/concepts/parallelization-distribution) ensuring constant CI time regardless of the repository size. The right number of machines is allocated for each PR to ensure good performance without wasting compute. - Nx Atomizer [automatically splits](/ci/features/split-e2e-tasks) large e2e tests to distribute them across machines. Nx can also automatically [identify and rerun flaky e2e tests](/ci/features/flaky-tasks). ### Connect to Nx Cloud {% highlightColor="green" %} Nx Cloud is a companion app for your CI system that provides remote caching, task distribution, e2e tests deflaking, better DX and more. Now that we're working on the CI pipeline, it is important for your changes to be pushed to a GitHub repository. 1. Commit your existing changes with `git add . && git commit -am "updates"` 2. [Create a new GitHub repository](https://github.com/new) 3. Follow GitHub's instructions to push your existing code to the repository Now connect your repository to Nx Cloud with the following command: ```shell npx nx connect ``` A browser window will open to register your repository in your [Nx Cloud](https://cloud.nx.app) account. The link is also printed to the terminal if the windows does not open, or you closed it before finishing the steps. The app will guide you to create a PR to enable Nx Cloud on your repository.  Once the PR is created, merge it into your main branch.  And make sure you pull the latest changes locally: ```shell git pull ``` You should now have an `nxCloudId` property specified in the `nx.json` file. ### Create a CI Workflow {% highlightColor="green" %} Use the following command to generate a CI workflow file. ```shell npx nx generate ci-workflow --ci=github ``` This generator creates a `.github/workflows/ci.yml` file that contains a CI pipeline that will run the `lint`, `test`, `build` and `e2e` tasks for projects that are affected by any given PR. If you would like to also distribute tasks across multiple machines to ensure fast and reliable CI runs, uncomment the `nx-cloud start-ci-run` line and have the `nx affected` line run the `e2e-ci` task instead of `e2e`. The key lines in the CI pipeline are: ```yml {% fileName=".github/workflows/ci.yml" highlightLines=["10-14", "21-23"] %} name: CI # ... jobs: main: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: fetch-depth: 0 # This enables task distribution via Nx Cloud # Run this command as early as possible, before dependencies are installed # Learn more at https://nx.dev/ci/reference/nx-cloud-cli#npx-nxcloud-startcirun # Uncomment this line to enable task distribution # - run: npx nx-cloud start-ci-run --distribute-on="3 linux-medium-js" --stop-agents-after="e2e-ci" - uses: actions/setup-node@v3 with: node-version: 20 cache: 'npm' - run: npm ci --legacy-peer-deps - uses: nrwl/nx-set-shas@v4 # Nx Affected runs only tasks affected by the changes in this PR/commit. Learn more: https://nx.dev/ci/features/affected # When you enable task distribution, run the e2e-ci task instead of e2e - run: npx nx affected -t lint test build e2e ``` ### Open a Pull Request {% highlightColor="green" %} Commit the changes and open a new PR on GitHub. ```shell git add . git commit -m 'add CI workflow file' git push origin add-workflow ``` When you view the PR on GitHub, you will see a comment from Nx Cloud that reports on the status of the CI run.  The `See all runs` link goes to a page with the progress and results of tasks that were run in the CI pipeline.  For more information about how Nx can improve your CI pipeline, check out one of these detailed tutorials: - [Circle CI with Nx](/ci/intro/tutorials/circle) - [GitHub Actions with Nx](/ci/intro/tutorials/github-actions) ## Next Steps Connect with the rest of the Nx community with these resources: - [:star:️ Star us on GitHub](https://github.com/nrwl/nx) to show your support and stay updated on new releases! - [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](/blog) - [Subscribe to our Youtube channel](https://www.youtube.com/@nxdevtools) for demos and Nx insights