JavaScript Coding Standards for Wikit
Overview
JavaScript in Wikit follows modern ES6+ standards, WordPress block development patterns, and integrates with the WordPress JavaScript ecosystem. All JavaScript must be compatible with the build process and follow consistent patterns.
Build System
Required Tools
- Node.js: >= 18.0.0
- npm: For package management
- Webpack: Module bundling
- Babel: ES6+ transpilation
- ESLint: Code linting
Build Commands
bash
# Development build with watching
npm start
# Production build
npm run build
# Watch JavaScript only
npm run watch:js
# Build JavaScript only
npm run build:jsFile Organization
Directory Structure
wikit-theme/
├── assets/
│ └── js/
│ ├── modules/ # ES6 modules
│ ├── vendor/ # Third-party scripts
│ └── main.js # Entry point
├── block-editor/
│ └── blocks/
│ └── {block-name}/
│ ├── index.js # Block registration
│ ├── edit.js # Editor component
│ └── save.js # Save component
└── dist/
└── js/ # Compiled outputModule System
ES6 Modules
javascript
// CORRECT: Named exports
export const utilityFunction = () => {
// Implementation
};
export class FeatureClass {
constructor() {
// Initialize
}
}
// CORRECT: Default export for main class
export default class MainFeature {
// Implementation
}
// CORRECT: Import statements
import { utilityFunction } from './modules/utilities';
import MainFeature from './modules/MainFeature';WordPress Dependencies
javascript
// CORRECT: WordPress packages
import { __ } from '@wordpress/i18n';
import { registerBlockType } from '@wordpress/blocks';
import { useSelect, useDispatch } from '@wordpress/data';
import { useState, useEffect } from '@wordpress/element';
import {
PanelBody,
TextControl,
SelectControl
} from '@wordpress/components';
// CORRECT: Using wp global
const { apiFetch } = wp;
const { addFilter } = wp.hooks;Block Development
Block Registration
javascript
/**
* Block: Custom Block
*/
import { registerBlockType } from '@wordpress/blocks';
import { __ } from '@wordpress/i18n';
import Edit from './edit';
import save from './save';
import metadata from './block.json';
registerBlockType( metadata.name, {
/**
* @see ./edit.js
*/
edit: Edit,
/**
* @see ./save.js
*/
save,
} );Edit Component
javascript
/**
* Editor Component
*/
import { __ } from '@wordpress/i18n';
import { useBlockProps, InspectorControls } from '@wordpress/block-editor';
import { PanelBody, TextControl } from '@wordpress/components';
export default function Edit( { attributes, setAttributes } ) {
const { content, alignment } = attributes;
const blockProps = useBlockProps( {
className: `align-${alignment}`,
} );
return (
<>
<InspectorControls>
<PanelBody title={ __( 'Settings', 'wikit' ) }>
<TextControl
label={ __( 'Content', 'wikit' ) }
value={ content }
onChange={ ( value ) => setAttributes( { content: value } ) }
/>
</PanelBody>
</InspectorControls>
<div { ...blockProps }>
{ content || __( 'Add content...', 'wikit' ) }
</div>
</>
);
}Save Component
javascript
/**
* Save Component
*/
import { useBlockProps } from '@wordpress/block-editor';
export default function save( { attributes } ) {
const { content, alignment } = attributes;
const blockProps = useBlockProps.save( {
className: `align-${alignment}`,
} );
return (
<div { ...blockProps }>
{ content }
</div>
);
}Code Style
Variables and Constants
javascript
// CORRECT: const for constants
const API_ENDPOINT = '/wp-json/wdg/v1/';
const MAX_ITEMS = 100;
// CORRECT: let for reassignable variables
let currentIndex = 0;
let isLoading = false;
// INCORRECT: var (never use)
var oldStyle = 'deprecated';
// CORRECT: Destructuring
const { title, content, meta } = post;
const [ first, second, ...rest ] = array;Functions
javascript
// CORRECT: Arrow functions for callbacks
const filtered = items.filter( item => item.active );
// CORRECT: Arrow function with block
const processData = ( data ) => {
const processed = data.map( item => item.value );
return processed.filter( Boolean );
};
// CORRECT: Regular function for methods
class Component {
handleClick() {
// Method implementation
}
}
// CORRECT: Async/await
const fetchData = async () => {
try {
const response = await apiFetch( { path: '/wp/v2/posts' } );
return response;
} catch ( error ) {
console.error( 'Fetch failed:', error );
}
};Object and Arrays
javascript
// CORRECT: Object shorthand
const name = 'Product';
const price = 99;
const product = { name, price };
// CORRECT: Spread operator
const extended = { ...defaults, ...options };
const combined = [ ...array1, ...array2 ];
// CORRECT: Template literals
const message = `Product ${name} costs $${price}`;
// CORRECT: Optional chaining
const city = user?.address?.city ?? 'Unknown';React/WordPress Components
Functional Components
javascript
// CORRECT: Functional component with hooks
const CustomComponent = ( { title, onUpdate } ) => {
const [ value, setValue ] = useState( '' );
useEffect( () => {
// Side effect
return () => {
// Cleanup
};
}, [ value ] );
return (
<div className="custom-component">
<h2>{ title }</h2>
<input
type="text"
value={ value }
onChange={ ( e ) => setValue( e.target.value ) }
/>
</div>
);
};WordPress Data
javascript
// CORRECT: Using WordPress data stores
import { useSelect, useDispatch } from '@wordpress/data';
const PostSelector = () => {
const posts = useSelect( ( select ) => {
return select( 'core' ).getEntityRecords( 'postType', 'post', {
per_page: 10,
_embed: true,
} );
}, [] );
const { editPost } = useDispatch( 'core/editor' );
return (
<SelectControl
label="Select Post"
options={ posts?.map( post => ( {
label: post.title.rendered,
value: post.id,
} ) ) || [] }
onChange={ ( postId ) => editPost( { meta: { selected_post: postId } } ) }
/>
);
};DOM Manipulation
Event Listeners
javascript
// CORRECT: Modern event handling
document.addEventListener( 'DOMContentLoaded', () => {
initializeFeatures();
} );
// CORRECT: Event delegation
document.addEventListener( 'click', ( event ) => {
if ( event.target.matches( '.button-class' ) ) {
handleButtonClick( event );
}
} );
// CORRECT: Remove listeners when needed
const controller = new AbortController();
element.addEventListener( 'click', handler, { signal: controller.signal } );
// Later: controller.abort();jQuery Usage
javascript
// ACCEPTABLE: When jQuery is required (legacy)
jQuery( document ).ready( function( $ ) {
$( '.selector' ).on( 'click', function() {
// Handler
} );
} );
// PREFERRED: Vanilla JavaScript
document.querySelectorAll( '.selector' ).forEach( element => {
element.addEventListener( 'click', handleClick );
} );AJAX and API
WordPress REST API
javascript
// CORRECT: Using apiFetch
import apiFetch from '@wordpress/api-fetch';
const fetchPosts = async () => {
try {
const posts = await apiFetch( {
path: '/wp/v2/posts',
method: 'GET',
data: {
per_page: 10,
orderby: 'date',
},
} );
return posts;
} catch ( error ) {
console.error( 'Error fetching posts:', error );
return [];
}
};
// CORRECT: With nonce
apiFetch.use( apiFetch.createNonceMiddleware( wdgData.nonce ) );AJAX Requests
javascript
// CORRECT: Modern fetch API
const submitForm = async ( formData ) => {
const response = await fetch( wdgData.ajaxUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams( {
action: 'wdg_process',
nonce: wdgData.nonce,
...formData,
} ),
} );
return response.json();
};Code Quality
ESLint Configuration
json
{
"extends": [
"plugin:@wordpress/eslint-plugin/recommended"
],
"env": {
"browser": true,
"es6": true
},
"globals": {
"wp": "readonly",
"jQuery": "readonly"
},
"rules": {
"no-console": [ "error", { "allow": [ "warn", "error" ] } ],
"prefer-const": "error",
"no-var": "error"
}
}Comments and Documentation
javascript
/**
* Process data and return formatted result
*
* @param {Object} data - Input data object
* @param {string} data.title - Title field
* @param {number} data.count - Count value
* @returns {Promise<Object>} Processed data
*/
const processData = async ( data ) => {
// Validate input
if ( ! data?.title ) {
throw new Error( 'Title is required' );
}
// Process and return
return {
...data,
processed: true,
timestamp: Date.now(),
};
};Performance
Optimization Patterns
javascript
// CORRECT: Debounce expensive operations
const debounce = ( func, wait ) => {
let timeout;
return ( ...args ) => {
clearTimeout( timeout );
timeout = setTimeout( () => func( ...args ), wait );
};
};
const handleSearch = debounce( ( query ) => {
// Search logic
}, 300 );
// CORRECT: Memoization for expensive computations
import { useMemo } from '@wordpress/element';
const ExpensiveComponent = ( { data } ) => {
const processed = useMemo( () => {
return data.map( complexTransformation );
}, [ data ] );
return <div>{ processed }</div>;
};
// CORRECT: Lazy loading
const LazyComponent = lazy( () => import( './HeavyComponent' ) );Do's and Don'ts
DO:
- ✅ Use ES6+ features
- ✅ Follow WordPress JavaScript standards
- ✅ Use WordPress packages when available
- ✅ Handle errors properly
- ✅ Use async/await for asynchronous code
- ✅ Implement proper loading states
- ✅ Clean up event listeners and subscriptions
- ✅ Use TypeScript types in JSDoc comments
DON'T:
- ❌ Use
vardeclarations - ❌ Pollute global scope
- ❌ Use inline event handlers in HTML
- ❌ Ignore ESLint warnings
- ❌ Use synchronous AJAX
- ❌ Manipulate DOM directly in React components
- ❌ Use
eval()ornew Function() - ❌ Commit
console.log()statements
Testing
Unit Testing
javascript
// Using Jest
describe( 'utilityFunction', () => {
it( 'should process data correctly', () => {
const input = { value: 10 };
const result = utilityFunction( input );
expect( result ).toEqual( { value: 10, processed: true } );
} );
} );
// Testing React components
import { render, screen } from '@testing-library/react';
test( 'renders component', () => {
render( <Component title="Test" /> );
expect( screen.getByText( 'Test' ) ).toBeInTheDocument();
} );Build Output
The build process outputs to:
/dist/js/- Compiled JavaScript/dist/css/- Compiled CSS- Source maps for debugging in development
Always commit built files for production deployments.