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,32 @@
module.exports = {
plugins: [ '@wordpress' ],
rules: {
'@wordpress/no-unused-vars-before-return': 'error',
'@wordpress/no-base-control-with-label-without-id': 'error',
'@wordpress/no-unguarded-get-range-at': 'error',
'@wordpress/no-global-active-element': 'error',
'@wordpress/no-global-get-selection': 'error',
'@wordpress/no-unsafe-wp-apis': 'error',
},
overrides: [
{
files: [ '*.native.js' ],
rules: {
'@wordpress/no-base-control-with-label-without-id': 'off',
'@wordpress/i18n-no-flanking-whitespace': 'error',
'@wordpress/i18n-hyphenated-range': 'error',
},
},
{
files: [
'*.test.js',
'**/test/*.js',
'packages/e2e-test-utils/**/*.js',
],
rules: {
'@wordpress/no-global-active-element': 'off',
'@wordpress/no-global-get-selection': 'off',
},
},
],
};

89
node_modules/@wordpress/eslint-plugin/configs/es5.js generated vendored Normal file
View File

@@ -0,0 +1,89 @@
module.exports = {
extends: [ require.resolve( './jsdoc.js' ) ],
rules: {
'array-bracket-spacing': [ 'error', 'always' ],
'array-callback-return': 'error',
'brace-style': [ 'error', '1tbs' ],
camelcase: [
'error',
{
properties: 'never',
},
],
'comma-dangle': [ 'error', 'always-multiline' ],
'comma-spacing': 'error',
'comma-style': [ 'error', 'last' ],
curly: [ 'error', 'all' ],
'dot-notation': 'error',
'eol-last': 'error',
eqeqeq: 'error',
'func-call-spacing': 'error',
indent: [ 'error', 'tab', { SwitchCase: 1 } ],
'key-spacing': 'error',
'keyword-spacing': 'error',
'linebreak-style': [ 'error', 'unix' ],
'no-alert': 'error',
'no-bitwise': 'error',
'no-caller': 'error',
'no-cond-assign': [ 'error', 'except-parens' ],
'no-console': 'error',
'no-debugger': 'error',
'no-dupe-args': 'error',
'no-dupe-keys': 'error',
'no-duplicate-case': 'error',
'no-else-return': 'error',
'no-eval': 'error',
'no-extra-semi': 'error',
'no-fallthrough': 'error',
'no-irregular-whitespace': 'error',
'no-lonely-if': 'error',
'no-multi-str': 'error',
'no-mixed-operators': 'error',
'no-mixed-spaces-and-tabs': 'error',
'no-multiple-empty-lines': [ 'error', { max: 1 } ],
'no-multi-spaces': 'error',
'no-nested-ternary': 'error',
'no-redeclare': 'error',
'no-shadow': 'error',
'no-trailing-spaces': 'error',
'no-undef': 'error',
'no-undef-init': 'error',
'no-unreachable': 'error',
'no-unsafe-negation': 'error',
'no-unused-expressions': 'error',
'no-unused-vars': [ 'error', { ignoreRestSiblings: true } ],
'no-useless-return': 'error',
'no-whitespace-before-property': 'error',
'no-with': 'error',
'object-curly-spacing': [ 'error', 'always' ],
'one-var-declaration-per-line': [ 'error', 'initializations' ],
'operator-linebreak': 'error',
'padded-blocks': [ 'error', 'never' ],
'quote-props': [ 'error', 'as-needed' ],
quotes: [ 'error', 'single', { avoidEscape: true } ],
semi: 'error',
'semi-spacing': 'error',
'space-before-blocks': [ 'error', 'always' ],
'space-before-function-paren': [
'error',
{
anonymous: 'never',
named: 'never',
asyncArrow: 'always',
},
],
'space-in-parens': [ 'error', 'always' ],
'space-infix-ops': 'error',
'space-unary-ops': [
'error',
{
overrides: {
'!': true,
},
},
],
'valid-typeof': 'error',
'vars-on-top': 'error',
'wrap-iife': 'error',
},
};

View File

@@ -0,0 +1,68 @@
/**
* External dependencies
*/
const { cosmiconfigSync } = require( 'cosmiconfig' );
const config = {
parser: '@babel/eslint-parser',
parserOptions: {
sourceType: 'module',
},
env: {
es6: true,
},
extends: [ require.resolve( './es5.js' ) ],
rules: {
// Disable ES5-specific (extended from ES5)
'vars-on-top': 'off',
// Enable ESNext-specific.
'arrow-parens': [ 'error', 'always' ],
'arrow-spacing': 'error',
'computed-property-spacing': [ 'error', 'always' ],
'constructor-super': 'error',
'no-const-assign': 'error',
'no-dupe-class-members': 'error',
'no-duplicate-imports': 'error',
'no-useless-computed-key': 'error',
'no-useless-constructor': 'error',
'no-var': 'error',
'object-shorthand': 'error',
'prefer-const': [
'error',
{
destructuring: 'all',
},
],
quotes: [
'error',
'single',
{ allowTemplateLiterals: true, avoidEscape: true },
],
'space-unary-ops': [
'error',
{
overrides: {
'!': true,
yield: true,
},
},
],
'template-curly-spacing': [ 'error', 'always' ],
},
};
// It won't recognize the `babel.config.json` file used in the project until the upstream bug in `cosmiconfig` is fixed:
// https://github.com/davidtheclark/cosmiconfig/issues/246.
const result = cosmiconfigSync( 'babel' ).search();
if ( ! result || ! result.filepath ) {
config.parserOptions = {
...config.parserOptions,
requireConfigFile: false,
babelOptions: {
presets: [ require.resolve( '@wordpress/babel-preset-default' ) ],
},
};
}
module.exports = config;

12
node_modules/@wordpress/eslint-plugin/configs/i18n.js generated vendored Normal file
View File

@@ -0,0 +1,12 @@
module.exports = {
plugins: [ '@wordpress' ],
rules: {
'@wordpress/valid-sprintf': 'error',
'@wordpress/i18n-translator-comments': 'error',
'@wordpress/i18n-text-domain': 'error',
'@wordpress/i18n-no-collapsible-whitespace': 'error',
'@wordpress/i18n-no-placeholders-only': 'error',
'@wordpress/i18n-no-variables': 'error',
'@wordpress/i18n-ellipsis': 'error',
},
};

View File

@@ -0,0 +1 @@
module.exports = require( 'requireindex' )( __dirname );

153
node_modules/@wordpress/eslint-plugin/configs/jsdoc.js generated vendored Normal file
View File

@@ -0,0 +1,153 @@
/**
* External dependencies
*/
const globals = require( 'globals' );
/**
* The temporary list of types defined in Gutenberg which are allowed to avoid
* ESLint warnings. It should be removed once importing is going to be implemented
* in the tool which generates public APIs from JSDoc comments. Related issue to
* fix the root cause `@wordpress/docgen`:
* https://github.com/WordPress/gutenberg/issues/18045.
*/
const temporaryWordPressInternalTypes = [
'WPBlockChildren',
'WPBlockNode',
'WPBlockSelection',
'WPBlockSerializationOptions',
'WPBlock',
'WPBlockPattern',
'WPBlockType',
'WPBlockTypeIcon',
'WPBlockTypeIconRender',
'WPBlockTypeIconDescriptor',
'WPIcon',
// These two should be removed once we use the TS types from "react".
'Component',
'Element',
];
/**
* The temporary list of external types used in Gutenberg which are allowed
* to avoid ESLint warnings. It's similar to `wordpressInternalTypes` and it
* should be removed once the related issues is fixed:
* https://github.com/WordPress/gutenberg/issues/18045
*/
const temporaryExternalTypes = [ 'DOMHighResTimeStamp', 'espree' ];
/**
* Helpful utilities that are globally defined and known to the TypeScript compiler.
*
* @see http://www.typescriptlang.org/docs/handbook/utility-types.html
*/
const typescriptUtilityTypes = [
'ArrayLike',
'Exclude',
'Extract',
'InstanceType',
'Iterable',
'IterableIterator',
'NonNullable',
'Omit',
'Parameters',
'Partial',
'Pick',
'PromiseLike',
'Readonly',
'ReadonlyArray',
'ReadonlyMap',
'ReadonlySet',
'Record',
'Required',
'ReturnType',
'ThisType',
'unknown',
'never',
'NodeJS',
'AsyncIterableIterator',
'NodeRequire',
'true',
'false',
];
module.exports = {
extends: [ 'plugin:jsdoc/recommended' ],
settings: {
jsdoc: {
preferredTypes: {
object: 'Object',
},
tagNamePreference: {
returns: 'return',
yields: 'yield',
},
},
},
rules: {
'jsdoc/no-defaults': 'off',
'jsdoc/no-undefined-types': [
'error',
{
definedTypes: [
// Required to reference browser types because we don't have the `browser` environment enabled for the project.
// Here we filter out all browser globals that don't begin with an uppercase letter because those
// generally refer to window-level event listeners and are not a valid type to reference (e.g. `onclick`).
...Object.keys( globals.browser ).filter( ( k ) =>
/^[A-Z]/.test( k )
),
...typescriptUtilityTypes,
...temporaryWordPressInternalTypes,
...temporaryExternalTypes,
'void',
'JSX',
],
},
],
'jsdoc/require-jsdoc': 'off',
'jsdoc/require-param-description': 'off',
'jsdoc/require-returns': 'off',
'jsdoc/require-yields': 'off',
'jsdoc/tag-lines': [
1,
'any',
{
startLines: null,
endLines: 0,
applyToEndTag: false,
},
],
'jsdoc/no-multi-asterisks': [
'error',
{ preventAtMiddleLines: false },
],
'jsdoc/check-access': 'error',
'jsdoc/check-alignment': 'error',
'jsdoc/check-line-alignment': [
'error',
'always',
{
tags: [ 'param', 'arg', 'argument', 'property', 'prop' ],
preserveMainDescriptionPostDelimiter: true,
},
],
'jsdoc/check-param-names': 'error',
'jsdoc/check-property-names': 'error',
'jsdoc/check-tag-names': 'error',
'jsdoc/check-types': 'error',
'jsdoc/check-values': 'off',
'jsdoc/empty-tags': 'error',
'jsdoc/implements-on-classes': 'error',
'jsdoc/require-param': 'error',
'jsdoc/require-param-name': 'error',
'jsdoc/require-param-type': 'error',
'jsdoc/require-property': 'error',
'jsdoc/require-property-description': 'error',
'jsdoc/require-property-name': 'error',
'jsdoc/require-property-type': 'error',
'jsdoc/require-returns-check': 'error',
'jsdoc/require-returns-description': 'error',
'jsdoc/require-returns-type': 'error',
'jsdoc/valid-types': 'error',
},
};

View File

@@ -0,0 +1,17 @@
module.exports = {
rules: {
curly: 'error',
eqeqeq: 'error',
'no-caller': 'error',
'no-cond-assign': [ 'error', 'except-parens' ],
'no-eq-null': 'error',
'no-irregular-whitespace': 'error',
'no-trailing-spaces': 'error',
'no-undef': 'error',
'no-unused-expressions': 'error',
'no-unused-vars': [ 'error', { ignoreRestSiblings: true } ],
'one-var': [ 'error', 'always' ],
quotes: [ 'error', 'single' ],
'wrap-iife': [ 'error', 'any' ],
},
};

View File

@@ -0,0 +1,16 @@
module.exports = {
extends: [ 'plugin:jsx-a11y/recommended' ],
plugins: [ 'jsx-a11y' ],
rules: {
'jsx-a11y/label-has-associated-control': [
'error',
{
assert: 'htmlFor',
},
],
'jsx-a11y/media-has-caption': 'off',
'jsx-a11y/no-noninteractive-tabindex': 'off',
'jsx-a11y/role-has-required-aria-props': 'off',
'jsx-quotes': 'error',
},
};

45
node_modules/@wordpress/eslint-plugin/configs/react.js generated vendored Normal file
View File

@@ -0,0 +1,45 @@
module.exports = {
extends: [ 'plugin:react/recommended' ],
parserOptions: {
ecmaFeatures: {
jsx: true,
},
},
settings: {
react: {
version: 'detect',
},
},
plugins: [ '@wordpress', 'react', 'react-hooks' ],
rules: {
'@wordpress/no-unused-vars-before-return': [
'error',
{
excludePattern: '^use',
},
],
'react/display-name': 'off',
'react/jsx-curly-spacing': [
'error',
{
when: 'always',
children: true,
},
],
'react/jsx-equals-spacing': 'error',
'react/jsx-indent': [ 'error', 'tab' ],
'react/jsx-indent-props': [ 'error', 'tab' ],
'react/jsx-key': 'error',
'react/jsx-tag-spacing': 'error',
'react/no-children-prop': 'off',
'react/prop-types': 'off',
'react/react-in-jsx-scope': 'off',
'react-hooks/exhaustive-deps': [
'warn',
{
additionalHooks: '^(useSelect|useSuspenseSelect)$',
},
],
'react-hooks/rules-of-hooks': 'error',
},
};

View File

@@ -0,0 +1,44 @@
// Exclude bundled WordPress packages from the list.
const wpPackagesRegExp = '^@wordpress/(?!(icons|interface|style-engine))';
const config = {
extends: [
require.resolve( './jsx-a11y.js' ),
require.resolve( './custom.js' ),
require.resolve( './react.js' ),
require.resolve( './esnext.js' ),
require.resolve( './i18n.js' ),
],
plugins: [ 'import' ],
env: {
node: true,
},
globals: {
window: true,
document: true,
SCRIPT_DEBUG: 'readonly',
wp: 'readonly',
},
settings: {
'import/internal-regex': wpPackagesRegExp,
'import/extensions': [ '.js', '.jsx' ],
},
rules: {
'import/no-extraneous-dependencies': [
'error',
{
peerDependencies: true,
},
],
'import/no-unresolved': [
'error',
{
ignore: [ wpPackagesRegExp ],
},
],
'import/default': 'warn',
'import/named': 'warn',
},
};
module.exports = config;

View File

@@ -0,0 +1,68 @@
/**
* External dependencies
*/
const { cosmiconfigSync } = require( 'cosmiconfig' );
/**
* WordPress dependencies
*/
/**
* Internal dependencies
*/
const { isPackageInstalled } = require( '../utils' );
const config = {
extends: [ require.resolve( './recommended-with-formatting.js' ) ],
};
if ( isPackageInstalled( 'prettier' ) ) {
config.extends.push( 'plugin:prettier/recommended' );
const { config: localPrettierConfig } =
cosmiconfigSync( 'prettier' ).search() || {};
const defaultPrettierConfig = require( '@wordpress/prettier-config' );
const prettierConfig = { ...defaultPrettierConfig, ...localPrettierConfig };
config.rules = {
'prettier/prettier': [ 'error', prettierConfig ],
// Prettier _disables_ this rule, but we want it!
// See https://github.com/prettier/eslint-config-prettier?tab=readme-ov-file#curly
// > This rule requires certain options.
// > …
// > If you like this rule, it can be used just fine with Prettier as long as you dont use the "multi-line" or "multi-or-nest" option.
curly: [ 'error', 'all' ],
};
}
if ( isPackageInstalled( 'typescript' ) ) {
config.settings = {
'import/resolver': {
node: {
extensions: [ '.js', '.jsx', '.ts', '.tsx' ],
},
},
};
config.extends.push( 'plugin:@typescript-eslint/eslint-recommended' );
config.ignorePatterns = [ '**/*.d.ts' ];
config.plugins = [ '@typescript-eslint' ];
config.overrides = [
{
files: [ '**/*.ts', '**/*.tsx' ],
parser: '@typescript-eslint/parser',
rules: {
'no-duplicate-imports': 'off',
'import/no-duplicates': 'error',
// Don't require redundant JSDoc types in TypeScript files.
'jsdoc/require-param-type': 'off',
'jsdoc/require-returns-type': 'off',
// Handled by TS itself.
'no-unused-vars': 'off',
// no-shadow doesn't work correctly in TS, so let's use a TS-dedicated version instead.
'no-shadow': 'off',
'@typescript-eslint/no-shadow': 'error',
},
},
];
}
module.exports = config;

View File

@@ -0,0 +1,11 @@
module.exports = {
extends: [ 'plugin:jest/recommended' ],
env: {
browser: true,
},
globals: {
browser: 'readonly',
page: 'readonly',
wp: 'readonly',
},
};

View File

@@ -0,0 +1,3 @@
module.exports = {
extends: [ 'plugin:playwright/recommended' ],
};

View File

@@ -0,0 +1,9 @@
module.exports = {
extends: [ 'plugin:jest/recommended' ],
rules: {
'jest/expect-expect': [
'error',
{ assertFunctionNames: [ 'expect', 'measurePerformance' ] },
],
},
};