Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
3 changes: 2 additions & 1 deletion packages/@apphosting/build/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
"url": "git+https://github.com/FirebaseExtended/firebase-framework-tools.git"
},
"bin": {
"build": "dist/bin/build.js"
"build": "dist/bin/build.js",
"apphosting-local-build": "dist/bin/localbuild.js"
},
"author": {
"name": "Firebase",
Expand Down
37 changes: 37 additions & 0 deletions packages/@apphosting/build/src/bin/localbuild.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#! /usr/bin/env node
import { spawn } from "child_process";
import { program } from "commander";
import { parse as semverParse } from "semver";

Check failure on line 4 in packages/@apphosting/build/src/bin/localbuild.ts

View workflow job for this annotation

GitHub Actions / Lint

'semverParse' is defined but never used
import { yellow, bgRed, bold } from "colorette";
// @ts-expect-error TODO add interface
import pickManifest from "npm-pick-manifest";

Check failure on line 7 in packages/@apphosting/build/src/bin/localbuild.ts

View workflow job for this annotation

GitHub Actions / Lint

'pickManifest' is defined but never used
import { discover } from "@apphosting/discover";

Check failure on line 8 in packages/@apphosting/build/src/bin/localbuild.ts

View workflow job for this annotation

GitHub Actions / Lint

'discover' is defined but never used

Choose a reason for hiding this comment

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

medium

There are several unused imports (semverParse, pickManifest, discover). These should be removed to keep the code clean. The associated // @ts-expect-error comment for the unused pickManifest import can also be removed.

Suggested change
import { parse as semverParse } from "semver";
import { yellow, bgRed, bold } from "colorette";
// @ts-expect-error TODO add interface
import pickManifest from "npm-pick-manifest";
import { discover } from "@apphosting/discover";
import { yellow, bgRed, bold } from "colorette";


program
// TODO: add framework option later. For now we support nextjs only.
.argument("<directory>", "path to the project's root directory")
.action(async () => {

Check failure on line 14 in packages/@apphosting/build/src/bin/localbuild.ts

View workflow job for this annotation

GitHub Actions / Lint

Delete `⏎`
const projectRoot = program.args[0];

Choose a reason for hiding this comment

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

medium

It's a better practice with commander to receive arguments as parameters in the .action() callback rather than accessing program.args. This improves readability and makes the handler less dependent on the program object's state.

Suggested change
.action(async () => {
const projectRoot = program.args[0];
.action(async (directory: string) => {
const projectRoot = directory;

const framework = "nextjs";
// TODO: We are using the latest framework adapter versions, but in the future
// we should parse the framework version and use the matching adapter version.
const adapterName = `@apphosting/adapter-nextjs`;
const packumentResponse = await fetch(`https://registry.npmjs.org/${adapterName}`);
if (!packumentResponse.ok) throw new Error(`Something went wrong fetching ${adapterName}`);
const packument = await packumentResponse.json();
const adapterVersion = packument["dist-tags"]["canary"];

Choose a reason for hiding this comment

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

medium

Accessing packument["dist-tags"]["canary"] directly can lead to a runtime error if the dist-tags property or the canary tag doesn't exist in the packument response. It's safer to add checks to ensure the properties exist before accessing them.

Suggested change
const packument = await packumentResponse.json();
const adapterVersion = packument["dist-tags"]["canary"];
const packument = await packumentResponse.json();
const adapterVersion = packument?.["dist-tags"]?.["canary"];
if (!adapterVersion) {
throw new Error(`Could not find 'canary' dist-tag for ${adapterName}.`);
}

// TODO: should check for existence of adapter in app's package.json and use that version instead.

console.log(" 🔥", bgRed(` ${adapterName}@${yellow(bold(adapterVersion))} `), "\n");

// Call it via NPX
const buildCommand = `apphosting-adapter-${framework}-build`;
spawn("npx", ["-y", "-p", `${adapterName}@${adapterVersion}`, buildCommand], {
cwd: projectRoot,
shell: true,
stdio: "inherit",
});

Choose a reason for hiding this comment

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

high

The spawn call is not awaited, which means the script will exit immediately without waiting for the npx build process to complete. This can lead to silent failures where the build fails but the script reports success. The child process should be awaited, and its exit code should be checked to determine if the build was successful.

    await new Promise<void>((resolve, reject) => {
      const child = spawn("npx", ["-y", "-p", `${adapterName}@${adapterVersion}`, buildCommand], {
        cwd: projectRoot,
        shell: true,
        stdio: "inherit",
      });
      child.on("close", (code) => {
        if (code === 0) {
          resolve();
        } else {
          reject(new Error(`Build process exited with code ${code}`));
        }
      });
      child.on("error", (err) => {
        reject(err);
      });
    });

});

program.parse();
Loading