diff --git a/scripts/babel/__tests__/remove-getters-test.js b/scripts/babel/__tests__/remove-getters-test.js new file mode 100644 index 00000000000..8c9fa707b49 --- /dev/null +++ b/scripts/babel/__tests__/remove-getters-test.js @@ -0,0 +1,66 @@ +/** + * Copyright (c) 2013-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +'use strict'; + +const babel = require('babel-core'); +const removeGetters = require('../remove-getters'); + +function transform(input) { + return babel.transform(input, { + plugins: [removeGetters], + }).code; +} + +function compare(input, output) { + const compiled = transform(input); + expect(compiled).toEqual(output); +} + +describe('remove-getters', () => { + it('should remove getters', () => { + compare( + `const object = { + get prop() { + return variable; + } +};`, + `const object = { + prop: variable +};` + ); + }); + + it('should not remove other methods or properties', () => { + compare( + `const object = { + prop: 'foo', + method() { + return 'bar'; + } +};`, + `const object = { + prop: 'foo', + method() { + return 'bar'; + } +};` + ); + }); + + it('should throw when finding getters with a different syntax from the ones generated by Rollup', () => { + expect(() => { + transform( + `const object = { + get prop() { + const foo = 'foo'; + return foo; + } +};` + ); + }).toThrow(); + }); +}); diff --git a/scripts/babel/remove-getters.js b/scripts/babel/remove-getters.js new file mode 100644 index 00000000000..3ce9e4a9009 --- /dev/null +++ b/scripts/babel/remove-getters.js @@ -0,0 +1,54 @@ +/** + * Copyright (c) 2013-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +'use strict'; + +module.exports = ({types: t}) => { + return { + visitor: { + ObjectMethod: path => { + // Turns this code: + // + // get prop() { + // return variable; + // } + // + // into this: + // + // prop: variable; + if (path.node.kind !== 'get') { + return; + } + + const keyNode = path.node.key; + const isValidKey = t.isIdentifier(keyNode); + if (!isValidKey) { + throw path.buildCodeFrameError( + 'Getter key format not supported. Expected identifier.' + ); + } + + const bodyNode = path.node.body; + const isValidBody = + bodyNode.body.length === 1 && + t.isReturnStatement(bodyNode.body[0]) && + t.isIdentifier(bodyNode.body[0].argument); + if (!isValidBody) { + throw path.buildCodeFrameError( + 'Getter body format not supported. Expected return of identifier.' + ); + } + + const prop = keyNode.name; + const variable = bodyNode.body[0].argument.name; + + path.replaceWith( + t.objectProperty(t.identifier(prop), t.identifier(variable)) + ); + }, + }, + }; +}; diff --git a/scripts/rollup/build.js b/scripts/rollup/build.js index fa1c8f90c66..f7939095419 100644 --- a/scripts/rollup/build.js +++ b/scripts/rollup/build.js @@ -2,6 +2,7 @@ const {rollup} = require('rollup'); const babel = require('rollup-plugin-babel'); +const babelCore = require('babel-core'); const closure = require('./plugins/closure-plugin'); const commonjs = require('rollup-plugin-commonjs'); const prettier = require('rollup-plugin-prettier'); @@ -335,6 +336,23 @@ function getPlugins( .replace(/require\(['"]react-is['"]\)/g, "require('ReactIs')"); }, }, + isFBBundle && { + transformBundle(source) { + // Run a final Babel pass after Rollup bundle. + const babelOptions = { + ast: false, + babelrc: false, + compact: false, + plugins: [ + // This is needed because Rollup outputs getters, but we can't use + // getters in www. + require('../babel/remove-getters'), + ], + }; + const result = babelCore.transform(source, babelOptions); + return result.code; + }, + }, // Apply dead code elimination and/or minification. isProduction && closure( @@ -420,11 +438,11 @@ async function createBundle(bundle, bundleType) { const packageName = Packaging.getPackageName(bundle.entry); let resolvedEntry = require.resolve(bundle.entry); - if ( + const isFBBundle = bundleType === FB_WWW_DEV || bundleType === FB_WWW_PROD || - bundleType === FB_WWW_PROFILING - ) { + bundleType === FB_WWW_PROFILING; + if (isFBBundle) { const resolvedFBEntry = resolvedEntry.replace('.js', '.fb.js'); if (fs.existsSync(resolvedFBEntry)) { resolvedEntry = resolvedFBEntry; @@ -470,11 +488,6 @@ async function createBundle(bundle, bundleType) { bundle.moduleType, bundle.modulesToStub ), - // We can't use getters in www. - legacy: - bundleType === FB_WWW_DEV || - bundleType === FB_WWW_PROD || - bundleType === FB_WWW_PROFILING, }; const [mainOutputPath, ...otherOutputPaths] = Packaging.getBundleOutputPaths( bundleType,