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

fix: handle deeply nested whole exports #111

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

FredericEspiau
Copy link
Contributor

@FredericEspiau FredericEspiau commented Dec 19, 2024

Fixes #109

But if you have a loop in your whole exports, now you are doomed 😬

@@ -92,7 +93,7 @@ const createLanguageService = ({
projectRoot: string;
fileService: FileService;
}) => {
const languageService = ts.createLanguageService({
return ts.createLanguageService({
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Tell me if you want me to revert these kind of changes, I've got some OCDs about this kind of stuffs 😬

@@ -148,7 +147,7 @@ const updateExportDeclaration = (code: string, unused: string[]) => {

const printer = ts.createPrinter();
const printed = result ? printer.printFile(result).replace(/\n$/, '') : '';
const leading = code.match(/^([\s]+)/)?.[0] || '';
const leading = code.match(/^(\s+)/)?.[0] || '';
Copy link
Contributor Author

Choose a reason for hiding this comment

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

\s is the same as [\s]

@@ -12,6 +12,7 @@ import { MemoryFileService } from './MemoryFileService.js';
import { findFileUsage } from './findFileUsage.js';
import { parseFile } from './parseFile.js';
import { Output } from './Output.js';
import * as Export from './export.js';
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've used namespaces to organise the code. This way you can do stuff like

Export.DeclarationWholeExport.isFileFound

Tell me what you think about this

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't have any particular opinion about using namespace imports (besides, it's not an issue in terms of detecting unused code since tsr can detect these 😎

Copy link
Contributor

@kazushisan kazushisan Dec 20, 2024

Choose a reason for hiding this comment

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

I’ve been thinking about this, but can we use import { foo } from 'foo' to keep the style consistent? Sorry for the confusion🙏

@FredericEspiau FredericEspiau force-pushed the fix/handle-deeply-nested-whole-exports branch from dbdf754 to 1e5eda1 Compare December 19, 2024 13:01
@@ -424,23 +475,19 @@ const processFile = ({
break;
}
case 'whole': {
if (!item.file) {
if (!Export.WholeExportDeclaration.isFileFound(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 had to create a type guard because TypeScript couldn't determine that item.file couldn't be undefined in my deeplyGetExportNames function

@@ -2,7 +2,8 @@ import ts from 'typescript';
import { Vertexes } from './DependencyGraph.js';
import { parseFile } from './parseFile.js';

const ALL_EXPORTS_OF_UNKNOWN_FILE = '__all_exports_of_unknown_file__';
const ALL_EXPORTS_OF_UNKNOWN_FILE = '#all_exports_of_unknown_file#';
const CIRCULAR_DEPENDENCY = '#circular_dependency#';
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This doesn't the whole circular import issue though

@@ -128,143 +129,6 @@ const getChange = (
};
};

type Export =
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Refactored in their own files

Copy link
Contributor

@kazushisan kazushisan left a comment

Choose a reason for hiding this comment

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

Thank you for your awesome work 🚀

@@ -536,13 +583,11 @@ export {};\n`,
}

if (changes.length === 0) {
const result = {
return {
Copy link
Contributor

Choose a reason for hiding this comment

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

/**
* Whole export when the file is found within the destFiles
*/
export type FileFound = {
Copy link
Contributor

Choose a reason for hiding this comment

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

declaring a type which is almost the same seems a bit redundant. How about

export type FileFound = WholeExportDeclaration & { file: string }

@@ -2,7 +2,8 @@ import ts from 'typescript';
import { Vertexes } from './DependencyGraph.js';
import { parseFile } from './parseFile.js';

const ALL_EXPORTS_OF_UNKNOWN_FILE = '__all_exports_of_unknown_file__';
const ALL_EXPORTS_OF_UNKNOWN_FILE = '#all_exports_of_unknown_file#';
Copy link
Contributor

Choose a reason for hiding this comment

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

I like your idea of using # for special keywords. Definitely better than my original implementation!

Comment on lines +186 to +187
*
* No need to memoize this function because `parseFile` already memoizes the file parsing.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
*
* No need to memoize this function because `parseFile` already memoizes the file parsing.

I think this comment is too much [nit]

@@ -0,0 +1,16 @@
import ts from 'typescript';
Copy link
Contributor

Choose a reason for hiding this comment

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

This is just a matter of preference, but I like to avoid splitting files until its absolutely necessary. I think the export.ts file is okay but can you move the code of namedExports.ts and wholeExportDeclaration.ts into this single file to align with the other parts of the project?

files,
fileNames,
options,
filesAlreadyVisited = new Set<string>(),
Copy link
Contributor

Choose a reason for hiding this comment

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

I think that this field is exposing unnecessary choice/information outside of the function's boundary. Can this be declared inside the function?

function foo() {
 const visited = new Set();

  // do something then
  deeply(theOtherArgs, visited)
}

or maybe using a stack instead of recursively calling functions is better to have a shared context. (converting to using a stack is just a suggestion, not a must)

@@ -1130,6 +1130,26 @@ export const b = 'b';`,
assert.equal(fileService.exists('/app/a_reexport.ts'), false);
assert.equal(fileService.exists('/app/a.ts'), false);
});

it('should look for deeply nested whole re-export without removing files', () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

I think the logic not supporting loops is acceptable as long as the behavior is defined 👍 Can you add a test case to assure what happens when there's a loop?

@@ -12,6 +12,7 @@ import { MemoryFileService } from './MemoryFileService.js';
import { findFileUsage } from './findFileUsage.js';
import { parseFile } from './parseFile.js';
import { Output } from './Output.js';
import * as Export from './export.js';
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't have any particular opinion about using namespace imports (besides, it's not an issue in terms of detecting unused code since tsr can detect these 😎

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.

export * doesn't work for deeply nested exports
2 participants