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,62 @@
/**
* List of translation functions exposed by the `@wordpress/i18n` package.
*
* @type {Set<string>} Translation functions.
*/
const TRANSLATION_FUNCTIONS = new Set( [ '__', '_x', '_n', '_nx' ] );
/**
* Regular expression matching format placeholder syntax.
*
* The pattern for matching named arguments is a naive and incomplete matcher
* against valid JavaScript identifier names.
*
* via Mathias Bynens:
*
* >An identifier must start with $, _, or any character in the Unicode
* >categories “Uppercase letter (Lu)”, “Lowercase letter (Ll)”, “Titlecase
* >letter (Lt)”, “Modifier letter (Lm)”, “Other letter (Lo)”, or “Letter
* >number (Nl)”.
* >
* >The rest of the string can contain the same characters, plus any U+200C zero
* >width non-joiner characters, U+200D zero width joiner characters, and
* >characters in the Unicode categories “Non-spacing mark (Mn)”, “Spacing
* >combining mark (Mc)”, “Decimal digit number (Nd)”, or “Connector
* >punctuation (Pc)”.
*
* If browser support is constrained to those supporting ES2015, this could be
* made more accurate using the `u` flag:
*
* ```
* /^[$_\p{L}\p{Nl}][$_\p{L}\p{Nl}\u200C\u200D\p{Mn}\p{Mc}\p{Nd}\p{Pc}]*$/u;
* ```
*
* @see http://www.pixelbeat.org/programming/gcc/format_specs.html
* @see https://mathiasbynens.be/notes/javascript-identifiers#valid-identifier-names
*
* @type {RegExp}
*/
const REGEXP_SPRINTF_PLACEHOLDER =
/%(((\d+)\$)|(\(([$_a-zA-Z][$_a-zA-Z0-9]*)\)))?[ +0#-]*\d*(\.(\d+|\*))?(ll|[lhqL])?([cduxXefgsp%])/g;
// ▲ ▲ ▲ ▲ ▲ ▲ ▲ type
// │ │ │ │ │ └ Length (unsupported)
// │ │ │ │ └ Precision / max width
// │ │ │ └ Min width (unsupported)
// │ │ └ Flags (unsupported)
// └ Index └ Name (for named arguments)
/**
* "Unordered" means there's no position specifier: '%s', not '%2$s'.
*
* @see https://github.com/WordPress/WordPress-Coding-Standards/blob/2f927b0ba2bfcbffaa8f3251c086e109302d6622/WordPress/Sniffs/WP/I18nSniff.php#L62-L81
*
* @type {RegExp}
*/
const REGEXP_SPRINTF_PLACEHOLDER_UNORDERED =
/(?:(?<!%)%[+-]?(?:(?:0|'.)?-?[0-9]*(?:\.(?:[ 0]|'.)?[0-9]+)?|(?:[ ])?-?[0-9]+(?:\.(?:[ 0]|'.)?[0-9]+)?)[bcdeEfFgGosuxX])/;
module.exports = {
TRANSLATION_FUNCTIONS,
REGEXP_SPRINTF_PLACEHOLDER,
REGEXP_SPRINTF_PLACEHOLDER_UNORDERED,
};

View File

@@ -0,0 +1,34 @@
/**
* Returns the actual text content from an argument passed to a translation function.
*
* @see eslint-plugin-wpcalypso
*
* @param {Object} node A Literal, TemplateLiteral or BinaryExpression (+) node
* @return {string|boolean} The concatenated string or false.
*/
function getTextContentFromNode( node ) {
if ( 'Literal' === node.type ) {
return node.value;
}
if ( 'BinaryExpression' === node.type && '+' === node.operator ) {
const left = getTextContentFromNode( node.left );
const right = getTextContentFromNode( node.right );
if ( left === false || right === false ) {
return false;
}
return left + right;
}
if ( node.type === 'TemplateLiteral' ) {
return node.quasis.map( ( quasis ) => quasis.value.raw ).join( '' );
}
return false;
}
module.exports = {
getTextContentFromNode,
};

View File

@@ -0,0 +1,40 @@
/**
* Given a function name and array of argument Node values,
* returns all arguments except for text domain and number arguments.
*
* @param {string} functionName Function name.
* @param {espree.Node[]} args Espree argument Node objects.
* @param {boolean} includeContext Whether to include the context argument or not.
*
* @return {espree.Node[]} Translate function arguments.
*/
function getTranslateFunctionArgs( functionName, args, includeContext = true ) {
switch ( functionName ) {
case '__':
// __( text, domain ) -> [ text ].
return args.slice( 0, 1 );
case '_x':
// _x( text, context, domain ) -> [ text, context ].
return includeContext ? args.slice( 0, 2 ) : args.slice( 0, 1 );
case '_n':
// _n( single, plural, number, domain ) -> [ single, plural ].
return args.slice( 0, 2 );
case '_nx':
// _nx( single, plural, number, context, domain ) -> [ single, plural, context ].
const result = args.slice( 0, 2 );
if ( includeContext ) {
result.push( args[ 3 ] );
}
return result;
default:
return [];
}
}
module.exports = {
getTranslateFunctionArgs,
};

View File

@@ -0,0 +1,17 @@
/**
* Get the actual translation function name from a CallExpression callee.
*
* Returns the "__" part from __ or i18n.__.
*
* @param {Object} callee
* @return {string} Function name.
*/
function getTranslateFunctionName( callee ) {
return callee.property && callee.property.name
? callee.property.name
: callee.name;
}
module.exports = {
getTranslateFunctionName,
};

22
node_modules/@wordpress/eslint-plugin/utils/index.js generated vendored Normal file
View File

@@ -0,0 +1,22 @@
/**
* Internal dependencies
*/
const {
TRANSLATION_FUNCTIONS,
REGEXP_SPRINTF_PLACEHOLDER,
REGEXP_SPRINTF_PLACEHOLDER_UNORDERED,
} = require( './constants' );
const { getTranslateFunctionArgs } = require( './get-translate-function-args' );
const { getTextContentFromNode } = require( './get-text-content-from-node' );
const { getTranslateFunctionName } = require( './get-translate-function-name' );
const isPackageInstalled = require( './is-package-installed' );
module.exports = {
TRANSLATION_FUNCTIONS,
REGEXP_SPRINTF_PLACEHOLDER,
REGEXP_SPRINTF_PLACEHOLDER_UNORDERED,
getTranslateFunctionArgs,
getTextContentFromNode,
getTranslateFunctionName,
isPackageInstalled,
};

View File

@@ -0,0 +1,16 @@
/**
* Checks whether the passed package name is installed in the project.
*
* @param {string} packageName The name of npm package.
* @return {boolean} Returns true when the package is installed or false otherwise.
*/
const isPackageInstalled = ( packageName ) => {
try {
if ( require.resolve( packageName ) ) {
return true;
}
} catch ( error ) {}
return false;
};
module.exports = isPackageInstalled;

View File

@@ -0,0 +1,18 @@
/**
* Internal dependencies
*/
import isPackageInstalled from '../is-package-installed';
describe( 'isPackageInstalled', () => {
test( 'returns false when package not installed', () => {
const result = isPackageInstalled( 'nonexistent-package-name' );
expect( result ).toBe( false );
} );
test( 'returns true when package installed', () => {
const result = isPackageInstalled( 'eslint' );
expect( result ).toBe( true );
} );
} );