diff --git a/src/actions/add-action.ts b/src/actions/add-action.ts index 7176253..7c6077c 100644 --- a/src/actions/add-action.ts +++ b/src/actions/add-action.ts @@ -20,7 +20,12 @@ import {getPackageInfo} from '@helpers/package'; import {findFiles} from '@helpers/utils'; import {nextUIComponents, nextUIComponentsMap} from 'src/constants/component'; import {resolver} from 'src/constants/path'; -import {NEXT_UI, individualTailwindRequired, pnpmRequired} from 'src/constants/required'; +import { + DOCS_PROVIDER_SETUP, + NEXT_UI, + individualTailwindRequired, + pnpmRequired +} from 'src/constants/required'; import {tailwindTemplate} from 'src/constants/templates'; import {getAutocompleteMultiselect} from 'src/prompts'; @@ -30,10 +35,12 @@ interface AddActionOptions { packagePath?: string; tailwindPath?: string; appPath?: string; + addApp?: boolean; } export async function addAction(components: string[], options: AddActionOptions) { const { + addApp = false, all = false, appPath = findFiles('**/App.(j|t)sx')[0], packagePath = resolver('package.json'), @@ -41,16 +48,23 @@ export async function addAction(components: string[], options: AddActionOptions) tailwindPath = findFiles('**/tailwind.config.(j|t)s')[0] } = options; + var {allDependenciesKeys, currentComponents} = getPackageInfo(packagePath); + if (!components.length && !all) { components = await getAutocompleteMultiselect( 'Select the NextUI components to add', - nextUIComponents.map((component) => { - return { - description: component.description, - title: component.name, - value: component.name - }; - }) + nextUIComponents + .filter( + (component) => + !currentComponents.some((currentComponent) => currentComponent.name === component.name) + ) + .map((component) => { + return { + description: component.description, + title: component.name, + value: component.name + }; + }) ); } else if (all) { components = [NEXT_UI]; @@ -79,7 +93,7 @@ export async function addAction(components: string[], options: AddActionOptions) } // Check whether the App.tsx file exists - if (!appPath) { + if (addApp && !appPath) { Logger.prefix( 'error', "❌ Cannot find the App.(j|t)sx file\nYou should specify appPath through 'add --appPath=yourAppPath'" @@ -149,15 +163,17 @@ export async function addAction(components: string[], options: AddActionOptions) Logger.info(`Added the required tailwind content in file: ${tailwindPath}`); } - /** ======================== Step 3 Provider ======================== */ - const [isCorrectProvider] = checkApp(type, appPath); + /** ======================== Step 3 Provider Need Manually Open ======================== */ + if (addApp && appPath && existsSync(appPath)) { + const [isCorrectProvider] = checkApp(type, appPath); - if (!isCorrectProvider) { - fixProvider(appPath, {format: prettier}); + if (!isCorrectProvider) { + fixProvider(appPath, {format: prettier}); - Logger.newLine(); - Logger.info(`Added the NextUIProvider in file: ${appPath}`); - Logger.warn('You need to check the NextUIProvider whether in the correct place'); + Logger.newLine(); + Logger.info(`Added the NextUIProvider in file: ${appPath}`); + Logger.warn('You need to check the NextUIProvider whether in the correct place'); + } } /** ======================== Step 4 Setup Pnpm ======================== */ @@ -177,7 +193,27 @@ export async function addAction(components: string[], options: AddActionOptions) // Finish adding the NextUI components Logger.newLine(); - Logger.success( - '✅ All the NextUI components have been added\nNow you can use the component you installed in your application' + Logger.success('✅ All the NextUI components have been added\n'); + + // Warn the user to check the NextUIProvider whether in the correct place + Logger.warn( + `Please check the ${chalk.bold( + 'NextUIProvider' + )} whether in the correct place (ignore if added)\nSee more info here: ${DOCS_PROVIDER_SETUP}` ); + + // Check whether the user has installed the All NextUI components + if ((allDependenciesKeys.has(NEXT_UI) || all) && currentComponents.length) { + // Check whether have added redundant dependencies + Logger.newLine(); + Logger.warn( + 'You do not need the `@nextui-org/react` package when using individual components\nWe suggest to use individual components for smaller bundle sizes' + ); + Logger.warn('The redundant dependencies are:'); + currentComponents.forEach((component) => { + Logger.info(`- ${component.package}`); + }); + } + + process.exit(0); } diff --git a/src/constants/required.ts b/src/constants/required.ts index d650854..36ed052 100644 --- a/src/constants/required.ts +++ b/src/constants/required.ts @@ -19,10 +19,11 @@ export const DOCS_TAILWINDCSS_SETUP = 'https://nextui.org/docs/guide/installation#tailwind-css-setup'; export const DOCS_APP_SETUP = 'https://nextui.org/docs/guide/installation#provider-setup'; export const DOCS_PNPM_SETUP = 'https://nextui.org/docs/guide/installation#setup-pnpm-optional'; +export const DOCS_PROVIDER_SETUP = 'https://nextui.org/docs/guide/installation#provider-setup'; // Record the required content of tailwind.config file export const tailwindRequired = { - content: '@nextui-org/theme/dist/**/*.{js,ts,jsx,tsx}', + content: './node_modules/@nextui-org/theme/dist/**/*.{js,ts,jsx,tsx}', darkMode: 'darkMode: "class"', plugins: 'nextui()' } as const; @@ -44,7 +45,7 @@ export const individualTailwindRequired = { ]; if (outputComponents.length === 1) { - return `@nextui-org/theme/dist/components/${currentComponents[0]}.js`; + return `./node_modules/@nextui-org/theme/dist/components/${currentComponents[0]}.js`; } const requiredContent = outputComponents .reduce((acc, component) => { @@ -52,7 +53,7 @@ export const individualTailwindRequired = { }, '') .replace(/\|$/, ''); - return `@nextui-org/theme/dist/components/(${requiredContent}).js`; + return `./node_modules/@nextui-org/theme/dist/components/(${requiredContent}).js`; }, plugins: 'nextui()' } as const; diff --git a/src/helpers/fix.ts b/src/helpers/fix.ts index 005ca12..66c2dfa 100644 --- a/src/helpers/fix.ts +++ b/src/helpers/fix.ts @@ -53,15 +53,42 @@ export function fixTailwind(type: CheckType, options: FixTailwind) { } let tailwindContent = readFileSync(tailwindPath, 'utf-8'); - const contentMatch = getMatchArray('content', tailwindContent); + let contentMatch = getMatchArray('content', tailwindContent); const pluginsMatch = getMatchArray('plugins', tailwindContent); for (const errorInfo of errorInfoList) { const [errorType, info] = transformErrorInfo(errorInfo); if (errorType === 'content') { + // Check if all the required content is added then skip + const allPublic = contentMatch.includes(tailwindRequired.content); + + if (allPublic) continue; + + contentMatch = contentMatch.filter((content) => !content.includes('@nextui-org/theme/dist/')); contentMatch.push(info); - tailwindContent = replaceMatchArray('content', tailwindContent, contentMatch); + tailwindContent = replaceMatchArray( + 'content', + tailwindContent, + contentMatch, + contentMatch + .map((v, index, arr) => { + // Add 4 spaces before the content + if (index === 0) { + if (arr.length === 1) { + return `\n ${JSON.stringify(v)}\n`; + } + + return `\n ${JSON.stringify(v)}`; + } + if (arr.length - 1 === index) { + return ` ${JSON.stringify(v)}\n `; + } + + return ` ${JSON.stringify(v)}`; + }) + .join(',\n') + ); } else if (errorType === 'plugins') { pluginsMatch.push(info); tailwindContent = replaceMatchArray('plugins', tailwindContent, pluginsMatch); diff --git a/src/helpers/match.ts b/src/helpers/match.ts index 8d2bd00..462613c 100644 --- a/src/helpers/match.ts +++ b/src/helpers/match.ts @@ -37,7 +37,7 @@ export function getMatchArray(key: string, target: string) { target .match(mixinReg)?.[1] ?.split(/,\n/) - .map((i) => i.trim()) + .map((i) => i.trim().replace(/[`'"]/g, '')) .filter(Boolean) ?? [] ); @@ -51,12 +51,19 @@ export function getMatchArray(key: string, target: string) { * @param target * @param value */ -export function replaceMatchArray(key: string, target: string, value: string[]) { +export function replaceMatchArray( + key: string, + target: string, + value: string[], + _replaceValue?: string +) { const mixinReg = new RegExp(`\\s*${key}:\\s\\[([\\w\\W]*?)\\]\\s*`); - const replaceValue = value.map((v) => JSON.stringify(v)).join(', '); + const replaceValue = _replaceValue ?? value.map((v) => JSON.stringify(v)).join(', '); if (mixinReg.test(target)) { - return target.replace(mixinReg, `\n ${key}: [${replaceValue}]`); + const _value = key === 'content' ? `\n ${key}: [${replaceValue}]` : `\n ${key}: [${value}]`; + + return target.replace(mixinReg, _value); } // If the key does not exist, add the key and value to the end of the target diff --git a/src/index.ts b/src/index.ts index 7f5761f..36bc0ac 100644 --- a/src/index.ts +++ b/src/index.ts @@ -47,6 +47,7 @@ nextui 'Add prettier format in the add content which required installed prettier', false ) + .option('--addApp [boolean]', 'Add App.tsx file content which required provider', false) .action(addAction); nextui