dsherret / ts-nameof

nameof in TypeScript
MIT License
492 stars 23 forks source link

Using ts-nameof with Vue #83

Open lundmikkel opened 4 years ago

lundmikkel commented 4 years ago

I've tried to install the npm package using both npm install ts-nameof @types/ts-nameof --save-dev and npm install @types/ts-nameof --save-dev, and included a reference to ts-nameof in my tsconfig.json like this:

{
  "compilerOptions": {
    "types": [
      "ts-nameof"
    ],
    "typeRoots": [
      "./node_modules/@types/",
    ],
  }
}

But without much luck. Do you have any suggestions as how to continue?

dsherret commented 4 years ago

@lundmikkel how are you compiling it? This is a compile time transformation so it needs to get injected into the compiler.

Generally the instructions are: https://github.com/dsherret/ts-nameof/blob/master/packages/ts-nameof/setup/tsc.md (which sucks, but that's the current state of things because compiler plugins aren't supported in tsconfig.json by default)

Shinigami92 commented 4 years ago

In vue.config.js I have

// ... chainWebpack
config.module
    .rule('ts')
    .exclude.add(/node_modules/)
    .end()
    .test(/\.ts$/)
    .use('ts-loader')
    .loader('ts-loader')
    .options({
      transpileOnly: true,
      // Transformer functions do not work with happy pack mode due to process forking, see:
      // https://github.com/TypeStrong/ts-loader#getcustomtransformers--program-program---before-transformerfactory-after-transformerfactory--
      getCustomTransformers: path.resolve(__dirname, 'vue-ts-nameof.js'),
      appendTsSuffixTo: ['\\.vue$'],
      happyPackMode: true
    })
    .end();
// ...

Content of vue-ts-nameof.js

const tsNameof = require('ts-nameof');

module.exports = () => ({
  before: [tsNameof]
});

Hope this can help in any way

babalubas090 commented 4 years ago

In order to make it work you can simply do this:

In vue.config.js you need to add:

const tsNameof = require('ts-nameof');

module.exports = {
  configureWebpack: {
    module: {
      rules: [
        {
          test: /\.ts$/,
          exclude: /node_modules/,
          use: [{
            loader: 'ts-loader',
            options: {
              getCustomTransformers: () => ({ before: [tsNameof] })
            }
          }]
        }
        ...
      ]
    }
    ...
  }
  ...
}
fairking commented 4 years ago

I tried something like the following but it is not working:

npm install ts-nameof --save-dev

Sample.vue

<template>
    <div>{{myClassName}} - {{nameof<MyClass>()}}<div>
<template>
<script lang="ts">
  import Vue from "vue";
  import { MyClass } from '../../classes';
  export default Vue.extend({
    data() {
      return {
        myClassName: nameof<MyClass>(), // Line 81
      };
    }
});
</script>

vue.config.js:

const tsNameof = require("ts-nameof");
...
module.exports = {
  ...
  configureWebpack: {
    module: {
      rules: [
      {
        test: /\.(ts|vue)$/,
        exclude: /node_modules/,
        use: [{
          loader: 'ts-loader',
          options: {
            getCustomTransformers: () => ({ before: [tsNameof] })
          }
        }]
      }
    ]
  }
  },
  ...
};

Error says:

Error   TS2552  (TS) Cannot find name 'nameof'. Did you mean 'name'?    \src\Sample.vue 81  Active

Do you have any example how to use it in vue? If not maybe with your help I can find out and create a PR with a sample. So others can use it?

Shinigami92 commented 4 years ago

@fairking Have you tried my workaround? https://github.com/dsherret/ts-nameof/issues/83#issuecomment-553907295

But I think whithin a template it will never work 🤔

<template>
    <div>{{myClassName}} - {{nameof<MyClass>()}}<div> // <- this one 🤔, you should move this into a variable
<template>
fairking commented 4 years ago

Tried. Got the following error:

 ERROR  Failed to compile with 1 errors                                                                       8:54:51 AM

 error  in ./src/components/HelloWorld.vue

Module Error (from ./node_modules/eslint-loader/index.js):

C:\Code\vue-ts-nameof-sample\src\components\HelloWorld.vue
  25:28  error  'nameof' is not defined  no-undef
  26:32  error  'nameof' is not defined  no-undef
  27:35  error  'nameof' is not defined  no-undef
  28:27  error  'nameof' is not defined  no-undef
  29:32  error  'nameof' is not defined  no-undef
  30:85  error  'nameof' is not defined  no-undef

✖ 6 problems (6 errors, 0 warnings)

 @ ./node_modules/cache-loader/dist/cjs.js??ref--14-0!./node_modules/babel-loader/lib!./node_modules/ts-loader??ref--14-2!./node_modules/cache-loader/dist/cjs.js??ref--0-0!./node_modules/vue-loader/lib??vue-loader-options!./src/App.vue?vue&type=script&lang=ts& 2:0-53 6:16-26
 @ ./src/App.vue?vue&type=script&lang=ts&
 @ ./src/App.vue
 @ ./src/main.ts
 @ multi (webpack)-dev-server/client?http://192.168.0.36:8080&sockPath=/sockjs-node (webpack)/hot/dev-server.js ./src/main.ts

No type errors found
Version: typescript 3.9.7
Time: 2567ms

See my example here: https://github.com/fairking/vue-ts-nameof-sample

Shinigami92 commented 4 years ago

@fairking I think you've already made progress and haven't noticed 😃

Your new problem is that eslint-loader is used too early. This of course does not know nameof and should only process if nameof has already been processed

Shinigami92 commented 4 years ago

@fairking

https://github.com/fairking/vue-ts-nameof-sample/blob/c24c227023f0da47fbaee713366383a49748b5c5/vue.config.js#L9

I'm not quite sure if my workaround supports embedded code in <script> I'm always using

<script lang="ts" src="./code.ts" />

So maybe cause you have code in your vue file, you need to find a improved way of handling this, good luck and post your results so others can benefit from it 😀

fairking commented 4 years ago

Moving ts scripts outside of .vue is not an option for me. The entire component must be in on single file otherwise it makes development nightmare. Sorry I am not an angular guy :-)

perf2711 commented 3 years ago

I had the same problem. Almost got it to work (vue-cli-service build sometimes worked, and sometimes it threw nameof errors).

Your new problem is that eslint-loader is used too early. This of course does not know nameof and should only process if nameof has already been processed

This comment actually guided me to the answer, so thanks @Shinigami92 😄

If someone is still trying to make this work, this is how I accomplished this.

In tsconfig.json add:

{
  /* compilerOptions, etc. */
  "files": [
    "./node_modules/ts-nameof/ts-nameof.d.ts"
  ]
}

so this declaration file would be visible globally.

My vue.config.js was looking like this:

const tsNameOf = require('ts-nameof');

module.exports = {
    chainWebpack: config => {
        config.module
            .rule('ts')
                .test(/\.ts$/)
                    .use('ts-loader')
                        .loader('ts-loader')
                            .options({
                                transpileOnly: true,
                                getCustomTransformers: () => ({ before: [tsNameOf] }),
                                appendTsSuffixTo: [/\.vue$/],
                                happyPackMode: true
                            })
                        .end()
                      .use('ts-nameof')
                        .loader('ts-nameof-loader')
                        .end()

I added the ts-nameof-loader (npm package has the same name) by the way, it helped actually replacing nameof calls with strings.

However it seems, that at this moment eslint-loader was "fighting" with ts-loader in parallel, creating a race condition, which caused the build to sometimes work, and sometimes not. So, by inspecting the webpack config using vue inspect, I moved the eslint-loader explicitly to run after ts-nameof-loader:

const tsNameOf = require('ts-nameof');

module.exports = {
    chainWebpack: config => {
        // Delete the rule completely
        config.module.rules.delete('eslint');

        config.module
            .rule('ts')
                .test(/\.ts$/)
                    .use('ts-loader')
                        .loader('ts-loader')
                            .options({
                                transpileOnly: true,
                                getCustomTransformers: () => ({ before: [tsNameOf] }),
                                appendTsSuffixTo: [/\.vue$/],
                                happyPackMode: true
                            })
                        .end()
                    .use('ts-nameof')
                        .loader('ts-nameof-loader')
                        .end()
                    .use('eslint') // Add the loader here
                        .loader('eslint-loader')
                        .options({
                            extensions: [
                                '.ts',
                                '.tsx',
                                '.vue'
                            ],
                            cache: true,
                            emitWarning: false,
                            emitError: false,
                            formatter: undefined
                        })

    }
};

And there we have it! obraz

Shinigami92 commented 3 years ago

I created a plugin for vite

https://www.npmjs.com/package/vite-plugin-ts-nameof

You still need files: ["./node_modules/ts-nameof/ts-nameof.d.ts"] in your tsconfig.json