# Core Architecture Standards for Parcel
This document outlines the core architecture standards for developing applications using Parcel. These standards promote maintainability, performance, security, and consistency across projects. They are tailored to the latest version of Parcel and emphasize modern best practices.
## 1. Project Structure and Organization
The project structure should be clear, logical, and scalable. This helps development teams navigate the codebase, understand dependencies, and onboard new members quickly.
### 1.1. Standard Directory Layout
* **Do This:** Adopt a consistent directory structure that separates source code, assets, and configuration files.
"""
my-parcel-project/
├── src/ # Source code
│ ├── components/ # Reusable components (e.g., React, Vue)
│ ├── pages/ # Route-specific components or pages
│ ├── assets/ # Static assets (images, fonts, etc.)
│ ├── styles/ # Global styles
│ ├── index.js # Main entry point
│ └── app.js # Primary application logic
├── dist/ # Output directory (generated by Parcel) - should be in .gitignore
├── .parcelrc # Parcel configuration file
├── package.json # Project dependencies and scripts
└── README.md # Project documentation
"""
* **Don't Do This:** Scatter files arbitrarily throughout the project. Avoid a flat structure for any directory (especially "/src").
* **Why:** Consistent structure simplifies navigation, dependency tracking, and build processes. It makes projects easier to understand and maintain over time.
### 1.2. Entry Point
* **Do This:** Define a clear entry point for your application, typically named "index.js" or "app.js" inside the "src/" directory. This is where Parcel will begin its bundling process. Use a plain JavaScript or Typescript file as your main entry and import any frameworks or libraries in that file.
"""javascript
// src/index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './app';
import './styles/global.css'; // Import global styles
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render();
"""
* **Don't Do This:** Have multiple, ambiguous entry points or rely on implicit file loading order. Avoid placing UI framework initialization code inside HTML.
* **Why:** A single, well-defined entry point provides clarity and simplifies the build process.
### 1.3. Component-Based Architecture
* **Do This:** Organize your code into reusable components. Use components from frameworks like React, Vue, or Svelte if you use these technologies. Separate the markup, logic, and styling for each component.
"""javascript
// src/components/Button.js
import React from 'react';
import './Button.css';
function Button({ children, onClick }) {
return (
{children}
);
}
export default Button;
"""
"""css
/* src/components/Button.css */
.button {
background-color: #4CAF50;
border: none;
color: white;
padding: 15px 32px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 16px;
cursor: pointer;
}
"""
* **Don't Do This:** Write monolithic files that mix markup, logic, and styling. Avoid needlessly large components; prefer breaking down into smaller, composable parts.
* **Why:** Component-based architecture promotes code reuse, maintainability, and testability.
### 1.4. Assets Directory
* **Do This:** Store static assets (images, fonts, etc.) in a dedicated "assets/" directory (or subdirectories). Reference these assets using relative paths.
"""html
import React from 'react';
import logo from '../assets/images/logo.png'; // Import the logo
function Logo() {
return ;
}
export default Logo;
"""
* **Don't Do This:** Embed assets directly in code as base64 strings or store them in arbitrary locations.
* **Why:** Organized assets simplify management and optimization. Parcel handles these assets efficiently, optimizing and versioning them.
## 2. Parcel Configuration (.parcelrc)
Parcel's behavior can be customized with a ".parcelrc" file. Use this file to configure transforms, resolvers, and other aspects of the build process. The ".parcelrc" should be placed in the root of your project.
### 2.1. Transformers
* **Do This:** Use transformers to process files with custom logic (e.g., transpiling TypeScript, processing CSS modules). Custom transformations should only be used when you must change how Parcel processes files. Prefer community plugins or built-in Parcel functionality first.
"""json
// .parcelrc
{
"transformers": {
"*.ts": ["@parcel/transformer-typescript-tsc"],
"*.css": ["@parcel/transformer-css", "@parcel/transformer-postcss"]
}
}
"""
* **Don't Do This:** Neglect to configure transformers when using non-standard file types or language features.
* **Why:** Transformers extend Parcel's functionality, allowing it to handle various file types and preprocessing steps.
### 2.2. Resolvers
* **Do This:** Configure resolvers if you need custom module resolution logic (e.g., mapping module names to specific files). Custom resolvers are a very rare need, but can configure where Parcel looks to find files.
"""json
// .parcelrc
{
"resolvers": ["@parcel/resolver-glob", "..."]
}
"""
* **Don't Do This:** Rely on default resolvers if your project has unusual module resolution requirements.
* **Why:** Resolvers control how Parcel finds modules, enabling advanced project structures and dependency management.
### 2.3. Extensions
* **Do This:** Use the "extends" property to inherit configurations from shareable presets. This lets you reuse common configurations across projects.
"""json
// .parcelrc
{
"extends": "@parcel/config-default"
}
"""
* **Don't Do This:** Duplicate configuration across multiple projects.
* **Why:** Extensions promote consistency and reduce boilerplate.
## 3. Module Bundling and Optimization
Parcel excels at module bundling and optimization. Leverage its features to improve performance.
### 3.1. Dynamic Imports
* **Do This:** Use dynamic imports ("import()") for code splitting and lazy loading components or modules only when needed.
"""javascript
// src/app.js
async function loadComponent() {
const { default: Component } = await import('./components/MyComponent');
// Use Component
}
"""
* **Don't Do This:** Load all code upfront unnecessarily, especially for large components or modules only used in specific scenarios.
* **Why:** Dynamic imports reduce initial load time by deferring the loading of non-essential code.
### 3.2. Tree Shaking
* **Do This:** Write modular code that allows Parcel to effectively tree-shake unused code. Ensure that imports are specific and avoid importing the entire library if only some parts are needed.
"""javascript
// Instead of:
import _ from 'lodash';
_.map(items, (item) => item.name);
// Do This (if you only need map):
import map from 'lodash/map';
map(items, (item) => item.name);
"""
* **Don't Do This:** Write code that prevents Parcel from identifying and removing dead code. Avoid importing entire libraries when only a few functions are needed.
* **Why:** Tree shaking reduces bundle size by eliminating unused code.
### 3.3. Asset Optimization
* **Do This:** Optimize images and other assets. While Parcel handles some optimization, use tools like ImageOptim or TinyPNG for further compression. Configure Parcel to handle different image sizes using "" in HTML.
"""html
"""
* **Don't Do This:** Include large, unoptimized assets that increase bundle size and load time.
* **Why:** Optimized assets improve performance by reducing file sizes and load times.
### 3.4. Production Builds
* **Do This:** Use the "parcel build" command for production builds and ensure your environment is set to production to trigger optimizations like minification and tree shaking.
"""bash
parcel build src/index.html --dist-dir ./dist --no-source-maps
"""
* **Don't Do This:** Deploy development builds to production. Avoid including source maps in production.
* **Why:** Production builds apply optimizations that significantly reduce bundle size and improve performance. Removing source maps protects code.
## 4. Code Style and Conventions
Consistent code style improves readability and maintainability.
### 4.1. Linting and Formatting
* **Do This:** Use ESLint and Prettier to enforce consistent code style and catch potential errors.
"""json
// .eslintrc.js
module.exports = {
"extends": ["eslint:recommended", "plugin:react/recommended"],
"parserOptions": {
"ecmaVersion": 2023,
"sourceType": "module",
"ecmaFeatures": {
"jsx": true
}
},
"env": {
"browser": true,
"es6": true,
"node": true
},
"rules": {
"no-unused-vars": "warn",
"react/prop-types": "off"
}
};
"""
"""json
// .prettierrc.js
module.exports = {
semi: true,
trailingComma: "es5",
singleQuote: true,
printWidth: 120,
tabWidth: 2,
};
"""
* **Don't Do This:** Ignore linting and formatting errors.
* **Why:** Consistent code style improves readability and reduces the likelihood of errors. Automated formatting reduces time spent on style issues during code reviews.
### 4.2. Naming Conventions
* **Do This:** Use descriptive and consistent naming conventions for variables, functions, and components. Prefer camelCase for variables and function names, PascalCase for React components, and UPPER_SNAKE_CASE for constants.
"""javascript
// Good
const userProfile = { ... };
function calculateTotal(amount, taxRate) { ... }
function UserProfileComponent() { ... }
const MAX_USERS = 100;
// Bad
const a = { ... }; // Non-descriptive
function calc(x, y) { ... } // Ambiguous
"""
* **Don't Do This:** Use short, cryptic, or inconsistent names.
* **Why:** Clear naming improves code readability and understandability.
### 4.3. Commenting
* **Do This:** Write clear and concise comments to explain complex logic or non-obvious code. Document component props and function parameters.
"""javascript
/**
* Calculates the total price including tax.
* @param {number} amount - The base amount.
* @param {number} taxRate - The tax rate (e.g., 0.05 for 5%).
* @returns {number} The total price.
*/
function calculateTotal(amount, taxRate) {
// Calculate the tax amount
const tax = amount * taxRate;
return amount + tax;
}
"""
* **Don't Do This:** Over-comment obvious code or under-comment complex code.
* **Why:** Comments improve code maintainability and help other developers understand the code.
## 5. Error Handling and Logging
Robust error handling and logging are essential for debugging and maintaining applications.
### 5.1. Error Boundaries
* **Do This:** Use error boundaries in React to catch JavaScript errors in component trees.
"""javascript
// src/components/ErrorBoundary.js
import React, { Component } from 'react';
class ErrorBoundary extends Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(error) {
return { hasError: true };
}
componentDidCatch(error, errorInfo) {
console.error(error, errorInfo); // Log the error
}
render() {
if (this.state.hasError) {
return Something went wrong.;
}
return this.props.children;
}
}
export default ErrorBoundary;
"""
"""javascript
// Usage in a component
import ErrorBoundary from './ErrorBoundary';
function MyComponent() {
return (
{/* Component that might throw an error */}
);
}
"""
* **Don't Do This:** Allow errors to crash the entire application.
* **Why:** Error boundaries prevent errors in one component from affecting the entire application.
### 5.2. Logging
* **Do This:** Use a logging library (e.g., "console.log", "winston", "Logger") to log important events and errors. Include sufficient information for debugging. In production, use a centralized logging system.
"""javascript
// Example with console.log
try {
// Some code that might throw an error
} catch (error) {
console.error('An error occurred:', error);
}
"""
* **Don't Do This:** Over-log in production or log sensitive information. Remove debugging logs before deploying to production.
* **Why:** Logging provides valuable insights into application behavior and helps diagnose issues.
## 6. Security Best Practices
Implement security best practices to protect your application from vulnerabilities.
### 6.1. Dependency Management
* **Do This:** Regularly audit and update dependencies using "npm audit" or "yarn audit" to identify and fix vulnerabilities. Use tools like Snyk, or Dependabot integrated into version control, especially GitHub, that automatically open pull requests to keep dependencies up to date
"""bash
npm audit
"""
* **Don't Do This:** Use outdated dependencies with known vulnerabilities. Ignore audit warnings.
* **Why:** Vulnerable dependencies can expose your application to security risks.
### 6.2. Input Validation
* **Do This:** Validate all user inputs to prevent injection attacks (e.g., XSS, SQL injection). Sanitize user inputs before displaying them.
"""javascript
// Example (basic)
function sanitizeInput(input) {
return input.replace(//g, '>');
}
const userInput = '';
const sanitizedInput = sanitizeInput(userInput);
// Use sanitizedInput safely
"""
* **Don't Do This:** Trust user inputs without validation.
* **Why:** Input validation prevents malicious users from injecting harmful code into your application.
### 6.3. Environment Variables
* **Do This:** Store sensitive information (e.g., API keys, database passwords) in environment variables. Use tools like "dotenv" or "cross-env" to manage environment variables. Never commit such vars to source control.. Use a ".env.example" or ".env.sample" file to document which vars are needed.
"""javascript
// Accessing environment variables
const apiKey = process.env.API_KEY;
"""
* **Don't Do This:** Hardcode sensitive information in your code or configuration files.
* **Why:** Environment variables prevent sensitive information from being exposed in code repositories.
## 7. Testing
Implement comprehensive testing strategies to ensure the quality and reliability of your application.
### 7.1. Unit Tests
* **Do This:** Write unit tests for individual components and functions to verify their correctness. Use testing frameworks like Jest or Mocha.
"""javascript
// Example with Jest
test('adds 1 + 2 to equal 3', () => {
expect(1 + 2).toBe(3);
});
"""
* **Don't Do This:** Neglect to write unit tests for critical components and functions.
* **Why:** Unit tests ensure that individual parts of the code function correctly.
### 7.2. Integration Tests
* **Do This:** Write integration tests to verify the interaction between different components or modules. Use frameworks like Cypress or Puppeteer for end-to-end integration testing.
* **Don't Do This:** Rely solely on unit tests without verifying the integration of different parts of the application.
* **Why:** Integration tests ensure that different parts of the code work together correctly.
### 7.3. Code Coverage
* **Do This:** Aim for high code coverage to ensure that most of your code is tested. Use code coverage tools to measure the percentage of code covered by tests.
* **Don't Do This:** Equate high code coverage with high-quality tests. Focus on testing critical functionality and edge cases.
* **Why:** Code coverage provides a metric for measuring the extent to which your code is tested.
By adhering to these core architectural standards, development teams can build robust, maintainable, and scalable Parcel applications. This consistent approach ensures code quality, reduces technical debt, and accelerates development cycles.
danielsogl
Created Mar 6, 2025
This guide explains how to effectively use .clinerules
with Cline, the AI-powered coding assistant.
The .clinerules
file is a powerful configuration file that helps Cline understand your project's requirements, coding standards, and constraints. When placed in your project's root directory, it automatically guides Cline's behavior and ensures consistency across your codebase.
Place the .clinerules
file in your project's root directory. Cline automatically detects and follows these rules for all files within the project.
# Project Overview project: name: 'Your Project Name' description: 'Brief project description' stack: - technology: 'Framework/Language' version: 'X.Y.Z' - technology: 'Database' version: 'X.Y.Z'
# Code Standards standards: style: - 'Use consistent indentation (2 spaces)' - 'Follow language-specific naming conventions' documentation: - 'Include JSDoc comments for all functions' - 'Maintain up-to-date README files' testing: - 'Write unit tests for all new features' - 'Maintain minimum 80% code coverage'
# Security Guidelines security: authentication: - 'Implement proper token validation' - 'Use environment variables for secrets' dataProtection: - 'Sanitize all user inputs' - 'Implement proper error handling'
Be Specific
Maintain Organization
Regular Updates
# Common Patterns Example patterns: components: - pattern: 'Use functional components by default' - pattern: 'Implement error boundaries for component trees' stateManagement: - pattern: 'Use React Query for server state' - pattern: 'Implement proper loading states'
Commit the Rules
.clinerules
in version controlTeam Collaboration
Rules Not Being Applied
Conflicting Rules
Performance Considerations
# Basic .clinerules Example project: name: 'Web Application' type: 'Next.js Frontend' standards: - 'Use TypeScript for all new code' - 'Follow React best practices' - 'Implement proper error handling' testing: unit: - 'Jest for unit tests' - 'React Testing Library for components' e2e: - 'Cypress for end-to-end testing' documentation: required: - 'README.md in each major directory' - 'JSDoc comments for public APIs' - 'Changelog updates for all changes'
# Advanced .clinerules Example project: name: 'Enterprise Application' compliance: - 'GDPR requirements' - 'WCAG 2.1 AA accessibility' architecture: patterns: - 'Clean Architecture principles' - 'Domain-Driven Design concepts' security: requirements: - 'OAuth 2.0 authentication' - 'Rate limiting on all APIs' - 'Input validation with Zod'
# Security Best Practices Standards for Parcel This document outlines the security best practices for developing applications with Parcel. Adhering to these standards will help mitigate common vulnerabilities and ensure a more secure application. These standards are designed to be used as a guide for developers and as context for AI coding assistants. ## 1. Input Validation and Sanitization ### 1.1. Why Input Validation Matters Input validation is critical for preventing various attacks, including Cross-Site Scripting (XSS), SQL Injection, and Command Injection. By validating and sanitizing all inputs, you ensure that only expected data is processed by your application. ### 1.2. General Principles * **Do This:** Validate all user inputs, including form fields, URL parameters, and API requests. * **Do This:** Use strict validation rules that define the allowed data types, formats, and lengths. * **Do This:** Sanitize inputs to remove or escape potentially harmful characters. * **Don't Do This:** Rely solely on client-side validation, as it can be bypassed. Always validate inputs on the server-side. * **Don't Do This:** Trust data from external sources without validation. ### 1.3. Implementing Input Validation in Parcel Parcel typically serves static assets and bundles JavaScript. Input validation generally occurs within application code (e.g., React, Vue, Svelte) that Parcel bundles. **Example: Validating Form Input in a React Component** """jsx import React, { useState } from 'react'; import DOMPurify from 'dompurify'; function MyFormComponent() { const [userInput, setUserInput] = useState(''); const [validatedInput, setValidatedInput] = useState(''); const handleChange = (event) => { setUserInput(event.target.value); }; const handleSubmit = (event) => { event.preventDefault(); // 1. Validation if (userInput.length > 100) { alert('Input too long!'); return; } const isValid = /^[a-zA-Z0-9\s]*$/.test(userInput); // Only allow alphanumeric and space if (!isValid) { alert('Invalid characters!'); return; } // 2. Sanitization using DOMPurify (for demonstration if rendering HTML) const sanitizedInput = DOMPurify.sanitize(userInput); setValidatedInput(sanitizedInput); console.log('Processed input:', sanitizedInput); // In a real application: you would send "sanitizedInput" to your backend API. }; return ( <form onSubmit={handleSubmit}> <label> Enter text: <input type="text" value={userInput} onChange={handleChange} /> </label> <button type="submit">Submit</button> {validatedInput && <p>You entered: {validatedInput}</p>} </form> ); } export default MyFormComponent; """ **Explanation:** 1. **Validation:** The example validates the input length and allowed characters. More complex validations (e.g., email format, date ranges) can be added. 2. **Sanitization:** "DOMPurify" is used to sanitize the input in case HTML is rendered. This is crucial to prevent XSS attacks. **Note:** When sending data via APIs, validate the data *again* on your backend. Never trust client-side validation alone. ### 1.4. Common Anti-Patterns * **Inadequate Length Checks:** Failing to limit the length of input fields can lead to buffer overflows or denial-of-service attacks. * **Insufficient Pattern Matching:** Using weak regular expressions for validation can allow malicious input to pass through. * **Ignoring Error Handling:** Not handling validation errors gracefully can provide attackers with information about your validation logic. ## 2. Cross-Site Scripting (XSS) Prevention ### 2.1. Understanding XSS XSS attacks occur when malicious scripts are injected into web pages viewed by other users. Parcel, being a bundler, processes and serves the JavaScript that ultimately executes in the user's browser, making XSS prevention crucial. ### 2.2. Strategies for XSS Prevention * **Do This:** Use Content Security Policy (CSP) to control the resources that the browser is allowed to load. * **Do This:** Employ output encoding to escape special characters when rendering user-supplied data. * **Do This:** Sanitize HTML inputs with libraries like "DOMPurify" before rendering them. * **Don't Do This:** Directly insert user input into HTML without encoding or sanitization. * **Don't Do This:** Disable CSP or reduce security settings without a very good reason. ### 2.3. Implementing CSP in Parcel CSP can be configured through HTTP headers or meta tags. Configuring it via HTTP headers is recommended for enhanced security. Parcel doesn't directly handle HTTP headers (that's your web server's job), but it is important you configure your webserver to send a strong CSP header. **Example: CSP Header (Example - adjust for your specific needs):** """ Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' https://example.com; style-src 'self' https://example.com; img-src 'self' data:; font-src 'self'; """ **Explanation:** * "default-src" "'self'": Specifies that resources should only be loaded from the same origin by default. * "script-src" "'self' 'unsafe-inline' https://example.com": Allows scripts from the same origin, inline scripts (use sparingly, try to avoid "unsafe-inline" if possible), and scripts from "https://example.com". Using a nonce is better than unsafe-inline, if possible. * "style-src" "'self' https://example.com": Allows styles from the same origin and "https://example.com". * "img-src" "'self' data:": Allows images from the same origin and data URIs. * "font-src" "'self'": Allows fonts from the same origin. **Modern Approach: CSP Nonces** A nonce ("number used once") is a cryptographically random token generated for each request. You can add the nonce to your script tags, so only scripts with the correct nonce will be executed. **Example: Integrating Nonces with a Backend:** Let's assume you have a backend that can generate a nonce, and you're using a templating engine to generate your HTML. 1. **Generate a Nonce on the Server:** * In your backend (e.g., Node.js with Express), generate a cryptographically secure random string: """javascript const crypto = require('crypto'); app.get('/', (req, res) => { const nonce = crypto.randomBytes(16).toString('hex'); res.render('index', { nonce }); }); """ 2. **Pass the Nonce to Your Template:** * Pass the nonce to your HTML template. 3. **Use the Nonce in Your HTML:** """html <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>CSP Example</title> <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'nonce-{{nonce}}'; style-src 'self'"> </head> <body> <h1>Hello, world!</h1> <script nonce="{{nonce}}" src="/dist/index.js"></script> </body> </html> """ 4. **Configure Parcel to Not Remove Nonce Attribute** Parcel and other bundlers might remove attributes they consider non-standard. You may need to configure parcel to keep the "nonce" attribute on script tags using the "html-minifier-terser" if you are using HTML minification. Check your configuration for your specific version of Parcel. **Important:** Ensure your server invalidates the nonce after each request to prevent replay attacks. ### 2.4. Avoiding "unsafe-inline" While the examples show "'unsafe-inline'", it's generally discouraged. Inline scripts and styles are more vulnerable to XSS attacks. Whenever possible: * Move JavaScript code into separate files. * Use CSS classes instead of inline styles. * Consider using a templating engine that automatically escapes variables for HTML context. ### 2.5 Sanitizing output with DOMPurify Often you will need to display user input. In these cases it is CRITICAL to sanitize the input before displaying it to prevent XSS attacks. """jsx import React from 'react'; import DOMPurify from 'dompurify'; function DisplayUserInput({ userInput }) { const sanitizedHTML = { __html: DOMPurify.sanitize(userInput) }; return <div dangerouslySetInnerHTML={sanitizedHTML} />; } export default DisplayUserInput; """ **Explanation:** * "DOMPurify.sanitize(userInput)": This makes sure that the output is safe. * "dangerouslySetInnerHTML": Use this with CAUTION. Only use after you are certain the input has been properly sanitized. ## 3. Dependency Management ### 3.1. Why Dependency Security Matters Modern JavaScript projects rely heavily on third-party libraries. Vulnerabilities in these dependencies can expose your application to security risks. ### 3.2. Best Practices for Dependency Management * **Do This:** Use a package manager like "npm" or "yarn" to manage dependencies. * **Do This:** Regularly update dependencies to patch known vulnerabilities. * **Do This:** Use a dependency scanning tool to identify vulnerabilities in your dependencies. * **Do This:** Review the security policies of the dependencies you use. * **Don't Do This:** Use outdated or unmaintained dependencies. * **Don't Do This:** Ignore security alerts from dependency scanning tools. ### 3.3. Dependency Scanning with "npm audit" "npm" provides a built-in command, "npm audit", to scan your project's dependencies for vulnerabilities. **Example: Running "npm audit"** """bash npm audit """ This command analyzes your "package-lock.json" or "yarn.lock" file and reports any known vulnerabilities. **Example Output:** """ === npm audit security report === found 5 vulnerabilities (3 moderate, 2 high) run "npm audit fix" to fix some of them, or commit changes to npm-shrinkwrap.json to install only safe updates ... """ ### 3.4. Using "npm audit fix" "npm audit fix" attempts to automatically update vulnerable dependencies to secure versions. **Example: Running "npm audit fix"** """bash npm audit fix """ **Important Considerations:** * "npm audit fix" might introduce breaking changes. Test your application thoroughly after running this command. * Some vulnerabilities cannot be automatically fixed. In these cases, you may need to manually update or replace the vulnerable dependency. ### 3.5. Using Snyk Snyk is a popular tool that can be integrated into CI/CD pipelines to automatically identify and fix vulnerabilties. ### 3.6. Pinning Dependencies and Using Lockfiles * **Do This:** Always use a lockfile (package-lock.json or yarn.lock). This ensures that everyone on your team (and production environments) uses the exact same versions of dependencies. * **Do This:** Consider pinning dependencies to specific patch versions to prevent unexpected issues from minor or major updates. However, be careful to update regularly to keep secure. Use semantic versioning ranges ("^" or "~") to allow updates while minimizing risk. ## 4. Secure Configuration ### 4.1. The Importance of Secure Configuration Configuration settings can significantly impact the security of your application. Storing sensitive data securely and configuring your application properly is vital. ### 4.2. Best Practices for Secure Configuration * **Do This:** Use environment variables to store sensitive information, such as API keys and database credentials. * **Do This:** Avoid hardcoding sensitive data in your source code. * **Do This:** Use a configuration management tool to manage environment-specific settings. * **Do This:** Restrict access to configuration files and environment variables. * **Don't Do This:** Commit sensitive data to version control. * **Don't Do This:** Expose sensitive configuration settings to the client-side code. ### 4.3. Using Environment Variables with Parcel Parcel can use environment variables defined in a ".env" file or system environment variables. Parcel automatically injects these variables into the bundled code at build time. **Example: Creating a ".env" file** """ API_KEY=YOUR_API_KEY DATABASE_URL=YOUR_DATABASE_URL """ **Example: Accessing Environment Variables in JavaScript** """javascript const apiKey = process.env.API_KEY; const databaseUrl = process.env.DATABASE_URL; console.log('API Key:', apiKey); console.log('Database URL:', databaseUrl); """ **Explanation:** * Parcel automatically replaces "process.env.API_KEY" and "process.env.DATABASE_URL" with the actual values from the ".env" file or system environment variables at build time. * **Important:** Environment variables are embedded in your bundled output. *Never* store secrets intended only for backend services in environment variables used in Parcel. They will be exposed to anyone who can view the JavaScript in the browser. ### 4.4. ".env" File Management * **Do This:** Add your ".env" file to your ".gitignore" file to prevent committing sensitive data to version control. * **Do This:** Use a tool like "dotenv" to load environment variables into "process.env" during development. **Example: Using "dotenv"** 1. Install "dotenv": """bash npm install dotenv """ 2. Require and configure "dotenv" at the top of your main application file: """javascript require('dotenv').config(); const apiKey = process.env.API_KEY; console.log('API Key:', apiKey); """ ## 5. Secure Data Handling ### 5.1. Sensitive Data Storage * **Do This:** Avoid storing sensitive data on the client-side whenever possible. If you must store it, encrypt it and use secure storage mechanisms (e.g., the browser's built-in Crypto API or secure cookies with appropriate flags). Note that even encrypted data is vulnerable if the encryption key is exposed. * **Do This:** Never store passwords or API keys directly in client-side storage. * **Don't Do This:** Rely on "localStorage" or "sessionStorage" for sensitive data, as they are easily accessible to JavaScript code. ### 5.2. Data Transmission * **Do This:** Always use HTTPS to encrypt data in transit between the client and the server. * **Do This:** Use strong TLS configurations and keep your SSL/TLS certificates up to date. ### 5.3. Secure Cookies * **Do This:** Set the "Secure" attribute on cookies to ensure they are only transmitted over HTTPS. * **Do This:** Set the "HttpOnly" attribute to prevent client-side scripts from accessing cookies. * **Do This:** Set the "SameSite" attribute to "Strict" or "Lax" to protect against Cross-Site Request Forgery (CSRF) attacks. **Example: Setting Secure Cookie Attributes on the Server (Example uses Node.js with Express):** """javascript app.get('/set-cookie', (req, res) => { res.cookie('myCookie', 'cookieValue', { secure: true, // Only send over HTTPS httpOnly: true, // Prevent client-side access sameSite: 'strict', // Protect against CSRF }); res.send('Cookie set!'); }); """ ## 6. Regular Security Audits and Testing ### 6.1. Why Audits and Testing Matter Regular security audits and testing are crucial for identifying and addressing vulnerabilities in your application. ### 6.2. Strategies for Audits and Testing * **Do This:** Perform regular security audits of your code and infrastructure. * **Do This:** Conduct penetration testing to simulate real-world attacks. * **Do This:** Use automated security scanning tools to identify common vulnerabilities. * **Do This:** Stay up-to-date with the latest security threats and best practices. ### 6.3. Types of Security Testing * **Static Analysis Security Testing (SAST):** Analyzing source code for potential vulnerabilities without executing the code. Examples include ESLint with security plugins. * **Dynamic Analysis Security Testing (DAST):** Testing the application in runtime to identify vulnerabilities. * **Penetration Testing:** Simulating real-world attacks to identify vulnerabilities. * **Dependency Scanning:** Identifying vulnerabilities in third-party dependencies. ## 7. Error Handling and Logging ### 7.1. Secure Error Handling * **Do This:** Implement proper error handling to prevent sensitive information from being exposed in error messages. * **Do This:** Log errors and exceptions to a secure location for debugging purposes. Do *not* log sensitive user data. * **Don't Do This:** Display detailed error messages to users in production, as they can reveal information about your application's internals. ### 7.2. Secure Logging * **Do This:** Log important events, such as authentication attempts, access control decisions, and data modifications. * **Do This:** Protect log files from unauthorized access. * **Do This:** Use structured logging formats to facilitate analysis. However, make SURE no sensitive data is logged. * **Don't Do This:** Store sensitive data in log files. ## 8. Subresource Integrity (SRI) ### 8.1. Why SRI Matters Subresource Integrity (SRI) allows browsers to verify that files fetched from CDNs or other third-party sources haven't been tampered with. ### 8.2. Implementing SRI * **Do This:** Generate SRI hashes for all external resources. * **Do This:** Include the "integrity" attribute in your "<script>" and "<link>" tags. **Example:** """html <script src="https://example.com/script.js" integrity="sha384-EXAMPLE_HASH" crossorigin="anonymous" ></script> """ **Explanation:** * "integrity": Specifies the expected SHA-256, SHA-384, or SHA-512 hash of the resource. * "crossorigin="anonymous"": Indicates that the resource should be fetched without sending credentials. ## 9. Secure Code Review ### 9.1. Why Code Review Matters Code reviews are an essential part of the software development process. They help identify potential security vulnerabilities and ensure that code meets security standards. ### 9.2. Best Practices for Code Review * **Do This:** Conduct regular code reviews by experienced developers. * **Do This:** Focus on security aspects during code reviews. * **Do This:** Use code review tools to automate the review process. * **Do This:** Document code review findings and track their resolution. ## 10. Parcel Specific Considerations ### 10.1. Plugin Security If you are using or writing Parcel plugins, ensure these plugins come from reputable sources. Malicious plugins can inject code or modify your build process in harmful ways. Review plugin code when possible, and keep plugins updated. ### 10.2. Output Directory Security Ensure that your Parcel output directory (usually "dist") is properly secured on your server. Prevent unauthorized access to these files, as they contain your application code. Server configuration (e.g., Nginx, Apache) plays a critical role here. ### 10.3. Be Aware of Build Time Dependencies Parcel uses many dependencies at build time (e.g., Babel, Terser). Follow dependency management best practices to secure these as well, since vulnerabilities in these build-time tools could lead to compromised builds. This detailed guide provides a solid foundation for developing secure Parcel applications. Remember to stay updated with the latest security best practices and adapt these guidelines to your specific project needs. Always prioritize security as an integral part of your development process.
# Tooling and Ecosystem Standards for Parcel This document outlines the coding standards for Parcel focusing on best practices for tooling and the Parcel ecosystem. Adhering to these standards ensures maintainability, performance, and security in Parcel-based projects. These guidelines will help developers write clean, efficient, and robust code leveraging modern tooling practices. ## 1. Linters and Formatters Consistent code style is crucial for team collaboration and code maintainability. Linters and formatters ensure code adheres to a predefined style guide, catching potential errors early in the development cycle. ### 1.1 ESLint ESLint identifies and reports on patterns found in ECMAScript/JavaScript code. Parcel integrates seamlessly with ESLint. **Standard:** Use ESLint with a well-defined configuration (e.g., Airbnb, Standard, or a custom one) to enforce code quality and style. **Why:** Consistent code style improves readability and reduces errors. **Do This:** 1. Install ESLint and a preferred configuration (e.g., "eslint-config-airbnb-base"). """bash npm install --save-dev eslint eslint-config-airbnb-base eslint-plugin-import """ 2. Create an ".eslintrc.js" file in the project root: """javascript // .eslintrc.js module.exports = { extends: 'airbnb-base', env: { browser: true, node: true, es6: true, }, rules: { 'no-console': 'warn', 'import/prefer-default-export': 'off', 'no-unused-vars': 'warn' }, }; """ 3. Add an ESLint script to "package.json": """json { "scripts": { "lint": "eslint src/**/*.js" } } """ 4. Run ESLint: """bash npm run lint """ **Don't Do This:** * Skip using ESLint, leading to inconsistent code style and potential errors. * Commit code with ESLint warnings or errors. Resolve all issues before committing. **Anti-pattern:** Disabling ESLint rules without a strong justification. If a rule consistently conflicts with project needs, re-evaluate the configuration instead of repeatedly disabling the rule. ### 1.2 Prettier Prettier is an opinionated code formatter that enforces a consistent style across your entire codebase. **Standard:** Use Prettier alongside ESLint to automate code formatting. **Why:** Automated formatting saves time and ensures a consistent appearance of the code. **Do This:** 1. Install Prettier: """bash npm install --save-dev prettier """ 2. Create a ".prettierrc.js" file in the project root to configure Prettier: """javascript // .prettierrc.js module.exports = { semi: true, trailingComma: 'es5', singleQuote: true, printWidth: 120, tabWidth: 2, }; """ 3. Add a Prettier script to "package.json": """json { "scripts": { "format": "prettier --write src/**/*.js" } } """ 4. Integrate Prettier with ESLint to avoid conflicts: """bash npm install --save-dev eslint-config-prettier eslint-plugin-prettier """ 5. Update ".eslintrc.js" """javascript // .eslintrc.js module.exports = { extends: [ 'airbnb-base', 'plugin:prettier/recommended' ], env: { browser: true, node: true, es6: true, }, rules: { 'no-console': 'warn', 'import/prefer-default-export': 'off', 'no-unused-vars': 'warn' }, plugins: ['prettier'], }; """ 6. Run Prettier """bash npm run format """ **Don't Do This:** * Skip code formatting, leading to inconsistent and hard-to-read code. * Manually format code instead of leveraging automated tools. * Configure Prettier in a way that conflicts with ESLint rules without proper integration. Failing to integrate Prettier and ESLint will result in one tool undoing the changes of another **Anti-pattern:** Ignoring Prettier's warnings. This leads to styling errors accumulating over time, which can increase the technical debt ### 1.3 Husky and Lint-Staged Husky and Lint-Staged are tools to run linters and formatters on staged files before committing. **Standard:** Use Husky and Lint-Staged to ensure that only clean and formatted code is committed. **Why:** Prevents committing code that violates linting or formatting rules automatically. **Do This:** 1. Install Husky and Lint-Staged: """bash npm install --save-dev husky lint-staged """ 2. Configure Husky to run Lint-Staged before commit: """json // package.json { "husky": { "hooks": { "pre-commit": "lint-staged" } }, "lint-staged": { "src/**/*.js": [ "eslint --fix", "prettier --write", "git add" ] } } """ Alternatively, use the "husky install" command and ".husky/pre-commit" file: """bash npx husky install npm set-script prepare "husky install" npm run prepare """ Then configure the ".husky/pre-commit" file """bash #!/usr/bin/env sh . "$(dirname -- "$0")/_/husky.sh" npx lint-staged """ **Don't Do This:** * Commit code without running linting and formatting checks. * Bypass Husky and Lint-Staged checks, which can introduce code quality issues. **Anti-pattern:** Configuring Husky and Lint-Staged without ESLint or Prettier properly configured. This can cause the git add command to endlessly loop. Ensure your lint and format scripts are configured before continuing ## 2. Debugging and Profiling Tools Effective debugging and profiling are essential for optimizing performance and identifying issues in Parcel applications. ### 2.1 Browser Developer Tools Modern browsers offer excellent developer tools for debugging JavaScript, inspecting network requests, and analyzing performance. **Standard:** Use browser developer tools extensively for debugging and performance analysis. **Why:** Provides real-time insights into the application's behavior and performance. **Do This:** * Use the "Sources" panel to set breakpoints and step through code. * Use the "Network" panel to analyze HTTP requests and responses. * Use the "Performance" panel to profile the application's performance, identify bottlenecks, and optimize rendering. * Use the "Console" to log messages. **Don't Do This:** * Rely solely on "console.log" for debugging complex issues. Use breakpoints to inspect variables and execution flow. * Ignore performance bottlenecks identified by the browser's performance profiling tools. * Skip inspecting network requests when troubleshooting data loading issues. **Example (Performance Profiling):** 1. Open the browser's developer tools. 2. Navigate to the "Performance" panel. 3. Click the "Record" button to start profiling. 4. Interact with the application to reproduce the performance issue. 5. Click the "Stop" button to stop profiling. 6. Analyze the performance timeline to identify slow functions or rendering bottlenecks. ### 2.2 Parcel's Diagnostic Tools Parcel provides built-in diagnostic tools for analyzing build performance and identifying issues during the bundling process. **Standard:** Leverage Parcel's diagnostic tools to optimize build times and troubleshoot bundling errors. **Why:** Provides insights into Parcel's build process and helps identify configuration issues. **Do This:** * Use the "--log-level verbose" or "--log-level debug" flag when running Parcel to get detailed output about the build process. """bash parcel src/index.html --log-level verbose """ * Analyze the output to identify slow modules, large assets, and other performance bottlenecks. * Use Parcel's cache invalidation reports to understand why modules are being rebuilt. * Consult the Parcel documentation for detailed information on diagnostic tools and options. **Don't Do This:** * Ignore error messages or warnings during the build process. * Fail to investigate slow build times, which can indicate configuration issues or inefficient code. **Example (Verbose Logging):** Running Parcel with "--log-level verbose" will print detailed information about the build process, including: * Module resolution * Asset transformation * Bundling and optimization phases ### 2.3 Source Maps Source maps map the transformed code back to the original source code, providing a better debugging experience. **Standard:** Enable source maps for all non-production builds. **Why:** Makes debugging significantly easier as developers can debug the original source code instead of the transformed code. **Do This:** 1. Ensure that source maps are enabled in your Parcel configuration (usually enabled by default in development mode). """bash parcel src/index.html --no-optimize --source-maps """ 2. Configure your browser to load source maps automatically. 3. Use the browser's debugger to set breakpoints in the original source code. **Don't Do This:** * Disable source maps in development builds. * Include source maps in production builds, which can expose sensitive source code. **Anti-pattern:** Omitting source maps during development makes debugging much harder and time-consuming, increasing the overall development time and frustration. ### 2.4 Accessibility Checker Parcel offers Accessibility Checker as development tool. **Standard:** Use accessibility checker in your projects. **Why:** Find accessibility issues in your code **Do This:** Review output from accessibility checker and fix the issues. **Don't Do This:** Ignore the results of accessibility checker. ## 3. Dependency Management Effective dependency management is critical for maintaining a stable, secure, and performant Parcel application. ### 3.1 Semantic Versioning (SemVer) Semantic versioning provides a clear and consistent way to manage dependencies and understand the potential impact of updates. **Standard:** Use semantic versioning (SemVer) for all dependencies and specify version ranges using the caret ("^") or tilde ("~") operators. **Why:** SemVer provides clarity about the nature and potential impact of updates. Using "^" allows updates within the same major version. **Do This:** * Use "npm install <package> --save" or "yarn add <package>" to add dependencies with the caret operator by default. """json // package.json { "dependencies": { "react": "^18.0.0" } } """ * Use the tilde operator ("~") to allow updates within the same minor version. """json // package.json { "dependencies": { "lodash": "~4.17.0" } } """ * Use exact versions only when absolutely necessary (e.g., for critical security patches or when a dependency has unstable SemVer practices). **Don't Do This:** * Use the "*" wildcard for dependencies, as this can lead to unpredictable behavior and breaking changes. * Use outdated or vulnerable dependencies without regular updates and security audits. * Update dependencies without testing the application thoroughly, which can introduce regressions. * Avoid specifying version ranges, which makes it harder to update and maintain the project. **Anti-pattern:** Using the wildcard will likely introduce unpredictable and unstable behavior in the application. ### 3.2 Dependency Auditing Regularly auditing dependencies for known vulnerabilities helps prevent security breaches and maintain a secure application. **Standard:** Regularly audit dependencies using "npm audit" or "yarn audit". **Why:** Identifies and mitigates security vulnerabilities in dependencies. **Do This:** * Run "npm audit" or "yarn audit" regularly (e.g., as part of the CI/CD pipeline). """bash npm audit # OR yarn audit """ * Review the audit report and update vulnerable dependencies to the recommended versions. * If a vulnerability cannot be fixed by updating, consider alternative dependencies or apply patches. **Don't Do This:** * Ignore dependency audit reports, which can lead to unaddressed security vulnerabilities. * Disable dependency auditing, as this can expose the application to known security risks. **Anti-pattern:** Ignoring audit warnings is a risky practice that increases the potential for security breaches. ### 3.3 Lockfiles Lockfiles ensure that all team members and deployment environments use the same dependency versions, preventing inconsistencies and unexpected behavior. **Standard:** Always commit the "package-lock.json" (for npm) or "yarn.lock" (for Yarn) file to the repository. **Why:** Ensures consistency across environments and prevents unexpected behavior due to version mismatches. **Do This:** * Ensure that the "package-lock.json" or "yarn.lock" file is always up-to-date and committed to the repository. * Never manually edit the lockfile. Use "npm install" or "yarn add" to update dependencies. * Use the "npm ci" or "yarn install --frozen-lockfile" command in CI/CD environments to ensure that the exact dependency versions from the lockfile are installed. **Don't Do This:** * Fail to commit lockfiles to the repository, leading to inconsistent dependency versions. * Manually edit lockfiles, which can cause conflicts and break dependency resolution. * Use different package managers (e.g., npm and Yarn) in the same project, which can lead to lockfile conflicts. **Anti-pattern:** Inconsistent dependency versions are a common source of bugs and deployment issues. Always use package lockfiles. ### 3.4 Optimizing Dependencies Reducing the size and number of dependencies improves build times and reduces the application's bundle size, leading to better performance. **Standard:** Regularly review dependencies and remove unused or redundant packages. **Why:** Smaller bundle sizes improve load times and reduce the attack surface. **Do This:** * Use tools like "npm prune" or "yarn autoclean" to remove unnecessary dependencies. """bash npm prune --production # OR yarn autoclean """ * Use tools like "bundle analyzer" or "webpack-bundle-analyzer" (compatible with Parcel through plugins) to identify large or redundant dependencies. * Consider using lighter alternatives for large dependencies if possible. * Use Parcel's tree shaking and code splitting features to eliminate dead code and reduce bundle sizes. **Don't Do This:** * Include unnecessary dependencies, which increases bundle size and build times. * Ignore large or redundant dependencies identified by bundle analysis tools. **Anti-pattern:** Bloated "node_modules" directories can significantly slow down build times. Review and optimize project dependencies regularly. ## 4. Code Splitting and Lazy Loading Code splitting and lazy loading are critical techniques for improving the performance of large Parcel applications by reducing the initial load time. Parcel supports code splitting out of the box using dynamic imports. ### 4.1 Dynamic Imports Dynamic imports allow you to load modules on demand, deferring the loading of non-critical code until it is needed. **Standard:** Use dynamic imports for non-critical modules, such as components that are only rendered under certain conditions or modules that are used in specific sections of the application. **Why:** Reduces initial load time by loading code only when it is needed. **Do This:** * Use the "import()" syntax to dynamically load modules. """javascript async function loadComponent() { const module = await import('./MyComponent'); const MyComponent = module.default; // Use MyComponent return <MyComponent />; } """ * Use dynamic imports within event handlers or conditional statements to load modules only when needed. """javascript import React, { useState } from 'react'; function MyComponent() { const [showModal, setShowModal] = useState(false); const openModal = async () => { const { default: Modal } = await import('./Modal'); setShowModal(<Modal onClose={() => setShowModal(false)} />); }; return ( <div> <button onClick={openModal}>Open Modal</button> {showModal} </div> ); } export default MyComponent; """ * Leverage Parcel's automatic code splitting with dynamic imports. Parcel detects these imports and automatically creates separate bundles for them. **Don't Do This:** * Load all modules upfront, which increases the initial load time. * Overuse dynamic imports, which can increase the complexity of the application and introduce unnecessary network requests. **Anti-pattern:** Avoid loading the entire application code at once. Use code splitting to improve initial load times. ### 4.2 Lazy Loading Lazy loading is a technique for deferring the loading of resources, such as images and videos, until they are visible in the viewport. **Standard:** Use lazy loading for images and other resources that are not immediately visible in the viewport. **Why:** Reduces initial load time and improves the user experience by loading resources only when they are needed. **Do This:** * Use the "loading="lazy"" attribute on "<img>" elements to enable native browser lazy loading. """html <img src="image.jpg" alt="My Image" loading="lazy"> """ * For more advanced lazy loading, use libraries like "react-lazyload" or "lozad.js". * Use Intersection Observer API to detect when an element is visible in the viewport and load the resource dynamically. **Don't Do This:** * Load all images upfront, which increases the initial load time. * Fail to use lazy loading for large images or videos that are not immediately visible in the viewport. **Anti-pattern:** Loading all assets upfront degrades the user experience. Use lazy loading for improved performance. ### 4.3 Preloading and Prefetching Preloading and prefetching are techniques for prioritizing the loading of critical resources and anticipating future navigation. **Standard:** Use preloading for critical resources that are needed immediately and prefetching for resources that are likely to be needed in the future. **Why:** Improves the user experience by loading critical resources quickly and anticipating future navigation. **Do This:** * Use the "<link rel="preload">" tag to preload critical resources, such as fonts and JavaScript modules. """html <link rel="preload" href="font.woff2" as="font" type="font/woff2" crossorigin> <link rel="preload" href="main.js" as="script"> """ * Use the "<link rel="prefetch">" tag to prefetch resources that are likely to be needed in the future, such as pages that the user is likely to visit. """html <link rel="prefetch" href="next-page.html"> """ * Use dynamic import with webpack magic comments. """javascript import(/* webpackPrefetch: true */ 'NextComponent'); """ **Don't Do This:** * Overuse preloading and prefetching, which can increase network congestion and degrade performance. * Fail to preload critical resources or prefetch resources that are likely to be needed in the future. **Anti-pattern:** Optimize the loading order of essential resources to enhance user experience. ## 5. Plugins and Extensions Parcel's plugin ecosystem allows you to extend its functionality and integrate with other tools and libraries. ### 5.1 Official Plugins Leverage official Parcel plugins to extend default functionality. **Standard:** Utilize official plugins when available to streamline build processes. **Why:** Official plugins are well-maintained and tested. **Do This:** * Search Parcel documentation for official plugins that address specific needs. * Install plugins using "npm" or "yarn". * Configure plugins according to their documentation within "package.json" or dedicated configuration files. **Don't Do This:** * Reinvent functionality that can be achieved with existing official plugins. * Use unofficial plugins without thoroughly vetting their code and security. **Code Example:** For Stylus support: """bash npm install -D @parcel/transformer-stylus """ ### 5.2 Community Plugins Community plugins can provide additional functionality and integrations beyond what is available in the core Parcel distribution or official plugins. **Standard:** Exercise caution when using community plugins. Evaluate their code quality, maintenance status, and security before integrating them into your project. **Why:** While community plugins can be valuable, they may not be as well-maintained or tested as official plugins. **Do This:** * Thoroughly investigate the plugin's repository, looking at the number of contributors, recent commits, and open issues. * Read through the plugin's code to understand its functionality and identify any potential security risks. * Test the plugin thoroughly in a development environment before deploying it to production. **Don't Do This:** * Install community plugins blindly without evaluating their code or maintenance status. * Use plugins that are no longer maintained or have unresolved security vulnerabilities. **Anti-pattern:** Integrating untrusted plugins into your build process greatly elevates security risk and introduces potential instability. ### 5.3 Custom Plugins Creating custom Parcel plugins allows you to tailor the build process to your specific needs and integrate custom tools and workflows. **Standard:** Follow Parcel's plugin API and best practices when creating custom plugins. **Why:** Ensures compatibility with future versions of Parcel and minimizes the risk of errors. **Do This:** * Consult the Parcel documentation for detailed information on the plugin API and available extension points. * Use Parcel's programmatic API to interact with the build process and access assets, dependencies, and other build information. * Write unit tests for your plugin to ensure that it functions correctly and does not introduce any regressions. **Don't Do This:** * Modify Parcel's core files directly, as this can break compatibility with future versions. * Create plugins that duplicate functionality that is already provided by Parcel or existing plugins. **Code Example (Custom Transformer Plugin):** """javascript // my-custom-transformer.js const { Transformer } = require('@parcel/plugin'); exports.default = new Transformer({ async transform({ asset }) { const content = await asset.getCode(); const transformedContent = content.replace('Hello', 'Goodbye'); asset.setCode(transformedContent); return [asset]; }, }); """ """json // package.json { "parcel": { "transformers": { "*.txt": ["./my-custom-transformer.js"] } } } """ This example demonstrates a simple transformer plugin that replaces "Hello" with "Goodbye" in ".txt" files. ## 6. Environment Variables Environment variables are a crucial element in modern application development with Parcel. They allow for configuring applications differently based on the environment. ### 6.1 Defining Environment Variables Define environment variables explicitly, differentiating between development and production. **Standard:** Use ".env" files for development and set environment variables directly in production environments. **Why:** Ensures configurations are separate and manageable, improving security and reliability. **Do This:** 1. Install "dotenv" to manage ".env" files during development """bash npm install --save-dev dotenv """ 2. Create ".env" file in root: """ API_KEY=development_key NODE_ENV=development """ 3. In production, set environment variables directly through the hosting provider or system configurations. **Don't Do This:** * Checking ".env" files into version control. * Hardcoding configurations in code. ### 6.2 Accessing Environment Variables Access environment variables uniformly across your application. **Standard:** Access environment variables using "process.env". **Why:** Provides a standard way to access environment-specific settings. **Do This:** * Use "process.env.VARIABLE_NAME" to access variables. Ensure you set "NODE_ENV" appropriately to control environment-specific behavior. """javascript const apiKey = process.env.API_KEY; console.log("API Key: ${apiKey}"); """ **Don't Do This:** * Directly importing or requiring ".env" files. Parcel and dotenv automatically inject these into "process.env" at build time. * Using different methods for accessing environment variables in different parts of the application. ### 6.3 Parcel-Specific Environment Variables Utilize Parcel's special environment variables which are available during the build process. **Standard:** Leverage "process.env.NODE_ENV" for conditional logic. **Why:** "NODE_ENV" is automatically set by Parcel based on the build mode **Do This:** * Distinguish between development and production modes which lets you enable or disable debugging features accordingly. """javascript if (process.env.NODE_ENV === 'development') { console.log('Debugging on'); } """ **Don't Do This:** * Assuming default environment values without explicitly checking "process.env". * Overriding "process.env.NODE_ENV" manually, let Parcel's configuration dictate the environment.
# Testing Methodologies Standards for Parcel This document outlines the testing methodologies standards for Parcel projects. It provides guidelines for writing effective unit, integration, and end-to-end tests, ensuring maintainability, performance, and security. These standards are designed to be used by developers and as context for AI coding assistants. ## 1. General Testing Principles ### 1.1. Test Pyramid **Understanding the Test Pyramid:** The test pyramid is a conceptual model that guides the distribution of tests. It advocates for having a large base of unit tests, a smaller layer of integration tests, and a very thin layer of end-to-end tests. **Do This:** * **Write Many Unit Tests:** These should be the foundation of your testing strategy. * **Write Some Integration Tests:** Focus on how different parts of your application work together. * **Write Few End-to-End Tests:** These are more brittle and time-consuming to maintain, so limit their scope to critical user flows. **Don't Do This:** * **Rely Heavily on End-to-End Tests:** This leads to slow test suites and fragile tests. * **Neglect Unit Tests:** Without a solid base of unit tests, you'll miss many bugs early in the development process. **Why This Matters:** A balanced test pyramid provides comprehensive coverage while minimizing maintenance overhead and maximizing the speed of your test suite. ### 1.2. Test-Driven Development (TDD) **TDD Overview:** TDD is a software development process where you write a failing test before you write any production code. Then, you write the minimum amount of code to make the test pass, and finally, you refactor. **Do This:** * **Write Tests First:** Before implementing any new functionality, write a test that defines the expected behavior. * **Follow the Red-Green-Refactor Cycle:** Make the test fail (Red), make it pass (Green), then refactor (Refactor). * **Keep Tests Small and Focused:** Each test should verify one specific aspect of your code. **Don't Do This:** * **Write Production Code Before Tests:** This often leads to biased testing and incomplete coverage. * **Skip the Refactor Step:** Refactoring ensures that your code is clean, maintainable, and efficient. **Why This Matters:** TDD promotes better design, reduces bugs, and improves code quality. ### 1.3. Code Coverage **Coverage Metrics:** Code coverage measures the percentage of your codebase that is executed when running your tests. Common metrics include line coverage, branch coverage, and function coverage. **Do This:** * **Aim for High Coverage:** Strive for code coverage above 80% for critical modules. * **Use Coverage Tools:** Tools like Istanbul (nyc) can help you measure coverage. * **Review Coverage Reports:** Use coverage reports to identify gaps in your testing strategy. **Don't Do This:** * **Chase 100% Coverage Blindly:** High coverage doesn't guarantee bug-free code. Focus on testing critical logic and edge cases. * **Ignore Low Coverage Areas:** Low coverage often indicates potential bugs or untested features. **Why This Matters:** Code coverage metrics help you identify areas of your code that need more testing, reducing the risk of undetected bugs. ## 2. Unit Testing Standards ### 2.1. Scope and Focus **Unit Test Definition:** Unit tests verify the behavior of individual units of code (e.g., functions, classes, modules) in isolation. **Do This:** * **Isolate Units:** Use mocks, stubs, and spies to isolate the unit under test from its dependencies. * **Test Public API:** Focus on testing the public interface of your units. * **Test Edge Cases:** Always include tests for edge cases, boundary conditions, and error handling. **Don't Do This:** * **Test Implementation Details:** Avoid testing private methods or internal logic that may change during refactoring. * **Write Integration Tests as Unit Tests:** Unit tests should be fast and focused. **Why This Matters:** Proper unit testing ensures that each component of your application works correctly in isolation, making it easier to identify and fix bugs. ### 2.2. Tools and Libraries **Recommended Libraries:** Jest, Mocha, Chai, and Sinon are popular JavaScript testing libraries. Jest is often preferred for Parcel projects due to its ease of setup and built-in features. **Do This:** * **Use Jest:** Configure Jest in your Parcel project for unit testing. It provides a good balance of features and ease of use. * **Use Mocking Libraries:** Employ libraries like "jest.mock" or "sinon" for mocking dependencies. **Code Example (Jest):** """javascript // my-module.js export function add(a, b) { return a + b; } export function processData(data) { // some complex data processing return data.map(item => add(item, 10)); } """ """javascript // my-module.test.js import { add, processData } from './my-module'; jest.mock('./my-module', () => { const originalModule = jest.requireActual('./my-module'); return { ...originalModule, add: jest.fn((a, b) => originalModule.add(a, b) + 1), // Mock implementation }; }); describe('my-module', () => { it('should add two numbers correctly', () => { expect(add(2, 3)).toBe(5); }); it('should process data correctly', () => { const data = [1, 2, 3]; const result = processData(data); expect(result).toEqual([11, 12, 13]); }); it('should call the mocked add function', () => { processData([0]); expect(add).toHaveBeenCalled(); }); }); """ **Don't Do This:** * **Rely on Global State:** Avoid modifying global state in your tests, as it can lead to unpredictable results. * **Write Flaky Tests:** Ensure that your tests are deterministic and always produce the same result for the same input. **Why This Matters:** Choosing the right tools and following best practices ensures that your unit tests are reliable, maintainable, and effective. ### 2.3. Mocking Strategies **Types of Mocks:** Mocks are objects that simulate the behavior of real dependencies. Stubs, spies, and fakes are different types of mocks. * **Stubs:** Provide predefined responses to method calls. * **Spies:** Record method calls and arguments. * **Mocks:** Combine the features of stubs and spies. **Do This:** * **Use Mocks Wisely:** Mock dependencies when necessary to isolate the unit under test. * **Verify Interactions:** Use spies to verify that your unit interacts with its dependencies as expected. * **Mock External Services:** Mock external APIs and services to avoid relying on external resources during testing. **Code Example (Jest Mocks):** """javascript // api-service.js export async function fetchData(url) { const response = await fetch(url); return response.json(); } """ """javascript // api-service.test.js import { fetchData } from './api-service'; jest.mock('./api-service', () => ({ fetchData: jest.fn(() => Promise.resolve({ data: 'mocked data' })), })); describe('api-service', () => { it('should fetch data correctly', async () => { const data = await fetchData('https://example.com/api'); expect(data).toEqual({ data: 'mocked data' }); expect(fetchData).toHaveBeenCalledWith('https://example.com/api'); }); }); """ **Don't Do This:** * **Over-Mock:** Avoid mocking everything, as it can make your tests meaningless. * **Mock Too Early:** Defer mocking until you encounter actual dependencies that are difficult to test. **Why This Matters:** Effective mocking allows you to test your code in isolation, ensuring that it behaves correctly regardless of the state of its dependencies. ### 2.4. Asynchronous Code Testing **Handling Promises and Async/Await:** Testing asynchronous code requires special techniques to ensure that your tests wait for the asynchronous operations to complete. **Do This:** * **Use "async/await" in Tests:** Use "async/await" to handle promises in your tests. * **Use "resolves" and "rejects" Matchers:** Jest provides "resolves" and "rejects" matchers to test promises. * **Use "done" Callback:** If you're using Mocha or other libraries, use the "done" callback to signal the completion of an asynchronous test. **Code Example (Jest Async/Await):** """javascript // async-function.js export async function fetchData(url) { const response = await fetch(url); return response.json(); } """ """javascript // async-function.test.js import { fetchData } from './async-function'; describe('async-function', () => { it('should fetch data correctly', async () => { const data = await fetchData('https://example.com/api'); await expect(data).resolves.toEqual({ data: 'some data' }); }); it('should handle errors correctly', async () => { jest.spyOn(global, 'fetch').mockImplementation(() => Promise.reject('API error')); await expect(fetchData('https://example.com/api')).rejects.toEqual('API error'); }); }); """ **Don't Do This:** * **Forget to Await Promises:** If you don't "await" promises in your tests, the tests may complete before the asynchronous operations finish. * **Use "setTimeout" for Synchronization:** Avoid using "setTimeout" to wait for asynchronous operations, as it can lead to unreliable tests. **Why This Matters:** Proper handling of asynchronous code ensures that your tests accurately verify the behavior of asynchronous operations. ## 3. Integration Testing Standards ### 3.1. Scope and Purpose **Integration Test Definition:** Integration tests verify the interactions between different components or modules of your application. **Do This:** * **Test Component Interactions:** Focus on testing how different parts of your application work together. * **Use Real Dependencies When Possible:** Avoid mocking unless necessary. * **Test Data Flows:** Verify that data flows correctly between components. **Don't Do This:** * **Test Individual Units:** Integration tests should not focus on testing individual units in isolation. * **Duplicate Unit Tests:** Avoid duplicating the same tests in both unit and integration tests. **Why This Matters:** Integration tests ensure that your application's components work together correctly, catching integration-related bugs early. ### 3.2. Testing Parcel Bundles **Considerations for Parcel:** Parcel's bundling process impacts integration testing. Ensure tests run against the bundled output or mock the bundle behavior effectively. **Do This:** * **Test against build output:** Run integration tests against the production-ready output of Parcel (usually in the "dist" folder). This confirms that Parcel is correctly bundling assets and code. * **Mock module resolution:** In some cases, for performance or isolation, simulate Parcel's module resolution using Jest's module mocking capabilities. **Code Example (Integration Test with Parcel build):** """javascript //integration.test.js const {myAppFunction} = require('./dist/main.js'); // Assuming output is in dist/main.js describe('Integration Tests', () => { it('should execute the application flow', () => { const result = myAppFunction("test input"); expect(result).toBe('expected output'); }); }); """ **Configuration:** * Add a test script to "package.json" that runs "parcel build" followed by your integration tests. **Don't Do This:** * Assume the development environment perfectly mirrors the bundled production environment. **Why this matters:** Bundling changes code, assets, and dependency resolution. Testing against the final output is critical, especially for complex applications. ### 3.3. Database Testing **Connecting to Databases:** Integration tests often involve connecting to real or test databases. **Do This:** * **Use Test Databases:** Always use separate test databases to avoid polluting your production data. * **Seed Databases:** Seed your test databases with known data before running tests. * **Clean Up After Tests:** Clean up your test databases after running tests to ensure a clean environment. **Code Example (Integration Test with Database):** """javascript // db-integration.test.js import { connectToDatabase, queryDatabase } from './db-utils'; describe('Database Integration Tests', () => { let db; beforeAll(async () => { db = await connectToDatabase('testdb'); await queryDatabase(db, 'CREATE TABLE IF NOT EXISTS users (id INT, name VARCHAR(255))'); await queryDatabase(db, 'INSERT INTO users (id, name) VALUES (1, \'John Doe\')'); }); afterAll(async () => { await queryDatabase(db, 'DROP TABLE users'); db.close(); }); it('should query data correctly', async () => { const result = await queryDatabase(db, 'SELECT * FROM users WHERE id = 1'); expect(result).toEqual([{ id: 1, name: 'John Doe' }]); }); }); """ **Don't Do This:** * **Use Production Databases:** Never run integration tests against your production databases. * **Leave Dirty Data:** Always clean up your test databases after running tests. **Why This Matters:** Proper database testing ensures that your application can interact with databases correctly and reliably. ## 4. End-to-End (E2E) Testing Standards ### 4.1. Scope and Focus **E2E Test Definition:** End-to-end tests verify the entire application flow, from the user interface to the backend services. **Do This:** * **Test Critical User Flows:** Focus on testing the most important user scenarios. * **Simulate User Interactions:** Use tools like Cypress or Puppeteer to simulate user interactions with your application. * **Verify UI Elements:** Verify that UI elements are displayed correctly and behave as expected. **Don't Do This:** * **Test Every Detail:** E2E tests are time-consuming to write and maintain, so focus on critical flows. * **Duplicate Unit or Integration Tests:** Avoid duplicating tests that can be covered by unit or integration tests. **Why This Matters:** E2E tests ensure that your application works correctly from the user's perspective, providing confidence in the overall system. ### 4.2. Tools and Frameworks **Recommended Frameworks:** Cypress, Puppeteer, and Playwright are popular E2E testing frameworks. **Do This:** * **Use Cypress:** Cypress is recommeded. It provides a great developer experience and powerful features for E2E testing. * **Use Page Objects:** Implement the page object pattern to organize your E2E tests. * **Write Reusable Commands:** Create reusable Cypress commands for common interactions. **Code Example (Cypress):** """javascript // cypress/integration/example.spec.js describe('My First E2E Test', () => { it('Visits the app root url', () => { cy.visit('/'); cy.contains('h1', 'Welcome to My App'); }); it('Navigates to the about page', () => { cy.visit('/'); cy.contains('a', 'About').click(); cy.url().should('include', '/about'); cy.contains('h1', 'About Page'); }); }); """ **Don't Do This:** * **Write Brittle Tests:** Avoid writing tests that are easily broken by minor UI changes. * **Ignore Performance:** Monitor the performance of your E2E tests and optimize them as needed. **Why This Matters:** Choosing the right tools and following best practices makes your E2E tests more reliable and maintainable. ### 4.3. Test Environment **Configuring the Test Environment:** E2E tests require a dedicated test environment that simulates a real production environment. **Do This:** * **Use a Separate Environment:** Set up a separate environment for running E2E tests. * **Seed the Environment :** Seed the test environment with realistic data. * **Clean Up After Each Test:** Clean up the test environment after each test to ensure a clean state. **Don't Do This:** * **Run Tests in Production:** Never run E2E tests in your production environment. * **Use Shared Environments:** Avoid sharing test environments between different teams or projects. **Why This Matters:** A properly configured test environment ensures that your E2E tests accurately reflect the behavior of your application in a production-like setting. ### 4.4. Accessibility Testing **Integrating Accessibility Checks:** Ensure your application is accessible to users with disabilities. **Do This:** * **Integrate axe-core:** Incorporate axe-core (or similar) into your E2E tests to automatically check for accessibility issues. * **Run Accessibility Checks:** Include accessibility checks in your E2E tests to identify and fix accessibility issues. * **Follow WCAG Guidelines:** Ensure that your application follows the Web Content Accessibility Guidelines (WCAG). **Code Example (Cypress with "cypress-axe"):** """javascript // cypress/integration/accessibility.spec.js import 'cypress-axe'; describe('Accessibility Tests', () => { it('Checks accessibility of the home page', () => { cy.visit('/'); cy.injectAxe(); cy.checkA11y(); // Runs axe-core and fails the test if there are any violations }); }); """ **Don't Do This:** * **Ignore Accessibility:** Accessibility should be a core consideration, not an afterthought. * **Rely Solely on Automated Checks:** Automated checks are valuable but can't replace manual testing with assistive technologies. **Why This Matters:** Accessibility testing ensures your application is usable by everyone, improving inclusivity and meeting legal requirements. ## 5. Performance Testing Standards ### 5.1. Benchmarking **Purpose:** To identify performance bottlenecks and validate that changes don't degrade performance. **Do This:** * **Automated Benchmarks:** Create automated benchmarks to measure the performance of critical code sections. * **Record Baseline:** Establish a baseline performance metric for your applications entry points. * **Track performance over time:** Track key performance indicators (KPIs) over time using monitoring tools and integrate the key signals into the CI/CD **Example Implementation (using "perf_hooks"):** """javascript // benchmark.js const { performance, PerformanceObserver } = require('perf_hooks'); function myFunction(n) { let result = 0; for (let i = 1; i <= n; i++) { result += i; } return result; } const obs = new PerformanceObserver((items) => { console.log(items.getEntries()[0].duration); performance.clearMarks(); }); obs.observe({ entryTypes: ['measure'] }); performance.mark('start'); myFunction(100000); performance.mark('end'); performance.measure('myFunction', 'start', 'end'); """ **Don't Do This:** * **Optimize too early:** focus on core functionality before performance * **Ignore benchmarks over time**: It's important to periodically reevaluate benchmarks to avoid performance degradation. ### 5.2. Using Parcel's Performance Features: **Purpose:** Optimize bundle size and load times. **Do This:** * **Enable Production Mode:** Ensure "NODE_ENV=production" when building for production. Parcel performs optimizations like minification and tree shaking. * **Analyze Bundle Size Report:** Use Parcel's bundle analyzer features (via plugins) to identify large dependencies and opportunities for optimization (dynamic imports). * **Implement lazy loading** Use dynamic imports to load modules on demand **Code Example (Dynamic Import):** """javascript //index.js async function loadModule() { const module = await import('./my-module'); module.init(); } """ **Don't Do This:** * **Ship unminified code:** Always use Parcel in production mode. * **Over-bundle**: Prevent "bundle bloat" By following these guidelines, you can create a robust and reliable testing strategy for your Parcel projects, ensuring the quality and stability of your application.
# State Management Standards for Parcel This document outlines the coding standards for managing application state in Parcel projects. It aims to provide best practices for data flow, reactivity, and overall state management within the Parcel ecosystem, promoting maintainability, performance, and a consistent development experience. It is designed to work with the latest version of Parcel. ## 1. Introduction to State Management in Parcel Projects Parcel, as a bundler, doesn't inherently dictate a specific state management solution. It's unopinionated about the frameworks and libraries you choose to use for managing state. Therefore, the choice of state management library or pattern largely depends on the complexity and scale of your application. These guidelines focus on principles that apply irrespective of the specific library you select when building your Parcel application. ### Understanding Parcel's Role Parcel's primary responsibility is bundling and transforming your code, optimizing it for the browser. This means Parcel is concerned with *how* your code is packaged, not *what* your code does. However, the *what* (the application logic, including state management) significantly impacts the *how*. Using a well-structured state management approach provides clearer dependencies for Parcel to optimize and ensures efficient code splitting. ### Key Considerations * **Complexity:** Simple applications may only require basic component state or context. More complex ones might benefit from dedicated state management libraries. * **Maintainability:** Code should be easy to understand, debug and modify. * **Performance:** Reduce unnecessary re-renders and optimize data flow. * **Scalability:** Design your state management system to scale with the growth of your application. * **Bundle Size:** Be mindful of the impact of large state management libraries on your final bundle size. Parcel's code splitting can help mitigate this, but conscious library selection is important. ## 2. Core Principles of State Management Regardless of the chosen library or pattern, these core principles should guide state management practices in Parcel projects. ### 2.1 Unidirectional Data Flow * **Do This:** Implement a unidirectional data flow pattern where data changes propagate in a single direction. This makes it easier to trace state changes and debug issues. * **Don't Do This:** Avoid two-way data binding or direct manipulation of state outside of the intended channels. * **Why:** Predictability and easier debugging. Unidirectional data flow reduces the likelihood of unintended side effects and makes it easier to reason about the state of the application at any given time. * **Example (React + Redux):** """javascript // Action const increment = () => ({ type: 'INCREMENT' }); // Reducer const counterReducer = (state = 0, action) => { switch (action.type) { case 'INCREMENT': return state + 1; default: return state; } }; // Store import { createStore } from 'redux'; const store = createStore(counterReducer); // Component import React from 'react'; import { connect } from 'react-redux'; const Counter = ({ counter, increment }) => ( <div> <p>Counter: {counter}</p> <button onClick={increment}>Increment</button> </div> ); const mapStateToProps = (state) => ({ counter: state }); const mapDispatchToProps = { increment }; export default connect(mapStateToProps, mapDispatchToProps)(Counter); """ ### 2.2 Immutability * **Do This:** Treat state as immutable. When state needs to be updated, create a new copy with the desired changes instead of modifying the existing state directly. * **Don't Do This:** Directly mutate state variables. Relying on mutation makes tracking changes difficult. * **Why:** Immutability simplifies change detection and allows for efficient updates in UI frameworks like React. It also improves predictability. * **Example (Using spread operator for immutability):** """javascript // Updating an object immutability const initialState = { name: 'Alice', age: 30, }; const updatedState = { ...initialState, age: 31 }; // Creates a new object console.log(initialState.age); // 30 console.log(updatedState.age); // 31 """ ### 2.3 Single Source of Truth * **Do This:** Designate a single source of truth for each piece of application state. Avoid duplicating data across multiple components or stores. * **Don't Do This:** Store the same data in different places, leading to potential inconsistencies. * **Why:** Eliminates data inconsistencies and simplifies updates. * **Example (Centralized store with React Context):** """javascript // StateContext.jsx import React, { createContext, useState, useContext } from 'react'; const StateContext = createContext(); export const StateProvider = ({ children }) => { const [count, setCount] = useState(0); const increment = () => { setCount(count + 1); }; return ( <StateContext.Provider value={{ count, increment }}> {children} </StateContext.Provider> ); }; export const useStateContext = () => useContext(StateContext); // Usage in a component: // MyComponent.jsx import React from 'react'; import { useStateContext } from './StateContext'; const MyComponent = () => { const { count, increment } = useStateContext(); return ( <div> <p>Count: {count}</p> <button onClick={increment}>Increment</button> </div> ); }; export default MyComponent; """ """javascript // index.js (entry point) import React from 'react'; import ReactDOM from 'react-dom/client'; import App from './App'; import { StateProvider } from './StateContext'; const root = ReactDOM.createRoot(document.getElementById('root')); root.render( <React.StrictMode> <StateProvider> <App /> </StateProvider> </React.StrictMode> ); """ ### 2.4 Separation of Concerns * **Do This:** Separate state management logic from UI components. Avoid embedding state management directly within components. * **Don't Do This:** Mixing state updates and UI logic within the same component. * **Why:** Improves code organization, testability, and reusability. This often means extracting state logic into custom hooks, reducers, or service layers. * **Example (Custom React Hook):** """javascript // useCounter.js (Custom Hook) import { useState } from 'react'; const useCounter = (initialValue = 0) => { const [count, setCount] = useState(initialValue); const increment = () => { setCount(count + 1); }; const decrement = () => { setCount(count - 1); }; return { count, increment, decrement }; }; export default useCounter; // CounterComponent.jsx import React from 'react'; import useCounter from './useCounter'; const CounterComponent = () => { const { count, increment, decrement } = useCounter(); return ( <div> <p>Count: {count}</p> <button onClick={increment}>Increment</button> <button onClick={decrement}>Decrement</button> </div> ); }; export default CounterComponent; """ ### 2.5 Minimizing Global State * **Do This:** Only store data in global state that is truly needed globally. Use component-local state or context for data specific to a particular component or subtree. * **Don't Do This:** Store every piece of data in a global store. * **Why:** Reduces unnecessary re-renders, minimizes dependencies, and improves performance. Global state increases application complexity and can lead to performance bottlenecks if not managed carefully. ## 3. State Management Approaches in Parcel Since Parcel is unopinionated, you're free to choose from a variety of approaches. Here are some common ones, along with considerations for their use with Parcel. ### 3.1 Component State (useState) * **Description:** The simplest approach, using the "useState" hook in React to manage state within individual components. * **When to Use:** For simple components with limited state requirements. It works well for isolated UI elements. * **Parcel Considerations:** No specific Parcel-related considerations. Parcel will bundle and optimize the code as usual. """javascript import React, { useState } from 'react'; const MyComponent = () => { const [count, setCount] = useState(0); return ( <div> <p>Count: {count}</p> <button onClick={() => setCount(count + 1)}>Increment</button> </div> ); }; export default MyComponent; """ ### 3.2 Context API * **Description:** React's built-in mechanism for sharing state across a component tree without prop drilling. * **When to Use:** When state needs to be accessed by multiple components within a specific part of the application. Good for themes, user authentication, or other application-wide settings. * **Parcel Considerations:** Again, no specific Parcel concerns. Parcel optimizes the React code transparently. """javascript // ThemeContext.jsx import React, { createContext, useState, useContext } from 'react'; const ThemeContext = createContext(); export const ThemeProvider = ({ children }) => { const [theme, setTheme] = useState('light'); const toggleTheme = () => { setTheme(theme === 'light' ? 'dark' : 'light'); }; return ( <ThemeContext.Provider value={{ theme, toggleTheme }}> {children} </ThemeContext.Provider> ); }; export const useTheme = () => useContext(ThemeContext); // Usage in component import React from 'react'; import { useTheme } from './ThemeContext'; const MyComponent = () => { const { theme, toggleTheme } = useTheme(); return ( <div style={{ backgroundColor: theme === 'light' ? 'white' : 'black', color: theme === 'light' ? 'black' : 'white' }}> <p>Current Theme: {theme}</p> <button onClick={toggleTheme}>Toggle Theme</button> </div> ); }; export default MyComponent; """ ### 3.3 Redux/Redux Toolkit * **Description:** A predictable state container for JavaScript apps. Centralizes application state and enforces a unidirectional data flow. * **When to Use:** For complex applications where state is shared across many components, and predictable state management is crucial. Redux Toolkit simplifies Redux setup and usage. * **Parcel Considerations:** Parcel can efficiently bundle Redux code and optimize it for production. It's important to configure Parcel correctly for tree shaking to remove unused Redux code. """javascript // store.js (Redux Toolkit) import { configureStore, createSlice } from '@reduxjs/toolkit'; const counterSlice = createSlice({ name: 'counter', initialState: { value: 0, }, reducers: { increment: (state) => { state.value += 1; }, decrement: (state) => { state.value -= 1; }, }, }); export const { increment, decrement } = counterSlice.actions; export const store = configureStore({ reducer: { counter: counterSlice.reducer, }, }); // Component example import React from 'react'; import { useSelector, useDispatch } from 'react-redux'; import { increment, decrement } from './store'; const Counter = () => { const count = useSelector((state) => state.counter.value); const dispatch = useDispatch(); return ( <div> <p>Count: {count}</p> <button onClick={() => dispatch(increment())}>Increment</button> <button onClick={() => dispatch(decrement())}>Decrement</button> </div> ); }; export default Counter; """ ### 3.4 Zustand * **Description:** A small, fast and scalable bearbones state-management solution using simplified flux principles. * **When to Use:** For mid-sized applications or parts of larger apps when Redux feels like overkill, but component state and Context feel limiting. * **Parcel Considerations:** Parcel will handle Zustand without issue. Keeping your store definitions small and well-modularized allows Parcel to optimize bundles appropriately. """javascript // store.js (Zustand) import { create } from 'zustand' const useStore = create((set) => ({ count: 0, increment: () => set((state) => ({ count: state.count + 1 })), decrement: () => set((state) => ({ count: state.count - 1 })), })) export default useStore; // Component example import React from 'react'; import useStore from './store'; const Counter = () => { const { count, increment, decrement } = useStore(); return ( <div> <p>Count: {count}</p> <button onClick={increment}>Increment</button> <button onClick={decrement}>Decrement</button> </div> ); }; export default Counter; """ ### 3.5 Jotai * **Description:** Primitive and flexible state management for React based on an atomic model. * **When to Use:** When you need fine-grained control over state updates and want to avoid re-renders in unrelated components. Works very well with concurrent React. * **Parcel Considerations:** Plays nicely with Parcel. Focus on keeping your atoms small and well-defined. """javascript // atoms.js (Jotai) import { atom } from 'jotai' export const countAtom = atom(0) // Component example import React from 'react'; import { useAtom } from 'jotai' import { countAtom } from './atoms' const Counter = () => { const [count, setCount] = useAtom(countAtom) return ( <div> <p>Count: {count}</p> <button onClick={() => setCount(count + 1)}>Increment</button> </div> ); }; export default Counter; """ ### 3.6 Recoil * **Description:** A state management library for React from Facebook, designed specifically for concurrent mode. Similar in some ways to Jotai, but with different tradeoffs. * **When to Use:** For complex applications using concurrent React, where predictable and efficient state updates are paramount. * **Parcel Considerations:** Requires proper configuration but integrates smoothly. Parcel optimizes the rendering. """javascript // atoms.js (Recoil) import { atom } from 'recoil'; export const countState = atom({ key: 'countState', // unique ID (globally unique) default: 0, // default value (aka initial value) }); // Component example import React from 'react'; import { useRecoilState } from 'recoil'; import { countState } from './atoms'; function Counter() { const [count, setCount] = useRecoilState(countState); return ( <div> <p>Count: {count}</p> <button onClick={() => setCount(count + 1)}>Increment</button> </div> ); } export default Counter; """ ## 4. Best Practices for Performance Efficient state management is crucial for optimal application performance. ### 4.1 Memoization * **Do This:** Use memoization to prevent unnecessary re-renders of components. React's "React.memo", "useMemo", and "useCallback" are essential tools. Redux's "reselect" library is also valuable. * **Don't Do This:** Neglect memoization, leading to frequent re-renders even when data hasn't changed. * **Why:** Improves performance by skipping re-renders when component props or state haven't changed. * **Example (React.memo):** """javascript import React from 'react'; const MyComponent = React.memo(({ data }) => { console.log('MyComponent rendered'); // Only renders when data prop changes return ( <p>Data: {data}</p> ); }); export default MyComponent; """ ### 4.2 Code Splitting * **Do This:** Utilize Parcel's built-in code splitting capabilities to break down your application into smaller chunks. Lazy-load components that are not immediately needed. * **Don't Do This:** Include all code in a single large bundle. * **Why:** Reduces initial load time and improves perceived performance. * **Example (Dynamic Import):** """javascript import React, { Suspense, lazy } from 'react'; const MyComponent = lazy(() => import('./MyComponent')); const App = () => ( <Suspense fallback={<div>Loading...</div>}> <MyComponent /> </Suspense> ); export default App; """ ### 4.3 Batch Updates * **Do This:** Batch state updates whenever possible to avoid triggering multiple re-renders. * **Don't Do This:** Performing multiple state updates in a short period without batching. * **Why:** Reduces the number of re-renders and improves performance. * **Example (Using "ReactDOM.unstable_batchedUpdates"):** """javascript import React from 'react'; import ReactDOM from 'react-dom/client'; const myFunc = () => { ReactDOM.unstable_batchedUpdates(() => { // Multiple state updates here setCount(count + 1); setValue(newValue); }); } """ ### 4.4 Selector Functions * **Do This:** Use selector functions to derive data from the store. Avoid performing complex calculations directly within components. * **Don't Do This:** Repeating complex calculations in multiple components. * **Why:** Improves performance by memoizing the results of calculations and preventing unnecessary re-renders. * **Example (Reselect with Redux):** """javascript import { createSelector } from 'reselect'; const getItems = (state) => state.items; const getFilter = (state) => state.filter; const getFilteredItems = createSelector( [getItems, getFilter], (items, filter) => items.filter(item => item.includes(filter)) ); """ ## 5. Common Anti-Patterns Avoid these common mistakes in your Parcel projects. ### 5.1 Prop Drilling * **Description:** Passing data through multiple layers of components that don't need it. * **Solution:** Use Context API, Redux, or a similar state management library to provide data directly to the components that need it. ### 5.2 Mutating State Directly * **Description:** Modifying state directly instead of creating a new copy. * **Solution:** Always create a new copy of the state when updating it using the spread operator or immutable data structures. ### 5.3 Over-reliance on Global State * **Description:** Storing too much data in global state, leading to unnecessary re-renders and increased complexity. * **Solution:** Use component-local state or context for data that is specific to a particular component or subtree. ### 5.4 Ignoring Performance * **Description:** Neglecting memoization, code splitting, and other performance optimization techniques. * **Solution:** Regularly profile your application to identify performance bottlenecks and implement appropriate optimizations. ## 6. Security Considerations ### 6.1 Sensitive Data * **Do This:** Avoid storing sensitive data (e.g., API keys, passwords) in client-side state. If necessary, encrypt it and store it securely. * **Don't Do This:** Storing sensitive information directly in the browser's local storage or in global state. * **Why:** Protecting user data and preventing security breaches. ### 6.2 Input Validation * **Do This:** Validate all user inputs before updating state to prevent malicious data from corrupting your application. * **Don't Do This:** Directly updating state with unfiltered user input. * **Why:** Preventing Cross-Site Scripting (XSS) attacks and other security vulnerabilities. ## 7. Conclusion By adhering to these standards, developers can build maintainable, performant, and secure Parcel applications with robust state management. Remember to choose the right state management approach based on the complexity of your application, and always prioritize unidirectional data flow, immutability, and separation of concerns. Continuously evaluate and refine your state management strategies as your application evolves.
# Code Style and Conventions Standards for Parcel This document outlines the coding style and conventions to be followed when developing with Parcel. Adhering to these standards will ensure code consistency, readability, maintainability, and ultimately, a more robust and collaborative development experience. These guidelines are tailored for the latest version of Parcel. ## 1. General Principles ### 1.1. Consistency * **Do This:** Maintain a consistent style throughout the codebase. If a pattern is established, follow it unless there's a compelling reason not to. * **Don't Do This:** Introduce arbitrary stylistic changes or deviate from established patterns without justification. * **Why:** Consistency reduces cognitive load and makes the code easier to understand and maintain. ### 1.2. Readability * **Do This:** Write code that is easy to read and understand. Use meaningful names, comments, and clear formatting. * **Don't Do This:** Write cryptic or overly clever code that is difficult for others (or your future self) to decipher. * **Why:** Readability is paramount for collaboration and long-term maintenance. ### 1.3. Maintainability * **Do This:** Design code that is modular, testable, and easy to modify. * **Don't Do This:** Create monolithic, tightly coupled code that is difficult to change without introducing regressions. * **Why:** Maintainable code allows for easier bug fixes, feature additions, and refactoring. ### 1.4. Performance * **Do This:** Write code that is performant and avoids unnecessary overhead. Consider the performance implications of your choices. * **Don't Do This:** Write code that is inefficient or wastes resources without a clear reason. * **Why:** Performance affects the user experience and the efficiency of the application. Parcel's strengths (like parallelization), need to be balanced with the performance implications of code written. ### 1.5. Security * **Do This:** Write code that is secure and protects against common vulnerabilities. * **Don't Do This:** Introduce security risks or vulnerabilities into the codebase. * **Why:** Security is crucial for protecting user data and preventing attacks. Parcel is responsible for bundling, but the security of the code it bundles remains the responsibility of the developer. ## 2. Formatting ### 2.1. Indentation * **Do This:** Use 2 spaces for indentation. Configuration of your editor is important. * **Don't Do This:** Use tabs or a mixture of tabs and spaces for indentation. * **Why:** Two spaces provide a good balance between readability and reducing horizontal space. """javascript // Correct indentation function myFunction() { const myVariable = 123; if (myVariable > 100) { console.log('Large number'); } } """ ### 2.2. Line Length * **Do This:** Aim for a maximum line length of 80-120 characters. * **Don't Do This:** Have excessively long lines that are difficult to read. * **Why:** Shorter lines are easier to read and fit on most screens. ### 2.3. Whitespace * **Do This:** * Use a single blank line to separate logical blocks of code. * Use whitespace around operators (e.g., "x = y + z"). * Use a space after commas (e.g., "myFunction(a, b, c)"). * **Don't Do This:** * Use excessive whitespace or inconsistent whitespace. * Omit whitespace around operators or after commas. * **Why:** Consistent whitespace improves readability. """javascript // Correct whitespace function calculateArea(width, height) { const area = width * height; return area; } """ """javascript // Incorrect whitespace function calculateArea(width,height){ const area=width*height; return area; } """ ### 2.4. Newlines * **Do This:** * End files with a newline character. * Separate statements with semicolons (";"). While Parcel often works without them due to ASI, explicit semicolons ensure consistency and avoid unexpected behavior. * **Don't Do This:** * Omit the final newline character in files * Rely solely on Automatic Semicolon Insertion(ASI) * **Why:** A final newline ensures proper file parsing. Explicit semicolons prevent parsing errors. ### 2.5. Quotes * **Do This:** Use single quotes ("'") for string literals unless you need to include single quotes within the string, in which case use double quotes ("""). Template literals are useful for string interpolation. * **Don't Do This:** Use double quotes when single quotes are sufficient. * **Why:** Single quotes are generally preferred for consistency, but double quotes are acceptable when necessary. Template literals enhance readability for complex strings. """javascript // Correct quotes const myString = 'Hello, world!'; const myStringWithApostrophe = "It's a beautiful day."; const name = 'Alice'; const greeting = "Hello, ${name}!"; // Template literal """ ### 2.6. Braces * **Do This:** Use braces for all control flow statements (e.g., "if", "else", "for", "while"), even if the body consists of a single statement. Place the opening brace on the same line as the statement. * **Don't Do This:** Omit braces for single-statement bodies. * **Why:** Braces improve readability and prevent errors when adding new statements. """javascript // Correct braces if (condition) { console.log('Condition is true'); } else { console.log('Condition is false'); } for (let i = 0; i < 10; i++) { console.log(i); } """ ## 3. Naming Conventions ### 3.1. Variables * **Do This:** Use camelCase for variable names (e.g., "myVariableName"). * **Don't Do This:** Use snake_case or PascalCase for variable names. * **Why:** camelCase is the standard convention for JavaScript. """javascript // Correct variable names const myVariableName = 123; let numberOfItems = 10; """ ### 3.2. Constants * **Do This:** Use UPPER_SNAKE_CASE for constant names (e.g., "MY_CONSTANT"). * **Don't Do This:** Use camelCase or PascalCase for constant names. * **Why:** UPPER_SNAKE_CASE distinguishes constants from regular variables. """javascript // Correct constant names const MAX_VALUE = 100; const API_URL = 'https://example.com/api'; """ ### 3.3. Functions * **Do This:** Use camelCase for function names (e.g., "myFunctionName"). Function names should clearly describe what the function does. * **Don't Do This:** Use snake_case or PascalCase for function names. * **Why:** camelCase is the standard convention for JavaScript. """javascript // Correct function names function calculateSum(a, b) { return a + b; } const greetUser = (name) => { console.log("Hello, ${name}!"); }; """ ### 3.4. Classes * **Do This:** Use PascalCase for class names (e.g., "MyClass"). * **Don't Do This:** Use camelCase or snake_case for class names. * **Why:** PascalCase distinguishes classes from functions and variables. """javascript // Correct class names class MyClass { constructor(value) { this.value = value; } getValue() { return this.value; } } """ ### 3.5. File Names * **Do This:** Use kebab-case for file names (e.g., "my-component.js"). Favor descriptive names. * **Don't Do This:** Use camelCase or snake_case for file names. * **Why:** kebab-case is a common convention and easy to read. ### 3.6. Directory Names * **Do This:** Use lowercase names for directory/folder names (e.g., "components"). * **Don't Do This:** Use camelCase or PascalCase for directory names. * **Why:** Consistency makes the project structure easier to navigate and remember. ## 4. Comments & Documentation ### 4.1. JSDoc Comments * **Do This:** Use JSDoc-style comments to document functions, classes, and modules. * **Don't Do This:** Neglect to document important code elements. * **Why:** JSDoc comments allow for automatic documentation generation and improve code understanding. AI tools can also leverage these comments, especially for reasoning about complex code. """javascript /** * Calculates the sum of two numbers. * * @param {number} a The first number. * @param {number} b The second number. * @returns {number} The sum of a and b. */ function calculateSum(a, b) { return a + b; } /** * A class representing a user. */ class User { /** * Creates a new User instance. * @param {string} name The name of the user. * @param {number} age The age of the user. */ constructor(name, age) { this.name = name; this.age = age; } /** * Gets the user's name. * @returns {string} The user's name. */ getName() { return this.name; } } """ ### 4.2. Inline Comments * **Do This:** Use inline comments to explain complex or non-obvious code logic. Aim for clarity, not verbosity. * **Don't Do This:** Use excessive or redundant comments that state the obvious. * **Why:** Inline comments help others understand the intent and rationale behind the code """javascript function processData(data) { // Filter out invalid data points (e.g., negative values). const validData = data.filter(value => value >= 0); // Calculate the average of the valid data points. const sum = validData.reduce((acc, value) => acc + value, 0); const average = sum / validData.length; return average; } """ ### 4.3. Code Explanations in Commit Messages * **Do This:** Provide meaningful commit messages that provide context for why the code was written. * **Don't Do This:** Include meaningless or generic commit messages. * **Why:** This can help AI tools understand the purpose of the code and the reasoning behind it. ## 5. Specific Parcel Considerations ### 5.1. Entry Points * **Do This:** Define clear entry points for your Parcel bundles in your "package.json". Ensure that the "main", "module" and "browser" fields are correctly set. The "source" filed can also be used for code transformations. * **Don't Do This:** Rely on implicit entry point resolution or omit important entry point definitions. * **Why:** Clear entry points ensure that Parcel can correctly bundle your code. """json // package.json { "name": "my-parcel-project", "version": "1.0.0", "main": "dist/index.js", // CommonJS entry point "module": "dist/module.js", // ES module entry point "browser": "dist/browser.js", // Browser-specific entry point "source": "src/index.js", // The source file "scripts": { "build": "parcel build" }, "devDependencies": { "parcel": "^2.11.0" } } """ ### 5.2. Asset Types * **Do This:** Import assets (e.g., images, CSS files) directly into your JavaScript code. Parcel automatically handles these imports * **Don't Do This:** Manually manage assets or use absolute paths that bypass Parcel's asset pipeline. * **Why:** Parcel optimizes and transforms assets automatically when they are imported directly. """javascript // Importing CSS import './styles.css'; // Importing an image import logo from './logo.png'; const imageElement = document.createElement('img'); imageElement.src = logo; // Parcel will handle the path to the optimized image. document.body.appendChild(imageElement); """ ### 5.3. Dynamic Imports * **Do This:** Use dynamic imports ("import()") for lazy-loading modules and code-splitting. * **Don't Do This:** Load all modules eagerly, especially modules that are only needed in specific situations. * **Why:** Dynamic imports improve initial load time and performance by only loading code when it's needed. """javascript // Dynamic import async function loadModule() { const module = await import('./my-module.js'); module.doSomething(); } loadModule(); """ ### 5.4. Environment Variables * **Do This:** Access environment variables using "process.env". Configure ".env" files for different environments (development, production, etc.). * **Don't Do This:** Hardcode environment-specific values directly into the code. * **Why:** Environment variables allow you to configure your application without modifying the code. """javascript // Accessing environment variables const apiUrl = process.env.API_URL || 'http://default-api-url.com'; console.log("API URL: ${apiUrl}"); """ Add to ".env": """ API_URL=http://my-api-url.com """ ### 5.5. Transformers * **Do This:** Leverage Parcel's transformer plugins to extend its functionality, such as using PostCSS for CSS transformations or Babel for JavaScript transpilation. Configure your transformer plugins in your ".parcelrc" file. * **Don't Do This:** Manually transpile or transform your code outside of Parcel's pipeline without a clear need. * **Why:** Transformers allow you to customize Parcel's build process and support different file types and languages. """json // .parcelrc { "extends": ["@parcel/config-default"], "transformers": { "*.css": ["@parcel/transformer-postcss"] } } """ ### 5.6. Bundler Configuration * **Do This:** Use the ".parcelrc" configuration file to customize Parcel's behavior. * **Don't Do This:** Rely on the default configuration for all projects. * **Why:** ".parcelrc" allows you to fine-tune Parcel's settings for specific projects. ### 5.7. Code Splitting Strategies * **Do This:** Strategically use code splitting to break your application into smaller bundles. Use dynamic imports or entry points for splitting. * **Don't Do This:** Create very large bundles that decrease initial load time and overall performance. * **Why:** Smaller bundles are more efficiently cached and downloaded, leading to faster page loads. Parcel automatically handles code splitting. ### 5.8 Tree Shaking * **Do This:** Write modular code that benefits from Parcel's tree shaking capabilities. Minimize unused exports. * **Don't Do This:** Use large, monolithic modules that are difficult to tree-shake. * **Why:** Tree shaking removes dead code from your bundles, reducing their size. Modern ES module syntax facilitates better tree shaking. """javascript // my-module.js export function usefulFunction() { console.log('This function is useful!'); } export function unusedFunction() { console.log('This function is never called.'); } // index.js import { usefulFunction } from './my-module.js'; usefulFunction(); // Only usefulFunction will be included in the final bundle """ ### 5.9 Minification and Optimization * **Do This:** Ensure your build process includes minification and optimization for production deployments. Parcel automatically handles these steps in production mode. * **Don't Do This:** Skip minification or optimization, especially in production environments. * **Why:** Minification reduces the size of your code, while optimization improves performance. ## 6. Anti-Patterns to Avoid ### 6.1. Global Scope Pollution * **Avoid:** Defining variables in the global scope without a clear reason. * **Why:** Global variables can cause conflicts and make code difficult to maintain. ### 6.2. Premature Optimization * **Avoid:** Optimizing code before identifying actual performance bottlenecks. * **Why:** Premature optimization can waste time and make code more complex without providing a significant benefit. ### 6.3. Magic Numbers * **Avoid:** Using unnamed numeric literals directly in code. * **Why:** Magic numbers make code difficult to understand and maintain. Define constants instead. """javascript // Anti-pattern: Magic number function calculateCircleArea(radius) { return 3.14159 * radius * radius; // What is 3.14159? } // Correct: Using a constant const PI = 3.14159; function calculateCircleArea(radius) { return PI * radius * radius; } """ ### 6.4. Deep Nesting * **Avoid:** Creating deeply nested code structures (e.g., nested if statements). * **Why:** Deep nesting makes code difficult to read and understand. Consider refactoring into smaller, modular functions. ### 6.5. Ignoring Errors * **Avoid:** Catching errors and then ignoring them without proper handling. * **Why:** Ignoring errors can lead to unexpected behavior and make it difficult to debug problems. ## 7. Tooling and Automation ### 7.1. Linters * **Use:** ESLint for JavaScript and Stylelint for CSS to enforce consistent style and detect potential errors. * **Why:** Linters automatically check your code for style violations and potential problems. ### 7.2. Formatters * **Use:** Prettier to automatically format your code according to the configured style rules. Integrate Prettier with your editor for seamless formatting. * **Why:** Formatters automatically ensure consistent code formatting. ### 7.3. Task Runners * **Use:** npm scripts or other task runners (e.g., Yarn, pnpm) to automate build processes, testing, and other common tasks. * **Why:** Task runners make it easier to manage and execute repetitive tasks. ## 8. Updating this Document This document should be reviewed and updated regularly to reflect the latest best practices, new features in Parcel, and any changes in team preferences. It should be treated as a living document, and all team members should be encouraged to contribute to its improvement.