From b9ce3ac5f5ef8be67e7425cad949d4b564548894 Mon Sep 17 00:00:00 2001 From: winches <329487092@qq.com> Date: Sat, 30 Nov 2024 03:42:35 +0800 Subject: [PATCH] feat: cli support beta components (#109) * feat: add command support beta package * fix: undefined component * feat: add beta component suppport * fix: doctor command * feat: add command add beta option * feat: upgrade command add beta option * docs: update README.md * fix: lock * fix: upgrade version * fix: upgrade select version * fix: compare issue --- README.md | 3 + package.json | 1 + pnpm-lock.yaml | 79 ++- src/actions/add-action.ts | 19 +- src/actions/upgrade-action.ts | 45 +- src/constants/components.json | 821 +++++++++++++++++++++++-- src/constants/store.ts | 6 + src/helpers/beta.ts | 45 ++ src/helpers/check.ts | 26 +- src/helpers/output-info.ts | 12 +- src/helpers/utils.ts | 4 +- src/index.ts | 9 +- src/prompts/get-beta-version-select.ts | 27 + src/scripts/cache/cache.ts | 21 +- src/scripts/helpers.ts | 78 ++- 15 files changed, 1038 insertions(+), 158 deletions(-) create mode 100644 src/helpers/beta.ts create mode 100644 src/prompts/get-beta-version-select.ts diff --git a/README.md b/README.md index 5016e94..a929bbd 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,7 @@ Usage: nextui [command] Options: -v, --version Output the current version --no-cache Disable cache, by default data will be cached for 30m after the first request + -d, --debug Debug mode will not install dependencies -h --help Display help information for commands Commands: @@ -132,6 +133,7 @@ nextui add [components...] [options] - `-app --appPath` [string] The path to the App.tsx file - `--prettier` [boolean] Add prettier format in the add content which required installed prettier - (default: `false`) - `--addApp` [boolean] Add App.tsx file content which required provider (default: `false`) +- `-b --beta` [boolean] Add beta components (default: `false`) ##### Example @@ -208,6 +210,7 @@ nextui upgrade [components...] [options] - `-p --packagePath` [string] The path to the package.json file - `-a --all` [boolean] Upgrade all the NextUI components (default: `false`) - `-w --write` [boolean] Write the upgrade version to package.json file (default: `false`) +- `-b --beta` [boolean] Upgrade beta components (default: `false`) - `-h --help` Display help for command ##### Example diff --git a/package.json b/package.json index db1379b..43b7279 100644 --- a/package.json +++ b/package.json @@ -64,6 +64,7 @@ "async-retry": "1.3.3", "chalk": "5.3.0", "commander": "11.0.0", + "compare-versions": "6.1.1", "fast-glob": "3.3.2", "find-up": "7.0.0", "gradient-string": "2.0.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4e218d8..12d0161 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -23,6 +23,9 @@ importers: commander: specifier: 11.0.0 version: 11.0.0 + compare-versions: + specifier: 6.1.1 + version: 6.1.1 fast-glob: specifier: 3.3.2 version: 3.3.2 @@ -56,7 +59,7 @@ importers: version: 20.11.30 '@typescript-eslint/eslint-plugin': specifier: 6.7.2 - version: 6.7.2(@typescript-eslint/parser@6.7.2)(eslint@8.50.0)(typescript@5.2.2) + version: 6.7.2(@typescript-eslint/parser@6.7.2(eslint@8.50.0)(typescript@5.2.2))(eslint@8.50.0)(typescript@5.2.2) '@typescript-eslint/parser': specifier: 6.7.2 version: 6.7.2(eslint@8.50.0)(typescript@5.2.2) @@ -80,13 +83,13 @@ importers: version: 9.0.0(eslint@8.50.0) eslint-import-resolver-typescript: specifier: 3.6.1 - version: 3.6.1(@typescript-eslint/parser@6.7.2)(eslint-plugin-import@2.28.1)(eslint@8.50.0) + version: 3.6.1(@typescript-eslint/parser@6.7.2(eslint@8.50.0)(typescript@5.2.2))(eslint-plugin-import@2.28.1)(eslint@8.50.0) eslint-plugin-import: specifier: 2.28.1 - version: 2.28.1(@typescript-eslint/parser@6.7.2)(eslint-import-resolver-typescript@3.6.1)(eslint@8.50.0) + version: 2.28.1(@typescript-eslint/parser@6.7.2(eslint@8.50.0)(typescript@5.2.2))(eslint-import-resolver-typescript@3.6.1)(eslint@8.50.0) eslint-plugin-prettier: specifier: 5.0.0 - version: 5.0.0(eslint-config-prettier@9.0.0)(eslint@8.50.0)(prettier@3.3.2) + version: 5.0.0(eslint-config-prettier@9.0.0(eslint@8.50.0))(eslint@8.50.0)(prettier@3.3.2) eslint-plugin-sort-destructure-keys: specifier: 1.5.0 version: 1.5.0(eslint@8.50.0) @@ -98,7 +101,7 @@ importers: version: 52.0.0(eslint@8.50.0) eslint-plugin-unused-imports: specifier: 3.0.0 - version: 3.0.0(@typescript-eslint/eslint-plugin@6.7.2)(eslint@8.50.0) + version: 3.0.0(@typescript-eslint/eslint-plugin@6.7.2(@typescript-eslint/parser@6.7.2(eslint@8.50.0)(typescript@5.2.2))(eslint@8.50.0)(typescript@5.2.2))(eslint@8.50.0) husky: specifier: 8.0.3 version: 8.0.3 @@ -110,7 +113,7 @@ importers: version: 3.3.2 tsup: specifier: 7.2.0 - version: 7.2.0(ts-node@10.9.2)(typescript@5.2.2) + version: 7.2.0(ts-node@10.9.2(@types/node@20.5.1)(typescript@5.2.2))(typescript@5.2.2) tsx: specifier: 4.7.1 version: 4.7.1 @@ -927,6 +930,9 @@ packages: compare-func@2.0.0: resolution: {integrity: sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==} + compare-versions@6.1.1: + resolution: {integrity: sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==} + concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} @@ -2921,12 +2927,12 @@ snapshots: '@types/node': 20.5.1 chalk: 4.1.2 cosmiconfig: 8.3.6(typescript@5.2.2) - cosmiconfig-typescript-loader: 4.4.0(@types/node@20.5.1)(cosmiconfig@8.3.6)(ts-node@10.9.2)(typescript@5.2.2) + cosmiconfig-typescript-loader: 4.4.0(@types/node@20.5.1)(cosmiconfig@8.3.6(typescript@5.2.2))(ts-node@10.9.2(@types/node@20.5.1)(typescript@5.2.2))(typescript@5.2.2) lodash.isplainobject: 4.0.6 lodash.merge: 4.6.2 lodash.uniq: 4.5.0 resolve-from: 5.0.0 - ts-node: 10.9.2(@types/node@20.11.30)(typescript@5.2.2) + ts-node: 10.9.2(@types/node@20.5.1)(typescript@5.2.2) typescript: 5.2.2 transitivePeerDependencies: - '@swc/core' @@ -3236,7 +3242,7 @@ snapshots: '@types/tinycolor2@1.4.6': {} - '@typescript-eslint/eslint-plugin@6.7.2(@typescript-eslint/parser@6.7.2)(eslint@8.50.0)(typescript@5.2.2)': + '@typescript-eslint/eslint-plugin@6.7.2(@typescript-eslint/parser@6.7.2(eslint@8.50.0)(typescript@5.2.2))(eslint@8.50.0)(typescript@5.2.2)': dependencies: '@eslint-community/regexpp': 4.10.0 '@typescript-eslint/parser': 6.7.2(eslint@8.50.0)(typescript@5.2.2) @@ -3251,6 +3257,7 @@ snapshots: natural-compare: 1.4.0 semver: 7.6.2 ts-api-utils: 1.3.0(typescript@5.2.2) + optionalDependencies: typescript: 5.2.2 transitivePeerDependencies: - supports-color @@ -3263,6 +3270,7 @@ snapshots: '@typescript-eslint/visitor-keys': 6.7.2 debug: 4.3.4 eslint: 8.50.0 + optionalDependencies: typescript: 5.2.2 transitivePeerDependencies: - supports-color @@ -3279,6 +3287,7 @@ snapshots: debug: 4.3.4 eslint: 8.50.0 ts-api-utils: 1.3.0(typescript@5.2.2) + optionalDependencies: typescript: 5.2.2 transitivePeerDependencies: - supports-color @@ -3294,6 +3303,7 @@ snapshots: is-glob: 4.0.3 semver: 7.6.2 ts-api-utils: 1.3.0(typescript@5.2.2) + optionalDependencies: typescript: 5.2.2 transitivePeerDependencies: - supports-color @@ -3614,6 +3624,8 @@ snapshots: array-ify: 1.0.0 dot-prop: 5.3.0 + compare-versions@6.1.1: {} + concat-map@0.0.1: {} confbox@0.1.7: {} @@ -3717,11 +3729,11 @@ snapshots: dependencies: browserslist: 4.23.0 - cosmiconfig-typescript-loader@4.4.0(@types/node@20.5.1)(cosmiconfig@8.3.6)(ts-node@10.9.2)(typescript@5.2.2): + cosmiconfig-typescript-loader@4.4.0(@types/node@20.5.1)(cosmiconfig@8.3.6(typescript@5.2.2))(ts-node@10.9.2(@types/node@20.5.1)(typescript@5.2.2))(typescript@5.2.2): dependencies: '@types/node': 20.5.1 cosmiconfig: 8.3.6(typescript@5.2.2) - ts-node: 10.9.2(@types/node@20.11.30)(typescript@5.2.2) + ts-node: 10.9.2(@types/node@20.5.1)(typescript@5.2.2) typescript: 5.2.2 cosmiconfig@8.3.6(typescript@5.2.2): @@ -3730,6 +3742,7 @@ snapshots: js-yaml: 4.1.0 parse-json: 5.2.0 path-type: 4.0.0 + optionalDependencies: typescript: 5.2.2 create-require@1.1.1: {} @@ -3982,13 +3995,13 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.7.2)(eslint-plugin-import@2.28.1)(eslint@8.50.0): + eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.7.2(eslint@8.50.0)(typescript@5.2.2))(eslint-plugin-import@2.28.1)(eslint@8.50.0): dependencies: debug: 4.3.4 enhanced-resolve: 5.16.1 eslint: 8.50.0 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.7.2)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.50.0) - eslint-plugin-import: 2.28.1(@typescript-eslint/parser@6.7.2)(eslint-import-resolver-typescript@3.6.1)(eslint@8.50.0) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.7.2(eslint@8.50.0)(typescript@5.2.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.7.2(eslint@8.50.0)(typescript@5.2.2))(eslint-plugin-import@2.28.1)(eslint@8.50.0))(eslint@8.50.0) + eslint-plugin-import: 2.28.1(@typescript-eslint/parser@6.7.2(eslint@8.50.0)(typescript@5.2.2))(eslint-import-resolver-typescript@3.6.1)(eslint@8.50.0) fast-glob: 3.3.2 get-tsconfig: 4.7.5 is-core-module: 2.13.1 @@ -3999,19 +4012,19 @@ snapshots: - eslint-import-resolver-webpack - supports-color - eslint-module-utils@2.8.1(@typescript-eslint/parser@6.7.2)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.50.0): + eslint-module-utils@2.8.1(@typescript-eslint/parser@6.7.2(eslint@8.50.0)(typescript@5.2.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.7.2(eslint@8.50.0)(typescript@5.2.2))(eslint-plugin-import@2.28.1)(eslint@8.50.0))(eslint@8.50.0): dependencies: - '@typescript-eslint/parser': 6.7.2(eslint@8.50.0)(typescript@5.2.2) debug: 3.2.7 + optionalDependencies: + '@typescript-eslint/parser': 6.7.2(eslint@8.50.0)(typescript@5.2.2) eslint: 8.50.0 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.7.2)(eslint-plugin-import@2.28.1)(eslint@8.50.0) + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.7.2(eslint@8.50.0)(typescript@5.2.2))(eslint-plugin-import@2.28.1)(eslint@8.50.0) transitivePeerDependencies: - supports-color - eslint-plugin-import@2.28.1(@typescript-eslint/parser@6.7.2)(eslint-import-resolver-typescript@3.6.1)(eslint@8.50.0): + eslint-plugin-import@2.28.1(@typescript-eslint/parser@6.7.2(eslint@8.50.0)(typescript@5.2.2))(eslint-import-resolver-typescript@3.6.1)(eslint@8.50.0): dependencies: - '@typescript-eslint/parser': 6.7.2(eslint@8.50.0)(typescript@5.2.2) array-includes: 3.1.8 array.prototype.findlastindex: 1.2.5 array.prototype.flat: 1.3.2 @@ -4020,7 +4033,7 @@ snapshots: doctrine: 2.1.0 eslint: 8.50.0 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.7.2)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.50.0) + eslint-module-utils: 2.8.1(@typescript-eslint/parser@6.7.2(eslint@8.50.0)(typescript@5.2.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.7.2(eslint@8.50.0)(typescript@5.2.2))(eslint-plugin-import@2.28.1)(eslint@8.50.0))(eslint@8.50.0) has: 1.0.4 is-core-module: 2.13.1 is-glob: 4.0.3 @@ -4030,18 +4043,21 @@ snapshots: object.values: 1.2.0 semver: 6.3.1 tsconfig-paths: 3.15.0 + optionalDependencies: + '@typescript-eslint/parser': 6.7.2(eslint@8.50.0)(typescript@5.2.2) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color - eslint-plugin-prettier@5.0.0(eslint-config-prettier@9.0.0)(eslint@8.50.0)(prettier@3.3.2): + eslint-plugin-prettier@5.0.0(eslint-config-prettier@9.0.0(eslint@8.50.0))(eslint@8.50.0)(prettier@3.3.2): dependencies: eslint: 8.50.0 - eslint-config-prettier: 9.0.0(eslint@8.50.0) prettier: 3.3.2 prettier-linter-helpers: 1.0.0 synckit: 0.8.8 + optionalDependencies: + eslint-config-prettier: 9.0.0(eslint@8.50.0) eslint-plugin-sort-destructure-keys@1.5.0(eslint@8.50.0): dependencies: @@ -4077,11 +4093,12 @@ snapshots: transitivePeerDependencies: - supports-color - eslint-plugin-unused-imports@3.0.0(@typescript-eslint/eslint-plugin@6.7.2)(eslint@8.50.0): + eslint-plugin-unused-imports@3.0.0(@typescript-eslint/eslint-plugin@6.7.2(@typescript-eslint/parser@6.7.2(eslint@8.50.0)(typescript@5.2.2))(eslint@8.50.0)(typescript@5.2.2))(eslint@8.50.0): dependencies: - '@typescript-eslint/eslint-plugin': 6.7.2(@typescript-eslint/parser@6.7.2)(eslint@8.50.0)(typescript@5.2.2) eslint: 8.50.0 eslint-rule-composer: 0.3.0 + optionalDependencies: + '@typescript-eslint/eslint-plugin': 6.7.2(@typescript-eslint/parser@6.7.2(eslint@8.50.0)(typescript@5.2.2))(eslint@8.50.0)(typescript@5.2.2) eslint-rule-composer@0.3.0: {} @@ -5037,11 +5054,12 @@ snapshots: possible-typed-array-names@1.0.0: {} - postcss-load-config@4.0.2(ts-node@10.9.2): + postcss-load-config@4.0.2(ts-node@10.9.2(@types/node@20.5.1)(typescript@5.2.2)): dependencies: lilconfig: 3.1.1 - ts-node: 10.9.2(@types/node@20.11.30)(typescript@5.2.2) yaml: 2.4.2 + optionalDependencies: + ts-node: 10.9.2(@types/node@20.5.1)(typescript@5.2.2) prelude-ls@1.2.1: {} @@ -5411,14 +5429,14 @@ snapshots: ts-interface-checker@0.1.13: {} - ts-node@10.9.2(@types/node@20.11.30)(typescript@5.2.2): + ts-node@10.9.2(@types/node@20.5.1)(typescript@5.2.2): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.11 '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 20.11.30 + '@types/node': 20.5.1 acorn: 8.11.3 acorn-walk: 8.3.2 arg: 4.1.3 @@ -5438,7 +5456,7 @@ snapshots: tslib@2.6.2: {} - tsup@7.2.0(ts-node@10.9.2)(typescript@5.2.2): + tsup@7.2.0(ts-node@10.9.2(@types/node@20.5.1)(typescript@5.2.2))(typescript@5.2.2): dependencies: bundle-require: 4.1.0(esbuild@0.18.20) cac: 6.7.14 @@ -5448,12 +5466,13 @@ snapshots: execa: 5.1.1 globby: 11.1.0 joycon: 3.1.1 - postcss-load-config: 4.0.2(ts-node@10.9.2) + postcss-load-config: 4.0.2(ts-node@10.9.2(@types/node@20.5.1)(typescript@5.2.2)) resolve-from: 5.0.0 rollup: 3.29.4 source-map: 0.8.0-beta.0 sucrase: 3.35.0 tree-kill: 1.2.2 + optionalDependencies: typescript: 5.2.2 transitivePeerDependencies: - supports-color diff --git a/src/actions/add-action.ts b/src/actions/add-action.ts index 41d3655..b50c019 100644 --- a/src/actions/add-action.ts +++ b/src/actions/add-action.ts @@ -5,6 +5,7 @@ import {existsSync, writeFileSync} from 'node:fs'; import chalk from 'chalk'; +import {getBetaComponents} from '@helpers/beta'; import { checkApp, checkIllegalComponents, @@ -36,6 +37,7 @@ interface AddActionOptions { tailwindPath?: string; appPath?: string; addApp?: boolean; + beta?: boolean; } export async function addAction(components: string[], options: AddActionOptions) { @@ -43,6 +45,7 @@ export async function addAction(components: string[], options: AddActionOptions) addApp = false, all = false, appPath = findFiles('**/App.(j|t)sx')[0], + beta = false, packagePath = resolver('package.json'), tailwindPath = findFiles('**/tailwind.config.(j|t)s')[0] } = options; @@ -73,7 +76,7 @@ export async function addAction(components: string[], options: AddActionOptions) } /** ======================== Add judge whether illegal component exist ======================== */ - if (!all && !checkIllegalComponents(components)) { + if (!all && !checkIllegalComponents(components, true)) { return; } @@ -113,7 +116,8 @@ export async function addAction(components: string[], options: AddActionOptions) if (all) { const [, ...missingDependencies] = await checkRequiredContentInstalled( 'all', - allDependenciesKeys + allDependenciesKeys, + {beta} ); if (missingDependencies.length) { @@ -131,12 +135,13 @@ export async function addAction(components: string[], options: AddActionOptions) } else { const [, ..._missingDependencies] = await checkRequiredContentInstalled( 'partial', - allDependenciesKeys + allDependenciesKeys, + {beta} ); - const missingDependencies = [ - ..._missingDependencies, - ...components.map((c) => store.nextUIComponentsMap[c]!.package) - ]; + const mergedComponents = beta + ? await getBetaComponents(components) + : components.map((c) => store.nextUIComponentsMap[c]!.package); + const missingDependencies = [..._missingDependencies, ...mergedComponents]; Logger.info( `Adding required dependencies: ${[...missingDependencies] diff --git a/src/actions/upgrade-action.ts b/src/actions/upgrade-action.ts index 68bbb5d..ba68e19 100644 --- a/src/actions/upgrade-action.ts +++ b/src/actions/upgrade-action.ts @@ -2,6 +2,7 @@ import type {AppendKeyValue} from '@helpers/type'; import fs from 'node:fs'; +import {getBetaVersion} from '@helpers/beta'; import {checkIllegalComponents} from '@helpers/check'; import {detect} from '@helpers/detect'; import {exec} from '@helpers/exec'; @@ -25,14 +26,28 @@ interface UpgradeActionOptions { minor?: boolean; patch?: boolean; write?: boolean; + beta?: boolean; } type TransformComponent = Required< AppendKeyValue & {isLatest: boolean} >; +function betaCompareVersions(version: string, latestVersion: string, beta: boolean) { + const compareResult = compareVersions(version, latestVersion); + + // Beta version is greater than latest version if beta is true + // Example: 2.1.0 < 2.1.0-beta.0 + return beta && compareResult === 1 && !version.includes('beta') ? false : compareResult >= 0; +} + export async function upgradeAction(components: string[], options: UpgradeActionOptions) { - const {all = false, packagePath = resolver('package.json'), write = false} = options; + const { + all = false, + beta = false, + packagePath = resolver('package.json'), + write = false + } = options; const {allDependencies, currentComponents, dependencies, devDependencies, packageJson} = getPackageInfo(packagePath, false); @@ -40,17 +55,21 @@ export async function upgradeAction(components: string[], options: UpgradeAction const transformComponents: TransformComponent[] = []; - for (const component of currentComponents) { - const latestVersion = - store.nextUIComponentsMap[component.name]?.version || - (await getLatestVersion(component.package)); - - transformComponents.push({ - ...component, - isLatest: compareVersions(component.version, latestVersion) >= 0, - latestVersion - }); - } + await Promise.all( + currentComponents.map(async (component) => { + const latestVersion = + store.nextUIComponentsMap[component.name]?.version || + (await getLatestVersion(component.package)); + const mergedVersion = beta ? await getBetaVersion(component.package) : latestVersion; + const compareResult = betaCompareVersions(component.version, mergedVersion, beta); + + transformComponents.push({ + ...component, + isLatest: compareResult, + latestVersion: mergedVersion + }); + }) + ); // If no Installed NextUI components then exit if (!transformComponents.length && !isNextUIAll) { @@ -84,7 +103,7 @@ export async function upgradeAction(components: string[], options: UpgradeAction components = await getAutocompleteMultiselect( 'Select the components to upgrade', transformComponents.map((component) => { - const isUpToDate = compareVersions(component.version, component.latestVersion) >= 0; + const isUpToDate = betaCompareVersions(component.version, component.latestVersion, beta); return { disabled: isUpToDate, diff --git a/src/constants/components.json b/src/constants/components.json index 0255f8a..9cd92cd 100644 --- a/src/constants/components.json +++ b/src/constants/components.json @@ -1,9 +1,692 @@ { + "betaComponents": [ + { + "name": "accordion", + "package": "@nextui-org/accordion", + "version": "2.1.0-beta.3", + "docs": "https://nextui.org/docs/components/accordion", + "description": "Collapse display a list of high-level options that can expand/collapse to reveal more information.", + "status": "stable", + "style": "", + "peerDependencies": { + "react": ">=18 || >=19.0.0-rc.0", + "react-dom": ">=18 || >=19.0.0-rc.0", + "framer-motion": ">=11.5.6", + "@nextui-org/theme": ">=2.3.0-beta.0", + "@nextui-org/system": ">=2.3.0-beta.0", + "tailwindcss": ">=3.4.0" + } + }, + { + "name": "alert", + "package": "@nextui-org/alert", + "version": "2.1.0-beta.3", + "docs": "https://nextui.org/docs/components/alert", + "description": "Alerts are temporary notifications that provide concise feedback about an action or event.", + "status": "stable", + "style": "", + "peerDependencies": { + "react": ">=18 || >=19.0.0-rc.0", + "react-dom": ">=18 || >=19.0.0-rc.0", + "@nextui-org/theme": ">=2.3.0-beta.0", + "@nextui-org/system": ">=2.3.0-beta.0", + "tailwindcss": ">=3.4.0" + } + }, + { + "name": "autocomplete", + "package": "@nextui-org/autocomplete", + "version": "2.2.0-beta.5", + "docs": "https://nextui.org/docs/components/autocomplete", + "description": "An autocomplete combines a text input with a listbox, allowing users to filter a list of options to items matching a query.", + "status": "stable", + "style": "", + "peerDependencies": { + "@nextui-org/system": ">=2.3.0-beta.0", + "@nextui-org/theme": ">=2.3.0-beta.0", + "framer-motion": ">=11.5.6 || >=12.0.0-alpha.1", + "react": ">=18 || >=19.0.0-rc.0", + "react-dom": ">=18 || >=19.0.0-rc.0", + "tailwindcss": ">=3.4.0" + } + }, + { + "name": "avatar", + "package": "@nextui-org/avatar", + "version": "2.1.0-beta.6", + "docs": "https://nextui.org/docs/components/avatar", + "description": "The Avatar component is used to represent a user, and displays the profile picture, initials or fallback icon.", + "status": "stable", + "style": "", + "peerDependencies": { + "react": ">=18 || >=19.0.0-rc.0", + "react-dom": ">=18 || >=19.0.0-rc.0", + "@nextui-org/theme": ">=2.3.0-beta.0", + "@nextui-org/system": ">=2.3.0-beta.0", + "tailwindcss": ">=3.4.0" + } + }, + { + "name": "badge", + "package": "@nextui-org/badge", + "version": "2.1.0-beta.3", + "docs": "https://nextui.org/docs/components/badge", + "description": "Badges are used as a small numerical value or status descriptor for UI elements.", + "status": "updated", + "style": "", + "peerDependencies": { + "react": ">=18 || >=19.0.0-rc.0", + "react-dom": ">=18 || >=19.0.0-rc.0", + "@nextui-org/theme": ">=2.3.0-beta.0", + "@nextui-org/system": ">=2.3.0-beta.0", + "tailwindcss": ">=3.4.0" + } + }, + { + "name": "breadcrumbs", + "package": "@nextui-org/breadcrumbs", + "version": "2.1.0-beta.3", + "docs": "https://nextui.org/docs/components/breadcrumbs", + "description": "Breadcrumbs display a hierarchy of links to the current page or resource in an application.", + "status": "stable", + "style": "", + "peerDependencies": { + "react": ">=18 || >=19.0.0-rc.0", + "react-dom": ">=18 || >=19.0.0-rc.0", + "@nextui-org/theme": ">=2.3.0-beta.0", + "@nextui-org/system": ">=2.3.0-beta.0", + "tailwindcss": ">=3.4.0" + } + }, + { + "name": "button", + "package": "@nextui-org/button", + "version": "2.1.0-beta.3", + "docs": "https://nextui.org/docs/components/button", + "description": "Buttons allow users to perform actions and choose with a single tap.", + "status": "stable", + "style": "", + "peerDependencies": { + "react": ">=18 || >=19.0.0-rc.0", + "react-dom": ">=18 || >=19.0.0-rc.0", + "framer-motion": ">=11.5.6 || >=12.0.0-alpha.1", + "@nextui-org/theme": ">=2.3.0-beta.0", + "@nextui-org/system": ">=2.3.0-beta.0", + "tailwindcss": ">=3.4.0" + } + }, + { + "name": "calendar", + "package": "@nextui-org/calendar", + "version": "2.1.0-beta.3", + "docs": "https://nextui.org/docs/components/calendar", + "description": "A calendar displays one or more date grids and allows users to select a single date.", + "status": "new", + "style": "", + "peerDependencies": { + "@nextui-org/system": ">=2.3.0-beta.0", + "@nextui-org/theme": ">=2.3.0-beta.0", + "react": ">=18 || >=19.0.0-rc.0", + "react-dom": ">=18 || >=19.0.0-rc.0", + "tailwindcss": ">=3.4.0" + } + }, + { + "name": "card", + "package": "@nextui-org/card", + "version": "2.1.0-beta.3", + "docs": "https://nextui.org/docs/components/card", + "description": "Card is a container for text, photos, and actions in the context of a single subject.", + "status": "stable", + "style": "", + "peerDependencies": { + "react": ">=18 || >=19.0.0-rc.0", + "react-dom": ">=18 || >=19.0.0-rc.0", + "framer-motion": ">=11.5.6 || >=12.0.0-alpha.1", + "@nextui-org/theme": ">=2.3.0-beta.0", + "@nextui-org/system": ">=2.3.0-beta.0", + "tailwindcss": ">=3.4.0" + } + }, + { + "name": "checkbox", + "package": "@nextui-org/checkbox", + "version": "2.2.0-beta.3", + "docs": "https://nextui.org/docs/components/checkbox", + "description": "Checkboxes allow users to select multiple items from a list of individual items, or to mark one individual item as selected.", + "status": "updated", + "style": "", + "peerDependencies": { + "@nextui-org/system": ">=2.3.0-beta.0", + "@nextui-org/theme": ">=2.3.0-beta.0", + "react": ">=18 || >=19.0.0-rc.0", + "react-dom": ">=18 || >=19.0.0-rc.0", + "tailwindcss": ">=3.4.0" + } + }, + { + "name": "chip", + "package": "@nextui-org/chip", + "version": "2.1.0-beta.3", + "docs": "https://nextui.org/docs/components/chip", + "description": "Chips help people enter information, make selections, filter content, or trigger actions.", + "status": "stable", + "style": "", + "peerDependencies": { + "react": ">=18 || >=19.0.0-rc.0", + "react-dom": ">=18 || >=19.0.0-rc.0", + "@nextui-org/theme": ">=2.3.0-beta.0", + "@nextui-org/system": ">=2.3.0-beta.0", + "tailwindcss": ">=3.4.0" + } + }, + { + "name": "code", + "package": "@nextui-org/code", + "version": "2.1.0-beta.3", + "docs": "https://nextui.org/docs/components/code", + "description": "Code is a component used to display inline code.", + "status": "stable", + "style": "", + "peerDependencies": { + "react": ">=18 || >=19.0.0-rc.0", + "react-dom": ">=18 || >=19.0.0-rc.0", + "@nextui-org/theme": ">=2.3.0-beta.0", + "tailwindcss": ">=3.4.0" + } + }, + { + "name": "date-input", + "package": "@nextui-org/date-input", + "version": "2.2.0-beta.3", + "docs": "https://nextui.org/docs/components/date-input", + "description": "A date input allows users to enter and edit date and time values using a keyboard.", + "status": "new", + "style": "", + "peerDependencies": { + "@nextui-org/system": ">=2.3.0-beta.0", + "@nextui-org/theme": ">=2.3.0-beta.0", + "react": ">=18 || >=19.0.0-rc.0", + "react-dom": ">=18 || >=19.0.0-rc.0", + "tailwindcss": ">=3.4.0" + } + }, + { + "name": "date-picker", + "package": "@nextui-org/date-picker", + "version": "2.2.0-beta.4", + "docs": "https://nextui.org/docs/components/date-picker", + "description": "A date picker combines a DateInput and a Calendar popover to allow users to enter or select a date and time value.", + "status": "new", + "style": "", + "peerDependencies": { + "@nextui-org/system": ">=2.3.0-beta.0", + "@nextui-org/theme": ">=2.3.0-beta.0", + "react": ">=18 || >=19.0.0-rc.0", + "react-dom": ">=18 || >=19.0.0-rc.0", + "tailwindcss": ">=3.4.0" + } + }, + { + "name": "divider", + "package": "@nextui-org/divider", + "version": "2.1.0-beta.3", + "docs": "https://nextui.org/docs/components/divider", + "description": ". A separator is a visual divider between two groups of content", + "status": "stable", + "style": "", + "peerDependencies": { + "react": ">=18 || >=19.0.0-rc.0", + "react-dom": ">=18 || >=19.0.0-rc.0", + "@nextui-org/theme": ">=2.3.0-beta.0", + "tailwindcss": ">=3.4.0" + } + }, + { + "name": "drawer", + "package": "@nextui-org/drawer", + "version": "2.1.0-beta.3", + "docs": "https://nextui.org/docs/components/drawer", + "description": "Used to render a content that slides in from the side of the screen.", + "status": "new", + "style": "", + "peerDependencies": { + "react": ">=18 || >=19.0.0-rc.0", + "react-dom": ">=18 || >=19.0.0-rc.0", + "@nextui-org/theme": ">=2.3.0-beta.0", + "@nextui-org/system": ">=2.3.0-beta.0", + "tailwindcss": ">=3.4.0" + } + }, + { + "name": "dropdown", + "package": "@nextui-org/dropdown", + "version": "2.2.0-beta.4", + "docs": "https://nextui.org/docs/components/dropdown", + "description": "A dropdown displays a list of actions or options that a user can choose.", + "status": "stable", + "style": "", + "peerDependencies": { + "@nextui-org/system": ">=2.3.0-beta.0", + "@nextui-org/theme": ">=2.3.0-beta.0", + "framer-motion": ">=11.5.6 || >=12.0.0-alpha.1", + "react": ">=18 || >=19.0.0-rc.0", + "react-dom": ">=18 || >=19.0.0-rc.0", + "tailwindcss": ">=3.4.0" + } + }, + { + "name": "image", + "package": "@nextui-org/image", + "version": "2.1.0-beta.6", + "docs": "https://nextui.org/docs/components/image", + "description": "A simple image component", + "status": "stable", + "style": "", + "peerDependencies": { + "react": ">=18 || >=19.0.0-rc.0", + "react-dom": ">=18 || >=19.0.0-rc.0", + "@nextui-org/theme": ">=2.3.0-beta.0", + "@nextui-org/system": ">=2.3.0-beta.0", + "tailwindcss": ">=3.4.0" + } + }, + { + "name": "input", + "package": "@nextui-org/input", + "version": "2.3.0-beta.4", + "docs": "https://nextui.org/docs/components/input", + "description": "The input component is designed for capturing user input within a text field.", + "status": "stable", + "style": "", + "peerDependencies": { + "react": ">=18 || >=19.0.0-rc.0", + "react-dom": ">=18 || >=19.0.0-rc.0", + "@nextui-org/theme": ">=2.3.0-beta.0", + "@nextui-org/system": ">=2.3.0-beta.0", + "tailwindcss": ">=3.4.0" + } + }, + { + "name": "kbd", + "package": "@nextui-org/kbd", + "version": "2.1.0-beta.3", + "docs": "https://nextui.org/docs/components/kbd", + "description": "The keyboard key components indicates which key or set of keys used to execute a specificv action", + "status": "stable", + "style": "", + "peerDependencies": { + "react": ">=18 || >=19.0.0-rc.0", + "react-dom": ">=18 || >=19.0.0-rc.0", + "@nextui-org/theme": ">=2.3.0-beta.0", + "tailwindcss": ">=3.4.0" + } + }, + { + "name": "link", + "package": "@nextui-org/link", + "version": "2.1.0-beta.3", + "docs": "https://nextui.org/docs/components/link", + "description": "Links allow users to click their way from page to page. This component is styled to resemble a hyperlink and semantically renders an <a>", + "status": "stable", + "style": "", + "peerDependencies": { + "react": ">=18 || >=19.0.0-rc.0", + "react-dom": ">=18 || >=19.0.0-rc.0", + "@nextui-org/theme": ">=2.3.0-beta.0", + "@nextui-org/system": ">=2.3.0-beta.0", + "tailwindcss": ">=3.4.0" + } + }, + { + "name": "listbox", + "package": "@nextui-org/listbox", + "version": "2.2.0-beta.3", + "docs": "https://nextui.org/docs/components/listbox", + "description": "A listbox displays a list of options and allows a user to select one or more of them.", + "status": "stable", + "style": "", + "peerDependencies": { + "react": ">=18 || >=19.0.0-rc.0", + "react-dom": ">=18 || >=19.0.0-rc.0", + "@nextui-org/theme": ">=2.3.0-beta.0", + "@nextui-org/system": ">=2.3.0-beta.0", + "tailwindcss": ">=3.4.0" + } + }, + { + "name": "menu", + "package": "@nextui-org/menu", + "version": "2.1.0-beta.3", + "docs": "https://nextui.org/docs/components/menu", + "description": "A menu displays a list of options and allows a user to select one or more of them.", + "status": "stable", + "style": "", + "peerDependencies": { + "react": ">=18 || >=19.0.0-rc.0", + "react-dom": ">=18 || >=19.0.0-rc.0", + "@nextui-org/theme": ">=2.3.0-beta.0", + "@nextui-org/system": ">=2.3.0-beta.0", + "tailwindcss": ">=3.4.0" + } + }, + { + "name": "modal", + "package": "@nextui-org/modal", + "version": "2.1.0-beta.3", + "docs": "https://nextui.org/docs/components/modal", + "description": "Displays a dialog with a custom content that requires attention or provides additional information.", + "status": "updated", + "style": "", + "peerDependencies": { + "react": ">=18 || >=19.0.0-rc.0", + "react-dom": ">=18 || >=19.0.0-rc.0", + "framer-motion": ">=11.5.6 || >=12.0.0-alpha.1", + "@nextui-org/theme": ">=2.3.0-beta.0", + "@nextui-org/system": ">=2.3.0-beta.0", + "tailwindcss": ">=3.4.0" + } + }, + { + "name": "navbar", + "package": "@nextui-org/navbar", + "version": "2.1.0-beta.3", + "docs": "https://nextui.org/docs/components/navbar", + "description": "A responsive navigation header positioned on top side of your page that includes support for branding, links, navigation, collapse and more.", + "status": "stable", + "style": "", + "peerDependencies": { + "react": ">=18 || >=19.0.0-rc.0", + "react-dom": ">=18 || >=19.0.0-rc.0", + "framer-motion": ">=11.5.6 || >=12.0.0-alpha.1", + "@nextui-org/theme": ">=2.3.0-beta.0", + "@nextui-org/system": ">=2.3.0-beta.0", + "tailwindcss": ">=3.4.0" + } + }, + { + "name": "pagination", + "package": "@nextui-org/pagination", + "version": "2.1.0-beta.3", + "docs": "https://nextui.org/docs/components/pagination", + "description": "The Pagination component allows you to display active page and navigate between multiple pages.", + "status": "stable", + "style": "", + "peerDependencies": { + "react": ">=18 || >=19.0.0-rc.0", + "react-dom": ">=18 || >=19.0.0-rc.0", + "@nextui-org/theme": ">=2.3.0-beta.0", + "@nextui-org/system": ">=2.3.0-beta.0", + "tailwindcss": ">=3.4.0" + } + }, + { + "name": "popover", + "package": "@nextui-org/popover", + "version": "2.2.0-beta.4", + "docs": "https://nextui.org/docs/components/popover", + "description": "A popover is an overlay element positioned relative to a trigger.", + "status": "stable", + "style": "", + "peerDependencies": { + "@nextui-org/system": ">=2.3.0-beta.0", + "@nextui-org/theme": ">=2.3.0-beta.0", + "framer-motion": ">=11.5.6 || >=12.0.0-alpha.1", + "react": ">=18 || >=19.0.0-rc.0", + "react-dom": ">=18 || >=19.0.0-rc.0", + "tailwindcss": ">=3.4.0" + } + }, + { + "name": "progress", + "package": "@nextui-org/progress", + "version": "2.1.0-beta.3", + "docs": "https://nextui.org/docs/components/progress", + "description": "Progress bars show either determinate or indeterminate progress of an operation over time.", + "status": "stable", + "style": "", + "peerDependencies": { + "react": ">=18 || >=19.0.0-rc.0", + "react-dom": ">=18 || >=19.0.0-rc.0", + "@nextui-org/theme": ">=2.3.0-beta.0", + "@nextui-org/system": ">=2.3.0-beta.0", + "tailwindcss": ">=3.4.0" + } + }, + { + "name": "radio", + "package": "@nextui-org/radio", + "version": "2.2.0-beta.3", + "docs": "https://nextui.org/docs/components/radio", + "description": "Radios allow users to select a single option from a list of mutually exclusive options.", + "status": "stable", + "style": "", + "peerDependencies": { + "react": ">=18 || >=19.0.0-rc.0", + "react-dom": ">=18 || >=19.0.0-rc.0", + "@nextui-org/theme": ">=2.3.0-beta.0", + "@nextui-org/system": ">=2.3.0-beta.0", + "tailwindcss": ">=3.4.0" + } + }, + { + "name": "ripple", + "package": "@nextui-org/ripple", + "version": "2.1.0-beta.3", + "docs": "https://nextui.org/docs/components/ripple", + "description": "A simple implementation to display a ripple animation when the source component is clicked", + "status": "stable", + "style": "", + "peerDependencies": { + "react": ">=18 || >=19.0.0-rc.0", + "react-dom": ">=18 || >=19.0.0-rc.0", + "framer-motion": ">=11.5.6 || >=12.0.0-alpha.1", + "@nextui-org/theme": ">=2.3.0-beta.0", + "@nextui-org/system": ">=2.3.0-beta.0", + "tailwindcss": ">=3.4.0" + } + }, + { + "name": "scroll-shadow", + "package": "@nextui-org/scroll-shadow", + "version": "2.2.0-beta.3", + "docs": "https://nextui.org/docs/components/scroll-shadow", + "description": "A component that applies top and bottom shadows when content overflows on scroll.", + "status": "stable", + "style": "", + "peerDependencies": { + "react": ">=18 || >=19.0.0-rc.0", + "react-dom": ">=18 || >=19.0.0-rc.0", + "@nextui-org/theme": ">=2.3.0-beta.0", + "@nextui-org/system": ">=2.3.0-beta.0", + "tailwindcss": ">=3.4.0" + } + }, + { + "name": "select", + "package": "@nextui-org/select", + "version": "2.3.0-beta.5", + "docs": "https://nextui.org/docs/components/select", + "description": "A select displays a collapsible list of options and allows a user to select one of them.", + "status": "stable", + "style": "", + "peerDependencies": { + "@nextui-org/system": ">=2.3.0-beta.0", + "@nextui-org/theme": ">=2.3.0-beta.0", + "framer-motion": ">=11.5.6 || >=12.0.0-alpha.1", + "react": ">=18 || >=19.0.0-rc.0", + "react-dom": ">=18 || >=19.0.0-rc.0", + "tailwindcss": ">=3.4.0" + } + }, + { + "name": "skeleton", + "package": "@nextui-org/skeleton", + "version": "2.1.0-beta.3", + "docs": "https://nextui.org/docs/components/skeleton", + "description": "Skeleton is used to display the loading state of some component.", + "status": "updated", + "style": "", + "peerDependencies": { + "react": ">=18 || >=19.0.0-rc.0", + "react-dom": ">=18 || >=19.0.0-rc.0", + "@nextui-org/theme": ">=2.3.0-beta.0", + "@nextui-org/system": ">=2.3.0-beta.0", + "tailwindcss": ">=3.4.0" + } + }, + { + "name": "slider", + "package": "@nextui-org/slider", + "version": "2.3.0-beta.3", + "docs": "https://nextui.org/docs/components/slider", + "description": "A slider allows a user to select one or more values within a range.", + "status": "stable", + "style": "", + "peerDependencies": { + "react": ">=18 || >=19.0.0-rc.0", + "react-dom": ">=18 || >=19.0.0-rc.0", + "@nextui-org/theme": ">=2.3.0-beta.0", + "@nextui-org/system": ">=2.3.0-beta.0", + "tailwindcss": ">=3.4.0" + } + }, + { + "name": "snippet", + "package": "@nextui-org/snippet", + "version": "2.1.0-beta.3", + "docs": "https://nextui.org/docs/components/snippet", + "description": "Display a snippet of copyable code for the command line.", + "status": "stable", + "style": "", + "peerDependencies": { + "react": ">=18 || >=19.0.0-rc.0", + "react-dom": ">=18 || >=19.0.0-rc.0", + "framer-motion": ">=11.5.6 || >=12.0.0-alpha.1", + "@nextui-org/theme": ">=2.3.0-beta.0", + "@nextui-org/system": ">=2.3.0-beta.0", + "tailwindcss": ">=3.4.0" + } + }, + { + "name": "spacer", + "package": "@nextui-org/spacer", + "version": "2.1.0-beta.3", + "docs": "https://nextui.org/docs/components/spacer", + "description": "A flexible spacer component designed to create consistent spacing and maintain alignment in your layout.", + "status": "stable", + "style": "", + "peerDependencies": { + "react": ">=18 || >=19.0.0-rc.0", + "react-dom": ">=18 || >=19.0.0-rc.0", + "@nextui-org/theme": ">=2.3.0-beta.0", + "tailwindcss": ">=3.4.0" + } + }, + { + "name": "spinner", + "package": "@nextui-org/spinner", + "version": "2.1.0-beta.3", + "docs": "https://nextui.org/docs/components/spinner", + "description": "Loaders express an unspecified wait time or display the length of a process.", + "status": "stable", + "style": "", + "peerDependencies": { + "react": ">=18 || >=19.0.0-rc.0", + "react-dom": ">=18 || >=19.0.0-rc.0", + "@nextui-org/theme": ">=2.3.0-beta.0", + "tailwindcss": ">=3.4.0" + } + }, + { + "name": "switch", + "package": "@nextui-org/switch", + "version": "2.1.0-beta.3", + "docs": "https://nextui.org/docs/components/switch", + "description": "A switch is similar to a checkbox, but represents on/off values as opposed to selection.", + "status": "stable", + "style": "toggle", + "peerDependencies": { + "react": ">=18 || >=19.0.0-rc.0", + "react-dom": ">=18 || >=19.0.0-rc.0", + "@nextui-org/theme": ">=2.3.0-beta.0", + "@nextui-org/system": ">=2.3.0-beta.0", + "tailwindcss": ">=3.4.0" + } + }, + { + "name": "table", + "package": "@nextui-org/table", + "version": "2.1.0-beta.3", + "docs": "https://nextui.org/docs/components/table", + "description": "Tables are used to display tabular data using rows and columns. ", + "status": "stable", + "style": "", + "peerDependencies": { + "react": ">=18 || >=19.0.0-rc.0", + "react-dom": ">=18 || >=19.0.0-rc.0", + "@nextui-org/theme": ">=2.3.0-beta.0", + "@nextui-org/system": ">=2.3.0-beta.0", + "tailwindcss": ">=3.4.0" + } + }, + { + "name": "tabs", + "package": "@nextui-org/tabs", + "version": "2.1.0-beta.3", + "docs": "https://nextui.org/docs/components/tabs", + "description": "Tabs organize content into multiple sections and allow users to navigate between them.", + "status": "updated", + "style": "", + "peerDependencies": { + "react": ">=18 || >=19.0.0-rc.0", + "react-dom": ">=18 || >=19.0.0-rc.0", + "framer-motion": ">=11.5.6 || >=12.0.0-alpha.1", + "@nextui-org/theme": ">=2.3.0-beta.0", + "@nextui-org/system": ">=2.3.0-beta.0", + "tailwindcss": ">=3.4.0" + } + }, + { + "name": "tooltip", + "package": "@nextui-org/tooltip", + "version": "2.1.0-beta.3", + "docs": "https://nextui.org/docs/components/tooltip", + "description": "A React Component for rendering dynamically positioned Tooltips", + "status": "updated", + "style": "popover", + "peerDependencies": { + "react": ">=18 || >=19.0.0-rc.0", + "react-dom": ">=18 || >=19.0.0-rc.0", + "framer-motion": ">=11.5.6 || >=12.0.0-alpha.1", + "@nextui-org/theme": ">=2.3.0-beta.0", + "@nextui-org/system": ">=2.3.0-beta.0", + "tailwindcss": ">=3.4.0" + } + }, + { + "name": "user", + "package": "@nextui-org/user", + "version": "2.1.0-beta.6", + "docs": "https://nextui.org/docs/components/user", + "description": "Flexible User Profile Component.", + "status": "stable", + "style": "", + "peerDependencies": { + "react": ">=18 || >=19.0.0-rc.0", + "react-dom": ">=18 || >=19.0.0-rc.0", + "@nextui-org/theme": ">=2.3.0-beta.0", + "@nextui-org/system": ">=2.3.0-beta.0", + "tailwindcss": ">=3.4.0" + } + } + ], + "betaVersion": "2.5.0-beta.8", "components": [ { "name": "accordion", "package": "@nextui-org/accordion", - "version": "2.0.38", + "version": "2.0.40", "docs": "https://nextui.org/docs/components/accordion", "description": "Collapse display a list of high-level options that can expand/collapse to reveal more information.", "status": "stable", @@ -20,24 +703,24 @@ { "name": "autocomplete", "package": "@nextui-org/autocomplete", - "version": "2.1.5", + "version": "2.1.7", "docs": "https://nextui.org/docs/components/autocomplete", "description": "An autocomplete combines a text input with a listbox, allowing users to filter a list of options to items matching a query.", "status": "stable", "style": "", "peerDependencies": { + "@nextui-org/system": ">=2.0.0", + "@nextui-org/theme": ">=2.1.0", + "framer-motion": ">=10.17.0", "react": ">=18", "react-dom": ">=18", - "framer-motion": ">=10.17.0", - "@nextui-org/theme": ">=2.1.0", - "@nextui-org/system": ">=2.0.0", "tailwindcss": ">=3.4.0" } }, { "name": "avatar", "package": "@nextui-org/avatar", - "version": "2.0.32", + "version": "2.0.33", "docs": "https://nextui.org/docs/components/avatar", "description": "The Avatar component is used to represent a user, and displays the profile picture, initials or fallback icon.", "status": "stable", @@ -53,7 +736,7 @@ { "name": "badge", "package": "@nextui-org/badge", - "version": "2.0.31", + "version": "2.0.32", "docs": "https://nextui.org/docs/components/badge", "description": "Badges are used as a small numerical value or status descriptor for UI elements.", "status": "updated", @@ -69,7 +752,7 @@ { "name": "breadcrumbs", "package": "@nextui-org/breadcrumbs", - "version": "2.0.12", + "version": "2.0.13", "docs": "https://nextui.org/docs/components/breadcrumbs", "description": "Breadcrumbs display a hierarchy of links to the current page or resource in an application.", "status": "stable", @@ -85,7 +768,7 @@ { "name": "button", "package": "@nextui-org/button", - "version": "2.0.37", + "version": "2.0.38", "docs": "https://nextui.org/docs/components/button", "description": "Buttons allow users to perform actions and choose with a single tap.", "status": "stable", @@ -102,7 +785,7 @@ { "name": "calendar", "package": "@nextui-org/calendar", - "version": "2.0.11", + "version": "2.0.12", "docs": "https://nextui.org/docs/components/calendar", "description": "A calendar displays one or more date grids and allows users to select a single date.", "status": "new", @@ -118,7 +801,7 @@ { "name": "card", "package": "@nextui-org/card", - "version": "2.0.33", + "version": "2.0.34", "docs": "https://nextui.org/docs/components/card", "description": "Card is a container for text, photos, and actions in the context of a single subject.", "status": "stable", @@ -135,7 +818,7 @@ { "name": "checkbox", "package": "@nextui-org/checkbox", - "version": "2.1.4", + "version": "2.1.5", "docs": "https://nextui.org/docs/components/checkbox", "description": "Checkboxes allow users to select multiple items from a list of individual items, or to mark one individual item as selected.", "status": "updated", @@ -151,7 +834,7 @@ { "name": "chip", "package": "@nextui-org/chip", - "version": "2.0.32", + "version": "2.0.33", "docs": "https://nextui.org/docs/components/chip", "description": "Chips help people enter information, make selections, filter content, or trigger actions.", "status": "stable", @@ -167,7 +850,7 @@ { "name": "code", "package": "@nextui-org/code", - "version": "2.0.32", + "version": "2.0.33", "docs": "https://nextui.org/docs/components/code", "description": "Code is a component used to display inline code.", "status": "stable", @@ -182,7 +865,7 @@ { "name": "date-input", "package": "@nextui-org/date-input", - "version": "2.1.3", + "version": "2.1.4", "docs": "https://nextui.org/docs/components/date-input", "description": "A date input allows users to enter and edit date and time values using a keyboard.", "status": "new", @@ -198,7 +881,7 @@ { "name": "date-picker", "package": "@nextui-org/date-picker", - "version": "2.1.6", + "version": "2.1.8", "docs": "https://nextui.org/docs/components/date-picker", "description": "A date picker combines a DateInput and a Calendar popover to allow users to enter or select a date and time value.", "status": "new", @@ -214,7 +897,7 @@ { "name": "divider", "package": "@nextui-org/divider", - "version": "2.0.31", + "version": "2.0.32", "docs": "https://nextui.org/docs/components/divider", "description": ". A separator is a visual divider between two groups of content", "status": "stable", @@ -229,24 +912,24 @@ { "name": "dropdown", "package": "@nextui-org/dropdown", - "version": "2.1.29", + "version": "2.1.31", "docs": "https://nextui.org/docs/components/dropdown", "description": "A dropdown displays a list of actions or options that a user can choose.", "status": "stable", "style": "", "peerDependencies": { + "@nextui-org/system": ">=2.0.0", + "@nextui-org/theme": ">=2.1.0", + "framer-motion": ">=10.17.0", "react": ">=18", "react-dom": ">=18", - "framer-motion": ">=10.17.0", - "@nextui-org/theme": ">=2.1.0", - "@nextui-org/system": ">=2.0.0", "tailwindcss": ">=3.4.0" } }, { "name": "image", "package": "@nextui-org/image", - "version": "2.0.31", + "version": "2.0.32", "docs": "https://nextui.org/docs/components/image", "description": "A simple image component", "status": "stable", @@ -262,7 +945,7 @@ { "name": "input", "package": "@nextui-org/input", - "version": "2.2.4", + "version": "2.2.5", "docs": "https://nextui.org/docs/components/input", "description": "The input component is designed for capturing user input within a text field.", "status": "stable", @@ -278,7 +961,7 @@ { "name": "kbd", "package": "@nextui-org/kbd", - "version": "2.0.33", + "version": "2.0.34", "docs": "https://nextui.org/docs/components/kbd", "description": "The keyboard key components indicates which key or set of keys used to execute a specificv action", "status": "stable", @@ -293,7 +976,7 @@ { "name": "link", "package": "@nextui-org/link", - "version": "2.0.34", + "version": "2.0.35", "docs": "https://nextui.org/docs/components/link", "description": "Links allow users to click their way from page to page. This component is styled to resemble a hyperlink and semantically renders an <a>", "status": "stable", @@ -309,7 +992,7 @@ { "name": "listbox", "package": "@nextui-org/listbox", - "version": "2.1.25", + "version": "2.1.27", "docs": "https://nextui.org/docs/components/listbox", "description": "A listbox displays a list of options and allows a user to select one or more of them.", "status": "stable", @@ -325,7 +1008,7 @@ { "name": "menu", "package": "@nextui-org/menu", - "version": "2.0.28", + "version": "2.0.30", "docs": "https://nextui.org/docs/components/menu", "description": "A menu displays a list of options and allows a user to select one or more of them.", "status": "stable", @@ -341,7 +1024,7 @@ { "name": "modal", "package": "@nextui-org/modal", - "version": "2.0.39", + "version": "2.0.41", "docs": "https://nextui.org/docs/components/modal", "description": "Displays a dialog with a custom content that requires attention or provides additional information.", "status": "stable", @@ -358,7 +1041,7 @@ { "name": "navbar", "package": "@nextui-org/navbar", - "version": "2.0.36", + "version": "2.0.37", "docs": "https://nextui.org/docs/components/navbar", "description": "A responsive navigation header positioned on top side of your page that includes support for branding, links, navigation, collapse and more.", "status": "stable", @@ -375,7 +1058,7 @@ { "name": "pagination", "package": "@nextui-org/pagination", - "version": "2.0.35", + "version": "2.0.36", "docs": "https://nextui.org/docs/components/pagination", "description": "The Pagination component allows you to display active page and navigate between multiple pages.", "status": "stable", @@ -391,24 +1074,24 @@ { "name": "popover", "package": "@nextui-org/popover", - "version": "2.1.27", + "version": "2.1.29", "docs": "https://nextui.org/docs/components/popover", "description": "A popover is an overlay element positioned relative to a trigger.", "status": "stable", "style": "", "peerDependencies": { + "@nextui-org/system": ">=2.0.0", + "@nextui-org/theme": ">=2.1.0", + "framer-motion": ">=10.17.0", "react": ">=18", "react-dom": ">=18", - "framer-motion": ">=10.17.0", - "@nextui-org/theme": ">=2.1.0", - "@nextui-org/system": ">=2.0.0", "tailwindcss": ">=3.4.0" } }, { "name": "progress", "package": "@nextui-org/progress", - "version": "2.0.33", + "version": "2.0.34", "docs": "https://nextui.org/docs/components/progress", "description": "Progress bars show either determinate or indeterminate progress of an operation over time.", "status": "stable", @@ -424,7 +1107,7 @@ { "name": "radio", "package": "@nextui-org/radio", - "version": "2.1.4", + "version": "2.1.5", "docs": "https://nextui.org/docs/components/radio", "description": "Radios allow users to select a single option from a list of mutually exclusive options.", "status": "stable", @@ -440,7 +1123,7 @@ { "name": "ripple", "package": "@nextui-org/ripple", - "version": "2.0.32", + "version": "2.0.33", "docs": "https://nextui.org/docs/components/ripple", "description": "A simple implementation to display a ripple animation when the source component is clicked", "status": "stable", @@ -457,7 +1140,7 @@ { "name": "scroll-shadow", "package": "@nextui-org/scroll-shadow", - "version": "2.1.19", + "version": "2.1.20", "docs": "https://nextui.org/docs/components/scroll-shadow", "description": "A component that applies top and bottom shadows when content overflows on scroll.", "status": "stable", @@ -473,24 +1156,24 @@ { "name": "select", "package": "@nextui-org/select", - "version": "2.2.5", + "version": "2.2.7", "docs": "https://nextui.org/docs/components/select", "description": "A select displays a collapsible list of options and allows a user to select one of them.", "status": "stable", "style": "", "peerDependencies": { + "@nextui-org/system": ">=2.0.0", + "@nextui-org/theme": ">=2.1.0", + "framer-motion": ">=10.17.0", "react": ">=18", "react-dom": ">=18", - "framer-motion": ">=10.17.0", - "@nextui-org/theme": ">=2.1.0", - "@nextui-org/system": ">=2.0.0", "tailwindcss": ">=3.4.0" } }, { "name": "skeleton", "package": "@nextui-org/skeleton", - "version": "2.0.31", + "version": "2.0.32", "docs": "https://nextui.org/docs/components/skeleton", "description": "Skeleton is used to display the loading state of some component.", "status": "updated", @@ -506,7 +1189,7 @@ { "name": "slider", "package": "@nextui-org/slider", - "version": "2.2.15", + "version": "2.2.17", "docs": "https://nextui.org/docs/components/slider", "description": "A slider allows a user to select one or more values within a range.", "status": "stable", @@ -522,7 +1205,7 @@ { "name": "snippet", "package": "@nextui-org/snippet", - "version": "2.0.41", + "version": "2.0.43", "docs": "https://nextui.org/docs/components/snippet", "description": "Display a snippet of copyable code for the command line.", "status": "stable", @@ -539,7 +1222,7 @@ { "name": "spacer", "package": "@nextui-org/spacer", - "version": "2.0.32", + "version": "2.0.33", "docs": "https://nextui.org/docs/components/spacer", "description": "A flexible spacer component designed to create consistent spacing and maintain alignment in your layout.", "status": "stable", @@ -554,7 +1237,7 @@ { "name": "spinner", "package": "@nextui-org/spinner", - "version": "2.0.33", + "version": "2.0.34", "docs": "https://nextui.org/docs/components/spinner", "description": "Loaders express an unspecified wait time or display the length of a process.", "status": "stable", @@ -569,7 +1252,7 @@ { "name": "switch", "package": "@nextui-org/switch", - "version": "2.0.33", + "version": "2.0.34", "docs": "https://nextui.org/docs/components/switch", "description": "A switch is similar to a checkbox, but represents on/off values as opposed to selection.", "status": "stable", @@ -585,7 +1268,7 @@ { "name": "table", "package": "@nextui-org/table", - "version": "2.0.39", + "version": "2.0.40", "docs": "https://nextui.org/docs/components/table", "description": "Tables are used to display tabular data using rows and columns. ", "status": "stable", @@ -601,7 +1284,7 @@ { "name": "tabs", "package": "@nextui-org/tabs", - "version": "2.0.35", + "version": "2.0.37", "docs": "https://nextui.org/docs/components/tabs", "description": "Tabs organize content into multiple sections and allow users to navigate between them.", "status": "updated", @@ -618,7 +1301,7 @@ { "name": "tooltip", "package": "@nextui-org/tooltip", - "version": "2.0.39", + "version": "2.0.41", "docs": "https://nextui.org/docs/components/tooltip", "description": "A React Component for rendering dynamically positioned Tooltips", "status": "updated", @@ -635,7 +1318,7 @@ { "name": "user", "package": "@nextui-org/user", - "version": "2.0.33", + "version": "2.0.34", "docs": "https://nextui.org/docs/components/user", "description": "Flexible User Profile Component.", "status": "stable", @@ -647,7 +1330,39 @@ "@nextui-org/system": ">=2.0.0", "tailwindcss": ">=3.4.0" } + }, + { + "name": "alert", + "package": "@nextui-org/alert", + "version": "2.1.0-beta.3", + "docs": "https://nextui.org/docs/components/alert", + "description": "Alerts are temporary notifications that provide concise feedback about an action or event.", + "status": "stable", + "style": "", + "peerDependencies": { + "react": ">=18 || >=19.0.0-rc.0", + "react-dom": ">=18 || >=19.0.0-rc.0", + "@nextui-org/theme": ">=2.3.0-beta.0", + "@nextui-org/system": ">=2.3.0-beta.0", + "tailwindcss": ">=3.4.0" + } + }, + { + "name": "drawer", + "package": "@nextui-org/drawer", + "version": "2.1.0-beta.3", + "docs": "https://nextui.org/docs/components/drawer", + "description": "Used to render a content that slides in from the side of the screen.", + "status": "new", + "style": "", + "peerDependencies": { + "react": ">=18 || >=19.0.0-rc.0", + "react-dom": ">=18 || >=19.0.0-rc.0", + "@nextui-org/theme": ">=2.3.0-beta.0", + "@nextui-org/system": ">=2.3.0-beta.0", + "tailwindcss": ">=3.4.0" + } } ], - "version": "2.4.6" + "version": "2.4.8" } diff --git a/src/constants/store.ts b/src/constants/store.ts index 1e501f7..85afcbd 100644 --- a/src/constants/store.ts +++ b/src/constants/store.ts @@ -1,5 +1,6 @@ import type {ExtractStoreData, SAFE_ANY} from '@helpers/type'; +import {getBetaVersion} from '@helpers/beta'; import {type Components, getLatestVersion} from 'src/scripts/helpers'; import {NEXTUI_CLI, NEXT_UI} from './required'; @@ -10,6 +11,7 @@ export type Store = { debug: boolean; cliLatestVersion: string; latestVersion: string; + betaVersion: string; nextUIComponents: Components; nextUIComponentsKeys: string[]; nextUIcomponentsPackages: string[]; @@ -35,6 +37,10 @@ export async function getStore( } else if (key === 'cliLatestVersion') { data = (await getLatestVersion(NEXTUI_CLI)) as SAFE_ANY; + store[key] = data; + } else if (key === 'betaVersion') { + data = (await getBetaVersion(NEXT_UI)) as SAFE_ANY; + store[key] = data; } } diff --git a/src/helpers/beta.ts b/src/helpers/beta.ts new file mode 100644 index 0000000..6c49bf7 --- /dev/null +++ b/src/helpers/beta.ts @@ -0,0 +1,45 @@ +import {getCacheExecData} from 'src/scripts/cache/cache'; + +import {Logger} from './logger'; + +export async function getBetaVersionData(component: string) { + const data = await getCacheExecData( + `npm view ${component} dist-tags --json`, + `Fetching ${component} tags` + ); + + return data; +} + +export function getPrefixComponent(component: string) { + return `@nextui-org/${component.replace('@nextui-org/', '')}`; +} + +export async function getBetaVersion(component: string) { + const data = await getBetaVersionData(component); + + try { + return JSON.parse(data).beta; + } catch (error) { + Logger.error(`Get beta version error: ${error}`); + process.exit(1); + } +} + +/** + * @example Input: ["drawer"] + * + * Return: + * ["@nextui-org/drawer@beta"] + */ +export async function getBetaComponents(components: string[]) { + const componentsVersionList = await Promise.all( + components.map(getPrefixComponent).map(async (c) => { + const version = await getBetaVersion(c); + + return `${getPrefixComponent(c)}@${version}`; + }) + ); + + return componentsVersionList; +} diff --git a/src/helpers/check.ts b/src/helpers/check.ts index e890aee..aa83c45 100644 --- a/src/helpers/check.ts +++ b/src/helpers/check.ts @@ -22,6 +22,7 @@ import { import {store} from 'src/constants/store'; import {compareVersions} from 'src/scripts/helpers'; +import {getBetaVersionData} from './beta'; import {Logger} from './logger'; import {getMatchArray, getMatchImport} from './match'; import {findMostMatchText} from './math-diff'; @@ -105,6 +106,7 @@ interface CheckPeerDependenciesConfig { peerDependencies?: boolean; allDependencies?: Record; packageNames?: string[]; + beta?: boolean; } /** @@ -122,12 +124,12 @@ export async function checkRequiredContentInstalled< dependenciesKeys: Set, checkPeerDependenciesConfig?: T extends {peerDependencies: infer P} ? P extends true - ? Required + ? Required> : T : T ): Promise { const result = [] as unknown as CheckResult; - const {allDependencies, packageNames, peerDependencies} = (checkPeerDependenciesConfig ?? + const {allDependencies, beta, packageNames, peerDependencies} = (checkPeerDependenciesConfig ?? {}) as Required; const peerDependenciesList: string[] = []; @@ -145,7 +147,7 @@ export async function checkRequiredContentInstalled< if (hasAllComponents && hasFramerMotion && !peerDependenciesList.length) { return [true]; } - !hasAllComponents && result.push(NEXT_UI); + !hasAllComponents && result.push(beta ? `${NEXT_UI}@${store.betaVersion}` : NEXT_UI); !hasFramerMotion && result.push(FRAMER_MOTION); !hasTailwind && result.push(TAILWINDCSS); } else if (type === 'partial') { @@ -158,8 +160,11 @@ export async function checkRequiredContentInstalled< return [true]; } !hasFramerMotion && result.push(FRAMER_MOTION); - !hasSystemUI && result.push(SYSTEM_UI); - !hasThemeUI && result.push(THEME_UI); + const betaSystemUI = await getBetaVersionData(SYSTEM_UI); + const betaThemeUI = await getBetaVersionData(THEME_UI); + + !hasSystemUI && result.push(beta ? `${SYSTEM_UI}@${betaSystemUI}` : SYSTEM_UI); + !hasThemeUI && result.push(beta ? `${THEME_UI}@${betaThemeUI}` : THEME_UI); !hasTailwind && result.push(TAILWINDCSS); } @@ -167,7 +172,7 @@ export async function checkRequiredContentInstalled< } export async function checkPeerDependencies( - config: Required> + config: Required> ) { const {allDependencies, packageNames} = config; const peerDepList: string[] = []; @@ -344,7 +349,10 @@ export function checkPnpm(npmrcPath: string): CheckResult { return [false, ...result]; } -export function checkIllegalComponents(components: string[], loggerError = true) { +export async function checkIllegalComponents( + components: string[] = [], + loggerError = true +): Promise { const illegalList: [string, null | string][] = []; for (const component of components) { @@ -379,8 +387,8 @@ export function checkIllegalComponents(components: string[], loggerError = true) }` ); - return false; + return false as T extends false ? boolean : string[]; } - return true; + return true as T extends false ? boolean : string[]; } diff --git a/src/helpers/output-info.ts b/src/helpers/output-info.ts index 6e1e394..9fe9777 100644 --- a/src/helpers/output-info.ts +++ b/src/helpers/output-info.ts @@ -20,6 +20,8 @@ const rounded = boxRound.round; const space = ' '; const padStart = `${rounded.vertical}${space}`; const padEnd = `${space}${rounded.vertical}${space}`; +const versionRegex = /([\W\w]+)\snew:/; +const newVersionRegex = /new:\s([\W\w]+)/; /** * Output the components information e.g. status, description, version, etc. @@ -63,8 +65,8 @@ export function outputComponents({ const str = String(component[key]); if (key === 'version') { - const newVersion = str.match(/new:\s([\d.]+)/)?.[1]; - const currentVersion = str.match(/([\d.]+)\snew:/)?.[1]; + const newVersion = str.match(newVersionRegex)?.[1]; + const currentVersion = str.match(versionRegex)?.[1]; const value = strip(generateComponentOutputVersion(currentVersion!, newVersion!)); @@ -95,11 +97,11 @@ export function outputComponents({ /** ======================== Replace version to new version ======================== */ if (commandName !== 'list' && key === 'version') { // Filter list command cause it will list all the latest components - const currentVersion = value.match(/([\d.]+)\snew:/)?.[1]; - const newVersion = value.match(/new:\s([\d.]+)/)?.[1]; + const currentVersion = value.match(versionRegex)?.[1]?.trim(); + const newVersion = value.match(newVersionRegex)?.[1]?.trim(); if (currentVersion === newVersion) { - value = value.replace(/\snew:\s[\d.]+(\s+)?/, ''); + value = value.replace(/\snew:\s[\W\w]+(\s+)?/, ''); value = fillAnsiLength( `${fillAnsiLength(value, componentKeyLengthMap.originVersion)} 🚀latest`, componentKeyLengthMap[key] diff --git a/src/helpers/utils.ts b/src/helpers/utils.ts index a629b2a..350e939 100644 --- a/src/helpers/utils.ts +++ b/src/helpers/utils.ts @@ -105,7 +105,7 @@ export function isMinorUpdate(currentVersion: string, latestVersion: string) { if (currentVersionArr[1] !== latestVersionArr[1]) { return `${chalk.white(latestVersionArr[0])}${chalk.white('.')}${chalk.cyanBright( - latestVersionArr.slice(1, 3).join('.') + latestVersionArr.slice(1).join('.') )}`; } @@ -119,7 +119,7 @@ export function isPatchUpdate(currentVersion: string, latestVersion: string) { if (currentVersionArr[2] !== latestVersionArr[2]) { return `${chalk.white(latestVersionArr.slice(0, 2).join('.'))}${chalk.white( '.' - )}${chalk.greenBright(latestVersionArr[2])}`; + )}${chalk.greenBright(latestVersionArr.slice(2).join('.'))}`; } return ''; diff --git a/src/index.ts b/src/index.ts index 5a5671f..c5a5880 100644 --- a/src/index.ts +++ b/src/index.ts @@ -99,6 +99,7 @@ nextui .option('-app --appPath [string]', 'Specify the path to the App.tsx file') .option('--prettier [boolean]', 'Apply Prettier formatting to the added content') .option('--addApp [boolean]', 'Include App.tsx file content that requires a provider', false) + .option('-b --beta [boolean]', 'Add beta components', false) .action(addAction); nextui @@ -108,6 +109,7 @@ nextui .option('-p --packagePath [string]', 'Specify the path to the package.json file') .option('-a --all [boolean]', 'Upgrade all components', false) .option('-w --write [boolean]', 'Write the upgrade version to package.json file', false) + .option('-b --beta [boolean]', 'Upgrade beta components', false) .action(upgradeAction); nextui @@ -148,6 +150,7 @@ nextui.hook('preAction', async (command) => { const options = (command as SAFE_ANY).rawArgs.slice(2); const noCache = options.includes('--no-cache'); const debug = options.includes('--debug') || options.includes('-d'); + // const componentsArgs = command.args?.slice(1); // Init cache initCache(noCache); @@ -161,8 +164,10 @@ nextui.hook('preAction', async (command) => { initStoreComponentsData(nextUIComponents); } - const cliLatestVersion = await getStore('cliLatestVersion'); - const latestVersion = await getStore('latestVersion'); + const [cliLatestVersion, latestVersion] = await Promise.all([ + getStore('cliLatestVersion'), + getStore('latestVersion') + ]); // Init latest version store.latestVersion = latestVersion; diff --git a/src/prompts/get-beta-version-select.ts b/src/prompts/get-beta-version-select.ts new file mode 100644 index 0000000..3c29ecd --- /dev/null +++ b/src/prompts/get-beta-version-select.ts @@ -0,0 +1,27 @@ +import {getBetaVersionData} from '@helpers/beta'; + +import {getSelect} from './index'; + +export async function getBetaVersionSelect(components: string[]) { + const result: string[] = []; + + for (const component of components) { + const betaVersionData = JSON.parse(await getBetaVersionData(component)); + + const selectedResult = await getSelect( + `Select beta version of ${component}`, + Object.values(betaVersionData).map((value) => { + const betaVersion = `${component}@${value}`; + + return { + title: betaVersion, + value: betaVersion + }; + }) + ); + + result.push(selectedResult); + } + + return result; +} diff --git a/src/scripts/cache/cache.ts b/src/scripts/cache/cache.ts index 3dfe2c5..d94fce6 100644 --- a/src/scripts/cache/cache.ts +++ b/src/scripts/cache/cache.ts @@ -89,7 +89,7 @@ export function isExpired(packageName: string, cacheData?: CacheData) { return ttl(pkgData.expiredDate) > 0; } -export async function getPackageData(packageName: string) { +export async function getPackageVersion(packageName: string) { const data = getCacheData(); const isExpiredPkg = isExpired(packageName, data); @@ -100,21 +100,32 @@ export async function getPackageData(packageName: string) { `Fetching ${packageName} latest version` ); - cacheData(packageName, {version}, data); + const pkgVersion = {version}; + + cacheData(packageName, pkgVersion, data); + + return pkgVersion; } return data[packageName]!; } -export async function getCacheExecData(key: string) { +export async function getCacheExecData( + key: string, + execMessage?: string +): Promise { const data = getCacheData(); const isExpiredPkg = isExpired(key, data); // If expired or don't exist then init data if (isExpiredPkg) { - const execResult = await oraExecCmd(key); + const execResult = await oraExecCmd(key, execMessage); + + const result = {execResult}; + + cacheData(key, result, data); - cacheData(key, {execResult}, data); + return result.execResult; } return data[key]!.execResult; diff --git a/src/scripts/helpers.ts b/src/scripts/helpers.ts index ff11a0f..3276ba4 100644 --- a/src/scripts/helpers.ts +++ b/src/scripts/helpers.ts @@ -1,16 +1,18 @@ +import type {SAFE_ANY} from '@helpers/type'; + import {exec} from 'node:child_process'; import {existsSync, readFileSync, writeFileSync} from 'node:fs'; import retry from 'async-retry'; import chalk from 'chalk'; +import {compareVersions as InternalCompareVersions} from 'compare-versions'; import ora, {oraPromise} from 'ora'; import {Logger} from '@helpers/logger'; -import {transformPeerVersion} from '@helpers/utils'; import {COMPONENTS_PATH} from 'src/constants/path'; import {getStore} from 'src/constants/store'; -import {getPackageData} from './cache/cache'; +import {getPackageVersion} from './cache/cache'; export type Dependencies = Record; @@ -28,6 +30,8 @@ export type Components = { export type ComponentsJson = { version: string; components: Components; + betaComponents: Components; + betaVersion: string; }; /** @@ -38,24 +42,13 @@ export type ComponentsJson = { * @param version1 * @param version2 */ -export function compareVersions(version1: string, version2: string) { - version1 = transformPeerVersion(version1); - version2 = transformPeerVersion(version2); - - const parts1 = version1.split('.').map(Number); - const parts2 = version2.split('.').map(Number); - - for (let i = 0; i < parts1.length; i++) { - if (parts1[i] !== undefined && parts2[i] !== undefined) { - if (parts1[i]! > parts2[i]!) { - return 1; - } else if (parts1[i]! < parts2[i]!) { - return -1; - } - } +export function compareVersions(version1 = '', version2 = '') { + try { + return InternalCompareVersions(version1, version2); + } catch { + // Can't not support ('18 || 19.0.0-rc.0' received) temporary solution + return 0; } - - return 0; } export async function updateComponents() { @@ -68,13 +61,16 @@ export async function updateComponents() { const components = JSON.parse(readFileSync(COMPONENTS_PATH, 'utf-8')) as ComponentsJson; const currentVersion = components.version; + const betaVersion = components.betaVersion; const latestVersion = await getStore('latestVersion'); + const latestBetaVersion = await getStore('betaVersion'); - if (compareVersions(currentVersion, latestVersion) === -1) { + if ( + compareVersions(currentVersion, latestVersion) === -1 || + compareVersions(betaVersion, latestBetaVersion) === -1 + ) { // After the first time, check the version and update - await autoUpdateComponents(latestVersion); - - return; + await autoUpdateComponents(latestVersion, latestBetaVersion); } } @@ -92,7 +88,7 @@ export async function getComponents() { return components; } -export async function oraExecCmd(cmd: string, text?: string) { +export async function oraExecCmd(cmd: string, text?: string): Promise { text = text ?? `Executing ${cmd}`; const spinner = ora({ @@ -129,11 +125,11 @@ export async function oraExecCmd(cmd: string, text?: string) { spinner.stop(); - return result as string; + return result; } export async function getLatestVersion(packageName: string): Promise { - const result = await getPackageData(packageName); + const result = await getPackageVersion(packageName); return result.version; } @@ -141,21 +137,39 @@ export async function getLatestVersion(packageName: string): Promise { const getUnpkgUrl = (version: string) => `https://unpkg.com/@nextui-org/react@${version}/dist/components.json`; -export async function autoUpdateComponents(latestVersion?: string) { - latestVersion = latestVersion || ((await getStore('latestVersion')) as string); +export async function autoUpdateComponents(latestVersion?: string, betaVersion?: string) { + [latestVersion, betaVersion] = await Promise.all([ + latestVersion || getStore('latestVersion'), + betaVersion || getStore('betaVersion') + ]); + const url = getUnpkgUrl(latestVersion); - const components = await downloadFile(url); + const [components, betaComponents] = await Promise.all([ + downloadFile(url), + downloadFile(getUnpkgUrl(betaVersion), false) + ]); + + const filterMissingComponents = betaComponents.filter( + (component) => !components.find((c) => c.name === component.name) + ); + + // Add missing beta components to components + components.push(...filterMissingComponents); - const componentsJson = { + const componentsJson: ComponentsJson = { + betaComponents, + betaVersion, components, version: latestVersion }; writeFileSync(COMPONENTS_PATH, JSON.stringify(componentsJson, null, 2), 'utf-8'); + + return componentsJson; } -export async function downloadFile(url: string): Promise { +export async function downloadFile(url: string, log = true): Promise { let data; await oraPromise( @@ -184,7 +198,7 @@ export async function downloadFile(url: string): Promise { ), { failText(error) { - Logger.prefix('error', `Update components data error: ${error}`); + log && Logger.prefix('error', `Update components data error: ${error}`); process.exit(1); }, successText: (() => {