Skip to content

Commit b011811

Browse files
Merge pull request #3 from formkit/release/0.3.0
chore: release/0.3.0
2 parents 9f68888 + 8aab083 commit b011811

File tree

4 files changed

+168
-100
lines changed

4 files changed

+168
-100
lines changed

playground/formkit.config.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { defineFormKitConfig } from '@formkit/vue'
2+
3+
export default defineFormKitConfig(() => ({}))

src/index.ts

Lines changed: 67 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,17 @@ import { existsSync } from 'fs'
66
import { parse } from '@vue/compiler-dom'
77
import 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 = /<FormKit|<form-kit/
19+
920
function 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
*/
89104
function 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 = /<FormKit|<form-kit/
148-
149-
/**
150-
* A regex to find the @__formkit_config__ comment in the code.
151-
*/
152-
const FORMKIT_CONFIG_RE =
153-
/(\/\*\s?@__formkit\.config\.ts__\s?\*\/(?:.|\n)+?)\)/g
154-
155157
export 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-
/\/\* @__default-config__ \*\/(?:.|\n)+?\/\* @__default-config__ \*\//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
}

test/__snapshots__/index.test.ts.snap

Lines changed: 60 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,69 @@
11
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
22

3-
exports[`index > injects import into script setup block 1`] = `
3+
exports[`virtual configuration file > generates correct configuration when custom config is available 1`] = `
4+
"import { defaultConfig } from \\"@formkit/vue\\";
5+
import _config from \\"~/playground/formkit.config.ts\\";
6+
const config = typeof _config === 'function' ? _config() : _config;
7+
export default defaultConfig(config);"
8+
`;
9+
10+
exports[`virtual configuration file > generates correct configuration when custom config is available and default config is disabled 1`] = `
11+
"import _config from \\"~/playground/formkit.config.ts\\";
12+
const config = typeof _config === 'function' ? _config() : _config;
13+
export default config;"
14+
`;
15+
16+
exports[`virtual configuration file > generates correct configuration when default config is disabled 1`] = `
17+
"const config = {};
18+
export default config;"
19+
`;
20+
21+
exports[`virtual configuration file > generates correct virtual configuration with default options 1`] = `
22+
"import { defaultConfig } from \\"@formkit/vue\\";
23+
const config = {};
24+
export default defaultConfig(config);"
25+
`;
26+
27+
exports[`vue file transformations > injects import into script setup block 1`] = `
428
"<script setup lang=\\"ts\\">
5-
import { FormKitLazyProvider } from '@formkit/vue'
29+
import { FormKitProvider } from \\"@formkit/vue\\";
30+
import __formkitConfig from \\"virtual:formkit-config\\";
631
import { FormKit } from '@formkit/vue'
732
</script>
833
934
<template>
1035
11-
<FormKitLazyProvider>
36+
<FormKitProvider :config=\\"__formkitConfig\\">
1237
<FormKit
1338
type=\\"text\\"
1439
label=\\"Your name\\"
1540
help=\\"Enter your name\\"
1641
/>
17-
</FormKitLazyProvider>
42+
</FormKitProvider>
1843
1944
</template>"
2045
`;
2146

22-
exports[`index > injects inside root node if there is one 1`] = `
23-
"<script setup>import { FormKitLazyProvider } from '@formkit/vue'</script>
47+
exports[`vue file transformations > injects inside root node if there is one 1`] = `
48+
"<script setup>
49+
import { FormKitProvider } from \\"@formkit/vue\\";
50+
import __formkitConfig from \\"virtual:formkit-config\\";
51+
</script>
2452
<template>
2553
<div class=\\"fizzbuzz\\">
2654
27-
<FormKitLazyProvider>
55+
<FormKitProvider :config=\\"__formkitConfig\\">
2856
<FormKit />
29-
</FormKitLazyProvider>
57+
</FormKitProvider>
3058
3159
</div>
3260
</template>"
3361
`;
3462

35-
exports[`index > injects inside root node with full sfc 1`] = `
63+
exports[`vue file transformations > injects inside root node with full sfc 1`] = `
3664
"<script lang=\\"ts\\" setup>
37-
import { FormKitLazyProvider } from '@formkit/vue'
65+
import { FormKitProvider } from \\"@formkit/vue\\";
66+
import __formkitConfig from \\"virtual:formkit-config\\";
3867
function handleLoginSubmit(values: any) {
3968
window.alert(\\"You are logged in. Credentials:
4069
\\" + JSON.stringify(values));
@@ -44,21 +73,22 @@ function handleLoginSubmit(values: any) {
4473
<template>
4574
<div>
4675
47-
<FormKitLazyProvider>
76+
<FormKitProvider :config=\\"__formkitConfig\\">
4877
<FormKit type=\\"form\\" submit-label=\\"login\\" @submit=\\"handleLoginSubmit\\">
4978
<FormKit type=\\"email\\" label=\\"Email\\" name=\\"email\\" />
5079
<FormKit type=\\"password\\" label=\\"Password\\" name=\\"password\\" />
5180
</FormKit>
52-
</FormKitLazyProvider>
81+
</FormKitProvider>
5382
5483
</div>
5584
</template>
5685
"
5786
`;
5887
59-
exports[`index > injects inside root node with multiple child elements 1`] = `
88+
exports[`vue file transformations > injects inside root node with multiple child elements 1`] = `
6089
"<script lang=\\"ts\\" setup>
61-
import { FormKitLazyProvider } from '@formkit/vue'
90+
import { FormKitProvider } from \\"@formkit/vue\\";
91+
import __formkitConfig from \\"virtual:formkit-config\\";
6292
function handleLoginSubmit(values: any) {
6393
window.alert(\\"You are logged in. Credentials:
6494
\\" + JSON.stringify(values));
@@ -68,7 +98,7 @@ function handleLoginSubmit(values: any) {
6898
<template>
6999
<div>
70100
71-
<FormKitLazyProvider>
101+
<FormKitProvider :config=\\"__formkitConfig\\">
72102
<main>
73103
<p>
74104
<FormKit type=\\"form\\" submit-label=\\"login\\" @submit=\\"handleLoginSubmit\\">
@@ -78,15 +108,18 @@ function handleLoginSubmit(values: any) {
78108
</p>
79109
</main>
80110
<div class=\\"filler\\">Here we go</div>
81-
</FormKitLazyProvider>
111+
</FormKitProvider>
82112
83113
</div>
84114
</template>
85115
"
86116
`;
87117
88-
exports[`index > injects setup block when using options api 1`] = `
89-
"<script setup lang=\\"ts\\">import { FormKitLazyProvider } from '@formkit/vue'</script>
118+
exports[`vue file transformations > injects setup block when using options api 1`] = `
119+
"<script setup lang=\\"ts\\">
120+
import { FormKitProvider } from \\"@formkit/vue\\";
121+
import __formkitConfig from \\"virtual:formkit-config\\";
122+
</script>
90123
<script lang=\\"ts\\">
91124
import { FormKit } from '@formkit/vue'
92125
@@ -100,22 +133,25 @@ export default {
100133
<template>
101134
<div>
102135
103-
<FormKitLazyProvider>
136+
<FormKitProvider :config=\\"__formkitConfig\\">
104137
<h1>Nothing to see here</h1>
105138
<FormKit type=\\"text\\" label=\\"Check me out\\" />
106-
</FormKitLazyProvider>
139+
</FormKitProvider>
107140
108141
</div>
109142
</template>"
110143
`;
111144
112-
exports[`index > injects the template block into an normally structured sfc 1`] = `
113-
"<script setup>import { FormKitLazyProvider } from '@formkit/vue'</script>
145+
exports[`vue file transformations > injects the template block into an normally structured sfc 1`] = `
146+
"<script setup>
147+
import { FormKitProvider } from \\"@formkit/vue\\";
148+
import __formkitConfig from \\"virtual:formkit-config\\";
149+
</script>
114150
<template>
115151
116-
<FormKitLazyProvider>
152+
<FormKitProvider :config=\\"__formkitConfig\\">
117153
<FormKit />
118-
</FormKitLazyProvider>
154+
</FormKitProvider>
119155
120156
</template>"
121157
`;

0 commit comments

Comments
 (0)