diff --git a/.github/ISSUE_TEMPLATE/----bug-report---.md b/.github/ISSUE_TEMPLATE/----bug-report---.md index d279fb4..df6bd53 100644 --- a/.github/ISSUE_TEMPLATE/----bug-report---.md +++ b/.github/ISSUE_TEMPLATE/----bug-report---.md @@ -4,7 +4,6 @@ about: Create a report to help us improve title: "\U0001F41B [Bug report]: " labels: bug assignees: '' - --- **Describe the bug** @@ -12,6 +11,7 @@ A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: + 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' @@ -24,15 +24,17 @@ A clear and concise description of what you expected to happen. If applicable, add screenshots to help explain your problem. **Desktop (please complete the following information):** - - OS: [e.g. iOS] - - Browser [e.g. chrome, safari] - - Version [e.g. 22] + +- OS: [e.g. iOS] +- Browser [e.g. chrome, safari] +- Version [e.g. 22] **Smartphone (please complete the following information):** - - Device: [e.g. iPhone6] - - OS: [e.g. iOS8.1] - - Browser [e.g. stock browser, safari] - - Version [e.g. 22] + +- Device: [e.g. iPhone6] +- OS: [e.g. iOS8.1] +- Browser [e.g. stock browser, safari] +- Version [e.g. 22] **Additional context** Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/---feature-request---.md b/.github/ISSUE_TEMPLATE/---feature-request---.md index 333c0cb..f022833 100644 --- a/.github/ISSUE_TEMPLATE/---feature-request---.md +++ b/.github/ISSUE_TEMPLATE/---feature-request---.md @@ -1,10 +1,9 @@ --- -name: "✨ [Feature request]: " +name: '✨ [Feature request]: ' about: Suggest an idea for this project -title: "✨ [Feature request]: " +title: '✨ [Feature request]: ' labels: '' assignees: '' - --- **Is your feature request related to a problem? Please describe.** diff --git a/.github/ISSUE_TEMPLATE/new-play.md b/.github/ISSUE_TEMPLATE/new-play.md index d55bf09..790f436 100644 --- a/.github/ISSUE_TEMPLATE/new-play.md +++ b/.github/ISSUE_TEMPLATE/new-play.md @@ -1,18 +1,15 @@ --- name: New Play about: Add a New Play Request -title: "[Add a Play]: " +title: '[Add a Play]: ' labels: play-request assignees: '' - --- -Thank You! +Thank You! ## What's the Play about? - ## What ReactJS concept will be used to create this play? - ## Are you willing to take it up for implementation? diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index f8baf61..fc3f3ec 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,4 +1,3 @@ - > First thing, PLEASE READ THIS: [ReactPlay Code Review Checklist](https://github.com/reactplay/react-play/wiki/ReactPlay-Code-Review-Checklist) # Description @@ -18,7 +17,7 @@ Please delete options that are not relevant. # How Has This Been Tested? -Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. +Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. # Checklist: diff --git a/.gitignore b/.gitignore index f4196e1..ec33281 100644 --- a/.gitignore +++ b/.gitignore @@ -106,4 +106,7 @@ dist # Packagelock files yarn.lock -package-lock.json \ No newline at end of file +package-lock.json + +#misc +yalc.sig \ No newline at end of file diff --git a/.npmrc b/.npmrc deleted file mode 100644 index f87ee10..0000000 --- a/.npmrc +++ /dev/null @@ -1 +0,0 @@ -@reactplay:registry=https://npm.pkg.github.com diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 918c366..ff9130b 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -17,23 +17,23 @@ diverse, inclusive, and healthy community. Examples of behavior that contributes to a positive environment for our community include: -* Demonstrating empathy and kindness toward other people -* Being respectful of differing opinions, viewpoints, and experiences -* Giving and gracefully accepting constructive feedback -* Accepting responsibility and apologizing to those affected by our mistakes, +- Demonstrating empathy and kindness toward other people +- Being respectful of differing opinions, viewpoints, and experiences +- Giving and gracefully accepting constructive feedback +- Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience -* Focusing on what is best not just for us as individuals, but for the +- Focusing on what is best not just for us as individuals, but for the overall community Examples of unacceptable behavior include: -* The use of sexualized language or imagery, and sexual attention or +- The use of sexualized language or imagery, and sexual attention or advances of any kind -* Trolling, insulting or derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or email +- Trolling, insulting or derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or email address, without their explicit permission -* Other conduct which could reasonably be considered inappropriate in a +- Other conduct which could reasonably be considered inappropriate in a professional setting ## Enforcement Responsibilities @@ -106,7 +106,7 @@ Violating these terms may lead to a permanent ban. ### 4. Permanent Ban **Community Impact**: Demonstrating a pattern of violation of community -standards, including sustained inappropriate behavior, harassment of an +standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. **Consequence**: A permanent ban from any sort of public interaction within diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index edf6317..6ca7fc8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,19 +1,19 @@ # Contributing When contributing to this repository, please first discuss the change you wish to make via [issue](https://github.com/reactplay/react-play/issues), -[email](mailto:ioreactplay@gmail.com), or any other method with the owners of this repository before making a change. +[email](mailto:ioreactplay@gmail.com), or any other method with the owners of this repository before making a change. Please note we have a [Code of Conduct](https://github.com/reactplay/react-play/blob/main/CODE_OF_CONDUCT.md), please follow it in all your interactions with the project. ## Pull Request Process -1. Ensure any install or build dependencies are removed before the end of the layer when doing a +1. Ensure any install or build dependencies are removed before the end of the layer when doing a build. -2. Update the README.md with details of changes to the interface, this includes new environment +2. Update the README.md with details of changes to the interface, this includes new environment variables, exposed ports, useful file locations and container parameters. 3. Increase the version numbers in any examples files and the README.md to the new version that this Pull Request would represent. The versioning scheme we use is [SemVer](http://semver.org/). -4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you +4. You may merge the Pull Request in once you have the sign-off of two other developers, or if you do not have permission to do that, you may request the second reviewer to merge it for you. ## Code of Conduct @@ -32,21 +32,21 @@ orientation. Examples of behavior that contributes to creating a positive environment include: -* Using welcoming and inclusive language -* Being respectful of differing viewpoints and experiences -* Gracefully accepting constructive criticism -* Focusing on what is best for the community -* Showing empathy towards other community members +- Using welcoming and inclusive language +- Being respectful of differing viewpoints and experiences +- Gracefully accepting constructive criticism +- Focusing on what is best for the community +- Showing empathy towards other community members Examples of unacceptable behavior by participants include: -* The use of sexualized language or imagery and unwelcome sexual attention or -advances -* Trolling, insulting/derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or electronic +- The use of sexualized language or imagery and unwelcome sexual attention or + advances +- Trolling, insulting/derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or electronic address, without explicit permission -* Other conduct which could reasonably be considered inappropriate in a +- Other conduct which could reasonably be considered inappropriate in a professional setting ### Our Responsibilities @@ -89,4 +89,4 @@ This Code of Conduct is adapted from the [Contributor Covenant][homepage], versi available at [http://contributor-covenant.org/version/1/4][version] [homepage]: http://contributor-covenant.org -[version]: http://contributor-covenant.org/version/1/4/ \ No newline at end of file +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/README.md b/README.md index 1173bcd..291fa0c 100644 --- a/README.md +++ b/README.md @@ -1,21 +1,26 @@ # Create React Play -## Welcome to `reactplay.io` ecosystem +## Welcome to `reactplay.io` ecosystem A package to generate boilerplate files for creating a play withing your local system. Another idea of this package to create a backbone structure for running all sort of maintenance and house keeping task. [![Node.js Package](https://github.com/reactplay/create-react-play/actions/workflows/npm-publish.yml/badge.svg)](https://github.com/reactplay/create-react-play/actions/workflows/npm-publish.yml) ## Command Line Options + Here are few options + - -h/help: Show all available arguments and flags - -c/create: Creates a play boilerplate within local directory structure - -u/update: Update an exsiting play - -p/prepare: Prepre an environment for build/run +- -plays: set the path folder for your plays ( default is src/plays) +- -i: Create default images for missing plays, create thumbnail and adapt image extension. (usage -i="a value") ## How it works ### Example: Create a play + 1. Fork [reactplay](https://github.com/reactplay/react-play) 2. Clone your branch 3. Install packages using @@ -23,39 +28,52 @@ Here are few options yarn #or npm install + #or + pnpm install ``` -4. run reactplay application using +4. run reactplay application using ```bash yarn start #or npm start + #or + pnpm start ``` 5. Hit "Create" button on web portal 6. Provide necessary information 7. Submit your play request 8. It will generate an unique id for you -9. Navigate to root directory of your local reactplay repo and run +9. Navigate to root directory of your local reactplay repo and run + ```bash npx create-react-play -c ``` This will create necessary resources and link them together. +
+ + or + ```bash + npx create-react-play -c -plays="plays" + ``` + if you want to create it in a specific folder ( here in the "./plays" folder at the application root) + + **Note:** If the play folder `/plays/` remain empty after running above command that means you might be in some older version of the package. Use `@latest` in that case + ```bash + npx create-react-play@latest -c + ``` - **Note:** If the play folder `/plays/` remain empty after running above command that means you might be in some older version of the package. Use `@latest` in that case - ```bash - npx create-react-play@latest -c - ``` - 10. Now you will find your play under ``` /plays/ ``` 11. Now its all with you, create an awesome play and flaunt it infront of the globe -12. Create a pull request when you are done +12. Create a pull request when you are done ### Example: Prepare environemnt + ```bash npx create-react-play@latest -p ``` diff --git a/bin/global.js b/bin/global.js index e4898e5..1504123 100644 --- a/bin/global.js +++ b/bin/global.js @@ -1,35 +1,25 @@ #!/usr/bin/env node -import { invoke_process } from "../lib/index.js"; -import { CONSOLE_COLORS } from "../util/const.js"; -import fs from "fs"; +import { invoke_process } from '../lib/index.js'; +import { CONSOLE_COLORS } from '../util/const.js'; +import fs from 'fs'; // import * as meta from ""; // Displays the text in the console -let meta = JSON.parse(fs.readFileSync("package.json", "utf-8")); -console.log(""); +let meta = JSON.parse(fs.readFileSync('package.json', 'utf-8')); +console.log(''); console.log( CONSOLE_COLORS.FgGreen, - "┌─────────────────────────────────────────────────────────────────────────┐" -); -console.log( - " | |" -); -console.log( - " | Welcome to |" + '┌─────────────────────────────────────────────────────────────────────────┐' ); +console.log(' | |'); +console.log(' | Welcome to |'); console.log( ` | Create React Play (v ${meta.version}) |` ); -console.log( - " | |" -); -console.log( - " | For help hit : create-react-play -h/help |" -); -console.log( - " └─────────────────────────────────────────────────────────────────────────┘" -); +console.log(' | |'); +console.log(' | For help hit : create-react-play -h/help |'); +console.log(' └─────────────────────────────────────────────────────────────────────────┘'); -console.log(CONSOLE_COLORS.Reset, ""); +console.log(CONSOLE_COLORS.Reset, ''); invoke_process(); diff --git a/hooks/basic_hooks.js b/hooks/basic_hooks.js index 0b2db44..f70fb88 100644 --- a/hooks/basic_hooks.js +++ b/hooks/basic_hooks.js @@ -1,26 +1,23 @@ -import { exec } from "child_process"; -import { Log } from "../log/index.js"; +import { exec } from 'child_process'; +import { Log } from '../log/index.js'; export const basic_get_package_manager = () => { - Log.log("Checking package manager"); - return "yarn"; + Log.log('Checking package manager'); + return 'yarn'; }; export const basic_check_shell_connectivity = () => { - Log.log("Checking shell connectivity"); + Log.log('Checking shell connectivity'); Log.log(process.cwd()); - exec( - `sh ${process.cwd()}\\react-play-dev-kit\\scripts\\sample.sh`, - (error, stdout, stderr) => { - if (error) { - Log.log(`error: ${error.message}`); - return; - } - if (stderr) { - Log.log(`stderr: ${stderr}`); - return; - } - Log.log(`stdout: ${stdout}`); + exec(`sh ${process.cwd()}\\react-play-dev-kit\\scripts\\sample.sh`, (error, stdout, stderr) => { + if (error) { + Log.log(`error: ${error.message}`); + return; } - ); + if (stderr) { + Log.log(`stderr: ${stderr}`); + return; + } + Log.log(`stdout: ${stdout}`); + }); }; diff --git a/hooks/data_hooks.js b/hooks/data_hooks.js index d27373f..8dee73c 100644 --- a/hooks/data_hooks.js +++ b/hooks/data_hooks.js @@ -1,5 +1,5 @@ -import { loadPlay } from "../lib/services/datasource/index.js"; -import { Log } from "../log/index.js"; +import { loadPlay } from '../lib/services/datasource/index.js'; +import { Log } from '../log/index.js'; export const data_read_table = (table_name) => { Log.log(`Reading ${table_name} table`); diff --git a/hooks/env_hooks.js b/hooks/env_hooks.js index 820d50e..8c0bbc0 100644 --- a/hooks/env_hooks.js +++ b/hooks/env_hooks.js @@ -1,7 +1,4 @@ -import { - processPlayContent, - processPlayIndex, -} from "../lib/services/template/index.js"; +import { processPlayContent, processPlayIndex } from '../lib/services/template/index.js'; export const prepare_environment = (data) => { processPlayContent(data); diff --git a/hooks/git_hooks.js b/hooks/git_hooks.js index 69aa441..3c28bdd 100644 --- a/hooks/git_hooks.js +++ b/hooks/git_hooks.js @@ -1,8 +1,8 @@ -import { Log } from "../log/index.js"; +import { Log } from '../log/index.js'; const REPO_MAP = { - play: "react_play_store", - application: "react_play", + play: 'react_play_store', + application: 'react_play' }; export const git_test_connection = (repo_name) => { @@ -20,7 +20,7 @@ export const git_pull_repo = (repo_name) => { Log.log(`Pulling repo ${repo_link}`); }; -export const git_fork_branch = (repo_name, branch_name = "main") => { +export const git_fork_branch = (repo_name, branch_name = 'main') => { const repo_link = get_repo_link(repo_name); Log.log(`Forking repo ${repo_link}/${branch_name}`); }; diff --git a/hooks/index.js b/hooks/index.js index b5a065b..7199c66 100644 --- a/hooks/index.js +++ b/hooks/index.js @@ -1,4 +1,4 @@ -export * from "./basic_hooks.js"; -export * from "./git_hooks.js"; -export * from "./data_hooks.js"; -export * from "./env_hooks.js"; +export * from './basic_hooks.js'; +export * from './git_hooks.js'; +export * from './data_hooks.js'; +export * from './env_hooks.js'; diff --git a/lib/arg-parser.js b/lib/arg-parser.js index 7061587..7fe84df 100644 --- a/lib/arg-parser.js +++ b/lib/arg-parser.js @@ -1,7 +1,7 @@ -import { ALL_ARGS_TYPES } from "../util/const.js"; +import { ALL_ARGS_TYPES } from '../util/const.js'; export const arg_objects = () => { - const res = { args: [], error: false, error_text: "" }; + const res = { args: [], error: false, error_text: '' }; const all_args = process.argv.slice(2); const skip_indexes = []; try { @@ -13,7 +13,7 @@ export const arg_objects = () => { if (!arg_type.flag) { res.args.push({ name: arg_type.name, - value: all_args[arg_index + 1], + value: all_args[arg_index + 1] }); skip_indexes.push(arg_index); } else { diff --git a/lib/index.js b/lib/index.js index 6197249..7803eaa 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,25 +1,25 @@ -import { arg_objects } from "./arg-parser.js"; -import { toKebabCase } from "../util/util.js"; -import { ALL_ARGS_TYPES } from "../util/const.js"; -import * as hooks from "../hooks/index.js"; -import { Log } from "../log/index.js"; +import { arg_objects } from './arg-parser.js'; +import { toKebabCase } from '../util/util.js'; +import { ALL_ARGS_TYPES } from '../util/const.js'; +import * as hooks from '../hooks/index.js'; +import { Log } from '../log/index.js'; export const invoke_process = async () => { // hooks.basic_check_shell_connectivity(); // Step 1: Sanity check - Log.header("Checking github connectivity"); - hooks.git_test_connection("application"); - hooks.git_test_connection("play"); + Log.header('Checking github connectivity'); + hooks.git_test_connection('application'); + hooks.git_test_connection('play'); // Step 1.1: Is the user logged in to GHU? // Step 1.1.1: If not, return error and ask to login. - Log.header("Checking package manager availability"); + Log.header('Checking package manager availability'); hooks.basic_get_package_manager(); // Step 1.2: Is yarn/npm installed? // Step 1.2.1: If not, return error and ask to login. const play_config = { issue: 320, - issue_details: "create-ludo-play", - branch: "", + issue_details: 'create-ludo-play', + branch: '' }; const arg_obj = arg_objects(); if (arg_obj.error) { @@ -28,8 +28,8 @@ export const invoke_process = async () => { if (arg_obj.args.length) { arg_obj.args.forEach(async (element) => { switch (element.name) { - case "create": - Log.header("Reading play information"); + case 'create': + Log.header('Reading play information'); // Step 2: Read play store // Step 2.1: Read play master information @@ -80,16 +80,16 @@ export const invoke_process = async () => { // console.log("> Starting the application"); // // Step 8: yarn/npm start break; - case "prepare": - Log.log("Preparing environment for build process"); + case 'prepare': + Log.log('Preparing environment for build process'); // Preparing src/plays/index.js hooks.prepare_content_index(); break; - case "help": + case 'help': Log.newLine(); Log.segementStart(`Help instructions`); - Log.log("Command: create-react-play [-switch] [value]", true); - Log.log(" The list of arguments are", true); + Log.log('Command: create-react-play [-switch] [value]', true); + Log.log(' The list of arguments are', true); ALL_ARGS_TYPES.forEach((a) => { Log.log(` ${a.args} ${a.description}`, true); }); @@ -98,9 +98,7 @@ export const invoke_process = async () => { } }); } else { - Log.highlight( - "Welcome to create-react-play. For more information check help flag" - ); + Log.highlight('Welcome to create-react-play. For more information check help flag'); } } }; diff --git a/lib/services/datasource/index.js b/lib/services/datasource/index.js index 2c2a407..7e64567 100644 --- a/lib/services/datasource/index.js +++ b/lib/services/datasource/index.js @@ -1,17 +1,17 @@ -import { submit } from "json-graphql-parser"; -import { Log } from "../../../log/index.js"; -import { FETH_ALL_PLAYS } from "./query.js"; +import { submit } from 'json-graphql-parser'; +import { Log } from '../../../log/index.js'; +import { FETH_ALL_PLAYS } from './query.js'; import { toKebabCase, toPascalcase, toSanitized, toSlug, toTitleCase, - toTitleCaseTrimmed, -} from "../../../util/string.js"; + toTitleCaseTrimmed +} from '../../../util/string.js'; // const URL = `${process.env.NHOST_ROTOCOL}://${process.env.NHOST_SERVER}/${process.env.NHOST_ENDPOINT}`; -const URL = "https://rgkjmwftqtbpayoyolwh.nhost.run/v1/graphql"; +const URL = 'https://rgkjmwftqtbpayoyolwh.hasura.ap-southeast-1.nhost.run/v1/graphql'; export const loadPlay = async (id) => { const res = await loadPlayAsync(id); @@ -25,17 +25,17 @@ export const loadPlayAsync = (id) => { ...FETH_ALL_PLAYS, ...{ where: { - operator: "", + operator: '', clause: [ { - field: "id", - operator: "eq", + field: 'id', + operator: 'eq', value: id, - type: "string", - }, - ], - }, - }, + type: 'string' + } + ] + } + } }, URL ) @@ -44,10 +44,8 @@ export const loadPlayAsync = (id) => { return play; }) .catch((err) => { - console.error("Error occured"); - console.error( - `Status ${err.response.status} : ${err.response.statusText}` - ); + console.error('Error occured'); + console.error(`Status ${err.response.status} : ${err.response.statusText}`); }); }; @@ -67,10 +65,8 @@ export const loadAllPlaysAsync = () => { return play_list; }) .catch((err) => { - console.error("Error occured"); - console.error( - `Status ${err.response.status} : ${err.response.statusText}` - ); + console.error('Error occured'); + console.error(`Status ${err.response.status} : ${err.response.statusText}`); }); }; diff --git a/lib/services/datasource/query.js b/lib/services/datasource/query.js index 6070536..80c63d4 100644 --- a/lib/services/datasource/query.js +++ b/lib/services/datasource/query.js @@ -1,27 +1,27 @@ export const FETH_ALL_PLAYS = { - display: "Simple fetch play", - name: "Fetch_Plays", - function: "plays", + display: 'Simple fetch play', + name: 'Fetch_Plays', + function: 'plays', write: false, params: [ - "blog", - "style", - "component", - "cover", - "created_at", - "description", - "featured", - "github", - "id", - "dev_mode", - "language", - "slug", - { level: ["name"] }, - "name", - "path", - { play_tags: { tag: ["name"] } }, - "updated_at", - { user: ["id", "displayName", "avatarUrl"] }, - "video", - ], + 'blog', + 'style', + 'component', + 'cover', + 'created_at', + 'description', + 'featured', + 'github', + 'id', + 'dev_mode', + 'language', + 'slug', + { level: ['name'] }, + 'name', + 'path', + { play_tags: { tag: ['name'] } }, + 'updated_at', + { user: ['id', 'displayName', 'avatarUrl'] }, + 'video' + ] }; diff --git a/lib/services/template/_templates/component_jsx.tmpl b/lib/services/template/_templates/component_jsx.tmpl new file mode 100644 index 0000000..59fc8e8 --- /dev/null +++ b/lib/services/template/_templates/component_jsx.tmpl @@ -0,0 +1,31 @@ +import PlayHeader from 'common/playlists/PlayHeader'; +import './styles.%PLAY_STYLE%'; + +// WARNING: Do not change the entry componenet name +function %PLAY_TITLE_NAME_SANITIZED%(props) { + + // Your Code Start below. + + return ( + <> +
+ +
+ {/* Your Code Starts Here */} +
+

Play Details - %PLAY_TITLE_STRING%

+

+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. + Pellentesque euismod, urna eu tincidunt consectetur, + nisi nunc ultricies nisi, eget consectetur nunc nisi + euismod nunc. +

+
+ {/* Your Code Ends Here */} +
+
+ + ); +} + +export default %PLAY_TITLE_NAME_SANITIZED%; \ No newline at end of file diff --git a/lib/services/template/_templates/package_json.tmpl b/lib/services/template/_templates/package_json.tmpl new file mode 100644 index 0000000..78424dd --- /dev/null +++ b/lib/services/template/_templates/package_json.tmpl @@ -0,0 +1,11 @@ +{ + "name": "%PLAY_TITLE_NAME_SANITIZED%", + "version": "1.0.0", + "description": "%PLAY_DESCRIPTION%", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "%PLAY_GITHUB%", + "license": "MIT" +} diff --git a/lib/services/template/_templates/play.cy_ts.tmpl b/lib/services/template/_templates/play.cy_ts.tmpl new file mode 100644 index 0000000..42f4052 --- /dev/null +++ b/lib/services/template/_templates/play.cy_ts.tmpl @@ -0,0 +1,27 @@ +/// + + +describe('Test %PLAY_TITLE_NAME_SANITIZED% play', () => { + beforeEach(() => { + cy.visit('/plays/%PLAY_URL%/'); + }); + + it('Header component should render properly', () => { + cy.header(); + }); + + it('Play page should contains author name', () => { + cy.contains('%PLAY_GITHUB%'); + }); + + + it('We should be able to see the details of the play', () => { + cy.get('.play-details').should('be.visible'); + }); + + + it('We should be able to see the actions', () => { + cy.get('.header-actions').should('be.visible'); + }); + +}); diff --git a/lib/services/template/index.js b/lib/services/template/index.js index ba1408d..92b43a7 100644 --- a/lib/services/template/index.js +++ b/lib/services/template/index.js @@ -1,2 +1,2 @@ -export * from "./play_content.js"; -export * from "./play_index.js"; +export * from './play_content.js'; +export * from './play_index.js'; diff --git a/lib/services/template/play_content.js b/lib/services/template/play_content.js index b835658..f51ca1f 100644 --- a/lib/services/template/play_content.js +++ b/lib/services/template/play_content.js @@ -1,23 +1,27 @@ -import fs from "fs"; -import * as path from "path"; -import { replaceAll, toSanitized, toSlug } from "../../../util/string.js"; -import { fileURLToPath } from "url"; -import { Log } from "../../../log/index.js"; -import { dirname } from "path"; +import fs from 'fs'; +import * as path from 'path'; +import { replaceAll, toSanitized, toSlug } from '../../../util/string.js'; +import { fileURLToPath } from 'url'; +import { Log } from '../../../log/index.js'; +import { dirname } from 'path'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); -const PLAY_LIST_DIRECTORY = "./src/plays"; -const TEMPLATE_DIRECTORY = "_templates"; +import { DIRECTORIES } from '../../../util/const.js'; + +const PLAY_LIST_DIRECTORY = DIRECTORIES.PLAY; +const TEMPLATE_DIRECTORY = '_templates'; const TEMPLATE_MAP = { - common: ["Readme_md"], - js: ["component_js"], - ts: ["component_tsx"], - css: ["styles_css"], - scss: ["styles_scss"], + common: ['Readme_md'], + pkg: ['package_json'], + cy: ['play.cy_ts'], + js: ['component_jsx'], + ts: ['component_tsx'], + css: ['styles_css'], + scss: ['styles_scss'] }; export const processPlayContent = (play) => { @@ -33,26 +37,25 @@ const createPlayDirectory = (play) => { return play_directory; }; -const processTemplates = (data, play_path) => { - processTemplate(data, "common", play_path); +export const processTemplates = (data, play_path) => { + data.url = `${toSlug(data.github.toLowerCase())}/${data.slug}`; + processTemplate(data, 'common', play_path); + processTemplate(data, 'pkg', play_path); + processTemplate(data, 'cy', 'cypress/e2e'); processTemplate(data, data.language, play_path); - processTemplate(data, data.style || "css", play_path); + processTemplate(data, data.style || 'css', play_path); }; const replaceTemplateVar = (play_data, filePath) => { - fs.readFile(filePath, "utf8", function (err, data) { + fs.readFile(filePath, 'utf8', function (err, data) { if (err) { return Log.log(err); } let updated_result = data; Object.keys(play_data).forEach((key) => { - updated_result = replaceAll( - updated_result, - `%PLAY_${key.toUpperCase()}%`, - play_data[key] - ); + updated_result = replaceAll(updated_result, `%PLAY_${key.toUpperCase()}%`, play_data[key]); }); - fs.writeFile(filePath, updated_result, "utf8", function (err) { + fs.writeFile(filePath, updated_result, 'utf8', function (err) { if (err) return Log.log(`Error: During file write: ${err}, ${play_data}`); }); }); @@ -60,36 +63,30 @@ const replaceTemplateVar = (play_data, filePath) => { const getTargetFileName = (source_file_name) => { // e.g: component_js.tmpl - const source_file_name_ext_index = source_file_name.lastIndexOf("."); // ['component_js','tmpl'] - const file_name_wo_ext = source_file_name.substring( - 0, - source_file_name_ext_index - ); // 'component_js' + const source_file_name_ext_index = source_file_name.lastIndexOf('.'); // ['component_js','tmpl'] + const file_name_wo_ext = source_file_name.substring(0, source_file_name_ext_index); // 'component_js' - const source_file_name_segment = file_name_wo_ext.split("_"); // ['component', 'js'] - const new_file_ext = - source_file_name_segment[source_file_name_segment.length - 1]; // 'js' + const source_file_name_segment = file_name_wo_ext.split('_'); // ['component', 'js'] + const new_file_ext = source_file_name_segment[source_file_name_segment.length - 1]; // 'js' source_file_name_segment.splice(-1, 1); // ['component'] - const new_file_name_wo_ext = source_file_name_segment.join(""); // 'component' + const new_file_name_wo_ext = source_file_name_segment.join(''); // 'component' const target_file_name = `${new_file_name_wo_ext}.${new_file_ext}`; return target_file_name; }; -const processTemplate = (play, template_type, play_path) => { +export const processTemplate = (play, template_type, play_path) => { TEMPLATE_MAP[template_type].forEach(async (file) => { const file_name = `${file}.tmpl`; let target_template_name = file_name; - if (template_type === "js" || template_type === "ts") { + if (template_type === 'js' || template_type === 'ts') { target_template_name = target_template_name.replace( - "component", + 'component', toSanitized(play.title_name) ); } const file_path = path.resolve(__dirname, TEMPLATE_DIRECTORY, file_name); - const target_path = `${play_path}/${getTargetFileName( - target_template_name - )}`; + const target_path = `${play_path}/${getTargetFileName(target_template_name)}`; await fs.copyFile( file_path, target_path, diff --git a/lib/services/template/play_index.js b/lib/services/template/play_index.js index d07d8ce..6c00248 100644 --- a/lib/services/template/play_index.js +++ b/lib/services/template/play_index.js @@ -1,10 +1,195 @@ -import fs from "fs"; -import { Log } from "../../../log/index.js"; -import { DIRECTORIES } from "../../../util/const.js"; -import { isValidString, toSanitized, toSlug } from "../../../util/string.js"; -import { loadAllPlaysAsync } from "../datasource/index.js"; +import fs from 'fs'; +import { Log } from '../../../log/index.js'; + +import { + IMAGE_EXTENSIONS, + IMAGE_SMALL_SIZE, + IMAGE_QUALITY_CONVERSION, + DIRECTORIES, + BUILD_IMAGES +} from '../../../util/const.js'; + +import { isValidString, toSanitized, toSlug } from '../../../util/string.js'; +import { loadAllPlaysAsync } from '../datasource/index.js'; + +import { processTemplate } from './play_content.js'; + +import axios from 'axios'; +import { fileTypeFromFile, fileTypeFromBuffer } from 'file-type'; +import Jimp from 'jimp'; +import webp from 'webp-converter'; const INDEX_FILE_PATH = `${DIRECTORIES.PLAY}/index.js`; +const INDEX_JSON_FILE_PATH = `${DIRECTORIES.PLAY}/index.json`; + +// this will grant 755 permission to webp executables +webp.grant_permission(); + +// Check if scr/plays exists for backward compatibilities +let isPmpmWorkspace = fs.existsSync('./pnpm-workspace.yaml'); + +/** + * @desc Download the cover from an url + * @params { string } url the url of the cover image + * @params { string } playPath the path for the play directory + */ +// function downloadCover(url, playPath) { +// let fileType = null; +// try { +// (async () => { +// const res = await axios.get(url, { responseType: 'arraybuffer' }); +// fileType = await fileTypeFromBuffer(res.data); +// fs.writeFileSync(`${playPath}/cover.${fileType.ext}`, res.data); +// })(); +// } catch (error) { +// Log.log(`${playPath}`); +// Log.error(error) +// } +// } + +/** + * @desc Copy image files from a source directory to another destination + * with a filename provided + * @params { string } srcFolder the source folder + * @params { string } destFolder the destination folder + * @params { string } filename the name of the file to copy + * @return void + */ +async function copyImages(srcFolder, destFolder, filename) { + try { + if (!fs.existsSync(destFolder)) { + fs.mkdirSync(destFolder); + } + } catch (err) { + Log.error(error); + } + + let timeout = 0; + if (!fs.existsSync(`${srcFolder}/${filename}`)) { + timeout = 1000; + } + + setTimeout(() => { + if (fs.existsSync(`${srcFolder}/${filename}`)) { + const filePath = `${srcFolder}/${filename}`; + const copy = `${destFolder}/${filename}`; + fs.copyFile(filePath, copy, (error) => { + if (error) { + Log.log(error); + } else { + Log.log(`Copy of ${filename} done!`); + } + }); + } + }, timeout); +} + +/** + * Check an image file named cover in the directory and return a string as file extension + * @params { string } playPath : path of the play in the plays directory + * @return { string } the extension of the file or empty string + */ +const getPlayCoverExtension = (playPath) => { + let extension = IMAGE_EXTENSIONS.reduce((ext, nextExt) => { + if (ext === '' && fs.existsSync(`${DIRECTORIES.PLAY}/${playPath}/cover.${nextExt}`)) { + return nextExt; + } + return ext; + }, ''); + return extension ?? ''; +}; + +/** + * @desc get an extension for an image and check if + * the extension has the correct type. If not, rename the file with + * the correct extension + * @params { string } playDir the plays directory + * @params { string } playPath the path of the directory ( name + slug) + * @params { string } currentExtension the extension to check + * @return void + */ +async function cleanUpFileExtension(playDir, playPath) { + const currentExtension = getPlayCoverExtension(playPath); + + if (currentExtension) { + const type = await fileTypeFromFile(`${playDir}/${playPath}/cover.${currentExtension}`); + if (type.ext !== currentExtension) { + fs.renameSync( + `${playDir}/${playPath}/cover.${currentExtension}`, + `${playDir}/${playPath}/cover.${type.ext}` + ); + } + } +} + +/** + * @desc Resize a provided image and create a webp version of it + * if the image is already a webp image, only resize will be done + * @params { string } playsDir the directory with the plays + * @params { string } playPath the path to the folder source of the image + * @params { string } coverFileExtension the extension of the image + */ +async function resizeAndCreateWebp(playsDir, playPath, coverFileExtension) { + if (fs.existsSync(`${playsDir}/${playPath}/cover.${coverFileExtension}`)) { + await resizeImages( + `${playsDir}/${playPath}`, + `cover.${coverFileExtension}`, + 'cover', + coverFileExtension + ) + .then(async (result) => { + await convertToWebp(`${playsDir}/${playPath}`, 'cover', coverFileExtension); + return true; + }) + .catch((error) => { + Log.error(error); + }); + } +} + +/** + * @desc Convert an image to the webp extension. + * and create a smaller version based on the IMAGE_SMALL_SIZE variable + * @params { string } filePath the path to the image + * @params { string } file the name of file, default : cover + * @params { string } the extension of the file provided + * @return Promise + */ +const convertToWebp = async (filePath, file = 'cover', originExtension) => { + return new Promise((resolve) => { + if (originExtension !== 'webp') { + webp.cwebp( + `${filePath}/${file}.${originExtension}`, + `${filePath}/${file}.webp`, + `-q ${IMAGE_QUALITY_CONVERSION}`, + '-v' + ); + + if (!fs.existsSync(`${filePath}/${file}_small.${originExtension}`)) { + resizeImages(filePath, `${file}.${originExtension}`, file); + } + + setTimeout(() => { + webp.cwebp( + `${filePath}/${file}_small.${originExtension}`, + `${filePath}/${file}_small.webp`, + `-q ${IMAGE_QUALITY_CONVERSION}`, + '-v' + ); + }, 1000); + } else { + webp.cwebp( + `${filePath}/${file}.${originExtension}`, + `${filePath}/${file}_small.webp`, + `-q ${IMAGE_QUALITY_CONVERSION}`, + '-v', + `-resize ${IMAGE_SMALL_SIZE} 0` + ); + } + resolve(); + }); +}; + export const processPlayIndex = () => { loadAllPlaysAsync().then((plays) => { const all_dirs = getAllPlaysLocally(); @@ -13,43 +198,139 @@ export const processPlayIndex = () => { }; const getAllPlaysLocally = () => { - const all_files = fs.readdirSync("./src/plays"); + const all_files = fs.readdirSync(DIRECTORIES.PLAY); return all_files; }; +async function createPublicImages() { + let success = false; + await resizeImages('./src/images', 'thumb-play.png', 'thumb-play', 'png') + .then(async (res) => { + // get webp version + await convertToWebp('./src/images', 'thumb-play', 'png'); + }) + .then(async (res) => { + // be sure to get webp small version + await convertToWebp('./src/images', 'thumb-play', 'webp'); + }) + .then(async (res) => { + await copyImages('./src/images', './public/images', 'thumb-play.png'); + await copyImages('./src/images', './public/images', 'thumb-play_small.png'); + await copyImages('./src/images', './public/images', 'thumb-play.webp'); + await copyImages('./src/images', './public/images', 'thumb-play_small.webp'); + success = true; + }) + .catch((error) => { + Log.warning('createPublicImages', error); + }) + .finally(() => { + success && Log.log('Copied images in public/images'); + }); +} + +async function resizeImages(filePath, srcImage, destinationFileName = 'cover') { + // Read the image. + const type = await fileTypeFromFile(`${filePath}/${srcImage}`); + + if (type.ext === 'webp') { + return convertToWebp(filePath, 'cover', 'webp'); + } + + const image = await Jimp.read(`${filePath}/${srcImage}`); + await image.resize(240, Jimp.AUTO); + await image.writeAsync(`${filePath}/${destinationFileName}_small.${type.ext}`); +} + const writeFile = (plays, dirs) => { - fs.writeFileSync( - INDEX_FILE_PATH, - "// Do not modify the content of this file" - ); - plays.forEach((play) => { + let folderUp = !isPmpmWorkspace ? '../../' : '../../../'; + + // developer can delete this file to generate all covers images again + const hasPublicThumbImage = fs.existsSync('./public/images/thumb-play_small.webp'); + + const playsPaths = new Map(); + + fs.writeFileSync(INDEX_FILE_PATH, '// Do not modify the content of this file'); + plays.forEach(async (play, index) => { let playPath = play.kebab_name; + let coverFileExtension = ''; + + playsPaths.set('#playsUpwardPath', folderUp); + if (play.path) { - const pathSegmentation = play.path.split("/"); + const pathSegmentation = play.path.split('/'); if (pathSegmentation.length > 1 && play.dev_mode !== true) { playPath = pathSegmentation[2]; if (playPath !== play.kebab_name) { - Log.log( - `Play name and path is not matching : ${play.name}(${playPath})` - ); + Log.log(`Play name and path is not matching : ${play.name}(${playPath})`); } } } Log.log(`Checking play locally: ${play.name}(${playPath})`); if (dirs.indexOf(playPath) > -1) { Log.log(`Play found locally : ${playPath}`); + const playCleanName = + isValidString(play.component) || + toSanitized(play.title_name) || + toSanitized(play.kebab_name); + + // if no cover file found and an url was provided + // we download a local copy + // if we found thumb-play_small.webp in public dir, we don't download + // from an url again to prevent to continuously request from these servers. + + if (BUILD_IMAGES && !hasPublicThumbImage && play?.cover) { + const res = await axios.get(play.cover, { responseType: 'arraybuffer' }); + let fileType = await fileTypeFromBuffer(res.data); + fs.writeFileSync(`${DIRECTORIES.PLAY}/${playPath}/cover.${fileType.ext}`, res.data); + } + + BUILD_IMAGES && cleanUpFileExtension(DIRECTORIES.PLAY, playPath); + + BUILD_IMAGES && setTimeout(() => { + // check for cover file and it's extension 1st time + coverFileExtension = getPlayCoverExtension(playPath, play); + + if (!IMAGE_EXTENSIONS.includes(coverFileExtension)) { + coverFileExtension = 'images/thumb-play.png'; + } else { + // we create a webp image and small size from cover + resizeAndCreateWebp(DIRECTORIES.PLAY, playPath, coverFileExtension); + } + + playsPaths.set(`${playPath}/${playCleanName}`, [ + `${encodeURI(play.github.toLowerCase())}/${play.slug}`, + coverFileExtension + ]); + }, index * 1000); + + !BUILD_IMAGES && playsPaths.set(`${playPath}/${playCleanName}`, [ + `${encodeURI(play.github.toLowerCase())}/${play.slug}` + ]); + + + // check if play has a package.json file or create it + if (!fs.existsSync(`${DIRECTORIES.PLAY}/${playPath}/package.json`)) { + processTemplate(play, 'pkg', `${DIRECTORIES.PLAY}/${playPath}`); + } + fs.appendFileSync( INDEX_FILE_PATH, - `\n export { default as ${ - isValidString(play.component) || toSanitized(play.title_name) - } } from 'plays/${playPath}/${ - isValidString(play.component) || - toSanitized(play.title_name) || - toSanitized(play.kebab_name) - }';` + `\n export { default as ${playCleanName} } from 'plays/${playPath}/${playCleanName}';` ); } else { Log.warning(`Play not found locally : ${play.name}(${playPath})`); } }); + + // copy thumbs image to public/images to avoid some problems + BUILD_IMAGES && !hasPublicThumbImage && createPublicImages(); + + plays.length && + setTimeout(() => { + // if ./plays folder exists (use for mono repo) + fs.writeFileSync( + INDEX_JSON_FILE_PATH, + `${JSON.stringify(Object.fromEntries(playsPaths), null, '\t')}` + ); + }, plays.length * 1000); }; diff --git a/log/index.js b/log/index.js index 3237ba6..a2b25ac 100644 --- a/log/index.js +++ b/log/index.js @@ -1,12 +1,14 @@ -let node = "CRP"; -const log = (text, ignoreHeader = false, node = "INFO") => { - console.log( - ` ${node} : ${ignoreHeader ? "" : `${getDate()}: ${node} : `} ${text}` - ); +let node = 'CRP'; +const log = (text, ignoreHeader = false, node = 'INFO') => { + console.log(` ${node} : ${ignoreHeader ? '' : `${getDate()}: ${node} : `} ${text}`); +}; + +const error = (text, ignoreHeader = false, node = 'ERROR') => { + console.error(` ${node} : ${ignoreHeader ? '' : `${getDate()}: ${node} : `} ${text}`); }; const warning = (text, ignoreHeader = false) => { - log(text, ignoreHeader, "WARNING"); + log(text, ignoreHeader, 'WARNING'); }; const header = (text) => { @@ -15,19 +17,19 @@ const header = (text) => { }; const highlight = (text) => { - console.log("---------------------------------------------------------"); + console.log('---------------------------------------------------------'); console.log(` ${text}`); - console.log("---------------------------------------------------------"); + console.log('---------------------------------------------------------'); }; const segementStart = (text) => { console.log(`----------------- Start: ${text}-----------------------`); - console.log(""); + console.log(''); }; const segementEnd = (text) => { - console.log(""); + console.log(''); console.log(`----------------- End: ${text}-----------------------`); - console.log(""); + console.log(''); }; const newLine = (text) => { @@ -35,21 +37,22 @@ const newLine = (text) => { }; const getDate = () => { - return new Date().toLocaleDateString("en-us", { - weekday: "long", - year: "numeric", - month: "short", - day: "numeric", + return new Date().toLocaleDateString('en-us', { + weekday: 'long', + year: 'numeric', + month: 'short', + day: 'numeric' }); }; export const Log = { log, warning, + error, header, highlight, segementEnd, segementStart, newLine, - node, + node }; diff --git a/package.json b/package.json index 62ae32e..dca772d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "create-react-play", - "version": "0.0.17", + "version": "0.0.18", "description": "Boiler plate for creating a play within reactplay system", "main": "index.js", "bin": { @@ -19,12 +19,13 @@ }, "homepage": "https://reactplay.io/", "dependencies": { - "axios": "^0.27.2", + "axios": "^1.4.0", "dotenv": "^16.0.1", - "json-graphql-parser": "^0.0.9" + "file-type": "^18.5.0", + "jimp": "^0.22.8", + "json-graphql-parser": "^0.1.9", + "webp-converter": "^2.3.3" }, "repository": "git://github.com/reactplay/create-react-play.git", - "devDependencies": { - "@types/node": "^18.0.0" - } + "yalcSig": "f9f8ee7b9fb10be45e4a8dd6fe4845b1" } diff --git a/util/const.js b/util/const.js index e60628e..5c39783 100644 --- a/util/const.js +++ b/util/const.js @@ -1,58 +1,84 @@ export const CONSOLE_COLORS = { - Reset: "\x1b[0m", - Bright: "\x1b[1m", - Dim: "\x1b[2m", - Underscore: "\x1b[4m", - Blink: "\x1b[5m", - Reverse: "\x1b[7m", - Hidden: "\x1b[8m", - - FgBlack: "\x1b[30m", - FgRed: "\x1b[31m", - FgGreen: "\x1b[32m", - FgYellow: "\x1b[33m", - FgBlue: "\x1b[34m", - FgMagenta: "\x1b[35m", - FgCyan: "\x1b[36m", - FgWhite: "\x1b[37m", - - BgBlack: "\x1b[40m", - BgRed: "\x1b[41m", - BgGreen: "\x1b[42m", - BgYellow: "\x1b[43m", - BgBlue: "\x1b[44m", - BgMagenta: "\x1b[45m", - BgCyan: "\x1b[46m", - BgWhite: "\x1b[47m", + Reset: '\x1b[0m', + Bright: '\x1b[1m', + Dim: '\x1b[2m', + Underscore: '\x1b[4m', + Blink: '\x1b[5m', + Reverse: '\x1b[7m', + Hidden: '\x1b[8m', + + FgBlack: '\x1b[30m', + FgRed: '\x1b[31m', + FgGreen: '\x1b[32m', + FgYellow: '\x1b[33m', + FgBlue: '\x1b[34m', + FgMagenta: '\x1b[35m', + FgCyan: '\x1b[36m', + FgWhite: '\x1b[37m', + + BgBlack: '\x1b[40m', + BgRed: '\x1b[41m', + BgGreen: '\x1b[42m', + BgYellow: '\x1b[43m', + BgBlue: '\x1b[44m', + BgMagenta: '\x1b[45m', + BgCyan: '\x1b[46m', + BgWhite: '\x1b[47m' }; export const ALL_ARGS_TYPES = [ { - name: "create", - args: ["create", "-c"], - description: "Create a play", - flag: false, + name: 'create', + args: ['create', '-c'], + description: 'Create a play', + flag: false }, { - name: "update", - args: ["update", "-u"], - description: "Update a play", - flag: false, + name: 'update', + args: ['update', '-u'], + description: 'Update a play', + flag: false }, { - name: "prepare", - args: ["prepare", "-p"], - description: "Prepare environment for build", - flag: true, + name: 'prepare', + args: ['prepare', '-p'], + description: 'Prepare environment for build', + flag: true }, { - name: "help", - args: ["help", "-h"], - description: "Print all available commands", - flag: true, + name: 'help', + args: ['help', '-h'], + description: 'Print all available commands', + flag: true }, + { + name: 'playspath', + args: ['-plays'], + description: 'Define a custom path for the plays', + flag: true + }, + { + name: 'images', + args: ['-i'], + description: 'Create small thumbsnail of image and correct bad extension', + flag: true + } ]; +let playsPath = './src/plays'; + +let customPath = process.argv.find((arg) => arg.includes('-plays=')); +if (customPath !== undefined) { + playsPath = './' + customPath.slice(7); +} + + +export const BUILD_IMAGES = process.argv.find((arg) => arg.includes('-i=')) !== undefined; +export const IMAGE_SMALL_SIZE = 260; +export const IMAGE_EXTENSIONS = ['png', 'jpg', 'jpeg', 'webp']; +export const IMAGE_QUALITY_CONVERSION = 80; + export const DIRECTORIES = { - PLAY: "./src/plays", + PLAY: playsPath, + E2E: './cypress/e2e' }; diff --git a/util/string.js b/util/string.js index 9a05aa1..09d0d60 100644 --- a/util/string.js +++ b/util/string.js @@ -2,18 +2,15 @@ export const toKebabCase = (str) => { return str .match(/[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g) .map((x) => x.toLowerCase()) - .join("-"); + .join('-'); }; export const toPascalcase = (str) => { let res = `${str}` .toLowerCase() - .replace(new RegExp(/[-_]+/, "g"), " ") - .replace(new RegExp(/[^\w\s]/, "g"), "") - .replace( - new RegExp(/\s+(.)(\w*)/, "g"), - ($1, $2, $3) => `${$2.toUpperCase() + $3}` - ) + .replace(new RegExp(/[-_]+/, 'g'), ' ') + .replace(new RegExp(/[^\w\s]/, 'g'), '') + .replace(new RegExp(/\s+(.)(\w*)/, 'g'), ($1, $2, $3) => `${$2.toUpperCase() + $3}`) .replace(new RegExp(/\w/), (s) => s.toUpperCase()); res = res.replace(/^./, str[0].toLowerCase()); return res; @@ -27,12 +24,12 @@ export const toTitleCase = (str) => { export const toTitleCaseTrimmed = (str) => { const titleCse = toTitleCase(str); - return titleCse.replace(/\s/g, ""); + return titleCse.replace(/\s/g, ''); }; export const replaceAll = (str, replaceWhat, replaceTo) => { - replaceWhat = replaceWhat.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&"); - var re = new RegExp(replaceWhat, "g"); + replaceWhat = replaceWhat.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&'); + var re = new RegExp(replaceWhat, 'g'); return str.replace(re, replaceTo); }; @@ -53,10 +50,10 @@ export const toSlug = (str) => { export const toSanitized = (str) => { //replace all special characters | symbols with a space - str = str.replace(/[`~!@#$%^&*()_\-+=\[\]{};:'"\\|\/,.<>?\s]/g, " "); + str = str.replace(/[`~!@#$%^&*()_\-+=\[\]{};:'"\\|\/,.<>?\s]/g, ' '); // trim spaces at start and end of string - str = str.replace(/^\s+|\s+$/gm, ""); + str = str.replace(/^\s+|\s+$/gm, ''); // replace space with dash/hyphen - str = str.replace(/\s+/g, "-"); + str = str.replace(/\s+/g, '-'); return str; }; diff --git a/util/util.js b/util/util.js index f97246c..46b8646 100644 --- a/util/util.js +++ b/util/util.js @@ -3,4 +3,4 @@ export const toKebabCase = (str) => str .match(/[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+/g) .map((x) => x.toLowerCase()) - .join("-"); + .join('-');