Cannot find module in VueJS Error using a JSON file - javascript

I was given a task in finding a way to dynamically import pages inside vuejs projects; built with a CLI tool the team I am in, is working on. I tried using functions as a string ("() => import(...)") and then eval that string and that did not work. Currenly I have used:
{
"routes": [
{
"name": "Login",
"path": "/login",
"component": "../../src/pages/auth/login/*"
},
{
"name": "Register",
"path": "/register",
"component": "#/pages/auth/register"
}
]
}
then I use a "driver" to then pass into our routes.ts file:
import * as dynamicRoutes from './routes.json';
const routes: any[] = [];
dynamicRoutes.default.routes.forEach((elem: any) => {
const component = async () => await require(elem.component).then((comp: any) => comp);
routes.push({
name: elem.name,
path: elem.path,
component,
});
});
export default routes;
Which im getting "cannnot find module" errors. Also when i log the output i get this when i check the component function: TypeError: 'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them
at Function.invokeGetter.
Is there a way that i can dynamically pass routes using this json file? (Because we are reading and writing to/from the json file to keep/update the routes)

You can use dynamic imports to lazy load Vue components, and it's even included by default when you install vue-router
const component = () => import(elem.component)

when you say dynamically import pages, I believe it is something related to lazy load the component dynamically ? If so, you need to have a HOC for all your route components which can basically use es6 import() (promise based) to fetch the component . You can also use webpack magic comments for the same(if you are using webpack).
hope it helps

Related

Vue 3: defineAsyncComponent not resolving .vue files and not splitting chunks

I'm trying to load Vue 3 components in a asynchronous way. I've found that there is a function called
defineAsyncComponent
which is supposed to be used as follows:
const GameUI = defineAsyncComponent(()=>import(filePath));
app.component("GameUI", GameUI);
filePath in this context is exactly: './components/GameUI/GameUI.element.vue'
Running the app like this leads to the following error:
Uncaught (in promise) Error: Cannot find module './components/GameUI/GameUI.element.vue'
at eval (eval at ./src lazy recursive)
But... if I change the filePath code to import the path as a string:
const GameUI = defineAsyncComponent(()=>import('./components/GameUI/GameUI.element.vue'));
The app works, it does find the component.
I don't want to use constant strings, because I have a lot of components and I want to load them asynchronously.
One of my main goals to achieve this, is to load the webapp by parts, component by component whenever they are needed, instead of loading them all on start.
I've also find that if I append a comment as follows:
const GameUI = defineAsyncComponent(()=>import(/* webpackChunkName: "GameUI" */ './components/GameUI/GameUI.element.vue'));
The JavaScript part for the GameUI component, should have it's own chunk.js file, but I always keep getting everything in a couple .js chunk files, which contradicts the async loading I want to achieve.
I'm using vue-cli-service, and my vue.config.js looks like this:
module.exports = {
productionSourceMap: false,
css: {
loaderOptions: {
sass: {
additionalData: process.env.NODE_ENV === 'production'
? '$baseURL:"/dist/";'
: '$baseURL:"/";'
}
}
},
publicPath: process.env.NODE_ENV === 'production'
? '/dist/'
: '/',
devServer: {
https: true,
port:"",
host:'website.com',
disableHostCheck: true,
cert: (fs.readFileSync('cert.pem')+""),
key: (fs.readFileSync('privkey.pem')+""),
ca: (fs.readFileSync('ca.pem')+""),
}
};
I've already tried multiple stuff I've found online, but they are not that much explanatory. I'm literally doing the same as some online articles I've found and cannot find the problem on my side.
The two main problems are:
Cannot load .vue files from variables, only full strings.
Cannot split the code into different .js files for every async loaded component.
The answer to the first question:
Because there have some limitations of async import
.
What you are doing cannot work because you are using a variable as value to defineAsyncComponent.
According to the limitations of async import, you cannot import your component usinig a variable. Instead what you can do is:
// If the component name to call is GameUI.element
const component = 'GameUI.element' // can be comed from anyting
const GameUI = defineAsyncComponent(()=>import(`./components/GameUI/${component}.vue`));
app.component("GameUI", GameUI);

Nuxtjs custom module

I'm quite new to Nuxtjs so I made a test project which purpose is merely the (of course) testing of Nuxtjs functionalities.
Currently I'm trying to create a simple custom module: afaik a module is basically a wrapper around a vou/js library/plugin, something like a high-level integration used to expose configurations on how the underlying library/plugin is imported and used in the Nuxt application.
So I'm trying with a simple module that declare some plain js classes that I'll use in my application, e.g. Order and Product, and that's what I came out with:
Directory structure
pages
the-page.vue
modules
classes
index.js
order.js
/modules/classes/index.js
const path = require('path')
export default function (moduleOptions) {
const { nuxt } = this
// add the debug plugin
this.addPlugin({
src: path.resolve(__dirname, 'order.js'),
})
}
/modules/classes/order.js
class Order {
constructor(id) {
this.id = id;
console.log('created order #' + this.id);
}
}
export {Order};
/nuxt.config.js
export default {
// ...
buildModules: [
// ...
'~/modules/classes'
],
// ...
}
/pages/the-page.vue
<script>
export default {
name: 'ThePage',
data () {
return {
}
},
methods: {
createOrder () {
const order = new Order(123)
}
}
}
</script>
The error
My defined class are still not imported in my pages:
/app/pages/the-page.vue
18:13 error 'order' is assigned a value but never used no-unused-vars
18:25 error 'Order' is not defined no-undef
Considerations
Probably I'm missing something about modules usage and/or implementation, but every tutorial I found starts with too complex scenarios, and since I'm at the beginning with Nuxtjs I need something easier to implement.
Ok, I found out that I was mistaken how NuxtJs modules are intended to work and was traying to do somenthing they are not intended for.
Nuxt modules cannot import js classes in every component of the application as I wanted to do, they just "add a property" to the main application instance that is made accessible through this.$<something>, like e.g. you can already do in simple Vue with the Vue Router or the Vuex store plugins that give access to the this.$router and this.$store properties.
NuxtJs modules just wrap simple plugins and expose configuration options to made.

How to fix 'Invariant Violation' caused by cyclic dependencies in react-redux

I have a React project set up like this:
It is a simple application. The Dashboard has a UserListContainer, containing a UserList, which lists four users with their ID and name. The UserList gets the Users from Data.ts
The application itself works just fine and displays the four users. But as soon as I try to test the UserList with enzymes shallow rendering, the tests give me the following error message:
Invariant Violation: You must pass a component to the function returned by connect. Instead received undefined
at invariant (node_modules/invariant/invariant.js:40:15)
at wrapWithConnect (node_modules/react-redux/lib/components/connectAdvanced.js:97:33)
at Object.<anonymous> (src/Users/UserListContainer.tsx:4:34)
at Object.<anonymous> (src/Users/index.ts:1:1)
at Object.<anonymous> (src/Dashboard/Dashboard.tsx:2:1)
at Object.<anonymous> (src/Dashboard/index.ts:1:1)
at Object.<anonymous> (src/Users/UserList.tsx:2:1)
at Object.<anonymous> (src/Users/__tests__/UserList.test.tsx:3:1)
The problem is basically that, even though we don't use the Dashboard when rendering the UserList shallowly, React still tries to build it. I guess that happens because we access Data through the Dashboard index, so React will also try to resolve Dashboard and its imports, namely UserListContainer, because they are exported through the same index file. When I import the users directly instead of through the index, the problem disappears.
We fixed this issue by breaking the cyclic dependency but if I encounter the error again, I want to know other ways to fix it. I would also like to understand why the web application still seems to be working just fine, while the tests fail.
Also, is there a way to prevent React from resolving the imports and exports when using enzymes shallow rendering?
Users/__tests__/UserList.test.tsx
test("reproduce the problem", () => {
const wrapper = shallow(<UserList />)
console.log(wrapper)
expect(1).toBe(1)
})
Users/UserList.tsx
import { Data } from "../Dashboard"
export const UserList: React.FC = () => (
<React.Fragment>
{Data.users.map(user => (
<div>
<code>{user.id} - </code>
<code>{user.name}</code>
</div>
))}
</React.Fragment>
)
Dashboard/index.ts
export { Dashboard } from "./Dashboard" // not used but still resolved
export { Data } from "./Data" // actually used
Dashboard/Data.ts
export const Data = {
users: [
{ id: "user1", name: "Albert" },
{ id: "user2", name: "Bertha" },
{ id: "user3", name: "Chloe" },
{ id: "user4", name: "Doug" }
]
}
Dashboard/Dashboard.tsx
import { UserListContainer } from "../Users"
export const Dashboard: React.FC = () => {
return <UserListContainer />
}
Users/UserListContainer.tsx
import { UserList } from "./UserList"
export const UserListContainer = connect()(UserList)
One way of fixing it would be to reorder the imports in your dashboard file:
export { Data } from "./Data" // actually used
export { Dashboard } from "./Dashboard" // not used but still resolved
Web application will work in most cases because it will start resolving from your index.tsx file (or whatever your entry file name is) and go from there. Jest on the other hand starts from your test file and only resolves those imports (you can find a nice explanation why this happens here: https://railsware.com/blog/how-to-analyze-circular-dependencies-in-es6/).
We had similar problems in our projects and unfortunatelly except reordering imports and better structuring your files there is no other solution.
One "hack" that you can also do is to add:
import 'problematic_module'
to your jest setupFilesAfterEnv. That way module will be resolved before each test (but i recommend this only as a last resort).

How to force Nextjs to create a chunk for particular module?

I'm trying to use dynamic import for importing a different modules depending on the props passed to the component. Paths to the components are kept in an object
const components = {
'apple': './Apple.js',
'orange': './Orange.js',
// ...
}
Then
// ...
import(components[this.props.type])
// ...
Unfortunately, I get the following error:
Unhandled Rejection (Error): Cannot find module './Apple.js'
It disappears when I explicitly specify the filename (import('./Apple.js')). Is it possible to force nextjs to serve these dynamic modules?
You need to use the dynamic utility from next/dynamic for dynamic import in Next.js. This function is inspired by react-loadable library and uses similar syntax.
The simplest way to dynamically import React component in Next.js is:
const DynamicComponent1 = dynamic(import('../components/hello1'))
If you want to import module that is not a React component than you need to use customizing rendering feature:
const DynamicDeliveryOptions = dynamic({
loader: () => import('../components/delivery-options'),
render(props, loaded) {
const options = loaded.deliveryOptions.map(
option => <option key={option.id}>{option.title}</option>
);
return <select>{options}</select>
},
});
Notice that signature of the render function in Next.js is different from
its signature in react-loadable library (props is the first argument in Next.js).
Returning to your example: I think the best way to import multiple modules will be to declare them all as dynamic modules and conditionally render depending on the passed props (modules won't be loaded before render).
You can tell webpack to put a dynamically named module inside a particular chunk using this syntax:
const module = await import(
/* webpackChunkName: "your_custom_chunk_name" */
"path/to/your/module"
);
Webpack will extract your module's code inside the file: /.next/server/chunks/your_custom_chunk_name.js.
(Tested on NextJs version 12.1.6)

Importing single file components VueJS

everyone.
I have a trivial doubt on making vue components.
I don't want to use browserify or webpack , cause I am working in django and it has most of it's templates in static files , although I read this , which does describe how to take in account both ( but that's for some other day ).
Problem :
I am making a single file component which I have to import and use, using my router but I can't, as the import just doesn't happen.
My Hello.vue
<template>
Some HTML code here.
</template>
<script>
module.exports = {
data() {
return {
coin : []
}
},
beforeRouteEnter (to, from, next) {
axios.get('my-django-rest-api-url')
.then(response => {
next(vm => {
vm.data = response.data
})
})
}
}
</script>
I have it in the index.html file itself , no other .js file,
<script>
import Hello from '#/components/Hello.vue'
Vue.use(VueRouter);
const dashboard = {template:'<p>This is the base template</p>'};
const profile = {
template: '#profile_template',
data () {
return {
profile_details: []
}
},
beforeRouteEnter (to, from, next) {
axios.get('my-api-url')
.then(response => {
next(vm => {
vm.profile_details = response.data
})
})
}
}
const router = new VueRouter({
routes: [
{ path: '/', component: dashboard },
{ path: '/profile', component: profile },
{ path: '/hello', component: Hello }
]
});
new Vue({
router : router,
}).$mount('#app');
</script>
What all I've tried :
1.<script src="../components/Hello.js" type="module"></script> and removing the import statement as suggested here
Replacing my Hello.js's code with this : export const Hello = { ...
Making a Hello.js file and importing it like this import Hello from '../components/Hello.js';
Error :
**Mozilla ( Quantum 57.0.4 64 bit ) ** : SyntaxError: import declarations may only appear at top level of a module
**Chrome ( 63.0.3239.108 (Official Build) (64-bit) ) ** :Uncaught SyntaxError: Unexpected identifier
P.S. : I have tried these in various combinations
Not a Vue.js guru, but here are a few perspectives that might help you.
Module loading is still not supported on modern browsers by default, and you'd need to set special flags in order to enable it (which the users of your app probably won't do).
If you insist on using import and export, you'd need Webpack. And most certainly Babel (or any other ES6 transpiler, e.g. Buble) as well.
If you prefer module.exports, then you'd need Browserify. It enables support for CommonJS in browser environments.
If neither is doable, then your best bet is defining Vue components in global scope. You can split them across separate files, and import each with a <script> individually. Definitely not the cleanest approach.
Single file components typically go inside of .vue files, but either way they require vue-loader which can be added and configured (again) with a bundler.
Last option is to just use an existing setup in place, if there is any (is there?). If you already have RequireJS, UMD, or something similar in place, adjust your components to fit that. Otherwise, use <script>s.
You are trying to do something which is not possible. Vue Single file components are not supported as raw component file by web browsers. The single file component is supposed to be compiled.
Please see this for more:
https://v2.vuejs.org/v2/guide/single-file-components.html
In Webpack, each file can be transformed by a “loader” before being included in the bundle, and Vue offers the vue-loader plugin to translate single-file (.vue) components.
A vue single file component is first "translated" (compiled) to pure javascript code which is use-able by browsers.

Categories