Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Scripts: Update stylelint dependency and the default config #64828

Merged
merged 22 commits into from
Sep 12, 2024

Conversation

mikeybinns
Copy link
Contributor

@mikeybinns mikeybinns commented Aug 27, 2024

What?

Updating the required version of Stylelint to the latest version, and ensuring we aren't removing any rules which were migrated to a new package.

Why?

Keeping the Stylelint dependency up to date is important so we continue to get support and related tooling continues to work.
It's also important that the WordPress config remains the same so there aren't issues with CSS across the large codebase.

How?

  • I've updated the stylelint peer dependency from ^14.2 to ^16.8.2 in the @wordpress/stylelint-config package
  • I've updated the stylelint-config-recommended dependency from ^6.0.0 to ^14.0.1 in the @wordpress/stylelint-config package
  • I've updated the stylelint-config-recommended-scss dependency from ^5.0.2 to ^14.1.0 in the @wordpress/stylelint-config package
  • I've added the @stylistic/stylelint-config dependency at version ^2.0.0 in the @wordpress/stylelint-config package
  • I've migrated all stylistic rules to the @stylistic namespace in configs and disable/enable comments
  • I've moved all stylistic rules into new configs so it is easy for consumers to opt out of stylistic rules instead of using something like stylelint-config-prettier to disable them.
  • I fixed a bug with the declaration-block-no-duplicate-properties rule where it was being overwritten by the stylelint-config-recommended-scss config in the scss config.
  • I've disabled all newly added rules, even if they would have been added in the recommended configs to make this migration easier (we can add new rules back later if deemed necessary in a MINOR release)
  • I've migrated scss/at-import-partial-extension to scss/load-partial-extension as the former was deprecated.

Once the @wordpress/stylelint-config package was updated, I had to make some changes to the @wordpress/scripts package and the root stylelint config as the scripts package directly uses the stylelint config, and scripts is used to lint all CSS in the monorepo using the root config.

  • I updated the Stylelint dependency from ^14.2 to ^16.8.2
  • I updated the scripts default config to use @wordpress/stylelint-config/scss-stylistic to keep using the stylistic rules
  • I updated the monorepo stylelint config to use @wordpress/stylelint-config/scss-stylistic and migrate and rule overrides to the new rule names.

Testing Instructions

  1. Run npm run lint:css to run the linting against the monorepo
  2. Run npm run test:unit -- --testPathPattern packages/stylelint-config to run the automated tests in only this package

Fixes: #48142
Fixes: #54127
Closes: #48143
Closes: #50336
Closes: #48769
Closes: #51192
Closes: #54935

Copy link

github-actions bot commented Aug 27, 2024

The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the props-bot label.

Unlinked Accounts

The following contributors have not linked their GitHub and WordPress.org accounts: @75th, @vicobot-0815.

Contributors, please read how to link your accounts to ensure your work is properly credited in WordPress releases.

If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.

Unlinked contributors: 75th, vicobot-0815.

Co-authored-by: mikeybinns <mikeybinns@git.wordpress.org>
Co-authored-by: gziolo <gziolo@git.wordpress.org>
Co-authored-by: jacobcassidy <jacobcassidy@git.wordpress.org>
Co-authored-by: mirka <0mirka00@git.wordpress.org>
Co-authored-by: paulschreiber <paulschreiber@git.wordpress.org>
Co-authored-by: stein2nd <stein2nd@git.wordpress.org>

To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook.

@mikeybinns mikeybinns marked this pull request as draft August 27, 2024 13:26
@mikeybinns
Copy link
Contributor Author

Converting to draft while I fix Stylelint unit tests :)

@mikeybinns
Copy link
Contributor Author

@gziolo I'm struggling with fixing these tests.

It seems that as of v16, Stylelint in Jest must be run in the node environment to work in Jest (source: stylelint/stylelint#7894) as the presence of a document breaks things.

I tried running the following command to test if that fix worked:

npm run test:unit -- --testPathPattern packages/stylelint-config --env=node

but this encounters a new error around global setup in jest-preset-default.
image

If I delete the contents of the packages/jest-preset-default/scripts/setup-globals.js and test/unit/config/global-mocks.js files, the tests proceed as expected (snapshot failures).

My jest knowledge isn't very good, and I certainly don't want to go messing around with the jest config, so would you be able to help me out with this? Perhaps there's a way to define a jest config just for this package?

I've pushed all my changes so you can pull down my branch.

@gziolo
Copy link
Member

gziolo commented Aug 28, 2024

https://jestjs.io/docs/configuration#testenvironment-string

Have you tried changing the test env for these test files in the package? That would be the correct environment anyway there. A code comment at the top of every failing test suite file should do the trick.

/**
 * @jest-environment node
 */

All the mocks that depend on window can be guarded with the if check to account for the node environment where window is undefined anyway. For example:

diff --git a/packages/jest-preset-default/scripts/setup-globals.js b/packages/jest-preset-default/scripts/setup-globals.js
index 37922a3302..242d525936 100644
--- a/packages/jest-preset-default/scripts/setup-globals.js
+++ b/packages/jest-preset-default/scripts/setup-globals.js
@@ -2,56 +2,63 @@
 // eslint-disable-next-line @wordpress/wp-global-usage
 globalThis.SCRIPT_DEBUG = true;
 
-// These are necessary to load TinyMCE successfully.
-global.URL = window.URL;
-global.window.tinyMCEPreInit = {
-	// Without this, TinyMCE tries to determine its URL by looking at the
-	// <script> tag where it was loaded from, which of course fails here.
-	baseURL: 'about:blank',
-};
-
-global.window.setImmediate = function ( callback ) {
-	return setTimeout( callback, 0 );
-};
-
-global.window.requestAnimationFrame = function requestAnimationFrame(
-	callback
-) {
-	// eslint-disable-next-line no-restricted-syntax
-	const randomDelay = Math.round( ( Math.random() * 1_000 ) / 60 );
-
-	return setTimeout( () => callback( Date.now() ), randomDelay );
-};
-
-global.window.cancelAnimationFrame = function cancelAnimationFrame( handle ) {
-	return clearTimeout( handle );
-};
-
-// Ignoring `options` argument since we unconditionally schedule this ASAP.
-global.window.requestIdleCallback = function requestIdleCallback( callback ) {
-	const start = Date.now();
-
-	return setTimeout(
-		() =>
-			callback( {
-				didTimeout: false,
-				timeRemaining: () => Math.max( 0, 50 - ( Date.now() - start ) ),
-			} ),
-		0
-	);
-};
-
-global.window.cancelIdleCallback = function cancelIdleCallback( handle ) {
-	return clearTimeout( handle );
-};
-
-global.window.matchMedia = () => ( {
-	matches: false,
-	addListener: () => {},
-	addEventListener: () => {},
-	removeListener: () => {},
-	removeEventListener: () => {},
-} );
-
-// UserSettings global.
-global.window.userSettings = { uid: 1 };
+if ( typeof window !== 'undefined' ) {
+	// These are necessary to load TinyMCE successfully.
+	global.URL = window.URL;
+	global.window.tinyMCEPreInit = {
+		// Without this, TinyMCE tries to determine its URL by looking at the
+		// <script> tag where it was loaded from, which of course fails here.
+		baseURL: 'about:blank',
+	};
+
+	global.window.setImmediate = function ( callback ) {
+		return setTimeout( callback, 0 );
+	};
+
+	global.window.requestAnimationFrame = function requestAnimationFrame(
+		callback
+	) {
+		// eslint-disable-next-line no-restricted-syntax
+		const randomDelay = Math.round( ( Math.random() * 1_000 ) / 60 );
+
+		return setTimeout( () => callback( Date.now() ), randomDelay );
+	};
+
+	global.window.cancelAnimationFrame = function cancelAnimationFrame(
+		handle
+	) {
+		return clearTimeout( handle );
+	};
+
+	// Ignoring `options` argument since we unconditionally schedule this ASAP.
+	global.window.requestIdleCallback = function requestIdleCallback(
+		callback
+	) {
+		const start = Date.now();
+
+		return setTimeout(
+			() =>
+				callback( {
+					didTimeout: false,
+					timeRemaining: () =>
+						Math.max( 0, 50 - ( Date.now() - start ) ),
+				} ),
+			0
+		);
+	};
+
+	global.window.cancelIdleCallback = function cancelIdleCallback( handle ) {
+		return clearTimeout( handle );
+	};
+
+	global.window.matchMedia = () => ( {
+		matches: false,
+		addListener: () => {},
+		addEventListener: () => {},
+		removeListener: () => {},
+		removeEventListener: () => {},
+	} );
+
+	// UserSettings global.
+	global.window.userSettings = { uid: 1 };
+}

@@ -1,7 +1,7 @@
'use strict';

module.exports = {
extends: 'stylelint-config-recommended',
extends: [ 'stylelint-config-recommended' ].map( require.resolve ),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the reason for using map here` with a single item?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I made it an array when I was previously using @stylistic/stylelint-config, but when I removed that config, I kept it as an array.

There was then a second error where it wouldn't find the config, so I fixed that with map/require.resolve as that's how the other configs are set up.

I can convert this to a non-mapped version if you'd prefer that?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see it is used in other places with multiple items. It's fine.

@gziolo
Copy link
Member

gziolo commented Aug 28, 2024

I mostly skimmed the changes applied and it all looks like everything is finally on the right track. The most important part is that linting passes! Excellent work 👏🏻

@mikeybinns
Copy link
Contributor Author

https://jestjs.io/docs/configuration#testenvironment-string

Have you tried changing the test env for these test files in the package? That would be the correct environment anyway there. A code comment at the top of every failing test suite file should do the trick.

/**
 * @jest-environment node
 */

Ah, yes I had tried this, but adding a eslint-disable rule about the tag being incorrect stopped it from working as it was no longer at the top of the file. This time, I tried adding an exclusion to the rule in the eslint config (just added to the monorepo root config) and it worked 👍

@mikeybinns
Copy link
Contributor Author

mikeybinns commented Aug 28, 2024

@gziolo As expected, once I sorted the global jest things to be wrapped in window checks, I run into this error:
image

Stylelint is now an ESM package as of v16, which Jest doesn't like.

I tried converting only the test files to ESM, and then adding that flag to the test:unit command in the root package.json, but I now get this different error:
image

It's not always 'stylelint', sometimes it's other files e.g. request for './hasLessInterpolation.js' is not yet fulfilled

From what I can see, it's an issue with jest-runtime and we might be SOL unless you have any ideas.
The Jest v30 alpha 1 has this line:

[jest-runtime] Properly handle re-exported native modules in ESM via CJS

This indicates to me that maybe the new Jest version will fix this issue?

Note: I've pushed all my code so far again.

@mikeybinns
Copy link
Contributor Author

@gziolo Actually, don't worry. I've found a way to use node exec to use the CLI to bypass using the node API entirely, and still get the same result. Probably is a bit clunky compared to using the API, but at least it works without having to rethink the whole jest setup 🥳

@mikeybinns
Copy link
Contributor Author

mikeybinns commented Sep 6, 2024

Copying the directory would work, but a cleaner way might be to use npm link.
Use Git to check out this PR's branch onto your machine.
Navigate in the command line to the packages/scripts folder (same location as the @wordpress/scripts package.json file)
Run npm link to create a link in node/npm
Go to your project and run npm link --save-dev @wordpress/scripts
This will install the scripts package you have locally as if it were a package in the project.
Note that your project package.json will now display a relative filepath for the scripts package:
image

This is essentially a symlink, which means if you were to make a change to the scripts, it would be instantly applied to wherever it's linked (great for quick dev changes).

npm link docs: https://docs.npmjs.com/cli/v10/commands/npm-link

@gziolo
Copy link
Member

gziolo commented Sep 12, 2024

My testing results from local copy.

  • npm install locally doesn't apply changes ✅
  • npm run lint:css passes ✅
  • Stylistic modification get detected by npm run lint:css, example:
Screenshot 2024-09-12 at 10 47 06

I also executed Prettier on all SCSS files which isn't configured correctly for WP coding conventions, this is the result in this branch:

Screenshot 2024-09-12 at 10 53 06

The same exercise in the trunk:

Screenshot 2024-09-12 at 10 56 22

Aside, it looks like all these violations caused by Prettier run and reported are fixable. I'm wondering if it's more reliable than what ESLint offers for JavaScript. Anyway, that's a separate story whether to add support for (S)CSS to wp-scripts format.

Copy link
Member

@gziolo gziolo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks good to me. Excellent work bringing it to the finish line!

I would love to see more testing before landing, but we can also publish the dev version of the package to npm and see how fast it works for fo.

@@ -1,7 +1,7 @@
'use strict';

module.exports = {
extends: 'stylelint-config-recommended',
extends: [ 'stylelint-config-recommended' ].map( require.resolve ),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see it is used in other places with multiple items. It's fine.

@gziolo gziolo enabled auto-merge (squash) September 12, 2024 09:12
@gziolo gziolo merged commit d649865 into WordPress:trunk Sep 12, 2024
60 checks passed
@github-actions github-actions bot added this to the Gutenberg 19.3 milestone Sep 12, 2024
@mikeybinns
Copy link
Contributor Author

On the Prettier angle, that's exactly why I separated it out into separate configs. I personally use prettier on my personal projects, so I have no need for the formatting rules to be applied.

If you do go ahead and use prettier, you can switch the config in wp-scripts from scss-stylistic to scss and prevent the need to have something like eslint-config-prettier to disable all formatting rules. 🙂

Glad to see testing went well! 🥳

@mikeybinns mikeybinns deleted the update-stylelint-dependency branch September 12, 2024 09:22
@gziolo
Copy link
Member

gziolo commented Sep 12, 2024

I published all the updates included in this PR with the @next dist-tag to npm. You can test it in your projects with:

npm install @wordpress/stylelint-config@next --save-dev

See https://www.npmjs.com/package/@wordpress/stylelint-config/v/23.0.1-next.5368f64a9.0.

We have time until next Wednesday when the regular bi-weekly npm release happens to apply any necessary fixes if people report issues.

@jacobcassidy
Copy link
Contributor

@gziolo The @next package doesn't include the scss-stylistic.js or stylistic.js files so when using the instructions to add:

{
	"extends": [ "@wordpress/stylelint-config/stylistic" ]
}

or

{
	"extends": [ "@wordpress/stylelint-config/scss-stylistic" ]
}

An error is thrown that those files don't exist.

@gziolo
Copy link
Member

gziolo commented Sep 13, 2024

@gziolo The @next package doesn't include the scss-stylistic.js or stylistic.js files so when using the instructions to add:

Thank you for testing. Great catch, I can confirm that when checking the content of the package:

https://unpkg.com/browse/@wordpress/stylelint-config@23.0.1-next.5368f64a9.0/

Screenshot 2024-09-13 at 11 29 17

Is anyone willing to contribute a change to the list of allowed files in the npm package?

"files": [
"CHANGELOG.md",
"LICENSE",
"README.md",
"index.js",
"scss.js"
],

@mikeybinns mikeybinns restored the update-stylelint-dependency branch September 13, 2024 09:34
@mikeybinns
Copy link
Contributor Author

Is anyone willing to contribute...

I'm on it 👍

@mikeybinns
Copy link
Contributor Author

Fix added here (didn't seem like I could add it to this PR anymore)
#65313

@gziolo
Copy link
Member

gziolo commented Sep 13, 2024

Yes, new PR is the way to go. Reviewing 👍🏻

@gziolo
Copy link
Member

gziolo commented Sep 13, 2024

New version is available on npm: https://www.npmjs.com/package/@wordpress/stylelint-config/v/23.0.1-next.1f6eadc42.0. I see both files when checking on unpkg.com:

Screenshot 2024-09-13 at 12 55 16

@jeffpaul
Copy link
Member

jeffpaul commented Nov 8, 2024

@vicobot-0815 mind sharing your WordPress.org username so I can ensure its included in the 6.7 credits listing?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Tool] stylelint config /packages/stylelint-config [Tool] WP Scripts /packages/scripts [Type] Breaking Change For PRs that introduce a change that will break existing functionality
Projects
None yet
5 participants