diff --git a/README.md b/README.md index d377be665..c5af8f161 100644 --- a/README.md +++ b/README.md @@ -51,31 +51,33 @@ Make sure that you have Node.js npm install flowbite ``` -2. Require Flowbite as a plugin inside the `tailwind.config.js` file: +2. Use the Flowbite CLI and run the following command to set up Flowbite in the `tailwind.config.js` file: -```javascript -module.exports = { - - plugins: [ - require('flowbite/plugin') - ] - -} +```bash +npx flowbite init ``` -3. Make sure that you add the template path to the `tailwind.config.js` file: +This will either generate a new default Tailwnd configuration file with Flowbite installed or set up Flowbite inside an existing Tailwind CSS project: ```javascript module.exports = { - content: [ - "./node_modules/flowbite/**/*.js" - ] + // other options ... + + content: [ + // other templates paths... + "./node_modules/flowbite/**/*.js" + ] + + plugins: [ + // other plugins... + require('flowbite/plugin') + ], } ``` -4. Include the main JavaScript file to make interactive elements work: +3. Include the main JavaScript file to make interactive elements work: ```html diff --git a/cli.js b/cli.js new file mode 100644 index 000000000..676d9b815 --- /dev/null +++ b/cli.js @@ -0,0 +1,210 @@ +#! /usr/bin/env node +/* eslint-disable @typescript-eslint/no-var-requires */ +// Check if --debug is passed +const debug = process.argv.includes('--debug'); +const fs = require('fs'); + +if ( + process.argv.includes('-h') || + process.argv.includes('--help') || + process.argv.length == 2 +) { + if (process.argv.includes('init')) { + console.log( + '\x1b[0;34mFlowbite CLI \x1b[0m\n\nUsage:\nflowbite init [options]\n\nOptions:\n -h, --help\t\tShow this help\n -d, --default\t\tCreates a default tailwind.config.js without Flowbite installed\n -p --postcss\t\tInitialize a postcss.config.js file\n --debug\t\tShow debug messages' + ); + process.exit(1); + } else { + console.log( + '\x1b[0;34mFlowbite CLI \x1b[0m\n\nUsage:\nflowbite init [options]\n\nOptions:\n -h, --help\t\tShow this help\n -d, --default\t\tCreates a default tailwind.config.js without Flowbite installed\n -p --postcss\t\tInitialize a postcss.config.js file\n --debug\t\tShow debug messages' + ); + process.exit(1); + } +} + +if (process.argv.includes('-v') || process.argv.includes('--version')) { + console.log( + '\x1b[0;34mFlowbite v' + require('./package.json').version + '\x1b[0m' + ); + process.exit(1); +} + +if (process.argv.includes('init')) { + init(); +} else { + console.log( + '\x1b[0;34m Flowbite CLI \x1b[0m\n\nUsage:\n\n flowbite init [options]\n\nOptions:\n -h, --help\t\tShow this help\n -v, --version\t\tShow the version\n --debug\t\tShow debug messages' + ); + process.exit(1); +} + +async function createTailwindConfig() { + if (fs.existsSync('tailwind.config.js')) { + log('tailwind.config.js already exists.'); + return; + } + const data = `module.exports = { + content: [], + theme: { + extend: {}, + }, + plugins: [], +}; +`; + + // Write the file + fs.writeFileSync('tailwind.config.js', data, 'utf8'); + log('tailwind.config.js created.'); +} + +async function updateTailwindConfig() { + // Read the file and replace plugins:[ with plugins: [require("flowbite") + var data = fs.readFileSync('tailwind.config.js', 'utf8'); + var config; + try { + // Try to read the config file + config = await require('./tailwind.config.js'); + } catch (e) { + log('Error: Invalid tailwind.config.js file.', true); + process.exit(1); + } + // Check if the plugin is already added + if ( + data.includes("require('flowbite/plugin')") && + data.includes('./node_modules/flowbite/**/*.js') + ) { + log('Flowbite has already been installed in tailwind.config.js.', true); + process.exit(1); + } + + // Check if flowbite is installed + if (!fs.existsSync('node_modules/flowbite')) { + log( + 'Flowbite is not installed.\nInstall it with \x1b[1m\x1b[4mnpm install flowbite\x1b[0m.', + true + ); + } + // Use regular expressions to find the line where the plugins are defined + const pluginLine = data.match(/plugins: \[[\s\S]*\]/)[0]; + + // Use regular expressions to find the current plugins + var currentPlugins = pluginLine?.match(/require\('.*'\)/g); + + // Checks if the plugin is already added + if (pluginLine) { + if (!currentPlugins) currentPlugins = []; + if (currentPlugins.includes("require('flowbite/plugin')")) + return log( + 'Flowbite has already been installed in tailwind.config.js.' + ); + // Add the new plugin to the current plugins + currentPlugins.push("require('flowbite/plugin')"); + + log('Flowbite plugin added to tailwind.config.js.'); + } + + // Check if the content is already added + if (!config.content.includes('./node_modules/flowbite/**/*.js')) { + config.content.push('./node_modules/flowbite/**/*.js'); + log('Flowbite node_modules added to tailwind.config.js.'); + } + + // Updates the file + var result = data.replace( + /plugins\s*:\s*\[(.*)]/gs, + 'plugins: [' + currentPlugins.join(', ') + ']' + ); + + // Add " to all the content paths + var content = config.content.map((path, index) => { + // Check if index is the last one + if (index === config.content.length - 1) + return "\n '" + path + "'\n "; + return "\n '" + path + "'"; + }); + result = result.replace( + /content\s*:\s*\[(.*?)]/gs, + 'content: [' + content.join(',') + ']' + ); + + // Write the file + fs.writeFileSync('tailwind.config.js', result, 'utf8'); + log('tailwind.config.js updated.'); +} + +async function postCss() { + // Check if postcss.config.js exists + if (!fs.existsSync('postcss.config.js')) { + if ( + !fs.existsSync('node_modules/postcss') && + !fs.existsSync('node_modules/tailwindcss') && + !fs.existsSync('node_modules/autoprefixer') + ) { + log( + "PostCSS and AutoPrefixer aren't installed.\nInstall them with \x1b[1m\x1b[4mnpm install -D tailwindcss postcss autoprefixer\x1b[0m.", + true + ); + } + // Create postcss.config.js + const data = `module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; +`; + fs.writeFileSync('postcss.config.js', data, 'utf8'); + log('postcss.config.js created.'); + } else { + return; + } +} + +/** + * + * @param {string} msg + * @param {boolean} important + * @returns + */ +function log(msg, important = false) { + if (important == true) { + console.log(msg + '\x1b[0m'); + } else if (debug == true) { + console.log(msg + '\x1b[0m'); + } +} + +/** + * Init the tailwind config + * @returns + */ +async function init() { + if (process.argv.includes('-d') || process.argv.includes('--default')) { + if (fs.existsSync('tailwind.config.js')) { + log('tailwind.config.js already exists.', true); + process.exit(1); + } + await createTailwindConfig(); + log('Tailwind config created without Flowbite', true); + if (process.argv.includes('-p') || process.argv.includes('--postcss')) { + await postCss(); + } + process.exit(1); + } + if (fs.existsSync('tailwind.config.js')) { + await updateTailwindConfig(); + log('Flowbite has been installed in tailwind.config.js.', true); + } else { + await createTailwindConfig(); + await updateTailwindConfig(); + log( + 'Created new tailwind.config.js file with Flowbite installed.', + true + ); + } + if (process.argv.includes('-p') || process.argv.includes('--postcss')) { + await postCss(); + log('Created postcss.config.js file.', true); + } + process.exit(1); +} diff --git a/content/getting-started/introduction.md b/content/getting-started/introduction.md index bac9cc27c..c911d1bce 100644 --- a/content/getting-started/introduction.md +++ b/content/getting-started/introduction.md @@ -61,34 +61,36 @@ Flowbite is technically a plugin that can be included into any existing Tailwind npm install flowbite ``` -2. Include Flowbite as a plugin inside the `tailwind.config.js` file: +2. Use the Flowbite CLI and run the following command to set up Flowbite in the `tailwind.config.js` file: -```javascript -module.exports = { - - plugins: [ - require('flowbite/plugin') - ] - -} +```bash +npx flowbite init ``` -3. Additionally to your own `content` data you should add `flowbite` to apply the classes from the interactive elements in the `tailwind.config.js` file: +This will either generate a new default Tailwnd configuration file with Flowbite installed or set up Flowbite inside an existing Tailwind CSS project: ```javascript module.exports = { - content: [ - "./node_modules/flowbite/**/*.js" - ] + // other options ... + + content: [ + // other templates paths... + "./node_modules/flowbite/**/*.js" + ] + + plugins: [ + // other plugins... + require('flowbite/plugin') + ], } ``` -4. Require the JavaScript code that powers the interactive elements before the end of your `` tag: +3. Include the main JavaScript file to make interactive elements work: ```html - + ``` ### Include via CDN diff --git a/content/getting-started/quickstart.md b/content/getting-started/quickstart.md index 36a57a9a2..d14a2aab9 100644 --- a/content/getting-started/quickstart.md +++ b/content/getting-started/quickstart.md @@ -27,34 +27,36 @@ Make sure that you have Node.js< npm install flowbite ``` -2. Require Flowbite as a plugin inside the `tailwind.config.js` file: +2. Use the Flowbite CLI and run the following command to set up Flowbite in the `tailwind.config.js` file: -```javascript -module.exports = { - - plugins: [ - require('flowbite/plugin') - ] - -} +```bash +npx flowbite init ``` -3. Additionally to your own `content` data you should add `flowbite` to apply the classes from the interactive elements in the `tailwind.config.js` file: +This will either generate a new default Tailwnd configuration file with Flowbite installed or set up Flowbite inside an existing Tailwind CSS project: ```javascript module.exports = { - content: [ - "./node_modules/flowbite/**/*.js" - ] + // other options ... + + content: [ + // other templates paths... + "./node_modules/flowbite/**/*.js" + ] + + plugins: [ + // other plugins... + require('flowbite/plugin') + ], } ``` -4. Require the JavaScript code that powers the interactive elements before the end of your `` tag: +3. Include the main JavaScript file to make interactive elements work: ```html - + ``` ### Include via CDN diff --git a/package-lock.json b/package-lock.json index 237bd0097..8f9418f3b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,9 @@ "@popperjs/core": "^2.9.3", "mini-svg-data-uri": "^1.4.3" }, + "bin": { + "flowbite": "cli.js" + }, "devDependencies": { "@babel/core": "^7.14.8", "@babel/preset-env": "^7.14.8", @@ -20,6 +23,7 @@ "@typescript-eslint/parser": "^5.46.1", "autoprefixer": "^10.3.3", "babel-loader": "^8.2.2", + "child_process": "^1.0.2", "copyfiles": "^2.4.1", "core-js": "^3.8.1", "css-loader": "^5.2.7", @@ -3323,6 +3327,12 @@ "node": ">=4" } }, + "node_modules/child_process": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/child_process/-/child_process-1.0.2.tgz", + "integrity": "sha512-Wmza/JzL0SiWz7kl6MhIKT5ceIlnFPJX+lwUGj7Clhy5MMldsSoJR0+uvRzOS5Kv45Mq7t1PoE8TsOA9bzvb6g==", + "dev": true + }, "node_modules/chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", @@ -7191,9 +7201,9 @@ "dev": true }, "node_modules/json5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", - "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, "bin": { "json5": "lib/cli.js" @@ -15860,8 +15870,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz", "integrity": "sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==", - "dev": true, - "requires": {} + "dev": true }, "@webpack-cli/info": { "version": "1.5.0", @@ -15876,8 +15885,7 @@ "version": "1.7.0", "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.7.0.tgz", "integrity": "sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==", - "dev": true, - "requires": {} + "dev": true }, "@xtuc/ieee754": { "version": "1.2.0", @@ -15917,15 +15925,13 @@ "version": "1.8.0", "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", - "dev": true, - "requires": {} + "dev": true }, "acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "requires": {} + "dev": true }, "acorn-node": { "version": "1.8.2", @@ -15968,8 +15974,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-1.0.1.tgz", "integrity": "sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==", - "dev": true, - "requires": {} + "dev": true }, "ajv-formats": { "version": "2.1.1", @@ -16004,8 +16009,7 @@ "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", - "dev": true, - "requires": {} + "dev": true }, "algoliasearch": { "version": "4.14.2", @@ -16470,6 +16474,12 @@ "supports-color": "^5.3.0" } }, + "child_process": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/child_process/-/child_process-1.0.2.tgz", + "integrity": "sha512-Wmza/JzL0SiWz7kl6MhIKT5ceIlnFPJX+lwUGj7Clhy5MMldsSoJR0+uvRzOS5Kv45Mq7t1PoE8TsOA9bzvb6g==", + "dev": true + }, "chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", @@ -16865,8 +16875,7 @@ "version": "6.3.1", "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.3.1.tgz", "integrity": "sha512-fBffmak0bPAnyqc/HO8C3n2sHrp9wcqQz6ES9koRF2/mLOVAx9zIQ3Y7R29sYCteTPqMCwns4WYQoCX91Xl3+w==", - "dev": true, - "requires": {} + "dev": true }, "css-has-pseudo": { "version": "0.10.0", @@ -17128,8 +17137,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.1.0.tgz", "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==", - "dev": true, - "requires": {} + "dev": true }, "csso": { "version": "4.2.0", @@ -17785,8 +17793,7 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.0.0.tgz", "integrity": "sha512-5EaAVPsIHu+grmm5WKjxUia4yHgRrbkd8I0ffqUSwixCPMVBrbS97UnzlEY/Q7OWo584vgixefM0kJnUfo/VjA==", - "dev": true, - "requires": {} + "dev": true }, "eslint-plugin-prettier": { "version": "4.2.1", @@ -18927,8 +18934,7 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", - "dev": true, - "requires": {} + "dev": true }, "ignore": { "version": "5.2.1", @@ -19384,9 +19390,9 @@ "dev": true }, "json5": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.1.tgz", - "integrity": "sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true }, "jsonfile": { @@ -20765,29 +20771,25 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz", "integrity": "sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==", - "dev": true, - "requires": {} + "dev": true }, "postcss-discard-duplicates": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz", "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==", - "dev": true, - "requires": {} + "dev": true }, "postcss-discard-empty": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz", "integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==", - "dev": true, - "requires": {} + "dev": true }, "postcss-discard-overridden": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz", "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==", - "dev": true, - "requires": {} + "dev": true }, "postcss-double-position-gradients": { "version": "1.0.0", @@ -21222,8 +21224,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", - "dev": true, - "requires": {} + "dev": true }, "postcss-modules-local-by-default": { "version": "4.0.0", @@ -21294,8 +21295,7 @@ "version": "5.1.0", "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz", "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==", - "dev": true, - "requires": {} + "dev": true }, "postcss-normalize-display-values": { "version": "5.1.0", @@ -21775,8 +21775,7 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.2.1.tgz", "integrity": "sha512-aIO8IguumORyRsmT+E7JfJ3A9FEoyhqZR7Au7TBOege3VZkgMvHJMkufeYp4zjnDK2iq4ktkvGMNOQR9T8lisQ==", - "dev": true, - "requires": {} + "dev": true }, "pretty-hrtime": { "version": "1.0.3", diff --git a/package.json b/package.json index f5ef48f11..ecc0f6b78 100644 --- a/package.json +++ b/package.json @@ -75,6 +75,7 @@ "@typescript-eslint/parser": "^5.46.1", "autoprefixer": "^10.3.3", "babel-loader": "^8.2.2", + "child_process": "^1.0.2", "copyfiles": "^2.4.1", "core-js": "^3.8.1", "css-loader": "^5.2.7", @@ -114,6 +115,8 @@ "files": [ "lib", "dist", - "plugin.js" - ] + "plugin.js", + "cli.js" + ], + "bin": "cli.js" }