pkg/nodejs/nextjs.go (49 lines of code) (raw):
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package nodejs
import (
"fmt"
gcp "github.com/GoogleCloudPlatform/buildpacks/pkg/gcpbuildpack"
"github.com/buildpacks/libcnb/v2"
)
var (
// nextJsVersionKey is the metadata key used to store the nextjs build adaptor version in the nextjs layer.
nextJsVersionKey = "version"
// PinnedNextjsAdapterVersion is the version of the nextjs adapter that will be used.
PinnedNextjsAdapterVersion = "14.0.12"
)
// InstallNextJsBuildAdaptor installs the nextjs build adaptor in the given layer if it is not already cached.
func InstallNextJsBuildAdaptor(ctx *gcp.Context, njsl *libcnb.Layer, njsVersion string) error {
layerName := njsl.Name
version, err := detectNextjsAdaptorVersion(njsVersion)
if err != nil {
return err
}
// Check the metadata in the cache layer to determine if we need to proceed.
metaVersion := ctx.GetMetadata(njsl, nextJsVersionKey)
if version == metaVersion {
ctx.CacheHit(layerName)
ctx.Logf("nextjs adaptor cache hit: %q, %q, skipping installation.", version, metaVersion)
} else {
ctx.CacheMiss(layerName)
if err := ctx.ClearLayer(njsl); err != nil {
return fmt.Errorf("clearing layer %q: %w", layerName, err)
}
// Download and install nextjs adaptor in layer.
ctx.Logf("Installing nextjs adaptor %s", version)
if err := downloadNextJsAdaptor(ctx, njsl.Path, version); err != nil {
return gcp.InternalErrorf("downloading nextjs adapter: %w", err)
}
}
// Store layer flags and metadata.
ctx.SetMetadata(njsl, nextJsVersionKey, version)
return nil
}
// detectNextjsAdaptorVersion determines the version of Nextjs that is needed by a nextjs project.
func detectNextjsAdaptorVersion(njsVersion string) (string, error) {
// TODO(b/323280044) account for different versions once development is more stable.
adapterVersion := PinnedNextjsAdapterVersion
return adapterVersion, nil
}
// downloadNextJsAdaptor downloads the Nextjs build adaptor into the provided directory.
func downloadNextJsAdaptor(ctx *gcp.Context, dirPath string, version string) error {
if _, err := ctx.Exec([]string{"npm", "install", "--prefix", dirPath, "@apphosting/adapter-nextjs@" + version}); err != nil {
ctx.Logf("Failed to install nextjs adaptor version: %s. Falling back to latest", version)
if _, err := ctx.Exec([]string{"npm", "install", "--prefix", dirPath, "@apphosting/adapter-nextjs@latest"}); err != nil {
return gcp.InternalErrorf("installing nextjs adaptor: %w", err)
}
}
return nil
}
// OverrideNextjsBuildScript overrides the build script to be the Nextjs build script
func OverrideNextjsBuildScript(njsl *libcnb.Layer) {
njsl.BuildEnvironment.Override(AppHostingBuildEnv, fmt.Sprintf("npm exec --prefix %s apphosting-adapter-nextjs-build", njsl.Path))
}