coderaiser/putout

Preserving space

wenq1 opened this issue ยท 21 comments

wenq1 commented

I have a replacer like this:

                let     js_source       = `testFunc (a, b);`;

                let     replacer3   = {
                        report:         () => 'nil',
                        replace:        () => ({
                                'testFunc (__args)':          ({__args}, path1) => {
                                        __args.splice (0, 0, __args[1]);   
                                        return path1;
                                },
                        }),
                };

                let     js_output3      =  putout (js_source, {
                        plugins: [
                                ['replacer3', replacer3],
                        ],
                });

The output becomes, testFunc(b, a, b), instead of testFunc (b, a, b) which preserves the original spacing. Can space preservation be achieved? For now I just use a linter as a post-step, which I don't think should be necessary.

Further to the question, if js_source is set to testFunc (/** @type a */ (a), b);, the output becomes testFunc/** @type a */(b, a, b);, which adds up to the spacing issue.

Very nice library indeed!

The thing is ๐ŸŠPutout is code transformer and it do AST-transformation mostly, for formatting you can use ESLint, or prettier, they are best in this feild.

To achive the best possible results I suggest you to use eslint-plugin-putout, you can add your config on top of it, and set whitespaces count with help of indent rule.

wenq1 commented

Thanks. Iโ€™m using custom plugins, and traverse the Filesystem manually, so eslint can only be used as a post step.

Regarding the second case, the output becomes testFunc/** @type a */(b, a, b);, which looks to me like a bug? As it messes up with the Jsdoc types fed into typescript.

Regarding the second case, the output becomes testFunc/** @type a */(b, a, b);, which looks to me like a bug? As it messes up with the Jsdoc types fed into typescript.

As I see testFunc (/** @type a */ (a), b); transformed to testFunc(/** @type a */ a, b); this is the same result you will have using no-extra-parens of ESLint.

wenq1 commented

spacing issue is fixable. Let's take a look again at /** @type */ issue. I provide a screenshot below:

Case1:

                let     js_source       = `testFunc (/** @type {string} */ a, b);`;

                let     replacer3   = {
                        report:         () => 'nil',
                        replace:        () => ({
                                'testFunc (__args)':          ({__args}, path1) => {
                                        __args.splice (0, 0, __args[1]);
                                        return path1;
                                },
                        }),
                };

                let     js_output3      =  putout (js_source, {
                        plugins: [
                                ['replacer3', replacer3],
                        ],
                });

                console.log (`- js_source: ${js_source}`);
                console.log (`- js_output3.code: ${js_output3.code}`);

Result:

[2023/02/12 10:40:45.433] - js_source: testFunc (/** @type {string} */ a, b);
[2023/02/12 10:40:45.435] - js_output3.code: testFunc(b, /** @type {string} */ a, b);

Case2:

                let     js_source       = `testFunc (/** @type {string} */ (a), b);`;        // this line is different from above

                let     replacer3   = {
                        report:         () => 'nil',
                        replace:        () => ({
                                'testFunc (__args)':          ({__args}, path1) => {
                                        __args.splice (0, 0, __args[1]);
                                        return path1;
                                },
                        }),
                };

                let     js_output3      =  putout (js_source, {
                        plugins: [
                                ['replacer3', replacer3],
                        ],
                });

                console.log (`- js_source: ${js_source}`);
                console.log (`- js_output3.code: ${js_output3.code}`);

Result:

[2023/02/12 10:42:10.574] - js_source: testFunc (/** @type {string} */ (a), b);
[2023/02/12 10:42:10.574] - js_output3.code: testFunc/** @type {string} */(b, a, b);

We are talking about case 2 here. Look at /** @type {string} */ here. It is before the parenthesis, not inside, which is completely wrong.

This is related mostly to Babel and Recast, ๐ŸŠPutout build on top of them, so you better write to that repositories about such issues with comments.

wenq1 commented

Better mention that in the docs.

Look comments is usually a bad practice, they should be made in rare cases when you need to clarify why bad code written. Why don't you just use TypeScript if you need types?

Better mention that in the docs.

PR's are welcome.

OK, we have 3 variants:

So the problem in recast. Do you have ideas for a PR? I can merge it to my fork it will be 10 times faster.

Just made in issue benjamn/recast#1272.

Landed support of CallExpression to @putout/recast v1.12.0.

Please re-install ๐ŸŠPutout. You can support project if it helpful to you :).

wenq1 commented

Better mention that in the docs.

PR's are welcome.

See #130. Now another question raised based on this: how can we tell whether it is indeed a recast bug or it is a putout one?

Look comments is usually a bad practice, they should be made in rare cases when you need to clarify why bad code written. Why don't you just use TypeScript if you need types?

Not today, thanks.

wenq1 commented

Regarding the recast issue, I briefly took a look.

The "leading comment" should be for argument1, not for the callee in the CallExpression.

Is fixed version works for you?

wenq1 commented

is a new version published? I'm currently on 2.16.0

2.16.0 is version of what?

Landed support of CallExpression to @putout/recast v1.12.0.

Please re-install ๐ŸŠPutout. You can support project if it helpful to you :).

wenq1 commented

yes it now resolves the commentary issue.

wenq1 commented

However, the output is still wrong.

Input:
testFunc (/** @type {string} */ (a), b);

Should be transformed into
testFunc(b, /** @type {string} */ (a), b);

not
testFunc(b, /** @type {string} */ a, b);

This is the the way to do casting with JSDoc to allow tsc to work with JS files.

There is no way to keep ( and ).

wenq1 commented

Have you considered allowing an option to use Prettier with putout? I just came across it and reading its docs. It seems to be a better fork of benjamin's recast's printer.

== Edit: its a bad idea as it seems after I read its doc.

wenq1 commented

I have experimented with recast the whole afternoon.You are correct. There's a zillion way the output formatting went horribly wrong. (e.g. benjamn/recast#297 is really getting me mad...)

I have experimented with recast the whole afternoon.You are correct. There's a zillion way the output formatting went horribly wrong. (e.g. benjamn/recast#297 is really getting me mad...)

You have a couple options to not be mad:

  1. Create a better tool, why not? If it will support everything Recast supports, and will have the same API I'll switch :).
  2. Try to understand Recast codebase, thing that guys doing really-really-really hard (and not really interesting to spend years on).
  3. Try to understand how comments presented in AST, if we talk about Babel, you have some kind of leadingComments and trailingComments as node properties, but if we talk about ESTree, the only thing we have is an array comments on top of AST:
    image

And try to understand is it leadingComments or trailingComments? Do you have any ideas of algorithm to determine this? Is it a lot of fun to think about it?

Recast supports both, and this is some kind of magic, that we can automatically transform codebase and keep it in "our style", if our style is more or less looks similar to others code, and simple enough to be determined programmatically.

I have experimented with recast the whole afternoon.You are correct. There's a zillion way the output formatting went horribly wrong. (e.g. benjamn/recast#297 is really getting me mad...)

You have a couple options to not be mad:

  1. Create a better tool, why not? If it will support everything Recast supports, and will have the same API I'll switch :).
  2. Try to understand Recast codebase, thing that guys doing really-really-really hard (and not really interesting to spend years on).
  3. Try to understand how comments presented in AST, if we talk about Babel, you have some kind of leadingComments and trailingComments as node properties, but if we talk about ESTree, the only thing we have is an array comments on top of AST:
    image

And try to understand is it leadingComments or trailingComments? Do you have any ideas of algorithm to determine this? Is it a lot of fun to think about it?

Recast supports both, and this is some kind of magic, that we can automatically transform codebase and keep it in "our style", if our style is more or less looks similar to others code, and simple enough to be determined programmatically.