0
votes

I'm using Laravel Mix to compile my Vue components and for it, I used TypeScript and class-based components. Every single class is exported from the component, and every single component is required by the context in the main application script but during rendering Vue throwing error.

Uncaught TypeError: Cannot set property 'render' of undefined
    at normalizeComponent (componentNormalizer.js:24)

I searched all of the Internet but people speak only about invalid export class. I'm sure about valid exporting class in components. I don't know what I'm doing wrong.

When I back to object-based components in plain JavaScript everything goes perfect, so maybe TypeScript config is wrong or something. I give up completely :(

app.ts

import Vue from 'vue';
import _ from "lodash"

export default class App {

    protected registerComponents(): void {
        const components = require.context('./', true, /\.vue$/i);

        components.keys().forEach((componentPath) => {
            // @ts-ignore
            const componentName = componentPath
                .split('/').pop() // full component name
                .split('.').slice(0, 1).shift(); // component name without extension

            Vue.component(
                _.kebabCase(componentName),
                components(componentPath)
            );
        })
    }

    protected boot(): void {
        this.registerComponents();
    }

    public init(): Vue {
        this.boot();

        return new Vue({
            el: '#main',
        });
    }
}

EventSignUpForm.vue

<template>
    <div>
        <p>Long-form v-model example</p>
    </div>
</template>

<script lang="ts">
    import Vue from 'vue'
    import {Component} from 'vue-property-decorator';

    @Component({
        name: 'EventSignUpForm',
    })
    export class EventSignUpForm extends Vue {

        protected count = 0

        public increment() {
            this.count++
        }

        public decrement() {
            this.count--
        }
    }

    export default EventSignUpForm;
</script>

tsconfig.json

{
    "compilerOptions": {
        "target": "es5",
        "module": "es2015",
        "moduleResolution": "node",
        "strict": true,
        "jsx": "preserve",
        "importHelpers": true,
        "experimentalDecorators": true,
        "emitDecoratorMetadata": true,
        "esModuleInterop": true,
        "allowSyntheticDefaultImports": true,
        "sourceMap": true,
        "baseUrl": ".",
        "types": [
            "node",
            "webpack-env"
        ],
        "paths": {
            "@/*": ["./resources/js/*"]
        },
        "lib": [
            "esnext",
            "dom",
            "dom.iterable",
            "scripthost"
        ]
    },
    "include": [
        "resources/js/**/*.ts",
        "resources/js/**/*.tsx",
        "resources/js/**/*.vue"
    ],
    "exclude": [
        "node_modules"
    ]
}

webpack.mix.js

class WebpackMix {
    constructor() {
        this.mix = require('laravel-mix');
    }

    configureWebpack(){
        this.mix.webpackConfig({
            module: {
                rules: [
                    {
                        test: /\.tsx?$/,
                        loader: "ts-loader",
                        exclude: /node_modules/,
                    }
                ]
            },
            resolve: {
                extensions: ["*", ".js", ".jsx", ".vue", ".ts", ".tsx"],
                alias: {
                    '@': path.resolve(__dirname, 'resources', 'js'),
                },
            }
        });
    }
    // others things  
}
2

2 Answers

1
votes

EventSignUpForm.vue: Make component export as export default:

export class EventSignUpForm extends Vue 

change to be

export default class EventSignUpForm extends Vue

and remove from bottom

export default EventSignUpForm;
0
votes

My workmate helps me solve this quite a difficult case.

What we do is add in webpack.mix.js to ts-loader options object. We need append TS suffix to Vue components. Now it is:

rules: [
  {
    test: /\.tsx?$/,
    loader: 'ts-loader',
    exclude: /node_modules/,
    options: {
      appendTsSuffixTo: [/\.vue$/]
    }
  }
]

What we do next is change tsconfig.js compilerOptions.module to be commonjs instead of es2015, like this:

{
    "compilerOptions": {
        "target": "es5",
        "module": "commonjs",
        // the rest remain unchanged
    }
}

As last thing all of imports/require Vue components must be the default import, I used just import in require.context but it must be changed to such:

protected registerComponents(): void {
  const components = require.context('./', true, /\.vue$/i);

  components.keys().forEach((componentPath) => {
    // @ts-ignore
    const componentName = componentPath
      .split('/').pop() // full component name
      .split('.').slice(0, 1).shift(); // component name without extension

    Vue.component(
      _.kebabCase(componentName),
      components(componentPath).default
    );
  })
}

This solves my problem, thanks to Adrian for your time and give working solution :)