@@ -6,6 +6,17 @@ import { existsSync } from 'fs'
6
6
import { parse } from '@vue/compiler-dom'
7
7
import type { RootNode , ElementNode , AttributeNode } from '@vue/compiler-dom'
8
8
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
+
9
20
function getRootBlock (
10
21
root : RootNode ,
11
22
block : 'template' | 'script' | 'style' ,
@@ -53,7 +64,7 @@ function langAttr(node?: ElementNode): string {
53
64
}
54
65
55
66
/**
56
- * Imports `FormKitLazyProvider ` component into the script block of the SFC.
67
+ * Imports `FormKitProvider ` component into the script block of the SFC.
57
68
* @param code - The SFC source code.
58
69
* @param id - The ID of the SFC file.
59
70
*/
@@ -67,31 +78,33 @@ function injectProviderImport(code: string): string {
67
78
return code
68
79
}
69
80
const script = getRootBlock ( root , 'script' )
70
- const importStatement = `import { FormKitLazyProvider } from '@formkit/vue'`
71
81
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
+ )
74
85
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' )
77
92
}
78
93
const startAt = setupScript . children [ 0 ] . loc . start . offset
79
94
const before = code . substring ( 0 , startAt )
80
95
const after = code . substring ( startAt )
81
- return `${ before } \n${ importStatement } ${ after } `
96
+ return `${ before } \n${ FORMKIT_PROVIDER_IMPORT_STATEMENT } ${ after } `
82
97
}
83
98
84
99
/**
85
- * Injects the `<FormKitLazyProvider >` component import into the SFC.
100
+ * Injects the `<FormKitProvider >` component import into the SFC.
86
101
* @param code - The SFC source code.
87
102
* @param id - The ID of the SFC file.
88
103
*/
89
104
function injectProviderComponent (
90
105
code : string ,
91
106
id : string ,
92
- config ?: boolean ,
93
- defaultConfig ?: boolean ,
94
- ) : { code : string ; map ?: any } {
107
+ ) : { code : string ; map ?: null } {
95
108
let root : RootNode
96
109
try {
97
110
root = parse ( code )
@@ -100,24 +113,25 @@ function injectProviderComponent(
100
113
console . error ( err )
101
114
return { code }
102
115
}
103
- const open = `<FormKitLazyProvider${ config ? ' config-file="true"' : '' } ${
104
- defaultConfig ? '' : ' :default-config="false"'
105
- } >`
106
- const close = '</FormKitLazyProvider>'
107
116
const template = getRootBlock ( root , 'template' )
108
117
if ( ! template ) {
109
118
console . warn (
110
- `No <template> block found in ${ id } . Skipping FormKitLazyProvider injection.` ,
119
+ `No <template> block found in ${ id } . Skipping FormKitProvider injection.` ,
111
120
)
112
121
return { code, map : null }
113
122
}
114
123
const startInsertAt = template . children [ 0 ] . loc . start . offset
115
124
const endInsertAt =
116
125
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
+
121
135
return { code, map : null }
122
136
}
123
137
@@ -140,80 +154,59 @@ function resolveConfig(configFile: string): string | undefined {
140
154
return paths . find ( ( path ) => existsSync ( path ) )
141
155
}
142
156
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
-
155
157
export const unpluginFactory : UnpluginFactory < Options | undefined > = (
156
158
options = {
157
159
configFile : './formkit.config' ,
158
160
defaultConfig : true ,
159
161
} ,
160
162
) => {
161
- const configPath = resolveConfig ( options . configFile || './formkit.config' )
162
-
163
163
return {
164
164
name : 'unplugin-formkit' ,
165
165
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' )
172
192
}
173
- } ,
193
+
194
+ return [ customConfigDefinition , `export default config;` ] . join ( '\n' )
195
+ }
174
196
} ,
197
+
175
198
// webpack's id filter is outside of loader logic,
176
199
// 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' )
187
202
} ,
188
203
189
204
// just like rollup transform
190
205
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
- }
207
206
// Test if the given code is a likely candidate for FormKit usage.
208
207
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 )
215
209
}
216
- return
217
210
} ,
218
211
}
219
212
}
0 commit comments