@@ -6,6 +6,17 @@ import { existsSync } from 'fs'
66import { parse } from '@vue/compiler-dom'
77import type { RootNode , ElementNode , AttributeNode } from '@vue/compiler-dom'
88
9+ const FORMKIT_CONFIG_ID = 'virtual:formkit-config'
10+ const FORMKIT_PROVIDER_IMPORT_STATEMENT = [
11+ `import { FormKitProvider } from "@formkit/vue";` ,
12+ `import __formkitConfig from "${ FORMKIT_CONFIG_ID } ";` ,
13+ ] . join ( '\n' )
14+ /**
15+ * A relatively cheap, albeit not foolproof, regex to determine if the code
16+ * being processed contains FormKit usage.
17+ */
18+ const CONTAINS_FORMKIT_RE = / < F o r m K i t | < f o r m - k i t /
19+
920function getRootBlock (
1021 root : RootNode ,
1122 block : 'template' | 'script' | 'style' ,
@@ -53,7 +64,7 @@ function langAttr(node?: ElementNode): string {
5364}
5465
5566/**
56- * Imports `FormKitLazyProvider ` component into the script block of the SFC.
67+ * Imports `FormKitProvider ` component into the script block of the SFC.
5768 * @param code - The SFC source code.
5869 * @param id - The ID of the SFC file.
5970 */
@@ -67,31 +78,33 @@ function injectProviderImport(code: string): string {
6778 return code
6879 }
6980 const script = getRootBlock ( root , 'script' )
70- const importStatement = `import { FormKitLazyProvider } from '@formkit/vue'`
7181 const setupScript = root . children . find (
72- ( node ) => node . type === 1 && node . tag === 'script' && isSetupScript ( node ) ,
73- ) as ElementNode | undefined
82+ ( node ) : node is ElementNode =>
83+ node . type === 1 && node . tag === 'script' && isSetupScript ( node ) ,
84+ )
7485 if ( ! setupScript ) {
75- return `<script setup${ langAttr ( script ) } >${ importStatement } </script>
76- ${ code } `
86+ return [
87+ `<script setup${ langAttr ( script ) } >` ,
88+ FORMKIT_PROVIDER_IMPORT_STATEMENT ,
89+ `</script>` ,
90+ code ,
91+ ] . join ( '\n' )
7792 }
7893 const startAt = setupScript . children [ 0 ] . loc . start . offset
7994 const before = code . substring ( 0 , startAt )
8095 const after = code . substring ( startAt )
81- return `${ before } \n${ importStatement } ${ after } `
96+ return `${ before } \n${ FORMKIT_PROVIDER_IMPORT_STATEMENT } ${ after } `
8297}
8398
8499/**
85- * Injects the `<FormKitLazyProvider >` component import into the SFC.
100+ * Injects the `<FormKitProvider >` component import into the SFC.
86101 * @param code - The SFC source code.
87102 * @param id - The ID of the SFC file.
88103 */
89104function injectProviderComponent (
90105 code : string ,
91106 id : string ,
92- config ?: boolean ,
93- defaultConfig ?: boolean ,
94- ) : { code : string ; map ?: any } {
107+ ) : { code : string ; map ?: null } {
95108 let root : RootNode
96109 try {
97110 root = parse ( code )
@@ -100,24 +113,25 @@ function injectProviderComponent(
100113 console . error ( err )
101114 return { code }
102115 }
103- const open = `<FormKitLazyProvider${ config ? ' config-file="true"' : '' } ${
104- defaultConfig ? '' : ' :default-config="false"'
105- } >`
106- const close = '</FormKitLazyProvider>'
107116 const template = getRootBlock ( root , 'template' )
108117 if ( ! template ) {
109118 console . warn (
110- `No <template> block found in ${ id } . Skipping FormKitLazyProvider injection.` ,
119+ `No <template> block found in ${ id } . Skipping FormKitProvider injection.` ,
111120 )
112121 return { code, map : null }
113122 }
114123 const startInsertAt = template . children [ 0 ] . loc . start . offset
115124 const endInsertAt =
116125 template . children [ template . children . length - 1 ] . loc . end . offset
117- const before = code . substring ( 0 , startInsertAt )
118- const content = code . substring ( startInsertAt , endInsertAt )
119- const after = code . substring ( endInsertAt )
120- code = `${ before } \n${ open } \n${ content } \n${ close } \n${ after } `
126+
127+ code = [
128+ code . substring ( 0 , startInsertAt ) ,
129+ `<FormKitProvider :config="__formkitConfig">` ,
130+ code . substring ( startInsertAt , endInsertAt ) ,
131+ '</FormKitProvider>' ,
132+ code . substring ( endInsertAt ) ,
133+ ] . join ( '\n' )
134+
121135 return { code, map : null }
122136}
123137
@@ -140,80 +154,59 @@ function resolveConfig(configFile: string): string | undefined {
140154 return paths . find ( ( path ) => existsSync ( path ) )
141155}
142156
143- /**
144- * A relatively cheap, albeit not foolproof, regex to determine if the code
145- * being processed contains FormKit usage.
146- */
147- const CONTAINS_FORMKIT_RE = / < F o r m K i t | < f o r m - k i t /
148-
149- /**
150- * A regex to find the @__formkit_config__ comment in the code.
151- */
152- const FORMKIT_CONFIG_RE =
153- / ( \/ \* \s ? @ _ _ f o r m k i t \. c o n f i g \. t s _ _ \s ? \* \/ (?: .| \n ) + ?) \) / g
154-
155157export const unpluginFactory : UnpluginFactory < Options | undefined > = (
156158 options = {
157159 configFile : './formkit.config' ,
158160 defaultConfig : true ,
159161 } ,
160162) => {
161- const configPath = resolveConfig ( options . configFile || './formkit.config' )
162-
163163 return {
164164 name : 'unplugin-formkit' ,
165165 enforce : 'pre' ,
166- vite : {
167- config ( ) {
168- return {
169- optimizeDeps : {
170- exclude : [ '@formkit/vue' ] ,
171- } ,
166+
167+ resolveId ( id ) {
168+ if ( id === FORMKIT_CONFIG_ID ) {
169+ return id
170+ }
171+ } ,
172+
173+ load ( id ) {
174+ if ( id === FORMKIT_CONFIG_ID ) {
175+ // Resolve FormKit configuration file path on-demand in case user has created/removed it since plugin was initialized.
176+ const configPath = resolveConfig (
177+ options . configFile || './formkit.config' ,
178+ )
179+ const customConfigDefinition = configPath
180+ ? [
181+ `import _config from "${ configPath } ";` ,
182+ `const config = typeof _config === 'function' ? _config() : _config;` ,
183+ ] . join ( '\n' )
184+ : 'const config = {};'
185+
186+ if ( options . defaultConfig !== false ) {
187+ return [
188+ `import { defaultConfig } from "@formkit/vue";` ,
189+ customConfigDefinition ,
190+ `export default defaultConfig(config);` ,
191+ ] . join ( '\n' )
172192 }
173- } ,
193+
194+ return [ customConfigDefinition , `export default config;` ] . join ( '\n' )
195+ }
174196 } ,
197+
175198 // webpack's id filter is outside of loader logic,
176199 // an additional hook is needed for better perf on webpack
177- transformInclude ( ) {
178- // TODO: resolve why @formkit /vue is not always identifiable by the id
179- // and remove this early return workaround:
180- return true
181- // return (
182- // id.endsWith('.vue') ||
183- // id.includes('@formkit/vue') ||
184- // id.includes('@formkit_vue') ||
185- // id.endsWith('packages/vue/dist/index.mjs')
186- // )
200+ transformInclude ( id ) {
201+ return id . endsWith ( '.vue' )
187202 } ,
188203
189204 // just like rollup transform
190205 async transform ( code , id ) {
191- // Replace all instances of `/* @__formkit_config__ */` in the code
192- // with the resolved path to the formkit.config.{ts,js,mjs} file.
193- if ( configPath && FORMKIT_CONFIG_RE . test ( code ) ) {
194- code = code . replace ( FORMKIT_CONFIG_RE , `"${ configPath } ")` )
195- if ( options . defaultConfig === false ) {
196- // If the user has explicitly disabled the default config, we need
197- // to remove the defaultConfig from the FormKitConfigLoader. We can
198- // do this by cutting the /* @__default -config__ */ comment area.
199- code = code . replace (
200- / \/ \* @ _ _ d e f a u l t - c o n f i g _ _ \* \/ (?: .| \n ) + ?\/ \* @ _ _ d e f a u l t - c o n f i g _ _ \* \/ / gi,
201- '' ,
202- )
203- }
204- // Parse the modified code using recast and return the code with a sourcemap.
205- return { code, map : null }
206- }
207206 // Test if the given code is a likely candidate for FormKit usage.
208207 if ( id . endsWith ( '.vue' ) && CONTAINS_FORMKIT_RE . test ( code ) ) {
209- return injectProviderComponent (
210- injectProviderImport ( code ) ,
211- id ,
212- ! ! configPath ,
213- options . defaultConfig ,
214- )
208+ return injectProviderComponent ( injectProviderImport ( code ) , id )
215209 }
216- return
217210 } ,
218211 }
219212}
0 commit comments