From 3d8e88912adcbc9db51963bf41739d543283caf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Mon, 18 Jun 2018 10:55:18 +0200 Subject: [PATCH] No duplicated ast nodes (#730) **Checklist**: - [ ] Documentation - [x] Tests - [x] Code complete fixes #726 --- package.json | 1 + packages/babel-plugin-emotion/package.json | 3 +- .../babel-plugin-emotion/src/babel-utils.js | 9 +- .../test/__snapshots__/css-prop.test.js.snap | 2 +- .../test/__snapshots__/macro.test.js.snap | 20 ++++ .../babel-plugin-emotion/test/macro.test.js | 37 +++++- packages/babel-plugin-emotion/test/util.js | 97 ++++++++------- yarn.lock | 111 ++---------------- 8 files changed, 129 insertions(+), 151 deletions(-) diff --git a/package.json b/package.json index 0afa9c79d..26416c36b 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "@babel/preset-flow": "7.0.0-beta.40", "@babel/preset-react": "7.0.0-beta.40", "@babel/preset-stage-0": "7.0.0-beta.40", + "babel-check-duplicated-nodes": "^1.0.0", "babel-cli": "^6.24.1", "babel-core": "^6.24.1", "babel-eslint": "^8.2.3", diff --git a/packages/babel-plugin-emotion/package.json b/packages/babel-plugin-emotion/package.json index 925d8060f..50497fb3a 100644 --- a/packages/babel-plugin-emotion/package.json +++ b/packages/babel-plugin-emotion/package.json @@ -28,7 +28,8 @@ "touch": "^1.0.0" }, "devDependencies": { - "@babel/core": "7.0.0-beta.32", + "@babel/core": "7.0.0-beta.40", + "babel-check-duplicated-nodes": "^1.0.0", "babel-cli": "^6.24.1", "npm-run-all": "^4.0.2", "rimraf": "^2.6.1" diff --git a/packages/babel-plugin-emotion/src/babel-utils.js b/packages/babel-plugin-emotion/src/babel-utils.js index d259e8aca..2411b7c15 100644 --- a/packages/babel-plugin-emotion/src/babel-utils.js +++ b/packages/babel-plugin-emotion/src/babel-utils.js @@ -4,6 +4,10 @@ import { hashArray } from './index' import type { BabelPath, EmotionBabelPluginPass } from './index' import type { Types, Identifier } from 'babel-flow-types' +function cloneNode(t, node) { + return (typeof t.cloneNode === 'function' ? t.cloneNode : t.cloneDeep)(node) +} + function getDeclaratorName(path: BabelPath, t: Types) { // $FlowFixMe const parent = path.findParent(p => p.isVariableDeclarator()) @@ -54,7 +58,7 @@ export function buildMacroRuntimeNode( state: EmotionMacroPluginPass, importName: string, t: Types -) { +): Identifier { const runtimeImportPath = getRuntimeImportPath(path, t) if (state.emotionImports === undefined) state.emotionImports = {} if (state.emotionImports[runtimeImportPath] === undefined) { @@ -66,7 +70,8 @@ export function buildMacroRuntimeNode( importName ] = path.scope.generateUidIdentifier(path.node.name) } - return state.emotionImports[runtimeImportPath][importName] + // $FlowFixMe + return cloneNode(t, state.emotionImports[runtimeImportPath][importName]) } export function addRuntimeImports(state: EmotionMacroPluginPass, t: Types) { diff --git a/packages/babel-plugin-emotion/test/__snapshots__/css-prop.test.js.snap b/packages/babel-plugin-emotion/test/__snapshots__/css-prop.test.js.snap index 86db0ec4e..4f8581246 100644 --- a/packages/babel-plugin-emotion/test/__snapshots__/css-prop.test.js.snap +++ b/packages/babel-plugin-emotion/test/__snapshots__/css-prop.test.js.snap @@ -356,7 +356,7 @@ var _emotion = require(\\"emotion\\"); var _react = _interopRequireWildcard(require(\\"react\\")); -function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } +function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = Object.defineProperty && Object.getOwnPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : {}; if (desc.get || desc.set) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } } newObj.default = obj; return newObj; } } class Home extends _react.Component { render() { diff --git a/packages/babel-plugin-emotion/test/__snapshots__/macro.test.js.snap b/packages/babel-plugin-emotion/test/__snapshots__/macro.test.js.snap index 2465810db..7d5a26238 100644 --- a/packages/babel-plugin-emotion/test/__snapshots__/macro.test.js.snap +++ b/packages/babel-plugin-emotion/test/__snapshots__/macro.test.js.snap @@ -241,3 +241,23 @@ _styled(\\"div\\", { target: \\"eo9rakq0\\" })(\\"display:flex;\\");" `; + +exports[`styled macro inserts unique AST nodes 1`] = ` +"\\"use strict\\"; + +Object.defineProperty(exports, \\"__esModule\\", { + value: true +}); +exports.demibold = exports.normal = void 0; + +var _styled = require(\\"./styled\\"); + +const normal = +/*#__PURE__*/ +(0, _styled.css)(\\"font-weight:400;\\"); +exports.normal = normal; +const demibold = +/*#__PURE__*/ +(0, _styled.css)(\\"font-weight:600;\\"); +exports.demibold = demibold;" +`; diff --git a/packages/babel-plugin-emotion/test/macro.test.js b/packages/babel-plugin-emotion/test/macro.test.js index 333066191..5322f77d1 100644 --- a/packages/babel-plugin-emotion/test/macro.test.js +++ b/packages/babel-plugin-emotion/test/macro.test.js @@ -1,6 +1,7 @@ // @flow import * as babel6 from 'babel-core' import * as babel7 from '@babel/core' +import checkDuplicatedNodes from 'babel-check-duplicated-nodes' import { createMacroTests } from './util' const styledCases = { @@ -73,13 +74,13 @@ const cases = { padding: 0; & > div { display: none; - + &:hover { color: green; - + & span { color: red; - + &:after { content: "end of line" } @@ -232,4 +233,34 @@ describe('styled macro', () => { }) ).toThrowError(/the emotion macro must be imported with es modules/) }) + + test('inserts unique AST nodes', () => { + const input = ` + import { css } from './styled/macro' + + const normal = css\` + font-weight: 400; + \` + + const demibold = css\` + font-weight: 600; + \` + + export { normal, demibold } + ` + + const { ast, code } = babel7.transform(input, { + plugins: [ + 'module:babel-plugin-macros', + '@babel/plugin-transform-modules-commonjs' + ], + filename: __filename, + babelrc: false, + ast: true + }) + + expect(code).toMatchSnapshot() + + expect(() => checkDuplicatedNodes(babel7, ast)).not.toThrow() + }) }) diff --git a/packages/babel-plugin-emotion/test/util.js b/packages/babel-plugin-emotion/test/util.js index 9219503c6..2018a8abe 100644 --- a/packages/babel-plugin-emotion/test/util.js +++ b/packages/babel-plugin-emotion/test/util.js @@ -1,10 +1,11 @@ // @flow /* eslint-env jest */ import plugin from 'babel-plugin-emotion' -import { transform as babel6Transform } from 'babel-core' -import { transform as babel7Transform } from '@babel/core' +import * as babel6 from 'babel-core' +import * as babel7 from '@babel/core' import stage2 from 'babel-plugin-syntax-object-rest-spread' import makeCases from 'jest-in-case' +import checkDuplicatedNodes from 'babel-check-duplicated-nodes' import { basename } from 'path' import * as fs from 'fs' @@ -25,38 +26,43 @@ jest.mock('fs') fs.existsSync.mockReturnValue(true) fs.statSync.mockReturnValue({ isFile: () => false }) -const createInlineTester = transform => opts => { +const isBabel7 = babel => parseInt(babel.version.split('.')[0], 10) === 7 + +const createInlineTester = babel => opts => { if (!opts.opts) opts.opts = {} - expect( - transform(opts.code, { - plugins: [ - stage2, - [ - plugin, - { - ...opts.opts - } - ] - ], - filename: opts.filename !== undefined ? opts.filename : 'emotion.js', - babelrc: false - }).code - ).toMatchSnapshot() + const { code, ast } = babel.transform(opts.code, { + plugins: [ + stage2, + [ + plugin, + { + ...opts.opts + } + ] + ], + filename: opts.filename !== undefined ? opts.filename : 'emotion.js', + babelrc: false, + ast: true + }) + if (isBabel7(babel)) { + expect(() => checkDuplicatedNodes(babel, ast)).not.toThrow() + } + expect(code).toMatchSnapshot() } export const createInlineTests = (title: string, cases: EmotionTestCases) => { describe(title, () => { - makeCases('babel 6', createInlineTester(babel6Transform), cases) - makeCases('babel 7', createInlineTester(babel7Transform), cases) + makeCases('babel 6', createInlineTester(babel6), cases) + makeCases('babel 7', createInlineTester(babel7), cases) }) } -const createExtractTester = transform => opts => { +const createExtractTester = babel => opts => { fs.writeFileSync.mockClear() let extract = true if (opts.extract === false) extract = false if (!opts.opts) opts.opts = {} - const { code } = transform(opts.code, { + const { code, ast } = babel.transform(opts.code, { plugins: [ stage2, [ @@ -68,8 +74,12 @@ const createExtractTester = transform => opts => { ] ], filename: opts.filename || 'emotion.js', - babelrc: false + babelrc: false, + ast: true }) + if (isBabel7(babel)) { + expect(() => checkDuplicatedNodes(babel, ast)).not.toThrow() + } if (extract) { expect( code + @@ -86,32 +96,35 @@ const createExtractTester = transform => opts => { export const createExtractTests = (title: string, cases: EmotionTestCases) => { describe(title, () => { - makeCases('babel 6', createExtractTester(babel6Transform), cases) - makeCases('babel 7', createExtractTester(babel7Transform), cases) + makeCases('babel 6', createExtractTester(babel6), cases) + makeCases('babel 7', createExtractTester(babel7), cases) }) } -const createMacroTester = transform => opts => { +const createMacroTester = babel => opts => { if (!opts.opts) opts.opts = {} - expect( - transform(opts.code, { - plugins: [ - [ - require('babel-plugin-macros'), - { - ...opts.opts - } - ] - ], - babelrc: false, - filename: opts.filename || __filename - }).code - ).toMatchSnapshot() + const { code, ast } = babel.transform(opts.code, { + plugins: [ + [ + require('babel-plugin-macros'), + { + ...opts.opts + } + ] + ], + babelrc: false, + filename: opts.filename || __filename, + ast: true + }) + if (isBabel7(babel)) { + expect(() => checkDuplicatedNodes(babel, ast)).not.toThrow() + } + expect(code).toMatchSnapshot() } export const createMacroTests = (title: string, cases: EmotionTestCases) => { describe(title, () => { - makeCases('babel 6', createMacroTester(babel6Transform), cases) - makeCases('babel 7', createMacroTester(babel7Transform), cases) + makeCases('babel 6', createMacroTester(babel6), cases) + makeCases('babel 7', createMacroTester(babel7), cases) }) } diff --git a/yarn.lock b/yarn.lock index c35be25e3..d9d875222 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,14 +2,6 @@ # yarn lockfile v1 -"@babel/code-frame@7.0.0-beta.32": - version "7.0.0-beta.32" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0-beta.32.tgz#04f231b8ec70370df830d9926ce0f5add074ec4c" - dependencies: - chalk "^2.0.0" - esutils "^2.0.2" - js-tokens "^3.0.0" - "@babel/code-frame@7.0.0-beta.40": version "7.0.0-beta.40" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.0.0-beta.40.tgz#37e2b0cf7c56026b4b21d3927cadf81adec32ac6" @@ -28,25 +20,6 @@ dependencies: "@babel/highlight" "7.0.0-beta.49" -"@babel/core@7.0.0-beta.32": - version "7.0.0-beta.32" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.0.0-beta.32.tgz#cc927d7d78a10d0444adaf08fbbda2ed644822f6" - dependencies: - "@babel/code-frame" "7.0.0-beta.32" - "@babel/generator" "7.0.0-beta.32" - "@babel/helpers" "7.0.0-beta.32" - "@babel/template" "7.0.0-beta.32" - "@babel/traverse" "7.0.0-beta.32" - "@babel/types" "7.0.0-beta.32" - babylon "7.0.0-beta.32" - convert-source-map "^1.1.0" - debug "^3.0.1" - json5 "^0.5.0" - lodash "^4.2.0" - micromatch "^2.3.11" - resolve "^1.3.2" - source-map "^0.5.0" - "@babel/core@7.0.0-beta.40": version "7.0.0-beta.40" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.0.0-beta.40.tgz#455464dd81d499fd97d32b473f0331f74379a33f" @@ -66,16 +39,6 @@ resolve "^1.3.2" source-map "^0.5.0" -"@babel/generator@7.0.0-beta.32": - version "7.0.0-beta.32" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.0.0-beta.32.tgz#37d8124ea7770b4555da28be0917b47f365aca97" - dependencies: - "@babel/types" "7.0.0-beta.32" - jsesc "^2.5.1" - lodash "^4.2.0" - source-map "^0.5.0" - trim-right "^1.0.1" - "@babel/generator@7.0.0-beta.40": version "7.0.0-beta.40" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.0.0-beta.40.tgz#ab61f9556f4f71dbd1138949c795bb9a21e302ea" @@ -139,14 +102,6 @@ "@babel/traverse" "7.0.0-beta.40" "@babel/types" "7.0.0-beta.40" -"@babel/helper-function-name@7.0.0-beta.32": - version "7.0.0-beta.32" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.0.0-beta.32.tgz#6161af4419f1b4e3ed2d28c0c79c160e218be1f3" - dependencies: - "@babel/helper-get-function-arity" "7.0.0-beta.32" - "@babel/template" "7.0.0-beta.32" - "@babel/types" "7.0.0-beta.32" - "@babel/helper-function-name@7.0.0-beta.40": version "7.0.0-beta.40" resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.0.0-beta.40.tgz#9d033341ab16517f40d43a73f2d81fc431ccd7b6" @@ -163,12 +118,6 @@ "@babel/template" "7.0.0-beta.44" "@babel/types" "7.0.0-beta.44" -"@babel/helper-get-function-arity@7.0.0-beta.32": - version "7.0.0-beta.32" - resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0-beta.32.tgz#93721a99db3757de575a83bab7c453299abca568" - dependencies: - "@babel/types" "7.0.0-beta.32" - "@babel/helper-get-function-arity@7.0.0-beta.40": version "7.0.0-beta.40" resolved "https://registry.yarnpkg.com/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0-beta.40.tgz#ac0419cf067b0ec16453e1274f03878195791c6e" @@ -265,14 +214,6 @@ "@babel/traverse" "7.0.0-beta.40" "@babel/types" "7.0.0-beta.40" -"@babel/helpers@7.0.0-beta.32": - version "7.0.0-beta.32" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.0.0-beta.32.tgz#a5ba0032f5a1d9021e7ae1bdb5efaf75f4292162" - dependencies: - "@babel/template" "7.0.0-beta.32" - "@babel/traverse" "7.0.0-beta.32" - "@babel/types" "7.0.0-beta.32" - "@babel/helpers@7.0.0-beta.40": version "7.0.0-beta.40" resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.0.0-beta.40.tgz#82f8e144f56b2896b1d624ca88ac4603023ececd" @@ -763,15 +704,6 @@ "@babel/plugin-syntax-dynamic-import" "7.0.0-beta.40" "@babel/plugin-syntax-import-meta" "7.0.0-beta.40" -"@babel/template@7.0.0-beta.32": - version "7.0.0-beta.32" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.0.0-beta.32.tgz#e1d9fdbd2a7bcf128f2f920744a67dab18072495" - dependencies: - "@babel/code-frame" "7.0.0-beta.32" - "@babel/types" "7.0.0-beta.32" - babylon "7.0.0-beta.32" - lodash "^4.2.0" - "@babel/template@7.0.0-beta.40": version "7.0.0-beta.40" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.0.0-beta.40.tgz#034988c6424eb5c3268fe6a608626de1f4410fc8" @@ -790,19 +722,6 @@ babylon "7.0.0-beta.44" lodash "^4.2.0" -"@babel/traverse@7.0.0-beta.32": - version "7.0.0-beta.32" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.0.0-beta.32.tgz#b78b754c6e1af3360626183738e4c7a05951bc99" - dependencies: - "@babel/code-frame" "7.0.0-beta.32" - "@babel/helper-function-name" "7.0.0-beta.32" - "@babel/types" "7.0.0-beta.32" - babylon "7.0.0-beta.32" - debug "^3.0.1" - globals "^10.0.0" - invariant "^2.2.0" - lodash "^4.2.0" - "@babel/traverse@7.0.0-beta.40": version "7.0.0-beta.40" resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.0.0-beta.40.tgz#d140e449b2e093ef9fe1a2eecc28421ffb4e521e" @@ -832,14 +751,6 @@ invariant "^2.2.0" lodash "^4.2.0" -"@babel/types@7.0.0-beta.32": - version "7.0.0-beta.32" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.0.0-beta.32.tgz#c317d0ecc89297b80bbcb2f50608e31f6452a5ff" - dependencies: - esutils "^2.0.2" - lodash "^4.2.0" - to-fast-properties "^2.0.0" - "@babel/types@7.0.0-beta.40": version "7.0.0-beta.40" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.0.0-beta.40.tgz#25c3d7aae14126abe05fcb098c65a66b6d6b8c14" @@ -1460,6 +1371,10 @@ axios@^0.16.1: follow-redirects "^1.2.3" is-buffer "^1.1.5" +babel-check-duplicated-nodes@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/babel-check-duplicated-nodes/-/babel-check-duplicated-nodes-1.0.0.tgz#a0b9fc7796abb0b69cf5f6f3f91d0f8d06e2aeeb" + babel-cli@^6.24.1, babel-cli@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-cli/-/babel-cli-6.26.0.tgz#502ab54874d7db88ad00b887a06383ce03d002f1" @@ -2382,10 +2297,6 @@ babel-types@^6.18.0, babel-types@^6.19.0, babel-types@^6.24.1, babel-types@^6.26 lodash "^4.17.4" to-fast-properties "^1.0.3" -babylon@7.0.0-beta.32: - version "7.0.0-beta.32" - resolved "https://registry.yarnpkg.com/babylon/-/babylon-7.0.0-beta.32.tgz#e9033cb077f64d6895f4125968b37dc0a8c3bc6e" - babylon@7.0.0-beta.40: version "7.0.0-beta.40" resolved "https://registry.yarnpkg.com/babylon/-/babylon-7.0.0-beta.40.tgz#91fc8cd56d5eb98b28e6fde41045f2957779940a" @@ -3553,7 +3464,7 @@ compressible@~2.0.13: compression@^1.5.2, compression@^1.6.2: version "1.7.2" - resolved "http://registry.npmjs.org/compression/-/compression-1.7.2.tgz#aaffbcd6aaf854b44ebb280353d5ad1651f59a69" + resolved "http://registry.yarnpkg.com/compression/-/compression-1.7.2.tgz#aaffbcd6aaf854b44ebb280353d5ad1651f59a69" dependencies: accepts "~1.3.4" bytes "3.0.0" @@ -5229,7 +5140,7 @@ event-emitter@~0.3.5: event-stream@~3.3.0: version "3.3.4" - resolved "http://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz#4ab4c9a0f5a54db9338b4c34d86bfce8f4b35571" + resolved "http://registry.yarnpkg.com/event-stream/-/event-stream-3.3.4.tgz#4ab4c9a0f5a54db9338b4c34d86bfce8f4b35571" dependencies: duplexer "~0.1.1" from "~0" @@ -6621,10 +6532,6 @@ global@^4.3.0, global@~4.3.0: min-document "^2.19.0" process "~0.5.1" -globals@^10.0.0: - version "10.4.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-10.4.0.tgz#5c477388b128a9e4c5c5d01c7a2aca68c68b2da7" - globals@^11.0.1, globals@^11.1.0: version "11.5.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.5.0.tgz#6bc840de6771173b191f13d3a9c94d441ee92642" @@ -10272,7 +10179,7 @@ onecolor@~2.4.0: onetime@^1.0.0: version "1.1.0" - resolved "http://registry.npmjs.org/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789" + resolved "http://registry.yarnpkg.com/onetime/-/onetime-1.1.0.tgz#a1f7838f8314c516f05ecefcbc4ccfe04b4ed789" onetime@^2.0.0: version "2.0.1" @@ -12087,8 +11994,8 @@ regenerator-transform@^0.10.0: private "^0.1.6" regenerator-transform@^0.12.3: - version "0.12.3" - resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.12.3.tgz#459adfb64f6a27164ab991b7873f45ab969eca8b" + version "0.12.4" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.12.4.tgz#aa9b6c59f4b97be080e972506c560b3bccbfcff0" dependencies: private "^0.1.6"