#48456 closed enhancement (fixed)
Update CodeMirror to latest v5 version
| Reported by: |
|
Owned by: |
|
|---|---|---|---|
| Milestone: | 7.0 | Priority: | normal |
| Severity: | normal | Version: | 4.9 |
| Component: | External Libraries | Keywords: | early has-patch needs-testing |
| Focuses: | javascript | Cc: |
Description
From what I can see in the JS file, we are using CodeMirror version 5.29.1 which is over 2 years old, according to https://codemirror.net/doc/releases.html
A lot has happened since then, with the current release being 5.49.2.
We should update and set a good example. Getting this in early for 5.4 will allow for testing.
Change History (20)
This ticket was mentioned in Slack in #core by david.baumwald. View the logs.
6 years ago
This ticket was mentioned in Slack in #core by david.baumwald. View the logs.
6 years ago
#5
@
6 years ago
- Milestone changed from 5.4 to Future Release
This ticket still needs patch and soak time. With 5.4 Beta 1 approaching, this is being moved to Future Release. If any maintainer or committer feels this can be resolved in time or wishes to assume ownership during a specific cycle, feel free to update the milestone accordingly.
#6
@
3 years ago
Just noting, the current version is now 5.65.9.
This ticket was mentioned in Slack in #core by jorbin. View the logs.
9 months ago
#8
@
9 months ago
When this is worked on, testing to see if #42822 is still valid should be included.
#9
@
4 weeks ago
- Milestone changed from Future Release to 7.0
Latest is now 5.65.18. This was released Sep 20, 2024.
Nevertheless, CodeMirror v5 is now legacy. What would be preferable (but much riskier and more work) is updating to v6.
This ticket was mentioned in PR #10778 on WordPress/wordpress-develop by @westonruter.
3 weeks ago
#12
- Keywords has-patch added; needs-patch removed
Trac ticket: https://core.trac.wordpress.org/ticket/48456
For Gemini CLI, I provided the following spec/plan:
From commit dbace684e214c434ede007bc69d964155d18fcf6 you can see that CodeMirror was originally introduced. In a91d3980dfee019e09b91919dac290716082203e it was integrated into the Customizer. Note also in 5bb7fc10c5185f26eb52cfa2c39bcbb80d65173a that JSHint was removed due to license compatibility. This was many years ago. The version of CodeMirror installed then was 5.29.1-alpha-ee20357. However, the latest version of CodeMirror v5 is 5.65.18 as seen at <https://github.com/codemirror/codemirror5/releases>. Granted, there is now a complete rewrite in CodeMirror v6, but before we upgrade to v6 we should first upgrade to the latest v5, as requested in <https://core.trac.wordpress.org/ticket/48456>. Part of the reason why we didn't upgrade previously is that there was no script to facilitate upgrading.
I want you to come up with a plan for how we might upgrade to the latest version of CodeMirror v5, taking into account all of the release notes for all versions of CodeMirror since 5.29.1, or rather, since 5.29.0 <https://github.com/codemirror/codemirror5/releases/tag/5.29.0>. I don't actually see that 5.29.1 was ever released since the actual release that included commit <https://github.com/codemirror/codemirror5/commit/ee20357> was 5.30.0. In any case, I need a plan to upgrade CodeMirror which is currently located in @src/js/_enqueues/vendor/codemirror/ (where initially it was located at @src/wp-includes/js/codemirror but with ef37f002ee3189cbe647a853d287eb4e5e1b0fba there was a build step introduced which caused files to be moved around).
So I need a plan for:
- A new Grunt task like
codemirror:updatewhich will handle pulling down the latest version of CodeMirror v5 and to update the versions forwp-codemirrorscript/style in @src/wp-includes/script-loader.php as well as the other script handles in that directory, specifically the add-ons forjshint,jsonlint,htmlhint,csslint. Thejshintadd-on was replaced in 5bb7fc10c5185f26eb52cfa2c39bcbb80d65173a withesprima, so that is out of scope for updating.- Updating dependencies of the
wp-codemirrorscript to account for any back-compat breakages. Note that thecode-editorscript/style is the main dependency. Take care to note how thecode-editordependency is used, namely in thewp-theme-plugin-editorscript, thewp_enqueue_code_editor()function where the script gets enqueued. Thewp_enqueue_code_editor()function is used in @src/wp-includes/widgets/class-wp-widget-custom-html.php and src/wp-includes/customize/class-wp-customize-code-editor-control.php as well as in @src/wp-admin/theme-editor.php and @src/wp-admin/plugin-editor.php.You will not make any changes to the codebase for now. Only come up with a plan. Remember that backwards compatibility is paramount in WordPress, so take care to consider how existing ecosystem code may be extending CodeMirror in 5.29.1-alpha-ee20357 which has been present in core for 9 years.
I also provided https://github.com/WordPress/better-code-editing as context for where the CodeMirror integration originally came from.
I then iterated on the plan with Gemini for awhile, and came up with the following:
# Plan for Upgrading CodeMirror to v5.65.18
This plan upgrades CodeMirror to version
5.65.18by transitioning from a manually bundled artifact to a build-generated asset sourced from npm. It mirrors the integration strategy of thebetter-code-editingplugin (the feature's origin) while adapting to the modernwordpress-developbuild system (Webpack) and updating dependencies to their latest compatible versions.
## 1. Dependency Management [COMPLETED]
Addcodemirrorand required linters topackage.jsondependencies. We are updating these to the latest versions compatible with CodeMirror 5, rather than using the outdated versions from the original plugin.
- Action: Update
package.jsondependencieswith:
"codemirror": "5.65.18""csslint": "1.0.5""htmlhint": "1.1.4""jsonlint": "1.6.3""esprima": "4.0.1"- Note:
jshintis excluded because Core uses a customfakejshint.jswrapper (powered byesprima) instead of the standard library.## 2. Source File Cleanup [COMPLETED]
Remove the pre-bundled CodeMirror files and standalone linters from the source tree, as they will be replaced by build artifacts.
- Action: Delete from
src/js/_enqueues/vendor/codemirror/:
codemirror.min.jscodemirror.min.csscsslint.jsesprima.jshtmlhint.jsjsonlint.js- Keep:
fakejshint.jsandhtmlhint-kses.js(Core-specific wrappers).## 3. Build Tooling Implementation [COMPLETED]
### 3.1. Webpack Entry Point (
tools/vendors/codemirror-entry.js)
Create an entry point that replicates the originalcodemirror.manifest.js. This file aggregates the core library, modes, keymaps, and addons into a single bundle and exposes the global object.
- Content:
// Define CodeMirror globally before other imports to ensure they attach to it. var CodeMirror = require( 'codemirror/lib/codemirror' ); // Keymaps require( 'codemirror/keymap/emacs' ); require( 'codemirror/keymap/sublime' ); require( 'codemirror/keymap/vim' ); // Addons (Hinting) require( 'codemirror/addon/hint/show-hint' ); require( 'codemirror/addon/hint/anyword-hint' ); require( 'codemirror/addon/hint/css-hint' ); require( 'codemirror/addon/hint/html-hint' ); require( 'codemirror/addon/hint/javascript-hint' ); require( 'codemirror/addon/hint/sql-hint' ); require( 'codemirror/addon/hint/xml-hint' ); // Addons (Linting) require( 'codemirror/addon/lint/lint' ); require( 'codemirror/addon/lint/css-lint' ); require( 'codemirror/addon/lint/html-lint' ); require( 'codemirror/addon/lint/javascript-lint' ); require( 'codemirror/addon/lint/json-lint' ); // Addons (Other) require( 'codemirror/addon/comment/comment' ); require( 'codemirror/addon/comment/continuecomment' ); require( 'codemirror/addon/fold/xml-fold' ); require( 'codemirror/addon/mode/overlay' ); require( 'codemirror/addon/edit/closebrackets' ); require( 'codemirror/addon/edit/closetag' ); require( 'codemirror/addon/edit/continuelist' ); require( 'codemirror/addon/edit/matchbrackets' ); require( 'codemirror/addon/edit/matchtags' ); require( 'codemirror/addon/edit/trailingspace' ); require( 'codemirror/addon/dialog/dialog' ); require( 'codemirror/addon/display/autorefresh' ); require( 'codemirror/addon/display/fullscreen' ); require( 'codemirror/addon/display/panel' ); require( 'codemirror/addon/display/placeholder' ); require( 'codemirror/addon/display/rulers' ); require( 'codemirror/addon/fold/brace-fold' ); require( 'codemirror/addon/fold/comment-fold' ); require( 'codemirror/addon/fold/foldcode' ); require( 'codemirror/addon/fold/foldgutter' ); require( 'codemirror/addon/fold/indent-fold' ); require( 'codemirror/addon/fold/markdown-fold' ); require( 'codemirror/addon/merge/merge' ); require( 'codemirror/addon/mode/loadmode' ); require( 'codemirror/addon/mode/multiplex' ); require( 'codemirror/addon/mode/simple' ); require( 'codemirror/addon/runmode/runmode' ); require( 'codemirror/addon/runmode/colorize' ); require( 'codemirror/addon/runmode/runmode-standalone' ); require( 'codemirror/addon/scroll/annotatescrollbar' ); require( 'codemirror/addon/scroll/scrollpastend' ); require( 'codemirror/addon/scroll/simplescrollbars' ); require( 'codemirror/addon/search/search' ); require( 'codemirror/addon/search/jump-to-line' ); require( 'codemirror/addon/search/match-highlighter' ); require( 'codemirror/addon/search/matchesonscrollbar' ); require( 'codemirror/addon/search/searchcursor' ); require( 'codemirror/addon/tern/tern' ); require( 'codemirror/addon/tern/worker' ); require( 'codemirror/addon/wrap/hardwrap' ); require( 'codemirror/addon/selection/active-line' ); require( 'codemirror/addon/selection/mark-selection' ); require( 'codemirror/addon/selection/selection-pointer' ); // Modes require( 'codemirror/mode/meta' ); require( 'codemirror/mode/clike/clike' ); require( 'codemirror/mode/css/css' ); require( 'codemirror/mode/diff/diff' ); require( 'codemirror/mode/htmlmixed/htmlmixed' ); require( 'codemirror/mode/http/http' ); require( 'codemirror/mode/javascript/javascript' ); require( 'codemirror/mode/jsx/jsx' ); require( 'codemirror/mode/markdown/markdown' ); require( 'codemirror/mode/gfm/gfm' ); require( 'codemirror/mode/nginx/nginx' ); require( 'codemirror/mode/php/php' ); require( 'codemirror/mode/sass/sass' ); require( 'codemirror/mode/shell/shell' ); require( 'codemirror/mode/sql/sql' ); require( 'codemirror/mode/xml/xml' ); require( 'codemirror/mode/yaml/yaml' ); // Global Exposure if ( ! window.wp ) { window.wp = {}; } window.wp.CodeMirror = CodeMirror;
### 3.2. Webpack Configuration [COMPLETED]
Createtools/webpack/codemirror.config.jsto handle the JS bundling.
- Input:
tools/vendors/codemirror-entry.js- Output:
src/wp-includes/js/codemirror/codemirror.min.js### 3.3. Grunt Configuration (
Gruntfile.js) [COMPLETED]
UpdateGruntfile.jsto manage the full build process for CodeMirror assets.
- Webpack Task: Added a
codemirrortarget pointing to the new config.- CSS Concatenation: Added a
concat:codemirrortarget to bundle styles intocodemirror.css:
node_modules/codemirror/lib/codemirror.cssnode_modules/codemirror/addon/hint/show-hint.cssnode_modules/codemirror/addon/lint/lint.cssnode_modules/codemirror/addon/dialog/dialog.cssnode_modules/codemirror/addon/display/fullscreen.cssnode_modules/codemirror/addon/fold/foldgutter.cssnode_modules/codemirror/addon/merge/merge.cssnode_modules/codemirror/addon/scroll/simplescrollbars.cssnode_modules/codemirror/addon/search/matchesonscrollbar.cssnode_modules/codemirror/addon/tern/tern.css- CSS Minification: Added a
cssmin:codemirrortarget.- Copy Task: Added a
copy:codemirrortarget to copy linters and preserve custom wrappers.- Build Integration: Added
build:codemirrorto the mainbuildtask.IMPORTANT: Build artifacts generated in
src/wp-includes/js/codemirror/are ignored by Git (via the/src/wp-includes/jsrule in.gitignore). These files must NOT be staged or committed.
## 4. Script Loader Update (
src/wp-includes/script-loader.php) [COMPLETED]
Update the registered versions for the following handles to match the new packages:
wp-codemirror(v5.65.18)csslint(v1.0.5)htmlhint(v1.1.4)jsonlint(v1.6.3)esprima(v4.0.1)## 5. Verification [COMPLETED]
- Build: Run
grunt build:codemirror --devand verify existence of:
src/wp-includes/js/codemirror/codemirror.min.jssrc/wp-includes/js/codemirror/codemirror.min.css- Linter files in the same directory.
- Runtime: Check
window.CodeMirror.versionandwindow.wp.CodeMirrorin the browser console.- Functionality:
- Test Theme Editor (syntax highlighting, line numbers).
- Test Custom CSS (linting warnings).
- Test Custom HTML Widget.
I then told Gemini to execute the plan, creating commits for each logical step.
# Initial Gemini Review
The changes look solid and represent a significant improvement in how CodeMirror is managed in WordPress Core, moving from committed artifacts to a proper build process backed by npm dependencies.
I have a few items for you to address:
- License Header in JS Bundle: The previous
codemirror.min.jsincluded a comment block with license information. Your new Webpack config (tools/webpack/codemirror.config.js) usesTerserPluginwithextractComments: falseandcomments: false, which strips all comments.
- Action: Please configure
TerserPluginto preserve@licenseor@preservecomments, or usewebpack.BannerPluginto prepend the CodeMirror license information to the generatedcodemirror.min.js.
Addressed in 2fb47e41d624d90749a7fbe8dfb46027db671115, 3fb228150ce80d7439e6497eb96aa51741df8d72, 79295b3b6000c31bc4b1ea85459bcb6471c034ab
- CSS Source Mapping/Banner: In
Gruntfile.js, you added a process function toconcat:codemirror:process: function( src, filepath ) { return '/* Source: ' + filepath.replace( 'node_modules/', '' ) + '*/\n' + src; }
This adds comments to the concatenated CSS file (
codemirror.css), but thecssmin:codemirrortask will likely strip these comments when generatingcodemirror.min.css.
- Action: Verify if these source comments are intended to remain in the minified file. If not, this is fine for the unminified version, but ensure the license is preserved here as well if required.
Yes. These comments are useful for when the unminified file is erved.
- Completeness of Addons: The list of requires in
tools/vendors/codemirror-entry.jsis extensive.
- Observation: Please double-check that this list matches the functionality provided by the previous
5.29.1bundle. Specifically, ensure no used modes or addons (likematch-highlighteror specific keymaps) were accidentally omitted if they were previously present.
fakejshint.jsandhtmlhint-kses.jsLocation:
- You are copying these from
SOURCE_DIR + 'js/_enqueues/vendor/codemirror/'toWORKING_DIR + 'wp-includes/js/codemirror/'.- Observation: Since these are Core-specific wrappers and not external vendor code (strictly speaking), ensure they are tracked correctly in git. The delete command removed the *other* files in that directory, leaving these two. This seems correct, but just confirming their source location is intended to stay in
vendor.
- Script Loader Versions:
- In
src/wp-includes/script-loader.php, you updated the versions.- Nit:
jshintis still listed as2.9.5. Since it relies onesprima(which you updated to4.0.1) andfakejshint.js, verify iffakejshint.jsneeds any updates to work with the neweresprimaversion.Overall, excellent work modernizing this dependency chain. Fix the license header issue, and it should be ready.
#13
@
3 weeks ago
- Keywords needs-testing added
I'm really happy with how quickly this upgrade came together, thanks to the assists from Gemini and Copilot. This is ready for review. I've done initial smoke tests to confirm the syntax highlighting and linting continues to work as expected in the current core surfaces: theme/plugin file editors, Additional CSS in Customizer, and Custom HTML widget (legacy).
@TobiasBg commented on PR #10778:
3 weeks ago
#14
I tested this PR in Playground, by installing my plugin (TablePress), which embeds CodeMirror on its "Plugin Options" screen, for adding CSS code.
Everything worked fine with the new version, no issues :-)
#15
@
3 weeks ago
- Summary changed from Update CodeMirror to latest version to Update CodeMirror to latest v5 version
#16
@
3 weeks ago
Once this is fixed I'm keen to explore the v6 upgrade, which will be much more liable to cause backwards compatibility breakages for extensions. But hopefully we can find shims to help bridge the gap.
#17
@
3 weeks ago
In addition to the PR attached to this ticket, I've opened a sub-PR to replace Esprima with Espree in the JavaScript linter: https://github.com/westonruter/wordpress-develop/pull/4 This is important because Esprima does not support ES6, so any scripts using const, let and the like currently show up as syntax errors.
This deprecates the use of fakejshint.js while at the same time registering espree as a script module. This then gets imported dynamically by a new javascript-lint.js which replaces that module in the CodeMirror build. Note that since classic scripts cannot explicitly depend on script modules (#61500), this is currently implemented as a bit of a hack. A new wp-codemirror script module is registered with an empty string src. This module has espree added as a dynamic dependency. The empty string has the effect of preventing the wp-codemirror script module from being printed, while at the same time any of its dependencies still get added to the importmap.
I expect to file a new ticket for this Esprima to Espree upgrade, once CodeMirror v5 is upgraded.
This ticket was mentioned in PR #10900 on WordPress/wordpress-develop by @westonruter.
2 days ago
#20
This is a follow-up to:
- https://github.com/WordPress/wordpress-develop/pull/10806
- https://github.com/WordPress/wordpress-develop/pull/10778
To address:
- [x] Fix TypeScript issues with
code-editor.js. - [x] Address https://github.com/WordPress/wordpress-develop/pull/10806#discussion_r2787837736. See dd7579f62c2733dc32b9b23e4d56ac462a0a83f2.
In subsequent PR:
- [ ] Remove jQuery dependency
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.jsonconfigured for JS checking (checkJs: true) and strict mode (strict: true). - Global Definitions: Created
typings/globals.d.tsto define global interfaces forwp,jQuery,_,Backbone, andHTMLHint. - Enhanced Type Definitions:
- Defined precise typedefs in
code-editor.js(CodeEditorInstance,LintingController,CombinedLintOptions, etc.) to replace genericObjectandFunctiontypes. - Integrated CodeMirror addon types (lint, show-hint) using triple-slash directives.
- Successfully eliminated almost all generic
anyusage in favor of specific intersection types and unions.
- Defined precise typedefs in
- 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
constandletvariable declarations. - Replaced deprecated APIs:
event.keyCodewithevent.keyandsubstr()withslice(). - Switched to native DOM APIs where appropriate (
Element.classList,Node.contains). - Standardized multi-line object literals with trailing commas.
- Refactored to use
-
htmlhint-kses.js:- Refactored to modern ES syntax (const/let, arrow functions, template literals, and
for...ofloops). - Applied Prettier formatting for consistency.
- Refactored to modern ES syntax (const/let, arrow functions, template literals, and
#### 3. Build Process & Reorganization
- File Relocation: Moved WordPress-maintained CodeMirror extensions (
fakejshint.js,htmlhint-kses.js,javascript-lint.js) fromvendor/tolib/to distinguish them from third-party vendor code. - Redundancy Cleanup: Deleted the redundant
esprima.jssource 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.optionsfor linting, following modern CodeMirror standards.
- ESLint Alignment: Added a local
.eslintrc.jsoninlib/codemirror/to correctly signal ES module parsing forjavascript-lint.jswhile maintaining compatibility for other files.
#### 4. Dependencies
- Added
@types/jquery,@types/codemirror,@types/underscore,@types/espree, and@types/htmlhintas devDependencies to support the static analysis effort.
All changes have been verified via tsc and jshint.
## Fixed PhpStorm Inspections
## Use of AI Tools
I used Gemini CLI for granular commits, each of which I reviewed.
Thanks for this one, @TobiasBg!
Preferably, #41870 would be fixed before/at the same time as updating CodeMirror so that updating in the future is much easier.
Though that is not a steadfast blocker, if you are interested in this, I recommend looking at how to help out there first.