Skip to content

fix(appkit): ensure @ast-grep/napi binding at install (napi-postinstall)#458

Open
atilafassina wants to merge 2 commits into
mainfrom
napi-fix
Open

fix(appkit): ensure @ast-grep/napi binding at install (napi-postinstall)#458
atilafassina wants to merge 2 commits into
mainfrom
napi-fix

Conversation

@atilafassina

Copy link
Copy Markdown
Contributor

What

Adds an install-time guarantee that the @ast-grep/napi native host binary is present, via napi-postinstall.

  • packages/shared gains napi-postinstall@0.3.4 (flows into published @databricks/appkit through the existing tools/dist-appkit.ts CLI-deps merge).
  • The published postinstall (packages/shared/scripts/postinstall.js) now ensures the binding before printing its setup hint.

Why

On Databricks Apps deploy, npm runs a bare npm install and silently skips the listed optional platform binary @ast-grep/napi-linux-x64-gnu (npm/cli#4828 cross-platform lockfile, or a supply-chain cutoff). The parent @ast-grep/napi then can't load its binding, crashing appkit generate-types during the app's own postinstall.

napi-postinstall detects the host platform and re-materializes the correct binding package, working around the npm skip. This is the root-cause fix, companion to the survivability/lazy-load change in #457: with the binding present, serving-type generation works fully rather than degrading.

Design

  • Non-fatal: any failure prints one stderr warning and returns — never breaks npm install.
  • npm-only: no-ops under pnpm / yarn-PnP (which don't exhibit the bug).
  • ESM/CJS: the published postinstall is ESM; napi-postinstall is CJS → loaded via dynamic import().
  • Self-contained: postinstall.js ships standalone (dist-appkit.ts copies just that file), so it imports only external runtime deps — never appkit src/dist.

Verification

  • build / -r typecheck / test / check green.
  • New unit tests (3) for ensureAstGrepBinding: invoked under an npm user-agent, non-fatal when the pre-fetch rejects, no-op under a non-npm (pnpm) user-agent.
  • Real-execution checks: the script's main-guard fires under npm's relative node scripts/postinstall.js invocation (so the side effects actually run on install); an npm-UA run loads the real CommonJS napi-postinstall via dynamic import() and exits 0; a pnpm-UA run no-ops to just the hint.

Refs: npm/cli#4828.

This pull request and its description were written by Isaac.

Signed-off-by: Atila Fassina <atila@fassina.eu>
@atilafassina atilafassina requested a review from a team as a code owner June 22, 2026 15:43
@atilafassina atilafassina requested review from Copilot and ditadi and removed request for Copilot June 22, 2026 15:43
@github-actions

Copy link
Copy Markdown
Contributor

🔬  Run evals on this PR  ·  Go to Evals Monitor →

Signed-off-by: Atila Fassina <atila@fassina.eu>
Copilot AI review requested due to automatic review settings June 23, 2026 13:10

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds a best-effort install-time prefetch to ensure the @ast-grep/napi native binding is present (via napi-postinstall), preventing appkit generate-types from failing on environments where npm skips optional native deps.

Changes:

  • Add napi-postinstall@0.3.4 as a runtime dependency of packages/shared (and update lockfile).
  • Expand the published postinstall script to prefetch @ast-grep/napi’s binding under npm with a hard timeout, then print the existing setup hint.
  • Add unit tests for the new ensureAstGrepBinding behavior and TS declarations to allow importing the standalone JS script in tests.

Reviewed changes

Copilot reviewed 4 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
pnpm-lock.yaml Locks the new napi-postinstall dependency.
packages/shared/tsconfig.json Includes scripts/**/*.d.ts so tests can import the standalone JS postinstall script without allowJs.
packages/shared/src/scripts/postinstall.test.ts Adds tests covering npm-only behavior, timeout/failure non-fatal behavior, and pnpm no-op.
packages/shared/scripts/postinstall.js Implements the npm-only, time-bounded prefetch plus a main guard and the setup hint printer.
packages/shared/scripts/postinstall.d.ts Provides dev-only typings for the standalone postinstall script to support TS tests.
packages/shared/package.json Adds napi-postinstall to runtime dependencies.
Files not reviewed (1)
  • pnpm-lock.yaml: Generated file

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +18 to +22
// Runs in the child: re-resolve and materialize the @ast-grep/napi host binding.
// `.catch(exit 1)` keeps the child quiet and turns a rejection into a non-zero exit
// that the parent treats as a failed (non-fatal) pre-fetch.
const PREFETCH_SCRIPT =
"Promise.resolve(require('napi-postinstall').checkAndPreparePackage('@ast-grep/napi')).catch(() => process.exit(1))";
Comment on lines +54 to +58
execFileSync(process.execPath, ["-e", PREFETCH_SCRIPT], {
cwd: pkgDir,
stdio: "inherit",
timeout: PREFETCH_TIMEOUT_MS,
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants