Skip to content

Create public ESLint plugin #483

Merged
dddlr merged 18 commits into
mainfrom
grant/create-public-eslint-plugin
May 6, 2025
Merged

Create public ESLint plugin #483
dddlr merged 18 commits into
mainfrom
grant/create-public-eslint-plugin

Conversation

@dddlr

@dddlr dddlr commented May 1, 2025

Copy link
Copy Markdown
Contributor

Create public ESLint plugin, containing two ESLint rules to enforce how conditional imports should be used.

Motivation

We need to enforce two specific things in how people use conditional imports:

  • they should always use type annotations, and
  • they should never export the conditional import function call (importCond)

ESLint rules are the easiest way to enforce this.

Changes

  • Added new package called @atlaspack/eslint-plugin-public
  • Updated typescript-eslint and prettier packages to fix type, test and build issues

Checklist

  • Existing or new tests cover this change
  • There is a changeset for this change, or one is not required
  • Test in Jira codebase, and make a draft PR to add this plugin to Jira codebase
  • Verify that the package will be deployed correctly, with help from team

@dddlr dddlr requested a review from a team as a code owner May 1, 2025 06:53
@changeset-bot

changeset-bot Bot commented May 1, 2025

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 76ac4ae

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@atlaspack/eslint-plugin Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@dddlr dddlr force-pushed the grant/create-public-eslint-plugin branch from 7334122 to e0efd3b Compare May 1, 2025 06:54
typeof import('./new.tsx')
>('gate-name', './old.tsx', './new.tsx');

export function getComponent() {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

context:

i’m curious as to why it’s considered bad practice to directly export something using importCond if it does work in production?

i could be wrong, but i feel like having a feature gate to control which component is used by dependents would be a semi common pattern, and having the additional function call to get the component makes code harder to read/understand, and puts us in a position where people may use wildly different names for a component, instead of at least needing to reference the component name?

e.g.:

/// @filename: foo-component.tsx
const FooComponent = importCond<typeof import('./old.tsx'), typeof import('./new.tsx')>(
    'gate-name',
    './old.tsx',
    './new.tsx',
);
export const getFooComponent = () => FooComponent;
/// @filename: bar-component.tsx
import { getFooComponent } from './foo-component';
// we can rename it here
const BarComponent = getFooComponent();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Exporting importCond function calls will cause issues in test environments, so we don't want to encourage them.

As for why we made this decision, we have a couple of reasons:

  • Moving the importCond means making the fg call not colocated into the relevant file for static analysis
  • Another form of barrel files (with similar costs to bundling and bundle size)
  • We don't want to encourage people to introduce code like in your example. Exporting an importCond call, even implicitly, means that we can't bundle split as efficiently. Each call to importCond is an opportunity to bundle split.

Comment thread packages/core/eslint-plugin-public/src/utils/index.ts Outdated
languageOptions: {
parser: tseslint.parser,
parserOptions: {
projectService: {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

question: / suggestion:

is type information required to run this rule? if not, we can probably remove the tsconfig/project services options to make running unit tests a bit faster?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

oh you're right, none of these options are actually needed 👀

i had added this (and filename) because i was running into trouble getting typescript-eslint to work in this repo and this seemed to help, but i can't actually reproduce the issues i was running into anymore 🤔 so they must not have been necessary after all

"@typescript-eslint/rule-tester": "^8.31.1"
},
"peerDependencies": {
"eslint": ">8.0.0"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion:

the semver >8.0.0 will match anything greater than 8.0.0, including 10.0.0 (which could have major breaking changes).

it would be considerably safer to use the range ^8.57.0 || ^9.0.0 to ensure that they only use a version that supports all the new properties in eslint rules (the ones mentioned here https://eslint.org/blog/2023/09/preparing-custom-rules-eslint-v9/ - while they are able to be used in in i believe 8.49.0, there might be some other things added between the versions? i can't remember)

Comment thread package.json
"buffer": "mischnic/buffer#b8a4fa94",
"cross-env": "^7.0.0",
"eslint": "^8.41.0",
"eslint": "^8.57.0",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion:

i would recommend using (or at least testing with) eslint v9 for the eslint plugin, even if it's a separate test (since that is what jira is using) if possible? :)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

i realise it's probably difficult to update the whole repo to using eslint v9, and likely not in scope for this task, because eslint-plugin-flowtype and eslint-plugin-monorepo haven't been updated in ages, but i thought it might be useful to mention?

you may be able to get away with using @eslint/compat and using fixupPluginRules though?

@dddlr dddlr May 2, 2025

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

i will probably leave moving to eslint v9 for a future PR to be honest 😅 as it would involve checking several other packages around the repo (at least, i couldn't complete the version upgrade within a one-hour timebox)

i wonder if it's possible to set up this repo to test with eslint v9 just for the @atlaspack/eslint-plugin package? 🤔

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I did confirm that the tests added in this PR do work locally when i change the whole repo to eslint v9, so integrating into jira would be fine 😅

Comment thread .changeset/fair-sheep-sing.md
@dddlr

dddlr commented May 2, 2025

Copy link
Copy Markdown
Contributor Author

Screenshot 2025-05-02 at 11 25 23
Screenshot 2025-05-02 at 11 28 20

getting stuck on prettier failing and also making what appear to not be one-to-one changes, even after I reverted the prettier version bump and config changes

I'll add these to .prettierignore just to unblock this PR

but I recognise that we will need to update prettier and at some point (maybe as part of an innovation week project?)

also noting that flow type comments are no longer supported for prettier and we will need to find an alternative solution: either moving to non-comment type annotations or just migrating to typescript

edit: anyone know why prettier fails due to this PR? it has no dependencies so theoretically its behaviour shouldn't change at all??

Comment thread packages/core/eslint-plugin-public/package.json Outdated
context.report({
node,
messageId: 'wrongTypeAnnotation',
// We should be more cautious here, so we make it a suggestion instead of an autofix

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nice one

typeof import('./new.tsx')
>('gate-name', './old.tsx', './new.tsx');

export function getComponent() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Exporting importCond function calls will cause issues in test environments, so we don't want to encourage them.

As for why we made this decision, we have a couple of reasons:

  • Moving the importCond means making the fg call not colocated into the relevant file for static analysis
  • Another form of barrel files (with similar costs to bundling and bundle size)
  • We don't want to encourage people to introduce code like in your example. Exporting an importCond call, even implicitly, means that we can't bundle split as efficiently. Each call to importCond is an opportunity to bundle split.

@AllySummers

Copy link
Copy Markdown

@dddlr based on this bit, it might be failing because of newer syntax?

e.g. the ??= (nullish coalescing assignment operator) is only very new, so it may not support that
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing_assignment

Screenshot 2025-05-02 at 11 50 39 am

Comment thread .prettierignore Outdated
@dddlr dddlr force-pushed the grant/create-public-eslint-plugin branch from d92ffb2 to cc4d65f Compare May 2, 2025 04:39
Comment thread .changeset/fair-sheep-sing.md Outdated
@dddlr dddlr force-pushed the grant/create-public-eslint-plugin branch from c2d9d42 to d8b114f Compare May 6, 2025 01:35
Comment thread packages/core/eslint-plugin/src/rules/importcond-type-annotations/README.md Outdated
@dddlr dddlr merged commit 91dc1eb into main May 6, 2025
@dddlr dddlr deleted the grant/create-public-eslint-plugin branch May 6, 2025 23:17
@github-actions github-actions Bot mentioned this pull request May 6, 2025
@atlaspack-ci atlaspack-ci Bot mentioned this pull request May 14, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants