@@ -412,17 +412,26 @@ export class ActionRunner {
412412 } ;
413413
414414 // START deploy tool call
415- // /
416- // /
417- // codegen `convex typecheck` includes typecheck of convex/ dir
418- // + typecheck
419- // |
420- // |
421- // app typecheck `tsc --noEmit --project tsconfig.app.json
422- // \
423- // \
415+ // / \
416+ // / \
417+ // codegen \ `convex typecheck` includes typecheck of convex/ dir
418+ // + typecheck \
419+ // | ESLint `eslint` must not include rules that check imports
420+ // | /
421+ // app typecheck / `tsc --noEmit --project tsconfig.app.json
422+ // \ /
423+ // \ /
424424 // deploy `deploy` can fail
425425
426+ // ESLint doesn't need to wait for codegen since we don't use rules like plugin-import to validate imports.
427+ const runEslint = async ( ) => {
428+ if ( await hasMatchingEslintConfig ( container ) ) {
429+ // ESLint results don't stream to the terminal
430+ return await run ( [ 'eslint' , 'convex' ] , outputLabels . convexLint ) ;
431+ }
432+ return '' ;
433+ } ;
434+
426435 const runCodegenAndTypecheck = async ( onOutput ?: ( output : string ) => void ) => {
427436 // Convex codegen does a convex directory typecheck, then tsc does a full-project typecheck.
428437 let output = await run ( [ 'convex' , 'codegen' ] , outputLabels . convexTypecheck , onOutput ) ;
@@ -435,9 +444,14 @@ export class ActionRunner {
435444 } ;
436445
437446 const t0 = performance . now ( ) ;
438- result += await runCodegenAndTypecheck ( ( output ) => {
439- this . terminalOutput . set ( output ) ;
440- } ) ;
447+ const [ eslintResult , codegenResult ] = await Promise . all ( [
448+ runEslint ( ) ,
449+ runCodegenAndTypecheck ( ( output ) => {
450+ this . terminalOutput . set ( output ) ;
451+ } ) ,
452+ ] ) ;
453+ result += codegenResult ;
454+ result += eslintResult ;
441455 result += await run ( [ 'convex' , 'dev' , '--once' , '--typecheck=disable' ] , outputLabels . convexDeploy ) ;
442456 const time = performance . now ( ) - t0 ;
443457 logger . info ( 'deploy action finished in' , time ) ;
@@ -513,3 +527,16 @@ function cleanConvexOutput(output: string) {
513527 }
514528 return result ;
515529}
530+
531+ async function hasMatchingEslintConfig ( container : WebContainer ) : Promise < boolean > {
532+ // Only run eslint if the file we expect is present and contains '@convex-dev/eslint-plugin'.
533+ let contents = '' ;
534+ try {
535+ contents = await container . fs . readFile ( 'eslint.config.js' , 'utf-8' ) ;
536+ } catch ( e : any ) {
537+ if ( ! e . message . includes ( 'ENOENT: no such file or directory' ) ) {
538+ throw e ;
539+ }
540+ }
541+ return contents . includes ( '@convex-dev/eslint-plugin' ) ;
542+ }
0 commit comments