Giter VIP home page Giter VIP logo

babel-plugin-extensible-destructuring's Introduction

babel-plugin-extensible-destructuring

Babel plugin that enables extensible destructuring, inspired by vacuumlabs/es-proposals.

Circle CI Join the chat at https://gitter.im/vacuumlabs/babel-destructuring

Install

npm install --save-dev babel-plugin-extensible-destructuring
npm install --save extensible-runtime

Usage (for version 4.x.x and Babel 6)

Add the plugin to your .babelrc configuration:

{
  "presets": ["es2015"],
  "plugins": ["extensible-destructuring"]
}

Or directly from code:

var babel = require("babel-core");
babel.transform("code", {
    presets: ['es2015'],
    plugins: ['extensible-destructuring']
})

Usage for babel 7

Add the plugin to your .babelrc configuration using the full name:

{
  "presets": ["@babel/preset-env"],
  "plugins": ["babel-plugin-extensible-destructuring"]
}

What it does

The plugin gives you more explicit control of what exactly happens in the process of destructuring. The plugin transforms all destructuring assignments such as:

var {a = 10} = o

to:

var a = __extensible_get__(o, a, 10)

Function __extensible_get__ gets automatically required from extensible-runtime package (this is a separate package and needs to be installed alongside this one. This package currently defines three different versions of __extensible_get__ you can choose from using the impl plugin option (see the section Plugin Options below).

  • normal: standard ES6 compatible: no magic here
  • immutable: the one that you can use with Immutable.js to destructure its Maps and Lists
  • safe: prevent returning values from being undefined. Also includes features of immutable

Option safe is the default one. Typically, there is no reason to use this plugin with normal option.

Check out the example folder for a working example; this is a standalone npm package with all the configuration necessary.

Plugin Options

In case you don't know, babel allows to specify options for each plugin. The syntax looks such as:

{
  "presets": ["es2015"],
  "plugins": [["extensible-destructuring", {"mode": "optout", "impl": "safe"}]]
}

Semantics of these options is:

mode

either 'optin' or 'optout'. In optin (default) mode, the destructuring assignments are transformed only if "use extensible" directive is present at the beginning of the file. OTOH, in optout mode the destructuring assignments are transformed unless you use "use !extensible" directive at the beginning of the file. You can change (default) optin in your .babelrc (note that plugins now becomes nested array)

This machinery is cool for already existing bigger projects in which you may want to start using safe mode gradually: You can use safe destructuring, but optin only those files, that are already written with no-undefined-policy in mind.

impl

Either 'normal', 'immutable' or 'safe'; specifies what version of __extensible_get__ to use.

Immutable destructuring example

Use .babelrc such as:

{
  "presets": ["es2015"],
  "plugins": [["extensible-destructuring", {"mode": "optout", "impl": "immutable"}]]
}

This code works as expected:

import {fromJS} from 'immutable';
const map = fromJS({author: {name: {first: "John", last: "Doe"}, birthdate: "10-10-2010"}});
const {author: {name: {first, last}, birthdate}} = map;

Safe destructuring example

Use .babelrc such as:

{
  "presets": ["es2015"],
  "plugins": [["extensible-destructuring", {"mode": "optout", "impl": "safe"}]]
}

Now, the code:

import {fromJS} from 'immutable';
const map = {a: 10, b: 20}
const {a, b, c} = map;

logs the error to the console:

Error: Key Error: object with keys [ "a", "b" ] does not contain property c

On the other hand

const {a, b, c = null} = map;

is perfectly OK.

Differences from version 3

  • no need to patch your code at the entry-point, no need to define global __extensible_get__. We see this as a bad practice and this was the most relevant reason for doing version 4.

  • option default was renamed to normal (default is sort-of reserved keyword when dealing with ES6 import/export)

  • 'polyfill' renamed to 'runtime', which is much more accurate naming

Using your own implementation of __extensible_get__

Although we believe the existing implementations of __extensible_get__ are quite sufficient, you still might want to use your own. In such a case:

In the .babelrc, set {impl: "mymyget"}, mymyget being the name of your newly created resolver. Then in the entry-point of your code you monkey-patch it inside the package (yes, this makes it accessible also for other parts of your project):

var extensibleRuntime = require('extensible-runtime');
const mymyget = () => ...;
extensibleRuntime.mymyget = mymyget;

Under The Hood

The plugin uses global __extensible_get__ function to destructure the assignment. The number of calls to __extensible_get__ is minimized, so if one writes:

const {a: {b: {c, d, e}}} = map;

plugin uses the unique temporary variable to get result such as:

var __extensible_get__ = require('extensible-runtime').safe
var _tmp = __extensible_get__(__extensible_get__(map, "a"), "b");
var c = __extensible_get__(_tmp, "c");
var d = __extensible_get__(_tmp, "d");
var e = __extensible_get__(_tmp, "e");

babel-plugin-extensible-destructuring's People

Contributors

cooperka avatar dagstuan avatar datayja avatar davidpricedev avatar gitter-badger avatar hleumas avatar palnes avatar patrik-novak avatar tomaskulich avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

babel-plugin-extensible-destructuring's Issues

Support for Babel 7

Hello,

The plugin doesnt currently seem to support babel 7, since it should be going stable soon maybe this plugin should implement support for it?

Currently I'm getting errors such as this one in the console when trying to build with babel 7:

Module build failed: TypeError: Cannot read property 'name' of undefined
    at DestructuringTransformer.pushObjectProperty (C:\...\node_modules\babel-plugin-extensible-destructuring\lib\index.js:198:96)
    at DestructuringTransformer.pushObjectPattern (C:\...\node_modules\babel-plugin-extensible-destructuring\lib\index.js:240:18)
    at DestructuringTransformer.push (C:\...\node_modules\babel-plugin-extensible-destructuring\lib\index.js:145:16)
...

Using a variable in the destructuring is not working for immutables

Consider this example:

import {Iterable, fromJS} from 'immutable';
Iterable.prototype[Symbol.for('get')] = function(value) {return this.get(value) }

const t = fromJS({
    user: {
        0: {data: 'some data'}
    }
});

const id = 0;
const {user: {[id]: {data}}} = t;

This outputs a following error: Cannot read property 'Symbol(get)' of undefined

Destructuring computed property with default value uses string instead of computed property

When running extensible destructuring against the following:

const a = 0;
const { [a]: test = 'default' } = { 0: 'test' };

The variable 'test' evaluates to 'default', when this should be 'test'. Without this plugin, when just using babel-env, babel produces the correct expected output. This syntax is extremely helpful for handling when a computed property is not matched in the parent object.

Running babel produces the following syntax:

var __extensible_get__ = require('extensible-runtime').immutable;

var a = 0;
var _ = { 0: 'test' };

var test = __extensible_get__(_, 'a', 'default');

(Note, the string 'a' instead of variable a)
I was able to resolve this on my own codebase by change to line 214 of src/index.js to the following, however unsure if this will have unintended side effects.
objRef = extensibleGet(propRef, prop.computed ? prop.key : t.stringLiteral(prop.key.name), pattern.right)

Please upgrade to Babel 6

After upgrading to Babel 6 I get this error message:

/Users/sean/workspace/plumcake-1/node_modules/babel-core/lib/transformation/file/options/option-manager.js:180
throw new ReferenceError(messages.get("pluginUnknown", plugin, loc, i));
^

ReferenceError: Unknown plugin "extensible-destructuring:after" specified in "/Users/sean/workspace/plumcake-1/.babelrc" at 0
at /Users/sean/workspace/plumcake-1/node_modules/babel-core/lib/transformation/file/options/option-manager.js:180:17
at Array.map (native)
at Function.normalisePlugins (/Users/sean/workspace/plumcake-1/node_modules/babel-core/lib/transformation/file/options/option-manager.js:161:20)
at OptionManager.mergeOptions (/Users/sean/workspace/plumcake-1/node_modules/babel-core/lib/transformation/file/options/option-manager.js:254:36)
at OptionManager.addConfig (/Users/sean/workspace/plumcake-1/node_modules/babel-core/lib/transformation/file/options/option-manager.js:206:10)
at OptionManager.findConfigs (/Users/sean/workspace/plumcake-1/node_modules/babel-core/lib/transformation/file/options/option-manager.js:347:16)
at OptionManager.init (/Users/sean/workspace/plumcake-1/node_modules/babel-core/lib/transformation/file/options/option-manager.js:392:12)
at compile (/Users/sean/workspace/plumcake-1/node_modules/babel-core/lib/api/register/node.js:96:22)
at loader (/Users/sean/workspace/plumcake-1/node_modules/babel-core/lib/api/register/node.js:136:14)
at Object.require.extensions.(anonymous function) as .js

Babel 7 Support

Is there a plan to support Babel 7? I get errors when I upgrade to Babel 7 and try to continue to use this plugin.

'optout' mode breaks React Native

Issue

When using mode optin, everything works as expected.

When using mode optout (see commit), the app fails to start with the error Unhandled JS Exception: ReferenceError: Can't find variable: require. On my full app, I don't even see this error and instead it simply shows an empty white screen that never loads.

Unfortunately I don't have time right now to diagnose this in much detail, but I've created a demo project to test the behavior.

Demo

I have a demo project here showing this issue in action.

seems to break with `__dirname`?

/Users/mhertzberg/RedOwl/the-ui/client/config/webpack/index.js:58
  var ROOT_PATH = __extensible_get__(_ref, 'ROOT_PATH');
                  ^

ReferenceError: __extensible_get__ is not defined
const path = require('path')
...
const ROOT_PATH = __dirname

Is there any ways to use this plugin with Flowtype?

type Test = Map<string, any>

const test: Test = Immutable.Map({...})
const { a, b, c } = test

property 'a'. Property not found on Map.

type Test = {
  a: number,
  b: number,
  c: string,
}

const test: Test = Immutable.Map({...})
const a = test.get('a')

Flow: property 'get'. Property not found in object type

type Test = {
  get: Function,
  set: Function,
  a: number,
  b: number,
  c: string,
}
const test: Test = Immutable.Map({...})
const a = test.get('a')
const { b, c } = test

this is OK but bit strange for use. if i use getIn or merge, I have to define that function also.

Error When using with globally installed babel

> babel pokus.js 
/Users/samuel/biznis/clients/ext-dest/node_modules/babel-plugin-extensible-destructuring/lib/index.js:345
    return new Plugin("extensible-destructuring", {
           ^
TypeError: undefined is not a function
    at module.exports (/Users/samuel/biznis/clients/ext-dest/node_modules/babel-plugin-extensible-destructuring/lib/index.js:345:9)
    at Function.memoisePluginContainer (/usr/local/lib/node_modules/babel/node_modules/babel-core/lib/babel/transformation/file/plugin-manager.js:46:23)
    at PluginManager.add (/usr/local/lib/node_modules/babel/node_modules/babel-core/lib/babel/transformation/file/plugin-manager.js:130:30)
    at File.buildTransformers (/usr/local/lib/node_modules/babel/node_modules/babel-core/lib/babel/transformation/file/index.js:281:21)
    at new File (/usr/local/lib/node_modules/babel/node_modules/babel-core/lib/babel/transformation/file/index.js:148:10)
    at TransformerPipeline.transform (/usr/local/lib/node_modules/babel/node_modules/babel-core/lib/babel/transformation/transformer-pipeline.js:87:16)
    at Object.exports.transform (/usr/local/lib/node_modules/babel/bin/babel/util.js:33:22)
    at Object.exports.compile (/usr/local/lib/node_modules/babel/bin/babel/util.js:41:18)
    at /usr/local/lib/node_modules/babel/bin/babel/file.js:120:23
    at arrayEach (/usr/local/lib/node_modules/babel/node_modules/lodash/index.js:1314:13)

Not work: destruct function arguments with immutable objects

function read({a, b, c}) {
    console.info(a) //ppp
    console.info(b) //11
}
read({a: "ppp", b: 11})
function read({a, b, c}) {
    console.info(a) //undefined
    console.info(b) //undefined
}
read(fromJS({a: "ppp", b: 11}))

Now I have to destruct immutable objects inside the functions and it works prefectly:

function read(obj) {
   const {a,b} = obj
 ...
}

Unhandled JS Exception: Syntax Error

Couldn't make it work on React Native.

screenshot

{
  "presets": [
    "es2015",
    "react-native",
    "react-native-stage-0/decorator-support"
  ],
  "plugins": [
    "transform-es2015-parameters",
    ["extensible-destructuring", {"mode": "optout", "impl": "safe"}]
  ]
}

Default array value causes a compile error

const [a, b, c = DEFAULT_C] = myArray;

Setting a default value when destructuring an array triggers the following compile error:

ERROR in ./my/source.js
Module build failed: Error: /my/project/my/source.js: shouldnt get here, handling of AssignmentPattert is inlined into ObjectPattern
    at DestructuringTransformer.push (/my/project/node_modules/babel-plugin-extensible-destructuring/lib/index.js:148:17)
    at DestructuringTransformer.pushArrayPattern (/my/project/node_modules/babel-plugin-extensible-destructuring/lib/index.js:388:16)
    at DestructuringTransformer.push (/my/project/node_modules/babel-plugin-extensible-destructuring/lib/index.js:146:16)
    at DestructuringTransformer.init (/my/project/node_modules/babel-plugin-extensible-destructuring/lib/index.js:405:14)
    at PluginPass.VariableDeclaration (/my/project/node_modules/babel-plugin-extensible-destructuring/lib/index.js:627:27)
    at newFn (/my/project/node_modules/babel-traverse/lib/visitors.js:276:21)
    at NodePath._call (/my/project/node_modules/babel-traverse/lib/path/context.js:76:18)
    at NodePath.call (/my/project/node_modules/babel-traverse/lib/path/context.js:48:17)
    at NodePath.visit (/my/project/node_modules/babel-traverse/lib/path/context.js:105:12)
    at TraversalContext.visitQueue (/my/project/node_modules/babel-traverse/lib/context.js:150:16)
 @ ./my/other.js 41:19-43

Cannot read property 'name' of undefined

Hi,
Using 4.2.0 with [email protected] gave us following issue on our webpack build.
Noticed when I do clean npm install yesterday.
Using 4.1.0 won't cause any issue.
Any insight on this...?

webpack build error
`
ERROR in ./src/utils/xxxx.js
Module build failed: TypeError: /Users/myuser/myproject/src/utils/myreactcomponent.js: Cannot read property 'name' of undefined
at DestructuringTransformer.pushObjectProperty (/Users/myuser/myproject/node_modules/babel-plugin-extensible-destructuring/lib/index.js:209:96)
at DestructuringTransformer.pushObjectPattern (/Users/myuser/myproject/node_modules/babel-plugin-extensible-destructuring/lib/index.js:251:18)
at DestructuringTransformer.push (/Users/myuser/myproject/node_modules/babel-plugin-extensible-destructuring/lib/index.js:156:16)
at DestructuringTransformer.init (/Users/myuser/myproject/node_modules/babel-plugin-extensible-destructuring/lib/index.js:417:14)
at PluginPass.VariableDeclaration (/Users/myuser/myproject/node_modules/babel-plugin-extensible-destructuring/lib/index.js:639:27)
at newFn (/Users/myuser/myproject/node_modules/babel-traverse/lib/visitors.js:276:21)
at NodePath._call (/Users/myuser/myproject/node_modules/babel-traverse/lib/path/context.js:76:18)
at NodePath.call (/Users/myuser/myproject/node_modules/babel-traverse/lib/path/context.js:48:17)
at NodePath.visit (/Users/myuser/myproject/node_modules/babel-traverse/lib/path/context.js:105:12)
at TraversalContext.visitQueue (/Users/myuser/myproject/node_modules/babel-traverse/lib/context.js:150:16)
at TraversalContext.visitMultiple (/Users/myuser/myproject/node_modules/babel-traverse/lib/context.js:103:17)
at TraversalContext.visit (/Users/myuser/myproject/node_modules/babel-traverse/lib/context.js:190:19)
at Function.traverse.node (/Users/myuser/myproject/node_modules/babel-traverse/lib/index.js:114:17)
at NodePath.visit (/Users/myuser/myproject/node_modules/babel-traverse/lib/path/context.js:115:19)
at TraversalContext.visitQueue (/Users/myuser/myproject/node_modules/babel-traverse/lib/context.js:150:16)
at TraversalContext.visitSingle (/Users/myuser/myproject/node_modules/babel-traverse/lib/context.js:108:19)
at TraversalContext.visit (/Users/myuser/myproject/node_modules/babel-traverse/lib/context.js:192:19)
at Function.traverse.node (/Users/myuser/myproject/node_modules/babel-traverse/lib/index.js:114:17)
...

`

Cannot destructure function properties

Take the following example:

import { fromJS } from 'immutable'

// Works as expected:
let { a, b: { c } } = fromJS({ a: 1, b: { c: 2 } })
console.log(a, c)

let barFn = () => {}
barFn.bar = 'baz'

// Works with plain JS, but fails with extensible-destructuring:
let { foo: { bar } } = { foo: barFn }
console.log(bar)

Destructuring a and c works fine, but trying to destructure bar throws Error: cannot resolve property bar in object of type function (function barFn() {}). Seems to be an easy fix by just changing the logic here: https://github.com/vacuumlabs/babel-plugin-extensible-destructuring/blob/master/runtime/src/runtime.js#L32-L34

I'll try to make a PR soon.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.