From fdc5e9adb885edf36696817f0358a27c8de7e3fe Mon Sep 17 00:00:00 2001 From: Christoffer Jahren Date: Fri, 31 Mar 2023 10:54:11 +0200 Subject: [PATCH] feat: add new package with vue extractor --- .gitignore | 1 + jest.config.js | 2 +- lerna.json | 2 +- packages/extractor-vue/README.md | 31 +++++ packages/extractor-vue/package.json | 41 +++++++ .../__snapshots__/extractor.test.ts.snap | 44 +++++++ .../src/__test__/example-script-setup.vue | 15 +++ .../src/__test__/example-script.vue | 28 +++++ .../src/__test__/extractor.test.ts | 63 ++++++++++ packages/extractor-vue/src/index.ts | 1 + packages/extractor-vue/src/vue-extractor.ts | 28 +++++ tsconfig.json | 7 +- yarn.lock | 113 +++++++++++++++++- 13 files changed, 369 insertions(+), 7 deletions(-) create mode 100644 packages/extractor-vue/README.md create mode 100644 packages/extractor-vue/package.json create mode 100644 packages/extractor-vue/src/__test__/__snapshots__/extractor.test.ts.snap create mode 100644 packages/extractor-vue/src/__test__/example-script-setup.vue create mode 100644 packages/extractor-vue/src/__test__/example-script.vue create mode 100644 packages/extractor-vue/src/__test__/extractor.test.ts create mode 100644 packages/extractor-vue/src/index.ts create mode 100644 packages/extractor-vue/src/vue-extractor.ts diff --git a/.gitignore b/.gitignore index 788dabd71..46c11229c 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ node_modules/ .yalc/ yalc.lock dist +build npm-debug.log yarn-error.log diff --git a/jest.config.js b/jest.config.js index 9febee3a7..4299de529 100644 --- a/jest.config.js +++ b/jest.config.js @@ -32,7 +32,6 @@ module.exports = { ".*.js.snap$", ], coverageReporters: ["lcov", "text"], - globalSetup: "./scripts/jest/setupTimezone.js", projects: [ { @@ -70,6 +69,7 @@ module.exports = { "/packages/format-json", "/packages/format-csv", "/packages/message-utils", + "/packages/extractor-vue", ], }, ], diff --git a/lerna.json b/lerna.json index 81c67225e..55f1cbc3b 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,5 @@ { - "version": "4.0.0-next.4", + "version": "4.0.1", "packages": [ "packages/*" ], diff --git a/packages/extractor-vue/README.md b/packages/extractor-vue/README.md new file mode 100644 index 000000000..d60222dcf --- /dev/null +++ b/packages/extractor-vue/README.md @@ -0,0 +1,31 @@ +# Vue extractor + +This package contains a custom extractors that handles Vue files. It supports extracting messages from script and setup scripts. + +## Installation + +```sh +npm install @lingui/extractor-vue +``` + +## Usage + +This custom extractor requires that you use typescript for your lingui configuration. + +```ts +import { vueExtractor } from "@lingui/extractor-vue" +import babel from "@lingui/cli/api/extractors/babel" + +const linguiConfig = { + locales: ["en", "nb"], + sourceLocale: "en", + catalogs: [ + { + path: "/src/{locale}", + include: ["/src"], + }, + ], + extractors: [babel, vueExtractor], +} +export default linguiConfig +``` diff --git a/packages/extractor-vue/package.json b/packages/extractor-vue/package.json new file mode 100644 index 000000000..1a998d8cc --- /dev/null +++ b/packages/extractor-vue/package.json @@ -0,0 +1,41 @@ +{ + "name": "@lingui/extractor-vue", + "version": "4.0.0-next.4", + "description": "Custom vue extractor to be used with CLI tool", + "main": "./dist/index.cjs", + "module": "./dist/index.mjs", + "types": "./dist/index.d.ts", + "keywords": [ + "cli", + "i18n", + "internationalization", + "i10n", + "localization", + "i9n", + "translation" + ], + "repository": "lingui/js-lingui", + "bugs": "https://github.com/lingui/js-lingui/issues", + "license": "MIT", + "author": { + "name": "Christoffer Jahren", + "email": "christoffer@jahren.it" + }, + "scripts": { + "build": "unbuild", + "stub": "unbuild --stub" + }, + "engines": { + "node": ">=16.0.0" + }, + "dependencies": { + "@babel/core": "^7.21.0", + "@lingui/cli": "^4.0.0-next.4", + "@lingui/conf": "^4.0.0-next.4", + "@vue/compiler-sfc": "^3.2.47" + }, + "devDependencies": { + "unbuild": "^1.1.2" + }, + "gitHead": "f2961fcb319265b08ddeda3aa2549d104fc4c6d4" +} diff --git a/packages/extractor-vue/src/__test__/__snapshots__/extractor.test.ts.snap b/packages/extractor-vue/src/__test__/__snapshots__/extractor.test.ts.snap new file mode 100644 index 000000000..a11d2352a --- /dev/null +++ b/packages/extractor-vue/src/__test__/__snapshots__/extractor.test.ts.snap @@ -0,0 +1,44 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`vue extractor should extract from vue script 1`] = ` +[ + { + comment: undefined, + context: undefined, + id: SVG Title, + message: undefined, + origin: [ + /Users/christoffer.jahren@schibsted.com/dev/js-lingui/test.vue, + 7, + 21, + ], + }, + { + comment: undefined, + context: undefined, + id: custom.id, + message: SVG description, + origin: [ + /Users/christoffer.jahren@schibsted.com/dev/js-lingui/test.vue, + 9, + 15, + ], + }, +] +`; + +exports[`vue extractor should extract from vue setup script 1`] = ` +[ + { + comment: undefined, + context: undefined, + id: Loading..., + message: undefined, + origin: [ + /Users/christoffer.jahren@schibsted.com/dev/js-lingui/test.vue, + 7, + 42, + ], + }, +] +`; diff --git a/packages/extractor-vue/src/__test__/example-script-setup.vue b/packages/extractor-vue/src/__test__/example-script-setup.vue new file mode 100644 index 000000000..461e3f192 --- /dev/null +++ b/packages/extractor-vue/src/__test__/example-script-setup.vue @@ -0,0 +1,15 @@ + + + + + diff --git a/packages/extractor-vue/src/__test__/example-script.vue b/packages/extractor-vue/src/__test__/example-script.vue new file mode 100644 index 000000000..3aac14622 --- /dev/null +++ b/packages/extractor-vue/src/__test__/example-script.vue @@ -0,0 +1,28 @@ + + + diff --git a/packages/extractor-vue/src/__test__/extractor.test.ts b/packages/extractor-vue/src/__test__/extractor.test.ts new file mode 100644 index 000000000..41e704444 --- /dev/null +++ b/packages/extractor-vue/src/__test__/extractor.test.ts @@ -0,0 +1,63 @@ +import { makeConfig } from "@lingui/conf" +import fs from "fs" +import path from "path" +import { vueExtractor } from "../vue-extractor" + +describe("vue extractor", () => { + const linguiConfig = makeConfig({ + locales: ["en", "nb"], + sourceLocale: "en", + rootDir: ".", + catalogs: [ + { + path: "/{locale}", + include: [""], + exclude: [], + }, + ], + extractorParserOptions: { + tsExperimentalDecorators: false, + flow: false, + }, + }) + + it("should extract from vue script", async () => { + const filePath = path.resolve(__dirname, "example-script.vue") + const code = fs.readFileSync(filePath, "utf-8") + + let messages = [] + + await vueExtractor.extract( + "test.vue", + code, + (res) => { + messages.push(res) + }, + { + linguiConfig, + } + ) + + expect(messages).toMatchSnapshot() + }) + + it("should extract from vue setup script", async () => { + const filePath = path.resolve(__dirname, "example-script-setup.vue") + const code = fs.readFileSync(filePath, "utf-8") + + let setupMessages = [] + + await vueExtractor.extract( + "test.vue", + code, + (res) => { + setupMessages.push(res) + }, + { + linguiConfig, + } + ) + + expect(setupMessages).toMatchSnapshot() + }) +}) diff --git a/packages/extractor-vue/src/index.ts b/packages/extractor-vue/src/index.ts new file mode 100644 index 000000000..30af98fa4 --- /dev/null +++ b/packages/extractor-vue/src/index.ts @@ -0,0 +1 @@ +export { vueExtractor } from "./vue-extractor" diff --git a/packages/extractor-vue/src/vue-extractor.ts b/packages/extractor-vue/src/vue-extractor.ts new file mode 100644 index 000000000..1d7af6eeb --- /dev/null +++ b/packages/extractor-vue/src/vue-extractor.ts @@ -0,0 +1,28 @@ +import * as vueCompiler from "@vue/compiler-sfc" +// @ts-expect-error issue with typings, will be fixed shortly +import babel from "@lingui/cli/api/extractors/babel" +import type { ExtractorCtx, ExtractorType } from "@lingui/conf" + +export const vueExtractor: ExtractorType = { + match(filename: string) { + return filename.endsWith(".vue") + }, + extract( + filename: string, + code: string, + onMessageExtracted, + ctx: ExtractorCtx + ) { + const { + descriptor: { script, scriptSetup }, + } = vueCompiler.parse(code, { sourceMap: true, filename }) + + const finalCode = `${script?.content} ${scriptSetup?.content}` + const sourcemaps = `${script?.map} ${scriptSetup?.map}` + + return babel.extract(filename, finalCode ?? code, onMessageExtracted, { + sourcemaps, + ...ctx, + }) + }, +} diff --git a/tsconfig.json b/tsconfig.json index 9a079276c..c0b3777e6 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -12,7 +12,9 @@ "skipLibCheck": true, "target": "es2017", "paths": { - "@lingui/babel-plugin-extract-messages": ["./packages/babel-plugin-extract-messages/src"], + "@lingui/babel-plugin-extract-messages": [ + "./packages/babel-plugin-extract-messages/src" + ], "@lingui/core": ["./packages/core/src"], "@lingui/message-utils/*": ["./packages/message-utils/src/*"], "@lingui/cli/api": ["./packages/cli/src/api"], @@ -20,7 +22,8 @@ "@lingui/conf": ["./packages/conf/src"], "@lingui/macro": ["./packages/macro/src"], "@lingui/format-po": ["./packages/format-po/src/po.ts"], - "@lingui/format-json": ["./packages/format-json/src/json.ts"] + "@lingui/format-json": ["./packages/format-json/src/json.ts"], + "@lingui/extractor-vue": ["./packages/extractor-vue/src"] } }, "exclude": [ diff --git a/yarn.lock b/yarn.lock index 5f8b36790..5cb531f78 100644 --- a/yarn.lock +++ b/yarn.lock @@ -439,6 +439,15 @@ __metadata: languageName: node linkType: hard +"@babel/parser@npm:^7.16.4": + version: 7.21.3 + resolution: "@babel/parser@npm:7.21.3" + bin: + parser: ./bin/babel-parser.js + checksum: a71e6456a1260c2a943736b56cc0acdf5f2a53c6c79e545f56618967e51f9b710d1d3359264e7c979313a7153741b1d95ad8860834cc2ab4ce4f428b13cc07be + languageName: node + linkType: hard + "@babel/parser@npm:^7.21.0, @babel/parser@npm:^7.21.2": version: 7.21.2 resolution: "@babel/parser@npm:7.21.2" @@ -2650,7 +2659,7 @@ __metadata: languageName: unknown linkType: soft -"@lingui/cli@4.0.0-next.4, @lingui/cli@workspace:*, @lingui/cli@workspace:packages/cli": +"@lingui/cli@4.0.0-next.4, @lingui/cli@^4.0.0-next.4, @lingui/cli@workspace:*, @lingui/cli@workspace:packages/cli": version: 0.0.0-use.local resolution: "@lingui/cli@workspace:packages/cli" dependencies: @@ -2695,7 +2704,7 @@ __metadata: languageName: unknown linkType: soft -"@lingui/conf@4.0.0-next.4, @lingui/conf@workspace:packages/conf": +"@lingui/conf@4.0.0-next.4, @lingui/conf@^4.0.0-next.4, @lingui/conf@workspace:packages/conf": version: 0.0.0-use.local resolution: "@lingui/conf@workspace:packages/conf" dependencies: @@ -2731,6 +2740,18 @@ __metadata: languageName: unknown linkType: soft +"@lingui/extractor-vue@workspace:packages/extractor-vue": + version: 0.0.0-use.local + resolution: "@lingui/extractor-vue@workspace:packages/extractor-vue" + dependencies: + "@babel/core": ^7.21.0 + "@lingui/cli": ^4.0.0-next.4 + "@lingui/conf": ^4.0.0-next.4 + "@vue/compiler-sfc": ^3.2.47 + unbuild: ^1.1.2 + languageName: unknown + linkType: soft + "@lingui/format-csv@workspace:packages/format-csv": version: 0.0.0-use.local resolution: "@lingui/format-csv@workspace:packages/format-csv" @@ -4491,6 +4512,76 @@ __metadata: languageName: node linkType: hard +"@vue/compiler-core@npm:3.2.47": + version: 3.2.47 + resolution: "@vue/compiler-core@npm:3.2.47" + dependencies: + "@babel/parser": ^7.16.4 + "@vue/shared": 3.2.47 + estree-walker: ^2.0.2 + source-map: ^0.6.1 + checksum: 9ccc2a0b897b59eea56ca4f92ed29c14cd1184f68532edf5fb0fe5cb2833bcf9e4836029effb6eb9a7c872e9e0350fafdcd96ff00c0b5b79e17ded0c068b5f84 + languageName: node + linkType: hard + +"@vue/compiler-dom@npm:3.2.47": + version: 3.2.47 + resolution: "@vue/compiler-dom@npm:3.2.47" + dependencies: + "@vue/compiler-core": 3.2.47 + "@vue/shared": 3.2.47 + checksum: 1eced735f865e6df0c2d7fa041f9f27996ff4c0d4baf5fad0f67e65e623215f4394c49bba337b78427c6e71f2cc2db12b19ec6b865b4c057c0a15ccedeb20752 + languageName: node + linkType: hard + +"@vue/compiler-sfc@npm:^3.2.47": + version: 3.2.47 + resolution: "@vue/compiler-sfc@npm:3.2.47" + dependencies: + "@babel/parser": ^7.16.4 + "@vue/compiler-core": 3.2.47 + "@vue/compiler-dom": 3.2.47 + "@vue/compiler-ssr": 3.2.47 + "@vue/reactivity-transform": 3.2.47 + "@vue/shared": 3.2.47 + estree-walker: ^2.0.2 + magic-string: ^0.25.7 + postcss: ^8.1.10 + source-map: ^0.6.1 + checksum: 4588a513310b9319a00adfdbe789cfe60d5ec19c51e8f2098152b9e81f54be170e16c40463f6b5e4c7ab79796fc31e2de93587a9dd1af136023fa03712b62e68 + languageName: node + linkType: hard + +"@vue/compiler-ssr@npm:3.2.47": + version: 3.2.47 + resolution: "@vue/compiler-ssr@npm:3.2.47" + dependencies: + "@vue/compiler-dom": 3.2.47 + "@vue/shared": 3.2.47 + checksum: 91bc6e46744d5405713c08d8e576971aa6d13a0cde84ec592d3221bf6ee228e49ce12233af8c18dc39723455b420df2951f3616ceb99758eb432485475fa7bc2 + languageName: node + linkType: hard + +"@vue/reactivity-transform@npm:3.2.47": + version: 3.2.47 + resolution: "@vue/reactivity-transform@npm:3.2.47" + dependencies: + "@babel/parser": ^7.16.4 + "@vue/compiler-core": 3.2.47 + "@vue/shared": 3.2.47 + estree-walker: ^2.0.2 + magic-string: ^0.25.7 + checksum: 6fe54374aa8c080c0c421e18134e84e723e2d3e53178cf084c1cd75bc8b1ffaaf07756801f3aa4e1e7ad1ba76356c28bbab4bc1b676159db8fc10f10f2cbd405 + languageName: node + linkType: hard + +"@vue/shared@npm:3.2.47": + version: 3.2.47 + resolution: "@vue/shared@npm:3.2.47" + checksum: 0aa711dc9160fa0e476e6e94eea4e019398adf2211352d0e4a672cfb6b65b104bbd5d234807d1c091107bdc0f5d818d0f12378987eb7861d39be3aa9f6cd6e3e + languageName: node + linkType: hard + "@webassemblyjs/ast@npm:1.11.1": version: 1.11.1 resolution: "@webassemblyjs/ast@npm:1.11.1" @@ -10786,6 +10877,15 @@ __metadata: languageName: node linkType: hard +"magic-string@npm:^0.25.7": + version: 0.25.9 + resolution: "magic-string@npm:0.25.9" + dependencies: + sourcemap-codec: ^1.4.8 + checksum: 9a0e55a15c7303fc360f9572a71cffba1f61451bc92c5602b1206c9d17f492403bf96f946dfce7483e66822d6b74607262e24392e87b0ac27b786e69a40e9b1a + languageName: node + linkType: hard + "magic-string@npm:^0.27.0": version: 0.27.0 resolution: "magic-string@npm:0.27.0" @@ -12502,7 +12602,7 @@ __metadata: languageName: node linkType: hard -"postcss@npm:^8.4.21": +"postcss@npm:^8.1.10, postcss@npm:^8.4.21": version: 8.4.21 resolution: "postcss@npm:8.4.21" dependencies: @@ -13778,6 +13878,13 @@ __metadata: languageName: node linkType: hard +"sourcemap-codec@npm:^1.4.8": + version: 1.4.8 + resolution: "sourcemap-codec@npm:1.4.8" + checksum: b57981c05611afef31605732b598ccf65124a9fcb03b833532659ac4d29ac0f7bfacbc0d6c5a28a03e84c7510e7e556d758d0bb57786e214660016fb94279316 + languageName: node + linkType: hard + "spdx-correct@npm:^3.0.0": version: 3.1.1 resolution: "spdx-correct@npm:3.1.1"