Skip to content

Commit 1ac4bc9

Browse files
OrbisKantfu
andauthored
feat: add dependency publish dates (#20)
Co-authored-by: Anthony Fu <github@antfu.me>
1 parent fc8e303 commit 1ac4bc9

File tree

27 files changed

+560
-158
lines changed

27 files changed

+560
-158
lines changed

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
"bumpp": "catalog:",
4242
"chalk": "catalog:",
4343
"eslint": "catalog:",
44+
"fast-npm-meta": "catalog:",
4445
"lint-staged": "catalog:",
4546
"nuxt": "catalog:",
4647
"nuxt-eslint-auto-explicit-import": "catalog:",
@@ -49,6 +50,7 @@
4950
"simple-git-hooks": "catalog:",
5051
"typescript": "catalog:",
5152
"unbuild": "catalog:",
53+
"unstorage": "catalog:",
5254
"vite": "catalog:",
5355
"vite-plugin-inspect": "catalog:",
5456
"vitest": "catalog:",

packages/node-modules-inspector/package.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
"cac": "catalog:",
3636
"debug": "catalog:",
3737
"fast-glob": "catalog:",
38+
"fast-npm-meta": "catalog:",
3839
"flatted": "catalog:",
3940
"get-port-please": "catalog:",
4041
"h3": "catalog:",
@@ -46,11 +47,12 @@
4647
"pathe": "catalog:",
4748
"picocolors": "catalog:",
4849
"structured-clone-es": "catalog:",
50+
"unstorage": "catalog:",
4951
"ws": "catalog:"
5052
},
5153
"devDependencies": {
5254
"@types/picomatch": "catalog:",
53-
"@types/semver": "^7.5.8",
55+
"@types/semver": "catalog:",
5456
"@unocss/nuxt": "catalog:",
5557
"@vueuse/nuxt": "catalog:",
5658
"@webcontainer/api": "catalog:",
@@ -61,9 +63,10 @@
6163
"d3-shape": "catalog:",
6264
"floating-vue": "catalog:",
6365
"fuse.js": "catalog:",
66+
"idb-keyval": "catalog:",
6467
"modern-screenshot": "catalog:",
6568
"picomatch": "catalog:",
66-
"semver": "^7.7.1",
69+
"semver": "catalog:",
6770
"shiki": "catalog:",
6871
"theme-vitesse": "catalog:",
6972
"vite-hot-client": "catalog:"

packages/node-modules-inspector/src/app/backends/websocket.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,15 @@ export function createWebSocketBackend(options: WebSocketBackendOptions): Backen
9595
throw err
9696
}
9797
},
98+
getPackagesPublishDate: async (specs: string[]) => {
99+
try {
100+
return await rpc.getPackagesPublishDate(specs)
101+
}
102+
catch (err) {
103+
error.value = err
104+
throw err
105+
}
106+
},
98107
openInEditor: (filename: string) => rpc.openInEditor.asEvent(filename),
99108
openInFinder: (filename: string) => rpc.openInFinder.asEvent(filename),
100109
},
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
<script setup lang="ts">
2+
import type { PackageNode } from 'node-modules-tools'
3+
import { computed } from 'vue'
4+
import { getPublishTime } from '~/state/payload'
5+
import { settings } from '~/state/settings'
6+
7+
const props = withDefaults(
8+
defineProps<{
9+
pkg: PackageNode
10+
colorize?: boolean
11+
}>(),
12+
{
13+
colorize: true,
14+
},
15+
)
16+
17+
const date = computed(() => getPublishTime(props.pkg))
18+
19+
const colorScale = [
20+
[180, 'color-scale-neutral'],
21+
[365, 'color-scale-low'],
22+
[365 * 3, 'color-scale-medium'],
23+
[365 * 5, 'color-scale-high'],
24+
[730 * 5, 'color-scale-critical'],
25+
] as const
26+
27+
const daysAgo = computed(() => {
28+
if (!date.value)
29+
return 0
30+
const now = +new Date()
31+
32+
const msPerDay = 24 * 60 * 60 * 1000
33+
return Math.floor((now - +date.value) / msPerDay)
34+
})
35+
36+
const timeAgo = computed(() => {
37+
if (daysAgo.value < 1)
38+
return [0, 'today']
39+
if (daysAgo.value > 365)
40+
return [+(daysAgo.value / 365).toFixed(1), 'yr']
41+
if (daysAgo.value > 30)
42+
return [Math.round(daysAgo.value / 30), 'mo']
43+
return [daysAgo.value, 'd']
44+
})
45+
46+
const color = computed(() => {
47+
if (!settings.value.colorizePackageSize && !props.colorize)
48+
return colorScale[0][1]
49+
50+
for (const [limit, color] of colorScale) {
51+
if (daysAgo.value < limit)
52+
return color
53+
}
54+
55+
return colorScale[colorScale.length - 1][1]
56+
})
57+
</script>
58+
59+
<template>
60+
<div
61+
v-if="date"
62+
:class="color"
63+
class="px-0.4em py-0.2em line-height-none bg-gray:5"
64+
:title="`Published at ${String(date)}`"
65+
>
66+
<span font-mono>{{ timeAgo[0] }}</span>
67+
<span op50 text-sm ml0.5>{{ timeAgo[1] }}</span>
68+
</div>
69+
</template>

packages/node-modules-inspector/src/app/components/display/FileSizeBadge.vue

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@ const KB = 1024
2323
const MB = KB ** 2
2424
2525
const colorScale = [
26-
[80 * KB, 'text-gray:75!'],
27-
[500 * KB, 'text-lime:75! saturate-50'],
28-
[1 * MB, 'text-amber:85! saturate-80'],
29-
[10 * MB, 'text-orange!'],
30-
[20 * MB, 'text-red!'],
26+
[80 * KB, 'color-scale-neutral'],
27+
[500 * KB, 'color-scale-low'],
28+
[1 * MB, 'color-scale-medium'],
29+
[10 * MB, 'color-scale-high'],
30+
[20 * MB, 'color-scale-critical'],
3131
] as const
3232
3333
const color = computed(() => {

packages/node-modules-inspector/src/app/components/grid/Item.vue

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,12 @@ defineProps<{
4040
rounded-full text-sm
4141
/>
4242

43+
<DisplayDateBadge
44+
v-if="settings.showPublishTimeBadge"
45+
:pkg
46+
rounded-full text-sm
47+
/>
48+
4349
<!--
4450
<span op25>·</span>
4551
<div op75>

packages/node-modules-inspector/src/app/components/panel/PackageDetails.vue

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { computed } from 'vue'
55
import { getBackend } from '~/backends'
66
import { selectedNode } from '~/state/current'
77
import { filters } from '~/state/filters'
8-
import { payloads } from '~/state/payload'
8+
import { getPublishTime, payloads } from '~/state/payload'
99
import { query } from '~/state/query'
1010
import { settings } from '~/state/settings'
1111
@@ -191,6 +191,10 @@ function getShallowestDependents(pkg: PackageNode) {
191191
<span op50>·</span>
192192
<DisplayNodeVersionRange :range="pkg.resolved.engines?.node" />
193193
</template>
194+
<template v-if="getPublishTime(pkg)">
195+
<span op50>·</span>
196+
<DisplayDateBadge :pkg rounded-full text-sm />
197+
</template>
194198
</div>
195199
</div>
196200
<div grid="~ cols-3 gap-2 items-center" p2>

packages/node-modules-inspector/src/app/components/panel/Settings.vue

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ const backend = getBackend()
2222
<OptionItem title="Show size badge" description="Show install size badge on package list">
2323
<OptionCheckbox v-model="settings.showInstallSizeBadge" />
2424
</OptionItem>
25+
<OptionItem title="Show publish time badge" description="Show publish time badge on package list">
26+
<OptionCheckbox v-model="settings.showPublishTimeBadge" />
27+
</OptionItem>
2528
<OptionItem title="Colorize size badge" description="Colorize package size badge">
2629
<OptionCheckbox v-model="settings.colorizePackageSize" />
2730
</OptionItem>

packages/node-modules-inspector/src/app/components/report/MultipleVersions.vue

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
<script setup lang="ts">
22
import type { PackageNode } from 'node-modules-tools'
33
import { useRouter } from '#app/composables/router'
4+
import { DisplayDateBadge } from '#components'
45
import { computed, nextTick } from 'vue'
56
import { compareSemver } from '~~/shared/utils'
67
import { selectedNode } from '~/state/current'
78
import { filters } from '~/state/filters'
89
import { payloads } from '~/state/payload'
10+
import { settings } from '~/state/settings'
911
1012
const router = useRouter()
1113
@@ -59,12 +61,13 @@ function showGraph(pkgs: PackageNode[]) {
5961
<div flex="~ col gap-1" p2>
6062
<button
6163
v-for="pkg of pkgs" :key="pkg.version"
62-
px2 rounded flex="~ items-center gap-04"
64+
px2 rounded flex="~ items-center gap-2"
6365
font-mono hover="bg-active"
6466
:class="selectedNode === pkg ? 'bg-active' : ''"
6567
@click="selectedNode = pkg"
6668
>
6769
<span op75 flex-auto text-left>v{{ pkg.version }}</span>
70+
<DisplayDateBadge v-if="settings.showPublishTimeBadge" :pkg :badge="false" rounded-full text-xs />
6871
<DisplayModuleType :pkg :badge="false" text-xs />
6972
</button>
7073
</div>
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<script setup lang="ts">
2+
import { DisplayDateBadge } from '#components'
3+
import { computed } from 'vue'
4+
import { selectedNode } from '~/state/current'
5+
import { getPublishTime, payloads } from '~/state/payload'
6+
7+
const packages = computed(() =>
8+
Array.from(payloads.filtered.packages)
9+
.filter(x => getPublishTime(x))
10+
.sort((a, b) => +getPublishTime(a)! - +getPublishTime(b)!),
11+
)
12+
</script>
13+
14+
<template>
15+
<ReportExpendableContainer
16+
v-if="packages.length"
17+
:list="packages"
18+
:title="['Oldest Packages', 'Newest Packages']"
19+
>
20+
<template #default="{ items }">
21+
<div grid="~ cols-[1fr_max-content] gap-x-4 gap-y-1">
22+
<div />
23+
<div text-sm op50 text-right>
24+
Publish Time
25+
</div>
26+
27+
<template v-for="pkg of items" :key="pkg.spec">
28+
<button
29+
font-mono text-left hover:bg-active px2 ml--2 rounded
30+
flex="~ gap-2 items-center"
31+
@click="selectedNode = pkg"
32+
>
33+
<DisplayModuleType :pkg />
34+
<DisplayPackageSpec :pkg />
35+
</button>
36+
<div flex justify-end>
37+
<DisplayDateBadge :pkg />
38+
</div>
39+
</template>
40+
</div>
41+
</template>
42+
</ReportExpendableContainer>
43+
</template>

0 commit comments

Comments
 (0)