diff --git a/src/index.ts b/src/index.ts
index 883d65d..89d9577 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -5,12 +5,13 @@ import { resolve } from 'pathe'
import { existsSync } from 'fs'
import { parse } from '@vue/compiler-dom'
import type { RootNode, ElementNode, AttributeNode } from '@vue/compiler-dom'
+import MagicString from 'magic-string'
const FORMKIT_CONFIG_ID = 'virtual:formkit-config'
-const FORMKIT_PROVIDER_IMPORT_STATEMENT = [
- `import { FormKitProvider } from "@formkit/vue";`,
- `import __formkitConfig from "${FORMKIT_CONFIG_ID}";`,
-].join('\n')
+const FORMKIT_PROVIDER_IMPORT_STATEMENT = `
+import { FormKitProvider } from "@formkit/vue";
+import __formkitConfig from "${FORMKIT_CONFIG_ID}";
+`
/**
* A relatively cheap, albeit not foolproof, regex to determine if the code
* being processed contains FormKit usage.
@@ -67,15 +68,19 @@ function langAttr(node?: ElementNode): string {
* Imports `FormKitProvider` component into the script block of the SFC.
* @param code - The SFC source code.
* @param id - The ID of the SFC file.
+ * @param s - A MagicString instance, for tracking sourcemaps.
*/
-function injectProviderImport(code: string): string {
+function injectProviderImport(
+ code: string,
+ s = new MagicString(code),
+): MagicString | undefined {
let root: RootNode
try {
root = parse(code)
} catch (err) {
console.warn('Failed to parse SFC:', code)
console.error(err)
- return code
+ return
}
const script = getRootBlock(root, 'script')
const setupScript = root.children.find(
@@ -83,56 +88,48 @@ function injectProviderImport(code: string): string {
node.type === 1 && node.tag === 'script' && isSetupScript(node),
)
if (!setupScript) {
- return [
- ``,
- code,
- ].join('\n')
+ const block = `\n`
+ return s.prepend(block)
}
+
const startAt = setupScript.children[0].loc.start.offset
- const before = code.substring(0, startAt)
- const after = code.substring(startAt)
- return `${before}\n${FORMKIT_PROVIDER_IMPORT_STATEMENT}${after}`
+ return s.appendLeft(startAt, FORMKIT_PROVIDER_IMPORT_STATEMENT)
}
/**
* Injects the `` component import into the SFC.
* @param code - The SFC source code.
* @param id - The ID of the SFC file.
+ * @param s - A MagicString instance, for tracking sourcemaps.
*/
function injectProviderComponent(
code: string,
id: string,
-): { code: string; map?: null } {
+ s = new MagicString(code),
+): MagicString | undefined {
let root: RootNode
try {
root = parse(code)
} catch (err) {
console.warn('Failed to parse SFC:', code)
console.error(err)
- return { code }
+ return
}
+
const template = getRootBlock(root, 'template')
if (!template) {
console.warn(
`No block found in ${id}. Skipping FormKitProvider injection.`,
)
- return { code, map: null }
+ return
}
+
const startInsertAt = template.children[0].loc.start.offset
const endInsertAt =
template.children[template.children.length - 1].loc.end.offset
- code = [
- code.substring(0, startInsertAt),
- ``,
- code.substring(startInsertAt, endInsertAt),
- '',
- code.substring(endInsertAt),
- ].join('\n')
-
- return { code, map: null }
+ s.appendRight(startInsertAt, ``)
+ s.appendLeft(endInsertAt, '')
}
/**
@@ -158,6 +155,7 @@ export const unpluginFactory: UnpluginFactory = (
options = {
configFile: './formkit.config',
defaultConfig: true,
+ sourcemap: false,
},
) => {
return {
@@ -204,8 +202,25 @@ export const unpluginFactory: UnpluginFactory = (
// just like rollup transform
async transform(code, id) {
// Test if the given code is a likely candidate for FormKit usage.
- if (id.endsWith('.vue') && CONTAINS_FORMKIT_RE.test(code)) {
- return injectProviderComponent(injectProviderImport(code), id)
+ if (!id.endsWith('.vue') || !CONTAINS_FORMKIT_RE.test(code)) {
+ return
+ }
+
+ // Generate a MagicString instance to track changes to code
+ const s = new MagicString(code)
+
+ injectProviderComponent(code, id, s)
+
+ // We can save extra parsing time by not returning anything or adding imports if we haven't added the wrapper
+ if (!s.hasChanged()) {
+ return
+ }
+
+ injectProviderImport(code, s)
+
+ return {
+ code: s.toString(),
+ map: options.sourcemap ? s.generateMap({ hires: true }) : undefined,
}
},
}
diff --git a/src/types.ts b/src/types.ts
index 20c4e17..441d9c4 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -1,4 +1,5 @@
export interface Options {
configFile?: string
defaultConfig?: boolean
+ sourcemap?: boolean
}
diff --git a/test/__snapshots__/index.test.ts.snap b/test/__snapshots__/index.test.ts.snap
index 80f7f5b..0f721ba 100644
--- a/test/__snapshots__/index.test.ts.snap
+++ b/test/__snapshots__/index.test.ts.snap
@@ -28,19 +28,16 @@ exports[`vue file transformations > injects import into script setup block 1`] =
"
-
-
-
-
-
+ />
"
`;
@@ -51,11 +48,7 @@ import __formkitConfig from \\"virtual:formkit-config\\";
-
-
-
-
-
+
"
`;
@@ -64,6 +57,7 @@ exports[`vue file transformations > injects inside root node with full sfc 1`] =
"
-
-
-
-
-
+
"
`;