in cmd/nodejs/functions_framework/main.go [64:207]
func buildFn(ctx *gcp.Context) error {
if _, ok := os.LookupEnv(env.FunctionSource); ok {
return gcp.UserErrorf("%s is not currently supported for Node.js buildpacks", env.FunctionSource)
}
indexJSExists, err := ctx.FileExists("index.js")
if err != nil {
return err
}
// Function source code should be defined in the "main" field in package.json, index.js or function.js.
// https://cloud.google.com/functions/docs/writing#structuring_source_code
fnFile := "function.js"
if indexJSExists {
fnFile = "index.js"
}
// Determine if the function has dependency on functions-framework.
hasFrameworkDependency := false
pjs, err := nodejs.ReadPackageJSONIfExists(ctx.ApplicationRoot())
if err != nil {
return fmt.Errorf("reading package.json: %w", err)
}
if pjs != nil {
_, hasFrameworkDependency = pjs.Dependencies[functionsFrameworkPackage]
if pjs.Main != "" {
fnFile = pjs.Main
}
}
fnFileExists, err := ctx.FileExists(fnFile)
if err != nil {
return err
}
if !fnFileExists {
return gcp.UserErrorf("%s does not exist", fnFile)
}
yarnPnP, err := usingYarnModuleResolution(ctx)
if err != nil {
return err
}
if yarnPnP && !hasFrameworkDependency {
return gcp.UserErrorf("This project is using Yarn Plug'n'Play but you have not included the Functions Framework in your dependencies. Please add it by running: 'yarn add @google-cloud/functions-framework'.")
}
pnpmLockExists, err := ctx.FileExists(nodejs.PNPMLock)
if err != nil {
return err
}
if pnpmLockExists && !hasFrameworkDependency {
return gcp.UserErrorf("This project is using pnpm but you have not included the Functions Framework in your dependencies. Please add it by running: 'pnpm add @google-cloud/functions-framework'.")
}
// TODO(mattrobertson) remove this check once Nodejs has backported the fix to v16. More info here:
// https://github.com/GoogleCloudPlatform/functions-framework-nodejs/issues/407
if skip, err := nodejs.SkipSyntaxCheck(ctx, fnFile, pjs); err != nil {
return err
} else if !skip {
// Syntax check the function code without executing to prevent run-time errors.
if yarnPnP {
if _, err := ctx.Exec([]string{"yarn", "node", "--check", fnFile}, gcp.WithUserAttribution); err != nil {
return err
}
} else {
if _, err := ctx.Exec([]string{"node", "--check", fnFile}, gcp.WithUserAttribution); err != nil {
return err
}
}
}
l, err := ctx.Layer(layerName, gcp.BuildLayer, gcp.CacheLayer, gcp.LaunchLayer)
if err != nil {
return fmt.Errorf("creating %v layer: %w", layerName, err)
}
// We use the absolute path to the functions-framework executable in order to
// avoid having to add its parent directory to PATH which could cause
// conflicts with user-specified dependencies in the case where the framework
// is not an explicit dependency.
//
// If the function specifies a framework dependency, the executable will be
// in node_modules, where it would have been installed by the preceding
// npm/yarn buildpack. Otherwise, it will be in the layer's node_modules,
// installed below.
ff := filepath.Join(".bin", "functions-framework")
if yarnPnP {
// In order for node module resolution to work in Yarn Plug'n'Play mode, we must invoke yarn to
// start the Functions Framework.
ff = "yarn functions-framework"
cloudfunctions.AddFrameworkVersionLabel(ctx, &cloudfunctions.FrameworkVersionInfo{
Runtime: "nodejs",
Version: "yarn",
})
} else if hasFrameworkDependency {
ctx.Logf("Handling functions with dependency on functions-framework.")
if err := ctx.ClearLayer(l); err != nil {
return fmt.Errorf("clearing layer %q: %w", l.Name, err)
}
ff = filepath.Join("node_modules", ff)
addFrameworkVersionLabel(ctx, functionsFrameworkNodeModulePath, false)
} else {
ctx.Logf("Handling functions without dependency on functions-framework.")
if err := cloudfunctions.AssertFrameworkInjectionAllowed(); err != nil {
return err
}
if err := installFunctionsFramework(ctx, l); err != nil {
vendorError := ""
if nodejs.IsUsingVendoredDependencies() {
vendorError = "Vendored dependencies detected, please make sure you have functions-framework installed locally to avoid the installation error by following: https://github.com/GoogleCloudPlatform/functions-framework-nodejs#installation."
}
return fmt.Errorf("%s installing functions-framework: %w", vendorError, err)
}
ff = filepath.Join(l.Path, "node_modules", ff)
addFrameworkVersionLabel(ctx, filepath.Join(l.Path, functionsFrameworkNodeModulePath), true)
nm := filepath.Join(ctx.ApplicationRoot(), "node_modules")
nmExists, err := ctx.FileExists(nm)
if err != nil {
return err
}
// Add user's node_modules to NODE_PATH so functions-framework can always find user's packages.
if nmExists {
l.LaunchEnvironment.Prepend("NODE_PATH", string(os.PathListSeparator), nm)
}
}
// Get and set the valid value for --max-old-space-size node_options.
// Keep the existing behaviour if the value is not provided or invalid
if size, err := getMaxOldSpaceSize(); err != nil {
return err
} else if size > 0 {
l.LaunchEnvironment.Prepend("NODE_OPTIONS", " ", fmt.Sprintf("--max-old-space-size=%d", size))
}
if err := ctx.SetFunctionsEnvVars(l); err != nil {
return err
}
ctx.AddWebProcess([]string{"/bin/bash", "-c", ff})
return nil
}