From da25380d50776827b3003c0fd6f0d413f0b3725f Mon Sep 17 00:00:00 2001 From: Taras Mankovski Date: Sun, 14 Oct 2018 08:57:30 -0400 Subject: [PATCH 1/5] Allows to configure babel options via ember-build --- .eslintrc.js | 1 + README.md | 39 +++++++++++++++++++ ember-cli-build.js | 5 +++ index.js | 39 ++++++++++++++++++- package.json | 7 +++- tests/dummy/app/components/babel-features.jsx | 11 ++++++ .../components/react-component-test.js | 7 ++++ 7 files changed, 105 insertions(+), 4 deletions(-) create mode 100644 tests/dummy/app/components/babel-features.jsx diff --git a/.eslintrc.js b/.eslintrc.js index 50725ee..689e880 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,5 +1,6 @@ module.exports = { root: true, + parser: 'babel-eslint', parserOptions: { ecmaFeatures: { jsx: true, diff --git a/README.md b/README.md index 994ee50..6593e48 100644 --- a/README.md +++ b/README.md @@ -210,6 +210,45 @@ export default class TodoItem extends React.Component { } ``` +## Object Rest Spread and Class Properties + +[Object Rest Spread]() and [Class Properties](https://babeljs.io/docs/en/babel-plugin-proposal-class-properties) are included in `create-react-app` and used in many React projects. To enable these features in your React components, you must configure the Babel compiler used by `ember-cli-react`. You can do this by passing `babelOptions` to `ember-cli-react` configurations. + +For Babel 6, + +1. Add Babel plugin packages to your project `babel-plugin-transform-object-rest-spread` and `babel-plugin-transform-class-properties` +2. In `ember-cli-build.js` file, configure + ```js + let app = new EmberApp(defaults, { + 'ember-cli-react': { + babelOptions: { + plugins: [ + 'transform-class-properties', + 'transform-object-rest-spread', + ], + }, + }, + }); + ``` + +```` +For Babel 7, + +1. Install the transforms `npm install --save-dev @babel/plugin-proposal-object-rest-spread @babel/plugin-proposal-class-properties` +2. Add these plugins to your `ember-cli-build.js` file + ```js + let app = new EmberAddon(defaults, { + 'ember-cli-react': { + babelOptions: { + plugins: [ + '@babel/plugin-proposal-object-rest-spread', + '@babel/plugin-proposal-class-properties' + ], + } + }, +}); +```` + ## What's Missing There is no React `link-to` equivalent for linking to Ember routes inside of diff --git a/ember-cli-build.js b/ember-cli-build.js index 69cc8bb..e6e3318 100644 --- a/ember-cli-build.js +++ b/ember-cli-build.js @@ -8,6 +8,11 @@ module.exports = function(defaults) { 'ember-cli-babel': { includePolyfill: true, }, + 'ember-cli-react': { + babelOptions: { + plugins: ['transform-class-properties', 'transform-object-rest-spread'], + }, + }, }); /* diff --git a/index.js b/index.js index 3f94806..7052cf0 100644 --- a/index.js +++ b/index.js @@ -6,9 +6,44 @@ const react = require('broccoli-react'); module.exports = { name: 'ember-cli-react', - preprocessTree: function(type, tree) { + appOptions() { + return ( + (this.parent && this.parent.options) || (this.app && this.app.options) + ); + }, + + included() { + let app; + + // @source: https://github.com/samselikoff/ember-cli-mirage/blob/master/index.js#L34 + // If the addon has the _findHost() method (in ember-cli >= 2.7.0), we'll just + // use that. + if (typeof this._findHost === 'function') { + app = this._findHost(); + } else { + // Otherwise, we'll use this implementation borrowed from the _findHost() + // method in ember-cli. + let current = this; + do { + app = current.app || app; + } while (current.parent.parent && (current = current.parent)); + } + + this.app = app; + this.addonBuildConfig = this.app.options['ember-cli-react'] || { + babelOptions: {}, + }; + + this._super.included.apply(this, arguments); + }, + + preprocessTree(type, tree) { if (type === 'js') { - tree = react(tree, { transform: { es6module: true } }); + let { babelOptions } = this.addonBuildConfig; + tree = react(tree, { + transform: { es6module: true }, + babelOptions, + }); } return tree; diff --git a/package.json b/package.json index 88450dc..2cd1c09 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "author": "alex@altschool.com", "license": "MIT", "devDependencies": { + "babel-eslint": "10.0.1", "broccoli-asset-rev": "^2.4.5", "ember-ajax": "^3.0.0", "ember-cli": "~2.15.1", @@ -59,10 +60,12 @@ "husky": "^0.14.3", "lint-staged": "^5.0.0", "loader.js": "^4.2.3", - "prettier": "^1.9.2" + "prettier": "^1.9.2", + "babel-plugin-transform-class-properties": "6.24.1", + "babel-plugin-transform-object-rest-spread": "6.26.0" }, "dependencies": { - "broccoli-react": "^0.8.0", + "broccoli-react": "eddhannay/broccoli-react#1399c761da19aff4492a521a1b4d53429433e499", "ember-auto-import": "^1.0.1", "ember-cli-babel": "^6.3.0", "react": "^15.5.4 || ^16.0.0", diff --git a/tests/dummy/app/components/babel-features.jsx b/tests/dummy/app/components/babel-features.jsx new file mode 100644 index 0000000..27b8210 --- /dev/null +++ b/tests/dummy/app/components/babel-features.jsx @@ -0,0 +1,11 @@ +import React from 'react'; + +class State { + message = 'supports clsss properties'; +} + +export default function BabelFeatures() { + let attrs = { 'data-test': 'allows-object-spread' }; + let s = new State(); + return
{s.message}
; +} diff --git a/tests/integration/components/react-component-test.js b/tests/integration/components/react-component-test.js index 2148d28..d049ba2 100644 --- a/tests/integration/components/react-component-test.js +++ b/tests/integration/components/react-component-test.js @@ -20,6 +20,13 @@ describeComponent( expect(this.$()).to.have.length(1); }); + it('uses babel features', function() { + this.render(hbs`{{react-component "babel-features"}}`); + expect(this.$('[data-test="allows-object-spread"]').text()).to.equal( + 'supports clsss properties' + ); + }); + it.skip('throws error when no component found', function() { expect(() => { this.render(hbs`{{react-component "missing-component"}}`); From 6a1940e1591c7df749e2ad9b3918d4a4763032c9 Mon Sep 17 00:00:00 2001 From: Taras Mankovski Date: Sun, 14 Oct 2018 09:25:34 -0400 Subject: [PATCH 2/5] Fix formatting --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 6593e48..73b463f 100644 --- a/README.md +++ b/README.md @@ -231,23 +231,23 @@ For Babel 6, }); ``` -```` For Babel 7, 1. Install the transforms `npm install --save-dev @babel/plugin-proposal-object-rest-spread @babel/plugin-proposal-class-properties` 2. Add these plugins to your `ember-cli-build.js` file - ```js - let app = new EmberAddon(defaults, { + +```js +let app = new EmberAddon(defaults, { 'ember-cli-react': { babelOptions: { plugins: [ '@babel/plugin-proposal-object-rest-spread', - '@babel/plugin-proposal-class-properties' + '@babel/plugin-proposal-class-properties', ], - } + }, }, }); -```` +``` ## What's Missing From 1b0ae19599c02a2376cc374d5814698a5197478c Mon Sep 17 00:00:00 2001 From: Taras Mankovski Date: Sun, 14 Oct 2018 09:27:01 -0400 Subject: [PATCH 3/5] Fixed spelling --- tests/dummy/app/components/babel-features.jsx | 2 +- tests/integration/components/react-component-test.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/dummy/app/components/babel-features.jsx b/tests/dummy/app/components/babel-features.jsx index 27b8210..5d86b35 100644 --- a/tests/dummy/app/components/babel-features.jsx +++ b/tests/dummy/app/components/babel-features.jsx @@ -1,7 +1,7 @@ import React from 'react'; class State { - message = 'supports clsss properties'; + message = 'supports class properties'; } export default function BabelFeatures() { diff --git a/tests/integration/components/react-component-test.js b/tests/integration/components/react-component-test.js index d049ba2..4e11138 100644 --- a/tests/integration/components/react-component-test.js +++ b/tests/integration/components/react-component-test.js @@ -23,7 +23,7 @@ describeComponent( it('uses babel features', function() { this.render(hbs`{{react-component "babel-features"}}`); expect(this.$('[data-test="allows-object-spread"]').text()).to.equal( - 'supports clsss properties' + 'supports class properties' ); }); From 53ac2869fa98ec86e9d8fff9f98b963e085600ac Mon Sep 17 00:00:00 2001 From: Taras Mankovski Date: Tue, 23 Oct 2018 08:39:59 -0400 Subject: [PATCH 4/5] Removed broccoli-react --- ember-cli-build.js | 6 +-- index.js | 77 ++++++++++++++++------------------ lib/configure-jsx-transform.js | 43 +++++++++++++++++++ package.json | 8 +++- 4 files changed, 88 insertions(+), 46 deletions(-) create mode 100644 lib/configure-jsx-transform.js diff --git a/ember-cli-build.js b/ember-cli-build.js index e6e3318..0463a23 100644 --- a/ember-cli-build.js +++ b/ember-cli-build.js @@ -8,10 +8,8 @@ module.exports = function(defaults) { 'ember-cli-babel': { includePolyfill: true, }, - 'ember-cli-react': { - babelOptions: { - plugins: ['transform-class-properties', 'transform-object-rest-spread'], - }, + babel: { + plugins: ['transform-class-properties', 'transform-object-rest-spread'], }, }); diff --git a/index.js b/index.js index 7052cf0..94ccee9 100644 --- a/index.js +++ b/index.js @@ -1,52 +1,49 @@ /* eslint-env node */ 'use strict'; -const react = require('broccoli-react'); +const configureJsxTransform = require('./lib/configure-jsx-transform'); +const mergeTrees = require('broccoli-merge-trees'); +const Funnel = require('broccoli-funnel'); +const path = require('path'); module.exports = { name: 'ember-cli-react', - appOptions() { - return ( - (this.parent && this.parent.options) || (this.app && this.app.options) - ); - }, - - included() { - let app; - - // @source: https://github.com/samselikoff/ember-cli-mirage/blob/master/index.js#L34 - // If the addon has the _findHost() method (in ember-cli >= 2.7.0), we'll just - // use that. - if (typeof this._findHost === 'function') { - app = this._findHost(); - } else { - // Otherwise, we'll use this implementation borrowed from the _findHost() - // method in ember-cli. - let current = this; - do { - app = current.app || app; - } while (current.parent.parent && (current = current.parent)); - } - - this.app = app; - this.addonBuildConfig = this.app.options['ember-cli-react'] || { - babelOptions: {}, - }; - + included(parent) { this._super.included.apply(this, arguments); - }, - - preprocessTree(type, tree) { - if (type === 'js') { - let { babelOptions } = this.addonBuildConfig; - tree = react(tree, { - transform: { es6module: true }, - babelOptions, - }); - } - return tree; + configureJsxTransform(parent); + + parent.registry.add('js', { + name: 'ember-cli-react', + ext: 'jsx', + toTree(tree) { + // get all JSX files and rename them to js + let jsx = new Funnel(tree, { + include: ['**/*.jsx'], + getDestinationPath(relativePath) { + let f = path.parse(relativePath); + if (f.ext === '.jsx') { + return path.join(f.dir, `${f.name}.js`); + } else { + return relativePath; + } + }, + }); + + // apply preprocessing from other babel plugins + let processed = parent.registry + .load('js') + .filter(p => p.name !== 'ember-cli-react') + .reduce((tree, plugin) => plugin.toTree(tree), jsx); + + let withoutJsx = new Funnel(tree, { + exclude: ['**/*.jsx'], + }); + + return mergeTrees([withoutJsx, processed]); + }, + }); }, options: { diff --git a/lib/configure-jsx-transform.js b/lib/configure-jsx-transform.js new file mode 100644 index 0000000..d20db1e --- /dev/null +++ b/lib/configure-jsx-transform.js @@ -0,0 +1,43 @@ +const VersionChecker = require('ember-cli-version-checker'); + +function requireTransform(transformName) { + return require.resolve(transformName); +} + +function hasPlugin(plugins, name) { + for (const maybePlugin of plugins) { + const plugin = Array.isArray(maybePlugin) ? maybePlugin[0] : maybePlugin; + const pluginName = typeof plugin === 'string' ? plugin : plugin.name; + + if (pluginName === name) { + return true; + } + } + + return false; +} + +module.exports = function configureJsxTransform(parent) { + const options = (parent.options = parent.options || {}); + + const checker = new VersionChecker(parent).for('ember-cli-babel', 'npm'); + + options.babel = options.babel || {}; + options.babel.plugins = options.babel.plugins || []; + + if (checker.satisfies('^6.0.0-beta.1')) { + if (!hasPlugin(options.babel.plugins, 'babel-plugin-transform-react-jsx')) { + options.babel.plugins.push( + requireTransform('babel-plugin-transform-react-jsx') + ); + } + } else if (checker.gte('7.0.0')) { + if ( + !hasPlugin(options.babel.plugins, '@babel/plugin-transform-react-jsx') + ) { + options.babel.plugins.push( + requireTransform('@babel/plugin-transform-react-jsx') + ); + } + } +}; diff --git a/package.json b/package.json index 2cd1c09..d0fe9b0 100644 --- a/package.json +++ b/package.json @@ -62,10 +62,14 @@ "loader.js": "^4.2.3", "prettier": "^1.9.2", "babel-plugin-transform-class-properties": "6.24.1", - "babel-plugin-transform-object-rest-spread": "6.26.0" + "babel-plugin-transform-object-rest-spread": "6.26.0", + "broccoli-funnel": "2.0.1", + "broccoli-merge-trees": "3.0.1", + "ember-cli-version-checker": "^2.1.2" }, "dependencies": { - "broccoli-react": "eddhannay/broccoli-react#1399c761da19aff4492a521a1b4d53429433e499", + "@babel/plugin-transform-react-jsx": "^7.0.0", + "babel-plugin-transform-react-jsx": "^6.24.1", "ember-auto-import": "^1.0.1", "ember-cli-babel": "^6.3.0", "react": "^15.5.4 || ^16.0.0", From bd021306c9c9cee44dffc604be8f5a73241e1c98 Mon Sep 17 00:00:00 2001 From: Taras Mankovski Date: Tue, 23 Oct 2018 08:46:46 -0400 Subject: [PATCH 5/5] Update README --- README.md | 23 ++++++++--------------- index.js | 2 +- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 73b463f..cfbea49 100644 --- a/README.md +++ b/README.md @@ -212,7 +212,7 @@ export default class TodoItem extends React.Component { ## Object Rest Spread and Class Properties -[Object Rest Spread]() and [Class Properties](https://babeljs.io/docs/en/babel-plugin-proposal-class-properties) are included in `create-react-app` and used in many React projects. To enable these features in your React components, you must configure the Babel compiler used by `ember-cli-react`. You can do this by passing `babelOptions` to `ember-cli-react` configurations. +[Object Rest Spread]() and [Class Properties](https://babeljs.io/docs/en/babel-plugin-proposal-class-properties) are included in `create-react-app` and used in many React projects. To enable these features you must add respective transforms to Babel plugins setting in your `ember-cli-build.js`. For Babel 6, @@ -220,13 +220,8 @@ For Babel 6, 2. In `ember-cli-build.js` file, configure ```js let app = new EmberApp(defaults, { - 'ember-cli-react': { - babelOptions: { - plugins: [ - 'transform-class-properties', - 'transform-object-rest-spread', - ], - }, + babel: { + plugins: ['transform-class-properties', 'transform-object-rest-spread'], }, }); ``` @@ -238,13 +233,11 @@ For Babel 7, ```js let app = new EmberAddon(defaults, { - 'ember-cli-react': { - babelOptions: { - plugins: [ - '@babel/plugin-proposal-object-rest-spread', - '@babel/plugin-proposal-class-properties', - ], - }, + babel: { + plugins: [ + '@babel/plugin-proposal-object-rest-spread', + '@babel/plugin-proposal-class-properties', + ], }, }); ``` diff --git a/index.js b/index.js index 94ccee9..45ea58b 100644 --- a/index.js +++ b/index.js @@ -31,7 +31,7 @@ module.exports = { }, }); - // apply preprocessing from other babel plugins + // apply preprocessing from other babel plugins to the jsx files let processed = parent.registry .load('js') .filter(p => p.name !== 'ember-cli-react')