Commit 14a83e9ceb4 for woocommerce

commit 14a83e9ceb47079f735e27e063422e9788cbaab7
Author: Manzoor Wani <manzoorwani.jk@gmail.com>
Date:   Fri Jul 17 17:57:01 2026 +0530

    Scaffold ESLint Flat Config instead of eslintrc (#66753)

    * Scaffold Flat Config instead of eslintrc

    The 7 .eslintrc.js.mustache templates were the last eslintrc files in the
    repo. ESLint 10, which @wordpress/scripts now installs, does not read
    eslintrc, so wp-scripts lint-js fell back to its own default config and the
    WooCommerce ruleset never applied to scaffolded extensions.

    Emit eslint.config.mjs, which wp-scripts detects. Flat Config also does not
    read .eslintignore, so extend-cart-checkout-block's patterns move into an
    ignores entry.

    * Pin @woocommerce/eslint-plugin to ^4.0.0 in scaffolds

    The scaffolded eslint.config.mjs needs the Flat Config export, which v4 is
    the first release to ship. v3 exports an eslintrc object, so an unpinned
    scaffold would pair a Flat Config with a plugin that cannot supply one.

    npmDevDependencies cannot carry a version: create-block resolves each entry
    with npm-package-arg and writes saveSpec || 'latest', and saveSpec is null
    for registry specs. Route the pins through customPackageJSON, which is
    spread over the generated devDependencies, and derive npmDevDependencies
    from the same map so the two cannot drift.

    This is also why the @wordpress/prettier-config@2.18.2 entry never took
    effect. It stays at latest to preserve existing behaviour.

diff --git a/packages/js/create-woo-extension/changelog/66078-update-scaffold-flat-config b/packages/js/create-woo-extension/changelog/66078-update-scaffold-flat-config
new file mode 100644
index 00000000000..505197d5c1e
--- /dev/null
+++ b/packages/js/create-woo-extension/changelog/66078-update-scaffold-flat-config
@@ -0,0 +1,4 @@
+Significance: minor
+Type: update
+
+Scaffold ESLint Flat Config (`eslint.config.mjs`) instead of `.eslintrc.js`, which recent ESLint versions no longer read.
diff --git a/packages/js/create-woo-extension/index.js b/packages/js/create-woo-extension/index.js
index 75b9bdcdf34..6add30c1087 100644
--- a/packages/js/create-woo-extension/index.js
+++ b/packages/js/create-woo-extension/index.js
@@ -5,19 +5,27 @@ const defaultDependencies = [
 	'@wordpress/i18n',
 	'@woocommerce/components',
 ];
-const defaultDevDependencies = [
-	'@woocommerce/dependency-extraction-webpack-plugin',
-	'@woocommerce/eslint-plugin',
-	'@wordpress/prettier-config',
-	'@wordpress/scripts',
-];
+/**
+ * `npmDevDependencies` cannot carry a version: create-block resolves each entry
+ * with npm-package-arg and writes `saveSpec || 'latest'`, and `saveSpec` is null
+ * for registry specs. Pins therefore have to go through `customPackageJSON`,
+ * which is spread over the generated `devDependencies`.
+ */
+const defaultDevDependencies = {
+	'@woocommerce/dependency-extraction-webpack-plugin': 'latest',
+	/* v4 is the first Flat Config release; the scaffolded eslint.config.mjs requires it. */
+	'@woocommerce/eslint-plugin': '^4.0.0',
+	'@wordpress/prettier-config': 'latest',
+	'@wordpress/scripts': 'latest',
+};

 module.exports = {
 	pluginTemplatesPath: join( __dirname, 'variants', 'default' ),
 	blockTemplatesPath: join( __dirname, 'variants', 'default', 'src' ),
 	defaultValues: {
 		npmDependencies: defaultDependencies,
-		npmDevDependencies: defaultDevDependencies,
+		npmDevDependencies: Object.keys( defaultDevDependencies ),
+		customPackageJSON: { devDependencies: defaultDevDependencies },
 		namespace: 'extension',
 		license: 'GPL-3.0+',
 		customScripts: {
diff --git a/packages/js/create-woo-extension/variants/add-report/.eslintrc.js.mustache b/packages/js/create-woo-extension/variants/add-report/.eslintrc.js.mustache
deleted file mode 100644
index 22ddc24c584..00000000000
--- a/packages/js/create-woo-extension/variants/add-report/.eslintrc.js.mustache
+++ /dev/null
@@ -1,6 +0,0 @@
-module.exports = {
-	extends: [ 'plugin:@woocommerce/eslint-plugin/recommended' ],
-	rules: {
-		'react/react-in-jsx-scope': 'off',
-	},
-};
diff --git a/packages/js/create-woo-extension/variants/add-report/eslint.config.mjs.mustache b/packages/js/create-woo-extension/variants/add-report/eslint.config.mjs.mustache
new file mode 100644
index 00000000000..6ce87643f58
--- /dev/null
+++ b/packages/js/create-woo-extension/variants/add-report/eslint.config.mjs.mustache
@@ -0,0 +1,10 @@
+import woocommerce from '@woocommerce/eslint-plugin';
+
+export default [
+	...woocommerce.configs.recommended,
+	{
+		rules: {
+			'react/react-in-jsx-scope': 'off',
+		},
+	},
+];
diff --git a/packages/js/create-woo-extension/variants/add-task/.eslintrc.js.mustache b/packages/js/create-woo-extension/variants/add-task/.eslintrc.js.mustache
deleted file mode 100644
index 22ddc24c584..00000000000
--- a/packages/js/create-woo-extension/variants/add-task/.eslintrc.js.mustache
+++ /dev/null
@@ -1,6 +0,0 @@
-module.exports = {
-	extends: [ 'plugin:@woocommerce/eslint-plugin/recommended' ],
-	rules: {
-		'react/react-in-jsx-scope': 'off',
-	},
-};
diff --git a/packages/js/create-woo-extension/variants/add-task/eslint.config.mjs.mustache b/packages/js/create-woo-extension/variants/add-task/eslint.config.mjs.mustache
new file mode 100644
index 00000000000..6ce87643f58
--- /dev/null
+++ b/packages/js/create-woo-extension/variants/add-task/eslint.config.mjs.mustache
@@ -0,0 +1,10 @@
+import woocommerce from '@woocommerce/eslint-plugin';
+
+export default [
+	...woocommerce.configs.recommended,
+	{
+		rules: {
+			'react/react-in-jsx-scope': 'off',
+		},
+	},
+];
diff --git a/packages/js/create-woo-extension/variants/dashboard-section/.eslintrc.js.mustache b/packages/js/create-woo-extension/variants/dashboard-section/.eslintrc.js.mustache
deleted file mode 100644
index 22ddc24c584..00000000000
--- a/packages/js/create-woo-extension/variants/dashboard-section/.eslintrc.js.mustache
+++ /dev/null
@@ -1,6 +0,0 @@
-module.exports = {
-	extends: [ 'plugin:@woocommerce/eslint-plugin/recommended' ],
-	rules: {
-		'react/react-in-jsx-scope': 'off',
-	},
-};
diff --git a/packages/js/create-woo-extension/variants/dashboard-section/eslint.config.mjs.mustache b/packages/js/create-woo-extension/variants/dashboard-section/eslint.config.mjs.mustache
new file mode 100644
index 00000000000..6ce87643f58
--- /dev/null
+++ b/packages/js/create-woo-extension/variants/dashboard-section/eslint.config.mjs.mustache
@@ -0,0 +1,10 @@
+import woocommerce from '@woocommerce/eslint-plugin';
+
+export default [
+	...woocommerce.configs.recommended,
+	{
+		rules: {
+			'react/react-in-jsx-scope': 'off',
+		},
+	},
+];
diff --git a/packages/js/create-woo-extension/variants/default/.eslintrc.js.mustache b/packages/js/create-woo-extension/variants/default/.eslintrc.js.mustache
deleted file mode 100644
index 22ddc24c584..00000000000
--- a/packages/js/create-woo-extension/variants/default/.eslintrc.js.mustache
+++ /dev/null
@@ -1,6 +0,0 @@
-module.exports = {
-	extends: [ 'plugin:@woocommerce/eslint-plugin/recommended' ],
-	rules: {
-		'react/react-in-jsx-scope': 'off',
-	},
-};
diff --git a/packages/js/create-woo-extension/variants/default/eslint.config.mjs.mustache b/packages/js/create-woo-extension/variants/default/eslint.config.mjs.mustache
new file mode 100644
index 00000000000..6ce87643f58
--- /dev/null
+++ b/packages/js/create-woo-extension/variants/default/eslint.config.mjs.mustache
@@ -0,0 +1,10 @@
+import woocommerce from '@woocommerce/eslint-plugin';
+
+export default [
+	...woocommerce.configs.recommended,
+	{
+		rules: {
+			'react/react-in-jsx-scope': 'off',
+		},
+	},
+];
diff --git a/packages/js/create-woo-extension/variants/sql-modification/.eslintrc.js.mustache b/packages/js/create-woo-extension/variants/sql-modification/.eslintrc.js.mustache
deleted file mode 100644
index 22ddc24c584..00000000000
--- a/packages/js/create-woo-extension/variants/sql-modification/.eslintrc.js.mustache
+++ /dev/null
@@ -1,6 +0,0 @@
-module.exports = {
-	extends: [ 'plugin:@woocommerce/eslint-plugin/recommended' ],
-	rules: {
-		'react/react-in-jsx-scope': 'off',
-	},
-};
diff --git a/packages/js/create-woo-extension/variants/sql-modification/eslint.config.mjs.mustache b/packages/js/create-woo-extension/variants/sql-modification/eslint.config.mjs.mustache
new file mode 100644
index 00000000000..6ce87643f58
--- /dev/null
+++ b/packages/js/create-woo-extension/variants/sql-modification/eslint.config.mjs.mustache
@@ -0,0 +1,10 @@
+import woocommerce from '@woocommerce/eslint-plugin';
+
+export default [
+	...woocommerce.configs.recommended,
+	{
+		rules: {
+			'react/react-in-jsx-scope': 'off',
+		},
+	},
+];
diff --git a/packages/js/create-woo-extension/variants/table-column/.eslintrc.js.mustache b/packages/js/create-woo-extension/variants/table-column/.eslintrc.js.mustache
deleted file mode 100644
index 22ddc24c584..00000000000
--- a/packages/js/create-woo-extension/variants/table-column/.eslintrc.js.mustache
+++ /dev/null
@@ -1,6 +0,0 @@
-module.exports = {
-	extends: [ 'plugin:@woocommerce/eslint-plugin/recommended' ],
-	rules: {
-		'react/react-in-jsx-scope': 'off',
-	},
-};
diff --git a/packages/js/create-woo-extension/variants/table-column/eslint.config.mjs.mustache b/packages/js/create-woo-extension/variants/table-column/eslint.config.mjs.mustache
new file mode 100644
index 00000000000..6ce87643f58
--- /dev/null
+++ b/packages/js/create-woo-extension/variants/table-column/eslint.config.mjs.mustache
@@ -0,0 +1,10 @@
+import woocommerce from '@woocommerce/eslint-plugin';
+
+export default [
+	...woocommerce.configs.recommended,
+	{
+		rules: {
+			'react/react-in-jsx-scope': 'off',
+		},
+	},
+];
diff --git a/packages/js/extend-cart-checkout-block/.eslintignore.mustache b/packages/js/extend-cart-checkout-block/.eslintignore.mustache
deleted file mode 100644
index 4e1a6d59046..00000000000
--- a/packages/js/extend-cart-checkout-block/.eslintignore.mustache
+++ /dev/null
@@ -1,3 +0,0 @@
-!.eslintrc.js
-build
-node_modules
\ No newline at end of file
diff --git a/packages/js/extend-cart-checkout-block/.eslintrc.js.mustache b/packages/js/extend-cart-checkout-block/.eslintrc.js.mustache
deleted file mode 100644
index dc023bdbb5b..00000000000
--- a/packages/js/extend-cart-checkout-block/.eslintrc.js.mustache
+++ /dev/null
@@ -1,16 +0,0 @@
-module.exports = {
-	extends: [ 'plugin:@woocommerce/eslint-plugin/recommended' ],
-	rules: {
-		'react/react-in-jsx-scope': 'off',
-		'import/no-unresolved': 'off',
-		'import/no-extraneous-dependencies': 'off',
-		'import/named': 'off',
-		'import/default': 'off',
-		'@wordpress/i18n-text-domain': [
-			'error',
-			{
-				allowedTextDomain: [ '{{slug}}' ],
-			},
-		],
-	},
-};
diff --git a/packages/js/extend-cart-checkout-block/README.md b/packages/js/extend-cart-checkout-block/README.md
index 3a5104ef9f2..9bcf4c7cc30 100644
--- a/packages/js/extend-cart-checkout-block/README.md
+++ b/packages/js/extend-cart-checkout-block/README.md
@@ -22,7 +22,7 @@ Add some items to your cart and visit the Checkout block, notice there is additi

 ### Linting

-You can lint the project according to the [WordPress coding standards](https://developer.wordpress.org/coding-standards/wordpress-coding-standards/javascript/) by running `npm run lint:js`. The configuration is ultimately read from the [WooCommerce recommended eslint config](https://github.com/woocommerce/woocommerce/blob/trunk/packages/js/eslint-plugin/configs/recommended.js). To modify the rules edit the `.estintrc.js` file.
+You can lint the project according to the [WordPress coding standards](https://developer.wordpress.org/coding-standards/wordpress-coding-standards/javascript/) by running `npm run lint:js`. The configuration is ultimately read from the [WooCommerce recommended eslint config](https://github.com/woocommerce/woocommerce/blob/trunk/packages/js/eslint-plugin/configs/recommended.js). To modify the rules edit the `eslint.config.mjs` file.

 ### Installing `wp-env` (optional)

diff --git a/packages/js/extend-cart-checkout-block/changelog/66078-update-scaffold-flat-config b/packages/js/extend-cart-checkout-block/changelog/66078-update-scaffold-flat-config
new file mode 100644
index 00000000000..505197d5c1e
--- /dev/null
+++ b/packages/js/extend-cart-checkout-block/changelog/66078-update-scaffold-flat-config
@@ -0,0 +1,4 @@
+Significance: minor
+Type: update
+
+Scaffold ESLint Flat Config (`eslint.config.mjs`) instead of `.eslintrc.js`, which recent ESLint versions no longer read.
diff --git a/packages/js/extend-cart-checkout-block/eslint.config.mjs.mustache b/packages/js/extend-cart-checkout-block/eslint.config.mjs.mustache
new file mode 100644
index 00000000000..78cc44e6293
--- /dev/null
+++ b/packages/js/extend-cart-checkout-block/eslint.config.mjs.mustache
@@ -0,0 +1,21 @@
+import woocommerce from '@woocommerce/eslint-plugin';
+
+export default [
+	{ ignores: [ 'build/**' ] },
+	...woocommerce.configs.recommended,
+	{
+		rules: {
+			'react/react-in-jsx-scope': 'off',
+			'import/no-unresolved': 'off',
+			'import/no-extraneous-dependencies': 'off',
+			'import/named': 'off',
+			'import/default': 'off',
+			'@wordpress/i18n-text-domain': [
+				'error',
+				{
+					allowedTextDomain: [ '{{slug}}' ],
+				},
+			],
+		},
+	},
+];
diff --git a/packages/js/extend-cart-checkout-block/index.js b/packages/js/extend-cart-checkout-block/index.js
index c302d12fea5..784f1cf45a4 100644
--- a/packages/js/extend-cart-checkout-block/index.js
+++ b/packages/js/extend-cart-checkout-block/index.js
@@ -1,3 +1,22 @@
+/**
+ * `npmDevDependencies` cannot carry a version: create-block resolves each entry
+ * with npm-package-arg and writes `saveSpec || 'latest'`, and `saveSpec` is null
+ * for registry specs. Pins therefore have to go through `customPackageJSON`,
+ * which is spread over the generated `devDependencies`.
+ *
+ * This is why the `@wordpress/prettier-config@2.18.2` entry that used to live in
+ * `npmDevDependencies` never took effect. It is left at `latest` to preserve the
+ * behaviour scaffolds have always had, and because the postinstall pulls
+ * wp-prettier 3, which 2.18.2 predates.
+ */
+const devDependencies = {
+	'@woocommerce/dependency-extraction-webpack-plugin': 'latest',
+	/* v4 is the first Flat Config release; the scaffolded eslint.config.mjs requires it. */
+	'@woocommerce/eslint-plugin': '^4.0.0',
+	'@wordpress/prettier-config': 'latest',
+	'@wordpress/scripts': 'latest',
+};
+
 module.exports = {
 	templatesPath: __dirname,
 	defaultValues: {
@@ -5,14 +24,10 @@ module.exports = {
 			postinstall:
 				'npm i --D "prettier@npm:wp-prettier@latest" && npm i --D eslint-plugin-prettier@latest',
 		},
-		npmDevDependencies: [
-			'@woocommerce/dependency-extraction-webpack-plugin',
-			'@woocommerce/eslint-plugin',
-			'@wordpress/prettier-config@2.18.2',
-			'@wordpress/scripts',
-		],
+		npmDevDependencies: Object.keys( devDependencies ),
 		customPackageJSON: {
 			files: [ '*.php', 'build', 'block.json' ],
+			devDependencies,
 		},
 	},
 };