[cli] Add Variants to Build Output API (#11098)

Makes adjustments to replace `flags` with `variants`.

Also marks the current `flags` implementation as deprecated, as it
should get removed soon. Which I'll do in a follow up PR.
This commit is contained in:
Andy
2024-02-01 10:28:12 +01:00
committed by GitHub
parent 4027a18337
commit 8ba0ce9324
5 changed files with 94 additions and 6 deletions

View File

@@ -0,0 +1,7 @@
---
'@vercel/build-utils': minor
'vercel': minor
'@vercel/next': patch
---
Mark `flags` as deprecated and replace them with `variants`

View File

@@ -440,7 +440,9 @@ export interface Cron {
schedule: string; schedule: string;
} }
// TODO: Proper description once complete /**
* @deprecated Replaced by Variants. Remove once fully replaced.
*/
export interface Flag { export interface Flag {
key: string; key: string;
defaultValue?: unknown; defaultValue?: unknown;
@@ -471,7 +473,9 @@ export interface BuildResultV2Typical {
framework?: { framework?: {
version: string; version: string;
}; };
/** @deprecated Replaced by Variants. Remove once fully replaced. */
flags?: Flag[]; flags?: Flag[];
variants?: Record<string, VariantDefinition>;
} }
export type BuildResultV2 = BuildResultV2Typical | BuildResultBuildOutput; export type BuildResultV2 = BuildResultV2Typical | BuildResultBuildOutput;
@@ -491,3 +495,28 @@ export type ShouldServe = (
export type StartDevServer = ( export type StartDevServer = (
options: StartDevServerOptions options: StartDevServerOptions
) => Promise<StartDevServerResult>; ) => Promise<StartDevServerResult>;
/**
* TODO: The following types will eventually be exported by a more
* relevant package.
*/
type VariantJSONArray = ReadonlyArray<VariantJSONValue>;
type VariantJSONValue =
| string
| boolean
| number
| null
| VariantJSONArray
| { [key: string]: VariantJSONValue };
type VariantOption = {
value: VariantJSONValue;
label?: string;
};
export interface VariantDefinition {
options?: VariantOption[];
url?: string;
description?: string;
}

View File

@@ -22,6 +22,7 @@ import {
Cron, Cron,
validateNpmrc, validateNpmrc,
Flag, Flag,
VariantDefinition,
} from '@vercel/build-utils'; } from '@vercel/build-utils';
import { import {
detectBuilders, detectBuilders,
@@ -95,7 +96,9 @@ interface BuildOutputConfig {
version: string; version: string;
}; };
crons?: Cron[]; crons?: Cron[];
/** @deprecated Replaced by Variants. Remove once fully replaced. */
flags?: Flag[]; flags?: Flag[];
variants?: Record<string, VariantDefinition>;
} }
/** /**
@@ -678,10 +681,13 @@ async function doBuild(
overrides: mergedOverrides, overrides: mergedOverrides,
framework, framework,
crons: mergedCrons, crons: mergedCrons,
/** @deprecated Replaced by Variants. Remove once fully replaced. */
flags: mergedFlags, flags: mergedFlags,
}; };
await fs.writeJSON(join(outputDir, 'config.json'), config, { spaces: 2 }); await fs.writeJSON(join(outputDir, 'config.json'), config, { spaces: 2 });
await writeVariantsJson(client, buildResults.values(), outputDir);
const relOutputDir = relative(cwd, outputDir); const relOutputDir = relative(cwd, outputDir);
output.print( output.print(
`${prependEmoji( `${prependEmoji(
@@ -826,6 +832,51 @@ function mergeFlags(
}); });
} }
/**
* Takes the build output and writes all the variants into the `variants.json`
* file. It'll skip variants that already exist.
*/
async function writeVariantsJson(
{ output }: Client,
buildResults: Iterable<BuildResult | BuildOutputConfig>,
outputDir: string
): Promise<void> {
const variantsFilePath = join(outputDir, 'variants.json');
let hasVariants = true;
const variants = (await fs.readJSON(variantsFilePath).catch(error => {
if (error.code === 'ENOENT') {
hasVariants = false;
return { definitions: {} };
}
throw error;
})) as { definitions: Record<string, VariantDefinition> };
for (const result of buildResults) {
if (!('variants' in result) || !result.variants) continue;
for (const [key, defintion] of Object.entries(result.variants)) {
if (result.variants[key]) {
output.warn(
`The variant "${key}" was found multiple times. Only its first occurrence will be considered.`
);
continue;
}
hasVariants = true;
variants.definitions[key] = defintion;
}
}
// Only create the file when there are variants to write,
// or when the file already exists.
if (hasVariants) {
await fs.writeJSON(variantsFilePath, variants, { spaces: 2 });
}
}
async function writeBuildJson(buildsJson: BuildsManifest, outputDir: string) { async function writeBuildJson(buildsJson: BuildsManifest, outputDir: string) {
await fs.writeJSON(join(outputDir, 'builds.json'), buildsJson, { spaces: 2 }); await fs.writeJSON(join(outputDir, 'builds.json'), buildsJson, { spaces: 2 });
} }

View File

@@ -47,7 +47,7 @@ import {
UnwrapPromise, UnwrapPromise,
getOperationType, getOperationType,
FunctionsConfigManifestV1, FunctionsConfigManifestV1,
VariantsManifest, VariantsManifestLegacy,
RSC_CONTENT_TYPE, RSC_CONTENT_TYPE,
RSC_PREFETCH_SUFFIX, RSC_PREFETCH_SUFFIX,
normalizePrefetches, normalizePrefetches,
@@ -158,7 +158,7 @@ export async function serverBuild({
imagesManifest?: NextImagesManifest; imagesManifest?: NextImagesManifest;
prerenderManifest: NextPrerenderedRoutes; prerenderManifest: NextPrerenderedRoutes;
requiredServerFilesManifest: NextRequiredServerFilesManifest; requiredServerFilesManifest: NextRequiredServerFilesManifest;
variantsManifest: VariantsManifest | null; variantsManifest: VariantsManifestLegacy | null;
}): Promise<BuildResult> { }): Promise<BuildResult> {
lambdaPages = Object.assign({}, lambdaPages, lambdaAppPaths); lambdaPages = Object.assign({}, lambdaPages, lambdaAppPaths);

View File

@@ -3257,7 +3257,8 @@ export function isApiPage(page: string | undefined) {
.match(/(serverless|server)\/pages\/api(\/|\.js$)/); .match(/(serverless|server)\/pages\/api(\/|\.js$)/);
} }
export type VariantsManifest = Record< /** @deprecated */
export type VariantsManifestLegacy = Record<
string, string,
{ {
defaultValue?: unknown; defaultValue?: unknown;
@@ -3268,7 +3269,7 @@ export type VariantsManifest = Record<
export async function getVariantsManifest( export async function getVariantsManifest(
entryPath: string, entryPath: string,
outputDirectory: string outputDirectory: string
): Promise<null | VariantsManifest> { ): Promise<null | VariantsManifestLegacy> {
const pathVariantsManifest = path.join( const pathVariantsManifest = path.join(
entryPath, entryPath,
outputDirectory, outputDirectory,
@@ -3282,7 +3283,7 @@ export async function getVariantsManifest(
if (!hasVariantsManifest) return null; if (!hasVariantsManifest) return null;
const variantsManifest: VariantsManifest = await fs.readJSON( const variantsManifest: VariantsManifestLegacy = await fs.readJSON(
pathVariantsManifest pathVariantsManifest
); );