[go: nahoru, domu]

Skip to content

Commit

Permalink
more robust entry point logic for nodejs
Browse files Browse the repository at this point in the history
Currently we set the default entry point for nodejs applications to `npm run start` even if the package.json does not define a start script. This commit updates the logic to use fall back to either the "main" script from the package.json or use `node index.js` if neither is available.

fixes #375

PiperOrigin-RevId: 602032240
Change-Id: I9776c9f2f4dca514fb0f3c9dc6ba93ef4ab375bb
  • Loading branch information
matthewrobertson authored and tkiela1 committed Jan 27, 2024
1 parent 189dfb9 commit f6258a2
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 3 deletions.
5 changes: 4 additions & 1 deletion cmd/nodejs/npm/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,10 @@ NOTE: Running the default build script can be skipped by passing the empty envir
el.SharedEnvironment.Default("NODE_ENV", nodejs.NodeEnv())

// Configure the entrypoint for production.
cmd := []string{"npm", "start"}
cmd, err := nodejs.DefaultStartCommand(ctx, pjs)
if err != nil {
return fmt.Errorf("detecting start command: %w", err)
}

if !devmode.Enabled(ctx) {
ctx.AddWebProcess(cmd)
Expand Down
1 change: 1 addition & 0 deletions pkg/nodejs/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ go_test(
embed = [":nodejs"],
rundir = ".",
deps = [
"//security/safeopen",
"//internal/mockprocess",
"//internal/testserver",
"//pkg/env",
Expand Down
2 changes: 0 additions & 2 deletions pkg/nodejs/nodejs.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,6 @@ type packageEnginesJSON struct {
const (
// ScriptBuild is the name of npm build scripts.
ScriptBuild = "build"
// ScriptStart is the name of npm start scripts.
ScriptStart = "start"
// ScriptGCPBuild is the name of "gcp-build" scripts.
ScriptGCPBuild = "gcp-build"
)
Expand Down
27 changes: 27 additions & 0 deletions pkg/nodejs/npm.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,3 +197,30 @@ func IsUsingVendoredDependencies() bool {
func runCommand(pkgTool, command string) string {
return fmt.Sprintf("%s run %s", pkgTool, strings.TrimSpace(command))
}

// DefaultStartCommand return the default command that should be used to configure the application
// web process if the user has not explicitly configured one. The algorithm follows the conventions
// of Nodejs package.json files: https://docs.npmjs.com/cli/v10/configuring-npm/package-json#main
// 1. if script.start is specified return `npm run start`
// 2. if the project contains server.js `npm run start`
// 3. if main is specified `node ${pjs.main}`
// 4. otherwise `node index.js“
func DefaultStartCommand(ctx *gcp.Context, pjs *PackageJSON) ([]string, error) {
if pjs == nil {
return []string{"node", "index.js"}, nil
}
if _, ok := pjs.Scripts["start"]; ok {
return []string{"npm", "run", "start"}, nil
}
exists, err := ctx.FileExists(ctx.ApplicationRoot(), "server.js")
if err != nil {
return nil, err
}
if exists {
return []string{"npm", "run", "start"}, nil
}
if pjs.Main != "" {
return []string{"node", pjs.Main}, nil
}
return []string{"node", "index.js"}, nil
}
82 changes: 82 additions & 0 deletions pkg/nodejs/npm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"encoding/json"
"testing"

"google3/security/safeopen/safeopen"
"github.com/GoogleCloudPlatform/buildpacks/pkg/env"
"github.com/GoogleCloudPlatform/buildpacks/pkg/gcpbuildpack"
"github.com/google/go-cmp/cmp"
Expand Down Expand Up @@ -330,3 +331,84 @@ func TestDetermineBuildCommands(t *testing.T) {
})
}
}

func TestDefaultStartCommand(t *testing.T) {
testsCases := []struct {
name string
pjs string
hasServerJs bool
want []string
}{
{
name: "no package.json",
want: []string{"node", "index.js"},
},
{
name: "no main or start",
pjs: `{
"scripts": {
"build": "tsc --build",
"clean": "tsc --build --clean"
}
}`,
want: []string{"node", "index.js"},
},
{
name: "main and start uses start",
pjs: `{
"main": "main.js",
"scripts": {
"start": "node main.js"
}
}`,
want: []string{"npm", "run", "start"},
},
{
name: "main module",
pjs: `{
"main": "main.js",
"scripts": {
"build": "node main.js"
}
}`,
want: []string{"node", "main.js"},
},
{
name: "main module and serverjs",
pjs: `{
"main": "main.js",
"scripts": {
"build": "node main.js"
}
}`,
hasServerJs: true,
want: []string{"npm", "run", "start"},
},
}
for _, tc := range testsCases {
t.Run(tc.name, func(t *testing.T) {
var pjs *PackageJSON = nil
if tc.pjs != "" {
if err := json.Unmarshal([]byte(tc.pjs), &pjs); err != nil {
t.Fatalf("failed to unmarshal package.json: %s, error: %v", tc.pjs, err)
}
}
home := t.TempDir()
if tc.hasServerJs {
_, err := safeopen.CreateBeneath(home, "server.js")
if err != nil {
t.Fatalf("failed to create server.js: %v", err)
}
}
ctx := gcpbuildpack.NewContext(gcpbuildpack.WithApplicationRoot(home))

got, err := DefaultStartCommand(ctx, pjs)
if err != nil {
t.Fatalf("DefaultStartCommand() got error: %v", err)
}
if diff := cmp.Diff(tc.want, got); diff != "" {
t.Errorf("DefaultStartCommand() mismatch (-want +got):\n%s", diff)
}
})
}
}

1 comment on commit f6258a2

@steren
Copy link
Contributor
@steren steren commented on f6258a2 Jan 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.