mirror of
https://github.com/misskey-dev/misskey.git
synced 2026-06-17 18:44:47 +02:00
Compare commits
8 Commits
l10n_devel
...
2026.6.0-a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f46450d857 | ||
|
|
b125ce1eb2 | ||
|
|
dc96c35296 | ||
|
|
8468a25488 | ||
|
|
6c7375924c | ||
|
|
ec6b1cc6a8 | ||
|
|
e093b32aa9 | ||
|
|
0b4764c68b |
@@ -3,8 +3,8 @@
|
||||
### General
|
||||
- Feat: ジョブキュー管理画面からキューの一時停止/再開ができるように
|
||||
- Feat: アンテナのタイムラインから個別のノートを削除できるように
|
||||
- Fix: コンパネからrootユーザーのパスワードをリセットしようとした際にエラーが通知されない問題を修正
|
||||
- Feat: ノート検索で投稿日時の期間を条件に加えられるように(#16035)
|
||||
- Fix: コンパネからrootユーザーのパスワードをリセットしようとした際にエラーが通知されない問題を修正
|
||||
|
||||
### Client
|
||||
- Enhance: ユーザーページのファイルタブでスクロール位置が保持されるように
|
||||
@@ -21,10 +21,14 @@
|
||||
|
||||
### Server
|
||||
- Enhance: リモートノートクリーニングジョブのスキップ処理のパフォーマンス改善
|
||||
- Fix: PerUserDriveChart がシステム所有ファイル (userId が null) の更新で `"group"` の非NULL制約違反によりクラッシュする問題を修正 (#17498)
|
||||
- Enhance: リモートノートクリーニングジョブの削除対象検索処理のパフォーマンス改善
|
||||
- Enhance: ActivityPub の画像添付に width/height を含めるように
|
||||
- Fix: backend バンドルで `@tensorflow/tfjs-node` を external に含めず、起動時に `@mapbox/node-pre-gyp` の `find()` が backend の package.json を誤検出して `is not node-pre-gyp ready` エラーを永続的に吐く問題を修正
|
||||
- Fix: MemoryKVCacheのキャッシュGC処理において、更新されたキャッシュが期限切れにならないことがある問題を修正
|
||||
- Fix: PerUserDriveChart がシステム所有ファイル (userId が null) の更新で `"group"` の非NULL制約違反によりクラッシュする問題を修正 (#17498)
|
||||
- Fix: センシティブメディア自動検出周りの依存関係・ファイルの解決に失敗する問題を修正
|
||||
- Fix: フォロワー限定投稿を指名投稿で引用した際に、引用した投稿の公開範囲が意図せず変更される問題を修正
|
||||
- Fix: Startup and shutdown failures (port-in-use, socket permission denied, plugin timeouts, leaked WebSocket connections) are now reported through the misskey logger instead of an UnhandledPromiseRejectionWarning stack trace
|
||||
|
||||
## 2026.5.4
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "misskey",
|
||||
"version": "2026.6.0-alpha.0",
|
||||
"version": "2026.6.0-alpha.2",
|
||||
"codename": "nasubi",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -54,7 +54,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"cssnano": "8.0.1",
|
||||
"esbuild": "0.28.0",
|
||||
"esbuild": "0.28.1",
|
||||
"execa": "9.6.1",
|
||||
"ignore-walk": "8.0.0",
|
||||
"js-yaml": "4.1.1",
|
||||
|
||||
@@ -172,6 +172,8 @@ export class ApRendererService {
|
||||
mediaType: file.webpublicType ?? file.type,
|
||||
url: this.driveFileEntityService.getPublicUrl(file),
|
||||
name: file.comment,
|
||||
width: file.properties?.width,
|
||||
height: file.properties?.height,
|
||||
sensitive: file.isSensitive,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -34,6 +34,8 @@ export interface IObject {
|
||||
href?: string;
|
||||
tag?: IObject | IObject[];
|
||||
sensitive?: boolean;
|
||||
width?: number;
|
||||
height?: number;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -299,12 +299,8 @@ export class MemoryKVCache<T> {
|
||||
const now = Date.now();
|
||||
|
||||
for (const [key, { date }] of this.cache.entries()) {
|
||||
// The map is ordered from oldest to youngest.
|
||||
// We can stop once we find an entry that's still active, because all following entries must *also* be active.
|
||||
const age = now - date;
|
||||
if (age < this.lifetime) break;
|
||||
|
||||
this.cache.delete(key);
|
||||
if (age >= this.lifetime) this.cache.delete(key);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -242,16 +242,16 @@ export class ServerService implements OnApplicationShutdown {
|
||||
|
||||
this.streamingApiServerService.attach(fastify.server);
|
||||
|
||||
fastify.server.on('error', err => {
|
||||
switch ((err as any).code) {
|
||||
const handleListenError = (err: unknown): void => {
|
||||
switch ((err as NodeJS.ErrnoException).code) {
|
||||
case 'EACCES':
|
||||
this.logger.error(`You do not have permission to listen on port ${this.config.port}.`);
|
||||
this.logger.error(`You do not have permission to listen on ${this.config.socket ?? `port ${this.config.port}`}.`);
|
||||
break;
|
||||
case 'EADDRINUSE':
|
||||
this.logger.error(`Port ${this.config.port} is already in use by another process.`);
|
||||
this.logger.error(`${this.config.socket ?? `Port ${this.config.port}`} is already in use by another process.`);
|
||||
break;
|
||||
default:
|
||||
this.logger.error(err);
|
||||
this.logger.error(err as Error);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -261,28 +261,39 @@ export class ServerService implements OnApplicationShutdown {
|
||||
// disableClustering
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
if (this.config.socket) {
|
||||
if (fs.existsSync(this.config.socket)) {
|
||||
fs.unlinkSync(this.config.socket);
|
||||
}
|
||||
fastify.listen({ path: this.config.socket }, (err, address) => {
|
||||
if (this.config.chmodSocket) {
|
||||
fs.chmodSync(this.config.socket!, this.config.chmodSocket);
|
||||
try {
|
||||
if (this.config.socket) {
|
||||
if (fs.existsSync(this.config.socket)) {
|
||||
fs.unlinkSync(this.config.socket);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
fastify.listen({ port: this.config.port, host: '0.0.0.0' });
|
||||
await fastify.listen({ path: this.config.socket });
|
||||
if (this.config.chmodSocket) {
|
||||
fs.chmodSync(this.config.socket, this.config.chmodSocket);
|
||||
}
|
||||
} else {
|
||||
await fastify.listen({ port: this.config.port, host: '0.0.0.0' });
|
||||
}
|
||||
await fastify.ready();
|
||||
} catch (err) {
|
||||
handleListenError(err);
|
||||
return;
|
||||
}
|
||||
|
||||
await fastify.ready();
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async dispose(): Promise<void> {
|
||||
await this.streamingApiServerService.detach();
|
||||
await this.#fastify.close();
|
||||
// fastify@5 close() waits for upgraded WebSocket connections to drain.
|
||||
// streamingApiServerService.attach() adds raw ws.Server upgrades that
|
||||
// fastify does not track in its connection registry, so close() can hang
|
||||
// forever during OnApplicationShutdown. Cap at 5s so PM2/systemd/k8s
|
||||
// shutdown timeouts aren't held hostage.
|
||||
await Promise.race([
|
||||
this.#fastify.close(),
|
||||
new Promise<void>(resolve => setTimeout(resolve, 5_000)),
|
||||
]).catch(err => this.logger.error('fastify.close() failed', err as Error));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -15,6 +15,7 @@ import { Test } from '@nestjs/testing';
|
||||
import { MockResolver } from '../misc/mock-resolver.js';
|
||||
import type { IActor, IApDocument, ICollection, IObject, IPost } from '@/core/activitypub/type.js';
|
||||
import type { MiRemoteUser } from '@/models/User.js';
|
||||
import type { MiDriveFile } from '@/models/DriveFile.js';
|
||||
import { ApImageService } from '@/core/activitypub/models/ApImageService.js';
|
||||
import { ApNoteService } from '@/core/activitypub/models/ApNoteService.js';
|
||||
import { ApPersonService } from '@/core/activitypub/models/ApPersonService.js';
|
||||
@@ -399,6 +400,28 @@ describe('ActivityPub', () => {
|
||||
});
|
||||
|
||||
describe('Images', () => {
|
||||
test('Render image document with dimensions', () => {
|
||||
const rendered = rendererService.renderDocument({
|
||||
id: genAidx(Date.now()),
|
||||
type: 'image/png',
|
||||
webpublicType: null,
|
||||
url: 'https://example.test/files/image.png',
|
||||
webpublicUrl: null,
|
||||
comment: null,
|
||||
isSensitive: false,
|
||||
properties: { width: 3600, height: 1890 },
|
||||
uri: null,
|
||||
userHost: null,
|
||||
isLink: false,
|
||||
webpublicAccessKey: null,
|
||||
} as MiDriveFile);
|
||||
|
||||
assert.strictEqual(rendered.type, 'Document');
|
||||
assert.strictEqual(rendered.mediaType, 'image/png');
|
||||
assert.strictEqual(rendered.width, 3600);
|
||||
assert.strictEqual(rendered.height, 1890);
|
||||
});
|
||||
|
||||
test('Create images', async () => {
|
||||
const imageObject: IApDocument = {
|
||||
type: 'Document',
|
||||
|
||||
234
packages/backend/test/unit/misc/cache.ts
Normal file
234
packages/backend/test/unit/misc/cache.ts
Normal file
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
|
||||
import { MemoryKVCache, MemorySingleCache } from '@/misc/cache.js';
|
||||
|
||||
describe('misc:MemoryKVCache', () => {
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
test('set and get returns the value within lifetime', () => {
|
||||
const cache = new MemoryKVCache<string>(1000);
|
||||
cache.set('key', 'value');
|
||||
expect(cache.get('key')).toBe('value');
|
||||
cache.dispose();
|
||||
});
|
||||
|
||||
test('get returns undefined after lifetime expires', () => {
|
||||
const cache = new MemoryKVCache<string>(1000);
|
||||
cache.set('key', 'value');
|
||||
vi.advanceTimersByTime(1001);
|
||||
expect(cache.get('key')).toBeUndefined();
|
||||
cache.dispose();
|
||||
});
|
||||
|
||||
test('delete removes the entry', () => {
|
||||
const cache = new MemoryKVCache<string>(1000);
|
||||
cache.set('key', 'value');
|
||||
cache.delete('key');
|
||||
expect(cache.get('key')).toBeUndefined();
|
||||
cache.dispose();
|
||||
});
|
||||
|
||||
describe('gc()', () => {
|
||||
test('removes expired entries', () => {
|
||||
const cache = new MemoryKVCache<string>(1000);
|
||||
cache.set('a', '1');
|
||||
cache.set('b', '2');
|
||||
vi.advanceTimersByTime(1001);
|
||||
cache.gc();
|
||||
expect(cache.get('a')).toBeUndefined();
|
||||
expect(cache.get('b')).toBeUndefined();
|
||||
cache.dispose();
|
||||
});
|
||||
|
||||
test('retains entries that have not yet expired', () => {
|
||||
const cache = new MemoryKVCache<string>(2000);
|
||||
cache.set('a', '1');
|
||||
vi.advanceTimersByTime(1001);
|
||||
cache.gc();
|
||||
expect(cache.get('a')).toBe('1');
|
||||
cache.dispose();
|
||||
});
|
||||
|
||||
test('removes only expired entries when mixed with live entries', () => {
|
||||
const cache = new MemoryKVCache<string>(2000);
|
||||
cache.set('old', 'oldValue');
|
||||
vi.advanceTimersByTime(2001);
|
||||
cache.set('new', 'newValue');
|
||||
cache.gc();
|
||||
expect(cache.get('old')).toBeUndefined();
|
||||
expect(cache.get('new')).toBe('newValue');
|
||||
cache.dispose();
|
||||
});
|
||||
|
||||
// Regression test for https://github.com/misskey-dev/misskey/issues/15500
|
||||
// Updated keys keep their original insertion position in Map. gc() must not
|
||||
// assume that entries are ordered from oldest to youngest, otherwise it can
|
||||
// stop early at an updated key and leave later, truly-expired keys alive.
|
||||
// The key observable symptom is that gc() fails to *remove* the expired entry
|
||||
// from the Map — get() has its own expiry check so it returns undefined either
|
||||
// way, but the stale entry keeps consuming memory.
|
||||
test('correctly expires old entries after a key is updated (issue #15500)', () => {
|
||||
const lifetime = 1000;
|
||||
const cache = new MemoryKVCache<string>(lifetime);
|
||||
|
||||
// Insert 'a' and 'b' at t=0
|
||||
cache.set('a', 'v1');
|
||||
cache.set('b', 'v1');
|
||||
|
||||
// Advance time and update 'a'. It stays at position 0 in the Map, so a
|
||||
// gc() implementation that stops at the first fresh entry would leave 'b'
|
||||
// in the Map even though get() would hide it as expired.
|
||||
vi.advanceTimersByTime(500);
|
||||
cache.set('a', 'v2'); // refresh 'a'; 'b' is still at t=0
|
||||
|
||||
// 'b' is now expired, 'a' has 400ms left
|
||||
vi.advanceTimersByTime(600); // total 1100ms
|
||||
|
||||
cache.gc();
|
||||
|
||||
// Verify the entry is actually removed from the Map, not just hidden by get().
|
||||
// get() always checks expiry itself, so it returns undefined even without gc() —
|
||||
// the real bug is memory not being freed.
|
||||
const entries = [...cache.entries];
|
||||
expect(entries.find(([k]) => k === 'b')).toBeUndefined(); // 'b' must be gone from Map
|
||||
expect(entries.find(([k]) => k === 'a')?.[1].value).toBe('v2'); // 'a' still in Map
|
||||
cache.dispose();
|
||||
});
|
||||
|
||||
test('gc does not break when cache is empty', () => {
|
||||
const cache = new MemoryKVCache<string>(1000);
|
||||
expect(() => cache.gc()).not.toThrow();
|
||||
cache.dispose();
|
||||
});
|
||||
});
|
||||
|
||||
test('set does not cause active entries iteration to revisit the same key', () => {
|
||||
const cache = new MemoryKVCache<{ id: string }>(1000);
|
||||
cache.set('key', { id: 'user-1' });
|
||||
|
||||
let iterations = 0;
|
||||
for (const [key, { value }] of cache.entries) {
|
||||
iterations++;
|
||||
if (value.id === 'user-1') {
|
||||
cache.set(key, value);
|
||||
}
|
||||
|
||||
expect(iterations).toBeLessThan(3);
|
||||
}
|
||||
|
||||
expect(iterations).toBe(1);
|
||||
cache.dispose();
|
||||
});
|
||||
|
||||
describe('fetch()', () => {
|
||||
test('calls fetcher on cache miss', async () => {
|
||||
const cache = new MemoryKVCache<string>(1000);
|
||||
const fetcher = vi.fn().mockResolvedValue('fetched');
|
||||
const result = await cache.fetch('key', fetcher);
|
||||
expect(fetcher).toHaveBeenCalledOnce();
|
||||
expect(result).toBe('fetched');
|
||||
cache.dispose();
|
||||
});
|
||||
|
||||
test('does not call fetcher on cache hit', async () => {
|
||||
const cache = new MemoryKVCache<string>(1000);
|
||||
cache.set('key', 'cached');
|
||||
const fetcher = vi.fn().mockResolvedValue('fetched');
|
||||
const result = await cache.fetch('key', fetcher);
|
||||
expect(fetcher).not.toHaveBeenCalled();
|
||||
expect(result).toBe('cached');
|
||||
cache.dispose();
|
||||
});
|
||||
|
||||
test('respects validator and bypasses cache when validator returns false', async () => {
|
||||
const cache = new MemoryKVCache<string>(1000);
|
||||
cache.set('key', 'cached');
|
||||
const fetcher = vi.fn().mockResolvedValue('fetched');
|
||||
const result = await cache.fetch('key', fetcher, () => false);
|
||||
expect(fetcher).toHaveBeenCalledOnce();
|
||||
expect(result).toBe('fetched');
|
||||
cache.dispose();
|
||||
});
|
||||
});
|
||||
|
||||
describe('fetchMaybe()', () => {
|
||||
test('does not cache undefined returned by fetcher', async () => {
|
||||
const cache = new MemoryKVCache<string>(1000);
|
||||
const fetcher = vi.fn().mockResolvedValue(undefined);
|
||||
const result = await cache.fetchMaybe('key', fetcher);
|
||||
expect(result).toBeUndefined();
|
||||
// A second call should invoke the fetcher again because undefined was not cached
|
||||
await cache.fetchMaybe('key', fetcher);
|
||||
expect(fetcher).toHaveBeenCalledTimes(2);
|
||||
cache.dispose();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('misc:MemorySingleCache', () => {
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
test('set and get returns the value within lifetime', () => {
|
||||
const cache = new MemorySingleCache<string>(1000);
|
||||
cache.set('value');
|
||||
expect(cache.get()).toBe('value');
|
||||
});
|
||||
|
||||
test('get returns undefined after lifetime expires', () => {
|
||||
const cache = new MemorySingleCache<string>(1000);
|
||||
cache.set('value');
|
||||
vi.advanceTimersByTime(1001);
|
||||
expect(cache.get()).toBeUndefined();
|
||||
});
|
||||
|
||||
test('delete removes the cached value', () => {
|
||||
const cache = new MemorySingleCache<string>(1000);
|
||||
cache.set('value');
|
||||
cache.delete();
|
||||
expect(cache.get()).toBeUndefined();
|
||||
});
|
||||
|
||||
describe('fetch()', () => {
|
||||
test('calls fetcher on cache miss', async () => {
|
||||
const cache = new MemorySingleCache<string>(1000);
|
||||
const fetcher = vi.fn().mockResolvedValue('fetched');
|
||||
const result = await cache.fetch(fetcher);
|
||||
expect(fetcher).toHaveBeenCalledOnce();
|
||||
expect(result).toBe('fetched');
|
||||
});
|
||||
|
||||
test('does not call fetcher on cache hit', async () => {
|
||||
const cache = new MemorySingleCache<string>(1000);
|
||||
cache.set('cached');
|
||||
const fetcher = vi.fn().mockResolvedValue('fetched');
|
||||
const result = await cache.fetch(fetcher);
|
||||
expect(fetcher).not.toHaveBeenCalled();
|
||||
expect(result).toBe('cached');
|
||||
});
|
||||
|
||||
test('respects validator and bypasses cache when validator returns false', async () => {
|
||||
const cache = new MemorySingleCache<string>(1000);
|
||||
cache.set('cached');
|
||||
const fetcher = vi.fn().mockResolvedValue('fetched');
|
||||
const result = await cache.fetch(fetcher, () => false);
|
||||
expect(fetcher).toHaveBeenCalledOnce();
|
||||
expect(result).toBe('fetched');
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -4,23 +4,12 @@
|
||||
*/
|
||||
|
||||
import { parseAst } from 'rolldown/parseAst';
|
||||
import * as estreeWalker from 'estree-walker';
|
||||
import { walk } from 'oxc-walker';
|
||||
import { assertNever } from '../utils.js';
|
||||
import type { ESTree } from 'rolldown/utils';
|
||||
import type { LocaleInliner, TextModification } from '../locale-inliner.js';
|
||||
import type { Logger } from '../logger.js';
|
||||
|
||||
// WalkerContext is not exported from estree-walker, so we define it here
|
||||
interface WalkerContext {
|
||||
skip: () => void;
|
||||
}
|
||||
|
||||
const walk = estreeWalker.walk as {
|
||||
(node: ESTree.Node, callback: {
|
||||
enter?: (this: WalkerContext, node: ESTree.Node, parent: ESTree.Node | null, property: string | number | symbol | null | undefined) => void;
|
||||
}): void;
|
||||
};
|
||||
|
||||
export function collectModifications(sourceCode: string, fileName: string, fileLogger: Logger, inliner: LocaleInliner): TextModification[] {
|
||||
if (sourceCode === '') return [];
|
||||
let programNode: ESTree.Program;
|
||||
@@ -42,7 +31,7 @@ export function collectModifications(sourceCode: string, fileName: string, fileL
|
||||
// 2) replace all `localStorage.getItem("lang")` with `localeName` variable
|
||||
// 3) replace all `await window.fetch(`/assets/locales/${d}.${x}.json`).then(u=>u.json())` with `localeJson` variable
|
||||
walk(programNode, {
|
||||
enter(this: WalkerContext, node: ESTree.Node) {
|
||||
enter(this, node) {
|
||||
if (node.type === 'Literal' && typeof node.value === 'string' && node.raw) {
|
||||
if (node.raw.substring(1).startsWith(inliner.scriptsDir)) {
|
||||
// we find `scripts/\w+\.js` literal and replace 'scripts' part with locale code
|
||||
@@ -130,13 +119,15 @@ export function collectModifications(sourceCode: string, fileName: string, fileL
|
||||
const toSkip = new Set();
|
||||
toSkip.add(i18nImport);
|
||||
walk(programNode, {
|
||||
enter(this: WalkerContext, node, parent, property) {
|
||||
enter(this, node, parent, ctx) {
|
||||
if (toSkip.has(node)) {
|
||||
// This is the import specifier, skip processing it
|
||||
this.skip();
|
||||
return;
|
||||
}
|
||||
|
||||
const property = ctx.key;
|
||||
|
||||
// We don't care original name part of the import declaration
|
||||
if (node.type === 'ImportDeclaration') this.skip();
|
||||
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
"rollup": "4.60.4"
|
||||
},
|
||||
"dependencies": {
|
||||
"estree-walker": "3.0.3",
|
||||
"i18n": "workspace:*",
|
||||
"magic-string": "0.30.21",
|
||||
"oxc-walker": "1.0.0",
|
||||
"rolldown": "1.0.3",
|
||||
"vite": "8.0.14"
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import * as estreeWalker from 'estree-walker';
|
||||
import { walk } from 'oxc-walker';
|
||||
import { RolldownMagicString } from 'rolldown';
|
||||
import { assertType } from './utils.js';
|
||||
import type { ESTree } from 'rolldown/utils';
|
||||
@@ -27,8 +27,7 @@ export function pluginRemoveUnrefI18n(
|
||||
if (!code.includes('unref(i18n)')) return null;
|
||||
const ast = this.parse(code);
|
||||
const magicString = meta.magicString ?? new RolldownMagicString(code);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(estreeWalker.walk as any)(ast, {
|
||||
walk(ast, {
|
||||
enter(node: ESTree.Node) {
|
||||
if (node.type === 'CallExpression' && node.callee.type === 'Identifier' && node.callee.name === 'unref'
|
||||
&& node.arguments.length === 1) {
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
"@rollup/pluginutils": "5.4.0",
|
||||
"@vitejs/plugin-vue": "6.0.7",
|
||||
"buraha": "0.0.1",
|
||||
"estree-walker": "3.0.3",
|
||||
"frontend-shared": "workspace:*",
|
||||
"i18n": "workspace:*",
|
||||
"icons-subsetter": "workspace:*",
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import * as estreeWalker from 'estree-walker';
|
||||
import { walk } from 'oxc-walker';
|
||||
import type { Plugin } from 'vite';
|
||||
import type { ESTree } from 'rolldown/utils';
|
||||
import { RolldownMagicString } from 'rolldown';
|
||||
@@ -185,7 +185,7 @@ function isClassProperty(node: ESTree.Node | null): node is Extract<ESTree.Node,
|
||||
}
|
||||
|
||||
export function unwindCssModuleClassName(ast: ESTree.Node, magicString: RolldownMagicString): void {
|
||||
(estreeWalker.walk as any)(ast, {
|
||||
walk(ast, {
|
||||
enter(node: ESTree.Node, parent: ESTree.Node | null): void {
|
||||
//#region
|
||||
if (parent?.type !== 'Program') return;
|
||||
@@ -267,7 +267,7 @@ export function unwindCssModuleClassName(ast: ESTree.Node, magicString: Rolldown
|
||||
*/
|
||||
//#endregion
|
||||
//#region
|
||||
(estreeWalker.walk as any)(render.body, {
|
||||
walk(render.body, {
|
||||
enter(childNode: ESTree.Node) {
|
||||
if (!isCssModuleReference(childNode, ctx.name, key)) return;
|
||||
const actualKey = getMemberPropertyName(childNode.property, childNode.computed);
|
||||
@@ -278,6 +278,9 @@ export function unwindCssModuleClassName(ast: ESTree.Node, magicString: Rolldown
|
||||
this.replace({
|
||||
type: 'Literal',
|
||||
value: actualValue,
|
||||
raw: JSON.stringify(actualValue),
|
||||
start: childNode.start,
|
||||
end: childNode.end,
|
||||
});
|
||||
},
|
||||
});
|
||||
@@ -314,7 +317,7 @@ export function unwindCssModuleClassName(ast: ESTree.Node, magicString: Rolldown
|
||||
*/
|
||||
//#endregion
|
||||
//#region
|
||||
(estreeWalker.walk as any)(render.body, {
|
||||
walk(render.body, {
|
||||
enter(childNode: ESTree.Node) {
|
||||
if (!isCssModuleReference(childNode, ctx.name, key)) return;
|
||||
const actualKey = getMemberPropertyName(childNode.property, childNode.computed);
|
||||
@@ -357,7 +360,7 @@ export function unwindCssModuleClassName(ast: ESTree.Node, magicString: Rolldown
|
||||
*/
|
||||
//#endregion
|
||||
//#region
|
||||
(estreeWalker.walk as any)(render.body, {
|
||||
walk(render.body, {
|
||||
enter(childNode: ESTree.Node, childParent: ESTree.Node | null) {
|
||||
if (childNode.type !== 'CallExpression') return;
|
||||
if (childNode.arguments.length !== 1) return;
|
||||
@@ -404,7 +407,7 @@ export function unwindCssModuleClassName(ast: ESTree.Node, magicString: Rolldown
|
||||
}
|
||||
const hasRemainingCssModuleReference = Array.from(moduleForest.keys()).some((key) => {
|
||||
let found = false;
|
||||
(estreeWalker.walk as any)(render.body, {
|
||||
walk(render.body, {
|
||||
enter(childNode: ESTree.Node) {
|
||||
if (!isCssModuleAccess(childNode, ctx.name, key)) return;
|
||||
found = true;
|
||||
@@ -417,7 +420,7 @@ export function unwindCssModuleClassName(ast: ESTree.Node, magicString: Rolldown
|
||||
//#region
|
||||
if (node.declarations[0].init.arguments[1].elements.length === 1) {
|
||||
if (componentNode.type === 'Identifier') {
|
||||
(estreeWalker.walk as any)(ast, {
|
||||
walk(ast, {
|
||||
enter(childNode: ESTree.Node) {
|
||||
if (childNode.type !== 'Identifier') return;
|
||||
if (childNode.name !== componentNode.name) return;
|
||||
|
||||
@@ -116,7 +116,6 @@
|
||||
"cypress": "15.16.0",
|
||||
"eslint-plugin-import": "2.32.0",
|
||||
"eslint-plugin-vue": "10.9.1",
|
||||
"estree-walker": "3.0.3",
|
||||
"happy-dom": "20.9.0",
|
||||
"intersection-observer": "0.12.2",
|
||||
"lightningcss": "1.32.0",
|
||||
@@ -125,6 +124,7 @@
|
||||
"msw": "2.14.6",
|
||||
"msw-storybook-addon": "2.0.7",
|
||||
"nodemon": "3.1.14",
|
||||
"oxc-walker": "1.0.0",
|
||||
"prettier": "3.8.3",
|
||||
"react": "19.2.6",
|
||||
"react-dom": "19.2.6",
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
"@typescript-eslint/eslint-plugin": "8.60.0",
|
||||
"@typescript-eslint/parser": "8.60.0",
|
||||
"chokidar": "5.0.0",
|
||||
"esbuild": "0.28.0",
|
||||
"esbuild": "0.28.1",
|
||||
"execa": "9.6.1",
|
||||
"nodemon": "3.1.14",
|
||||
"tsx": "4.22.3"
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
"@types/seedrandom": "3.0.8",
|
||||
"@typescript-eslint/eslint-plugin": "8.60.0",
|
||||
"@typescript-eslint/parser": "8.60.0",
|
||||
"esbuild": "0.28.0",
|
||||
"esbuild": "0.28.1",
|
||||
"execa": "9.6.1",
|
||||
"nodemon": "3.1.14"
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"type": "module",
|
||||
"name": "misskey-js",
|
||||
"version": "2026.6.0-alpha.0",
|
||||
"version": "2026.6.0-alpha.2",
|
||||
"description": "Misskey SDK for JavaScript",
|
||||
"license": "MIT",
|
||||
"main": "./built/index.js",
|
||||
@@ -42,7 +42,7 @@
|
||||
"@typescript-eslint/eslint-plugin": "8.60.0",
|
||||
"@typescript-eslint/parser": "8.60.0",
|
||||
"@vitest/coverage-v8": "4.1.7",
|
||||
"esbuild": "0.28.0",
|
||||
"esbuild": "0.28.1",
|
||||
"execa": "9.6.1",
|
||||
"ncp": "2.0.0",
|
||||
"nodemon": "3.1.14",
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
"@types/node": "24.12.4",
|
||||
"@typescript-eslint/eslint-plugin": "8.60.0",
|
||||
"@typescript-eslint/parser": "8.60.0",
|
||||
"esbuild": "0.28.0",
|
||||
"esbuild": "0.28.1",
|
||||
"execa": "9.6.1",
|
||||
"nodemon": "3.1.14"
|
||||
},
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"i18n": "workspace:*",
|
||||
"esbuild": "0.28.0",
|
||||
"esbuild": "0.28.1",
|
||||
"idb-keyval": "6.2.4",
|
||||
"misskey-js": "workspace:*"
|
||||
},
|
||||
|
||||
390
pnpm-lock.yaml
generated
390
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -54,6 +54,8 @@ minimumReleaseAgeExclude:
|
||||
- '@typescript/native-preview*'
|
||||
- pnpm
|
||||
- '@types/archiver' # そのうち消す
|
||||
- esbuild # 脆弱性対応。そのうち消す
|
||||
- '@esbuild/*' # 脆弱性対応。そのうち消す
|
||||
overrides:
|
||||
'@aiscript-dev/aiscript-languageserver': '-'
|
||||
chokidar: 5.0.0
|
||||
|
||||
Reference in New Issue
Block a user