What is the equivalent of module.exports for ES6 Modules? - javascript

I have an existing package written in JavaScript that I'm trying to convert to TypeScript. My question is, what is the equivalent of module.exports for ES6 Modules?
For example if I have the following code:
module.exports = function () {
console.log("Hello World");
};
The only way I have found to do this in ES Modules is by doing something like the following.
const myFunc = function () {
console.log("Hello World");
};
export default myFunc;
// OR
export { myFunc };
Of course, this is not the same thing as module.exports = //....
Which leads to this problem. When I run this through TypeScript and get the outputted code, users of the package will either need to use an import defaultExport from "module-name"; statement (which isn't bad), or instead of being able to access it using require("module-name") they will have to use require("module-name").default (if they are using standard JS, not TypeScript). Which obviously is a breaking API change for standard JavaScript users.
Obviously, the goal here is to have no breaking API changes (and require no changes for customers code).
One more requirement to any solution. In other places in the package I need to be able to export a lot of different files as one object. So in the past I've been doing:
module.exports = {
"thing1": require("./a.js"),
"thing2": require("./b.js")
};
So I need to be able to export this as normal without requiring the user to do thing1.default. This one so far seems like the easier requirement since I'm exporting an object which ES Modules seems to handle really well. It's when you are exporting a singular (non-named) expression that it gets really tricky.
Is there a way to do this without having to make this a breaking change to the API?

The solution here is to use the TypeScript export = command. This allows you to use either require or import with the module.
export = function () {
console.log("Hello World");
};
import myModule from "./lib/myModule";
// OR
const myModule = require("./lib/myModule");
For more information the TypeScript documentation has a guide about Migrating from JavaScript, and specifically a section called Exporting from Modules.

Typescript recommends not to rewrite your code, but instead to configure it to allow js modules.
When we were working on converting a very large app from js to TS, we took the position of writing new code in TS, and slowly converting js into ts (but not spend a whole lot of time just converting).
We needed to enable few options in the tsconfig.json, such as :
{
...,
"compilerOptions" : {
...
"moduleResolution": "node",
"allowJs": true,
...
},
"include" [
"path/to/my/js",
]
}
you also need to update your webpack.config.js:
module.exports = {
// ... my settings
resolve: {
// Add '.ts' and '.tsx' as resolvable extensions.
extensions: ["", ".webpack.js", ".web.js", ".ts", ".tsx", ".js"]
},
}
You might also have to declare require as a function (if you are getting errors such as require is undefined):
declare function require(path: string): any;
more info is available here :
https://www.typescriptlang.org/docs/handbook/migrating-from-javascript.html

Most accurate replacement for module.exports in typescript is export default
export default {
"thing1": require("./a.js"),
"thing2": require("./b.js")
}
then you can use them like below
import myThings from '.....'

Related

Cannot use newly installed plugins (node modules) in Nuxt pages/components

First off, I'm a beginner with NuxtJS and front-end development in general, so it might be that I'm missing something - though I do believe I went through all the options before posting here. Apologies in advance if that is not the case.
I've been having trouble using installed modules that I've registered as plugins. For example, take mapbox-sdk.
After installing it with npm install #mapbox/mapbox-sdk, which correctly creates #mapbox/mapbox-sdk in node_modules, I register it in nuxt.config.js:
plugins: [
...
"~/plugins/mapbox-sdk.js",
],
Of course, I also create the mapbox-sdk.js file in plugins/, containing:
import "#mapbox/mapbox-sdk";
Then, in a page (say, myMap.vue), when I try:
var mapboxClient = mapboxSdk({ accessToken: MY_ACCESS_TOKEN });
which is the basic usage example in the documentation, I get:
mapboxSdk is not defined
in the console. This behavior extends to every single module I installed today, but is not the case for modules I had previously installed.
The reason why you're getting the error mapboxSdk is not defined is because there are a few issues with the way you've set up this plugin.
Docs here https://nuxtjs.org/docs/2.x/directory-structure/plugins/, they have some useful diagrams.
There are a couple of ways you can use this package.
Plugin
// ~/plugins/mapbox-sdk.js
import mapboxSdk from '#mapbox/mapbox-sdk'
export default (_ctx, inject) => {
// Exposing the mapboxSdk to your Nuxt app as $mapBox.
inject('mapBox', mapboxSdk)
}
Then in nuxt.config.js, same as you've already done.
plugins: [
...
"~/plugins/mapbox-sdk.js",
],
Then in your component myMap.vue
var mapboxClient = this.$mapBox({ accessToken: MY_ACCESS_TOKEN });
Directly in the component:
If you don't wish to use a plugin, the way that #kissu mentioned above https://stackoverflow.com/a/67421094/12205549 will also work.
Try adding this after the import to let Vue know that this method exists (in the same .vue file) at first
<script>
import mapboxSdk from '#mapbox/mapbox-sdk'
export default {
methods: {
mapboxSdk,
},
mounted() {
console.log('mapbox function >>', mapboxSdk)
},
}
</script>
Do you have it working in a .vue component at first ?

Import JS web assembly into TypeScript

I'm trying to use wasm-clingo in my TypeScript React project. I tried to write my own d.ts file for the project:
// wasm-clingo.d.ts
declare module 'wasm-clingo' {
export const Module: any;
}
and import like this:
import { Module } from 'wasm-clingo';
but when I console.log(Module) it says undefined. What did I do wrong?
Notes:
clingo.js is the main js file.
index.html and index_amd.html are two example pages
Solution:
I solved the problem like this:
// wasm-clingo.d.ts
declare module 'wasm-clingo' {
const Clingo: (Module: any) => Promise<any>;
namespace Clingo {}
export = Clingo;
}
and
import * as Clingo from 'wasm-clingo';
Here's the source for this solution
I know you found a solution acceptable to you; however, you don't really have any types here, you just have Module declared as any, which gives you no typescript benefits at all. In a similar situation I used #types/emscripten, which provides full type definitions for web assembly modules compiled using emscripten. You simply need to do:
npm install --save-dev #types/emscripten
then change your tsconfig.json types array to add an entry for emscripten.
After that you can just write Module.ccall(...) etc. If you like you could of course write const Clingo = Module and then make calls against that if you want a more descriptive name than Module (which is a terrible name!).
You're welcome ;)
I think the issue is that wasm-clingo exports the module itself but import { Module } from 'wasm-clingo' expects a property.
Try
import Clingo_ from 'wasm-clingo';
const Clingo: typeof Clingo_ = (Clingo_ as any).default || Clingo_;

How to expose Webpack-built library as function?

I'm working on a JavaScript library which is essentially a Vue.js component bundled together with Vue itself. The project was started via the vue-cli and is packaged with Webpack.
When this library gets imported via a <script> tag, I'd like it to expose a single function like:
Mapboard({div: '#some-div')
The library itself is currently just:
export default (opts) => console.log('hello')
I tweaked my Webpack config to set a library name:
module.exports = {
output: {
library: 'Mapboard'
}
}
which successfully exposes a global called Mapboard, but it's object, not a function:
{
__esModule: true,
default: function (e) {...}
}
Is there any way to prevent Webpack from exporting this as an ES6 module? I'd like it to just be the function exported by the module, as shown above.
Take a look to this webpack 2 official article: https://webpack.js.org/guides/author-libraries/
That is for ES5 and ES6, if you are building your library using TypeScript, you probably need extra configuration for typings...
http://siawyoung.com/coding/javascript/exporting-es6-modules-as-single-scripts-with-webpack.html
This article helps a lot.The core point to solve this problem is:
The fix for this is simple: simply write a file that imports those ES6 modules the ES6 way, but exports them in nodeJS module.exports style. Webpack will then handle them correctly!
That's right!Just change your function into :
function foo(opts) {
console.log('hello');
}
module.exports = foo;
And everything should work as expected.
NOTE:Code write like this may not work:
module.exports = function foo(opts) {
console.log('hello');
}

How can I use a config file in React?

Let's say I have 5 jsx files and each file uses some config parameter.
My index.js file imports all of these 5 jsx files.
Instead of having my config data spread accross 5 files, is there a way for my jsx files to get the data from a global JS object which has loaded the data from a config file?
I've seen some examples, but I've not been able to get them to work.
JS6 import function | Example using webpack
Assuming ES6:
config.js
export const myConfig = { importantData: '', apiUrl: '', ... };
Then:
jsxFileOne.js, jsxFileTwo.js, ...
import { myConfig } from 'config.js';
There are other ways to import & export things globally leveraging webpack, but this should get you started.
If your project is built using Webpack, consider using node-env-file.
Example config file snippets:
development.env
API_SERVER_URL=https://www.your-server.com
webpack.config.js
const envFile = require('node-env-file');
...
const appSettingsFile = isDevBuild ? '/settings/development.env' : '/settings/production.env';
try {
envFile(path.join(__dirname + appSettingsFile));
} catch (error) {
console.log("Failed to read env file!: " + __dirname + appSettingsFile);
}
...
plugins: [
new webpack.DefinePlugin({
"process.env": {
API_SERVER_URL: JSON.stringify(process.env.API_SERVER_URL)
}
})
...
]
Inside your js/jsx code, you can now access process.env.API_SERVER_URL variable which will contain the required value.
It seems dotenv package is more popular, you can try this out as well.
Very old problem, that nobody took the time to solve, until now. I leave this for future readers because this is a top search result for configuration in React.
I created wj-config to deal exactly with this. Be sure to pay close attention to the React notes as you will need to enable top-level awaits in webpack, either by ejecting or using the #craco/craco NPM package.
You may also read this blog post that explains its use.

Typescript compiler error when importing json file

So the code is simple:
calls.json
{"SERVER":{
"requests":{
"one":"1"
}
} }
file.ts
import json = require('../static/calls.json');
console.log(json.SERVER);
the generated javascript is correct and when running the node js server, the console log json.SERVER prints '{ requests: { one: '1' } }', as it should.
The typescript compiler (commonjs) however, somehow does not particularly like this situation and throws: "Cannot find module '../static/calls.json'".
Ofcourse I tried writing a .d.ts file, like this:
declare module '../static/calls.json'{
var exp:any;
export = exp;
}
this then obviously throws: "Ambient module declaration cannot specify relative module name".
I also tried different variants, like:
declare module 'calls.json' {
import * as json from '/private/static/calls.json';
export = json;
}
and then requiring:
import json = require('calls.json');
None work properly and have their own little compiler errors :)
I want to use an external .json file because I use commonjs serverside and amd clientside and I want a single file for loading constants.
Use var instead of import.
var json = require('./calls.json');
You're loading a JSON file, not a module, so import shouldn't be used is this case. When var is used, require() is treated like a normal function again.
If you're using a Node.js definition, everything should just work, otherwise require will need to be defined.
TS 2.9 added support for well typed json imports. Just add:
{
"compilerOptions": {
"resolveJsonModule": true
}
}
in your tsconfig.json or jsconfig.json. Now imports such as:
import json = require('../static/calls.json');
and
import * as json from '../static/calls.json';
should be resolved and have proper typings too!
Another solution is to change data.json to data.ts and export like this
export default {
"key" : {
...
}
}
and import as you would expect:
import { default as data } from './data'
This can also be done by using import statement if using webpack v2 which is already packed with json-loader.
Note that this is not async
import data from './data.json';//Note that this is not async
Also, in your typings.d.ts file add the following wildcard module to avoid typescript error saying: Cannot find module
declare module "*.json" {
const value: any;
export default value;
}
For anyone interested in async imports, check this article by 2uality
As of Typescript 2.9 you can import JSON file natively without any additional hack/loader needed.
The following excerpt is copied from said link above.
...TypeScript is now able to import JSON files as input files when using the node strategy for moduleResolution. This means you can use json files as part of their project, and they’ll be well-typed!
./src/settings.json
{
"dry": false,
"debug":
./src/foo.ts
import settings from "./settings.json";
settings.debug === true; // Okay
settings.dry === 2; // Error! Can't compare a `boolean` and `number`
For Angular 6 it can work with simple HTTP get call as below
Service
//interface, could be Array , object
export interface ResultJSON{
}
//Read JSON file for 3DWide
getJSON() {
return this.http.get(this.filepathUrl);
}
Component :import both service and interface and use as below
resultJSON :ResultJSON;
this
.testService
.getJSON()
.subscribe((data: ResultJSON) => {
this.resultJSON= data;
console.log(this.resultJSON);
});

Categories

Resources