Merge branch 'develop' into img-max

This commit is contained in:
tamaina 2023-04-14 07:26:46 +00:00
commit 3a1b67c4c6
42 changed files with 287 additions and 174 deletions

View file

@ -84,7 +84,7 @@
"got": "12.6.0",
"happy-dom": "8.9.0",
"hpagent": "1.2.0",
"ioredis": "4.28.5",
"ioredis": "5.3.1",
"ip-cidr": "3.1.0",
"is-svg": "4.3.2",
"js-yaml": "4.1.0",
@ -159,7 +159,6 @@
"@types/content-disposition": "0.5.5",
"@types/escape-regexp": "0.0.1",
"@types/fluent-ffmpeg": "2.1.21",
"@types/ioredis": "4.28.10",
"@types/jest": "29.5.0",
"@types/js-yaml": "4.0.5",
"@types/jsdom": "21.1.1",

View file

@ -1,6 +1,6 @@
import { setTimeout } from 'node:timers/promises';
import { Global, Inject, Module } from '@nestjs/common';
import Redis from 'ioredis';
import * as Redis from 'ioredis';
import { DataSource } from 'typeorm';
import { DI } from './di-symbols.js';
import { loadConfig } from './config.js';
@ -25,7 +25,7 @@ const $db: Provider = {
const $redis: Provider = {
provide: DI.redis,
useFactory: (config) => {
return new Redis({
return new Redis.Redis({
port: config.redis.port,
host: config.redis.host,
family: config.redis.family == null ? 0 : config.redis.family,
@ -40,7 +40,7 @@ const $redis: Provider = {
const $redisForPub: Provider = {
provide: DI.redisForPub,
useFactory: (config) => {
const redis = new Redis({
const redis = new Redis.Redis({
port: config.redisForPubsub.port,
host: config.redisForPubsub.host,
family: config.redisForPubsub.family == null ? 0 : config.redisForPubsub.family,
@ -56,7 +56,7 @@ const $redisForPub: Provider = {
const $redisForSub: Provider = {
provide: DI.redisForSub,
useFactory: (config) => {
const redis = new Redis({
const redis = new Redis.Redis({
port: config.redisForPubsub.port,
host: config.redisForPubsub.host,
family: config.redisForPubsub.family == null ? 0 : config.redisForPubsub.family,

View file

@ -1,5 +1,5 @@
import { Inject, Injectable } from '@nestjs/common';
import Redis from 'ioredis';
import * as Redis from 'ioredis';
import type { Antenna } from '@/models/entities/Antenna.js';
import type { Note } from '@/models/entities/Note.js';
import type { User } from '@/models/entities/User.js';

View file

@ -1,7 +1,7 @@
import { promisify } from 'node:util';
import { Inject, Injectable } from '@nestjs/common';
import redisLock from 'redis-lock';
import Redis from 'ioredis';
import * as Redis from 'ioredis';
import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js';

View file

@ -1,5 +1,5 @@
import { Inject, Injectable } from '@nestjs/common';
import Redis from 'ioredis';
import * as Redis from 'ioredis';
import type { BlockingsRepository, ChannelFollowingsRepository, FollowingsRepository, MutingsRepository, RenoteMutingsRepository, UserProfile, UserProfilesRepository, UsersRepository } from '@/models/index.js';
import { MemoryKVCache, RedisKVCache } from '@/misc/cache.js';
import type { LocalUser, User } from '@/models/entities/User.js';

View file

@ -1,6 +1,6 @@
import { Inject, Injectable } from '@nestjs/common';
import { DataSource, In, IsNull } from 'typeorm';
import Redis from 'ioredis';
import * as Redis from 'ioredis';
import { DI } from '@/di-symbols.js';
import { IdService } from '@/core/IdService.js';
import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js';

View file

@ -59,6 +59,8 @@ type AddFileArgs = {
uri?: string | null;
/** Mark file as sensitive */
sensitive?: boolean | null;
/** Extension to force */
ext?: string | null;
requestIp?: string | null;
requestHeaders?: Record<string, string> | null;
@ -125,7 +127,7 @@ export class DriveService {
/***
* Save file
* @param path Path for original
* @param name Name for original
* @param name Name for original (should be extention corrected)
* @param type Content-Type for original
* @param hash Hash for original
* @param size Size for original
@ -151,7 +153,7 @@ export class DriveService {
}
// 拡張子からContent-Typeを設定してそうな挙動を示すオブジェクトストレージ (upcloud?) も存在するので、
// 許可されているファイル形式でしか拡張子をつけない
// 許可されているファイル形式でしかURLに拡張子をつけない
if (!FILE_TYPE_BROWSERSAFE.includes(type)) {
ext = '';
}
@ -173,7 +175,7 @@ export class DriveService {
//#region Uploads
this.registerLogger.info(`uploading original: ${key}`);
const uploads = [
this.upload(key, fs.createReadStream(path), type, ext, name),
this.upload(key, fs.createReadStream(path), type, null, name),
];
if (alts.webpublic) {
@ -189,7 +191,7 @@ export class DriveService {
thumbnailUrl = `${ baseUrl }/${ thumbnailKey }`;
this.registerLogger.info(`uploading thumbnail: ${thumbnailKey}`);
uploads.push(this.upload(thumbnailKey, alts.thumbnail.data, alts.thumbnail.type, alts.thumbnail.ext));
uploads.push(this.upload(thumbnailKey, alts.thumbnail.data, alts.thumbnail.type, alts.thumbnail.ext, `${name}.thumbnail`));
}
await Promise.all(uploads);
@ -443,6 +445,7 @@ export class DriveService {
sensitive = null,
requestIp = null,
requestHeaders = null,
ext = null,
}: AddFileArgs): Promise<DriveFile> {
let skipNsfwCheck = false;
const instance = await this.metaService.fetch();
@ -474,7 +477,7 @@ export class DriveService {
// DriveFile.nameは256文字, validateFileNameは200文字制限であるため、
// extを付加してデータベースの文字数制限に当たることはまずない
(name && this.driveFileEntityService.validateFileName(name)) ? name : 'untitled',
info.type.ext,
ext ?? info.type.ext,
);
if (user && !force) {

View file

@ -1,5 +1,5 @@
import { Inject, Injectable } from '@nestjs/common';
import Redis from 'ioredis';
import * as Redis from 'ioredis';
import type { InstancesRepository } from '@/models/index.js';
import type { Instance } from '@/models/entities/Instance.js';
import { MemoryKVCache, RedisKVCache } from '@/misc/cache.js';

View file

@ -1,5 +1,5 @@
import { Inject, Injectable } from '@nestjs/common';
import Redis from 'ioredis';
import * as Redis from 'ioredis';
import type { User } from '@/models/entities/User.js';
import type { Note } from '@/models/entities/Note.js';
import type { UserList } from '@/models/entities/UserList.js';

View file

@ -1,6 +1,6 @@
import { Inject, Injectable } from '@nestjs/common';
import { DataSource } from 'typeorm';
import Redis from 'ioredis';
import * as Redis from 'ioredis';
import { DI } from '@/di-symbols.js';
import { Meta } from '@/models/entities/Meta.js';
import { GlobalEventService } from '@/core/GlobalEventService.js';

View file

@ -1,7 +1,7 @@
import { setImmediate } from 'node:timers/promises';
import * as mfm from 'mfm-js';
import { In, DataSource } from 'typeorm';
import Redis from 'ioredis';
import * as Redis from 'ioredis';
import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common';
import { extractMentions } from '@/misc/extract-mentions.js';
import { extractCustomEmojisFromMfm } from '@/misc/extract-custom-emojis-from-mfm.js';

View file

@ -1,5 +1,5 @@
import { setTimeout } from 'node:timers/promises';
import Redis from 'ioredis';
import * as Redis from 'ioredis';
import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common';
import { In } from 'typeorm';
import { DI } from '@/di-symbols.js';
@ -111,7 +111,7 @@ export class NotificationService implements OnApplicationShutdown {
// 2秒経っても(今回作成した)通知が既読にならなかったら「未読の通知がありますよ」イベントを発行する
setTimeout(2000, 'unread notification', { signal: this.#shutdownController.signal }).then(async () => {
const latestReadNotificationId = await this.redisClient.get(`latestReadNotification:${notifieeId}`);
if (latestReadNotificationId && (latestReadNotificationId >= await redisIdPromise)) return;
if (latestReadNotificationId && (latestReadNotificationId >= (await redisIdPromise)!)) return;
this.globalEventService.publishMainStream(notifieeId, 'unreadNotification', packed);
this.pushNotificationService.pushNotification(notifieeId, 'notification', packed);

View file

@ -1,6 +1,6 @@
import { Inject, Injectable } from '@nestjs/common';
import push from 'web-push';
import Redis from 'ioredis';
import * as Redis from 'ioredis';
import { DI } from '@/di-symbols.js';
import type { Config } from '@/config.js';
import type { Packed } from '@/misc/json-schema';

View file

@ -1,5 +1,5 @@
import { Inject, Injectable } from '@nestjs/common';
import Redis from 'ioredis';
import * as Redis from 'ioredis';
import { In } from 'typeorm';
import type { Role, RoleAssignment, RoleAssignmentsRepository, RolesRepository, UsersRepository } from '@/models/index.js';
import { MemoryKVCache, MemorySingleCache } from '@/misc/cache.js';

View file

@ -1,5 +1,5 @@
import { Inject, Injectable } from '@nestjs/common';
import Redis from 'ioredis';
import * as Redis from 'ioredis';
import type { User } from '@/models/entities/User.js';
import type { UserKeypairsRepository } from '@/models/index.js';
import { RedisKVCache } from '@/misc/cache.js';

View file

@ -1,5 +1,5 @@
import { Inject, Injectable } from '@nestjs/common';
import Redis from 'ioredis';
import * as Redis from 'ioredis';
import type { WebhooksRepository } from '@/models/index.js';
import type { Webhook } from '@/models/entities/Webhook.js';
import { DI } from '@/di-symbols.js';

View file

@ -1,6 +1,6 @@
import { Inject, Injectable } from '@nestjs/common';
import { In, Not } from 'typeorm';
import Redis from 'ioredis';
import * as Redis from 'ioredis';
import Ajv from 'ajv';
import { ModuleRef } from '@nestjs/core';
import { DI } from '@/di-symbols.js';

View file

@ -1,4 +1,4 @@
import Redis from 'ioredis';
import * as Redis from 'ioredis';
import { bindThis } from '@/decorators.js';
export class RedisKVCache<T> {
@ -38,7 +38,7 @@ export class RedisKVCache<T> {
await this.redisClient.set(
`kvcache:${this.name}:${key}`,
this.toRedisConverter(value),
'ex', Math.round(this.lifetime / 1000),
'EX', Math.round(this.lifetime / 1000),
);
}
}
@ -122,7 +122,7 @@ export class RedisSingleCache<T> {
await this.redisClient.set(
`singlecache:${this.name}`,
this.toRedisConverter(value),
'ex', Math.round(this.lifetime / 1000),
'EX', Math.round(this.lifetime / 1000),
);
}
}

View file

@ -106,7 +106,7 @@ export class ExportBlockingProcessorService {
this.logger.succ(`Exported to: ${path}`);
const fileName = 'blocking-' + dateFormat(new Date(), 'yyyy-MM-dd-HH-mm-ss') + '.csv';
const driveFile = await this.driveService.addFile({ user, path, name: fileName, force: true });
const driveFile = await this.driveService.addFile({ user, path, name: fileName, force: true, ext: 'csv' });
this.logger.succ(`Exported to: ${driveFile.id}`);
} finally {

View file

@ -121,7 +121,7 @@ export class ExportFavoritesProcessorService {
this.logger.succ(`Exported to: ${path}`);
const fileName = 'favorites-' + dateFormat(new Date(), 'yyyy-MM-dd-HH-mm-ss') + '.json';
const driveFile = await this.driveService.addFile({ user, path, name: fileName, force: true });
const driveFile = await this.driveService.addFile({ user, path, name: fileName, force: true, ext: 'json' });
this.logger.succ(`Exported to: ${driveFile.id}`);
} finally {

View file

@ -110,7 +110,7 @@ export class ExportFollowingProcessorService {
this.logger.succ(`Exported to: ${path}`);
const fileName = 'following-' + dateFormat(new Date(), 'yyyy-MM-dd-HH-mm-ss') + '.csv';
const driveFile = await this.driveService.addFile({ user, path, name: fileName, force: true });
const driveFile = await this.driveService.addFile({ user, path, name: fileName, force: true, ext: 'csv' });
this.logger.succ(`Exported to: ${driveFile.id}`);
} finally {

View file

@ -110,7 +110,7 @@ export class ExportMutingProcessorService {
this.logger.succ(`Exported to: ${path}`);
const fileName = 'mute-' + dateFormat(new Date(), 'yyyy-MM-dd-HH-mm-ss') + '.csv';
const driveFile = await this.driveService.addFile({ user, path, name: fileName, force: true });
const driveFile = await this.driveService.addFile({ user, path, name: fileName, force: true, ext: 'csv' });
this.logger.succ(`Exported to: ${driveFile.id}`);
} finally {

View file

@ -117,7 +117,7 @@ export class ExportNotesProcessorService {
this.logger.succ(`Exported to: ${path}`);
const fileName = 'notes-' + dateFormat(new Date(), 'yyyy-MM-dd-HH-mm-ss') + '.json';
const driveFile = await this.driveService.addFile({ user, path, name: fileName, force: true });
const driveFile = await this.driveService.addFile({ user, path, name: fileName, force: true, ext: 'json' });
this.logger.succ(`Exported to: ${driveFile.id}`);
} finally {

View file

@ -86,7 +86,7 @@ export class ExportUserListsProcessorService {
this.logger.succ(`Exported to: ${path}`);
const fileName = 'user-lists-' + dateFormat(new Date(), 'yyyy-MM-dd-HH-mm-ss') + '.csv';
const driveFile = await this.driveService.addFile({ user, path, name: fileName, force: true });
const driveFile = await this.driveService.addFile({ user, path, name: fileName, force: true, ext: 'csv' });
this.logger.succ(`Exported to: ${driveFile.id}`);
} finally {

View file

@ -1,6 +1,6 @@
import { Inject, Injectable } from '@nestjs/common';
import Limiter from 'ratelimiter';
import Redis from 'ioredis';
import * as Redis from 'ioredis';
import { DI } from '@/di-symbols.js';
import type Logger from '@/logger.js';
import { LoggerService } from '@/core/LoggerService.js';

View file

@ -1,6 +1,6 @@
import { EventEmitter } from 'events';
import { Inject, Injectable } from '@nestjs/common';
import Redis from 'ioredis';
import * as Redis from 'ioredis';
import * as websocket from 'websocket';
import { DI } from '@/di-symbols.js';
import type { UsersRepository, BlockingsRepository, ChannelFollowingsRepository, FollowingsRepository, MutingsRepository, UserProfilesRepository, RenoteMutingsRepository } from '@/models/index.js';

View file

@ -87,12 +87,18 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
//const emojis = await q.take(ps.limit).getMany();
emojis = await q.getMany();
const queryarry = ps.query.match(/\:([a-z0-9_]*)\:/g);
emojis = emojis.filter(emoji =>
emoji.name.includes(ps.query!) ||
emoji.aliases.some(a => a.includes(ps.query!)) ||
emoji.category?.includes(ps.query!));
if (queryarry) {
emojis = emojis.filter(emoji =>
queryarry.includes(`:${emoji.name}:`)
);
} else {
emojis = emojis.filter(emoji =>
emoji.name.includes(ps.query!) ||
emoji.aliases.some(a => a.includes(ps.query!)) ||
emoji.category?.includes(ps.query!));
}
emojis.splice(ps.limit + 1);
} else {
emojis = await q.take(ps.limit).getMany();

View file

@ -2,7 +2,7 @@ import * as os from 'node:os';
import si from 'systeminformation';
import { Inject, Injectable } from '@nestjs/common';
import { DataSource } from 'typeorm';
import Redis from 'ioredis';
import * as Redis from 'ioredis';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { DI } from '@/di-symbols.js';

View file

@ -1,5 +1,5 @@
import { Inject, Injectable } from '@nestjs/common';
import Redis from 'ioredis';
import * as Redis from 'ioredis';
import { Endpoint } from '@/server/api/endpoint-base.js';
import type { NotesRepository, AntennasRepository } from '@/models/index.js';
import { QueryService } from '@/core/QueryService.js';

View file

@ -1,5 +1,5 @@
import { Inject, Injectable } from '@nestjs/common';
import Redis from 'ioredis';
import * as Redis from 'ioredis';
import { Endpoint } from '@/server/api/endpoint-base.js';
import type { ChannelsRepository, Note, NotesRepository } from '@/models/index.js';
import { QueryService } from '@/core/QueryService.js';

View file

@ -1,5 +1,5 @@
import { Brackets, In } from 'typeorm';
import Redis from 'ioredis';
import * as Redis from 'ioredis';
import { Inject, Injectable } from '@nestjs/common';
import type { UsersRepository, FollowingsRepository, MutingsRepository, UserProfilesRepository, NotesRepository } from '@/models/index.js';
import { obsoleteNotificationTypes, notificationTypes } from '@/types.js';

View file

@ -1,6 +1,6 @@
import { Inject, Injectable } from '@nestjs/common';
import { DataSource } from 'typeorm';
import Redis from 'ioredis';
import * as Redis from 'ioredis';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { DI } from '@/di-symbols.js';
import { resetDb } from '@/misc/reset-db.js';

View file

@ -1,5 +1,5 @@
import { Inject, Injectable } from '@nestjs/common';
import Redis from 'ioredis';
import * as Redis from 'ioredis';
import { Endpoint } from '@/server/api/endpoint-base.js';
import type { NotesRepository, RolesRepository } from '@/models/index.js';
import { QueryService } from '@/core/QueryService.js';

View file

@ -898,7 +898,8 @@ describe('Endpoints', () => {
userId: bob.id,
}, alice);
assert.strictEqual('memo' in res.body, false);
// memoには常に文字列かnullが入っている(5cac151)
assert.strictEqual(res.body.memo, null);
});
test('メモは個人ごとに独立して保存される', async () => {

View file

@ -31,7 +31,7 @@ describe('ユーザー', () => {
}, {});
};
// FIXME: 足りないキーがたくさんある
// BUG misskey-jsとjson-schemaと実際に返ってくるデータが全部違う
type UserLite = misskey.entities.UserLite & {
badgeRoles: any[],
};
@ -55,6 +55,7 @@ describe('ユーザー', () => {
return successfulApiCall({ endpoint: 'users/show', parameters: { userId: id }, user: me }) as any;
};
// UserLiteのキーが過不足なく入っている
const userLite = (user: User): Partial<UserLite> => {
return stripUndefined({
id: user.id,
@ -76,6 +77,7 @@ describe('ユーザー', () => {
});
};
// UserDetailedNotMeのキーが過不足なく入っている
const userDetailedNotMe = (user: User): Partial<UserDetailedNotMe> => {
return stripUndefined({
...userLite(user),
@ -109,9 +111,11 @@ describe('ユーザー', () => {
usePasswordLessLogin: user.usePasswordLessLogin,
securityKeys: user.securityKeys,
roles: user.roles,
memo: user.memo,
});
};
// Relations関連のキーが過不足なく入っている
const userDetailedNotMeWithRelations = (user: User): Partial<UserDetailedNotMe> => {
return stripUndefined({
...userDetailedNotMe(user),
@ -126,6 +130,7 @@ describe('ユーザー', () => {
});
};
// MeDetailedのキーが過不足なく入っている
const meDetailed = (user: User, security = false): Partial<MeDetailed> => {
return stripUndefined({
...userDetailedNotMe(user),
@ -371,6 +376,7 @@ describe('ユーザー', () => {
assert.strictEqual(response.usePasswordLessLogin, false);
assert.strictEqual(response.securityKeys, false);
assert.deepStrictEqual(response.roles, []);
assert.strictEqual(response.memo, null);
// MeDetailedOnly
assert.strictEqual(response.avatarId, null);
@ -410,7 +416,7 @@ describe('ユーザー', () => {
//#endregion
//#region 自分の情報(i)
test('を読み取ることができる。(自分)', async () => {
test('を読み取ることができること(自分)、キーが過不足なく入っていること。', async () => {
const response = await successfulApiCall({
endpoint: 'i',
parameters: {},
@ -554,6 +560,21 @@ describe('ユーザー', () => {
assert.deepStrictEqual(response2, expected2);
});
//#endregion
//#region メモの更新(users/update-memo)
test.each([
{ label: '最大長', memo: 'x'.repeat(2048) },
{ label: '空文字', memo: '', expects: null },
{ label: 'null', memo: null },
])('を書き換えることができる(メモを$labelに)', async ({ memo, expects }) => {
const expected = { ...await show(bob.id), memo: expects === undefined ? memo : expects };
const parameters = { userId: bob.id, memo };
await successfulApiCall({ endpoint: 'users/update-memo', parameters, user: alice });
const response = await show(bob.id);
assert.deepStrictEqual(response, expected);
});
//#endregion
//#region ユーザー(users)

View file

@ -1,6 +1,6 @@
import { resolve } from 'node:path';
import type { StorybookConfig } from '@storybook/vue3-vite';
import { mergeConfig } from 'vite';
import { type Plugin, mergeConfig } from 'vite';
import turbosnap from 'vite-plugin-turbosnap';
const config = {
stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'],
@ -22,6 +22,10 @@ const config = {
disableTelemetry: true,
},
async viteFinal(config) {
const replacePluginForIsChromatic = config.plugins?.findIndex((plugin) => plugin && (plugin as Partial<Plugin>)?.name === 'replace') ?? -1;
if (~replacePluginForIsChromatic) {
config.plugins?.splice(replacePluginForIsChromatic, 1);
}
return mergeConfig(config, {
plugins: [
turbosnap({

View file

@ -17,6 +17,7 @@
"@discordapp/twemoji": "14.1.2",
"@rollup/plugin-alias": "4.0.3",
"@rollup/plugin-json": "6.0.0",
"@rollup/plugin-replace": "^5.0.2",
"@rollup/pluginutils": "5.0.2",
"@syuilo/aiscript": "0.13.1",
"@tabler/icons-webfont": "2.12.0",
@ -32,6 +33,7 @@
"chartjs-chart-matrix": "2.0.1",
"chartjs-plugin-gradient": "0.6.1",
"chartjs-plugin-zoom": "2.0.1",
"chromatic": "6.17.3",
"compare-versions": "5.0.1",
"cropperjs": "2.0.0-beta.2",
"date-fns": "2.29.3",
@ -116,7 +118,6 @@
"@vue/runtime-core": "3.2.47",
"astring": "1.8.4",
"chokidar-cli": "3.0.0",
"chromatic": "6.17.3",
"cross-env": "7.0.3",
"cypress": "12.9.0",
"eslint": "8.37.0",

View file

@ -8,6 +8,7 @@
</template>
<script lang="ts" setup>
import isChromatic from 'chromatic/isChromatic';
import { onUnmounted } from 'vue';
import { i18n } from '@/i18n';
import { dateTimeFormat } from '@/scripts/intl-const';
@ -17,7 +18,7 @@ const props = withDefaults(defineProps<{
origin?: Date | null;
mode?: 'relative' | 'absolute' | 'detail';
}>(), {
origin: null,
origin: isChromatic() ? new Date('2023-04-01T00:00:00Z') : null,
mode: 'relative',
});

View file

@ -53,7 +53,15 @@ function search() {
}
if (selectedTags.size === 0) {
searchEmojis = customEmojis.value.filter(emoji => emoji.name.includes(q) || emoji.aliases.includes(q));
const queryarry = q.match(/\:([a-z0-9_]*)\:/g);
if (queryarry) {
searchEmojis = customEmojis.value.filter(emoji =>
queryarry.includes(`:${emoji.name}:`)
);
} else {
searchEmojis = customEmojis.value.filter(emoji => emoji.name.includes(q) || emoji.aliases.includes(q));
}
} else {
searchEmojis = customEmojis.value.filter(emoji => (emoji.name.includes(q) || emoji.aliases.includes(q)) && [...selectedTags].every(t => emoji.aliases.includes(t)));
}

View file

@ -1,4 +1,5 @@
import path from 'path';
import pluginReplace from '@rollup/plugin-replace';
import pluginVue from '@vitejs/plugin-vue';
import { type UserConfig, defineConfig } from 'vite';
@ -46,6 +47,16 @@ export function getConfig(): UserConfig {
reactivityTransform: true,
}),
pluginJson5(),
...process.env.NODE_ENV === 'production'
? [
pluginReplace({
preventAssignment: true,
values: {
'isChromatic()': JSON.stringify(false),
},
}),
]
: [],
],
resolve: {