webreflection / babel-plugin-transform-builtin-classes Goto Github PK
View Code? Open in Web Editor NEWA fix for the infamous Babel #4480 bug.
Home Page: https://github.com/babel/babel/issues/4480
License: ISC License
A fix for the infamous Babel #4480 bug.
Home Page: https://github.com/babel/babel/issues/4480
License: ISC License
Is there a reason not to provide a default set of builtin globals to check for? For example, Array
and HTMLElement
seem like a good defaults.
Just opening this as a note, but I understand you might not have interest in implementing this. It'd probably take some complicated static analysis.
Another option could be to allow users to explicitly tell the plugin by writing a comment:
function SomeMixin(base) {
// @transform-builtin-classes extends HTMLElement
return class Foo extends base { ... }
}
then the plugin could pick up on this cue, and it is better (IMO) than specifying a list in babelrc.
.babelrc
{
"presets": [
"es2015"
],
"plugins": [
"babel-plugin-transform-es2015-classes",
[
"babel-plugin-transform-builtin-classes",
{
"globals": [
"Array",
"Error",
"HTMLElement",
"HTMLButtonElement"
]
}
]
]
}
packages
"babel-cli": "^6.26.0",
"babel-core": "^6.26.3",
"babel-plugin-transform-builtin-classes": "^0.6.1",
"babel-plugin-transform-es2015-classes": "^6.24.1",
"babel-preset-env": "^1.7.0",
"babel-preset-es2015": "^6.24.1",`
input
// finally fixed
class List extends Array {}
console.assert(new List instanceof List);
// finally fixed too
class MyElement extends HTMLElement {
connectedCallback() {
this.textContent = 'hello';
}
}
customElements.define('my-element', MyElement);
const node = new MyElement;
console.assert(node.constructor === MyElement);
console.assert(node instanceof MyElement);
console.assert(node instanceof HTMLElement);
document.body.appendChild(node);`
Output
'use strict';
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
// finally fixed
var List = function (_Array) {
_inherits(List, _Array);
function List() {
_classCallCheck(this, List);
return _possibleConstructorReturn(this, (List.__proto__ || Object.getPrototypeOf(List)).apply(this, arguments));
}
return List;
}(Array);
console.assert(new List() instanceof List);
// finally fixed too
var MyElement = function (_HTMLElement) {
_inherits(MyElement, _HTMLElement);
function MyElement() {
_classCallCheck(this, MyElement);
return _possibleConstructorReturn(this, (MyElement.__proto__ || Object.getPrototypeOf(MyElement)).apply(this, arguments));
}
_createClass(MyElement, [{
key: 'connectedCallback',
value: function connectedCallback() {
this.textContent = 'hello';
}
}]);
return MyElement;
}(HTMLElement);
customElements.define('my-element', MyElement);
var node = new MyElement();
console.assert(node.constructor === MyElement);
console.assert(node instanceof MyElement);
console.assert(node instanceof HTMLElement);
document.body.appendChild(node);
Assertion failed: console.assert
(anonymous) @ customElementES5.js:24
customElementES5.js:34 Uncaught TypeError: Failed to construct 'HTMLElement': Please use the 'new' operator, this DOM object constructor cannot be called as a function.
at new MyElement (customElementES5.js:34)
at customElementES5.js:49
MyElement @ customElementES5.js:34
(anonymous) @ customElementES5.js:49
export default
function WebComponent(base) {
base = base || HTMLElement
return class WebComponent extends base { ... }
}
A user might pass anything in: HTMLImageElement
, HTMLButtonElement
, or some class extending from a native one, etc.
Will this still work?
I'm trying to use this by adding a .babelrc file to my Meteor project:
{
"plugins": [
["transform-builtin-classes", {
"globals": ["HTMLElement"]
}]
]
}
Meteor's Babel setup uses "@babel/plugin-transform-classes": "^7.0.0-beta.39",
.
But, nothing happens. My Custom Elements still have the error I'm trying to avoid when HTMLElement
is not called as new HTMLElement
.
Maybe babel-plugin-transform-builtin-classes
isn't working with the new @babel/plugin-transform-classes
plugin, which is no longer named babel-plugin-transform-es2015-classes
?
My HTMLElement subclasses are not being wrapped with your helper function, so I tore my project down to the basics. One thing I do not understand is why the plugin doesn't seem to work from the command line.
For example, if I run the following from the root of this project, I do not generate a wrapped HTMLElement.
babel test/cool-element.js
Hey, thanks for this plugin!
I think now that Babel 7 is out, this plugin might need to be changed to support it? Currently the error I'm seeing is:
ERROR in ./test.js
Module build failed (from ../node_modules/babel-loader/lib/index.js):
TypeError: path.inShadow is not a function
at ReplaceSupers.ReturnStatement (/Users/owenbuckley/Workspace/project-evergreen/repos/todo-app/node_modules/babel-helper-replace-supers/lib/index.js:55:15)
at NodePath._call (/Users/owenbuckley/Workspace/project-evergreen/repos/todo-app/node_modules/@babel/traverse/lib/path/context.js:53:20)
at NodePath.call (/Users/owenbuckley/Workspace/project-evergreen/repos/todo-app/node_modules/@babel/traverse/lib/path/context.js:40:17)
at NodePath.visit (/Users/owenbuckley/Workspace/project-evergreen/repos/todo-app/node_modules/@babel/traverse/lib/path/context.js:88:12)
at TraversalContext.visitQueue (/Users/owenbuckley/Workspace/project-evergreen/repos/todo-app/node_modules/@babel/traverse/lib/context.js:118:16)
at TraversalContext.visitMultiple (/Users/owenbuckley/Workspace/project-evergreen/repos/todo-app/node_modules/@babel/traverse/lib/context.js:85:17)
at TraversalContext.visit (/Users/owenbuckley/Workspace/project-evergreen/repos/todo-app/node_modules/@babel/traverse/lib/context.js:144:19)
at Function.traverse.node (/Users/owenbuckley/Workspace/project-evergreen/repos/todo-app/node_modules/@babel/traverse/lib/index.js:94:17)
at NodePath.visit (/Users/owenbuckley/Workspace/project-evergreen/repos/todo-app/node_modules/@babel/traverse/lib/path/context.js:95:18)
at TraversalContext.visitQueue (/Users/owenbuckley/Workspace/project-evergreen/repos/todo-app/node_modules/@babel/traverse/lib/context.js:118:16)
@ multi ../node_modules/webpack-dev-server/client?http://localhost:1981 ./test test[1]
Using .babelrc in conjunection with webpack + babel-loader
{
"presets": ["@babel/preset-env"],
"plugins": [
"babel-plugin-transform-es2015-classes",
["babel-plugin-transform-builtin-classes", {
"globals": ["Array", "Error", "HTMLElement"]
}]
]
}
Thanks and let me know if you need anymore info!
Hello! This plugin looks like what I've always wanted.
I'm having some issues however.
Custom elements fail to initiate in IE11 with this error:
Working fine in Chrome 59, Firefox 54, Edge 38, and Safari 10
The JS was compiled using gulp-babel
with settings:
const gulp = require('gulp')
const babel = require('gulp-babel')
const concat = require('gulp-concat')
gulp.task('default', () => {
return gulp.src('source/js/**/*.js')
.pipe(concat('app.js'))
.pipe(babel({
presets: [
'es2015'
],
plugins: [
['transform-builtin-classes', {
'globals': ['Array', 'Error', 'HTMLElement']
}]
]
}))
.pipe(gulp.dest('./build/js'))
})
Bare bones HTML and JS for testing:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
</head>
<body>
<script src="https://cdn.rawgit.com/webcomponents/custom-elements/f27a9390/custom-elements.min.js" charset="utf-8"></script>
<script src="app.js" charset="utf-8"></script>
<cool-element></cool-element>
</body>
</html>
class CoolElement extends HTMLElement {
connectedCallback () {
this.innerText = 'Hello World'
}
}
customElements.define('cool-element', CoolElement)
Am I missing anything?
We use this plugin in production at our company.
But there seems to be a problem.
In Internet explorer, I can not get this to work.
If I do this:
class List extends Array {}
console.log(new List());
console.log(new List(1));
console.log(new List(1) instanceof List);
console.log(new List(1) instanceof Array);
I get the following:
To reproduce, I installed this virtual box on https://developer.microsoft.com/en-us/microsoft-edge/tools/vms/:
In the line return sPO(new Constructor, Class.prototype);
Constructor doesn't have "this" context as Class.prototype and that why document-register-element can't get constructor from "this" statement.
The different with Reflect in the context of call new Constructor
A very common pattern emerging when developing web components is the use of mixins. Whether using functional mixins or class based mixins, one does not typically code this way:
class MyElement extends HTMLElement
Instead, the mixin looks something like this:
import Composable from './composable'
class MyElement extends Composable(HTMLElement).compose( Mixin1, Mixin 2)
<composable.js>
export default ( Base ) => {
class Composable extends Base { ... }
return Composable
}
or
var MyElement = Mixin1(Mixin2(HTMLElement))
I'm not sure if the plugin can be written to work with these runtime semantics.
I have tried to create a subclass of HTMLElement
called FixedHTMLElement
which is passed in to Composable
.
class FixedHTMLElement extends HTMLElement {}
class MyElement extends Composable(FixedHTMLElement).compose( Mixin1, Mixin 2)
But this didn't work. Are there any approaches that may work with your plugin for these cases?
Hello, I just try this with Node.js 8.9.0, but it doesn't worked.
This is the code am I tried:
.babelrc
{
"presets": ["env", "flow"],
"plugins": [
"transform-flow-strip-types",
"babel-plugin-transform-es2015-classes",
["babel-plugin-transform-builtin-classes", {
"globals": ["Error"],
"logIfPatched": true
}]
]
}
exceptions.js
/* @flow */
class BadRequestError extends Error {
constructor(message:string) {
super(message);
}
}
class InvalidRequestBodyError extends Error {
constructor(message:string) {
super(message);
}
}
class UnauthorizedError extends Error {
constructor(message:string) {
super(message);
}
}
class ExpiredResourceError extends Error {
constructor(message:string) {
super(message);
}
}
class InternalServerError extends Error {
constructor(message:string) {
super(message);
}
}
class ResourceNotFoundError extends Error {
constructor(message:string) {
super(message);
}
}
module.exports = {
BadRequestError,
InvalidRequestBodyError,
UnauthorizedError,
ExpiredResourceError,
InternalServerError,
ResourceNotFoundError
};
index.js
/**
* Return proper status code
* @param {Error} error
*/
function getStatusCode(error:Error):number {
console.log(error instanceof ResourceNotFoundError); // Returns false here!!
if(error instanceof InvalidRequestBodyError)
return 400;
else if(error instanceof BadRequestError)
return 400;
else if(error instanceof UnauthorizedError)
return 401;
else if(error instanceof ResourceNotFoundError)
return 404;
else if(error instanceof ExpiredResourceError)
return 410;
return 500;
}
Using [email protected] and [email protected]
I think I see: I need this transform in case document-register-element
isn't loaded, because in that case the transpiled code won't work with native CE v1 even if we had followed the caveat workarounds. Is this right?
It may be helpful for people to have the following details written somewhere:
It's been hard to piece everything together by trial and error over the course of a few days and lots of fiddling, so basically I may have forgotten some details, and you probably know it all best.
Hey!
The plugin only injects the necessary _fixBabelExtend
function on "first" compile of the source. Meaning that if the node
thread is kept alive, using a 'watch'-command or other, the result is not as expected when the code is recompiled upon save. Assuming some fixIsInjected
-boolean kept in memory that never resets?
I have this happening when using gulp-watch
to trigger the task, and when just using the --watch
flag with babel-cli
.
Example package.json
:
{
"dependencies": {
"babel-plugin-transform-builtin-classes": "^0.3.2",
"babel-preset-es2015": "^6.24.1"
},
"babel": {
"presets": [
"es2015"
],
"plugins": [
["transform-builtin-classes", {
"globals": [
"HTMLElement"
]
}]
]
}
}
Example test.js:
class TestComponent extends HTMLElement {
connectedCallback () {
// Hi
}
}
Example cli command:
babel test.js -o out.js --watch
To get around this issue, I'm having to spawn new threads for each compile.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.