11import { type PluginObj , transformAsync , types as t } from "@babel/core"
22import type { Path } from "@effect/platform/Path"
33import { Effect , pipe } from "effect"
4- import type { PluginOption } from "vite"
4+ import type { Plugin } from "vite"
55
66import { babelPluginName , componentPathAttributeName , isJsxFile , normalizeModuleId } from "../core/component-path.js"
77import { createJsxTaggerVisitor , type JsxTaggerContext } from "../core/jsx-tagger.js"
@@ -20,9 +20,11 @@ export type ComponentTaggerOptions = {
2020
2121type BabelTransformResult = Awaited < ReturnType < typeof transformAsync > >
2222
23+ type BabelSourceMap = NonNullable < NonNullable < BabelTransformResult > [ "map" ] >
24+
2325type ViteTransformResult = {
2426 readonly code : string
25- readonly map : NonNullable < BabelTransformResult > [ "map" ] | null
27+ readonly map : string | null
2628}
2729
2830class ComponentTaggerError extends Error {
@@ -33,16 +35,35 @@ class ComponentTaggerError extends Error {
3335 }
3436}
3537
36- const toViteResult = ( result : BabelTransformResult ) : ViteTransformResult | null => {
37- if ( result === null || result . code === null || result . code === undefined ) {
38+ const toSourceMapInput = ( map : BabelSourceMap | null | undefined ) : string | null => {
39+ if ( map === null || map === undefined ) {
3840 return null
3941 }
4042
43+ return JSON . stringify ( {
44+ file : map . file ,
45+ mappings : map . mappings ,
46+ names : map . names ,
47+ sources : map . sources ,
48+ version : map . version ,
49+ ...( map . sourceRoot === undefined ? { } : { sourceRoot : map . sourceRoot } ) ,
50+ ...( map . sourcesContent === undefined ? { } : { sourcesContent : map . sourcesContent } )
51+ } )
52+ }
53+
54+ const toViteResult = ( result : BabelTransformResult , originalCode : string ) : ViteTransformResult => {
55+ if ( result === null || result . code === null || result . code === undefined ) {
56+ return {
57+ code : originalCode ,
58+ map : null
59+ }
60+ }
61+
4162 const { code } = result
4263
4364 return {
4465 code,
45- map : result . map ?? null
66+ map : toSourceMapInput ( result . map )
4667 }
4768}
4869
@@ -73,7 +94,7 @@ const makeBabelTagger = (relativeFilename: string, attributeName: string): Plugi
7394 *
7495 * @param code - Source code to transform.
7596 * @param id - Vite module id for the source code.
76- * @returns Vite-compatible transform result or null when no output is produced .
97+ * @returns Vite-compatible transform result.
7798 *
7899 * @pure false
79100 * @effect Babel transform
@@ -85,17 +106,17 @@ const makeBabelTagger = (relativeFilename: string, attributeName: string): Plugi
85106// QUOTE(TZ): "Сам компонент должен быть в текущем app но вот что бы его протестировать надо создать ещё один проект который наш текущий апп будет подключать"
86107// REF: user-2026-01-14-frontend-consumer
87108// SOURCE: n/a
88- // FORMAT THEOREM: forall c in Code: transform(c) = r -> r is tagged or null
109+ // FORMAT THEOREM: forall c in Code: transform(c) = r -> r is tagged
89110// PURITY: SHELL
90- // EFFECT: Effect<ViteTransformResult | null , ComponentTaggerError, never>
111+ // EFFECT: Effect<ViteTransformResult, ComponentTaggerError, never>
91112// INVARIANT: errors are surfaced as ComponentTaggerError only
92113// COMPLEXITY: O(n)/O(1)
93114const runTransform = (
94115 code : string ,
95116 id : string ,
96117 rootDir : string ,
97118 attributeName : string
98- ) : Effect . Effect < ViteTransformResult | null , ComponentTaggerError , Path > => {
119+ ) : Effect . Effect < ViteTransformResult , ComponentTaggerError , Path > => {
99120 const cleanId = normalizeModuleId ( id )
100121
101122 return pipe (
@@ -120,15 +141,15 @@ const runTransform = (
120141 }
121142 } )
122143 ) ,
123- Effect . map ( ( result ) => toViteResult ( result ) )
144+ Effect . map ( ( result ) => toViteResult ( result , code ) )
124145 )
125146}
126147
127148/**
128149 * Creates a Vite plugin that injects a single component-path data attribute.
129150 *
130151 * @param options - Configuration options for the plugin.
131- * @returns Vite PluginOption for pre-transform tagging.
152+ * @returns Vite plugin for pre-transform tagging.
132153 *
133154 * @pure false
134155 * @effect Babel transform through Effect
@@ -143,26 +164,28 @@ const runTransform = (
143164// SOURCE: n/a
144165// FORMAT THEOREM: forall id: isJsxFile(id) -> transform(id) adds specified attribute
145166// PURITY: SHELL
146- // EFFECT: Effect<ViteTransformResult | null , ComponentTaggerError, never>
167+ // EFFECT: Effect<ViteTransformResult, ComponentTaggerError, never>
147168// INVARIANT: no duplicate attributes with the same name
148169// COMPLEXITY: O(n)/O(1)
149- export const componentTagger = ( options ?: ComponentTaggerOptions ) : PluginOption => {
170+ export const componentTagger = ( options ?: ComponentTaggerOptions ) : Plugin => {
150171 const attributeName = options ?. attributeName ?? componentPathAttributeName
151172 let resolvedRoot = process . cwd ( )
152173
153- return {
174+ const plugin : Plugin = {
154175 name : "component-path-tagger" ,
155176 enforce : "pre" ,
156177 apply : "serve" ,
157178 configResolved ( config ) {
158179 resolvedRoot = config . root
159180 } ,
160- transform ( code , id ) {
181+ transform ( code , id , _options ) {
161182 if ( ! isJsxFile ( id ) ) {
162183 return null
163184 }
164185
165186 return Effect . runPromise ( pipe ( runTransform ( code , id , resolvedRoot , attributeName ) , Effect . provide ( NodePathLayer ) ) )
166187 }
167188 }
189+
190+ return plugin
168191}
0 commit comments