Babel not transpiling `class` in *.mjs files - javascript

Given
import test from './test.js';
import test2 from './test.mjs';
and test.js and test.mjs both containing
class A {
test() {
console.log('from mjs'); // or console.log('from js');
}
}
export default A;
Babel transpiles
[...];
(0, _createClass2.default)(A, [{
key: "test",
value: function test() {
console.log('from js');
}
}]);
[...]
class A {
test() {
console.log('from mjs');
}
}
[...]
How should I configure babel to treat *.mjs files exactly like *.js files. I need them to be *.mjs files so that I can run it in node without transpilation.
Plugins I am currently explicitly adding are
'#babel/plugin-proposal-class-properties', '#babel/plugin-transform-runtime', '#babel/plugin-transform-classes'

You need to add *.mjs extension to your Webpack configuration:
module: {
rules: [
{
test: /\.m?js$/,
use: {
loader: 'babel-loader',
options: {
// ...

Related

Cannot export API in WebPack

I am creating a library in Javascript and I am shipping it as a bundle .js file using Webpack. The following file lib.js serves as the entry for Webpack in order to expose all the API in the library:
import * as bodies from "./bodies.js";
import * as composites from "./composites.js";
import * as connections from "./connections.js";
export var bodies = {
Body: bodies.Body,
Pyramid: composites.Pyramid
};
export var connections = {
Connection: connections.Connections
};
All the files imported basically export classes that I am referencing in lib.js:
// In bodies.js
export class Body { ... };
// In composites.js
export class Pyramid { ... };
// In connections.js
export class Connection { ... };
The file for bundling using Webpack is:
const path = require('path');
module.exports = {
entry: './lib.js',
output: {
filename: 'lib-bundle.js',
path: path.resolve(__dirname, 'out')
},
module: {
rules: [
/* In order to transpile ES6 */
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: { presets: ['env'] }
}
}
],
}
};
Webpack successfully bundle everything and I get my lib file in the end.
Problems using it
Then I use it in another project:
import * as mylib from "./lib/lib-bundle.js";
// Trying to use Pyramid
var pyramid = new mylib.bodies.Pyramid();
I use again Webpack to bundle this file into a file called start.js which i import in my page:
<script type="application/javascript" src="./start.js"></script>
However when running this page, I get an error. If I run the F12 tools and break in the bundle where I try creating an instance of the pyramid, there i can clearly see that object mylib does not have anything I have exposed. It is empty, lacking all the objects I exposed before.
What am I doing wrong?
You need to specify a libraryTarget in the output section of your webpack config file.
With it the bundle will correctly export your defined values, which
can be then imported with the various module loaders.
I suggest using libraryTarget: "umd" since it will add support for the most commonly used loaders. From the webpack docs:
This exposes your library under all the module definitions, allowing it to work with CommonJS, AMD and as global variable.
The resulting webpack config file is as follows:
const path = require('path');
module.exports = {
entry: './lib.js',
output: {
filename: 'lib-bundle.js',
path: path.resolve(__dirname, 'out'),
libraryTarget: 'umd',
},
module: {
rules: [
/* In order to transpile ES6 */
{
test: /\.js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: { presets: ['env'] }
}
}
],
}
};

Using jquery in a shared webpack module, which is outside the webpack root

I have multiple layouts, which depend on some shared typescript files, thats why I want to share this files with multiple layouts which are using webpack.
I'm trying to include jquery in my ajax.ts and get this error:
ERROR in ../_shared/ajax.ts
Module not found: Error: Can't resolve 'jquery' in '{...}/layouts/_shared'
_shared/ajax.ts:
import * as $ from 'jquery';
export class AjaxListener {
constructor(){
// use jquery with $
}
}
layoutA/app.ts:
import { AjaxListener } from "../_shared/ajax";
import { App } from "../_shared/app";
let app = new App();
let ajaxListener = new AjaxListener();
My Folder Structure looks like this:
/layouts
/_shared
/ajax.ts
/app.ts
/layoutA
/app.ts
/webpack.config.js
/package.json (contains "#types/jquery": "^2.0.47" and "jquery": "^3.2.1")
/tsconfig.json
tsconfig.json:
{
"compilerOptions": {
"module": "es6",
"target": "es6",
"sourceMap": true
},
"exclude": [
"node_modules",
"typings/browser",
"typings/browser.d.ts",
"typings/main",
"typings/main.d.ts"
]
}
webpack.config.js:
const ExtractTextPlugin = require("extract-text-webpack-plugin");
var path = require("path");
var distPath = path.join(__dirname, "dist");
module.exports = [
{
entry: {
app: ['./app.sass', './app.ts']
},
resolve: {
extensions: [".tsx", ".js", ".ts", ".sass"]
},
cache: false,
output: {
path: distPath,
filename: "[name]_scripts.js"
},
module: {
rules : [
{
enforce: 'pre',
// "test" is commonly used to match the file extension
test: /\.js$/,
loader: "source-map-loader"
},
{
// "test" is commonly used to match the file extension
test: /\.tsx?$/,
exclude: [/node_modules/],
use: [ 'babel-loader', 'ts-loader' ]
},
{
test: /\.sass$/,
use: [
{
loader: "style-loader" // creates style nodes from JS strings
},{
loader: "css-loader", options: { sourceMap: true } // translates CSS into CommonJS
},{
loader: "sass-loader", options: { sourceMap: true } // compiles Sass to CSS
}
]
}
]
},
devtool: "eval"
}
]
If I try to import jquery inside layoutA/app.ts file (webpack root), it works fine. Since the ajax.ts lives outside this folder, which is the best way to import libraries like jquery, lodash etc. in these files?
The following points must be observed for the best way to load js libraries in your context:
Install every js library (e.g. jquery) with a package manager like npm
To each library it needs a TypeScript definitions file (e.g. #types/jquery, to find under npmjs.com)
Install this TypeScript definition file also with npm
Note every TypeScript definition in the tsconfig.json under "files" like
"files":[
"node_modules/#types/jquery/index.d.ts",
"node_modules/#types/requirejs/index.d.ts",
]
Do this compellingly (point 1-4) with the library requirejs. This is a js file and module loader.
Now you are ready to load the js library in the TypeScript main file like:
require(["jquery", "urijs"], function($, uri) {
// your code
});
Other notes:
In the index.html file: reference under the script tags only the js bundle files, builded by e.g. webpack.
Webpack needs a TypeScript loader.
Reference exported TypeScript classes in the tsconfig.json file under 'files' and also in the TypeScript file like:
import {Car} from "./classes/Car";
Hope it helps you, to get a proper structur!
Supplement:
Try the following: reference a js library in your ayax.ts like:
private example = require("./[path to npm library]/node_modules/test/src/test.js");
If you call the library name like 'test' in the require command, then its not possible to resolve 'test'. It try to resolve over the package.json and can not find it because its outside of the root.

Karma unit test for ES6 modules with babel

I'm following instructions from karma-babel-preprocessor to set up unit tests in a project I'm currently working, but I always the error
'require is not defined'
My karma.conf.js is as follows:
files: [
{ pattern: './test/unit/*.spec.js', watched: true },
{ pattern: './src/js/es6_modules/*.js', watched: false },
],
preprocessors: {
'./src/js/es6_modules/*.js': ['babel'],
'./test/unit/*.spec.js': ['babel'] //, 'coverage'
},
babelPreprocessor: {
options: {
presets: ['es2015'],
sourceMap: 'inline'
},
filename: function (file) {
return file.originalPath.replace(/\.js$/, '.es5.js');
},
sourceFileName: function (file) {
return file.originalPath;
}
}
The scripts in src/js/es6_modules jave ES6 classes exported. Something like:
export default class MyClass {
}
And my spec file would need to import this
import { MyClass } from "../../src/js/es6_modules/myclass";
I have seen some thread here at SO that said I would need to use browserify, but I can't find any doc (or example) on how to use it together with babel in karma. Does anyone know to configure this properly?
I fixed it using browserify instead of babel, and using a babelify transform
preprocessors: {
'./src/js/es6_modules/*.js': ['browserify'],
'./test/unit/*.spec.js': ['browserify']
},
browserify: {
debug: true,
"transform": [
[
"babelify",
{
presets: ["es2015"]
}
]
]
},

How to expose an es6 module globally

I need to write a module that will be available on the window global.
I'm using es6 to create the module and every single class I define has it's own file.
I'm using webpack to babelify and bundle these classes.
The entry point of my module is also the file containing the global to be exposed.
I've tried every method to make this possibe, icluding:
expose-loader
import-loader
expoert-loader
output: library
black-magic :(
Example of code I've tried:
I want to get: window.MyMod
// mymod.js
export class MyMod {
constructor(aaa) {
this.aaa = aaa;
}
toString() {
return this.aaa;
}
}
// webpack.config
var entries = [
'./src/mymod.js'
];
module.exports = {
...,
module: {
loaders: [
{
test: require.resolve('./src/mymod.js'),
loader: 'expose?MyMod'
},
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
query: {
presets: ['es2015']
}
}
]
}
This only gets me an object MyMod on the window that contains MyMod as a constructor.
Any help will be appreciated.
You should combine export default class Foo with the library and libraryTarget settings in Webpack's config. Something like:
// src/Foo.js
export default class Foo { ... }
// webpack.config.json
{
"output": {
"library": "Foo",
"libraryTarget": "var"
}
}
You should be able to use the library as window.Foo once the bundle has been loaded.
This is basically the same issue as Exporting a class with Webpack and Babel not working , except that you have a named export instead of a default export. Your entry file should be
import {MyMod} from './mymod';
module.exports = MyMod;
or
module.exports = require('./mymod').MyMod;
If you don't want to do any of these and keep './src/mymod.js' as entry file, use a CommonJS export instead of an ES6 export in that file:
// mymod.js
exports.MyMod = class MyMod {
constructor(aaa) {
this.aaa = aaa;
}
toString() {
return this.aaa;
}
}

using ES6 npm module (webpack) in the front end

I'm making an npm module using ES6 syntax (imports, exports etc) via webpack.
When I try to initialize an instance of the exported class in the frontend to test it, it isn't working and I've got a feeling it's to do with what CommonJS exports return vs ES6 exports.
example.js
export default class Example { ....
example-after-webpack-stuff.js
var Example = (function() ....
demo.js
var example = new Example();
demo.html
<script src="../example-after-webpack-stuff.js"></script>
<script src="demo.js"></script>
I receive the following:
Error: Uncaught TypeError: Example is not a function
EDIT
Webpack config:
module.exports = {
entry: './src/example.js',
output: {
filename: "./dist/example.js",
library: "Example",
libraryTarget: "var"
},
module: {
loaders: [
{
exclude: /(node_modules|bower_components)/,
loader: 'babel',
query: {
presets: ['es2015']
}
}
]
}
};
Since babel 6, export default class Example will be compiled to exports.default = Example. While in Babel 5, it will be compiled to exports = Example. So you code will run without error using babel 5.
In babel 6, you can use CommonJS way module.exports:
class Example {
constructor() {
...
}
}
module.exports = Example;
Or you can use babel-plugin-add-module-exports to change babel 6's behavior.
npm install babel-plugin-add-module-exports --save-dev
Add it in webpack.config.js:
loaders: [
{
exclude: /(node_modules|bower_components)/,
loader: 'babel',
query: {
presets: ['es2015'],
plugins: ['add-module-exports']
}
}
]

Categories