web
web copied to clipboard
Unable to use @web/dev-server-esbuild#0.3.0 with TS and lit
Related to https://github.com/lit/lit/issues/2716
After updating to version 0.3.0 I started seeing errors related to the use of decorators. I was able to pinpoint the problem to the update of the @web/dev-server-esbuild
library in my projects. I was able to run my projects again after downgrading to version 0.2.16
.
Please, refer to the referenced issue for more details.
This is likely an issue with esbuild, and/or your configuration. The plugin is just a passthrough.
Mine configuration is pretty much what's in the @open-wc init with addition of esbuildPlugin
and some local API for development:
// import { hmrPlugin, presets } from '@open-wc/dev-server-hmr';
import { esbuildPlugin } from '@web/dev-server-esbuild';
import LocalApiStore from './demo/store/StorePlugin.js';
/** Use Hot Module replacement by adding --hmr to the start command */
const hmr = process.argv.includes('--hmr');
export default /** @type {import('@web/dev-server').DevServerConfig} */ ({
open: '/demo/',
/** Use regular watch mode if HMR is not enabled. */
watch: !hmr,
/** Resolve bare module imports */
nodeResolve: {
exportConditions: ['browser', 'development'],
},
/** Compile JS for older browsers. Requires @web/dev-server-esbuild plugin */
// esbuildTarget: 'auto'
/** Set appIndex to enable SPA routing */
// appIndex: 'demo/index.html',
plugins: [
esbuildPlugin({ ts: true, target: 'auto' }),
/** Use Hot Module Replacement by uncommenting. Requires @open-wc/dev-server-hmr plugin */
// hmr && hmrPlugin({ exclude: ['**/*/node_modules/**/*'], presets: [presets.litElement] }),
LocalApiStore,
],
// preserveSymlinks: true,
// See documentation for all available options
});
I also didn't change much in the tsconfig file:
{
"compilerOptions": {
"target": "es2018",
"module": "esnext",
"moduleResolution": "node",
"noEmitOnError": true,
"lib": ["es2017", "dom"],
"strict": true,
"esModuleInterop": false,
"allowSyntheticDefaultImports": true,
"experimentalDecorators": true,
"importHelpers": true,
"outDir": "dist",
"sourceMap": true,
"inlineSources": true,
"rootDir": "./",
"declaration": true,
"incremental": true
},
"include": ["**/*.ts"]
}
Some System information:
- Node version: 16.14.0
- NPM version: 8.7.0
- OS: Ubuntu 20.04
I'm having a similar issue with version 0.3.0
of @web/dev-server-esbuild
.
Also the issue disappears when I downgrade the version to 0.2.16
.
I don't think it's an issue with the esbuild plugin.
But surely some strange things are happening.
From version 0.2.16
to 0.3.0
, the only change I see is that now one can use the esbuild
major version 14.
In version 0.2.16
, the esbuild
version used is the 0.12.29
.
When one upgrades the esbuild plugin to 0.3.0
, the esbuild
version changes to 0.14.36
.
I have created a repository where one can see that something strange is happening when the esbuild
version is the latest:
https://github.com/jordimarimon/esbuild-test-server-decorators
Steps to reproduce:
- Clone the repository
- Run
npm install
- Run the tests:
npm run test
- You will see that all tests pass
Change the @web/dev-server-esbuild
version to 0.3.0
- Run the tests:
npm run test
- Now you should see that tests pass in all browsers except in chromium
When working with Lit, the decorators that don't have a descriptor, a default descriptor is created:
See: https://github.com/lit/lit/blob/main/packages/reactive-element/src/reactive-element.ts#L648 See: https://github.com/lit/lit/blob/3fec73e669bc01660747668e2f173508e2a2a006/packages/reactive-element/src/reactive-element.ts#L707
The default descriptor adds an instance field prefixed with two underscores and a prototype field.
This seems to not be the case in chromium when using the latest version of @web/dev-server-esbuild
and esbuild
.
Things start getting stranger when I serve a demo page with an output generated with the latest version of esbuild
. The output generated with the latest version of esbuild does have the correct instance and prototype field defined.
To reproduce this last case:
- Build the component:
npm run build:esbuild
- Serve the demo page:
npm start
- Open the devtools in Chrome -> You should see that the property descriptors are correctly defined
Also when I execute the tests, it does pass in Firefox and Webkit. If this was an error in the latest version of esbuild
, all browsers should fail.
The problem is only visible in Chromium with the latests versions of @web/dev-server-esbuild
and esbuild
.
I really don't know why this is happening.
I'm aware that this is not an issue with the @web/dev-server-esbuild
because it's just a passthrough.
But it can't also be an issue with the latest version of esbuild
because the tests does pass in Firefox and Webkit. And also I don't see anything wrong in the output of esbuild (see dist-esbuild
folder after running npm run build:esbuild
).
Sorry to post it here because probably it doesn't have anything to do with the esbuild plugin but because I found it related, I want it at least to share it here.
I'm going to continue to research what is happening.
I had the same problem. Change the target from "auto" to a specific ECMAScript version, e.g. "es2017". It's needed because of a change in ESBuild.
Change class field behavior to match TypeScript 4.3
TypeScript 4.3 includes a subtle breaking change that wasn't mentioned in the TypeScript 4.3 blog post: class fields will now be compiled with different semantics if "target": "ESNext" is present in tsconfig.json. Specifically in this case useDefineForClassFields will default to true when not specified instead of false. This means class field behavior in TypeScript code will now match JavaScript instead of doing something else:
class Base { set foo(value) { console.log('set', value) } } class Derived extends Base { foo = 123 } new Derived()
In TypeScript 4.2 and below, the TypeScript compiler would generate code that prints set 123 when tsconfig.json contains "target": "ESNext" but in TypeScript 4.3, the TypeScript compiler will now generate code that doesn't print anything. This is the difference between "assign" semantics and "define" semantics. With this release, esbuild has been changed to follow the TypeScript 4.3 behavior.
I think this issue could also be fixed with #1921 and setting useDefineForClassFields to false
in tsconfig.
Indeed, that issue and PR were instigated by this very problem. :)
In my repo of base UI components (which was affected by this) I heavily used mixins. I believe this was causing the problem. It took me a while but it refactored the library to replace these mixins with classes and proper inheritance chain. I upgraded the version of @web/dev-server-esbuild
to 3.0.0 just now and set useDefineForClassFields
to true
to be compatible with ES standards. Both the demo pages and the tests are now working. Probably not a path forward for everyone but it worked for me.
@jarrod Are you sure changing useDefineForClassFields
made a difference in your case? Currently @web/dev-server-esbuild
doesn’t read tsconfig.json
so it doesn’t make sense that changing that file would have any impact.
No, this didn't make the difference. I changed the entire code base to avoid mixins, I redesigned how elements inherit properties, and I am now avoiding a situation where a property is set on a derived class that shadows parent property. At least I thought it was working. It was working in demo pages and tests but when I published the library and started using it in another project I couldn't make it work. Property setters didn't work. Only when I called a function that also sets a property in the parent class it started partially working. Eventually, I reverted to useDefineForClassFields: false
.
Some weird thing happened today. I updated dependencies and the project stopped working again. I notices that in the compiled by TSC output classes are emitted as expected, e.g.
class A {
constructor() {
this.a = 2;
}
}
While the generated by the esbuildPlugin / esbuild code is similar to this:
class A {
a = 2;
}
This naturally causes the class to not respect decorated properties as the a
filed is defined in the A
class. I didn't change the configuration so it must be the update. However, after spending last 4 hours on this I still don't know why this is happening or how to fix this.
Edit: Here's a workaround: https://github.com/evanw/esbuild/issues/2220#issuecomment-1116082001
We need to downgrade it: https://github.com/IBM/pwa-lit-template/pull/516
We need to downgrade the esbuild
version since 0.3.0
in: https://github.com/Qiskit/web-components/pull/79
We also had this problem with the error message Error: The following properties on element [...] will not trigger updates as expected because they are set using class fields: [...]. Native class fields and some compiled output will overwrite accessors used for detecting changes. See https://lit.dev/msg/class-field-shadowing for more information.
This was my solution to the problem:
- update to ^0.3.1
- set
useDefineForClassFields
tofalse
in tsconfig - load
tsconfig.json
file with
esbuildPlugin({
ts: true,
target: "auto",
tsconfig: fileURLToPath(new URL('./tsconfig.json', import.meta.url)),
}),
according to https://github.com/modernweb-dev/web/blob/master/packages/dev-server-esbuild/CHANGELOG.md#031