vuejs / babel-plugin-transform-vue-jsx

babel plugin for vue 2.0 jsx
1.85k stars 132 forks source link

h is not defined in .tsx functions #174

Open gettinToasty opened 5 years ago

gettinToasty commented 5 years ago

May be a duplicate of https://github.com/vuejs/babel-plugin-transform-vue-jsx/issues/93

After configuring our TsxComponent class to properly allow babel to handle h auto-injection it appears that there is no actual injection happening in .tsx files:

TsxComponent

interface ITsx {
  render(): JSX.Element;
}

export default abstract class TsxComponent<T> extends Vue implements ITsx {
  private vueTsxProps: Readonly<{}> & Readonly<T>;

  render() {
    return <div />;
  }
}

Implementation

export default class Foo extends TsxComponent<{}> {
  render() {
    return <h1>Hello World</h1>
  }
}

Error

Vue warn]: Error in render: "ReferenceError: h is not defined"
maksnester commented 5 years ago

var h = this.$createElement isn't added in transpiled functions automatically.

However in case of render I can just write render(h) and it works. But in other functions I have to add this string manually to make it work.

Posted a question on stackoverflow as well.

gettinToasty commented 5 years ago

@Alendorff not according to the features of this library:

Starting with version 3.4.0 we automatically inject const h = this.$createElement in any method and getter (not functions or arrow functions) declared in ES2015 syntax that has JSX so you can drop the (h) parameter.

maksnester commented 5 years ago

@gettinToasty yes, sure, I expected this behavior, but that's not happened in my case. 🤷‍♂️ JSX works fine, TSX - not.

healqq commented 5 years ago

I would reccomend to look at https://github.com/kaorun343/vue-property-decorator and https://github.com/vuejs/vue-class-component, if you want to have component classes in TSX. At least check how they handle that.

gettinToasty commented 5 years ago

We use vue-property-decorator for our component classes, I don't think that's part of the issue here.

maksnester commented 5 years ago

@healqq I didn't catch your point. At what exactly I should look? I use vue-property-decorator with TSX of course. It's quite hard to effectively utilize typescript in vue project without it.

maksnester commented 5 years ago

@healqq by the way even in their example they added h manually as an argument

healqq commented 5 years ago

@Alendorff that is just a bad(old) example, we’Re using it for our project and h is autoinjected. Other option would be to wait for vue3 release, but it’ll take some time

healqq commented 5 years ago

Can you post your tsconfig? Maybe the issue is there?

gettinToasty commented 5 years ago

we have jsx: preserve in our tsconfig so ts-loader shouldnt even touch it during compile

healqq commented 5 years ago

That's correct, yes. Maybe you can do minimal repo that replicates issue?

juanfernandoe commented 5 years ago

Hi,

this is a minimal example of how I resolve this issue. there are 4 approaches.

  1. Separate template from logic
  2. typescript static type check in the template
  3. vuejs + tsx
  4. Hot reload

Test1.vue.ts

import { Vue, Component } from 'vue-property-decorator'
import { CreateElement, VNode } from 'vue';
import { render } from './Test1.vui';

@Component({})
export class Test1 extends Vue {
    public message:string = 'hello world';
    public render(h: CreateElement): VNode {
        return render.bind(this)(h);
    }
}

export default Test1;

Test1.vui.tsx

import { CreateElement, VNode } from 'vue';
import { Test1 } from './Test1.vue';

export function render(this: Test1, h: CreateElement): VNode {
    return (
        <div>
            <h1>Title</h1>
            {this.message}
        </div>
    );
}

add this rule to webpack


{
    test: /\.tsx$/,
    use: ['babel-loader', 'vue-jsx-hot-loader', 'ts-loader'],
    exclude: /node_modules/
}

tsconfig.json


{
    "compilerOptions": {
        "lib": [
            "dom",
            "es6",
            "es2015.promise"
        ],
        "sourceMap": true,
        "strict": true,
        "module": "esnext",
        "moduleResolution": "node",
        "target": "es6",
        "experimentalDecorators": true,
        "emitDecoratorMetadata": true,
        "allowSyntheticDefaultImports": true,
        "jsx": "preserve",
        "jsxFactory": "h",
        "typeRoots": ["./node_modules/@types", "./types"]
    }
}

I hope it helps someone.

miguelramos commented 4 years ago

I got the same thing with rollup+tsx. If i use .vue all files works but with tsx h is not defined. Tried loy of babel presets but always got the same thing. BRRRRRRR....

// rollup config

import { RollupOptions, rollup } from 'rollup';
/* eslint-disable sort-imports-es6/sort-imports-es6 */
/* eslint-disable @typescript-eslint/no-var-requires */
import { join, resolve } from 'path';

import { BuildPackage } from '../build-package';
import { isObject } from '../utils';
import { terser } from 'rollup-plugin-terser';

const autoprefixer = require('autoprefixer');
const nodeResolve = require('rollup-plugin-node-resolve');
const vue = require('rollup-plugin-vue');
const babel = require('rollup-plugin-babel');
const cjs = require('rollup-plugin-commonjs');
const rollString = require('rollup-plugin-string');
const ts = require('rollup-plugin-typescript2');
const requireContext = require('rollup-plugin-require-context');

const { assign, keys } = Object;

const baseConfig = (settings: BuildPackage) => {
  const {
    options: { entryFile },
    sourceDir
  } = settings;
  const pkg = require(join(sourceDir, 'package.json'));
  /*const input = Array.isArray(entryFile)
    ? [...entryFile].map(file => join(sourceDir, file))
    : join(sourceDir, entryFile);
  */
  return {
    external: [...keys(pkg.dependencies), ...keys(pkg.peerDependencies), ...['path', 'url']],
    // inlineDynamicImports: true,
    input: entryFile,
    plugins: [
      nodeResolve({
        mainFields: ['main', 'module', 'browser'],
        extensions: ['.js', '.jsx', '.ts', '.tsx', '.vue']
      }),
      cjs(),
      requireContext(),
      rollString.string({
        include: '**/*.svg'
      }),
      vue({
        css: false,
        style: {
          postcssPlugins: [autoprefixer]
        },
        compileTemplate: true
      }),
      ts({
        cacheRoot: resolve(sourceDir, 'node_modules/.rts2_cache'),
        tsconfig: join(sourceDir, 'tsconfig.build.json'),
        tsconfigOverride: {
          compilerOptions: {
            declaration: true,
            declarationDir: join(settings.outputDir, '@types'),
            moduleResolution: 'node',
            rootDir: settings.sourceDir
          }
        },
        useTsconfigDeclarationDir: true
      }),
      babel({
        // babelrc: false,
        exclude: 'node_modules/**',
        extensions: ['.js', '.jsx', '.ts', '.tsx', '.vue'],
        presets: [
          [
            '@babel/preset-env',
            {
              modules: false
            }
          ],
          //'@vue/babel-preset-jsx'
          //'@vue/babel-preset-app'
          '@vue/app'
        ],
        include: [join(settings.sourceDir, '**/*')],
        //plugins: ["transform-vue-jsx"],
        /*plugins: [
          "@babel/plugin-syntax-jsx",
          ["@babel/plugin-transform-react-jsx", { pragma : 'dom' }]
        ],*/
        //externalHelpers: true,
        runtimeHelpers: true
      })
    ]
  };
};

// compiled es format

import { __extends, __decorate, __metadata } from 'tslib';
import Vue from 'vue';
import { Component } from 'vue-property-decorator';

var UIButton =
/** @class */
function (_super) {
  __extends(UIButton, _super);

  function UIButton(props) {
    return _super.call(this, props) || this;
  }

  UIButton.prototype.render = function (_h) {
    return h("div", {
      "attrs": {
        "className": 'ui__button'
      }
    }, [h("button", ["Ok Button"])]);
  };

  UIButton = __decorate([Component({
    name: 'ui-button'
  }), __metadata("design:paramtypes", [Object])], UIButton);
  return UIButton;
}(Vue);

export default UIButton;

// compiled umd format

(function (global, factory) {
  typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('tslib'), require('vue'), require('vue-property-decorator')) :
  typeof define === 'function' && define.amd ? define(['exports', 'tslib', 'vue', 'vue-property-decorator'], factory) :
  (global = global || self, factory(global.ui = {}, global.tslib, global.Vue, global.vuePropertyDecorator));
}(this, (function (exports, tslib, Vue, vuePropertyDecorator) { 'use strict';

  Vue = Vue && Object.prototype.hasOwnProperty.call(Vue, 'default') ? Vue['default'] : Vue;

  var UIButton =
  /** @class */
  function (_super) {
    tslib.__extends(UIButton, _super);

    function UIButton(props) {
      return _super.call(this, props) || this;
    }

    UIButton.prototype.render = function (_h) {
      return h("div", {
        "attrs": {
          "className": 'ui__button'
        }
      }, [h("button", ["Ok Button"])]);
    };

    UIButton = tslib.__decorate([vuePropertyDecorator.Component({
      name: 'ui-button'
    }), tslib.__metadata("design:paramtypes", [Object])], UIButton);
    return UIButton;
  }(Vue);

  // eslint-disable-next-line sort-imports-es6/sort-imports-es6
  /**
   * Plugin install function
   *
   * @param vue - Vue Instance
   * @param _options - Plubin Options
   *
   * @internal
   */
  // eslint-disable-next-line id-match, @typescript-eslint/no-unused-vars, @typescript-eslint/no-explicit-any

  var install = function install(Vue, _options) {
    Vue.component('ui-button', UIButton);
  };
  /**
   * @public
   */
  // eslint-disable-next-line @typescript-eslint/no-explicit-any

  var plugin = {
    install: install,
    version: '1.0.0'
  };
  var GlobalVue = null;

  if (typeof window !== 'undefined') {
    GlobalVue = window.Vue;
  } else if (typeof global !== 'undefined') {
    GlobalVue = global.Vue;
  }

  if (GlobalVue) {
    GlobalVue.use(plugin);
  }

  exports.UIButton = UIButton;
  exports.plugin = plugin;

  Object.defineProperty(exports, '__esModule', { value: true });

})));

When imported to a vue app created by cli always complain about h function. Any clues for Rollup+tsx??