diff --git a/.eslintignore b/.eslintignore index c0a1165bb2d41..d168d82d7a560 100644 --- a/.eslintignore +++ b/.eslintignore @@ -26,6 +26,7 @@ packages/next-env/**/*.d.ts packages/create-next-app/templates/** test/integration/eslint/** test/development/basic/legacy-decorators/**/* +test/production/emit-decorator-metadata/**/*.js test-timings.json packages/next-swc/crates/** bench/nested-deps/pages/** diff --git a/packages/next/build/swc/options.js b/packages/next/build/swc/options.js index ce9f7634fc175..71504105236b4 100644 --- a/packages/next/build/swc/options.js +++ b/packages/next/build/swc/options.js @@ -21,6 +21,9 @@ export function getBaseSWCOptions({ const enableDecorators = Boolean( jsConfig?.compilerOptions?.experimentalDecorators ) + const emitDecoratorMetadata = Boolean( + jsConfig?.compilerOptions?.emitDecoratorMetadata + ) return { jsc: { ...(resolvedBaseUrl && paths @@ -47,6 +50,7 @@ export function getBaseSWCOptions({ } : {}), legacyDecorator: enableDecorators, + decoratorMetadata: emitDecoratorMetadata, react: { importSource: jsConfig?.compilerOptions?.jsxImportSource || 'react', runtime: 'automatic', diff --git a/test/production/emit-decorator-metadata/app/jsconfig.json b/test/production/emit-decorator-metadata/app/jsconfig.json new file mode 100644 index 0000000000000..6dacb8cc2c548 --- /dev/null +++ b/test/production/emit-decorator-metadata/app/jsconfig.json @@ -0,0 +1,6 @@ +{ + "compilerOptions": { + "experimentalDecorators": true, + "emitDecoratorMetadata": true + } +} diff --git a/test/production/emit-decorator-metadata/app/pages/api/[[...params]].js b/test/production/emit-decorator-metadata/app/pages/api/[[...params]].js new file mode 100644 index 0000000000000..53edd542b66c7 --- /dev/null +++ b/test/production/emit-decorator-metadata/app/pages/api/[[...params]].js @@ -0,0 +1,14 @@ +import { createHandler, Get, Param } from '@storyofams/next-api-decorators' + +class HelloHandler { + @Get('/:myParam') + // This fails due to library looking for Reflect.getMetadata("design:paramtypes", ...). + // Design:paramtypes is never emitted due to missing SWC flag. + async get(@Param('myParam') myParam) { + return { + myParam, + } + } +} + +export default createHandler(HelloHandler) diff --git a/test/production/emit-decorator-metadata/app/pages/index.js b/test/production/emit-decorator-metadata/app/pages/index.js new file mode 100644 index 0000000000000..80537366522b7 --- /dev/null +++ b/test/production/emit-decorator-metadata/app/pages/index.js @@ -0,0 +1,26 @@ +import React from 'react' +import 'reflect-metadata' +import { container, singleton } from 'tsyringe' + +@singleton() +class HelloService { + getHello() { + return 'Hello, world!' + } +} + +const helloService = container.resolve(HelloService) + +export default function Home() { + const message = helloService.getHello() + + return

{message}

+} + +export function getServerSideProps() { + return { + props: { + now: Date.now(), + }, + } +} diff --git a/test/production/emit-decorator-metadata/index.test.ts b/test/production/emit-decorator-metadata/index.test.ts new file mode 100644 index 0000000000000..68476ecf08663 --- /dev/null +++ b/test/production/emit-decorator-metadata/index.test.ts @@ -0,0 +1,47 @@ +import { join } from 'path' +import webdriver from 'next-webdriver' +import { createNext, FileRef } from 'e2e-utils' +import { NextInstance } from 'test/lib/next-modes/base' +import { BrowserInterface } from 'test/lib/browsers/base' +import { fetchViaHTTP } from 'next-test-utils' + +describe('emitDecoratorMetadata SWC option', () => { + let next: NextInstance + + beforeAll(async () => { + next = await createNext({ + files: { + 'jsconfig.json': new FileRef(join(__dirname, 'app/jsconfig.json')), + pages: new FileRef(join(__dirname, 'app/pages')), + }, + dependencies: { + '@storyofams/next-api-decorators': '1.6.0', + 'reflect-metadata': '0.1.13', + 'path-to-regexp': '6.2.0', + tsyringe: '4.6.0', + }, + }) + }) + + afterAll(() => next.destroy()) + + it('should compile with emitDecoratorMetadata enabled', async () => { + let browser: BrowserInterface + try { + browser = await webdriver(next.appPort, '/') + const message = await browser.elementByCss('#message').text() + + expect(message).toBe('Hello, world!') + } finally { + if (browser) { + await browser.close() + } + } + }) + + it('should compile with emitDecoratorMetadata enabled for API', async () => { + const res = await fetchViaHTTP(next.url, '/api/something') + expect(res.status).toBe(200) + expect(await res.json()).toEqual({ myParam: 'something' }) + }) +})