2

Please help. I can't seem to get Vue to detect my Single File Components.

Error message:

ERROR in ./src/App.vue (./node_modules/ts-loader!./node_modules/vue-loader/lib/selector.js?type=script&index=0!./src/App.vue)
Module build failed: Error: Could not find file: 'F:\Projects\core\client\src\App.vue'.
    at getValidSourceFile (F:\Projects\core\client\node_modules\typescript\lib\typescript.js:107554:23)
    at Object.getEmitOutput (F:\Projects\core\client\node_modules\typescript\lib\typescript.js:108052:30)
    at Object.getEmitOutput (F:\Projects\core\client\node_modules\ts-loader\dist\instances.js:187:41)
    at getEmit (F:\Projects\core\client\node_modules\ts-loader\dist\index.js:196:37)
    at successLoader (F:\Projects\core\client\node_modules\ts-loader\dist\index.js:34:11)
    at Object.loader (F:\Projects\core\client\node_modules\ts-loader\dist\index.js:21:12)
 @ ./src/App.vue 7:0-97 7:0-97 8:0-110 21:2-16
 @ ./src/index.ts
 @ multi (webpack)-dev-server/client?http://localhost:9000 webpack/hot/dev-server ./src/index.ts

I have also added the vue-shims.d.ts file as suggested by other posts.

declare module "*.vue" {
    import Vue from "vue"
    export default Vue
}

tsconfig.json

{
  "compilerOptions": {
    // this aligns with Vue's browser support
    "target": "es5",
    // this enables stricter inference for data properties on `this`
    "strict": true,
    // if using webpack 2+ or rollup, to leverage tree shaking:
    "module": "es2015",
    "moduleResolution": "node",
    "lib": [ "es2015", "es2017", "es6", "dom", "es2015.iterable" ],
    "noImplicitAny": true,
    "strictNullChecks": true,
    "allowSyntheticDefaultImports": true
  }
}

If I change the imports to ./App.vue.d then it works, but obviously importing a typings file doesn't give you the actual contents of the file you need.

App.vue

<template>
    <div class="height-100 width-100">
        <div class="app-container height-100 width-100">
            <router-view></router-view>
        </div>
        <offline-notice></offline-notice>
    </div>
</template>

<script lang="ts">
    import Vue from "vue"

    export default Vue.extend({
        components: {
            "offline-notice": () => import("../src/components/common/OfflineNotice.vue")
        }
    })
</script>

<style lang="sass">
    @import "assets/stylesheets/variables"
    @import "assets/stylesheets/base"
    @import "assets/stylesheets/helpers"
    @import "../node_modules/izitoast/dist/css/iziToast.min.css"
    @import "assets/stylesheets/components/notifications"
</style>

index.ts

import Vue from "vue"
import VueRouter from "vue-router"
import "element-ui/lib/theme-chalk/index.css"
import "./assets/stylesheets/vendor/ionicons.min.scss"
import "babel-polyfill"

import { store } from "./store"
import { routes } from "./routes/routes-setup"
import { setupSelectedElementUIComponents } from "../config/element-ui-helper"

import App from "./App.vue"

Vue.use(VueRouter)

setupSelectedElementUIComponents(Vue)

const router = new VueRouter({
    routes,
    scrollBehavior(to, from, savedPosition) {
        if (savedPosition) {
            return savedPosition
        }
        if (to.hash) {
            return {selector: to.hash}
        }
    }
})

// noinspection TsLint
new Vue({
    el: "#app",
    router,
    store,
    render: h => h(App),
})

My base webpack config:

const ExtractTextPlugin = require("extract-text-webpack-plugin")
const ForkTsCheckerWebpackPlugin = require("fork-ts-checker-webpack-plugin")

module.exports = {
    entry: {
        main: "./src/index.ts",
        vendor: [
            "string-format",
            "element-ui",
            "izitoast",
            "vue-swal",
            "vue",
            "axios",
            "croppie",
            "vue-router",
            "vuex",
            "vuex-persistedstate",
        ]
    },
    module: {
        rules: [
            {
                test: /\.ts$/,
                exclude: /node_modules|vue\/src/,
                loader: "ts-loader",
                options: {
                    appendTsSuffixTo: [/\.vue$/]
                }
            },
            {
                test: /\.vue$/,
                loader: "vue-loader",
                options: {
                    esModule: true
                }
            },
            {
                test: /\.tsx?$/,
                loader: 'ts-loader',
                exclude: /node_modules/,
                options: {
                    appendTsSuffixTo: [/\.vue$/],
                }
            },
            {
                test: /\.js$/,
                loader: "babel-loader",
                exclude: /node_modules/
            },
            {
                test: /\.(png|jpg|gif|svg)$/,
                loader: "file",
                options: {
                    name: "[name].[ext]?[hash]"
                }
            },
            {
                test: /\.css$/,
                use: ["style-loader", "css-loader"]
            },
            {
                test: /\.(scss|sass)$/,
                use: ExtractTextPlugin.extract({
                    fallback: "style-loader",
                    use: ["css-loader", "sass-loader"]
                })
            },
            {
                // Match woff2 in addition to patterns like .woff?v=1.1.1.
                test: /\.(woff|woff2)(\?v=\d+\.\d+\.\d+)?$/,
                loader: "url-loader",
                options: {
                    // Limit at 50k. Above that it emits separate files
                    limit: 50000,

                    // url-loader sets mimetype if it"s passed.
                    // Without this it derives it from the file extension
                    mimetype: "application/font-woff",

                    // Output below fonts directory
                    name: "./fonts/[name].[ext]"
                }
            },
            {
                test: /\.(ttf|eot|woff|woff2)$/,
                loader: "file-loader",
                options: {
                    name: "fonts/[name].[ext]"
                }
            },
        ]
    },
    resolve: {
        alias: {
            'vue$': 'vue/dist/vue.esm.js'
        },
        extensions: [".js", ".vue", ".json", ".scss", ".sass", ".ts"]
    },
    optimization: {
        splitChunks: {
            chunks: "async",
            minChunks: 1,
            maxAsyncRequests: 5,
            maxInitialRequests: 3,
            name: true,
            cacheGroups: {
                default: {
                    minChunks: 2,
                    priority: -20,
                    reuseExistingChunk: true
                },
                vendors: {
                    test: /[\\/]node_modules[\\/]/,
                    priority: -10
                }
            }
        }
    },
    plugins: [
        new ExtractTextPlugin({
            filename: "[name].[hash].css"
        }),
        new ForkTsCheckerWebpackPlugin({
            vue: true,
        })
    ]
}

1 Answer 1

2

This is what ended up working for me.

Upgrade from "vue-loader": "^14.2.2", to "vue-loader": "^15.2.4", and adding the VueLoaderPlugin plugin to my webpack configuration (Webpack 4).

webpack.common.js

const {VueLoaderPlugin} = require('vue-loader')
...
plugins: [
    new ExtractTextPlugin({
        filename: "[name].[hash].css"
    }),
    // new ForkTsCheckerWebpackPlugin({
    //     vue: true,
    // }),
    new VueLoaderPlugin()
]
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.