iT邦幫忙

2023 iThome 鐵人賽

DAY 20
0

NX 有提供擴充工具幫助快速生成專案或是元件庫的架構,像是:

pnpm exec nx generate @nx/next:application

利用擴充工具 @nx/next 可以幫助快速建立一個跟 NX 整合好的 Next.js 專案,可是生成的預設模板是沒有 tailwind 設定的,如果想用 tailwind 的話還要額外做設定,做個一兩次還好,但每次添加專案都要重複的話就有點煩了。

這時候就要想辦法自訂義生成工具了,像 @nx/next:application 也是社群定義好的擴充工具,我們也可以做自己的工具。

首先安裝 NX 的擴充開發工具。

pnpm add -D @nx/plugin@latest

有了開發工具就能用指令生成插件模組,一樣會放在 lib 底下。

pnpm exec nx generate @nx/plugin:plugin --name plugins/next-plugin

現在新開的插件底下還什麼都沒有,來建立一個新的生成工具。

pnpm exec nx generate @nx/plugin:generator --name=application --plugin=plugins-next-plugin

生成一個叫 application 的生成工具。

來看一下有了增加了內容後的插件模組結構。

next-plugin
├── README.md
├── generators.json
├── jest.config.ts
├── package.json
├── project.json
├── src
│   ├── generators
│   │   └── application
│   │       ├── files
│   │       │   └── src
│   │       │       └── index.ts.template
│   │       ├── generator.spec.ts
│   │       ├── generator.ts
│   │       ├── schema.d.ts
│   │       └── schema.json
│   └── index.ts
├── tsconfig.json
├── tsconfig.lib.json
└── tsconfig.spec.json

generators.json 是 NX 用於識別每個插件有哪些指令可以用的進入點。

{
  "generators": {
    "application": {
      "factory": "./src/generators/application/generator",
      "schema": "./src/generators/application/schema.json",
      "description": "application generator"
    }
  }
}

factory 指向實際指令執行的程式碼, schema 指向定義指令有哪先參數的設定檔。

這邊為了節省篇幅,先直接提供修改後的 generator 程式碼,再回頭說明。

// libs/plugins/next-plugin/src/generators/application/generator.ts
import { Tree } from '@nx/devkit';
import { applicationGenerator as nextApplicationGenerator } from '@nx/next';
import { setupTailwindGenerator } from '@nx/react';

type ApplicationGeneratorSchema = Parameters<
  typeof nextApplicationGenerator
>[1];

type SetupTailwindOptions = Parameters<typeof setupTailwindGenerator>[1];

export async function applicationGenerator(
  tree: Tree,
  options: ApplicationGeneratorSchema,
) {
  await nextApplicationGenerator(tree, {
    ...options,
    style: 'none',
  });

  const tailwindOptions: SetupTailwindOptions = {
    project: options.name,
  };
  await setupTailwindGenerator(tree, tailwindOptions);
}

export default applicationGenerator;

首先生成器是可以組合的,這邊主要組合了 @nx/next:application@nx/react:setup-tailwind 兩個步驟,分別對應 nextApplicationGeneratorsetupTailwindGenerator 這兩個函式。

而原本 @nx/next:applicationstyle 參數可以指定專案要用什麼樣的樣式生成器,但因為我要用 tailwind ,就直接覆寫為 none

再來 schema.json 要定義指令能使用的參數,這邊就全部照抄 @nx/next:application 的定義,只是要改一下 $id 跟移除 style 參數。

{
  "$schema": "http://json-schema.org/schema",
  "cli": "nx",
  "$id": "NextApp",
  "title": "Create a Next.js Application for Nx",
  "description": "Create a Next.js Application for Nx.",
  "type": "object",
  "properties": {
    "name": {
      "description": "The name of the application.",
      "type": "string",
      "$default": {
        "$source": "argv",
        "index": 0
      },
      "x-prompt": "What name would you like to use for the application?",
      "pattern": "^[a-zA-Z].*$",
      "x-priority": "important"
    },
    "directory": {
      "description": "The directory of the new application.",
      "type": "string",
      "alias": "d",
      "x-priority": "important"
    },
    "linter": {
      "description": "The tool to use for running lint checks.",
      "type": "string",
      "enum": ["eslint"],
      "default": "eslint"
    },
    "skipFormat": {
      "description": "Skip formatting files.",
      "type": "boolean",
      "default": false,
      "x-priority": "internal"
    },
    "unitTestRunner": {
      "type": "string",
      "enum": ["jest", "none"],
      "description": "Test runner to use for unit tests.",
      "default": "jest"
    },
    "e2eTestRunner": {
      "type": "string",
      "enum": ["cypress", "playwright", "none"],
      "description": "Test runner to use for end to end (E2E) tests.",
      "x-prompt": "Which E2E test runner would you like to use?",
      "default": "cypress"
    },
    "tags": {
      "type": "string",
      "description": "Add tags to the application (used for linting).",
      "alias": "t"
    },
    "js": {
      "type": "boolean",
      "description": "Generate JavaScript files rather than TypeScript files.",
      "default": false
    },
    "setParserOptionsProject": {
      "type": "boolean",
      "description": "Whether or not to configure the ESLint `parserOptions.project` option. We do not do this by default for lint performance reasons.",
      "default": false
    },
    "swc": {
      "description": "Enable the Rust-based compiler SWC to compile JS/TS files.",
      "type": "boolean",
      "default": true
    },
    "customServer": {
      "description": "Use a custom Express server for the Next.js application.",
      "type": "boolean",
      "default": false
    },
    "skipPackageJson": {
      "type": "boolean",
      "default": false,
      "description": "Do not add dependencies to `package.json`.",
      "x-priority": "internal"
    },
    "appDir": {
      "type": "boolean",
      "default": true,
      "description": "Enable the App Router for this project.",
      "x-prompt": "Would you like to use the App Router (recommended)?"
    },
    "rootProject": {
      "description": "Create an application at the root of the workspace.",
      "type": "boolean",
      "default": false,
      "hidden": true,
      "x-priority": "internal"
    }
  },
  "required": []
}

這樣就能解省掉建立專案後添加 Tailwind 設定的步驟了,還有一些細部的修改可以陸續追加。

測試指令:

pnpm exec nx generate @ironman-nextjs/plugins/next-plugin:application

上一篇
在 NX 中建立 Vue 元件庫
下一篇
Next App Router 設定 i18n
系列文
組裝前端開發工具箱,從 NX 入手30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言