feat(assist): import organizer revamping#5364
Conversation
55e0cba to
2891850
Compare
Parser conformance results onjs/262
jsx/babel
symbols/microsoft
ts/babel
ts/microsoft
|
091d124 to
8e0ab47
Compare
8e0ab47 to
965c65f
Compare
CodSpeed Performance ReportMerging #5364 will not alter performanceComparing Summary
|
965c65f to
0a39ae9
Compare
dyc3
left a comment
There was a problem hiding this comment.
Not a full review, looks great at a glance!
0a39ae9 to
3274c15
Compare
arendjr
left a comment
There was a problem hiding this comment.
Nice work! Do you still want to include this into the beta?
...cases_overrides_organize_imports/does_handle_included_file_and_disable_organize_imports.snap
Outdated
Show resolved
Hide resolved
crates/biome_cli/tests/snapshots/main_commands_ci/ci_formatter_linter_organize_imports.snap
Outdated
Show resolved
Hide resolved
crates/biome_js_analyze/src/assist/source/organize_imports/util.rs
Outdated
Show resolved
Hide resolved
crates/biome_js_analyze/src/assist/source/organize_imports/util.rs
Outdated
Show resolved
Hide resolved
crates/biome_js_analyze/src/assist/source/organize_imports/import_source.rs
Outdated
Show resolved
Hide resolved
crates/biome_js_analyze/src/assist/source/organize_imports/import_source.rs
Show resolved
Hide resolved
Yes. I can add something to the blog post. |
261b2f0 to
ea01bdd
Compare
There was a problem hiding this comment.
The documentation must be updated to reflect the new options. Which means that:
- we need to document
legacyand how it works (probably not needed but it needs to be highlighted) - we need to document
importGroupsand provide enough examples to explain the new behaviour
I'm not going to block the PR, but this is something we must do before the beta, if we plan to release it for the beta
506f5a6 to
945cdd6
Compare
945cdd6 to
ab1dbae
Compare
arendjr
left a comment
There was a problem hiding this comment.
I think we’re getting there, right? 😊
I have just committed the blank-line between groups feature. I have to document the new system before merging the PR. |
0f6a3e6 to
871bdb3
Compare
dffa3cc to
b92d845
Compare
|
The documentation is committed. Feel free to review. |
b92d845 to
6d0760b
Compare
There was a problem hiding this comment.
This is an outstanding feature; thank you, @Conaclos! I'm sure a lot of users will be pleased. I hope you're proud of what you just did.
I left various comments around the docs. Many are grammar fixes. Here are some high-level pointers:
- consider a glossary to explain specific terms to place at the beginning
- Let's first discuss our defaults without mentioning custom groups. Then, once all the important information is listed, we can have "custom groups" at the very end. A user needs to know about default groups, blank lines, comments, etc., to create groups.
- Before showing the example, could you explain it and describe its outcome, too? I know the codegen shows a diagnostic, but it's important for accessibility too.
| /// This ensures that copyright notice and file header comment stay at the top of the file. | ||
| /// | ||
| /// ```js,expect_diagnostic | ||
| /// // Copyright notice and file header comment |
There was a problem hiding this comment.
I know that I am reviewing only the docs now, but I wonder if I should narrow down the expectation only to multiline comments. What do you think?
6d0760b to
5f535ae
Compare
| /// Conversely, `@any/lib/subpath` matches `@any/lib/**`, but not `@any/lib`. | ||
| /// Thus you have to specify the two patterns if you want to accept all imports/exports that starts with `@any/lib`. |
There was a problem hiding this comment.
| /// Conversely, `@any/lib/subpath` matches `@any/lib/**`, but not `@any/lib`. | |
| /// Thus you have to specify the two patterns if you want to accept all imports/exports that starts with `@any/lib`. | |
| /// Conversely, `@any/lib/subpath` is matched by `@any/lib/**`, but not `@any/lib`. | |
| /// So you have to specify both patterns if you want to accept all imports/exports that start with `@any/lib`. |
There was a problem hiding this comment.
is matched by
Is it not the reverse?
A string matches a glob pattern?
There was a problem hiding this comment.
Well, there's a subject doing the matching, and an object being matched. To be really pedantic, I think you should even say there's an algorithm using a glob to match a string (because a glob on its own cannot perform an action), meaning the string is matched by the algorithm. But more commonly I think it's acceptable to say a glob is doing the matching, which is also how you're using it almost everywhere else in the documentation, including the sentence above. But in a few places, like here, you swap the subject and the object, which is confusing because it leads the reader to believe @any/lib/subpath is the glob.
When I read this, I literally stopped to wonder what part of @any/lib/subpath was the pattern.
a69265b to
402ebd2
Compare
5065375 to
2c70082
Compare
arendjr
left a comment
There was a problem hiding this comment.
Left one more round of suggestions/typo fixes. They should all be directly applicable from the comment ;)
Thanks for your immense work on this!
| /// | ||
| /// ## Supported glob patterns | ||
| /// | ||
| /// You have to understand the structure of a source to understand which source match a glob. |
There was a problem hiding this comment.
| /// You have to understand the structure of a source to understand which source match a glob. | |
| /// You have to understand the structure of a source to understand how it can be matched by a glob pattern. |
| /// `file.js` matches `*.js`. | ||
| /// Conversely, `src/file.js` doesn't match `*.js` |
There was a problem hiding this comment.
| /// `file.js` matches `*.js`. | |
| /// Conversely, `src/file.js` doesn't match `*.js` | |
| /// `*.js` matches `file.js`, but does not match `src/file.js`. |
| /// `file.js` and `src/file.js` match `**` and `**/*.js` | ||
| /// Conversely, `README.txt` doesn't match `**/*.js` because the source ends with `.txt`. |
There was a problem hiding this comment.
| /// `file.js` and `src/file.js` match `**` and `**/*.js` | |
| /// Conversely, `README.txt` doesn't match `**/*.js` because the source ends with `.txt`. | |
| /// `**` and `**/*.js` match both `file.js` and `src/file.js`. | |
| /// Conversely, **/*.js` doesn't match `README.txt` because the source ends with `.txt`. |
| /// `file.js` matches `!*.test.js`. | ||
| /// `src/file.js` matches `!*.js` because the source contains several segments. |
There was a problem hiding this comment.
| /// `file.js` matches `!*.test.js`. | |
| /// `src/file.js` matches `!*.js` because the source contains several segments. | |
| /// `!*.test.js` matches `file.js` because it doesn't end with `.test.js`. | |
| /// `!*.js` matches `src/file.js` because the source contains several segments. |
acd4100 to
ea6b399
Compare
Summary
This long awaited feature is finally here! It is one of the most difficult features I have implemented. It took me one failed attempt and many hours. I am very happy with the result.
This PR implements #3177 with some divergences:
This is left to a future PR.
This drastically simplifies import matching and makes it more powerful.
This new import organizer provides the following features:
It orders named specifiers using a natural order
It orders import attributes according to their keys using a natural order
Imports and exports are separated into chunks before to be sorted.
A new chunk starts every time we met a side effect (bare) import, a distinct statement kind, or a detached comment.
A detached comment is separated by a blank line of the import/export.
Chunk of imports or exports are first sorted according to the group they belong to.
By default, there is a single group: the implicit group.
Users can define their own groups and define an order between them.
The implicit group always comes last.
Groups are either predefined (like
:BUN:and:NODE:) or globs with exceptions.Imports of a group are sorted according to other parameters, notably their import source.
See the RFC and the implementation for more details.
The implementation takes care of removing and adding newlines where required.
Chunks are separated by a blank line except for side effect imports when other imports surround them.
Comments are either attached or detached to an import/export.
A detached comment (separated by a blank line of the import/export) creates a new chunk and is kept above the chunk.
I added an exception for the header comment in a file: this comment is never attached. This ensures that Copyright notice are never moved.
Limitation / Possible improvements
@scope/libdoesn't match the glob@scope/lib/**.This could create some frictions.
node:**This could be easily fixed.
As a workaround, users have to match against both
node:*andnode:*/**Trivia handling when sorting named specifiers and attributes should be improved.(Done)Some data structures could be shared for sorting distinct named specifiers and import attributes.(Not worth the complexity?)Implementation strategy
The
runprocedure checks if everything is organized and register any "issues" that it founds.An issue can report an unorganized import/export or an unsorted chunk (more precisely unsorted prefix of a chunk).
The
actionprocedure is responsible for fixing the found issues.This is different of the previous implementation where the entire sorting logic was in
run.This new approach pursuits two objectives:
Notably all the chunk logic is in
run.actiondoesn't bother about it.Also, by dividing chunk sorting and individual import/export organizer, we are able to avoid a costly chunk sort when only named specifiers of an import are unsorted.
I think there is room for improving the code. However, it is already a big PR and I would like to ship it before we release Biome 2.0 beta.
Test Plan
I added some tests.