diff --git a/e2e/README.md b/e2e/README.md index 149039202..5e4faeea7 100644 --- a/e2e/README.md +++ b/e2e/README.md @@ -17,7 +17,7 @@ Rslib will try to cover the common scenarios in the [integration test cases of M | banner-footer | 🟢 | | | buildType | 🟢 | | | copy | 🟢 | | -| decorator | ⚪️ | | +| decorator | 🟢 | | | define | 🟢 | | | dts | 🟢 | | | dts-composite | ⚪️ | | @@ -26,21 +26,21 @@ Rslib will try to cover the common scenarios in the [integration test cases of M | format | 🟡 | Support `cjs` and `esm`, `umd` still need to be tested | | input | 🟢 | | | jsx | ⚪️ | | -| metafile | ⚪️ | | +| metafile | ⚫️ | | | minify | 🟢 | | | platform | 🟢 | | | redirect | ⚪️ | | | resolve | 🟢 | | | shims | 🟡 | Support shims `__filename` and `__dirname` in esm
`import.meta.url` in cjs need to be supported | | sideEffects | ⚪️ | | -| sourceDir | ⚪️ | | +| sourceDir | 🟢 | | | sourceMap | 🟢 | | | splitting | ⚪️ | | | style | ⚪️ | | | target | 🟢 | | | transformImport | 🟢 | | | transformLodash | 🟢 | | -| tsconfig | ⚪️ | | -| tsconfigExtends | ⚪️ | | +| tsconfig | 🟢 | | +| tsconfigExtends | 🟢 | | | umdGlobals | ⚪️ | | | umdModuleName | ⚪️ | | diff --git a/e2e/cases/decorators/__fixtures__/src/index.ts b/e2e/cases/decorators/__fixtures__/src/index.ts new file mode 100644 index 000000000..1d0c8e0ef --- /dev/null +++ b/e2e/cases/decorators/__fixtures__/src/index.ts @@ -0,0 +1,13 @@ +function enhancer(name: string) { + return function enhancer(target: any) { + target.prototype.name = name; + }; +} +@enhancer('rslib') +export class Person { + version: string; + + constructor() { + this.version = '1.0.0'; + } +} diff --git a/e2e/cases/decorators/__fixtures__/tsconfig.decorators.json b/e2e/cases/decorators/__fixtures__/tsconfig.decorators.json new file mode 100644 index 000000000..2b866fec1 --- /dev/null +++ b/e2e/cases/decorators/__fixtures__/tsconfig.decorators.json @@ -0,0 +1,8 @@ +{ + "extends": "@rslib/tsconfig/base", + "compilerOptions": { + "baseUrl": "./", + "experimentalDecorators": true + }, + "include": ["src"] +} diff --git a/e2e/cases/decorators/__fixtures__/tsconfig.json b/e2e/cases/decorators/__fixtures__/tsconfig.json new file mode 100644 index 000000000..888d3e460 --- /dev/null +++ b/e2e/cases/decorators/__fixtures__/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "@rslib/tsconfig/base", + "compilerOptions": { + "baseUrl": "./" + }, + "include": ["src"] +} diff --git a/e2e/cases/decorators/__snapshots__/index.test.ts.snap b/e2e/cases/decorators/__snapshots__/index.test.ts.snap new file mode 100644 index 000000000..52cce022a --- /dev/null +++ b/e2e/cases/decorators/__snapshots__/index.test.ts.snap @@ -0,0 +1,785 @@ +// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html + +exports[`decorators default to 2022-03 1`] = ` +"// The require scope +var __webpack_require__ = {}; +/************************************************************************/ // webpack/runtime/define_property_getters +(()=>{ + __webpack_require__.d = function(exports, definition) { + for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) Object.defineProperty(exports, key, { + enumerable: true, + get: definition[key] + }); + }; +})(); +// webpack/runtime/has_own_property +(()=>{ + __webpack_require__.o = function(obj, prop) { + return Object.prototype.hasOwnProperty.call(obj, prop); + }; +})(); +// webpack/runtime/make_namespace_object +(()=>{ + // define __esModule on exports + __webpack_require__.r = function(exports) { + if ('undefined' != typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports, Symbol.toStringTag, { + value: 'Module' + }); + Object.defineProperty(exports, '__esModule', { + value: true + }); + }; +})(); +/************************************************************************/ var __webpack_exports__ = {}; +__webpack_require__.r(__webpack_exports__); +__webpack_require__.d(__webpack_exports__, { + Person: function() { + return _Person; + } +}); +function applyDecs2203RFactory() { + function createAddInitializerMethod(initializers, decoratorFinishedRef) { + return function(initializer) { + assertNotFinished(decoratorFinishedRef, \\"addInitializer\\"); + assertCallable(initializer, \\"An initializer\\"); + initializers.push(initializer); + }; + } + function memberDec(dec, name, desc, initializers, kind, isStatic, isPrivate, metadata, value) { + var kindStr; + switch(kind){ + case 1: + kindStr = \\"accessor\\"; + break; + case 2: + kindStr = \\"method\\"; + break; + case 3: + kindStr = \\"getter\\"; + break; + case 4: + kindStr = \\"setter\\"; + break; + default: + kindStr = \\"field\\"; + } + var ctx = { + kind: kindStr, + name: isPrivate ? \\"#\\" + name : name, + static: isStatic, + private: isPrivate, + metadata: metadata + }; + var decoratorFinishedRef = { + v: false + }; + ctx.addInitializer = createAddInitializerMethod(initializers, decoratorFinishedRef); + var get, set; + if (0 === kind) { + if (isPrivate) { + get = desc.get; + set = desc.set; + } else { + get = function() { + return this[name]; + }; + set = function(v) { + this[name] = v; + }; + } + } else if (2 === kind) get = function() { + return desc.value; + }; + else { + if (1 === kind || 3 === kind) get = function() { + return desc.get.call(this); + }; + if (1 === kind || 4 === kind) set = function(v) { + desc.set.call(this, v); + }; + } + ctx.access = get && set ? { + get: get, + set: set + } : get ? { + get: get + } : { + set: set + }; + try { + return dec(value, ctx); + } finally{ + decoratorFinishedRef.v = true; + } + } + function assertNotFinished(decoratorFinishedRef, fnName) { + if (decoratorFinishedRef.v) throw new Error(\\"attempted to call \\" + fnName + \\" after decoration was finished\\"); + } + function assertCallable(fn, hint) { + if (\\"function\\" != typeof fn) throw new TypeError(hint + \\" must be a function\\"); + } + function assertValidReturnValue(kind, value) { + var type = typeof value; + if (1 === kind) { + if (\\"object\\" !== type || null === value) throw new TypeError(\\"accessor decorators must return an object with get, set, or init properties or void 0\\"); + if (void 0 !== value.get) assertCallable(value.get, \\"accessor.get\\"); + if (void 0 !== value.set) assertCallable(value.set, \\"accessor.set\\"); + if (void 0 !== value.init) assertCallable(value.init, \\"accessor.init\\"); + } else if (\\"function\\" !== type) { + var hint; + hint = 0 === kind ? \\"field\\" : 10 === kind ? \\"class\\" : \\"method\\"; + throw new TypeError(hint + \\" decorators must return a function or void 0\\"); + } + } + function applyMemberDec(ret, base, decInfo, name, kind, isStatic, isPrivate, initializers, metadata) { + var decs = decInfo[0]; + var desc, init, value; + if (isPrivate) desc = 0 === kind || 1 === kind ? { + get: decInfo[3], + set: decInfo[4] + } : 3 === kind ? { + get: decInfo[3] + } : 4 === kind ? { + set: decInfo[3] + } : { + value: decInfo[3] + }; + else if (0 !== kind) desc = Object.getOwnPropertyDescriptor(base, name); + if (1 === kind) value = { + get: desc.get, + set: desc.set + }; + else if (2 === kind) value = desc.value; + else if (3 === kind) value = desc.get; + else if (4 === kind) value = desc.set; + var newValue, get, set; + if (\\"function\\" == typeof decs) { + newValue = memberDec(decs, name, desc, initializers, kind, isStatic, isPrivate, metadata, value); + if (void 0 !== newValue) { + assertValidReturnValue(kind, newValue); + if (0 === kind) init = newValue; + else if (1 === kind) { + init = newValue.init; + get = newValue.get || value.get; + set = newValue.set || value.set; + value = { + get: get, + set: set + }; + } else value = newValue; + } + } else for(var i = decs.length - 1; i >= 0; i--){ + var dec = decs[i]; + newValue = memberDec(dec, name, desc, initializers, kind, isStatic, isPrivate, metadata, value); + if (void 0 !== newValue) { + assertValidReturnValue(kind, newValue); + var newInit; + if (0 === kind) newInit = newValue; + else if (1 === kind) { + newInit = newValue.init; + get = newValue.get || value.get; + set = newValue.set || value.set; + value = { + get: get, + set: set + }; + } else value = newValue; + if (void 0 !== newInit) { + if (void 0 === init) init = newInit; + else if (\\"function\\" == typeof init) init = [ + init, + newInit + ]; + else init.push(newInit); + } + } + } + if (0 === kind || 1 === kind) { + if (void 0 === init) init = function(instance, init) { + return init; + }; + else if (\\"function\\" != typeof init) { + var ownInitializers = init; + init = function(instance, init) { + var value = init; + for(var i = 0; i < ownInitializers.length; i++)value = ownInitializers[i].call(instance, value); + return value; + }; + } else { + var originalInitializer = init; + init = function(instance, init) { + return originalInitializer.call(instance, init); + }; + } + ret.push(init); + } + if (0 !== kind) { + if (1 === kind) { + desc.get = value.get; + desc.set = value.set; + } else if (2 === kind) desc.value = value; + else if (3 === kind) desc.get = value; + else if (4 === kind) desc.set = value; + if (isPrivate) { + if (1 === kind) { + ret.push(function(instance, args) { + return value.get.call(instance, args); + }); + ret.push(function(instance, args) { + return value.set.call(instance, args); + }); + } else if (2 === kind) ret.push(value); + else ret.push(function(instance, args) { + return value.call(instance, args); + }); + } else Object.defineProperty(base, name, desc); + } + } + function applyMemberDecs(Class, decInfos, metadata) { + var ret = []; + var protoInitializers; + var staticInitializers; + var existingProtoNonFields = new Map(); + var existingStaticNonFields = new Map(); + for(var i = 0; i < decInfos.length; i++){ + var decInfo = decInfos[i]; + if (!!Array.isArray(decInfo)) { + var kind = decInfo[1]; + var name = decInfo[2]; + var isPrivate = decInfo.length > 3; + var isStatic = kind >= 5; + var base; + var initializers; + if (isStatic) { + base = Class; + kind -= 5; + staticInitializers = staticInitializers || []; + initializers = staticInitializers; + } else { + base = Class.prototype; + protoInitializers = protoInitializers || []; + initializers = protoInitializers; + } + if (0 !== kind && !isPrivate) { + var existingNonFields = isStatic ? existingStaticNonFields : existingProtoNonFields; + var existingKind = existingNonFields.get(name) || 0; + if (true === existingKind || 3 === existingKind && 4 !== kind || 4 === existingKind && 3 !== kind) throw new Error(\\"Attempted to decorate a public method/accessor that has the same name as a previously decorated public method/accessor. This is not currently supported by the decorators plugin. Property name was: \\" + name); + if (!existingKind && kind > 2) existingNonFields.set(name, kind); + else existingNonFields.set(name, true); + } + applyMemberDec(ret, base, decInfo, name, kind, isStatic, isPrivate, initializers, metadata); + } + } + pushInitializers(ret, protoInitializers); + pushInitializers(ret, staticInitializers); + return ret; + } + function pushInitializers(ret, initializers) { + if (initializers) ret.push(function(instance) { + for(var i = 0; i < initializers.length; i++)initializers[i].call(instance); + return instance; + }); + } + function applyClassDecs(targetClass, classDecs, metadata) { + if (classDecs.length > 0) { + var initializers = []; + var newClass = targetClass; + var name = targetClass.name; + for(var i = classDecs.length - 1; i >= 0; i--){ + var decoratorFinishedRef = { + v: false + }; + try { + var nextNewClass = classDecs[i](newClass, { + kind: \\"class\\", + name: name, + addInitializer: createAddInitializerMethod(initializers, decoratorFinishedRef), + metadata + }); + } finally{ + decoratorFinishedRef.v = true; + } + if (void 0 !== nextNewClass) { + assertValidReturnValue(10, nextNewClass); + newClass = nextNewClass; + } + } + return [ + defineMetadata(newClass, metadata), + function() { + for(var i = 0; i < initializers.length; i++)initializers[i].call(newClass); + } + ]; + } + } + function defineMetadata(Class, metadata) { + return Object.defineProperty(Class, Symbol.metadata || Symbol.for(\\"Symbol.metadata\\"), { + configurable: true, + enumerable: true, + value: metadata + }); + } + return function(targetClass, memberDecs, classDecs, parentClass) { + if (void 0 !== parentClass) var parentMetadata = parentClass[Symbol.metadata || Symbol.for(\\"Symbol.metadata\\")]; + var metadata = Object.create(void 0 === parentMetadata ? null : parentMetadata); + var e = applyMemberDecs(targetClass, memberDecs, metadata); + if (!classDecs.length) defineMetadata(targetClass, metadata); + return { + e: e, + get c () { + return applyClassDecs(targetClass, classDecs, metadata); + } + }; + }; +} +function _apply_decs_2203_r(targetClass, memberDecs, classDecs, parentClass) { + return (_apply_decs_2203_r = applyDecs2203RFactory())(targetClass, memberDecs, classDecs, parentClass); +} +var _dec, _initClass; +function enhancer(name) { + return function(target) { + target.prototype.name = name; + }; +} +let _Person; +_dec = enhancer('rslib'); +class Person { + static{ + ({ c: [_Person, _initClass] } = _apply_decs_2203_r(this, [], [ + _dec + ])); + } + constructor(){ + this.version = '1.0.0'; + } + version; + static{ + _initClass(); + } +} +" +`; + +exports[`decorators with experimentalDecorators in tsconfig 1`] = ` +"// The require scope +var __webpack_require__ = {}; +/************************************************************************/ // webpack/runtime/define_property_getters +(()=>{ + __webpack_require__.d = function(exports, definition) { + for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) Object.defineProperty(exports, key, { + enumerable: true, + get: definition[key] + }); + }; +})(); +// webpack/runtime/has_own_property +(()=>{ + __webpack_require__.o = function(obj, prop) { + return Object.prototype.hasOwnProperty.call(obj, prop); + }; +})(); +// webpack/runtime/make_namespace_object +(()=>{ + // define __esModule on exports + __webpack_require__.r = function(exports) { + if ('undefined' != typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports, Symbol.toStringTag, { + value: 'Module' + }); + Object.defineProperty(exports, '__esModule', { + value: true + }); + }; +})(); +/************************************************************************/ var __webpack_exports__ = {}; +__webpack_require__.r(__webpack_exports__); +__webpack_require__.d(__webpack_exports__, { + Person: function() { + return Person; + } +}); +function _ts_decorate(decorators, target, key, desc) { + var c = arguments.length, r = c < 3 ? target : null === desc ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; + if (\\"object\\" == typeof Reflect && \\"function\\" == typeof Reflect.decorate) 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 (\\"object\\" == typeof Reflect && \\"function\\" == typeof Reflect.metadata) return Reflect.metadata(k, v); +} +function enhancer(name) { + return function(target) { + target.prototype.name = name; + }; +} +class Person { + constructor(){ + this.version = '1.0.0'; + } +} +Person = _ts_decorate([ + enhancer('rslib'), + _ts_metadata(\\"design:type\\", Function), + _ts_metadata(\\"design:paramtypes\\", []) +], Person); +" +`; + +exports[`decorators with experimentalDecorators in tsconfig 2`] = ` +"// The require scope +var __webpack_require__ = {}; +/************************************************************************/ // webpack/runtime/define_property_getters +(()=>{ + __webpack_require__.d = function(exports, definition) { + for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) Object.defineProperty(exports, key, { + enumerable: true, + get: definition[key] + }); + }; +})(); +// webpack/runtime/has_own_property +(()=>{ + __webpack_require__.o = function(obj, prop) { + return Object.prototype.hasOwnProperty.call(obj, prop); + }; +})(); +// webpack/runtime/make_namespace_object +(()=>{ + // define __esModule on exports + __webpack_require__.r = function(exports) { + if ('undefined' != typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports, Symbol.toStringTag, { + value: 'Module' + }); + Object.defineProperty(exports, '__esModule', { + value: true + }); + }; +})(); +/************************************************************************/ var __webpack_exports__ = {}; +__webpack_require__.r(__webpack_exports__); +__webpack_require__.d(__webpack_exports__, { + Person: function() { + return _Person; + } +}); +function applyDecs2203RFactory() { + function createAddInitializerMethod(initializers, decoratorFinishedRef) { + return function(initializer) { + assertNotFinished(decoratorFinishedRef, \\"addInitializer\\"); + assertCallable(initializer, \\"An initializer\\"); + initializers.push(initializer); + }; + } + function memberDec(dec, name, desc, initializers, kind, isStatic, isPrivate, metadata, value) { + var kindStr; + switch(kind){ + case 1: + kindStr = \\"accessor\\"; + break; + case 2: + kindStr = \\"method\\"; + break; + case 3: + kindStr = \\"getter\\"; + break; + case 4: + kindStr = \\"setter\\"; + break; + default: + kindStr = \\"field\\"; + } + var ctx = { + kind: kindStr, + name: isPrivate ? \\"#\\" + name : name, + static: isStatic, + private: isPrivate, + metadata: metadata + }; + var decoratorFinishedRef = { + v: false + }; + ctx.addInitializer = createAddInitializerMethod(initializers, decoratorFinishedRef); + var get, set; + if (0 === kind) { + if (isPrivate) { + get = desc.get; + set = desc.set; + } else { + get = function() { + return this[name]; + }; + set = function(v) { + this[name] = v; + }; + } + } else if (2 === kind) get = function() { + return desc.value; + }; + else { + if (1 === kind || 3 === kind) get = function() { + return desc.get.call(this); + }; + if (1 === kind || 4 === kind) set = function(v) { + desc.set.call(this, v); + }; + } + ctx.access = get && set ? { + get: get, + set: set + } : get ? { + get: get + } : { + set: set + }; + try { + return dec(value, ctx); + } finally{ + decoratorFinishedRef.v = true; + } + } + function assertNotFinished(decoratorFinishedRef, fnName) { + if (decoratorFinishedRef.v) throw new Error(\\"attempted to call \\" + fnName + \\" after decoration was finished\\"); + } + function assertCallable(fn, hint) { + if (\\"function\\" != typeof fn) throw new TypeError(hint + \\" must be a function\\"); + } + function assertValidReturnValue(kind, value) { + var type = typeof value; + if (1 === kind) { + if (\\"object\\" !== type || null === value) throw new TypeError(\\"accessor decorators must return an object with get, set, or init properties or void 0\\"); + if (void 0 !== value.get) assertCallable(value.get, \\"accessor.get\\"); + if (void 0 !== value.set) assertCallable(value.set, \\"accessor.set\\"); + if (void 0 !== value.init) assertCallable(value.init, \\"accessor.init\\"); + } else if (\\"function\\" !== type) { + var hint; + hint = 0 === kind ? \\"field\\" : 10 === kind ? \\"class\\" : \\"method\\"; + throw new TypeError(hint + \\" decorators must return a function or void 0\\"); + } + } + function applyMemberDec(ret, base, decInfo, name, kind, isStatic, isPrivate, initializers, metadata) { + var decs = decInfo[0]; + var desc, init, value; + if (isPrivate) desc = 0 === kind || 1 === kind ? { + get: decInfo[3], + set: decInfo[4] + } : 3 === kind ? { + get: decInfo[3] + } : 4 === kind ? { + set: decInfo[3] + } : { + value: decInfo[3] + }; + else if (0 !== kind) desc = Object.getOwnPropertyDescriptor(base, name); + if (1 === kind) value = { + get: desc.get, + set: desc.set + }; + else if (2 === kind) value = desc.value; + else if (3 === kind) value = desc.get; + else if (4 === kind) value = desc.set; + var newValue, get, set; + if (\\"function\\" == typeof decs) { + newValue = memberDec(decs, name, desc, initializers, kind, isStatic, isPrivate, metadata, value); + if (void 0 !== newValue) { + assertValidReturnValue(kind, newValue); + if (0 === kind) init = newValue; + else if (1 === kind) { + init = newValue.init; + get = newValue.get || value.get; + set = newValue.set || value.set; + value = { + get: get, + set: set + }; + } else value = newValue; + } + } else for(var i = decs.length - 1; i >= 0; i--){ + var dec = decs[i]; + newValue = memberDec(dec, name, desc, initializers, kind, isStatic, isPrivate, metadata, value); + if (void 0 !== newValue) { + assertValidReturnValue(kind, newValue); + var newInit; + if (0 === kind) newInit = newValue; + else if (1 === kind) { + newInit = newValue.init; + get = newValue.get || value.get; + set = newValue.set || value.set; + value = { + get: get, + set: set + }; + } else value = newValue; + if (void 0 !== newInit) { + if (void 0 === init) init = newInit; + else if (\\"function\\" == typeof init) init = [ + init, + newInit + ]; + else init.push(newInit); + } + } + } + if (0 === kind || 1 === kind) { + if (void 0 === init) init = function(instance, init) { + return init; + }; + else if (\\"function\\" != typeof init) { + var ownInitializers = init; + init = function(instance, init) { + var value = init; + for(var i = 0; i < ownInitializers.length; i++)value = ownInitializers[i].call(instance, value); + return value; + }; + } else { + var originalInitializer = init; + init = function(instance, init) { + return originalInitializer.call(instance, init); + }; + } + ret.push(init); + } + if (0 !== kind) { + if (1 === kind) { + desc.get = value.get; + desc.set = value.set; + } else if (2 === kind) desc.value = value; + else if (3 === kind) desc.get = value; + else if (4 === kind) desc.set = value; + if (isPrivate) { + if (1 === kind) { + ret.push(function(instance, args) { + return value.get.call(instance, args); + }); + ret.push(function(instance, args) { + return value.set.call(instance, args); + }); + } else if (2 === kind) ret.push(value); + else ret.push(function(instance, args) { + return value.call(instance, args); + }); + } else Object.defineProperty(base, name, desc); + } + } + function applyMemberDecs(Class, decInfos, metadata) { + var ret = []; + var protoInitializers; + var staticInitializers; + var existingProtoNonFields = new Map(); + var existingStaticNonFields = new Map(); + for(var i = 0; i < decInfos.length; i++){ + var decInfo = decInfos[i]; + if (!!Array.isArray(decInfo)) { + var kind = decInfo[1]; + var name = decInfo[2]; + var isPrivate = decInfo.length > 3; + var isStatic = kind >= 5; + var base; + var initializers; + if (isStatic) { + base = Class; + kind -= 5; + staticInitializers = staticInitializers || []; + initializers = staticInitializers; + } else { + base = Class.prototype; + protoInitializers = protoInitializers || []; + initializers = protoInitializers; + } + if (0 !== kind && !isPrivate) { + var existingNonFields = isStatic ? existingStaticNonFields : existingProtoNonFields; + var existingKind = existingNonFields.get(name) || 0; + if (true === existingKind || 3 === existingKind && 4 !== kind || 4 === existingKind && 3 !== kind) throw new Error(\\"Attempted to decorate a public method/accessor that has the same name as a previously decorated public method/accessor. This is not currently supported by the decorators plugin. Property name was: \\" + name); + if (!existingKind && kind > 2) existingNonFields.set(name, kind); + else existingNonFields.set(name, true); + } + applyMemberDec(ret, base, decInfo, name, kind, isStatic, isPrivate, initializers, metadata); + } + } + pushInitializers(ret, protoInitializers); + pushInitializers(ret, staticInitializers); + return ret; + } + function pushInitializers(ret, initializers) { + if (initializers) ret.push(function(instance) { + for(var i = 0; i < initializers.length; i++)initializers[i].call(instance); + return instance; + }); + } + function applyClassDecs(targetClass, classDecs, metadata) { + if (classDecs.length > 0) { + var initializers = []; + var newClass = targetClass; + var name = targetClass.name; + for(var i = classDecs.length - 1; i >= 0; i--){ + var decoratorFinishedRef = { + v: false + }; + try { + var nextNewClass = classDecs[i](newClass, { + kind: \\"class\\", + name: name, + addInitializer: createAddInitializerMethod(initializers, decoratorFinishedRef), + metadata + }); + } finally{ + decoratorFinishedRef.v = true; + } + if (void 0 !== nextNewClass) { + assertValidReturnValue(10, nextNewClass); + newClass = nextNewClass; + } + } + return [ + defineMetadata(newClass, metadata), + function() { + for(var i = 0; i < initializers.length; i++)initializers[i].call(newClass); + } + ]; + } + } + function defineMetadata(Class, metadata) { + return Object.defineProperty(Class, Symbol.metadata || Symbol.for(\\"Symbol.metadata\\"), { + configurable: true, + enumerable: true, + value: metadata + }); + } + return function(targetClass, memberDecs, classDecs, parentClass) { + if (void 0 !== parentClass) var parentMetadata = parentClass[Symbol.metadata || Symbol.for(\\"Symbol.metadata\\")]; + var metadata = Object.create(void 0 === parentMetadata ? null : parentMetadata); + var e = applyMemberDecs(targetClass, memberDecs, metadata); + if (!classDecs.length) defineMetadata(targetClass, metadata); + return { + e: e, + get c () { + return applyClassDecs(targetClass, classDecs, metadata); + } + }; + }; +} +function _apply_decs_2203_r(targetClass, memberDecs, classDecs, parentClass) { + return (_apply_decs_2203_r = applyDecs2203RFactory())(targetClass, memberDecs, classDecs, parentClass); +} +var _dec, _initClass; +function enhancer(name) { + return function(target) { + target.prototype.name = name; + }; +} +let _Person; +_dec = enhancer('rslib'); +class Person { + static{ + ({ c: [_Person, _initClass] } = _apply_decs_2203_r(this, [], [ + _dec + ])); + } + constructor(){ + this.version = '1.0.0'; + } + version; + static{ + _initClass(); + } +} +" +`; diff --git a/e2e/cases/decorators/default/package.json b/e2e/cases/decorators/default/package.json new file mode 100644 index 000000000..7772a0694 --- /dev/null +++ b/e2e/cases/decorators/default/package.json @@ -0,0 +1,6 @@ +{ + "name": "decorators-default-test", + "version": "1.0.0", + "private": true, + "type": "module" +} diff --git a/e2e/cases/decorators/default/rslib.config.ts b/e2e/cases/decorators/default/rslib.config.ts new file mode 100644 index 000000000..ed308bd88 --- /dev/null +++ b/e2e/cases/decorators/default/rslib.config.ts @@ -0,0 +1,12 @@ +import { generateBundleEsmConfig } from '@e2e/helper'; +import { defineConfig } from '@rslib/core'; + +export default defineConfig({ + lib: [generateBundleEsmConfig()], + source: { + entry: { + index: '../__fixtures__/src/index.ts', + }, + tsconfigPath: '../__fixtures__/tsconfig.json', + }, +}); diff --git a/e2e/cases/decorators/index.test.ts b/e2e/cases/decorators/index.test.ts new file mode 100644 index 000000000..d4c1ff64c --- /dev/null +++ b/e2e/cases/decorators/index.test.ts @@ -0,0 +1,21 @@ +import { join } from 'node:path'; +import { buildAndGetResults } from '@e2e/helper'; +import { expect, test } from 'vitest'; + +test('decorators default to 2022-03', async () => { + const fixturePath = join(__dirname, 'default'); + const { entries } = await buildAndGetResults(fixturePath); + + // use 2022-03 decorator by default + expect(entries.esm).toMatchSnapshot(); +}); + +test('decorators with experimentalDecorators in tsconfig', async () => { + const fixturePath = join(__dirname, 'tsconfig'); + const { entries } = await buildAndGetResults(fixturePath); + + // use legacy decorator when experimentalDecorators in tsconfig + expect(entries.esm0).toMatchSnapshot(); + // use 2022-03 if config source.decorators + expect(entries.esm1).toMatchSnapshot(); +}); diff --git a/e2e/cases/decorators/tsconfig/package.json b/e2e/cases/decorators/tsconfig/package.json new file mode 100644 index 000000000..6f96bf104 --- /dev/null +++ b/e2e/cases/decorators/tsconfig/package.json @@ -0,0 +1,6 @@ +{ + "name": "decorators-tsconfig-test", + "version": "1.0.0", + "private": true, + "type": "module" +} diff --git a/e2e/cases/decorators/tsconfig/rslib.config.ts b/e2e/cases/decorators/tsconfig/rslib.config.ts new file mode 100644 index 000000000..989858be6 --- /dev/null +++ b/e2e/cases/decorators/tsconfig/rslib.config.ts @@ -0,0 +1,32 @@ +import { generateBundleEsmConfig } from '@e2e/helper'; +import { defineConfig } from '@rslib/core'; + +export default defineConfig({ + lib: [ + generateBundleEsmConfig({ + output: { + distPath: { + root: './dist/esm/legacy', + }, + }, + }), + generateBundleEsmConfig({ + output: { + distPath: { + root: './dist/esm/stage3', + }, + }, + source: { + decorators: { + version: '2022-03', + }, + }, + }), + ], + source: { + entry: { + index: '../__fixtures__/src/index.ts', + }, + tsconfigPath: '../__fixtures__/tsconfig.decorators.json', + }, +}); diff --git a/e2e/cases/tsconfig/index.test.ts b/e2e/cases/tsconfig/index.test.ts new file mode 100644 index 000000000..c3a75d355 --- /dev/null +++ b/e2e/cases/tsconfig/index.test.ts @@ -0,0 +1,11 @@ +import { buildAndGetResults } from '@e2e/helper'; +import { expect, test } from 'vitest'; + +test('tsconfig path', async () => { + const fixturePath = __dirname; + const { isSuccess, entries } = await buildAndGetResults(fixturePath); + + expect(isSuccess).toBe(true); + expect(entries.esm).toContain(`const foo = 'foo'`); + expect(entries.cjs).toContain(`const foo = 'foo'`); +}); diff --git a/e2e/cases/tsconfig/package.json b/e2e/cases/tsconfig/package.json new file mode 100644 index 000000000..7bd946b1e --- /dev/null +++ b/e2e/cases/tsconfig/package.json @@ -0,0 +1,6 @@ +{ + "name": "tsconfig-test", + "version": "1.0.0", + "private": true, + "type": "module" +} diff --git a/e2e/cases/tsconfig/rslib.config.ts b/e2e/cases/tsconfig/rslib.config.ts new file mode 100644 index 000000000..fbc37d007 --- /dev/null +++ b/e2e/cases/tsconfig/rslib.config.ts @@ -0,0 +1,12 @@ +import { generateBundleCjsConfig, generateBundleEsmConfig } from '@e2e/helper'; +import { defineConfig } from '@rslib/core'; + +export default defineConfig({ + lib: [generateBundleEsmConfig(), generateBundleCjsConfig()], + source: { + entry: { + index: './src/index.ts', + }, + tsconfigPath: './tsconfig.custom.json', + }, +}); diff --git a/e2e/cases/tsconfig/src/foo.ts b/e2e/cases/tsconfig/src/foo.ts new file mode 100644 index 000000000..40be958d9 --- /dev/null +++ b/e2e/cases/tsconfig/src/foo.ts @@ -0,0 +1,2 @@ +export const foo = 'foo'; +export type A = string; diff --git a/e2e/cases/tsconfig/src/index.ts b/e2e/cases/tsconfig/src/index.ts new file mode 100644 index 000000000..fd9954c96 --- /dev/null +++ b/e2e/cases/tsconfig/src/index.ts @@ -0,0 +1,5 @@ +import { foo } from '$/foo'; + +console.info(foo); + +export type { A } from './foo'; diff --git a/e2e/cases/tsconfig/tsconfig.custom.json b/e2e/cases/tsconfig/tsconfig.custom.json new file mode 100644 index 000000000..f826a3dd5 --- /dev/null +++ b/e2e/cases/tsconfig/tsconfig.custom.json @@ -0,0 +1,10 @@ +{ + "extends": "@rslib/tsconfig/base", + "compilerOptions": { + "baseUrl": "./", + "paths": { + "$/*": ["src/*"] + } + }, + "include": ["src"] +} diff --git a/packages/core/package.json b/packages/core/package.json index 155b6e0e6..c96c11685 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -51,6 +51,7 @@ "prebundle": "1.2.2", "rslib": "npm:@rslib/core@0.0.5", "rslog": "^1.2.3", + "tsconfck": "3.1.3", "typescript": "^5.5.4" }, "peerDependencies": { diff --git a/packages/core/prebundle.config.mjs b/packages/core/prebundle.config.mjs index 90f1f6f91..cf004823e 100644 --- a/packages/core/prebundle.config.mjs +++ b/packages/core/prebundle.config.mjs @@ -14,6 +14,16 @@ export default { dependencies: [ 'commander', 'fast-glob', + { + name: 'rslog', + afterBundle(task) { + // use the cjs bundle of rslog + fs.copyFileSync( + join(task.depPath, 'dist/index.cjs'), + join(task.distPath, 'index.js'), + ); + }, + }, { name: 'picocolors', beforeBundle({ depPath }) { diff --git a/packages/core/rslib.config.ts b/packages/core/rslib.config.ts index 3d7166382..5d5658483 100644 --- a/packages/core/rslib.config.ts +++ b/packages/core/rslib.config.ts @@ -22,6 +22,7 @@ export default defineConfig({ picocolors: '../compiled/picocolors/index.js', 'fast-glob': '../compiled/fast-glob/index.js', commander: '../compiled/commander/index.js', + rslog: '../compiled/rslog/index.js', }, }, }); diff --git a/packages/core/src/config.ts b/packages/core/src/config.ts index 37ffd862a..1091768a4 100644 --- a/packages/core/src/config.ts +++ b/packages/core/src/config.ts @@ -40,6 +40,7 @@ import { } from './utils/helper'; import { logger } from './utils/logger'; import { transformSyntaxToBrowserslist } from './utils/syntax'; +import { loadTsconfig } from './utils/tsconfig'; /** * This function helps you to autocomplete configuration types. @@ -360,6 +361,25 @@ export function composeBannerFooterConfig( }; } +export function composeDecoratorsConfig( + compilerOptions?: Record, + version?: NonNullable< + NonNullable['decorators'] + >['version'], +): RsbuildConfig { + if (version || !compilerOptions?.experimentalDecorators) { + return {}; + } + + return { + source: { + decorators: { + version: 'legacy', + }, + }, + }; +} + export async function createConstantRsbuildConfig(): Promise { return defineRsbuildConfig({ mode: 'production', @@ -790,6 +810,10 @@ const composeExternalHelpersConfig = ( async function composeLibRsbuildConfig(config: LibConfig, configPath: string) { const rootPath = dirname(configPath); const pkgJson = readPackageJson(rootPath); + const { compilerOptions } = await loadTsconfig( + rootPath, + config.source?.tsconfigPath, + ); const { format, @@ -837,6 +861,10 @@ async function composeLibRsbuildConfig(config: LibConfig, configPath: string) { ); const minifyConfig = composeMinifyConfig(config.output?.minify); const bannerFooterConfig = composeBannerFooterConfig(banner, footer); + const decoratorsConfig = composeDecoratorsConfig( + compilerOptions, + config.source?.decorators?.version, + ); return mergeRsbuildConfig( formatConfig, @@ -853,6 +881,7 @@ async function composeLibRsbuildConfig(config: LibConfig, configPath: string) { minifyConfig, dtsConfig, bannerFooterConfig, + decoratorsConfig, ); } diff --git a/packages/core/src/utils/tsconfig.ts b/packages/core/src/utils/tsconfig.ts new file mode 100644 index 000000000..552a12391 --- /dev/null +++ b/packages/core/src/utils/tsconfig.ts @@ -0,0 +1,21 @@ +import { basename, join } from 'node:path'; +import { find, parse } from 'tsconfck'; + +export async function loadTsconfig( + root: string, + tsconfigPath = 'tsconfig.json', +): Promise<{ + compilerOptions?: Record; +}> { + const tsconfigFileName = await find(join(root, tsconfigPath), { + root, + configName: basename(tsconfigPath), + }); + + if (tsconfigFileName) { + const { tsconfig } = await parse(tsconfigFileName); + return tsconfig; + } + + return {}; +} diff --git a/packages/core/tests/extension.test.ts b/packages/core/tests/extension.test.ts index 7e5b05f39..dcad0ce77 100644 --- a/packages/core/tests/extension.test.ts +++ b/packages/core/tests/extension.test.ts @@ -1,11 +1,18 @@ import { describe, expect, it, vi } from 'vitest'; +import type { Format, PkgJson } from '../src/types'; import { getDefaultExtension } from '../src/utils/extension'; +type Options = { + format: Format; + pkgJson?: PkgJson; + autoExtension: boolean; +}; + vi.mock('rslog'); describe('should get extension correctly', () => { it('autoExtension is false', () => { - const options = { + const options: Options = { format: 'cjs', pkgJson: {}, autoExtension: false, @@ -20,7 +27,7 @@ describe('should get extension correctly', () => { }); it('package.json is broken', () => { - const options = { + const options: Options = { format: 'cjs', autoExtension: true, }; @@ -34,7 +41,7 @@ describe('should get extension correctly', () => { }); it('format is cjs and type is module in package.json', () => { - const options = { + const options: Options = { format: 'cjs', pkgJson: { type: 'module', @@ -52,7 +59,7 @@ describe('should get extension correctly', () => { }); it('format is cjs and type is commonjs in package.json', () => { - const options = { + const options: Options = { format: 'cjs', pkgJson: { type: 'commonjs', @@ -70,7 +77,7 @@ describe('should get extension correctly', () => { }); it('format is esm and type is commonjs in package.json', () => { - const options = { + const options: Options = { format: 'esm', pkgJson: { type: 'commonjs', @@ -88,7 +95,7 @@ describe('should get extension correctly', () => { }); it('format is esm and type is module in package.json', () => { - const options = { + const options: Options = { format: 'esm', pkgJson: { type: 'module', diff --git a/packages/core/tests/fixtures/tsconfig/exist/tsconfig.custom.json b/packages/core/tests/fixtures/tsconfig/exist/tsconfig.custom.json new file mode 100644 index 000000000..34d0787d0 --- /dev/null +++ b/packages/core/tests/fixtures/tsconfig/exist/tsconfig.custom.json @@ -0,0 +1,5 @@ +{ + "compilerOptions": { + "rootDir": "custom" + } +} diff --git a/packages/core/tests/fixtures/tsconfig/exist/tsconfig.json b/packages/core/tests/fixtures/tsconfig/exist/tsconfig.json new file mode 100644 index 000000000..397165af4 --- /dev/null +++ b/packages/core/tests/fixtures/tsconfig/exist/tsconfig.json @@ -0,0 +1,5 @@ +{ + "compilerOptions": { + "rootDir": "./" + } +} diff --git a/packages/core/tests/fixtures/tsconfig/no-exist/index.js b/packages/core/tests/fixtures/tsconfig/no-exist/index.js new file mode 100644 index 000000000..3329a7d97 --- /dev/null +++ b/packages/core/tests/fixtures/tsconfig/no-exist/index.js @@ -0,0 +1 @@ +export const foo = 'foo'; diff --git a/packages/core/tests/tsconfig.test.ts b/packages/core/tests/tsconfig.test.ts new file mode 100644 index 000000000..ccea7091e --- /dev/null +++ b/packages/core/tests/tsconfig.test.ts @@ -0,0 +1,33 @@ +import { join } from 'node:path'; +import { describe, expect, it } from 'vitest'; +import { loadTsconfig } from '../src/utils/tsconfig'; + +describe('loadTsconfig', () => { + it('should return tsconfig when a valid tsconfig file is found', async () => { + const fixtureDir = join(__dirname, 'fixtures/tsconfig/exist'); + const result1 = await loadTsconfig(fixtureDir); + const result2 = await loadTsconfig(fixtureDir, './tsconfig.custom.json'); + + expect(result1).toMatchInlineSnapshot(` + { + "compilerOptions": { + "rootDir": "./", + }, + } + `); + expect(result2).toMatchInlineSnapshot(` + { + "compilerOptions": { + "rootDir": "custom", + }, + } + `); + }); + + it('should return an empty object when no tsconfig file is found', async () => { + const fixtureDir = join(__dirname, 'fixtures/tsconfig/no-exist'); + const result = await loadTsconfig(fixtureDir); + + expect(result).toEqual({}); + }); +}); diff --git a/packages/core/tsconfig.json b/packages/core/tsconfig.json index 7be964e5e..d00b7b5b2 100644 --- a/packages/core/tsconfig.json +++ b/packages/core/tsconfig.json @@ -12,7 +12,9 @@ "moduleResolution": "Bundler", "paths": { "commander": ["./compiled/commander"], - "picocolors": ["./compiled/picocolors"] + "picocolors": ["./compiled/picocolors"], + "fast-glob": ["./compiled/fast-glob"], + "rslog": ["./compiled/rslog"] } }, "include": ["src"], diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 932edfc51..8a2488440 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -159,6 +159,10 @@ importers: e2e/cases/copy: {} + e2e/cases/decorators/default: {} + + e2e/cases/decorators/tsconfig: {} + e2e/cases/define: {} e2e/cases/dts/bundle-false/abort-on-error: {} @@ -261,6 +265,8 @@ importers: specifier: ^4.17.7 version: 4.17.7 + e2e/cases/tsconfig: {} + e2e/scripts: {} examples/express-plugin: @@ -335,6 +341,9 @@ importers: rslog: specifier: ^1.2.3 version: 1.2.3 + tsconfck: + specifier: 3.1.3 + version: 3.1.3(typescript@5.5.4) typescript: specifier: ^5.5.4 version: 5.5.4 @@ -3896,6 +3905,16 @@ packages: trough@2.2.0: resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} + tsconfck@3.1.3: + resolution: {integrity: sha512-ulNZP1SVpRDesxeMLON/LtWM8HIgAJEIVpVVhBM6gsmvQ8+Rh+ZG7FWGvHh7Ah3pRABwVJWklWCr/BTZSv0xnQ==} + engines: {node: ^18 || >=20} + hasBin: true + peerDependencies: + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + tsconfig-paths@4.2.0: resolution: {integrity: sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==} engines: {node: '>=6'} @@ -8153,6 +8172,10 @@ snapshots: trough@2.2.0: {} + tsconfck@3.1.3(typescript@5.5.4): + optionalDependencies: + typescript: 5.5.4 + tsconfig-paths@4.2.0: dependencies: json5: 2.2.3 diff --git a/scripts/dictionary.txt b/scripts/dictionary.txt index fac4da9ea..e7c7b2384 100644 --- a/scripts/dictionary.txt +++ b/scripts/dictionary.txt @@ -118,6 +118,7 @@ templating transpiling treeshaking tsbuildinfo +tsconfck tsdoc tsup unocss