Skip to content

Calling .replaceWithText(getFullText(...)) is inconsistent #721

Closed
@abirmingham

Description

@abirmingham

Describe the bug

Version: 4.0.1

Hello! I'm trying to migrate all export default cases in my codebase to named exports. In order to do so, I'm using the following code:

ts-morph code

Array.from(file.getExportedDeclarations().entries()).forEach(
    ([id, declaration]) => {
        if (id !== 'default') return;
        if (
            !_.isEqual(declaration.map(x => x.getKind()), [
                SyntaxKind.FunctionDeclaration,
            ]) &&
            !_.isEqual(declaration.map(x => x.getKind()), [
                SyntaxKind.FunctionDeclaration,
                SyntaxKind.Identifier,
            ])
        ) {
            console.error('Unexpected');
        }
        const exportAssignment = declaration[0];

        const nameTokens = file
            .getBaseNameWithoutExtension()
            .split('.');
        const name = nameTokens[nameTokens.length - 1];

        const oldText =
            exportAssignment.getPreviousSibling().getKind() ===
            SyntaxKind.SingleLineCommentTrivia
                ? exportAssignment.getText()
                : exportAssignment.getFullText();
        let newNode;
        if (oldText.includes('export default function')) {
            newNode = exportAssignment.replaceWithText(
                oldText.replace('export default', `export`),
            );
        } else {
            newNode = exportAssignment.replaceWithText(
                oldText.replace(
                    'export default',
                    `export const ${name} =`,
                ),
            );
        }
        
        // 3) Track references
        file.getReferencingSourceFiles().forEach(f => {
            results.push({
                exportFile: file,
                importFile: f,
                namedExport: { name },
            });
        });
    },
);

Note the acrobatics in order to define oldText. This is because getFullText() returns both SingleLineCommentTrivia and js doc comments, but SingleLineCommentTrivia exist as sibling nodes while the latter does not. So exportAssignment.replaceWithText(x) must define x to include js doc comments but exclude SingleLineCommentTrivia. This requires testing the previous sibling node. Is this desired? Am I doing it wrong?

Thanks!

Example Default Import 1

// tslint:disable-next-line:no-any only-arrow-functions
export default function Foo() {...}

Example Default Import 2

/** This is Foo */
export default function Foo() {...}

Example Default Import 3

 /**
  * Prevents an input's `onChange` property from firing on every change. Firing
  * on every change makes the input appear laggy. Instead it waits for changes
  * to stop happening for a set amount of time, then it triggers an `onChange`.
  */
 // tslint:disable-next-line:only-arrow-functions
export function Debounce<Val, BaseProps extends ControlledInput<Val>>(
     Component:
         | React.ComponentClass<BaseProps>
         | React.FunctionComponent<BaseProps>,
     ) {...}

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions