From 96316dcf889f1bf51e69e3188d570514e662a5df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Ribaudo?= Date: Sat, 14 Apr 2018 19:27:02 +0200 Subject: [PATCH 1/4] Add legacy option to plugin-proposal-decorators. --- .../README.md | 49 +++- .../src/index.js | 275 +----------------- .../src/transformer-legacy.js | 265 +++++++++++++++++ .../src/transformer.js | 3 + .../fixtures/decl-to-expression/options.json | 2 +- .../test/fixtures/options.json | 5 +- .../babel-plugin-syntax-decorators/README.md | 8 + .../src/index.js | 9 +- packages/babel-preset-stage-1/src/index.js | 2 +- 9 files changed, 335 insertions(+), 283 deletions(-) create mode 100644 packages/babel-plugin-proposal-decorators/src/transformer-legacy.js create mode 100644 packages/babel-plugin-proposal-decorators/src/transformer.js diff --git a/packages/babel-plugin-proposal-decorators/README.md b/packages/babel-plugin-proposal-decorators/README.md index 1929d8a35f..b5bf1a1b20 100644 --- a/packages/babel-plugin-proposal-decorators/README.md +++ b/packages/babel-plugin-proposal-decorators/README.md @@ -62,11 +62,33 @@ Add the following line to your .babelrc file: } ``` -#### NOTE: Order of Plugins Matters! +### Via CLI + +```sh +babel --plugins @babel/plugin-proposal-decorators script.js +``` + +### Via Node API + +```javascript +require("@babel/core").transform("code", { + plugins: ["@babel/plugin-proposal-decorators"] +}); +``` + +## Options + +### `legacy` + +`boolean`, defaults to `false`. + +Use the legacy (stage 1) decorators syntax and behavior. + +#### NOTE: Compatibility with `@babel/plugin-proposal-class-properties` If you are including your plugins manually and using `@babel/plugin-proposal-class-properties`, make sure that `@babel/plugin-proposal-decorators` comes *before* `@babel/plugin-proposal-class-properties`. -Currently, `@babel/plugin-proposal-class-properties` must be used in `loose` mode to support the `@babel/plugin-proposal-decorators`. To use `@babel/plugin-proposal-class-properties` in spec mode with decorators, wait for the next major version of decorators (Stage 2). +When using the `legacy: true` mode, `@babel/plugin-proposal-class-properties` must be used in `loose` mode to support the `@babel/plugin-proposal-decorators`. Wrong: @@ -85,25 +107,20 @@ Right: { "plugins": [ "@babel/plugin-proposal-decorators", + "@babel/plugin-proposal-class-properties" + ] +} +``` + +```json +{ + "plugins": [ + ["@babel/plugin-proposal-decorators", { "legacy": true }], ["@babel/plugin-proposal-class-properties", { "loose" : true }] ] } ``` -### Via CLI - -```sh -babel --plugins @babel/plugin-proposal-decorators script.js -``` - -### Via Node API - -```javascript -require("@babel/core").transform("code", { - plugins: ["@babel/plugin-proposal-decorators"] -}); -``` - ## References * [Proposal: JavaScript Decorators](https://github.com/wycats/javascript-decorators/blob/master/README.md) diff --git a/packages/babel-plugin-proposal-decorators/src/index.js b/packages/babel-plugin-proposal-decorators/src/index.js index 8a1efe87c5..be15f039be 100644 --- a/packages/babel-plugin-proposal-decorators/src/index.js +++ b/packages/babel-plugin-proposal-decorators/src/index.js @@ -2,277 +2,28 @@ import { declare } from "@babel/helper-plugin-utils"; import syntaxDecorators from "@babel/plugin-syntax-decorators"; -import { template, types as t } from "@babel/core"; +import visitor from "./transformer"; +import legacyVisitor from "./transformer-legacy"; -const buildClassDecorator = template(` - DECORATOR(CLASS_REF = INNER) || CLASS_REF; -`); - -const buildClassPrototype = template(` - CLASS_REF.prototype; -`); - -const buildGetDescriptor = template(` - Object.getOwnPropertyDescriptor(TARGET, PROPERTY); -`); - -const buildGetObjectInitializer = template(` - (TEMP = Object.getOwnPropertyDescriptor(TARGET, PROPERTY), (TEMP = TEMP ? TEMP.value : undefined), { - enumerable: true, - configurable: true, - writable: true, - initializer: function(){ - return TEMP; - } - }) -`); - -export default declare(api => { +export default declare((api, options) => { api.assertVersion(7); - const WARNING_CALLS = new WeakSet(); + const { legacy = false } = options; + if (typeof legacy !== "boolean") { + throw new Error("'legacy' must be a boolean."); + } - /** - * If the decorator expressions are non-identifiers, hoist them to before the class so we can be sure - * that they are evaluated in order. - */ - function applyEnsureOrdering(path) { - // TODO: This should probably also hoist computed properties. - const decorators = (path.isClass() - ? [path].concat(path.get("body.body")) - : path.get("properties") - ).reduce((acc, prop) => acc.concat(prop.node.decorators || []), []); - - const identDecorators = decorators.filter( - decorator => !t.isIdentifier(decorator.callee), + if (legacy !== true) { + throw new Error( + "The new decorators proposal is not supported yet." + + ' You muse pass the `"legacy": true` option to' + + " @babel/plugin-proposal-decorators", ); - if (identDecorators.length === 0) return; - - return t.sequenceExpression( - identDecorators - .map(decorator => { - const callee = decorator.callee; - const id = (decorator.callee = path.scope.generateDeclaredUidIdentifier( - "dec", - )); - return t.assignmentExpression("=", id, callee); - }) - .concat([path.node]), - ); - } - - /** - * Given a class expression with class-level decorators, create a new expression - * with the proper decorated behavior. - */ - function applyClassDecorators(classPath) { - if (!hasClassDecorators(classPath.node)) return; - - const decorators = classPath.node.decorators || []; - classPath.node.decorators = null; - - const name = classPath.scope.generateDeclaredUidIdentifier("class"); - - return decorators - .map(dec => dec.callee) - .reverse() - .reduce(function(acc, decorator) { - return buildClassDecorator({ - CLASS_REF: t.cloneNode(name), - DECORATOR: t.cloneNode(decorator), - INNER: acc, - }).expression; - }, classPath.node); - } - - function hasClassDecorators(classNode) { - return !!(classNode.decorators && classNode.decorators.length); - } - - /** - * Given a class expression with method-level decorators, create a new expression - * with the proper decorated behavior. - */ - function applyMethodDecorators(path, state) { - if (!hasMethodDecorators(path.node.body.body)) return; - - return applyTargetDecorators(path, state, path.node.body.body); - } - - function hasMethodDecorators(body) { - return body.some(node => node.decorators && node.decorators.length); - } - - /** - * Given an object expression with property decorators, create a new expression - * with the proper decorated behavior. - */ - function applyObjectDecorators(path, state) { - if (!hasMethodDecorators(path.node.properties)) return; - - return applyTargetDecorators(path, state, path.node.properties); - } - - /** - * A helper to pull out property decorators into a sequence expression. - */ - function applyTargetDecorators(path, state, decoratedProps) { - const name = path.scope.generateDeclaredUidIdentifier( - path.isClass() ? "class" : "obj", - ); - - const exprs = decoratedProps.reduce(function(acc, node) { - const decorators = node.decorators || []; - node.decorators = null; - - if (decorators.length === 0) return acc; - - if (node.computed) { - throw path.buildCodeFrameError( - "Computed method/property decorators are not yet supported.", - ); - } - - const property = t.isLiteral(node.key) - ? node.key - : t.stringLiteral(node.key.name); - - const target = - path.isClass() && !node.static - ? buildClassPrototype({ - CLASS_REF: name, - }).expression - : name; - - if (t.isClassProperty(node, { static: false })) { - const descriptor = path.scope.generateDeclaredUidIdentifier( - "descriptor", - ); - - const initializer = node.value - ? t.functionExpression( - null, - [], - t.blockStatement([t.returnStatement(node.value)]), - ) - : t.nullLiteral(); - - node.value = t.callExpression( - state.addHelper("initializerWarningHelper"), - [descriptor, t.thisExpression()], - ); - - WARNING_CALLS.add(node.value); - - acc = acc.concat([ - t.assignmentExpression( - "=", - descriptor, - t.callExpression(state.addHelper("applyDecoratedDescriptor"), [ - t.cloneNode(target), - t.cloneNode(property), - t.arrayExpression(decorators.map(dec => t.cloneNode(dec.callee))), - t.objectExpression([ - t.objectProperty( - t.identifier("enumerable"), - t.booleanLiteral(true), - ), - t.objectProperty(t.identifier("initializer"), initializer), - ]), - ]), - ), - ]); - } else { - acc = acc.concat( - t.callExpression(state.addHelper("applyDecoratedDescriptor"), [ - t.cloneNode(target), - t.cloneNode(property), - t.arrayExpression(decorators.map(dec => t.cloneNode(dec.callee))), - t.isObjectProperty(node) || - t.isClassProperty(node, { static: true }) - ? buildGetObjectInitializer({ - TEMP: path.scope.generateDeclaredUidIdentifier("init"), - TARGET: t.cloneNode(target), - PROPERTY: t.cloneNode(property), - }).expression - : buildGetDescriptor({ - TARGET: t.cloneNode(target), - PROPERTY: t.cloneNode(property), - }).expression, - t.cloneNode(target), - ]), - ); - } - - return acc; - }, []); - - return t.sequenceExpression([ - t.assignmentExpression("=", t.cloneNode(name), path.node), - t.sequenceExpression(exprs), - t.cloneNode(name), - ]); } return { inherits: syntaxDecorators, - visitor: { - ClassDeclaration(path) { - const { node } = path; - - if (!hasClassDecorators(node) && !hasMethodDecorators(node.body.body)) { - return; - } - - const ref = node.id - ? t.cloneNode(node.id) - : path.scope.generateUidIdentifier("class"); - const letDeclaration = t.variableDeclaration("let", [ - t.variableDeclarator(ref, t.toExpression(node)), - ]); - - if (path.parentPath.isExportDefaultDeclaration()) { - // Split the class declaration and the export into two separate statements. - path.parentPath.replaceWithMultiple([ - letDeclaration, - t.exportNamedDeclaration(null, [ - t.exportSpecifier(t.cloneNode(ref), t.identifier("default")), - ]), - ]); - } else { - path.replaceWith(letDeclaration); - } - }, - ClassExpression(path, state) { - // Create a replacement for the class node if there is one. We do one pass to replace classes with - // class decorators, and a second pass to process method decorators. - const decoratedClass = - applyEnsureOrdering(path) || - applyClassDecorators(path, state) || - applyMethodDecorators(path, state); - - if (decoratedClass) path.replaceWith(decoratedClass); - }, - ObjectExpression(path, state) { - const decoratedObject = - applyEnsureOrdering(path) || applyObjectDecorators(path, state); - - if (decoratedObject) path.replaceWith(decoratedObject); - }, - - AssignmentExpression(path, state) { - if (!WARNING_CALLS.has(path.node.right)) return; - - path.replaceWith( - t.callExpression(state.addHelper("initializerDefineProperty"), [ - t.cloneNode(path.get("left.object").node), - t.stringLiteral(path.get("left.property").node.name), - t.cloneNode(path.get("right.arguments")[0].node), - t.cloneNode(path.get("right.arguments")[1].node), - ]), - ); - }, - }, + visitor: legacy ? legacyVisitor : visitor, }; }); diff --git a/packages/babel-plugin-proposal-decorators/src/transformer-legacy.js b/packages/babel-plugin-proposal-decorators/src/transformer-legacy.js new file mode 100644 index 0000000000..b9e88388bc --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/src/transformer-legacy.js @@ -0,0 +1,265 @@ +// Fork of https://github.com/loganfsmyth/babel-plugin-proposal-decorators-legacy + +import { template, types as t } from "@babel/core"; + +const buildClassDecorator = template(` + DECORATOR(CLASS_REF = INNER) || CLASS_REF; +`); + +const buildClassPrototype = template(` + CLASS_REF.prototype; +`); + +const buildGetDescriptor = template(` + Object.getOwnPropertyDescriptor(TARGET, PROPERTY); +`); + +const buildGetObjectInitializer = template(` + (TEMP = Object.getOwnPropertyDescriptor(TARGET, PROPERTY), (TEMP = TEMP ? TEMP.value : undefined), { + enumerable: true, + configurable: true, + writable: true, + initializer: function(){ + return TEMP; + } + }) +`); + +const WARNING_CALLS = new WeakSet(); + +/** + * If the decorator expressions are non-identifiers, hoist them to before the class so we can be sure + * that they are evaluated in order. + */ +function applyEnsureOrdering(path) { + // TODO: This should probably also hoist computed properties. + const decorators = (path.isClass() + ? [path].concat(path.get("body.body")) + : path.get("properties") + ).reduce((acc, prop) => acc.concat(prop.node.decorators || []), []); + + const identDecorators = decorators.filter( + decorator => !t.isIdentifier(decorator.callee), + ); + if (identDecorators.length === 0) return; + + return t.sequenceExpression( + identDecorators + .map(decorator => { + const callee = decorator.callee; + const id = (decorator.callee = path.scope.generateDeclaredUidIdentifier( + "dec", + )); + return t.assignmentExpression("=", id, callee); + }) + .concat([path.node]), + ); +} + +/** + * Given a class expression with class-level decorators, create a new expression + * with the proper decorated behavior. + */ +function applyClassDecorators(classPath) { + if (!hasClassDecorators(classPath.node)) return; + + const decorators = classPath.node.decorators || []; + classPath.node.decorators = null; + + const name = classPath.scope.generateDeclaredUidIdentifier("class"); + + return decorators + .map(dec => dec.callee) + .reverse() + .reduce(function(acc, decorator) { + return buildClassDecorator({ + CLASS_REF: t.cloneNode(name), + DECORATOR: t.cloneNode(decorator), + INNER: acc, + }).expression; + }, classPath.node); +} + +function hasClassDecorators(classNode) { + return !!(classNode.decorators && classNode.decorators.length); +} + +/** + * Given a class expression with method-level decorators, create a new expression + * with the proper decorated behavior. + */ +function applyMethodDecorators(path, state) { + if (!hasMethodDecorators(path.node.body.body)) return; + + return applyTargetDecorators(path, state, path.node.body.body); +} + +function hasMethodDecorators(body) { + return body.some(node => node.decorators && node.decorators.length); +} + +/** + * Given an object expression with property decorators, create a new expression + * with the proper decorated behavior. + */ +function applyObjectDecorators(path, state) { + if (!hasMethodDecorators(path.node.properties)) return; + + return applyTargetDecorators(path, state, path.node.properties); +} + +/** + * A helper to pull out property decorators into a sequence expression. + */ +function applyTargetDecorators(path, state, decoratedProps) { + const name = path.scope.generateDeclaredUidIdentifier( + path.isClass() ? "class" : "obj", + ); + + const exprs = decoratedProps.reduce(function(acc, node) { + const decorators = node.decorators || []; + node.decorators = null; + + if (decorators.length === 0) return acc; + + if (node.computed) { + throw path.buildCodeFrameError( + "Computed method/property decorators are not yet supported.", + ); + } + + const property = t.isLiteral(node.key) + ? node.key + : t.stringLiteral(node.key.name); + + const target = + path.isClass() && !node.static + ? buildClassPrototype({ + CLASS_REF: name, + }).expression + : name; + + if (t.isClassProperty(node, { static: false })) { + const descriptor = path.scope.generateDeclaredUidIdentifier("descriptor"); + + const initializer = node.value + ? t.functionExpression( + null, + [], + t.blockStatement([t.returnStatement(node.value)]), + ) + : t.nullLiteral(); + + node.value = t.callExpression( + state.addHelper("initializerWarningHelper"), + [descriptor, t.thisExpression()], + ); + + WARNING_CALLS.add(node.value); + + acc = acc.concat([ + t.assignmentExpression( + "=", + descriptor, + t.callExpression(state.addHelper("applyDecoratedDescriptor"), [ + t.cloneNode(target), + t.cloneNode(property), + t.arrayExpression(decorators.map(dec => t.cloneNode(dec.callee))), + t.objectExpression([ + t.objectProperty( + t.identifier("enumerable"), + t.booleanLiteral(true), + ), + t.objectProperty(t.identifier("initializer"), initializer), + ]), + ]), + ), + ]); + } else { + acc = acc.concat( + t.callExpression(state.addHelper("applyDecoratedDescriptor"), [ + t.cloneNode(target), + t.cloneNode(property), + t.arrayExpression(decorators.map(dec => t.cloneNode(dec.callee))), + t.isObjectProperty(node) || t.isClassProperty(node, { static: true }) + ? buildGetObjectInitializer({ + TEMP: path.scope.generateDeclaredUidIdentifier("init"), + TARGET: t.cloneNode(target), + PROPERTY: t.cloneNode(property), + }).expression + : buildGetDescriptor({ + TARGET: t.cloneNode(target), + PROPERTY: t.cloneNode(property), + }).expression, + t.cloneNode(target), + ]), + ); + } + + return acc; + }, []); + + return t.sequenceExpression([ + t.assignmentExpression("=", t.cloneNode(name), path.node), + t.sequenceExpression(exprs), + t.cloneNode(name), + ]); +} + +export default { + ClassDeclaration(path) { + const { node } = path; + + if (!hasClassDecorators(node) && !hasMethodDecorators(node.body.body)) { + return; + } + + const ref = node.id + ? t.cloneNode(node.id) + : path.scope.generateUidIdentifier("class"); + const letDeclaration = t.variableDeclaration("let", [ + t.variableDeclarator(ref, t.toExpression(node)), + ]); + + if (path.parentPath.isExportDefaultDeclaration()) { + // Split the class declaration and the export into two separate statements. + path.parentPath.replaceWithMultiple([ + letDeclaration, + t.exportNamedDeclaration(null, [ + t.exportSpecifier(t.cloneNode(ref), t.identifier("default")), + ]), + ]); + } else { + path.replaceWith(letDeclaration); + } + }, + ClassExpression(path, state) { + // Create a replacement for the class node if there is one. We do one pass to replace classes with + // class decorators, and a second pass to process method decorators. + const decoratedClass = + applyEnsureOrdering(path) || + applyClassDecorators(path, state) || + applyMethodDecorators(path, state); + + if (decoratedClass) path.replaceWith(decoratedClass); + }, + ObjectExpression(path, state) { + const decoratedObject = + applyEnsureOrdering(path) || applyObjectDecorators(path, state); + + if (decoratedObject) path.replaceWith(decoratedObject); + }, + + AssignmentExpression(path, state) { + if (!WARNING_CALLS.has(path.node.right)) return; + + path.replaceWith( + t.callExpression(state.addHelper("initializerDefineProperty"), [ + t.cloneNode(path.get("left.object").node), + t.stringLiteral(path.get("left.property").node.name), + t.cloneNode(path.get("right.arguments")[0].node), + t.cloneNode(path.get("right.arguments")[1].node), + ]), + ); + }, +}; diff --git a/packages/babel-plugin-proposal-decorators/src/transformer.js b/packages/babel-plugin-proposal-decorators/src/transformer.js new file mode 100644 index 0000000000..8dab9cadab --- /dev/null +++ b/packages/babel-plugin-proposal-decorators/src/transformer.js @@ -0,0 +1,3 @@ +// Not implemented yet + +export default {}; diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/decl-to-expression/options.json b/packages/babel-plugin-proposal-decorators/test/fixtures/decl-to-expression/options.json index f2cf7da30b..ea69269c5d 100644 --- a/packages/babel-plugin-proposal-decorators/test/fixtures/decl-to-expression/options.json +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/decl-to-expression/options.json @@ -1,4 +1,4 @@ { "presets": [], - "plugins": ["proposal-decorators"] + "plugins": [["proposal-decorators", { "legacy": true }]] } \ No newline at end of file diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/options.json b/packages/babel-plugin-proposal-decorators/test/fixtures/options.json index 9a5cc94a73..c5b3178ebf 100644 --- a/packages/babel-plugin-proposal-decorators/test/fixtures/options.json +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/options.json @@ -1,4 +1,7 @@ { "presets": ["es2015"], - "plugins": ["proposal-decorators", ["proposal-class-properties", {"loose": true}]] + "plugins": [ + ["proposal-decorators", { "legacy": true }], + ["proposal-class-properties", { "loose": true }] + ] } diff --git a/packages/babel-plugin-syntax-decorators/README.md b/packages/babel-plugin-syntax-decorators/README.md index adca25dcd3..a062755761 100644 --- a/packages/babel-plugin-syntax-decorators/README.md +++ b/packages/babel-plugin-syntax-decorators/README.md @@ -33,3 +33,11 @@ require("@babel/core").transform("code", { plugins: ["@babel/plugin-syntax-decorators"] }); ``` + +## Options + +### `legacy` + +`boolean`, defaults to `false`. + +Use the legacy (stage 1) decorators syntax. diff --git a/packages/babel-plugin-syntax-decorators/src/index.js b/packages/babel-plugin-syntax-decorators/src/index.js index 950d6ebc51..5db5628955 100644 --- a/packages/babel-plugin-syntax-decorators/src/index.js +++ b/packages/babel-plugin-syntax-decorators/src/index.js @@ -1,11 +1,16 @@ import { declare } from "@babel/helper-plugin-utils"; -export default declare(api => { +export default declare((api, options) => { api.assertVersion(7); + const { legacy = false } = options; + if (typeof legacy !== "boolean") { + throw new Error("'legacy' must be a boolean."); + } + return { manipulateOptions(opts, parserOpts) { - parserOpts.plugins.push("decorators"); + parserOpts.plugins.push(legacy ? "decorators" : "decorators2"); }, }; }); diff --git a/packages/babel-preset-stage-1/src/index.js b/packages/babel-preset-stage-1/src/index.js index 9a4ee11118..7ae22ea366 100644 --- a/packages/babel-preset-stage-1/src/index.js +++ b/packages/babel-preset-stage-1/src/index.js @@ -32,7 +32,7 @@ export default declare((api, opts) => { return { presets: [[presetStage2, { loose, useBuiltIns }]], plugins: [ - transformDecorators, + [transformDecorators, { legacy: true }], transformExportDefaultFrom, transformLogicalAssignmentOperators, [transformOptionalChaining, { loose }], From 2679d6775cc957e6cc77f23da0e31cd24bbfd18f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Ribaudo?= Date: Sun, 15 Apr 2018 10:54:46 +0200 Subject: [PATCH 2/4] Add "decoratorsLegacy" to presets --- packages/babel-preset-stage-0/README.md | 6 +++++ packages/babel-preset-stage-0/src/index.js | 25 ++++++++++++-------- packages/babel-preset-stage-1/README.md | 6 +++++ packages/babel-preset-stage-1/package.json | 1 - packages/babel-preset-stage-1/src/index.js | 27 +++++++++++++--------- packages/babel-preset-stage-2/README.md | 6 +++++ packages/babel-preset-stage-2/package.json | 1 + packages/babel-preset-stage-2/src/index.js | 25 +++++++++++++------- 8 files changed, 68 insertions(+), 29 deletions(-) diff --git a/packages/babel-preset-stage-0/README.md b/packages/babel-preset-stage-0/README.md index b67d1259e2..42d1db872d 100644 --- a/packages/babel-preset-stage-0/README.md +++ b/packages/babel-preset-stage-0/README.md @@ -47,3 +47,9 @@ Enable "loose" transformations for any plugins in this preset that allow them. `boolean`, defaults to `false`. Will use the native built-in instead of trying to polyfill behavior for any plugins that require one. + +### `decoratorsLegacy` + +`boolean`, defaults to `false`. + +Use the legacy (stage 1) decorators syntax and behavior. diff --git a/packages/babel-preset-stage-0/src/index.js b/packages/babel-preset-stage-0/src/index.js index fbcf5f3df1..25bd1f26e7 100644 --- a/packages/babel-preset-stage-0/src/index.js +++ b/packages/babel-preset-stage-0/src/index.js @@ -3,16 +3,10 @@ import presetStage1 from "@babel/preset-stage-1"; import transformFunctionBind from "@babel/plugin-proposal-function-bind"; -export default declare((api, opts) => { +export default declare((api, opts = {}) => { api.assertVersion(7); - let loose = false; - let useBuiltIns = false; - - if (opts !== undefined) { - if (opts.loose !== undefined) loose = opts.loose; - if (opts.useBuiltIns !== undefined) useBuiltIns = opts.useBuiltIns; - } + const { loose = false, useBuiltIns = false, decoratorsLegacy = false } = opts; if (typeof loose !== "boolean") { throw new Error("@babel/preset-stage-0 'loose' option must be a boolean."); @@ -22,9 +16,22 @@ export default declare((api, opts) => { "@babel/preset-stage-0 'useBuiltIns' option must be a boolean.", ); } + if (typeof decoratorsLegacy !== "boolean") { + throw new Error( + "@babel/preset-stage-0 'decoratorsLegacy' option must be a boolean.", + ); + } + + if (decoratorsLegacy !== true) { + throw new Error( + "The new decorators proposal is not supported yet." + + ' You muse pass the `"decoratorsLegacy": true` option to' + + " @babel/preset-stage-0", + ); + } return { - presets: [[presetStage1, { loose, useBuiltIns }]], + presets: [[presetStage1, { loose, useBuiltIns, decoratorsLegacy }]], plugins: [transformFunctionBind], }; }); diff --git a/packages/babel-preset-stage-1/README.md b/packages/babel-preset-stage-1/README.md index 735f6dd90b..0166562289 100644 --- a/packages/babel-preset-stage-1/README.md +++ b/packages/babel-preset-stage-1/README.md @@ -58,6 +58,12 @@ Enable "loose" transformations for any plugins in this preset that allow them. Will use the native built-in instead of trying to polyfill behavior for any plugins that require one. +### `decoratorsLegacy` + +`boolean`, defaults to `false`. + +Use the legacy (stage 1) decorators syntax and behavior. + ## References - Chapter "[The TC39 process for ECMAScript features](http://exploringjs.com/es2016-es2017/ch_tc39-process.html)" in "Exploring ES2016 and ES2017" by Axel Rauschmayer diff --git a/packages/babel-preset-stage-1/package.json b/packages/babel-preset-stage-1/package.json index 820e92916e..a1da8a5475 100644 --- a/packages/babel-preset-stage-1/package.json +++ b/packages/babel-preset-stage-1/package.json @@ -9,7 +9,6 @@ "main": "lib/index.js", "dependencies": { "@babel/helper-plugin-utils": "7.0.0-beta.44", - "@babel/plugin-proposal-decorators": "7.0.0-beta.44", "@babel/plugin-proposal-do-expressions": "7.0.0-beta.44", "@babel/plugin-proposal-export-default-from": "7.0.0-beta.44", "@babel/plugin-proposal-logical-assignment-operators": "7.0.0-beta.44", diff --git a/packages/babel-preset-stage-1/src/index.js b/packages/babel-preset-stage-1/src/index.js index 7ae22ea366..31b2e88a46 100644 --- a/packages/babel-preset-stage-1/src/index.js +++ b/packages/babel-preset-stage-1/src/index.js @@ -1,7 +1,6 @@ import { declare } from "@babel/helper-plugin-utils"; import presetStage2 from "@babel/preset-stage-2"; -import transformDecorators from "@babel/plugin-proposal-decorators"; import transformExportDefaultFrom from "@babel/plugin-proposal-export-default-from"; import transformLogicalAssignmentOperators from "@babel/plugin-proposal-logical-assignment-operators"; import transformOptionalChaining from "@babel/plugin-proposal-optional-chaining"; @@ -9,16 +8,10 @@ import transformPipelineOperator from "@babel/plugin-proposal-pipeline-operator" import transformNullishCoalescingOperator from "@babel/plugin-proposal-nullish-coalescing-operator"; import transformDoExpressions from "@babel/plugin-proposal-do-expressions"; -export default declare((api, opts) => { +export default declare((api, opts = {}) => { api.assertVersion(7); - let loose = false; - let useBuiltIns = false; - - if (opts !== undefined) { - if (opts.loose !== undefined) loose = opts.loose; - if (opts.useBuiltIns !== undefined) useBuiltIns = opts.useBuiltIns; - } + const { loose = false, useBuiltIns = false, decoratorsLegacy = false } = opts; if (typeof loose !== "boolean") { throw new Error("@babel/preset-stage-1 'loose' option must be a boolean."); @@ -28,11 +21,23 @@ export default declare((api, opts) => { "@babel/preset-stage-1 'useBuiltIns' option must be a boolean.", ); } + if (typeof decoratorsLegacy !== "boolean") { + throw new Error( + "@babel/preset-stage-1 'decoratorsLegacy' option must be a boolean.", + ); + } + + if (decoratorsLegacy !== true) { + throw new Error( + "The new decorators proposal is not supported yet." + + ' You muse pass the `"decoratorsLegacy": true` option to' + + " @babel/preset-stage-1", + ); + } return { - presets: [[presetStage2, { loose, useBuiltIns }]], + presets: [[presetStage2, { loose, useBuiltIns, decoratorsLegacy }]], plugins: [ - [transformDecorators, { legacy: true }], transformExportDefaultFrom, transformLogicalAssignmentOperators, [transformOptionalChaining, { loose }], diff --git a/packages/babel-preset-stage-2/README.md b/packages/babel-preset-stage-2/README.md index 60131d788a..10342ad4db 100644 --- a/packages/babel-preset-stage-2/README.md +++ b/packages/babel-preset-stage-2/README.md @@ -58,6 +58,12 @@ Enable "loose" transformations for any plugins in this preset that allow them. Will use the native built-in instead of trying to polyfill behavior for any plugins that require one. +### `decoratorsLegacy` + +`boolean`, defaults to `false`. + +Use the legacy (stage 1) decorators syntax and behavior. + ## References - Chapter "[The TC39 process for ECMAScript features](http://exploringjs.com/es2016-es2017/ch_tc39-process.html)" in "Exploring ES2016 and ES2017" by Axel Rauschmayer diff --git a/packages/babel-preset-stage-2/package.json b/packages/babel-preset-stage-2/package.json index 946574bde0..3d789a5601 100644 --- a/packages/babel-preset-stage-2/package.json +++ b/packages/babel-preset-stage-2/package.json @@ -9,6 +9,7 @@ "main": "lib/index.js", "dependencies": { "@babel/helper-plugin-utils": "7.0.0-beta.44", + "@babel/plugin-proposal-decorators": "7.0.0-beta.44", "@babel/plugin-proposal-export-namespace-from": "7.0.0-beta.44", "@babel/plugin-proposal-function-sent": "7.0.0-beta.44", "@babel/plugin-proposal-numeric-separator": "7.0.0-beta.44", diff --git a/packages/babel-preset-stage-2/src/index.js b/packages/babel-preset-stage-2/src/index.js index 22c1fd81bd..fa87562bf9 100644 --- a/packages/babel-preset-stage-2/src/index.js +++ b/packages/babel-preset-stage-2/src/index.js @@ -1,21 +1,16 @@ import { declare } from "@babel/helper-plugin-utils"; import presetStage3 from "@babel/preset-stage-3"; +import transformDecorators from "@babel/plugin-proposal-decorators"; import transformFunctionSent from "@babel/plugin-proposal-function-sent"; import transformExportNamespaceFrom from "@babel/plugin-proposal-export-namespace-from"; import transformNumericSeparator from "@babel/plugin-proposal-numeric-separator"; import transformThrowExpressions from "@babel/plugin-proposal-throw-expressions"; -export default declare((api, opts) => { +export default declare((api, opts = {}) => { api.assertVersion(7); - let loose = false; - let useBuiltIns = false; - - if (opts !== undefined) { - if (opts.loose !== undefined) loose = opts.loose; - if (opts.useBuiltIns !== undefined) useBuiltIns = opts.useBuiltIns; - } + const { loose = false, useBuiltIns = false, decoratorsLegacy = false } = opts; if (typeof loose !== "boolean") { throw new Error("@babel/preset-stage-2 'loose' option must be a boolean."); @@ -25,10 +20,24 @@ export default declare((api, opts) => { "@babel/preset-stage-2 'useBuiltIns' option must be a boolean.", ); } + if (typeof decoratorsLegacy !== "boolean") { + throw new Error( + "@babel/preset-stage-2 'decoratorsLegacy' option must be a boolean.", + ); + } + + if (decoratorsLegacy !== true) { + throw new Error( + "The new decorators proposal is not supported yet." + + ' You muse pass the `"decoratorsLegacy": true` option to' + + " @babel/preset-stage-2", + ); + } return { presets: [[presetStage3, { loose, useBuiltIns }]], plugins: [ + [transformDecorators, { legacy: decoratorsLegacy }], transformFunctionSent, transformExportNamespaceFrom, transformNumericSeparator, From 29db23ed878a5ebdd3dc2dffe9eb88535f3c2875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Ribaudo?= Date: Sun, 15 Apr 2018 11:09:28 +0200 Subject: [PATCH 3/4] Remove old comment --- packages/babel-plugin-proposal-decorators/src/index.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/babel-plugin-proposal-decorators/src/index.js b/packages/babel-plugin-proposal-decorators/src/index.js index be15f039be..a7e9c5486a 100644 --- a/packages/babel-plugin-proposal-decorators/src/index.js +++ b/packages/babel-plugin-proposal-decorators/src/index.js @@ -1,5 +1,3 @@ -// Fork of https://github.com/loganfsmyth/babel-plugin-proposal-decorators-legacy - import { declare } from "@babel/helper-plugin-utils"; import syntaxDecorators from "@babel/plugin-syntax-decorators"; import visitor from "./transformer"; From 0cd868a5f2bd0fbb0ddd2a2473f51eeef93d5185 Mon Sep 17 00:00:00 2001 From: Brian Ng Date: Thu, 19 Apr 2018 09:17:33 -0500 Subject: [PATCH 4/4] fixup tests --- .../transformation/misc/regression-4855/options.json | 5 ++++- .../test/fixtures/public-loose/foobar/options.json | 5 ++++- .../test/fixtures/public/foobar/options.json | 5 ++++- .../test/fixtures/regression/6154/options.json | 5 ++++- .../test/fixtures/regression/7030/options.json | 5 ++++- .../test/fixtures/function-name/modules-2/options.json | 8 +++++++- .../test/fixtures/function-name/modules-3/options.json | 8 +++++++- .../test/fixtures/function-name/modules/options.json | 8 +++++++- .../test/fixtures/function-name/options.json | 7 ++++++- 9 files changed, 47 insertions(+), 9 deletions(-) diff --git a/packages/babel-core/test/fixtures/transformation/misc/regression-4855/options.json b/packages/babel-core/test/fixtures/transformation/misc/regression-4855/options.json index 797622c330..e8683e2506 100644 --- a/packages/babel-core/test/fixtures/transformation/misc/regression-4855/options.json +++ b/packages/babel-core/test/fixtures/transformation/misc/regression-4855/options.json @@ -1,4 +1,7 @@ { "compact": false, - "presets": ["es2015","stage-2"] + "presets": [ + "es2015", + ["stage-2", { "decoratorsLegacy": true }] + ] } diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/public-loose/foobar/options.json b/packages/babel-plugin-proposal-class-properties/test/fixtures/public-loose/foobar/options.json index 49003d6aa0..f5a0eb1d16 100644 --- a/packages/babel-plugin-proposal-class-properties/test/fixtures/public-loose/foobar/options.json +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/public-loose/foobar/options.json @@ -1,4 +1,7 @@ { "plugins": ["external-helpers", ["proposal-class-properties", {"loose": true}]], - "presets": ["stage-0", "es2015"] + "presets": [ + ["stage-0", { "decoratorsLegacy": true }], + "es2015" + ] } diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/public/foobar/options.json b/packages/babel-plugin-proposal-class-properties/test/fixtures/public/foobar/options.json index 6d4c71cef4..e0415a0526 100644 --- a/packages/babel-plugin-proposal-class-properties/test/fixtures/public/foobar/options.json +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/public/foobar/options.json @@ -1,4 +1,7 @@ { "plugins": ["external-helpers", "proposal-class-properties"], - "presets": ["stage-0", "es2015"] + "presets": [ + ["stage-0", { "decoratorsLegacy": true }], + "es2015" + ] } diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/regression/6154/options.json b/packages/babel-plugin-proposal-class-properties/test/fixtures/regression/6154/options.json index 05581748b9..265cb856e3 100644 --- a/packages/babel-plugin-proposal-class-properties/test/fixtures/regression/6154/options.json +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/regression/6154/options.json @@ -1,3 +1,6 @@ { - "presets": ["es2015", "stage-2"] + "presets": [ + "es2015", + ["stage-2", { "decoratorsLegacy": true }] + ] } diff --git a/packages/babel-plugin-proposal-decorators/test/fixtures/regression/7030/options.json b/packages/babel-plugin-proposal-decorators/test/fixtures/regression/7030/options.json index eaf32387b5..64d67dac24 100644 --- a/packages/babel-plugin-proposal-decorators/test/fixtures/regression/7030/options.json +++ b/packages/babel-plugin-proposal-decorators/test/fixtures/regression/7030/options.json @@ -1,3 +1,6 @@ { - "presets": ["es2015", "stage-0"] + "presets": [ + "es2015", + ["stage-0", { "decoratorsLegacy": true }] + ] } diff --git a/packages/babel-plugin-transform-function-name/test/fixtures/function-name/modules-2/options.json b/packages/babel-plugin-transform-function-name/test/fixtures/function-name/modules-2/options.json index 2f38126a16..129fe5ba31 100644 --- a/packages/babel-plugin-transform-function-name/test/fixtures/function-name/modules-2/options.json +++ b/packages/babel-plugin-transform-function-name/test/fixtures/function-name/modules-2/options.json @@ -1,3 +1,9 @@ { - "plugins": ["external-helpers", "transform-function-name", "transform-classes", "proposal-decorators", "transform-modules-commonjs"] + "plugins": [ + "external-helpers", + "transform-function-name", + "transform-classes", + ["proposal-decorators", { "legacy": true }], + "transform-modules-commonjs" + ] } diff --git a/packages/babel-plugin-transform-function-name/test/fixtures/function-name/modules-3/options.json b/packages/babel-plugin-transform-function-name/test/fixtures/function-name/modules-3/options.json index ec5ef44618..acc2af3bf3 100644 --- a/packages/babel-plugin-transform-function-name/test/fixtures/function-name/modules-3/options.json +++ b/packages/babel-plugin-transform-function-name/test/fixtures/function-name/modules-3/options.json @@ -1,3 +1,9 @@ { - "plugins": ["external-helpers", "transform-function-name", "transform-modules-commonjs", "transform-classes", "proposal-decorators"] + "plugins": [ + "external-helpers", + "transform-function-name", + "transform-modules-commonjs", + "transform-classes", + ["proposal-decorators", { "legacy": true }] + ] } diff --git a/packages/babel-plugin-transform-function-name/test/fixtures/function-name/modules/options.json b/packages/babel-plugin-transform-function-name/test/fixtures/function-name/modules/options.json index ec5ef44618..acc2af3bf3 100644 --- a/packages/babel-plugin-transform-function-name/test/fixtures/function-name/modules/options.json +++ b/packages/babel-plugin-transform-function-name/test/fixtures/function-name/modules/options.json @@ -1,3 +1,9 @@ { - "plugins": ["external-helpers", "transform-function-name", "transform-modules-commonjs", "transform-classes", "proposal-decorators"] + "plugins": [ + "external-helpers", + "transform-function-name", + "transform-modules-commonjs", + "transform-classes", + ["proposal-decorators", { "legacy": true }] + ] } diff --git a/packages/babel-plugin-transform-function-name/test/fixtures/function-name/options.json b/packages/babel-plugin-transform-function-name/test/fixtures/function-name/options.json index 30be0dc181..14c812c3c4 100644 --- a/packages/babel-plugin-transform-function-name/test/fixtures/function-name/options.json +++ b/packages/babel-plugin-transform-function-name/test/fixtures/function-name/options.json @@ -1,3 +1,8 @@ { - "plugins": ["external-helpers", "transform-function-name", "transform-classes", "proposal-decorators"] + "plugins": [ + "external-helpers", + "transform-function-name", + "transform-classes", + ["proposal-decorators", { "legacy": true }] + ] }