Can someone assist me with an issue I'm facing in Vue where it's not detecting 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,
})
]
}