import type {Compiler, WebpackPluginInstance} from 'webpack' import {NextConfig} from 'next' import path from 'path' import fs from 'fs/promises' import {PathLike} from 'fs' import fetch from 'node-fetch' import {apiUrl} from '@fudge-ai/javascript/dist/config' import {SourceMapUploadBody} from '@fudge-ai/utils' async function getFiles(dir: string): Promise { const dirents = await fs.readdir(dir, {withFileTypes: true}) const files = await Promise.all( dirents.map((dirent) => { const res = path.resolve(dir, dirent.name) return dirent.isDirectory() ? getFiles(res) : res }), ) return Array.prototype.concat(...files) } const uploadSourceMapFile = async (sourceMapPath: string) => { const contentStr = await fs.readFile(sourceMapPath, 'utf8') let content: object try { content = JSON.parse(contentStr) } catch (e) { return } if (!(content as any).file) return const data: SourceMapUploadBody = { content, filePath: (content as any).file, sourceMapOriginPath: sourceMapPath, } const response = await fetch(apiUrl('/sourcemap-upload'), { method: 'PUT', headers: { 'Content-Type': 'application/json', 'x-fudge-token': 'test8394-1627-44c9-848f-38971fcaef34', }, body: JSON.stringify(data), }) if (!response.ok) { console.log('Res:', await response.text()) throw new Error('Failed to upload source map') } } class FudgeWebpackPlugin { warnOnFailure = true async afterAssetEmit(compiler: Compiler) { let outDir = compiler.options.output.path if (!outDir) throw new Error('Missing output path') if (outDir.includes('.next/server/chunks')) { outDir = outDir.replace('/chunks', '') } console.log('Upload source maps from', outDir) const allFilePaths = await getFiles(outDir) console.log('allFilePaths', allFilePaths) const sourceMapPaths = allFilePaths.filter((filePath) => path.basename(filePath).endsWith('.map')) console.log('sourceMapPaths', sourceMapPaths) for (const sourceMapPath of sourceMapPaths) { await uploadSourceMapFile(sourceMapPath) } // console.log('sourceMapFiles', toUpload) // await uploadSourceMaps(this.options) } apply(compiler: Compiler) { compiler.hooks.afterEmit.tapPromise('FudgeWebpackPlugin', async () => { try { await this.afterAssetEmit(compiler) } catch (error) { if (this.warnOnFailure) { console.warn('FudgeWebpackPlugin upload failure', error) } else { throw error } } }) } } export const withFudgeConfig = (nextConfig: NextConfig) => { const originalWebpack = nextConfig.webpack nextConfig.webpack = (webpackConfig, context) => { if (!context.dev) { if (!webpackConfig.devtool) { webpackConfig.devtool = 'hidden-source-map' } webpackConfig.plugins = webpackConfig.plugins || [] webpackConfig.plugins.push(new FudgeWebpackPlugin()) } return originalWebpack?.(webpackConfig, context) ?? webpackConfig } return nextConfig }