style: format with prettier

This commit is contained in:
Carlos 2024-08-13 20:05:53 -04:00
parent 7422ddf34b
commit 0e72a35ba7
13 changed files with 2058 additions and 98 deletions

15
.eslintrc.js Normal file
View File

@ -0,0 +1,15 @@
module.exports = {
env: {
browser: true,
es2021: true,
node: true,
},
extends: ['eslint:recommended', 'plugin:prettier/recommended'],
parserOptions: {
ecmaVersion: 12,
sourceType: 'module',
},
rules: {
'prettier/prettier': 'error',
},
};

7
.prettierrc Normal file
View File

@ -0,0 +1,7 @@
{
"trailingComma": "es5",
"tabWidth": 2,
"semi": true,
"singleQuote": true,
"printWidth": 80
}

View File

@ -1,4 +1,3 @@
# Contributor Covenant Code of Conduct
## Our Pledge
@ -46,18 +45,22 @@ All community leaders are obligated to respect the privacy and security of the r
Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series of actions.
**Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. 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 individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within the community.

101
README.md
View File

@ -1,4 +1,3 @@
# React Crafter
**React Crafter** is a CLI tool designed to quickly scaffold a modern React application with TypeScript, Ant Design, Sass, Webpack, and essential development tools like Husky and linters pre-configured. This tool simplifies the initial setup, allowing developers to start coding with best practices from the get-go.
@ -49,58 +48,76 @@ npx react-crafter awesome-project
Heres a summary of the commands you can use after setting up your project:
1. **Start Development Server**:
```bash
npm start
```
Starts the development server with Webpack. The project is served using Webpack Dev Server with the configuration specified in `webpack.config.js`.
```bash
npm start
```
Starts the development server with Webpack. The project is served using Webpack Dev Server with the configuration specified in `webpack.config.js`.
2. **Build for Production**:
```bash
npm run build
```
Builds the project for production. Webpack compiles the project and outputs the optimized bundle in the `/dist` directory.
```bash
npm run build
```
Builds the project for production. Webpack compiles the project and outputs the optimized bundle in the `/dist` directory.
3. **Run Tests**:
```bash
npm test
```
Placeholder for running tests. Currently, it does not run any tests but can be customized to run Jest or other test suites.
```bash
npm test
```
Placeholder for running tests. Currently, it does not run any tests but can be customized to run Jest or other test suites.
4. **Run Tests in Watch Mode**:
```bash
npm run test:dev
```
Runs tests in watch mode using React Scripts. Suitable for a test-driven development approach.
```bash
npm run test:dev
```
Runs tests in watch mode using React Scripts. Suitable for a test-driven development approach.
5. **Format Staged Files**:
```bash
npm run pretty-quick
```
Formats all staged files using Prettier. Ensures that code is consistently formatted before committing.
```bash
npm run pretty-quick
```
Formats all staged files using Prettier. Ensures that code is consistently formatted before committing.
6. **Lint Entire Codebase**:
```bash
npm run lint:prettier
```
Checks the format of the entire codebase using a custom script. It can be used to ensure that all files adhere to Prettiers formatting rules.
```bash
npm run lint:prettier
```
Checks the format of the entire codebase using a custom script. It can be used to ensure that all files adhere to Prettiers formatting rules.
7. **Format Entire Codebase**:
```bash
npm run prettier
```
Formats the entire codebase using Prettier based on the configuration in `.prettierrc`.
```bash
npm run prettier
```
Formats the entire codebase using Prettier based on the configuration in `.prettierrc`.
8. **Format & Commit**:
```bash
npm run prettier:commit
```
Applies Prettier formatting to staged files before committing. Ensures that committed code is properly formatted.
```bash
npm run prettier:commit
```
Applies Prettier formatting to staged files before committing. Ensures that committed code is properly formatted.
9. **Eject Project**:
```bash
npm run eject
```
Ejects the project from Create React App. This command exposes the underlying configuration files for full control but cannot be undone.
```bash
npm run eject
```
Ejects the project from Create React App. This command exposes the underlying configuration files for full control but cannot be undone.
10. **Prepare Husky**:
```bash
@ -132,12 +149,14 @@ This feature ensures you can start working in your preferred environment immedia
We welcome contributions to React Crafter! If you have ideas, find bugs, or want to improve the project, please feel free to contribute. Here's how you can get involved:
### 1. Fork the Repository
Start by forking the repository to your own GitHub account:
1. Navigate to the [React Crafter GitHub repository](#).
2. Click the "Fork" button in the top right corner.
### 2. Clone Your Fork
Clone your forked repository to your local machine:
```bash
@ -146,6 +165,7 @@ cd react-crafter
```
### 3. Create a New Branch
Create a new branch for your feature or bug fix:
```bash
@ -153,9 +173,11 @@ git checkout -b feature/your-feature-name
```
### 4. Make Changes
Make your changes or improvements in your new branch. Follow the existing code style and conventions.
### 5. Test Your Changes
Before submitting your changes, make sure everything works as expected:
```bash
@ -163,6 +185,7 @@ npm run test
```
### 6. Commit and Push
Commit your changes with a descriptive commit message:
```bash
@ -172,15 +195,19 @@ git push origin feature/your-feature-name
```
### 7. Create a Pull Request
Go to the original repository and click on the "Pull Requests" tab. Click "New Pull Request" and select your branch. Provide a description of your changes and submit the pull request.
### 8. Review Process
Your pull request will be reviewed by the maintainers. You may be asked to make additional changes or clarifications before your code is merged.
### 9. Celebrate 🎉
Once your pull request is merged, youve officially contributed to React Crafter! Thank you for your contribution.
### Code of Conduct
Please note that we have a [Code of Conduct](CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. Please report unacceptable behavior to [cargdev@gmail.com](mailto:cargdev@gmail.com).
## License

25
check-format.js Normal file
View File

@ -0,0 +1,25 @@
const { exec } = require('child_process');
const { promisify } = require('util');
const execPromise = promisify(exec);
async function run() {
// Dynamically import the ES Module
const ora = (await import('ora')).default;
const spinner = ora('✨ Checking code formatting...').start();
try {
const { stdout } = await execPromise(
'npm run pretty-quick --check . --config .prettierrc'
);
spinner.succeed('✅ Code formatting check passed.');
console.log(stdout);
} catch (error) {
spinner.fail('❌ Code formatting check failed.');
console.error(error.message);
process.exit(1);
}
}
run();

192
index.js
View File

@ -29,8 +29,14 @@ function createApp(projectDirectory) {
if (fs.existsSync(root)) {
console.clear();
console.log(chalk.red.bold('❌ Error: Directory already exists!'));
console.log(chalk.yellow(`\nThe directory ${chalk.blue(root)} already exists.`));
console.log(chalk.cyan('Please choose a different project name or delete the existing directory.'));
console.log(
chalk.yellow(`\nThe directory ${chalk.blue(root)} already exists.`)
);
console.log(
chalk.cyan(
'Please choose a different project name or delete the existing directory.'
)
);
return; // Exit the function if the directory exists
}
@ -41,7 +47,9 @@ function createApp(projectDirectory) {
fs.mkdirSync(root);
process.chdir(root);
const spinner = ora('⏳ Installing packages. This might take a couple of minutes.').start();
const spinner = ora(
'⏳ Installing packages. This might take a couple of minutes.'
).start();
execSync('npx create-react-app . --template typescript', { stdio: 'ignore' });
@ -89,7 +97,11 @@ function askUserWhereToOpen(directory) {
openInNeovim(directory);
break;
default:
console.log(chalk.yellow('Project setup complete. You can manually open the project if needed.'));
console.log(
chalk.yellow(
'Project setup complete. You can manually open the project if needed.'
)
);
}
});
}
@ -102,12 +114,18 @@ function openInTerminal(directory) {
spawn('open', ['-a', 'Terminal', directory]);
} else if (platform === 'win32') {
// Windows
spawn('cmd.exe', ['/c', 'start', 'cmd.exe', '/K', `cd /d ${directory}`], { shell: true });
spawn('cmd.exe', ['/c', 'start', 'cmd.exe', '/K', `cd /d ${directory}`], {
shell: true,
});
} else if (platform === 'linux') {
// Linux
spawn('gnome-terminal', ['--working-directory=' + directory]);
} else {
console.log(chalk.red('Unsupported platform. Please manually navigate to the directory.'));
console.log(
chalk.red(
'Unsupported platform. Please manually navigate to the directory.'
)
);
}
}
@ -124,36 +142,80 @@ function printCommandSummary() {
console.log(chalk.cyan('\nAvailable Commands:'));
console.log(chalk.green('1. 🚀 npm start'));
console.log(chalk.white(' Starts the development server with Webpack. The project is served using Webpack Dev Server with the configuration specified in webpack.config.js.'));
console.log(
chalk.white(
' Starts the development server with Webpack. The project is served using Webpack Dev Server with the configuration specified in webpack.config.js.'
)
);
console.log(chalk.green('\n2. 🛠️ npm run build'));
console.log(chalk.white(' Builds the project for production. Webpack compiles the project and outputs the optimized bundle in the /dist directory.'));
console.log(
chalk.white(
' Builds the project for production. Webpack compiles the project and outputs the optimized bundle in the /dist directory.'
)
);
console.log(chalk.green('\n3. 🧪 npm test'));
console.log(chalk.white(' Placeholder for running tests. Currently, it does not run any tests but can be customized to run Jest or other test suites.'));
console.log(
chalk.white(
' Placeholder for running tests. Currently, it does not run any tests but can be customized to run Jest or other test suites.'
)
);
console.log(chalk.green('\n4. 🧪 npm run test:dev'));
console.log(chalk.white(' Runs tests in watch mode using React Scripts. Suitable for a test-driven development approach.'));
console.log(
chalk.white(
' Runs tests in watch mode using React Scripts. Suitable for a test-driven development approach.'
)
);
console.log(chalk.green('\n5. 🎨 npm run pretty-quick'));
console.log(chalk.white(' Formats all staged files using Prettier. Ensures that code is consistently formatted before committing.'));
console.log(
chalk.white(
' Formats all staged files using Prettier. Ensures that code is consistently formatted before committing.'
)
);
console.log(chalk.green('\n6. 🔍 npm run lint:prettier'));
console.log(chalk.white(' Checks the format of the entire codebase using a custom script. It can be used to ensure that all files adhere to Prettiers formatting rules.'));
console.log(
chalk.white(
' Checks the format of the entire codebase using a custom script. It can be used to ensure that all files adhere to Prettiers formatting rules.'
)
);
console.log(chalk.green('\n7. ✨ npm run prettier'));
console.log(chalk.white(' Formats the entire codebase using Prettier based on the configuration in .prettierrc.'));
console.log(
chalk.white(
' Formats the entire codebase using Prettier based on the configuration in .prettierrc.'
)
);
console.log(chalk.green('\n8. ✨ npm run prettier:commit'));
console.log(chalk.white(' Applies Prettier formatting to staged files before committing. Ensures that committed code is properly formatted.'));
console.log(
chalk.white(
' Applies Prettier formatting to staged files before committing. Ensures that committed code is properly formatted.'
)
);
console.log(chalk.green('\n9. 🚨 npm run eject'));
console.log(chalk.white(' Ejects the project from Create React App. This command exposes the underlying configuration files for full control but cannot be undone.'));
console.log(
chalk.white(
' Ejects the project from Create React App. This command exposes the underlying configuration files for full control but cannot be undone.'
)
);
console.log(chalk.green('\n10. 🛡️ npm run prepare'));
console.log(chalk.white(' Installs Husky hooks. This script is automatically run after dependencies are installed, setting up Git hooks for the project.'));
console.log(
chalk.white(
' Installs Husky hooks. This script is automatically run after dependencies are installed, setting up Git hooks for the project.'
)
);
console.log(chalk.yellow('\n🎉 Your project is ready! Use the above commands to start working on your new React app.'));
console.log(
chalk.yellow(
'\n🎉 Your project is ready! Use the above commands to start working on your new React app.'
)
);
}
function runPrettierCommit() {
@ -194,22 +256,24 @@ function deletePreCommitHook() {
}
function updatePackageJson() {
const spinner = ora('📝 Updating package.json with custom scripts...').start();
const spinner = ora(
'📝 Updating package.json with custom scripts...'
).start();
const packageJsonPath = path.resolve('package.json');
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
packageJson.scripts = {
"start": "webpack serve --config webpack.config.js --mode development",
"build": "webpack --config webpack.config.js --mode production",
"test": "echo \"Error: no test specified\" && exit 0",
"test:dev": "react-scripts test",
"pretty-quick": "pretty-quick",
"lint:prettier": "node check-format.js",
"prettier": "prettier --write . --config .prettierrc",
"prettier:commit": "node prettier-commit.js",
"eject": "react-scripts eject",
"prepare": "husky install"
start: 'webpack serve --config webpack.config.js --mode development',
build: 'webpack --config webpack.config.js --mode production',
test: 'echo "Error: no test specified" && exit 0',
'test:dev': 'react-scripts test',
'pretty-quick': 'pretty-quick',
'lint:prettier': 'node check-format.js',
prettier: 'prettier --write . --config .prettierrc',
'prettier:commit': 'node prettier-commit.js',
eject: 'react-scripts eject',
prepare: 'husky install',
};
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
@ -220,18 +284,42 @@ function copyPreConfiguredFiles(destinationPath) {
const spinner = ora('📁 Copying pre-configured files...').start();
const filesToCopy = [
{ src: path.resolve(__dirname, 'pre-files/check-format.js'), dest: path.join(destinationPath, 'check-format.js') },
{ src: path.resolve(__dirname, 'pre-files/commit-msg-linter.js'), dest: path.join(destinationPath, '.husky/commit-msg-linter.js') },
{ src: path.resolve(__dirname, 'pre-files/lint-check.js'), dest: path.join(destinationPath, '.husky/lint-check.js') },
{ src: path.resolve(__dirname, 'pre-files/prettier-commit.js'), dest: path.join(destinationPath, 'prettier-commit.js') },
{ src: path.resolve(__dirname, 'pre-files/webpack.config.js'), dest: path.join(destinationPath, 'webpack.config.js') },
{ src: path.resolve(__dirname, 'pre-files/.babelrc'), dest: path.join(destinationPath, '.babelrc') },
{ src: path.resolve(__dirname, 'pre-files/.eslintrc.js'), dest: path.join(destinationPath, '.eslintrc.js') },
{ src: path.resolve(__dirname, 'pre-files/.prettierrc'), dest: path.join(destinationPath, '.prettierrc') },
{
src: path.resolve(__dirname, 'pre-files/check-format.js'),
dest: path.join(destinationPath, 'check-format.js'),
},
{
src: path.resolve(__dirname, 'pre-files/commit-msg-linter.js'),
dest: path.join(destinationPath, '.husky/commit-msg-linter.js'),
},
{
src: path.resolve(__dirname, 'pre-files/lint-check.js'),
dest: path.join(destinationPath, '.husky/lint-check.js'),
},
{
src: path.resolve(__dirname, 'pre-files/prettier-commit.js'),
dest: path.join(destinationPath, 'prettier-commit.js'),
},
{
src: path.resolve(__dirname, 'pre-files/webpack.config.js'),
dest: path.join(destinationPath, 'webpack.config.js'),
},
{
src: path.resolve(__dirname, 'pre-files/.babelrc'),
dest: path.join(destinationPath, '.babelrc'),
},
{
src: path.resolve(__dirname, 'pre-files/.eslintrc.js'),
dest: path.join(destinationPath, '.eslintrc.js'),
},
{
src: path.resolve(__dirname, 'pre-files/.prettierrc'),
dest: path.join(destinationPath, '.prettierrc'),
},
// Add more files here if needed
];
filesToCopy.forEach(file => {
filesToCopy.forEach((file) => {
fs.copyFileSync(file.src, file.dest);
});
@ -245,13 +333,19 @@ function copyPreConfiguredFiles(destinationPath) {
function installDependencies() {
const spinner = ora('🔄 Installing additional dependencies...').start();
execSync('npm install @babel/core @babel/preset-env @babel/preset-react @reduxjs/toolkit @testing-library/jest-dom @testing-library/react @testing-library/user-event @types/jest @types/node @types/react @types/react-dom ajv antd babel-loader css-loader jest playwright react react-dom react-redux react-scripts redux sass sass-loader style-loader typescript web-vitals webpack webpack-cli', { stdio: 'ignore' });
execSync(
'npm install @babel/core @babel/preset-env @babel/preset-react @reduxjs/toolkit @testing-library/jest-dom @testing-library/react @testing-library/user-event @types/jest @types/node @types/react @types/react-dom ajv antd babel-loader css-loader jest playwright react react-dom react-redux react-scripts redux sass sass-loader style-loader typescript web-vitals webpack webpack-cli',
{ stdio: 'ignore' }
);
spinner.succeed('✅ Additional dependencies installed.');
}
function installDevDependencies() {
const spinner = ora('🔄 Installing additional dev dependencies...').start();
execSync('npm install --save-dev @babel/plugin-proposal-private-property-in-object ora prettier @commitlint/cli @commitlint/config-conventional @svgr/webpack dotenv dotenv-webpack husky url-loader webpack-dev-server pretty-quick', { stdio: 'ignore' });
execSync(
'npm install --save-dev @babel/plugin-proposal-private-property-in-object ora prettier @commitlint/cli @commitlint/config-conventional @svgr/webpack dotenv dotenv-webpack husky url-loader webpack-dev-server pretty-quick',
{ stdio: 'ignore' }
);
spinner.succeed('✅ Additional dev dependencies installed.');
}
@ -287,16 +381,18 @@ function setupCommitlint() {
const spinner = ora('🔍 Setting up Commitlint...').start();
const commitMsg = `#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
node ./.husky/commit-msg-linter.js "$1"`
node ./.husky/commit-msg-linter.js "$1"`;
const commitLintMsgLinter = `module.exports = {
extends: ['@commitlint/config-conventional'],
};`
};`;
const prePush = `#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
node .husky/lint-check.js`
node .husky/lint-check.js`;
execSync('npm install @commitlint/{config-conventional,cli} --save-dev', { stdio: 'ignore' });
execSync('npm install @commitlint/{config-conventional,cli} --save-dev', {
stdio: 'ignore',
});
execSync('touch .husky/commit-msg', { stdio: 'ignore' });
execSync('touch .husky/pre-push', { stdio: 'ignore' });
execSync('chmod +x .husky/commit-msg', { stdio: 'ignore' });
@ -315,11 +411,11 @@ function setupRedux() {
'src/store',
'src/store/slices',
'src/store/middleware',
'src/store/selectors'
'src/store/selectors',
];
reduxStructure.forEach(dir => {
fs.mkdirSync(dir, { recursive: true});
reduxStructure.forEach((dir) => {
fs.mkdirSync(dir, { recursive: true });
});
const storeIndex = `
@ -365,10 +461,10 @@ function createAtomicStructure() {
'src/components/molecules',
'src/components/organisms',
'src/components/templates',
'src/components/pages'
'src/components/pages',
];
atomicStructure.forEach(dir => {
atomicStructure.forEach((dir) => {
fs.mkdirSync(dir, { recursive: true });
});
spinner.succeed('🏗️ Atomic design structure created.');

View File

@ -1,7 +1,7 @@
module.exports = {
testEnvironment: "node", // Set up the environment (node, jsdom, etc.)
testEnvironment: 'node', // Set up the environment (node, jsdom, etc.)
verbose: true, // Show detailed test results
coverageDirectory: "coverage", // Directory for code coverage reports
coverageDirectory: 'coverage', // Directory for code coverage reports
collectCoverage: true, // Collect coverage information
coverageReporters: ["text", "lcov"], // Reporters for coverage
coverageReporters: ['text', 'lcov'], // Reporters for coverage
};

863
node_modules/.package-lock.json generated vendored

File diff suppressed because it is too large Load Diff

867
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -26,7 +26,10 @@
"author": "Carlos Gutierrez <cargdev@gmail.com>",
"scripts": {
"start": "node index.js",
"test": "jest"
"test": "jest",
"lint:prettier": "node check-format.js",
"prettier": "prettier --write . --config .prettierrc",
"prettier:commit": "node prettier-commit.js"
},
"dependencies": {
"chalk": "^4.1.0",
@ -45,7 +48,11 @@
"babel-loader": "^8.2.2",
"css-loader": "^5.1.1",
"dotenv-webpack": "^7.0.2",
"eslint": "^9.9.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.2.1",
"jest": "^29.7.0",
"prettier": "^3.3.3",
"sass-loader": "^11.0.1",
"style-loader": "^2.0.0",
"ts-node": "^10.9.2",

45
prettier-commit.js Normal file
View File

@ -0,0 +1,45 @@
const { execSync } = require('child_process');
(async () => {
const ora = (await import('ora')).default;
const spinner = ora('Running Prettier...').start();
try {
// Run Prettier
execSync('npm run prettier', { stdio: 'inherit' });
spinner.succeed('Prettier has formatted the files.');
// Check for changes
spinner.start('Checking for changes...');
const changes = execSync('git status --porcelain', { encoding: 'utf-8' });
if (changes) {
spinner.succeed('Changes detected.');
// Read the latest commit message to ensure uniqueness
const latestCommitMessage = execSync('git log -1 --pretty=%B', {
encoding: 'utf-8',
}).trim();
// Generate a unique commit message
let commitMessage = 'style: format with prettier';
if (latestCommitMessage.includes(commitMessage)) {
commitMessage = `style: format with prettier ${Date.now()}`;
}
// Add and commit changes
spinner.start('Adding changes to Git...');
execSync('git add .', { stdio: 'inherit' });
spinner.succeed('Changes added to Git.');
spinner.start('Committing changes...');
execSync(`git commit -m "${commitMessage}"`, { stdio: 'inherit' });
spinner.succeed('Changes committed.');
} else {
spinner.info('No changes detected by Prettier.');
}
} catch (error) {
spinner.fail('An error occurred while running Prettier.');
console.error(error);
process.exit(1);
}
})();

View File

@ -7,12 +7,12 @@ describe('printCommandSummary', () => {
it('should print the command summary without errors', () => {
// Mock console.log to capture output
const logSpy = jest.spyOn(console, 'log').mockImplementation(() => {});
printCommandSummary();
// Check that console.log was called
expect(logSpy).toHaveBeenCalled();
// Restore console.log
logSpy.mockRestore();
});

View File

@ -7,12 +7,17 @@ jest.mock('child_process');
describe('installDependencies', () => {
it('should install dependencies without errors', () => {
const execSyncMock = jest.spyOn(child_process, 'execSync').mockImplementation(() => {});
const execSyncMock = jest
.spyOn(child_process, 'execSync')
.mockImplementation(() => {});
installDependencies();
expect(execSyncMock).toHaveBeenCalledWith(expect.stringContaining('npm install'), expect.anything());
expect(execSyncMock).toHaveBeenCalledWith(
expect.stringContaining('npm install'),
expect.anything()
);
execSyncMock.mockRestore();
});
});