Skip to content

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:js

File 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 output

Module 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 var declarations
  • ❌ Pollute global scope
  • ❌ Use inline event handlers in HTML
  • ❌ Ignore ESLint warnings
  • ❌ Use synchronous AJAX
  • ❌ Manipulate DOM directly in React components
  • ❌ Use eval() or new 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.

Released under the MIT License.