From 5c6d594240cbc3de1b8a26da3a6aaf7112ea93c3 Mon Sep 17 00:00:00 2001 From: kazuya kawaguchi Date: Sun, 22 Sep 2024 19:03:22 +0900 Subject: [PATCH] feat(unplugin-vue-i18n): support `module` option --- packages/unplugin-vue-i18n/README.md | 28 +++- packages/unplugin-vue-i18n/package.json | 1 - .../unplugin-vue-i18n/src/core/options.ts | 13 +- .../unplugin-vue-i18n/src/core/resource.ts | 9 +- packages/unplugin-vue-i18n/src/index.ts | 7 +- packages/unplugin-vue-i18n/src/types.ts | 3 + packages/unplugin-vue-i18n/src/utils/index.ts | 2 - packages/unplugin-vue-i18n/src/utils/pkg.ts | 139 ------------------ .../unplugin-vue-i18n/src/utils/resolver.ts | 60 -------- packages/unplugin-vue-i18n/test/pkg.test.ts | 37 ----- pnpm-lock.yaml | 3 - 11 files changed, 37 insertions(+), 265 deletions(-) delete mode 100644 packages/unplugin-vue-i18n/src/utils/pkg.ts delete mode 100644 packages/unplugin-vue-i18n/src/utils/resolver.ts delete mode 100644 packages/unplugin-vue-i18n/test/pkg.test.ts diff --git a/packages/unplugin-vue-i18n/README.md b/packages/unplugin-vue-i18n/README.md index e4740b6..3582f38 100644 --- a/packages/unplugin-vue-i18n/README.md +++ b/packages/unplugin-vue-i18n/README.md @@ -253,13 +253,6 @@ This plugin will automatically select and bundle Vue I18n build according to the About details, See the [here](https://vue-i18n.intlify.dev/guide/advanced/optimization.html#improve-performance-and-reduce-bundle-size-with-runtime-build-only) -### For `petite-vue-i18n` - -This plugin will automatically select and bundle `petite-vue-i18n` build according to the following vite behavior: - -- vite dev: `petite-vue-i18n.esm-bundler.js` -- vite build: `petite-vue-i18n.runtime.esm-bundler.js` - ## 🔧 Options @@ -302,6 +295,27 @@ This plugin will automatically select and bundle `petite-vue-i18n` build accordi > [!WARNING] If you use the `js` and `ts` resources formats, set the paths, so your application code is not targeted. We recommend that resources be isolated from the application code. +### `module` + +- **Type:** `string` +- **Default:** `'vue-i18n'` + + > [!NOTE] + This options is supported from v5.1.0, and works with vue-i18n v10 and later. + + Bundle target vue-i18n module. You can specify either `‘vue-i18n’` or `‘petite-vue-i18n’`. + + The default is `'vue-i18n'`, and the following installed in node_modules will be bundled. + + - development: `vue-i18n.esm-bundler.js` + - production: `vue-i18n.runtime.esm-bundler.js` + + In the case of `‘petite-vue-i18n’`, the following installed in node_modules will be bundled. + + - development: `petite-vue-i18n.esm-bundler.js` + - production: `petite-vue-i18n.runtime.esm-bundler.js` + + If you are using petite-vue-i18n, you will need to set this value. ### `strictMessage` diff --git a/packages/unplugin-vue-i18n/package.json b/packages/unplugin-vue-i18n/package.json index 6b0589a..ba696c0 100644 --- a/packages/unplugin-vue-i18n/package.json +++ b/packages/unplugin-vue-i18n/package.json @@ -34,7 +34,6 @@ "fast-glob": "^3.2.12", "js-yaml": "^4.1.0", "json5": "^2.2.3", - "mlly": "^1.7.1", "pathe": "^1.0.0", "picocolors": "^1.0.0", "source-map-js": "^1.0.2", diff --git a/packages/unplugin-vue-i18n/src/core/options.ts b/packages/unplugin-vue-i18n/src/core/options.ts index 62b0b23..1a76615 100644 --- a/packages/unplugin-vue-i18n/src/core/options.ts +++ b/packages/unplugin-vue-i18n/src/core/options.ts @@ -3,12 +3,10 @@ import { isString, isBoolean, isArray } from '@intlify/shared' import type { PluginOptions } from '../types' import type { TranslationDirectiveResolveIndetifier } from '../vue' -import type { InstalledPackageInfo } from '../utils' -export function resolveOptions( - options: PluginOptions, - installedPkgInfo: InstalledPackageInfo -) { +export function resolveOptions(options: PluginOptions) { + const moduleType = (options.module || 'vue-i18n') as string + // normalize for `options.onlyLocales` let onlyLocales: string[] = [] if (options.onlyLocales) { @@ -43,14 +41,14 @@ export function resolveOptions( const dropMessageCompiler = !!options.dropMessageCompiler // prettier-ignore - const compositionOnly = installedPkgInfo.pkg === 'vue-i18n' + const compositionOnly = moduleType === 'vue-i18n' ? isBoolean(options.compositionOnly) ? options.compositionOnly : true : true // prettier-ignore - const fullInstall = installedPkgInfo.pkg === 'vue-i18n' + const fullInstall = moduleType === 'vue-i18n' ? isBoolean(options.fullInstall) ? options.fullInstall : true @@ -85,6 +83,7 @@ export function resolveOptions( return { include, exclude, + module: moduleType, onlyLocales, forceStringify, defaultSFCLang, diff --git a/packages/unplugin-vue-i18n/src/core/resource.ts b/packages/unplugin-vue-i18n/src/core/resource.ts index f7c3a1f..96922a6 100644 --- a/packages/unplugin-vue-i18n/src/core/resource.ts +++ b/packages/unplugin-vue-i18n/src/core/resource.ts @@ -36,7 +36,6 @@ import type { TransformResult } from 'unplugin' import type { ResolvedOptions } from '../core/options' -import type { InstalledPackageInfo } from '../utils' import type { VueQuery } from '../vue' import type { PluginOptions } from '../types' @@ -50,6 +49,7 @@ export function resourcePlugin( onlyLocales, include, exclude, + module, forceStringify, defaultSFCLang, globalSFCScope, @@ -63,18 +63,17 @@ export function resourcePlugin( escapeHtml, transformI18nBlock }: ResolvedOptions, - meta: UnpluginContextMeta, - installedPkgInfo: InstalledPackageInfo + meta: UnpluginContextMeta ): UnpluginOptions { const filter = createFilter(include, exclude) const getVueI18nAliasPath = ({ ssr = false, runtimeOnly = false }) => { - return `${installedPkgInfo.alias}/dist/${installedPkgInfo.pkg}${runtimeOnly ? '.runtime' : ''}.${ + return `${module}/dist/${module}${runtimeOnly ? '.runtime' : ''}.${ !ssr ? 'esm-bundler.js' /* '.mjs' */ : 'node.mjs' }` } let isProduction = false let sourceMap = false - const vueI18nAliasName = installedPkgInfo.alias + const vueI18nAliasName = module debug(`vue-i18n alias name: ${vueI18nAliasName}`) let vuePlugin: RollupPlugin | null = null diff --git a/packages/unplugin-vue-i18n/src/index.ts b/packages/unplugin-vue-i18n/src/index.ts index 31e3033..6c8d8ed 100644 --- a/packages/unplugin-vue-i18n/src/index.ts +++ b/packages/unplugin-vue-i18n/src/index.ts @@ -1,13 +1,12 @@ import { createUnplugin } from 'unplugin' import createDebug from 'debug' -import { raiseError, checkInstallPackage, resolveNamespace } from './utils' +import { raiseError, resolveNamespace } from './utils' import { resolveOptions, resourcePlugin, directivePlugin } from './core' import type { UnpluginFactory, UnpluginInstance } from 'unplugin' import type { PluginOptions } from './types' const debug = createDebug(resolveNamespace('root')) -const installedPkgInfo = checkInstallPackage(debug) export * from './types' @@ -22,10 +21,10 @@ export const unpluginFactory: UnpluginFactory = ( } debug('plugin options (resolving):', options) - const resolvedOptions = resolveOptions(options, installedPkgInfo) + const resolvedOptions = resolveOptions(options) debug('plugin options (resolved):', resolvedOptions) - const plugins = [resourcePlugin(resolvedOptions, meta, installedPkgInfo)] + const plugins = [resourcePlugin(resolvedOptions, meta)] if (resolvedOptions.optimizeTranslationDirective) { if (meta.framework === 'webpack') { raiseError( diff --git a/packages/unplugin-vue-i18n/src/types.ts b/packages/unplugin-vue-i18n/src/types.ts index 63c012e..5b8276d 100644 --- a/packages/unplugin-vue-i18n/src/types.ts +++ b/packages/unplugin-vue-i18n/src/types.ts @@ -1,8 +1,11 @@ export type SFCLangFormat = 'json' | 'json5' | 'yml' | 'yaml' +export type VueI18nModule = 'vue-i18n' | 'petite-vue-i18n' + export interface PluginOptions { include?: string | string[] onlyLocales?: string | string[] allowDynamic?: boolean + module?: VueI18nModule dropMessageCompiler?: boolean runtimeOnly?: boolean compositionOnly?: boolean diff --git a/packages/unplugin-vue-i18n/src/utils/index.ts b/packages/unplugin-vue-i18n/src/utils/index.ts index f37e65d..2c77e2a 100644 --- a/packages/unplugin-vue-i18n/src/utils/index.ts +++ b/packages/unplugin-vue-i18n/src/utils/index.ts @@ -1,4 +1,2 @@ export * from './log' export * from './plugin' -export * from './resolver' -export * from './pkg' diff --git a/packages/unplugin-vue-i18n/src/utils/pkg.ts b/packages/unplugin-vue-i18n/src/utils/pkg.ts deleted file mode 100644 index 4ee6137..0000000 --- a/packages/unplugin-vue-i18n/src/utils/pkg.ts +++ /dev/null @@ -1,139 +0,0 @@ -// SPDX-License-Identifier: MIT -// Modified by: kazuya kawaguchi (a.k.a. kazupon) -// Auther: Pooya Parsa (https://github.com/pi0) -// Forked from: https://github.com/unjs/pkg-types -// Note: Modified to work as a synchronous API - -import { statSync } from 'node:fs' -import { join, resolve, isAbsolute } from 'pathe' -import { resolvePathSync } from 'mlly' - -import type { ResolveOptions as _ResolveOptions } from 'mlly' - -export interface FindFileOptions { - /** - * The starting directory for the search. - * @default . (same as `process.cwd()`) - */ - startingFrom?: string - /** - * A pattern to match a path segment above which you don't want to ascend - * @default /^node_modules$/ - */ - rootPattern?: RegExp - /** - * If true, search starts from root level descending into subdirectories - */ - reverse?: boolean - /** - * A matcher that can evaluate whether the given path is a valid file (for example, - * by testing whether the file path exists. - * - * @default fs.statSync(path).isFile() - */ - test?: (filePath: string) => boolean | undefined -} - -const defaultFindOptions: Required = { - startingFrom: '.', - rootPattern: /^node_modules$/, - reverse: false, - test: (filePath: string) => { - try { - if (statSync(filePath).isFile()) { - return true - } - } catch { - // Ignore - } - } -} - -/** - * Asynchronously finds a file by name, starting from the specified directory and traversing up (or down if reverse). - * @param filename - The name of the file to find. - * @param _options - Options to customise the search behaviour. - * @returns a promise that resolves to the path of the file found. - * @throws Will throw an error if the file cannot be found. - */ -function findFile( - filename: string | string[], - _options: FindFileOptions = {} -): string { - const filenames = Array.isArray(filename) ? filename : [filename] - const options = { ...defaultFindOptions, ..._options } - const basePath = resolve(options.startingFrom) - const leadingSlash = basePath[0] === '/' - const segments = basePath.split('/').filter(Boolean) - - // Restore leading slash - if (leadingSlash) { - segments[0] = '/' + segments[0] - } - - // Limit to node_modules scope if it exists - let root = segments.findIndex(r => r.match(options.rootPattern)) - if (root === -1) { - root = 0 - } - - if (options.reverse) { - for (let index = root + 1; index <= segments.length; index++) { - for (const filename of filenames) { - const filePath = join(...segments.slice(0, index), filename) - if (options.test(filePath)) { - return filePath - } - } - } - } else { - for (let index = segments.length; index > root; index--) { - for (const filename of filenames) { - const filePath = join(...segments.slice(0, index), filename) - if (options.test(filePath)) { - return filePath - } - } - } - } - - throw new Error( - `Cannot find matching ${filename} in ${options.startingFrom} or parent directories` - ) -} - -/** - * Asynchronously finds the next file with the given name, starting in the given directory and moving up. - * Alias for findFile without reversing the search. - * @param filename - The name of the file to find. - * @param _options - Options to customise the search behaviour. - * @returns A promise that resolves to the path of the next file found. - */ -function findNearestFile( - filename: string | string[], - _options: FindFileOptions = {} -): string { - return findFile(filename, _options) -} - -/** - * Represents the options for resolving paths with additional file finding capabilities. - */ -export type ResolveOptions = _ResolveOptions & FindFileOptions - -/** - * Resolves the path to the nearest `package.json` file from a given directory. - * @param id - The base path for the search, defaults to the current working directory. - * @param options - Options to modify the search behaviour. See {@link ResolveOptions}. - * @returns A promise resolving to the path of the nearest `package.json` file. - */ -export function resolvePackageJSON( - id: string = process.cwd(), - options: ResolveOptions = {} -): string { - const resolvedPath = isAbsolute(id) ? id : resolvePathSync(id, options) - return findNearestFile('package.json', { - startingFrom: resolvedPath, - ...options - }) -} diff --git a/packages/unplugin-vue-i18n/src/utils/resolver.ts b/packages/unplugin-vue-i18n/src/utils/resolver.ts deleted file mode 100644 index 4f8f74f..0000000 --- a/packages/unplugin-vue-i18n/src/utils/resolver.ts +++ /dev/null @@ -1,60 +0,0 @@ -import fs from 'node:fs' -import createDebug from 'debug' -import module from 'node:module' -import { resolvePackageJSON } from './pkg' - -const SUPPORT_PACKAGES = ['vue-i18n', 'petite-vue-i18n'] as const - -type SupportPackage = (typeof SUPPORT_PACKAGES)[number] - -export type InstalledPackageInfo = { - alias: string - pkg: SupportPackage -} - -const _require = module.createRequire(import.meta.url) - -export function checkInstallPackage( - debug: createDebug.Debugger -): InstalledPackageInfo { - const pkgInfo = - resolvePkgPath('vue-i18n', debug) || - resolvePkgPath('petite-vue-i18n', debug) - if (!pkgInfo) { - throw new Error( - `requires 'vue-i18n' or 'petite-vue-i18n' to be present in the dependency tree.` - ) - } - - debug('installed package info:', pkgInfo) - return pkgInfo -} - -function resolvePkgPath( - id: string, - debug: createDebug.Debugger -): InstalledPackageInfo | null { - try { - /** - * NOTE: - * Assuming the case of using npm alias `npm:`, - * get the installed package name from `package.json` - */ - const modPath = _require.resolve(id) - debug('modPath:', modPath, id) - const pkgJsonPath = resolvePackageJSON(modPath) - debug('pkgJsonPath:', pkgJsonPath) - const pkgJson = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8')) as { - name: string - } - const pkgName: string = pkgJson.name.startsWith('vue-i18n') - ? 'vue-i18n' - : pkgJson.name.startsWith('petite-vue-i18n') - ? 'petite-vue-i18n' - : '' - return pkgJson ? { alias: id, pkg: pkgName as SupportPackage } : null - } catch (e) { - debug(`cannot find '${id}'`, e) - return null - } -} diff --git a/packages/unplugin-vue-i18n/test/pkg.test.ts b/packages/unplugin-vue-i18n/test/pkg.test.ts deleted file mode 100644 index a18bc2e..0000000 --- a/packages/unplugin-vue-i18n/test/pkg.test.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { fileURLToPath } from 'node:url' -import { dirname, resolve } from 'pathe' -import { promises as fs } from 'node:fs' -import { resolvePackageJSON } from '../src/utils' - -const fixtureDir = resolve( - dirname(fileURLToPath(import.meta.url)), - 'fixtures/packages' -) -const resolveFixture = (...p: string[]) => resolve(fixtureDir, ...p) - -async function readJSON(file: string) { - return JSON.parse(await fs.readFile(file, 'utf-8')) as { name: string } -} - -describe('resolvePackageJSON', () => { - test('in current', async () => { - const { name } = await readJSON( - await resolvePackageJSON(resolveFixture('.')) - ) - expect(name).toEqual('root') - }) - - test('in pkg1', async () => { - const { name } = await readJSON( - await resolvePackageJSON(resolveFixture('./pkg1')) - ) - expect(name).toEqual('pkg1') - }) - - test('do not have package.json', async () => { - const { name } = await readJSON( - await resolvePackageJSON(resolveFixture('./no-pkg')) - ) - expect(name).toEqual('root') - }) -}) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9f0c9ac..967f302 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -300,9 +300,6 @@ importers: json5: specifier: ^2.2.3 version: 2.2.3 - mlly: - specifier: ^1.7.1 - version: 1.7.1 pathe: specifier: ^1.0.0 version: 1.1.2