I'm building a node.js application and I'm trying to put all my mongodb logic in a seperate file. At this time this file only has one function to initialize the mongodb connection. I want to export all the functions from this file using module.exports.
My mongo file looks as follows:
import { connect } from "mongoose";
const run = async (db: string): Promise<void> => {
await connect(db, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
};
module.exports = {
run
};
I want to use this run function in index.ts and im trying to import it as an ES6 module but I can't get it working with the above code.
How I'm importing:
index.ts:
import * as mongo from "./mongo";
Trying to call my run method:
mongo.run('dburl');
This throws following error: 'property run does not exist'
Now I have found a solution to this problem by adding an extra export before my run declaration:
export const run = async (db: string): Promise<void> => {...}
I don't understand why I have to do this as I'm already exporting this function inside module.exports, am I importing it wrong in my index file or is there a better way of doing this?
MDN: JavaScript Modules
There are different types of modules in JS.
ES6 Modules use the export keyword. You can import all the exports like this import * as ... from "..." or import individual exports via import { ... } from "..."
CommonJS modules use modules.exports which is equivalent to export default in ES6 modules. These you can import like this import ... from "...".
So when you use modules.exports you would either have to change your import to:
const mongo = require('./mongo');
Or you could change your export to:
export { run }
Related
I'm attempting to create an apollo client plugin for a Nuxt 3 application. It's currently throwing an error regarding a package called ts-invariant:
file:///Users/[my name]/Repositories/[project]/node_modules/#apollo/client/utilities/globals/fix-graphql.js:1
import { remove } from "ts-invariant/process/index.js";
^^^^^^
SyntaxError: Named export 'remove' not found. The requested module 'ts-invariant/process/index.js' is a CommonJS module, which may not support all module.exports as named exports.
CommonJS modules can always be imported via the default export, for example using:
import pkg from 'ts-invariant/process/index.js';
const { remove } = pkg;
at ModuleJob._instantiate (node:internal/modules/esm/module_job:124:21)
at async ModuleJob.run (node:internal/modules/esm/module_job:181:5)
at async Promise.all (index 0)
at async ESMLoader.import (node:internal/modules/esm/loader:281:24)
at async __instantiateModule__ (file:///Users/[my name]/Repositories/[project]/.nuxt/dist/server/server.mjs:4550:3)
[vite dev] Error loading external "/Users/[my name]/Repositories/[project]/node_modules/#apollo/client/core/index.js".
at file://./.nuxt/dist/server/server.mjs:3170:289
at async __instantiateModule__ (file://./.nuxt/dist/server/server.mjs:4550:3)
I feel like I know enough about this error to know it has something to do with how Nuxt 3 deals with ESM, but I can't be for certain.
Here's the nuxt plugin:
plugins/apollo-client.js
import { defineNuxtPlugin } from "#app"
import { ApolloClient, InMemoryCache } from "#apollo/client/core"
import { DefaultApolloClient } from "#vue/apollo-composable"
export default defineNuxtPlugin((nuxtApp) => {
const config = useRuntimeConfig()
const apolloClient = new ApolloClient({
uri: config.PUBLIC_API_ENDPOINT,
cache: new InMemoryCache(),
})
nuxtApp.vueApp.provide(DefaultApolloClient, apolloClient)
})
In a normal scenario, I might use the nuxt-apollo community module, but it is currently afk regarding a nuxt 3 port, so a plugin it is.
Here's some documentation I relied on for my plugin:
https://v4.apollo.vuejs.org/guide-composable/setup.html#vue-3
https://v3.nuxtjs.org/docs/directory-structure/plugins
Solved by including #apollo/client and ts-invariant/process into the nuxt build transpile like so:
// nuxt.config.js
// ...
build: {
postcss: {
postcssOptions: require('./postcss.config.js')
},
transpile: [
'#apollo/client',
'ts-invariant/process',
],
},
// ...
I think I've pinpointed the underlying issue. Apollo Client (3.5.10 at the time of writing early 2022) is using "module":"index.js" to declare the path of the ESM exports.
However it seems that Webpack 5 based bundlers do not support this. Using exports in the package.json fixes it for good for me.
You should upvote this feature request.
And here is my palliative until then, using a small script to alter the package.json.
I have a large third party library that I need to share between two projects. The project has multiple folders with multiple files that contain multiple exports. Instead of importing these modules like this
import {BaseContainer} from '#company/customproject/src/containers/BaseContainer.js'
I would like to do this
import { BaseContainer } from '#company/customproject'
I know I can manually import all the modules into a single index.js file in the base directory but i am wondering if there is an easier way to do not have import them all explicitly
I know I can manually import all the modules into a single index.js file in the base directory but i am wondering if there is an easier way to do not have import them all explicitly
You should really just create an index.js file and import into that whatever you want to export so that you can control what APIs get exported and to not export private APIs.
That said there is an automated tool that generates an index.js automatically for you:
> npm install -g create-index
> create-index ./src
Which will generate an index.js with all the exports.
As the other answer suggests, you should create an index.js within each directory and explicitly export contents
#company/customproject/index.js
import {BaseContainer, SomeOtherContainer} from './src/containers'
export {
BaseContainer,
SomeOtherContainer
}
#company/customproject/src/containers/index.js
import BaseContainer from './BaseContainer'
import SomeOtherContainer from './SomeOtherContainer'
export {
BaseContainer,
SomeOtherContainer
}
Another option to autoload an entire directory is using require and module.exports to export every scanned file, however. You would likely run into conflicts using both ES6 import/export along with module.exports and default export statements.
#company/customproject/index.js
const fs = require('fs')
const modules = {}
fs.readdirSync(__dirname+'/src/containers').forEach(file => {
file = file.replace('.js', '')
modules[file] = require('./src/containers/'+file)
// map default export statement
if (modules[file].default) {
modules[file] = modules[file].default
}
})
module.exports = modules
Then simply use it in any ES5 or ES6 module
const {BaseContainer} = require('#company/customproject')
or
import {BaseContainer} from '#company/customproject'
I've been struggling to destruct my mongo object in another file, my object structure is like below.
const env = {
project: 'CRIBBBLE BACKEND',
url: 'localhost',
api: {
url: 'https://api.dribbble.com/v1/',
},
port: parseInt(process.env.PORT, 10) || 3000,
mongo: database,
};
export default env;
But when I'm trying to import the mongo object in another js file like this { mongo } from 'config' the returned value is undefined.
But if I change the export default value to module.exports it works as its expected.
So, I'm just wondering what is the difference between module.exports and export default?
While you export using export default foo the whole exported foo is available using {default: foo} after import. That's why you cannot access properties you want. Try to import as: import * as bar from './foo' and explore the bar using console.log(bar) to see what's happening underneath. Also for more info have a look: es6 module exports on 2ality.com
module.exports is a NodeJS (CommonJS) style of importing and exporting from different files. import/export is ES6 feature of doing the same thing.
If you export defualt you need to import it like import env from 'location'(Look I emit the {} part) and then access the mongo via env.mongo. You can't just get the mongo object directly.
I'm following ES2015. I want to translate regular javascript import statements to ES2015 import statement(s).
What I have:
I have javascript import line as below:
var db = require('../config').get('db')
What I've tried:
import { config } from '../config'
const db = config.db
NOTE
config folder has the index.js which I want to load. In the regular var ... = require('...') statement automatically loads index.js if exists. And I want the ES2015 script also automatically loads when imported.
I think what you're looking for is:
import { db } from '../config'
Assuming db is properly export-ed from config.js, that should work.
Just to clarify, there's three main kinds of imports in JS native modules:
Import the whole module:
import * as foo from 'path/to/foo';
const something = foo.something;
Import specific named exports of the module. This works if the module exports the appropriate objects using export statements:
import { something } from 'path/to/foo';
Import the default export of the module. This only works if the module has an export default statement in it:
import thedefaultexport from 'path/to/foo';
It looks like the '../config' module exports a single object with a get() method. If this is the case, import the whole module, like so:
import * as config from '../config';
And get the database like so:
const db = config.get('db');
If possible, you might want to refactor your '../config' module so that it exports db directly.
export {db};
And then you can use the syntax #AsadSaeeduddin suggested:
import {dp} from '../config';
To share data between modules, a usual pattern is to capsulate the data into a common module and import it in other modules.
In my case the data to be shared is a logger, which need to be initialized before used. I call init() at the entry point of my application.
// main.js
let logger = require('#my/logger');
logger.init({...});
let xxx = require('./moduleX');
let yyy = require('./moduleY');
In other modules, the initialized logger can be used:
// moduleX.js
let logger = require('#my/logger');
const log = logger('moduleX');
function x() {
log.debug('some msg');
}
Above code works well in node.js. But if I change to ES6 module syntax, it doesn't work because ES6 module import is hoisted.
// main.js
import {logger} from '#my/logger';
logger.init({...}); // this line is run after import moduleX
import {x} from './moduleX';
// moduleX.js
import {logger} from '#my/logger';
const log = logger('moduleX'); // logger is not initialized !
export function x() {
log.debug('some msg');
}
With ES6 module, how can I initialize some data and share them to other modules?
There was a similar question but the answer doesn't fit my case.
Update:
Some answers suggest to put the code which access shared data into function so that the code isn't invoked immediately at module load. But what if I really need to access it during module loading? I updated my code to demonstrate the use case -- it would be too trivial to call logger(name) in every function if not make log as module scope const.
Finally I solve it in the way that #PaoloMoretti mentioned in his comment.
Write a module in my app to init the logger for my app:
// logger_init.js
import {logger} from '#my/logger';
logger.init({...});
Import the initialization module once at the entry point of application, prior to imports of any other modules that use logger as well. It guarantees that the initialization is done before loading other modules.
// main.js
import './logger_init';
import {x} from '#my/other_module';
import {y} from './module_in_myapp';
Other modules can use initialized logger directly:
// #my/other_module
import {logger} from '#my/logger';
const log = logger('moduleX'); // logger has been initialized
export function x() {
log.debug('some msg');
}
The dependency tree is:
<init>
myapp --+--> logger_init ------------> #my/logger
| <use> ↑
+--> module_in_myapp -----------+
| <use> |
+--> #my/other_module ----------+
Why I don't adopt the way that add a wrapper module which init and return a logger (as Bergi's answer) is because the modules uses logger could be reusable modules not in my application.
Try to provide some entry points to your xxx.js and yyy.js modules or even make them as functions.
// main.js
import {logger} from '#my/logger';
import * as xxx from './xxx';
logger.init({...});
xxx.run();
// xxx.js
import {logger} from '#my/logger';
export function run () {
logger.debug('In xxx');
}
You could have each module that has a dependency on some initialisation routine return a function that can be executed manually to invoke its functionality, rather than expose that functionality as an immediate side-effect of importing it. For example, in xxx.js:
// xxx.js
export default function (log) {
log('xxx');
}
Then place all initialisation operations within an entry file and invoke the modules defined in the above way after these operations are complete:
// main.js
import xxx from './xxx';
import {logger} from '#my/logger';
logger.init({...});
xxx(logger);
But what if I really need to access it during module loading?
Please see the amended code examples above. Pass the instance of logger to the function exported by each module.
A logger, which need to be initialized before used. What if I need to access it during module loading?
Make an extra module with an initialised logger:
// main.js
import {x as xxx} from './moduleX';
import {y as yyy} from './moduleY';
// logger_ready.js
import {logger} from '#my/logger';
logger.init({…});
export default logger;
// moduleX.js
import logger from 'logger_ready';
const log = logger('moduleX'); // logger is initialized here!
export function x() {
log.debug('some msg');
}
After experiments, found that the BroadcastChannel api is perfect to share data or events easily between different es6 modules, tabs, workers, frames...
We can pass objects or arrays by using json stringify there too:
To send datas:
new BroadcastChannel('myapp_section8').postMessage('Hello world!')
Example receiving, from another module:
new BroadcastChannel('myapp_section8').addEventListener('message', function(e){
console.log(e.data)
})
It avoid to use any DOM elements to dispatchEvent, it simplify stuffs a lot!