Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,051 changes: 802 additions & 249 deletions package-lock.json

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions packages/@apphosting/adapter-nextjs/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@apphosting/adapter-nextjs",
"version": "14.0.18",
"version": "15.0.0",
"main": "dist/index.js",
"description": "Experimental addon to the Firebase CLI to add web framework support",
"repository": {
Expand All @@ -21,7 +21,7 @@
"type": "module",
"sideEffects": false,
"scripts": {
"build": "rm -rf dist && tsc && chmod +x ./dist/bin/*",
"build": "rm -rf dist && tsc && chmod +x ./dist/bin/* && npx -y esbuild ./dist/index.js --bundle --format=cjs --platform=node --outfile=./dist/index.cjs",
"test": "npm run test:unit && npm run test:functional",
"test:unit": "ts-mocha -p tsconfig.json 'src/**/*.spec.ts' 'src/*.spec.ts'",
"test:functional": "node --loader ts-node/esm ./e2e/run-local.ts",
Expand All @@ -30,7 +30,7 @@
},
"exports": {
".": {
"node": "./dist/index.js",
"node": "./dist/index.cjs",
"default": null
},
"./dist/*": {
Expand Down Expand Up @@ -60,7 +60,7 @@
"@types/mocha": "*",
"@types/tmp": "*",
"mocha": "*",
"next": "~14.0.0",
"next": "15.6.0-canary.54",
"semver": "*",
"tmp": "*",
"ts-mocha": "*",
Expand Down
20 changes: 0 additions & 20 deletions packages/@apphosting/adapter-nextjs/src/bin/build.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,11 @@
}`,
};
generateTestFiles(tmpDir, files);
await generateBuildOutput(

Check failure on line 48 in packages/@apphosting/adapter-nextjs/src/bin/build.spec.ts

View workflow job for this annotation

GitHub Actions / Lint

Replace `⏎······tmpDir,⏎······tmpDir,⏎······outputBundleOptions,⏎······path.join(tmpDir,·".next"),⏎····` with `tmpDir,·tmpDir,·outputBundleOptions,·path.join(tmpDir,·".next")`
tmpDir,
tmpDir,
outputBundleOptions,
path.join(tmpDir, ".next"),
defaultNextVersion,
adapterMetadata,
);
await validateOutputDirectory(outputBundleOptions, path.join(tmpDir, ".next"));

Expand Down Expand Up @@ -116,8 +114,6 @@
serverFilePath: path.join(tmpDir, ".next", "standalone", "apps", "next-app", "server.js"),
},
path.join(tmpDir, ".next"),
defaultNextVersion,
adapterMetadata,
);

const expectedFiles = {
Expand Down Expand Up @@ -149,16 +145,11 @@
}`,
};
generateTestFiles(tmpDir, files);
await generateBuildOutput(

Check failure on line 148 in packages/@apphosting/adapter-nextjs/src/bin/build.spec.ts

View workflow job for this annotation

GitHub Actions / Lint

Replace `⏎······tmpDir,⏎······tmpDir,⏎······outputBundleOptions,⏎······path.join(tmpDir,·".next"),⏎····` with `tmpDir,·tmpDir,·outputBundleOptions,·path.join(tmpDir,·".next")`
tmpDir,
tmpDir,
outputBundleOptions,
path.join(tmpDir, ".next"),
defaultNextVersion,
{
adapterPackageName: "@apphosting/adapter-nextjs",
adapterVersion: "14.0.1",
},
);
assert.rejects(
async () => await validateOutputDirectory(outputBundleOptions, path.join(tmpDir, ".next")),
Expand All @@ -184,8 +175,6 @@
serverFilePath: path.join(standaloneAppPath, "server.js"),
},
path.join(tmpDir, ".next"),
defaultNextVersion,
adapterMetadata,
);

const expectedFiles = {
Expand All @@ -209,13 +198,11 @@
".next/static/staticfile": "",
};
generateTestFiles(tmpDir, files);
await generateBuildOutput(

Check failure on line 201 in packages/@apphosting/adapter-nextjs/src/bin/build.spec.ts

View workflow job for this annotation

GitHub Actions / Lint

Replace `⏎······tmpDir,⏎······tmpDir,⏎······outputBundleOptions,⏎······path.join(tmpDir,·".next"),⏎····` with `tmpDir,·tmpDir,·outputBundleOptions,·path.join(tmpDir,·".next")`
tmpDir,
tmpDir,
outputBundleOptions,
path.join(tmpDir, ".next"),
defaultNextVersion,
adapterMetadata,
);
await validateOutputDirectory(outputBundleOptions, path.join(tmpDir, ".next"));

Expand All @@ -235,16 +222,11 @@
".gitignore": "/.next/",
};
generateTestFiles(tmpDir, files);
await generateBuildOutput(

Check failure on line 225 in packages/@apphosting/adapter-nextjs/src/bin/build.spec.ts

View workflow job for this annotation

GitHub Actions / Lint

Replace `⏎······tmpDir,⏎······tmpDir,⏎······outputBundleOptions,⏎······path.join(tmpDir,·".next"),⏎····` with `tmpDir,·tmpDir,·outputBundleOptions,·path.join(tmpDir,·".next")`
tmpDir,
tmpDir,
outputBundleOptions,
path.join(tmpDir, ".next"),
defaultNextVersion,
{
adapterPackageName: "@apphosting/adapter-nextjs",
adapterVersion: "14.0.1",
},
);
await validateOutputDirectory(outputBundleOptions, path.join(tmpDir, ".next"));

Expand All @@ -271,13 +253,11 @@
}`,
};
generateTestFiles(tmpDir, files);
await generateBuildOutput(

Check failure on line 256 in packages/@apphosting/adapter-nextjs/src/bin/build.spec.ts

View workflow job for this annotation

GitHub Actions / Lint

Replace `⏎······tmpDir,⏎······tmpDir,⏎······outputBundleOptions,⏎······path.join(tmpDir,·".next"),⏎····` with `tmpDir,·tmpDir,·outputBundleOptions,·path.join(tmpDir,·".next")`
tmpDir,
tmpDir,
outputBundleOptions,
path.join(tmpDir, ".next"),
defaultNextVersion,
adapterMetadata,
);
await validateOutputDirectory(outputBundleOptions, path.join(tmpDir, ".next"));

Expand Down
74 changes: 13 additions & 61 deletions packages/@apphosting/adapter-nextjs/src/bin/build.ts
Original file line number Diff line number Diff line change
@@ -1,78 +1,30 @@
#! /usr/bin/env node
import {
loadConfig,
populateOutputBundleOptions,
generateBuildOutput,
validateOutputDirectory,
getAdapterMetadata,
exists,
} from "../utils.js";
import { join } from "path";
import { getBuildOptions, runBuild } from "@apphosting/common";
import {
addRouteOverrides,
overrideNextConfig,
restoreNextConfig,
validateNextConfigOverride,
} from "../overrides.js";
import { generateBuildOutput, getAdapterMetadata, loadConfig, populateOutputBundleOptions, validateOutputDirectory } from "../utils.js";

Check failure on line 3 in packages/@apphosting/adapter-nextjs/src/bin/build.ts

View workflow job for this annotation

GitHub Actions / Lint

'getAdapterMetadata' is defined but never used

Check failure on line 3 in packages/@apphosting/adapter-nextjs/src/bin/build.ts

View workflow job for this annotation

GitHub Actions / Lint

Replace `·generateBuildOutput,·getAdapterMetadata,·loadConfig,·populateOutputBundleOptions,·validateOutputDirectory·` with `⏎··generateBuildOutput,⏎··getAdapterMetadata,⏎··loadConfig,⏎··populateOutputBundleOptions,⏎··validateOutputDirectory,⏎`
import { join } from "node:path";

const root = process.cwd();
const opts = getBuildOptions();

// Set standalone mode
process.env.NEXT_PRIVATE_STANDALONE = "true";
// Opt-out sending telemetry to Vercel
process.env.NEXT_TELEMETRY_DISABLED = "1";

const nextConfig = await loadConfig(root, opts.projectDirectory);
await runBuild();

/**
* Override user's Next Config to optimize the app for Firebase App Hosting
* and validate that the override resulted in a valid config that Next.js can
* load.
*
* We restore the user's Next Config at the end of the build, after the config file has been
* copied over to the output directory, so that the user's original code is not modified.
*
* If the app does not have a next.config.[js|mjs|ts] file in the first place,
* then can skip config override.
*
* Note: loadConfig always returns a fileName (default: next.config.js) even if
* one does not exist in the app's root: https://github.com/vercel/next.js/blob/23681508ca34b66a6ef55965c5eac57de20eb67f/packages/next/src/server/config.ts#L1115
*/
const nextConfigPath = join(root, nextConfig.configFileName);
if (await exists(nextConfigPath)) {
await overrideNextConfig(root, nextConfig.configFileName);
await validateNextConfigOverride(root, opts.projectDirectory, nextConfig.configFileName);
}
const opts = getBuildOptions();
const root = process.cwd();

try {
await runBuild();
const nextConfig = await loadConfig(root, opts.projectDirectory);

const adapterMetadata = getAdapterMetadata();
const nextBuildDirectory = join(opts.projectDirectory, nextConfig.distDir);
const outputBundleOptions = populateOutputBundleOptions(
const nextBuildDirectory = join(opts.projectDirectory, nextConfig.distDir);
const outputBundleOptions = populateOutputBundleOptions(
root,

Check failure on line 18 in packages/@apphosting/adapter-nextjs/src/bin/build.ts

View workflow job for this annotation

GitHub Actions / Lint

Delete `··`
opts.projectDirectory,

Check failure on line 19 in packages/@apphosting/adapter-nextjs/src/bin/build.ts

View workflow job for this annotation

GitHub Actions / Lint

Delete `··`
nextBuildDirectory,

Check failure on line 20 in packages/@apphosting/adapter-nextjs/src/bin/build.ts

View workflow job for this annotation

GitHub Actions / Lint

Delete `··`
);
);

await addRouteOverrides(
outputBundleOptions.outputDirectoryAppPath,
nextConfig.distDir,
adapterMetadata,
);

const nextjsVersion = process.env.FRAMEWORK_VERSION || "unspecified";
await generateBuildOutput(
await generateBuildOutput(
root,
opts.projectDirectory,
outputBundleOptions,
nextBuildDirectory,
nextjsVersion,
adapterMetadata,
);
await validateOutputDirectory(outputBundleOptions, nextBuildDirectory);
} finally {
await restoreNextConfig(root, nextConfig.configFileName);
}
);

await validateOutputDirectory(outputBundleOptions, nextBuildDirectory);
73 changes: 72 additions & 1 deletion packages/@apphosting/adapter-nextjs/src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,72 @@
export {};
import { generateBundleYaml, getAdapterMetadata, populateOutputBundleOptions } from "./utils.js";
import type { NextAdapter } from "next";
import { addRouteOverrides } from "./overrides.js";
import { PHASE_PRODUCTION_BUILD } from "./constants.js";

const adapter: NextAdapter = {
name: '@apphosting/adapter-nextjs',
// FEEDBACK: we need to be able to override user-defined config, before defaults injected
// it would be nice if this where a separate phase or callback
async modifyConfig(config, { phase }) {
if (phase === PHASE_PRODUCTION_BUILD) {
return {
...config,
images: {
...(config.images || {}),
...(config.images?.unoptimized === undefined && config.images?.loader === undefined
? { unoptimized: true }
: {}),
},
headers: async () => {
const originalHeaders = config.headers && await config.headers() || [];
const adapterMetadata = getAdapterMetadata();
// TODO add our middleware header OR not... :P
return [
...originalHeaders,
{
source: "/(.*)",
headers: [{
key: "x-fah-adapter",
value: `nextjs-${adapterMetadata.adapterVersion}`,
}],
},
]
},
experimental: {
...(config.experimental || {}),
nodeMiddleware: true,
},
output: 'standalone',
}
}
// TODO override config for production build
return config;
},
// This fires before standalone is bundled, so we need to pick things back up after build in bin/build.ts
// FEEDBACK: can we get a hook after bundle?
async onBuildComplete(context) {

const nextBuildDirectory = context.distDir;

if (context.outputs.middleware?.config?.matchers) {
await addRouteOverrides(nextBuildDirectory, context.outputs.middleware.config.matchers);
}

const outputBundleOptions = populateOutputBundleOptions(
context.repoRoot,
context.projectDir,
nextBuildDirectory,
);

const adapterMetadata = getAdapterMetadata();

const root = process.cwd();

const nextjsVersion = process.env.FRAMEWORK_VERSION || context.nextVersion || "unspecified";

await generateBundleYaml(outputBundleOptions, root, nextjsVersion, adapterMetadata);

},
};

module.exports = adapter;
Loading
Loading