Skip to content

Conversation

macabeus
Copy link
Contributor

@macabeus macabeus commented Aug 11, 2025

This PR follows up on the issue raised here: ast-grep/ast-grep#2146.

Currently, the packages assume that ./parser.so is compatible with the system architecture when running the app.
This assumption breaks when the app is bundled to run on multiple platforms.

In my case, I’m using @ast-grep/lang-c in a VS Code extension and need a bundle that works for both ARM and x86 architectures.

As a solution, I updated packages/c/index.js to load parser.so from the prebuilds folder based on process.platform and process.arch, similar to how requireNative in @ast-grep/napi works.

Notes:
This PR is an MVP to resolve my immediate issue and unblock release for some users.
It currently only addresses @ast-grep/lang-c.
The extra copy of parser.so should be removed in a proper implementation.
Intended as a proof-of-concept rather than a merge-ready fix.

Please let me know what you think about it before I spend more time working on it.

Summary by CodeRabbit

  • New Features

    • Library path resolution is now lazy and dynamic across language packages: the native parser is resolved at runtime (preferring prebuilt binaries, falling back to local) and cached on first use.
    • Postinstall detects prebuilt parsers and skips unnecessary rebuilds.
  • Bug Fixes

    • Clearer runtime error when no suitable parser is found.
  • Documentation

    • README updated to reflect the postinstall prebuild no-op behavior.

Copy link

coderabbitai bot commented Aug 11, 2025

Walkthrough

Replaces eager static parser paths across language packages with a lazy, cached resolver that prefers a prebuilt via resolvePrebuild(__dirname) and falls back to a local parser.so, throwing if neither exists. Alters postinstall to no-op when a prebuild is present and exports resolvePrebuild.

Changes

Cohort / File(s) Summary
Core C package
packages/c/index.js
Replace static libPath with a module-scoped cache and getLibPath(); libraryPath becomes a getter that prefers resolvePrebuild(__dirname), then parser.so, else throws. Adds fs and @ast-grep/setup-lang usages; other exports unchanged.
Language packages (bulk)
packages/angular/index.js, packages/bash/index.js, packages/cpp/index.js, packages/csharp/index.js, packages/css/index.js, packages/dart/index.js, packages/elixir/index.js, packages/go/index.js, packages/haskell/index.js, packages/html/index.js, packages/java/index.js, packages/javascript/index.js, packages/json/index.js, packages/kotlin/index.js, packages/lua/index.js, packages/markdown/index.js, packages/php/index.js, packages/python/index.js, packages/ruby/index.js, packages/rust/index.js, packages/scala/index.js, packages/sql/index.js, packages/swift/index.js, packages/toml/index.js, packages/tsx/index.js, packages/typescript/index.js, packages/yaml/index.js
Convert eager libraryPath: libPath exports to lazy, memoized getters. Add getLibPath() which prefers resolvePrebuild(__dirname), falls back to local parser.so, and throws if missing. Introduce let libPath cache and import fs + prebuild resolver. Public export keys unchanged except libraryPath is now an accessor.
Setup & install scripts
scripts/setup/index.ts
Remove copying of prebuilt into parser.so; when resolvePrebuild finds a prebuild, log and return (no copy). If no prebuild, fallback to building from source. Export resolvePrebuild in addition to postinstall.
Template for new langs
scripts/create-lang/template/index.js, README.md
Update template and README to reflect lazy resolver and changed postinstall behavior (prebuild branch is a no-op rather than copying). scripts/create-lang/template/index.js uses the same getLibPath() + getter pattern.

Sequence Diagram(s)

sequenceDiagram
  participant Consumer as Consumer (require / import)
  participant Module as language package (index.js)
  participant Resolver as resolvePrebuild
  participant FS as Filesystem

  Consumer->>Module: access libraryPath
  Module->>Resolver: resolvePrebuild(__dirname)
  alt prebuild found
    Resolver-->>Module: prebuiltPath
    Module->>FS: stat(prebuiltPath)
    FS-->>Module: exists
    Module-->>Consumer: return prebuiltPath (cached)
  else no prebuild
    Resolver-->>Module: null
    Module->>FS: stat(path.join(__dirname,'parser.so'))
    alt local parser exists
      FS-->>Module: exists
      Module-->>Consumer: return local parser path (cached)
    else none found
      FS-->>Module: missing
      Module-->>Consumer: throw Error("No parser found. Please ensure the parser is built or a prebuild is available.")
    end
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~30 minutes

Possibly related PRs

  • ✨ Rust #58 — touches packages/rust/index.js, overlaps with changes switching to a lazy, prebuild-aware libraryPath.
  • ✨ Scala #59 — modifies packages/scala/index.js, directly overlaps with the lazy libraryPath conversion.
  • ✨ Ruby #57 — adds Ruby package and postinstall setup; overlaps on Ruby parser path resolution and install/setup flow.

Suggested reviewers

  • HerringtonDarkholme

Poem

I hopped through folders, nose alight,
Searched for prebuilds by day and night,
If one I find I give a cheer,
If none I sniff the local here—
Then cache the path and thump with glee, a parser found (or woe, oh me!) 🐇

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the "Integrations" page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@macabeus macabeus changed the title Fix bundling for multiple platforms Fix cross-platform compatibility when bundling an app Aug 11, 2025
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (2)
packages/c/index.js (2)

27-27: Allow an override via env var for bundlers and custom layouts

Some bundlers or packaging setups may prefer to supply the exact native path. Add an opt-in env override without changing the public API.

+const overrideLibPath = process.env.AST_GREP_LANG_C_PARSER_PATH
-const libPath = path.join(__dirname, getNativePath())
+const libPath = overrideLibPath || path.join(__dirname, getNativePath())

12-12: Minor style consistency: trailing semicolons

Returns on Line 12 and Line 18 end with semicolons while the rest do not. Align with the repository’s style (remove or add consistently).

-      return 'prebuilds/prebuild-macOS-ARM64/parser.so';
+      return 'prebuilds/prebuild-macOS-ARM64/parser.so'
-      return 'prebuilds/prebuild-Linux-X64/parser.so';
+      return 'prebuilds/prebuild-Linux-X64/parser.so'

Also applies to: 18-18

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7ef4c6c and d8029d2.

📒 Files selected for processing (1)
  • packages/c/index.js (1 hunks)
🔇 Additional comments (1)
packages/c/index.js (1)

3-25: Confirm prebuild artifacts layout

I didn’t find a packages/c/prebuilds directory or any parser.* files in the source tree—those files are likely generated or published separately. Before applying the table-driven refactor for getNativePath(), please:

  • Inspect your built or published package (e.g. the installed @…/parser module under node_modules) and note the exact folder names (Windows vs. windows, macOS vs. darwin, etc.), architecture suffixes (X64, x64, ARM64, etc.) and file extensions (.dll, .dylib, .so).
  • Ensure your PLATFORMS and ARCHES mappings in packages/c/index.js exactly match those artifact names.
  • Verify that require(path) at runtime correctly resolves to the existing binary files.

Without this check, changing to a generalized lookup risks breaking native library loading.

@HerringtonDarkholme
Copy link
Member

HerringtonDarkholme commented Aug 11, 2025

The resolution has already done in the postinstall script. https://github.com/ast-grep/langs/blob/main/scripts/setup/index.ts
I think the issue is vscode bundling instead of runtime parser detection. Note, the prebundle packages only have three platforms. Users still need post-install script to build the so themselves on unsupported platforms.

Because you are bundling the package using macos so the MacOS parser.so is copied. You can either try

  • bunlding different packages for different platforms
  • copy the package lang-c content to your vscode bundle, change the loading script to detect platform when running

To make this merge, it should include these

  • add fallbacks for unsupported platforms instead of throwing error
  • still keep the postinstall script for unsupported platforms (pre-built platform may benefit from this solution since pnpm does not run scripts by default)
  • apply the change to all languages

I am not sure if you want to pursue this approach since it is quite tiringsome.

@macabeus
Copy link
Contributor Author

macabeus commented Aug 12, 2025

@HerringtonDarkholme Hmm... Got it. I think I can take it, yes!
It'll be more elegant than the alternative solutions.

Just to make sure if I understand well the pending tasks:

  1. Update the postinstall script to not copy a prebuilt file to the root.
  2. Update the postinstall script to check if we already have a prebuilt file. If we don't have a prebuilt file, keep it building one.
  3. Update my getNativePath function to prioritize the file from the root (the one we might have built on the previous step). If we don't have one, check the prebuilt files (as implemented from this PR)
  4. Apply this change for all the ~25 languages.

By this way, we still keep the support for the platforms that we don't have a prebuilt file.
But it'll still throw an exception if we don't have parser.so on the root and we don't have a prebuilt file. It may happen if the postinstall script didn't run, or if it failed.
I'm not sure what do you mean by "add fallbacks for unsupported platforms instead of throwing error". Can you clarify it, please?

apply the change to all languages

Hopefully, copilot will help me to update the logic for all the ~25 languages as soon as I have it working well for C 🙏

As soon as you confirm that my understanding is right, I start working on it.

@macabeus
Copy link
Contributor Author

@HerringtonDarkholme I made the changes for lang-c. Please let me know if my changes make sense. Then, I can apply the same changes for all the other languages.
I did a quick test on my macbook and windows computers, and it's using the prebuild files as expected.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🔭 Outside diff range comments (3)
scripts/setup/index.ts (3)

37-48: Build may run in the wrong directory; set cwd and surface output

execSync defaults to the current process cwd. In monorepos or when invoked from a different folder, this can run the wrong build script. Also, surfacing stdio helps diagnose build failures.

-  execSync('npm run build')
+  execSync('npm run build', { cwd: dirname, stdio: 'inherit' })

64-79: Make resolvePrebuild resilient: handle alt dir patterns and file extensions

Current logic assumes prebuilds/prebuild-OS-ARCH/parser.so with capitalized OS/arch and .so on all platforms. To better match real-world layouts and Windows/macOS ext differences, try multiple directory patterns and extensions, and log raw platform/arch when not mapped.

-function resolvePrebuild(dir: string) {
-  const os = PLATFORM_MAP[process.platform]
-  const arch = ARCH_MAP[process.arch]
-  const prebuild = path.join(
-    dir,
-    'prebuilds',
-    `prebuild-${os}-${arch}`,
-    'parser.so',
-  )
-  if (!os || !arch || !fs.existsSync(prebuild)) {
-    log(`no prebuild for ${os} ${arch}`)
-    return undefined
-  }
-  log(`found prebuild for ${os} ${arch}`)
-  return prebuild
-}
+function resolvePrebuild(dir: string) {
+  const rawOS = process.platform
+  const rawArch = process.arch
+  const os = PLATFORM_MAP[rawOS] ?? rawOS
+  const arch = ARCH_MAP[rawArch] ?? rawArch
+
+  // Support both "prebuild-OS-ARCH" and "OS-ARCH" folder styles just in case.
+  const candidateDirs = [
+    path.join(dir, 'prebuilds', `prebuild-${os}-${arch}`),
+    path.join(dir, 'prebuilds', `${os}-${arch}`),
+    path.join(dir, 'prebuilds', `${rawOS}-${rawArch}`),
+  ]
+  // Try common extensions per platform. Some repos ship .so across platforms; include that too.
+  const exts =
+    rawOS === 'win32' ? ['dll', 'so'] :
+    rawOS === 'darwin' ? ['dylib', 'so'] :
+    ['so']
+
+  for (const d of candidateDirs) {
+    for (const ext of exts) {
+      const p = path.join(d, `parser.${ext}`)
+      if (fs.existsSync(p)) {
+        log(`found prebuild for ${os} ${arch} at ${p}`)
+        return p
+      }
+    }
+  }
+  log(`no prebuild for ${rawOS} ${rawArch} under: ${candidateDirs.join(', ')}`)
+  return undefined
+}

50-60: Fallback to raw process.platform/process.arch and/or detect prebuild dirs

I ran your check — no prebuild directories or parser binaries were found in the repo.

  • Location to change:
    • scripts/setup/index.ts (around lines 50–60)

Suggested change (keep the explicit map but fall back to raw values and consider scanning prebuild dirs):

const PLATFORM_MAP: Record<string, string> = {
  darwin: 'macOS',
  linux: 'Linux',
  win32: 'Windows',
}

const ARCH_MAP: Record<string, string> = {
  x64: 'X64',
  arm64: 'ARM64',
}

// Use mapped value or fall back to the runtime value so unknown platforms/arches still work:
const targetPlatform = PLATFORM_MAP[process.platform] ?? process.platform
const targetArch = ARCH_MAP[process.arch] ?? process.arch

// (Optional) Further harden by introspecting available prebuild directories at runtime
// and selecting the best match (e.g., handle ia32, arm, musl, freebsd, etc.).

Please apply the fallback and/or implement a simple prebuild-dir scan to avoid false "no prebuild" reports for unsupported-but-valid architectures or OS variants.

🧹 Nitpick comments (1)
scripts/setup/index.ts (1)

20-26: Early return on prebuild found is fine; include path to aid debugging

Log the resolved path so consumers know exactly which binary was used. The message grammar can also be tightened.

-    log('prebuild found, do not need to build')
+    log('prebuild found at', prebuild, '- no build needed')
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d8029d2 and 2f71305.

📒 Files selected for processing (2)
  • packages/c/index.js (1 hunks)
  • scripts/setup/index.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/c/index.js
🔇 Additional comments (1)
scripts/setup/index.ts (1)

81-81: Exporting resolvePrebuild is a good move

This enables runtime loaders (e.g., per-language packages) to share the same resolution logic.

@HerringtonDarkholme
Copy link
Member

Thanks! @macabeus

I currently don't have time for a review but I will find some time later this weekend.

Appreciate your help

return native;
}

throw new Error('No parser found. Please ensure the parser is built or a prebuild is available.');

Choose a reason for hiding this comment

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

this is a nice error message. if the user is on an unsupported platform and install the library using pnpm, this error message can direct them

@HerringtonDarkholme
Copy link
Member

Hi @macabeus this change is surprisingly short and beautiful. Please go ahead and I will merge after the other languages are done.

@macabeus
Copy link
Contributor Author

@HerringtonDarkholme Done!

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 17

♻️ Duplicate comments (18)
packages/yaml/index.js (1)

5-17: Mirror Swift feedback: error message and cross-platform fallback

Same comments apply here as in packages/swift/index.js about:

  • Making the error include platform/arch and install hint.
  • Considering .dll/.dylib fallback if postinstall doesn’t always emit parser.so.
  • Potential centralization in @ast-grep/setup-lang.
packages/html/index.js (1)

5-17: Mirror Swift feedback: error message and cross-platform fallback

Recommend the same improvements as noted for packages/swift/index.js:

  • More actionable error text.
  • Optional .dll/.dylib fallback check.
  • Consider centralizing the logic in @ast-grep/setup-lang.
packages/csharp/index.js (1)

5-17: Mirror Swift feedback: error message and cross-platform fallback

Recommend the same improvements (actionable error, optional .dll/.dylib fallback, centralizing logic).

packages/elixir/index.js (3)

16-17: Improve the error to guide users (see rust comment)

Add platform/arch and remediation steps to the error string.


2-3: Ensure @ast-grep/setup-lang is listed as a runtime dependency

This module now imports resolvePrebuild at runtime.


11-16: Align local-build fallback with platform-specific extension (see rust comment)

Same concern as in packages/rust/index.js: use .dll/.dylib/.so based on process.platform so local builds on Windows/macOS are discovered.

packages/lua/index.js (3)

16-17: Make error messages actionable

Adopt the enhanced message suggested in packages/rust/index.js to help users recover when neither prebuild nor local parser exists.


2-3: Dependency check: @ast-grep/setup-lang

Ensure this package declares @ast-grep/setup-lang in dependencies/optionalDependencies.


11-16: Use platform-appropriate extension for local fallback (dll/dylib/so)

Replicate the platform-aware extension logic suggested in packages/rust/index.js to make the local-build fallback work across OSes.

packages/javascript/index.js (3)

16-17: Enhance error message with platform and guidance

Adopt the actionable error style proposed in packages/rust/index.js.


2-3: Declare @ast-grep/setup-lang as a runtime dependency

This import needs to be present in package.json dependencies.


11-16: Platform-specific extension for local fallback is needed

As noted in other files, hardcoding parser.so breaks local fallback on Windows/macOS. Compute the extension.

packages/ruby/index.js (3)

16-17: Actionable error would improve UX

Include platform, arch, and recovery steps.


2-3: Runtime dep check: @ast-grep/setup-lang

Please ensure dependency is declared.


11-16: Fix local fallback for Windows/macOS

Use a platform-based extension to discover locally built parser.{dll|dylib|so}.

packages/markdown/index.js (1)

2-4: Dependency check mirrors css.

Ensure @ast-grep/setup-lang is listed under dependencies for packages/markdown.

Same script as in packages/css applies; just change the path to packages/markdown/package.json.

packages/toml/index.js (1)

2-4: Dependency check mirrors css.

Confirm @ast-grep/setup-lang is a runtime dependency of packages/toml.

Same validation approach as for packages/css/package.json.

packages/python/index.js (1)

2-4: Dependency check mirrors css.

Verify packages/python includes @ast-grep/setup-lang in dependencies.

Use the same grep-based check against packages/python/package.json.

🧹 Nitpick comments (22)
README.md (1)

76-78: Clarify grammar and subject; use “prebuilt parser”

Minor grammar/clarity fix to avoid ambiguity and match terminology elsewhere.

Apply this diff:

-If there is prebuild available, it does nothing.
-If there is no prebuild available, it will build the dynamic library from the tree-sitter grammar files.
+If a prebuilt parser is available, the postinstall script does nothing.
+If no prebuilt parser is available, it will build the dynamic library from the tree-sitter grammar files.
packages/swift/index.js (1)

7-16: Style nit: semicolon consistency

This file mixes styles (top-level requires without semicolons; returns/assignments with semicolons). Consider removing semicolons here to match the existing style in this file and others.

Apply this diff:

-    return prebuild;
+    return prebuild
@@
-  const native = path.join(__dirname, 'parser.so');
+  const native = path.join(__dirname, 'parser.so')
@@
-    return native;
+    return native
@@
-  throw new Error('No parser found. Please ensure the parser is built or a prebuild is available.');
+  throw new Error('No parser found. Please ensure the parser is built or a prebuild is available.')
@@
-      libPath = getLibPath();
+      libPath = getLibPath()
@@
-    return libPath;
+    return libPath

Also applies to: 23-26

packages/yaml/index.js (1)

7-16: Semicolon style consistency

Match the file’s prevailing no-semicolon style.

Apply this diff:

-    return prebuild;
+    return prebuild
@@
-  const native = path.join(__dirname, 'parser.so');
+  const native = path.join(__dirname, 'parser.so')
@@
-    return native;
+    return native
@@
-  throw new Error('No parser found. Please ensure the parser is built or a prebuild is available.');
+  throw new Error('No parser found. Please ensure the parser is built or a prebuild is available.')
@@
-      libPath = getLibPath();
+      libPath = getLibPath()
@@
-    return libPath;
+    return libPath

Also applies to: 23-26

packages/html/index.js (1)

7-16: Nit: semicolon consistency

Align with the file’s no-semicolon style.

Apply this diff:

-    return prebuild;
+    return prebuild
@@
-  const native = path.join(__dirname, 'parser.so');
+  const native = path.join(__dirname, 'parser.so')
@@
-    return native;
+    return native
@@
-  throw new Error('No parser found. Please ensure the parser is built or a prebuild is available.');
+  throw new Error('No parser found. Please ensure the parser is built or a prebuild is available.')
@@
-      libPath = getLibPath();
+      libPath = getLibPath()
@@
-    return libPath;
+    return libPath

Also applies to: 23-26

packages/csharp/index.js (1)

7-16: Nit: semicolon consistency

Keep style consistent within the file.

Apply this diff:

-    return prebuild;
+    return prebuild
@@
-  const native = path.join(__dirname, 'parser.so');
+  const native = path.join(__dirname, 'parser.so')
@@
-    return native;
+    return native
@@
-  throw new Error('No parser found. Please ensure the parser is built or a prebuild is available.');
+  throw new Error('No parser found. Please ensure the parser is built or a prebuild is available.')
@@
-      libPath = getLibPath();
+      libPath = getLibPath()
@@
-    return libPath;
+    return libPath

Also applies to: 23-26

packages/scala/index.js (1)

16-16: Make the error more actionable (include platform/arch and package).

Adding platform and package context helps users debug missing prebuilds/postinstall failures faster.

-  throw new Error('No parser found. Please ensure the parser is built or a prebuild is available.');
+  throw new Error(`No parser found for ${process.platform}-${process.arch} in ${path.basename(__dirname)}. Ensure postinstall ran or a matching prebuild exists.`);
packages/tsx/index.js (1)

16-16: Improve error messaging with platform/arch context.

-  throw new Error('No parser found. Please ensure the parser is built or a prebuild is available.');
+  throw new Error(`No parser found for ${process.platform}-${process.arch} in ${path.basename(__dirname)}. Ensure postinstall ran or a matching prebuild exists.`);
packages/bash/index.js (1)

16-16: Add context to the error for easier debugging.

-  throw new Error('No parser found. Please ensure the parser is built or a prebuild is available.');
+  throw new Error(`No parser found for ${process.platform}-${process.arch} in ${path.basename(__dirname)}. Ensure postinstall ran or a matching prebuild exists.`);
packages/typescript/index.js (2)

16-16: Clarify error with platform/arch and package name.

-  throw new Error('No parser found. Please ensure the parser is built or a prebuild is available.');
+  throw new Error(`No parser found for ${process.platform}-${process.arch} in ${path.basename(__dirname)}. Ensure postinstall ran or a matching prebuild exists.`);

5-17: Consider centralizing this resolver to remove duplication across ~25 packages.

A single helper in @ast-grep/setup-lang (e.g., getNativePath(dir): string) that encapsulates:

  • prebuild resolution,
  • local parser.so fallback,
  • standardized error,
    would reduce maintenance and ensure consistent behavior across languages and templates.
packages/java/index.js (1)

16-16: Enhance error message for quicker diagnostics.

-  throw new Error('No parser found. Please ensure the parser is built or a prebuild is available.');
+  throw new Error(`No parser found for ${process.platform}-${process.arch} in ${path.basename(__dirname)}. Ensure postinstall ran or a matching prebuild exists.`);
packages/haskell/index.js (2)

16-16: Make error actionable with platform/arch and tried paths

Improve diagnosability by including platform/arch and the attempted local path. This helps users/bundlers understand what failed.

-  throw new Error('No parser found. Please ensure the parser is built or a prebuild is available.');
+  throw new Error(`No parser found for ${process.platform}-${process.arch}. Searched prebuilds and "${native}". If you are bundling (e.g. VS Code), ensure postinstall ran to build a local parser or include matching prebuilds.`);

5-17: Reduce duplication by centralizing resolver logic

This resolver is duplicated across ~25 packages. Consider exposing a single helper from @ast-grep/setup-lang, e.g., resolveLibraryPath(__dirname), that implements:

  • prebuild-first lookup,
  • local parser fallback,
  • standardized, actionable error message.

This will keep behavior consistent and make future changes one-liners per package.

packages/php/index.js (1)

16-16: Improve the error with platform/arch and next steps

Provide actionable context to users and bundlers.

-  throw new Error('No parser found. Please ensure the parser is built or a prebuild is available.');
+  throw new Error(`No parser found for ${process.platform}-${process.arch}. Searched prebuilds and "${native}". If you are bundling (e.g. VS Code), ensure postinstall ran to build a local parser or include matching prebuilds.`);
packages/dart/index.js (1)

16-16: Enhance error message for debugging

Add platform/arch and the attempted path to aid users.

-  throw new Error('No parser found. Please ensure the parser is built or a prebuild is available.');
+  throw new Error(`No parser found for ${process.platform}-${process.arch}. Searched prebuilds and "${native}". If you are bundling (e.g. VS Code), ensure postinstall ran to build a local parser or include matching prebuilds.`);
packages/json/index.js (1)

16-16: Provide more context in the error message

Small improvement for supportability.

-  throw new Error('No parser found. Please ensure the parser is built or a prebuild is available.');
+  throw new Error(`No parser found for ${process.platform}-${process.arch}. Searched prebuilds and "${native}". If you are bundling (e.g. VS Code), ensure postinstall ran to build a local parser or include matching prebuilds.`);
packages/go/index.js (1)

16-16: Make the thrown error more actionable

Include platform/arch and attempted path.

-  throw new Error('No parser found. Please ensure the parser is built or a prebuild is available.');
+  throw new Error(`No parser found for ${process.platform}-${process.arch}. Searched prebuilds and "${native}". If you are bundling (e.g. VS Code), ensure postinstall ran to build a local parser or include matching prebuilds.`);
packages/cpp/index.js (1)

16-16: Improve error message with actionable context (optional)

Include platform/arch and attempted locations to aid debugging.

-  throw new Error('No parser found. Please ensure the parser is built or a prebuild is available.');
+  throw new Error(`No parser found for ${process.platform}-${process.arch} in ${__dirname}.
+Tried prebuilds and ${native}. Ensure postinstall ran or build a parser.so for this platform.`);
packages/kotlin/index.js (1)

5-17: Deduplicate getLibPath logic across packages (optional)

This helper is copy-pasted across ~25 packages. Consider exposing a shared getLibPath(cwd) from @ast-grep/setup-lang to remove duplication and ensure consistent behavior/errors.

packages/rust/index.js (2)

16-17: Make the error actionable and self-identifying

Include platform/arch and a suggestion to build or install prebuilds, so users know how to fix it in unsupported environments or when postinstall is skipped.

Apply this diff:

-  throw new Error('No parser found. Please ensure the parser is built or a prebuild is available.');
+  const lang = path.basename(__dirname);
+  throw new Error(
+    `@ast-grep/lang-${lang}: no parser found for ${process.platform}-${process.arch}. ` +
+    `Try one of:\n` +
+    `  1) npx @ast-grep/setup-lang postinstall\n` +
+    `  2) reinstall to trigger postinstall build on this platform\n` +
+    `  3) install a matching prebuilt artifact`
+  );

5-17: Reduce duplication: centralize path resolution in setup package

This getLibPath is duplicated across ~25 packages. Consider exporting a resolveLibraryPath(__dirname) helper from @ast-grep/setup-lang that encapsulates:

  • prebuild-first detection,
  • platform-specific local filename,
  • actionable error message.

This cuts maintenance overhead and keeps behavior consistent across languages.

packages/css/index.js (1)

5-17: Avoid duplicating resolver logic across ~25 packages.

Consider hoisting this logic into @ast-grep/setup-lang (e.g., export a getNativePath(dirname): string) so each language can just call it. This reduces maintenance overhead and keeps behavior uniform.

Happy to sketch the shared helper API and a codemod to update all packages in one sweep.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2f71305 and b14c18f.

📒 Files selected for processing (29)
  • README.md (1 hunks)
  • packages/angular/index.js (1 hunks)
  • packages/bash/index.js (1 hunks)
  • packages/cpp/index.js (1 hunks)
  • packages/csharp/index.js (1 hunks)
  • packages/css/index.js (1 hunks)
  • packages/dart/index.js (1 hunks)
  • packages/elixir/index.js (1 hunks)
  • packages/go/index.js (1 hunks)
  • packages/haskell/index.js (1 hunks)
  • packages/html/index.js (1 hunks)
  • packages/java/index.js (1 hunks)
  • packages/javascript/index.js (1 hunks)
  • packages/json/index.js (1 hunks)
  • packages/kotlin/index.js (1 hunks)
  • packages/lua/index.js (1 hunks)
  • packages/markdown/index.js (1 hunks)
  • packages/php/index.js (1 hunks)
  • packages/python/index.js (1 hunks)
  • packages/ruby/index.js (1 hunks)
  • packages/rust/index.js (1 hunks)
  • packages/scala/index.js (1 hunks)
  • packages/sql/index.js (1 hunks)
  • packages/swift/index.js (1 hunks)
  • packages/toml/index.js (1 hunks)
  • packages/tsx/index.js (1 hunks)
  • packages/typescript/index.js (1 hunks)
  • packages/yaml/index.js (1 hunks)
  • scripts/create-lang/template/index.js (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (20)
packages/rust/index.js (3)
packages/bash/index.js (6)
  • fs (2-2)
  • require (3-3)
  • prebuild (6-6)
  • native (11-11)
  • path (1-1)
  • libPath (19-19)
packages/kotlin/index.js (5)
  • fs (2-2)
  • prebuild (6-6)
  • native (11-11)
  • path (1-1)
  • libPath (19-19)
packages/lua/index.js (5)
  • fs (2-2)
  • prebuild (6-6)
  • native (11-11)
  • path (1-1)
  • libPath (19-19)
packages/toml/index.js (2)
packages/bash/index.js (3)
  • prebuild (6-6)
  • path (1-1)
  • libPath (19-19)
scripts/setup/index.ts (1)
  • resolvePrebuild (81-81)
packages/python/index.js (2)
packages/markdown/index.js (4)
  • prebuild (6-6)
  • native (11-11)
  • path (1-1)
  • libPath (19-19)
scripts/setup/index.ts (1)
  • resolvePrebuild (81-81)
packages/tsx/index.js (4)
packages/lua/index.js (4)
  • fs (2-2)
  • prebuild (6-6)
  • native (11-11)
  • libPath (19-19)
packages/tsx/nursery.js (2)
  • require (1-1)
  • path (4-4)
packages/tsx/postinstall.js (1)
  • require (1-1)
scripts/setup/index.ts (1)
  • resolvePrebuild (81-81)
packages/dart/index.js (17)
packages/bash/index.js (6)
  • fs (2-2)
  • require (3-3)
  • prebuild (6-6)
  • native (11-11)
  • path (1-1)
  • libPath (19-19)
packages/angular/index.js (6)
  • fs (2-2)
  • require (3-3)
  • prebuild (6-6)
  • native (11-11)
  • path (1-1)
  • libPath (19-19)
packages/csharp/index.js (6)
  • fs (2-2)
  • require (3-3)
  • prebuild (6-6)
  • native (11-11)
  • path (1-1)
  • libPath (19-19)
packages/cpp/index.js (6)
  • fs (2-2)
  • require (3-3)
  • prebuild (6-6)
  • native (11-11)
  • path (1-1)
  • libPath (19-19)
packages/haskell/index.js (6)
  • fs (2-2)
  • require (3-3)
  • prebuild (6-6)
  • native (11-11)
  • path (1-1)
  • libPath (19-19)
packages/css/index.js (6)
  • fs (2-2)
  • require (3-3)
  • prebuild (6-6)
  • native (11-11)
  • path (1-1)
  • libPath (19-19)
packages/html/index.js (6)
  • fs (2-2)
  • require (3-3)
  • prebuild (6-6)
  • native (11-11)
  • path (1-1)
  • libPath (19-19)
packages/elixir/index.js (6)
  • fs (2-2)
  • require (3-3)
  • prebuild (6-6)
  • native (11-11)
  • path (1-1)
  • libPath (19-19)
packages/java/index.js (6)
  • fs (2-2)
  • require (3-3)
  • prebuild (6-6)
  • native (11-11)
  • path (1-1)
  • libPath (19-19)
packages/json/index.js (6)
  • fs (2-2)
  • require (3-3)
  • prebuild (6-6)
  • native (11-11)
  • path (1-1)
  • libPath (19-19)
packages/markdown/index.js (6)
  • fs (2-2)
  • require (3-3)
  • prebuild (6-6)
  • native (11-11)
  • path (1-1)
  • libPath (19-19)
packages/kotlin/index.js (6)
  • fs (2-2)
  • require (3-3)
  • prebuild (6-6)
  • native (11-11)
  • path (1-1)
  • libPath (19-19)
packages/lua/index.js (5)
  • fs (2-2)
  • prebuild (6-6)
  • native (11-11)
  • path (1-1)
  • libPath (19-19)
packages/php/index.js (5)
  • fs (2-2)
  • prebuild (6-6)
  • native (11-11)
  • path (1-1)
  • libPath (19-19)
packages/dart/postinstall.js (1)
  • require (1-1)
packages/dart/nursery.js (1)
  • require (1-1)
scripts/setup/index.ts (1)
  • resolvePrebuild (81-81)
packages/yaml/index.js (1)
scripts/setup/index.ts (1)
  • resolvePrebuild (81-81)
packages/css/index.js (1)
packages/lua/index.js (5)
  • fs (2-2)
  • prebuild (6-6)
  • native (11-11)
  • path (1-1)
  • libPath (19-19)
packages/javascript/index.js (1)
scripts/setup/index.ts (1)
  • resolvePrebuild (81-81)
packages/markdown/index.js (2)
packages/lua/index.js (5)
  • fs (2-2)
  • prebuild (6-6)
  • native (11-11)
  • path (1-1)
  • libPath (19-19)
packages/php/index.js (4)
  • fs (2-2)
  • prebuild (6-6)
  • native (11-11)
  • libPath (19-19)
packages/scala/index.js (2)
packages/kotlin/index.js (5)
  • fs (2-2)
  • prebuild (6-6)
  • native (11-11)
  • path (1-1)
  • libPath (19-19)
packages/lua/index.js (5)
  • fs (2-2)
  • prebuild (6-6)
  • native (11-11)
  • path (1-1)
  • libPath (19-19)
packages/bash/index.js (1)
packages/lua/index.js (5)
  • fs (2-2)
  • prebuild (6-6)
  • native (11-11)
  • path (1-1)
  • libPath (19-19)
packages/swift/index.js (1)
scripts/setup/index.ts (1)
  • resolvePrebuild (81-81)
packages/html/index.js (3)
packages/lua/index.js (5)
  • fs (2-2)
  • prebuild (6-6)
  • native (11-11)
  • path (1-1)
  • libPath (19-19)
packages/php/index.js (5)
  • fs (2-2)
  • prebuild (6-6)
  • native (11-11)
  • path (1-1)
  • libPath (19-19)
scripts/setup/index.ts (1)
  • resolvePrebuild (81-81)
scripts/create-lang/template/index.js (1)
scripts/setup/index.ts (1)
  • resolvePrebuild (81-81)
packages/cpp/index.js (3)
packages/json/index.js (5)
  • fs (2-2)
  • prebuild (6-6)
  • native (11-11)
  • path (1-1)
  • libPath (19-19)
packages/cpp/postinstall.js (1)
  • require (1-1)
scripts/setup/index.ts (1)
  • resolvePrebuild (81-81)
packages/elixir/index.js (3)
packages/lua/index.js (5)
  • fs (2-2)
  • prebuild (6-6)
  • native (11-11)
  • path (1-1)
  • libPath (19-19)
packages/php/index.js (5)
  • fs (2-2)
  • prebuild (6-6)
  • native (11-11)
  • path (1-1)
  • libPath (19-19)
scripts/setup/index.ts (1)
  • resolvePrebuild (81-81)
packages/java/index.js (2)
packages/javascript/index.js (5)
  • fs (2-2)
  • prebuild (6-6)
  • native (11-11)
  • path (1-1)
  • libPath (19-19)
packages/lua/index.js (5)
  • fs (2-2)
  • prebuild (6-6)
  • native (11-11)
  • path (1-1)
  • libPath (19-19)
packages/angular/index.js (4)
packages/html/index.js (6)
  • fs (2-2)
  • require (3-3)
  • prebuild (6-6)
  • native (11-11)
  • path (1-1)
  • libPath (19-19)
packages/markdown/index.js (5)
  • fs (2-2)
  • prebuild (6-6)
  • native (11-11)
  • path (1-1)
  • libPath (19-19)
packages/kotlin/index.js (5)
  • fs (2-2)
  • prebuild (6-6)
  • native (11-11)
  • path (1-1)
  • libPath (19-19)
scripts/setup/index.ts (1)
  • resolvePrebuild (81-81)
packages/php/index.js (2)
packages/lua/index.js (4)
  • fs (2-2)
  • prebuild (6-6)
  • native (11-11)
  • libPath (19-19)
scripts/setup/index.ts (1)
  • resolvePrebuild (81-81)
packages/typescript/index.js (3)
packages/kotlin/index.js (5)
  • fs (2-2)
  • prebuild (6-6)
  • native (11-11)
  • path (1-1)
  • libPath (19-19)
packages/lua/index.js (4)
  • fs (2-2)
  • prebuild (6-6)
  • native (11-11)
  • libPath (19-19)
scripts/setup/index.ts (1)
  • resolvePrebuild (81-81)
🪛 LanguageTool
README.md

[grammar] ~76-~76: There might be a mistake here.
Context: ...e @ast-grep/lang-* packages. If there is prebuild available, it does nothing. If...

(QB_NEW_EN)


[grammar] ~76-~76: There might be a mistake here.
Context: ... is prebuild available, it does nothing. If there is no prebuild available, it wi...

(QB_NEW_EN)

🪛 GitHub Actions: CI
packages/bash/index.js

[error] 6-6: TypeError: resolvePrebuild is not a function during test step 'node nursery.js test' (bash package).

packages/cpp/index.js

[error] 6-6: TypeError: resolvePrebuild is not a function during test step 'node nursery.js test' (cpp package).

packages/angular/index.js

[error] 6-6: TypeError: resolvePrebuild is not a function during test step 'node nursery.js test' (angular package).

🔇 Additional comments (42)
packages/swift/index.js (2)

21-27: Getter + memoization for libraryPath looks good

Lazy resolution with caching avoids unnecessary I/O and plays well with bundlers that only access the property at runtime.


11-14: No change required — repository expects parser.so

Verification: scripts/setup/index.ts explicitly lists 'parser.so' (line 71) and no parser.dll or parser.dylib were found under prebuilds/ or packages/. The hard-coded parser.so fallback in packages/swift/index.js matches the repo's setup, so the suggested OS-specific fallback is not required.

Files of interest:

  • scripts/setup/index.ts:71 — contains 'parser.so'
  • packages/swift/index.js (lines 11–14) — currently uses parser.so
packages/yaml/index.js (1)

21-27: LGTM: lazy getter + cached resolution

Pattern matches the rest of the repo and avoids early path resolution during bundling.

packages/html/index.js (1)

21-27: Approach approved

The lazy getter with memoization is appropriate and consistent with the cross-package pattern.

packages/csharp/index.js (1)

21-27: Getter + cache is solid

Matches the repo’s pattern and defers I/O until actually needed.

packages/scala/index.js (1)

22-27: Lazy getter + caching LGTM.

The lazy resolution via a getter and caching into libPath is clean and avoids build-time bundler capture.

packages/tsx/index.js (1)

22-27: Getter + memoization looks good.

This avoids premature resolution during bundling.

packages/bash/index.js (1)

22-27: Lazy getter pattern approved.

Once the guard is added, the runtime behavior is as intended.

packages/typescript/index.js (1)

22-27: Getter + memoization LGTM.

packages/java/index.js (1)

22-27: Library path getter and caching are solid.

packages/haskell/index.js (2)

5-17: LGTM: robust lazy resolution with prebuild-first fallback and caching

The getter-based lazy resolution and prebuild-first strategy look correct and consistent with the PR’s goals. Caching avoids repeated fs checks.

Also applies to: 22-27


11-14: Confirmed — repo standardizes on parser.so; no OS-specific fallback needed

Repo-wide search shows parser.so is used in package build scripts and index.js files; no occurrences of parser.dll or parser.dylib were found. Keep the current code as-is.

packages/php/index.js (3)

5-17: LGTM: prebuild-aware lazy resolution and caching are correct

Matches the desired runtime detection behavior for bundled apps and avoids repeated fs checks.

Also applies to: 22-27


2-3: Dependency check for @ast-grep/setup-lang — OK

Confirmed packages/php/package.json lists "@ast-grep/setup-lang" in dependencies at version "0.0.3"; no runtime dependency change needed.

  • packages/php/package.json: dependencies["@ast-grep/setup-lang"] = "0.0.3"

11-14: Verify extension uniformity (parser.so vs OS-specific) — no action required

I searched the repo for parser.(so|dll|dylib). Only parser.so references were found (build scripts and index.js files across packages use tree-sitter build -o parser.so), so the check in packages/php/index.js is correct.

Examples:

  • scripts/setup/index.ts:71 — 'parser.so'
  • packages/php/index.js:11 — const native = path.join(__dirname, 'parser.so');
  • many packages' package.json build scripts use tree-sitter build -o parser.so

Snippet in packages/php/index.js (unchanged and valid):

  const native = path.join(__dirname, 'parser.so');
  if (fs.existsSync(native)) {
    return native;
  }
packages/dart/index.js (3)

5-17: LGTM: consistent lazy resolver with caching

The implementation aligns with the cross-platform runtime detection requirements and mirrors other languages.

Also applies to: 22-27


2-3: Dependency verified — no action needed

packages/dart/package.json declares "@ast-grep/setup-lang": "0.0.3", so the runtime dependency is present.

  • packages/dart/index.js — lines 2-3: requires '@ast-grep/setup-lang'
  • packages/dart/package.json — dependencies include "@ast-grep/setup-lang": "0.0.3"

11-14: No action required — repo consistently uses parser.so

I searched for parser.(so|dll|dylib) and only parser.so appears in build scripts and runtime loaders. Build commands use tree-sitter build -o parser.so and index.js files load 'parser.so' (e.g. packages/dart/package.json & packages/dart/index.js, packages/python/index.js, packages/yaml/package.json), so an OS-specific fallback is not needed.

packages/json/index.js (3)

5-17: LGTM: prebuild-first lazy resolution with cache

Matches the repo-wide pattern and solves the bundling issue at runtime.

Also applies to: 22-27


2-3: @ast-grep/setup-lang dependency present — no action required

Verified that packages/json/package.json declares @ast-grep/setup-lang at version 0.0.3.

  • File: packages/json/package.json
  • Dependency: "@ast-grep/setup-lang": "0.0.3"

11-14: No change required — repo consistently uses parser.so

ripgrep shows the repo consistently builds and loads a single filename: parser.so. Examples:

  • packages/*/index.js (e.g. packages/json/index.js, packages/typescript/index.js) — load path.join(__dirname, 'parser.so')
  • packages/*/package.json — "build": "tree-sitter build -o parser.so"
  • scripts/setup/index.ts — references 'parser.so'

Action: keep as-is. Add OS-specific fallbacks (e.g. .dll/.dylib) only if you intend to support platform-specific prebuilt binaries.

packages/go/index.js (3)

5-17: LGTM: coherent lazy resolution pattern

Correctly prioritizes prebuilds, falls back to local parser, and caches the resolved path.

Also applies to: 22-27


2-3: Dependency present — no action required

Verified packages/go/package.json includes @ast-grep/[email protected], so the runtime import in packages/go/index.js is satisfied.

  • File checked: packages/go/package.json — @ast-grep/setup-lang: 0.0.3
  • Import location: packages/go/index.js (uses resolvePrebuild from '@ast-grep/setup-lang')

11-14: No change required — repo consistently builds/loads parser.so

Grepped the repo for parser.(so|dll|dylib). Only occurrences of "parser.so" were found (build outputs and index.js loaders); no .dll or .dylib files/refs exist. The current .so-only fallback is consistent across the codebase.

Examples:

  • scripts/setup/index.ts
  • packages/*/index.js (e.g., packages/go/index.js, packages/javascript/index.js)
  • packages/*/package.json build scripts (tree-sitter build -o parser.so)
packages/rust/index.js (4)

5-9: Prebuild-first resolution is appropriate for bundled, multi-arch scenarios

Preferring resolvePrebuild(__dirname) at runtime aligns with the PR objective and avoids bundlers picking an incompatible binary at build time. Good move.


22-27: Lazy getter + cache keeps module load side-effect free

Using a getter to compute once and cache avoids early fs work and keeps bundling safer. Looks good.


11-16: Keep parser.so — the repo intentionally normalizes the native name

The codebase/build scripts always use "parser.so" as the canonical artifact name, so computing platform-specific extensions would be incorrect.

  • scripts/setup/index.ts — resolvePrebuild builds a prebuild path with 'parser.so' (line ~71).
  • packages/*/package.json — many packages build with tree-sitter build -o parser.so.
  • packages/*/index.js — language packages (e.g. packages/rust/index.js, packages/javascript/index.js, packages/python/index.js, etc.) check for path.join(__dirname, 'parser.so').

Do not apply the suggested diff; ignore this review suggestion.

Likely an incorrect or invalid review comment.


2-3: No action needed — @ast-grep/setup-lang is declared as a runtime dependency

Verified: all packages that import "@ast-grep/setup-lang" list it under dependencies (runtime), so consumers should not hit MODULE_NOT_FOUND.

File/context checked:

  • packages/rust/index.js (lines 2-3)
    const fs = require('node:fs')
    const { resolvePrebuild } = require('@ast-grep/setup-lang')
packages/elixir/index.js (2)

5-9: Prebuild-first resolution matches bundling needs

This mirrors the agreed approach and avoids bundler-time architecture pinning. Looks good.


22-27: Getter + cache pattern LGTM

The lazy resolution and caching are appropriate.

packages/lua/index.js (2)

5-9: Runtime prebuild resolution is correct

Good shift to lazy, prebuild-first detection to support bundled multi-arch extensions.


22-27: Lazy getter pattern LGTM

Caching after first resolution avoids repeated fs checks.

packages/javascript/index.js (2)

5-9: Prebuild-first resolution is appropriate

Matches the desired runtime selection and unblocks cross-arch bundling.


22-27: Getter + cache LGTM

Non-eager resolution is desirable here.

packages/ruby/index.js (2)

5-9: LGTM: prebuild-first dynamic resolution

Consistent with the broader approach and solves the original issue.


22-27: Lazy getter pattern LGTM

The caching behavior is correct and minimal.

packages/css/index.js (3)

22-27: LGTM: lazy getter and memoization are appropriate.

The getter defers environment detection to runtime and caches the resolved path; this directly addresses bundler cross-arch issues.


2-4: Dependency present — no action required: @ast-grep/setup-lang is declared in packages/css/package.json

Verified: packages/css/package.json references "@ast-grep/setup-lang". No change needed for this package.


11-14: Confirmed: repo consistently expects parser.so — please verify this matches your target platforms

Findings (brief):

  • All language packages use a fallback of path.join(__dirname, 'parser.so') (e.g., packages/css/index.js, packages/javascript/index.js).
  • package.json build scripts across packages use: "tree-sitter build -o parser.so".
  • scripts/setup/index.ts resolvePrebuild looks for prebuilds/.../parser.so.

Actionable note:

  • If your build/postinstall guarantees a parser.so on every platform you support, no code change is needed.
  • Otherwise, update resolvePrebuild/getLibPath to derive the filename by platform or fall back to searching for parser.* (safer for mixed/unsupported platforms).
packages/markdown/index.js (1)

22-27: LGTM: the lazy getter solution is consistent and correct.

packages/toml/index.js (1)

22-27: LGTM: memoized getter is the right call here.

packages/python/index.js (1)

22-27: LGTM: lazy property + cache matches the cross-platform goal.

Comment on lines +2 to +3
const fs = require('node:fs')
const { resolvePrebuild } = require('@ast-grep/setup-lang')
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Fix CI: guard against missing resolvePrebuild export from @ast-grep/setup-lang

CI shows "TypeError: resolvePrebuild is not a function". Make the import resilient to older setup-lang builds by avoiding destructuring and falling back to a no-op when the export is absent.

Apply:

-const fs = require('node:fs')
-const { resolvePrebuild } = require('@ast-grep/setup-lang')
+const fs = require('node:fs')
+const setupLang = require('@ast-grep/setup-lang')
+const resolvePrebuild = typeof setupLang.resolvePrebuild === 'function' ? setupLang.resolvePrebuild : () => null

Verification (optional):


🏁 Script executed:

#!/bin/bash
set -euo pipefail
echo "Check that resolvePrebuild is exported in scripts/setup/index.ts"
rg -n "export\s+(function|const)\s+resolvePrebuild" scripts/setup/index.ts || {
  echo "resolvePrebuild not exported from scripts/setup/index.ts"; exit 1; }

echo "Find any remaining destructured imports that can throw at runtime"
rg -n "const\s*\{\s*resolvePrebuild\s*\}\s*=\s*require\('@ast-grep/setup-lang'\)"

Length of output: 367


Fix CI: guard against missing resolvePrebuild export from @ast-grep/setup-lang

CI fails with "TypeError: resolvePrebuild is not a function". Verification shows resolvePrebuild is not exported from scripts/setup/index.ts (rg reported "resolvePrebuild not exported from scripts/setup/index.ts"). Make the import resilient by avoiding destructured require and falling back to a no-op.

Files to change:

  • packages/cpp/index.js — replace the destructured import (lines ~2-3) with a safe require:
-const fs = require('node:fs')
-const { resolvePrebuild } = require('@ast-grep/setup-lang')
+const fs = require('node:fs')
+const setupLang = require('@ast-grep/setup-lang')
+const resolvePrebuild = typeof setupLang.resolvePrebuild === 'function' ? setupLang.resolvePrebuild : () => null
  • scripts/setup/index.ts — currently does not export resolvePrebuild; either export it if intended to be a public API, or keep the consumer-side guard above (recommended).
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const fs = require('node:fs')
const { resolvePrebuild } = require('@ast-grep/setup-lang')
const fs = require('node:fs')
const setupLang = require('@ast-grep/setup-lang')
const resolvePrebuild = typeof setupLang.resolvePrebuild === 'function' ? setupLang.resolvePrebuild : () => null
🤖 Prompt for AI Agents
In packages/cpp/index.js around lines 2 to 3, the destructured require of
resolvePrebuild can throw when @ast-grep/setup-lang doesn't export it; change
the import to require the module as a whole and guard by assigning
resolvePrebuild to the module's export if present and a function, otherwise fall
back to a no-op function. Update the top of the file to use that guarded
resolvePrebuild value so callers won't crash in CI; optionally add a brief
comment explaining the fallback. Ensure no other parts of the file assume
resolvePrebuild is always present without checking.

return native;
}

throw new Error('No parser found. Please ensure the parser is built or a prebuild is available.');
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve the error to aid debugging across platforms and PNPM v10 installs

Make the error actionable by including platform/arch, the attempted local path, and a hint about PNPM’s postinstall allowlist.

Apply this diff:

-  throw new Error('No parser found. Please ensure the parser is built or a prebuild is available.');
+  throw new Error(`No parser found for ${process.platform}-${process.arch}. Tried prebuilds and ${native}. If using pnpm >= 10, ensure postinstall ran (see README for --allow-build usage).`);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
throw new Error('No parser found. Please ensure the parser is built or a prebuild is available.');
throw new Error(`No parser found for ${process.platform}-${process.arch}. Tried prebuilds and ${native}. If using pnpm >= 10, ensure postinstall ran (see README for --allow-build usage).`);
🤖 Prompt for AI Agents
In packages/swift/index.js around line 16, the thrown Error is too generic;
update it to include runtime details and actionable hints by constructing the
error message with platform and architecture (process.platform and
process.arch), the exact local path you attempted to load (build the path with
__dirname and the expected parser filename), and a short note suggesting PNPM
v10 users may need to add the postinstall script to PNPM’s allowlist (or check
that prebuilds exist). Replace the static message with one that concatenates
these values so the error shows platform/arch, the resolved path attempted, and
the PNPM postinstall hint.

Comment on lines +5 to +9
function getLibPath() {
const prebuild = resolvePrebuild(__dirname)
if (prebuild) {
return prebuild;
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Avoid runtime TypeError when resolvePrebuild is absent.

Same concern as other packages: guard the call so we can fall back to the local parser without crashing when resolvePrebuild isn’t exported.

-  const prebuild = resolvePrebuild(__dirname)
+  const prebuild = typeof resolvePrebuild === 'function' ? resolvePrebuild(__dirname) : undefined
   if (prebuild) {
     return prebuild;
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
function getLibPath() {
const prebuild = resolvePrebuild(__dirname)
if (prebuild) {
return prebuild;
}
function getLibPath() {
const prebuild = typeof resolvePrebuild === 'function' ? resolvePrebuild(__dirname) : undefined
if (prebuild) {
return prebuild;
}
🤖 Prompt for AI Agents
In packages/tsx/index.js around lines 5 to 9, the code calls
resolvePrebuild(__dirname) unguarded which will throw a runtime TypeError if
resolvePrebuild is not exported; modify the code to check that resolvePrebuild
is a function before calling it (e.g. typeof resolvePrebuild === 'function' or
optional chaining) and only call it when present, otherwise fall back to the
local parser path so the module doesn't crash when resolvePrebuild is absent.

@HerringtonDarkholme
Copy link
Member

I'm going to merge this first and the remaining issue is actually different issues

@HerringtonDarkholme HerringtonDarkholme merged commit 13682f1 into ast-grep:main Aug 14, 2025
2 of 5 checks passed
@HerringtonDarkholme
Copy link
Member

released

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants