diff --git a/.github/workflows/nodejs.yml b/.github/workflows/nodejs.yml
index 184e90b895..0e09ee0143 100644
--- a/.github/workflows/nodejs.yml
+++ b/.github/workflows/nodejs.yml
@@ -12,5 +12,6 @@ jobs:
uses: zakodium/workflows/.github/workflows/nodejs.yml@nodejs-v1
with:
lint-check-types: false
- disable-tests: true
disable-test-package: true
+ upload-coverage: false
+ node-version-matrix: '[24]'
diff --git a/package-lock.json b/package-lock.json
index 32c32b39a0..a3de81cb76 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -53,6 +53,7 @@
"@rollup/plugin-commonjs": "^29.0.0",
"@rollup/plugin-json": "^6.1.0",
"@rollup/plugin-node-resolve": "^16.0.3",
+ "@types/node": "^24.10.13",
"add-stream": "^1.0.0",
"bower": "^1.8.14",
"conventional-changelog": "^6.0.0",
@@ -70,6 +71,7 @@
"lodash": "^4.17.23",
"mkpath": "^1.0.0",
"prettier": "^3.8.1",
+ "requirejs": "^2.3.8",
"rollup": "^4.59.0",
"rollup-plugin-polyfill-node": "^0.13.0",
"tempfile": "^3.0.0",
@@ -2449,6 +2451,16 @@
"@types/lodash": "*"
}
},
+ "node_modules/@types/node": {
+ "version": "24.10.13",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.13.tgz",
+ "integrity": "sha512-oH72nZRfDv9lADUBSo104Aq7gPHpQZc4BTx38r9xf9pg5LfP6EzSyH2n7qFmmxRQXh7YlUXODcYsg6PuTDSxGg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~7.16.0"
+ }
+ },
"node_modules/@types/normalize-package-data": {
"version": "2.4.4",
"resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz",
@@ -11214,6 +11226,13 @@
"node": "*"
}
},
+ "node_modules/undici-types": {
+ "version": "7.16.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
+ "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/unicode-canonical-property-names-ecmascript": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.1.tgz",
diff --git a/package.json b/package.json
index 1dd5bfa6d0..967a17adc8 100644
--- a/package.json
+++ b/package.json
@@ -19,7 +19,8 @@
"prerelease": "grunt bump:prerelease",
"prettier": "prettier --check src",
"prettier-write": "prettier --write src",
- "test": "npm run eslint && npm run prettier",
+ "test": "npm run eslint && npm run prettier && npm run test-only",
+ "test-only": "node --test tests/**",
"release:minor": "npm run test && grunt bump:minor --release",
"release:patch": "npm run test && grunt bump:patch --release"
},
@@ -44,6 +45,7 @@
"@rollup/plugin-commonjs": "^29.0.0",
"@rollup/plugin-json": "^6.1.0",
"@rollup/plugin-node-resolve": "^16.0.3",
+ "@types/node": "^24.10.13",
"add-stream": "^1.0.0",
"bower": "^1.8.14",
"conventional-changelog": "^6.0.0",
@@ -61,6 +63,7 @@
"lodash": "^4.17.23",
"mkpath": "^1.0.0",
"prettier": "^3.8.1",
+ "requirejs": "^2.3.8",
"rollup": "^4.59.0",
"rollup-plugin-polyfill-node": "^0.13.0",
"tempfile": "^3.0.0",
diff --git a/src/modules/module.js b/src/modules/module.js
index 724fc1ff15..9721f66df2 100644
--- a/src/modules/module.js
+++ b/src/modules/module.js
@@ -221,10 +221,10 @@ define([
toolbar[i].title || ''
}">`;
if (toolbar[i].icon) {
- html += `
`;
+ html += `
`;
}
if (toolbar[i].cssClass) {
- html += ``;
+ html += ``;
}
html += '';
}
diff --git a/src/src/main/entrypoint.js b/src/src/main/entrypoint.js
index 5e8612178e..90891beea0 100644
--- a/src/src/main/entrypoint.js
+++ b/src/src/main/entrypoint.js
@@ -3,6 +3,7 @@
define([
'jquery',
'lodash',
+ 'src/util/jquery_prefilter',
'src/header/header',
'src/util/repository',
'src/main/grid',
@@ -25,6 +26,7 @@ define([
], function (
$,
_,
+ jqueryPrefilter,
Header,
Repository,
Grid,
@@ -42,6 +44,20 @@ define([
Config,
Sandbox,
) {
+ jQuery.htmlPrefilter = (preHtml) => {
+ const { warn, html } = jqueryPrefilter(preHtml);
+ if (warn) {
+ // eslint-disable-next-line no-console
+ console.warn(
+ 'jQuery.htmlPrefilter modified invalid html, make sure to update your html markup',
+ {
+ before: preHtml,
+ after: html,
+ },
+ );
+ }
+ return html;
+ };
var _viewLoaded, _dataLoaded, _modulesSet;
var RepositoryData = new Repository(),
diff --git a/src/src/util/jquery_prefilter.js b/src/src/util/jquery_prefilter.js
new file mode 100644
index 0000000000..2ac5f1fd0d
--- /dev/null
+++ b/src/src/util/jquery_prefilter.js
@@ -0,0 +1,28 @@
+'use strict';
+
+define(() => {
+ // https://jquery.com/upgrade-guide/3.5/
+ const rxhtmlTag =
+ /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^/\0>\u0020\t\r\n\f]*)[^>]*)\/>/gi;
+
+ return function jqueryPrefilter(html) {
+ // Self-closing tags are invalid except for void elements like input
+ const filteredHtml = html.replaceAll(rxhtmlTag, '<$1>$2>');
+ if (filteredHtml !== html) {
+ // Ignore svg content because it is actually XML
+ const cleanedHtml = html.replaceAll(/