From 9fbd12380056a23ed35dbaa68456f5be86d9f0de Mon Sep 17 00:00:00 2001 From: Timofei Iatsenko Date: Tue, 14 Mar 2023 10:42:50 +0100 Subject: [PATCH] feat(extractor): support TS experimental decorators --- packages/cli/package.json | 11 ++--- packages/cli/src/api/catalog.test.ts | 49 +++++++++++++------ packages/cli/src/api/extractors/babel.ts | 24 ++++----- .../collect-syntax-flow/lingui.config.js | 10 ---- .../collect-typescript-jsx/jsx-syntax.jsx | 22 +++++---- .../collect-typescript-jsx/lingui.config.js | 3 -- .../tsx-experimental-decorators.tsx | 15 ++++++ .../collect-typescript-jsx/tsx-syntax.tsx | 24 ++++----- packages/conf/__typetests__/index.test-d.tsx | 2 +- .../conf/src/__snapshots__/index.test.ts.snap | 2 +- packages/conf/src/makeConfig.ts | 4 +- packages/conf/src/types.ts | 7 ++- website/docs/ref/conf.md | 7 +-- yarn.lock | 33 ++++++------- 14 files changed, 121 insertions(+), 92 deletions(-) delete mode 100644 packages/cli/src/api/fixtures/collect-syntax-flow/lingui.config.js delete mode 100644 packages/cli/src/api/fixtures/collect-typescript-jsx/lingui.config.js create mode 100644 packages/cli/src/api/fixtures/collect-typescript-jsx/tsx-experimental-decorators.tsx diff --git a/packages/cli/package.json b/packages/cli/package.json index f9e1869f9..6f1f46620 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -45,12 +45,11 @@ "build/" ], "dependencies": { - "@babel/core": "^7.20.12", - "@babel/generator": "^7.20.14", - "@babel/parser": "^7.20.15", - "@babel/plugin-syntax-jsx": "^7.18.6", - "@babel/runtime": "^7.20.13", - "@babel/types": "^7.20.7", + "@babel/core": "^7.21.0", + "@babel/generator": "^7.21.1", + "@babel/parser": "^7.21.2", + "@babel/runtime": "^7.21.0", + "@babel/types": "^7.21.2", "@lingui/babel-plugin-extract-messages": "4.0.0-next.1", "@lingui/conf": "4.0.0-next.1", "@lingui/core": "4.0.0-next.1", diff --git a/packages/cli/src/api/catalog.test.ts b/packages/cli/src/api/catalog.test.ts index 76c8aad25..7a38ca493 100644 --- a/packages/cli/src/api/catalog.test.ts +++ b/packages/cli/src/api/catalog.test.ts @@ -16,6 +16,7 @@ import { makeCatalog, } from "../tests" import { AllCatalogsType } from "./types" +import { extractFromFiles } from "./catalog/extractFromFiles" export const fixture = (...dirs: string[]) => path.resolve(__dirname, path.join("fixtures", ...dirs)) + @@ -165,26 +166,47 @@ describe("Catalog", () => { describe("collect", () => { it("should support JSX and Typescript", async () => { - const catalog = new Catalog( - { - name: "messages", - path: "locales/{locale}", - include: [fixture("collect-typescript-jsx/")], - exclude: [], - }, + const messages = await extractFromFiles( + [ + fixture("collect-typescript-jsx/jsx-syntax.jsx"), + fixture("collect-typescript-jsx/tsx-syntax.tsx"), + fixture("collect-typescript-jsx/macro.tsx"), + ], mockConfig() ) - const messages = await catalog.collect() - expect(messages).toBeTruthy() expect(messages).toMatchSnapshot() }) - it("should support Flow syntax if enabled", async () => { - process.env.LINGUI_CONFIG = path.join( - __dirname, - "fixtures/collect-syntax-flow/lingui.config.js" + it("should support experimental typescript decorators under a flag", async () => { + const messages = await extractFromFiles( + [fixture("collect-typescript-jsx/tsx-experimental-decorators.tsx")], + mockConfig({ + extractorParserOptions: { + tsExperimentalDecorators: true, + }, + }) ) + + expect(messages).toBeTruthy() + expect(messages).toMatchInlineSnapshot(` + { + xDAtGP: { + context: undefined, + extractedComments: [], + message: Message, + origin: [ + [ + collect-typescript-jsx/tsx-experimental-decorators.tsx, + 15, + ], + ], + }, + } + `) + }) + + it("should support Flow syntax if enabled", async () => { const catalog = new Catalog( { name: "messages", @@ -200,7 +222,6 @@ describe("Catalog", () => { ) const messages = await catalog.collect() - expect(messages).toBeTruthy() expect(messages).toMatchSnapshot() }) it("should extract messages from source files", async () => { diff --git a/packages/cli/src/api/extractors/babel.ts b/packages/cli/src/api/extractors/babel.ts index 627166cb8..6bfe86bb4 100644 --- a/packages/cli/src/api/extractors/babel.ts +++ b/packages/cli/src/api/extractors/babel.ts @@ -30,22 +30,24 @@ const extractor: ExtractorType = { async extract(filename, code, onMessageExtracted, ctx) { const parserOptions = ctx.linguiConfig.extractorParserOptions - const parserPlugins: ParserPlugin[] = [ - // https://babeljs.io/docs/en/babel-parser#latest-ecmascript-features - [ - "decorators", - { - decoratorsBeforeExport: parserOptions?.decoratorsBeforeExport || true, - }, - ], - ] + // https://babeljs.io/docs/en/babel-parser#latest-ecmascript-features + const parserPlugins: ParserPlugin[] = [] if ( [/\.ts$/, /\.mts$/, /\.cts$/, /\.tsx$/].some((r) => filename.match(r)) ) { parserPlugins.push("typescript") - } else if (parserOptions?.flow) { - parserPlugins.push("flow") + if (parserOptions.tsExperimentalDecorators) { + parserPlugins.push("decorators-legacy") + } else { + parserPlugins.push("decorators") + } + } else { + parserPlugins.push("decorators") + + if (parserOptions?.flow) { + parserPlugins.push("flow") + } } if ([/\.jsx$/, /\.tsx$/].some((r) => filename.match(r))) { diff --git a/packages/cli/src/api/fixtures/collect-syntax-flow/lingui.config.js b/packages/cli/src/api/fixtures/collect-syntax-flow/lingui.config.js deleted file mode 100644 index 265afcf7f..000000000 --- a/packages/cli/src/api/fixtures/collect-syntax-flow/lingui.config.js +++ /dev/null @@ -1,10 +0,0 @@ -/** - * - * @type {import('@lingui/conf').LinguiConfig} - */ -module.exports = { - locales: ["en", "cs"], - extractorParserOptions: { - flow: true - } -} diff --git a/packages/cli/src/api/fixtures/collect-typescript-jsx/jsx-syntax.jsx b/packages/cli/src/api/fixtures/collect-typescript-jsx/jsx-syntax.jsx index a169d7425..fe2bbffc0 100644 --- a/packages/cli/src/api/fixtures/collect-typescript-jsx/jsx-syntax.jsx +++ b/packages/cli/src/api/fixtures/collect-typescript-jsx/jsx-syntax.jsx @@ -5,34 +5,38 @@ const jsx =
Hello!
@Decorator() export class TestDecorator { @Decorator() - prop; + prop @Decorator() - method() {}; + method() {} } +export +@Decorator() +class TestDecoratorAfterExport {} + class A { // classProperties - b = 1; + b = 1 // classPrivateProperties - #b = 1; + #b = 1 } // dynamicImport -import('./guy').then(a) +import("./guy").then(a) // exportNamespaceFrom export * as ns from "mod" // nullishCoalescingOperator -const a = a ?? b; +const a = a ?? b // objectRestSpread -const b = { b, ...c }; +const b = { b, ...c } // optionalChaining -const c = a?.b; +const c = a?.b // topLevelAwait -await promise; +await promise diff --git a/packages/cli/src/api/fixtures/collect-typescript-jsx/lingui.config.js b/packages/cli/src/api/fixtures/collect-typescript-jsx/lingui.config.js deleted file mode 100644 index 56ad10839..000000000 --- a/packages/cli/src/api/fixtures/collect-typescript-jsx/lingui.config.js +++ /dev/null @@ -1,3 +0,0 @@ -module.exports = { - locales: ["en", "cs"] -} diff --git a/packages/cli/src/api/fixtures/collect-typescript-jsx/tsx-experimental-decorators.tsx b/packages/cli/src/api/fixtures/collect-typescript-jsx/tsx-experimental-decorators.tsx new file mode 100644 index 000000000..7e2ca4de5 --- /dev/null +++ b/packages/cli/src/api/fixtures/collect-typescript-jsx/tsx-experimental-decorators.tsx @@ -0,0 +1,15 @@ +import { t } from "@lingui/macro" + +@Decorator() +export class TestDecorator { + // supports typescript legacy decorator on parameters + constructor(@Decorator() param) {} + + @Decorator() + prop + + @Decorator() + method() {} +} + +t`Message` diff --git a/packages/cli/src/api/fixtures/collect-typescript-jsx/tsx-syntax.tsx b/packages/cli/src/api/fixtures/collect-typescript-jsx/tsx-syntax.tsx index 0b7c3ba2d..f5d97078d 100644 --- a/packages/cli/src/api/fixtures/collect-typescript-jsx/tsx-syntax.tsx +++ b/packages/cli/src/api/fixtures/collect-typescript-jsx/tsx-syntax.tsx @@ -2,46 +2,46 @@ const jsx =
Hello!
// Typescript syntax function foo(bar: string): string { - return bar; + return bar } -const test1: string = ""; +const test1: string = "" // check parsing different syntax proposals @Decorator() export class TestDecorator { @Decorator() - prop; + prop @Decorator() - method() {}; + method() {} } // optional chaining -const test = foo?.bar?.baz; +const test = foo?.bar?.baz class A { // classProperties - b = 1; + b = 1 // classPrivateProperties - #b = 1; + #b = 1 } // dynamicImport -import('./guy').then(a) +import("./guy").then(a) // exportNamespaceFrom export * as ns from "mod" // nullishCoalescingOperator -const a = a ?? b; +const a = a ?? b // objectRestSpread -const b = { b, ...c }; +const b = { b, ...c } // optionalChaining -const c = a?.b; +const c = a?.b // topLevelAwait -await promise; +await promise diff --git a/packages/conf/__typetests__/index.test-d.tsx b/packages/conf/__typetests__/index.test-d.tsx index 1728317ee..1f582b46e 100644 --- a/packages/conf/__typetests__/index.test-d.tsx +++ b/packages/conf/__typetests__/index.test-d.tsx @@ -30,7 +30,7 @@ expectAssignable({ }, extractorParserOptions: { flow: false, - decoratorsBeforeExport: false, + tsExperimentalDecorators: false, }, fallbackLocales: {} as FallbackLocales, format: "po", diff --git a/packages/conf/src/__snapshots__/index.test.ts.snap b/packages/conf/src/__snapshots__/index.test.ts.snap index 1eebd8872..bfa7784f3 100644 --- a/packages/conf/src/__snapshots__/index.test.ts.snap +++ b/packages/conf/src/__snapshots__/index.test.ts.snap @@ -35,8 +35,8 @@ exports[`@lingui/conf should return default config 1`] = ` minified: true, }, extractorParserOptions: { - decoratorsBeforeExport: false, flow: false, + tsExperimentalDecorators: false, }, fallbackLocales: { en-gb: en, diff --git a/packages/conf/src/makeConfig.ts b/packages/conf/src/makeConfig.ts index f2fe00fc0..548b33448 100644 --- a/packages/conf/src/makeConfig.ts +++ b/packages/conf/src/makeConfig.ts @@ -53,7 +53,7 @@ export const defaultConfig: LinguiConfig = { }, extractorParserOptions: { flow: false, - decoratorsBeforeExport: false, + tsExperimentalDecorators: false, }, fallbackLocales: {} as FallbackLocales, format: "po", @@ -82,7 +82,7 @@ export const exampleConfig = { ), extractorParserOptions: { flow: false, - decoratorsBeforeExport: false, + tsExperimentalDecorators: false, }, } diff --git a/packages/conf/src/types.ts b/packages/conf/src/types.ts index 456a144b4..9434c8082 100644 --- a/packages/conf/src/types.ts +++ b/packages/conf/src/types.ts @@ -65,9 +65,12 @@ export type LinguiConfig = { compileNamespace?: "es" | "ts" | "cjs" | string extractorParserOptions?: { /** - * default true + * default false + * + * By default, standard decorators (Stage3) are applied for TS files + * Enable this if you want to use TypesScript's experimental decorators. */ - decoratorsBeforeExport?: boolean + tsExperimentalDecorators?: boolean /** * Enable if you use flow. This will apply Flow syntax to js files */ diff --git a/website/docs/ref/conf.md b/website/docs/ref/conf.md index 0b3fd125c..aa899b4ec 100644 --- a/website/docs/ref/conf.md +++ b/website/docs/ref/conf.md @@ -216,10 +216,11 @@ Specify extra options used to parse source files when messages are being extract ```ts "extractorParserOptions": { /** - * default true - * Use for Stage 3 decorators syntax. + * default false + * By default, standard decorators (Stage3) are applied for TS files + * Enable this if you want to use TypesScript's experimental decorators. */ - decoratorsBeforeExport?: boolean + tsExperimentalDecorators?: boolean /** * default false * Enable if you use flow. This will apply Flow syntax to files with .js, cjs, .mjs extension. diff --git a/yarn.lock b/yarn.lock index fbdd1c9cc..786394c51 100644 --- a/yarn.lock +++ b/yarn.lock @@ -86,17 +86,6 @@ __metadata: languageName: node linkType: hard -"@babel/generator@npm:^7.20.14": - version: 7.20.14 - resolution: "@babel/generator@npm:7.20.14" - dependencies: - "@babel/types": ^7.20.7 - "@jridgewell/gen-mapping": ^0.3.2 - jsesc: ^2.5.1 - checksum: 5f6aa2d86af26e76d276923a5c34191124a119b16ee9ccc34aef654a7dec84fbd7d2daed2e6458a6a06bf87f3661deb77c9fea59b8f67faff5c90793c96d76d6 - languageName: node - linkType: hard - "@babel/generator@npm:^7.20.7": version: 7.20.7 resolution: "@babel/generator@npm:7.20.7" @@ -432,7 +421,7 @@ __metadata: languageName: node linkType: hard -"@babel/parser@npm:7.20.15, @babel/parser@npm:^7.20.15": +"@babel/parser@npm:7.20.15": version: 7.20.15 resolution: "@babel/parser@npm:7.20.15" bin: @@ -1486,6 +1475,15 @@ __metadata: languageName: node linkType: hard +"@babel/runtime@npm:^7.21.0": + version: 7.21.0 + resolution: "@babel/runtime@npm:7.21.0" + dependencies: + regenerator-runtime: ^0.13.11 + checksum: 7b33e25bfa9e0e1b9e8828bb61b2d32bdd46b41b07ba7cb43319ad08efc6fda8eb89445193e67d6541814627df0ca59122c0ea795e412b99c5183a0540d338ab + languageName: node + linkType: hard + "@babel/template@npm:^7.18.10, @babel/template@npm:^7.20.7, @babel/template@npm:^7.3.3": version: 7.20.7 resolution: "@babel/template@npm:7.20.7" @@ -2181,12 +2179,11 @@ __metadata: version: 0.0.0-use.local resolution: "@lingui/cli@workspace:packages/cli" dependencies: - "@babel/core": ^7.20.12 - "@babel/generator": ^7.20.14 - "@babel/parser": ^7.20.15 - "@babel/plugin-syntax-jsx": ^7.18.6 - "@babel/runtime": ^7.20.13 - "@babel/types": ^7.20.7 + "@babel/core": ^7.21.0 + "@babel/generator": ^7.21.1 + "@babel/parser": ^7.21.2 + "@babel/runtime": ^7.21.0 + "@babel/types": ^7.21.2 "@lingui/babel-plugin-extract-messages": 4.0.0-next.1 "@lingui/conf": 4.0.0-next.1 "@lingui/core": 4.0.0-next.1