diff --git a/packages/config/src/types.ts b/packages/config/src/types.ts index 9095200c..688c36d7 100644 --- a/packages/config/src/types.ts +++ b/packages/config/src/types.ts @@ -543,6 +543,7 @@ export interface BundlerSetup { export interface BuildContext { production: boolean; handler: BuildEntryPoint; + skipProjectBuild?: boolean; } export type PresetMetadata = { diff --git a/packages/presets/src/index.ts b/packages/presets/src/index.ts index 9debfb3b..186df063 100644 --- a/packages/presets/src/index.ts +++ b/packages/presets/src/index.ts @@ -11,6 +11,7 @@ export * from './presets/javascript'; export * from './presets/jekyll'; export * from './presets/next'; export * from './presets/nuxt'; +export * from './presets/opennextjs'; export * from './presets/preact'; export * from './presets/qwik'; export * from './presets/react'; diff --git a/packages/presets/src/presets/opennextjs/config.ts b/packages/presets/src/presets/opennextjs/config.ts new file mode 100644 index 00000000..b6fdccce --- /dev/null +++ b/packages/presets/src/presets/opennextjs/config.ts @@ -0,0 +1,58 @@ +import type { AzionBuild, AzionConfig } from 'azion/config'; + +const config: AzionConfig = { + build: { + entry: '.open-next/worker.js', + polyfills: true, + bundler: 'esbuild', + preset: 'opennextjs', + } as AzionBuild, + origin: [ + { + name: 'origin-storage-default', + type: 'object_storage', + }, + ], + functions: [ + { + name: 'handler', + path: '.edge/functions/handler.js', + }, + ], + rules: { + request: [ + { + name: 'Set storage origin for all requests _next_static', + match: '^\\/_next\\/static\\/', // starts with '/_next/static/' + behavior: { + setOrigin: { + name: 'origin-storage-default', + type: 'object_storage', + }, + deliver: true, + }, + }, + { + name: 'Deliver Static Assets', + match: '.(css|js|ttf|woff|woff2|pdf|svg|jpg|jpeg|gif|bmp|png|ico|mp4|json)$', + behavior: { + setOrigin: { + name: 'origin-storage-default', + type: 'object_storage', + }, + deliver: true, + }, + }, + { + name: 'Execute Edge Function', + match: '^/', + behavior: { + runFunction: 'handler', + forwardCookies: true, + }, + }, + ], + }, +}; + +export default config; diff --git a/packages/presets/src/presets/opennextjs/index.ts b/packages/presets/src/presets/opennextjs/index.ts new file mode 100644 index 00000000..5495a0b8 --- /dev/null +++ b/packages/presets/src/presets/opennextjs/index.ts @@ -0,0 +1,6 @@ +import type { AzionBuildPreset } from 'azion/config'; +import config from './config'; +import metadata from './metadata'; +import prebuild from './prebuild'; + +export const opennextjs: AzionBuildPreset = { config, metadata, prebuild }; diff --git a/packages/presets/src/presets/opennextjs/metadata.ts b/packages/presets/src/presets/opennextjs/metadata.ts new file mode 100644 index 00000000..c75857ae --- /dev/null +++ b/packages/presets/src/presets/opennextjs/metadata.ts @@ -0,0 +1,7 @@ +import type { PresetMetadata } from 'azion/config'; + +const metadata: PresetMetadata = { + name: 'opennextjs', +}; + +export default metadata; diff --git a/packages/presets/src/presets/opennextjs/prebuild.ts b/packages/presets/src/presets/opennextjs/prebuild.ts new file mode 100644 index 00000000..6b0eb895 --- /dev/null +++ b/packages/presets/src/presets/opennextjs/prebuild.ts @@ -0,0 +1,80 @@ +import { BuildConfiguration, BuildContext } from 'azion/config'; +import { exec } from 'azion/utils/node'; +import { readFile } from 'fs/promises'; +import path from 'path'; + +/** + * Runs custom prebuild actions for OpenNextjs + */ +async function prebuild(buildConfig: BuildConfiguration, ctx: BuildContext): Promise { + const pkgOpenNextjsName = '@aziontech/opennextjs-azion'; + const openNextjsCommand = 'npm exec opennextjs-azion'; + + // Check if the OpenNextjs Azion is installed + const isOpenNextjsInstalled = await checkIfOpenNextjsIsInstalled(); + if (!isOpenNextjsInstalled) { + const packageManager = await indentifyPackageManager(); + const execCommand = + packageManager === 'yarn' + ? `yarn add -D ${pkgOpenNextjsName}` + : packageManager === 'pnpm' + ? `pnpm add -D ${pkgOpenNextjsName}` + : `npm i -D ${pkgOpenNextjsName}`; + await exec(execCommand, { + scope: 'OpenNextjs', + verbose: true, + interactive: true, + }); + } + // Run OpenNextjs command build + if (ctx.production || !ctx.skipProjectBuild) { + const skipBuild = ctx.skipProjectBuild ? '--skipBuild' : ''; + await exec(`${openNextjsCommand} build -- ${skipBuild}`, { + scope: 'OpenNextjs', + verbose: true, + interactive: true, + }); + } + + // Run OpenNextjs commands to populate assets + if (ctx.production) { + await exec(`${openNextjsCommand} populateAssets`, { + scope: 'OpenNextjs', + verbose: true, + }); + } + + // Run OpenNextjs commands to populate cache + if (ctx.production) { + await exec(`${openNextjsCommand} populateCache`, { + scope: 'OpenNextjs', + verbose: true, + }); + } +} + +async function checkIfOpenNextjsIsInstalled(): Promise { + const packageJsonPath = path.resolve(process.cwd(), 'package.json'); + const packageJson = await readFile(packageJsonPath, 'utf-8'); + const packageJsonObj = JSON.parse(packageJson); + if (!packageJsonObj.devDependencies || !packageJsonObj.devDependencies['@aziontech/opennextjs-azion']) { + return false; + } + return true; +} + +async function indentifyPackageManager(): Promise { + const lockFiles = ['package-lock.json', 'yarn.lock', 'pnpm-lock.yaml']; + for (const lockFile of lockFiles) { + const filePath = path.resolve(process.cwd(), lockFile); + try { + await readFile(filePath, 'utf-8'); + return lockFile.includes('yarn') ? 'yarn' : lockFile.includes('pnpm') ? 'pnpm' : 'npm'; + } catch (error) { + // File not found, continue to the next one + } + } + return 'npm'; // Default to npm if no lock file is found +} + +export default prebuild;