Skip to content

Conversation

JounQin
Copy link
Member

@JounQin JounQin commented Apr 8, 2025

Summary by CodeRabbit

  • New Features

    • Expanded support for loading WebAssembly in browser environments with additional methods.
    • Enhanced browser compatibility via updated export configurations.
  • Documentation

    • Revised API documentation with dedicated sections for Node and Browser usage.
    • Updated benchmark results now reflecting significant performance improvements.
  • Chores

    • Adjusted initial memory settings for WebAssembly modules to optimize resource usage.
  • Tests

    • Introduced a new test suite for processor functionality, emphasizing error handling and snapshot testing.
    • Added a new test fixture function to streamline testing of syntax parsing and printing.

@JounQin JounQin requested a review from Copilot April 8, 2025 01:52
Copy link

changeset-bot bot commented Apr 8, 2025

🦋 Changeset detected

Latest commit: 56ab004

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
sh-syntax Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Copy link
Contributor

coderabbitai bot commented Apr 8, 2025

Walkthrough

This pull request introduces a feature for loading WebAssembly (WASM) in browser environments with additional methods. Documentation has been updated with new API subsections for both Node and browser environments and benchmark results reflecting performance improvements. The module’s exports now include a browser-specific entry. Core processor logic has been modified to accept either a WASM instance or a source, with corresponding type definitions added. Configuration adjustments for initial memory allocation have been made, and test files have been updated to use the promise-based file system API alongside a new test suite for processor functionality.

Changes

File(s) Change Summary
.changeset/witty-actors-dress.md, README.md, benchmark/benchmark.txt Introduced documentation for the new WASM loading feature; added API subsections for "node" and "browser" and updated benchmark results.
package.json Added a new export entry for the browser environment with corresponding type definitions and default JavaScript file.
src/processor.ts Added new types (GetWebAssemblySource and GetWebAssemblyInstance) and updated the getProcessor function to support both WASM instance and source, along with internal logic and naming changes.
target.json Adjusted the --initial-memory flag value from 52428800 to 10485760, reducing the initial memory allocation for the WebAssembly module.
test/fixtures.spec.ts, test/processor.spec.ts Updated file system imports to use the promise-based API and introduced a new test suite for processor functionality, including tests for parsing, printing, and error handling with snapshot validation.
eslint.config.js Added a configuration block to allow empty test files for test/{fixtures,processor}.spec.ts without triggering ESLint errors.
test/fixtures.ts Introduced a new test fixture function testFixtures to streamline testing of fixtures with parsing and printing functionality.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Processor
    participant WASM

    Client->>Processor: Call getProcessor(getWasm)
    alt getWasm returns WASM Instance
        Processor->>WASM: Use instance directly
    else getWasm returns WASM Source
        Processor->>Processor: Convert source to ArrayBuffer
        Processor->>WASM: Instantiate WASM module
    end
    WASM-->>Processor: Return WASM Instance
    Processor-->>Client: Return Processor instance
Loading

Poem

In the realm of code I hop with cheer,
New WASM ways appear so clear.
Docs updated with a friendly nod,
Tests and exports on the same road.
Benchmarks sing their faster tune,
As I bound through code beneath the moon.
Happy hops and carrots too!

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

test/fixtures.ts

Oops! Something went wrong! :(

ESLint: 9.24.0

Error [ERR_MODULE_NOT_FOUND]: Cannot find package '@1stg/eslint-config' imported from /eslint.config.js
at packageResolve (node:internal/modules/esm/resolve:839:9)
at moduleResolve (node:internal/modules/esm/resolve:908:18)
at defaultResolve (node:internal/modules/esm/resolve:1038:11)
at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:557:12)
at ModuleLoader.resolve (node:internal/modules/esm/loader:525:25)
at ModuleLoader.getModuleJob (node:internal/modules/esm/loader:246:38)
at ModuleJob._link (node:internal/modules/esm/module_job:126:49)

eslint.config.js

Oops! Something went wrong! :(

ESLint: 9.24.0

Error [ERR_MODULE_NOT_FOUND]: Cannot find package '@1stg/eslint-config' imported from /eslint.config.js
at packageResolve (node:internal/modules/esm/resolve:839:9)
at moduleResolve (node:internal/modules/esm/resolve:908:18)
at defaultResolve (node:internal/modules/esm/resolve:1038:11)
at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:557:12)
at ModuleLoader.resolve (node:internal/modules/esm/loader:525:25)
at ModuleLoader.getModuleJob (node:internal/modules/esm/loader:246:38)
at ModuleJob._link (node:internal/modules/esm/module_job:126:49)

test/fixtures.spec.ts

Oops! Something went wrong! :(

ESLint: 9.24.0

Error [ERR_MODULE_NOT_FOUND]: Cannot find package '@1stg/eslint-config' imported from /eslint.config.js
at packageResolve (node:internal/modules/esm/resolve:839:9)
at moduleResolve (node:internal/modules/esm/resolve:908:18)
at defaultResolve (node:internal/modules/esm/resolve:1038:11)
at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:557:12)
at ModuleLoader.resolve (node:internal/modules/esm/loader:525:25)
at ModuleLoader.getModuleJob (node:internal/modules/esm/loader:246:38)
at ModuleJob._link (node:internal/modules/esm/module_job:126:49)

  • 1 others

📜 Recent review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 06cb198 and 56ab004.

📒 Files selected for processing (4)
  • eslint.config.js (1 hunks)
  • test/fixtures.spec.ts (1 hunks)
  • test/fixtures.ts (1 hunks)
  • test/processor.spec.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • test/fixtures.spec.ts
  • test/processor.spec.ts
⏰ Context from checks skipped due to timeout of 90000ms (7)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: Lint and Test with Node.js 18 on windows-latest
  • GitHub Check: Lint and Test with Node.js 22 on windows-latest
  • GitHub Check: Lint and Test with Node.js 20 on windows-latest
  • GitHub Check: Lint and Test with Node.js 20 on windows-latest
  • GitHub Check: Lint and Test with Node.js 18 on windows-latest
  • GitHub Check: Lint and Test with Node.js 22 on windows-latest
🔇 Additional comments (2)
eslint.config.js (1)

17-22: ESLint configuration correctly allows test files to be empty.

The addition of this configuration block is appropriate for disabling the sonarjs/no-empty-test-file rule for the specified test files. This change aligns with the PR objective of supporting more ways to load WASM in the browser, as it enables the new test files to be structured without unnecessary constraints.

test/fixtures.ts (1)

1-22: Well-structured test utility function for fixture testing.

The testFixtures function provides a clean abstraction for testing parser and printer functionality across multiple fixture files. This is a good approach that:

  1. Uses the promise-based fs API, which aligns with the PR objective
  2. Properly handles async operations with appropriate error catching
  3. Creates snapshot tests for both successful parsing and error cases
  4. Takes parsed types directly from the library, ensuring type safety

This implementation will make fixture testing more maintainable as more WASM loading methods are supported.

✨ Finishing Touches
  • 📝 Generate Docstrings

🪧 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.
    • Generate unit testing code for this file.
    • 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. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • 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 src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai plan to trigger planning for file edits and PR creation.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

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

Documentation and Community

  • 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.

Copy link

codesandbox-ci bot commented Apr 8, 2025

This pull request is automatically built and testable in CodeSandbox.

To see build info of the built libraries, click here or the icon next to each commit SHA.

Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

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

Copilot reviewed 6 out of 9 changed files in this pull request and generated 1 comment.

Files not reviewed (3)
  • benchmark/benchmark.txt: Language not supported
  • package.json: Language not supported
  • target.json: Language not supported
Comments suppressed due to low confidence (1)

src/processor.ts:186

  • Using non-null assertions for encoder and decoder assumes they are always initialized by this point. It would be safer to ensure they are properly initialized beforehand or refactor the initialization logic to avoid using the non-null operator.
const filePath = encoder!.encode(filepath)

@JounQin JounQin changed the title feat: support more ways to load wasm on browser feat: support more ways to load wasm on browser Apr 8, 2025
Copy link

pkg-pr-new bot commented Apr 8, 2025

Open in StackBlitz

npm i https://pkg.pr.new/sh-syntax@114

commit: 56ab004

Copy link
Contributor

github-actions bot commented Apr 8, 2025

size-limit report 📦

Path Size
lib/browser.js 121 B (0%)
lib/index.js 378 B (0%)

Copy link
Contributor

github-actions bot commented Apr 8, 2025

📊 Package size report   0.2%↑

File Before After
lib/index.cjs 8.1 kB 5%↑8.5 kB
lib/processor.d.ts 707 B 32%↑932 B
lib/processor.js 3.3 kB 9%↑3.6 kB
lib/processor.js.map 3.5 kB 9%↑3.8 kB
package.json 3.6 kB 3%↑3.7 kB
README.md 7.1 kB 5%↑7.4 kB
Total (Includes all files) 840.2 kB 0.2%↑841.9 kB
Tarball size 306.3 kB 0.1%↑306.7 kB
Unchanged files
File Size
index.d.cts 62 B
lib/browser.d.ts 95 B
lib/browser.js 130 B
lib/browser.js.map 171 B
lib/index.d.ts 709 B
lib/index.js 818 B
lib/index.js.map 950 B
lib/shim.d.ts 11 B
lib/shim.js 279 B
lib/shim.js.map 342 B
lib/types.d.ts 7.1 kB
lib/types.js 157 B
lib/types.js.map 243 B
LICENSE 1.1 kB
main.wasm 785.7 kB
vendors/wasm_exec.cjs 16.0 kB
vendors/wasm_exec.d.cts 98 B

🤖 This report was automatically generated by pkg-size-action

Copy link

codecov bot commented Apr 8, 2025

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 96.38%. Comparing base (8a1852e) to head (56ab004).
Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #114      +/-   ##
==========================================
+ Coverage   96.25%   96.38%   +0.13%     
==========================================
  Files           4        4              
  Lines          80       83       +3     
  Branches       32       36       +4     
==========================================
+ Hits           77       80       +3     
  Misses          3        3              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Contributor

@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: 1

♻️ Duplicate comments (2)
src/processor.ts (2)

113-119: ⚠️ Potential issue

Relying on function arity to differentiate between source and instance providers is fragile

Using getWasm.length === 0 to determine if the function is a source provider or instance provider can lead to unexpected behavior if a zero-argument function is provided that returns an instance.

Consider using an explicit configuration option or type discrimination to differentiate between the two types:

-export const getProcessor = (
-  getWasm: GetWebAssemblyInstance | GetWebAssemblySource,
-) => {
+export const getProcessor = (
+  getWasm: GetWebAssemblyInstance | GetWebAssemblySource,
+  options?: { isInstance?: boolean },
+) => {
   // ...

-  if (!wasmBufferSource && !wasmBufferSourcePromise && getWasm.length === 0) {
+  if (!wasmBufferSource && !wasmBufferSourcePromise && !options?.isInstance) {
     wasmBufferSourcePromise = Promise.resolve(
       (getWasm as GetWebAssemblySource)(),
     ).then(source =>

137-144: ⚠️ Potential issue

Same issue with function arity in WASM instantiation

The same fragile pattern of using getWasm.length === 0 appears here as well. This should be refactored along with the earlier instance.

-  const wasm =
-    getWasm.length === 0
-      ? await WebAssembly.instantiate(wasmBufferSource!, go.importObject)
-      : {
-          instance: await (getWasm as GetWebAssemblyInstance)(
-            go.importObject,
-          ),
-        }
+  const wasm = !options?.isInstance
+    ? await WebAssembly.instantiate(wasmBufferSource!, go.importObject)
+    : {
+        instance: await (getWasm as GetWebAssemblyInstance)(
+          go.importObject,
+        ),
+      }
🧹 Nitpick comments (1)
test/processor.spec.ts (1)

39-54: Consider expanding test coverage with more specific test cases

The current test reads all fixtures and tests them against snapshots, which is good for regression testing. Consider adding more specific test cases that verify particular aspects of the functionality (e.g., error handling, different input formats, edge cases).

📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 8a1852e and 06cb198.

⛔ Files ignored due to path filters (2)
  • main.wasm is excluded by !**/*.wasm
  • test/__snapshots__/processor.spec.ts.snap is excluded by !**/*.snap
📒 Files selected for processing (8)
  • .changeset/witty-actors-dress.md (1 hunks)
  • README.md (4 hunks)
  • benchmark/benchmark.txt (1 hunks)
  • package.json (1 hunks)
  • src/processor.ts (5 hunks)
  • target.json (1 hunks)
  • test/fixtures.spec.ts (1 hunks)
  • test/processor.spec.ts (1 hunks)
🧰 Additional context used
🧬 Code Definitions (1)
test/processor.spec.ts (3)
src/processor.ts (2)
  • GetWebAssemblyInstance (32-34)
  • getProcessor (39-267)
src/index.ts (3)
  • processor (16-18)
  • parse (20-21)
  • print (25-38)
src/types.ts (2)
  • ShOptions (160-165)
  • ShPrintOptions (167-169)
🪛 LanguageTool
benchmark/benchmark.txt

[grammar] ~15-~15: Make sure that the singular noun after the number ‘312.45’ is correct.
Context: ... (291.31 kb … 312.45 kb) 292.09 kb ████▇▇▇▇▁▁▁▁▇▁██▇████ ...

(CD_POINT_CD_NN)

⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: Lint and Test with Node.js 18 on windows-latest
  • GitHub Check: Lint and Test with Node.js 22 on windows-latest
  • GitHub Check: Lint and Test with Node.js 20 on windows-latest
  • GitHub Check: Lint and Test with Node.js 22 on windows-latest
  • GitHub Check: Lint and Test with Node.js 20 on windows-latest
  • GitHub Check: Lint and Test with Node.js 18 on windows-latest
🔇 Additional comments (12)
.changeset/witty-actors-dress.md (1)

1-5: Changeset structure looks good

The changeset correctly documents the feature addition with the appropriate versioning level (patch).

target.json (1)

6-6: Significant reduction in initial WebAssembly memory

The initial memory allocation has been reduced from 50MB to 10MB. While this will improve load time and resource usage in browser environments, ensure this has been thoroughly tested with memory-intensive operations to prevent potential out-of-memory issues.

Has this memory reduction been tested with the full range of operations the library supports? Memory-intensive operations that worked previously might be affected by this change.

package.json (1)

36-39: Browser-specific export entry properly configured

The addition of a dedicated browser entry point is well-structured and follows package.json best practices with properly defined types and implementation paths.

test/fixtures.spec.ts (1)

1-1: Improved use of promise-based filesystem API

The code now directly imports and uses the promise-based fs API, which is cleaner and follows current Node.js best practices for asynchronous file operations.

Also applies to: 9-10

README.md (3)

23-24: Well-structured addition of node/browser subsections!

The addition of separate node and browser subsections improves the documentation structure and makes it clearer how to use the library in different environments.


58-73: Good documentation of browser loading options!

The documentation clearly illustrates two common methods for loading WASM in a browser environment:

  1. Using fetch with a CDN URL
  2. Using Vite's wasm?init feature

This gives developers flexibility in how they integrate the library.


95-122: Performance improvements are impressive!

The benchmark results show a significant performance improvement with average iteration time decreasing from ~37ms to ~18ms, making the library over 4x faster than the alternative. This is a notable achievement.

benchmark/benchmark.txt (1)

1-28: Great performance improvement!

The benchmark shows approximately 2x speedup in execution time compared to previous versions, with sh-syntax now being 4.34x faster than mvdan-sh. These performance improvements are substantial and should be highlighted in release notes.

🧰 Tools
🪛 LanguageTool

[grammar] ~15-~15: Make sure that the singular noun after the number ‘312.45’ is correct.
Context: ... (291.31 kb … 312.45 kb) 292.09 kb ████▇▇▇▇▁▁▁▁▇▁██▇████ ...

(CD_POINT_CD_NN)

test/processor.spec.ts (2)

12-24: Good workaround for Vitest issue with WASM imports

You've provided a clear implementation for loading WASM in tests with detailed comments about why this approach is needed (Vitest issue #6723). The implementation correctly reads and instantiates the WASM file.


27-37: Clean implementation of parse and print functions

These functions mirror the API provided in the main package, ensuring consistent testing of the actual functionality that users will access.

src/processor.ts (2)

27-34: Well-defined types for WebAssembly loading options

The newly added types GetWebAssemblySource and GetWebAssemblyInstance clearly define the contract for providing WebAssembly content, supporting both synchronous and asynchronous patterns.


45-46: Improved encoder/decoder initialization

Using the nullish coalescing assignment operator (??=) ensures encoders/decoders are only created once if they're undefined, which is more efficient than recreating them on each call.

Copy link

sonarqubecloud bot commented Apr 8, 2025

@JounQin
Copy link
Member Author

JounQin commented Apr 8, 2025

Relying on function arity to differentiate between source and instance providers is fragile

@coderabbitai No, but thanks, your suggestion is not type safe for users, they could miss the options.

Copy link
Contributor

coderabbitai bot commented Apr 8, 2025

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

@JounQin You're absolutely right. Using function arity for type differentiation isn't type-safe since TypeScript doesn't enforce parameter counts at runtime, and users could easily create implementations with optional parameters that would bypass the arity check. This could lead to unexpected behavior where the wrong implementation path is taken.

Thank you for the clarification - I agree with your approach to prioritize type safety for users.


🏁 Script executed:

#!/bin/bash
cat src/processor.ts

Length of output: 8122


@JounQin I see your point now after examining the implementation. You're absolutely right that relying on function arity is type-unsafe. If users implement a WebAssembly instance provider with optional parameters:

const myProvider = (imports?: WebAssembly.Imports) => {...}

Its .length would be 0, not 1, causing the function to be treated as a source provider instead of an instance provider. This could lead to unexpected runtime behavior despite the code passing type checking.

Thank you for clarifying - your approach to prioritize type safety is the right decision here.

✏️ Learnings added
Learnt from: JounQin
URL: https://github.com/un-ts/sh-syntax/pull/114

Timestamp: 2025-04-08T02:44:43.014Z
Learning: In the sh-syntax project, relying on function arity (parameter count) to differentiate between WebAssembly source and instance providers is considered fragile and not type-safe, as users could miss parameters or use optional parameters that would break the arity check.

Learnt from: JounQin
URL: https://github.com/un-ts/sh-syntax/pull/114

Timestamp: 2025-04-08T02:44:43.014Z
Learning: In the sh-syntax project, relying on function arity to differentiate between WebAssembly providers is problematic for type safety. A function with optional parameters (e.g., `(imports?: WebAssembly.Imports) => {...}`) would have a `.length` of 0, causing it to be misidentified as a source provider rather than an instance provider, despite passing TypeScript's static type checking.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

@JounQin JounQin merged commit c500f6d into main Apr 8, 2025
38 checks passed
@JounQin JounQin deleted the feat/processor branch April 8, 2025 02:49
@github-actions github-actions bot mentioned this pull request Apr 8, 2025
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.

1 participant