NativeScript / nativescript-dev-webpack

A package to help with webpacking NativeScript apps.
Apache License 2.0
97 stars 49 forks source link

Cannot use const enums from plugins #1012

Closed PeterStaev closed 5 years ago

PeterStaev commented 5 years ago

Environment Provide version numbers for the following components (information can be retrieved by running tns info in your project folder or by inspecting the package.json of the project):

Describe the bug Previously it was possible to have an exported const enum defined in a plugin's .d.ts and then use its values in the app. With the new web pack only workflow and new version of the web pack plugin this is no longer possible.

To Reproduce

Expected behavior Users should be able to use const enums defined in the .d.ts files of plugins/npm packages.

Sample project const-enum-imports.zip Tried both with export const and declare const in the .d.ts but neither are working.

Additional context

DimitarTachev commented 5 years ago

Hi @PeterStaev,

We've missed documenting this case. When exposing a const enum from a plugin, you have to define it in a .ts file (without the declare keyword) and enable the preserveConstEnums option in your tsconfig.

In other words, in the attached example, you need the following files: ./test-plugin/test-plugin.ts:

export const enum IntEnum {
    X,
    Y,
    Z
}

export const enum StringEnum {
    X = "x",
    Y = "y",
    Z = "z",
}

export function test() {
    return "this is a test";
}

./test-plugin/tsconfig.json:

{
    "compilerOptions": {
        ...
        "preserveConstEnums": true,
    }
    ...
}
PeterStaev commented 5 years ago

@DimitarTachev , how will this work in an actual plugin published on NPM? For example here: https://github.com/PeterStaev/nativescript-telerik-reporting/tree/master/reporting/document I have a TS file that during compile of the plugin is transpiled to JS and this is what is included in the plugin package. Then I have a .d.ts that is included as typing info. If I don't transpile the code to JS, then how would the plugin be used form Nativescript Core JS flavor?

Also as far as I understand that compile option should be set for the app and not the plugin, right?

DimitarTachev commented 5 years ago

@PeterStaev,

When you set the preserveConstEnums property, the enums will be included in the generated JavaScript files. In other words, you just need to publish a plugin version which is transpiled with the above-mentioned configuration.

P.S. You could also keep the enum declarations in the d.ts files.

PeterStaev commented 5 years ago

@DimitarTachev , I've just tried your solution, but this is far from ideal. Adding this compile option, makes the const enum work just like a normal enum, i.e. it gets a "class" created within the JS file of the plugin. Not only that, but then in the code using the plugin the members are not replaced with the constant value, but instead are using the generated class...

So you might as well say in the docs that const enums are not supported in plugins, since using what you suggested makes const enums behave not like const enums 😞

DimitarTachev commented 5 years ago

@PeterStaev

Developing a plugin, you have to ensure that your const enums could be used both from the .d.ts file and from the .js file. Based on the selected flavor, the end-users could decide if they will inline the enums from the .d.ts files or use the classes from the js files.

I suppose that if you do not emit the enum classes in the JS files, your plugin cannot be used in NativeScript Core JS flavor even with NativeScript 5.4. Did your setup work properly in this flavor before NativeScript 6.0?

The only difference in NativeScript 6.0 should be in the NativeScript Core TS flavor. As recommended by the ts-loader for much faster builds and required by the ts-loader for HMR builds, we've switched the transpileOnly option of the ts-loader to true which leads to the const enums behavior change.

As far as I know, the const enums are still inlined by default in Angular apps and the users are able to enable them in NativeScript Core TS apps by disabling the transpileOnly flag in their webpack.config but as I already said, that's the faster and recommended approach by the ts-loader and they will also have some hmr issues.

If you want to optimize the enum calls in your plugin and inline them, you could execute multiple TypeScript compilations and have both inlined enums and generated enum classes in the JS files but I don't believe it worth it.

PeterStaev commented 5 years ago

@DimitarTachev , the plugin was working fine prior to 6.0 in both webpacked and non-webpacked apps.

For people using Core JS flavor, they would have to type the string themselves, instead of using the enum. But for people using TS they would be able to use the intellisense of the const enum in the .d.ts and yet, when the file transpiled to JS it would be replaced with the respective string, thus eliminating the additional code.

I could use a specific type for this (i.e. declare type DocumentType = "PDF" | "HTML5" |....) but then TS users will still have to type in the string instead of using the intellisense.

DimitarTachev commented 5 years ago

@PeterStaev

Thanks for the additional details.

I'm closing this issue because as I've already said, this limitation is caused by the ts-loader and even before NativeScript 6.0 the ts-loader was configured in transpileOnly mode when running the apps with hmr. You could take a look at the configuration of nativescript-dev-webpack@0.22.0 - https://github.com/NativeScript/nativescript-dev-webpack/blob/0.22.0/templates/webpack.typescript.js#L214.

Also, even before the 0.22 version of the Webpack plugin and the official HMR support in NativeScript, the end-users were able to change this setting in their webpack.config for performance improvements and I don't see any breaking changes for const enums in the plugin development in NativeScript 6.0. We've just changed our recommendations for the transpileOnly value by changing the default value for the TypeScript apps.