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