Skip to content

Conversation

@knotbin
Copy link
Contributor

@knotbin knotbin commented Aug 4, 2025

Summary by CodeRabbit (edited by me)

  • Bug Fixes
    • To fix compatibility with Deno which doesn't support module.register, environments that natively support TypeScript (including Deno) now

closes #1393

Checklist

  • Tests updated (or not required).
  • Documentation updated (or not required).
  • Changeset added, and when a BREAKING CHANGE occurs, it needs to be clearly marked (or not required).

Summary by CodeRabbit

  • Bug Fixes

    • Improved compatibility with the Deno runtime, ensuring smooth configuration loading and TypeScript support detection.
  • Chores

    • Added a changeset to document the compatibility update.

@knotbin knotbin requested a review from colinaaa as a code owner August 4, 2025 04:38
@changeset-bot
Copy link

changeset-bot bot commented Aug 4, 2025

🦋 Changeset detected

Latest commit: c59e9e7

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

This PR includes changesets to release 3 packages
Name Type
@lynx-js/rspeedy Patch
create-rspeedy Patch
upgrade-rspeedy 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

@CLAassistant
Copy link

CLAassistant commented Aug 4, 2025

CLA assistant check
All committers have signed the CLA.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 4, 2025

📝 Walkthrough

Walkthrough

A new changeset was added to document a patch update for @lynx-js/rspeedy, addressing Deno compatibility. In the loadConfig.ts file, logic was updated to better detect Deno environments and conditionally register module loaders, refining how native TypeScript and JavaScript files are handled when running under Deno.

Changes

Cohort / File(s) Change Summary
Changeset Addition
.changeset/clear-candies-start.md
Added a changeset file describing a patch update for Deno compatibility.
Deno Compatibility & Loader Logic
packages/rspeedy/core/src/config/loadConfig.ts
Refined detection of Deno runtime and native TypeScript support; updated logic to conditionally register module loaders and introduced helper functions for environment checks.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~7 minutes

Assessment against linked issues

Objective Addressed Explanation
Improve rspeedy module resolution to handle modules installed with deno install (#1393)
Ensure logic accounts for Deno-specific module resolution and runtime detection (#1393)

Suggested reviewers

  • colinaaa

Poem

A patch for Deno, swift and neat,
Now rspeedy’s module load is complete.
With loaders checked and helpers new,
Deno runs as smooth as dew.
The bunny hops with glee today—
Compatibility’s here to stay! 🐇✨

Note

⚡️ Unit Test Generation is now available in beta!

Learn more here, or try it out under "Finishing Touches" below.


📜 Recent review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 64ce3fd and c59e9e7.

📒 Files selected for processing (1)
  • packages/rspeedy/core/src/config/loadConfig.ts (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/rspeedy/core/src/config/loadConfig.ts
✨ 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.
    • Explain this complex logic.
    • 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 explain this code block.
  • 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 explain its main purpose.
    • @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 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 generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @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.

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
Collaborator

@colinaaa colinaaa left a comment

Choose a reason for hiding this comment

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

Thank you for your PR! Just a few comments

* @returns - The `unregister` function.
*/
export function register(options) {
// Check for Deno environment first
Copy link
Collaborator

Choose a reason for hiding this comment

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

We already have a hasNativeTSSupport function at:

function hasNativeTSSupport(): boolean {

which is based on process.features.typescript.

Although deno does not support process.features.typescript yet :)

Image

But it would be great to have the similar logic together.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I tried just adding the Deno logic to hasNativeTSSupport but it resulted in the same error when trying to run with Deno because the current logic assumes nativeTsSupport runtimes still support module.register.

I ended up using hasNativeTSSupport in a conditional for running register.

This only meant I had to change the commonConfig.js imports to commonConfig.ts in web-tests because these specific tests are run directly from the TypeScript source and not compiled into JS.

All other tests and libraries built perfectly fine with no changes and all tests pass.

I understand this change may be too much for just adding Deno support and I really did want to keep it isolated but that would have meant using the original change or making the conditional specific to only Deno. Please let me know if this meets your standards.

Copy link
Collaborator

Choose a reason for hiding this comment

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

We are using register without condition to fix #1406.

I do think it would be necessary to keep this way since there could be a lot of user that are doing this way (import with .js for a .ts file) since this is the default of TypeScript.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I would prefer something like this:

  const unregister = shouldUseNativeImport(configPath)
    ? /** noop */ () => void 0
    : register({
      load: !hasNativeTSSupport(),
      resolve: true,
    })


function shouldUseNativeImport(configPath: string): boolean {
  return isJavaScriptPath(configPath) || isDeno()
}

function isDeno(): boolean {
  // @ts-expect-error - Deno global may not be defined in Node.js
  if (typeof Deno !== 'undefined' || process.versions?.deno) {
    return true
  }
  return false
}

wdyt

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's a perfect solution! Sorry I tried a bunch of combinations of isJavaScriptPath, hasNativeTSSupport and the Deno logic but this one is clearly the most logical. Just updated it to reflect this and removed the web-test changes. Let me know what you think.

@knotbin knotbin requested a review from PupilTong as a code owner August 4, 2025 21:49
@knotbin knotbin requested a review from colinaaa August 5, 2025 03:48
Copy link
Collaborator

@colinaaa colinaaa left a comment

Choose a reason for hiding this comment

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

We need to be compatible with using .js to import .ts file. Please consider make a change to shouldUseNativeImport instead.

* @returns - The `unregister` function.
*/
export function register(options) {
// Check for Deno environment first
Copy link
Collaborator

Choose a reason for hiding this comment

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

We are using register without condition to fix #1406.

I do think it would be necessary to keep this way since there could be a lot of user that are doing this way (import with .js for a .ts file) since this is the default of TypeScript.

* @returns - The `unregister` function.
*/
export function register(options) {
// Check for Deno environment first
Copy link
Collaborator

Choose a reason for hiding this comment

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

I would prefer something like this:

  const unregister = shouldUseNativeImport(configPath)
    ? /** noop */ () => void 0
    : register({
      load: !hasNativeTSSupport(),
      resolve: true,
    })


function shouldUseNativeImport(configPath: string): boolean {
  return isJavaScriptPath(configPath) || isDeno()
}

function isDeno(): boolean {
  // @ts-expect-error - Deno global may not be defined in Node.js
  if (typeof Deno !== 'undefined' || process.versions?.deno) {
    return true
  }
  return false
}

wdyt

@knotbin knotbin requested a review from colinaaa August 5, 2025 04:57
@knotbin knotbin changed the title fix rspeedy deno compat fix(rspeedy/core): deno compat Aug 5, 2025
@codecov
Copy link

codecov bot commented Aug 5, 2025

Codecov Report

❌ Patch coverage is 88.23529% with 2 lines in your changes missing coverage. Please review.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
packages/rspeedy/core/src/config/loadConfig.ts 88.23% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

@codspeed-hq
Copy link

codspeed-hq bot commented Aug 5, 2025

CodSpeed Performance Report

Merging #1412 will not alter performance

Comparing knotbin:main (c59e9e7) with main (48be4b8)

Summary

✅ 10 untouched benchmarks

@relativeci
Copy link

relativeci bot commented Aug 5, 2025

React Example

#3678 Bundle Size — 235.14KiB (0%).

c59e9e7(current) vs 5450d00 main#3660(baseline)

Bundle metrics  no changes
                 Current
#3678
     Baseline
#3660
No change  Initial JS 0B 0B
No change  Initial CSS 0B 0B
No change  Cache Invalidation 0% 0%
No change  Chunks 0 0
No change  Assets 4 4
No change  Modules 156 156
No change  Duplicate Modules 63 63
No change  Duplicate Code 45.94% 45.94%
No change  Packages 2 2
No change  Duplicate Packages 0 0
Bundle size by type  no changes
                 Current
#3678
     Baseline
#3660
No change  IMG 145.76KiB 145.76KiB
No change  Other 89.38KiB 89.38KiB

Bundle analysis reportBranch knotbin:mainProject dashboard


Generated by RelativeCIDocumentationReport issue

Copy link
Collaborator

@colinaaa colinaaa left a comment

Choose a reason for hiding this comment

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

LGTM! Thank you!

@relativeci
Copy link

relativeci bot commented Aug 5, 2025

Web Explorer

#3668 Bundle Size — 342.17KiB (0%).

c59e9e7(current) vs 5450d00 main#3652(baseline)

Bundle metrics  Change 1 change
                 Current
#3668
     Baseline
#3652
No change  Initial JS 142.14KiB 142.14KiB
No change  Initial CSS 31.84KiB 31.84KiB
No change  Cache Invalidation 0% 0%
No change  Chunks 7 7
No change  Assets 7 7
Change  Modules 211(-0.47%) 212
No change  Duplicate Modules 17 17
No change  Duplicate Code 4.02% 4.02%
No change  Packages 4 4
No change  Duplicate Packages 0 0
Bundle size by type  no changes
                 Current
#3668
     Baseline
#3652
No change  JS 227.22KiB 227.22KiB
No change  Other 83.11KiB 83.11KiB
No change  CSS 31.84KiB 31.84KiB

Bundle analysis reportBranch knotbin:mainProject dashboard


Generated by RelativeCIDocumentationReport issue

@colinaaa colinaaa merged commit c071a44 into lynx-family:main Aug 5, 2025
135 of 152 checks passed
@colinaaa
Copy link
Collaborator

colinaaa commented Aug 6, 2025

Hi @knotbin, I still find it not working with Deno even with this change is merged.

I've run:

deno run -A npm:create-rspeedy-canary
deno install
deno run build

which results in:

error: Uncaught (in promise) TypeError: Could not find package '@lynx-js/chunk-loading-webpack-plugin' from referrer '/root/deno-project/node_modules/@lynx-js/rspeedy/dist/cli/main.js'.

I'm not fimiliar with Deno, but as you can see in packages/rspeedy/core/package.json, the @lynx-js/chunk-loading-webpack-plugin is a direct dependency of @lynx-js/rspeedy. And npm run build would work just fine after deno install. So I beleive this should be a bug of Deno.

@knotbin
Copy link
Contributor Author

knotbin commented Aug 6, 2025

Hi @colinaaa, I'm sorry this didn't fully fix the issue.

This issue is why the PR initially forced rspeedy into unmanaged mode. Rspeedy now works in Deno if you run deno task build --unmanaged which wasn't true before the PR.

More details below but the reason I didn't catch this when I made the PR was that I wasn't getting this error when I ran in the examples/react dir and that was because the @lynx-js/rspeedy dependency was set to a workspace dependency.

Deno installs dependencies into the node-modules folder in a different pattern than node would but symlinks them to where node would expect them to be. I assume this symlinking has been specifically done to work with node and that's why running npm run build or npm run dev works after deno install. If you install dependencies with npm install and then run deno task build or deno task dev it works fine.

It seems like the problem is specifically with resolving dependencies from that specific endpoint (node_modules/@lynx-js/rspeedy/dist/cli/main.js which if you command+click it, you'll see is actually symlinked to node_modules/.deno/@[email protected]/node_modules/@lynx-js/rspeedy-canary/dist/cli/main.js) because I wasn't getting this error when I ran in the examples/react dir which I now realize was because the @lynx-js/rspeedy dependency was set to a workspace dependency.

I'll do some digging to make this work without --unmanaged. Rspack doesn't any errors like this so I'll look at the difference between the module resolution logic in the two (and if there's anything you can add about that, that would be a great help) and/or see if I can find why this might be happening on the Deno side.

@colinaaa
Copy link
Collaborator

colinaaa commented Aug 6, 2025

Thanks for your explanation @knotbin!

For canary versions, we use npm aliases:

{
  "dependencies": {
    "@lynx-js/chunk-loading-webpack-plugin": "npm:@lynx-js/chunk-loading-webpack-plugin-canary@version"
  }
}

I'm not sure if the error is caused by npm alias, since it changes the package name.

- node_modules/.deno/@[email protected]/node_modules/
  - @lynx-js/rspeedy-canary
  - @lynx-js/chunk-loading-webpack-plugin-canary

It seems like we can only find @lynx-js/chunk-loading-webpack-plugin-canary but not @lynx-js/chunk-loading-webpack-plugin in node_modules/@lynx-js/rspeedy/dist/cli/main.js (@lynx-js/rspeedy-canary

@knotbin
Copy link
Contributor Author

knotbin commented Aug 6, 2025

@colinaaa I've traced the issue to the fact that start.ts (called by rspeedy.js) seems to override the runtime's module resolution and implement a custom resolver using very jank filesystem logic to load the main.ts. It appears that loading main.ts this way without using import or require confuses Deno and causes it to load the file either without any context or with the context of start.ts. --unmanaged fixes this because it just loads the main.ts file by importing it .

Trying to reimplement an import seems like a surefire way to introduce inconsistencies in resolution and bugs like the Deno one here. Package managers like PNPM, Deno, Bun, Yarn and even NPM now already handle resolving workspace dependencies gracefully.

I might be missing something but the --unmanaged flag is something I've never seen before in a package. In all the other tools I've used, the functionality of the --unmanaged flag is built in as the default. There's probably some use case where it makes sense to have the "managed" mode but I've never seen it used in any other CLI including Rspack, so I think it makes much more sense (to me) to have unmanaged as the default.

I'm probably missing something, because the start.ts file is just part of the initial commit so it's hard for me to see why the functionality was added, so me know if there's something I'm not thinking of.

colinaaa pushed a commit that referenced this pull request Aug 9, 2025
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.


# Releases
## @lynx-js/[email protected]

### Patch Changes

- Supports `recyclable` attribute in `<list-item>` to control whether
the list item is recyclable. The `recyclable` attribute depends on Lynx
Engine 3.4 or later.
([#1388](#1388))

    ```jsx
    <list-item recyclable={false} />
    ```

- feat: Support using a host element as direct child of Suspense
([#1455](#1455))

- Add profile in production build:
([#1336](#1336))

    1.  `diff:__COMPONENT_NAME__`: how long ReactLynx diff took.
    2.  `render:__COMPONENT_NAME__`: how long your render function took.
3. `setState`: an instant trace event, indicate when your setState was
called.

NOTE: `__COMPONENT_NAME__` may be unreadable when minified, setting
`displayName` may help.

- Add `onBackgroundSnapshotInstanceUpdateId` event on dev for Preact
Devtools to keep the correct snapshotInstanceId info.
([#1173](#1173))

- fix: Prevent error when spreading component props onto an element
([#1459](#1459))

- fix: Correctly check for the existence of background functions in MTS
([#1416](#1416))

    ```ts
    function handleTap() {
      "main thread";
      // The following check always returned false before this fix
      if (myHandleTap) {
        runOnBackground(myHandleTap)();
      }
    }
    ```

## @lynx-js/[email protected]

### Patch Changes

- Remove the experimental `provider` option.
([#1432](#1432))

- Add `output.filename.wasm` and `output.filename.assets` options.
([#1449](#1449))

- fix deno compatibility
([#1412](#1412))

- Should call the `api.onCloseBuild` hook after the build finished.
([#1446](#1446))

- Bump Rsbuild v1.4.15.
([#1423](#1423))

- Support using function in `output.filename.*`.
([#1449](#1449))

## [email protected]

### Patch Changes

- Support ESLint for ReactLynx templates
([#1274](#1274))

## @lynx-js/[email protected]

### Patch Changes

- Updated dependencies
\[[`c8ce6aa`](c8ce6aa)]:
    -   @lynx-js/[email protected]
    -   @lynx-js/[email protected]
    -   @lynx-js/[email protected]
    -   @lynx-js/[email protected]

## @lynx-js/[email protected]

### Patch Changes

- Fix the `Package subpath './compat' is not defined by "exports"`
error. ([#1460](#1460))

## @lynx-js/[email protected]

### Patch Changes

- Fix `GlobalEventEmitter` type definition, the `emit(eventName: string,
data: unknown)` function should recevie an array typed `data` and pass
as param list of listeners.
([#1479](#1479))

## @lynx-js/[email protected]

### Patch Changes

- fix: load main-thread chunk in ESM format
([#1437](#1437))

See [nodejs/node#59362](nodejs/node#59362) for
more details.

- feat: support path() for `createQuerySelector`
([#1456](#1456))

- Added `getPathInfo` API to `NativeApp` and its cross-thread handler
for retrieving the path from a DOM node to the root.
- Implemented endpoint and handler registration in both background and
UI threads.
    -   Implemented `nativeApp.getPathInfo()`

-   Updated dependencies \[]:
    -   @lynx-js/[email protected]

## @lynx-js/[email protected]

### Patch Changes

- fix: load main-thread chunk in ESM format
([#1437](#1437))

See [nodejs/node#59362](nodejs/node#59362) for
more details.

- feat: support path() for `createQuerySelector`
([#1456](#1456))

- Added `getPathInfo` API to `NativeApp` and its cross-thread handler
for retrieving the path from a DOM node to the root.
- Implemented endpoint and handler registration in both background and
UI threads.
    -   Implemented `nativeApp.getPathInfo()`

- fix: when `onNativeModulesCall` is delayed in mounting, the
NativeModules execution result may be undefined.
([#1457](#1457))

- fix: `onNativeModulesCall` && `onNapiModulesCall` use getter to get.
([#1466](#1466))

- Updated dependencies
\[[`29434ae`](29434ae),
[`fb7096b`](fb7096b)]:
    -   @lynx-js/[email protected]
    -   @lynx-js/[email protected]
    -   @lynx-js/[email protected]
    -   @lynx-js/[email protected]

## @lynx-js/[email protected]

### Patch Changes

- fix: load main-thread chunk in ESM format
([#1437](#1437))

See [nodejs/node#59362](nodejs/node#59362) for
more details.

## @lynx-js/[email protected]

### Patch Changes

- feat: add autocomplete attribute support for x-input component
([#1444](#1444))

Implements autocomplete attribute forwarding from the x-input custom
element to the internal HTML input element in the shadow DOM. This
enables standard browser autocomplete functionality for x-input
elements.

- Add referrerpolicy attribute support to x-image web component
([#1420](#1420))

-   Updated dependencies \[]:
    -   @lynx-js/[email protected]

## @lynx-js/[email protected]

### Patch Changes

- fix: load main-thread chunk in ESM format
([#1437](#1437))

See [nodejs/node#59362](nodejs/node#59362) for
more details.

- Updated dependencies
\[[`29434ae`](29434ae),
[`fb7096b`](fb7096b)]:
    -   @lynx-js/[email protected]
    -   @lynx-js/[email protected]

## @lynx-js/[email protected]

### Patch Changes

- feat: support path() for `createQuerySelector`
([#1456](#1456))

- Added `getPathInfo` API to `NativeApp` and its cross-thread handler
for retrieving the path from a DOM node to the root.
- Implemented endpoint and handler registration in both background and
UI threads.
    -   Implemented `nativeApp.getPathInfo()`

- Updated dependencies
\[[`29434ae`](29434ae),
[`fb7096b`](fb7096b)]:
    -   @lynx-js/[email protected]
    -   @lynx-js/[email protected]
    -   @lynx-js/[email protected]

## [email protected]



## @lynx-js/[email protected]



## @lynx-js/[email protected]



## @lynx-js/[email protected]

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
@colinaaa
Copy link
Collaborator

Thank you @knotbin for your reply! Sorry for the delay :)

I might be missing something but the --unmanaged flag is something I've never seen before in a package.

Both the idea and the implementation came from heft. There are tools like turbo also use the same mechanism.

It seems like the implementation is not good enough since it simulate the Node.js resolve.

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.

rspeedy doesn't resolve modules installed with deno install

3 participants