Skip to content

Commit 004d040

Browse files
authored
Merge branch 'main' into module-replacements-v3
2 parents edfd26c + f70a11b commit 004d040

File tree

94 files changed

+5353
-2721
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

94 files changed

+5353
-2721
lines changed

.coderabbit.yaml

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
language: en-GB
2+
3+
reviews:
4+
profile: chill
5+
# Keep the high-level summary enabled (default), but place it in the
6+
# walkthrough comment instead of relying on PR description updates.
7+
high_level_summary_in_walkthrough: true
8+
review_status: true
9+
review_details: false
10+
changed_files_summary: true
11+
sequence_diagrams: true
12+
estimate_code_review_effort: false
13+
assess_linked_issues: true
14+
related_issues: true
15+
related_prs: true
16+
suggested_labels: false
17+
suggested_reviewers: true
18+
in_progress_fortune: false
19+
poem: false
20+
21+
slop_detection:
22+
enabled: true
23+
label: '007'
24+
25+
auto_review:
26+
auto_pause_after_reviewed_commits: 5
27+
labels:
28+
- '!release'
29+
ignore_title_keywords:
30+
- 'WIP'
31+
- '[skip-review]'
32+
- 'chore(release)'
33+
ignore_usernames:
34+
- 'renovate[bot]'
35+
- 'dependabot[bot]'
36+
- 'github-actions[bot]'
37+
38+
# Built-in PR metadata/content checks. Modes are: off, warning, error.
39+
pre_merge_checks:
40+
docstrings:
41+
mode: off
42+
title:
43+
mode: error
44+
description:
45+
mode: warning
46+
issue_assessment:
47+
mode: warning
48+
49+
tools:
50+
gitleaks:
51+
enabled: true
52+
osvScanner:
53+
enabled: true
54+
actionlint:
55+
enabled: true
56+
yamllint:
57+
enabled: true
58+
shellcheck:
59+
enabled: true
60+
dotenvLint:
61+
enabled: true
62+
63+
# Disable tools redundant with our own CI
64+
eslint:
65+
enabled: false
66+
biome:
67+
enabled: false
68+
oxc:
69+
enabled: false
70+
markdownlint:
71+
enabled: false
72+
languagetool:
73+
enabled: false
74+
github-checks:
75+
enabled: false
76+
77+
# Security-related, but not a good fit for this repo
78+
checkov:
79+
enabled: false
80+
trivy:
81+
enabled: false
82+
opengrep:
83+
enabled: false
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
name: dependency-diff-comment
2+
3+
on:
4+
workflow_run:
5+
workflows: ['dependency-diff']
6+
types:
7+
- completed
8+
9+
permissions:
10+
pull-requests: write
11+
actions: read
12+
13+
jobs:
14+
dependency-diff-comment:
15+
name: 💬 Dependency diff comment
16+
runs-on: ubuntu-slim
17+
if: github.event.workflow_run.conclusion == 'success'
18+
19+
steps:
20+
- name: 📥 Download artifact
21+
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
22+
with:
23+
name: e18e-diff-result
24+
run-id: ${{ github.event.workflow_run.id }}
25+
github-token: ${{ secrets.GITHUB_TOKEN }}
26+
27+
- name: 💬 Post comment
28+
uses: e18e/action-dependency-diff@f825d5b5c5ce0a42dc46c47ec20de24460affcd8 # v1.5.0
29+
with:
30+
mode: comment-from-artifact
31+
artifact-path: e18e-diff-result.json
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
name: dependency-diff
2+
3+
on:
4+
pull_request:
5+
branches:
6+
- main
7+
- release
8+
paths:
9+
- package.json
10+
- pnpm-lock.yaml
11+
- pnpm-workspace.yaml
12+
- docs/package.json
13+
- cli/package.json
14+
15+
concurrency:
16+
group: ${{ github.workflow }}-${{ github.event.number || github.sha }}
17+
cancel-in-progress: true
18+
19+
permissions:
20+
contents: read
21+
22+
jobs:
23+
dependency-diff:
24+
name: 🔎 Dependency diff
25+
runs-on: ubuntu-slim
26+
27+
steps:
28+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
29+
with:
30+
fetch-depth: 0
31+
32+
- name: 🔎 Compare dependencies
33+
id: analyze
34+
uses: e18e/action-dependency-diff@f825d5b5c5ce0a42dc46c47ec20de24460affcd8 # v1.5.0
35+
with:
36+
mode: artifact
37+
detect-replacements: 'true'
38+
duplicate-threshold: '4'
39+
dependency-threshold: '15'
40+
size-threshold: '200000'
41+
42+
- name: 📤 Upload artifact
43+
if: steps.analyze.outputs.artifact-path
44+
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
45+
with:
46+
name: e18e-diff-result
47+
path: ${{ steps.analyze.outputs.artifact-path }}

.github/workflows/welcome-close.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ jobs:
3939
const body = [
4040
`Thanks for your first contribution, @${author}! ${emoji}`,
4141
'',
42-
`We'd love to welcome you to the npmx community. Come and say hi on [Discord](https://chat.npmx.dev)! And once you've joined, visit [npmx.wamellow.com](https://npmx.wamellow.com/) to claim the **contributor** role.`,
42+
`We'd love to welcome you to the npmx community. Come and say hi on [Discord](https://build.npmx.dev)! And once you've joined, visit [npmx.wamellow.com](https://npmx.wamellow.com/) to claim the **contributor** role.`,
4343
].join('\n');
4444
4545
await github.rest.issues.createComment({

.storybook/main.ts

Lines changed: 91 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import type { StorybookConfig } from '@storybook-vue/nuxt'
2+
import { readFileSync } from 'node:fs'
3+
import { resolve } from 'node:path'
24

35
const config = {
46
stories: [
@@ -21,29 +23,95 @@ const config = {
2123
async viteFinal(newConfig) {
2224
newConfig.plugins ??= []
2325

26+
// Fix: nuxt:components:imports-alias relies on internal Nuxt state that is
27+
// cleaned up after nuxt.close() in @storybook-vue/nuxt's loadNuxtViteConfig.
28+
// When that state is gone, `import X from '#components'` is left unresolved
29+
// and Vite 8 falls through to package-subpath resolution, which fails with
30+
// "Missing '#components' specifier in 'nuxt' package".
31+
// This plugin intercepts #components first and serves a virtual module built
32+
// from the components.d.ts written during the same Nuxt boot.
33+
// Resolve the Nuxt build dir from Vite's alias map, which can be either a
34+
// plain-object (Record<string, string>) or Vite's resolved array form
35+
// (readonly Alias[] where find is string | RegExp). We must handle both
36+
// without casting to Record<string, string>, which would be unsound for the
37+
// array form.
38+
const aliases = newConfig.resolve?.alias
39+
const buildDir = (() => {
40+
if (!aliases) return undefined
41+
if (Array.isArray(aliases)) {
42+
const entry = aliases.find(a => a.find === '#build')
43+
return typeof entry?.replacement === 'string' ? entry.replacement : undefined
44+
}
45+
const value = (aliases as Record<string, unknown>)['#build']
46+
return typeof value === 'string' ? value : undefined
47+
})()
48+
newConfig.plugins.unshift({
49+
name: 'storybook-nuxt-components',
50+
enforce: 'pre',
51+
resolveId(id) {
52+
if (id === '#components') return '\0virtual:#components'
53+
return null
54+
},
55+
load(id) {
56+
if (id !== '\0virtual:#components') return
57+
if (!buildDir) {
58+
throw new Error('[storybook-nuxt-components] Could not resolve the `#build` alias.')
59+
}
60+
const dtsPath = resolve(buildDir, 'components.d.ts')
61+
// Wire the generated declaration file into Vite's file-watch graph so
62+
// that the virtual module is invalidated when Nuxt regenerates it.
63+
this.addWatchFile(dtsPath)
64+
const dts = readFileSync(dtsPath, 'utf-8')
65+
const lines: string[] = []
66+
// Match only the direct `typeof import("…").default` form.
67+
// Lazy/island wrappers (LazyComponent<T>, IslandComponent<T>) are
68+
// excluded intentionally — Storybook only needs the concrete type.
69+
// The format has been stable across all Nuxt 3 releases.
70+
const re =
71+
/^export const (\w+): typeof import\("([^"]+)"\)(?:\.default|\[['"]default['"]\])\s*;?$/gm
72+
let match: RegExpExecArray | null
73+
while ((match = re.exec(dts)) !== null) {
74+
const [, name, rel] = match
75+
if (!name || !rel) continue
76+
const abs = resolve(buildDir, rel).replaceAll('\\', '/')
77+
lines.push(`export { default as ${name} } from ${JSON.stringify(abs)}`)
78+
}
79+
if (lines.length === 0) {
80+
throw new Error(
81+
`[storybook-nuxt-components] No component exports were found in ${dtsPath}.`,
82+
)
83+
}
84+
return lines.join('\n')
85+
},
86+
})
87+
2488
// Bridge compatibility between Storybook v10 core and v9 @storybook-vue/nuxt
2589
// v10 expects module federation globals that v9 doesn't provide
2690
newConfig.plugins.push({
2791
name: 'storybook-v10-compat',
2892
transformIndexHtml: {
2993
order: 'pre',
30-
handler(html) {
31-
const script = `
32-
<script>
33-
// Minimal shims for Storybook v10 module federation system
34-
// These will be replaced when Storybook runtime loads
35-
window.__STORYBOOK_MODULE_GLOBAL__ = { global: window };
36-
window.__STORYBOOK_MODULE_CLIENT_LOGGER__ = {
37-
deprecate: console.warn.bind(console, '[deprecated]'),
38-
once: console.log.bind(console),
39-
logger: console
40-
};
41-
window.__STORYBOOK_MODULE_CHANNELS__ = {
42-
Channel: class { on() {} off() {} emit() {} once() {} },
43-
createBrowserChannel: () => new window.__STORYBOOK_MODULE_CHANNELS__.Channel()
44-
};
45-
</script>`
46-
return html.replace(/<script>/, script + '<script>')
94+
handler() {
95+
return [
96+
{
97+
tag: 'script',
98+
injectTo: 'head-prepend' as const,
99+
children: [
100+
'// Minimal shims for Storybook v10 module federation system',
101+
'// These will be replaced when Storybook runtime loads',
102+
'window.__STORYBOOK_MODULE_GLOBAL__ = { global: window };',
103+
'window.__STORYBOOK_MODULE_CLIENT_LOGGER__ = {',
104+
" deprecate: console.warn.bind(console, '[deprecated]'),",
105+
' once: console.log.bind(console),',
106+
' logger: console',
107+
'};',
108+
'window.__STORYBOOK_MODULE_CHANNELS__ = {',
109+
' Channel: class { on() {} off() {} emit() {} once() {} },',
110+
' createBrowserChannel: () => new window.__STORYBOOK_MODULE_CHANNELS__.Channel()',
111+
'};',
112+
].join('\n'),
113+
},
114+
]
47115
},
48116
},
49117
})
@@ -73,7 +141,12 @@ const config = {
73141
const wrapped = async function (this: unknown, ...args: unknown[]) {
74142
try {
75143
return await originalFn.apply(this, args)
76-
} catch {
144+
} catch (err) {
145+
// oxlint-disable-next-line no-console -- Log and swallow errors to avoid breaking the Storybook build when vue-docgen-api encounters an unparseable component.
146+
console.warn(
147+
'[storybook:vue-docgen-plugin] Suppressed docgen error (component docs may be missing):',
148+
err,
149+
)
77150
return undefined
78151
}
79152
}

.vite-hooks/pre-commit

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
vp staged

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ What npmx offers:
7777
| Organization pages |||
7878
| Provenance indicators |||
7979
| Code browser |||
80-
| Dark mode | ||
80+
| Dark mode | ||
8181
| Outdated dependency warnings |||
8282
| Module format badges (ESM/CJS) |||
8383
| TypeScript types indicator |||

app/components/Chart/SplitSparkline.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,7 @@ const configs = computed(() => {
244244

245245
<template #skeleton>
246246
<!-- This empty div overrides the default built-in scanning animation on load -->
247-
<div />
247+
<div></div>
248248
</template>
249249
</VueUiSparkline>
250250
</div>

app/components/Code/FileTree.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ const { toggleDir, isExpanded, autoExpandAncestors } = useFileTreeState(props.ba
3939
watch(
4040
() => props.currentPath,
4141
path => {
42-
if (path) {
42+
if (depth.value === 0 && path) {
4343
autoExpandAncestors(path)
4444
}
4545
},

app/components/CollapsibleSection.vue

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
<script setup lang="ts">
22
import { shallowRef, computed } from 'vue'
33
import { LinkBase } from '#components'
4+
import type { IconClass } from '~/types/icon'
45
56
interface Props {
67
title: string
78
subtitle?: string
89
isLoading?: boolean
910
headingLevel?: `h${number}`
1011
id: string
11-
icon?: string
12+
icon?: IconClass
1213
}
1314
1415
const props = withDefaults(defineProps<Props>(), {
@@ -62,8 +63,13 @@ function toggle() {
6263
}
6364
6465
const ariaLabel = computed(() => {
65-
const action = isOpen.value ? 'Collapse' : 'Expand'
66-
return props.title ? `${action} ${props.title}` : action
66+
if (!props.title) {
67+
return isOpen.value ? $t('common.collapse') : $t('common.expand')
68+
}
69+
70+
return isOpen.value
71+
? $t('common.collapse_with_name', { name: props.title })
72+
: $t('common.expand_with_name', { name: props.title })
6773
})
6874
useHead({
6975
style: [

0 commit comments

Comments
 (0)