Skip to content

Code Editor: Improve types and options handling#10900

Open
westonruter wants to merge 52 commits intoWordPress:trunkfrom
westonruter:fix/wp-code-editor-types-and-options
Open

Code Editor: Improve types and options handling#10900
westonruter wants to merge 52 commits intoWordPress:trunkfrom
westonruter:fix/wp-code-editor-types-and-options

Conversation

@westonruter
Copy link
Member

@westonruter westonruter commented Feb 10, 2026

This is a follow-up to:

To address:

In subsequent PRs:

  • Remove jQuery dependency
  • Add TypeScript to precommit task and GHA workflow.

Trac ticket: https://core.trac.wordpress.org/ticket/48456

Gemini Summary

This branch introduces comprehensive TypeScript static analysis for JavaScript files using JSDoc, modernizes several Code Editor components, and resolves logical and build-process issues.

1. TypeScript Static Analysis & Type Safety

  • Infrastructure: Introduced tsconfig.json configured for JS checking (checkJs: true) and strict mode (strict: true).
  • Global Definitions: Created typings/globals.d.ts to define global interfaces for wp, jQuery, _, Backbone, and HTMLHint.
  • Enhanced Type Definitions:
    • Defined precise typedefs in code-editor.js (CodeEditorInstance, LintingController, CombinedLintOptions, etc.) to replace generic Object and Function types.
    • Integrated CodeMirror addon types (lint, show-hint) using triple-slash directives.
    • Successfully eliminated almost all generic any usage in favor of specific intersection types and unions.
  • Strict Checking Enablement: Resolved numerous strict-mode issues, including null safety for optional callbacks and properties.

2. Code Modernization & Refactoring

  • code-editor.js:
    • Refactored to use const and let variable declarations.
    • Replaced deprecated APIs: event.keyCode with event.key and substr() with slice().
    • Switched to native DOM APIs where appropriate (Element.classList, Node.contains).
    • Standardized multi-line object literals with trailing commas.
  • htmlhint-kses.js:
    • Refactored to modern ES syntax (const/let, arrow functions, template literals, and for...of loops).
    • Applied Prettier formatting for consistency.

3. Build Process & Reorganization

  • File Relocation: Moved WordPress-maintained CodeMirror extensions (fakejshint.js, htmlhint-kses.js, javascript-lint.js) from vendor/ to lib/ to distinguish them from third-party vendor code.
  • Redundancy Cleanup: Deleted the redundant esprima.js source file; the build now correctly sources the latest version from the npm package.
  • Linter Optimization:
    • Fixed a race condition/double-linting bug during initialization by constructing complete options before editor creation.
    • Removed obsolete nested options.options for linting, following modern CodeMirror standards.
  • ESLint Alignment: Added a local .eslintrc.json in lib/codemirror/ to correctly signal ES module parsing for javascript-lint.js while maintaining compatibility for other files.

4. Dependencies

  • Added @types/jquery, @types/codemirror, @types/underscore, @types/espree, and @types/htmlhint as devDependencies to support the static analysis effort.

All changes have been verified via tsc and jshint.

Fixed PhpStorm Inspections

image image

Use of AI Tools

I used Gemini CLI for granular commits, each of which I reviewed.


This Pull Request is for code review only. Please keep all other discussion in the Trac ticket. Do not merge this Pull Request. See GitHub Pull Requests for Code Review in the Core Handbook for more details.

westonruter and others added 4 commits February 10, 2026 15:30
This update replaces all 'var' declarations with 'const' or 'let' and adds the 'eslint-env es6' directive to ensure proper parsing by ESLint.

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Updates the JSDoc types from {CodeMirror} to {CodeMirror.Editor} for editor instances to correctly reference the instance interface instead of the namespace. This resolves typing issues in IDEs where methods like getOption() were reported as unresolved.

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Replaces the deprecated String.prototype.substr() method with slice() in code-editor.js to adhere to modern JavaScript best practices and resolve editor warnings.

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Updates the keyboard event handling in code-editor.js to use the modern event.key property instead of the deprecated event.keyCode, following current web standards.

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
@github-actions
Copy link

Test using WordPress Playground

The changes in this pull request can previewed and tested using a WordPress Playground instance.

WordPress Playground is an experimental project that creates a full WordPress instance entirely within the browser.

Some things to be aware of

  • The Plugin and Theme Directories cannot be accessed within Playground.
  • All changes will be lost when closing a tab with a Playground instance.
  • All changes will be lost when refreshing the page.
  • A fresh instance is created each time the link below is clicked.
  • Every time this pull request is updated, a new ZIP file containing all changes is created. If changes are not reflected in the Playground instance,
    it's possible that the most recent build failed, or has not completed. Check the list of workflow runs to be sure.

For more details about these limitations and more, check out the Limitations page in the WordPress Playground documentation.

Test this pull request with WordPress Playground.

westonruter and others added 25 commits February 10, 2026 16:03
Resolves "Referenced UMD global variable" warnings in PhpStorm by passing window._ as a parameter to the IIFE and explicitly typing it with JSDoc.

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Fleshes out the CodeEditorSettings type definition with properties for CodeMirror, CSSLint, JSHint, and HTMLHint based on the provided configuration object. Updates all settings parameter references to use this new type for improved editor intelligence.

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Resolves type mismatch errors in PhpStorm by marking nested properties in the CodeEditorSettings typedef as optional. This allows empty objects to be assigned to these properties in defaultSettings while maintaining autocompletion support.

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Updates CodeEditorInstance to use CodeMirror.EditorFromTextArea and explicitly types local variables in wp.codeEditor.initialize to resolve assignability warnings in PhpStorm. Switched to a deep extend for instanceSettings to ensure nested configuration objects are correctly handled.

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Updates the CodeEditorSettings typedef to use the official CodeMirror.EditorConfiguration type for the codemirror property and adds specific string literal types for direction and inputStyle. This resolves type assignability errors in PhpStorm.

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Replaces the internal editor.display.wrapper property with the public editor.getWrapperElement() method to resolve unresolved variable warnings in IDEs.

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Replaces the jQuery.contains() call with the native DOM element.contains() method. This resolves a parameter mismatch error in some IDEs and leverages modern native APIs.

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Replaces the jQuery.hasClass() call with the native classList.contains() method. This follows modern JavaScript practices and removes an unnecessary jQuery dependency for this check.

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Replaces the invalid onUpdateLintingOverridden.apply() call with a direct function call. The previous usage incorrectly attempted to pass three arguments to apply() and used the annotations array as the 'this' context.

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Updates the JSDoc types for editor parameters in configureLinting and configureTabbing to use CodeMirror.EditorFromTextArea. This resolves unresolved method warnings in PhpStorm for getTextArea() and ensures correct typing for instances created via fromTextArea().

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Updates JSDoc types to include optional performLint and showHint methods provided by CodeMirror addons. Adds a check for performLint existence before calling it to ensure safety and resolve IDE warnings.

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Replaces _.filter() with the native Array.prototype.filter() method. This helps PhpStorm correctly infer the type of the annotation object from the LintAnnotation[] array, resolving issues where it was incorrectly identified as a string.

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Defines CodeMirrorState and CodeMirrorTokenState typedefs to document properties added by addons (completionActive) and mode-specific states (htmlState, curState). Applies these types to the CodeMirror instance and token variable to resolve unresolved variable warnings in PhpStorm.

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
- Refines the CodeMirrorEditor typedef as a standalone interface with relaxed string signatures for getOption, setOption, on, and off, resolving string assignability warnings in PhpStorm.
- Introduces CodeMirrorSettings to correctly extend EditorConfiguration with addon-specific properties.
- Updates JSDoc for IIFE parameters and internal functions to use these more precise types.
- Adds the 'focused' property to CodeMirrorState.
- Explicitly types the CodeMirror instance in wp.codeEditor.initialize.

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Initializes shouldAutocomplete to false and uses boolean coercion for the HTML tag name check. This makes the code more robust and ensures the variable consistent with its intended purpose as a boolean flag.

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
… JSDoc.

Add a 'static-analysis:js' script to package.json that runs 'tsc'.
Configure 'tsconfig.json' to check JSDoc in 'src/js/_enqueues/wp/code-editor.js' without emitting files, with settings mirrored from '.jshintrc'.
Add global type definitions in 'typings/globals.d.ts' for 'wp', 'jQuery', '_', and 'Backbone'.
Update 'src/js/_enqueues/wp/code-editor.js' to use global variables directly, satisfying both TypeScript and JSHint.

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Break down CodeEditorSettings into nested typedefs (CSSLintRules, JSHintRules, HTMLHintRules) to resolve parsing errors with dot notation in property names.
Rename 'wp.codeEditor~CodeEditorInstance' to 'CodeEditorInstance' to resolve syntax errors caused by the '~' character.

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
…ings.

- Prefix unused variables '_cm' and '_editor' with an underscore to comply with 'noUnusedLocals' and 'noUnusedParameters'.
- Remove the unused 'CodeMirrorState' typedef.
- Add 'options' property to the 'CodeMirrorEditor' typedef to allow type-safe access to 'codemirror.options.mode'.

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
- Replace 'any' with 'string[]' in the 'gutters' cast for better specificity.
- Use a single direct cast for 'gutters' at declaration.
- Change 'unknown' to 'string' in the 'option' cast for improved readability.

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
- Type 'event' as 'JQuery.MouseDownEvent' to resolve IDE warnings.
- Cast 'event.target' to 'Node' and 'HTMLElement' for safe property access.

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Resolve IDE and TypeScript assignment mismatch errors by explicitly casting the returned editor instance to the custom 'CodeMirrorEditor' type.

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
- Integrate CodeMirror 'lint' and 'show-hint' addon types via triple-slash references in 'typings/globals.d.ts'.
- Define 'CodeMirrorState' and refine 'CodeMirrorEditor' typedefs in 'code-editor.js' to provide better IDE resolution for 'completionActive', 'performLint', and 'showHint'.
- Add a type cast in 'getLintOptions' for safe property access on the lint options object.

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
westonruter and others added 19 commits February 13, 2026 21:50
Move WordPress-specific CodeMirror extensions (fakejshint.js, htmlhint-kses.js, javascript-lint.js) from 'vendor/codemirror/' to 'lib/codemirror/'.
Move 'esprima.js' to the 'vendor/' root and remove the now-empty 'vendor/codemirror/' directory.
Update Grunt and Webpack configurations to reflect these source changes while preserving the existing build output structure in 'wp-includes/'.

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
- Add 'javascript-lint.js' and 'htmlhint-kses.js' to 'tsconfig.json' include list.
- Enable 'allowSyntheticDefaultImports' in 'tsconfig.json' for CodeMirror imports.
- Add 'HTMLHint' global to 'typings/globals.d.ts'.
- Install '@types/espree' for proper type definitions of the dynamically imported linter.
- Refine 'SupportedJSHintOptions' and helper function JSDoc to use 'espree' types, ensuring strict type safety for 'ecmaVersion' without resorting to 'any'.
- Add local '.eslintrc.json' to 'lib/codemirror/' to correctly signal ES module parsing for the IDE.

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Use 'const' and 'let' for variable declarations and employ an arrow function for the 'tagstart' listener to simplify context handling and improve readability.

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Refactor the attribute iteration to use a 'for...of' loop, improving code clarity and following modern JavaScript practices.

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Refactor the error reporting to use template literals for string interpolation, consistent with modern JavaScript standards.

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
- Install '@types/htmlhint' to provide proper type definitions for HTML linter rules.
- Update 'tsconfig.json' and 'typings/globals.d.ts' to use these types.
- Add JSDoc annotations to 'htmlhint-kses.js' to eliminate implicit 'any' warnings and improve type safety.

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
- Define 'WpCodeEditor', 'CodeEditorInstance', and 'LintingController' typedefs to replace generic 'Object' and 'Function' types.
- Move all typedefs to the top of the file to ensure they are properly recognized by the TypeScript compiler and to resolve 'unused' warnings.
- Use specific CodeMirror addon types for linting options.
- Refine function signatures with detailed parameter and return type information.

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
- Set 'strict: true' in 'tsconfig.json'.
- Add required '<any>' type arguments to 'Linter' generic type references in 'code-editor.js'.
- Implement null checks for 'onTabPrevious', 'onTabNext', and 'change.origin' in 'code-editor.js'.
- Pass 'instanceSettings' to 'configureTabbing' to ensure settings are always defined.

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
- Replace 'Linter' with 'LintStateOptions' in typedefs and return types to correctly match CodeMirror's addon configuration structure.
- Refine the 'linterOptions' cast to use a specific union of rules types ('CSSLintRules | JSHintRules | HTMLHintRules') instead of 'any'.
- Update 'getLintOptions' logic to use 'linterOptions.options' for rule assignment, consistent with modern CodeMirror practices.

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
- Remove the redundant 'options.options' nesting in 'getLintOptions', following modern CodeMirror practices (PR 4944).
- Use a comprehensive intersection type for 'linterOptions' that includes 'LintStateOptions', rule unions, and an explicit 'rules' property to satisfy the TypeScript compiler in strict mode.

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
- Define 'CombinedLintOptions' intersection type to precisely model the flat options structure, including standard CodeMirror lint options and specific linter rules.
- Replace generic 'any' types with 'CombinedLintOptions' in typedefs and return types.
- Add defensive checks when configuring HTMLHint rules to satisfy strict null checking.

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
- Update 'updateErrorNotice' signature to accept the base 'Editor' type, improving compatibility with CodeMirror event listeners.
- Register 'updateErrorNotice' directly as a 'blur' listener, eliminating redundant wrapper functions and the need for a type cast.
- Update 'LintingController' typedef to match the revised function signature.

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Ensure consistency with project formatting standards by adding trailing commas to multi-line object literals in 'code-editor.js', 'javascript-lint.js', and 'htmlhint-kses.js'.

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Update '.eslintrc.json' in 'lib/codemirror/' to use 'overrides', ensuring that only 'javascript-lint.js' is parsed as an ES module while maintaining the default 'script' source type for other files in the directory.

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file isn't deleted, but moved to another directory. But it was so heavily updated to use modern JS syntax that it isn't recognized as a rename.

"grunt": "grunt",
"lint:jsdoc": "wp-scripts lint-js",
"lint:jsdoc:fix": "wp-scripts lint-js --fix",
"static-analysis:js": "tsc",
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not currently incorporated into the precommit Grunt task or in any GHA workflow. But I didn't want to expand the scope even more.

westonruter and others added 2 commits February 13, 2026 23:16
Ensure the build process uses the latest version of 'esprima.js' from the npm package, consistent with 'trunk', while keeping the relocated source file in 'src/js/_enqueues/vendor/esprima.js' for development enqueues.

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Delete 'src/js/_enqueues/vendor/esprima.js' as it is redundant; the build process correctly sources the latest version directly from 'node_modules/esprima/dist/esprima.js', consistent with project standards for third-party libraries.

Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file should have been deleted previously as part of #10778

@westonruter

This comment was marked as duplicate.

@westonruter westonruter marked this pull request as ready for review February 14, 2026 07:51
@github-actions
Copy link

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

Core Committers: Use this line as a base for the props when committing in SVN:

Props westonruter.

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@westonruter westonruter requested a review from sirreal February 14, 2026 07:51
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: The coding standards failure is due to an existing JSHint error with fakejshint.js which is now deprecated. The file was unchanged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant