From edc9fa3dc09ac468e4759d76235a543069d244e0 Mon Sep 17 00:00:00 2001 From: unknown Date: Tue, 11 Aug 2015 18:34:23 +0300 Subject: [PATCH 1/2] Removed the usage of babel/polyfill, and instead used runtime. As recommended by babel. --- Gulpfile.js | 2 +- package.json | 9 ++------- src/playground.jsx | 1 - 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/Gulpfile.js b/Gulpfile.js index e822b2d..776f86b 100644 --- a/Gulpfile.js +++ b/Gulpfile.js @@ -17,7 +17,7 @@ gulp.task("clean", function (cb) { gulp.task("babel", ["clean"], function () { return gulp.src("src/*.js*") - .pipe(babel()) + .pipe(babel({optional: ["runtime"]})) .pipe(gulp.dest("lib")); }); diff --git a/package.json b/package.json index f760545..d889286 100644 --- a/package.json +++ b/package.json @@ -7,21 +7,17 @@ "live", "component" ], - "version": "0.1.1", "author": "Ken Wheeler", - "homepage": "https://github.com/FormidableLabs/component-playground#readme", "bugs": { "url": "https://github.com/FormidableLabs/component-playground/issues" }, - "repository": { "type": "git", "url": "git+https://github.com/FormidableLabs/component-playground.git" }, "private": false, - "dependencies": { "babel": "^5.1.11", "babel-core": "^5.1.13" @@ -32,6 +28,7 @@ "devDependencies": { "babel-eslint": "^3.1.23", "babel-loader": "^5.3.0", + "babel-runtime": "^5.8.20", "chai": "^3.0.0", "css-loader": "~0.15.1", "del": "^1.1.1", @@ -64,12 +61,10 @@ "webpack": "^1.10.1", "webpack-dev-server": "^1.7.0" }, - "main": "index.js", - "engines":{ + "engines": { "node": ">= 0.10.0" }, "scripts": {}, - "license": "MIT" } diff --git a/src/playground.jsx b/src/playground.jsx index a7cf5c4..f4fcf6f 100644 --- a/src/playground.jsx +++ b/src/playground.jsx @@ -1,7 +1,6 @@ /* no-unused-vars:0 */ "use strict"; -import polyfill from "babel/polyfill"; import React from "react/addons"; import Editor from "./editor"; From 10a4e2d7f3e320a8f5625ca4e2f70a5e3965d9e7 Mon Sep 17 00:00:00 2001 From: unknown Date: Thu, 13 Aug 2015 19:37:48 +0300 Subject: [PATCH 2/2] Fixed using babel's runtime instead of polyfill, to avoid pollution of the global namespace. --- babel-plugin-playground/package.json | 19 +++ babel-plugin-playground/src/definitions.json | 168 +++++++++++++++++++ babel-plugin-playground/src/index.js | 92 ++++++++++ demo/webpack.demo.config.js | 6 +- package.json | 7 +- src/compiler.js | 57 +++++++ src/es6-preview.jsx | 17 +- src/preview.jsx | 32 +--- 8 files changed, 352 insertions(+), 46 deletions(-) create mode 100644 babel-plugin-playground/package.json create mode 100644 babel-plugin-playground/src/definitions.json create mode 100644 babel-plugin-playground/src/index.js create mode 100644 src/compiler.js diff --git a/babel-plugin-playground/package.json b/babel-plugin-playground/package.json new file mode 100644 index 0000000..c3c8660 --- /dev/null +++ b/babel-plugin-playground/package.json @@ -0,0 +1,19 @@ +{ + "name": "babel-plugin-playground", + "version": "0.1.0", + "description": "Externalise references to helpers and builtins, automatically polyfilling your code without polluting globals", + "repository": "https://github.com/omerts/component-playground", + "license": "MIT", + "main": "lib/index.js", + "devDependencies": { + "babel": "^5.6.0" + }, + "scripts": { + "build": "babel-plugin build", + "push": "babel-plugin publish", + "test": "babel-plugin test" + }, + "keywords": [ + "babel-plugin" + ] +} diff --git a/babel-plugin-playground/src/definitions.json b/babel-plugin-playground/src/definitions.json new file mode 100644 index 0000000..9c82814 --- /dev/null +++ b/babel-plugin-playground/src/definitions.json @@ -0,0 +1,168 @@ +{ + "builtins": { + "Symbol": "symbol", + "Promise": "promise", + "Map": "map", + "WeakMap": "weak-map", + "Set": "set", + "WeakSet": "weak-set" + }, + + "methods": { + "Array": { + "concat": "array/concat", + "copyWithin": "array/copy-within", + "entries": "array/entries", + "every": "array/every", + "fill": "array/fill", + "filter": "array/filter", + "findIndex": "array/find-index", + "find": "array/find", + "forEach": "array/for-each", + "from": "array/from", + "includes": "array/includes", + "indexOf": "array/index-of", + "join": "array/join", + "keys": "array/keys", + "lastIndexOf": "array/last-index-of", + "map": "array/map", + "of": "array/of", + "pop": "array/pop", + "push": "array/push", + "reduceRight": "array/reduce-right", + "reduce": "array/reduce", + "reverse": "array/reverse", + "shift": "array/shift", + "slice": "array/slice", + "some": "array/some", + "sort": "array/sort", + "splice": "array/splice", + "turn": "array/turn", + "unshift": "array/unshift", + "values": "array/values" + }, + + "Object": { + "assign": "object/assign", + "classof": "object/classof", + "create": "object/create", + "define": "object/define", + "defineProperties": "object/define-properties", + "defineProperty": "object/define-property", + "entries": "object/entries", + "freeze": "object/freeze", + "getOwnPropertyDescriptor": "object/get-own-property-descriptor", + "getOwnPropertyDescriptors": "object/get-own-property-descriptors", + "getOwnPropertyNames": "object/get-own-property-names", + "getOwnPropertySymbols": "object/get-own-property-symbols", + "getPrototypePf": "object/get-prototype-of", + "index": "object/index", + "isExtensible": "object/is-extensible", + "isFrozen": "object/is-frozen", + "isObject": "object/is-object", + "isSealed": "object/is-sealed", + "is": "object/is", + "keys": "object/keys", + "make": "object/make", + "preventExtensions": "object/prevent-extensions", + "seal": "object/seal", + "setPrototypeOf": "object/set-prototype-of", + "values": "object/values" + }, + + "RegExp": { + "escape": "regexp/escape" + }, + + "Function": { + "only": "function/only", + "part": "function/part" + }, + + "Math": { + "acosh": "math/acosh", + "asinh": "math/asinh", + "atanh": "math/atanh", + "cbrt": "math/cbrt", + "clz32": "math/clz32", + "cosh": "math/cosh", + "expm1": "math/expm1", + "fround": "math/fround", + "hypot": "math/hypot", + "pot": "math/pot", + "imul": "math/imul", + "log10": "math/log10", + "log1p": "math/log1p", + "log2": "math/log2", + "sign": "math/sign", + "sinh": "math/sinh", + "tanh": "math/tanh", + "trunc": "math/trunc" + }, + + "Date": { + "addLocale": "date/add-locale", + "formatUTC": "date/format-utc", + "format": "date/format" + }, + + "Symbol": { + "for": "symbol/for", + "hasInstance": "symbol/has-instance", + "is-concat-spreadable": "symbol/is-concat-spreadable", + "iterator": "symbol/iterator", + "keyFor": "symbol/key-for", + "match": "symbol/match", + "replace": "symbol/replace", + "search": "symbol/search", + "species": "symbol/species", + "split": "symbol/split", + "toPrimitive": "symbol/to-primitive", + "toStringTag": "symbol/to-string-tag", + "unscopables": "symbol/unscopables" + }, + + "String": { + "at": "string/at", + "codePointAt": "string/code-point-at", + "endsWith": "string/ends-with", + "escapeHTML": "string/escape-html", + "fromCodePoint": "string/from-code-point", + "includes": "string/includes", + "raw": "string/raw", + "repeat": "string/repeat", + "startsWith": "string/starts-with", + "unescapeHTML": "string/unescape-html" + }, + + "Number": { + "EPSILON": "number/epsilon", + "isFinite": "number/is-finite", + "isInteger": "number/is-integer", + "isNaN": "number/is-nan", + "isSafeInteger": "number/is-safe-integer", + "MAX_SAFE_INTEGER": "number/max-safe-integer", + "MIN_SAFE_INTEGER": "number/min-safe-integer", + "parseFloat": "number/parse-float", + "parseInt": "number/parse-int", + "random": "number/random" + }, + + "Reflect": { + "apply": "reflect/apply", + "construct": "reflect/construct", + "defineProperty": "reflect/define-property", + "deleteProperty": "reflect/delete-property", + "enumerate": "reflect/enumerate", + "getOwnPropertyDescriptor": "reflect/get-own-property-descriptor", + "getPrototypeOf": "reflect/get-prototype-of", + "get": "reflect/get", + "has": "reflect/has", + "isExtensible": "reflect/is-extensible", + "ownKeys": "reflect/own-keys", + "preventExtensions": "reflect/prevent-extensions", + "setPrototypeOf": "reflect/set-prototype-of", + "set": "reflect/set" + } + } +} diff --git a/babel-plugin-playground/src/index.js b/babel-plugin-playground/src/index.js new file mode 100644 index 0000000..ac9e5fb --- /dev/null +++ b/babel-plugin-playground/src/index.js @@ -0,0 +1,92 @@ +import definitions from "./definitions.json"; + +export default function ({ Plugin, types: t }) { + function has(obj, key) { + return Object.prototype.hasOwnProperty.call(obj, key); + } + + return new Plugin("playground", { + visitor: { + ReferencedIdentifier(node, parent, scope, file) { + if (node.name === "regeneratorRuntime") { + return t.identifier('_regeneratorRuntime'); + } + + if (t.isMemberExpression(parent)) return; + if (!has(definitions.builtins, node.name)) return; + if (scope.getBindingIdentifier(node.name)) return; + + // Symbol() -> _core.Symbol(); new Promise -> new _core.Promise + return t.identifier(`_core.${node.name}`); + }, + + CallExpression(node, parent, scope, file) { + // arr[Symbol.iterator]() -> _core.$for.getIterator(arr) + + if (node.arguments.length) return; + + var callee = node.callee; + if (!t.isMemberExpression(callee)) return; + if (!callee.computed) return; + if (!this.get("callee.property").matchesPattern("Symbol.iterator")) return; + + return t.callExpression(t.identifier('_core.getIterator'), [callee.object]); + }, + + BinaryExpression(node, parent, scope, file) { + // Symbol.iterator in arr -> core.$for.isIterable(arr) + + if (node.operator !== "in") return; + if (!this.get("left").matchesPattern("Symbol.iterator")) return; + + return t.callExpression(t.identifier('_core.isIterable'), [node.right]); + }, + + MemberExpression: { + enter(node, parent, scope, file) { + // Array.from -> _core.Array.from + + if (!this.isReferenced()) return; + + var obj = node.object; + var prop = node.property; + + if (!t.isReferenced(obj, node)) return; + + if (node.computed) return; + + if (!has(definitions.methods, obj.name)) return; + + var methods = definitions.methods[obj.name]; + if (!has(methods, prop.name)) return; + + // doesn't reference the global + if (scope.getBindingIdentifier(obj.name)) return; + + // special case Object.defineProperty to not use core-js when using string keys + if (obj.name === "Object" && prop.name === "defineProperty" && this.parentPath.isCallExpression()) { + var call = this.parentPath.node; + if (call.arguments.length === 3 && t.isLiteral(call.arguments[1])) return; + } + + return t.identifier(`_core.${obj.name}.${prop.name}`); + }, + exit(node, parent, scope, file) { + if (!this.isReferenced()) return; + + var prop = node.property; + var obj = node.object; + + if (!has(definitions.builtins, obj.name)) return; + if (scope.getBindingIdentifier(obj.name)) return; + + var modulePath = definitions.builtins[obj.name]; + return t.memberExpression( + t.identifier(`_core.${obj.name}`), + prop + ); + } + } + } + }); +} diff --git a/demo/webpack.demo.config.js b/demo/webpack.demo.config.js index 6c8c42f..62cd30b 100644 --- a/demo/webpack.demo.config.js +++ b/demo/webpack.demo.config.js @@ -18,7 +18,7 @@ module.exports = webpack({ module: { loaders: [{ test: /\.(js|jsx)$/, - exclude: /node_modules/, + exclude: /node_modules|babel-plugin-playground/, loader: 'babel-loader?stage=1' }, { test: /\.css$/, @@ -38,7 +38,7 @@ module.exports = webpack({ ], resolve: { root: [__dirname], - modulesDirectories: ['node_modules', 'src'], + modulesDirectories: ['node_modules', 'babel-plugin-playground', 'src'], extensions: ['','.js','.jsx'] } -}); \ No newline at end of file +}); diff --git a/package.json b/package.json index d889286..4b7049b 100644 --- a/package.json +++ b/package.json @@ -19,16 +19,17 @@ }, "private": false, "dependencies": { - "babel": "^5.1.11", - "babel-core": "^5.1.13" + "babel-core": "^5.8.22", + "babel-runtime": "^5.8.20", + "json-loader": "^0.5.2" }, "peerDependencies": { "react": "0.13.x" }, "devDependencies": { + "babel": "^5.8.21", "babel-eslint": "^3.1.23", "babel-loader": "^5.3.0", - "babel-runtime": "^5.8.20", "chai": "^3.0.0", "css-loader": "~0.15.1", "del": "^1.1.1", diff --git a/src/compiler.js b/src/compiler.js new file mode 100644 index 0000000..954f3c2 --- /dev/null +++ b/src/compiler.js @@ -0,0 +1,57 @@ +import babel from "babel-core/browser"; + +// Force injection of babel runtime +import _core from "babel-runtime/core-js"; +import _regeneratorRuntime from "babel-runtime/regenerator"; +import playground from '../babel-plugin-playground'; + +function _getKeys(scope) { + return Object.keys({_core, _regeneratorRuntime, ...scope}).join(","); +} + +export function transform(srcCode, scope, context, noRender) { + if (noRender) { + const generateContextTypes = function (context) { + const keys = Object.keys(context).map(val => `${val}: React.PropTypes.any.isRequired`); + return `{ ${keys.join(", ")} }`; + }; + + return babel.transform(` + (function (${_getKeys(scope)}, mountNode) { + return React.createClass({ + // childContextTypes: { test: React.PropTypes.string }, + childContextTypes: ${generateContextTypes(context)}, + getChildContext: function () { return ${JSON.stringify(context)}; }, + render: function () { + return ( + ${srcCode} + ); + } + }); + }); + `, { plugins: [{transformer: playground, position: 'after'}], stage: 1 }).code; + } else { + return babel.transform(` + (function (${_getKeys(scope)}, mountNode) { + ${srcCode} + }); + `, { plugins: [{transformer: playground, position: 'after'}], stage: 1 }).code; + } +} + +export function tranformWithConsole(srcCode, scope) { + return babel.transform(` + (function(${_getKeys(scope)}) { + var list = []; + var console = { log(...x) { + list.push({val: x, multipleArgs: x.length !== 1}) + }}; + ${srcCode} + return list; + }); + `, { plugins: [{transformer: playground, position: 'after'}], stage: 1}).code; +} + +export function getScope() { + return [_core, _regeneratorRuntime]; +} diff --git a/src/es6-preview.jsx b/src/es6-preview.jsx index fed1155..bdef2ad 100644 --- a/src/es6-preview.jsx +++ b/src/es6-preview.jsx @@ -2,7 +2,7 @@ "use strict"; import React from "react/addons"; -import babel from "babel-core/browser"; +import {tranformWithConsole, getScope} from "./compiler"; const getType = function (el) { let t = typeof el; @@ -98,16 +98,7 @@ const Preview = React.createClass({ }, _compileCode() { - return babel.transform(` - (function(${Object.keys(this.props.scope).join(",")}) { - var list = []; - var console = { log(...x) { - list.push({val: x, multipleArgs: x.length !== 1}) - }}; - ${this.props.code} - return list; - }); - `, { stage: 1 }).code; + return tranformWithConsole(this.props.code, this.props.scope); }, _setTimeout() { @@ -125,12 +116,14 @@ const Preview = React.createClass({ } try { - const scope = []; + const scope = getScope(); + for (const s in this.props.scope) { if (this.props.scope.hasOwnProperty(s)) { scope.push(this.props.scope[s]); } } + scope.push(mountNode); const compiledCode = this._compileCode(); const Component = React.createElement( diff --git a/src/preview.jsx b/src/preview.jsx index b47d048..eedc9f8 100644 --- a/src/preview.jsx +++ b/src/preview.jsx @@ -2,6 +2,7 @@ import React from "react/addons"; import babel from "babel-core/browser"; +import {transform, getScope} from "./compiler"; const Preview = React.createClass({ propTypes: { @@ -27,33 +28,7 @@ const Preview = React.createClass({ }, _compileCode() { - if (this.props.noRender) { - const generateContextTypes = function (context) { - const keys = Object.keys(context).map(val => `${val}: React.PropTypes.any.isRequired`); - return `{ ${keys.join(", ")} }`; - }; - - return babel.transform(` - (function (${Object.keys(this.props.scope).join(", ")}, mountNode) { - return React.createClass({ - // childContextTypes: { test: React.PropTypes.string }, - childContextTypes: ${generateContextTypes(this.props.context)}, - getChildContext: function () { return ${JSON.stringify(this.props.context)}; }, - render: function () { - return ( - ${this.props.code} - ); - } - }); - }); - `, { stage: 1 }).code; - } else { - return babel.transform(` - (function (${Object.keys(this.props.scope).join(",")}, mountNode) { - ${this.props.code} - }); - `, { stage: 1 }).code; - } + return transform(this.props.code, this.props.scope, this.props.context, this.props.noRender); }, _setTimeout() { @@ -65,7 +40,7 @@ const Preview = React.createClass({ const mountNode = this.refs.mount.getDOMNode(); try { - const scope = []; + const scope = getScope(); for (const s in this.props.scope) { if (this.props.scope.hasOwnProperty(s)) { @@ -76,6 +51,7 @@ const Preview = React.createClass({ scope.push(mountNode); const compiledCode = this._compileCode(); + if (this.props.noRender) { const Component = React.createElement( eval(compiledCode).apply(null, scope)