fix: prevent asset conflicts between React and Grid.js versions

Add coexistence checks to all enqueue methods to prevent loading
both React and Grid.js assets simultaneously.

Changes:
- ReactAdmin.php: Only enqueue React assets when ?react=1
- Init.php: Skip Grid.js when React active on admin pages
- Form.php, Coupon.php, Access.php: Restore classic assets when ?react=0
- Customer.php, Product.php, License.php: Add coexistence checks

Now the toggle between Classic and React versions works correctly.

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
dwindown
2026-04-18 17:02:14 +07:00
parent bd9cdac02e
commit e8fbfb14c1
74973 changed files with 6658406 additions and 71 deletions

View File

@@ -0,0 +1,15 @@
/**
* Returns a string with greater-than sign replaced.
*
* Note that if a resolution for Trac#45387 comes to fruition, it is no longer
* necessary for `__unstableEscapeGreaterThan` to exist.
*
* See: https://core.trac.wordpress.org/ticket/45387
*
* @param {string} value Original string.
*
* @return {string} Escaped string.
*/
export default function __unstableEscapeGreaterThan( value ) {
return value.replace( />/g, '&gt;' );
}

123
node_modules/@wordpress/escape-html/src/index.js generated vendored Normal file
View File

@@ -0,0 +1,123 @@
/**
* Internal dependencies
*/
import __unstableEscapeGreaterThan from './escape-greater';
/**
* Regular expression matching invalid attribute names.
*
* "Attribute names must consist of one or more characters other than controls,
* U+0020 SPACE, U+0022 ("), U+0027 ('), U+003E (>), U+002F (/), U+003D (=),
* and noncharacters."
*
* @see https://html.spec.whatwg.org/multipage/syntax.html#attributes-2
*
* @type {RegExp}
*/
const REGEXP_INVALID_ATTRIBUTE_NAME = /[\u007F-\u009F "'>/="\uFDD0-\uFDEF]/;
/**
* Returns a string with ampersands escaped. Note that this is an imperfect
* implementation, where only ampersands which do not appear as a pattern of
* named, decimal, or hexadecimal character references are escaped. Invalid
* named references (i.e. ambiguous ampersand) are still permitted.
*
* @see https://w3c.github.io/html/syntax.html#character-references
* @see https://w3c.github.io/html/syntax.html#ambiguous-ampersand
* @see https://w3c.github.io/html/syntax.html#named-character-references
*
* @param {string} value Original string.
*
* @return {string} Escaped string.
*/
export function escapeAmpersand( value ) {
return value.replace( /&(?!([a-z0-9]+|#[0-9]+|#x[a-f0-9]+);)/gi, '&amp;' );
}
/**
* Returns a string with quotation marks replaced.
*
* @param {string} value Original string.
*
* @return {string} Escaped string.
*/
export function escapeQuotationMark( value ) {
return value.replace( /"/g, '&quot;' );
}
/**
* Returns a string with less-than sign replaced.
*
* @param {string} value Original string.
*
* @return {string} Escaped string.
*/
export function escapeLessThan( value ) {
return value.replace( /</g, '&lt;' );
}
/**
* Returns an escaped attribute value.
*
* @see https://w3c.github.io/html/syntax.html#elements-attributes
*
* "[...] the text cannot contain an ambiguous ampersand [...] must not contain
* any literal U+0022 QUOTATION MARK characters (")"
*
* Note we also escape the greater than symbol, as this is used by wptexturize to
* split HTML strings. This is a WordPress specific fix
*
* Note that if a resolution for Trac#45387 comes to fruition, it is no longer
* necessary for `__unstableEscapeGreaterThan` to be used.
*
* See: https://core.trac.wordpress.org/ticket/45387
*
* @param {string} value Attribute value.
*
* @return {string} Escaped attribute value.
*/
export function escapeAttribute( value ) {
return __unstableEscapeGreaterThan(
escapeQuotationMark( escapeAmpersand( value ) )
);
}
/**
* Returns an escaped HTML element value.
*
* @see https://w3c.github.io/html/syntax.html#writing-html-documents-elements
*
* "the text must not contain the character U+003C LESS-THAN SIGN (<) or an
* ambiguous ampersand."
*
* @param {string} value Element value.
*
* @return {string} Escaped HTML element value.
*/
export function escapeHTML( value ) {
return escapeLessThan( escapeAmpersand( value ) );
}
/**
* Returns an escaped Editable HTML element value. This is different from
* `escapeHTML`, because for editable HTML, ALL ampersands must be escaped in
* order to render the content correctly on the page.
*
* @param {string} value Element value.
*
* @return {string} Escaped HTML element value.
*/
export function escapeEditableHTML( value ) {
return escapeLessThan( value.replace( /&/g, '&amp;' ) );
}
/**
* Returns true if the given attribute name is valid, or false otherwise.
*
* @param {string} name Attribute name to test.
*
* @return {boolean} Whether attribute is valid.
*/
export function isValidAttributeName( name ) {
return ! REGEXP_INVALID_ATTRIBUTE_NAME.test( name );
}

113
node_modules/@wordpress/escape-html/src/test/index.js generated vendored Normal file
View File

@@ -0,0 +1,113 @@
/**
* Internal dependencies
*/
import {
escapeAmpersand,
escapeQuotationMark,
escapeLessThan,
escapeAttribute,
escapeHTML,
isValidAttributeName,
escapeEditableHTML,
} from '../';
import __unstableEscapeGreaterThan from '../escape-greater';
function testUnstableEscapeGreaterThan( implementation ) {
it( 'should escape greater than', () => {
const result = implementation( 'Chicken > Ribs' );
expect( result ).toBe( 'Chicken &gt; Ribs' );
} );
}
function testEscapeAmpersand( implementation ) {
it( 'should escape ampersand', () => {
const result = implementation(
'foo & bar &amp; &AMP; baz &#931; &#bad; &#x3A3; &#X3a3; &#xevil;'
);
expect( result ).toBe(
'foo &amp; bar &amp; &AMP; baz &#931; &amp;#bad; &#x3A3; &#X3a3; &amp;#xevil;'
);
} );
}
function testEscapeQuotationMark( implementation ) {
it( 'should escape quotation mark', () => {
const result = implementation( '"Be gone!"' );
expect( result ).toBe( '&quot;Be gone!&quot;' );
} );
}
function testEscapeLessThan( implementation ) {
it( 'should escape less than', () => {
const result = implementation( 'Chicken < Ribs' );
expect( result ).toBe( 'Chicken &lt; Ribs' );
} );
}
describe( 'escapeAmpersand', () => {
testEscapeAmpersand( escapeAmpersand );
} );
describe( 'escapeQuotationMark', () => {
testEscapeQuotationMark( escapeQuotationMark );
} );
describe( 'escapeLessThan', () => {
testEscapeLessThan( escapeLessThan );
} );
describe( 'escapeGreaterThan', () => {
testUnstableEscapeGreaterThan( __unstableEscapeGreaterThan );
} );
describe( 'escapeAttribute', () => {
testEscapeAmpersand( escapeAttribute );
testEscapeQuotationMark( escapeAttribute );
testUnstableEscapeGreaterThan( escapeAttribute );
} );
describe( 'escapeHTML', () => {
testEscapeAmpersand( escapeHTML );
testEscapeLessThan( escapeHTML );
} );
describe( 'isValidAttributeName', () => {
it( 'should return false for attribute with controls', () => {
const result = isValidAttributeName( 'bad\u007F' );
expect( result ).toBe( false );
} );
it( 'should return false for attribute with non-permitted characters', () => {
const result = isValidAttributeName( 'bad"' );
expect( result ).toBe( false );
} );
it( 'should return false for attribute with noncharacters', () => {
const result = isValidAttributeName( 'bad\uFDD0' );
expect( result ).toBe( false );
} );
it( 'should return true for valid attribute name', () => {
const result = isValidAttributeName( 'good' );
expect( result ).toBe( true );
} );
} );
describe( 'escapeEditableHTML', () => {
it( 'should escape < and all ampersands', () => {
const result = escapeEditableHTML(
'<a href="https://w.org">WP</a> & &lt;strong&gt;'
);
expect( result ).toBe(
'&lt;a href="https://w.org">WP&lt;/a> &amp; &amp;lt;strong&amp;gt;'
);
} );
} );