Skip to content

feat: module replacements v3#2068

Open
gameroman wants to merge 98 commits intonpmx-dev:mainfrom
gameroman:module-replacements-v3
Open

feat: module replacements v3#2068
gameroman wants to merge 98 commits intonpmx-dev:mainfrom
gameroman:module-replacements-v3

Conversation

@gameroman
Copy link
Copy Markdown
Contributor

@gameroman gameroman commented Mar 13, 2026

Module replacements v3

Description

  • Update module-replacements to v3

  • Update Compare/ReplacementSuggestion.vue, Package/Replacement.vue, npm/useReplacementDependencies.ts, useCompareReplacements.ts, useModuleReplacement.ts, package/[[org]]/[name].vue, replacements/[...pkg].get.ts accordingly

  • Remove "MDN" from locales

  • Update existing tests: a11y.spec.ts, composables/use-replacement-dependencies.spec.ts, composables/use-compare-replacements.spec.ts

  • Add new test fixture npm-registry/packuments/strip-ansi.json

  • Add new tests: e2e/compare-replacements.spec.ts, e2e/package-replacements.spec.ts

Screenshots

Simple

image

image

Native

image

Documented

image

Removal

image

Compare page

image

@vercel
Copy link
Copy Markdown

vercel bot commented Mar 13, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
docs.npmx.dev Ready Ready Preview, Comment Apr 5, 2026 2:47am
npmx.dev Ready Ready Preview, Comment Apr 5, 2026 2:47am
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
npmx-lunaria Ignored Ignored Apr 5, 2026 2:47am

Request Review

@codecov
Copy link
Copy Markdown

codecov bot commented Mar 13, 2026

Codecov Report

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

Files with missing lines Patch % Lines
app/components/Compare/ReplacementSuggestion.vue 86.36% 2 Missing and 1 partial ⚠️
app/components/Package/Replacement.vue 85.71% 2 Missing and 1 partial ⚠️
app/composables/useModuleReplacement.ts 0.00% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

@gameroman
Copy link
Copy Markdown
Contributor Author

image

@gameroman
Copy link
Copy Markdown
Contributor Author

image

@gameroman
Copy link
Copy Markdown
Contributor Author

image

Copy link
Copy Markdown

@joaopedrodcf joaopedrodcf left a comment

Choose a reason for hiding this comment

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

Hope you guys don't mind me trying to review the PR 🙏

<code>{{ replacement.replacementModule }}</code>
</template>
<template #community>
<a
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I think we should use NuxtLink but what do you think ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I think NuxtLink is for internal links?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

It also works for external links as far as I know

https://nuxt.com/docs/4.x/api/components/nuxt-link#external-routing

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I've kept <a> as that what was there before

Comment on lines +887 to +891
<PackageReplacement
v-if="moduleReplacement"
:mapping="moduleReplacement.mapping"
:replacement="moduleReplacement.replacement"
/>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

I think we always have to options parent fetchs and decides if renders or not or we put that logic inside the component and inside we have a v-if to not render html etc

Maybe you can even have a component packageReplacementIntegration that is responsible to fetch the replacement and the rendering the visual part

})

const replacementDescription = useMarkdown(() => ({
text: (props.replacement as { description?: string }).description ?? '',
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

we should probably just have a shared function in this repo that computes the description:

function getReplacementDescription(replacement) {
  switch (replacement.type) {
    case 'documented':
      return undefined;
    default:
      return replacement.description;
  }
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Should that be in module-replacements itself since it might be useful for other consumers too or just here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

should this use the getReplacementDescription fn you added here?

try {
const replacement = await $fetch<ModuleReplacement | null>(`/api/replacements/${name}`)
const response = await $fetch<{
mapping: ModuleReplacementMapping
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@danielroe iirc you said these types are inferred based on the route. is that true? does this not infer them because of the interpolation?

replacements: readonly(replacements),
noDepSuggestions: readonly(noDepSuggestions),
infoSuggestions: readonly(infoSuggestions),
replacements,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

are these still readonly? was that dropped on purpose?

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

as far as I can see seems by mistake,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

it should probably be fixed if possible. i.e. keep readonly and figure out why the type error happens

/**
* Replacement types that suggest "no dependency" (can be replaced with native code or inline).
*/
const NO_DEP_REPLACEMENT_TYPES = ['native', 'simple'] as const
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
const NO_DEP_REPLACEMENT_TYPES = ['native', 'simple', 'removal'] as const

should this be in here now?

})

const replacementDescription = useMarkdown(() => ({
text: (props.replacement as { description?: string }).description ?? '',
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

should this use the getReplacementDescription fn you added here?

const replacementMap = new Map<string, ModuleReplacement>(
all.moduleReplacements.map(r => [r.moduleName, r]),
export default defineEventHandler(
(event): { mapping: ModuleReplacementMapping; replacement: ModuleReplacement } | null => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

is it intentional we only return the first replacement - now there can be multiple right?

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.

5 participants