1
- import { parse as parseAST } from "@babel/parser" ;
2
1
import path from "path" ;
3
2
import chalk from "chalk" ;
4
3
import esbuild from "esbuild" ;
4
+ import { parse as parseAST } from "@babel/parser" ;
5
+ import { Identifier , ImportSpecifier } from "@babel/types" ;
6
+ import * as Sentry from "@sentry/node" ;
5
7
import { Filesystem } from "./fs.js" ;
6
8
import { Context , logFailure , logWarning } from "./context.js" ;
7
9
import { wasmPlugin } from "./wasm.js" ;
@@ -16,20 +18,22 @@ export type { Filesystem } from "./fs.js";
16
18
17
19
export const actionsDir = "actions" ;
18
20
19
- // Returns a generator of { isDir, path } for all paths
21
+ // Returns a generator of { isDir, path, depth } for all paths
20
22
// within dirPath in some topological order (not including
21
23
// dirPath itself).
22
24
export function * walkDir (
23
25
fs : Filesystem ,
24
26
dirPath : string ,
25
- ) : Generator < { isDir : boolean ; path : string } , void , void > {
27
+ depth ?: number ,
28
+ ) : Generator < { isDir : boolean ; path : string ; depth : number } , void , void > {
29
+ depth = depth ?? 0 ;
26
30
for ( const dirEntry of fs . listDir ( dirPath ) . sort ( ) ) {
27
31
const childPath = path . join ( dirPath , dirEntry . name ) ;
28
32
if ( dirEntry . isDirectory ( ) ) {
29
- yield { isDir : true , path : childPath } ;
30
- yield * walkDir ( fs , childPath ) ;
33
+ yield { isDir : true , path : childPath , depth } ;
34
+ yield * walkDir ( fs , childPath , depth + 1 ) ;
31
35
} else if ( dirEntry . isFile ( ) ) {
32
- yield { isDir : false , path : childPath } ;
36
+ yield { isDir : false , path : childPath , depth } ;
33
37
}
34
38
}
35
39
}
@@ -259,6 +263,29 @@ export async function bundleAuthConfig(ctx: Context, dir: string) {
259
263
return result . modules ;
260
264
}
261
265
266
+ export async function doesImportConvexHttpRouter ( source : string ) {
267
+ try {
268
+ const ast = parseAST ( source , {
269
+ sourceType : "module" ,
270
+ plugins : [ "typescript" ] ,
271
+ } ) ;
272
+ return ast . program . body . some ( ( node ) => {
273
+ if ( node . type !== "ImportDeclaration" ) return false ;
274
+ return node . specifiers . some ( ( s ) => {
275
+ const specifier = s as ImportSpecifier ;
276
+ const imported = specifier . imported as Identifier ;
277
+ return imported . name === "httpRouter" ;
278
+ } ) ;
279
+ } ) ;
280
+ } catch {
281
+ return (
282
+ source . match (
283
+ / i m p o r t \s * \{ \s * h t t p R o u t e r .* \} \s * f r o m \s * " \s * c o n v e x \/ s e r v e r \s * " / ,
284
+ ) !== null
285
+ ) ;
286
+ }
287
+ }
288
+
262
289
export async function entryPoints (
263
290
ctx : Context ,
264
291
dir : string ,
@@ -272,20 +299,39 @@ export async function entryPoints(
272
299
}
273
300
} ;
274
301
275
- for ( const { isDir, path : fpath } of walkDir ( ctx . fs , dir ) ) {
302
+ for ( const { isDir, path : fpath , depth } of walkDir ( ctx . fs , dir ) ) {
276
303
if ( isDir ) {
277
304
continue ;
278
305
}
279
306
const relPath = path . relative ( dir , fpath ) ;
280
- const base = path . parse ( fpath ) . base ;
307
+ const parsedPath = path . parse ( fpath ) ;
308
+ const base = parsedPath . base ;
309
+ const extension = parsedPath . ext . toLowerCase ( ) ;
281
310
282
311
if ( relPath . startsWith ( "_deps" + path . sep ) ) {
283
312
logFailure (
284
313
ctx ,
285
314
`The path "${ fpath } " is within the "_deps" directory, which is reserved for dependencies. Please move your code to another directory.` ,
286
315
) ;
287
316
return await ctx . crash ( 1 , "invalid filesystem data" ) ;
288
- } else if ( relPath . startsWith ( "_generated" + path . sep ) ) {
317
+ }
318
+
319
+ if ( depth === 0 && base . toLowerCase ( ) . startsWith ( "https." ) ) {
320
+ const source = ctx . fs . readUtf8File ( fpath ) ;
321
+ if ( await doesImportConvexHttpRouter ( source ) )
322
+ logWarning (
323
+ ctx ,
324
+ chalk . yellow (
325
+ `Found ${ fpath } . HTTP action routes will not be imported from this file. Did you mean to include http${ extension } ?` ,
326
+ ) ,
327
+ ) ;
328
+ Sentry . captureMessage (
329
+ `User code top level directory contains file ${ base } which imports httpRouter.` ,
330
+ "warning" ,
331
+ ) ;
332
+ }
333
+
334
+ if ( relPath . startsWith ( "_generated" + path . sep ) ) {
289
335
log ( chalk . yellow ( `Skipping ${ fpath } ` ) ) ;
290
336
} else if ( base . startsWith ( "." ) ) {
291
337
log ( chalk . yellow ( `Skipping dotfile ${ fpath } ` ) ) ;
0 commit comments