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

prefer-export-from: Fix TypeScript compatibility #1728

Merged
merged 22 commits into from Feb 16, 2022

Conversation

nrgnrg
Copy link
Contributor

@nrgnrg nrgnrg commented Feb 10, 2022

closes #1727

When applying a fix checks the exportKind and adds type as a prefix to the specifier if it's a type export.

@nrgnrg
Copy link
Contributor Author

nrgnrg commented Feb 10, 2022

@fisker

@nrgnrg nrgnrg force-pushed the fix/prefer-export-from-types branch from 670afc3 to 7d16cc0 Compare February 10, 2022 15:48
@fisker
Copy link
Collaborator

fisker commented Feb 10, 2022

I'm sorry to say this is not correct solution, those "imported" can also variables.

I don't use ts myself, but afaik, exportKind can be on specifier and declaration(correct me if I'm wrong). We need get all imported exported kind. And make sure we don't reuse the wrong export.

Example:

import { foo } from "foo";
export {foo};
export type  {bar } from "foo";

The export can't be reused, need insert another export.

@fisker
Copy link
Collaborator

fisker commented Feb 10, 2022

Normally I don't dig deep for the ts parser, we can also simply skip the fix if you see any importKind or exportKind is "type" if you find hard to fix. So the fix won't break your code until someone make a better fix.

@nrgnrg
Copy link
Contributor Author

nrgnrg commented Feb 10, 2022

I'm not sure I follow exactly.

Given your example the correct fix would be either:

export { foo, type bar } from "foo";

or

export { foo } from "foo";
export type { bar } from "foo";

Which this fix correctly substitutes, I've run this change with --fix on our largish codebase at work and it's made the correct substitution every time.

@fisker
Copy link
Collaborator

fisker commented Feb 10, 2022

What's the result for the code I posted? (example updated)

@nrgnrg
Copy link
Contributor Author

nrgnrg commented Feb 10, 2022

Ah ok I gotcha, it's reusing the export type in this instance and converting foo to a type export.

@nrgnrg
Copy link
Contributor Author

nrgnrg commented Feb 10, 2022

This is perhaps not the most perfect solution because we could be smarter about it, but by just ignoring export declarations which are type declarations as far as i can see we cannot end up in a scenario where a value is converted to a type.

@nrgnrg
Copy link
Contributor Author

nrgnrg commented Feb 10, 2022

Your example now auto fix's to this:

export type { bar } from "foo";

export { foo } from "foo";

@nrgnrg nrgnrg force-pushed the fix/prefer-export-from-types branch from 99cb224 to 8e6ce4a Compare February 10, 2022 17:43
@sindresorhus
Copy link
Owner

This will need a test.

@nrgnrg nrgnrg force-pushed the fix/prefer-export-from-types branch from 5eaf934 to 1f24e32 Compare February 10, 2022 18:00
@nrgnrg
Copy link
Contributor Author

nrgnrg commented Feb 10, 2022

I've corrected the test failures and added a test for the change.

rules/prefer-export-from.js Outdated Show resolved Hide resolved
rules/prefer-export-from.js Outdated Show resolved Hide resolved
rules/prefer-export-from.js Outdated Show resolved Hide resolved
@nrgnrg
Copy link
Contributor Author

nrgnrg commented Feb 11, 2022

I think I've addressed you comments, I now see the following output for the given inputs (before and after this change).

Previously: value exports converted to type exports breaking code, or vice versa
Now: value exports preserved, type export style preserved (inline or multiple statements)

// input
import { foo } from "foo";
export { foo };
export type { bar } from "foo";

// Existing Transform:
export type { bar, foo } from "foo";

// Updated Transform:
export type { bar } from "foo";
export { foo } from "foo";
// input
import { foo } from "foo";
export { foo };
export { type bar } from "foo";

// Existing Transform:
export { type bar, foo } from "foo";

// Updated Transform:
export { type bar, foo } from "foo";
// input
import foo, { bar } from "foo";
export type { bar };
export default foo;

// Existing Transform:
export type { bar, default } from "foo";

// Updated Transform:
export type { bar } from "foo";
export { default } from "foo";
// input
import { foo } from 'foo';
export { foo };
export type { bar } from "foo";
export { baz } from "foo";

// Existing Transform:
export type { bar, foo } from "foo";
export { baz } from "foo";

// Updated Transform:
export type { bar } from "foo";
export { baz, foo } from "foo";
// input
import { foo } from 'foo';
export { foo };
export { type bar } from "foo";
export { baz } from "foo";

// Existing Transform:
export { type bar, foo } from "foo";
export { baz } from "foo";

// Updated Transform:
export { type bar, foo } from "foo";
export { baz } from "foo";

@nrgnrg nrgnrg requested a review from fisker February 11, 2022 10:16
@nrgnrg nrgnrg changed the title fix: prefer-export-from do not convert type exports to value exports fix: prefer-export-from do not convert exportKind type to value or value to type when applying fix Feb 11, 2022
@nrgnrg nrgnrg changed the title fix: prefer-export-from do not convert exportKind type to value or value to type when applying fix fix: prefer-export-from preserve exportKind when applying fix Feb 11, 2022
@fisker
Copy link
Collaborator

fisker commented Feb 11, 2022

Always add tests when you do logic change.

@nrgnrg
Copy link
Contributor Author

nrgnrg commented Feb 11, 2022

@fisker the additional test cases have been added now

@nrgnrg nrgnrg force-pushed the fix/prefer-export-from-types branch from f5740ab to 72f0ad9 Compare February 11, 2022 11:34
@fisker
Copy link
Collaborator

fisker commented Feb 14, 2022

I'm working to make some improvements.

@fisker
Copy link
Collaborator

fisker commented Feb 14, 2022

@nrgnrg Can you take a look? Main improvements are in 03ba7c6.

  1. Reuse type export declaration
  2. Insert a type export instead of a value export

I don't use typescript myself, but I assume

export type {foo} from 'foo';

is preferred over

export {type foo} from "foo";

?

Are those changes good to you?

@fisker
Copy link
Collaborator

fisker commented Feb 14, 2022

import type {foo} from  'foo';
export type bar = foo;

This seems not handled by our rule, if you are interested, maybe you can try to fix this in a separate PR?

@nrgnrg
Copy link
Contributor Author

nrgnrg commented Feb 14, 2022

Your changes look good to me 👍

The inline type import/export syntax (export { type foo } from "foo";) is a more recent addition to the language which was added to avoid several import/export statements. Personally I find it more concise but the export type { foo } from 'foo' syntax would support older versions of TS.

The responsibility of enforcing one style over the other does not feel like the responsibility of this rule, there is a tracking issue in typescript-eslint to enforce consistent usage of the inline syntax by making it a part of import/no-duplicates so I think as long as this rule plays nicely with changes made there to enforce the inline syntax this is probably the correct way to go here in order to support older typescript versions.

@fisker
Copy link
Collaborator

fisker commented Feb 14, 2022

Thanks for the information, I guess I was wrong, can you change it to prefer value export first? It should be simple.

@nrgnrg
Copy link
Contributor Author

nrgnrg commented Feb 15, 2022

can you change it to prefer value export first? It should be simple.

Inline type syntax? yeah I'll try and find the time this week to update that.

Thanks for your work helping on this

@nrgnrg nrgnrg requested a review from fisker February 15, 2022 14:26
@nrgnrg
Copy link
Contributor Author

nrgnrg commented Feb 15, 2022

@fisker let me know how this looks

@fisker
Copy link
Collaborator

fisker commented Feb 16, 2022

Actually, I mean 3020b59, if {type foo} is preferred, we should insert it in a value export, even type export exists.

@sindresorhus What's your opinion on this

import {type foo} from 'foo';
export {type foo}
export type {bar} from 'foo';
export {baz} from 'foo';

FIX 1:

export type {bar, foo} from 'foo'; // Reuse this type export.
export {baz} from 'foo';

FIX 2:

export type {bar} from 'foo';
export {baz, type foo} from 'foo'; // Reuse this value export.

@sindresorhus
Copy link
Owner

I would go with fix 1. That's what most people would want, I think.

@fisker
Copy link
Collaborator

fisker commented Feb 16, 2022

Okay, I'll revert.

@sindresorhus
Copy link
Owner

However, my preference would be:

export {baz, type foo, type bar} from 'foo';

Since it makes it possible to have a single statement.

I think there should be a TODO comment to switch to that when the TS version with it is more widespread.

@fisker
Copy link
Collaborator

fisker commented Feb 16, 2022

@nrgnrg already mentioned this in #1728 (comment), that should be handled in a separate rule.

Copy link
Collaborator

@fisker fisker left a comment

Choose a reason for hiding this comment

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

I'm fine with those changes, thanks for working on this, good job @nrgnrg .

@sindresorhus sindresorhus changed the title fix: prefer-export-from preserve exportKind when applying fix prefer-export-from: Fix TypeScript compatibility Feb 16, 2022
@sindresorhus sindresorhus merged commit f14aa95 into sindresorhus:main Feb 16, 2022
@nrgnrg nrgnrg deleted the fix/prefer-export-from-types branch February 16, 2022 16:34
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.

unicorn/prefer-export-from breaks type exports in typescript projects using the isolatedModules flag.
3 participants