typescript - TypeError: (0 , import_preact_render_to_string.renderToString) är inte en funktion


Jag har en mycket grundläggande uppsättning för en Express.js-applikation och försöker lägga till Auth.js till den. När jag gör det startar API:et, men när jag laddar någon väg bortom /api/auth orsakar det nedanstående stackspår och inga loggar i konsolen. Det enda andra tecknet på ett problem är att det omdirigerar till /api/auth/error?error=Configuration

[auth][error] TypeError: (0 , import_preact_render_to_string.renderToString) is not a function
api:dev:at send (/Users/josh/projects/saas-starter-kit/node_modules/@auth/core/lib/pages/index.js:13:350)
api:dev:at Object.error (/Users/josh/projects/saas-starter-kit/node_modules/@auth/core/lib/pages/index.js:119:20)
api:dev:at AuthInternal (/Users/josh/projects/saas-starter-kit/node_modules/@auth/core/lib/index.js:31:31)
api:dev:at Auth (/Users/josh/projects/saas-starter-kit/node_modules/@auth/core/index.js:109:34)
api:dev:at <anonymous> (/Users/josh/projects/saas-starter-kit/node_modules/@auth/express/index.js:141:45)

Hur man reproducerar

tsconfig.json

{
  "compilerOptions": {
    "target": "es2016",                           
    "module": "commonjs",                    
    "outDir": "./dist",                            
    "esModuleInterop": true,         
    "forceConsistentCasingInFileNames": true,  
    "strict": true,
    "skipLibCheck": true 
  }
}

index.ts

import { logger } from '@repo/logger'
import 'dotenv/config'
import { createServer } from './server'

const port = process.env.PORT || 3001
const server = createServer()

server.listen(port, async() => {
    logger.info(`api server is starting: Date: ${new Date()}`)
})

server.ts

import express, { type Express } from 'express'
import morgan from 'morgan'

import { ExpressAuth } from "@auth/express"
import GitHub from "@auth/express/providers/github"

export const createServer = (): Express => {
 const app = express()

 app
  .use(express.urlencoded({ extended: true }))
  .use(express.json())
  .set("trust proxy", true)
  .use("/api/auth/*", ExpressAuth({
    debug: true,
    trustHost: true,
    providers: [
      GitHub
    ],
   }))
    .get('/status', (_, res) => {
     return res.json({ ok: true })
    })


    return app
}

.env.local

AUTH_SECRET=""
AUTH_GITHUB_ID=""
AUTH_GITHUB_SECRET=""
AUTH_URL="http://localhost:3001/api/auth"

kommando för att starta upp

dotenvx run --env-file ./.env.local -- nodemon --exec \"node -r esbuild-register ./src/index.ts\" -e .ts

Förväntat beteende Jag skulle ha förväntat mig att inloggningssidan skulle ladda något istället för ett serverkonsolfelmeddelande.

Svar #1

I mitt fall var samma problem på grund av 'CommonJS'-modulen, som jag använder i mitt projekt. Ärligt talat vet jag inte hur det är relaterat... men min lösning var att använda dynamiska imports, eftersom konfigurera TypeScript blev för svårt för mig. Jag skriver AuthJsDepencencies-loader för att kapsla in dessa detaljer utanför mitt projekt och använder det ungefär så här:

async function main() {
  const app = express()
  const authHandler = await AuthJsDependencies.load()
  app.use('/auth/*', authHandler) 
}

Om intressant - inte det bästa exemplet på asynkron singelton för att ladda beroenden. Viktigt! Det märkligaste var CommonJS + dynamiska imports. Som nämnts här Hur man förhindrar att TypeScript transpilerar dynamiska imports till require()? DET ÄR OMÖJLIGT... men förslaget med eval fungerade för mig.

import type { ExpressAuth } from "@auth/express";
import type Google from "@auth/express/providers/google";
import type SequelizeAdapter from "@auth/sequelize-adapter";

type Target = {
  ExpressAuth: typeof ExpressAuth;
  SequelizeAdapter: typeof SequelizeAdapter;
  Google: typeof Google;
};

export class AuthJsDependencies {
  private static target?: Target;

  public static async load() {
    if (!AuthJsDependencies.target) {
      AuthJsDependencies.target = await AuthJsDependencies._load();
    }

    return this.target!;
  }

  public static async _load() {
    const [
      { ExpressAuth },
      { default: SequelizeAdapter },
      { default: Google },
    ] = await Promise.all([
      eval('import("@auth/express")'),
      eval('import("@auth/sequelize-adapter")'),
      eval('import("@auth/express/providers/google")'),
    ]);

    return {
      ExpressAuth,
      SequelizeAdapter,
      Google,
    } as Target;
  }
}