From c0d7360a8c85a4e3a8c2f084710269f899039471 Mon Sep 17 00:00:00 2001 From: Vibhu Date: Fri, 30 Oct 2026 18:50:54 +0530 Subject: [PATCH 1/3] feat: support Babel 8 Widen the @babel/core peer range to ^7.0.0 || ^8.0.0. Babel 8 removed the t.jSX* builder aliases, so the css prop transform now uses t.jsx*. Inline the JSX parser option instead of inheriting @babel/plugin-syntax-jsx, whose 7.x peer range rejects Babel 8 and made npm warn ERESOLVE. Pin preset-env modules to commonjs for the build so lib/ stays CommonJS once it is built with Babel 8. Babel 8 is ESM-only and cannot load inside Jest, so scripts/babel8.js runs the built plugin over the fixtures on Babel 8 and CI runs it. Closes #425 --- .babelrc | 1 + .changeset/support-babel-8.md | 5 + .github/workflows/test.yml | 3 + AGENTS.md | 1 + README.md | 2 +- package.json | 6 +- pnpm-lock.yaml | 204 +++++++++++++++++- scripts/babel8.js | 107 +++++++++ src/index.js | 15 +- src/visitors/transpileCssProp.js | 28 +-- .../.babelrc | 17 ++ .../code.js | 4 + .../output.js | 5 + test/index.test.js | 19 ++ 14 files changed, 395 insertions(+), 22 deletions(-) create mode 100644 .changeset/support-babel-8.md create mode 100644 scripts/babel8.js create mode 100644 test/fixtures/typescript-parser-plugin-with-options-keeps-angle-bracket-assertions/.babelrc create mode 100644 test/fixtures/typescript-parser-plugin-with-options-keeps-angle-bracket-assertions/code.js create mode 100644 test/fixtures/typescript-parser-plugin-with-options-keeps-angle-bracket-assertions/output.js diff --git a/.babelrc b/.babelrc index 9ffb370b..5f434168 100644 --- a/.babelrc +++ b/.babelrc @@ -3,6 +3,7 @@ [ "@babel/preset-env", { + "modules": "commonjs", "targets": { "node": 12 } diff --git a/.changeset/support-babel-8.md b/.changeset/support-babel-8.md new file mode 100644 index 00000000..2dd15251 --- /dev/null +++ b/.changeset/support-babel-8.md @@ -0,0 +1,5 @@ +--- +'babel-plugin-styled-components': minor +--- + +Support Babel 8. The plugin now installs alongside `@babel/core@8` without peer dependency warnings or errors, and the `css` prop transform no longer crashes with `t.jSXIdentifier is not a function` on Babel 8. The plugin also no longer depends on `@babel/plugin-syntax-jsx`, which only accepted Babel 7 as a peer. Babel 7 output is unchanged. diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c507f5e1..2360da22 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -33,3 +33,6 @@ jobs: - name: Run tests run: pnpm test + + - name: Run fixtures on Babel 8 + run: pnpm build && pnpm test:babel8 diff --git a/AGENTS.md b/AGENTS.md index 32319c8f..57deca20 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -11,6 +11,7 @@ Commands - `pnpm exec jest -u`: regenerate fixture outputs, only after the snapshot-drift rule below is satisfied - `pnpm exec jest --coverage`: prints live coverage - `pnpm build`: compile `src/` to `lib/`; run it to verify every set of changes. Don't start a dev server unless asked +- `pnpm test:babel8`: the built `lib/` over the fixtures on Babel 8, outside Jest because Babel 8 is ESM-only; run `pnpm build` first - `pnpm bench [--base ] [--config default|all|bare] [--rounds N] [--normalize-aliases]`: interleaved A/B of the plugin at `` (default `origin/main`) against the working tree over generated inputs; fails when outputs differ - `pnpm bench:profile [--input small|medium|large|large-cjs]`: CPU profile with the plugin frames that drive Babel's work - `node scripts/compat.js [--base ]`: styled-components v5, v6, and v7 prerelease smoke against the working tree and `` (default `origin/main`) diff --git a/README.md b/README.md index 9ddd3693..a8d1a07e 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ This plugin is a highly recommended supplement to the base styled-components lib This plugin is tested against: -- `@babel/core` `^7` +- `@babel/core` `^7` and `^8` - `styled-components` `>= 6` (earlier majors may still work but aren't exercised in CI) ## Quick start diff --git a/package.json b/package.json index 99eb35a6..8ba9c480 100644 --- a/package.json +++ b/package.json @@ -23,11 +23,13 @@ "devDependencies": { "@babel/cli": "^7.26.4", "@babel/core": "^7.26.0", + "@babel/plugin-syntax-jsx": "^7.28.6", "@babel/plugin-transform-class-properties": "^7.25.9", "@babel/plugin-transform-modules-commonjs": "^7.26.3", "@babel/preset-env": "^7.26.0", "@changesets/cli": "2.31.0", "babel-test": "^0.2.4", + "babel8": "npm:@babel/core@^8.0.9", "jest": "^29.7.0", "jest-file-snapshot": "^0.5.0", "prettier": "^3.4.2", @@ -37,11 +39,10 @@ "dependencies": { "@babel/helper-annotate-as-pure": "^7.25.9", "@babel/helper-module-imports": "^7.25.9", - "@babel/plugin-syntax-jsx": "^7.25.9", "picomatch": "^4.0.2" }, "peerDependencies": { - "@babel/core": "^7.0.0", + "@babel/core": "^7.0.0 || ^8.0.0", "styled-components": ">= 2" }, "scripts": { @@ -49,6 +50,7 @@ "style": "prettier --write src/**/*.js", "build": "babel src -d lib", "test": "jest", + "test:babel8": "node scripts/babel8.js", "test:watch": "pnpm test -- --watch", "bench": "node scripts/bench/index.js", "bench:profile": "node scripts/bench/profile.js", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9d21c5ac..75a1d4c9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -175,9 +175,6 @@ importers: '@babel/helper-module-imports': specifier: ^7.25.9 version: 7.28.6(supports-color@8.1.1) - '@babel/plugin-syntax-jsx': - specifier: ^7.25.9 - version: 7.28.6(@babel/core@7.29.0(supports-color@8.1.1)) picomatch: specifier: ^4.0.2 version: 4.0.4 @@ -188,6 +185,9 @@ importers: '@babel/core': specifier: ^7.26.0 version: 7.29.0(supports-color@8.1.1) + '@babel/plugin-syntax-jsx': + specifier: ^7.28.6 + version: 7.28.6(@babel/core@7.29.0(supports-color@8.1.1)) '@babel/plugin-transform-class-properties': specifier: ^7.25.9 version: 7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1) @@ -203,6 +203,9 @@ importers: babel-test: specifier: ^0.2.4 version: 0.2.4(patch_hash=6b3cf0bcf4b9110049af53f6978b3834388ea424736399f5321452cba461e47e) + babel8: + specifier: npm:@babel/core@^8.0.9 + version: '@babel/core@8.0.9' jest: specifier: ^29.7.0 version: 29.7.0(@types/node@25.8.0)(supports-color@8.1.1) @@ -232,18 +235,34 @@ packages: resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} engines: {node: '>=6.9.0'} + '@babel/code-frame@8.0.6': + resolution: {integrity: sha512-uMHhvmDmwFiLuiozaoRYTZHqMFuu3fem/X9JY78lohAuLoh8LSGGC8JqUzo2cVuBsBxkjMZNk56CE7ock8Xxkw==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/compat-data@7.29.3': resolution: {integrity: sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==} engines: {node: '>=6.9.0'} + '@babel/compat-data@8.0.5': + resolution: {integrity: sha512-YLsYoQMvL8l8WrGpN3Zj7O1wK5LEBN+cQtux7BcuHyxIXve724XG+zuJ1n3U1cUweRtTzQOA4IHbuQw3N34SZw==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/core@7.29.0': resolution: {integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==} engines: {node: '>=6.9.0'} + '@babel/core@8.0.9': + resolution: {integrity: sha512-wXtyKlpR/pXf/u0vmib+OBakQaIzLglLcRoKpWxCHW/whqq7YfQYPD0IAAl9H/evi40MyLIjYYdvb6N+9DI2yA==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/generator@7.29.1': resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==} engines: {node: '>=6.9.0'} + '@babel/generator@8.0.6': + resolution: {integrity: sha512-/pcanjdRsonCexCuxgGD8fKnBLLzk2i9uM4kmcmGyyev2R5YDs+rafMYZ89RvaGY1mW8T7C5au0TyYaEeHBGng==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/helper-annotate-as-pure@7.27.3': resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==} engines: {node: '>=6.9.0'} @@ -252,6 +271,10 @@ packages: resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==} engines: {node: '>=6.9.0'} + '@babel/helper-compilation-targets@8.0.7': + resolution: {integrity: sha512-ilAZCdOOlOVA7dQbaFNw2f/59p6GW33NxTopc3DvEE3efp3ZXTrGX/yZScaKw8/vd9bq31Ag0EcXiLw32bAgJg==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/helper-create-class-features-plugin@7.29.3': resolution: {integrity: sha512-RpLYy2sb51oNLjuu1iD3bwBqCBWUzjO0ocp+iaCP/lJtb2CPLcnC2Fftw+4sAzaMELGeWTgExSKADbdo0GFVzA==} engines: {node: '>=6.9.0'} @@ -273,6 +296,10 @@ packages: resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} engines: {node: '>=6.9.0'} + '@babel/helper-globals@8.0.6': + resolution: {integrity: sha512-OdnA/N52LhzFpeUuVz5YGuSWjc7TPHiFmVYZbPlSgExocO2GwbxVpSni9MVs5wwW+wZ4kPuwBEBN9pIPDYz1mA==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/helper-member-expression-to-functions@7.28.5': resolution: {integrity: sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==} engines: {node: '>=6.9.0'} @@ -315,14 +342,26 @@ packages: resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} engines: {node: '>=6.9.0'} + '@babel/helper-string-parser@8.0.8': + resolution: {integrity: sha512-fVM+gSuKbdv9cpmyRNh5EvsDHUZCdxQH3FdnFKSkNYHpbsn95bbrxm+ES3MVeQjcqwi2O5ur/DjLfmTQaO60ng==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/helper-validator-identifier@7.28.5': resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} engines: {node: '>=6.9.0'} + '@babel/helper-validator-identifier@8.0.6': + resolution: {integrity: sha512-HYx0YmuchRxRSpnjvcNffnPS8DCauKNmnumIuHG3jnQAVewRA7kfJGwF13ayEpSZs7s4R/oBH4eppuXcAQd5JQ==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/helper-validator-option@7.27.1': resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} engines: {node: '>=6.9.0'} + '@babel/helper-validator-option@8.0.0': + resolution: {integrity: sha512-U4Dybxh4WESWHt5XhBeExi4DrY0/DNK1aHpQbsrQXCUbFHuMweT0TpLEWKvaraV2Y6fS+ZXunsZ8zIuZIgvF2Q==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/helper-wrap-function@7.28.6': resolution: {integrity: sha512-z+PwLziMNBeSQJonizz2AGnndLsP2DeGHIxDAn+wdHOGuo4Fo1x1HBPPXeE9TAOPHNNWQKCSlA2VZyYyyibDnQ==} engines: {node: '>=6.9.0'} @@ -331,11 +370,20 @@ packages: resolution: {integrity: sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==} engines: {node: '>=6.9.0'} + '@babel/helpers@8.0.8': + resolution: {integrity: sha512-FBmhiDWs625wHg03OKiLQDeT6LOc2lWBbW97Yqs2iI1tZcucjpmYnO29NHsQ83XxKvredcvUymW/AAn2UtnakA==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/parser@7.29.3': resolution: {integrity: sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==} engines: {node: '>=6.0.0'} hasBin: true + '@babel/parser@8.0.8': + resolution: {integrity: sha512-Gq65JogAr0S2p0BzvfSJboyuiRnxDQApyOqHkKmkrvm+VlLK4gC3pKsiOKEdnhFBemrFbSnOu90mZZWDpiJnmQ==} + engines: {node: ^22.18.0 || >=24.11.0} + hasBin: true + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5': resolution: {integrity: sha512-87GDMS3tsmMSi/3bWOte1UblL+YUTFMV8SZPZ2eSEL17s74Cw/l63rR6NmGVKMYW2GYi85nE+/d6Hw5N0bEk2Q==} engines: {node: '>=6.9.0'} @@ -806,14 +854,26 @@ packages: resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} engines: {node: '>=6.9.0'} + '@babel/template@8.0.0': + resolution: {integrity: sha512-eAD0QW/AlbamBbw0FeGiwasbCVPq5ncW0HNVyLP3B9czqLyh4gvw+5JTSNt6le9+ziAU7mqDZsKTHf3jTb4chQ==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/traverse@7.29.0': resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==} engines: {node: '>=6.9.0'} + '@babel/traverse@8.0.7': + resolution: {integrity: sha512-bVV0brinsTWgc6ptSIoQo3GbITtl0VZAqEUJbHHcyfDPEu+q/fEvlYEa+6H85HoK8IdksKBFtKJbJFZ6Kff+zg==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/types@7.29.0': resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} engines: {node: '>=6.9.0'} + '@babel/types@8.0.8': + resolution: {integrity: sha512-iXSSZAKQWmaeAwGtFw10v9fg5hUiUs0L+w+Z2K11kym3pDNK+SF0VcAQPsEFvWcTSZ7LMgClMXDBvTbH0IlICg==} + engines: {node: ^22.18.0 || >=24.11.0} + '@bcoe/v8-coverage@0.2.3': resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} @@ -968,6 +1028,9 @@ packages: '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + '@jridgewell/gen-mapping@0.4.0-beta.0': + resolution: {integrity: sha512-JdGNkbE4GlNPYQhM0L95fBQr7ctLZJ276QXQLTad4t1oSdnnCI3fDq9DW3BqYAWv8Wc3+HS+4Gsii1oPMCfz1w==} + '@jridgewell/remapping@2.3.5': resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} @@ -978,6 +1041,9 @@ packages: '@jridgewell/sourcemap-codec@1.5.5': resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + '@jridgewell/sourcemap-codec@1.6.0': + resolution: {integrity: sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==} + '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} @@ -1023,6 +1089,9 @@ packages: '@types/babel__traverse@7.28.0': resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} + '@types/gensync@1.0.5': + resolution: {integrity: sha512-MbsRCT7mTikHwKZ0X+LVUTLRrZZRLipTuXEO9qOYO+zmjMVk81axyClMROf6uoPD9MRVu46bx8zoR0Ad9q3NAg==} + '@types/graceful-fs@4.1.9': resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} @@ -1035,6 +1104,9 @@ packages: '@types/istanbul-reports@3.0.4': resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} + '@types/jsesc@2.5.1': + resolution: {integrity: sha512-9VN+6yxLOPLOav+7PwjZbxiID2bVaeq0ED4qSQmdQTdjnXJSaCVKTR58t15oqH1H5t8Ng2ZX1SabJVoN9Q34bw==} + '@types/node@12.20.55': resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} @@ -1303,6 +1375,10 @@ packages: emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + empathic@2.1.0: + resolution: {integrity: sha512-AnfC1ATldl49/cvZdLPDjBfrRNwbDO05aibiOtzQu3qtlbJtomNLhF30HEtn/7iBz50dlMECqATo3fG0LrdEgw==} + engines: {node: '>=14'} + enquirer@2.4.1: resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} engines: {node: '>=8.6'} @@ -1386,6 +1462,10 @@ packages: resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} engines: {node: '>=8'} + flru@1.0.2: + resolution: {integrity: sha512-kWyh8ADvHBFz6ua5xYOPnUroZTT/bwWfrCeL0Wj1dzG4/YOmOcfJ99W8dOVyyynJN35rZ9aCOtHChqQovV7yog==} + engines: {node: '>=6'} + fs-extra@7.0.1: resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==} engines: {node: '>=6 <7 || >=8'} @@ -1475,6 +1555,9 @@ packages: engines: {node: '>=8'} hasBin: true + import-meta-resolve@4.2.0: + resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} + imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} @@ -1696,6 +1779,9 @@ packages: node-notifier: optional: true + js-tokens@10.0.0: + resolution: {integrity: sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==} + js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -1817,6 +1903,10 @@ packages: resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} engines: {node: '>=8'} + obug@3.0.0: + resolution: {integrity: sha512-5vvB5+W7ePv+p3uqxi+RcW1XAzLW0/hxt3/4X4Lc4qHudzOhmBiBwOY6DRob4WnanAEGvNcLjF+KNOufrUoEQw==} + engines: {node: '>=12.20.0'} + once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} @@ -2192,6 +2282,10 @@ packages: resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==} engines: {node: '>=10.12.0'} + verkit@0.5.0: + resolution: {integrity: sha512-u4J1hr6Y+C/hit34GwL3eIcUf3wngT8TlYUjK7tk0jxydKLAq3sYvifjdTDA6U3KCKRcQCQDGf/srjxfZF3l7g==} + engines: {node: '>=18.12.0'} + walker@1.0.8: resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} @@ -2252,8 +2346,15 @@ snapshots: js-tokens: 4.0.0 picocolors: 1.1.1 + '@babel/code-frame@8.0.6': + dependencies: + '@babel/helper-validator-identifier': 8.0.6 + js-tokens: 10.0.0 + '@babel/compat-data@7.29.3': {} + '@babel/compat-data@8.0.5': {} + '@babel/core@7.29.0(supports-color@8.1.1)': dependencies: '@babel/code-frame': 7.29.0 @@ -2274,6 +2375,25 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/core@8.0.9': + dependencies: + '@babel/code-frame': 8.0.6 + '@babel/generator': 8.0.6 + '@babel/helper-compilation-targets': 8.0.7 + '@babel/helpers': 8.0.8 + '@babel/parser': 8.0.8 + '@babel/template': 8.0.0 + '@babel/traverse': 8.0.7 + '@babel/types': 8.0.8 + '@types/gensync': 1.0.5 + convert-source-map: 2.0.0 + empathic: 2.1.0 + gensync: 1.0.0-beta.2 + import-meta-resolve: 4.2.0 + json5: 2.2.3 + obug: 3.0.0 + verkit: 0.5.0 + '@babel/generator@7.29.1': dependencies: '@babel/parser': 7.29.3 @@ -2282,6 +2402,15 @@ snapshots: '@jridgewell/trace-mapping': 0.3.31 jsesc: 3.1.0 + '@babel/generator@8.0.6': + dependencies: + '@babel/parser': 8.0.8 + '@babel/types': 8.0.8 + '@jridgewell/gen-mapping': 0.4.0-beta.0 + '@jridgewell/trace-mapping': 0.3.31 + '@types/jsesc': 2.5.1 + jsesc: 3.1.0 + '@babel/helper-annotate-as-pure@7.27.3': dependencies: '@babel/types': 7.29.0 @@ -2294,6 +2423,14 @@ snapshots: lru-cache: 5.1.1 semver: 6.3.1 + '@babel/helper-compilation-targets@8.0.7': + dependencies: + '@babel/compat-data': 8.0.5 + '@babel/helper-validator-option': 8.0.0 + browserslist: 4.28.2 + flru: 1.0.2 + verkit: 0.5.0 + '@babel/helper-create-class-features-plugin@7.29.3(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1)': dependencies: '@babel/core': 7.29.0(supports-color@8.1.1) @@ -2327,6 +2464,8 @@ snapshots: '@babel/helper-globals@7.28.0': {} + '@babel/helper-globals@8.0.6': {} + '@babel/helper-member-expression-to-functions@7.28.5(supports-color@8.1.1)': dependencies: '@babel/traverse': 7.29.0(supports-color@8.1.1) @@ -2383,10 +2522,16 @@ snapshots: '@babel/helper-string-parser@7.27.1': {} + '@babel/helper-string-parser@8.0.8': {} + '@babel/helper-validator-identifier@7.28.5': {} + '@babel/helper-validator-identifier@8.0.6': {} + '@babel/helper-validator-option@7.27.1': {} + '@babel/helper-validator-option@8.0.0': {} + '@babel/helper-wrap-function@7.28.6(supports-color@8.1.1)': dependencies: '@babel/template': 7.28.6 @@ -2400,10 +2545,19 @@ snapshots: '@babel/template': 7.28.6 '@babel/types': 7.29.0 + '@babel/helpers@8.0.8': + dependencies: + '@babel/template': 8.0.0 + '@babel/types': 8.0.8 + '@babel/parser@7.29.3': dependencies: '@babel/types': 7.29.0 + '@babel/parser@8.0.8': + dependencies: + '@babel/types': 8.0.8 + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.28.5(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1)': dependencies: '@babel/core': 7.29.0(supports-color@8.1.1) @@ -2972,6 +3126,12 @@ snapshots: '@babel/parser': 7.29.3 '@babel/types': 7.29.0 + '@babel/template@8.0.0': + dependencies: + '@babel/code-frame': 8.0.6 + '@babel/parser': 8.0.8 + '@babel/types': 8.0.8 + '@babel/traverse@7.29.0(supports-color@8.1.1)': dependencies: '@babel/code-frame': 7.29.0 @@ -2984,11 +3144,26 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/traverse@8.0.7': + dependencies: + '@babel/code-frame': 8.0.6 + '@babel/generator': 8.0.6 + '@babel/helper-globals': 8.0.6 + '@babel/parser': 8.0.8 + '@babel/template': 8.0.0 + '@babel/types': 8.0.8 + obug: 3.0.0 + '@babel/types@7.29.0': dependencies: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 + '@babel/types@8.0.8': + dependencies: + '@babel/helper-string-parser': 8.0.8 + '@babel/helper-validator-identifier': 8.0.6 + '@bcoe/v8-coverage@0.2.3': {} '@changesets/apply-release-plan@7.1.1': @@ -3332,6 +3507,11 @@ snapshots: '@jridgewell/sourcemap-codec': 1.5.5 '@jridgewell/trace-mapping': 0.3.31 + '@jridgewell/gen-mapping@0.4.0-beta.0': + dependencies: + '@jridgewell/sourcemap-codec': 1.6.0 + '@jridgewell/trace-mapping': 0.3.31 + '@jridgewell/remapping@2.3.5': dependencies: '@jridgewell/gen-mapping': 0.3.13 @@ -3341,6 +3521,8 @@ snapshots: '@jridgewell/sourcemap-codec@1.5.5': {} + '@jridgewell/sourcemap-codec@1.6.0': {} + '@jridgewell/trace-mapping@0.3.31': dependencies: '@jridgewell/resolve-uri': 3.1.2 @@ -3408,6 +3590,8 @@ snapshots: dependencies: '@babel/types': 7.29.0 + '@types/gensync@1.0.5': {} + '@types/graceful-fs@4.1.9': dependencies: '@types/node': 25.8.0 @@ -3422,6 +3606,8 @@ snapshots: dependencies: '@types/istanbul-lib-report': 3.0.3 + '@types/jsesc@2.5.1': {} + '@types/node@12.20.55': {} '@types/node@25.8.0': @@ -3704,6 +3890,8 @@ snapshots: emoji-regex@8.0.0: {} + empathic@2.1.0: {} + enquirer@2.4.1: dependencies: ansi-colors: 4.1.3 @@ -3790,6 +3978,8 @@ snapshots: locate-path: 5.0.0 path-exists: 4.0.0 + flru@1.0.2: {} + fs-extra@7.0.1: dependencies: graceful-fs: 4.2.11 @@ -3872,6 +4062,8 @@ snapshots: pkg-dir: 4.2.0 resolve-cwd: 3.0.0 + import-meta-resolve@4.2.0: {} + imurmurhash@0.1.4: {} inflight@1.0.6: @@ -4279,6 +4471,8 @@ snapshots: - supports-color - ts-node + js-tokens@10.0.0: {} + js-tokens@4.0.0: {} js-yaml@3.14.2: @@ -4372,6 +4566,8 @@ snapshots: dependencies: path-key: 3.1.1 + obug@3.0.0: {} + once@1.4.0: dependencies: wrappy: 1.0.2 @@ -4678,6 +4874,8 @@ snapshots: '@types/istanbul-lib-coverage': 2.0.6 convert-source-map: 2.0.0 + verkit@0.5.0: {} + walker@1.0.8: dependencies: makeerror: 1.0.12 diff --git a/scripts/babel8.js b/scripts/babel8.js new file mode 100644 index 00000000..c05d33bf --- /dev/null +++ b/scripts/babel8.js @@ -0,0 +1,107 @@ +// Runs the built plugin (`lib/`) through Babel 8 over the fixture suite and +// compares against the same output.js / error.js files Jest uses on Babel 7. +// Babel 8 ships as ESM only, which Jest's module sandbox cannot require, so +// this runs as plain Node. Run with `pnpm build && pnpm test:babel8`. +const fs = require('fs') +const path = require('path') +const babel = require('babel8') + +const ROOT = path.resolve(__dirname, '..') +const FIXTURES = path.join(ROOT, 'test', 'fixtures') +const PLUGIN = '../../../src' + +// Their snapshots are mostly Babel's own output (helpers, module interop, +// preset-env targets), which differs between majors by design. +const usesOtherBabelPlugins = config => + [...(config.presets || []), ...(config.plugins || [])].some( + entry => (Array.isArray(entry) ? entry[0] : entry) !== PLUGIN + ) + +const SKIP = { + 'imports-inside-exported-ambient-namespace': + 'Babel 8 rejects an import inside `declare namespace` while parsing, as TypeScript does', +} + +// Mirrors the babel-test patch in patches/ so error.js matches across machines +const normalizeError = e => + String(e) + .replace(/\u001b\[[0-9;]*m/g, '') + .replace( + new RegExp( + ROOT.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&').replace( + /\\\\|\//g, + '[\\\\/]' + ), + 'gi' + ), + '' + ) + .replace(/[^\s:]*/g, p => p.replace(/\\/g, '/')) + '\n' + +const read = file => + fs.existsSync(file) ? fs.readFileSync(file, 'utf8') : null + +// Babel reads .babelrc as JSON5; the fixtures only lean on trailing commas +const readConfig = dir => + JSON.parse(read(path.join(dir, '.babelrc')).replace(/,(\s*[\]}])/g, '$1')) + +const main = async () => { + const lib = path.join(ROOT, 'lib', 'index.js') + if (!fs.existsSync(lib)) { + console.error('lib/ is missing. Run `pnpm build` first.') + process.exit(1) + } + + let passed = 0 + const skipped = [] + const failed = [] + + for (const name of fs.readdirSync(FIXTURES).sort()) { + const dir = path.join(FIXTURES, name) + const config = readConfig(dir) + + if (SKIP[name] || usesOtherBabelPlugins(config)) { + skipped.push( + `${name}: ${SKIP[name] || 'output depends on Babel presets or plugins'}` + ) + continue + } + + const filename = path.join(dir, 'code.js') + const plugins = config.plugins.map(entry => + Array.isArray(entry) ? [lib, ...entry.slice(1)] : lib + ) + + let actual + try { + const result = await babel.transformAsync(read(filename), { + ...config, + plugins, + babelrc: false, + configFile: false, + cwd: ROOT, + filename, + }) + actual = result.code + '\n' + } catch (e) { + actual = normalizeError(e) + } + + const expected = + read(path.join(dir, 'output.js')) ?? read(path.join(dir, 'error.js')) + if (actual === expected) passed++ + else failed.push({ name, expected, actual }) + } + + for (const { name, expected, actual } of failed) { + console.log(`\n✗ ${name}\n--- expected\n${expected}--- actual\n${actual}`) + } + console.log(`\nSkipped on Babel ${babel.version}:`) + for (const line of skipped) console.log(` ${line}`) + console.log( + `\nBabel ${babel.version}: ${passed} passed, ${failed.length} failed, ${skipped.length} skipped` + ) + if (failed.length) process.exit(1) +} + +main() diff --git a/src/index.js b/src/index.js index 78e77f18..3eb98168 100644 --- a/src/index.js +++ b/src/index.js @@ -1,4 +1,3 @@ -import syntax from '@babel/plugin-syntax-jsx' import assignStyledRequired from './visitors/assignStyledRequired' import transpileCssProp from './visitors/transpileCssProp' import { @@ -13,7 +12,19 @@ export default function ({ types: t }) { const processTaggedTemplateVisit = processTaggedTemplate(t) return { - inherits: syntax, + // Same as @babel/plugin-syntax-jsx, whose peer range is pinned to one + // Babel major. With TypeScript, the TypeScript preset decides per file + // (.tsx) whether JSX is on, and forcing it breaks `value` assertions + manipulateOptions(opts, parserOpts) { + if ( + parserOpts.plugins.some( + p => (Array.isArray(p) ? p[0] : p) === 'typescript' + ) + ) { + return + } + parserOpts.plugins.push('jsx') + }, visitor: { Program(path, state) { path.traverse( diff --git a/src/visitors/transpileCssProp.js b/src/visitors/transpileCssProp.js index c34ac531..f7949a18 100644 --- a/src/visitors/transpileCssProp.js +++ b/src/visitors/transpileCssProp.js @@ -162,10 +162,10 @@ export default t => { (t.isJSXAttribute(x) ? x.name.name !== 'css' : false) ) - elem.node.name = t.jSXIdentifier(id.name) + elem.node.name = t.jsxIdentifier(id.name) if (elem.parentPath.node.closingElement) { - elem.parentPath.node.closingElement.name = t.jSXIdentifier(id.name) + elem.parentPath.node.closingElement.name = t.jsxIdentifier(id.name) } // object syntax @@ -209,9 +209,9 @@ export default t => { const identifier = getLocalIdentifier(path) elem.node.attributes.push( - t.jSXAttribute( - t.jSXIdentifier(identifier.name), - t.jSXExpressionContainer(property.key) + t.jsxAttribute( + t.jsxIdentifier(identifier.name), + t.jsxExpressionContainer(property.key) ) ) @@ -240,9 +240,9 @@ export default t => { const identifier = getLocalIdentifier(path) elem.node.attributes.push( - t.jSXAttribute( - t.jSXIdentifier(identifier.name), - t.jSXExpressionContainer(property.argument) + t.jsxAttribute( + t.jsxIdentifier(identifier.name), + t.jsxExpressionContainer(property.argument) ) ) @@ -267,9 +267,9 @@ export default t => { const identifier = getLocalIdentifier(path) elem.node.attributes.push( - t.jSXAttribute( - t.jSXIdentifier(identifier.name), - t.jSXExpressionContainer(property.value) + t.jsxAttribute( + t.jsxIdentifier(identifier.name), + t.jsxExpressionContainer(property.value) ) ) @@ -311,9 +311,9 @@ export default t => { const p = t.identifier('p') elem.node.attributes.push( - t.jSXAttribute( - t.jSXIdentifier(identifier.name), - t.jSXExpressionContainer(ex) + t.jsxAttribute( + t.jsxIdentifier(identifier.name), + t.jsxExpressionContainer(ex) ) ) diff --git a/test/fixtures/typescript-parser-plugin-with-options-keeps-angle-bracket-assertions/.babelrc b/test/fixtures/typescript-parser-plugin-with-options-keeps-angle-bracket-assertions/.babelrc new file mode 100644 index 00000000..e10817d7 --- /dev/null +++ b/test/fixtures/typescript-parser-plugin-with-options-keeps-angle-bracket-assertions/.babelrc @@ -0,0 +1,17 @@ +{ + "parserOpts": { + "plugins": [["typescript", { "dts": false }]] + }, + "plugins": [ + [ + "../../../src", + { + "displayName": true, + "fileName": false, + "ssr": false, + "transpileTemplateLiterals": false, + "minify": false + } + ] + ] +} diff --git a/test/fixtures/typescript-parser-plugin-with-options-keeps-angle-bracket-assertions/code.js b/test/fixtures/typescript-parser-plugin-with-options-keeps-angle-bracket-assertions/code.js new file mode 100644 index 00000000..5f5dff74 --- /dev/null +++ b/test/fixtures/typescript-parser-plugin-with-options-keeps-angle-bracket-assertions/code.js @@ -0,0 +1,4 @@ +import styled from 'styled-components' + +const Foo = styled.div`color: red;` +const wrapped = Foo diff --git a/test/fixtures/typescript-parser-plugin-with-options-keeps-angle-bracket-assertions/output.js b/test/fixtures/typescript-parser-plugin-with-options-keeps-angle-bracket-assertions/output.js new file mode 100644 index 00000000..4edf2e3c --- /dev/null +++ b/test/fixtures/typescript-parser-plugin-with-options-keeps-angle-bracket-assertions/output.js @@ -0,0 +1,5 @@ +import styled from 'styled-components'; +const Foo = styled.div.withConfig({ + displayName: "Foo" +})`color: red;`; +const wrapped = Foo; diff --git a/test/index.test.js b/test/index.test.js index 6e749c65..11ff39eb 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -29,3 +29,22 @@ describe('fixture snapshots', () => { expect(leaks).toEqual([]) }) }) + +describe('runtime dependencies', () => { + const majors = range => + new Set((range.match(/\^\d+/g) || []).map(m => m.slice(1))) + const pkg = require('../package.json') + const supported = majors(pkg.peerDependencies['@babel/core']) + + it('accept every @babel/core major the plugin supports', () => { + const narrower = Object.keys(pkg.dependencies) + .map(name => [name, require(`${name}/package.json`).peerDependencies]) + .filter(([, peers]) => peers && peers['@babel/core']) + .filter(([, peers]) => + [...supported].some(major => !majors(peers['@babel/core']).has(major)) + ) + .map(([name, peers]) => `${name} ${peers['@babel/core']}`) + + expect(narrower).toEqual([]) + }) +}) From 25c8d2e1feffca2f2c85478ac8ba4e509fdce49b Mon Sep 17 00:00:00 2001 From: Vibhu Date: Sat, 31 Oct 2026 01:42:24 +0530 Subject: [PATCH 2/3] fix(css-prop): build name expressions only for non-tag elements Babel 8 validates t.identifier, so building an expression for a hyphenated tag threw even though the tag path never uses it. Build it only in the branch that needs it. scripts/babel8.js skipped the fixtures that cover this. Fixtures that use Babel's own presets or plugins now run with this plugin alone on both majors and must match. --- scripts/babel8.js | 62 +++++++++++-------- src/visitors/transpileCssProp.js | 5 +- .../css-prop-on-hyphenated-element/.babelrc | 13 ++++ .../css-prop-on-hyphenated-element/code.js | 3 + .../css-prop-on-hyphenated-element/output.js | 3 + 5 files changed, 57 insertions(+), 29 deletions(-) create mode 100644 test/fixtures/css-prop-on-hyphenated-element/.babelrc create mode 100644 test/fixtures/css-prop-on-hyphenated-element/code.js create mode 100644 test/fixtures/css-prop-on-hyphenated-element/output.js diff --git a/scripts/babel8.js b/scripts/babel8.js index c05d33bf..1d59cf23 100644 --- a/scripts/babel8.js +++ b/scripts/babel8.js @@ -1,17 +1,19 @@ // Runs the built plugin (`lib/`) through Babel 8 over the fixture suite and // compares against the same output.js / error.js files Jest uses on Babel 7. -// Babel 8 ships as ESM only, which Jest's module sandbox cannot require, so -// this runs as plain Node. Run with `pnpm build && pnpm test:babel8`. +// A fixture that also runs Babel's own presets or plugins has a snapshot that +// differs between majors by design, so it runs with this plugin alone and +// must match Babel 7's output instead. Babel 8 ships as ESM only, which +// Jest's module sandbox cannot require, so this runs as plain Node. Run with +// `pnpm build && pnpm test:babel8`. const fs = require('fs') const path = require('path') +const babel7 = require('@babel/core') const babel = require('babel8') const ROOT = path.resolve(__dirname, '..') const FIXTURES = path.join(ROOT, 'test', 'fixtures') const PLUGIN = '../../../src' -// Their snapshots are mostly Babel's own output (helpers, module interop, -// preset-env targets), which differs between majors by design. const usesOtherBabelPlugins = config => [...(config.presets || []), ...(config.plugins || [])].some( entry => (Array.isArray(entry) ? entry[0] : entry) !== PLUGIN @@ -45,6 +47,20 @@ const read = file => const readConfig = dir => JSON.parse(read(path.join(dir, '.babelrc')).replace(/,(\s*[\]}])/g, '$1')) +const transform = async (instance, code, options) => { + try { + const result = await instance.transformAsync(code, { + ...options, + babelrc: false, + configFile: false, + cwd: ROOT, + }) + return result.code + '\n' + } catch (e) { + return normalizeError(e) + } +} + const main = async () => { const lib = path.join(ROOT, 'lib', 'index.js') if (!fs.existsSync(lib)) { @@ -60,35 +76,27 @@ const main = async () => { const dir = path.join(FIXTURES, name) const config = readConfig(dir) - if (SKIP[name] || usesOtherBabelPlugins(config)) { - skipped.push( - `${name}: ${SKIP[name] || 'output depends on Babel presets or plugins'}` - ) + if (SKIP[name]) { + skipped.push(`${name}: ${SKIP[name]}`) continue } const filename = path.join(dir, 'code.js') - const plugins = config.plugins.map(entry => - Array.isArray(entry) ? [lib, ...entry.slice(1)] : lib - ) - - let actual - try { - const result = await babel.transformAsync(read(filename), { - ...config, - plugins, - babelrc: false, - configFile: false, - cwd: ROOT, - filename, - }) - actual = result.code + '\n' - } catch (e) { - actual = normalizeError(e) + const code = read(filename) + const pluginOnly = usesOtherBabelPlugins(config) + const options = { + ...config, + presets: [], + plugins: config.plugins + .filter(entry => (Array.isArray(entry) ? entry[0] : entry) === PLUGIN) + .map(entry => (Array.isArray(entry) ? [lib, ...entry.slice(1)] : lib)), + filename, } - const expected = - read(path.join(dir, 'output.js')) ?? read(path.join(dir, 'error.js')) + const actual = await transform(babel, code, options) + const expected = pluginOnly + ? await transform(babel7, code, options) + : (read(path.join(dir, 'output.js')) ?? read(path.join(dir, 'error.js'))) if (actual === expected) passed++ else failed.push({ name, expected, actual }) } diff --git a/src/visitors/transpileCssProp.js b/src/visitors/transpileCssProp.js index f7949a18..b378fa32 100644 --- a/src/visitors/transpileCssProp.js +++ b/src/visitors/transpileCssProp.js @@ -95,7 +95,6 @@ export default t => { const elem = path.parentPath const name = getName(elem.node.name, t, path) - const nameExpression = getNameExpression(elem.node.name, t, path) const id = path.scope.generateUidIdentifier( 'Styled' + name.replace(/^([a-z])/, (match, p1) => p1.toUpperCase()) ) @@ -106,7 +105,9 @@ export default t => { if (TAG_NAME_REGEXP.test(name)) { styled = t.callExpression(importName, [t.stringLiteral(name)]) } else { - styled = t.callExpression(importName, [nameExpression]) + styled = t.callExpression(importName, [ + getNameExpression(elem.node.name, t, path), + ]) if ( bindings[name] && diff --git a/test/fixtures/css-prop-on-hyphenated-element/.babelrc b/test/fixtures/css-prop-on-hyphenated-element/.babelrc new file mode 100644 index 00000000..21885a8b --- /dev/null +++ b/test/fixtures/css-prop-on-hyphenated-element/.babelrc @@ -0,0 +1,13 @@ +{ + "plugins": [ + [ + "../../../src", + { + "ssr": false, + "displayName": false, + "transpileTemplateLiterals": false, + "minify": false + } + ] + ] +} diff --git a/test/fixtures/css-prop-on-hyphenated-element/code.js b/test/fixtures/css-prop-on-hyphenated-element/code.js new file mode 100644 index 00000000..8b77a294 --- /dev/null +++ b/test/fixtures/css-prop-on-hyphenated-element/code.js @@ -0,0 +1,3 @@ +import styled from 'styled-components' + +const Element = () => H diff --git a/test/fixtures/css-prop-on-hyphenated-element/output.js b/test/fixtures/css-prop-on-hyphenated-element/output.js new file mode 100644 index 00000000..fbee308b --- /dev/null +++ b/test/fixtures/css-prop-on-hyphenated-element/output.js @@ -0,0 +1,3 @@ +import styled from 'styled-components'; +const Element = () => <_StyledButtonGhost>H; +var _StyledButtonGhost = styled("button-ghost")`flex: 1`; From 8bed53b3bb8c10ed865683319f3043af4b26fcea Mon Sep 17 00:00:00 2001 From: Vibhu Date: Sat, 31 Oct 2026 02:01:44 +0530 Subject: [PATCH 3/3] fix(jsx): leave the jsx parser plugin off TypeScript module files Babel 8's TypeScript preset no longer removes the jsx parser plugin, so adding it broke angle-bracket assertions and generic arrow functions in .ts files with "Unterminated JSX contents". That preset also no longer adds jsx to .tsx files, so decide by file extension. Run .ts and .tsx through the real Babel 8 preset in scripts/babel8.js. --- package.json | 1 + pnpm-lock.yaml | 151 +++++++++++++++++++++++++++++++++++++++++++++ scripts/babel8.js | 25 ++++++++ src/index.js | 6 +- test/index.test.js | 30 +++++++++ 5 files changed, 211 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 8ba9c480..a22298c1 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "@changesets/cli": "2.31.0", "babel-test": "^0.2.4", "babel8": "npm:@babel/core@^8.0.9", + "babel8-preset-typescript": "npm:@babel/preset-typescript@^8", "jest": "^29.7.0", "jest-file-snapshot": "^0.5.0", "prettier": "^3.4.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 75a1d4c9..5cd42a37 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -206,6 +206,9 @@ importers: babel8: specifier: npm:@babel/core@^8.0.9 version: '@babel/core@8.0.9' + babel8-preset-typescript: + specifier: npm:@babel/preset-typescript@^8 + version: '@babel/preset-typescript@8.0.7(@babel/core@7.29.0(supports-color@8.1.1))' jest: specifier: ^29.7.0 version: 29.7.0(@types/node@25.8.0)(supports-color@8.1.1) @@ -267,6 +270,10 @@ packages: resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==} engines: {node: '>=6.9.0'} + '@babel/helper-annotate-as-pure@8.0.0': + resolution: {integrity: sha512-NSpMkMsvvZqzThJ0p1B02cbtA2ObEyfBvq950bmNkyxsxvcxwhvvCB036rKhlEnuBBo30bOrk13u3FzlKSoRrw==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/helper-compilation-targets@7.28.6': resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==} engines: {node: '>=6.9.0'} @@ -281,6 +288,12 @@ packages: peerDependencies: '@babel/core': ^7.0.0 + '@babel/helper-create-class-features-plugin@8.0.7': + resolution: {integrity: sha512-7ug3KrOhNVPPxFFLTel0/pMzimKzU/vTlNJKVL/dKguCpmNJZjjibyFbWg+Qs8QHmiIZPNuZ2UNnDGdHWGEpOg==} + engines: {node: ^22.18.0 || >=24.11.0} + peerDependencies: + '@babel/core': ^8.0.0 + '@babel/helper-create-regexp-features-plugin@7.28.5': resolution: {integrity: sha512-N1EhvLtHzOvj7QQOUCCS3NrPJP8c5W6ZXCHDn7Yialuy1iu4r5EmIYkXlKNqT99Ciw+W0mDqWoR6HWMZlFP3hw==} engines: {node: '>=6.9.0'} @@ -304,24 +317,48 @@ packages: resolution: {integrity: sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==} engines: {node: '>=6.9.0'} + '@babel/helper-member-expression-to-functions@8.0.5': + resolution: {integrity: sha512-GLe05QD98BkNFTkjaqeqF9QSRoHLKrrB4tpoplyQuuPrQJ72rtmkM4wvqJLX/sjZPqkbK5MUYSsuUTqdJoOVjA==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/helper-module-imports@7.28.6': resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==} engines: {node: '>=6.9.0'} + '@babel/helper-module-imports@8.0.0': + resolution: {integrity: sha512-NZ7mSS93o4ndX4KrbD7W8Sf3QT8Qe24PrnFyUcuOPDzK6faqDFKjY9RG7he7+I7FdiQ4llpnosFqzrXa+Vy3Ew==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/helper-module-transforms@7.28.6': resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 + '@babel/helper-module-transforms@8.0.6': + resolution: {integrity: sha512-YUi+3X0cb0v97xKhuqkLdrwQtP6XXv1eVbhLRCFyMwG481UskbMQk4mjH7K/Jg3oJ1KLskvFwwq4kF/QnABkWQ==} + engines: {node: ^22.18.0 || >=24.11.0} + peerDependencies: + '@babel/core': ^8.0.0 + '@babel/helper-optimise-call-expression@7.27.1': resolution: {integrity: sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==} engines: {node: '>=6.9.0'} + '@babel/helper-optimise-call-expression@8.0.0': + resolution: {integrity: sha512-3W6satvtPuCUkUx63S2jMoW9EQNYkADgs1HTfufmL7gCmAulHMKupA/12WNz4A0GMMFn/YnWWwqOT9IZrJHQjg==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/helper-plugin-utils@7.28.6': resolution: {integrity: sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==} engines: {node: '>=6.9.0'} + '@babel/helper-plugin-utils@8.0.1': + resolution: {integrity: sha512-3PKFgjTyPlhFhorfP+SjKQxLViIL++zWjFOO4hGriYU+Bsm983DxEM1JmDRJVWXV0O9npu+xXRqz7Pbd3mh70g==} + engines: {node: ^22.18.0 || >=24.11.0} + peerDependencies: + '@babel/core': ^8.0.0 + '@babel/helper-remap-async-to-generator@7.27.1': resolution: {integrity: sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==} engines: {node: '>=6.9.0'} @@ -334,10 +371,20 @@ packages: peerDependencies: '@babel/core': ^7.0.0 + '@babel/helper-replace-supers@8.0.1': + resolution: {integrity: sha512-B1SZADIcy3tmH8CmWvj4SHi/oAPom4UL3uknTc2QRNsPVLFk/sPnZvQL/8kj7Y5omvjMqie0vklvs6XM4OLW5Q==} + engines: {node: ^22.18.0 || >=24.11.0} + peerDependencies: + '@babel/core': ^8.0.0 + '@babel/helper-skip-transparent-expression-wrappers@7.27.1': resolution: {integrity: sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==} engines: {node: '>=6.9.0'} + '@babel/helper-skip-transparent-expression-wrappers@8.0.0': + resolution: {integrity: sha512-xmCA9kP3IhySsqhzwIdWGlDN/1A4cCKNBO/uwZx/3YzmDoMePwno2Q5/Bq0q+tYaKbeF940YiKV/kaW8Mzvpjw==} + engines: {node: ^22.18.0 || >=24.11.0} + '@babel/helper-string-parser@7.27.1': resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} engines: {node: '>=6.9.0'} @@ -523,6 +570,12 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/plugin-syntax-typescript@8.0.3': + resolution: {integrity: sha512-jmTPwps7oSQSZaV1SxkQ3C12UWyufGysGc5OzDpZzvPAIX4mO7dJT3hoqkWVrSImvkcMiknir1iLN1SNV/CZzg==} + engines: {node: ^22.18.0 || >=24.11.0} + peerDependencies: + '@babel/core': ^8.0.0 + '@babel/plugin-syntax-unicode-sets-regex@7.18.6': resolution: {integrity: sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==} engines: {node: '>=6.9.0'} @@ -679,6 +732,12 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-modules-commonjs@8.0.1': + resolution: {integrity: sha512-PMuzulWrrzFNmY3lXSk/tV9NRb7y0eZZLJY4UEo2TKszroxvUZHAPPi+T9FDyrQhod+TQA+t+8/QYaaMpiEuhA==} + engines: {node: ^22.18.0 || >=24.11.0} + peerDependencies: + '@babel/core': ^8.0.0 + '@babel/plugin-transform-modules-systemjs@7.29.4': resolution: {integrity: sha512-N7QmZ0xRZfjHOfZeQLJjwgX2zS9pdGHSVl/cjSGlo4dXMqvurfxXDMKY4RqEKzPozV78VMcd0lxyG13mlbKc4w==} engines: {node: '>=6.9.0'} @@ -811,6 +870,12 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/plugin-transform-typescript@8.0.6': + resolution: {integrity: sha512-WF3yRlQtClzScvQbWct00ix63+w3DPiyU0WmCBU4YqK539n3VvJFElElpevoD8wfu6953CbiNoUZBi2B7/5jXw==} + engines: {node: ^22.18.0 || >=24.11.0} + peerDependencies: + '@babel/core': ^8.0.0 + '@babel/plugin-transform-unicode-escapes@7.27.1': resolution: {integrity: sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==} engines: {node: '>=6.9.0'} @@ -846,6 +911,12 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0 + '@babel/preset-typescript@8.0.7': + resolution: {integrity: sha512-sUFLFphAG6YNjCnJpSKpyoNxkfyeM2KNuUjJJN0R6dZusMcK2V3Fg11MVHPFvb37AZeDNocc+xM748vdh9z82Q==} + engines: {node: ^22.18.0 || >=24.11.0} + peerDependencies: + '@babel/core': ^8.0.0 + '@babel/runtime@7.29.2': resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==} engines: {node: '>=6.9.0'} @@ -2415,6 +2486,10 @@ snapshots: dependencies: '@babel/types': 7.29.0 + '@babel/helper-annotate-as-pure@8.0.0': + dependencies: + '@babel/types': 8.0.8 + '@babel/helper-compilation-targets@7.28.6': dependencies: '@babel/compat-data': 7.29.3 @@ -2444,6 +2519,17 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/helper-create-class-features-plugin@8.0.7(@babel/core@7.29.0(supports-color@8.1.1))': + dependencies: + '@babel/core': 7.29.0(supports-color@8.1.1) + '@babel/helper-annotate-as-pure': 8.0.0 + '@babel/helper-member-expression-to-functions': 8.0.5 + '@babel/helper-optimise-call-expression': 8.0.0 + '@babel/helper-replace-supers': 8.0.1(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/helper-skip-transparent-expression-wrappers': 8.0.0 + '@babel/traverse': 8.0.7 + verkit: 0.5.0 + '@babel/helper-create-regexp-features-plugin@7.28.5(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: '@babel/core': 7.29.0(supports-color@8.1.1) @@ -2473,6 +2559,11 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/helper-member-expression-to-functions@8.0.5': + dependencies: + '@babel/traverse': 8.0.7 + '@babel/types': 8.0.8 + '@babel/helper-module-imports@7.28.6(supports-color@8.1.1)': dependencies: '@babel/traverse': 7.29.0(supports-color@8.1.1) @@ -2480,6 +2571,11 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/helper-module-imports@8.0.0': + dependencies: + '@babel/traverse': 8.0.7 + '@babel/types': 8.0.8 + '@babel/helper-module-transforms@7.28.6(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1)': dependencies: '@babel/core': 7.29.0(supports-color@8.1.1) @@ -2489,12 +2585,27 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/helper-module-transforms@8.0.6(@babel/core@7.29.0(supports-color@8.1.1))': + dependencies: + '@babel/core': 7.29.0(supports-color@8.1.1) + '@babel/helper-module-imports': 8.0.0 + '@babel/helper-validator-identifier': 8.0.6 + '@babel/traverse': 8.0.7 + '@babel/helper-optimise-call-expression@7.27.1': dependencies: '@babel/types': 7.29.0 + '@babel/helper-optimise-call-expression@8.0.0': + dependencies: + '@babel/types': 8.0.8 + '@babel/helper-plugin-utils@7.28.6': {} + '@babel/helper-plugin-utils@8.0.1(@babel/core@7.29.0(supports-color@8.1.1))': + dependencies: + '@babel/core': 7.29.0(supports-color@8.1.1) + '@babel/helper-remap-async-to-generator@7.27.1(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1)': dependencies: '@babel/core': 7.29.0(supports-color@8.1.1) @@ -2513,6 +2624,13 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/helper-replace-supers@8.0.1(@babel/core@7.29.0(supports-color@8.1.1))': + dependencies: + '@babel/core': 7.29.0(supports-color@8.1.1) + '@babel/helper-member-expression-to-functions': 8.0.5 + '@babel/helper-optimise-call-expression': 8.0.0 + '@babel/traverse': 8.0.7 + '@babel/helper-skip-transparent-expression-wrappers@7.27.1(supports-color@8.1.1)': dependencies: '@babel/traverse': 7.29.0(supports-color@8.1.1) @@ -2520,6 +2638,11 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/helper-skip-transparent-expression-wrappers@8.0.0': + dependencies: + '@babel/traverse': 8.0.7 + '@babel/types': 8.0.8 + '@babel/helper-string-parser@7.27.1': {} '@babel/helper-string-parser@8.0.8': {} @@ -2695,6 +2818,11 @@ snapshots: '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-syntax-typescript@8.0.3(@babel/core@7.29.0(supports-color@8.1.1))': + dependencies: + '@babel/core': 7.29.0(supports-color@8.1.1) + '@babel/helper-plugin-utils': 8.0.1(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: '@babel/core': 7.29.0(supports-color@8.1.1) @@ -2869,6 +2997,12 @@ snapshots: transitivePeerDependencies: - supports-color + '@babel/plugin-transform-modules-commonjs@8.0.1(@babel/core@7.29.0(supports-color@8.1.1))': + dependencies: + '@babel/core': 7.29.0(supports-color@8.1.1) + '@babel/helper-module-transforms': 8.0.6(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/helper-plugin-utils': 8.0.1(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-modules-systemjs@7.29.4(@babel/core@7.29.0(supports-color@8.1.1))(supports-color@8.1.1)': dependencies: '@babel/core': 7.29.0(supports-color@8.1.1) @@ -3011,6 +3145,15 @@ snapshots: '@babel/core': 7.29.0(supports-color@8.1.1) '@babel/helper-plugin-utils': 7.28.6 + '@babel/plugin-transform-typescript@8.0.6(@babel/core@7.29.0(supports-color@8.1.1))': + dependencies: + '@babel/core': 7.29.0(supports-color@8.1.1) + '@babel/helper-annotate-as-pure': 8.0.0 + '@babel/helper-create-class-features-plugin': 8.0.7(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/helper-plugin-utils': 8.0.1(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/helper-skip-transparent-expression-wrappers': 8.0.0 + '@babel/plugin-syntax-typescript': 8.0.3(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-unicode-escapes@7.27.1(@babel/core@7.29.0(supports-color@8.1.1))': dependencies: '@babel/core': 7.29.0(supports-color@8.1.1) @@ -3118,6 +3261,14 @@ snapshots: '@babel/types': 7.29.0 esutils: 2.0.3 + '@babel/preset-typescript@8.0.7(@babel/core@7.29.0(supports-color@8.1.1))': + dependencies: + '@babel/core': 7.29.0(supports-color@8.1.1) + '@babel/helper-plugin-utils': 8.0.1(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/helper-validator-option': 8.0.0 + '@babel/plugin-transform-modules-commonjs': 8.0.1(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/plugin-transform-typescript': 8.0.6(@babel/core@7.29.0(supports-color@8.1.1)) + '@babel/runtime@7.29.2': {} '@babel/template@7.28.6': diff --git a/scripts/babel8.js b/scripts/babel8.js index 1d59cf23..0b94f868 100644 --- a/scripts/babel8.js +++ b/scripts/babel8.js @@ -9,6 +9,7 @@ const fs = require('fs') const path = require('path') const babel7 = require('@babel/core') const babel = require('babel8') +const presetTypescript = require('babel8-preset-typescript').default const ROOT = path.resolve(__dirname, '..') const FIXTURES = path.join(ROOT, 'test', 'fixtures') @@ -24,6 +25,20 @@ const SKIP = { 'Babel 8 rejects an import inside `declare namespace` while parsing, as TypeScript does', } +// Fixtures always parse as code.js, so the extension rules run here +const TYPESCRIPT_CASES = [ + { + file: 'a.ts', + code: 'export const C = B', + expected: 'export const C = B;\n', + }, + { + file: 'a.tsx', + code: 'const A =
', + expected: 'const A =
;\n', + }, +] + // Mirrors the babel-test patch in patches/ so error.js matches across machines const normalizeError = e => String(e) @@ -101,6 +116,16 @@ const main = async () => { else failed.push({ name, expected, actual }) } + for (const { file, code, expected } of TYPESCRIPT_CASES) { + const actual = await transform(babel, code, { + filename: path.join(ROOT, file), + plugins: [lib], + presets: [presetTypescript], + }) + if (actual === expected) passed++ + else failed.push({ name: file, expected, actual }) + } + for (const { name, expected, actual } of failed) { console.log(`\n✗ ${name}\n--- expected\n${expected}--- actual\n${actual}`) } diff --git a/src/index.js b/src/index.js index 3eb98168..d8384c93 100644 --- a/src/index.js +++ b/src/index.js @@ -13,10 +13,12 @@ export default function ({ types: t }) { return { // Same as @babel/plugin-syntax-jsx, whose peer range is pinned to one - // Babel major. With TypeScript, the TypeScript preset decides per file - // (.tsx) whether JSX is on, and forcing it breaks `value` assertions + // Babel major. Presets run after this, and Babel 8's TypeScript preset + // neither removes `jsx` from .ts files nor adds it to .tsx files, so the + // extension decides: `jsx` breaks `value` assertions in .ts manipulateOptions(opts, parserOpts) { if ( + /\.[cm]?ts$/.test(opts.filename) || parserOpts.plugins.some( p => (Array.isArray(p) ? p[0] : p) === 'typescript' ) diff --git a/test/index.test.js b/test/index.test.js index 11ff39eb..7dc493c0 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -2,6 +2,8 @@ import fs from 'fs' import path from 'path' import { create } from 'babel-test' import { toMatchFile } from 'jest-file-snapshot' +import { transformSync } from '@babel/core' +import plugin from '../src' expect.extend({ toMatchFile }) @@ -48,3 +50,31 @@ describe('runtime dependencies', () => { expect(narrower).toEqual([]) }) }) + +describe('jsx parser option', () => { + // Stands in for Babel 8's TypeScript preset, which runs after this plugin + // and leaves any `jsx` parser plugin in place + const lateTypescript = () => ({ + manipulateOptions(opts, parserOpts) { + parserOpts.plugins.push('typescript') + }, + }) + + const transform = (code, filename) => + transformSync(code, { + babelrc: false, + configFile: false, + filename, + plugins: [plugin, lateTypescript], + }).code + + it.each(['a.ts', 'a.mts', 'a.cts'])('stays off for %s', filename => { + expect(transform('export const C = B', filename)).toBe( + 'export const C = B;' + ) + }) + + it.each(['a.tsx', 'a.js'])('turns on for %s', filename => { + expect(transform('const A =
', filename)).toBe('const A =
;') + }) +})