From a477f50ba3324e6c74d74ac3697351a3a01f2852 Mon Sep 17 00:00:00 2001 From: kanno <812137533@qq.com> Date: Tue, 8 Oct 2024 03:22:45 +0800 Subject: [PATCH] fix: should respect legacy decorators (#67) Co-authored-by: Sukka --- src/index.ts | 8 +- src/options.ts | 6 +- test/__snapshots__/index.ts.snap | 353 +++++++++++++++--- test/fixtures/decorators/index.ts | 32 +- test/fixtures/legacy-decorators/index.ts | 25 ++ test/fixtures/legacy-decorators/tsconfig.json | 9 + test/index.ts | 10 +- 7 files changed, 381 insertions(+), 62 deletions(-) create mode 100644 test/fixtures/legacy-decorators/index.ts create mode 100644 test/fixtures/legacy-decorators/tsconfig.json diff --git a/src/index.ts b/src/index.ts index a14cfd4..33346d4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -15,7 +15,7 @@ import { } from '@swc/core'; import createDeepMerge from '@fastify/deepmerge'; -import { getOptions, getEnableExperimentalDecorators } from './options'; +import { getOptions, checkIsLegacyTypeScript } from './options'; import type { Plugin as VitePlugin } from 'vite'; @@ -67,7 +67,7 @@ function swc(options: PluginOptions = {}): RollupPlugin { return null; }; - const enableExperimentalDecorators = getEnableExperimentalDecorators(); + const isLegacyTypeScript = checkIsLegacyTypeScript(); return { name: 'swc', @@ -127,7 +127,7 @@ function swc(options: PluginOptions = {}): RollupPlugin { parser: { syntax: isTypeScript ? 'typescript' : 'ecmascript', [isTypeScript ? 'tsx' : 'jsx']: isTypeScript ? isTsx : isJsx, - decorators: enableExperimentalDecorators || tsconfigOptions.experimentalDecorators + decorators: !isLegacyTypeScript || tsconfigOptions.experimentalDecorators }, transform: { decoratorMetadata: tsconfigOptions.emitDecoratorMetadata, @@ -140,7 +140,7 @@ function swc(options: PluginOptions = {}): RollupPlugin { pragmaFrag: tsconfigOptions.jsxFragmentFactory, development: tsconfigOptions.jsx === 'react-jsxdev' ? true : undefined }, - decoratorVersion: enableExperimentalDecorators ? '2022-03' : '2021-12' + decoratorVersion: isLegacyTypeScript ? '2021-12' : (tsconfigOptions.experimentalDecorators ? '2021-12' : '2022-03') }, target: tsconfigOptions.target?.toLowerCase() as JscTarget | undefined, baseUrl: tsconfigOptions.baseUrl, diff --git a/src/options.ts b/src/options.ts index 223083d..0dd5fc7 100644 --- a/src/options.ts +++ b/src/options.ts @@ -59,14 +59,14 @@ export const getOptions = ( return compilerOptions; }; -export const getEnableExperimentalDecorators = () => { +export const checkIsLegacyTypeScript = () => { try { // @ts-expect-error -- It's required to using 'import.mtea.url' but i don't want to change the tsconfig. const tsPath = resolve('typescript/package.json', import.meta.url); const { version } = JSON.parse(fs.readFileSync(fileURLToPath(tsPath), 'utf-8')); const [major] = version.split('.'); - // Only check experimental decorators for TypeScript 5+ - return +major >= 5; + // typescript 5+ enable experimentalDecorators by default so we think it's not legacy + return +major < 5; } catch { return false; } diff --git a/test/__snapshots__/index.ts.snap b/test/__snapshots__/index.ts.snap index b6d8023..a866ec4 100644 --- a/test/__snapshots__/index.ts.snap +++ b/test/__snapshots__/index.ts.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`rollup-plugin-swc3 swc (rollup 2) detect decorator for typescript5 1`] = ` +exports[`rollup-plugin-swc3 swc (rollup 2) detect decorator for typescript5 1`] = ` "function _array_like_to_array(arr, len) { if (len == null || len > arr.length) len = arr.length; for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i]; @@ -14,6 +14,20 @@ function _class_call_check(instance, Constructor) { throw new TypeError(\\"Cannot call a class as a function\\"); } } +function _defineProperties(target, props) { + for(var i = 0; i < props.length; i++){ + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if (\\"value\\" in descriptor) descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); + } +} +function _create_class(Constructor, protoProps, staticProps) { + if (protoProps) _defineProperties(Constructor.prototype, protoProps); + if (staticProps) _defineProperties(Constructor, staticProps); + return Constructor; +} function _define_property(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { @@ -437,23 +451,97 @@ function applyDecs2203RFactory() { function _apply_decs_2203_r(targetClass, memberDecs, classDecs, parentClass) { return (_apply_decs_2203_r = applyDecs2203RFactory())(targetClass, memberDecs, classDecs, parentClass); } -var // @ts-expect-error -_init_name; -var printMemberName = function(target, memberName) { - console.log(memberName); -}; -var Person = function Person() { - _class_call_check(this, Person); - _define_property(this, \\"name\\", _init_name(this, \\"Jon\\")); -}; +var _initProto; +function trace(value, param) { + var kind = param.kind; param.name; + if (kind === 'method') { + return function() { + for(var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++){ + args[_key] = arguments[_key]; + } + console.log('trace'); + return value.apply(this, args); + }; + } +} +var People = /*#__PURE__*/ function() { + function People() { + _class_call_check(this, People); + _define_property(this, \\"xzy\\", void 0); + _initProto(this); + this.xzy = 'xzy'; + } + _create_class(People, [ + { + key: \\"test\\", + value: function test() { + return this.xzy; + } + } + ]); + return People; +}(); var ref, ref1; -ref = _apply_decs_2203_r(Person, [ +ref = _apply_decs_2203_r(People, [ [ - printMemberName, - 0, - \\"name\\" + trace, + 2, + \\"test\\" ] -], []), ref1 = _sliced_to_array(ref.e, 1), _init_name = ref1[0]; +], []), ref1 = _sliced_to_array(ref.e, 1), _initProto = ref1[0]; +var p = new People(); +p.test(); +" +`; + +exports[`rollup-plugin-swc3 swc (rollup 2) detect legacy decorator for typescript5 1`] = ` +"function _define_property(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} +function _ts_decorate(decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === \\"object\\" && typeof Reflect.decorate === \\"function\\") r = Reflect.decorate(decorators, target, key, desc); + else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +} +function _ts_metadata(k, v) { + if (typeof Reflect === \\"object\\" && typeof Reflect.metadata === \\"function\\") return Reflect.metadata(k, v); +} +function trace(_target, _name, descriptor) { + const original = descriptor.value; + descriptor.value = function() { + console.log('trace'); + return original.call(this); + }; + return descriptor; +} +class People { + test() { + return this.xzy; + } + constructor(){ + _define_property(this, \\"xzy\\", void 0); + this.xzy = 'xzy'; + } +} +_ts_decorate([ + trace, + _ts_metadata(\\"design:type\\", Function), + _ts_metadata(\\"design:paramtypes\\", []), + _ts_metadata(\\"design:returntype\\", void 0) +], People.prototype, \\"test\\", null); +const p = new People(); +p.test(); " `; @@ -1158,7 +1246,7 @@ export { foo }; " `; -exports[`rollup-plugin-swc3 swc (rollup 3) detect decorator for typescript5 1`] = ` +exports[`rollup-plugin-swc3 swc (rollup 3) detect decorator for typescript5 1`] = ` "function _array_like_to_array(arr, len) { if (len == null || len > arr.length) len = arr.length; for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i]; @@ -1172,6 +1260,20 @@ function _class_call_check(instance, Constructor) { throw new TypeError(\\"Cannot call a class as a function\\"); } } +function _defineProperties(target, props) { + for(var i = 0; i < props.length; i++){ + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if (\\"value\\" in descriptor) descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); + } +} +function _create_class(Constructor, protoProps, staticProps) { + if (protoProps) _defineProperties(Constructor.prototype, protoProps); + if (staticProps) _defineProperties(Constructor, staticProps); + return Constructor; +} function _define_property(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { @@ -1595,23 +1697,97 @@ function applyDecs2203RFactory() { function _apply_decs_2203_r(targetClass, memberDecs, classDecs, parentClass) { return (_apply_decs_2203_r = applyDecs2203RFactory())(targetClass, memberDecs, classDecs, parentClass); } -var // @ts-expect-error -_init_name; -var printMemberName = function(target, memberName) { - console.log(memberName); -}; -var Person = function Person() { - _class_call_check(this, Person); - _define_property(this, \\"name\\", _init_name(this, \\"Jon\\")); -}; +var _initProto; +function trace(value, param) { + var kind = param.kind; param.name; + if (kind === 'method') { + return function() { + for(var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++){ + args[_key] = arguments[_key]; + } + console.log('trace'); + return value.apply(this, args); + }; + } +} +var People = /*#__PURE__*/ function() { + function People() { + _class_call_check(this, People); + _define_property(this, \\"xzy\\", void 0); + _initProto(this); + this.xzy = 'xzy'; + } + _create_class(People, [ + { + key: \\"test\\", + value: function test() { + return this.xzy; + } + } + ]); + return People; +}(); var ref, ref1; -ref = _apply_decs_2203_r(Person, [ +ref = _apply_decs_2203_r(People, [ [ - printMemberName, - 0, - \\"name\\" + trace, + 2, + \\"test\\" ] -], []), ref1 = _sliced_to_array(ref.e, 1), _init_name = ref1[0]; +], []), ref1 = _sliced_to_array(ref.e, 1), _initProto = ref1[0]; +var p = new People(); +p.test(); +" +`; + +exports[`rollup-plugin-swc3 swc (rollup 3) detect legacy decorator for typescript5 1`] = ` +"function _define_property(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} +function _ts_decorate(decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === \\"object\\" && typeof Reflect.decorate === \\"function\\") r = Reflect.decorate(decorators, target, key, desc); + else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +} +function _ts_metadata(k, v) { + if (typeof Reflect === \\"object\\" && typeof Reflect.metadata === \\"function\\") return Reflect.metadata(k, v); +} +function trace(_target, _name, descriptor) { + const original = descriptor.value; + descriptor.value = function() { + console.log('trace'); + return original.call(this); + }; + return descriptor; +} +class People { + test() { + return this.xzy; + } + constructor(){ + _define_property(this, \\"xzy\\", void 0); + this.xzy = 'xzy'; + } +} +_ts_decorate([ + trace, + _ts_metadata(\\"design:type\\", Function), + _ts_metadata(\\"design:paramtypes\\", []), + _ts_metadata(\\"design:returntype\\", void 0) +], People.prototype, \\"test\\", null); +const p = new People(); +p.test(); " `; @@ -2314,7 +2490,7 @@ export { foo }; " `; -exports[`rollup-plugin-swc3 swc (rollup 4) detect decorator for typescript5 1`] = ` +exports[`rollup-plugin-swc3 swc (rollup 4) detect decorator for typescript5 1`] = ` "function _array_like_to_array(arr, len) { if (len == null || len > arr.length) len = arr.length; for(var i = 0, arr2 = new Array(len); i < len; i++)arr2[i] = arr[i]; @@ -2328,6 +2504,19 @@ function _class_call_check(instance, Constructor) { throw new TypeError(\\"Cannot call a class as a function\\"); } } +function _defineProperties(target, props) { + for(var i = 0; i < props.length; i++){ + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if (\\"value\\" in descriptor) descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); + } +} +function _create_class(Constructor, protoProps, staticProps) { + if (protoProps) _defineProperties(Constructor.prototype, protoProps); + return Constructor; +} function _define_property(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { @@ -2751,23 +2940,97 @@ function applyDecs2203RFactory() { function _apply_decs_2203_r(targetClass, memberDecs, classDecs, parentClass) { return (_apply_decs_2203_r = applyDecs2203RFactory())(targetClass, memberDecs, classDecs, parentClass); } -var // @ts-expect-error -_init_name; -var printMemberName = function(target, memberName) { - console.log(memberName); -}; -var Person = function Person() { - _class_call_check(this, Person); - _define_property(this, \\"name\\", _init_name(this, \\"Jon\\")); -}; +var _initProto; +function trace(value, param) { + var kind = param.kind; param.name; + if (kind === 'method') { + return function() { + for(var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++){ + args[_key] = arguments[_key]; + } + console.log('trace'); + return value.apply(this, args); + }; + } +} +var People = /*#__PURE__*/ function() { + function People() { + _class_call_check(this, People); + _define_property(this, \\"xzy\\", void 0); + _initProto(this); + this.xzy = 'xzy'; + } + _create_class(People, [ + { + key: \\"test\\", + value: function test() { + return this.xzy; + } + } + ]); + return People; +}(); var ref, ref1; -ref = _apply_decs_2203_r(Person, [ +ref = _apply_decs_2203_r(People, [ [ - printMemberName, - 0, - \\"name\\" + trace, + 2, + \\"test\\" ] -], []), ref1 = _sliced_to_array(ref.e, 1), _init_name = ref1[0]; +], []), ref1 = _sliced_to_array(ref.e, 1), _initProto = ref1[0]; +var p = new People(); +p.test(); +" +`; + +exports[`rollup-plugin-swc3 swc (rollup 4) detect legacy decorator for typescript5 1`] = ` +"function _define_property(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + return obj; +} +function _ts_decorate(decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (typeof Reflect === \\"object\\" && typeof Reflect.decorate === \\"function\\") r = Reflect.decorate(decorators, target, key, desc); + else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; + return c > 3 && r && Object.defineProperty(target, key, r), r; +} +function _ts_metadata(k, v) { + if (typeof Reflect === \\"object\\" && typeof Reflect.metadata === \\"function\\") return Reflect.metadata(k, v); +} +function trace(_target, _name, descriptor) { + const original = descriptor.value; + descriptor.value = function() { + console.log('trace'); + return original.call(this); + }; + return descriptor; +} +class People { + test() { + return this.xzy; + } + constructor(){ + _define_property(this, \\"xzy\\", void 0); + this.xzy = 'xzy'; + } +} +_ts_decorate([ + trace, + _ts_metadata(\\"design:type\\", Function), + _ts_metadata(\\"design:paramtypes\\", []), + _ts_metadata(\\"design:returntype\\", void 0) +], People.prototype, \\"test\\", null); +const p = new People(); +p.test(); " `; diff --git a/test/fixtures/decorators/index.ts b/test/fixtures/decorators/index.ts index 3754a4c..5a8a8ec 100644 --- a/test/fixtures/decorators/index.ts +++ b/test/fixtures/decorators/index.ts @@ -1,9 +1,23 @@ -const printMemberName = (target: any, memberName: string) => { - console.log(memberName); - }; - -class Person { - // @ts-expect-error - @printMemberName - name: string = "Jon"; -} \ No newline at end of file +function trace(value, {kind, name}) { + if (kind === 'method') { + return function (...args) { + console.log('trace') + return value.apply(this, args); + }; + } +} + +class People { + xzy: string + constructor(){ + this.xzy = 'xzy'; + } + @trace + test() { + return this.xzy + } +} + +const p = new People(); + +p.test(); diff --git a/test/fixtures/legacy-decorators/index.ts b/test/fixtures/legacy-decorators/index.ts new file mode 100644 index 0000000..3b41e0d --- /dev/null +++ b/test/fixtures/legacy-decorators/index.ts @@ -0,0 +1,25 @@ +function trace(_target: any, _name: string, descriptor: PropertyDescriptor) { + const original = descriptor.value; + descriptor.value = function () { + console.log('trace'); + return original.call(this); + }; + + return descriptor; +} + +class People { + xzy: string; + constructor() { + this.xzy = 'xzy'; + } + + @trace + test() { + return this.xzy; + } +} + +const p = new People(); + +p.test(); diff --git a/test/fixtures/legacy-decorators/tsconfig.json b/test/fixtures/legacy-decorators/tsconfig.json new file mode 100644 index 0000000..324640c --- /dev/null +++ b/test/fixtures/legacy-decorators/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "target": "ES2017", + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "module": "ES2022", + "baseUrl": "./" + } +} diff --git a/test/index.ts b/test/index.ts index 36c5907..b99c0a1 100644 --- a/test/index.ts +++ b/test/index.ts @@ -376,7 +376,7 @@ const tests = (rollupImpl: typeof rollup2 | typeof rollup3 | typeof rollup4, iso ))[0].code.should.matchSnapshot(); }); - it('detect decorator for typescript5', async () => { + it('detect decorator for typescript5', async () => { const dir = await fixture('decorators'); (await build( rollupImpl, @@ -384,6 +384,14 @@ const tests = (rollupImpl: typeof rollup2 | typeof rollup3 | typeof rollup4, iso { input: './index.ts', dir } ))[0].code.should.matchSnapshot(); }); + it('detect legacy decorator for typescript5', async () => { + const dir = await fixture('legacy-decorators'); + (await build( + rollupImpl, + {}, + { input: './index.ts', dir } + ))[0].code.should.matchSnapshot(); + }); }; describe('rollup-plugin-swc3', () => {