@@ -2,6 +2,7 @@ import babel from '@rollup/plugin-babel'
2
2
import { defineConfig } from 'rollup'
3
3
import typescript from 'rollup-plugin-typescript2'
4
4
import packageJson from './package.json' with { type : 'json' }
5
+ import MagicString from 'magic-string'
5
6
6
7
const dependencies = [
7
8
...Object . keys ( packageJson . peerDependencies ?? { } ) ,
@@ -26,9 +27,111 @@ export default defineConfig({
26
27
extensions : [ '.ts' , '.tsx' ] ,
27
28
babelHelpers : 'bundled' ,
28
29
} ) ,
30
+ /**
31
+ * This custom rollup plugin allows us to preserve directives in source
32
+ * code, such as "use client", in order to support React Server Components.
33
+ *
34
+ * The source for this plugin is inspired by:
35
+ * https://github.com/Ephem/rollup-plugin-preserve-directives
36
+ */
37
+ {
38
+ name : 'preserve-directives' ,
39
+ transform ( code ) {
40
+ const ast = this . parse ( code )
41
+ if ( ast . type !== 'Program' || ! ast . body ) {
42
+ return {
43
+ code,
44
+ ast,
45
+ map : null ,
46
+ }
47
+ }
48
+
49
+ let hasClientDirective = false
50
+
51
+ for ( const node of ast . body ) {
52
+ if ( ! node ) {
53
+ continue
54
+ }
55
+
56
+ if ( node . type !== 'ExpressionStatement' ) {
57
+ continue
58
+ }
59
+
60
+ if ( node . directive === 'use client' ) {
61
+ hasClientDirective = true
62
+ break
63
+ }
64
+ }
65
+
66
+ if ( hasClientDirective ) {
67
+ return {
68
+ code,
69
+ ast,
70
+ map : null ,
71
+ meta : {
72
+ hasClientDirective : true ,
73
+ } ,
74
+ }
75
+ }
76
+
77
+ return {
78
+ code,
79
+ ast,
80
+ map : null ,
81
+ }
82
+ } ,
83
+ renderChunk : {
84
+ order : 'post' ,
85
+ handler ( code , chunk , options ) {
86
+ // If `preserveModules` is not set to true, we can't be sure if the client
87
+ // directive corresponds to the whole chunk or just a part of it.
88
+ if ( ! options . preserveModules ) {
89
+ return undefined
90
+ }
91
+
92
+ let chunkHasClientDirective = false
93
+
94
+ for ( const moduleId of Object . keys ( chunk . modules ) ) {
95
+ const hasClientDirective = this . getModuleInfo ( moduleId ) ?. meta ?. hasClientDirective
96
+ if ( hasClientDirective ) {
97
+ chunkHasClientDirective = true
98
+ break
99
+ }
100
+ }
101
+
102
+ if ( chunkHasClientDirective ) {
103
+ const transformed = new MagicString ( code )
104
+ transformed . prepend ( `"use client";\n` )
105
+ const sourcemap = transformed . generateMap ( {
106
+ includeContent : true ,
107
+ } )
108
+ return {
109
+ code : transformed . toString ( ) ,
110
+ map : sourcemap ,
111
+ }
112
+ }
113
+
114
+ return null
115
+ } ,
116
+ } ,
117
+ } ,
29
118
] ,
119
+ onwarn ( warning , defaultHandler ) {
120
+ // Dependencies or modules may use "use client" as an indicator for React
121
+ // Server Components that this module should only be loaded on the client.
122
+ if ( warning . code === 'MODULE_LEVEL_DIRECTIVE' && warning . message . includes ( 'use client' ) ) {
123
+ return
124
+ }
125
+
126
+ if ( warning . code === 'CIRCULAR_DEPENDENCY' ) {
127
+ throw warning
128
+ }
129
+
130
+ defaultHandler ( warning )
131
+ } ,
30
132
output : {
31
133
dir : 'dist' ,
32
134
format : 'esm' ,
135
+ preserveModules : true ,
33
136
} ,
34
137
} )
0 commit comments