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

The execution results of decorator code compiled by esbuild and compiled by tsc are inconsistent #3911

Closed
alamhubb opened this issue Sep 13, 2024 · 7 comments

Comments

@alamhubb
Copy link

alamhubb commented Sep 13, 2024

main.ts

function Resource(target, {kind, name}) {
    return function (initialValue) {
        console.log(222222) //1
        return 666;
    };
}

class TestSerivce {
    @Resource
    testA;

    test() {
        console.log(11111);
        console.log(this.testA);
    }
}

const newc = new TestSerivce()
newc.test()

excute esbuild
npx esbuild main.ts --bundle --platform=node --outfile=out.js; node out.js
get console undefined
image

esbuild version 0.21.5

excute tsc compiler

tsc main.ts;node main.js

get result success

image

Related issues

vitejs/vite#18101 (comment)

@evanw
Copy link
Owner

evanw commented Sep 13, 2024

main.ts

function Resource(target, {kind, name}) {
    return function (initialValue) {
        console.log(222222) //1
        return 666;
    };
}

class TestSerivce {
    @Resource
    testA;

    test() {
        console.log(11111);
        console.log(this.testA);
    }
}

const newc = new TestSerivce()
newc.test()

excute esbuild npx esbuild main.ts --bundle --platform=node --outfile=out.js; node out.js

I can't reproduce this, sorry. Here's the live link to your code example. As you can see in the live link, the command line you're using doesn't tell esbuild to transform decorators at all, so your reproduction instructions result in the output code still containing decorators. In that case the behavior of decorators is up to the JavaScript VM that you use to run them, which has nothing to do with esbuild.

If you want esbuild to transform decorators, then you'll have to tell it to do so. You can do that by setting target to a target other than esnext (the default value) such as --target=es6 or --target=node20 or even very specifically with --supported:decorators=false.

I'm guessing what's happening here is that you actually have a tsconfig.json file containing "experimentalDecorators": true that you aren't describing in your reproduction instructions. If that's the case then esbuild will compile your decorators using TypeScript's legacy decorator behavior which does have the behavior that you observed where only 111111 is printed. Here's a live link with tsconfig.json included. If you're doing that and you want esbuild to not treat decorators as TypeScript experimental decorators, then you should not use "experimentalDecorators": true.

@alamhubb
Copy link
Author

Thanks for your reply.

Sorry, my English is a little poor. I am Chinese, so what I say below may not be accurate. Please forgive me.

The tsconfig in my project does contain the configuration.

"experimentalDecorators": true,

So I successfully got the output

image

image

When I use tsc to compile ts code, I set experimentalDecorators to false and get the correct result 666.
When I use esbuild, I set experimentalDecorators to false, and it prompts that decorators cannot be recognized.

Please forgive me for my poor English, the following content may contain errors

microsoft/TypeScript#50820

image

I understand that the proposal says that when experimentalDecorators is false, the decorator should be automatically converted.

But esbuild does not convert it. I think there may be something wrong with my understanding. Please help me solve it.

I want esbuild to output the correct result 666. How can I do it? Does esbuild support the implementation of the latest tc39 proposal?

And the two input parameters of the decorator are,

ES Decorators now accept exactly two arguments: target and context:

target — A value representing the element being decorated

context — An object containing additional context information about the decorated element

@alamhubb
Copy link
Author

main.ts

function Resource(target, {kind, name}) {
    return function (initialValue) {
        console.log(222222) //1
        return 666;
    };
}

class TestSerivce {
    @Resource
    testA;

    test() {
        console.log(11111);
        console.log(this.testA);
    }
}

const newc = new TestSerivce()
newc.test()

excute esbuild npx esbuild main.ts --bundle --platform=node --outfile=out.js; node out.js

I can't reproduce this, sorry. Here's the live link to your code example. As you can see in the live link, the command line you're using doesn't tell esbuild to transform decorators at all, so your reproduction instructions result in the output code still containing decorators. In that case the behavior of decorators is up to the JavaScript VM that you use to run them, which has nothing to do with esbuild.

If you want esbuild to transform decorators, then you'll have to tell it to do so. You can do that by setting target to a target other than esnext (the default value) such as --target=es6 or --target=node20 or even very specifically with --supported:decorators=false.

I'm guessing what's happening here is that you actually have a tsconfig.json file containing "experimentalDecorators": true that you aren't describing in your reproduction instructions. If that's the case then esbuild will compile your decorators using TypeScript's legacy decorator behavior which does have the behavior that you observed where only 111111 is printed. Here's a live link with tsconfig.json included. If you're doing that and you want esbuild to not treat decorators as TypeScript experimental decorators, then you should not use "experimentalDecorators": true.

Thank you for your reply, but I still have some questions. I forgot to reply you above. The content is all above. Please check it out.

@alamhubb
Copy link
Author

alamhubb commented Sep 13, 2024

main.ts

function Resource(target, {kind, name}) {
    return function (initialValue) {
        console.log(222222) //1
        return 666;
    };
}

class TestSerivce {
    @Resource
    testA;

    test() {
        console.log(11111);
        console.log(this.testA);
    }
}

const newc = new TestSerivce()
newc.test()

excute esbuild npx esbuild main.ts --bundle --platform=node --outfile=out.js; node out.js

I can't reproduce this, sorry. Here's the live link to your code example. As you can see in the live link, the command line you're using doesn't tell esbuild to transform decorators at all, so your reproduction instructions result in the output code still containing decorators. In that case the behavior of decorators is up to the JavaScript VM that you use to run them, which has nothing to do with esbuild.

If you want esbuild to transform decorators, then you'll have to tell it to do so. You can do that by setting target to a target other than esnext (the default value) such as --target=es6 or --target=node20 or even very specifically with --supported:decorators=false.

I'm guessing what's happening here is that you actually have a tsconfig.json file containing "experimentalDecorators": true that you aren't describing in your reproduction instructions. If that's the case then esbuild will compile your decorators using TypeScript's legacy decorator behavior which does have the behavior that you observed where only 111111 is printed. Here's a live link with tsconfig.json included. If you're doing that and you want esbuild to not treat decorators as TypeScript experimental decorators, then you should not use "experimentalDecorators": true.

Thank you, my problem was solved, I set target=es2020, and then converted successfully

image

https://esbuild.github.io/try/#YgAwLjIzLjEAbWFpbi50cyAtLXRhcmdldD1lczIwMjAgLS1wbGF0Zm9ybT1ub2RlIC0tb3V0ZmlsZT1vdXQuanMAZQBtYWluLnRzAGZ1bmN0aW9uIFJlc291cmNlKHRhcmdldCwge2tpbmQsIG5hbWV9KSB7CiAgICByZXR1cm4gZnVuY3Rpb24gKGluaXRpYWxWYWx1ZSkgewogICAgICAgIGNvbnNvbGUubG9nKDIyMjIyMikgLy8xCiAgICAgICAgcmV0dXJuIDY2NjsKICAgIH07Cn0KCmNsYXNzIFRlc3RTZXJpdmNlIHsKICAgIEBSZXNvdXJjZQogICAgdGVzdEE7CgogICAgdGVzdCgpIHsKICAgICAgICBjb25zb2xlLmxvZygxMTExMSk7CiAgICAgICAgY29uc29sZS5sb2codGhpcy50ZXN0QSk7CiAgICB9Cn0KCmNvbnN0IG5ld2MgPSBuZXcgVGVzdFNlcml2Y2UoKQpuZXdjLnRlc3QoKQ

But I still have a small question, that is, when tsc compiles, decorators can be successfully compiled without any parameters

but esbuild needs to set target=es2020, and tc39 states that when experimentalDecorators is set to false, it should be automatically converted. Is it that the default target version of tsc is inconsistent with the default target version of esbuild?

@evanw
Copy link
Owner

evanw commented Sep 13, 2024

Is it that the default target version of tsc is inconsistent with the default target version of esbuild?

Yes. The default target for esbuild is esnext (docs) while the default target for tsc is es5 (docs).

@alamhubb
Copy link
Author

alamhubb commented Sep 13, 2024

I am using vite to develop an application. Vite is based on esbuild packaging. However, since I just used esbuild to get the correct result from the link, but my vite result is still wrong, I thought it was a problem with vite.

Then I debugged the source code of vite.

At the end of the debugging, I found that the problem came from the configuration item loader=ts of esbuild, and

https://esbuild.github.io/content-types/#tsconfig-json

target
useDefineForClassFields

For compatibility with TypeScript, esbuild also copies TypeScript's behavior where when useDefineForClassFields is not
specified, it defaults to false when tsconfig.json contains a target that is earlier than ES2022. But I recommend setting
useDefineForClassFields explicitly if you need it instead of relying on this default value coming from the value of the target
setting. Note that the target setting in tsconfig.json is only used by esbuild for determining the default value of
useDefineForClassFields. It does not affect esbuild's own target setting, even though
they have the same name.

image

If I use a decorator

This rule is too hidden, and it does not use the configuration in tsconfig.json, so additional configuration is required.

esbuild: {
        sourcemap: false,
        target: 'es2020',
        tsconfigRaw: {
            compilerOptions: {
                useDefineForClassFields: true,
                target: 'es2020'
            }
        }
    }

This problem makes me sad. Can this rule be made better?

For example, tsconfigRaw will replace compilerOptions with the configuration in tsconfig.json by default

@alamhubb alamhubb reopened this Sep 13, 2024
@alamhubb
Copy link
Author

alamhubb commented Sep 15, 2024

This is a problem with vite, not related to esbuild, so close the question, thank you very much for your answer

vitejs/vite#18105 (comment)

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

No branches or pull requests

2 participants