How do I make "export default" compile to "module.exports" - node.js

I'm writing in typescript with it set to compile to commonjs. Whenever I put export default, it compiles into exports.default instead of module.exports. I am making an NPM package, so I want this to be fixed. How do I solve it? If needed, I can add my tsconfig.json file.

const test = ()=> 'text';
export = test;
compiled
"use strict";
const test = () => 'text';
module.exports = test;
click demo
typescript doc: https://www.typescriptlang.org/docs/handbook/modules.html#export--and-import--require

You can simply write it with module.exports in your TS file
const myExportedObj = {
};
module.exports = {
foo: myExportedObj,
};
Output:
var myExportedObj = {};
module.exports = {
foo: myExportedObj,
};
Or, as far as I can see from your question, you could don't export default but export instead
TS
export const myExportedObj = {
};
JS
exports.myExportedObj = {};
Another good approach is to make an index.ts file and export modules from there
TS
export * as Context from './context';
JS
exports.Context = require("./context");

Related

RequireJS + Mocha + JSDom + Node -> Shim config not supported in Node

I'm trying to setup a Mocha testing framework using JSDom with RequireJS. Because I'm running the test on node instead of using a browser (since I'm using JSDom), all the non AMD modules doesn't seem to be imported and is throwing Shim config not supported in Node. Does anyone know how I can export those modules to AMD or what the right approach is? (aka what I'm doing wrong)
Example of my set-up
Component.js
define(["jquery", "non_AMD_Module", ... ], function($, NonAMDModule, ...) {
let component = {
...
foo = () => {
NonAMDModule.bar();
};
};
return component;
});
Component.test.js
const requirejs = require('requirejs');
const { JSDOM } = require('jsdom');
requirejs.config({
baseUrl: "dist/app",
paths: {
jquery: "lib/jquery",
component: "path_to_component",
non_AMD_Module: "path_to_module"
},
shim: {
non_AMD_Module: { exports: "non_AMD_Module" } // This doesn't work
}
});
const { window } = new JSDOM("<html></html>");
global.window = window;
global.document = window.document;
global.$ = requirejs('jquery');
const Component = requireJS('component');
describe('test', () => {
it('is a simple test', () => {
const testComponent = new Component();
testComponent.foo();
}
});
When I run the test suite, I get:
Mocha Exploded!
TypeError: Cannot read property 'bar' of undefined
running r.js -convert "path_to_module" did not work for this module
Looking at the source code for jQuery, I found that there's this boiler-plate coded that exports it to AMD.
This can be added at the bottom of the non-AMD-module in order to export it to an AMD module accessible by RequireJS
if ( typeof define === "function" && define.amd ) {
define([], function {
return non_AMD_Module;
});
}
Other Resources:
Shim a module in Require.js that uses module.exports possible?
https://github.com/requirejs/requirejs/wiki/Updating-existing-libraries#anon

Mocking dotenv with Jest

I have a test from a web application that was originally created using CRA. So It uses Jest and react-testing-library. I am also in a TypeScript environment.
The beginnings of the test I have:
import { render, screen } from "#testing-library/react";
import dotenv from 'dotenv';
import App from './App';
. . . .
jest.mock('dotenv');
//(what goes here?)
But here is where I need help. I am not sure how to mock the module. In the component I have something like:
if (process.env.REACT_APP_CONSTRUCTION === "true") {
return (<UnderConstruction />);
} else {
return (<App/ >);
}
In testing this component I would like to test the two scenarios, one where the environment returns "true" and the other otherwise.
Ideas or suggestions?
You can create mock env variables after each test in jest like below.
Also you don't need to mock jest.mock('dotenv'); so you can delete that part in your test file.
const OLD_ENV = process.env
afterEach(() => {
cleanup()
jest.clearAllMocks()
jest.resetModules()
process.env = { ...OLD_ENV }
delete process.env.NODE_ENV
})
it('scenario 1', () => {
// Given
process.env.REACT_APP_CONSTRUCTION = true // mock variable for scenario 1
// When
const { queryByText } = render(<App />)
const underConstructionText = queryByText('Under Construction')// Just an example
// Then
expect(underConstructionText).toBeInTheDocument()
})
it('scenario 2', () => {
// Given
process.env.REACT_APP_CONSTRUCTION = false // mock variable for scenario 2
...
// When
const { queryByText } = render(<App />)
const underConstructionText = queryByText('Under Construction')
// Then
expect(underConstructionText).not.toBeInTheDocument()
})

node.js adding new export field without braking existing requires

Consider following simple scenario, file config.js:
const config = {
a: '123'
}
module.exports = config;
and it's usage:
const cfg = require('./conifg');
console.log(cfg.a);
Now I'm in need to add additional export member to config.js:
const config = {
a: '123'
}
function someFunction() {
console.log('blah');
}
module.exports = {
config,
someFunction
};
This modification brakes down so-far working code, because cfg.a in
const cfg = require('./conifg');
console.log(cfg.a);
points now to undefined.
Is there any way to extend module.exports while remaining it's "default" exported member to not brake things down?
you may export all property of config separately
module.exports = {
...config,
someFunction
};
or if you don't want use spread, you can access by
const cfg = require('./conifg');
console.log(cfg.config.a);
You can do it like this
const config = {
a: '123',
someFunction: someFunction
}
function someFunction() {
console.log('blah');
}
module.exports = config;

Unit testing in Typescript with Dependency Injection

I'm running into problems with typescript during unit testing. I try to unit test an module like the following code example:
import {Express} from 'express'
export interface BootClassInterface {
setup(): Express
}
export class BootClass implements BootClassInterface {
constructor(private express: () => Express ){
}
public test(){
const app = this.express()
app.get("/test", (req, res) => {
res.send("Hello World")
})
return app;
}
}
For testing propose I want to know if express().get() was called and if the first parameter was '/test'. Before I switched to typescript I always used the module sinonJS to spy or stub the functionality so that I could test of a certain dependency was used correctly. Now with Typescript I run into problems with the strict types that I've set in the module. By example:
import * as chai from 'chai'
import 'mocha'
import * as sinon from 'sinon'
import * as express from 'express'
import * as Boot from '../../src/Core/Boot'
const assert = chai.assert
suite('[CORE] unit test /Core/Boot.ts', () => {
let BootClass: Boot.BootClassInterface
setup(() => {
const expressApp = () => {
const app = express()
app.get = sinon.spy()
return app
}
const Services: Boot.BootServicesInterface = { express: expressApp }
BootClass = new Boot.BootClass(Services)
})
test('Do first test', () => {
const app = BootClass.setup()
chai.assert.equal(app.get.called, 1)
})
})
The code example above results in the following Typescript compiling error:
error TS2339: Property 'called' does not exist on type 'ApplicationRequestHandler<Express>'.
I can see why typescript returns this error and somehow it is even obvious. I even know a possible solution where I accept Express as any in the module.
But I'm looking for a more elegant way to be able to spy/stub/mock my dependencies for testing propose, but at the same time have the advantages of strict typing.
AS you have stated you specify Express as the return type on BootClassInterface interface. Therefore app would be considered Express and it will look up its properties instead of your mock.
You can also just cast one property of app as any. Try:
chai.assert.equal((<any>app.get).called, 1)
Or using Sinon type definition:
chai.assert.equal((<SinonSpy>app.get).called, 1)

Using es6 transpiled classes in Nodejs

So I have my example classes in my file ContrivedExampleA.es6
export class Person {
constructor(name, age) {
this._name = name;
this._age = age;
}
get name() {
return this._name;
}
get age() {
return this._age;
}
}
export class Greeter {
constructor(person) {
this._person = person;
}
greet() {
return `Hello ${this._person.name}, I believe you are ${this._person.age} years old!`;
}
}
which I compile (successfully), using the following Gulp task:
var gulp = require('gulp');
var browserify = require('browserify');
var babelify = require('babelify');
var source = require('vinyl-source-stream');
gulp.task('build', function () {
return browserify({entries: './src/ContrivedExampleA.es6', extensions: ['.es6'], debug: true})
.transform(babelify)
.bundle()
.pipe(source('example.js'))
.pipe(gulp.dest('build'));
});
So I want to use this code in regular Nodejs land, so I do the following:
"use strict";
var Person = require('./example').Person;
var Greeter = require('./example').Greeter;
var person = new Person("Test Person", 25);
var greeter = new Greeter(person);
console.log(greeter.greet());
However, both Person and Greeter are undefined. I've even tried including transform-es2015-modules-commonjs in my .babelrc file which hasn't helped anything.
What am I doing wrong? and how can I get these transpiled ES6 classes imported into a regular Nodejs file?
EDIT
I also have a .babelrc file which looks like:
{
"presets": ["es2015"],
"plugins": ["transform-es2015-modules-commonjs"]
}
Ignore my previous answer, I was focusing on the wrong thing.
If you want to get the regular ES6 classes imported into a Nodejs file, you could skip the browserify and babelify steps all together, and just use the gulp-babel plugin directly. I would recommend using gulp-rename too.
Your ContrivedExampleA.es6 file would stay the same, but your gulpfile.js would become:
var gulp = require('gulp');
var babel = require('gulp-babel');
var rename = require('gulp-rename');
gulp.task('build', function () {
return gulp.src('./src/ContrivedExampleA.es6')
.pipe(babel({presets: ["es2015"], plugins: ["transform-es2015-modules-commonjs"]}))
.pipe(rename('example.js'))
.pipe(gulp.dest('build'));
});
Note: since you have a .babelrc you could probably skip passing the options to babel.
Then in your node file, you could simply do
"use strict";
var Person = require('./example').Person;
var Greeter = require('./example').Greeter;
var person = new Person("Test Person", 25);
var greeter = new Greeter(person);
console.log(greeter.greet());

Resources