@@ -4,10 +4,13 @@ import {
4
4
existsSync ,
5
5
mkdirSync ,
6
6
readdirSync ,
7
- renameSync ,
8
7
rmSync ,
9
8
writeFileSync ,
9
+ copyFileSync ,
10
10
} from 'node:fs' ;
11
+ import os from 'node:os' ;
12
+ import { randomBytes } from 'node:crypto' ;
13
+
11
14
import type { Plugin } from 'vite' ;
12
15
13
16
import { unstable_getPlatformObject } from '../../server.js' ;
@@ -62,24 +65,82 @@ export default {
62
65
};
63
66
` ;
64
67
65
- const getFiles = ( dir : string , files : string [ ] = [ ] ) : string [ ] => {
66
- const entries = readdirSync ( dir , { withFileTypes : true } ) ;
67
- for ( const entry of entries ) {
68
- const fullPath = path . join ( dir , entry . name ) ;
69
- if ( entry . isDirectory ( ) ) {
70
- getFiles ( fullPath , files ) ;
71
- } else {
72
- files . push ( fullPath ) ;
68
+ function copyFiles ( srcDir : string , destDir : string , extensions : string [ ] ) {
69
+ const files = readdirSync ( srcDir , { withFileTypes : true } ) ;
70
+ for ( const file of files ) {
71
+ const srcPath = path . join ( srcDir , file . name ) ;
72
+ const destPath = path . join ( destDir , file . name ) ;
73
+ if ( file . isDirectory ( ) ) {
74
+ mkdirSync ( destPath , { recursive : true } ) ;
75
+ copyFiles ( srcPath , destPath , extensions ) ;
76
+ } else if ( extensions . some ( ( ext ) => file . name . endsWith ( ext ) ) ) {
77
+ copyFileSync ( srcPath , destPath ) ;
73
78
}
74
79
}
75
- return files ;
76
- } ;
80
+ }
77
81
78
- const WORKER_JS_NAME = '_worker.js' ;
79
- const ROUTES_JSON_NAME = '_routes.json' ;
80
- const HEADERS_NAME = '_headers' ;
82
+ function copyDirectory ( srcDir : string , destDir : string ) {
83
+ const files = readdirSync ( srcDir , { withFileTypes : true } ) ;
84
+ for ( const file of files ) {
85
+ const srcPath = path . join ( srcDir , file . name ) ;
86
+ const destPath = path . join ( destDir , file . name ) ;
87
+ if ( file . isDirectory ( ) ) {
88
+ mkdirSync ( destPath , { recursive : true } ) ;
89
+ copyDirectory ( srcPath , destPath ) ;
90
+ } else {
91
+ copyFileSync ( srcPath , destPath ) ;
92
+ }
93
+ }
94
+ }
81
95
82
- type StaticRoutes = { version : number ; include : string [ ] ; exclude : string [ ] } ;
96
+ function separatePublicAssetsFromFunctions ( {
97
+ outDir,
98
+ functionDir,
99
+ assetsDir,
100
+ } : {
101
+ outDir : string ;
102
+ functionDir : string ;
103
+ assetsDir : string ;
104
+ } ) {
105
+ const tempDist = path . join (
106
+ os . tmpdir ( ) ,
107
+ `dist_${ randomBytes ( 16 ) . toString ( 'hex' ) } ` ,
108
+ ) ;
109
+ const tempPublicDir = path . join ( tempDist , DIST_PUBLIC ) ;
110
+ const workerPublicDir = path . join ( functionDir , DIST_PUBLIC ) ;
111
+
112
+ // Create a temp dir to prepare the separated files
113
+ rmSync ( tempDist , { recursive : true , force : true } ) ;
114
+ mkdirSync ( tempDist , { recursive : true } ) ;
115
+
116
+ // Move the current dist dir to the temp dir
117
+ // Folders are copied instead of moved to avoid issues on Windows
118
+ copyDirectory ( outDir , tempDist ) ;
119
+ rmSync ( outDir , { recursive : true , force : true } ) ;
120
+
121
+ // Create empty directories at the desired deploy locations
122
+ // for the function and the assets
123
+ mkdirSync ( functionDir , { recursive : true } ) ;
124
+ mkdirSync ( assetsDir , { recursive : true } ) ;
125
+
126
+ // Move tempDist/public to assetsDir
127
+ copyDirectory ( tempPublicDir , assetsDir ) ;
128
+ rmSync ( tempPublicDir , { recursive : true , force : true } ) ;
129
+
130
+ // Move tempDist to functionDir
131
+ copyDirectory ( tempDist , functionDir ) ;
132
+ rmSync ( tempDist , { recursive : true , force : true } ) ;
133
+
134
+ // Traverse assetsDir and copy specific files to functionDir/public
135
+ mkdirSync ( workerPublicDir , { recursive : true } ) ;
136
+ copyFiles ( assetsDir , workerPublicDir , [
137
+ '.txt' ,
138
+ '.html' ,
139
+ '.json' ,
140
+ '.js' ,
141
+ '.css' ,
142
+ ] ) ;
143
+ }
83
144
84
145
export function deployCloudflarePlugin ( opts : {
85
146
srcDir : string ;
@@ -136,93 +197,18 @@ export function deployCloudflarePlugin(opts: {
136
197
}
137
198
138
199
const outDir = path . join ( rootDir , opts . distDir ) ;
139
-
140
- // Advanced-mode Cloudflare Pages imports _worker.js
141
- // and can be configured with _routes.json to serve other static root files
142
- mkdirSync ( path . join ( outDir , WORKER_JS_NAME ) ) ;
143
- const outPaths = readdirSync ( outDir ) ;
144
- for ( const p of outPaths ) {
145
- if ( p === WORKER_JS_NAME ) {
146
- continue ;
147
- }
148
- renameSync ( path . join ( outDir , p ) , path . join ( outDir , WORKER_JS_NAME , p ) ) ;
149
- }
150
-
151
- const workerEntrypoint = path . join ( outDir , WORKER_JS_NAME , 'index.js' ) ;
152
- if ( ! existsSync ( workerEntrypoint ) ) {
153
- writeFileSync (
154
- workerEntrypoint ,
155
- `
156
- import server from './${ SERVE_JS } '
157
-
158
- export default {
159
- ...server
160
- }
161
- ` ,
162
- ) ;
163
- }
164
-
165
- // Create _routes.json if one doesn't already exist in the public dir
166
- // https://developers.cloudflare.com/pages/functions/routing/#functions-invocation-routes
167
- const routesFile = path . join ( outDir , ROUTES_JSON_NAME ) ;
168
- const publicDir = path . join ( outDir , WORKER_JS_NAME , DIST_PUBLIC ) ;
169
- if ( ! existsSync ( path . join ( publicDir , ROUTES_JSON_NAME ) ) ) {
170
- // exclude strategy
171
- const staticPaths : string [ ] = [ '/assets/*' ] ;
172
- const paths = getFiles ( publicDir ) ;
173
- for ( const p of paths ) {
174
- const basePath = path . dirname ( p . replace ( publicDir , '' ) ) || '/' ;
175
- const name = path . basename ( p ) ;
176
- const entry =
177
- name === 'index.html'
178
- ? basePath + ( basePath !== '/' ? '/' : '' )
179
- : path . join ( basePath , name . replace ( / \. h t m l $ / , '' ) ) ;
180
- if (
181
- entry . startsWith ( '/assets/' ) ||
182
- entry . startsWith ( '/' + WORKER_JS_NAME + '/' ) ||
183
- entry === '/' + WORKER_JS_NAME ||
184
- entry === '/' + ROUTES_JSON_NAME ||
185
- entry === '/' + HEADERS_NAME
186
- ) {
187
- continue ;
188
- }
189
- if ( ! staticPaths . includes ( entry ) ) {
190
- staticPaths . push ( entry ) ;
191
- }
192
- }
193
- const MAX_CLOUDFLARE_RULES = 100 ;
194
- if ( staticPaths . length + 1 > MAX_CLOUDFLARE_RULES ) {
195
- throw new Error (
196
- `The number of static paths exceeds the limit of ${ MAX_CLOUDFLARE_RULES } . ` +
197
- `You need to create a custom ${ ROUTES_JSON_NAME } file in the public folder. ` +
198
- `See https://developers.cloudflare.com/pages/functions/routing/#functions-invocation-routes` ,
199
- ) ;
200
- }
201
- const staticRoutes : StaticRoutes = {
202
- version : 1 ,
203
- include : [ '/*' ] ,
204
- exclude : staticPaths ,
205
- } ;
206
- writeFileSync ( routesFile , JSON . stringify ( staticRoutes ) ) ;
207
- }
208
-
209
- // Move the public files to the root of the dist folder
210
- const publicPaths = readdirSync (
211
- path . join ( outDir , WORKER_JS_NAME , DIST_PUBLIC ) ,
212
- ) ;
213
- for ( const p of publicPaths ) {
214
- renameSync (
215
- path . join ( outDir , WORKER_JS_NAME , DIST_PUBLIC , p ) ,
216
- path . join ( outDir , p ) ,
217
- ) ;
218
- }
219
- rmSync ( path . join ( outDir , WORKER_JS_NAME , DIST_PUBLIC ) , {
220
- recursive : true ,
221
- force : true ,
200
+ const assetsDistDir = path . join ( outDir , 'assets' ) ;
201
+ const workerDistDir = path . join ( outDir , 'worker' ) ;
202
+
203
+ // Move the public static assets to a separate folder from the server files
204
+ separatePublicAssetsFromFunctions ( {
205
+ outDir,
206
+ assetsDir : assetsDistDir ,
207
+ functionDir : workerDistDir ,
222
208
} ) ;
223
209
224
210
appendFileSync (
225
- path . join ( outDir , WORKER_JS_NAME , DIST_ENTRIES_JS ) ,
211
+ path . join ( workerDistDir , DIST_ENTRIES_JS ) ,
226
212
`export const buildData = ${ JSON . stringify ( platformObject . buildData ) } ;` ,
227
213
) ;
228
214
@@ -233,9 +219,18 @@ export default {
233
219
`
234
220
# See https://developers.cloudflare.com/pages/functions/wrangler-configuration/
235
221
name = "waku-project"
236
- compatibility_date = "2024-09-02 "
222
+ compatibility_date = "2024-09-23 "
237
223
compatibility_flags = [ "nodejs_als" ]
238
224
pages_build_output_dir = "./dist"
225
+ main = "./dist/worker/serve-cloudflare.js"
226
+
227
+ assets = {
228
+ directory = "./dist/assets",
229
+ binding = "ASSETS",
230
+ html_handling = "drop-trailing-slash",
231
+ # "single-page-application" | "404-page" | "none"
232
+ not_found_handling = "404-page"
233
+ }
239
234
` ,
240
235
) ;
241
236
}
0 commit comments