ts-nameof icon indicating copy to clipboard operation
ts-nameof copied to clipboard

Using ts-nameof with Vue

Open lundmikkel opened this issue 5 years ago • 11 comments

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?

lundmikkel avatar Oct 25 '19 08:10 lundmikkel

@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)

dsherret avatar Oct 25 '19 13:10 dsherret

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

Shinigami92 avatar Nov 14 '19 14:11 Shinigami92

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] })
            }
          }]
        }
        ...
      ]
    }
    ...
  }
  ...
}

babalubas090 avatar Jun 01 '20 14:06 babalubas090

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?

fairking avatar Jul 27 '20 16:07 fairking

@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>

Shinigami92 avatar Jul 27 '20 18:07 Shinigami92

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

fairking avatar Jul 27 '20 20:07 fairking

@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 avatar Jul 28 '20 06:07 Shinigami92

@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 😀

Shinigami92 avatar Jul 28 '20 08:07 Shinigami92

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 :-)

fairking avatar Jul 28 '20 09:07 fairking

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

perf2711 avatar Nov 25 '20 08:11 perf2711

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

Shinigami92 avatar Feb 26 '21 21:02 Shinigami92