From 8da2039383be0d9e349cbb19cbff7497dc67dc51 Mon Sep 17 00:00:00 2001 From: kazuya kawaguchi Date: Mon, 4 Jan 2021 17:45:38 +0900 Subject: [PATCH 1/2] feat: sourcemap supporting --- package.json | 5 +- src/index.ts | 95 +++++++++++++++++++---- test/__snapshots__/sourcemap.test.ts.snap | 5 ++ test/sourcemap.test.ts | 11 +++ test/utils.ts | 14 +++- yarn.lock | 8 +- 6 files changed, 112 insertions(+), 26 deletions(-) create mode 100644 test/__snapshots__/sourcemap.test.ts.snap create mode 100644 test/sourcemap.test.ts diff --git a/package.json b/package.json index 582111c..b4d2769 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,8 @@ "dependencies": { "@intlify/cli": "^0.1.2", "@intlify/shared": "^9.0.0-beta.16", - "@rollup/pluginutils": "^4.1.0" + "@rollup/pluginutils": "^4.1.0", + "source-map": "0.6.1" }, "devDependencies": { "@types/debug": "^4.1.5", @@ -56,7 +57,7 @@ "ts-jest": "^26.4.0", "typescript": "^4.1.3", "typescript-eslint-language-service": "^4.1.2", - "vite": "2.0.0-beta.3", + "vite": "2.0.0-beta.4", "vue": "^3.0.5", "vue-i18n": "^9.0.0-beta.18" }, diff --git a/src/index.ts b/src/index.ts index 4d90133..a706ca7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,6 +3,7 @@ import path from 'path' import { isEmptyObject, isString } from '@intlify/shared' import { createFilter } from '@rollup/pluginutils' import { generateJSON, generateYAML } from '@intlify/cli' +import { SourceMapGenerator, SourceMapConsumer, RawSourceMap } from 'source-map' import { debug as Debug } from 'debug' import { parseVueRequest } from './query' @@ -17,6 +18,7 @@ export function pluginI18n( ): Plugin { debug('plugin options:', options) + const forceStringify = !!options.forceStringify const filter = createFilter(options.include) let config: ResolvedConfig | null = null @@ -30,39 +32,75 @@ export function pluginI18n( async transform(code: string, id: string) { const { filename, query } = parseVueRequest(id) - debug('transform', id, code, JSON.stringify(query)) - - const parseOptions = getOptions( - filename, - config != null ? (config.mode as DevEnv) : 'development', - query as Record, - options.forceStringify - ) as CodeGenOptions - debug('parseOptions', parseOptions) + debug('transform', id, JSON.stringify(query)) + const sourceMap = + config != null + ? config.isProduction + ? isString(config.build.sourcemap) + ? true + : config.build.sourcemap + : true + : false + let inSourceMap: RawSourceMap | undefined + debug('sourcemap', sourceMap, id) let langInfo = 'json' if (!query.vue) { if (/\.(json5?|ya?ml)$/.test(id) && filter(id)) { + if (sourceMap) { + const map = this.getCombinedSourcemap() + console.log(map) + inSourceMap = (map as unknown) as RawSourceMap + } langInfo = path.parse(filename).ext // NOTE: // `.json` is handled default in vite, and it's transformed to JS object. let _source = code if (langInfo === '.json') { _source = await getRawJSON(id) + console.log('raw json', _source) } + const generate = /\.?json5?/.test(langInfo) ? generateJSON : generateYAML - const { code: generatedCode } = generate(_source, parseOptions) - debug('generated code', generatedCode) + + const parseOptions = getOptions( + filename, + config != null ? config.isProduction : false, + query as Record, + sourceMap, + inSourceMap, + forceStringify + ) as CodeGenOptions + debug('parseOptions', parseOptions) + + const { code: generatedCode, map } = generate(_source, parseOptions) + debug('generated', map) + // TODO: error handling & sourcempa - return Promise.resolve(generatedCode) + return Promise.resolve({ + code: generatedCode, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + map: (sourceMap ? map : inSourceMap) as any + }) } else { return Promise.resolve(code) } } else { // for Vue SFC if (isCustomBlock(query as Record)) { + if (sourceMap) { + const map = this.getCombinedSourcemap() + console.log(map) + // const s = new SourceMapConsumer((map as any).toJSON()) + // const s = new SourceMapConsumer(map as any) + // s.eachMapping(m => { + // console.log('sourcemap json', m) + // }) + inSourceMap = (map as unknown) as RawSourceMap + } + if ('src' in query) { if (isString(query.lang)) { langInfo = query.lang === 'i18n' ? 'json' : query.lang @@ -72,13 +110,30 @@ export function pluginI18n( langInfo = query.lang } } + const generate = /\.?json5?/.test(langInfo) ? generateJSON : generateYAML - const { code: generatedCode } = generate(code, parseOptions) - debug('generated code', generatedCode) + + const parseOptions = getOptions( + filename, + config != null ? config.isProduction : false, + query as Record, + sourceMap, + inSourceMap, + forceStringify + ) as CodeGenOptions + debug('parseOptions', parseOptions) + + const { code: generatedCode, map } = generate(code, parseOptions) + debug('generated', map) + // TODO: error handling & sourcempa - return Promise.resolve(generatedCode) + return Promise.resolve({ + code: generatedCode, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + map: (sourceMap ? map : inSourceMap) as any + }) } else { return Promise.resolve(code) } @@ -104,12 +159,18 @@ function isCustomBlock(query: Record): boolean { function getOptions( filename: string, - mode: DevEnv, + isProduction: boolean, query: Record, - forceStringify = false + sourceMap: boolean, + inSourceMap: RawSourceMap | undefined, + forceStringify: boolean ): Record { + const mode: DevEnv = isProduction ? 'production' : 'production' + const baseOptions = { filename, + sourceMap, + inSourceMap, forceStringify, env: mode, onWarn: (msg: string): void => { diff --git a/test/__snapshots__/sourcemap.test.ts.snap b/test/__snapshots__/sourcemap.test.ts.snap new file mode 100644 index 0000000..9465dee --- /dev/null +++ b/test/__snapshots__/sourcemap.test.ts.snap @@ -0,0 +1,5 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`fully blocks 1`] = `";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"`; + +exports[`i18n custom block only 1`] = `";;;;;;;;;;;;;;;;;;;;;;;;;;"`; diff --git a/test/sourcemap.test.ts b/test/sourcemap.test.ts new file mode 100644 index 0000000..e954c5d --- /dev/null +++ b/test/sourcemap.test.ts @@ -0,0 +1,11 @@ +import { bundleAndRun } from './utils' + +test('i18n custom block only', async () => { + const { map } = await bundleAndRun('basic.vue', { sourcemap: true }) + expect(map.mappings).toMatchSnapshot() +}) + +test('fully blocks', async () => { + const { map } = await bundleAndRun('fully-block.vue', { sourcemap: true }) + expect(map.mappings).toMatchSnapshot() +}) diff --git a/test/utils.ts b/test/utils.ts index 3834f50..5cc95ae 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -9,6 +9,7 @@ async function bundle(fixture: string, options: Record = {}) { const input = (options.input as string) || './fixtures/entry.ts' const target = (options.target as string) || './fixtures' const include = (options.include as string[]) || [] + const sourcemap = (options.sourcemap as boolean) || false const silent = isBoolean(options.silent) ? options.silent === false ? 'info' @@ -22,6 +23,7 @@ async function bundle(fixture: string, options: Record = {}) { }, plugins: [vue(), pluginI18n({ include })], build: { + sourcemap, write: false, minify: false, rollupOptions: { @@ -29,15 +31,19 @@ async function bundle(fixture: string, options: Record = {}) { } } }) - // eslint-disable-next-line @typescript-eslint/no-explicit-any - return { code: (result as any).output[0].code } + return { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + code: (result as any).output[0].code, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + map: (result as any).output[0].map + } } export async function bundleAndRun( fixture: string, options: Record = {} ) { - const { code } = await bundle(fixture, options) + const { code, map } = await bundle(fixture, options) let dom: JSDOM | null = null let jsdomError @@ -63,6 +69,8 @@ export async function bundleAndRun( window, module, exports, + code, + map, jsdomError }) } diff --git a/yarn.lock b/yarn.lock index ead030d..3afa439 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7365,10 +7365,10 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" -vite@2.0.0-beta.3: - version "2.0.0-beta.3" - resolved "https://registry.yarnpkg.com/vite/-/vite-2.0.0-beta.3.tgz#50b62a705749f1187d094b0f9a4dad9e1d7630a8" - integrity sha512-jY0H655nqpclHhk4gJJ6oINvX+REGzNqAOAaNbU3P2TDM6PxAWpXsvGjkC/DonkZtuV/m3q7UhACS/tm5hJ04A== +vite@2.0.0-beta.4: + version "2.0.0-beta.4" + resolved "https://registry.yarnpkg.com/vite/-/vite-2.0.0-beta.4.tgz#6ea8e08ae5e6b510548d02ec770d34046622aef7" + integrity sha512-V0HV6xyUPQ9ktJ8gLm0Et7zTFtp8WmISsH/K+FuGF3aJ4VJOlSYEaget1nHCauUPI7WDPe97suz7SCIVHW2iEg== dependencies: esbuild "^0.8.26" postcss "^8.2.1" From 5d5bfb1e4f9f6b7bb664cbb7f021536f59180a4f Mon Sep 17 00:00:00 2001 From: kazuya kawaguchi Date: Fri, 8 Jan 2021 00:26:19 +0900 Subject: [PATCH 2/2] updates --- README.md | 84 +++++++++++++++-------- src/index.ts | 52 +++++++++----- test/__snapshots__/sourcemap.test.ts.snap | 4 +- 3 files changed, 92 insertions(+), 48 deletions(-) diff --git a/README.md b/README.md index 6c2c37e..46ed220 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,12 @@ ![Test](https://github.com/intlify/vite-plugin-vue-i18n/workflows/Test/badge.svg) [![npm](https://img.shields.io/npm/v/@intlify/vite-plugin-vue-i18n.svg)](https://www.npmjs.com/package/@intlify/vite-plugin-vue-i18n) -Vite plugin for i18n resource pre-compilation and custom blocks +Vite plugin for Vue I18n + + +## :star: Features +- i18n resources pre-compilation +- i18n custom block ## :cd: Installation @@ -19,9 +24,10 @@ $ npm i --save-dev @intlify/vite-plugin-vue-i18n $ yarn add -D @intlify/vite-plugin-vue-i18n ``` -## :rocket: Usages -### i18n resource pre-compilation +## :rocket: Usage + +### i18n resources pre-compilation Since vue-i18n@v9.0, The locale messages are handled with message compiler, which converts them to javascript functions after compiling. After compiling, message compiler converts them into javascript functions, which can improve the performance of the application. @@ -35,15 +41,15 @@ the below example that `examples/composition/vite.config.ts`: ```ts import path from 'path' -import vue from '@vitejs/plugin-vue' -import { pluginI18n } from '@intlify/vite-plugin-vue-i18n' +import Vue from '@vitejs/plugin-vue' +import { VueI18n } from '@intlify/vite-plugin-vue-i18n' import type { UserConfig } from 'vite' const config: UserConfig = { plugins: [ - vue(), // you need to install `@vitejs/plugin-vue` - pluginI18n({ + Vue(), // you need to install `@vitejs/plugin-vue` + VueI18n({ // you need to set i18n resource including paths ! include: path.resolve(__dirname, './path/to/src/locales/**') }) @@ -53,9 +59,9 @@ const config: UserConfig = { export default config ``` -### `i18n` custom block +### i18n custom block -the below example that `examples/composition/App.vue` have `i18n` custom block: +the below example that `examples/composition/App.vue` have i18n custom block: ```vue