Object.assign is not a function - javascript

I'm using babel with gulp and create a simple DOM library in ES6. But after running and when i'm going to use it, I got the Object.assign is not a function in chrome console.
this is the gulp code
gulp.task('scripts', function() {
return gulp.src(src + 'js/*.js')
.pipe(babel())
.pipe(concat('main.js'))
.pipe(gulp.dest(dest + 'js'));
});
this is the class file
class DOM {
constructor( selector ) {
var elements = document.querySelectorAll(selector);
this.length = elements.length;
Object.assign(this, elements);
}
...
}
const dom = selector => new DOM(selector);
and I'm using it in client side like dom('#elId');

As I suspect you already know, Google Chrome uses V8, which supports ECMAScript 5th edition. Object.assign is introduced in ECMAScript 6th edition.
In order to use these additions, you need to include the ES6 polyfill provided by Babel:
This will emulate a full ES6 environment. [...]
Available from the browser-polyfill.js file within a babel-core npm release. This needs to be included before all your compiled Babel code. You can either prepend it to your compiled code or include it in a <script> before it.

Install babel-core:
$ npm install babel-core --save-dev
Import polyfill module into your js:
import 'babel-core/polyfill';
Use babel to compile your code!

Related

Why am I getting ".at" is not a function?

I know how to index an array with [] but I'm trying to display an item from an array based on its index using Array.at() method as described here MDN Docs - Array.at
But I get the following error:
Uncaught TypeError: arr1.at is not a function
I double-checked it, and everything is ok, however I don't know what's going wrong.
Here is my code:
const arr1 = [10, 20, 30, 40, 50];
const res = arr1.at(2);
console.log(res);
Note: This is different than the proposed duplicate of How to get value at a specific index of array In JavaScript?. That question is about methods for accessing an array, this question is why a new API for doing so is unavailable and how to rectify that.
If you get this message, whatever platform you're running the code on does not support the method yet. It's quite new - while the most recent versions of most browsers support it, anything before 2021 definitely won't. This method was only very recently signed off on (end of August 2021) and incorporated into the official specification, so it won't exist in older environments. Either upgrade your environment, or add a polyfill.
Per the proposal document, a "rough polyfill" that should be standards-compliant for most cases is:
function at(n) {
// ToInteger() abstract op
n = Math.trunc(n) || 0;
// Allow negative indexing from the end
if (n < 0) n += this.length;
// OOB access is guaranteed to return undefined
if (n < 0 || n >= this.length) return undefined;
// Otherwise, this is just normal property access
return this[n];
}
const TypedArray = Reflect.getPrototypeOf(Int8Array);
for (const C of [Array, String, TypedArray]) {
Object.defineProperty(C.prototype, "at",
{ value: at,
writable: true,
enumerable: false,
configurable: true });
}
Simply run that before trying to use .at, and you should be able to use it, even on older incompatible environments. You can also install this more exhaustive shim instead if you wish.
One option is to use the core-js library...
Modular standard library for JavaScript. Includes polyfills for
ECMAScript up to 2021: promises, symbols, collections, iterators,
typed arrays, many other features, ECMAScript proposals, some
cross-platform WHATWG / W3C features and proposals like URL. You can
load only required features or use it without global namespace
pollution.
First install it with npm or yarn:
npm install core-js
or
yarn add core-js
Then use it in your JS or TS project like this:
import 'core-js/features/array/at';
let arr = [];
arr.push(42);
console.log(arr.at(0));
Note that the code above only imports the at method for array. To import all polyfills or a subset:
// polyfill all `core-js` features, including early-stage proposals:
import "core-js";
// or:
import "core-js/features";
// polyfill all actual features - stable ES, web standards and stage 3 ES proposals:
import "core-js/actual";
// polyfill only stable features - ES and web standards:
import "core-js/stable";
// polyfill only stable ES features:
import "core-js/es";
If you're using node.js then please check the version of node.
It should be greater than 16.6.0. If not then update the node.js version.
Browser compatibility of Array.prototype.at()
The code is working fine in Stackoverflow code terminal. Your machine might not support the same due to JS versioning. The method is introduced recently.
I ran into this same issue when using Pipedream. I didn't realize that at() wasn't included in the NodeJS version they're using hinted at by Li Ki.
I implemented a similar solution proposed by Bjørnar Hvidsten; however, to get it to work in Pipedream, I simply added the following to the top of my NodeJS code.
import 'core-js'
const myArray = [1,2,3]
console.log(typeof myArray.at) // "function"
P.S. Referencing specific parts of core-js resulted in errors inside Pipedream, at least how I was trying to call it.

Unable to minify Vue.js application

I have a Vue.js Application with the following excerpt of code:
(function() {
initApp();
})();
function initApp() {
window.myApp = new Vue({
el: '#wrapper',
data() {
return {
somedata: []
}
}
});
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.min.js"></script>
When I try to minify it, it fails with the error Error : Unexpected token: punc (() but the application runs successfully. I'm not sure why?
Those compressors simply only support an old version of JavaScript. Their support is restricted to at most ES5. To make your code work, convert it:
(function() {
initApp();
})();
function initApp() {
window.myApp = new Vue({
el: '#wrapper',
data: function() { // changed this line
return {
somedata: []
}
}
});
}
And it should compress.
Details:
They use uglify-js: "^3.3.10", that is known for not supporting ES6(uglify-es does) .
From their repo (emphasis mine):
UglifyJS 3
UglifyJS is a JavaScript parser, minifier, compressor and beautifier
toolkit.
Note:
(...)
uglify-js only supports JavaScript (ECMAScript 5).
To minify ECMAScript 2015 or above, transpile using tools like Babel.
Your compressor isn’t ES6 compliant
You’re getting that error because, like pacdcjunior's said, your compressor isn’t ES6 compliant. (I got your same error when I switched from jQuery to Vue.js—using ES6 syntax.)
Solution: Use Terser instead.
It’s ES6 compliant, in active development (at the time of writing), and is a direct replacement for Uglifyjs.
Bonus: How to minify lots of files in one pass with Terser
You can minify a single file in Terser from the command line like this:
$ terser file.js -m -o file.min.js
But if you have a load of files to minify in one go, that will be tedious. I found this excellent answer and modified it just a little. Add this to your .bash_profile:
alias renderjs='rm *.min.js; for f in *.js; do short=${f%.js}; terser $f -m -o $short.min.js; done'
Navigate to your js directory and run renderjs. Now all your *.js files have a minified version. Nice!
Do you mean the compiled js file (app.js)? If that case you just compile for production "npm run production".

How to use uglify-es with gulp?

I'm using gulp to uglify my files and it works fine for older Javascript.
There has already been a question about how to uglify an ES6-javascript file: how to uglify javascript classes?
That's because my code does not work (classes are ES5 or smth):
gulp.task('handleJs', () => {
gulp.src('src/frontend/xy/js/file.js')
.pipe(uglify());
}
The answer doesn't seem to be up-to-date anymore because uglify-js-harmony is deprecated (https://www.npmjs.com/package/uglify-js-harmony).
It says, I should use uglify-es (not -js) but I don't find a solution, how to use it with gulp? Which npm-packages do I really need for that and how does the code have to look like?
This is documented to a degree in the README for gulp-uglify. For clarity, I've slightly modified the example there to match your snippet.
You need to npm install the pump, gulp-uglify, and uglify-es packages first (along with any other packages your project needs. Then, you can setup your gulp task similar to as follows:
var uglifyjs = require('uglify-es');
var composer = require('gulp-uglify/composer');
var pump = require('pump');
var minify = composer(uglifyjs, console);
gulp.task('handleJs', function (cb) {
var options = {};
pump([
gulp.src('src/frontend/xy/js/file.js'),
minify(options),
gulp.dest('dist')
],
cb
);
});

Babel does not convert ES6 to JavaScript that is understandable by browsers

I use Gulp as task manager and Babel for convert my ES6 program to a version that is understandable for browsers, not for Node!
const gulp = require('gulp');
const babel = require('gulp-babel');
gulp.task('default', () =>
gulp.src('src/app.js')
.pipe(babel({
presets: ['es2015']
}))
.pipe(gulp.dest('dist'))
);
and in src/app.js I have:
import { square, diag } from 'lib';
//Some code here....
But, when I run the gulp, it make a file in dist but it converts import which is in app.js file to require keyword that is not understandable for browsers... I thought bable will merge imported files in src/app.js to one file in dist
How can I convert my library code to be supported by browsers using Babel?
Babel's job is to transpile. Combining and minifying scripts is a separate task.
You need to add Browserify, Webpack, RequireJS, or similar, and then tell Babel that's what you're using so it emits code to use those. Some further information on the setup page.
Basicly Babeljs converts your ES6 to ES5, Imagine you have an ES6 code:
const secret = ({ msg = 'es 6 hey!'} = {}) => () => msg;
The above code is in ES6, and Babeljs will help you to convert it to ES5:
'use strict';
var secret = function secret() {
var _ref = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
var _ref$msg = _ref.msg;
var msg = _ref$msg === undefined ? 'es 6 hey!' : _ref$msg;
return function () {
return msg;
};
};
And require is not a [current] JavaScript standard, you need a 3rd-party library that also have a function named require, libraries like requirejs, browserify, etc.
Even, if you have an ES6 program that includes any export or import keywords when you are working with Babili (online ES6 to ES5 converter) that will notify you that those converted keywords are not supported by browsers:
require is not supported in the browser, you need a commonjs
environment such as node.js/io.js, browserify/webpack etc
Online demo

Import existing AMD module into ES6 module

I have an existing application where I have AMD modules defined using RequireJS. I use "text" and "i18n" plugins for requirejs extensively in my project.
I have been experimenting with ES6 modules lately and would like to use them while creating new modules in my application. However, I want to reuse the existing AMD modules and import them while defining my ES6 modules.
Is this even possible? I know Traceur and Babel can create AMD modules from ES6 modules, but that only works for new modules with no dependency on existing AMD modules, but I could not find an example of reusing the existing AMD modules.
Any help will be appreciated. This is a blocker for me right now to start using all ES6 goodies.
Thanks
Yes, it can be done. Create a new application with the following structure:
gulpfile.js
index.html
js/foo.js
js/main.es6
node_modules
Install gulp and gulp-babel. (I prefer to install gulp locally but you may want it globally: that's up to you.)
index.html:
<!DOCTYPE html>
<html>
<head>
<title>Something</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.1.20/require.js"></script>
<script>
require.config({
baseUrl: "js",
deps: ["main"]
});
</script>
</head>
<body>
</body>
</html>
gulpfile.js:
"use strict";
var gulp = require('gulp');
var babel = require('gulp-babel');
gulp.task("copy", function () {
return gulp.src(["./js/**/*.js", "./index.html"], { base: '.' })
.pipe(gulp.dest("build"));
});
gulp.task("compile-es6", function () {
return gulp.src("js/**/*.es6")
.pipe(babel({"modules": "amd"}))
.pipe(gulp.dest("build/js"));
});
gulp.task("default", ["copy", "compile-es6"]);
js/foo.js:
define(function () {
return {
"foo": "the value of the foo field on module foo."
};
});
js/main.es6:
import foo from "foo";
console.log("in main: ", foo.foo);
After you've run gulp to build the application, open the file build/index.html in your browser. You'll see on the console:
in main: the value of the foo field on module foo.
The ES6 module main was able to load the AMD module foo and use the exported value. It would also be possible to have a native-AMD module load an ES6 module that has been converted to AMD. Once Babel has done its work, they are all AMD modules as far as an AMD loader is concerned.
In addition to #Louis's answer, assuming you already have a bunch of third party libraries specified in require.js configuration, in your new ES6 modules, whenever you are importing a module, be it amd or es6, you'll be fine as long as you keep the imported module name consistent. For example:
Here is the gulpfile:
gulp.task("es6", function () {
return gulp.src("modules/newFolder//es6/*.js")
.pipe(babel({
"presets": ["es2015"],
"plugins": ["transform-es2015-modules-amd"]
// don't forget to install this plugin
}))
.pipe(gulp.dest("modules/newFolder/build"));
});
Here is the es6 file:
import d3 from 'd3';
import myFunc from 'modules/newFolder/es6module'
// ...
This will be compiled to sth like this:
define(['d3', 'modules/newFolder/es6module'], function (_d, _myFunc) {
'use strict';
// ...
});
as long as the module in define(['d3', 'modules/newFolder/es6module'], ... of the compiled file is fine in a original AMD file, it should work with under existing require.js setup, such as compress files etc.
In terms of #coderC's question about require.js loaders, I was using i18n!nls/lang in AMD modules, at first I thought it would be a really tricky thing to find an alternative of AMD plugin loaders in ES6 modules, and I switched to other localization tools such as i18next. But it turned out that it's okay to do this:
import lang from 'i18n!nls/lang';
// import other modules..
because it will be compiled by gulp task to sth like:
define(['d3', 'i18n!nls/lang'], function (_d, _lang) {
// ....
This way, we don't have to worry about the require.js loader.
In a nutshell, in ES6 modules, if you want to use existing AMD plugin/modules, you just need to ensure the compiled file is conformed with the existing setup. Additionally, you can also try the ES6 module bundler Rollup to bundle all the new ES6 files.
Hope this can be helpful for those who are trying to integrate ES6 syntax in project.
A few changes for the latest version of babel:
First, babel({"modules": "amd"}) doesn't work with the latest version of babel. Instead, use babel({"plugins": ["#babel/plugin-transform-modules-amd"]}). (You'll need to install that plugin as a separate module in npm, i.e. with npm install --save-dev #babel/plugin-transform-modules-amd.)
Second, the syntax for gulp.task no longer accepts arrays as its second argument. Instead, use gulp.parallel or gulp.series to create a compound task.
Your gulpfile will end up looking like this:
"use strict";
var gulp = require('gulp');
var babel = require('gulp-babel');
gulp.task("copy", function () {
return gulp.src(["./js/**/*.js", "./index.html"], { base: '.' })
.pipe(gulp.dest("build"));
});
gulp.task("compile-es6", function () {
return gulp.src("js/**/*.es6")
.pipe(babel({"plugins": ["#babel/plugin-transform-modules-amd"]}))
.pipe(gulp.dest("build/js"));
});
gulp.task("default", gulp.parallel("copy", "compile-es6"));

Categories