JavaScript export / import doesn't work - javascript

it might be a silly question but I can't fix it anyway. I have a JavaScript file with various functions I'd like to export.
export function AddNumbers(...numbers)
{
let value = 0;
for(var i = 0;i < numbers.length;i++)
{
value += numbers[i];
}
return value;
}
When I call this method (using mocha) I get an error message "export function AddNumbers(...numbers) Unexpected token export". The project is build as ES6. Does anybody know what I'm doing wrong?
Best regards,
Torsten

You need to use module.exports as NodeJS uses CommonJS Module syntax which requires to use module.exports and not just export which is defined by ES6 module syntax. So, make sure CommonJS is also configured properly in your project.

Another solution is to use Babel. Install it with
npm install babel-core --save-dev
npm install babel-preset-es2015 --save-dev
Create in root directory a file .babelrc with following content
{
"preset" : ["es2015"]
}
and finally change the script in package.json to run into:
"scripts": {
"test": "mocha Tests --require babel-core/register"
}
and now export / import works.

Related

Node.js: Cannot find module but the specified path exists

I am currently developing my own npm package and I created a separate project to download this package from npm for an independent test. The package is being developed in typescript and I have a main file with several additional module files. In my main file, I am importing all of the classes from the other modules, then exporting all of them under the main file. I don't know if this is good practice but when I run the main file on the test project, it says it can't find the module when the path it specifies exists in the working directory.
Code Snippets:
Main file:
import { EventBus } from "./modules/eventbus/eventbus";
import { EventHandler } from "./modules/eventbus/eventhandler";
import { EventType } from "./modules/eventbus/eventtype";
import { Event } from "./modules/eventbus/event";
import { SemVer } from "./modules/semver";
export { SemVer, Event, EventBus, EventHandler, EventType };
Error:
Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/workspaces/epic-engine-testing/node_modules/epic-engine/lib/modules/eventbus/eventbus' imported from /workspaces/epic-engine-testing/node_modules/epic-engine/lib/index.js
Working directory:
Testing file:
import { EventBus, EventHandler, EventType, Event } from "epic-engine";
class SomeType extends EventType {
constructor() {
super();
}
}
const eventbus = new EventBus();
const handler = new EventHandler<SomeType>(eventbus, "type", () => {});
eventbus.createHandler(handler);
const event = new Event<SomeType>(eventbus, new SomeType(), "type");
package.json:
{
"devDependencies": {
"#tsconfig/esm": "^1.0.2",
"#types/jest": "^29.2.3",
"jest": "^29.3.1",
"ts-jest": "^29.0.3",
"tslint": "^6.1.3",
"typescript": "^4.9.3"
},
"name": "epic-engine",
"description": "Pure TS engine developed by EpicPuppy613",
"version": "0.1.0-dev.5",
"main": "lib/index.js",
"types": "lib/index.d.ts",
"type": "module",
"scripts": {
"test": "jest --config jestconfig.json",
"build": "tsc",
"prepare": "npm run build",
"lint": "tslint -p tsconfig.json",
"prepublishOnly": "npm test && npm run lint",
"preversion": "npm run lint",
"version": "git add -A src",
"postversion": "git push && git push --tags"
},
"repository": {
"type": "git",
"url": "git+https://github.com/EpicPuppy613/epic-engine.git"
},
"author": "EpicPuppy613",
"license": "MIT",
"bugs": {
"url": "https://github.com/EpicPuppy613/epic-engine/issues"
},
"homepage": "https://github.com/EpicPuppy613/epic-engine#readme",
"files": [
"lib/**/*"
]
}
I tried a bunch of things including changing the references to use .js, using absolute paths instead, and changing some settings in tsconfig.json.
Why is Node.js not finding the submodules or would it be better to export the modules in a different way?
Save time with npm link command
I created a separate project to download this package from npm for an independent test.
First, you can use the handy npm link command to save yourself the trouble of uploading your package just so you can test. As per the docs the npm link command:
...is handy for installing your own stuff, so that you can work on it and test iteratively without having to continually rebuild.
Install your package as a dependency
With that out of the way, I think the hint is in the error message. Note it says:
Cannot find module /workspaces/epic-engine-testing/node_modules/...
Here it seems Node is looking for the file in the epic-engine-testing project, so you must have the package.json file for the test project reference your package (i.e. the one you want to test). So go into your epic-engine-testing project folder and at the terminal type npm install epic-engine#0.1.0-dev.5. That should install your package so it can be found. If that doesn't resolve it, you'll need to share the package.json file for the epic-engine-testing to help us see what's going on.
Using the export { ... } from '...' syntax
Your main file can use the re-exports synax and be simplified to this when exporting:
export { EventBus } from "./modules/eventbus/eventbus";
export { EventHandler } from "./modules/eventbus/eventhandler";
export { EventType } from "./modules/eventbus/eventtype";
export { Event } from "./modules/eventbus/event";
export { SemVer } from "./modules/semver";
// the line below is not necessary when using above syntax.
// export { SemVer, Event, EventBus, EventHandler, EventType };
After compiling the ts files into js files, wherever you have import syntax, Node.js looks for .js files to resolve them. So it needs to be explicitly given a module name with .js extension in import.
You may need to read this doc on how the import mechanism works in Node.js.
To fix this issue, you have multiple choices(since the target you've defined is ES6):
change moduleResolution to nodeNext and add .js extension whenever you would importing modules in typescript:
import { EventBus } from "./modules/eventbus/eventbus.js";
import { EventHandler } from "./modules/eventbus/eventhandler.js";
import { EventType } from "./modules/eventbus/eventtype.js";
...
You don't need to be worried about it, typescript is well smart. Based on this comment from one of typescript contributor:
.js file extensions are now allowed
Using rollup package. The rollup package won't manipulate your files. Instead, it bundles your output files.

How to run a TypeScript function from a package.json script

Currently I am running a javascript function from my package.json script, but I was looking to change it to a typescript function in a typescript file and was wondering how to adjust for that?
In javascript I have the following, which works and runs the desired function:
"scripts": {
"command": "node -e 'require(\"./javaScriptFileName\").functionName()'"
}
The typescript file I am exporting the function from looks like this:
const fs = require("fs");
module.exports.functionName = function(path: string): void
{
if(fs.exists(path))
{
console.log("Exists");
}
}
I've tried getting the ts-node package and replacing "node" with "ts-node", which doesn't seem to work.
Assuming that javaScriptFileName.ts is structured like:
export default {
functionName: () => console.log('whatever')
}
Then can just run it via ts-node like you would in any typescript file: import the file, then run the function.
ts-node -e "import myLib from './javaScriptFileName'; myLib.foo()"

Can I turn off create-react-app chunking mechanism?

I am setting up my React app project using create-react-app.
I was wondering if there is a way to turn-off the chunking mechanism that is built-in into the react scripts. The thing is that I need to fix the name of the bundle created on the build.
It can be done by extending your CRA with react-app-rewired package which allows you to modify webpack config.
Changes needed to remove hash in build file names.
Install react-app-rewired
npm install react-app-rewired --save-dev
create config-overrides.js file in your root folder (where package.json is)
place the following code to the config-overrides.js file. It keeps all CRA settings, only remove the hash part from filenames.
module.exports = function override(config, env) {
config.output = {
...config.output, // copy all settings
filename: "static/js/[name].js",
chunkFilename: "static/js/[name].chunk.js",
};
return config;
};
use the new config. In the package.json file in scripts section replace "build": "react-scripts build", with "build": "react-app-rewired build",
Unless you are going to change more configuration, it is enough to only use react-app-rewired in build. Otherwise replace react-scripts with react-app-rewired in others scripts except eject
I've found that you can disable chunking by setting splitChunks webpack configuration. For more details check https://github.com/facebook/create-react-app/issues/5306#issuecomment-431431877
However, this does not remove the contenthash part from the bundle name and you will still have that random string in the name.
To remove this, go to your webpack.config and edit the bundle name
'static/js/[name].[contenthash:8].js' => 'static/js/[name].js'
This is extended and improved version of Darko's answer. I created it mostly to save time for others who is not fully satisfied with solution mentioned in this comment and didn't have a patience to dig to this comment that solved the issue in much nicer way.
Main idea of this "hacky" approach is to re-write standard react-scripts's webpack configuration on the fly and inject it back to original scripts.
For that you would need to install rewire package from npmjs.org, like so:
npm install rewire --save-dev
Then you create separate build script that will will "wrap" original react build script and make sure that it will relieve corrected webpack configuration. Conventional way is to save this file inside ./scripts folder. So let's call it ./scripts/build.js. It's content:
const rewire = require('rewire');
const path = require('path');
// Pointing to file which we want to re-wire — this is original build script
const defaults = rewire('react-scripts/scripts/build.js');
// Getting configuration from original build script
let config = defaults.__get__('config');
// If we want to move build result into a different folder, we can do that!
// Please note: that should be an absolute path!
config.output.path = path.join(path.dirname(__dirname), 'custom/target/folder');
// If we want to rename resulting bundle file to not have hashes, we can do that!
config.output.filename = 'custom-bundle-name.js';
// And the last thing: disabling splitting
config.optimization.splitChunks = {
cacheGroups: {
default: false,
},
};
config.optimization.runtimeChunk = false;
Then, we should use this build script instead of standard one in our packages.json, something like so:
...
"scripts": {
"start": "react-scripts start",
"build": "node ./scripts/build.js",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
...
As others have pointed out you can try this with react-app-rewired instead of ejecting. Here is a version that also handles css and media files:
After installing npm install react-app-rewired --save-dev I created a config-overrides.js with the following content:
module.exports = function override(config, env) {
if (env !== "production") {
return config;
}
// Get rid of hash for js files
config.output.filename = "static/js/[name].js"
config.output.chunkFilename = "static/js/[name].chunk.js"
// Get rid of hash for css files
const miniCssExtractPlugin = config.plugins.find(element => element.constructor.name === "MiniCssExtractPlugin");
miniCssExtractPlugin.options.filename = "static/css/[name].css"
miniCssExtractPlugin.options.chunkFilename = "static/css/[name].css"
// Get rid of hash for media files
config.module.rules[1].oneOf.forEach(oneOf => {
if (!oneOf.options || oneOf.options.name !== "static/media/[name].[hash:8].[ext]") {
return;
}
oneOf.options.name = "static/media/[name].[ext]"
});
return config;
};
I don't know how to turn off chunking but what you could do try achieve you goal
Update to latest react and react-dom , run 'yarn react#next react-dom#next' (or npm command to do same)
You should now have the latest react versions - so you can code split using React.lazy/React.Suspense, use hooks and so on.
So now you can name your chunks using (component or dependency examples below)
const MyComp = lazy(() => import(/* webpackChunkName: 'MyChunkNmame'
*/ './MyComp'), );
const myLib= await import(/* webpackChunkName: "myLib" */ 'myLib');
If you have an issue with errors when using the import syntax you need to use the babel-plugin-syntax-dynamic-import plugin. Put the "babel" field in your package json.
Now you can name your chunks and implement the latest way to code split - hope that helps. Here is a link to React.lazy React.Suspense - https://reactjs.org/blog/2018/10/23/react-v-16-6.html
There is a hack without needing eject:
yarn add --dev rewire
create file in root and name it build-non-split.js
fill inside it by below codes:
const rewire = require('rewire');
const defaults = rewire('react-scripts/scripts/build.js');
let config = defaults.__get__('config');
config.optimization.splitChunks = {
cacheGroups: {
default: false,
},
};
config.optimization.runtimeChunk = false;
change the build script inside your package.json to:
"build": "node ./scripts/build-non-split.js",
yarn build

Unit testing using Jasmine and TypeScript

I am trying to get a unit test written in Typescript using Jasmine to compile. With the following in my unit-test file, Resharper prompts me with a link to import types from jasmine.d.ts.
/// <reference path="sut.ts" />
/// <reference path="../../../scripts/typings/jasmine/jasmine.d.ts" />
describe("Person FullName", function () {
var person;
BeforeEach(function () {
person = new Person();
person.setFirstName("Joe");
person.setLastName("Smith");
});
It("should concatenate first and last names", function () {
Expect(person.getFullName()).toBe("Joe, Smith");
});
});
So I click on the link and end up with the following (actually resharper only prefixed the describe function with "Jasmine.", so I manually prefixed the other Jasmine calls):
/// <reference path="sut.ts" />
/// <reference path="../../../scripts/typings/jasmine/jasmine.d.ts" />
import Jasmine = require("../../../Scripts/typings/jasmine/jasmine");
Jasmine.describe("Person FullName", function () {
var person;
Jasmine.BeforeEach(function () {
person = new Person();
person.setFirstName("Joe");
person.setLastName("Smith");
});
Jasmine.It("should concatenate first and last names", function () {
Jasmine.Expect(person.getFullName()).toBe("Joe, Smith");
});
});
However the import statement has a red squiggly line with error message "Unable to resolve external module ../../../scripts/typings/jasmine/jasmine. Module cannot be aliased to a non-module type"
Any idea what is causing this error? I've checked that the "Module System" option is set to AMD in my project build settings. I've also checked that the jasmine module is defined in jasmine.d.ts. I downloaded this file from DefinitelyTyped site.
declare module jasmine {
...
}
Here's (in my opinion) the best way to test a ts-node app as of 2018:
npm install --save-dev typescript jasmine #types/jasmine ts-node
In package.json:
{
"scripts": {
"test": "ts-node node_modules/jasmine/bin/jasmine"
}
}
In jasmine.json change file pattern to *.ts
"spec_files": ["**/*[sS]pec.ts"],
In your spec files:
import "jasmine";
import something from "../src/something";
describe("something", () => {
it("should work", () => {
expect(something.works()).toBe(true);
});
});
To run the tests:
npm test
This will use the locally installed versions of ts-node and jasmine. This is better than using globally installed versions, because with local versions, you can be sure that everyone is using the same version.
Note: if you have a web app instead of a node app, you should probably run your tests using Karma instead of the Jasmine CLI.
Put this at the top of your typescript spec file:
/// <reference path="../../node_modules/#types/jasmine/index.d.ts" />
let Jasmine = require('jasmine');
You must install the following Jasmine modules for that to work:
$ npm install jasmine-core jasmine #types/jasmine #ert78gb/jasmine-ts --save-dev
Once you do that, the IDE (such as WebStorm) will recognize Jasmine and its functions such as describe(), it(), and expect().. So you don't need to prefix them with "Jasmine." Also, you can run your spec files from the command line using the jasmine-ts module. Install these command line tools globally:
$ npm install -g jasmine #ert78gb/jasmine-ts
Then configure the "jasmine" command line module so that Jasmine can find its configuration file. Then you should be able to run jasmine-ts and your spec file should run fine from the command line:
./node_modules/.bin/jasmine-ts src/something.spec.ts
.. and, you can configure your IDE to run it like that as well, and debug runs that way should also work (works for me).
Writing your tests this way, you can run a Jasmine test spec on the server side without Karma, or run it in a web browser using Karma. Same typescript code.
If you have issues with imports, use tsconfig-paths
npm i ts-node tsconfig-paths types/jasmine jasmine --save-dev
Run typescript-enabled jasmine:
ts-node -r tsconfig-paths/register node_modules/jasmine/bin/jasmine.js
Ensure that your jasmine will search .ts files:
"spec_files": [
"**/*[sS]pec.ts"
],
"helpers": [
"helpers/**/*.ts"
],
To test your scripts you may also need polyfills if you use them in your project. Create a helper file with required imports, like helpers/global/polifill.ts
import 'core-js';
For me I did the following:
Install Typings
npm install typings --global
Then add the typings in for jasmine
typings install dt~jasmine --save --global
You could try a side-effect only import which brings in the #types/jasmine declaration and places the jasmine functions into the global scope so you don't need to prefix each call with jasmine. allowing a quick port from existing unit tests and still plays nice with webpack.
// tslint:disable-next-line:no-import-side-effect
import "jasmine";
describe("My Unit Test", () => { /* ... */ } );
Of course you still need to install jasmine and the typings:
$ npm i jasmine #types/jasmine --save-dev
But no need for specialized jasmine loaders for ts or node. Just run jasmine against the compiled js files:
$ node ./node_modules/jasmine/bin/jasmine.js --config=test/support/jasmine.json
Assuming your typescript files are within a "test" subdirectory compiling to bin/test and you have a test/support/jasmine.json with something like this:
{
"spec_dir": "bin/test",
"spec_files": [
"**/*[sS]pec.js"
],
"stopSpecOnExpectationFailure": false,
"random": false
}
P.S. all of the above works on Windows too
Include this to your jasmine html file,...
<script type="text/javascript" src="jasmine/lib/jasmine-2.0.0/jasmine.js"></script>
...or install the npm jasmine package:
npm install --save-dev jasmine
when you are using the second way (jasmine as module) you have to import it:
var jasmine = require('jasmine');
or
import jasmine from 'jasmine';
then change the other code:
jasmine.describe("Person FullName", function () {
var person;
jasmine.beforeEach(function () {
person = new Person();
person.setFirstName("Joe");
person.setLastName("Smith");
});
jasmine.it("should concatenate first and last names", function () {
jasmine.expect(person.getFullName()).toBe("Joe, Smith");
});
});
Personally i would prefer the first way without using the jasmine npm module. (I didn't test the module yet)
You didn't ask for this, but for bonus points: once you get AJ's answer up and running (using ts-node to invoke the Jasmine startup script), you can add a new task:
"scripts": {
"watch": "ts-node-dev --respawn -- ./node_modules/jasmine/bin/jasmine src/**.spec.ts"
}
Of course, you can pass your specs or any other arguments using Jasmine's config file instead, if you like. Now, Jasmine will run all your specs once, then ts-node-dev will sit in the background waiting for your tests or anything they might have require'd to change, at which point jasmine will be run again. I haven't worked out a way to only run the tests that have changed (or tests whose imports have changed) yet -- as far as I can tell, that's not supported anyway;
My folder structure
Spec folder is on the root of project
spec
\- dist // compiled tests
\- helpers // files modified testing env
\- ts-console.ts // pretty prints of results
\- support
\- jasmine.json
\- YourTestHere.spec.ts
\- tsconfig.json // tsconfig for your tests
Files content
ts-console.ts
const TSConsoleReporter = require("jasmine-console-reporter");
jasmine.getEnv().clearReporters();
jasmine.getEnv().addReporter(new TSConsoleReporter());
jasmine.json
{
"spec_dir": "spec/dist",
"spec_files": [
"**/*[sS]pec.js"
],
"helpers": [
"spec/helpers/**/*.js"
],
"stopSpecOnExpectationFailure": false,
"random": true
}
With extra script in package.json
"scripts": {
"test": "rm -rf ./spec/dist && tsc -p ./spec && jasmine"
}
and add line "/spec/dist" to .gitignore
Run your tests!
Run your tests with npm test.
How does it work?
Directory for tests is cleaned.
Tests are compiled to spec/dist folder to JS.
Tests are runned from this location.
I hope it will help you. Good coding.

How to transpile React ES6 classes using Grunt + Browserify + Reactify?

I want to use the new ES6 React classes that was introduced with React v0.13, but I am unable to get it to compile correctly. Say I have the following React component defined in the new class syntax:
(function() {
'use strict';
import React from 'react';
class _UserDashboard extends React.Component {
render() {
return(
<div className="user-dashboard">
<Books />
</div>
);
}
}
export const UserDashboard = React.createClass(_UserDashboard.prototype);
}());
The trouble I run into here is that at compile time using Grunt and Browserify and a Reactify transform, Reactify throws an error when it encounters the import keyword:
ReactifyError: /Users/****/Sites/***/assets/js/components/UserDashboard.jsx: Parse Error: Line 7: Unexpected reserved word while parsing file: /Users/****/Sites/****/assets/js/components/UserDashboard.jsx
The problem here seems to have to do with Reactify's use of react-tools, see here and here. But I'm not sure if it is possible to enable the es6module option within Reactify yet.
I tried these two variations to no avail:
...
transform: [[ 'reactify', {'es6module': true} ]]
...
and
...
transform: [[ 'reactify', {'es6':true, 'es6module':true} ]]
...
Does anyone know how this can be done?
You should be able to do all of this with Babel and babelify (the corresponding Browserify plugin).
Babel itself is an ES6+ to ES5 transpiler, but it comes with first class JSX support:
JSX and React
Babel works perfectly with React, featuring a built-in JSX transformer.
Your Browserify transform would become:
{
"browserify": {
"transform": ["babelify"]
}
}
Edit: As of Babel 6, many components have been moved out into a separate presets packages which you'll need to install and include in the transform command.
For React and JSX, you'll need babel-preset-react.
For ES6, you'll need babel-preset-es2015
Together these can be specified in the transform.
transform: [[babelify, { presets: ["react", "es2015"] }]]
Babelify is a best option for my point of view. Small clarification to #Dan's answer. For the babel v 6.0 you will need to install 2 preset.
ES2015
React
with something similar to npm install --save-dev babel-preset-react and npm install --save-dev babel-preset-es2015 commands and then specify this in babelify settings with: transform: [[babelify, {presets: ["es2015", "react"]}]]
And if in your code you use some newest react features like ...other for example, you also need add stage-0 preset.

Categories