Skip to main content
Version: Next

Frontend Dynamic Provider Plugin Principle

provider​

For each compiled component, it can be wrapped by introducing a provider to achieve unified use of some environment variables and styles. You can introduce it yourself, or you don't need to write it, define a plugin to automatically implement the introduction.

import BusinessFieldSetting from './Views/BusinessFieldSetting'
import Setting from '@/components/Setting/Setting'
import { provider } from '@/provider/index'
import field from '@/assets/svg/field.svg'

export default {
is: 'BusinessFieldSetting',
name: 'Business Field Setting',
category: 'run',
icon: field,
authorizationRequired: true,
canvasView: provider(BusinessFieldSetting), <--- provider here
settingsView: Setting,
}

Plugin Implementation Principle:

The Vite plugin is named vite-plugin-widget-provider, specifically designed to automatically process component entry files under the src/widgets/**/index.ts path during the build phase, improving component encapsulation consistency and development efficiency. The plugin matches all .ts files by constructing a regular expression, and further screens out index.ts files under the widgets directory as targets.

The core logic is divided into two steps:

  1. First, extract the import block, export default content, and canvasView field in the file through the parseCode function;

  2. Secondly, in mergeCodeString, determine whether the canvasView field has been wrapped with provider(). If it is not wrapped and provider/index is not introduced, automatically insert import { provider } from "@/provider/index", and convert the original canvasView: SomeComponent to canvasView: provider(SomeComponent).

This transformation is completed through the transform hook provided by Vite, which can directly replace the target file content, thereby realizing automatic component wrapping and context capability injection. The plugin only takes effect in apply: 'build' mode, ensuring that it does not affect development debugging. The overall design logic is clear, avoiding repetitive injection and manual operations, and has good scalability and maintainability in multi-component development scenarios.

import path from 'path'
const isWin = process.platform === 'win32'
const fileRegex = /\.(ts)$/
const basePath = path.resolve(process.cwd(), './src/widgets')
const filePath = isWin ? basePath.replaceAll('\\', '/') : basePath
const regex = new RegExp(`${filePath}/([^/]*)/index.ts`)
const NOT_PAGE = 'notPage:true'
/**
* Extract key characters
* @param {*} code
* @returns
*/
const parseCode = (code) => {
const importRegion = code.match(/import[^]*?("|')(.*?)\1;/g)
const exportDefaultRegion = code.match(/export default [\s\S]*?;/)
const canvasView = code.match(/canvasView: ([^,]+),/)
const canvasViewValue = canvasView ? canvasView[1] : ''

const imports = importRegion ? importRegion.join('\n') : ''
const exportDefault = exportDefaultRegion ? exportDefaultRegion[0] : ''

return { imports, exportDefault, canvasView: canvasView[0], canvasViewValue }
}

/**
* Merge processing code
* @param {*} param0
* @param {*} originCode
*/
const mergeCodeString = (
{ imports, exportDefault, canvasView, canvasViewValue },
originCode
) => {
const code = `${imports}\n${exportDefault}`
if (
canvasView.includes('provider(') &&
originCode.includes('provider/index')
) {
return code
}

const providerCode = 'import { provider } from "@/provider/index";'

const exportDefaultCode = exportDefault.replace(
canvasView,
`canvasView: provider(${canvasViewValue}),`
)
return `${imports}\n${providerCode}\n${exportDefaultCode}`
}

export default function VitePluginWidgetProvider(): any {
return {
name: 'vite-plugin-widget-provider',
apply: 'build',

transform(code, id) {
if (fileRegex.test(id)) {
if (regex.test(id)) {
const codeData = parseCode(code)
const transformCode = mergeCodeString(codeData, code)
// const emptyCode = code.replaceAll(' ', '')

const newCode = transformCode
// const newCode = emptyCode.includes(NOT_PAGE) ? code : transformCode
return {
code: newCode,
map: null, // Provide source map if feasible
}
}
}
},
}
}