From 2231fd72eb1d806edae09e96694329a2cb360df5 Mon Sep 17 00:00:00 2001
From: eryue0220
Date: Wed, 4 Feb 2026 18:15:22 +0800
Subject: [PATCH 1/9] feat: add deprecate feature
---
.../Package/DeprecatePackageModal.vue | 176 ++++++++++++++++++
app/pages/package/[...package].vue | 35 ++++
cli/src/npm-client.ts | 27 +++
cli/src/schemas.ts | 16 ++
cli/src/server.ts | 9 +
cli/src/types.ts | 1 +
lunaria/files/en-GB.json | 15 +-
lunaria/files/en-US.json | 15 +-
8 files changed, 292 insertions(+), 2 deletions(-)
create mode 100644 app/components/Package/DeprecatePackageModal.vue
diff --git a/app/components/Package/DeprecatePackageModal.vue b/app/components/Package/DeprecatePackageModal.vue
new file mode 100644
index 000000000..c6164f3ff
--- /dev/null
+++ b/app/components/Package/DeprecatePackageModal.vue
@@ -0,0 +1,176 @@
+
+
+
+
+
+
+
+
+
+
{{ $t('package.deprecation.modal.success') }}
+
+ {{ $t('package.deprecation.modal.success_detail') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ deprecateError }}
+
+
+
+
+
diff --git a/app/pages/package/[...package].vue b/app/pages/package/[...package].vue
index 11be99087..288271f65 100644
--- a/app/pages/package/[...package].vue
+++ b/app/pages/package/[...package].vue
@@ -187,6 +187,17 @@ const deprecationNoticeMessage = useMarkdown(() => ({
text: deprecationNotice.value?.message ?? '',
}))
+const { isConnected, npmUser } = useConnector()
+const deprecateModal = useTemplateRef<{ open: () => void }>('deprecateModal')
+
+const isPackageOwner = computed(() => {
+ const maintainers = pkg.value?.maintainers
+ const user = npmUser.value
+ if (!maintainers?.length || !user) return false
+ const userLower = user.toLowerCase()
+ return maintainers.some((m: { name?: string }) => (m.name ?? '').toLowerCase() === userLower)
+})
+
const sizeTooltip = computed(() => {
const chunks = [
displayVersion.value &&
@@ -1135,6 +1146,22 @@ defineOgImageComponent('Package', {
:peer-dependencies-meta="displayVersion.peerDependenciesMeta"
:optional-dependencies="displayVersion.optionalDependencies"
/>
+
+
+
+
+
@@ -1153,6 +1180,14 @@ defineOgImageComponent('Package', {
{{ $t('common.go_back_home') }}
+
+
+
diff --git a/cli/src/npm-client.ts b/cli/src/npm-client.ts
index 77ebcccf9..68cd177b2 100644
--- a/cli/src/npm-client.ts
+++ b/cli/src/npm-client.ts
@@ -428,3 +428,30 @@ export async function packageInit(
})
}
}
+
+/**
+ * Deprecate a package or a specific version with a custom message.
+ * @param pkg Package name (e.g. "vue" or "@nuxt/kit")
+ * @param reason Deprecation message shown to users
+ * @param version Optional version to deprecate (e.g. "1.0.0"); if omitted, deprecates the whole package
+ * @param options.dryRun If true, passes --dry-run to npm (report what would be done without making changes)
+ * @param options.registry Registry URL (e.g. "https://registry.npmjs.org"); if set, passes --registry
+ */
+export async function packageDeprecate(
+ pkg: string,
+ reason: string,
+ version?: string,
+ otp?: string,
+ options?: { dryRun?: boolean; registry?: string },
+): Promise {
+ validatePackageName(pkg)
+ const target = version ? `${pkg}@${version}` : pkg
+ const args = ['deprecate', target, reason]
+ if (options?.dryRun) {
+ args.push('--dry-run')
+ }
+ if (options?.registry?.trim()) {
+ args.push('--registry', options.registry.trim())
+ }
+ return execNpm(args, { otp })
+}
diff --git a/cli/src/schemas.ts b/cli/src/schemas.ts
index 95ee4c1ca..33203c977 100644
--- a/cli/src/schemas.ts
+++ b/cli/src/schemas.ts
@@ -109,6 +109,7 @@ export const OperationTypeSchema = v.picklist([
'owner:add',
'owner:rm',
'package:init',
+ 'package:deprecate',
])
/**
@@ -240,6 +241,18 @@ export const PackageInitParamsSchema = v.object({
author: v.optional(UsernameSchema),
})
+const PackageDeprecateParamsSchema = v.object({
+ pkg: PackageNameSchema,
+ message: v.pipe(
+ v.string(),
+ v.nonEmpty('Deprecation message is required'),
+ v.maxLength(500, 'Message is too long'),
+ ),
+ version: v.optional(v.pipe(v.string(), v.nonEmpty())),
+ dryRun: v.optional(v.picklist(['true', 'false'], 'dryRun must be "true" or "false"')),
+ registry: v.optional(v.pipe(v.string(), v.minLength(1, 'Registry URL cannot be empty'))),
+})
+
// ============================================================================
// Helper Functions
// ============================================================================
@@ -289,6 +302,9 @@ export function validateOperationParams(
case 'package:init':
v.parse(PackageInitParamsSchema, params)
break
+ case 'package:deprecate':
+ v.parse(PackageDeprecateParamsSchema, params)
+ break
}
}
diff --git a/cli/src/server.ts b/cli/src/server.ts
index fc609e06f..9e4d7c620 100644
--- a/cli/src/server.ts
+++ b/cli/src/server.ts
@@ -23,6 +23,7 @@ import {
ownerAdd,
ownerRemove,
packageInit,
+ packageDeprecate,
listUserPackages,
type NpmExecResult,
} from './npm-client.ts'
@@ -734,6 +735,14 @@ async function executeOperation(op: PendingOperation, otp?: string): Promise
Date: Wed, 4 Feb 2026 18:22:19 +0800
Subject: [PATCH 2/9] add i18n
---
i18n/locales/en.json | 15 ++++++++++++++-
i18n/locales/zh-CN.json | 15 ++++++++++++++-
2 files changed, 28 insertions(+), 2 deletions(-)
diff --git a/i18n/locales/en.json b/i18n/locales/en.json
index f67cdff4f..f7e17d057 100644
--- a/i18n/locales/en.json
+++ b/i18n/locales/en.json
@@ -128,9 +128,22 @@
"navigation": "Package",
"copy_name": "Copy package name",
"deprecation": {
+ "action": "Deprecate",
+ "action_change": "Change deprecation",
"package": "This package has been deprecated.",
"version": "This version has been deprecated.",
- "no_reason": "No reason provided"
+ "no_reason": "No reason provided",
+ "modal": {
+ "deprecating": "Deprecating",
+ "title": "Deprecate",
+ "title_version": "Deprecate Version",
+ "reason": "Reason",
+ "reason_placeholder": "e.g. Use package-x instead. This package is no longer maintained.",
+ "success": "Package deprecated",
+ "success_detail": "The package has been deprecated.",
+ "version": "Version",
+ "version_placeholder": "Leave empty to deprecate the whole package"
+ }
},
"replacement": {
"title": "You might not need this dependency.",
diff --git a/i18n/locales/zh-CN.json b/i18n/locales/zh-CN.json
index 22b562dd2..c5c8f4eea 100644
--- a/i18n/locales/zh-CN.json
+++ b/i18n/locales/zh-CN.json
@@ -124,9 +124,22 @@
"navigation": "包导航",
"copy_name": "拷贝包名",
"deprecation": {
+ "action": "废弃",
+ "action_change": "修改弃用",
"package": "这个包已经被弃用。",
"version": "这个版本已经被弃用。",
- "no_reason": "没有提供原因"
+ "no_reason": "没有提供原因",
+ "modal": {
+ "deprecating": "废弃中",
+ "title": "废弃",
+ "title_version": "废弃版本",
+ "reason": "原因",
+ "reason_placeholder": "例如:使用 package-x 替代。这个包不再维护。",
+ "success": "包已废弃",
+ "success_detail": "这个包已废弃。",
+ "version": "版本",
+ "version_placeholder": "留空则废弃整个包"
+ }
},
"replacement": {
"title": "你可能不需要这个依赖。",
From bb372288ff32968c178c2041336cf0be84d165de Mon Sep 17 00:00:00 2001
From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com>
Date: Wed, 4 Feb 2026 10:24:54 +0000
Subject: [PATCH 3/9] [autofix.ci] apply automated fixes
---
lunaria/files/zh-CN.json | 15 ++++++++++++++-
1 file changed, 14 insertions(+), 1 deletion(-)
diff --git a/lunaria/files/zh-CN.json b/lunaria/files/zh-CN.json
index 22b562dd2..c5c8f4eea 100644
--- a/lunaria/files/zh-CN.json
+++ b/lunaria/files/zh-CN.json
@@ -124,9 +124,22 @@
"navigation": "包导航",
"copy_name": "拷贝包名",
"deprecation": {
+ "action": "废弃",
+ "action_change": "修改弃用",
"package": "这个包已经被弃用。",
"version": "这个版本已经被弃用。",
- "no_reason": "没有提供原因"
+ "no_reason": "没有提供原因",
+ "modal": {
+ "deprecating": "废弃中",
+ "title": "废弃",
+ "title_version": "废弃版本",
+ "reason": "原因",
+ "reason_placeholder": "例如:使用 package-x 替代。这个包不再维护。",
+ "success": "包已废弃",
+ "success_detail": "这个包已废弃。",
+ "version": "版本",
+ "version_placeholder": "留空则废弃整个包"
+ }
},
"replacement": {
"title": "你可能不需要这个依赖。",
From 58e112d3f907a9300a8ed35ed64558cd3b8f1d02 Mon Sep 17 00:00:00 2001
From: eryue0220
Date: Wed, 4 Feb 2026 23:07:32 +0800
Subject: [PATCH 4/9] update
---
app/components/Package/DeprecatePackageModal.vue | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
diff --git a/app/components/Package/DeprecatePackageModal.vue b/app/components/Package/DeprecatePackageModal.vue
index c6164f3ff..ad21d7f65 100644
--- a/app/components/Package/DeprecatePackageModal.vue
+++ b/app/components/Package/DeprecatePackageModal.vue
@@ -43,9 +43,10 @@ async function handleDeprecate() {
params.version = deprecateVersion.value.trim()
}
+ const escapedMessage = message.replace(/"/g, '\\"')
const command = params.version
- ? `npm deprecate ${props.packageName}@${params.version} "${message}"`
- : `npm deprecate ${props.packageName} "${message}"`
+ ? `npm deprecate ${props.packageName}@${params.version} "${escapedMessage}"`
+ : `npm deprecate ${props.packageName} "${escapedMessage}"`
const operation = await addOperation({
type: 'package:deprecate',
@@ -72,14 +73,14 @@ async function handleDeprecate() {
close()
connectorModal.open()
} else {
- deprecateError.value = completedOp.result?.stderr || t('deprecate.modal.failed')
+ deprecateError.value = completedOp.result?.stderr || t('common.try_again')
}
} else {
close()
connectorModal.open()
}
} catch (err) {
- deprecateError.value = err instanceof Error ? err.message : t('deprecate.modal.failed')
+ deprecateError.value = err instanceof Error ? err.message : t('common.try_again')
} finally {
isDeprecating.value = false
}
From 2159074cb47308311c26573704db2e1743ebc6ea Mon Sep 17 00:00:00 2001
From: eryue0220
Date: Wed, 4 Feb 2026 23:10:57 +0800
Subject: [PATCH 5/9] update
---
cli/src/npm-client.ts | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/cli/src/npm-client.ts b/cli/src/npm-client.ts
index 901ab135d..88dbde1d2 100644
--- a/cli/src/npm-client.ts
+++ b/cli/src/npm-client.ts
@@ -448,13 +448,23 @@ export async function packageDeprecate(
options?: { dryRun?: boolean; registry?: string },
): Promise {
validatePackageName(pkg)
+
+ const reasonText = reason.trim()
+
+ if (!reasonText) {
+ throw new Error('Deprecation reason must not be empty')
+ }
+
const target = version ? `${pkg}@${version}` : pkg
const args = ['deprecate', target, reason]
+
if (options?.dryRun) {
args.push('--dry-run')
}
+
if (options?.registry?.trim()) {
args.push('--registry', options.registry.trim())
}
+
return execNpm(args, { otp })
}
From 2530ba930ee6860d86e1bf9d4316cb7ad5c31669 Mon Sep 17 00:00:00 2001
From: eryue0220
Date: Wed, 4 Feb 2026 23:29:33 +0800
Subject: [PATCH 6/9] update
---
app/components/Package/DeprecatePackageModal.vue | 3 ++-
cli/src/npm-client.ts | 2 +-
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/app/components/Package/DeprecatePackageModal.vue b/app/components/Package/DeprecatePackageModal.vue
index ad21d7f65..66f061b9f 100644
--- a/app/components/Package/DeprecatePackageModal.vue
+++ b/app/components/Package/DeprecatePackageModal.vue
@@ -1,5 +1,6 @@