Skip to content

Commit 9368828

Browse files
committed
Greatly simplify external-plugin
1 parent 3aca80b commit 9368828

File tree

4 files changed

+41
-101
lines changed

4 files changed

+41
-101
lines changed
Binary file not shown.

src/loader-query.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import type { SFCPluginOptions } from './types'
2-
31
export interface VueQuery {
42
vue?: boolean
53
type?: 'script' | 'template' | 'style' | 'custom' | 'fluent'
@@ -33,7 +31,7 @@ export function parseVueRequest(id: string) {
3331
}
3432
}
3533

36-
export function isCustomBlock(query: VueQuery, options: SFCPluginOptions): boolean {
34+
export function isCustomBlock(query: VueQuery, options: { blockType: string }): boolean {
3735
return (
3836
'vue' in query
3937
&& (query.type === 'custom' // for vite (@vite-plugin-vue)

src/plugins/external-plugin.ts

Lines changed: 40 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -5,39 +5,18 @@ import { createUnplugin } from 'unplugin'
55
import MagicString from 'magic-string'
66
import { createFilter, makeLegalIdentifier } from '@rollup/pluginutils'
77

8-
import type { ExternalPluginOptions, InsertInfo } from '../types'
8+
import type { ExternalPluginOptions } from '../types'
9+
import { isCustomBlock, parseVueRequest } from '../loader-query'
910
import { getSyntaxErrors } from './ftl/parse'
1011

11-
function getInsertInfo(source: string): InsertInfo {
12-
let target = null
13-
14-
// vite-plugin-vue2
15-
if (source.includes('__component__'))
16-
target = '__component__'
17-
18-
// rollup-plugin-vue
19-
if (source.includes('export default script'))
20-
target = 'script'
21-
22-
// @vitejs/plugin-vue
23-
if (source.includes('_sfc_main')) {
24-
return {
25-
target: '_sfc_main',
26-
insertPos: source.indexOf('export default'),
27-
usePos: source.indexOf('_export_sfc(_sfc_main, [') + 24,
28-
}
29-
}
30-
31-
// vue-loader
32-
if (source.includes('__exports__'))
33-
target = '__exports__'
34-
35-
const insertPos = source.indexOf('export default')
36-
37-
if (insertPos === -1 || target === null)
38-
throw new Error('Could not parse vue component. This is the issue with unplugin-fluent-vue.\nPlease report this issue to the unplugin-fluent-vue repository.')
12+
const isVue = createFilter(['**/*.vue'])
13+
const isFtl = createFilter(['**/*.ftl'])
3914

40-
return { insertPos, target }
15+
interface Dependency {
16+
locale: string
17+
ftlPath: string
18+
relativeFtlPath: string
19+
importVariable: string
4120
}
4221

4322
async function fileExists(filename: string): Promise<boolean> {
@@ -54,17 +33,7 @@ function normalizePath(path: string) {
5433
return path.replace(/\\/g, '/')
5534
}
5635

57-
const isVue = createFilter(['**/*.vue'])
58-
const isFtl = createFilter(['**/*.ftl'])
59-
60-
interface Dependency {
61-
locale: string
62-
ftlPath: string
63-
relativeFtlPath: string
64-
importVariable: string
65-
}
66-
67-
export const unplugin = createUnplugin((options: ExternalPluginOptions, meta) => {
36+
export const unplugin = createUnplugin((options: ExternalPluginOptions) => {
6837
const resolvedOptions = {
6938
checkSyntax: true,
7039
virtualModuleName: 'virtual:ftl-for-file',
@@ -81,38 +50,6 @@ export const unplugin = createUnplugin((options: ExternalPluginOptions, meta) =>
8150
}
8251
}
8352

84-
const insertFtlImports = (magic: MagicString, translations: Dependency[]) => {
85-
for (const dep of translations)
86-
magic.prepend(`import ${dep.importVariable} from '${dep.relativeFtlPath}';\n`)
87-
}
88-
89-
const insertHotCode = (magic: MagicString, translations: Dependency[], target: string, insertPos: number) => {
90-
const __HOT_API__ = meta.framework === 'webpack' ? 'import.meta.webpackHot' : 'import.meta.hot'
91-
92-
magic.appendLeft(insertPos, `
93-
if (${__HOT_API__}) {
94-
${__HOT_API__}.accept([${translations.map(dep => `'${dep.relativeFtlPath}'`).join(', ')}], (mods) => {
95-
${translations.map(({ locale, importVariable }) => `${target}.fluent['${locale}'] = ${importVariable}`).join('\n')}
96-
97-
if (mods) {
98-
${translations.map(({ locale }, index) => `if (mods['${index}']) ${target}.fluent['${locale}'] = mods['${index}'].default`).join('\n')}
99-
}
100-
101-
delete ${target}._fluent
102-
if (typeof __VUE_HMR_RUNTIME__ !== 'undefined') {
103-
// Vue 3
104-
__VUE_HMR_RUNTIME__.reload(${target}.__hmrId, ${target})
105-
} else {
106-
// Vue 2
107-
// There is no proper api to access HMR for component from custom block
108-
// so use this magic
109-
delete ${target}._Ctor
110-
}
111-
})
112-
}
113-
`)
114-
}
115-
11653
const getTranslationsForFile = async (id: string) => {
11754
const dependencies: Dependency[] = []
11855
for (const locale of options.locales) {
@@ -135,13 +72,21 @@ if (${__HOT_API__}) {
13572
return dependencies
13673
}
13774

75+
const isFluentCustomBlock = (id: string) => {
76+
const request = parseVueRequest(id)
77+
return isCustomBlock(request.query, { blockType: 'fluent' })
78+
}
79+
13880
return {
13981
name: 'unplugin-fluent-vue-external',
140-
enforce: meta.framework === 'webpack' ? 'post' : undefined,
82+
enforce: 'pre',
14183
resolveId(id, importer) {
14284
if (id === resolvedOptions.virtualModuleName)
14385
return `${id}?importer=${importer}`
14486
},
87+
loadInclude(id: string) {
88+
return id.startsWith(resolvedOptions.virtualModuleName)
89+
},
14590
async load(id) {
14691
if (!id.startsWith(resolvedOptions.virtualModuleName))
14792
return
@@ -164,34 +109,19 @@ if (${__HOT_API__}) {
164109
return code
165110
},
166111
transformInclude(id: string) {
167-
return isVue(id) || isFtl(id)
112+
return isVue(id) || isFtl(id) || isFluentCustomBlock(id)
168113
},
169114
async transform(source: string, id: string) {
170115
if (isVue(id)) {
171116
const magic = new MagicString(source, { filename: id })
172117

173-
const { insertPos, target, usePos } = getInsertInfo(source)
174-
175118
const translations = await getTranslationsForFile(id)
176119

177120
if (translations.length === 0)
178121
return
179122

180-
for (const { ftlPath } of translations)
181-
this.addWatchFile(ftlPath)
182-
183-
insertFtlImports(magic, translations)
184-
185-
if (usePos == null) {
186-
magic.appendLeft(insertPos, `${target}.fluent = ${target}.fluent || {};\n`)
187-
for (const dep of translations)
188-
magic.appendLeft(insertPos, `${target}.fluent['${dep.locale}'] = ${dep.importVariable}\n`)
189-
}
190-
else {
191-
magic.appendRight(usePos, `['fluent',{${translations.map(dep => `'${dep.locale}':${dep.importVariable}`).join(',')}}],`)
192-
}
193-
194-
insertHotCode(magic, translations, target, insertPos)
123+
for (const { relativeFtlPath, locale } of translations)
124+
magic.append(`<fluent locale="${locale}" src="${relativeFtlPath}"></fluent>\n`)
195125

196126
return {
197127
code: magic.toString(),
@@ -212,6 +142,24 @@ export default /*#__PURE__*/ new FluentResource(${JSON.stringify(source)})
212142
`
213143
}
214144

145+
const query = parseVueRequest(id).query
146+
if (isFluentCustomBlock(id)) {
147+
if (options.checkSyntax) {
148+
const errorsText = getSyntaxErrors(source)
149+
if (errorsText)
150+
this.error(errorsText)
151+
}
152+
153+
return `
154+
import { FluentResource } from '@fluent/bundle'
155+
156+
export default function (Component) {
157+
const target = Component.options || Component
158+
target.fluent = target.fluent || {}
159+
target.fluent['${query.locale}'] = new FluentResource(${JSON.stringify(source)})
160+
}`
161+
}
162+
215163
return undefined
216164
},
217165
}

src/types.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,3 @@ export interface SFCPluginOptions {
1919
blockType?: string
2020
checkSyntax?: boolean
2121
}
22-
23-
export interface InsertInfo {
24-
insertPos: number
25-
usePos?: number
26-
target: string
27-
}

0 commit comments

Comments
 (0)