From e0c7794208dbac6d84ad6723741f896527630f34 Mon Sep 17 00:00:00 2001 From: Sanna Jammeh <50969683+sannajammeh@users.noreply.github.com> Date: Sun, 6 Feb 2022 17:48:09 +0100 Subject: [PATCH] Add decoratorMetadata flag if enabled by tsconfig (#32914) fixes #32913 Adds support for decorator metadata in SWC when enabled through ts/jsconfig. ## Bug - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Errors have helpful link attached, see `contributing.md` ## Feature - [x] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [x] Related issues linked using `fixes #number` - [x] Integration tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have helpful link attached, see `contributing.md` ## Documentation / Examples - [ ] Make sure the linting passes by running `yarn lint` Co-authored-by: JJ Kasper <22380829+ijjk@users.noreply.github.com> --- .eslintignore | 1 + packages/next/build/swc/options.js | 4 ++ .../emit-decorator-metadata/app/jsconfig.json | 6 +++ .../app/pages/api/[[...params]].js | 14 ++++++ .../app/pages/index.js | 26 ++++++++++ .../emit-decorator-metadata/index.test.ts | 47 +++++++++++++++++++ 6 files changed, 98 insertions(+) create mode 100644 test/production/emit-decorator-metadata/app/jsconfig.json create mode 100644 test/production/emit-decorator-metadata/app/pages/api/[[...params]].js create mode 100644 test/production/emit-decorator-metadata/app/pages/index.js create mode 100644 test/production/emit-decorator-metadata/index.test.ts diff --git a/.eslintignore b/.eslintignore index c0a1165bb2d41ff..d168d82d7a560c6 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 ce9f7634fc17554..71504105236b499 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 000000000000000..6dacb8cc2c548f0 --- /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 000000000000000..53edd542b66c78a --- /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 000000000000000..80537366522b71e --- /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 000000000000000..68476ecf0866306 --- /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' }) + }) +})