From 1288093d4eab6b4ad5ad32f0dc661b73fda057f2 Mon Sep 17 00:00:00 2001 From: "Q.Ben Zheng" <40693636+Zhengqbbb@users.noreply.github.com> Date: Fri, 1 Nov 2024 21:23:38 +0800 Subject: [PATCH] feat(plugin-loader): support esm and ts(experiment) configure load (#197) [Experiment] Node.js v22 LTS natively load TypeScript configuration files: need inject env to turn on: 1. czg: `NODE_OPTIONS='--experimental-transform-types --disable-warning ExperimentalWarning' czg` 2. commitizen + cz-git: `NODE_OPTIONS='--experimental-transform-types --disable-warning ExperimentalWarning' cz` --- .commitlintrc.cjs => commitlint.config.mjs | 17 ++-- docs/config/index.md | 83 ++++++++++++++++-- docs/zh/config/index.md | 86 +++++++++++++++++-- packages/@cz-git/plugin-loader/package.json | 1 + .../plugin-loader/src/esm-ts-loader.ts | 43 ++++++++++ packages/@cz-git/plugin-loader/src/index.ts | 42 ++++++--- pnpm-lock.yaml | 3 + 7 files changed, 238 insertions(+), 37 deletions(-) rename .commitlintrc.cjs => commitlint.config.mjs (77%) create mode 100644 packages/@cz-git/plugin-loader/src/esm-ts-loader.ts diff --git a/.commitlintrc.cjs b/commitlint.config.mjs similarity index 77% rename from .commitlintrc.cjs rename to commitlint.config.mjs index f40822cb1..031a38f05 100644 --- a/.commitlintrc.cjs +++ b/commitlint.config.mjs @@ -1,17 +1,18 @@ -const { execSync } = require('node:child_process') -const fg = require('fast-glob') +import { execSync } from 'node:child_process' +import { defineConfig } from 'cz-git' +import fg from 'fast-glob' -// git branch name = feature/cli_33 => auto get defaultIssues = #33 +/** Get git branch issue number. e.g feature/cli_33 => #33 */ const issue = execSync('git rev-parse --abbrev-ref HEAD') .toString() .trim() .split('_')[1] -// dynamic get monorepo packages name -const packages = fg.sync('*', { cwd: 'packages/@cz-git', onlyDirectories: true }) +/** Get monorepo packages name */ +const packages = fg + .sync('*', { cwd: 'packages/@cz-git', onlyDirectories: true }) -/** @type {import('cz-git').UserConfig} */ -module.exports = { +export default defineConfig({ extends: ['@commitlint/config-conventional'], rules: { 'scope-enum': [2, 'always', ['cz-git', 'site', 'cli', ...packages]], @@ -37,4 +38,4 @@ module.exports = { customIssuePrefixAlign: !issue ? 'top' : 'bottom', defaultIssues: !issue ? '' : `#${issue}`, }, -} +}) diff --git a/docs/config/index.md b/docs/config/index.md index 2bc35159d..fac59ccb0 100644 --- a/docs/config/index.md +++ b/docs/config/index.md @@ -17,7 +17,7 @@ sitemap: - `commitlint.config.cjs` - `commitlint.config.mjs` -::::: details Click to expand `.commitlintrc.js` complete default configuration template +::::: details Click to expand `commitlint.config.js` complete default configuration template :::: code-group ::: code-group-item CommonJS @@ -77,7 +77,7 @@ If your project does not use commitlint,and want to use other profiles. You can ## Emoji template -::: details Click to expand `.commitlintrc.js` complete emoji template template +::: details Click to expand `commitlint.config.js` complete emoji template template <<< @/snippets/commitlint.config.emoji.js{24-34,36 js} @@ -121,13 +121,80 @@ https://cdn.jsdelivr.net/gh/Zhengqbbb/cz-git@{{ v }}/docs/public/schema/cz-git.j ## TypeScript template -:::danger -**Since v1.3.0. The typescript configuration file will no longer be loaded**. e.g(`cz.config.ts`) +- Added in `cz-git`, `czg` version `v1.11.0` +- Will use Node.js LTS version >= `v22.11.0` ==experimental feature==, **native TypeScript configuration file loading**
==Need injection of experimental options== `NODE_OPTIONS='--experimental-transform-types --disable-warning ExperimentalWarning'` to use +- For details: [see Node.js documentation](https://nodejs.org/api/cli.html#--experimental-transform-types) | [Node.js TypeScript support roadmap](https://github.com/nodejs/loaders/issues/217) +- ==TIP==: You can first use [ESM js](#javascript-template) configuration files as a transition for future TypeScript configuration files, and switch after Node.js runs TypeScript stably + +::::: details Click to expand `NODE_OPTIONS` injection methods +:::: code-group +::: code-group-item package.json - with cross-env package +```diff +"scripts": { +- "cz": "czg" ++ "cz": "cross-env NODE_OPTIONS='--experimental-transform-types --disable-warning ExperimentalWarning' czg" +} +``` +::: +::: code-group-item Global - with alias command +```sh +# .zshrc | .bashrc +alias czg="NODE_OPTIONS='--experimental-transform-types --disable-warning ExperimentalWarning' \czg" +``` +::: +::: code-group-item Global - with export command +```sh +# .zshrc | .bashrc +export NODE_OPTIONS="--experimental-transform-types --disable-warning ExperimentalWarning" +``` +::: +:::: +::::: + +--- + +- `.commitlintrc.ts` +- `.commitlintrc.mts` +- `.commitlintrc.cts` +- `commitlint.config.ts` +- `commitlint.config.mts` +- `commitlint.config.cts` + +::::: details Click to expand `commitlint.config.ts` complete default configuration template + +:::: code-group +::: code-group-item CommonJS + +<<< @/snippets/commitlint.config.cjs{ts} + +::: +::: code-group-item ESM + +<<< @/snippets/commitlint.config.mjs{ts} + +::: +:::: + +::::: + +--- + +- `cz.config.ts` +- `cz.config.mts` +- `cz.config.cts` + +::::: details Click to expand `cz.config.ts` complete default configuration template +:::: code-group +::: code-group-item CommonJS + +<<< @/snippets/cz.config.cjs{ts} -- Using the TypeScript configuration file will **affects command line tool startup speed**. -- Increase the package size. ::: +::: code-group-item ESM + +<<< @/snippets/cz.config.mjs{ts} -:::tip -Using the js configuration file to add the `@type` annotation can be a good way to provide code hints at configuration time. ::: +:::: + +::::: diff --git a/docs/zh/config/index.md b/docs/zh/config/index.md index f6c0881a2..f0b8addca 100644 --- a/docs/zh/config/index.md +++ b/docs/zh/config/index.md @@ -16,7 +16,7 @@ sitemap: - `commitlint.config.cjs` - `commitlint.config.mjs` -::::: details 点击展开 `.commitlintrc.js` 完整 默认 配置模板 +::::: details 点击展开 `commitlint.config.js` 完整 默认 配置模板 :::: code-group ::: code-group-item CommonJS @@ -77,7 +77,7 @@ sitemap: ## 中英文对照模板 -::: details 点击展开 `.commitlintrc.js` 完整 中英文 配置模板 +::: details 点击展开 `commitlint.config.js` 完整 中英文 配置模板 <<< @/snippets/commitlint.config.cn-en.js{js} @@ -90,7 +90,7 @@ sitemap: [推荐使用中英文对照](#中英文对照模板),可以很好给予团队的新人帮助。 ::: -::: details 点击展开 `.commitlintrc.js` 完整 纯汉化 配置模板 +::: details 点击展开 `commitlint.config.js` 完整 纯汉化 配置模板 <<< @/snippets/commitlint.config.cn.js{js} @@ -98,7 +98,7 @@ sitemap: ## Emoji 模板 -::: details 点击展开 `.commitlintrc.js` 完整 emoji 配置模板 +::: details 点击展开 `commitlint.config.js` 完整 emoji 配置模板 <<< @/snippets/commitlint.config.emoji.js{24-34,36 js} @@ -142,10 +142,80 @@ https://cdn.jsdelivr.net/gh/Zhengqbbb/cz-git@{{ v }}/docs/public/schema/cz-git.j ## TypeScript 模板 -:::danger -从 v1.3.0 开始,将不再支持 TypeScript 配置文件。例如(`cz.config.ts`) +- 从 `cz-git`, `czg` 的 `v1.11.0` 版本开始 +- 将使用 Node.js LTS 版本 >= `v22.11.0` 的==实验性功能==, **原生加载 TypeScript 配置文件**
==需注入开启实验性的参数== `NODE_OPTIONS='--experimental-transform-types --disable-warning ExperimentalWarning'` 使用 +- 详情: [可查看 Node.js 文档](https://nodejs.org/api/cli.html#--experimental-transform-types) | [Node.js TypeScript 支持路线图](https://github.com/nodejs/loaders/issues/217) +- ==建议==: 可先使用 [ESM js](#javascript-%E6%A8%A1%E6%9D%BF) 配置文件作为未来 TypeScript 配置文件的过渡,待 Node.js 运行 TypeScript 稳定后,再进行切换 + +::::: details 点击展开 `NODE_OPTIONS` 注入方式 +:::: code-group +::: code-group-item 项目 package.json 脚本注入 - 配合包 cross-env +```diff +"scripts": { +- "cz": "czg" ++ "cz": "cross-env NODE_OPTIONS='--experimental-transform-types --disable-warning ExperimentalWarning' czg" +} +``` +::: +::: code-group-item 全局注入 - 使用 alias 命令 +```sh +# .zshrc | .bashrc +alias czg="NODE_OPTIONS='--experimental-transform-types --disable-warning ExperimentalWarning' \czg" +``` ::: +::: code-group-item 全局注入 - 使用 export 命令 +```sh +# .zshrc | .bashrc +export NODE_OPTIONS="--experimental-transform-types --disable-warning ExperimentalWarning" +``` +::: +:::: +::::: + +--- + +- `.commitlintrc.ts` +- `.commitlintrc.mts` +- `.commitlintrc.cts` +- `commitlint.config.ts` +- `commitlint.config.mts` +- `commitlint.config.cts` + +::::: details 点击展开 `commitlint.config.ts` 完整 默认 配置模板 + +:::: code-group +::: code-group-item CommonJS + +<<< @/snippets/commitlint.config.cjs{ts} -:::tip -使用 js 配置文件添加 `@type` 注释可以很好提供在配置时的代码提示. ::: +::: code-group-item ESM + +<<< @/snippets/commitlint.config.mjs{ts} + +::: +:::: + +::::: + +--- + +- `cz.config.ts` +- `cz.config.mts` +- `cz.config.cts` + +::::: details 点击展开 `cz.config.ts` 完整 默认 配置模板 +:::: code-group +::: code-group-item CommonJS + +<<< @/snippets/cz.config.cjs{ts} + +::: +::: code-group-item ESM + +<<< @/snippets/cz.config.mjs{ts} + +::: +:::: + +::::: diff --git a/packages/@cz-git/plugin-loader/package.json b/packages/@cz-git/plugin-loader/package.json index 53459bca9..ac8bf525e 100644 --- a/packages/@cz-git/plugin-loader/package.json +++ b/packages/@cz-git/plugin-loader/package.json @@ -43,6 +43,7 @@ "devDependencies": { "@commitlint/resolve-extends": "catalog:commitlint", "@commitlint/types": "catalog:commitlint", + "@cz-git/inquirer": "workspace:*", "@types/tmp": "^0.2.3", "cosmiconfig": "catalog:", "pkg-dir": "5.0.0", diff --git a/packages/@cz-git/plugin-loader/src/esm-ts-loader.ts b/packages/@cz-git/plugin-loader/src/esm-ts-loader.ts new file mode 100644 index 000000000..90f1a5448 --- /dev/null +++ b/packages/@cz-git/plugin-loader/src/esm-ts-loader.ts @@ -0,0 +1,43 @@ +import process from 'node:process' +import { style } from '@cz-git/inquirer' +import type { Loader } from 'cosmiconfig' + +type LoaderError = Error & { + code?: string +} + +// export NODE_OPTIONS="--experimental-transform-types --disable-warning ExperimentalWarning" +export function esmTsLoader(): Loader { + return async (cfgPath: string, _: string) => { + try { + const result = await import(cfgPath) as { default?: any } + return result.default || result + } + catch (e: any) { + // TODO: bun - The Current native bun will cause the TUI unnormal + // @ts-ignore // Usage: `deno task cz` + const isDeno = typeof Deno !== 'undefined' && Deno?.version?.deno + if (isDeno) + throw e + + const error = e as LoaderError + const isNodeLTSInRange = (() => { + if (!process.version.startsWith('v')) + return false + const major = process.version.split('.')[0].slice(1) + const minor = process.version.split('.')[1] + return Number(major) >= 22 && Number(minor) >= 10 + })() + if (error?.code === 'ERR_UNKNOWN_FILE_EXTENSION') { + if (isNodeLTSInRange) + console.log(style.gray(`Loading file: ${cfgPath}\nRequires injecting experimental NODE_OPTIONS env: --experimental-transform-types\nSee: ${style.underline('https://cz-git.qbb.sh/config/#typescript-template')}`)) + else + console.log(style.gray(`Loading file: ${cfgPath}\n1. Requires Node.js version >= v22.10.0\n2. Inject experimental NODE_OPTIONS env: --experimental-transform-types\nSee: ${style.underline('https://cz-git.qbb.sh/config/#typescript-template')}`)) + return {} + } + else { + throw error + } + } + } +} diff --git a/packages/@cz-git/plugin-loader/src/index.ts b/packages/@cz-git/plugin-loader/src/index.ts index 1b1ee56f0..3b5a7dfc3 100644 --- a/packages/@cz-git/plugin-loader/src/index.ts +++ b/packages/@cz-git/plugin-loader/src/index.ts @@ -5,6 +5,7 @@ import process from 'node:process' import resolveExtends from '@commitlint/resolve-extends' import { cosmiconfig } from 'cosmiconfig' import type { RulesConfig } from '@commitlint/types' +import { esmTsLoader } from './esm-ts-loader' type ExectableConfig = (() => T) | (() => Promise) type Config = T | Promise | ExectableConfig @@ -23,14 +24,24 @@ export interface LoaderOptions { packageProp?: string[] } +const commonCosmiconfigOptions = { + ignoreEmptySearchPlaces: true, + cache: true, + loaders: { + '.ts': esmTsLoader(), + '.cts': esmTsLoader(), + '.mjs': esmTsLoader(), + '.mts': esmTsLoader(), + }, +} + export async function loader(options: LoaderOptions) { const cwd = options.cwd || process.cwd() const cosmiconfigFn = cosmiconfig(options.moduleName, { searchPlaces: options.searchPlaces || [], packageProp: options.packageProp || options.moduleName, stopDir: options.stopDir, - ignoreEmptySearchPlaces: true, - cache: true, + ...commonCosmiconfigOptions, }) const resultPath = options.explicitPath ? path.resolve(cwd, options.explicitPath) : undefined @@ -80,8 +91,16 @@ export async function clLoader(cwd?: string): Promise { `.${moduleName}rc.yml`, `.${moduleName}rc.js`, `.${moduleName}rc.cjs`, + `.${moduleName}rc.mjs`, + `.${moduleName}rc.ts`, + `.${moduleName}rc.cts`, + `.${moduleName}rc.mts`, `${moduleName}.config.js`, `${moduleName}.config.cjs`, + `${moduleName}.config.mjs`, + `${moduleName}.config.ts`, + `${moduleName}.config.cts`, + `${moduleName}.config.mts`, ], cwd, } @@ -115,6 +134,10 @@ export async function czLoader(cwd?: string) { `.${moduleName}rc`, `${moduleName}.config.js`, `${moduleName}.config.cjs`, + `${moduleName}.config.mjs`, + `${moduleName}.config.ts`, + `${moduleName}.config.cts`, + `${moduleName}.config.mts`, 'package.json', ], packageProp: ['config', 'commitizen'], @@ -125,10 +148,8 @@ export async function czLoader(cwd?: string) { return {} if (typeof data.config.czConfig === 'string') { const base = (data && data.filepath) ? path.dirname(data.filepath) : process.cwd() - data = await cosmiconfig('commitizen', { - ignoreEmptySearchPlaces: true, - cache: true, - }).load(path.resolve(base, data.config.czConfig)) + data = await cosmiconfig('commitizen', commonCosmiconfigOptions) + .load(path.resolve(base, data.config.czConfig)) } return await execute(data?.config || data || {}, true) } @@ -166,13 +187,8 @@ export interface UserOptions { export async function configLoader(options?: UserOptions) { // provide cli config loader if (typeof options?.configPath === 'string') { - const czData = await cosmiconfig( - 'commitizen', - { - ignoreEmptySearchPlaces: true, - cache: true, - }, - ).load(path.resolve(options.cwd || process.cwd(), options.configPath)) + const czData = await cosmiconfig('commitizen', commonCosmiconfigOptions) + .load(path.resolve(options.cwd || process.cwd(), options.configPath)) return { prompt: await execute(czData?.config || czData || {}, true) } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1b0ddb5cd..55c969ed5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -200,6 +200,9 @@ importers: '@commitlint/types': specifier: catalog:commitlint version: 19.5.0 + '@cz-git/inquirer': + specifier: workspace:* + version: link:../plugin-inquirer '@types/tmp': specifier: ^0.2.3 version: 0.2.3