devex: changelog system (#5309)
* devex: changelog system * feat: changelog CIs * feat: web alias for platform + hosting * feat: upload binaries to gh release * feat: improve copy text * fix: release workflow * fix: changelog CIs + PR health check comment * fix: action * fix: comment style * fix: comment * fix: remove health * fix: deploy use Modrinth bot machine account * feat: new system * fix: pr comment structure
This commit is contained in:
121
.github/workflows/changelog-comment.yml
vendored
Normal file
121
.github/workflows/changelog-comment.yml
vendored
Normal file
@@ -0,0 +1,121 @@
|
||||
name: Changelog Comment
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, reopened]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
pr_number:
|
||||
description: 'PR number to post the changelog comment on (for testing)'
|
||||
required: true
|
||||
type: number
|
||||
|
||||
jobs:
|
||||
comment:
|
||||
name: Post changelog comment
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: 💬 Post or update changelog comment
|
||||
uses: actions/github-script@v7
|
||||
with:
|
||||
github-token: ${{ secrets.CROWDIN_GH_TOKEN }}
|
||||
script: |
|
||||
const marker = '<!-- changelog -->';
|
||||
const mergedMarker = '<!-- changelog-merged -->';
|
||||
|
||||
const sections = ['### Added', '', '### Changed', '', '### Deprecated', '', '### Removed', '', '### Fixed', '', '### Security'].join('\n');
|
||||
const productBlock = (name) => `<details>\n<summary>${name}</summary>\n\n${sections}\n\n</details>`;
|
||||
|
||||
const template = [
|
||||
marker,
|
||||
'## Pull request changelog',
|
||||
'',
|
||||
'<!-- Fill in the changelog under each product area this PR affects.',
|
||||
' Empty sections are ignored. Leave a product collapsed/empty',
|
||||
' if it doesn\'t apply. -->',
|
||||
'',
|
||||
productBlock('App'),
|
||||
'',
|
||||
productBlock('Website'),
|
||||
'',
|
||||
productBlock('Hosting'),
|
||||
].join('\n');
|
||||
|
||||
// Resolve PR number from event or workflow_dispatch input
|
||||
const prNumber = context.payload.pull_request?.number
|
||||
?? parseInt('${{ github.event.inputs.pr_number }}', 10);
|
||||
|
||||
if (!prNumber || isNaN(prNumber)) {
|
||||
core.setFailed('Could not determine PR number');
|
||||
return;
|
||||
}
|
||||
|
||||
// Get PR details (need base ref for child PR detection)
|
||||
const { data: pr } = await github.rest.pulls.get({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
pull_number: prNumber,
|
||||
});
|
||||
|
||||
// Check if bot comment already exists
|
||||
const { data: comments } = await github.rest.issues.listComments({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
});
|
||||
|
||||
const existingComment = comments.find(c => c.body.includes(marker));
|
||||
if (existingComment) {
|
||||
core.info('Changelog comment already exists, skipping');
|
||||
return;
|
||||
}
|
||||
|
||||
// Post the template comment
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
body: template,
|
||||
});
|
||||
|
||||
core.info(`Posted changelog comment on PR #${prNumber}`);
|
||||
|
||||
// Detect child PR: check if this PR's base branch is another open PR's head branch
|
||||
const baseRef = pr.base.ref;
|
||||
|
||||
if (baseRef === 'main' || baseRef === 'prod') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Look for a parent PR whose head branch matches our base branch
|
||||
const { data: candidatePRs } = await github.rest.pulls.list({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
state: 'open',
|
||||
head: `${context.repo.owner}:${baseRef}`,
|
||||
});
|
||||
|
||||
if (candidatePRs.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const parentPR = candidatePRs[0];
|
||||
core.info(`Detected parent PR #${parentPR.number} for child PR #${prNumber}`);
|
||||
|
||||
// Add admonition to child PR's changelog comment
|
||||
const { data: childComments } = await github.rest.issues.listComments({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: prNumber,
|
||||
});
|
||||
|
||||
const childChangelogComment = childComments.find(c => c.body.includes(marker));
|
||||
if (childChangelogComment && !childChangelogComment.body.includes(mergedMarker)) {
|
||||
await github.rest.issues.updateComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
comment_id: childChangelogComment.id,
|
||||
body: `${mergedMarker}\n> [!NOTE]\n> This changelog has been merged into the changelog for #${parentPR.number}\n\n${childChangelogComment.body}`,
|
||||
});
|
||||
}
|
||||
3
.github/workflows/frontend-preview.yml
vendored
3
.github/workflows/frontend-preview.yml
vendored
@@ -50,14 +50,15 @@ jobs:
|
||||
uses: peter-evans/find-comment@v3
|
||||
id: fc
|
||||
with:
|
||||
token: ${{ secrets.CROWDIN_GH_TOKEN }}
|
||||
issue-number: ${{ github.event.pull_request.number }}
|
||||
comment-author: 'github-actions[bot]'
|
||||
body-includes: Frontend previews
|
||||
|
||||
- name: Comment deploy URL on PR
|
||||
if: github.event_name == 'pull_request'
|
||||
uses: peter-evans/create-or-update-comment@v5
|
||||
with:
|
||||
token: ${{ secrets.CROWDIN_GH_TOKEN }}
|
||||
issue-number: ${{ github.event.pull_request.number }}
|
||||
comment-id: ${{ steps.fc.outputs.comment-id }}
|
||||
body: |
|
||||
|
||||
44
.github/workflows/theseus-release.yml
vendored
44
.github/workflows/theseus-release.yml
vendored
@@ -1,47 +1,43 @@
|
||||
name: Modrinth App release
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version-tag:
|
||||
description: Version tag to release to the wide public
|
||||
type: string
|
||||
required: true
|
||||
release-notes:
|
||||
description: Release notes to include in the Tauri version manifest
|
||||
default: A new release of the Modrinth App is available!
|
||||
type: string
|
||||
required: true
|
||||
workflow_run:
|
||||
workflows: ['Modrinth App build']
|
||||
types: [completed]
|
||||
|
||||
jobs:
|
||||
release:
|
||||
name: Release Modrinth App
|
||||
if: >-
|
||||
github.event.workflow_run.conclusion == 'success' &&
|
||||
startsWith(github.event.workflow_run.head_branch, 'v')
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
env:
|
||||
VERSION_TAG: ${{ github.event.workflow_run.head_branch }}
|
||||
LINUX_X64_BUNDLE_ARTIFACT_NAME: App bundle (x86_64-unknown-linux-gnu)
|
||||
WINDOWS_X64_BUNDLE_ARTIFACT_NAME: App bundle (x86_64-pc-windows-msvc)
|
||||
MACOS_UNIVERSAL_BUNDLE_ARTIFACT_NAME: App bundle (universal-apple-darwin)
|
||||
LAUNCHER_FILES_BUCKET_BASE_URL: https://launcher-files.modrinth.com
|
||||
|
||||
steps:
|
||||
- name: 📥 Check out code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: 📥 Download Modrinth App artifacts
|
||||
uses: dawidd6/action-download-artifact@v11
|
||||
with:
|
||||
workflow: theseus-build.yml
|
||||
workflow_conclusion: success
|
||||
event: push
|
||||
branch: ${{ inputs.version-tag }}
|
||||
branch: ${{ env.VERSION_TAG }}
|
||||
use_unzip: true
|
||||
|
||||
- name: 🛠️ Generate version manifest
|
||||
env:
|
||||
VERSION_TAG: ${{ inputs.version-tag }}
|
||||
RELEASE_NOTES: ${{ inputs.release-notes }}
|
||||
run: |
|
||||
# Reference: https://tauri.app/plugin/updater/#server-support
|
||||
jq -nc \
|
||||
--arg versionTag "${VERSION_TAG#v}" \
|
||||
--arg releaseNotes "$RELEASE_NOTES" \
|
||||
--arg releaseNotes "See the full changelog at https://modrinth.com/news/changelog" \
|
||||
--rawfile macOsAarch64UpdateArtifactSignature "${MACOS_UNIVERSAL_BUNDLE_ARTIFACT_NAME}/universal-apple-darwin/release/bundle/macos/Modrinth App.app.tar.gz.sig" \
|
||||
--rawfile macOsX64UpdateArtifactSignature "${MACOS_UNIVERSAL_BUNDLE_ARTIFACT_NAME}/universal-apple-darwin/release/bundle/macos/Modrinth App.app.tar.gz.sig" \
|
||||
--rawfile linuxX64UpdateArtifactSignature "${LINUX_X64_BUNDLE_ARTIFACT_NAME}/release/bundle/appimage/Modrinth App_${VERSION_TAG#v}_amd64.AppImage.tar.gz.sig" \
|
||||
@@ -83,7 +79,6 @@ jobs:
|
||||
|
||||
- name: 📤 Upload release artifacts
|
||||
env:
|
||||
VERSION_TAG: ${{ inputs.version-tag }}
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.LAUNCHER_FILES_BUCKET_ACCESS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.LAUNCHER_FILES_BUCKET_SECRET_ACCESS_KEY }}
|
||||
AWS_BUCKET: ${{ secrets.LAUNCHER_FILES_BUCKET_NAME }}
|
||||
@@ -116,3 +111,18 @@ jobs:
|
||||
done
|
||||
|
||||
aws s3 cp updates.json "s3://${AWS_BUCKET}"
|
||||
|
||||
- name: 🏷️ Create GitHub release
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: |
|
||||
VERSION="${VERSION_TAG#v}"
|
||||
|
||||
gh release create "$VERSION_TAG" \
|
||||
--title "Modrinth App ${VERSION}" \
|
||||
--notes "See the full changelog at https://modrinth.com/news/changelog" \
|
||||
"${WINDOWS_X64_BUNDLE_ARTIFACT_NAME}/release/bundle/nsis/Modrinth App_${VERSION}_x64-setup.exe" \
|
||||
"${MACOS_UNIVERSAL_BUNDLE_ARTIFACT_NAME}/universal-apple-darwin/release/bundle/dmg/Modrinth App_${VERSION}_universal.dmg" \
|
||||
"${LINUX_X64_BUNDLE_ARTIFACT_NAME}/release/bundle/appimage/Modrinth App_${VERSION}_amd64.AppImage" \
|
||||
"${LINUX_X64_BUNDLE_ARTIFACT_NAME}/release/bundle/deb/Modrinth App_${VERSION}_amd64.deb" \
|
||||
"${LINUX_X64_BUNDLE_ARTIFACT_NAME}/release/bundle/rpm/Modrinth App-${VERSION}-1.x86_64.rpm"
|
||||
|
||||
@@ -115,7 +115,7 @@ export default defineNuxtConfig({
|
||||
await import('./src/templates/docs/index.ts').then((m) => m.default),
|
||||
)
|
||||
const blogArticles = await import('@modrinth/blog').then((m) => m.articles)
|
||||
const { getChangelog } = await import('@modrinth/utils')
|
||||
const { getChangelog } = await import('@modrinth/blog')
|
||||
|
||||
nitroConfig.prerender = nitroConfig.prerender || {}
|
||||
nitroConfig.prerender.routes = nitroConfig.prerender.routes || []
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ChevronLeftIcon } from '@modrinth/assets'
|
||||
import { getChangelog } from '@modrinth/blog'
|
||||
import { ChangelogEntry, Timeline } from '@modrinth/ui'
|
||||
import { getChangelog } from '@modrinth/utils'
|
||||
|
||||
const route = useRoute()
|
||||
|
||||
|
||||
@@ -1,24 +1,18 @@
|
||||
<script setup lang="ts">
|
||||
import { getChangelog, type Product } from '@modrinth/blog'
|
||||
import { ChangelogEntry } from '@modrinth/ui'
|
||||
import Timeline from '@modrinth/ui/src/components/base/Timeline.vue'
|
||||
import { getChangelog, type Product } from '@modrinth/utils'
|
||||
|
||||
import NavTabs from '~/components/ui/NavTabs.vue'
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
const filter = ref<Product | undefined>(undefined)
|
||||
const allChangelogEntries = ref(getChangelog())
|
||||
|
||||
function updateFilter() {
|
||||
if (route.query.filter) {
|
||||
let value = route.query.filter
|
||||
if (route.query.filter === 'servers') {
|
||||
router.push({ query: { ...route.query, filter: 'hosting' } })
|
||||
value = 'hosting'
|
||||
}
|
||||
filter.value = value as Product
|
||||
filter.value = route.query.filter as Product
|
||||
} else {
|
||||
filter.value = undefined
|
||||
}
|
||||
@@ -44,7 +38,7 @@ const changelogEntries = computed(() =>
|
||||
href: '',
|
||||
},
|
||||
{
|
||||
label: 'Website',
|
||||
label: 'Platform',
|
||||
href: 'web',
|
||||
},
|
||||
{
|
||||
|
||||
@@ -24,9 +24,11 @@
|
||||
"storybook": "pnpm --filter @modrinth/ui storybook",
|
||||
"build-storybook": "pnpm --filter @modrinth/ui build-storybook",
|
||||
"icons:add": "pnpm --filter @modrinth/assets icons:add",
|
||||
"changelog:collect": "node scripts/run.mjs collect-changelog",
|
||||
"scripts": "node scripts/run.mjs"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@clack/prompts": "^1.0.0",
|
||||
"@modrinth/tooling-config": "workspace:*",
|
||||
"@tailwindcss/container-queries": "^0.1.1",
|
||||
"@types/node": "^20.1.0",
|
||||
@@ -34,9 +36,11 @@
|
||||
"@vue/compiler-sfc": "^3.5.26",
|
||||
"chalk": "^5.6.2",
|
||||
"if-ci": "^3.0.0",
|
||||
"keep-a-changelog": "^3.0.2",
|
||||
"prettier": "^3.3.2",
|
||||
"turbo": "^2.5.4",
|
||||
"vue": "^3.5.13"
|
||||
"vue": "^3.5.13",
|
||||
"yaml": "^2.8.2"
|
||||
},
|
||||
"packageManager": "pnpm@9.15.0",
|
||||
"pnpm": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
export type Product = 'web' | 'hosting' | 'api' | 'app'
|
||||
export type Product = 'web' | 'hosting' | 'app'
|
||||
|
||||
export type VersionEntry = {
|
||||
date: dayjs.Dayjs
|
||||
@@ -1 +1,2 @@
|
||||
export * from './changelog'
|
||||
export { articles } from './compiled'
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@modrinth/utils": "workspace:*",
|
||||
"dayjs": "^1.11.10",
|
||||
"glob": "^10.2.7",
|
||||
"gray-matter": "^4.0.3",
|
||||
"html-minifier-terser": "^7.2.0",
|
||||
|
||||
@@ -56,6 +56,7 @@
|
||||
"@codemirror/view": "^6.22.1",
|
||||
"@modrinth/api-client": "workspace:*",
|
||||
"@modrinth/assets": "workspace:*",
|
||||
"@modrinth/blog": "workspace:*",
|
||||
"@modrinth/utils": "workspace:*",
|
||||
"@tanstack/vue-query": "^5.90.7",
|
||||
"@tresjs/cientos": "^4.3.0",
|
||||
|
||||
@@ -42,8 +42,8 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { VersionEntry } from '@modrinth/blog/changelog'
|
||||
import { renderHighlightedString } from '@modrinth/utils'
|
||||
import type { VersionEntry } from '@modrinth/utils/changelog'
|
||||
import dayjs from 'dayjs'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
@@ -87,7 +87,7 @@ const versionName = computed(() => props.entry.version ?? longDate.value)
|
||||
const messages = defineMessages({
|
||||
web: {
|
||||
id: 'changelog.product.web',
|
||||
defaultMessage: 'Website',
|
||||
defaultMessage: 'Platform',
|
||||
},
|
||||
hosting: {
|
||||
id: 'changelog.product.hosting',
|
||||
@@ -97,10 +97,6 @@ const messages = defineMessages({
|
||||
id: 'changelog.product.app',
|
||||
defaultMessage: 'App',
|
||||
},
|
||||
api: {
|
||||
id: 'changelog.product.api',
|
||||
defaultMessage: 'API',
|
||||
},
|
||||
justNow: {
|
||||
id: 'changelog.justNow',
|
||||
defaultMessage: 'Just now',
|
||||
|
||||
@@ -182,9 +182,6 @@
|
||||
"changelog.justNow": {
|
||||
"defaultMessage": "Just now"
|
||||
},
|
||||
"changelog.product.api": {
|
||||
"defaultMessage": "API"
|
||||
},
|
||||
"changelog.product.app": {
|
||||
"defaultMessage": "App"
|
||||
},
|
||||
@@ -192,7 +189,7 @@
|
||||
"defaultMessage": "Hosting"
|
||||
},
|
||||
"changelog.product.web": {
|
||||
"defaultMessage": "Website"
|
||||
"defaultMessage": "Platform"
|
||||
},
|
||||
"collections.label.private": {
|
||||
"defaultMessage": "Private"
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
export * from './billing'
|
||||
export * from './changelog'
|
||||
export * from './highlightjs'
|
||||
export * from './licenses'
|
||||
export * from './parse'
|
||||
|
||||
226
pnpm-lock.yaml
generated
226
pnpm-lock.yaml
generated
@@ -13,6 +13,9 @@ importers:
|
||||
|
||||
.:
|
||||
devDependencies:
|
||||
'@clack/prompts':
|
||||
specifier: ^1.0.0
|
||||
version: 1.1.0
|
||||
'@modrinth/tooling-config':
|
||||
specifier: workspace:*
|
||||
version: link:packages/tooling-config
|
||||
@@ -34,6 +37,9 @@ importers:
|
||||
if-ci:
|
||||
specifier: ^3.0.0
|
||||
version: 3.0.0
|
||||
keep-a-changelog:
|
||||
specifier: ^3.0.2
|
||||
version: 3.0.2
|
||||
prettier:
|
||||
specifier: ^3.3.2
|
||||
version: 3.8.1
|
||||
@@ -43,6 +49,9 @@ importers:
|
||||
vue:
|
||||
specifier: ^3.5.13
|
||||
version: 3.5.27(typescript@5.9.3)
|
||||
yaml:
|
||||
specifier: ^2.8.2
|
||||
version: 2.8.2
|
||||
|
||||
apps/app:
|
||||
dependencies:
|
||||
@@ -152,7 +161,7 @@ importers:
|
||||
devDependencies:
|
||||
'@eslint/compat':
|
||||
specifier: ^1.1.1
|
||||
version: 1.4.1(eslint@9.39.2(jiti@1.21.7))
|
||||
version: 1.4.1(eslint@9.39.2(jiti@2.6.1))
|
||||
'@formatjs/cli':
|
||||
specifier: ^6.2.12
|
||||
version: 6.12.2(@vue/compiler-core@3.5.27)(vue@3.5.27(typescript@5.9.3))
|
||||
@@ -161,22 +170,22 @@ importers:
|
||||
version: link:../../packages/tooling-config
|
||||
'@nuxt/eslint-config':
|
||||
specifier: ^0.5.6
|
||||
version: 0.5.7(@typescript-eslint/utils@8.54.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)
|
||||
version: 0.5.7(@typescript-eslint/utils@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@taijased/vue-render-tracker':
|
||||
specifier: ^1.0.7
|
||||
version: 1.0.7(vue@3.5.27(typescript@5.9.3))
|
||||
'@vitejs/plugin-vue':
|
||||
specifier: ^6.0.3
|
||||
version: 6.0.4(vite@6.4.1(@types/node@20.19.31)(jiti@1.21.7)(lightningcss@1.30.2)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2))(vue@3.5.27(typescript@5.9.3))
|
||||
version: 6.0.4(vite@6.4.1(@types/node@20.19.31)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2))(vue@3.5.27(typescript@5.9.3))
|
||||
autoprefixer:
|
||||
specifier: ^10.4.19
|
||||
version: 10.4.24(postcss@8.5.6)
|
||||
eslint:
|
||||
specifier: ^9.9.1
|
||||
version: 9.39.2(jiti@1.21.7)
|
||||
version: 9.39.2(jiti@2.6.1)
|
||||
eslint-plugin-turbo:
|
||||
specifier: ^2.5.4
|
||||
version: 2.8.2(eslint@9.39.2(jiti@1.21.7))(turbo@2.8.2)
|
||||
version: 2.8.2(eslint@9.39.2(jiti@2.6.1))(turbo@2.8.2)
|
||||
postcss:
|
||||
specifier: ^8.4.39
|
||||
version: 8.5.6
|
||||
@@ -194,7 +203,7 @@ importers:
|
||||
version: 5.9.3
|
||||
vite:
|
||||
specifier: ^6.0.0
|
||||
version: 6.4.1(@types/node@20.19.31)(jiti@1.21.7)(lightningcss@1.30.2)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2)
|
||||
version: 6.4.1(@types/node@20.19.31)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2)
|
||||
vue-component-type-helpers:
|
||||
specifier: ^3.1.8
|
||||
version: 3.2.4
|
||||
@@ -467,6 +476,9 @@ importers:
|
||||
'@modrinth/utils':
|
||||
specifier: workspace:*
|
||||
version: link:../utils
|
||||
dayjs:
|
||||
specifier: ^1.11.10
|
||||
version: 1.11.19
|
||||
glob:
|
||||
specifier: ^10.2.7
|
||||
version: 10.5.0
|
||||
@@ -601,6 +613,9 @@ importers:
|
||||
'@modrinth/assets':
|
||||
specifier: workspace:*
|
||||
version: link:../assets
|
||||
'@modrinth/blog':
|
||||
specifier: workspace:*
|
||||
version: link:../blog
|
||||
'@modrinth/utils':
|
||||
specifier: workspace:*
|
||||
version: link:../utils
|
||||
@@ -718,7 +733,7 @@ importers:
|
||||
version: 5.2.4(vite@5.4.21(@types/node@20.19.31)(lightningcss@1.30.2)(sass@1.97.3)(terser@5.46.0))(vue@3.5.27(typescript@5.9.3))
|
||||
eslint-plugin-storybook:
|
||||
specifier: ^10.1.10
|
||||
version: 10.2.4(eslint@9.39.2(jiti@2.6.1))(storybook@10.2.4(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3)
|
||||
version: 10.2.4(eslint@9.39.2(jiti@1.21.7))(storybook@10.2.4(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3)
|
||||
storybook:
|
||||
specifier: ^10.1.10
|
||||
version: 10.2.4(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||
@@ -1018,9 +1033,15 @@ packages:
|
||||
'@clack/core@1.0.0-alpha.7':
|
||||
resolution: {integrity: sha512-3vdh6Ar09D14rVxJZIm3VQJkU+ZOKKT5I5cC0cOVazy70CNyYYjiwRj9unwalhESndgxx6bGc/m6Hhs4EKF5XQ==}
|
||||
|
||||
'@clack/core@1.1.0':
|
||||
resolution: {integrity: sha512-SVcm4Dqm2ukn64/8Gub2wnlA5nS2iWJyCkdNHcvNHPIeBTGojpdJ+9cZKwLfmqy7irD4N5qLteSilJlE0WLAtA==}
|
||||
|
||||
'@clack/prompts@1.0.0-alpha.9':
|
||||
resolution: {integrity: sha512-sKs0UjiHFWvry4SiRfBi5Qnj0C/6AYx8aKkFPZQSuUZXgAram25ZDmhQmP7vj1aFyLpfHWtLQjWvOvcat0TOLg==}
|
||||
|
||||
'@clack/prompts@1.1.0':
|
||||
resolution: {integrity: sha512-pkqbPGtohJAvm4Dphs2M8xE29ggupihHdy1x84HNojZuMtFsHiUlRvqD24tM2+XmI+61LlfNceM3Wr7U5QES5g==}
|
||||
|
||||
'@cloudflare/kv-asset-handler@0.4.2':
|
||||
resolution: {integrity: sha512-SIOD2DxrRRwQ+jgzlXCqoEFiKOFqaPjhnNTGKXSRLvp1HiOvapLaFG2kEr9dYQTYe8rKrd9uvDUzmAITeNyaHQ==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
@@ -6412,6 +6433,10 @@ packages:
|
||||
resolution: {integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==}
|
||||
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
|
||||
|
||||
ini@6.0.0:
|
||||
resolution: {integrity: sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ==}
|
||||
engines: {node: ^20.17.0 || >=22.9.0}
|
||||
|
||||
inline-style-parser@0.2.7:
|
||||
resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==}
|
||||
|
||||
@@ -6662,6 +6687,10 @@ packages:
|
||||
jszip@3.10.1:
|
||||
resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==}
|
||||
|
||||
keep-a-changelog@3.0.2:
|
||||
resolution: {integrity: sha512-MKk4RWduGopP7MoA8bkzdXQk9CV6cbqNjMzpClM/mA2kT+jsbvO6co2h3UuYPe6/bX/UITemzLBNEanplrIOmQ==}
|
||||
hasBin: true
|
||||
|
||||
keyv@4.5.4:
|
||||
resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
|
||||
|
||||
@@ -8345,6 +8374,11 @@ packages:
|
||||
engines: {node: '>=10'}
|
||||
hasBin: true
|
||||
|
||||
semver@7.7.4:
|
||||
resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==}
|
||||
engines: {node: '>=10'}
|
||||
hasBin: true
|
||||
|
||||
send@1.2.1:
|
||||
resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==}
|
||||
engines: {node: '>= 18'}
|
||||
@@ -10098,12 +10132,21 @@ snapshots:
|
||||
picocolors: 1.1.1
|
||||
sisteransi: 1.0.5
|
||||
|
||||
'@clack/core@1.1.0':
|
||||
dependencies:
|
||||
sisteransi: 1.0.5
|
||||
|
||||
'@clack/prompts@1.0.0-alpha.9':
|
||||
dependencies:
|
||||
'@clack/core': 1.0.0-alpha.7
|
||||
picocolors: 1.1.1
|
||||
sisteransi: 1.0.5
|
||||
|
||||
'@clack/prompts@1.1.0':
|
||||
dependencies:
|
||||
'@clack/core': 1.1.0
|
||||
sisteransi: 1.0.5
|
||||
|
||||
'@cloudflare/kv-asset-handler@0.4.2': {}
|
||||
|
||||
'@cloudflare/unenv-preset@2.12.0(unenv@2.0.0-rc.24)(workerd@1.20260131.0)':
|
||||
@@ -10686,11 +10729,11 @@ snapshots:
|
||||
|
||||
'@eslint-community/regexpp@4.12.2': {}
|
||||
|
||||
'@eslint/compat@1.4.1(eslint@9.39.2(jiti@1.21.7))':
|
||||
'@eslint/compat@1.4.1(eslint@9.39.2(jiti@2.6.1))':
|
||||
dependencies:
|
||||
'@eslint/core': 0.17.0
|
||||
optionalDependencies:
|
||||
eslint: 9.39.2(jiti@1.21.7)
|
||||
eslint: 9.39.2(jiti@2.6.1)
|
||||
|
||||
'@eslint/config-array@0.21.1':
|
||||
dependencies:
|
||||
@@ -11311,36 +11354,36 @@ snapshots:
|
||||
- utf-8-validate
|
||||
- vue
|
||||
|
||||
'@nuxt/eslint-config@0.5.7(@typescript-eslint/utils@8.54.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)':
|
||||
'@nuxt/eslint-config@0.5.7(@typescript-eslint/utils@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)':
|
||||
dependencies:
|
||||
'@eslint/js': 9.39.2
|
||||
'@nuxt/eslint-plugin': 0.5.7(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)
|
||||
'@stylistic/eslint-plugin': 2.13.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)
|
||||
'@typescript-eslint/eslint-plugin': 8.54.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)
|
||||
'@typescript-eslint/parser': 8.54.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)
|
||||
eslint: 9.39.2(jiti@1.21.7)
|
||||
eslint-config-flat-gitignore: 0.3.0(eslint@9.39.2(jiti@1.21.7))
|
||||
'@nuxt/eslint-plugin': 0.5.7(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@stylistic/eslint-plugin': 2.13.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@typescript-eslint/eslint-plugin': 8.54.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@typescript-eslint/parser': 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
|
||||
eslint: 9.39.2(jiti@2.6.1)
|
||||
eslint-config-flat-gitignore: 0.3.0(eslint@9.39.2(jiti@2.6.1))
|
||||
eslint-flat-config-utils: 0.4.0
|
||||
eslint-plugin-import-x: 4.16.1(@typescript-eslint/utils@8.54.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.2(jiti@1.21.7))
|
||||
eslint-plugin-jsdoc: 50.8.0(eslint@9.39.2(jiti@1.21.7))
|
||||
eslint-plugin-regexp: 2.10.0(eslint@9.39.2(jiti@1.21.7))
|
||||
eslint-plugin-unicorn: 55.0.0(eslint@9.39.2(jiti@1.21.7))
|
||||
eslint-plugin-vue: 9.33.0(eslint@9.39.2(jiti@1.21.7))
|
||||
eslint-plugin-import-x: 4.16.1(@typescript-eslint/utils@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))
|
||||
eslint-plugin-jsdoc: 50.8.0(eslint@9.39.2(jiti@2.6.1))
|
||||
eslint-plugin-regexp: 2.10.0(eslint@9.39.2(jiti@2.6.1))
|
||||
eslint-plugin-unicorn: 55.0.0(eslint@9.39.2(jiti@2.6.1))
|
||||
eslint-plugin-vue: 9.33.0(eslint@9.39.2(jiti@2.6.1))
|
||||
globals: 15.15.0
|
||||
local-pkg: 0.5.1
|
||||
pathe: 1.1.2
|
||||
vue-eslint-parser: 9.4.3(eslint@9.39.2(jiti@1.21.7))
|
||||
vue-eslint-parser: 9.4.3(eslint@9.39.2(jiti@2.6.1))
|
||||
transitivePeerDependencies:
|
||||
- '@typescript-eslint/utils'
|
||||
- eslint-import-resolver-node
|
||||
- supports-color
|
||||
- typescript
|
||||
|
||||
'@nuxt/eslint-plugin@0.5.7(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)':
|
||||
'@nuxt/eslint-plugin@0.5.7(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)':
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 8.54.0
|
||||
'@typescript-eslint/utils': 8.54.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)
|
||||
eslint: 9.39.2(jiti@1.21.7)
|
||||
'@typescript-eslint/utils': 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
|
||||
eslint: 9.39.2(jiti@2.6.1)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
- typescript
|
||||
@@ -12764,18 +12807,6 @@ snapshots:
|
||||
|
||||
'@stripe/stripe-js@7.9.0': {}
|
||||
|
||||
'@stylistic/eslint-plugin@2.13.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)':
|
||||
dependencies:
|
||||
'@typescript-eslint/utils': 8.54.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)
|
||||
eslint: 9.39.2(jiti@1.21.7)
|
||||
eslint-visitor-keys: 4.2.1
|
||||
espree: 10.4.0
|
||||
estraverse: 5.3.0
|
||||
picomatch: 4.0.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
- typescript
|
||||
|
||||
'@stylistic/eslint-plugin@2.13.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)':
|
||||
dependencies:
|
||||
'@typescript-eslint/utils': 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
|
||||
@@ -12787,7 +12818,6 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
- typescript
|
||||
optional: true
|
||||
|
||||
'@svgdotjs/svg.draggable.js@3.0.6(@svgdotjs/svg.js@3.2.5)':
|
||||
dependencies:
|
||||
@@ -13185,22 +13215,6 @@ snapshots:
|
||||
dependencies:
|
||||
'@types/node': 20.19.31
|
||||
|
||||
'@typescript-eslint/eslint-plugin@8.54.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)':
|
||||
dependencies:
|
||||
'@eslint-community/regexpp': 4.12.2
|
||||
'@typescript-eslint/parser': 8.54.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)
|
||||
'@typescript-eslint/scope-manager': 8.54.0
|
||||
'@typescript-eslint/type-utils': 8.54.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)
|
||||
'@typescript-eslint/utils': 8.54.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)
|
||||
'@typescript-eslint/visitor-keys': 8.54.0
|
||||
eslint: 9.39.2(jiti@1.21.7)
|
||||
ignore: 7.0.5
|
||||
natural-compare: 1.4.0
|
||||
ts-api-utils: 2.4.0(typescript@5.9.3)
|
||||
typescript: 5.9.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/eslint-plugin@8.54.0(@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)':
|
||||
dependencies:
|
||||
'@eslint-community/regexpp': 4.12.2
|
||||
@@ -13217,18 +13231,6 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)':
|
||||
dependencies:
|
||||
'@typescript-eslint/scope-manager': 8.54.0
|
||||
'@typescript-eslint/types': 8.54.0
|
||||
'@typescript-eslint/typescript-estree': 8.54.0(typescript@5.9.3)
|
||||
'@typescript-eslint/visitor-keys': 8.54.0
|
||||
debug: 4.4.3
|
||||
eslint: 9.39.2(jiti@1.21.7)
|
||||
typescript: 5.9.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/parser@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)':
|
||||
dependencies:
|
||||
'@typescript-eslint/scope-manager': 8.54.0
|
||||
@@ -13259,18 +13261,6 @@ snapshots:
|
||||
dependencies:
|
||||
typescript: 5.9.3
|
||||
|
||||
'@typescript-eslint/type-utils@8.54.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)':
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 8.54.0
|
||||
'@typescript-eslint/typescript-estree': 8.54.0(typescript@5.9.3)
|
||||
'@typescript-eslint/utils': 8.54.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)
|
||||
debug: 4.4.3
|
||||
eslint: 9.39.2(jiti@1.21.7)
|
||||
ts-api-utils: 2.4.0(typescript@5.9.3)
|
||||
typescript: 5.9.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/type-utils@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)':
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 8.54.0
|
||||
@@ -13435,10 +13425,10 @@ snapshots:
|
||||
vite: 5.4.21(@types/node@20.19.31)(lightningcss@1.30.2)(sass@1.97.3)(terser@5.46.0)
|
||||
vue: 3.5.27(typescript@5.9.3)
|
||||
|
||||
'@vitejs/plugin-vue@6.0.4(vite@6.4.1(@types/node@20.19.31)(jiti@1.21.7)(lightningcss@1.30.2)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2))(vue@3.5.27(typescript@5.9.3))':
|
||||
'@vitejs/plugin-vue@6.0.4(vite@6.4.1(@types/node@20.19.31)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2))(vue@3.5.27(typescript@5.9.3))':
|
||||
dependencies:
|
||||
'@rolldown/pluginutils': 1.0.0-rc.2
|
||||
vite: 6.4.1(@types/node@20.19.31)(jiti@1.21.7)(lightningcss@1.30.2)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2)
|
||||
vite: 6.4.1(@types/node@20.19.31)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2)
|
||||
vue: 3.5.27(typescript@5.9.3)
|
||||
|
||||
'@vitejs/plugin-vue@6.0.4(vite@7.3.1(@types/node@20.19.31)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2))(vue@3.5.27(typescript@5.9.3))':
|
||||
@@ -14964,10 +14954,10 @@ snapshots:
|
||||
|
||||
escape-string-regexp@5.0.0: {}
|
||||
|
||||
eslint-config-flat-gitignore@0.3.0(eslint@9.39.2(jiti@1.21.7)):
|
||||
eslint-config-flat-gitignore@0.3.0(eslint@9.39.2(jiti@2.6.1)):
|
||||
dependencies:
|
||||
'@eslint/compat': 1.4.1(eslint@9.39.2(jiti@1.21.7))
|
||||
eslint: 9.39.2(jiti@1.21.7)
|
||||
'@eslint/compat': 1.4.1(eslint@9.39.2(jiti@2.6.1))
|
||||
eslint: 9.39.2(jiti@2.6.1)
|
||||
find-up-simple: 1.0.1
|
||||
|
||||
eslint-config-prettier@10.1.8(eslint@9.39.2(jiti@2.6.1)):
|
||||
@@ -14985,12 +14975,12 @@ snapshots:
|
||||
optionalDependencies:
|
||||
unrs-resolver: 1.11.1
|
||||
|
||||
eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.54.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.2(jiti@1.21.7)):
|
||||
eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.2(jiti@2.6.1)):
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 8.54.0
|
||||
comment-parser: 1.4.5
|
||||
debug: 4.4.3
|
||||
eslint: 9.39.2(jiti@1.21.7)
|
||||
eslint: 9.39.2(jiti@2.6.1)
|
||||
eslint-import-context: 0.1.9(unrs-resolver@1.11.1)
|
||||
is-glob: 4.0.3
|
||||
minimatch: 10.1.2
|
||||
@@ -14998,18 +14988,18 @@ snapshots:
|
||||
stable-hash-x: 0.2.0
|
||||
unrs-resolver: 1.11.1
|
||||
optionalDependencies:
|
||||
'@typescript-eslint/utils': 8.54.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)
|
||||
'@typescript-eslint/utils': 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
eslint-plugin-jsdoc@50.8.0(eslint@9.39.2(jiti@1.21.7)):
|
||||
eslint-plugin-jsdoc@50.8.0(eslint@9.39.2(jiti@2.6.1)):
|
||||
dependencies:
|
||||
'@es-joy/jsdoccomment': 0.50.2
|
||||
are-docs-informative: 0.0.2
|
||||
comment-parser: 1.4.1
|
||||
debug: 4.4.3
|
||||
escape-string-regexp: 4.0.0
|
||||
eslint: 9.39.2(jiti@1.21.7)
|
||||
eslint: 9.39.2(jiti@2.6.1)
|
||||
espree: 10.4.0
|
||||
esquery: 1.7.0
|
||||
parse-imports-exports: 0.2.4
|
||||
@@ -15027,12 +15017,12 @@ snapshots:
|
||||
optionalDependencies:
|
||||
eslint-config-prettier: 10.1.8(eslint@9.39.2(jiti@2.6.1))
|
||||
|
||||
eslint-plugin-regexp@2.10.0(eslint@9.39.2(jiti@1.21.7)):
|
||||
eslint-plugin-regexp@2.10.0(eslint@9.39.2(jiti@2.6.1)):
|
||||
dependencies:
|
||||
'@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@1.21.7))
|
||||
'@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@2.6.1))
|
||||
'@eslint-community/regexpp': 4.12.2
|
||||
comment-parser: 1.4.5
|
||||
eslint: 9.39.2(jiti@1.21.7)
|
||||
eslint: 9.39.2(jiti@2.6.1)
|
||||
jsdoc-type-pratt-parser: 4.8.0
|
||||
refa: 0.12.1
|
||||
regexp-ast-analysis: 0.7.1
|
||||
@@ -15042,29 +15032,29 @@ snapshots:
|
||||
dependencies:
|
||||
eslint: 9.39.2(jiti@2.6.1)
|
||||
|
||||
eslint-plugin-storybook@10.2.4(eslint@9.39.2(jiti@2.6.1))(storybook@10.2.4(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3):
|
||||
eslint-plugin-storybook@10.2.4(eslint@9.39.2(jiti@1.21.7))(storybook@10.2.4(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4))(typescript@5.9.3):
|
||||
dependencies:
|
||||
'@typescript-eslint/utils': 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
|
||||
eslint: 9.39.2(jiti@2.6.1)
|
||||
'@typescript-eslint/utils': 8.54.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3)
|
||||
eslint: 9.39.2(jiti@1.21.7)
|
||||
storybook: 10.2.4(@testing-library/dom@10.4.1)(prettier@3.8.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
- typescript
|
||||
|
||||
eslint-plugin-turbo@2.8.2(eslint@9.39.2(jiti@1.21.7))(turbo@2.8.2):
|
||||
eslint-plugin-turbo@2.8.2(eslint@9.39.2(jiti@2.6.1))(turbo@2.8.2):
|
||||
dependencies:
|
||||
dotenv: 16.0.3
|
||||
eslint: 9.39.2(jiti@1.21.7)
|
||||
eslint: 9.39.2(jiti@2.6.1)
|
||||
turbo: 2.8.2
|
||||
|
||||
eslint-plugin-unicorn@55.0.0(eslint@9.39.2(jiti@1.21.7)):
|
||||
eslint-plugin-unicorn@55.0.0(eslint@9.39.2(jiti@2.6.1)):
|
||||
dependencies:
|
||||
'@babel/helper-validator-identifier': 7.28.5
|
||||
'@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@1.21.7))
|
||||
'@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@2.6.1))
|
||||
ci-info: 4.4.0
|
||||
clean-regexp: 1.0.0
|
||||
core-js-compat: 3.48.0
|
||||
eslint: 9.39.2(jiti@1.21.7)
|
||||
eslint: 9.39.2(jiti@2.6.1)
|
||||
esquery: 1.7.0
|
||||
globals: 15.15.0
|
||||
indent-string: 4.0.0
|
||||
@@ -15091,16 +15081,16 @@ snapshots:
|
||||
'@stylistic/eslint-plugin': 2.13.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@typescript-eslint/parser': 8.54.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3)
|
||||
|
||||
eslint-plugin-vue@9.33.0(eslint@9.39.2(jiti@1.21.7)):
|
||||
eslint-plugin-vue@9.33.0(eslint@9.39.2(jiti@2.6.1)):
|
||||
dependencies:
|
||||
'@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@1.21.7))
|
||||
eslint: 9.39.2(jiti@1.21.7)
|
||||
'@eslint-community/eslint-utils': 4.9.1(eslint@9.39.2(jiti@2.6.1))
|
||||
eslint: 9.39.2(jiti@2.6.1)
|
||||
globals: 13.24.0
|
||||
natural-compare: 1.4.0
|
||||
nth-check: 2.1.1
|
||||
postcss-selector-parser: 6.1.2
|
||||
semver: 7.7.3
|
||||
vue-eslint-parser: 9.4.3(eslint@9.39.2(jiti@1.21.7))
|
||||
vue-eslint-parser: 9.4.3(eslint@9.39.2(jiti@2.6.1))
|
||||
xml-name-validator: 4.0.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
@@ -15895,6 +15885,8 @@ snapshots:
|
||||
|
||||
ini@4.1.1: {}
|
||||
|
||||
ini@6.0.0: {}
|
||||
|
||||
inline-style-parser@0.2.7: {}
|
||||
|
||||
intl-messageformat@10.7.18:
|
||||
@@ -16114,6 +16106,11 @@ snapshots:
|
||||
readable-stream: 2.3.8(patch_hash=h52dazg37p4h3yox67pw36akse)
|
||||
setimmediate: 1.0.5
|
||||
|
||||
keep-a-changelog@3.0.2:
|
||||
dependencies:
|
||||
ini: 6.0.0
|
||||
semver: 7.7.4
|
||||
|
||||
keyv@4.5.4:
|
||||
dependencies:
|
||||
json-buffer: 3.0.1
|
||||
@@ -18367,6 +18364,8 @@ snapshots:
|
||||
|
||||
semver@7.7.3: {}
|
||||
|
||||
semver@7.7.4: {}
|
||||
|
||||
send@1.2.1:
|
||||
dependencies:
|
||||
debug: 4.4.3
|
||||
@@ -19358,23 +19357,6 @@ snapshots:
|
||||
sass: 1.97.3
|
||||
terser: 5.46.0
|
||||
|
||||
vite@6.4.1(@types/node@20.19.31)(jiti@1.21.7)(lightningcss@1.30.2)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2):
|
||||
dependencies:
|
||||
esbuild: 0.25.12
|
||||
fdir: 6.5.0(picomatch@4.0.3)
|
||||
picomatch: 4.0.3
|
||||
postcss: 8.5.6
|
||||
rollup: 4.57.1
|
||||
tinyglobby: 0.2.15
|
||||
optionalDependencies:
|
||||
'@types/node': 20.19.31
|
||||
fsevents: 2.3.3
|
||||
jiti: 1.21.7
|
||||
lightningcss: 1.30.2
|
||||
sass: 1.97.3
|
||||
terser: 5.46.0
|
||||
yaml: 2.8.2
|
||||
|
||||
vite@6.4.1(@types/node@20.19.31)(jiti@2.6.1)(lightningcss@1.30.2)(sass@1.97.3)(terser@5.46.0)(yaml@2.8.2):
|
||||
dependencies:
|
||||
esbuild: 0.25.12
|
||||
@@ -19569,10 +19551,10 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
vue-eslint-parser@9.4.3(eslint@9.39.2(jiti@1.21.7)):
|
||||
vue-eslint-parser@9.4.3(eslint@9.39.2(jiti@2.6.1)):
|
||||
dependencies:
|
||||
debug: 4.4.3
|
||||
eslint: 9.39.2(jiti@1.21.7)
|
||||
eslint: 9.39.2(jiti@2.6.1)
|
||||
eslint-scope: 7.2.2
|
||||
eslint-visitor-keys: 3.4.3
|
||||
espree: 9.6.1
|
||||
|
||||
425
scripts/collect-changelog.ts
Normal file
425
scripts/collect-changelog.ts
Normal file
@@ -0,0 +1,425 @@
|
||||
import { execSync } from 'child_process'
|
||||
import chalk from 'chalk'
|
||||
import * as fs from 'fs'
|
||||
import * as path from 'path'
|
||||
|
||||
type Product = 'web' | 'app' | 'hosting'
|
||||
|
||||
const CHANGELOG_MARKER = '<!-- changelog -->'
|
||||
const SECTION_ORDER = ['added', 'changed', 'deprecated', 'removed', 'fixed', 'security'] as const
|
||||
const SECTION_HEADERS: Record<string, string> = {
|
||||
added: '## Added',
|
||||
changed: '## Changed',
|
||||
deprecated: '## Deprecated',
|
||||
removed: '## Removed',
|
||||
fixed: '## Fixed',
|
||||
security: '## Security',
|
||||
}
|
||||
|
||||
const PRODUCT_SUMMARY_MAP: Record<string, Product> = {
|
||||
App: 'app',
|
||||
Website: 'web',
|
||||
Hosting: 'hosting',
|
||||
}
|
||||
|
||||
const GITHUB_API = 'https://api.github.com'
|
||||
const REPO = 'modrinth/code'
|
||||
|
||||
interface ParsedChangelog {
|
||||
entries: Map<Product, Map<string, string[]>>
|
||||
}
|
||||
|
||||
interface PRInfo {
|
||||
number: number
|
||||
mergedAt: string
|
||||
}
|
||||
|
||||
interface CommentInfo {
|
||||
id: number
|
||||
body: string
|
||||
}
|
||||
|
||||
function parseArgs(argv: string[]): {
|
||||
version?: string
|
||||
dryRun: boolean
|
||||
} {
|
||||
let version: string | undefined
|
||||
let dryRun = false
|
||||
|
||||
let i = 0
|
||||
while (i < argv.length) {
|
||||
if (argv[i] === '--') {
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if (argv[i] === '--version') {
|
||||
i++
|
||||
if (i >= argv.length || argv[i].startsWith('--')) {
|
||||
console.error(chalk.red('--version requires a value'))
|
||||
process.exit(1)
|
||||
}
|
||||
version = argv[i]
|
||||
i++
|
||||
} else if (argv[i] === '--dry-run') {
|
||||
dryRun = true
|
||||
i++
|
||||
} else {
|
||||
console.error(chalk.red(`Unknown argument: ${argv[i]}`))
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
return { version, dryRun }
|
||||
}
|
||||
|
||||
async function getParser() {
|
||||
const mod = await import('keep-a-changelog')
|
||||
return mod.parser
|
||||
}
|
||||
|
||||
function parseChangelogComment(body: string, parse: Function): ParsedChangelog | null {
|
||||
if (!body.includes(CHANGELOG_MARKER)) return null
|
||||
|
||||
const entries = new Map<Product, Map<string, string[]>>()
|
||||
|
||||
const detailsRegex = /<details>\s*<summary>(.*?)<\/summary>([\s\S]*?)<\/details>/g
|
||||
let match
|
||||
while ((match = detailsRegex.exec(body)) !== null) {
|
||||
const summaryLabel = match[1].trim()
|
||||
const product = PRODUCT_SUMMARY_MAP[summaryLabel]
|
||||
if (!product) continue
|
||||
|
||||
const content = match[2].replace(/<!--[\s\S]*?-->/g, '').trim()
|
||||
const firstSection = content.search(/^### /m)
|
||||
if (firstSection === -1) continue
|
||||
|
||||
const changelogMd = `# Changelog\n\n## [Unreleased]\n${content.slice(firstSection)}`
|
||||
|
||||
let changelog
|
||||
try {
|
||||
changelog = parse(changelogMd)
|
||||
} catch {
|
||||
continue
|
||||
}
|
||||
|
||||
const unreleased = changelog.findRelease()
|
||||
if (!unreleased || unreleased.isEmpty()) continue
|
||||
|
||||
const sections = new Map<string, string[]>()
|
||||
for (const type of SECTION_ORDER) {
|
||||
const changes = unreleased.changes.get(type)
|
||||
if (changes && changes.length > 0) {
|
||||
sections.set(type, changes.map((c: { title: string }) => c.title))
|
||||
}
|
||||
}
|
||||
|
||||
if (sections.size > 0) {
|
||||
entries.set(product, sections)
|
||||
}
|
||||
}
|
||||
|
||||
if (entries.size === 0) return null
|
||||
|
||||
return { entries }
|
||||
}
|
||||
|
||||
function linkifyIssues(text: string): string {
|
||||
return text.replace(
|
||||
/\[#(\d+)\]/g,
|
||||
'[#$1](https://github.com/modrinth/code/issues/$1)',
|
||||
)
|
||||
}
|
||||
|
||||
function buildBody(sections: Map<string, string[]>): string {
|
||||
const parts: string[] = []
|
||||
|
||||
for (const type of SECTION_ORDER) {
|
||||
const entries = sections.get(type)
|
||||
if (!entries || entries.length === 0) continue
|
||||
|
||||
const lines = entries.map((e) => `- ${linkifyIssues(e)}`)
|
||||
parts.push(`${SECTION_HEADERS[type]}\n${lines.join('\n')}`)
|
||||
}
|
||||
|
||||
return parts.join('\n\n')
|
||||
}
|
||||
|
||||
function mergeSections(
|
||||
allEntries: { sections: Map<string, string[]>; prNumber: number }[],
|
||||
): Map<string, string[]> {
|
||||
const merged = new Map<string, string[]>()
|
||||
|
||||
for (const { sections } of allEntries) {
|
||||
for (const [type, entries] of sections) {
|
||||
if (!merged.has(type)) {
|
||||
merged.set(type, [])
|
||||
}
|
||||
merged.get(type)!.push(...entries)
|
||||
}
|
||||
}
|
||||
|
||||
return merged
|
||||
}
|
||||
|
||||
async function githubFetch(endpoint: string, token: string, options?: RequestInit): Promise<Response> {
|
||||
const url = endpoint.startsWith('http') ? endpoint : `${GITHUB_API}${endpoint}`
|
||||
return fetch(url, {
|
||||
...options,
|
||||
headers: {
|
||||
Authorization: `token ${token}`,
|
||||
Accept: 'application/vnd.github.v3+json',
|
||||
...options?.headers,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
async function fetchMergedPRs(token: string, sinceDate: string): Promise<PRInfo[]> {
|
||||
const prs: PRInfo[] = []
|
||||
let page = 1
|
||||
|
||||
while (true) {
|
||||
const res = await githubFetch(
|
||||
`/repos/${REPO}/pulls?state=closed&base=main&sort=updated&direction=desc&per_page=100&page=${page}`,
|
||||
token,
|
||||
)
|
||||
|
||||
if (!res.ok) {
|
||||
console.error(chalk.red(`GitHub API error: ${res.status} ${res.statusText}`))
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const data = (await res.json()) as Array<{
|
||||
number: number
|
||||
merged_at: string | null
|
||||
updated_at: string
|
||||
}>
|
||||
|
||||
if (data.length === 0) break
|
||||
|
||||
let foundOlder = false
|
||||
for (const pr of data) {
|
||||
if (!pr.merged_at) continue
|
||||
|
||||
if (new Date(pr.merged_at) > new Date(sinceDate)) {
|
||||
prs.push({ number: pr.number, mergedAt: pr.merged_at })
|
||||
} else {
|
||||
foundOlder = true
|
||||
}
|
||||
}
|
||||
|
||||
if (foundOlder || data.length < 100) break
|
||||
page++
|
||||
}
|
||||
|
||||
return prs
|
||||
}
|
||||
|
||||
async function fetchBotComment(token: string, prNumber: number): Promise<CommentInfo | null> {
|
||||
const res = await githubFetch(
|
||||
`/repos/${REPO}/issues/${prNumber}/comments?per_page=100`,
|
||||
token,
|
||||
)
|
||||
|
||||
if (!res.ok) return null
|
||||
|
||||
const comments = (await res.json()) as Array<{ id: number; body: string }>
|
||||
const comment = comments.find((c) => c.body.includes(CHANGELOG_MARKER))
|
||||
|
||||
if (!comment) return null
|
||||
return { id: comment.id, body: comment.body }
|
||||
}
|
||||
|
||||
async function markCommentBaked(token: string, commentId: number, currentBody: string): Promise<void> {
|
||||
const admonition = '> [!NOTE]\n> This changelog has been baked. Any further edits will not be reflected.\n\n'
|
||||
const newBody = admonition + currentBody
|
||||
|
||||
await githubFetch(`/repos/${REPO}/issues/comments/${commentId}`, token, {
|
||||
method: 'PATCH',
|
||||
body: JSON.stringify({ body: newBody }),
|
||||
})
|
||||
}
|
||||
|
||||
function getCurrentPacificISO(): string {
|
||||
const now = new Date()
|
||||
|
||||
const formatter = new Intl.DateTimeFormat('en-US', {
|
||||
timeZone: 'America/Los_Angeles',
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
hour12: false,
|
||||
})
|
||||
|
||||
const parts = formatter.formatToParts(now)
|
||||
const get = (type: string) => parts.find((p) => p.type === type)?.value ?? '00'
|
||||
|
||||
const year = get('year')
|
||||
const month = get('month')
|
||||
const day = get('day')
|
||||
const hour = get('hour')
|
||||
const minute = get('minute')
|
||||
const second = get('second')
|
||||
|
||||
const jan = new Date(now.getFullYear(), 0, 1)
|
||||
const jul = new Date(now.getFullYear(), 6, 1)
|
||||
const stdOffset = Math.max(jan.getTimezoneOffset(), jul.getTimezoneOffset())
|
||||
const pacificNow = new Date(
|
||||
now.toLocaleString('en-US', { timeZone: 'America/Los_Angeles' }),
|
||||
)
|
||||
const utcNow = new Date(now.toLocaleString('en-US', { timeZone: 'UTC' }))
|
||||
const offsetMinutes = (utcNow.getTime() - pacificNow.getTime()) / 60000
|
||||
const isDST = offsetMinutes !== stdOffset
|
||||
|
||||
const offsetStr = isDST ? '-07:00' : '-08:00'
|
||||
|
||||
return `${year}-${month}-${day}T${hour}:${minute}:${second}${offsetStr}`
|
||||
}
|
||||
|
||||
function generateEntry(product: string, body: string, version?: string): string {
|
||||
const dateStr = getCurrentPacificISO()
|
||||
const versionLine = version ? `\n\t\tversion: '${version}',` : ''
|
||||
|
||||
return `\t{
|
||||
\t\tdate: \`${dateStr}\`,
|
||||
\t\tproduct: '${product}',${versionLine}
|
||||
\t\tbody: \`${body}\`,
|
||||
\t},`
|
||||
}
|
||||
|
||||
function insertIntoChangelog(changelogPath: string, entryString: string): void {
|
||||
const content = fs.readFileSync(changelogPath, 'utf-8')
|
||||
|
||||
const marker = 'const VERSIONS: VersionEntry[] = ['
|
||||
const markerIndex = content.indexOf(marker)
|
||||
|
||||
if (markerIndex === -1) {
|
||||
console.error(chalk.red('Could not find VERSIONS array in changelog.ts'))
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const insertPos = content.indexOf('\n', markerIndex) + 1
|
||||
|
||||
const newContent = content.slice(0, insertPos) + entryString + '\n' + content.slice(insertPos)
|
||||
|
||||
fs.writeFileSync(changelogPath, newContent, 'utf-8')
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const args = parseArgs(process.argv.slice(2))
|
||||
|
||||
let token = process.env.GITHUB_TOKEN
|
||||
if (!token) {
|
||||
try {
|
||||
token = execSync('gh auth token', { encoding: 'utf-8' }).trim()
|
||||
} catch {
|
||||
console.error(chalk.red('GITHUB_TOKEN not set and `gh auth token` failed. Run `gh auth login` or set GITHUB_TOKEN.'))
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
const parse = await getParser()
|
||||
const rootDir = path.resolve(__dirname, '..')
|
||||
const changelogPath = path.join(rootDir, 'packages', 'blog', 'changelog.ts')
|
||||
|
||||
const prodDate = execSync('git log -1 --format=%aI origin/prod', { encoding: 'utf-8' }).trim()
|
||||
console.log(chalk.gray(`Last prod commit: ${prodDate}`))
|
||||
|
||||
const prs = await fetchMergedPRs(token, prodDate)
|
||||
console.log(chalk.gray(`Found ${prs.length} merged PR(s) since last prod deploy`))
|
||||
|
||||
if (prs.length === 0) {
|
||||
console.log(chalk.yellow('No merged PRs found since last prod deploy'))
|
||||
return
|
||||
}
|
||||
|
||||
const allEntries = new Map<Product, { sections: Map<string, string[]>; prNumber: number }[]>()
|
||||
const processedComments: { commentId: number; body: string }[] = []
|
||||
|
||||
for (const pr of prs) {
|
||||
const comment = await fetchBotComment(token, pr.number)
|
||||
if (!comment) {
|
||||
console.log(chalk.gray(`PR #${pr.number}: no changelog comment, skipping`))
|
||||
continue
|
||||
}
|
||||
|
||||
if (comment.body.includes('> This changelog has been baked.')) {
|
||||
console.log(chalk.gray(`PR #${pr.number}: already baked, skipping`))
|
||||
continue
|
||||
}
|
||||
|
||||
const parsed = parseChangelogComment(comment.body, parse)
|
||||
if (!parsed) {
|
||||
console.log(chalk.gray(`PR #${pr.number}: no changelog entries, skipping`))
|
||||
continue
|
||||
}
|
||||
|
||||
const products = [...parsed.entries.keys()]
|
||||
const types = [...new Set([...parsed.entries.values()].flatMap((s) => [...s.keys()]))]
|
||||
console.log(chalk.cyan(`PR #${pr.number}: ${products.join(', ')} — ${types.join(', ')}`))
|
||||
|
||||
for (const [product, sections] of parsed.entries) {
|
||||
if (!allEntries.has(product)) {
|
||||
allEntries.set(product, [])
|
||||
}
|
||||
allEntries.get(product)!.push({ sections, prNumber: pr.number })
|
||||
}
|
||||
|
||||
processedComments.push({ commentId: comment.id, body: comment.body })
|
||||
}
|
||||
|
||||
if (allEntries.size === 0) {
|
||||
console.log(chalk.yellow('No changelog entries found in merged PRs'))
|
||||
return
|
||||
}
|
||||
|
||||
const hasApp = allEntries.has('app')
|
||||
if (hasApp && !args.version) {
|
||||
console.error(chalk.red('--version is required when app changelog entries exist'))
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const products = [...allEntries.keys()].reverse()
|
||||
for (const product of products) {
|
||||
const productEntries = allEntries.get(product)!
|
||||
const mergedSections = mergeSections(productEntries)
|
||||
const body = buildBody(mergedSections)
|
||||
|
||||
if (body.includes('`')) {
|
||||
console.error(
|
||||
chalk.yellow(
|
||||
`Warning: Changelog body for ${product} contains backticks — this may break the template literal in changelog.ts`,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
const version = product === 'app' ? args.version : undefined
|
||||
const entryString = generateEntry(product, body, version)
|
||||
|
||||
if (args.dryRun) {
|
||||
console.log(chalk.cyan(`\n[dry-run] Would insert for ${product}:`))
|
||||
console.log(entryString)
|
||||
} else {
|
||||
insertIntoChangelog(changelogPath, entryString)
|
||||
console.log(chalk.green(`Inserted changelog entry for ${product}`))
|
||||
}
|
||||
}
|
||||
|
||||
if (!args.dryRun) {
|
||||
for (const { commentId, body } of processedComments) {
|
||||
await markCommentBaked(token, commentId, body)
|
||||
}
|
||||
console.log(chalk.gray(`Marked ${processedComments.length} comment(s) as baked`))
|
||||
}
|
||||
|
||||
console.log()
|
||||
if (args.dryRun) {
|
||||
console.log(chalk.cyan('Dry run complete — no changes written'))
|
||||
} else {
|
||||
console.log(chalk.green('Done! Review the changes in packages/blog/changelog.ts'))
|
||||
}
|
||||
}
|
||||
|
||||
main()
|
||||
Reference in New Issue
Block a user