From 8a74f79378636a9aae48f5e77fe4c4f213cd68fe Mon Sep 17 00:00:00 2001 From: Hazelnoot Date: Tue, 7 Oct 2025 19:51:28 -0400 Subject: [PATCH] fix storybook lint/built --- packages/frontend/.storybook/charts.ts | 8 ++- packages/frontend/.storybook/fakes.ts | 42 ++++++++++++- packages/frontend/.storybook/generate.tsx | 9 ++- packages/frontend/.storybook/locale.d.ts | 3 + packages/frontend/.storybook/main.ts | 7 ++- packages/frontend/.storybook/mocks.ts | 3 +- packages/frontend/.storybook/package.json | 3 - .../frontend/.storybook/preload-locale.ts | 4 +- packages/frontend/.storybook/preload-theme.ts | 6 +- packages/frontend/.storybook/preview.ts | 23 +++---- packages/frontend/.storybook/themes.d.ts | 3 + .../frontend/.storybook/tsconfig.gen.json | 17 ------ packages/frontend/.storybook/tsconfig.json | 9 --- .../frontend/.storybook/tsconfig.vue.json | 19 ------ packages/frontend/eslint.config.js | 61 +++++++++---------- packages/frontend/tsconfig.json | 4 +- packages/frontend/tsconfig.storybook.json | 15 +++++ packages/frontend/tsconfig.vue.storybook.json | 31 ++++++++++ 18 files changed, 157 insertions(+), 110 deletions(-) create mode 100644 packages/frontend/.storybook/locale.d.ts delete mode 100644 packages/frontend/.storybook/package.json create mode 100644 packages/frontend/.storybook/themes.d.ts delete mode 100644 packages/frontend/.storybook/tsconfig.gen.json delete mode 100644 packages/frontend/.storybook/tsconfig.json delete mode 100644 packages/frontend/.storybook/tsconfig.vue.json create mode 100644 packages/frontend/tsconfig.storybook.json create mode 100644 packages/frontend/tsconfig.vue.storybook.json diff --git a/packages/frontend/.storybook/charts.ts b/packages/frontend/.storybook/charts.ts index 31bb9e51c5..acc75c69ab 100644 --- a/packages/frontend/.storybook/charts.ts +++ b/packages/frontend/.storybook/charts.ts @@ -3,10 +3,10 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { HttpResponse, http } from 'msw'; +import { HttpResponse } from 'msw'; import type { DefaultBodyType, HttpResponseResolver, JsonBodyType, PathParams } from 'msw'; import seedrandom from 'seedrandom'; -import { action } from '@storybook/addon-actions'; +import { action } from 'storybook/actions'; function getChartArray(seed: string, limit: number, option?: { accumulate?: boolean, mul?: number }): number[] { const rng = seedrandom(seed); @@ -30,7 +30,9 @@ export function getChartResolver(fields: string[], option?: { accumulate?: boole action(`GET ${request.url}`)(); const limitParam = new URL(request.url).searchParams.get('limit'); const limit = limitParam ? parseInt(limitParam) : 30; - const res = {}; + // What the *fuck* is the type of this object??? + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const res: any = {}; for (const field of fields) { const layers = field.split('.'); let current = res; diff --git a/packages/frontend/.storybook/fakes.ts b/packages/frontend/.storybook/fakes.ts index 3105f148d7..73d209c561 100644 --- a/packages/frontend/.storybook/fakes.ts +++ b/packages/frontend/.storybook/fakes.ts @@ -220,6 +220,11 @@ export function federationInstance(): entities.FederationInstance { themeColor: '', infoUpdatedAt: '', latestRequestReceivedAt: '', + isMediaSilenced: false, + rejectReports: false, + rejectQuotes: false, + isBubbled: false, + mandatoryCW: null, }; } @@ -240,6 +245,13 @@ export function note(id = 'somenoteid'): entities.Note { reactionCount: 0, renoteCount: 0, repliesCount: 0, + threadId: '', + userHost: null, + isMutingThread: false, + isMutingNote: false, + isFavorited: false, + isRenoted: false, + bypassSilence: false, }; } @@ -254,10 +266,27 @@ export function userLite(id = 'someuserid', username = 'miskist', host: entities avatarBlurhash: 'eQFRshof5NWBRi},juayfPju53WB?0ofs;s*a{ofjuay^SoMEJR%ay', avatarDecorations: [], emojis: {}, + createdAt: '', + updatedAt: null, + lastFetchedAt: null, + approved: false, + description: null, + isAdmin: false, + isModerator: false, + isSystem: false, + noindex: false, + enableRss: false, + mandatoryCW: null, + isSilenced: false, + bypassSilence: false, + followersCount: 0, + followingCount: 0, + notesCount: 0, + attributionDomains: [], }; } -export function userDetailed(id = 'someuserid', username = 'miskist', host: entities.UserDetailed['host'] = 'misskey-hub.net', name: entities.UserDetailed['name'] = 'Misskey User'): entities.UserDetailed { +function userDetailed(id = 'someuserid', username = 'miskist', host: entities.UserDetailed['host'] = 'misskey-hub.net', name: entities.UserDetailed['name'] = 'Misskey User'): entities.UserDetailed { return { ...userLite(id, username, host, name), bannerBlurhash: 'eQA^IW^-MH8w9tE8I=S^o{$*R4RikXtSxutRozjEnNR.RQadoyozog', @@ -312,9 +341,17 @@ export function userDetailed(id = 'someuserid', username = 'miskist', host: enti alsoKnownAs: null, notify: 'none', memo: null, + backgroundUrl: null, + backgroundId: null, + backgroundBlurhash: null, + listenbrainz: null, + canChat: true, + chatScope: 'none', }; } +export default userDetailed; + export function inviteCode(isUsed = false, hasExpiration = false, isExpired = false, isCreatedBySystem = false) { const date = new Date(); const createdAt = new Date(); @@ -382,9 +419,10 @@ export function role(params: { condFormula: { id: '', type: 'or', - values: [] + values: [], }, policies: {}, + preserveAssignmentOnMoveAccount: true, }; } diff --git a/packages/frontend/.storybook/generate.tsx b/packages/frontend/.storybook/generate.tsx index 00eed832cf..36b6bd1340 100644 --- a/packages/frontend/.storybook/generate.tsx +++ b/packages/frontend/.storybook/generate.tsx @@ -23,7 +23,7 @@ interface ImportDeclaration extends estree.ImportDeclaration { const generator = { ...GENERATOR, - ImportDeclaration(node: ImportDeclaration, state: State) { + ImportDeclaration(node: ImportDeclaration, state: State): void { state.write('import '); if (node.kind === 'type') state.write('type '); const { specifiers } = node; @@ -63,7 +63,7 @@ const generator = { state.write(';'); }, - SatisfiesExpression(node: SatisfiesExpression, state: State) { + SatisfiesExpression(node: SatisfiesExpression, state: State): void { switch (node.expression.type) { case 'ArrowFunctionExpression': { state.write('('); @@ -72,7 +72,7 @@ const generator = { break; } default: { - // @ts-expect-error ??? + // @ts-expect-error Produces "Expression produces a union type that is too complex to represent" for some reason this[node.expression.type](node.expression, state); break; } @@ -94,7 +94,6 @@ type SplitCamel< : SplitCamel : YN; -// @ts-expect-error ??? type SplitKebab = T extends `${infer XH}-${infer XR}` ? [XH, ...SplitKebab] : [T]; @@ -110,7 +109,6 @@ type ToKebab = T extends readonly [ ? `${XH}${XR extends readonly string[] ? `-${ToKebab}` : ''}` : ''; -// @ts-expect-error ??? type ToPascal = T extends readonly [ infer XH extends string, ...infer XR extends readonly string[] @@ -126,6 +124,7 @@ function h( return Object.assign(props || {}, { type }) as T; } +// eslint-disable-next-line @typescript-eslint/no-namespace declare namespace h.JSX { type Element = estree.Node; type IntrinsicElements = { diff --git a/packages/frontend/.storybook/locale.d.ts b/packages/frontend/.storybook/locale.d.ts new file mode 100644 index 0000000000..030b930342 --- /dev/null +++ b/packages/frontend/.storybook/locale.d.ts @@ -0,0 +1,3 @@ +import locales from '../../../locales/index.js'; + +export default locales['ja-JP'] as const; diff --git a/packages/frontend/.storybook/main.ts b/packages/frontend/.storybook/main.ts index c1119c2523..88920fde1d 100644 --- a/packages/frontend/.storybook/main.ts +++ b/packages/frontend/.storybook/main.ts @@ -7,7 +7,7 @@ import { createRequire } from 'node:module'; import { dirname, join, resolve } from 'node:path'; import { fileURLToPath } from 'node:url'; import type { StorybookConfig } from '@storybook/vue3-vite'; -import { type Plugin, mergeConfig } from 'vite'; +import { mergeConfig } from 'vite'; import turbosnap from 'vite-plugin-turbosnap'; const require = createRequire(import.meta.url); @@ -29,19 +29,20 @@ const config = { options: {}, }, docs: { + // @ts-expect-error This seems to be wrong, but I can't find what the alternative might be. autodocs: 'tag', }, core: { disableTelemetry: true, }, async viteFinal(config) { - const replacePluginForIsChromatic = config.plugins?.findIndex((plugin: Plugin) => plugin && plugin.name === 'replace') ?? -1; + const replacePluginForIsChromatic = config.plugins?.findIndex(plugin => plugin && 'name' in plugin && plugin.name === 'replace') ?? -1; if (~replacePluginForIsChromatic) { config.plugins?.splice(replacePluginForIsChromatic, 1); } //pluginsからcreateSearchIndexを削除、複数あるかもしれないので全て削除 - config.plugins = config.plugins?.filter((plugin: Plugin) => plugin && plugin.name !== 'createSearchIndex') ?? []; + config.plugins = config.plugins?.filter(plugin => plugin && 'name' in plugin && plugin.name !== 'createSearchIndex') ?? []; return mergeConfig(config, { plugins: [ diff --git a/packages/frontend/.storybook/mocks.ts b/packages/frontend/.storybook/mocks.ts index 870c364059..cc2e03cdb5 100644 --- a/packages/frontend/.storybook/mocks.ts +++ b/packages/frontend/.storybook/mocks.ts @@ -3,7 +3,8 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { type SharedOptions, http, HttpResponse } from 'msw'; +import { http, HttpResponse } from 'msw'; +import type { SharedOptions } from 'msw'; export const onUnhandledRequest = ((req, print) => { const url = new URL(req.url); diff --git a/packages/frontend/.storybook/package.json b/packages/frontend/.storybook/package.json deleted file mode 100644 index bedb411a91..0000000000 --- a/packages/frontend/.storybook/package.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "type": "module" -} diff --git a/packages/frontend/.storybook/preload-locale.ts b/packages/frontend/.storybook/preload-locale.ts index b9b092262d..6c9af0cafb 100644 --- a/packages/frontend/.storybook/preload-locale.ts +++ b/packages/frontend/.storybook/preload-locale.ts @@ -7,7 +7,7 @@ import { writeFile } from 'node:fs/promises'; import locales from '../../../locales/index.js'; await writeFile( - new URL('locale.ts', import.meta.url), - `export default ${JSON.stringify(locales['ja-JP'], undefined, 2)} as const;`, + new URL('locale.js', import.meta.url), + `export default ${JSON.stringify(locales['ja-JP'], undefined, 2)};`, 'utf8', ); diff --git a/packages/frontend/.storybook/preload-theme.ts b/packages/frontend/.storybook/preload-theme.ts index 1f9969bcb6..6d418495a9 100644 --- a/packages/frontend/.storybook/preload-theme.ts +++ b/packages/frontend/.storybook/preload-theme.ts @@ -34,12 +34,12 @@ const keys = [ await Promise.all(keys.map((key) => readFile(new URL(`../../frontend-shared/themes/${key}.json5`, import.meta.url), 'utf8'))).then((sources) => { writeFile( - new URL('./themes.ts', import.meta.url), + new URL('./themes.js', import.meta.url), `export default ${JSON.stringify( Object.fromEntries(sources.map((source, i) => [keys[i], JSON5.parse(source)])), undefined, 2, - )} as const;`, - 'utf8' + )};`, + 'utf8', ); }); diff --git a/packages/frontend/.storybook/preview.ts b/packages/frontend/.storybook/preview.ts index 593287dbdf..c9e60c238c 100644 --- a/packages/frontend/.storybook/preview.ts +++ b/packages/frontend/.storybook/preview.ts @@ -5,13 +5,15 @@ import { FORCE_RE_RENDER, FORCE_REMOUNT } from '@storybook/core-events'; import { addons } from '@storybook/preview-api'; -import { type Preview, setup } from '@storybook/vue3'; +import { setup } from '@storybook/vue3'; import isChromatic from 'chromatic/isChromatic'; import { initialize, mswLoader } from 'msw-storybook-addon'; -import { userDetailed } from './fakes.js'; +import userDetailed from './fakes.js'; import locale from './locale.js'; import { commonHandlers, onUnhandledRequest } from './mocks.js'; import themes from './themes.js'; +import type { Preview } from '@storybook/vue3'; +import type * as MisskeyOS from '../src/os.js'; import '../src/style.scss'; const appInitialized = Symbol(); @@ -19,13 +21,13 @@ const appInitialized = Symbol(); let lastStory: string | null = null; let moduleInitialized = false; let unobserve = () => {}; -let misskeyOS = null; +let misskeyOS: typeof MisskeyOS | null = null; function loadTheme(applyTheme: typeof import('../src/theme')['applyTheme']) { unobserve(); - const theme = themes[window.document.documentElement.dataset.misskeyTheme]; + const theme = themes[window.document.documentElement.dataset.misskeyTheme as string]; if (theme) { - applyTheme(themes[window.document.documentElement.dataset.misskeyTheme]); + applyTheme(themes[window.document.documentElement.dataset.misskeyTheme as string]); } else { applyTheme(themes['l-light']); } @@ -33,9 +35,9 @@ function loadTheme(applyTheme: typeof import('../src/theme')['applyTheme']) { for (const entry of entries) { if (entry.attributeName === 'data-misskey-theme') { const target = entry.target as HTMLElement; - const theme = themes[target.dataset.misskeyTheme]; + const theme = themes[target.dataset.misskeyTheme as string]; if (theme) { - applyTheme(themes[target.dataset.misskeyTheme]); + applyTheme(themes[target.dataset.misskeyTheme as string]); } else { target.removeAttribute('style'); } @@ -97,7 +99,7 @@ const preview = { } else { lastStory = context.id; const channel = addons.getChannel(); - const resetIndexedDBPromise = globalThis.indexedDB?.databases + const resetIndexedDBPromise = (globalThis.indexedDB as IDBFactory | undefined)?.databases ? indexedDB.databases().then((r) => { for (let i = 0; i < r.length; i++) { indexedDB.deleteDatabase(r[i].name!); @@ -105,7 +107,6 @@ const preview = { }).catch(() => {}) : Promise.resolve(); const resetDefaultStorePromise = import('../src/store').then(({ store }) => { - // @ts-expect-error ??? store.init(); }).catch(() => {}); Promise.all([resetIndexedDBPromise, resetDefaultStorePromise]).then(() => { @@ -122,12 +123,12 @@ const preview = { } return story; }, - (Story, context) => { + (_, context) => { return { setup() { return { context, - popups: misskeyOS.popups, + popups: misskeyOS?.popups, }; }, template: diff --git a/packages/frontend/.storybook/themes.d.ts b/packages/frontend/.storybook/themes.d.ts new file mode 100644 index 0000000000..2abf788c31 --- /dev/null +++ b/packages/frontend/.storybook/themes.d.ts @@ -0,0 +1,3 @@ +import type { Theme } from '../src/theme.js'; + +export default {} as Record; diff --git a/packages/frontend/.storybook/tsconfig.gen.json b/packages/frontend/.storybook/tsconfig.gen.json deleted file mode 100644 index c85994ecdc..0000000000 --- a/packages/frontend/.storybook/tsconfig.gen.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/tsconfig", - "extends": "../tsconfig.vue.json", - "compilerOptions": { - "module": "NodeNext", - "moduleResolution": "NodeNext", - "forceConsistentCasingInFileNames": true, - "jsx": "react", - "jsxFactory": "h" - }, - "files": [ - "./changes.ts", - "./generate.tsx", - "./preload-locale.ts", - "./preload-theme.ts" - ] -} diff --git a/packages/frontend/.storybook/tsconfig.json b/packages/frontend/.storybook/tsconfig.json deleted file mode 100644 index 0a79681e84..0000000000 --- a/packages/frontend/.storybook/tsconfig.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/tsconfig", - "files": [], - // WebStorm only reads one tsconfig per directory, so this tricks it into loading both. - "references": [ - { "path": "./tsconfig.gen.json" }, - { "path": "./tsconfig.vue.json" } - ] -} diff --git a/packages/frontend/.storybook/tsconfig.vue.json b/packages/frontend/.storybook/tsconfig.vue.json deleted file mode 100644 index e6e6df85b0..0000000000 --- a/packages/frontend/.storybook/tsconfig.vue.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "$schema": "https://json.schemastore.org/tsconfig", - "extends": "../tsconfig.vue.json", - "compilerOptions": { - "jsx": "preserve" - }, - "include": [ - "**/*.js", - "**/*.jsx", - "**/*.ts", - "**/*.tsx" - ], - "exclude": [ - "./changes.ts", - "./generate.tsx", - "./preload-locale.ts", - "./preload-theme.ts" - ] -} diff --git a/packages/frontend/eslint.config.js b/packages/frontend/eslint.config.js index fab16f8061..3c232931e8 100644 --- a/packages/frontend/eslint.config.js +++ b/packages/frontend/eslint.config.js @@ -15,9 +15,14 @@ export default [ }, ...pluginVue.configs['flat/recommended'], { - files: ['{src,test,js,@types}/**/*.{ts,vue}'], + files: ['{src,test,js,@types}/**/*.{ts,vue}', '.storybook/**/*.ts', '.storybook/**/*.tsx', '.storybook/**/*.js', '.storybook/**/*.jsx'], ignores: [ '*.*', + '.storybook/changes.ts', + '.storybook/main.ts', + '.storybook/generate.tsx', + '.storybook/preload-locale.ts', + '.storybook/preload-theme.ts', ], plugins: { sharkey: { rules: { locale: localeRule } } }, languageOptions: { @@ -46,7 +51,7 @@ export default [ parserOptions: { extraFileExtensions: ['.vue'], parser: tsParser, - project: ['./tsconfig.vue.json'], + project: ['tsconfig.vue.json'], sourceType: 'module', tsconfigRootDir: import.meta.dirname, }, @@ -173,37 +178,31 @@ export default [ 'no-restricted-globals': 'off', }, }, + { + files: ['.storybook/**/*.ts', '.storybook/**/*.tsx', '.storybook/**/*.js', '.storybook/**/*.jsx'], + rules: { + 'import/no-default-export': 'off', + 'no-restricted-globals': 'off', + }, + }, { files: [ - ".storybook/changes.ts", - ".storybook/generate.tsx", - ".storybook/preload-locale.ts", - ".storybook/preload-theme.ts" + '.storybook/changes.ts', + '.storybook/main.ts', + '.storybook/generate.tsx', + '.storybook/preload-locale.ts', + '.storybook/preload-theme.ts', ], languageOptions: { parserOptions: { parser: tsParser, - project: ['.storybook/tsconfig.gen.json'], + project: ['tsconfig.storybook.json'], sourceType: 'module', tsconfigRootDir: import.meta.dirname, }, }, - }, - { - files: ['.storybook/**/*.ts', '.storybook/**/*.tsx', '.storybook/**/*.js', '.storybook/**/*.jsx'], - ignores: [ - ".storybook/changes.ts", - ".storybook/generate.tsx", - ".storybook/preload-locale.ts", - ".storybook/preload-theme.ts" - ], - languageOptions: { - parserOptions: { - parser: tsParser, - project: ['.storybook/tsconfig.vue.json'], - sourceType: 'module', - tsconfigRootDir: import.meta.dirname, - }, + rules: { + 'import/no-default-export': 'off', }, }, { @@ -233,15 +232,15 @@ export default [ }, { ignores: [ - '**/lib/', - '**/temp/', - '**/built/', - '**/coverage/', - '**/node_modules/', - '**/libopenmpt/', - '**/storybook-static/', + '**/lib', + '**/temp', + '**/built', + '**/coverage', + '**/node_modules', + '**/libopenmpt', + '**/storybook-static', 'vue-shims.d.ts', - 'assets/' + 'assets', ], }, ]; diff --git a/packages/frontend/tsconfig.json b/packages/frontend/tsconfig.json index 77e8ba1be6..a46e46b585 100644 --- a/packages/frontend/tsconfig.json +++ b/packages/frontend/tsconfig.json @@ -4,6 +4,8 @@ // WebStorm only reads one tsconfig per directory, so this tricks it into loading both. "references": [ { "path": "./tsconfig.scripts.json" }, - { "path": "./tsconfig.vue.json" } + { "path": "./tsconfig.vue.json" }, + { "path": "./tsconfig.vue.storybook.json" }, + { "path": "./tsconfig.storybook.json" } ] } diff --git a/packages/frontend/tsconfig.storybook.json b/packages/frontend/tsconfig.storybook.json new file mode 100644 index 0000000000..a618ded035 --- /dev/null +++ b/packages/frontend/tsconfig.storybook.json @@ -0,0 +1,15 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "../shared/tsconfig.node.jsonc", + "compilerOptions": { + "jsx": "react", + "jsxFactory": "h" + }, + "files": [ + ".storybook/changes.ts", + ".storybook/main.ts", + ".storybook/generate.tsx", + ".storybook/preload-locale.ts", + ".storybook/preload-theme.ts" + ] +} diff --git a/packages/frontend/tsconfig.vue.storybook.json b/packages/frontend/tsconfig.vue.storybook.json new file mode 100644 index 0000000000..99e559c37f --- /dev/null +++ b/packages/frontend/tsconfig.vue.storybook.json @@ -0,0 +1,31 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "./tsconfig.vue.json", + "compilerOptions": { + "esModuleInterop": true, + "allowSyntheticDefaultImports": true + }, + "include": [ + "./lib/**/*.ts", + "./src/**/*.ts", + "./src/**/*.vue", + "./test/**/*.ts", + "./test/**/*.vue", + "./@types/**/*.ts", + "./vue-shims.d.ts", + ".storybook/**/*.js", + ".storybook/**/*.jsx", + ".storybook/**/*.ts", + ".storybook/**/*.tsx" + ], + "exclude": [ + "node_modules", + "eslint.config.js", + "vite.*", + ".storybook/changes.ts", + ".storybook/main.ts", + ".storybook/generate.tsx", + ".storybook/preload-locale.ts", + ".storybook/preload-theme.ts" + ] +}