diff --git a/packages/frontend/@types/theme.d.ts b/packages/frontend/@types/theme.d.ts
index f4ba42b89d..376bbb0e9c 100644
--- a/packages/frontend/@types/theme.d.ts
+++ b/packages/frontend/@types/theme.d.ts
@@ -4,7 +4,7 @@
  */
 
 declare module '@/themes/*.json5' {
-	import { Theme } from '@/scripts/theme';
+	import { Theme } from '@/scripts/theme.js';
 
 	const theme: Theme;
 
diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index d38611c2fc..a89bd84ff0 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -1,6 +1,7 @@
 {
 	"name": "frontend",
 	"private": true,
+	"type": "module",
 	"scripts": {
 		"watch": "vite",
 		"build": "vite build",
diff --git a/packages/frontend/src/_boot_.ts b/packages/frontend/src/_boot_.ts
index a397e57ad3..efb78fe447 100644
--- a/packages/frontend/src/_boot_.ts
+++ b/packages/frontend/src/_boot_.ts
@@ -7,8 +7,8 @@
 import 'vite/modulepreload-polyfill';
 
 import '@/style.scss';
-import { mainBoot } from './boot/main-boot';
-import { subBoot } from './boot/sub-boot';
+import { mainBoot } from '@/boot/main-boot.js';
+import { subBoot } from '@/boot/sub-boot.js';
 
 const subBootPaths = ['/share', '/auth', '/miauth', '/signup-complete'];
 
diff --git a/packages/frontend/src/account.ts b/packages/frontend/src/account.ts
index 43bda37b6c..0e4e4b50ff 100644
--- a/packages/frontend/src/account.ts
+++ b/packages/frontend/src/account.ts
@@ -5,14 +5,14 @@
 
 import { defineAsyncComponent, reactive, ref } from 'vue';
 import * as Misskey from 'misskey-js';
-import { showSuspendedDialog } from './scripts/show-suspended-dialog';
-import { i18n } from './i18n';
-import { miLocalStorage } from './local-storage';
-import { MenuButton } from './types/menu';
-import { del, get, set } from '@/scripts/idb-proxy';
-import { apiUrl } from '@/config';
-import { waiting, api, popup, popupMenu, success, alert } from '@/os';
-import { unisonReload, reloadChannel } from '@/scripts/unison-reload';
+import { showSuspendedDialog } from '@/scripts/show-suspended-dialog.js';
+import { i18n } from '@/i18n.js';
+import { miLocalStorage } from '@/local-storage.js';
+import { MenuButton } from '@/types/menu.js';
+import { del, get, set } from '@/scripts/idb-proxy.js';
+import { apiUrl } from '@/config.js';
+import { waiting, api, popup, popupMenu, success, alert } from '@/os.js';
+import { unisonReload, reloadChannel } from '@/scripts/unison-reload.js';
 
 // TODO: 他のタブと永続化されたstateを同期
 
diff --git a/packages/frontend/src/boot/common.ts b/packages/frontend/src/boot/common.ts
index 8f5de88ccd..2175bd1a7f 100644
--- a/packages/frontend/src/boot/common.ts
+++ b/packages/frontend/src/boot/common.ts
@@ -5,26 +5,26 @@
 
 import { computed, createApp, watch, markRaw, version as vueVersion, defineAsyncComponent, App } from 'vue';
 import { compareVersions } from 'compare-versions';
-import widgets from '@/widgets';
-import directives from '@/directives';
-import components from '@/components';
-import { version, ui, lang, updateLocale } from '@/config';
-import { applyTheme } from '@/scripts/theme';
-import { isDeviceDarkmode } from '@/scripts/is-device-darkmode';
-import { i18n, updateI18n } from '@/i18n';
-import { confirm, alert, post, popup, toast } from '@/os';
-import { $i, refreshAccount, login, updateAccount, signout } from '@/account';
-import { defaultStore, ColdDeviceStorage } from '@/store';
-import { fetchInstance, instance } from '@/instance';
-import { deviceKind } from '@/scripts/device-kind';
-import { reloadChannel } from '@/scripts/unison-reload';
-import { reactionPicker } from '@/scripts/reaction-picker';
-import { getUrlWithoutLoginId } from '@/scripts/login-id';
-import { getAccountFromId } from '@/scripts/get-account-from-id';
-import { deckStore } from '@/ui/deck/deck-store';
-import { miLocalStorage } from '@/local-storage';
-import { fetchCustomEmojis } from '@/custom-emojis';
-import { mainRouter } from '@/router';
+import widgets from '@/widgets/index.js';
+import directives from '@/directives/index.js';
+import components from '@/components/index.js';
+import { version, ui, lang, updateLocale } from '@/config.js';
+import { applyTheme } from '@/scripts/theme.js';
+import { isDeviceDarkmode } from '@/scripts/is-device-darkmode.js';
+import { i18n, updateI18n } from '@/i18n.js';
+import { confirm, alert, post, popup, toast } from '@/os.js';
+import { $i, refreshAccount, login, updateAccount, signout } from '@/account.js';
+import { defaultStore, ColdDeviceStorage } from '@/store.js';
+import { fetchInstance, instance } from '@/instance.js';
+import { deviceKind } from '@/scripts/device-kind.js';
+import { reloadChannel } from '@/scripts/unison-reload.js';
+import { reactionPicker } from '@/scripts/reaction-picker.js';
+import { getUrlWithoutLoginId } from '@/scripts/login-id.js';
+import { getAccountFromId } from '@/scripts/get-account-from-id.js';
+import { deckStore } from '@/ui/deck/deck-store.js';
+import { miLocalStorage } from '@/local-storage.js';
+import { fetchCustomEmojis } from '@/custom-emojis.js';
+import { mainRouter } from '@/router.js';
 
 export async function common(createVue: () => App<Element>) {
 	console.info(`Misskey v${version}`);
diff --git a/packages/frontend/src/boot/main-boot.ts b/packages/frontend/src/boot/main-boot.ts
index 9ab1f6e14c..f2af951d63 100644
--- a/packages/frontend/src/boot/main-boot.ts
+++ b/packages/frontend/src/boot/main-boot.ts
@@ -4,21 +4,21 @@
  */
 
 import { computed, createApp, watch, markRaw, version as vueVersion, defineAsyncComponent } from 'vue';
-import { common } from './common';
-import { version, ui, lang, updateLocale } from '@/config';
-import { i18n, updateI18n } from '@/i18n';
-import { confirm, alert, post, popup, toast } from '@/os';
-import { useStream } from '@/stream';
-import * as sound from '@/scripts/sound';
-import { $i, refreshAccount, login, updateAccount, signout } from '@/account';
-import { defaultStore, ColdDeviceStorage } from '@/store';
-import { makeHotkey } from '@/scripts/hotkey';
-import { reactionPicker } from '@/scripts/reaction-picker';
-import { miLocalStorage } from '@/local-storage';
-import { claimAchievement, claimedAchievements } from '@/scripts/achievements';
-import { mainRouter } from '@/router';
-import { initializeSw } from '@/scripts/initialize-sw';
-import { deckStore } from '@/ui/deck/deck-store';
+import { common } from './common.js';
+import { version, ui, lang, updateLocale } from '@/config.js';
+import { i18n, updateI18n } from '@/i18n.js';
+import { confirm, alert, post, popup, toast } from '@/os.js';
+import { useStream } from '@/stream.js';
+import * as sound from '@/scripts/sound.js';
+import { $i, refreshAccount, login, updateAccount, signout } from '@/account.js';
+import { defaultStore, ColdDeviceStorage } from '@/store.js';
+import { makeHotkey } from '@/scripts/hotkey.js';
+import { reactionPicker } from '@/scripts/reaction-picker.js';
+import { miLocalStorage } from '@/local-storage.js';
+import { claimAchievement, claimedAchievements } from '@/scripts/achievements.js';
+import { mainRouter } from '@/router.js';
+import { initializeSw } from '@/scripts/initialize-sw.js';
+import { deckStore } from '@/ui/deck/deck-store.js';
 
 export async function mainBoot() {
 	const { isClientUpdated } = await common(() => createApp(
diff --git a/packages/frontend/src/boot/sub-boot.ts b/packages/frontend/src/boot/sub-boot.ts
index 2cc19f2df3..9b4670e130 100644
--- a/packages/frontend/src/boot/sub-boot.ts
+++ b/packages/frontend/src/boot/sub-boot.ts
@@ -4,7 +4,7 @@
  */
 
 import { computed, createApp, watch, markRaw, version as vueVersion, defineAsyncComponent } from 'vue';
-import { common } from './common';
+import { common } from './common.js';
 
 export async function subBoot() {
 	const { isClientUpdated } = await common(() => createApp(
diff --git a/packages/frontend/src/cache.ts b/packages/frontend/src/cache.ts
index 1f3d28ba57..25d2b3c15f 100644
--- a/packages/frontend/src/cache.ts
+++ b/packages/frontend/src/cache.ts
@@ -4,8 +4,8 @@
  */
 
 import * as Misskey from 'misskey-js';
-import { Cache } from '@/scripts/cache';
-import { api } from '@/os';
+import { Cache } from '@/scripts/cache.js';
+import { api } from '@/os.js';
 
 export const clipsCache = new Cache<Misskey.entities.Clip[]>(1000 * 60 * 30, () => api('clips/list'));
 export const rolesCache = new Cache(1000 * 60 * 30, () => api('admin/roles/list'));
diff --git a/packages/frontend/src/components/MkAbuseReport.vue b/packages/frontend/src/components/MkAbuseReport.vue
index cb97875bcd..66114b8734 100644
--- a/packages/frontend/src/components/MkAbuseReport.vue
+++ b/packages/frontend/src/components/MkAbuseReport.vue
@@ -44,9 +44,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 import MkButton from '@/components/MkButton.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
 import MkKeyValue from '@/components/MkKeyValue.vue';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
-import { dateString } from '@/filters/date';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
+import { dateString } from '@/filters/date.js';
 
 const props = defineProps<{
 	report: any;
diff --git a/packages/frontend/src/components/MkAbuseReportWindow.vue b/packages/frontend/src/components/MkAbuseReportWindow.vue
index 6e0dee6973..7814681ea2 100644
--- a/packages/frontend/src/components/MkAbuseReportWindow.vue
+++ b/packages/frontend/src/components/MkAbuseReportWindow.vue
@@ -35,8 +35,8 @@ import * as Misskey from 'misskey-js';
 import MkWindow from '@/components/MkWindow.vue';
 import MkTextarea from '@/components/MkTextarea.vue';
 import MkButton from '@/components/MkButton.vue';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
 
 const props = defineProps<{
 	user: Misskey.entities.User;
diff --git a/packages/frontend/src/components/MkAccountMoved.vue b/packages/frontend/src/components/MkAccountMoved.vue
index e68efb1a3b..155d9fe3a9 100644
--- a/packages/frontend/src/components/MkAccountMoved.vue
+++ b/packages/frontend/src/components/MkAccountMoved.vue
@@ -15,9 +15,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkMention from './MkMention.vue';
-import { i18n } from '@/i18n';
-import { host as localHost } from '@/config';
-import { api } from '@/os';
+import { i18n } from '@/i18n.js';
+import { host as localHost } from '@/config.js';
+import { api } from '@/os.js';
 
 const user = ref<Misskey.entities.UserLite>();
 
diff --git a/packages/frontend/src/components/MkAchievements.stories.impl.ts b/packages/frontend/src/components/MkAchievements.stories.impl.ts
index fd971e5ac6..a67e1def13 100644
--- a/packages/frontend/src/components/MkAchievements.stories.impl.ts
+++ b/packages/frontend/src/components/MkAchievements.stories.impl.ts
@@ -9,7 +9,7 @@ import { rest } from 'msw';
 import { userDetailed } from '../../.storybook/fakes';
 import { commonHandlers } from '../../.storybook/mocks';
 import MkAchievements from './MkAchievements.vue';
-import { ACHIEVEMENT_TYPES } from '@/scripts/achievements';
+import { ACHIEVEMENT_TYPES } from '@/scripts/achievements.js';
 export const Empty = {
 	render(args) {
 		return {
diff --git a/packages/frontend/src/components/MkAchievements.vue b/packages/frontend/src/components/MkAchievements.vue
index f9d62773f5..bea0ed26d8 100644
--- a/packages/frontend/src/components/MkAchievements.vue
+++ b/packages/frontend/src/components/MkAchievements.vue
@@ -54,9 +54,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import * as Misskey from 'misskey-js';
 import { onMounted } from 'vue';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
-import { ACHIEVEMENT_TYPES, ACHIEVEMENT_BADGES, claimAchievement } from '@/scripts/achievements';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
+import { ACHIEVEMENT_TYPES, ACHIEVEMENT_BADGES, claimAchievement } from '@/scripts/achievements.js';
 
 const props = withDefaults(defineProps<{
 	user: Misskey.entities.User;
diff --git a/packages/frontend/src/components/MkAnnouncementDialog.vue b/packages/frontend/src/components/MkAnnouncementDialog.vue
index 0b97911e18..aaac3dd29b 100644
--- a/packages/frontend/src/components/MkAnnouncementDialog.vue
+++ b/packages/frontend/src/components/MkAnnouncementDialog.vue
@@ -24,11 +24,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { onMounted, shallowRef } from 'vue';
 import * as Misskey from 'misskey-js';
-import * as os from '@/os';
+import * as os from '@/os.js';
 import MkModal from '@/components/MkModal.vue';
 import MkButton from '@/components/MkButton.vue';
-import { i18n } from '@/i18n';
-import { $i, updateAccount } from '@/account';
+import { i18n } from '@/i18n.js';
+import { $i, updateAccount } from '@/account.js';
 
 const props = withDefaults(defineProps<{
 	announcement: Misskey.entities.Announcement;
diff --git a/packages/frontend/src/components/MkAsUi.vue b/packages/frontend/src/components/MkAsUi.vue
index 76156e69d0..099baf0d72 100644
--- a/packages/frontend/src/components/MkAsUi.vue
+++ b/packages/frontend/src/components/MkAsUi.vue
@@ -61,13 +61,13 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { Ref } from 'vue';
-import * as os from '@/os';
+import * as os from '@/os.js';
 import MkButton from '@/components/MkButton.vue';
 import MkInput from '@/components/MkInput.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
 import MkTextarea from '@/components/MkTextarea.vue';
 import MkSelect from '@/components/MkSelect.vue';
-import { AsUiComponent } from '@/scripts/aiscript/ui';
+import { AsUiComponent } from '@/scripts/aiscript/ui.js';
 import MkFolder from '@/components/MkFolder.vue';
 import MkPostForm from '@/components/MkPostForm.vue';
 
diff --git a/packages/frontend/src/components/MkAutocomplete.stories.impl.ts b/packages/frontend/src/components/MkAutocomplete.stories.impl.ts
index e363859301..8232759ba0 100644
--- a/packages/frontend/src/components/MkAutocomplete.stories.impl.ts
+++ b/packages/frontend/src/components/MkAutocomplete.stories.impl.ts
@@ -13,7 +13,7 @@ import { userDetailed } from '../../.storybook/fakes';
 import { commonHandlers } from '../../.storybook/mocks';
 import MkAutocomplete from './MkAutocomplete.vue';
 import MkInput from './MkInput.vue';
-import { tick } from '@/scripts/test-utils';
+import { tick } from '@/scripts/test-utils.js';
 const common = {
 	render(args) {
 		return {
diff --git a/packages/frontend/src/components/MkAutocomplete.vue b/packages/frontend/src/components/MkAutocomplete.vue
index 0b9f93c6ee..7c4f910559 100644
--- a/packages/frontend/src/components/MkAutocomplete.vue
+++ b/packages/frontend/src/components/MkAutocomplete.vue
@@ -41,16 +41,16 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts">
 import { markRaw, ref, shallowRef, computed, onUpdated, onMounted, onBeforeUnmount, nextTick, watch } from 'vue';
 import sanitizeHtml from 'sanitize-html';
-import contains from '@/scripts/contains';
-import { char2twemojiFilePath, char2fluentEmojiFilePath } from '@/scripts/emoji-base';
-import { acct } from '@/filters/user';
-import * as os from '@/os';
-import { MFM_TAGS } from '@/scripts/mfm-tags';
-import { defaultStore } from '@/store';
-import { emojilist, getEmojiName } from '@/scripts/emojilist';
-import { i18n } from '@/i18n';
-import { miLocalStorage } from '@/local-storage';
-import { customEmojis } from '@/custom-emojis';
+import contains from '@/scripts/contains.js';
+import { char2twemojiFilePath, char2fluentEmojiFilePath } from '@/scripts/emoji-base.js';
+import { acct } from '@/filters/user.js';
+import * as os from '@/os.js';
+import { MFM_TAGS } from '@/scripts/mfm-tags.js';
+import { defaultStore } from '@/store.js';
+import { emojilist, getEmojiName } from '@/scripts/emojilist.js';
+import { i18n } from '@/i18n.js';
+import { miLocalStorage } from '@/local-storage.js';
+import { customEmojis } from '@/custom-emojis.js';
 
 type EmojiDef = {
 	emoji: string;
diff --git a/packages/frontend/src/components/MkAvatars.vue b/packages/frontend/src/components/MkAvatars.vue
index b52ba5b7cd..5644a324cf 100644
--- a/packages/frontend/src/components/MkAvatars.vue
+++ b/packages/frontend/src/components/MkAvatars.vue
@@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { onMounted, ref } from 'vue';
 import * as Misskey from 'misskey-js';
-import * as os from '@/os';
+import * as os from '@/os.js';
 
 const props = withDefaults(defineProps<{
 	userIds: string[];
diff --git a/packages/frontend/src/components/MkCaptcha.vue b/packages/frontend/src/components/MkCaptcha.vue
index d28b37121d..14e59acad2 100644
--- a/packages/frontend/src/components/MkCaptcha.vue
+++ b/packages/frontend/src/components/MkCaptcha.vue
@@ -12,8 +12,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { ref, shallowRef, computed, onMounted, onBeforeUnmount, watch } from 'vue';
-import { defaultStore } from '@/store';
-import { i18n } from '@/i18n';
+import { defaultStore } from '@/store.js';
+import { i18n } from '@/i18n.js';
 
 // APIs provided by Captcha services
 export type Captcha = {
diff --git a/packages/frontend/src/components/MkChannelFollowButton.vue b/packages/frontend/src/components/MkChannelFollowButton.vue
index 2daa26e8a4..41b02a7e3f 100644
--- a/packages/frontend/src/components/MkChannelFollowButton.vue
+++ b/packages/frontend/src/components/MkChannelFollowButton.vue
@@ -26,8 +26,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { ref } from 'vue';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
 
 const props = withDefaults(defineProps<{
 	channel: Record<string, any>;
diff --git a/packages/frontend/src/components/MkChannelList.vue b/packages/frontend/src/components/MkChannelList.vue
index 4b93bbb14d..83d4401d2e 100644
--- a/packages/frontend/src/components/MkChannelList.vue
+++ b/packages/frontend/src/components/MkChannelList.vue
@@ -21,8 +21,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import MkChannelPreview from '@/components/MkChannelPreview.vue';
 import MkPagination, { Paging } from '@/components/MkPagination.vue';
-import { i18n } from '@/i18n';
-import { infoImageUrl } from '@/instance';
+import { i18n } from '@/i18n.js';
+import { infoImageUrl } from '@/instance.js';
 
 const props = withDefaults(defineProps<{
 	pagination: Paging;
diff --git a/packages/frontend/src/components/MkChannelPreview.vue b/packages/frontend/src/components/MkChannelPreview.vue
index 2583ee3831..4512f2dd60 100644
--- a/packages/frontend/src/components/MkChannelPreview.vue
+++ b/packages/frontend/src/components/MkChannelPreview.vue
@@ -41,7 +41,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { computed } from 'vue';
-import { i18n } from '@/i18n';
+import { i18n } from '@/i18n.js';
 
 const props = defineProps<{
 	channel: Record<string, any>;
diff --git a/packages/frontend/src/components/MkChart.vue b/packages/frontend/src/components/MkChart.vue
index 2f1a93548a..fe7077bdbf 100644
--- a/packages/frontend/src/components/MkChart.vue
+++ b/packages/frontend/src/components/MkChart.vue
@@ -22,14 +22,14 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { onMounted, ref, shallowRef, watch, PropType } from 'vue';
 import { Chart } from 'chart.js';
 import gradient from 'chartjs-plugin-gradient';
-import * as os from '@/os';
-import { defaultStore } from '@/store';
-import { useChartTooltip } from '@/scripts/use-chart-tooltip';
-import { chartVLine } from '@/scripts/chart-vline';
-import { alpha } from '@/scripts/color';
-import date from '@/filters/date';
-import { initChart } from '@/scripts/init-chart';
-import { chartLegend } from '@/scripts/chart-legend';
+import * as os from '@/os.js';
+import { defaultStore } from '@/store.js';
+import { useChartTooltip } from '@/scripts/use-chart-tooltip.js';
+import { chartVLine } from '@/scripts/chart-vline.js';
+import { alpha } from '@/scripts/color.js';
+import date from '@/filters/date.js';
+import { initChart } from '@/scripts/init-chart.js';
+import { chartLegend } from '@/scripts/chart-legend.js';
 import MkChartLegend from '@/components/MkChartLegend.vue';
 
 initChart();
diff --git a/packages/frontend/src/components/MkClickerGame.vue b/packages/frontend/src/components/MkClickerGame.vue
index 62a6f452c1..1c3920962e 100644
--- a/packages/frontend/src/components/MkClickerGame.vue
+++ b/packages/frontend/src/components/MkClickerGame.vue
@@ -21,11 +21,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { computed, onMounted, onUnmounted } from 'vue';
 import MkPlusOneEffect from '@/components/MkPlusOneEffect.vue';
-import * as os from '@/os';
-import { useInterval } from '@/scripts/use-interval';
-import * as game from '@/scripts/clicker-game';
-import number from '@/filters/number';
-import { claimAchievement } from '@/scripts/achievements';
+import * as os from '@/os.js';
+import { useInterval } from '@/scripts/use-interval.js';
+import * as game from '@/scripts/clicker-game.js';
+import number from '@/filters/number.js';
+import { claimAchievement } from '@/scripts/achievements.js';
 
 const saveData = game.saveData;
 const cookies = computed(() => saveData.value?.cookies);
diff --git a/packages/frontend/src/components/MkClipPreview.vue b/packages/frontend/src/components/MkClipPreview.vue
index 716e6dacb4..2f6790fa49 100644
--- a/packages/frontend/src/components/MkClipPreview.vue
+++ b/packages/frontend/src/components/MkClipPreview.vue
@@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { i18n } from '@/i18n';
+import { i18n } from '@/i18n.js';
 
 defineProps<{
 	clip: any;
diff --git a/packages/frontend/src/components/MkColorInput.vue b/packages/frontend/src/components/MkColorInput.vue
index d28bb2ae70..983a35103c 100644
--- a/packages/frontend/src/components/MkColorInput.vue
+++ b/packages/frontend/src/components/MkColorInput.vue
@@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { onMounted, nextTick, ref, shallowRef, watch, computed, toRefs } from 'vue';
-import { i18n } from '@/i18n';
+import { i18n } from '@/i18n.js';
 
 const props = defineProps<{
 	modelValue: string | null;
diff --git a/packages/frontend/src/components/MkContainer.vue b/packages/frontend/src/components/MkContainer.vue
index 4c3b8a66da..659f3a909e 100644
--- a/packages/frontend/src/components/MkContainer.vue
+++ b/packages/frontend/src/components/MkContainer.vue
@@ -40,8 +40,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { onMounted, onUnmounted, ref, shallowRef, watch } from 'vue';
-import { defaultStore } from '@/store';
-import { i18n } from '@/i18n';
+import { defaultStore } from '@/store.js';
+import { i18n } from '@/i18n.js';
 
 const props = withDefaults(defineProps<{
 	showHeader?: boolean;
diff --git a/packages/frontend/src/components/MkContextMenu.vue b/packages/frontend/src/components/MkContextMenu.vue
index 2ca04a8a3b..6cca7fc353 100644
--- a/packages/frontend/src/components/MkContextMenu.vue
+++ b/packages/frontend/src/components/MkContextMenu.vue
@@ -21,9 +21,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { onMounted, onBeforeUnmount } from 'vue';
 import MkMenu from './MkMenu.vue';
 import { MenuItem } from './types/menu.vue';
-import contains from '@/scripts/contains';
-import { defaultStore } from '@/store';
-import * as os from '@/os';
+import contains from '@/scripts/contains.js';
+import { defaultStore } from '@/store.js';
+import * as os from '@/os.js';
 
 const props = defineProps<{
 	items: MenuItem[];
diff --git a/packages/frontend/src/components/MkCropperDialog.vue b/packages/frontend/src/components/MkCropperDialog.vue
index eef2d10bf2..81f3936600 100644
--- a/packages/frontend/src/components/MkCropperDialog.vue
+++ b/packages/frontend/src/components/MkCropperDialog.vue
@@ -36,12 +36,12 @@ import * as Misskey from 'misskey-js';
 import Cropper from 'cropperjs';
 import tinycolor from 'tinycolor2';
 import MkModalWindow from '@/components/MkModalWindow.vue';
-import * as os from '@/os';
-import { $i } from '@/account';
-import { defaultStore } from '@/store';
-import { apiUrl } from '@/config';
-import { i18n } from '@/i18n';
-import { getProxiedImageUrl } from '@/scripts/media-proxy';
+import * as os from '@/os.js';
+import { $i } from '@/account.js';
+import { defaultStore } from '@/store.js';
+import { apiUrl } from '@/config.js';
+import { i18n } from '@/i18n.js';
+import { getProxiedImageUrl } from '@/scripts/media-proxy.js';
 
 const emit = defineEmits<{
 	(ev: 'ok', cropped: Misskey.entities.DriveFile): void;
diff --git a/packages/frontend/src/components/MkCwButton.vue b/packages/frontend/src/components/MkCwButton.vue
index 813498206b..54c2159356 100644
--- a/packages/frontend/src/components/MkCwButton.vue
+++ b/packages/frontend/src/components/MkCwButton.vue
@@ -13,8 +13,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { computed } from 'vue';
 import * as Misskey from 'misskey-js';
-import { concat } from '@/scripts/array';
-import { i18n } from '@/i18n';
+import { concat } from '@/scripts/array.js';
+import { i18n } from '@/i18n.js';
 
 const props = defineProps<{
 	modelValue: boolean;
diff --git a/packages/frontend/src/components/MkDateSeparatedList.vue b/packages/frontend/src/components/MkDateSeparatedList.vue
index e6690ed8a0..66d4b542be 100644
--- a/packages/frontend/src/components/MkDateSeparatedList.vue
+++ b/packages/frontend/src/components/MkDateSeparatedList.vue
@@ -7,9 +7,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { defineComponent, h, PropType, TransitionGroup, useCssModule } from 'vue';
 import MkAd from '@/components/global/MkAd.vue';
 import { isDebuggerEnabled, stackTraceInstances } from '@/debug';
-import { i18n } from '@/i18n';
-import * as os from '@/os';
-import { defaultStore } from '@/store';
+import { i18n } from '@/i18n.js';
+import * as os from '@/os.js';
+import { defaultStore } from '@/store.js';
 import { MisskeyEntity } from '@/types/date-separated-list';
 
 export default defineComponent({
diff --git a/packages/frontend/src/components/MkDialog.vue b/packages/frontend/src/components/MkDialog.vue
index 0c993bd884..a83c18c0b3 100644
--- a/packages/frontend/src/components/MkDialog.vue
+++ b/packages/frontend/src/components/MkDialog.vue
@@ -61,7 +61,7 @@ import MkModal from '@/components/MkModal.vue';
 import MkButton from '@/components/MkButton.vue';
 import MkInput from '@/components/MkInput.vue';
 import MkSelect from '@/components/MkSelect.vue';
-import { i18n } from '@/i18n';
+import { i18n } from '@/i18n.js';
 
 type Input = {
 	type: 'text' | 'number' | 'password' | 'email' | 'url' | 'date' | 'time' | 'search' | 'datetime-local';
diff --git a/packages/frontend/src/components/MkDonation.vue b/packages/frontend/src/components/MkDonation.vue
index 1552c80d6f..a2a0b6023b 100644
--- a/packages/frontend/src/components/MkDonation.vue
+++ b/packages/frontend/src/components/MkDonation.vue
@@ -38,11 +38,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import MkButton from '@/components/MkButton.vue';
 import MkLink from '@/components/MkLink.vue';
-import { host } from '@/config';
-import { i18n } from '@/i18n';
-import * as os from '@/os';
-import { miLocalStorage } from '@/local-storage';
-import { instance } from '@/instance';
+import { host } from '@/config.js';
+import { i18n } from '@/i18n.js';
+import * as os from '@/os.js';
+import { miLocalStorage } from '@/local-storage.js';
+import { instance } from '@/instance.js';
 
 const emit = defineEmits<{
 	(ev: 'closed'): void;
diff --git a/packages/frontend/src/components/MkDrive.file.vue b/packages/frontend/src/components/MkDrive.file.vue
index 7633ddd0fd..e3f96724d9 100644
--- a/packages/frontend/src/components/MkDrive.file.vue
+++ b/packages/frontend/src/components/MkDrive.file.vue
@@ -41,11 +41,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { computed, ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkDriveFileThumbnail from '@/components/MkDriveFileThumbnail.vue';
-import bytes from '@/filters/bytes';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
-import { $i } from '@/account';
-import { getDriveFileMenu } from '@/scripts/get-drive-file-menu';
+import bytes from '@/filters/bytes.js';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
+import { $i } from '@/account.js';
+import { getDriveFileMenu } from '@/scripts/get-drive-file-menu.js';
 
 const props = withDefaults(defineProps<{
 	file: Misskey.entities.DriveFile;
diff --git a/packages/frontend/src/components/MkDrive.folder.vue b/packages/frontend/src/components/MkDrive.folder.vue
index 16c987a0a2..5322664664 100644
--- a/packages/frontend/src/components/MkDrive.folder.vue
+++ b/packages/frontend/src/components/MkDrive.folder.vue
@@ -34,11 +34,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { computed, defineAsyncComponent, ref } from 'vue';
 import * as Misskey from 'misskey-js';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
-import { defaultStore } from '@/store';
-import { claimAchievement } from '@/scripts/achievements';
-import copyToClipboard from '@/scripts/copy-to-clipboard';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
+import { defaultStore } from '@/store.js';
+import { claimAchievement } from '@/scripts/achievements.js';
+import copyToClipboard from '@/scripts/copy-to-clipboard.js';
 
 const props = withDefaults(defineProps<{
 	folder: Misskey.entities.DriveFolder;
diff --git a/packages/frontend/src/components/MkDrive.navFolder.vue b/packages/frontend/src/components/MkDrive.navFolder.vue
index 69e6a0df5a..59458ad568 100644
--- a/packages/frontend/src/components/MkDrive.navFolder.vue
+++ b/packages/frontend/src/components/MkDrive.navFolder.vue
@@ -20,8 +20,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { ref } from 'vue';
 import * as Misskey from 'misskey-js';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
 
 const props = defineProps<{
 	folder?: Misskey.entities.DriveFolder;
diff --git a/packages/frontend/src/components/MkDrive.vue b/packages/frontend/src/components/MkDrive.vue
index 781e1019f7..648e4c4e3d 100644
--- a/packages/frontend/src/components/MkDrive.vue
+++ b/packages/frontend/src/components/MkDrive.vue
@@ -101,12 +101,12 @@ import MkButton from './MkButton.vue';
 import XNavFolder from '@/components/MkDrive.navFolder.vue';
 import XFolder from '@/components/MkDrive.folder.vue';
 import XFile from '@/components/MkDrive.file.vue';
-import * as os from '@/os';
-import { useStream } from '@/stream';
-import { defaultStore } from '@/store';
-import { i18n } from '@/i18n';
-import { uploadFile, uploads } from '@/scripts/upload';
-import { claimAchievement } from '@/scripts/achievements';
+import * as os from '@/os.js';
+import { useStream } from '@/stream.js';
+import { defaultStore } from '@/store.js';
+import { i18n } from '@/i18n.js';
+import { uploadFile, uploads } from '@/scripts/upload.js';
+import { claimAchievement } from '@/scripts/achievements.js';
 
 const props = withDefaults(defineProps<{
 	initialFolder?: Misskey.entities.DriveFolder;
diff --git a/packages/frontend/src/components/MkDriveSelectDialog.vue b/packages/frontend/src/components/MkDriveSelectDialog.vue
index 4d3548a87a..e65f4dd403 100644
--- a/packages/frontend/src/components/MkDriveSelectDialog.vue
+++ b/packages/frontend/src/components/MkDriveSelectDialog.vue
@@ -28,8 +28,8 @@ import { ref, shallowRef } from 'vue';
 import * as Misskey from 'misskey-js';
 import XDrive from '@/components/MkDrive.vue';
 import MkModalWindow from '@/components/MkModalWindow.vue';
-import number from '@/filters/number';
-import { i18n } from '@/i18n';
+import number from '@/filters/number.js';
+import { i18n } from '@/i18n.js';
 
 withDefaults(defineProps<{
 	type?: 'file' | 'folder';
diff --git a/packages/frontend/src/components/MkDriveWindow.vue b/packages/frontend/src/components/MkDriveWindow.vue
index f54cebd7a8..72aa79b153 100644
--- a/packages/frontend/src/components/MkDriveWindow.vue
+++ b/packages/frontend/src/components/MkDriveWindow.vue
@@ -23,7 +23,7 @@ import { } from 'vue';
 import * as Misskey from 'misskey-js';
 import XDrive from '@/components/MkDrive.vue';
 import MkWindow from '@/components/MkWindow.vue';
-import { i18n } from '@/i18n';
+import { i18n } from '@/i18n.js';
 
 defineProps<{
 	initialFolder?: Misskey.entities.DriveFolder;
diff --git a/packages/frontend/src/components/MkEmojiPicker.section.vue b/packages/frontend/src/components/MkEmojiPicker.section.vue
index 4c8c0c7902..08297ea5ba 100644
--- a/packages/frontend/src/components/MkEmojiPicker.section.vue
+++ b/packages/frontend/src/components/MkEmojiPicker.section.vue
@@ -27,7 +27,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { ref, computed, Ref } from 'vue';
-import { getEmojiName } from '@/scripts/emojilist';
+import { getEmojiName } from '@/scripts/emojilist.js';
 
 const props = defineProps<{
 	emojis: string[] | Ref<string[]>;
diff --git a/packages/frontend/src/components/MkEmojiPicker.vue b/packages/frontend/src/components/MkEmojiPicker.vue
index 87d0456931..32f440ae5d 100644
--- a/packages/frontend/src/components/MkEmojiPicker.vue
+++ b/packages/frontend/src/components/MkEmojiPicker.vue
@@ -100,15 +100,15 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { ref, shallowRef, computed, watch, onMounted } from 'vue';
 import * as Misskey from 'misskey-js';
 import XSection from '@/components/MkEmojiPicker.section.vue';
-import { emojilist, emojiCharByCategory, UnicodeEmojiDef, unicodeEmojiCategories as categories, getEmojiName } from '@/scripts/emojilist';
+import { emojilist, emojiCharByCategory, UnicodeEmojiDef, unicodeEmojiCategories as categories, getEmojiName } from '@/scripts/emojilist.js';
 import MkRippleEffect from '@/components/MkRippleEffect.vue';
-import * as os from '@/os';
-import { isTouchUsing } from '@/scripts/touch';
-import { deviceKind } from '@/scripts/device-kind';
-import { i18n } from '@/i18n';
-import { defaultStore } from '@/store';
-import { customEmojiCategories, customEmojis, customEmojisMap } from '@/custom-emojis';
-import { $i } from '@/account';
+import * as os from '@/os.js';
+import { isTouchUsing } from '@/scripts/touch.js';
+import { deviceKind } from '@/scripts/device-kind.js';
+import { i18n } from '@/i18n.js';
+import { defaultStore } from '@/store.js';
+import { customEmojiCategories, customEmojis, customEmojisMap } from '@/custom-emojis.js';
+import { $i } from '@/account.js';
 
 const props = withDefaults(defineProps<{
 	showPinned?: boolean;
diff --git a/packages/frontend/src/components/MkEmojiPickerDialog.vue b/packages/frontend/src/components/MkEmojiPickerDialog.vue
index 855d82c064..9d3132c540 100644
--- a/packages/frontend/src/components/MkEmojiPickerDialog.vue
+++ b/packages/frontend/src/components/MkEmojiPickerDialog.vue
@@ -34,7 +34,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { shallowRef } from 'vue';
 import MkModal from '@/components/MkModal.vue';
 import MkEmojiPicker from '@/components/MkEmojiPicker.vue';
-import { defaultStore } from '@/store';
+import { defaultStore } from '@/store.js';
 
 withDefaults(defineProps<{
 	manualShowing?: boolean | null;
diff --git a/packages/frontend/src/components/MkFeaturedPhotos.vue b/packages/frontend/src/components/MkFeaturedPhotos.vue
index 7f4b00c84c..cef1943d5c 100644
--- a/packages/frontend/src/components/MkFeaturedPhotos.vue
+++ b/packages/frontend/src/components/MkFeaturedPhotos.vue
@@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { ref } from 'vue';
 import * as Misskey from 'misskey-js';
-import * as os from '@/os';
+import * as os from '@/os.js';
 
 const meta = ref<Misskey.entities.DetailedInstanceMetadata>();
 
diff --git a/packages/frontend/src/components/MkFileCaptionEditWindow.vue b/packages/frontend/src/components/MkFileCaptionEditWindow.vue
index dc9a789137..28888fb9c8 100644
--- a/packages/frontend/src/components/MkFileCaptionEditWindow.vue
+++ b/packages/frontend/src/components/MkFileCaptionEditWindow.vue
@@ -30,7 +30,7 @@ import * as Misskey from 'misskey-js';
 import MkModalWindow from '@/components/MkModalWindow.vue';
 import MkTextarea from '@/components/MkTextarea.vue';
 import MkDriveFileThumbnail from '@/components/MkDriveFileThumbnail.vue';
-import { i18n } from '@/i18n';
+import { i18n } from '@/i18n.js';
 
 const props = defineProps<{
 	file: Misskey.entities.DriveFile;
diff --git a/packages/frontend/src/components/MkFileListForAdmin.vue b/packages/frontend/src/components/MkFileListForAdmin.vue
index 400da420f0..3edd30bc37 100644
--- a/packages/frontend/src/components/MkFileListForAdmin.vue
+++ b/packages/frontend/src/components/MkFileListForAdmin.vue
@@ -40,9 +40,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 import * as Misskey from 'misskey-js';
 import MkPagination from '@/components/MkPagination.vue';
 import MkDriveFileThumbnail from '@/components/MkDriveFileThumbnail.vue';
-import bytes from '@/filters/bytes';
-import { i18n } from '@/i18n';
-import { dateString } from '@/filters/date';
+import bytes from '@/filters/bytes.js';
+import { i18n } from '@/i18n.js';
+import { dateString } from '@/filters/date.js';
 
 const props = defineProps<{
 	pagination: any;
diff --git a/packages/frontend/src/components/MkFlashPreview.vue b/packages/frontend/src/components/MkFlashPreview.vue
index f99f91d89a..ab435585d9 100644
--- a/packages/frontend/src/components/MkFlashPreview.vue
+++ b/packages/frontend/src/components/MkFlashPreview.vue
@@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { } from 'vue';
-import { userName } from '@/filters/user';
+import { userName } from '@/filters/user.js';
 
 const props = defineProps<{
 	//flash: Misskey.entities.Flash;
diff --git a/packages/frontend/src/components/MkFoldableSection.vue b/packages/frontend/src/components/MkFoldableSection.vue
index da58586e24..ed3cb0868b 100644
--- a/packages/frontend/src/components/MkFoldableSection.vue
+++ b/packages/frontend/src/components/MkFoldableSection.vue
@@ -30,8 +30,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { onMounted, ref, shallowRef, watch } from 'vue';
 import tinycolor from 'tinycolor2';
-import { miLocalStorage } from '@/local-storage';
-import { defaultStore } from '@/store';
+import { miLocalStorage } from '@/local-storage.js';
+import { defaultStore } from '@/store.js';
 
 const miLocalStoragePrefix = 'ui:folder:' as const;
 
diff --git a/packages/frontend/src/components/MkFolder.vue b/packages/frontend/src/components/MkFolder.vue
index 4610261111..60ecc13056 100644
--- a/packages/frontend/src/components/MkFolder.vue
+++ b/packages/frontend/src/components/MkFolder.vue
@@ -51,7 +51,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { nextTick, onMounted } from 'vue';
-import { defaultStore } from '@/store';
+import { defaultStore } from '@/store.js';
 
 const props = withDefaults(defineProps<{
 	defaultOpen?: boolean;
diff --git a/packages/frontend/src/components/MkFollowButton.vue b/packages/frontend/src/components/MkFollowButton.vue
index a9f4dc9d35..15043fcd0b 100644
--- a/packages/frontend/src/components/MkFollowButton.vue
+++ b/packages/frontend/src/components/MkFollowButton.vue
@@ -37,11 +37,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { onBeforeUnmount, onMounted } from 'vue';
 import * as Misskey from 'misskey-js';
-import * as os from '@/os';
-import { useStream } from '@/stream';
-import { i18n } from '@/i18n';
-import { claimAchievement } from '@/scripts/achievements';
-import { $i } from '@/account';
+import * as os from '@/os.js';
+import { useStream } from '@/stream.js';
+import { i18n } from '@/i18n.js';
+import { claimAchievement } from '@/scripts/achievements.js';
+import { $i } from '@/account.js';
 
 const props = withDefaults(defineProps<{
 	user: Misskey.entities.UserDetailed,
diff --git a/packages/frontend/src/components/MkForgotPassword.vue b/packages/frontend/src/components/MkForgotPassword.vue
index a548089011..521ac11d12 100644
--- a/packages/frontend/src/components/MkForgotPassword.vue
+++ b/packages/frontend/src/components/MkForgotPassword.vue
@@ -44,9 +44,9 @@ import MkModalWindow from '@/components/MkModalWindow.vue';
 import MkButton from '@/components/MkButton.vue';
 import MkInput from '@/components/MkInput.vue';
 import MkInfo from '@/components/MkInfo.vue';
-import * as os from '@/os';
-import { instance } from '@/instance';
-import { i18n } from '@/i18n';
+import * as os from '@/os.js';
+import { instance } from '@/instance.js';
+import { i18n } from '@/i18n.js';
 
 const emit = defineEmits<{
 	(ev: 'done'): void;
diff --git a/packages/frontend/src/components/MkFormDialog.vue b/packages/frontend/src/components/MkFormDialog.vue
index b8cc3d38d8..24404728ca 100644
--- a/packages/frontend/src/components/MkFormDialog.vue
+++ b/packages/frontend/src/components/MkFormDialog.vue
@@ -69,7 +69,7 @@ import MkRange from './MkRange.vue';
 import MkButton from './MkButton.vue';
 import MkRadios from './MkRadios.vue';
 import MkModalWindow from '@/components/MkModalWindow.vue';
-import { i18n } from '@/i18n';
+import { i18n } from '@/i18n.js';
 
 const props = defineProps<{
 	title: string;
diff --git a/packages/frontend/src/components/MkGalleryPostPreview.vue b/packages/frontend/src/components/MkGalleryPostPreview.vue
index 5ac64aae31..316632b1a6 100644
--- a/packages/frontend/src/components/MkGalleryPostPreview.vue
+++ b/packages/frontend/src/components/MkGalleryPostPreview.vue
@@ -35,7 +35,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 import * as Misskey from 'misskey-js';
 import { computed, ref } from 'vue';
 import ImgWithBlurhash from '@/components/MkImgWithBlurhash.vue';
-import { defaultStore } from '@/store';
+import { defaultStore } from '@/store.js';
 
 const props = defineProps<{
 	post: Misskey.entities.GalleryPost;
diff --git a/packages/frontend/src/components/MkGoogle.vue b/packages/frontend/src/components/MkGoogle.vue
index 3f661b3236..b899656e4f 100644
--- a/packages/frontend/src/components/MkGoogle.vue
+++ b/packages/frontend/src/components/MkGoogle.vue
@@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { ref } from 'vue';
-import { i18n } from '@/i18n';
+import { i18n } from '@/i18n.js';
 
 const props = defineProps<{
 	q: string;
diff --git a/packages/frontend/src/components/MkHeatmap.vue b/packages/frontend/src/components/MkHeatmap.vue
index 9748d409fd..0022531e58 100644
--- a/packages/frontend/src/components/MkHeatmap.vue
+++ b/packages/frontend/src/components/MkHeatmap.vue
@@ -15,11 +15,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { onMounted, nextTick, watch } from 'vue';
 import { Chart } from 'chart.js';
-import * as os from '@/os';
-import { defaultStore } from '@/store';
-import { useChartTooltip } from '@/scripts/use-chart-tooltip';
-import { alpha } from '@/scripts/color';
-import { initChart } from '@/scripts/init-chart';
+import * as os from '@/os.js';
+import { defaultStore } from '@/store.js';
+import { useChartTooltip } from '@/scripts/use-chart-tooltip.js';
+import { alpha } from '@/scripts/color.js';
+import { initChart } from '@/scripts/init-chart.js';
 
 initChart();
 
diff --git a/packages/frontend/src/components/MkImgWithBlurhash.vue b/packages/frontend/src/components/MkImgWithBlurhash.vue
index b3e04c7e4d..4fb573fdbc 100644
--- a/packages/frontend/src/components/MkImgWithBlurhash.vue
+++ b/packages/frontend/src/components/MkImgWithBlurhash.vue
@@ -24,8 +24,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { $ref } from 'vue/macros';
 import DrawBlurhash from '@/workers/draw-blurhash?worker';
 import TestWebGL2 from '@/workers/test-webgl2?worker';
-import { WorkerMultiDispatch } from '@/scripts/worker-multi-dispatch';
-import { extractAvgColorFromBlurhash } from '@/scripts/extract-avg-color-from-blurhash';
+import { WorkerMultiDispatch } from '@/scripts/worker-multi-dispatch.js';
+import { extractAvgColorFromBlurhash } from '@/scripts/extract-avg-color-from-blurhash.js';
 
 const canvasPromise = new Promise<WorkerMultiDispatch | HTMLCanvasElement>(resolve => {
 	// テスト環境で Web Worker インスタンスは作成できない
@@ -61,7 +61,7 @@ const canvasPromise = new Promise<WorkerMultiDispatch | HTMLCanvasElement>(resol
 import { computed, nextTick, onMounted, onUnmounted, shallowRef, watch } from 'vue';
 import { v4 as uuid } from 'uuid';
 import { render } from 'buraha';
-import { defaultStore } from '@/store';
+import { defaultStore } from '@/store.js';
 
 const props = withDefaults(defineProps<{
 	transition?: {
diff --git a/packages/frontend/src/components/MkInput.vue b/packages/frontend/src/components/MkInput.vue
index 5ca9dfcd9c..e9397ce86f 100644
--- a/packages/frontend/src/components/MkInput.vue
+++ b/packages/frontend/src/components/MkInput.vue
@@ -43,8 +43,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { onMounted, nextTick, ref, shallowRef, watch, computed, toRefs } from 'vue';
 import { debounce } from 'throttle-debounce';
 import MkButton from '@/components/MkButton.vue';
-import { useInterval } from '@/scripts/use-interval';
-import { i18n } from '@/i18n';
+import { useInterval } from '@/scripts/use-interval.js';
+import { i18n } from '@/i18n.js';
 
 const props = defineProps<{
 	modelValue: string | number | null;
diff --git a/packages/frontend/src/components/MkInstanceCardMini.vue b/packages/frontend/src/components/MkInstanceCardMini.vue
index ed8d466c07..de726e3aa4 100644
--- a/packages/frontend/src/components/MkInstanceCardMini.vue
+++ b/packages/frontend/src/components/MkInstanceCardMini.vue
@@ -17,8 +17,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import * as Misskey from 'misskey-js';
 import MkMiniChart from '@/components/MkMiniChart.vue';
-import * as os from '@/os';
-import { getProxiedImageUrlNullable } from '@/scripts/media-proxy';
+import * as os from '@/os.js';
+import { getProxiedImageUrlNullable } from '@/scripts/media-proxy.js';
 
 const props = defineProps<{
 	instance: Misskey.entities.Instance;
diff --git a/packages/frontend/src/components/MkInstanceStats.vue b/packages/frontend/src/components/MkInstanceStats.vue
index 9ecf114e1a..509254de74 100644
--- a/packages/frontend/src/components/MkInstanceStats.vue
+++ b/packages/frontend/src/components/MkInstanceStats.vue
@@ -88,14 +88,14 @@ import { onMounted } from 'vue';
 import { Chart } from 'chart.js';
 import MkSelect from '@/components/MkSelect.vue';
 import MkChart from '@/components/MkChart.vue';
-import { useChartTooltip } from '@/scripts/use-chart-tooltip';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
+import { useChartTooltip } from '@/scripts/use-chart-tooltip.js';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
 import MkHeatmap from '@/components/MkHeatmap.vue';
 import MkFoldableSection from '@/components/MkFoldableSection.vue';
 import MkRetentionHeatmap from '@/components/MkRetentionHeatmap.vue';
 import MkRetentionLineChart from '@/components/MkRetentionLineChart.vue';
-import { initChart } from '@/scripts/init-chart';
+import { initChart } from '@/scripts/init-chart.js';
 
 initChart();
 
diff --git a/packages/frontend/src/components/MkInstanceTicker.vue b/packages/frontend/src/components/MkInstanceTicker.vue
index afa0004cc7..1a4f2bfbb9 100644
--- a/packages/frontend/src/components/MkInstanceTicker.vue
+++ b/packages/frontend/src/components/MkInstanceTicker.vue
@@ -12,9 +12,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { } from 'vue';
-import { instanceName } from '@/config';
-import { instance as Instance } from '@/instance';
-import { getProxiedImageUrlNullable } from '@/scripts/media-proxy';
+import { instanceName } from '@/config.js';
+import { instance as Instance } from '@/instance.js';
+import { getProxiedImageUrlNullable } from '@/scripts/media-proxy.js';
 
 const props = defineProps<{
 	instance?: {
diff --git a/packages/frontend/src/components/MkInviteCode.vue b/packages/frontend/src/components/MkInviteCode.vue
index e529ff4f91..ff3794ad18 100644
--- a/packages/frontend/src/components/MkInviteCode.vue
+++ b/packages/frontend/src/components/MkInviteCode.vue
@@ -62,9 +62,9 @@ import { computed } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkFolder from '@/components/MkFolder.vue';
 import MkButton from '@/components/MkButton.vue';
-import copyToClipboard from '@/scripts/copy-to-clipboard';
-import { i18n } from '@/i18n';
-import * as os from '@/os';
+import copyToClipboard from '@/scripts/copy-to-clipboard.js';
+import { i18n } from '@/i18n.js';
+import * as os from '@/os.js';
 
 const props = defineProps<{
 	invite: Misskey.entities.Invite;
diff --git a/packages/frontend/src/components/MkKeyValue.vue b/packages/frontend/src/components/MkKeyValue.vue
index 41e756de03..4f1e4df040 100644
--- a/packages/frontend/src/components/MkKeyValue.vue
+++ b/packages/frontend/src/components/MkKeyValue.vue
@@ -17,9 +17,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { } from 'vue';
-import copyToClipboard from '@/scripts/copy-to-clipboard';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
+import copyToClipboard from '@/scripts/copy-to-clipboard.js';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
 
 const props = withDefaults(defineProps<{
 	copy?: string | null;
diff --git a/packages/frontend/src/components/MkLaunchPad.vue b/packages/frontend/src/components/MkLaunchPad.vue
index de38fad7b2..321acc0fbc 100644
--- a/packages/frontend/src/components/MkLaunchPad.vue
+++ b/packages/frontend/src/components/MkLaunchPad.vue
@@ -28,8 +28,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { } from 'vue';
 import MkModal from '@/components/MkModal.vue';
 import { navbarItemDef } from '@/navbar';
-import { defaultStore } from '@/store';
-import { deviceKind } from '@/scripts/device-kind';
+import { defaultStore } from '@/store.js';
+import { deviceKind } from '@/scripts/device-kind.js';
 
 const props = withDefaults(defineProps<{
 	src?: HTMLElement;
diff --git a/packages/frontend/src/components/MkLink.vue b/packages/frontend/src/components/MkLink.vue
index 8b77ee8bfd..0501d797f8 100644
--- a/packages/frontend/src/components/MkLink.vue
+++ b/packages/frontend/src/components/MkLink.vue
@@ -15,9 +15,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { defineAsyncComponent } from 'vue';
-import { url as local } from '@/config';
-import { useTooltip } from '@/scripts/use-tooltip';
-import * as os from '@/os';
+import { url as local } from '@/config.js';
+import { useTooltip } from '@/scripts/use-tooltip.js';
+import * as os from '@/os.js';
 
 const props = withDefaults(defineProps<{
 	url: string;
diff --git a/packages/frontend/src/components/MkMediaBanner.vue b/packages/frontend/src/components/MkMediaBanner.vue
index e0761a9dfe..10b2ac9ece 100644
--- a/packages/frontend/src/components/MkMediaBanner.vue
+++ b/packages/frontend/src/components/MkMediaBanner.vue
@@ -35,8 +35,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { onMounted } from 'vue';
 import * as Misskey from 'misskey-js';
-import { soundConfigStore } from '@/scripts/sound';
-import { i18n } from '@/i18n';
+import { soundConfigStore } from '@/scripts/sound.js';
+import { i18n } from '@/i18n.js';
 
 const props = withDefaults(defineProps<{
 	media: Misskey.entities.DriveFile;
diff --git a/packages/frontend/src/components/MkMediaImage.vue b/packages/frontend/src/components/MkMediaImage.vue
index 346d76301c..c2d442f59f 100644
--- a/packages/frontend/src/components/MkMediaImage.vue
+++ b/packages/frontend/src/components/MkMediaImage.vue
@@ -53,13 +53,13 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { watch } from 'vue';
 import * as Misskey from 'misskey-js';
-import { getStaticImageUrl } from '@/scripts/media-proxy';
-import bytes from '@/filters/bytes';
+import { getStaticImageUrl } from '@/scripts/media-proxy.js';
+import bytes from '@/filters/bytes.js';
 import ImgWithBlurhash from '@/components/MkImgWithBlurhash.vue';
-import { defaultStore } from '@/store';
-import { i18n } from '@/i18n';
-import * as os from '@/os';
-import { iAmModerator } from '@/account';
+import { defaultStore } from '@/store.js';
+import { i18n } from '@/i18n.js';
+import * as os from '@/os.js';
+import { iAmModerator } from '@/account.js';
 
 const props = withDefaults(defineProps<{
 	image: Misskey.entities.DriveFile;
diff --git a/packages/frontend/src/components/MkMediaList.vue b/packages/frontend/src/components/MkMediaList.vue
index 4c1017ea8f..f134f2021d 100644
--- a/packages/frontend/src/components/MkMediaList.vue
+++ b/packages/frontend/src/components/MkMediaList.vue
@@ -71,10 +71,10 @@ import 'photoswipe/style.css';
 import XBanner from '@/components/MkMediaBanner.vue';
 import XImage from '@/components/MkMediaImage.vue';
 import XVideo from '@/components/MkMediaVideo.vue';
-import * as os from '@/os';
+import * as os from '@/os.js';
 import { FILE_TYPE_BROWSERSAFE } from '@/const';
-import { defaultStore } from '@/store';
-import { getScrollContainer, getBodyScrollHeight } from '@/scripts/scroll';
+import { defaultStore } from '@/store.js';
+import { getScrollContainer, getBodyScrollHeight } from '@/scripts/scroll.js';
 
 const props = defineProps<{
 	mediaList: Misskey.entities.DriveFile[];
diff --git a/packages/frontend/src/components/MkMediaVideo.vue b/packages/frontend/src/components/MkMediaVideo.vue
index 28a957436c..c5afe09745 100644
--- a/packages/frontend/src/components/MkMediaVideo.vue
+++ b/packages/frontend/src/components/MkMediaVideo.vue
@@ -33,9 +33,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { ref } from 'vue';
 import * as Misskey from 'misskey-js';
-import bytes from '@/filters/bytes';
-import { defaultStore } from '@/store';
-import { i18n } from '@/i18n';
+import bytes from '@/filters/bytes.js';
+import { defaultStore } from '@/store.js';
+import { i18n } from '@/i18n.js';
 
 const props = defineProps<{
 	video: Misskey.entities.DriveFile;
diff --git a/packages/frontend/src/components/MkMention.vue b/packages/frontend/src/components/MkMention.vue
index 09f50ce8cf..ffc9ca3f4d 100644
--- a/packages/frontend/src/components/MkMention.vue
+++ b/packages/frontend/src/components/MkMention.vue
@@ -17,9 +17,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { toUnicode } from 'punycode';
 import { } from 'vue';
 import tinycolor from 'tinycolor2';
-import { host as localHost } from '@/config';
-import { $i } from '@/account';
-import { defaultStore } from '@/store';
+import { host as localHost } from '@/config.js';
+import { $i } from '@/account.js';
+import { defaultStore } from '@/store.js';
 
 const props = defineProps<{
 	username: string;
diff --git a/packages/frontend/src/components/MkMenu.vue b/packages/frontend/src/components/MkMenu.vue
index 3b20856e12..ec8383fe84 100644
--- a/packages/frontend/src/components/MkMenu.vue
+++ b/packages/frontend/src/components/MkMenu.vue
@@ -63,12 +63,12 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts">
 import { Ref, defineAsyncComponent, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue';
-import { focusPrev, focusNext } from '@/scripts/focus';
+import { focusPrev, focusNext } from '@/scripts/focus.js';
 import MkSwitchButton from '@/components/MkSwitch.button.vue';
 import { MenuItem, InnerMenuItem, MenuPending, MenuAction, MenuSwitch, MenuParent } from '@/types/menu';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
-import { isTouchUsing } from '@/scripts/touch';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
+import { isTouchUsing } from '@/scripts/touch.js';
 
 const childrenCache = new WeakMap<MenuParent, MenuItem[]>();
 </script>
diff --git a/packages/frontend/src/components/MkMiniChart.vue b/packages/frontend/src/components/MkMiniChart.vue
index eee1e10399..8d2a147306 100644
--- a/packages/frontend/src/components/MkMiniChart.vue
+++ b/packages/frontend/src/components/MkMiniChart.vue
@@ -34,7 +34,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { watch } from 'vue';
 import { v4 as uuid } from 'uuid';
 import tinycolor from 'tinycolor2';
-import { useInterval } from '@/scripts/use-interval';
+import { useInterval } from '@/scripts/use-interval.js';
 
 const props = defineProps<{
 	src: number[];
diff --git a/packages/frontend/src/components/MkModal.vue b/packages/frontend/src/components/MkModal.vue
index 4ba078d4eb..ec5039c504 100644
--- a/packages/frontend/src/components/MkModal.vue
+++ b/packages/frontend/src/components/MkModal.vue
@@ -43,10 +43,10 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { nextTick, normalizeClass, onMounted, onUnmounted, provide, watch } from 'vue';
-import * as os from '@/os';
-import { isTouchUsing } from '@/scripts/touch';
-import { defaultStore } from '@/store';
-import { deviceKind } from '@/scripts/device-kind';
+import * as os from '@/os.js';
+import { isTouchUsing } from '@/scripts/touch.js';
+import { defaultStore } from '@/store.js';
+import { deviceKind } from '@/scripts/device-kind.js';
 
 function getFixedContainer(el: Element | null): Element | null {
 	if (el == null || el.tagName === 'BODY') return null;
diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue
index fdf22c5995..0bc94f5bd2 100644
--- a/packages/frontend/src/components/MkNote.vue
+++ b/packages/frontend/src/components/MkNote.vue
@@ -149,26 +149,26 @@ import MkPoll from '@/components/MkPoll.vue';
 import MkUsersTooltip from '@/components/MkUsersTooltip.vue';
 import MkUrlPreview from '@/components/MkUrlPreview.vue';
 import MkInstanceTicker from '@/components/MkInstanceTicker.vue';
-import { pleaseLogin } from '@/scripts/please-login';
-import { focusPrev, focusNext } from '@/scripts/focus';
-import { checkWordMute } from '@/scripts/check-word-mute';
-import { userPage } from '@/filters/user';
-import * as os from '@/os';
-import { defaultStore, noteViewInterruptors } from '@/store';
-import { reactionPicker } from '@/scripts/reaction-picker';
-import { extractUrlFromMfm } from '@/scripts/extract-url-from-mfm';
-import { $i } from '@/account';
-import { i18n } from '@/i18n';
-import { getAbuseNoteMenu, getCopyNoteLinkMenu, getNoteClipMenu, getNoteMenu } from '@/scripts/get-note-menu';
-import { useNoteCapture } from '@/scripts/use-note-capture';
-import { deepClone } from '@/scripts/clone';
-import { useTooltip } from '@/scripts/use-tooltip';
-import { claimAchievement } from '@/scripts/achievements';
-import { getNoteSummary } from '@/scripts/get-note-summary';
+import { pleaseLogin } from '@/scripts/please-login.js';
+import { focusPrev, focusNext } from '@/scripts/focus.js';
+import { checkWordMute } from '@/scripts/check-word-mute.js';
+import { userPage } from '@/filters/user.js';
+import * as os from '@/os.js';
+import { defaultStore, noteViewInterruptors } from '@/store.js';
+import { reactionPicker } from '@/scripts/reaction-picker.js';
+import { extractUrlFromMfm } from '@/scripts/extract-url-from-mfm.js';
+import { $i } from '@/account.js';
+import { i18n } from '@/i18n.js';
+import { getAbuseNoteMenu, getCopyNoteLinkMenu, getNoteClipMenu, getNoteMenu } from '@/scripts/get-note-menu.js';
+import { useNoteCapture } from '@/scripts/use-note-capture.js';
+import { deepClone } from '@/scripts/clone.js';
+import { useTooltip } from '@/scripts/use-tooltip.js';
+import { claimAchievement } from '@/scripts/achievements.js';
+import { getNoteSummary } from '@/scripts/get-note-summary.js';
 import { MenuItem } from '@/types/menu';
 import MkRippleEffect from '@/components/MkRippleEffect.vue';
-import { showMovedDialog } from '@/scripts/show-moved-dialog';
-import { shouldCollapsed } from '@/scripts/collapsed';
+import { showMovedDialog } from '@/scripts/show-moved-dialog.js';
+import { shouldCollapsed } from '@/scripts/collapsed.js';
 
 const props = defineProps<{
 	note: Misskey.entities.Note;
diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue
index 1e3374a484..0bcf98cca2 100644
--- a/packages/frontend/src/components/MkNoteDetailed.vue
+++ b/packages/frontend/src/components/MkNoteDetailed.vue
@@ -192,24 +192,24 @@ import MkPoll from '@/components/MkPoll.vue';
 import MkUsersTooltip from '@/components/MkUsersTooltip.vue';
 import MkUrlPreview from '@/components/MkUrlPreview.vue';
 import MkInstanceTicker from '@/components/MkInstanceTicker.vue';
-import { pleaseLogin } from '@/scripts/please-login';
-import { checkWordMute } from '@/scripts/check-word-mute';
-import { userPage } from '@/filters/user';
-import { notePage } from '@/filters/note';
-import * as os from '@/os';
-import { defaultStore, noteViewInterruptors } from '@/store';
-import { reactionPicker } from '@/scripts/reaction-picker';
-import { extractUrlFromMfm } from '@/scripts/extract-url-from-mfm';
-import { $i } from '@/account';
-import { i18n } from '@/i18n';
-import { getNoteClipMenu, getNoteMenu } from '@/scripts/get-note-menu';
-import { useNoteCapture } from '@/scripts/use-note-capture';
-import { deepClone } from '@/scripts/clone';
-import { useTooltip } from '@/scripts/use-tooltip';
-import { claimAchievement } from '@/scripts/achievements';
+import { pleaseLogin } from '@/scripts/please-login.js';
+import { checkWordMute } from '@/scripts/check-word-mute.js';
+import { userPage } from '@/filters/user.js';
+import { notePage } from '@/filters/note.js';
+import * as os from '@/os.js';
+import { defaultStore, noteViewInterruptors } from '@/store.js';
+import { reactionPicker } from '@/scripts/reaction-picker.js';
+import { extractUrlFromMfm } from '@/scripts/extract-url-from-mfm.js';
+import { $i } from '@/account.js';
+import { i18n } from '@/i18n.js';
+import { getNoteClipMenu, getNoteMenu } from '@/scripts/get-note-menu.js';
+import { useNoteCapture } from '@/scripts/use-note-capture.js';
+import { deepClone } from '@/scripts/clone.js';
+import { useTooltip } from '@/scripts/use-tooltip.js';
+import { claimAchievement } from '@/scripts/achievements.js';
 import { MenuItem } from '@/types/menu';
 import MkRippleEffect from '@/components/MkRippleEffect.vue';
-import { showMovedDialog } from '@/scripts/show-moved-dialog';
+import { showMovedDialog } from '@/scripts/show-moved-dialog.js';
 import MkUserCardMini from '@/components/MkUserCardMini.vue';
 import MkPagination, { Paging } from '@/components/MkPagination.vue';
 import MkReactionIcon from '@/components/MkReactionIcon.vue';
diff --git a/packages/frontend/src/components/MkNoteHeader.vue b/packages/frontend/src/components/MkNoteHeader.vue
index 81d0c1f9c2..dda7238d27 100644
--- a/packages/frontend/src/components/MkNoteHeader.vue
+++ b/packages/frontend/src/components/MkNoteHeader.vue
@@ -31,9 +31,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { } from 'vue';
 import * as Misskey from 'misskey-js';
-import { i18n } from '@/i18n';
-import { notePage } from '@/filters/note';
-import { userPage } from '@/filters/user';
+import { i18n } from '@/i18n.js';
+import { notePage } from '@/filters/note.js';
+import { userPage } from '@/filters/user.js';
 
 defineProps<{
 	note: Misskey.entities.Note;
diff --git a/packages/frontend/src/components/MkNotePreview.vue b/packages/frontend/src/components/MkNotePreview.vue
index 3cc84283c0..fc6ea89085 100644
--- a/packages/frontend/src/components/MkNotePreview.vue
+++ b/packages/frontend/src/components/MkNotePreview.vue
@@ -21,7 +21,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { } from 'vue';
-import { $i } from '@/account';
+import { $i } from '@/account.js';
 
 const props = defineProps<{
 	text: string;
diff --git a/packages/frontend/src/components/MkNoteSimple.vue b/packages/frontend/src/components/MkNoteSimple.vue
index a27f6bbf64..f8ef3f3fa6 100644
--- a/packages/frontend/src/components/MkNoteSimple.vue
+++ b/packages/frontend/src/components/MkNoteSimple.vue
@@ -27,7 +27,7 @@ import * as Misskey from 'misskey-js';
 import MkNoteHeader from '@/components/MkNoteHeader.vue';
 import MkSubNoteContent from '@/components/MkSubNoteContent.vue';
 import MkCwButton from '@/components/MkCwButton.vue';
-import { $i } from '@/account';
+import { $i } from '@/account.js';
 
 const props = defineProps<{
 	note: Misskey.entities.Note;
diff --git a/packages/frontend/src/components/MkNoteSub.vue b/packages/frontend/src/components/MkNoteSub.vue
index cdf2d70c3e..2a3cd9bf02 100644
--- a/packages/frontend/src/components/MkNoteSub.vue
+++ b/packages/frontend/src/components/MkNoteSub.vue
@@ -45,10 +45,10 @@ import * as Misskey from 'misskey-js';
 import MkNoteHeader from '@/components/MkNoteHeader.vue';
 import MkSubNoteContent from '@/components/MkSubNoteContent.vue';
 import MkCwButton from '@/components/MkCwButton.vue';
-import { notePage } from '@/filters/note';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
-import { $i } from '@/account';
+import { notePage } from '@/filters/note.js';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
+import { $i } from '@/account.js';
 import { userPage } from "@/filters/user";
 import { checkWordMute } from "@/scripts/check-word-mute";
 import { defaultStore } from "@/store";
diff --git a/packages/frontend/src/components/MkNotes.vue b/packages/frontend/src/components/MkNotes.vue
index ee50e32e31..e57698a824 100644
--- a/packages/frontend/src/components/MkNotes.vue
+++ b/packages/frontend/src/components/MkNotes.vue
@@ -36,8 +36,8 @@ import { shallowRef } from 'vue';
 import MkNote from '@/components/MkNote.vue';
 import MkDateSeparatedList from '@/components/MkDateSeparatedList.vue';
 import MkPagination, { Paging } from '@/components/MkPagination.vue';
-import { i18n } from '@/i18n';
-import { infoImageUrl } from '@/instance';
+import { i18n } from '@/i18n.js';
+import { infoImageUrl } from '@/instance.js';
 
 const props = defineProps<{
 	pagination: Paging;
diff --git a/packages/frontend/src/components/MkNotification.vue b/packages/frontend/src/components/MkNotification.vue
index ea2b6c1d4b..f71834b099 100644
--- a/packages/frontend/src/components/MkNotification.vue
+++ b/packages/frontend/src/components/MkNotification.vue
@@ -109,14 +109,14 @@ import MkReactionIcon from '@/components/MkReactionIcon.vue';
 import MkFollowButton from '@/components/MkFollowButton.vue';
 import XReactionTooltip from '@/components/MkReactionTooltip.vue';
 import MkButton from '@/components/MkButton.vue';
-import { getNoteSummary } from '@/scripts/get-note-summary';
-import { notePage } from '@/filters/note';
-import { userPage } from '@/filters/user';
-import { i18n } from '@/i18n';
-import * as os from '@/os';
-import { useTooltip } from '@/scripts/use-tooltip';
-import { $i } from '@/account';
-import { infoImageUrl } from '@/instance';
+import { getNoteSummary } from '@/scripts/get-note-summary.js';
+import { notePage } from '@/filters/note.js';
+import { userPage } from '@/filters/user.js';
+import { i18n } from '@/i18n.js';
+import * as os from '@/os.js';
+import { useTooltip } from '@/scripts/use-tooltip.js';
+import { $i } from '@/account.js';
+import { infoImageUrl } from '@/instance.js';
 
 const props = withDefaults(defineProps<{
 	notification: Misskey.entities.Notification;
diff --git a/packages/frontend/src/components/MkNotificationSettingWindow.vue b/packages/frontend/src/components/MkNotificationSettingWindow.vue
index ae206a2b65..a25914b214 100644
--- a/packages/frontend/src/components/MkNotificationSettingWindow.vue
+++ b/packages/frontend/src/components/MkNotificationSettingWindow.vue
@@ -44,7 +44,7 @@ import MkInfo from './MkInfo.vue';
 import MkButton from './MkButton.vue';
 import MkModalWindow from '@/components/MkModalWindow.vue';
 import { notificationTypes } from '@/const';
-import { i18n } from '@/i18n';
+import { i18n } from '@/i18n.js';
 
 type TypesMap = Record<typeof notificationTypes[number], Ref<boolean>>
 
diff --git a/packages/frontend/src/components/MkNotifications.vue b/packages/frontend/src/components/MkNotifications.vue
index cc7657ba97..ad1cb92ce1 100644
--- a/packages/frontend/src/components/MkNotifications.vue
+++ b/packages/frontend/src/components/MkNotifications.vue
@@ -27,11 +27,11 @@ import MkPagination, { Paging } from '@/components/MkPagination.vue';
 import XNotification from '@/components/MkNotification.vue';
 import MkDateSeparatedList from '@/components/MkDateSeparatedList.vue';
 import MkNote from '@/components/MkNote.vue';
-import { useStream } from '@/stream';
-import { $i } from '@/account';
-import { i18n } from '@/i18n';
+import { useStream } from '@/stream.js';
+import { $i } from '@/account.js';
+import { i18n } from '@/i18n.js';
 import { notificationTypes } from '@/const';
-import { infoImageUrl } from '@/instance';
+import { infoImageUrl } from '@/instance.js';
 
 const props = defineProps<{
 	includeTypes?: typeof notificationTypes[number][];
diff --git a/packages/frontend/src/components/MkNumber.vue b/packages/frontend/src/components/MkNumber.vue
index 0f657cc70f..aa04ab253b 100644
--- a/packages/frontend/src/components/MkNumber.vue
+++ b/packages/frontend/src/components/MkNumber.vue
@@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { reactive, watch } from 'vue';
 import gsap from 'gsap';
-import number from '@/filters/number';
+import number from '@/filters/number.js';
 
 const props = defineProps<{
 	value: number;
diff --git a/packages/frontend/src/components/MkNumberDiff.vue b/packages/frontend/src/components/MkNumberDiff.vue
index fe3db465ff..a98b6c4713 100644
--- a/packages/frontend/src/components/MkNumberDiff.vue
+++ b/packages/frontend/src/components/MkNumberDiff.vue
@@ -11,7 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { computed } from 'vue';
-import number from '@/filters/number';
+import number from '@/filters/number.js';
 
 const props = defineProps<{
 	value: number;
diff --git a/packages/frontend/src/components/MkObjectView.value.vue b/packages/frontend/src/components/MkObjectView.value.vue
index 1459534d42..aa05c43c0b 100644
--- a/packages/frontend/src/components/MkObjectView.value.vue
+++ b/packages/frontend/src/components/MkObjectView.value.vue
@@ -35,7 +35,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { reactive } from 'vue';
-import number from '@/filters/number';
+import number from '@/filters/number.js';
 import XValue from '@/components/MkObjectView.value.vue';
 
 const props = defineProps<{
diff --git a/packages/frontend/src/components/MkOmit.vue b/packages/frontend/src/components/MkOmit.vue
index 3197e9b769..8c113bd777 100644
--- a/packages/frontend/src/components/MkOmit.vue
+++ b/packages/frontend/src/components/MkOmit.vue
@@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { onMounted, onUnmounted } from 'vue';
-import { i18n } from '@/i18n';
+import { i18n } from '@/i18n.js';
 
 const props = withDefaults(defineProps<{
 	maxHeight?: number;
diff --git a/packages/frontend/src/components/MkPagePreview.vue b/packages/frontend/src/components/MkPagePreview.vue
index 65464956be..05b577c49c 100644
--- a/packages/frontend/src/components/MkPagePreview.vue
+++ b/packages/frontend/src/components/MkPagePreview.vue
@@ -30,7 +30,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { } from 'vue';
 import * as Misskey from 'misskey-js';
-import { userName } from '@/filters/user';
+import { userName } from '@/filters/user.js';
 import MediaImage from '@/components/MkMediaImage.vue';
 
 const props = defineProps<{
diff --git a/packages/frontend/src/components/MkPageWindow.vue b/packages/frontend/src/components/MkPageWindow.vue
index b4941f0b09..3b273ac545 100644
--- a/packages/frontend/src/components/MkPageWindow.vue
+++ b/packages/frontend/src/components/MkPageWindow.vue
@@ -32,17 +32,17 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { ComputedRef, onMounted, onUnmounted, provide, shallowRef } from 'vue';
 import RouterView from '@/components/global/RouterView.vue';
 import MkWindow from '@/components/MkWindow.vue';
-import { popout as _popout } from '@/scripts/popout';
-import copyToClipboard from '@/scripts/copy-to-clipboard';
-import { url } from '@/config';
-import { mainRouter, routes, page } from '@/router';
-import { $i } from '@/account';
+import { popout as _popout } from '@/scripts/popout.js';
+import copyToClipboard from '@/scripts/copy-to-clipboard.js';
+import { url } from '@/config.js';
+import { mainRouter, routes, page } from '@/router.js';
+import { $i } from '@/account.js';
 import { Router, useScrollPositionManager } from '@/nirax';
-import { i18n } from '@/i18n';
-import { PageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata';
-import { openingWindowsCount } from '@/os';
-import { claimAchievement } from '@/scripts/achievements';
-import { getScrollContainer } from '@/scripts/scroll';
+import { i18n } from '@/i18n.js';
+import { PageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata.js';
+import { openingWindowsCount } from '@/os.js';
+import { claimAchievement } from '@/scripts/achievements.js';
+import { getScrollContainer } from '@/scripts/scroll.js';
 
 const props = defineProps<{
 	initialPath: string;
diff --git a/packages/frontend/src/components/MkPagination.vue b/packages/frontend/src/components/MkPagination.vue
index 1a770fbd19..5a87273386 100644
--- a/packages/frontend/src/components/MkPagination.vue
+++ b/packages/frontend/src/components/MkPagination.vue
@@ -45,13 +45,13 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts">
 import { computed, ComputedRef, isRef, nextTick, onActivated, onBeforeUnmount, onDeactivated, onMounted, ref, watch } from 'vue';
 import * as Misskey from 'misskey-js';
-import * as os from '@/os';
-import { onScrollTop, isTopVisible, getBodyScrollHeight, getScrollContainer, onScrollBottom, scrollToBottom, scroll, isBottomVisible } from '@/scripts/scroll';
-import { useDocumentVisibility } from '@/scripts/use-document-visibility';
+import * as os from '@/os.js';
+import { onScrollTop, isTopVisible, getBodyScrollHeight, getScrollContainer, onScrollBottom, scrollToBottom, scroll, isBottomVisible } from '@/scripts/scroll.js';
+import { useDocumentVisibility } from '@/scripts/use-document-visibility.js';
 import MkButton from '@/components/MkButton.vue';
-import { defaultStore } from '@/store';
+import { defaultStore } from '@/store.js';
 import { MisskeyEntity } from '@/types/date-separated-list';
-import { i18n } from '@/i18n';
+import { i18n } from '@/i18n.js';
 
 const SECOND_FETCH_LIMIT = 30;
 const TOLERANCE = 16;
@@ -89,7 +89,7 @@ function concatMapWithArray(map: MisskeyEntityMap, entities: MisskeyEntity[]): M
 }
 </script>
 <script lang="ts" setup>
-import { infoImageUrl } from '@/instance';
+import { infoImageUrl } from '@/instance.js';
 
 const props = withDefaults(defineProps<{
 	pagination: Paging;
diff --git a/packages/frontend/src/components/MkPlusOneEffect.vue b/packages/frontend/src/components/MkPlusOneEffect.vue
index 8086b97aed..0bc98f4334 100644
--- a/packages/frontend/src/components/MkPlusOneEffect.vue
+++ b/packages/frontend/src/components/MkPlusOneEffect.vue
@@ -11,7 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { onMounted } from 'vue';
-import * as os from '@/os';
+import * as os from '@/os.js';
 
 const props = withDefaults(defineProps<{
 	x: number;
diff --git a/packages/frontend/src/components/MkPoll.vue b/packages/frontend/src/components/MkPoll.vue
index 2889644247..682f8e3060 100644
--- a/packages/frontend/src/components/MkPoll.vue
+++ b/packages/frontend/src/components/MkPoll.vue
@@ -29,11 +29,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { computed, ref } from 'vue';
 import * as Misskey from 'misskey-js';
-import { sum } from '@/scripts/array';
-import { pleaseLogin } from '@/scripts/please-login';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
-import { useInterval } from '@/scripts/use-interval';
+import { sum } from '@/scripts/array.js';
+import { pleaseLogin } from '@/scripts/please-login.js';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
+import { useInterval } from '@/scripts/use-interval.js';
 
 const props = defineProps<{
 	note: Misskey.entities.Note;
diff --git a/packages/frontend/src/components/MkPollEditor.vue b/packages/frontend/src/components/MkPollEditor.vue
index 172a937eee..43e576d1ab 100644
--- a/packages/frontend/src/components/MkPollEditor.vue
+++ b/packages/frontend/src/components/MkPollEditor.vue
@@ -58,9 +58,9 @@ import MkInput from './MkInput.vue';
 import MkSelect from './MkSelect.vue';
 import MkSwitch from './MkSwitch.vue';
 import MkButton from './MkButton.vue';
-import { formatDateTimeString } from '@/scripts/format-time-string';
-import { addTime } from '@/scripts/time';
-import { i18n } from '@/i18n';
+import { formatDateTimeString } from '@/scripts/format-time-string.js';
+import { addTime } from '@/scripts/time.js';
+import { i18n } from '@/i18n.js';
 
 const props = defineProps<{
 	modelValue: {
diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue
index 9ee9ac9ba0..2b4dcc8ed4 100644
--- a/packages/frontend/src/components/MkPostForm.vue
+++ b/packages/frontend/src/components/MkPostForm.vue
@@ -107,23 +107,23 @@ import MkNoteSimple from '@/components/MkNoteSimple.vue';
 import MkNotePreview from '@/components/MkNotePreview.vue';
 import XPostFormAttaches from '@/components/MkPostFormAttaches.vue';
 import MkPollEditor from '@/components/MkPollEditor.vue';
-import { host, url } from '@/config';
-import { erase, unique } from '@/scripts/array';
-import { extractMentions } from '@/scripts/extract-mentions';
-import { formatTimeString } from '@/scripts/format-time-string';
-import { Autocomplete } from '@/scripts/autocomplete';
-import * as os from '@/os';
-import { selectFiles } from '@/scripts/select-file';
-import { defaultStore, notePostInterruptors, postFormActions } from '@/store';
+import { host, url } from '@/config.js';
+import { erase, unique } from '@/scripts/array.js';
+import { extractMentions } from '@/scripts/extract-mentions.js';
+import { formatTimeString } from '@/scripts/format-time-string.js';
+import { Autocomplete } from '@/scripts/autocomplete.js';
+import * as os from '@/os.js';
+import { selectFiles } from '@/scripts/select-file.js';
+import { defaultStore, notePostInterruptors, postFormActions } from '@/store.js';
 import MkInfo from '@/components/MkInfo.vue';
-import { i18n } from '@/i18n';
-import { instance } from '@/instance';
-import { $i, notesCount, incNotesCount, getAccounts, openAccountMenu as openAccountMenu_ } from '@/account';
-import { uploadFile } from '@/scripts/upload';
-import { deepClone } from '@/scripts/clone';
+import { i18n } from '@/i18n.js';
+import { instance } from '@/instance.js';
+import { $i, notesCount, incNotesCount, getAccounts, openAccountMenu as openAccountMenu_ } from '@/account.js';
+import { uploadFile } from '@/scripts/upload.js';
+import { deepClone } from '@/scripts/clone.js';
 import MkRippleEffect from '@/components/MkRippleEffect.vue';
-import { miLocalStorage } from '@/local-storage';
-import { claimAchievement } from '@/scripts/achievements';
+import { miLocalStorage } from '@/local-storage.js';
+import { claimAchievement } from '@/scripts/achievements.js';
 
 const modal = inject('modal');
 
diff --git a/packages/frontend/src/components/MkPostFormAttaches.vue b/packages/frontend/src/components/MkPostFormAttaches.vue
index 8fee0d80f0..624a44ecae 100644
--- a/packages/frontend/src/components/MkPostFormAttaches.vue
+++ b/packages/frontend/src/components/MkPostFormAttaches.vue
@@ -23,8 +23,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { defineAsyncComponent } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkDriveFileThumbnail from '@/components/MkDriveFileThumbnail.vue';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
 
 const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default));
 
diff --git a/packages/frontend/src/components/MkPushNotificationAllowButton.vue b/packages/frontend/src/components/MkPushNotificationAllowButton.vue
index 432bedd03e..ba64775298 100644
--- a/packages/frontend/src/components/MkPushNotificationAllowButton.vue
+++ b/packages/frontend/src/components/MkPushNotificationAllowButton.vue
@@ -41,11 +41,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script setup lang="ts">
-import { $i, getAccounts } from '@/account';
+import { $i, getAccounts } from '@/account.js';
 import MkButton from '@/components/MkButton.vue';
-import { instance } from '@/instance';
-import { api, apiWithDialog, promiseDialog } from '@/os';
-import { i18n } from '@/i18n';
+import { instance } from '@/instance.js';
+import { api, apiWithDialog, promiseDialog } from '@/os.js';
+import { i18n } from '@/i18n.js';
 
 defineProps<{
 	primary?: boolean;
diff --git a/packages/frontend/src/components/MkRange.vue b/packages/frontend/src/components/MkRange.vue
index b5c1718320..2cfc27ceee 100644
--- a/packages/frontend/src/components/MkRange.vue
+++ b/packages/frontend/src/components/MkRange.vue
@@ -23,7 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { computed, defineAsyncComponent, onMounted, onUnmounted, ref, watch, shallowRef } from 'vue';
-import * as os from '@/os';
+import * as os from '@/os.js';
 
 const props = withDefaults(defineProps<{
 	modelValue: number;
diff --git a/packages/frontend/src/components/MkReactionEffect.vue b/packages/frontend/src/components/MkReactionEffect.vue
index f373d179e8..88e262d880 100644
--- a/packages/frontend/src/components/MkReactionEffect.vue
+++ b/packages/frontend/src/components/MkReactionEffect.vue
@@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { onMounted } from 'vue';
-import * as os from '@/os';
+import * as os from '@/os.js';
 import MkReactionIcon from '@/components/MkReactionIcon.vue';
 
 const props = withDefaults(defineProps<{
diff --git a/packages/frontend/src/components/MkReactionsViewer.details.vue b/packages/frontend/src/components/MkReactionsViewer.details.vue
index fd5bc5e9e3..fdd96d05ae 100644
--- a/packages/frontend/src/components/MkReactionsViewer.details.vue
+++ b/packages/frontend/src/components/MkReactionsViewer.details.vue
@@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { } from 'vue';
 import MkTooltip from './MkTooltip.vue';
 import MkReactionIcon from '@/components/MkReactionIcon.vue';
-import { getEmojiName } from '@/scripts/emojilist';
+import { getEmojiName } from '@/scripts/emojilist.js';
 
 defineProps<{
 	showing: boolean;
diff --git a/packages/frontend/src/components/MkReactionsViewer.reaction.vue b/packages/frontend/src/components/MkReactionsViewer.reaction.vue
index 7e2fbb1619..5e87c7238b 100644
--- a/packages/frontend/src/components/MkReactionsViewer.reaction.vue
+++ b/packages/frontend/src/components/MkReactionsViewer.reaction.vue
@@ -21,13 +21,13 @@ import { computed, onMounted, shallowRef, watch } from 'vue';
 import * as Misskey from 'misskey-js';
 import XDetails from '@/components/MkReactionsViewer.details.vue';
 import MkReactionIcon from '@/components/MkReactionIcon.vue';
-import * as os from '@/os';
-import { useTooltip } from '@/scripts/use-tooltip';
-import { $i } from '@/account';
+import * as os from '@/os.js';
+import { useTooltip } from '@/scripts/use-tooltip.js';
+import { $i } from '@/account.js';
 import MkReactionEffect from '@/components/MkReactionEffect.vue';
-import { claimAchievement } from '@/scripts/achievements';
-import { defaultStore } from '@/store';
-import { i18n } from '@/i18n';
+import { claimAchievement } from '@/scripts/achievements.js';
+import { defaultStore } from '@/store.js';
+import { i18n } from '@/i18n.js';
 
 const props = defineProps<{
 	reaction: string;
diff --git a/packages/frontend/src/components/MkReactionsViewer.vue b/packages/frontend/src/components/MkReactionsViewer.vue
index c0e7171ca6..52ead19a4b 100644
--- a/packages/frontend/src/components/MkReactionsViewer.vue
+++ b/packages/frontend/src/components/MkReactionsViewer.vue
@@ -21,7 +21,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 import * as Misskey from 'misskey-js';
 import { watch } from 'vue';
 import XReaction from '@/components/MkReactionsViewer.reaction.vue';
-import { defaultStore } from '@/store';
+import { defaultStore } from '@/store.js';
 
 const props = withDefaults(defineProps<{
 	note: Misskey.entities.Note;
diff --git a/packages/frontend/src/components/MkRemoteCaution.vue b/packages/frontend/src/components/MkRemoteCaution.vue
index e7d9d74dae..0ce67e872b 100644
--- a/packages/frontend/src/components/MkRemoteCaution.vue
+++ b/packages/frontend/src/components/MkRemoteCaution.vue
@@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { i18n } from '@/i18n';
+import { i18n } from '@/i18n.js';
 
 defineProps<{
 	href: string;
diff --git a/packages/frontend/src/components/MkRetentionHeatmap.vue b/packages/frontend/src/components/MkRetentionHeatmap.vue
index 737ddff6ad..3dc9a94ae2 100644
--- a/packages/frontend/src/components/MkRetentionHeatmap.vue
+++ b/packages/frontend/src/components/MkRetentionHeatmap.vue
@@ -15,11 +15,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { onMounted, nextTick } from 'vue';
 import { Chart } from 'chart.js';
-import * as os from '@/os';
-import { defaultStore } from '@/store';
-import { useChartTooltip } from '@/scripts/use-chart-tooltip';
-import { alpha } from '@/scripts/color';
-import { initChart } from '@/scripts/init-chart';
+import * as os from '@/os.js';
+import { defaultStore } from '@/store.js';
+import { useChartTooltip } from '@/scripts/use-chart-tooltip.js';
+import { alpha } from '@/scripts/color.js';
+import { initChart } from '@/scripts/init-chart.js';
 
 initChart();
 
diff --git a/packages/frontend/src/components/MkRetentionLineChart.vue b/packages/frontend/src/components/MkRetentionLineChart.vue
index 01298701fb..e2682ec06b 100644
--- a/packages/frontend/src/components/MkRetentionLineChart.vue
+++ b/packages/frontend/src/components/MkRetentionLineChart.vue
@@ -11,12 +11,12 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { onMounted, shallowRef } from 'vue';
 import { Chart } from 'chart.js';
 import tinycolor from 'tinycolor2';
-import { defaultStore } from '@/store';
-import { useChartTooltip } from '@/scripts/use-chart-tooltip';
-import { chartVLine } from '@/scripts/chart-vline';
-import { alpha } from '@/scripts/color';
-import { initChart } from '@/scripts/init-chart';
-import * as os from '@/os';
+import { defaultStore } from '@/store.js';
+import { useChartTooltip } from '@/scripts/use-chart-tooltip.js';
+import { chartVLine } from '@/scripts/chart-vline.js';
+import { alpha } from '@/scripts/color.js';
+import { initChart } from '@/scripts/init-chart.js';
+import * as os from '@/os.js';
 
 initChart();
 
diff --git a/packages/frontend/src/components/MkRippleEffect.vue b/packages/frontend/src/components/MkRippleEffect.vue
index 3015f67e65..860b083327 100644
--- a/packages/frontend/src/components/MkRippleEffect.vue
+++ b/packages/frontend/src/components/MkRippleEffect.vue
@@ -63,7 +63,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { onMounted } from 'vue';
-import * as os from '@/os';
+import * as os from '@/os.js';
 
 const props = withDefaults(defineProps<{
 	x: number;
diff --git a/packages/frontend/src/components/MkRolePreview.vue b/packages/frontend/src/components/MkRolePreview.vue
index cb9595b746..3e4586cee4 100644
--- a/packages/frontend/src/components/MkRolePreview.vue
+++ b/packages/frontend/src/components/MkRolePreview.vue
@@ -28,7 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { } from 'vue';
-import { i18n } from '@/i18n';
+import { i18n } from '@/i18n.js';
 
 const props = withDefaults(defineProps<{
 	role: any;
diff --git a/packages/frontend/src/components/MkSelect.vue b/packages/frontend/src/components/MkSelect.vue
index cf9a9f225f..5a1c788005 100644
--- a/packages/frontend/src/components/MkSelect.vue
+++ b/packages/frontend/src/components/MkSelect.vue
@@ -34,9 +34,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { onMounted, nextTick, ref, watch, computed, toRefs, VNode, useSlots } from 'vue';
 import MkButton from '@/components/MkButton.vue';
-import * as os from '@/os';
-import { useInterval } from '@/scripts/use-interval';
-import { i18n } from '@/i18n';
+import * as os from '@/os.js';
+import { useInterval } from '@/scripts/use-interval.js';
+import { i18n } from '@/i18n.js';
 
 const props = defineProps<{
 	modelValue: string | null;
diff --git a/packages/frontend/src/components/MkSignin.vue b/packages/frontend/src/components/MkSignin.vue
index 247fcb4b29..0c65bf8227 100644
--- a/packages/frontend/src/components/MkSignin.vue
+++ b/packages/frontend/src/components/MkSignin.vue
@@ -53,14 +53,14 @@ import { defineAsyncComponent } from 'vue';
 import { toUnicode } from 'punycode/';
 import { UserDetailed } from 'misskey-js/built/entities';
 import { supported as webAuthnSupported, get as webAuthnRequest, parseRequestOptionsFromJSON } from '@github/webauthn-json/browser-ponyfill';
-import { showSuspendedDialog } from '@/scripts/show-suspended-dialog';
+import { showSuspendedDialog } from '@/scripts/show-suspended-dialog.js';
 import MkButton from '@/components/MkButton.vue';
 import MkInput from '@/components/MkInput.vue';
 import MkInfo from '@/components/MkInfo.vue';
-import { host as configHost } from '@/config';
-import * as os from '@/os';
-import { login } from '@/account';
-import { i18n } from '@/i18n';
+import { host as configHost } from '@/config.js';
+import * as os from '@/os.js';
+import { login } from '@/account.js';
+import { i18n } from '@/i18n.js';
 
 let signing = $ref(false);
 let user = $ref<UserDetailed | null>(null);
diff --git a/packages/frontend/src/components/MkSigninDialog.vue b/packages/frontend/src/components/MkSigninDialog.vue
index 2b72143e88..05cef6ed3b 100644
--- a/packages/frontend/src/components/MkSigninDialog.vue
+++ b/packages/frontend/src/components/MkSigninDialog.vue
@@ -23,7 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { } from 'vue';
 import MkSignin from '@/components/MkSignin.vue';
 import MkModalWindow from '@/components/MkModalWindow.vue';
-import { i18n } from '@/i18n';
+import { i18n } from '@/i18n.js';
 
 withDefaults(defineProps<{
 	autoSet?: boolean;
diff --git a/packages/frontend/src/components/MkSignupDialog.form.vue b/packages/frontend/src/components/MkSignupDialog.form.vue
index 60541b256b..a67251eda1 100644
--- a/packages/frontend/src/components/MkSignupDialog.form.vue
+++ b/packages/frontend/src/components/MkSignupDialog.form.vue
@@ -82,11 +82,11 @@ import MkButton from './MkButton.vue';
 import MkInput from './MkInput.vue';
 import MkSwitch from './MkSwitch.vue';
 import MkCaptcha, { type Captcha } from '@/components/MkCaptcha.vue';
-import * as config from '@/config';
-import * as os from '@/os';
-import { login } from '@/account';
-import { instance } from '@/instance';
-import { i18n } from '@/i18n';
+import * as config from '@/config.js';
+import * as os from '@/os.js';
+import { login } from '@/account.js';
+import { instance } from '@/instance.js';
+import { i18n } from '@/i18n.js';
 
 const props = withDefaults(defineProps<{
 	autoSet?: boolean;
diff --git a/packages/frontend/src/components/MkSignupDialog.rules.stories.impl.ts b/packages/frontend/src/components/MkSignupDialog.rules.stories.impl.ts
index 2a5dc7b448..ab26df6342 100644
--- a/packages/frontend/src/components/MkSignupDialog.rules.stories.impl.ts
+++ b/packages/frontend/src/components/MkSignupDialog.rules.stories.impl.ts
@@ -9,8 +9,8 @@ import { userEvent, waitFor, within } from '@storybook/testing-library';
 import { StoryObj } from '@storybook/vue3';
 import { onBeforeUnmount } from 'vue';
 import MkSignupServerRules from './MkSignupDialog.rules.vue';
-import { i18n } from '@/i18n';
-import { instance } from '@/instance';
+import { i18n } from '@/i18n.js';
+import { instance } from '@/instance.js';
 export const Empty = {
 	render(args) {
 		return {
diff --git a/packages/frontend/src/components/MkSignupDialog.rules.vue b/packages/frontend/src/components/MkSignupDialog.rules.vue
index 627b14a46f..aa4a184d7b 100644
--- a/packages/frontend/src/components/MkSignupDialog.rules.vue
+++ b/packages/frontend/src/components/MkSignupDialog.rules.vue
@@ -61,13 +61,13 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { computed, onMounted, ref, watch } from 'vue';
-import { instance } from '@/instance';
-import { i18n } from '@/i18n';
+import { instance } from '@/instance.js';
+import { i18n } from '@/i18n.js';
 import MkButton from '@/components/MkButton.vue';
 import MkFolder from '@/components/MkFolder.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
 import MkInfo from '@/components/MkInfo.vue';
-import * as os from '@/os';
+import * as os from '@/os.js';
 
 const availableServerRules = instance.serverRules.length > 0;
 const availableTos = instance.tosUrl != null;
diff --git a/packages/frontend/src/components/MkSignupDialog.vue b/packages/frontend/src/components/MkSignupDialog.vue
index 21d5fe5903..d860ba5fe6 100644
--- a/packages/frontend/src/components/MkSignupDialog.vue
+++ b/packages/frontend/src/components/MkSignupDialog.vue
@@ -38,8 +38,8 @@ import { $ref } from 'vue/macros';
 import XSignup from '@/components/MkSignupDialog.form.vue';
 import XServerRules from '@/components/MkSignupDialog.rules.vue';
 import MkModalWindow from '@/components/MkModalWindow.vue';
-import { i18n } from '@/i18n';
-import { instance } from '@/instance';
+import { i18n } from '@/i18n.js';
+import { instance } from '@/instance.js';
 
 const props = withDefaults(defineProps<{
 	autoSet?: boolean;
diff --git a/packages/frontend/src/components/MkSubNoteContent.vue b/packages/frontend/src/components/MkSubNoteContent.vue
index b823ed4373..34fdd1cce4 100644
--- a/packages/frontend/src/components/MkSubNoteContent.vue
+++ b/packages/frontend/src/components/MkSubNoteContent.vue
@@ -34,9 +34,9 @@ import { } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkMediaList from '@/components/MkMediaList.vue';
 import MkPoll from '@/components/MkPoll.vue';
-import { i18n } from '@/i18n';
-import { $i } from '@/account';
-import { shouldCollapsed } from '@/scripts/collapsed';
+import { i18n } from '@/i18n.js';
+import { $i } from '@/account.js';
+import { shouldCollapsed } from '@/scripts/collapsed.js';
 
 const props = defineProps<{
 	note: Misskey.entities.Note;
diff --git a/packages/frontend/src/components/MkSwitch.button.vue b/packages/frontend/src/components/MkSwitch.button.vue
index 1d420f86c9..a7e91acc39 100644
--- a/packages/frontend/src/components/MkSwitch.button.vue
+++ b/packages/frontend/src/components/MkSwitch.button.vue
@@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { toRefs, Ref } from 'vue';
-import { i18n } from '@/i18n';
+import { i18n } from '@/i18n.js';
 
 const props = withDefaults(defineProps<{
 	checked: boolean | Ref<boolean>;
diff --git a/packages/frontend/src/components/MkTextarea.vue b/packages/frontend/src/components/MkTextarea.vue
index 5abd0e5d99..7c1ddcbbed 100644
--- a/packages/frontend/src/components/MkTextarea.vue
+++ b/packages/frontend/src/components/MkTextarea.vue
@@ -35,7 +35,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { onMounted, nextTick, ref, watch, computed, toRefs, shallowRef } from 'vue';
 import { debounce } from 'throttle-debounce';
 import MkButton from '@/components/MkButton.vue';
-import { i18n } from '@/i18n';
+import { i18n } from '@/i18n.js';
 
 const props = defineProps<{
 	modelValue: string | null;
diff --git a/packages/frontend/src/components/MkTimeline.vue b/packages/frontend/src/components/MkTimeline.vue
index cb0aaf085c..d6712e7606 100644
--- a/packages/frontend/src/components/MkTimeline.vue
+++ b/packages/frontend/src/components/MkTimeline.vue
@@ -10,10 +10,10 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { computed, provide, onUnmounted } from 'vue';
 import MkNotes from '@/components/MkNotes.vue';
-import { useStream } from '@/stream';
-import * as sound from '@/scripts/sound';
-import { $i } from '@/account';
-import { defaultStore } from '@/store';
+import { useStream } from '@/stream.js';
+import * as sound from '@/scripts/sound.js';
+import { $i } from '@/account.js';
+import { defaultStore } from '@/store.js';
 
 const props = defineProps<{
 	src: string;
diff --git a/packages/frontend/src/components/MkToast.vue b/packages/frontend/src/components/MkToast.vue
index 9a998d62ac..48908cf3e6 100644
--- a/packages/frontend/src/components/MkToast.vue
+++ b/packages/frontend/src/components/MkToast.vue
@@ -23,8 +23,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { onMounted } from 'vue';
-import * as os from '@/os';
-import { defaultStore } from '@/store';
+import * as os from '@/os.js';
+import { defaultStore } from '@/store.js';
 
 defineProps<{
 	message: string;
diff --git a/packages/frontend/src/components/MkTokenGenerateWindow.vue b/packages/frontend/src/components/MkTokenGenerateWindow.vue
index 0c731f402c..5f3c2f3a71 100644
--- a/packages/frontend/src/components/MkTokenGenerateWindow.vue
+++ b/packages/frontend/src/components/MkTokenGenerateWindow.vue
@@ -46,7 +46,7 @@ import MkSwitch from './MkSwitch.vue';
 import MkButton from './MkButton.vue';
 import MkInfo from './MkInfo.vue';
 import MkModalWindow from '@/components/MkModalWindow.vue';
-import { i18n } from '@/i18n';
+import { i18n } from '@/i18n.js';
 
 const props = withDefaults(defineProps<{
 	title?: string | null;
diff --git a/packages/frontend/src/components/MkTooltip.vue b/packages/frontend/src/components/MkTooltip.vue
index 770872ef05..d21c6317aa 100644
--- a/packages/frontend/src/components/MkTooltip.vue
+++ b/packages/frontend/src/components/MkTooltip.vue
@@ -22,9 +22,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { nextTick, onMounted, onUnmounted, shallowRef } from 'vue';
-import * as os from '@/os';
-import { calcPopupPosition } from '@/scripts/popup-position';
-import { defaultStore } from '@/store';
+import * as os from '@/os.js';
+import { calcPopupPosition } from '@/scripts/popup-position.js';
+import { defaultStore } from '@/store.js';
 
 const props = withDefaults(defineProps<{
 	showing: boolean;
diff --git a/packages/frontend/src/components/MkUpdated.vue b/packages/frontend/src/components/MkUpdated.vue
index 070200e0e2..699d7af33e 100644
--- a/packages/frontend/src/components/MkUpdated.vue
+++ b/packages/frontend/src/components/MkUpdated.vue
@@ -19,9 +19,9 @@ import { onMounted, shallowRef } from 'vue';
 import MkModal from '@/components/MkModal.vue';
 import MkButton from '@/components/MkButton.vue';
 import MkSparkle from '@/components/MkSparkle.vue';
-import { version } from '@/config';
-import { i18n } from '@/i18n';
-import { confetti } from '@/scripts/confetti';
+import { version } from '@/config.js';
+import { i18n } from '@/i18n.js';
+import { confetti } from '@/scripts/confetti.js';
 
 const modal = shallowRef<InstanceType<typeof MkModal>>();
 
diff --git a/packages/frontend/src/components/MkUrlPreview.vue b/packages/frontend/src/components/MkUrlPreview.vue
index 77c62ae0b9..e2844f8fa1 100644
--- a/packages/frontend/src/components/MkUrlPreview.vue
+++ b/packages/frontend/src/components/MkUrlPreview.vue
@@ -85,13 +85,13 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { defineAsyncComponent, onUnmounted } from 'vue';
 import type { summaly } from 'summaly';
-import { url as local } from '@/config';
-import { i18n } from '@/i18n';
-import * as os from '@/os';
-import { deviceKind } from '@/scripts/device-kind';
+import { url as local } from '@/config.js';
+import { i18n } from '@/i18n.js';
+import * as os from '@/os.js';
+import { deviceKind } from '@/scripts/device-kind.js';
 import MkButton from '@/components/MkButton.vue';
-import { versatileLang } from '@/scripts/intl-const';
-import { defaultStore } from '@/store';
+import { versatileLang } from '@/scripts/intl-const.js';
+import { defaultStore } from '@/store.js';
 
 type SummalyResult = Awaited<ReturnType<typeof summaly>>;
 
diff --git a/packages/frontend/src/components/MkUrlPreviewPopup.vue b/packages/frontend/src/components/MkUrlPreviewPopup.vue
index e99f1575c5..0ab012dfb7 100644
--- a/packages/frontend/src/components/MkUrlPreviewPopup.vue
+++ b/packages/frontend/src/components/MkUrlPreviewPopup.vue
@@ -14,8 +14,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { onMounted } from 'vue';
 import MkUrlPreview from '@/components/MkUrlPreview.vue';
-import * as os from '@/os';
-import { defaultStore } from '@/store';
+import * as os from '@/os.js';
+import { defaultStore } from '@/store.js';
 
 const props = defineProps<{
 	showing: boolean;
diff --git a/packages/frontend/src/components/MkUserAnnouncementEditDialog.vue b/packages/frontend/src/components/MkUserAnnouncementEditDialog.vue
index 7099d203c9..235df8822f 100644
--- a/packages/frontend/src/components/MkUserAnnouncementEditDialog.vue
+++ b/packages/frontend/src/components/MkUserAnnouncementEditDialog.vue
@@ -55,8 +55,8 @@ import * as Misskey from 'misskey-js';
 import MkModalWindow from '@/components/MkModalWindow.vue';
 import MkButton from '@/components/MkButton.vue';
 import MkInput from '@/components/MkInput.vue';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
 import MkTextarea from '@/components/MkTextarea.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
 import MkRadios from '@/components/MkRadios.vue';
diff --git a/packages/frontend/src/components/MkUserCardMini.vue b/packages/frontend/src/components/MkUserCardMini.vue
index 416ffe833a..fbc2e09b0b 100644
--- a/packages/frontend/src/components/MkUserCardMini.vue
+++ b/packages/frontend/src/components/MkUserCardMini.vue
@@ -18,8 +18,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 import * as Misskey from 'misskey-js';
 import { onMounted } from 'vue';
 import MkMiniChart from '@/components/MkMiniChart.vue';
-import * as os from '@/os';
-import { acct } from '@/filters/user';
+import * as os from '@/os.js';
+import { acct } from '@/filters/user.js';
 
 const props = withDefaults(defineProps<{
 	user: Misskey.entities.User;
diff --git a/packages/frontend/src/components/MkUserInfo.vue b/packages/frontend/src/components/MkUserInfo.vue
index f8384b31c5..c13ef60f3b 100644
--- a/packages/frontend/src/components/MkUserInfo.vue
+++ b/packages/frontend/src/components/MkUserInfo.vue
@@ -36,11 +36,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import * as Misskey from 'misskey-js';
 import MkFollowButton from '@/components/MkFollowButton.vue';
-import number from '@/filters/number';
-import { userPage } from '@/filters/user';
-import { i18n } from '@/i18n';
-import { $i } from '@/account';
-import { isFfVisibleForMe } from '@/scripts/isFfVisibleForMe';
+import number from '@/filters/number.js';
+import { userPage } from '@/filters/user.js';
+import { i18n } from '@/i18n.js';
+import { $i } from '@/account.js';
+import { isFfVisibleForMe } from '@/scripts/isFfVisibleForMe.js';
 
 defineProps<{
 	user: Misskey.entities.UserDetailed;
diff --git a/packages/frontend/src/components/MkUserList.vue b/packages/frontend/src/components/MkUserList.vue
index 1cd491a9d5..56a61dce23 100644
--- a/packages/frontend/src/components/MkUserList.vue
+++ b/packages/frontend/src/components/MkUserList.vue
@@ -23,8 +23,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import MkUserInfo from '@/components/MkUserInfo.vue';
 import MkPagination, { Paging } from '@/components/MkPagination.vue';
-import { i18n } from '@/i18n';
-import { infoImageUrl } from '@/instance';
+import { i18n } from '@/i18n.js';
+import { infoImageUrl } from '@/instance.js';
 
 const props = withDefaults(defineProps<{
 	pagination: Paging;
diff --git a/packages/frontend/src/components/MkUserOnlineIndicator.vue b/packages/frontend/src/components/MkUserOnlineIndicator.vue
index 217887e63c..8b792fe496 100644
--- a/packages/frontend/src/components/MkUserOnlineIndicator.vue
+++ b/packages/frontend/src/components/MkUserOnlineIndicator.vue
@@ -18,7 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { } from 'vue';
 import * as Misskey from 'misskey-js';
-import { i18n } from '@/i18n';
+import { i18n } from '@/i18n.js';
 
 const props = defineProps<{
 	user: Misskey.entities.User;
diff --git a/packages/frontend/src/components/MkUserPopup.vue b/packages/frontend/src/components/MkUserPopup.vue
index 8c173ffa70..33ef07d54b 100644
--- a/packages/frontend/src/components/MkUserPopup.vue
+++ b/packages/frontend/src/components/MkUserPopup.vue
@@ -58,14 +58,14 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { onMounted } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkFollowButton from '@/components/MkFollowButton.vue';
-import { userPage } from '@/filters/user';
-import * as os from '@/os';
-import { getUserMenu } from '@/scripts/get-user-menu';
-import number from '@/filters/number';
-import { i18n } from '@/i18n';
-import { defaultStore } from '@/store';
-import { $i } from '@/account';
-import { isFfVisibleForMe } from '@/scripts/isFfVisibleForMe';
+import { userPage } from '@/filters/user.js';
+import * as os from '@/os.js';
+import { getUserMenu } from '@/scripts/get-user-menu.js';
+import number from '@/filters/number.js';
+import { i18n } from '@/i18n.js';
+import { defaultStore } from '@/store.js';
+import { $i } from '@/account.js';
+import { isFfVisibleForMe } from '@/scripts/isFfVisibleForMe.js';
 
 const props = defineProps<{
 	showing: boolean;
diff --git a/packages/frontend/src/components/MkUserSelectDialog.vue b/packages/frontend/src/components/MkUserSelectDialog.vue
index 0d886c08a5..ac38c4b62f 100644
--- a/packages/frontend/src/components/MkUserSelectDialog.vue
+++ b/packages/frontend/src/components/MkUserSelectDialog.vue
@@ -62,11 +62,11 @@ import * as Misskey from 'misskey-js';
 import MkInput from '@/components/MkInput.vue';
 import FormSplit from '@/components/form/split.vue';
 import MkModalWindow from '@/components/MkModalWindow.vue';
-import * as os from '@/os';
-import { defaultStore } from '@/store';
-import { i18n } from '@/i18n';
-import { $i } from '@/account';
-import { hostname } from '@/config';
+import * as os from '@/os.js';
+import { defaultStore } from '@/store.js';
+import { i18n } from '@/i18n.js';
+import { $i } from '@/account.js';
+import { hostname } from '@/config.js';
 
 const emit = defineEmits<{
 	(ev: 'ok', selected: Misskey.entities.UserDetailed): void;
diff --git a/packages/frontend/src/components/MkUserSetupDialog.Follow.vue b/packages/frontend/src/components/MkUserSetupDialog.Follow.vue
index f3484cbb43..4ecca7334c 100644
--- a/packages/frontend/src/components/MkUserSetupDialog.Follow.vue
+++ b/packages/frontend/src/components/MkUserSetupDialog.Follow.vue
@@ -35,14 +35,14 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { computed, ref, watch } from 'vue';
-import { instance } from '@/instance';
-import { i18n } from '@/i18n';
+import { instance } from '@/instance.js';
+import { i18n } from '@/i18n.js';
 import MkButton from '@/components/MkButton.vue';
 import MkFolder from '@/components/MkFolder.vue';
 import XUser from '@/components/MkUserSetupDialog.User.vue';
 import MkInfo from '@/components/MkInfo.vue';
-import * as os from '@/os';
-import { $i } from '@/account';
+import * as os from '@/os.js';
+import { $i } from '@/account.js';
 import MkPagination from '@/components/MkPagination.vue';
 
 const pinnedUsers = { endpoint: 'pinned-users', noPaging: true };
diff --git a/packages/frontend/src/components/MkUserSetupDialog.Privacy.vue b/packages/frontend/src/components/MkUserSetupDialog.Privacy.vue
index dbb8259b5c..841ab5ba0c 100644
--- a/packages/frontend/src/components/MkUserSetupDialog.Privacy.vue
+++ b/packages/frontend/src/components/MkUserSetupDialog.Privacy.vue
@@ -45,13 +45,13 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { computed, ref, watch } from 'vue';
-import { instance } from '@/instance';
-import { i18n } from '@/i18n';
+import { instance } from '@/instance.js';
+import { i18n } from '@/i18n.js';
 import MkSwitch from '@/components/MkSwitch.vue';
 import MkInfo from '@/components/MkInfo.vue';
 import MkFolder from '@/components/MkFolder.vue';
-import * as os from '@/os';
-import { $i } from '@/account';
+import * as os from '@/os.js';
+import { $i } from '@/account.js';
 
 let isLocked = ref(false);
 let hideOnlineStatus = ref(false);
diff --git a/packages/frontend/src/components/MkUserSetupDialog.Profile.vue b/packages/frontend/src/components/MkUserSetupDialog.Profile.vue
index d155773330..8de9bbdbb1 100644
--- a/packages/frontend/src/components/MkUserSetupDialog.Profile.vue
+++ b/packages/frontend/src/components/MkUserSetupDialog.Profile.vue
@@ -31,16 +31,16 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { computed, ref, watch } from 'vue';
-import { instance } from '@/instance';
-import { i18n } from '@/i18n';
+import { instance } from '@/instance.js';
+import { i18n } from '@/i18n.js';
 import MkButton from '@/components/MkButton.vue';
 import MkInput from '@/components/MkInput.vue';
 import MkTextarea from '@/components/MkTextarea.vue';
 import FormSlot from '@/components/form/slot.vue';
 import MkInfo from '@/components/MkInfo.vue';
-import { chooseFileFromPc } from '@/scripts/select-file';
-import * as os from '@/os';
-import { $i } from '@/account';
+import { chooseFileFromPc } from '@/scripts/select-file.js';
+import * as os from '@/os.js';
+import { $i } from '@/account.js';
 
 const name = ref($i.name ?? '');
 const description = ref($i.description ?? '');
diff --git a/packages/frontend/src/components/MkUserSetupDialog.User.vue b/packages/frontend/src/components/MkUserSetupDialog.User.vue
index d09890a4ca..746781d71f 100644
--- a/packages/frontend/src/components/MkUserSetupDialog.User.vue
+++ b/packages/frontend/src/components/MkUserSetupDialog.User.vue
@@ -28,9 +28,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 import * as Misskey from 'misskey-js';
 import { ref } from 'vue';
 import MkButton from '@/components/MkButton.vue';
-import { i18n } from '@/i18n';
-import { $i } from '@/account';
-import * as os from '@/os';
+import { i18n } from '@/i18n.js';
+import { $i } from '@/account.js';
+import * as os from '@/os.js';
 
 const props = defineProps<{
 	user: Misskey.entities.UserDetailed;
diff --git a/packages/frontend/src/components/MkUserSetupDialog.vue b/packages/frontend/src/components/MkUserSetupDialog.vue
index 1adc21ba38..d60e01c44d 100644
--- a/packages/frontend/src/components/MkUserSetupDialog.vue
+++ b/packages/frontend/src/components/MkUserSetupDialog.vue
@@ -130,12 +130,12 @@ import XProfile from '@/components/MkUserSetupDialog.Profile.vue';
 import XFollow from '@/components/MkUserSetupDialog.Follow.vue';
 import XPrivacy from '@/components/MkUserSetupDialog.Privacy.vue';
 import MkAnimBg from '@/components/MkAnimBg.vue';
-import { i18n } from '@/i18n';
-import { instance } from '@/instance';
-import { host } from '@/config';
+import { i18n } from '@/i18n.js';
+import { instance } from '@/instance.js';
+import { host } from '@/config.js';
 import MkPushNotificationAllowButton from '@/components/MkPushNotificationAllowButton.vue';
-import { defaultStore } from '@/store';
-import * as os from '@/os';
+import { defaultStore } from '@/store.js';
+import * as os from '@/os.js';
 
 const emit = defineEmits<{
 	(ev: 'closed'): void;
diff --git a/packages/frontend/src/components/MkVisibilityPicker.vue b/packages/frontend/src/components/MkVisibilityPicker.vue
index f3107f6d11..982a69925b 100644
--- a/packages/frontend/src/components/MkVisibilityPicker.vue
+++ b/packages/frontend/src/components/MkVisibilityPicker.vue
@@ -45,7 +45,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { nextTick } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkModal from '@/components/MkModal.vue';
-import { i18n } from '@/i18n';
+import { i18n } from '@/i18n.js';
 
 const modal = $shallowRef<InstanceType<typeof MkModal>>();
 
diff --git a/packages/frontend/src/components/MkVisitorDashboard.ActiveUsersChart.vue b/packages/frontend/src/components/MkVisitorDashboard.ActiveUsersChart.vue
index c862b24ae5..26de7dee52 100644
--- a/packages/frontend/src/components/MkVisitorDashboard.ActiveUsersChart.vue
+++ b/packages/frontend/src/components/MkVisitorDashboard.ActiveUsersChart.vue
@@ -17,11 +17,11 @@ import { onMounted } from 'vue';
 import { Chart } from 'chart.js';
 import gradient from 'chartjs-plugin-gradient';
 import tinycolor from 'tinycolor2';
-import * as os from '@/os';
-import { defaultStore } from '@/store';
-import { useChartTooltip } from '@/scripts/use-chart-tooltip';
-import { chartVLine } from '@/scripts/chart-vline';
-import { initChart } from '@/scripts/init-chart';
+import * as os from '@/os.js';
+import { defaultStore } from '@/store.js';
+import { useChartTooltip } from '@/scripts/use-chart-tooltip.js';
+import { chartVLine } from '@/scripts/chart-vline.js';
+import { initChart } from '@/scripts/init-chart.js';
 
 initChart();
 
diff --git a/packages/frontend/src/components/MkVisitorDashboard.vue b/packages/frontend/src/components/MkVisitorDashboard.vue
index 865ebe2ef9..7a8d7e6109 100644
--- a/packages/frontend/src/components/MkVisitorDashboard.vue
+++ b/packages/frontend/src/components/MkVisitorDashboard.vue
@@ -59,11 +59,11 @@ import XSignupDialog from '@/components/MkSignupDialog.vue';
 import MkButton from '@/components/MkButton.vue';
 import MkTimeline from '@/components/MkTimeline.vue';
 import MkInfo from '@/components/MkInfo.vue';
-import { instanceName } from '@/config';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
-import { instance } from '@/instance';
-import number from '@/filters/number';
+import { instanceName } from '@/config.js';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
+import { instance } from '@/instance.js';
+import number from '@/filters/number.js';
 import MkNumber from '@/components/MkNumber.vue';
 import XActiveUsersChart from '@/components/MkVisitorDashboard.ActiveUsersChart.vue';
 
diff --git a/packages/frontend/src/components/MkWidgets.vue b/packages/frontend/src/components/MkWidgets.vue
index af795044a2..e2d7ceab7b 100644
--- a/packages/frontend/src/components/MkWidgets.vue
+++ b/packages/frontend/src/components/MkWidgets.vue
@@ -55,8 +55,8 @@ import { v4 as uuid } from 'uuid';
 import MkSelect from '@/components/MkSelect.vue';
 import MkButton from '@/components/MkButton.vue';
 import { widgets as widgetDefs } from '@/widgets';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
 
 const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default));
 
diff --git a/packages/frontend/src/components/MkWindow.vue b/packages/frontend/src/components/MkWindow.vue
index 008f1910ba..ccb8b09b6c 100644
--- a/packages/frontend/src/components/MkWindow.vue
+++ b/packages/frontend/src/components/MkWindow.vue
@@ -54,11 +54,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { onBeforeUnmount, onMounted, provide } from 'vue';
-import contains from '@/scripts/contains';
-import * as os from '@/os';
+import contains from '@/scripts/contains.js';
+import * as os from '@/os.js';
 import { MenuItem } from '@/types/menu';
-import { i18n } from '@/i18n';
-import { defaultStore } from '@/store';
+import { i18n } from '@/i18n.js';
+import { defaultStore } from '@/store.js';
 
 const minHeight = 50;
 const minWidth = 250;
diff --git a/packages/frontend/src/components/MkYouTubePlayer.vue b/packages/frontend/src/components/MkYouTubePlayer.vue
index 9b9c3d5ec4..d74ad0eda4 100644
--- a/packages/frontend/src/components/MkYouTubePlayer.vue
+++ b/packages/frontend/src/components/MkYouTubePlayer.vue
@@ -25,8 +25,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import MkWindow from '@/components/MkWindow.vue';
-import { versatileLang } from '@/scripts/intl-const';
-import { defaultStore } from '@/store';
+import { versatileLang } from '@/scripts/intl-const.js';
+import { defaultStore } from '@/store.js';
 
 const props = defineProps<{
 	url: string;
diff --git a/packages/frontend/src/components/form/suspense.vue b/packages/frontend/src/components/form/suspense.vue
index 99ee973193..e3db639ff0 100644
--- a/packages/frontend/src/components/form/suspense.vue
+++ b/packages/frontend/src/components/form/suspense.vue
@@ -21,8 +21,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { ref, watch } from 'vue';
 import MkButton from '@/components/MkButton.vue';
-import { defaultStore } from '@/store';
-import { i18n } from '@/i18n';
+import { defaultStore } from '@/store.js';
+import { i18n } from '@/i18n.js';
 
 const props = defineProps<{
 	p: () => Promise<any>;
diff --git a/packages/frontend/src/components/global/MkA.stories.impl.ts b/packages/frontend/src/components/global/MkA.stories.impl.ts
index 4a41280f90..62f4805a11 100644
--- a/packages/frontend/src/components/global/MkA.stories.impl.ts
+++ b/packages/frontend/src/components/global/MkA.stories.impl.ts
@@ -8,7 +8,7 @@ import { expect } from '@storybook/jest';
 import { userEvent, within } from '@storybook/testing-library';
 import { StoryObj } from '@storybook/vue3';
 import MkA from './MkA.vue';
-import { tick } from '@/scripts/test-utils';
+import { tick } from '@/scripts/test-utils.js';
 export const Default = {
 	render(args) {
 		return {
diff --git a/packages/frontend/src/components/global/MkA.vue b/packages/frontend/src/components/global/MkA.vue
index acf4e969fd..2c50511b8b 100644
--- a/packages/frontend/src/components/global/MkA.vue
+++ b/packages/frontend/src/components/global/MkA.vue
@@ -10,12 +10,12 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import * as os from '@/os';
-import copyToClipboard from '@/scripts/copy-to-clipboard';
-import { url } from '@/config';
-import { popout as popout_ } from '@/scripts/popout';
-import { i18n } from '@/i18n';
-import { useRouter } from '@/router';
+import * as os from '@/os.js';
+import copyToClipboard from '@/scripts/copy-to-clipboard.js';
+import { url } from '@/config.js';
+import { popout as popout_ } from '@/scripts/popout.js';
+import { i18n } from '@/i18n.js';
+import { useRouter } from '@/router.js';
 
 const props = withDefaults(defineProps<{
 	to: string;
diff --git a/packages/frontend/src/components/global/MkAcct.vue b/packages/frontend/src/components/global/MkAcct.vue
index 878986402d..42d29db488 100644
--- a/packages/frontend/src/components/global/MkAcct.vue
+++ b/packages/frontend/src/components/global/MkAcct.vue
@@ -18,8 +18,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 import * as Misskey from 'misskey-js';
 import { toUnicode } from 'punycode/';
 import MkCondensedLine from './MkCondensedLine.vue';
-import { host as hostRaw } from '@/config';
-import { defaultStore } from '@/store';
+import { host as hostRaw } from '@/config.js';
+import { defaultStore } from '@/store.js';
 
 defineProps<{
 	user: Misskey.entities.UserDetailed;
diff --git a/packages/frontend/src/components/global/MkAd.stories.impl.ts b/packages/frontend/src/components/global/MkAd.stories.impl.ts
index 0354a84f49..360bc88b4a 100644
--- a/packages/frontend/src/components/global/MkAd.stories.impl.ts
+++ b/packages/frontend/src/components/global/MkAd.stories.impl.ts
@@ -8,7 +8,7 @@ import { expect } from '@storybook/jest';
 import { userEvent, waitFor, within } from '@storybook/testing-library';
 import { StoryObj } from '@storybook/vue3';
 import MkAd from './MkAd.vue';
-import { i18n } from '@/i18n';
+import { i18n } from '@/i18n.js';
 
 let lock: Promise<undefined> | undefined;
 
diff --git a/packages/frontend/src/components/global/MkAd.vue b/packages/frontend/src/components/global/MkAd.vue
index 1d43e9fead..421fe99127 100644
--- a/packages/frontend/src/components/global/MkAd.vue
+++ b/packages/frontend/src/components/global/MkAd.vue
@@ -33,13 +33,13 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { ref } from 'vue';
-import { i18n } from '@/i18n';
-import { instance } from '@/instance';
-import { host } from '@/config';
+import { i18n } from '@/i18n.js';
+import { instance } from '@/instance.js';
+import { host } from '@/config.js';
 import MkButton from '@/components/MkButton.vue';
-import { defaultStore } from '@/store';
-import * as os from '@/os';
-import { $i } from '@/account';
+import { defaultStore } from '@/store.js';
+import * as os from '@/os.js';
+import { $i } from '@/account.js';
 
 type Ad = (typeof instance)['ads'][number];
 
diff --git a/packages/frontend/src/components/global/MkAvatar.vue b/packages/frontend/src/components/global/MkAvatar.vue
index 7c344ccf7c..27c25b9490 100644
--- a/packages/frontend/src/components/global/MkAvatar.vue
+++ b/packages/frontend/src/components/global/MkAvatar.vue
@@ -31,11 +31,11 @@ import { watch } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkImgWithBlurhash from '../MkImgWithBlurhash.vue';
 import MkA from './MkA.vue';
-import { getStaticImageUrl } from '@/scripts/media-proxy';
-import { extractAvgColorFromBlurhash } from '@/scripts/extract-avg-color-from-blurhash';
-import { acct, userPage } from '@/filters/user';
+import { getStaticImageUrl } from '@/scripts/media-proxy.js';
+import { extractAvgColorFromBlurhash } from '@/scripts/extract-avg-color-from-blurhash.js';
+import { acct, userPage } from '@/filters/user.js';
 import MkUserOnlineIndicator from '@/components/MkUserOnlineIndicator.vue';
-import { defaultStore } from '@/store';
+import { defaultStore } from '@/store.js';
 
 const animation = $ref(defaultStore.state.animation);
 const squareAvatars = $ref(defaultStore.state.squareAvatars);
diff --git a/packages/frontend/src/components/global/MkCustomEmoji.vue b/packages/frontend/src/components/global/MkCustomEmoji.vue
index bef7c59d77..063b122f8b 100644
--- a/packages/frontend/src/components/global/MkCustomEmoji.vue
+++ b/packages/frontend/src/components/global/MkCustomEmoji.vue
@@ -10,9 +10,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { computed } from 'vue';
-import { getProxiedImageUrl, getStaticImageUrl } from '@/scripts/media-proxy';
-import { defaultStore } from '@/store';
-import { customEmojisMap } from '@/custom-emojis';
+import { getProxiedImageUrl, getStaticImageUrl } from '@/scripts/media-proxy.js';
+import { defaultStore } from '@/store.js';
+import { customEmojisMap } from '@/custom-emojis.js';
 
 const props = defineProps<{
 	name: string;
diff --git a/packages/frontend/src/components/global/MkEmoji.vue b/packages/frontend/src/components/global/MkEmoji.vue
index 00550cd7bb..e06549a891 100644
--- a/packages/frontend/src/components/global/MkEmoji.vue
+++ b/packages/frontend/src/components/global/MkEmoji.vue
@@ -11,9 +11,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { computed } from 'vue';
-import { char2twemojiFilePath, char2fluentEmojiFilePath } from '@/scripts/emoji-base';
-import { defaultStore } from '@/store';
-import { getEmojiName } from '@/scripts/emojilist';
+import { char2twemojiFilePath, char2fluentEmojiFilePath } from '@/scripts/emoji-base.js';
+import { defaultStore } from '@/store.js';
+import { getEmojiName } from '@/scripts/emojilist.js';
 
 const props = defineProps<{
 	emoji: string;
diff --git a/packages/frontend/src/components/global/MkError.vue b/packages/frontend/src/components/global/MkError.vue
index ade05febb2..7181ae61a1 100644
--- a/packages/frontend/src/components/global/MkError.vue
+++ b/packages/frontend/src/components/global/MkError.vue
@@ -15,9 +15,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import MkButton from '@/components/MkButton.vue';
-import { i18n } from '@/i18n';
-import { defaultStore } from '@/store';
-import { serverErrorImageUrl } from '@/instance';
+import { i18n } from '@/i18n.js';
+import { defaultStore } from '@/store.js';
+import { serverErrorImageUrl } from '@/instance.js';
 
 const emit = defineEmits<{
 	(ev: 'retry'): void;
diff --git a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts
index f9e502dc44..2ae3fc89c8 100644
--- a/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts
+++ b/packages/frontend/src/components/global/MkMisskeyFlavoredMarkdown.ts
@@ -15,8 +15,8 @@ import MkCode from '@/components/MkCode.vue';
 import MkGoogle from '@/components/MkGoogle.vue';
 import MkSparkle from '@/components/MkSparkle.vue';
 import MkA from '@/components/global/MkA.vue';
-import { host } from '@/config';
-import { defaultStore } from '@/store';
+import { host } from '@/config.js';
+import { defaultStore } from '@/store.js';
 
 const QUOTE_STYLE = `
 display: block;
diff --git a/packages/frontend/src/components/global/MkPageHeader.tabs.vue b/packages/frontend/src/components/global/MkPageHeader.tabs.vue
index 2fb3b99944..e62967963f 100644
--- a/packages/frontend/src/components/global/MkPageHeader.tabs.vue
+++ b/packages/frontend/src/components/global/MkPageHeader.tabs.vue
@@ -54,7 +54,7 @@ export type Tab = {
 
 <script lang="ts" setup>
 import { onMounted, onUnmounted, watch, nextTick, shallowRef } from 'vue';
-import { defaultStore } from '@/store';
+import { defaultStore } from '@/store.js';
 
 const props = withDefaults(defineProps<{
 	tabs?: Tab[];
diff --git a/packages/frontend/src/components/global/MkPageHeader.vue b/packages/frontend/src/components/global/MkPageHeader.vue
index c29407f727..ef8bfbbbfc 100644
--- a/packages/frontend/src/components/global/MkPageHeader.vue
+++ b/packages/frontend/src/components/global/MkPageHeader.vue
@@ -44,10 +44,10 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { onMounted, onUnmounted, ref, inject } from 'vue';
 import tinycolor from 'tinycolor2';
 import XTabs, { Tab } from './MkPageHeader.tabs.vue';
-import { scrollToTop } from '@/scripts/scroll';
+import { scrollToTop } from '@/scripts/scroll.js';
 import { globalEvents } from '@/events';
-import { injectPageMetadata } from '@/scripts/page-metadata';
-import { $i, openAccountMenu as openAccountMenu_ } from '@/account';
+import { injectPageMetadata } from '@/scripts/page-metadata.js';
+import { $i, openAccountMenu as openAccountMenu_ } from '@/account.js';
 
 const props = withDefaults(defineProps<{
 	tabs?: Tab[];
diff --git a/packages/frontend/src/components/global/MkSpacer.vue b/packages/frontend/src/components/global/MkSpacer.vue
index 17e1846b0b..a384e06f77 100644
--- a/packages/frontend/src/components/global/MkSpacer.vue
+++ b/packages/frontend/src/components/global/MkSpacer.vue
@@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { inject } from 'vue';
-import { deviceKind } from '@/scripts/device-kind';
+import { deviceKind } from '@/scripts/device-kind.js';
 
 const props = withDefaults(defineProps<{
 	contentMax?: number | null;
diff --git a/packages/frontend/src/components/global/MkTime.stories.impl.ts b/packages/frontend/src/components/global/MkTime.stories.impl.ts
index 5105dc0cdc..0eeefa4859 100644
--- a/packages/frontend/src/components/global/MkTime.stories.impl.ts
+++ b/packages/frontend/src/components/global/MkTime.stories.impl.ts
@@ -7,8 +7,8 @@
 import { expect } from '@storybook/jest';
 import { StoryObj } from '@storybook/vue3';
 import MkTime from './MkTime.vue';
-import { i18n } from '@/i18n';
-import { dateTimeFormat } from '@/scripts/intl-const';
+import { i18n } from '@/i18n.js';
+import { dateTimeFormat } from '@/scripts/intl-const.js';
 const now = new Date('2023-04-01T00:00:00.000Z');
 const future = new Date(8640000000000000);
 const oneHourAgo = new Date(now.getTime() - 3600000);
diff --git a/packages/frontend/src/components/global/MkTime.vue b/packages/frontend/src/components/global/MkTime.vue
index 4b91ba0f7a..d06aa036e7 100644
--- a/packages/frontend/src/components/global/MkTime.vue
+++ b/packages/frontend/src/components/global/MkTime.vue
@@ -15,8 +15,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import isChromatic from 'chromatic/isChromatic';
 import { onMounted, onUnmounted } from 'vue';
-import { i18n } from '@/i18n';
-import { dateTimeFormat } from '@/scripts/intl-const';
+import { i18n } from '@/i18n.js';
+import { dateTimeFormat } from '@/scripts/intl-const.js';
 
 const props = withDefaults(defineProps<{
 	time: Date | string | number | null;
diff --git a/packages/frontend/src/components/global/MkUrl.vue b/packages/frontend/src/components/global/MkUrl.vue
index 6bcfe3e1a5..a8832cde01 100644
--- a/packages/frontend/src/components/global/MkUrl.vue
+++ b/packages/frontend/src/components/global/MkUrl.vue
@@ -26,10 +26,10 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { defineAsyncComponent, ref } from 'vue';
 import { toUnicode as decodePunycode } from 'punycode/';
-import { url as local } from '@/config';
-import * as os from '@/os';
-import { useTooltip } from '@/scripts/use-tooltip';
-import { safeURIDecode } from '@/scripts/safe-uri-decode';
+import { url as local } from '@/config.js';
+import * as os from '@/os.js';
+import { useTooltip } from '@/scripts/use-tooltip.js';
+import { safeURIDecode } from '@/scripts/safe-uri-decode.js';
 
 const props = defineProps<{
 	url: string;
diff --git a/packages/frontend/src/components/global/RouterView.vue b/packages/frontend/src/components/global/RouterView.vue
index 5a4679f69f..99f42f4fcb 100644
--- a/packages/frontend/src/components/global/RouterView.vue
+++ b/packages/frontend/src/components/global/RouterView.vue
@@ -18,7 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { inject, onBeforeUnmount, provide } from 'vue';
 import { Resolved, Router } from '@/nirax';
-import { defaultStore } from '@/store';
+import { defaultStore } from '@/store.js';
 
 const props = defineProps<{
 	router?: Router;
diff --git a/packages/frontend/src/components/page/page.note.vue b/packages/frontend/src/components/page/page.note.vue
index 82ffcfc867..d052ce2c1f 100644
--- a/packages/frontend/src/components/page/page.note.vue
+++ b/packages/frontend/src/components/page/page.note.vue
@@ -16,7 +16,7 @@ import * as Misskey from 'misskey-js';
 import { NoteBlock } from './block.type';
 import MkNote from '@/components/MkNote.vue';
 import MkNoteDetailed from '@/components/MkNoteDetailed.vue';
-import * as os from '@/os';
+import * as os from '@/os.js';
 
 const props = defineProps<{
 	block: NoteBlock,
diff --git a/packages/frontend/src/components/page/page.text.vue b/packages/frontend/src/components/page/page.text.vue
index 9cb32acb81..35021be95e 100644
--- a/packages/frontend/src/components/page/page.text.vue
+++ b/packages/frontend/src/components/page/page.text.vue
@@ -15,8 +15,8 @@ import { defineAsyncComponent } from 'vue';
 import * as mfm from 'mfm-js';
 import * as Misskey from 'misskey-js';
 import { TextBlock } from './block.type';
-import { extractUrlFromMfm } from '@/scripts/extract-url-from-mfm';
-import { $i } from '@/account';
+import { extractUrlFromMfm } from '@/scripts/extract-url-from-mfm.js';
+import { $i } from '@/account.js';
 
 const MkUrlPreview = defineAsyncComponent(() => import('@/components/MkUrlPreview.vue'));
 
diff --git a/packages/frontend/src/config.ts b/packages/frontend/src/config.ts
index 9ce3f8fa15..60fc8c9d34 100644
--- a/packages/frontend/src/config.ts
+++ b/packages/frontend/src/config.ts
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { miLocalStorage } from './local-storage';
+import { miLocalStorage } from '@/local-storage.js';
 
 const address = new URL(location.href);
 const siteName = document.querySelector<HTMLMetaElement>('meta[property="og:site_name"]')?.content;
diff --git a/packages/frontend/src/custom-emojis.ts b/packages/frontend/src/custom-emojis.ts
index 771f21c5b4..8ecd1bd2eb 100644
--- a/packages/frontend/src/custom-emojis.ts
+++ b/packages/frontend/src/custom-emojis.ts
@@ -5,9 +5,9 @@
 
 import { shallowRef, computed, markRaw, watch } from 'vue';
 import * as Misskey from 'misskey-js';
-import { api, apiGet } from './os';
-import { useStream } from '@/stream';
-import { get, set } from '@/scripts/idb-proxy';
+import { api, apiGet } from '@/os.js';
+import { useStream } from '@/stream.js';
+import { get, set } from '@/scripts/idb-proxy.js';
 
 const storageCache = await get('emojis');
 export const customEmojis = shallowRef<Misskey.entities.CustomEmoji[]>(Array.isArray(storageCache) ? storageCache : []);
diff --git a/packages/frontend/src/directives/click-anime.ts b/packages/frontend/src/directives/click-anime.ts
index f9c1fb4d21..2b3cdb27a5 100644
--- a/packages/frontend/src/directives/click-anime.ts
+++ b/packages/frontend/src/directives/click-anime.ts
@@ -4,7 +4,7 @@
  */
 
 import { Directive } from 'vue';
-import { defaultStore } from '@/store';
+import { defaultStore } from '@/store.js';
 
 export default {
 	mounted(el: HTMLElement, binding, vn) {
diff --git a/packages/frontend/src/directives/follow-append.ts b/packages/frontend/src/directives/follow-append.ts
index 1da28c718f..ae3e31e291 100644
--- a/packages/frontend/src/directives/follow-append.ts
+++ b/packages/frontend/src/directives/follow-append.ts
@@ -4,7 +4,7 @@
  */
 
 import { Directive } from 'vue';
-import { getScrollContainer, getScrollPosition } from '@/scripts/scroll';
+import { getScrollContainer, getScrollPosition } from '@/scripts/scroll.js';
 
 export default {
 	mounted(src, binding, vn) {
diff --git a/packages/frontend/src/directives/ripple.ts b/packages/frontend/src/directives/ripple.ts
index 4a11fbd406..cabd155c87 100644
--- a/packages/frontend/src/directives/ripple.ts
+++ b/packages/frontend/src/directives/ripple.ts
@@ -4,7 +4,7 @@
  */
 
 import MkRippleEffect from '@/components/MkRippleEffect.vue';
-import { popup } from '@/os';
+import { popup } from '@/os.js';
 
 export default {
 	mounted(el, binding, vn) {
diff --git a/packages/frontend/src/directives/tooltip.ts b/packages/frontend/src/directives/tooltip.ts
index f0e3cdabe4..5d6ec2928b 100644
--- a/packages/frontend/src/directives/tooltip.ts
+++ b/packages/frontend/src/directives/tooltip.ts
@@ -7,8 +7,8 @@
 // ただディレクティブ内でonUnmountedなどのcomposition api使えるのか不明
 
 import { defineAsyncComponent, Directive, ref } from 'vue';
-import { isTouchUsing } from '@/scripts/touch';
-import { popup, alert } from '@/os';
+import { isTouchUsing } from '@/scripts/touch.js';
+import { popup, alert } from '@/os.js';
 
 const start = isTouchUsing ? 'touchstart' : 'mouseenter';
 const end = isTouchUsing ? 'touchend' : 'mouseleave';
diff --git a/packages/frontend/src/directives/user-preview.ts b/packages/frontend/src/directives/user-preview.ts
index 5fd65b0587..e0fd10047a 100644
--- a/packages/frontend/src/directives/user-preview.ts
+++ b/packages/frontend/src/directives/user-preview.ts
@@ -4,7 +4,7 @@
  */
 
 import { defineAsyncComponent, Directive, ref } from 'vue';
-import { popup } from '@/os';
+import { popup } from '@/os.js';
 
 export class UserPreview {
 	private el;
diff --git a/packages/frontend/src/filters/date.ts b/packages/frontend/src/filters/date.ts
index 1d878e2970..23541f1094 100644
--- a/packages/frontend/src/filters/date.ts
+++ b/packages/frontend/src/filters/date.ts
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { dateTimeFormat } from '@/scripts/intl-const';
+import { dateTimeFormat } from '@/scripts/intl-const.js';
 
 export default (d: Date | number | undefined) => dateTimeFormat.format(d);
 export const dateString = (d: string) => dateTimeFormat.format(new Date(d));
diff --git a/packages/frontend/src/filters/number.ts b/packages/frontend/src/filters/number.ts
index 6e0fde15b0..d0e4f4991f 100644
--- a/packages/frontend/src/filters/number.ts
+++ b/packages/frontend/src/filters/number.ts
@@ -3,6 +3,6 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { numberFormat } from '@/scripts/intl-const';
+import { numberFormat } from '@/scripts/intl-const.js';
 
 export default n => n == null ? 'N/A' : numberFormat.format(n);
diff --git a/packages/frontend/src/filters/user.ts b/packages/frontend/src/filters/user.ts
index a8e697699b..a7a13bec6e 100644
--- a/packages/frontend/src/filters/user.ts
+++ b/packages/frontend/src/filters/user.ts
@@ -5,7 +5,7 @@
 
 import * as Misskey from 'misskey-js';
 import * as Misskey from 'misskey-js';
-import { url } from '@/config';
+import { url } from '@/config.js';
 
 export const acct = (user: misskey.Acct) => {
 	return Misskey.acct.toString(user);
diff --git a/packages/frontend/src/i18n.ts b/packages/frontend/src/i18n.ts
index 995171b10c..858db74dac 100644
--- a/packages/frontend/src/i18n.ts
+++ b/packages/frontend/src/i18n.ts
@@ -4,9 +4,9 @@
  */
 
 import { markRaw } from 'vue';
-import type { Locale } from '../../../locales';
-import { locale } from '@/config';
-import { I18n } from '@/scripts/i18n';
+import type { Locale } from '../../../locales/index.js';
+import { locale } from '@/config.js';
+import { I18n } from '@/scripts/i18n.js';
 
 export const i18n = markRaw(new I18n<Locale>(locale));
 
diff --git a/packages/frontend/src/instance.ts b/packages/frontend/src/instance.ts
index 83ec917594..cbfd95951c 100644
--- a/packages/frontend/src/instance.ts
+++ b/packages/frontend/src/instance.ts
@@ -5,9 +5,9 @@
 
 import { computed, reactive } from 'vue';
 import * as Misskey from 'misskey-js';
-import { api } from './os';
-import { miLocalStorage } from './local-storage';
-import { DEFAULT_INFO_IMAGE_URL, DEFAULT_NOT_FOUND_IMAGE_URL, DEFAULT_SERVER_ERROR_IMAGE_URL } from '@/const';
+import { api } from '@/os.js';
+import { miLocalStorage } from '@/local-storage.js';
+import { DEFAULT_INFO_IMAGE_URL, DEFAULT_NOT_FOUND_IMAGE_URL, DEFAULT_SERVER_ERROR_IMAGE_URL } from '@/const.js';
 
 // TODO: 他のタブと永続化されたstateを同期
 
diff --git a/packages/frontend/src/navbar.ts b/packages/frontend/src/navbar.ts
index 4719678fcc..7f182a98f7 100644
--- a/packages/frontend/src/navbar.ts
+++ b/packages/frontend/src/navbar.ts
@@ -4,14 +4,14 @@
  */
 
 import { computed, reactive } from 'vue';
-import { $i } from './account';
-import { miLocalStorage } from './local-storage';
-import { openInstanceMenu } from './ui/_common_/common';
-import { lookup } from './scripts/lookup';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
-import { ui } from '@/config';
-import { unisonReload } from '@/scripts/unison-reload';
+import { $i } from '@/account.js';
+import { miLocalStorage } from '@/local-storage.js';
+import { openInstanceMenu } from '@/ui/_common_/common.js';
+import { lookup } from '@/scripts/lookup.js';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
+import { ui } from '@/config.js';
+import { unisonReload } from '@/scripts/unison-reload.js';
 
 export const navbarItemDef = reactive({
 	notifications: {
diff --git a/packages/frontend/src/nirax.ts b/packages/frontend/src/nirax.ts
index 66ae39fa56..9755bdcb18 100644
--- a/packages/frontend/src/nirax.ts
+++ b/packages/frontend/src/nirax.ts
@@ -7,7 +7,7 @@
 
 import { EventEmitter } from 'eventemitter3';
 import { Component, onMounted, shallowRef, ShallowRef } from 'vue';
-import { safeURIDecode } from '@/scripts/safe-uri-decode';
+import { safeURIDecode } from '@/scripts/safe-uri-decode.js';
 
 type RouteDef = {
 	path: string;
diff --git a/packages/frontend/src/os.ts b/packages/frontend/src/os.ts
index ede13e2a79..4cd1c3ef31 100644
--- a/packages/frontend/src/os.ts
+++ b/packages/frontend/src/os.ts
@@ -5,13 +5,13 @@
 
 // TODO: なんでもかんでもos.tsに突っ込むのやめたいのでよしなに分割する
 
-import { pendingApiRequestsCount, api, apiGet } from '@/scripts/api';
+import { pendingApiRequestsCount, api, apiGet } from '@/scripts/api.js';
 export { pendingApiRequestsCount, api, apiGet };
 import { Component, markRaw, Ref, ref, defineAsyncComponent } from 'vue';
 import { EventEmitter } from 'eventemitter3';
 import insertTextAtCursor from 'insert-text-at-cursor';
 import * as Misskey from 'misskey-js';
-import { i18n } from './i18n';
+import { i18n } from '@/i18n.js';
 import MkPostFormDialog from '@/components/MkPostFormDialog.vue';
 import MkWaitingDialog from '@/components/MkWaitingDialog.vue';
 import MkPageWindow from '@/components/MkPageWindow.vue';
@@ -21,9 +21,9 @@ import MkEmojiPickerDialog from '@/components/MkEmojiPickerDialog.vue';
 import MkEmojiPickerWindow from '@/components/MkEmojiPickerWindow.vue';
 import MkPopupMenu from '@/components/MkPopupMenu.vue';
 import MkContextMenu from '@/components/MkContextMenu.vue';
-import { MenuItem } from '@/types/menu';
-import copyToClipboard from './scripts/copy-to-clipboard';
-import { showMovedDialog } from './scripts/show-moved-dialog';
+import { MenuItem } from '@/types/menu.js';
+import copyToClipboard from '@/scripts/copy-to-clipboard.js';
+import { showMovedDialog } from '@/scripts/show-moved-dialog.js';
 
 export const openingWindowsCount = ref(0);
 
diff --git a/packages/frontend/src/pages/_error_.vue b/packages/frontend/src/pages/_error_.vue
index 8a5555d454..7a3e2f444b 100644
--- a/packages/frontend/src/pages/_error_.vue
+++ b/packages/frontend/src/pages/_error_.vue
@@ -28,14 +28,14 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkButton from '@/components/MkButton.vue';
-import { version } from '@/config';
-import * as os from '@/os';
-import { unisonReload } from '@/scripts/unison-reload';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
-import { miLocalStorage } from '@/local-storage';
-import { defaultStore } from '@/store';
-import { serverErrorImageUrl } from '@/instance';
+import { version } from '@/config.js';
+import * as os from '@/os.js';
+import { unisonReload } from '@/scripts/unison-reload.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { miLocalStorage } from '@/local-storage.js';
+import { defaultStore } from '@/store.js';
+import { serverErrorImageUrl } from '@/instance.js';
 
 const props = withDefaults(defineProps<{
 	error?: Error;
diff --git a/packages/frontend/src/pages/about-misskey.vue b/packages/frontend/src/pages/about-misskey.vue
index 7cc6040faa..d734666bff 100644
--- a/packages/frontend/src/pages/about-misskey.vue
+++ b/packages/frontend/src/pages/about-misskey.vue
@@ -111,18 +111,18 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { nextTick, onBeforeUnmount } from 'vue';
-import { version } from '@/config';
+import { version } from '@/config.js';
 import FormLink from '@/components/form/link.vue';
 import FormSection from '@/components/form/section.vue';
 import MkButton from '@/components/MkButton.vue';
 import MkLink from '@/components/MkLink.vue';
-import { physics } from '@/scripts/physics';
-import { i18n } from '@/i18n';
-import { defaultStore } from '@/store';
-import * as os from '@/os';
-import { definePageMetadata } from '@/scripts/page-metadata';
-import { claimAchievement, claimedAchievements } from '@/scripts/achievements';
-import { $i } from '@/account';
+import { physics } from '@/scripts/physics.js';
+import { i18n } from '@/i18n.js';
+import { defaultStore } from '@/store.js';
+import * as os from '@/os.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { claimAchievement, claimedAchievements } from '@/scripts/achievements.js';
+import { $i } from '@/account.js';
 
 const patronsWithIcon = [{
 	name: 'カイヤン',
diff --git a/packages/frontend/src/pages/about.emojis.vue b/packages/frontend/src/pages/about.emojis.vue
index d014a6eb7d..c8f56f1674 100644
--- a/packages/frontend/src/pages/about.emojis.vue
+++ b/packages/frontend/src/pages/about.emojis.vue
@@ -42,9 +42,9 @@ import XEmoji from './emojis.emoji.vue';
 import MkButton from '@/components/MkButton.vue';
 import MkInput from '@/components/MkInput.vue';
 import MkFoldableSection from '@/components/MkFoldableSection.vue';
-import { customEmojis, customEmojiCategories, getCustomEmojiTags } from '@/custom-emojis';
-import { i18n } from '@/i18n';
-import { $i } from '@/account';
+import { customEmojis, customEmojiCategories, getCustomEmojiTags } from '@/custom-emojis.js';
+import { i18n } from '@/i18n.js';
+import { $i } from '@/account.js';
 
 const customEmojiTags = getCustomEmojiTags();
 let q = $ref('');
diff --git a/packages/frontend/src/pages/about.federation.vue b/packages/frontend/src/pages/about.federation.vue
index 0c40656058..2aa5af939b 100644
--- a/packages/frontend/src/pages/about.federation.vue
+++ b/packages/frontend/src/pages/about.federation.vue
@@ -56,7 +56,7 @@ import MkSelect from '@/components/MkSelect.vue';
 import MkPagination, { Paging } from '@/components/MkPagination.vue';
 import MkInstanceCardMini from '@/components/MkInstanceCardMini.vue';
 import FormSplit from '@/components/form/split.vue';
-import { i18n } from '@/i18n';
+import { i18n } from '@/i18n.js';
 
 let host = $ref('');
 let state = $ref('federating');
diff --git a/packages/frontend/src/pages/about.vue b/packages/frontend/src/pages/about.vue
index 0ff9463841..02768b0774 100644
--- a/packages/frontend/src/pages/about.vue
+++ b/packages/frontend/src/pages/about.vue
@@ -101,7 +101,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { computed, watch } from 'vue';
 import XEmojis from './about.emojis.vue';
 import XFederation from './about.federation.vue';
-import { version, host } from '@/config';
+import { version, host } from '@/config.js';
 import FormLink from '@/components/form/link.vue';
 import FormSection from '@/components/form/section.vue';
 import FormSuspense from '@/components/form/suspense.vue';
@@ -109,12 +109,12 @@ import FormSplit from '@/components/form/split.vue';
 import MkFolder from '@/components/MkFolder.vue';
 import MkKeyValue from '@/components/MkKeyValue.vue';
 import MkInstanceStats from '@/components/MkInstanceStats.vue';
-import * as os from '@/os';
-import number from '@/filters/number';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
-import { claimAchievement } from '@/scripts/achievements';
-import { instance } from '@/instance';
+import * as os from '@/os.js';
+import number from '@/filters/number.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { claimAchievement } from '@/scripts/achievements.js';
+import { instance } from '@/instance.js';
 
 const props = withDefaults(defineProps<{
 	initialTab?: string;
diff --git a/packages/frontend/src/pages/achievements.vue b/packages/frontend/src/pages/achievements.vue
index 8f557e3878..188ec5caa8 100644
--- a/packages/frontend/src/pages/achievements.vue
+++ b/packages/frontend/src/pages/achievements.vue
@@ -15,10 +15,10 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { onActivated, onDeactivated, onMounted, onUnmounted } from 'vue';
 import MkAchievements from '@/components/MkAchievements.vue';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
-import { $i } from '@/account';
-import { claimAchievement } from '@/scripts/achievements';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { $i } from '@/account.js';
+import { claimAchievement } from '@/scripts/achievements.js';
 
 let timer: number | null;
 
diff --git a/packages/frontend/src/pages/admin-file.vue b/packages/frontend/src/pages/admin-file.vue
index 4083c02049..4ce0f2936c 100644
--- a/packages/frontend/src/pages/admin-file.vue
+++ b/packages/frontend/src/pages/admin-file.vue
@@ -76,11 +76,11 @@ import MkKeyValue from '@/components/MkKeyValue.vue';
 import FormSection from '@/components/form/section.vue';
 import MkUserCardMini from '@/components/MkUserCardMini.vue';
 import MkInfo from '@/components/MkInfo.vue';
-import bytes from '@/filters/bytes';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
-import { iAmAdmin, iAmModerator } from '@/account';
+import bytes from '@/filters/bytes.js';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { iAmAdmin, iAmModerator } from '@/account.js';
 
 let tab = $ref('overview');
 let file: any = $ref(null);
diff --git a/packages/frontend/src/pages/admin-user.vue b/packages/frontend/src/pages/admin-user.vue
index 1d18819f1d..5d671acf31 100644
--- a/packages/frontend/src/pages/admin-user.vue
+++ b/packages/frontend/src/pages/admin-user.vue
@@ -214,12 +214,12 @@ import MkSelect from '@/components/MkSelect.vue';
 import FormSuspense from '@/components/form/suspense.vue';
 import MkFileListForAdmin from '@/components/MkFileListForAdmin.vue';
 import MkInfo from '@/components/MkInfo.vue';
-import * as os from '@/os';
-import { url } from '@/config';
-import { userPage, acct } from '@/filters/user';
-import { definePageMetadata } from '@/scripts/page-metadata';
-import { i18n } from '@/i18n';
-import { iAmAdmin, $i } from '@/account';
+import * as os from '@/os.js';
+import { url } from '@/config.js';
+import { userPage, acct } from '@/filters/user.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { i18n } from '@/i18n.js';
+import { iAmAdmin, $i } from '@/account.js';
 import MkRolePreview from '@/components/MkRolePreview.vue';
 import MkPagination, { Paging } from '@/components/MkPagination.vue';
 
diff --git a/packages/frontend/src/pages/admin/RolesEditorFormula.vue b/packages/frontend/src/pages/admin/RolesEditorFormula.vue
index 87d0d276ee..0112c9eb7f 100644
--- a/packages/frontend/src/pages/admin/RolesEditorFormula.vue
+++ b/packages/frontend/src/pages/admin/RolesEditorFormula.vue
@@ -60,8 +60,8 @@ import { v4 as uuid } from 'uuid';
 import MkInput from '@/components/MkInput.vue';
 import MkSelect from '@/components/MkSelect.vue';
 import MkButton from '@/components/MkButton.vue';
-import { i18n } from '@/i18n';
-import { deepClone } from '@/scripts/clone';
+import { i18n } from '@/i18n.js';
+import { deepClone } from '@/scripts/clone.js';
 
 const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default));
 
diff --git a/packages/frontend/src/pages/admin/_header_.vue b/packages/frontend/src/pages/admin/_header_.vue
index 93aead5484..503fb2af9c 100644
--- a/packages/frontend/src/pages/admin/_header_.vue
+++ b/packages/frontend/src/pages/admin/_header_.vue
@@ -35,11 +35,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { computed, onMounted, onUnmounted, ref, shallowRef, watch, nextTick } from 'vue';
 import tinycolor from 'tinycolor2';
-import { popupMenu } from '@/os';
-import { scrollToTop } from '@/scripts/scroll';
+import { popupMenu } from '@/os.js';
+import { scrollToTop } from '@/scripts/scroll.js';
 import MkButton from '@/components/MkButton.vue';
 import { globalEvents } from '@/events';
-import { injectPageMetadata } from '@/scripts/page-metadata';
+import { injectPageMetadata } from '@/scripts/page-metadata.js';
 
 type Tab = {
 	key?: string | null;
diff --git a/packages/frontend/src/pages/admin/abuses.vue b/packages/frontend/src/pages/admin/abuses.vue
index 959341ee64..875702ee7e 100644
--- a/packages/frontend/src/pages/admin/abuses.vue
+++ b/packages/frontend/src/pages/admin/abuses.vue
@@ -58,8 +58,8 @@ import XHeader from './_header_.vue';
 import MkSelect from '@/components/MkSelect.vue';
 import MkPagination from '@/components/MkPagination.vue';
 import XAbuseReport from '@/components/MkAbuseReport.vue';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 
 let reports = $shallowRef<InstanceType<typeof MkPagination>>();
 
diff --git a/packages/frontend/src/pages/admin/ads.vue b/packages/frontend/src/pages/admin/ads.vue
index 74cc9ef54b..cd9d86ca45 100644
--- a/packages/frontend/src/pages/admin/ads.vue
+++ b/packages/frontend/src/pages/admin/ads.vue
@@ -76,9 +76,9 @@ import MkTextarea from '@/components/MkTextarea.vue';
 import MkRadios from '@/components/MkRadios.vue';
 import MkFolder from '@/components/MkFolder.vue';
 import FormSplit from '@/components/form/split.vue';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 
 let ads: any[] = $ref([]);
 
diff --git a/packages/frontend/src/pages/admin/announcements.vue b/packages/frontend/src/pages/admin/announcements.vue
index a0a7b569f3..f12c068a10 100644
--- a/packages/frontend/src/pages/admin/announcements.vue
+++ b/packages/frontend/src/pages/admin/announcements.vue
@@ -74,9 +74,9 @@ import MkTextarea from '@/components/MkTextarea.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
 import MkRadios from '@/components/MkRadios.vue';
 import MkInfo from '@/components/MkInfo.vue';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 import MkFolder from '@/components/MkFolder.vue';
 
 let announcements: any[] = $ref([]);
diff --git a/packages/frontend/src/pages/admin/bot-protection.vue b/packages/frontend/src/pages/admin/bot-protection.vue
index c26bfa1004..7f5709feb9 100644
--- a/packages/frontend/src/pages/admin/bot-protection.vue
+++ b/packages/frontend/src/pages/admin/bot-protection.vue
@@ -70,9 +70,9 @@ import MkInput from '@/components/MkInput.vue';
 import MkButton from '@/components/MkButton.vue';
 import FormSuspense from '@/components/form/suspense.vue';
 import FormSlot from '@/components/form/slot.vue';
-import * as os from '@/os';
-import { fetchInstance } from '@/instance';
-import { i18n } from '@/i18n';
+import * as os from '@/os.js';
+import { fetchInstance } from '@/instance.js';
+import { i18n } from '@/i18n.js';
 
 const MkCaptcha = defineAsyncComponent(() => import('@/components/MkCaptcha.vue'));
 
diff --git a/packages/frontend/src/pages/admin/branding.vue b/packages/frontend/src/pages/admin/branding.vue
index 2f369380fe..d064dbd0a9 100644
--- a/packages/frontend/src/pages/admin/branding.vue
+++ b/packages/frontend/src/pages/admin/branding.vue
@@ -103,13 +103,13 @@ import MkTextarea from '@/components/MkTextarea.vue';
 import FormSection from '@/components/form/section.vue';
 import FormSplit from '@/components/form/split.vue';
 import FormSuspense from '@/components/form/suspense.vue';
-import * as os from '@/os';
-import { instance, fetchInstance } from '@/instance';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import * as os from '@/os.js';
+import { instance, fetchInstance } from '@/instance.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 import MkButton from '@/components/MkButton.vue';
 import MkColorInput from '@/components/MkColorInput.vue';
-import { host } from '@/config';
+import { host } from '@/config.js';
 
 let iconUrl: string | null = $ref(null);
 let app192IconUrl: string | null = $ref(null);
diff --git a/packages/frontend/src/pages/admin/database.vue b/packages/frontend/src/pages/admin/database.vue
index 8c38a62518..bba03deb4b 100644
--- a/packages/frontend/src/pages/admin/database.vue
+++ b/packages/frontend/src/pages/admin/database.vue
@@ -21,11 +21,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { } from 'vue';
 import FormSuspense from '@/components/form/suspense.vue';
 import MkKeyValue from '@/components/MkKeyValue.vue';
-import * as os from '@/os';
-import bytes from '@/filters/bytes';
-import number from '@/filters/number';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import * as os from '@/os.js';
+import bytes from '@/filters/bytes.js';
+import number from '@/filters/number.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 
 const databasePromiseFactory = () => os.api('admin/get-table-stats').then(res => Object.entries(res).sort((a, b) => b[1].size - a[1].size));
 
diff --git a/packages/frontend/src/pages/admin/email-settings.vue b/packages/frontend/src/pages/admin/email-settings.vue
index 8c19178a97..32294847bb 100644
--- a/packages/frontend/src/pages/admin/email-settings.vue
+++ b/packages/frontend/src/pages/admin/email-settings.vue
@@ -72,10 +72,10 @@ import FormInfo from '@/components/MkInfo.vue';
 import FormSuspense from '@/components/form/suspense.vue';
 import FormSplit from '@/components/form/split.vue';
 import FormSection from '@/components/form/section.vue';
-import * as os from '@/os';
-import { fetchInstance, instance } from '@/instance';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import * as os from '@/os.js';
+import { fetchInstance, instance } from '@/instance.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 import MkButton from '@/components/MkButton.vue';
 
 let enableEmail: boolean = $ref(false);
diff --git a/packages/frontend/src/pages/admin/federation.vue b/packages/frontend/src/pages/admin/federation.vue
index 7b92345bf0..7dc0b46946 100644
--- a/packages/frontend/src/pages/admin/federation.vue
+++ b/packages/frontend/src/pages/admin/federation.vue
@@ -64,8 +64,8 @@ import MkSelect from '@/components/MkSelect.vue';
 import MkPagination from '@/components/MkPagination.vue';
 import MkInstanceCardMini from '@/components/MkInstanceCardMini.vue';
 import FormSplit from '@/components/form/split.vue';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 
 let host = $ref('');
 let state = $ref('federating');
diff --git a/packages/frontend/src/pages/admin/files.vue b/packages/frontend/src/pages/admin/files.vue
index 3436a3937b..6fb6ef40f9 100644
--- a/packages/frontend/src/pages/admin/files.vue
+++ b/packages/frontend/src/pages/admin/files.vue
@@ -41,9 +41,9 @@ import XHeader from './_header_.vue';
 import MkInput from '@/components/MkInput.vue';
 import MkSelect from '@/components/MkSelect.vue';
 import MkFileListForAdmin from '@/components/MkFileListForAdmin.vue';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 
 let origin = $ref('local');
 let type = $ref(null);
diff --git a/packages/frontend/src/pages/admin/index.vue b/packages/frontend/src/pages/admin/index.vue
index 5bd6fce814..ab4e7620dd 100644
--- a/packages/frontend/src/pages/admin/index.vue
+++ b/packages/frontend/src/pages/admin/index.vue
@@ -29,14 +29,14 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { onActivated, onMounted, onUnmounted, provide, watch } from 'vue';
-import { i18n } from '@/i18n';
+import { i18n } from '@/i18n.js';
 import MkSuperMenu from '@/components/MkSuperMenu.vue';
 import MkInfo from '@/components/MkInfo.vue';
-import { instance } from '@/instance';
-import * as os from '@/os';
-import { lookupUser } from '@/scripts/lookup-user';
-import { useRouter } from '@/router';
-import { definePageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata';
+import { instance } from '@/instance.js';
+import * as os from '@/os.js';
+import { lookupUser } from '@/scripts/lookup-user.js';
+import { useRouter } from '@/router.js';
+import { definePageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata.js';
 
 const isEmpty = (x: string | null) => x == null || x === '';
 
diff --git a/packages/frontend/src/pages/admin/instance-block.vue b/packages/frontend/src/pages/admin/instance-block.vue
index b868b48bd8..9a0f5e026b 100644
--- a/packages/frontend/src/pages/admin/instance-block.vue
+++ b/packages/frontend/src/pages/admin/instance-block.vue
@@ -25,10 +25,10 @@ import XHeader from './_header_.vue';
 import MkButton from '@/components/MkButton.vue';
 import MkTextarea from '@/components/MkTextarea.vue';
 import FormSuspense from '@/components/form/suspense.vue';
-import * as os from '@/os';
-import { fetchInstance } from '@/instance';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import * as os from '@/os.js';
+import { fetchInstance } from '@/instance.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 
 let blockedHosts: string = $ref('');
 
diff --git a/packages/frontend/src/pages/admin/invites.vue b/packages/frontend/src/pages/admin/invites.vue
index 95f1c3b0ff..74dc2e4c36 100644
--- a/packages/frontend/src/pages/admin/invites.vue
+++ b/packages/frontend/src/pages/admin/invites.vue
@@ -57,8 +57,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { computed, ref, shallowRef } from 'vue';
 import XHeader from './_header_.vue';
-import { i18n } from '@/i18n';
-import * as os from '@/os';
+import { i18n } from '@/i18n.js';
+import * as os from '@/os.js';
 import MkButton from '@/components/MkButton.vue';
 import MkFolder from '@/components/MkFolder.vue';
 import MkSelect from '@/components/MkSelect.vue';
@@ -66,7 +66,7 @@ import MkInput from '@/components/MkInput.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
 import MkPagination, { Paging } from '@/components/MkPagination.vue';
 import MkInviteCode from '@/components/MkInviteCode.vue';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 
 const pagingComponent = shallowRef<InstanceType<typeof MkPagination>>();
 
diff --git a/packages/frontend/src/pages/admin/moderation.vue b/packages/frontend/src/pages/admin/moderation.vue
index 313d8412b2..46f92729e8 100644
--- a/packages/frontend/src/pages/admin/moderation.vue
+++ b/packages/frontend/src/pages/admin/moderation.vue
@@ -57,10 +57,10 @@ import MkTextarea from '@/components/MkTextarea.vue';
 import FormSection from '@/components/form/section.vue';
 import FormSplit from '@/components/form/split.vue';
 import FormSuspense from '@/components/form/suspense.vue';
-import * as os from '@/os';
-import { fetchInstance } from '@/instance';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import * as os from '@/os.js';
+import { fetchInstance } from '@/instance.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 import MkButton from '@/components/MkButton.vue';
 import FormLink from '@/components/form/link.vue';
 
diff --git a/packages/frontend/src/pages/admin/object-storage.vue b/packages/frontend/src/pages/admin/object-storage.vue
index 6ebf3f738f..dbcf135c80 100644
--- a/packages/frontend/src/pages/admin/object-storage.vue
+++ b/packages/frontend/src/pages/admin/object-storage.vue
@@ -89,10 +89,10 @@ import MkSwitch from '@/components/MkSwitch.vue';
 import MkInput from '@/components/MkInput.vue';
 import FormSuspense from '@/components/form/suspense.vue';
 import FormSplit from '@/components/form/split.vue';
-import * as os from '@/os';
-import { fetchInstance } from '@/instance';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import * as os from '@/os.js';
+import { fetchInstance } from '@/instance.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 import MkButton from '@/components/MkButton.vue';
 
 let useObjectStorage: boolean = $ref(false);
diff --git a/packages/frontend/src/pages/admin/other-settings.vue b/packages/frontend/src/pages/admin/other-settings.vue
index 7a4d3831a0..7574c9d7d9 100644
--- a/packages/frontend/src/pages/admin/other-settings.vue
+++ b/packages/frontend/src/pages/admin/other-settings.vue
@@ -46,10 +46,10 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { } from 'vue';
 import XHeader from './_header_.vue';
 import FormSuspense from '@/components/form/suspense.vue';
-import * as os from '@/os';
-import { fetchInstance } from '@/instance';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import * as os from '@/os.js';
+import { fetchInstance } from '@/instance.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 import MkSwitch from '@/components/MkSwitch.vue';
 
 let enableServerMachineStats: boolean = $ref(false);
diff --git a/packages/frontend/src/pages/admin/overview.active-users.vue b/packages/frontend/src/pages/admin/overview.active-users.vue
index d15e4651d7..8426c463d2 100644
--- a/packages/frontend/src/pages/admin/overview.active-users.vue
+++ b/packages/frontend/src/pages/admin/overview.active-users.vue
@@ -16,11 +16,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { onMounted } from 'vue';
 import { Chart } from 'chart.js';
 import gradient from 'chartjs-plugin-gradient';
-import * as os from '@/os';
-import { defaultStore } from '@/store';
-import { useChartTooltip } from '@/scripts/use-chart-tooltip';
-import { chartVLine } from '@/scripts/chart-vline';
-import { initChart } from '@/scripts/init-chart';
+import * as os from '@/os.js';
+import { defaultStore } from '@/store.js';
+import { useChartTooltip } from '@/scripts/use-chart-tooltip.js';
+import { chartVLine } from '@/scripts/chart-vline.js';
+import { initChart } from '@/scripts/init-chart.js';
 
 initChart();
 
diff --git a/packages/frontend/src/pages/admin/overview.ap-requests.vue b/packages/frontend/src/pages/admin/overview.ap-requests.vue
index d3e0267094..cd54041c34 100644
--- a/packages/frontend/src/pages/admin/overview.ap-requests.vue
+++ b/packages/frontend/src/pages/admin/overview.ap-requests.vue
@@ -23,12 +23,12 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { onMounted } from 'vue';
 import { Chart } from 'chart.js';
 import gradient from 'chartjs-plugin-gradient';
-import * as os from '@/os';
-import { useChartTooltip } from '@/scripts/use-chart-tooltip';
-import { chartVLine } from '@/scripts/chart-vline';
-import { defaultStore } from '@/store';
-import { alpha } from '@/scripts/color';
-import { initChart } from '@/scripts/init-chart';
+import * as os from '@/os.js';
+import { useChartTooltip } from '@/scripts/use-chart-tooltip.js';
+import { chartVLine } from '@/scripts/chart-vline.js';
+import { defaultStore } from '@/store.js';
+import { alpha } from '@/scripts/color.js';
+import { initChart } from '@/scripts/init-chart.js';
 
 initChart();
 
diff --git a/packages/frontend/src/pages/admin/overview.federation.vue b/packages/frontend/src/pages/admin/overview.federation.vue
index afb4b7f873..346a9c0258 100644
--- a/packages/frontend/src/pages/admin/overview.federation.vue
+++ b/packages/frontend/src/pages/admin/overview.federation.vue
@@ -48,11 +48,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { onMounted } from 'vue';
 import XPie from './overview.pie.vue';
-import * as os from '@/os';
-import number from '@/filters/number';
+import * as os from '@/os.js';
+import number from '@/filters/number.js';
 import MkNumberDiff from '@/components/MkNumberDiff.vue';
-import { i18n } from '@/i18n';
-import { useChartTooltip } from '@/scripts/use-chart-tooltip';
+import { i18n } from '@/i18n.js';
+import { useChartTooltip } from '@/scripts/use-chart-tooltip.js';
 
 let topSubInstancesForPie: any = $ref(null);
 let topPubInstancesForPie: any = $ref(null);
diff --git a/packages/frontend/src/pages/admin/overview.instances.vue b/packages/frontend/src/pages/admin/overview.instances.vue
index a493bd54b4..da9decaab0 100644
--- a/packages/frontend/src/pages/admin/overview.instances.vue
+++ b/packages/frontend/src/pages/admin/overview.instances.vue
@@ -18,10 +18,10 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { ref } from 'vue';
-import * as os from '@/os';
-import { useInterval } from '@/scripts/use-interval';
+import * as os from '@/os.js';
+import { useInterval } from '@/scripts/use-interval.js';
 import MkInstanceCardMini from '@/components/MkInstanceCardMini.vue';
-import { defaultStore } from '@/store';
+import { defaultStore } from '@/store.js';
 
 const instances = ref([]);
 const fetching = ref(true);
diff --git a/packages/frontend/src/pages/admin/overview.moderators.vue b/packages/frontend/src/pages/admin/overview.moderators.vue
index c448ec037d..4086ca51f0 100644
--- a/packages/frontend/src/pages/admin/overview.moderators.vue
+++ b/packages/frontend/src/pages/admin/overview.moderators.vue
@@ -18,8 +18,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { onMounted } from 'vue';
-import * as os from '@/os';
-import { defaultStore } from '@/store';
+import * as os from '@/os.js';
+import { defaultStore } from '@/store.js';
 
 let moderators: any = $ref(null);
 let fetching = $ref(true);
diff --git a/packages/frontend/src/pages/admin/overview.pie.vue b/packages/frontend/src/pages/admin/overview.pie.vue
index b9c17dd616..a67b67402d 100644
--- a/packages/frontend/src/pages/admin/overview.pie.vue
+++ b/packages/frontend/src/pages/admin/overview.pie.vue
@@ -10,8 +10,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { onMounted, shallowRef } from 'vue';
 import { Chart } from 'chart.js';
-import { useChartTooltip } from '@/scripts/use-chart-tooltip';
-import { initChart } from '@/scripts/init-chart';
+import { useChartTooltip } from '@/scripts/use-chart-tooltip.js';
+import { initChart } from '@/scripts/init-chart.js';
 
 initChart();
 
diff --git a/packages/frontend/src/pages/admin/overview.queue.chart.vue b/packages/frontend/src/pages/admin/overview.queue.chart.vue
index 8dfbc39ecd..38309e351a 100644
--- a/packages/frontend/src/pages/admin/overview.queue.chart.vue
+++ b/packages/frontend/src/pages/admin/overview.queue.chart.vue
@@ -10,11 +10,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { onMounted, shallowRef } from 'vue';
 import { Chart } from 'chart.js';
-import { defaultStore } from '@/store';
-import { useChartTooltip } from '@/scripts/use-chart-tooltip';
-import { chartVLine } from '@/scripts/chart-vline';
-import { alpha } from '@/scripts/color';
-import { initChart } from '@/scripts/init-chart';
+import { defaultStore } from '@/store.js';
+import { useChartTooltip } from '@/scripts/use-chart-tooltip.js';
+import { chartVLine } from '@/scripts/chart-vline.js';
+import { alpha } from '@/scripts/color.js';
+import { initChart } from '@/scripts/init-chart.js';
 
 initChart();
 
diff --git a/packages/frontend/src/pages/admin/overview.queue.vue b/packages/frontend/src/pages/admin/overview.queue.vue
index 79823c8c81..1af9d89f62 100644
--- a/packages/frontend/src/pages/admin/overview.queue.vue
+++ b/packages/frontend/src/pages/admin/overview.queue.vue
@@ -37,8 +37,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { markRaw, onMounted, onUnmounted, ref } from 'vue';
 import XChart from './overview.queue.chart.vue';
-import number from '@/filters/number';
-import { useStream } from '@/stream';
+import number from '@/filters/number.js';
+import { useStream } from '@/stream.js';
 
 const connection = markRaw(useStream().useChannel('queueStats'));
 
diff --git a/packages/frontend/src/pages/admin/overview.stats.vue b/packages/frontend/src/pages/admin/overview.stats.vue
index c543f16740..5899177efa 100644
--- a/packages/frontend/src/pages/admin/overview.stats.vue
+++ b/packages/frontend/src/pages/admin/overview.stats.vue
@@ -62,12 +62,12 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { onMounted } from 'vue';
-import * as os from '@/os';
+import * as os from '@/os.js';
 import MkNumberDiff from '@/components/MkNumberDiff.vue';
 import MkNumber from '@/components/MkNumber.vue';
-import { i18n } from '@/i18n';
-import { customEmojis } from '@/custom-emojis';
-import { defaultStore } from '@/store';
+import { i18n } from '@/i18n.js';
+import { customEmojis } from '@/custom-emojis.js';
+import { defaultStore } from '@/store.js';
 
 let stats: any = $ref(null);
 let usersComparedToThePrevDay = $ref<number>();
diff --git a/packages/frontend/src/pages/admin/overview.users.vue b/packages/frontend/src/pages/admin/overview.users.vue
index a366293bf8..6ee83c51e7 100644
--- a/packages/frontend/src/pages/admin/overview.users.vue
+++ b/packages/frontend/src/pages/admin/overview.users.vue
@@ -17,10 +17,10 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import * as os from '@/os';
-import { useInterval } from '@/scripts/use-interval';
+import * as os from '@/os.js';
+import { useInterval } from '@/scripts/use-interval.js';
 import MkUserCardMini from '@/components/MkUserCardMini.vue';
-import { defaultStore } from '@/store';
+import { defaultStore } from '@/store.js';
 
 let newUsers = $ref(null);
 let fetching = $ref(true);
diff --git a/packages/frontend/src/pages/admin/overview.vue b/packages/frontend/src/pages/admin/overview.vue
index 6b63f868b7..170dc0d212 100644
--- a/packages/frontend/src/pages/admin/overview.vue
+++ b/packages/frontend/src/pages/admin/overview.vue
@@ -76,10 +76,10 @@ import XStats from './overview.stats.vue';
 import XRetention from './overview.retention.vue';
 import XModerators from './overview.moderators.vue';
 import XHeatmap from './overview.heatmap.vue';
-import * as os from '@/os';
-import { useStream } from '@/stream';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import * as os from '@/os.js';
+import { useStream } from '@/stream.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 import MkFoldableSection from '@/components/MkFoldableSection.vue';
 
 const rootEl = $shallowRef<HTMLElement>();
diff --git a/packages/frontend/src/pages/admin/proxy-account.vue b/packages/frontend/src/pages/admin/proxy-account.vue
index 61aa5a4405..9681215aa1 100644
--- a/packages/frontend/src/pages/admin/proxy-account.vue
+++ b/packages/frontend/src/pages/admin/proxy-account.vue
@@ -26,10 +26,10 @@ import MkKeyValue from '@/components/MkKeyValue.vue';
 import MkButton from '@/components/MkButton.vue';
 import MkInfo from '@/components/MkInfo.vue';
 import FormSuspense from '@/components/form/suspense.vue';
-import * as os from '@/os';
-import { fetchInstance } from '@/instance';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import * as os from '@/os.js';
+import { fetchInstance } from '@/instance.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 
 let proxyAccount: any = $ref(null);
 let proxyAccountId: any = $ref(null);
diff --git a/packages/frontend/src/pages/admin/queue.chart.chart.vue b/packages/frontend/src/pages/admin/queue.chart.chart.vue
index cc9bc65aa3..566670c843 100644
--- a/packages/frontend/src/pages/admin/queue.chart.chart.vue
+++ b/packages/frontend/src/pages/admin/queue.chart.chart.vue
@@ -10,11 +10,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { onMounted, shallowRef } from 'vue';
 import { Chart } from 'chart.js';
-import { defaultStore } from '@/store';
-import { useChartTooltip } from '@/scripts/use-chart-tooltip';
-import { chartVLine } from '@/scripts/chart-vline';
-import { alpha } from '@/scripts/color';
-import { initChart } from '@/scripts/init-chart';
+import { defaultStore } from '@/store.js';
+import { useChartTooltip } from '@/scripts/use-chart-tooltip.js';
+import { chartVLine } from '@/scripts/chart-vline.js';
+import { alpha } from '@/scripts/color.js';
+import { initChart } from '@/scripts/init-chart.js';
 
 initChart();
 
diff --git a/packages/frontend/src/pages/admin/queue.chart.vue b/packages/frontend/src/pages/admin/queue.chart.vue
index 07127db2d4..d9f4af454d 100644
--- a/packages/frontend/src/pages/admin/queue.chart.vue
+++ b/packages/frontend/src/pages/admin/queue.chart.vue
@@ -50,10 +50,10 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { markRaw, onMounted, onUnmounted, ref } from 'vue';
 import XChart from './queue.chart.chart.vue';
-import number from '@/filters/number';
-import * as os from '@/os';
-import { useStream } from '@/stream';
-import { i18n } from '@/i18n';
+import number from '@/filters/number.js';
+import * as os from '@/os.js';
+import { useStream } from '@/stream.js';
+import { i18n } from '@/i18n.js';
 import MkFolder from '@/components/MkFolder.vue';
 
 const connection = markRaw(useStream().useChannel('queueStats'));
diff --git a/packages/frontend/src/pages/admin/queue.vue b/packages/frontend/src/pages/admin/queue.vue
index f5f37c0338..ece54ab12b 100644
--- a/packages/frontend/src/pages/admin/queue.vue
+++ b/packages/frontend/src/pages/admin/queue.vue
@@ -18,10 +18,10 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import XQueue from './queue.chart.vue';
 import XHeader from './_header_.vue';
-import * as os from '@/os';
-import * as config from '@/config';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import * as os from '@/os.js';
+import * as config from '@/config.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 import MkButton from '@/components/MkButton.vue';
 
 let tab = $ref('deliver');
diff --git a/packages/frontend/src/pages/admin/relays.vue b/packages/frontend/src/pages/admin/relays.vue
index ad822f4c61..8100d8188b 100644
--- a/packages/frontend/src/pages/admin/relays.vue
+++ b/packages/frontend/src/pages/admin/relays.vue
@@ -27,9 +27,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { } from 'vue';
 import XHeader from './_header_.vue';
 import MkButton from '@/components/MkButton.vue';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 
 let relays: any[] = $ref([]);
 
diff --git a/packages/frontend/src/pages/admin/roles.edit.vue b/packages/frontend/src/pages/admin/roles.edit.vue
index f901236511..29726e805b 100644
--- a/packages/frontend/src/pages/admin/roles.edit.vue
+++ b/packages/frontend/src/pages/admin/roles.edit.vue
@@ -26,10 +26,10 @@ import { computed } from 'vue';
 import { v4 as uuid } from 'uuid';
 import XHeader from './_header_.vue';
 import XEditor from './roles.editor.vue';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
-import { useRouter } from '@/router';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { useRouter } from '@/router.js';
 import MkButton from '@/components/MkButton.vue';
 import { rolesCache } from '@/cache';
 
diff --git a/packages/frontend/src/pages/admin/roles.editor.vue b/packages/frontend/src/pages/admin/roles.editor.vue
index 68d2fe07d5..2ef3e254cd 100644
--- a/packages/frontend/src/pages/admin/roles.editor.vue
+++ b/packages/frontend/src/pages/admin/roles.editor.vue
@@ -508,10 +508,10 @@ import MkFolder from '@/components/MkFolder.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
 import MkRange from '@/components/MkRange.vue';
 import FormSlot from '@/components/form/slot.vue';
-import { i18n } from '@/i18n';
+import { i18n } from '@/i18n.js';
 import { ROLE_POLICIES } from '@/const';
-import { instance } from '@/instance';
-import { deepClone } from '@/scripts/clone';
+import { instance } from '@/instance.js';
+import { deepClone } from '@/scripts/clone.js';
 
 const emit = defineEmits<{
 	(ev: 'update:modelValue', v: any): void;
diff --git a/packages/frontend/src/pages/admin/roles.role.vue b/packages/frontend/src/pages/admin/roles.role.vue
index 779fb6d51b..0b02f419bc 100644
--- a/packages/frontend/src/pages/admin/roles.role.vue
+++ b/packages/frontend/src/pages/admin/roles.role.vue
@@ -66,15 +66,15 @@ import { computed, reactive } from 'vue';
 import XHeader from './_header_.vue';
 import XEditor from './roles.editor.vue';
 import MkFolder from '@/components/MkFolder.vue';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
-import { useRouter } from '@/router';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { useRouter } from '@/router.js';
 import MkButton from '@/components/MkButton.vue';
 import MkUserCardMini from '@/components/MkUserCardMini.vue';
 import MkInfo from '@/components/MkInfo.vue';
 import MkPagination, { Paging } from '@/components/MkPagination.vue';
-import { infoImageUrl } from '@/instance';
+import { infoImageUrl } from '@/instance.js';
 
 const router = useRouter();
 
diff --git a/packages/frontend/src/pages/admin/roles.vue b/packages/frontend/src/pages/admin/roles.vue
index 4ac3cdc69b..8d23335430 100644
--- a/packages/frontend/src/pages/admin/roles.vue
+++ b/packages/frontend/src/pages/admin/roles.vue
@@ -209,11 +209,11 @@ import MkSwitch from '@/components/MkSwitch.vue';
 import MkButton from '@/components/MkButton.vue';
 import MkRange from '@/components/MkRange.vue';
 import MkRolePreview from '@/components/MkRolePreview.vue';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
-import { instance } from '@/instance';
-import { useRouter } from '@/router';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { instance } from '@/instance.js';
+import { useRouter } from '@/router.js';
 import MkFoldableSection from '@/components/MkFoldableSection.vue';
 import { ROLE_POLICIES } from '@/const';
 
diff --git a/packages/frontend/src/pages/admin/security.vue b/packages/frontend/src/pages/admin/security.vue
index 94d9bf1c19..a2594ee6c5 100644
--- a/packages/frontend/src/pages/admin/security.vue
+++ b/packages/frontend/src/pages/admin/security.vue
@@ -117,10 +117,10 @@ import FormSuspense from '@/components/form/suspense.vue';
 import MkRange from '@/components/MkRange.vue';
 import MkInput from '@/components/MkInput.vue';
 import MkButton from '@/components/MkButton.vue';
-import * as os from '@/os';
-import { fetchInstance } from '@/instance';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import * as os from '@/os.js';
+import { fetchInstance } from '@/instance.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 
 let summalyProxy: string = $ref('');
 let enableHcaptcha: boolean = $ref(false);
diff --git a/packages/frontend/src/pages/admin/server-rules.vue b/packages/frontend/src/pages/admin/server-rules.vue
index 85961a0886..4cd1f6cbec 100644
--- a/packages/frontend/src/pages/admin/server-rules.vue
+++ b/packages/frontend/src/pages/admin/server-rules.vue
@@ -43,10 +43,10 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { defineAsyncComponent } from 'vue';
 import XHeader from './_header_.vue';
-import * as os from '@/os';
-import { fetchInstance, instance } from '@/instance';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import * as os from '@/os.js';
+import { fetchInstance, instance } from '@/instance.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 import MkButton from '@/components/MkButton.vue';
 import MkInput from '@/components/MkInput.vue';
 
diff --git a/packages/frontend/src/pages/admin/settings.vue b/packages/frontend/src/pages/admin/settings.vue
index d667eb540f..adcf061149 100644
--- a/packages/frontend/src/pages/admin/settings.vue
+++ b/packages/frontend/src/pages/admin/settings.vue
@@ -111,10 +111,10 @@ import MkTextarea from '@/components/MkTextarea.vue';
 import FormSection from '@/components/form/section.vue';
 import FormSplit from '@/components/form/split.vue';
 import FormSuspense from '@/components/form/suspense.vue';
-import * as os from '@/os';
-import { fetchInstance } from '@/instance';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import * as os from '@/os.js';
+import { fetchInstance } from '@/instance.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 import MkButton from '@/components/MkButton.vue';
 
 let name: string | null = $ref(null);
diff --git a/packages/frontend/src/pages/admin/users.vue b/packages/frontend/src/pages/admin/users.vue
index 084d5c0ed0..fcf41de734 100644
--- a/packages/frontend/src/pages/admin/users.vue
+++ b/packages/frontend/src/pages/admin/users.vue
@@ -62,12 +62,12 @@ import XHeader from './_header_.vue';
 import MkInput from '@/components/MkInput.vue';
 import MkSelect from '@/components/MkSelect.vue';
 import MkPagination from '@/components/MkPagination.vue';
-import * as os from '@/os';
-import { lookupUser } from '@/scripts/lookup-user';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import * as os from '@/os.js';
+import { lookupUser } from '@/scripts/lookup-user.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 import MkUserCardMini from '@/components/MkUserCardMini.vue';
-import { dateString } from '@/filters/date';
+import { dateString } from '@/filters/date.js';
 
 let paginationComponent = $shallowRef<InstanceType<typeof MkPagination>>();
 
diff --git a/packages/frontend/src/pages/ads.vue b/packages/frontend/src/pages/ads.vue
index 18bd202ef2..ee58049554 100644
--- a/packages/frontend/src/pages/ads.vue
+++ b/packages/frontend/src/pages/ads.vue
@@ -17,10 +17,10 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { computed, watch } from 'vue';
-import * as os from '@/os';
-import { definePageMetadata } from '@/scripts/page-metadata';
-import { i18n } from '@/i18n';
-import { instance } from '@/instance';
+import * as os from '@/os.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { i18n } from '@/i18n.js';
+import { instance } from '@/instance.js';
 
 definePageMetadata({
 	title: i18n.ts.ads,
diff --git a/packages/frontend/src/pages/announcements.vue b/packages/frontend/src/pages/announcements.vue
index 9937eb4cbf..babac9d805 100644
--- a/packages/frontend/src/pages/announcements.vue
+++ b/packages/frontend/src/pages/announcements.vue
@@ -44,10 +44,10 @@ import { ref } from 'vue';
 import MkPagination from '@/components/MkPagination.vue';
 import MkButton from '@/components/MkButton.vue';
 import MkInfo from '@/components/MkInfo.vue';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
-import { $i, updateAccount } from '@/account';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { $i, updateAccount } from '@/account.js';
 
 const paginationCurrent = {
 	endpoint: 'announcements' as const,
diff --git a/packages/frontend/src/pages/antenna-timeline.vue b/packages/frontend/src/pages/antenna-timeline.vue
index f410e78e06..4ea91796d5 100644
--- a/packages/frontend/src/pages/antenna-timeline.vue
+++ b/packages/frontend/src/pages/antenna-timeline.vue
@@ -26,11 +26,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { computed, watch } from 'vue';
 import MkTimeline from '@/components/MkTimeline.vue';
-import { scroll } from '@/scripts/scroll';
-import * as os from '@/os';
-import { useRouter } from '@/router';
-import { definePageMetadata } from '@/scripts/page-metadata';
-import { i18n } from '@/i18n';
+import { scroll } from '@/scripts/scroll.js';
+import * as os from '@/os.js';
+import { useRouter } from '@/router.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { i18n } from '@/i18n.js';
 
 const router = useRouter();
 
diff --git a/packages/frontend/src/pages/api-console.vue b/packages/frontend/src/pages/api-console.vue
index b5476b8217..01657c4c2a 100644
--- a/packages/frontend/src/pages/api-console.vue
+++ b/packages/frontend/src/pages/api-console.vue
@@ -41,8 +41,8 @@ import MkButton from '@/components/MkButton.vue';
 import MkInput from '@/components/MkInput.vue';
 import MkTextarea from '@/components/MkTextarea.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
-import * as os from '@/os';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import * as os from '@/os.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 
 const body = ref('{}');
 const endpoint = ref('');
diff --git a/packages/frontend/src/pages/auth.form.vue b/packages/frontend/src/pages/auth.form.vue
index cf014f5f3d..3f6f58df55 100644
--- a/packages/frontend/src/pages/auth.form.vue
+++ b/packages/frontend/src/pages/auth.form.vue
@@ -23,8 +23,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkButton from '@/components/MkButton.vue';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
 
 const props = defineProps<{
 	session: Misskey.entities.AuthSession;
diff --git a/packages/frontend/src/pages/auth.vue b/packages/frontend/src/pages/auth.vue
index ac4e1f43cf..124323a483 100644
--- a/packages/frontend/src/pages/auth.vue
+++ b/packages/frontend/src/pages/auth.vue
@@ -46,10 +46,10 @@ import { onMounted } from 'vue';
 import * as Misskey from 'misskey-js';
 import XForm from './auth.form.vue';
 import MkSignin from '@/components/MkSignin.vue';
-import * as os from '@/os';
-import { $i, login } from '@/account';
-import { definePageMetadata } from '@/scripts/page-metadata';
-import { i18n } from '@/i18n';
+import * as os from '@/os.js';
+import { $i, login } from '@/account.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { i18n } from '@/i18n.js';
 
 const props = defineProps<{
 	token: string;
diff --git a/packages/frontend/src/pages/channel-editor.vue b/packages/frontend/src/pages/channel-editor.vue
index 59d6d14cee..39ce093cf2 100644
--- a/packages/frontend/src/pages/channel-editor.vue
+++ b/packages/frontend/src/pages/channel-editor.vue
@@ -70,11 +70,11 @@ import MkTextarea from '@/components/MkTextarea.vue';
 import MkButton from '@/components/MkButton.vue';
 import MkInput from '@/components/MkInput.vue';
 import MkColorInput from '@/components/MkColorInput.vue';
-import { selectFile } from '@/scripts/select-file';
-import * as os from '@/os';
-import { useRouter } from '@/router';
-import { definePageMetadata } from '@/scripts/page-metadata';
-import { i18n } from '@/i18n';
+import { selectFile } from '@/scripts/select-file.js';
+import * as os from '@/os.js';
+import { useRouter } from '@/router.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { i18n } from '@/i18n.js';
 import MkFolder from '@/components/MkFolder.vue';
 import MkSwitch from "@/components/MkSwitch.vue";
 
diff --git a/packages/frontend/src/pages/channel.vue b/packages/frontend/src/pages/channel.vue
index be0917871a..00e88cbbfb 100644
--- a/packages/frontend/src/pages/channel.vue
+++ b/packages/frontend/src/pages/channel.vue
@@ -72,17 +72,17 @@ import { computed, watch } from 'vue';
 import MkPostForm from '@/components/MkPostForm.vue';
 import MkTimeline from '@/components/MkTimeline.vue';
 import XChannelFollowButton from '@/components/MkChannelFollowButton.vue';
-import * as os from '@/os';
-import { useRouter } from '@/router';
-import { $i, iAmModerator } from '@/account';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
-import { deviceKind } from '@/scripts/device-kind';
+import * as os from '@/os.js';
+import { useRouter } from '@/router.js';
+import { $i, iAmModerator } from '@/account.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { deviceKind } from '@/scripts/device-kind.js';
 import MkNotes from '@/components/MkNotes.vue';
-import { url } from '@/config';
+import { url } from '@/config.js';
 import MkButton from '@/components/MkButton.vue';
 import MkInput from '@/components/MkInput.vue';
-import { defaultStore } from '@/store';
+import { defaultStore } from '@/store.js';
 import MkNote from '@/components/MkNote.vue';
 import MkInfo from '@/components/MkInfo.vue';
 import MkFoldableSection from '@/components/MkFoldableSection.vue';
diff --git a/packages/frontend/src/pages/channels.vue b/packages/frontend/src/pages/channels.vue
index 8523cacebe..8219a947f2 100644
--- a/packages/frontend/src/pages/channels.vue
+++ b/packages/frontend/src/pages/channels.vue
@@ -58,9 +58,9 @@ import MkInput from '@/components/MkInput.vue';
 import MkRadios from '@/components/MkRadios.vue';
 import MkButton from '@/components/MkButton.vue';
 import MkFoldableSection from '@/components/MkFoldableSection.vue';
-import { useRouter } from '@/router';
-import { definePageMetadata } from '@/scripts/page-metadata';
-import { i18n } from '@/i18n';
+import { useRouter } from '@/router.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { i18n } from '@/i18n.js';
 
 const router = useRouter();
 
diff --git a/packages/frontend/src/pages/clicker.vue b/packages/frontend/src/pages/clicker.vue
index 9f9e021e6e..5b194881d1 100644
--- a/packages/frontend/src/pages/clicker.vue
+++ b/packages/frontend/src/pages/clicker.vue
@@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import MkClickerGame from '@/components/MkClickerGame.vue';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 
 definePageMetadata({
 	title: '🍪👈',
diff --git a/packages/frontend/src/pages/clip.vue b/packages/frontend/src/pages/clip.vue
index 9a826be19e..80b94acb6b 100644
--- a/packages/frontend/src/pages/clip.vue
+++ b/packages/frontend/src/pages/clip.vue
@@ -29,11 +29,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { computed, watch, provide } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkNotes from '@/components/MkNotes.vue';
-import { $i } from '@/account';
-import { i18n } from '@/i18n';
-import * as os from '@/os';
-import { definePageMetadata } from '@/scripts/page-metadata';
-import { url } from '@/config';
+import { $i } from '@/account.js';
+import { i18n } from '@/i18n.js';
+import * as os from '@/os.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { url } from '@/config.js';
 import MkButton from '@/components/MkButton.vue';
 import { clipsCache } from '@/cache';
 
diff --git a/packages/frontend/src/pages/custom-emojis-manager.vue b/packages/frontend/src/pages/custom-emojis-manager.vue
index 61f82f7b04..bee73045b7 100644
--- a/packages/frontend/src/pages/custom-emojis-manager.vue
+++ b/packages/frontend/src/pages/custom-emojis-manager.vue
@@ -80,10 +80,10 @@ import MkInput from '@/components/MkInput.vue';
 import MkPagination from '@/components/MkPagination.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
 import FormSplit from '@/components/form/split.vue';
-import { selectFile, selectFiles } from '@/scripts/select-file';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import { selectFile, selectFiles } from '@/scripts/select-file.js';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 
 const emojisPaginationComponent = shallowRef<InstanceType<typeof MkPagination>>();
 
diff --git a/packages/frontend/src/pages/drive.vue b/packages/frontend/src/pages/drive.vue
index b7d2c380d2..54fb83fc1d 100644
--- a/packages/frontend/src/pages/drive.vue
+++ b/packages/frontend/src/pages/drive.vue
@@ -12,8 +12,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { computed } from 'vue';
 import XDrive from '@/components/MkDrive.vue';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 
 let folder = $ref(null);
 
diff --git a/packages/frontend/src/pages/emoji-edit-dialog.vue b/packages/frontend/src/pages/emoji-edit-dialog.vue
index 19f7582d5e..2e6050490e 100644
--- a/packages/frontend/src/pages/emoji-edit-dialog.vue
+++ b/packages/frontend/src/pages/emoji-edit-dialog.vue
@@ -81,11 +81,11 @@ import MkButton from '@/components/MkButton.vue';
 import MkInput from '@/components/MkInput.vue';
 import MkInfo from '@/components/MkInfo.vue';
 import MkFolder from '@/components/MkFolder.vue';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
-import { customEmojiCategories } from '@/custom-emojis';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
+import { customEmojiCategories } from '@/custom-emojis.js';
 import MkSwitch from '@/components/MkSwitch.vue';
-import { selectFile, selectFiles } from '@/scripts/select-file';
+import { selectFile, selectFiles } from '@/scripts/select-file.js';
 import MkRolePreview from '@/components/MkRolePreview.vue';
 
 const props = defineProps<{
diff --git a/packages/frontend/src/pages/emojis.emoji.vue b/packages/frontend/src/pages/emojis.emoji.vue
index 22063b0d4a..9aaa7890a9 100644
--- a/packages/frontend/src/pages/emojis.emoji.vue
+++ b/packages/frontend/src/pages/emojis.emoji.vue
@@ -15,9 +15,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { } from 'vue';
-import * as os from '@/os';
-import copyToClipboard from '@/scripts/copy-to-clipboard';
-import { i18n } from '@/i18n';
+import * as os from '@/os.js';
+import copyToClipboard from '@/scripts/copy-to-clipboard.js';
+import { i18n } from '@/i18n.js';
 
 const props = defineProps<{
 	emoji: {
diff --git a/packages/frontend/src/pages/explore.featured.vue b/packages/frontend/src/pages/explore.featured.vue
index 89daa112b3..0558faec16 100644
--- a/packages/frontend/src/pages/explore.featured.vue
+++ b/packages/frontend/src/pages/explore.featured.vue
@@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import MkNotes from '@/components/MkNotes.vue';
 import MkTab from '@/components/MkTab.vue';
-import { i18n } from '@/i18n';
+import { i18n } from '@/i18n.js';
 
 const paginationForNotes = {
 	endpoint: 'notes/featured' as const,
diff --git a/packages/frontend/src/pages/explore.roles.vue b/packages/frontend/src/pages/explore.roles.vue
index 21d12c6e02..995ccd777c 100644
--- a/packages/frontend/src/pages/explore.roles.vue
+++ b/packages/frontend/src/pages/explore.roles.vue
@@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { } from 'vue';
 import MkRolePreview from '@/components/MkRolePreview.vue';
-import * as os from '@/os';
+import * as os from '@/os.js';
 
 let roles = $ref();
 
diff --git a/packages/frontend/src/pages/explore.users.vue b/packages/frontend/src/pages/explore.users.vue
index c360c43fd8..1f187d6b8a 100644
--- a/packages/frontend/src/pages/explore.users.vue
+++ b/packages/frontend/src/pages/explore.users.vue
@@ -67,8 +67,8 @@ import { watch } from 'vue';
 import MkUserList from '@/components/MkUserList.vue';
 import MkFoldableSection from '@/components/MkFoldableSection.vue';
 import MkTab from '@/components/MkTab.vue';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
 
 const props = defineProps<{
 	tag?: string;
diff --git a/packages/frontend/src/pages/explore.vue b/packages/frontend/src/pages/explore.vue
index 54341a41a7..fd846d979a 100644
--- a/packages/frontend/src/pages/explore.vue
+++ b/packages/frontend/src/pages/explore.vue
@@ -26,8 +26,8 @@ import XFeatured from './explore.featured.vue';
 import XUsers from './explore.users.vue';
 import XRoles from './explore.roles.vue';
 import MkFoldableSection from '@/components/MkFoldableSection.vue';
-import { definePageMetadata } from '@/scripts/page-metadata';
-import { i18n } from '@/i18n';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { i18n } from '@/i18n.js';
 
 const props = withDefaults(defineProps<{
 	tag?: string;
diff --git a/packages/frontend/src/pages/favorites.vue b/packages/frontend/src/pages/favorites.vue
index 4f1247f0fa..63a0057b74 100644
--- a/packages/frontend/src/pages/favorites.vue
+++ b/packages/frontend/src/pages/favorites.vue
@@ -29,9 +29,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 import MkPagination from '@/components/MkPagination.vue';
 import MkNote from '@/components/MkNote.vue';
 import MkDateSeparatedList from '@/components/MkDateSeparatedList.vue';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
-import { infoImageUrl } from '@/instance';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { infoImageUrl } from '@/instance.js';
 
 const pagination = {
 	endpoint: 'i/favorites' as const,
diff --git a/packages/frontend/src/pages/flash/flash-edit.vue b/packages/frontend/src/pages/flash/flash-edit.vue
index 7ffb9e4190..f494218c18 100644
--- a/packages/frontend/src/pages/flash/flash-edit.vue
+++ b/packages/frontend/src/pages/flash/flash-edit.vue
@@ -36,13 +36,13 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { computed } from 'vue';
 import MkButton from '@/components/MkButton.vue';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 import MkTextarea from '@/components/MkTextarea.vue';
 import MkInput from '@/components/MkInput.vue';
 import MkSelect from '@/components/MkSelect.vue';
-import { useRouter } from '@/router';
+import { useRouter } from '@/router.js';
 
 const PRESET_DEFAULT = `/// @ 0.16.0
 
diff --git a/packages/frontend/src/pages/flash/flash-index.vue b/packages/frontend/src/pages/flash/flash-index.vue
index fac07a47dd..4a686efd45 100644
--- a/packages/frontend/src/pages/flash/flash-index.vue
+++ b/packages/frontend/src/pages/flash/flash-index.vue
@@ -42,9 +42,9 @@ import { computed } from 'vue';
 import MkFlashPreview from '@/components/MkFlashPreview.vue';
 import MkPagination from '@/components/MkPagination.vue';
 import MkButton from '@/components/MkButton.vue';
-import { useRouter } from '@/router';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import { useRouter } from '@/router.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 
 const router = useRouter();
 
diff --git a/packages/frontend/src/pages/flash/flash.vue b/packages/frontend/src/pages/flash/flash.vue
index 60c5690337..32a835831c 100644
--- a/packages/frontend/src/pages/flash/flash.vue
+++ b/packages/frontend/src/pages/flash/flash.vue
@@ -59,17 +59,17 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { computed, onDeactivated, onUnmounted, Ref, ref, watch } from 'vue';
 import { Interpreter, Parser, values } from '@syuilo/aiscript';
 import MkButton from '@/components/MkButton.vue';
-import * as os from '@/os';
-import { url } from '@/config';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import * as os from '@/os.js';
+import { url } from '@/config.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 import MkAsUi from '@/components/MkAsUi.vue';
-import { AsUiComponent, AsUiRoot, registerAsUiLib } from '@/scripts/aiscript/ui';
-import { createAiScriptEnv } from '@/scripts/aiscript/api';
+import { AsUiComponent, AsUiRoot, registerAsUiLib } from '@/scripts/aiscript/ui.js';
+import { createAiScriptEnv } from '@/scripts/aiscript/api.js';
 import MkFolder from '@/components/MkFolder.vue';
 import MkCode from '@/components/MkCode.vue';
-import { defaultStore } from '@/store';
-import { $i } from '@/account';
+import { defaultStore } from '@/store.js';
+import { $i } from '@/account.js';
 
 const props = defineProps<{
 	id: string;
diff --git a/packages/frontend/src/pages/follow-requests.vue b/packages/frontend/src/pages/follow-requests.vue
index fffcba385d..4f7fdef0ba 100644
--- a/packages/frontend/src/pages/follow-requests.vue
+++ b/packages/frontend/src/pages/follow-requests.vue
@@ -40,11 +40,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { shallowRef, computed } from 'vue';
 import MkPagination from '@/components/MkPagination.vue';
 import MkButton from '@/components/MkButton.vue';
-import { userPage, acct } from '@/filters/user';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
-import { infoImageUrl } from '@/instance';
+import { userPage, acct } from '@/filters/user.js';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { infoImageUrl } from '@/instance.js';
 
 const paginationComponent = shallowRef<InstanceType<typeof MkPagination>>();
 
diff --git a/packages/frontend/src/pages/follow.vue b/packages/frontend/src/pages/follow.vue
index 78d90bac8a..e382cabd74 100644
--- a/packages/frontend/src/pages/follow.vue
+++ b/packages/frontend/src/pages/follow.vue
@@ -11,9 +11,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { } from 'vue';
 import * as Misskey from 'misskey-js';
-import * as os from '@/os';
-import { mainRouter } from '@/router';
-import { i18n } from '@/i18n';
+import * as os from '@/os.js';
+import { mainRouter } from '@/router.js';
+import { i18n } from '@/i18n.js';
 
 async function follow(user): Promise<void> {
 	const { canceled } = await os.confirm({
diff --git a/packages/frontend/src/pages/gallery/edit.vue b/packages/frontend/src/pages/gallery/edit.vue
index a36140ea73..f3cbd4947c 100644
--- a/packages/frontend/src/pages/gallery/edit.vue
+++ b/packages/frontend/src/pages/gallery/edit.vue
@@ -44,11 +44,11 @@ import MkInput from '@/components/MkInput.vue';
 import MkTextarea from '@/components/MkTextarea.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
 import FormSuspense from '@/components/form/suspense.vue';
-import { selectFiles } from '@/scripts/select-file';
-import * as os from '@/os';
-import { useRouter } from '@/router';
-import { definePageMetadata } from '@/scripts/page-metadata';
-import { i18n } from '@/i18n';
+import { selectFiles } from '@/scripts/select-file.js';
+import * as os from '@/os.js';
+import { useRouter } from '@/router.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { i18n } from '@/i18n.js';
 
 const router = useRouter();
 
diff --git a/packages/frontend/src/pages/gallery/index.vue b/packages/frontend/src/pages/gallery/index.vue
index af0ac2191b..43bb7c496d 100644
--- a/packages/frontend/src/pages/gallery/index.vue
+++ b/packages/frontend/src/pages/gallery/index.vue
@@ -51,9 +51,9 @@ import { watch } from 'vue';
 import MkFoldableSection from '@/components/MkFoldableSection.vue';
 import MkPagination from '@/components/MkPagination.vue';
 import MkGalleryPostPreview from '@/components/MkGalleryPostPreview.vue';
-import { definePageMetadata } from '@/scripts/page-metadata';
-import { i18n } from '@/i18n';
-import { useRouter } from '@/router';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { i18n } from '@/i18n.js';
+import { useRouter } from '@/router.js';
 
 const router = useRouter();
 
diff --git a/packages/frontend/src/pages/gallery/post.vue b/packages/frontend/src/pages/gallery/post.vue
index 586cca1852..3f4f657e94 100644
--- a/packages/frontend/src/pages/gallery/post.vue
+++ b/packages/frontend/src/pages/gallery/post.vue
@@ -63,17 +63,17 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { computed, watch } from 'vue';
 import MkButton from '@/components/MkButton.vue';
-import * as os from '@/os';
+import * as os from '@/os.js';
 import MkContainer from '@/components/MkContainer.vue';
 import MkPagination from '@/components/MkPagination.vue';
 import MkGalleryPostPreview from '@/components/MkGalleryPostPreview.vue';
 import MkFollowButton from '@/components/MkFollowButton.vue';
-import { url } from '@/config';
-import { useRouter } from '@/router';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
-import { defaultStore } from '@/store';
-import { $i } from '@/account';
+import { url } from '@/config.js';
+import { useRouter } from '@/router.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { defaultStore } from '@/store.js';
+import { $i } from '@/account.js';
 
 const router = useRouter();
 
diff --git a/packages/frontend/src/pages/instance-info.vue b/packages/frontend/src/pages/instance-info.vue
index d316b21f89..2285e4c88e 100644
--- a/packages/frontend/src/pages/instance-info.vue
+++ b/packages/frontend/src/pages/instance-info.vue
@@ -127,15 +127,15 @@ import FormSection from '@/components/form/section.vue';
 import MkKeyValue from '@/components/MkKeyValue.vue';
 import MkSelect from '@/components/MkSelect.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
-import * as os from '@/os';
-import number from '@/filters/number';
-import { iAmModerator, iAmAdmin } from '@/account';
-import { definePageMetadata } from '@/scripts/page-metadata';
-import { i18n } from '@/i18n';
+import * as os from '@/os.js';
+import number from '@/filters/number.js';
+import { iAmModerator, iAmAdmin } from '@/account.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { i18n } from '@/i18n.js';
 import MkUserCardMini from '@/components/MkUserCardMini.vue';
 import MkPagination from '@/components/MkPagination.vue';
-import { getProxiedImageUrlNullable } from '@/scripts/media-proxy';
-import { dateString } from '@/filters/date';
+import { getProxiedImageUrlNullable } from '@/scripts/media-proxy.js';
+import { dateString } from '@/filters/date.js';
 
 const props = defineProps<{
 	host: string;
diff --git a/packages/frontend/src/pages/invite.vue b/packages/frontend/src/pages/invite.vue
index 97da77214c..b44b580e86 100644
--- a/packages/frontend/src/pages/invite.vue
+++ b/packages/frontend/src/pages/invite.vue
@@ -38,14 +38,14 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { computed, ref, shallowRef } from 'vue';
 import type * as Misskey from 'misskey-js';
-import { i18n } from '@/i18n';
-import * as os from '@/os';
+import { i18n } from '@/i18n.js';
+import * as os from '@/os.js';
 import MkButton from '@/components/MkButton.vue';
 import MkPagination, { Paging } from '@/components/MkPagination.vue';
 import MkInviteCode from '@/components/MkInviteCode.vue';
-import { definePageMetadata } from '@/scripts/page-metadata';
-import { serverErrorImageUrl, instance } from '@/instance';
-import { $i } from '@/account';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { serverErrorImageUrl, instance } from '@/instance.js';
+import { $i } from '@/account.js';
 
 const pagingComponent = shallowRef<InstanceType<typeof MkPagination>>();
 const currentInviteLimit = ref<null | number>(null);
diff --git a/packages/frontend/src/pages/list.vue b/packages/frontend/src/pages/list.vue
index 1c26566455..db0b201b73 100644
--- a/packages/frontend/src/pages/list.vue
+++ b/packages/frontend/src/pages/list.vue
@@ -35,13 +35,13 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { watch, computed } from 'vue';
-import * as os from '@/os';
-import { userPage } from '@/filters/user';
-import { i18n } from '@/i18n';
+import * as os from '@/os.js';
+import { userPage } from '@/filters/user.js';
+import { i18n } from '@/i18n.js';
 import MkUserCardMini from '@/components/MkUserCardMini.vue';
 import MkButton from '@/components/MkButton.vue';
-import { definePageMetadata } from '@/scripts/page-metadata';
-import { serverErrorImageUrl } from '@/instance';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { serverErrorImageUrl } from '@/instance.js';
 
 const props = defineProps<{
 	listId: string;
diff --git a/packages/frontend/src/pages/miauth.vue b/packages/frontend/src/pages/miauth.vue
index 4df40db917..1572d27aab 100644
--- a/packages/frontend/src/pages/miauth.vue
+++ b/packages/frontend/src/pages/miauth.vue
@@ -46,10 +46,10 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { } from 'vue';
 import MkSignin from '@/components/MkSignin.vue';
 import MkButton from '@/components/MkButton.vue';
-import * as os from '@/os';
-import { $i, login } from '@/account';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import * as os from '@/os.js';
+import { $i, login } from '@/account.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 
 const props = defineProps<{
 	session: string;
diff --git a/packages/frontend/src/pages/my-antennas/create.vue b/packages/frontend/src/pages/my-antennas/create.vue
index 11e231ab70..2e08c13f1b 100644
--- a/packages/frontend/src/pages/my-antennas/create.vue
+++ b/packages/frontend/src/pages/my-antennas/create.vue
@@ -11,9 +11,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import XAntenna from './editor.vue';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
-import { useRouter } from '@/router';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { useRouter } from '@/router.js';
 import { antennasCache } from '@/cache';
 
 const router = useRouter();
diff --git a/packages/frontend/src/pages/my-antennas/edit.vue b/packages/frontend/src/pages/my-antennas/edit.vue
index c3b74e573d..6600ebcc64 100644
--- a/packages/frontend/src/pages/my-antennas/edit.vue
+++ b/packages/frontend/src/pages/my-antennas/edit.vue
@@ -11,10 +11,10 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import XAntenna from './editor.vue';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
-import { useRouter } from '@/router';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
+import { useRouter } from '@/router.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { antennasCache } from '@/cache';
 
 const router = useRouter();
diff --git a/packages/frontend/src/pages/my-antennas/editor.vue b/packages/frontend/src/pages/my-antennas/editor.vue
index 72594bec0c..cd920b75e6 100644
--- a/packages/frontend/src/pages/my-antennas/editor.vue
+++ b/packages/frontend/src/pages/my-antennas/editor.vue
@@ -54,8 +54,8 @@ import MkInput from '@/components/MkInput.vue';
 import MkTextarea from '@/components/MkTextarea.vue';
 import MkSelect from '@/components/MkSelect.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
 
 const props = defineProps<{
 	antenna: any
diff --git a/packages/frontend/src/pages/my-antennas/index.vue b/packages/frontend/src/pages/my-antennas/index.vue
index 18ba289641..2d9cd05b45 100644
--- a/packages/frontend/src/pages/my-antennas/index.vue
+++ b/packages/frontend/src/pages/my-antennas/index.vue
@@ -30,10 +30,10 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { onActivated } from 'vue';
 import MkButton from '@/components/MkButton.vue';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { antennasCache } from '@/cache';
-import { infoImageUrl } from '@/instance';
+import { infoImageUrl } from '@/instance.js';
 
 const antennas = $computed(() => antennasCache.value.value ?? []);
 
diff --git a/packages/frontend/src/pages/my-clips/index.vue b/packages/frontend/src/pages/my-clips/index.vue
index 5e7b1ac8a3..8289d65a4b 100644
--- a/packages/frontend/src/pages/my-clips/index.vue
+++ b/packages/frontend/src/pages/my-clips/index.vue
@@ -30,9 +30,9 @@ import { watch } from 'vue';
 import MkPagination from '@/components/MkPagination.vue';
 import MkButton from '@/components/MkButton.vue';
 import MkClipPreview from '@/components/MkClipPreview.vue';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { clipsCache } from '@/cache';
 
 const pagination = {
diff --git a/packages/frontend/src/pages/my-lists/index.vue b/packages/frontend/src/pages/my-lists/index.vue
index a779e4a944..3e7efb5a7c 100644
--- a/packages/frontend/src/pages/my-lists/index.vue
+++ b/packages/frontend/src/pages/my-lists/index.vue
@@ -32,12 +32,12 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { onActivated } from 'vue';
 import MkButton from '@/components/MkButton.vue';
 import MkAvatars from '@/components/MkAvatars.vue';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { userListsCache } from '@/cache';
-import { infoImageUrl } from '@/instance';
-import { $i } from '@/account';
+import { infoImageUrl } from '@/instance.js';
+import { $i } from '@/account.js';
 
 const items = $computed(() => userListsCache.value.value ?? []);
 
diff --git a/packages/frontend/src/pages/my-lists/list.vue b/packages/frontend/src/pages/my-lists/list.vue
index 5a6d0b15a6..df13998f6b 100644
--- a/packages/frontend/src/pages/my-lists/list.vue
+++ b/packages/frontend/src/pages/my-lists/list.vue
@@ -50,18 +50,18 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { computed, ref, watch } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkButton from '@/components/MkButton.vue';
-import * as os from '@/os';
-import { mainRouter } from '@/router';
-import { definePageMetadata } from '@/scripts/page-metadata';
-import { i18n } from '@/i18n';
-import { userPage } from '@/filters/user';
+import * as os from '@/os.js';
+import { mainRouter } from '@/router.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { i18n } from '@/i18n.js';
+import { userPage } from '@/filters/user.js';
 import MkUserCardMini from '@/components/MkUserCardMini.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
 import MkFolder from '@/components/MkFolder.vue';
 import MkInput from '@/components/MkInput.vue';
 import { userListsCache } from '@/cache';
-import { $i } from '@/account';
-import { defaultStore } from '@/store';
+import { $i } from '@/account.js';
+import { defaultStore } from '@/store.js';
 const {
 	enableInfiniteScroll,
 } = defaultStore.reactiveState;
diff --git a/packages/frontend/src/pages/not-found.vue b/packages/frontend/src/pages/not-found.vue
index 312729476d..b3d40e3ef8 100644
--- a/packages/frontend/src/pages/not-found.vue
+++ b/packages/frontend/src/pages/not-found.vue
@@ -13,10 +13,10 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
-import { pleaseLogin } from '@/scripts/please-login';
-import { notFoundImageUrl } from '@/instance';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { pleaseLogin } from '@/scripts/please-login.js';
+import { notFoundImageUrl } from '@/instance.js';
 
 const props = defineProps<{
 	showLoginPopup?: boolean;
diff --git a/packages/frontend/src/pages/note.vue b/packages/frontend/src/pages/note.vue
index ea5e4f5090..33a49c3af7 100644
--- a/packages/frontend/src/pages/note.vue
+++ b/packages/frontend/src/pages/note.vue
@@ -50,12 +50,12 @@ import MkNoteDetailed from '@/components/MkNoteDetailed.vue';
 import MkNotes from '@/components/MkNotes.vue';
 import MkRemoteCaution from '@/components/MkRemoteCaution.vue';
 import MkButton from '@/components/MkButton.vue';
-import * as os from '@/os';
-import { definePageMetadata } from '@/scripts/page-metadata';
-import { i18n } from '@/i18n';
-import { dateString } from '@/filters/date';
+import * as os from '@/os.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { i18n } from '@/i18n.js';
+import { dateString } from '@/filters/date.js';
 import MkClipPreview from '@/components/MkClipPreview.vue';
-import { defaultStore } from '@/store';
+import { defaultStore } from '@/store.js';
 
 const props = defineProps<{
 	noteId: string;
diff --git a/packages/frontend/src/pages/notifications.vue b/packages/frontend/src/pages/notifications.vue
index e3f94d1e0d..3ae6319c7e 100644
--- a/packages/frontend/src/pages/notifications.vue
+++ b/packages/frontend/src/pages/notifications.vue
@@ -24,9 +24,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { computed } from 'vue';
 import XNotifications from '@/components/MkNotifications.vue';
 import MkNotes from '@/components/MkNotes.vue';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { notificationTypes } from '@/const';
 
 let tab = $ref('all');
diff --git a/packages/frontend/src/pages/oauth.vue b/packages/frontend/src/pages/oauth.vue
index 0dbaed1081..878fa6be4e 100644
--- a/packages/frontend/src/pages/oauth.vue
+++ b/packages/frontend/src/pages/oauth.vue
@@ -35,9 +35,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import MkSignin from '@/components/MkSignin.vue';
 import MkButton from '@/components/MkButton.vue';
-import { $i, login } from '@/account';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import { $i, login } from '@/account.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 
 const transactionIdMeta = document.querySelector<HTMLMetaElement>('meta[name="misskey:oauth:transaction-id"]');
 if (transactionIdMeta) {
diff --git a/packages/frontend/src/pages/page-editor/els/page-editor.el.image.vue b/packages/frontend/src/pages/page-editor/els/page-editor.el.image.vue
index 50d728d62c..4fffd311b2 100644
--- a/packages/frontend/src/pages/page-editor/els/page-editor.el.image.vue
+++ b/packages/frontend/src/pages/page-editor/els/page-editor.el.image.vue
@@ -24,8 +24,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { onMounted } from 'vue';
 import XContainer from '../page-editor.container.vue';
 import MkDriveFileThumbnail from '@/components/MkDriveFileThumbnail.vue';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
 
 const props = defineProps<{
 	modelValue: any
diff --git a/packages/frontend/src/pages/page-editor/els/page-editor.el.note.vue b/packages/frontend/src/pages/page-editor/els/page-editor.el.note.vue
index 18316acaf8..fc436aad75 100644
--- a/packages/frontend/src/pages/page-editor/els/page-editor.el.note.vue
+++ b/packages/frontend/src/pages/page-editor/els/page-editor.el.note.vue
@@ -29,8 +29,8 @@ import MkInput from '@/components/MkInput.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
 import MkNote from '@/components/MkNote.vue';
 import MkNoteDetailed from '@/components/MkNoteDetailed.vue';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
 
 const props = defineProps<{
 	modelValue: any
diff --git a/packages/frontend/src/pages/page-editor/els/page-editor.el.section.vue b/packages/frontend/src/pages/page-editor/els/page-editor.el.section.vue
index 044be39072..31c2c96d7c 100644
--- a/packages/frontend/src/pages/page-editor/els/page-editor.el.section.vue
+++ b/packages/frontend/src/pages/page-editor/els/page-editor.el.section.vue
@@ -25,9 +25,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { defineAsyncComponent, inject, onMounted, watch } from 'vue';
 import { v4 as uuid } from 'uuid';
 import XContainer from '../page-editor.container.vue';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
-import { deepClone } from '@/scripts/clone';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
+import { deepClone } from '@/scripts/clone.js';
 import MkButton from '@/components/MkButton.vue';
 
 const XBlocks = defineAsyncComponent(() => import('../page-editor.blocks.vue'));
diff --git a/packages/frontend/src/pages/page-editor/els/page-editor.el.text.vue b/packages/frontend/src/pages/page-editor/els/page-editor.el.text.vue
index 0d18b0d565..2ecf5790b8 100644
--- a/packages/frontend/src/pages/page-editor/els/page-editor.el.text.vue
+++ b/packages/frontend/src/pages/page-editor/els/page-editor.el.text.vue
@@ -18,7 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 /* eslint-disable vue/no-mutating-props */
 import { watch } from 'vue';
 import XContainer from '../page-editor.container.vue';
-import { i18n } from '@/i18n';
+import { i18n } from '@/i18n.js';
 
 const props = defineProps<{
 	modelValue: any
diff --git a/packages/frontend/src/pages/page-editor/page-editor.container.vue b/packages/frontend/src/pages/page-editor/page-editor.container.vue
index 29c7235b99..c1bc0c61c9 100644
--- a/packages/frontend/src/pages/page-editor/page-editor.container.vue
+++ b/packages/frontend/src/pages/page-editor/page-editor.container.vue
@@ -29,7 +29,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { ref } from 'vue';
-import { i18n } from '@/i18n';
+import { i18n } from '@/i18n.js';
 
 const props = withDefaults(defineProps<{
 	expanded?: boolean;
diff --git a/packages/frontend/src/pages/page-editor/page-editor.vue b/packages/frontend/src/pages/page-editor/page-editor.vue
index be0f7c9fde..377267bdbc 100644
--- a/packages/frontend/src/pages/page-editor/page-editor.vue
+++ b/packages/frontend/src/pages/page-editor/page-editor.vue
@@ -68,13 +68,13 @@ import MkButton from '@/components/MkButton.vue';
 import MkSelect from '@/components/MkSelect.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
 import MkInput from '@/components/MkInput.vue';
-import { url } from '@/config';
-import * as os from '@/os';
-import { selectFile } from '@/scripts/select-file';
-import { mainRouter } from '@/router';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
-import { $i } from '@/account';
+import { url } from '@/config.js';
+import * as os from '@/os.js';
+import { selectFile } from '@/scripts/select-file.js';
+import { mainRouter } from '@/router.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { $i } from '@/account.js';
 
 const props = defineProps<{
 	initPageId?: string;
diff --git a/packages/frontend/src/pages/page.vue b/packages/frontend/src/pages/page.vue
index 5cb70da973..98cbaab2bb 100644
--- a/packages/frontend/src/pages/page.vue
+++ b/packages/frontend/src/pages/page.vue
@@ -78,18 +78,18 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { computed, watch } from 'vue';
 import XPage from '@/components/page/page.vue';
 import MkButton from '@/components/MkButton.vue';
-import * as os from '@/os';
-import { url } from '@/config';
+import * as os from '@/os.js';
+import { url } from '@/config.js';
 import MkMediaImage from '@/components/MkMediaImage.vue';
 import MkFollowButton from '@/components/MkFollowButton.vue';
 import MkContainer from '@/components/MkContainer.vue';
 import MkPagination from '@/components/MkPagination.vue';
 import MkPagePreview from '@/components/MkPagePreview.vue';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
-import { pageViewInterruptors, defaultStore } from '@/store';
-import { deepClone } from '@/scripts/clone';
-import { $i } from '@/account';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { pageViewInterruptors, defaultStore } from '@/store.js';
+import { deepClone } from '@/scripts/clone.js';
+import { $i } from '@/account.js';
 
 const props = defineProps<{
 	pageName: string;
diff --git a/packages/frontend/src/pages/pages.vue b/packages/frontend/src/pages/pages.vue
index 90c0fae689..6f40b24771 100644
--- a/packages/frontend/src/pages/pages.vue
+++ b/packages/frontend/src/pages/pages.vue
@@ -40,9 +40,9 @@ import { computed } from 'vue';
 import MkPagePreview from '@/components/MkPagePreview.vue';
 import MkPagination from '@/components/MkPagination.vue';
 import MkButton from '@/components/MkButton.vue';
-import { useRouter } from '@/router';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import { useRouter } from '@/router.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 
 const router = useRouter();
 
diff --git a/packages/frontend/src/pages/registry.keys.vue b/packages/frontend/src/pages/registry.keys.vue
index 46fabc08c0..a1a5fd0cf3 100644
--- a/packages/frontend/src/pages/registry.keys.vue
+++ b/packages/frontend/src/pages/registry.keys.vue
@@ -35,9 +35,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { watch } from 'vue';
 import JSON5 from 'json5';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 import FormLink from '@/components/form/link.vue';
 import FormSection from '@/components/form/section.vue';
 import MkButton from '@/components/MkButton.vue';
diff --git a/packages/frontend/src/pages/registry.value.vue b/packages/frontend/src/pages/registry.value.vue
index a8531fae30..ebcb04e9f5 100644
--- a/packages/frontend/src/pages/registry.value.vue
+++ b/packages/frontend/src/pages/registry.value.vue
@@ -47,9 +47,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { watch } from 'vue';
 import JSON5 from 'json5';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 import MkButton from '@/components/MkButton.vue';
 import MkKeyValue from '@/components/MkKeyValue.vue';
 import MkTextarea from '@/components/MkTextarea.vue';
diff --git a/packages/frontend/src/pages/registry.vue b/packages/frontend/src/pages/registry.vue
index 64afcead9a..37a0b52511 100644
--- a/packages/frontend/src/pages/registry.vue
+++ b/packages/frontend/src/pages/registry.vue
@@ -21,9 +21,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import JSON5 from 'json5';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 import FormLink from '@/components/form/link.vue';
 import FormSection from '@/components/form/section.vue';
 import MkButton from '@/components/MkButton.vue';
diff --git a/packages/frontend/src/pages/reset-password.vue b/packages/frontend/src/pages/reset-password.vue
index 8f42bc6949..718ca7d773 100644
--- a/packages/frontend/src/pages/reset-password.vue
+++ b/packages/frontend/src/pages/reset-password.vue
@@ -23,10 +23,10 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { defineAsyncComponent, onMounted } from 'vue';
 import MkInput from '@/components/MkInput.vue';
 import MkButton from '@/components/MkButton.vue';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
-import { mainRouter } from '@/router';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
+import { mainRouter } from '@/router.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 
 const props = defineProps<{
 	token?: string;
diff --git a/packages/frontend/src/pages/role.vue b/packages/frontend/src/pages/role.vue
index 8ad5d0c38f..b2a0a931f9 100644
--- a/packages/frontend/src/pages/role.vue
+++ b/packages/frontend/src/pages/role.vue
@@ -29,13 +29,13 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { computed, watch } from 'vue';
-import * as os from '@/os';
+import * as os from '@/os.js';
 import MkUserList from '@/components/MkUserList.vue';
-import { definePageMetadata } from '@/scripts/page-metadata';
-import { i18n } from '@/i18n';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { i18n } from '@/i18n.js';
 import MkTimeline from '@/components/MkTimeline.vue';
-import { instanceName } from '@/config';
-import { serverErrorImageUrl } from '@/instance';
+import { instanceName } from '@/config.js';
+import { serverErrorImageUrl } from '@/instance.js';
 
 const props = withDefaults(defineProps<{
 	role: string;
diff --git a/packages/frontend/src/pages/scratchpad.vue b/packages/frontend/src/pages/scratchpad.vue
index 6d68a26c3c..8d5a29d394 100644
--- a/packages/frontend/src/pages/scratchpad.vue
+++ b/packages/frontend/src/pages/scratchpad.vue
@@ -44,15 +44,15 @@ import 'vue-prism-editor/dist/prismeditor.min.css';
 import { Interpreter, Parser, utils } from '@syuilo/aiscript';
 import MkContainer from '@/components/MkContainer.vue';
 import MkButton from '@/components/MkButton.vue';
-import { createAiScriptEnv } from '@/scripts/aiscript/api';
-import * as os from '@/os';
-import { $i } from '@/account';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
-import { AsUiComponent, AsUiRoot, registerAsUiLib } from '@/scripts/aiscript/ui';
+import { createAiScriptEnv } from '@/scripts/aiscript/api.js';
+import * as os from '@/os.js';
+import { $i } from '@/account.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { AsUiComponent, AsUiRoot, registerAsUiLib } from '@/scripts/aiscript/ui.js';
 import MkAsUi from '@/components/MkAsUi.vue';
-import { miLocalStorage } from '@/local-storage';
-import { claimAchievement } from '@/scripts/achievements';
+import { miLocalStorage } from '@/local-storage.js';
+import { claimAchievement } from '@/scripts/achievements.js';
 
 const parser = new Parser();
 let aiscript: Interpreter;
diff --git a/packages/frontend/src/pages/search.note.vue b/packages/frontend/src/pages/search.note.vue
index b0881fdedb..c1692d8be2 100644
--- a/packages/frontend/src/pages/search.note.vue
+++ b/packages/frontend/src/pages/search.note.vue
@@ -48,13 +48,13 @@ import MkInput from '@/components/MkInput.vue';
 import MkRadios from '@/components/MkRadios.vue';
 import MkButton from '@/components/MkButton.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
-import { i18n } from '@/i18n';
-import * as os from '@/os';
+import { i18n } from '@/i18n.js';
+import * as os from '@/os.js';
 import MkFoldableSection from '@/components/MkFoldableSection.vue';
-import { $i } from '@/account';
-import { instance } from '@/instance';
+import { $i } from '@/account.js';
+import { instance } from '@/instance.js';
 import MkInfo from '@/components/MkInfo.vue';
-import { useRouter } from '@/router';
+import { useRouter } from '@/router.js';
 import MkFolder from '@/components/MkFolder.vue';
 
 const router = useRouter();
diff --git a/packages/frontend/src/pages/search.user.vue b/packages/frontend/src/pages/search.user.vue
index 8a71ed00b7..1a7501adc6 100644
--- a/packages/frontend/src/pages/search.user.vue
+++ b/packages/frontend/src/pages/search.user.vue
@@ -30,13 +30,13 @@ import MkUserList from '@/components/MkUserList.vue';
 import MkInput from '@/components/MkInput.vue';
 import MkRadios from '@/components/MkRadios.vue';
 import MkButton from '@/components/MkButton.vue';
-import { i18n } from '@/i18n';
-import * as os from '@/os';
+import { i18n } from '@/i18n.js';
+import * as os from '@/os.js';
 import MkFoldableSection from '@/components/MkFoldableSection.vue';
-import { $i } from '@/account';
-import { instance } from '@/instance';
+import { $i } from '@/account.js';
+import { instance } from '@/instance.js';
 import MkInfo from '@/components/MkInfo.vue';
-import { useRouter } from '@/router';
+import { useRouter } from '@/router.js';
 
 const router = useRouter();
 
diff --git a/packages/frontend/src/pages/search.vue b/packages/frontend/src/pages/search.vue
index ab04120bbe..e205fe850f 100644
--- a/packages/frontend/src/pages/search.vue
+++ b/packages/frontend/src/pages/search.vue
@@ -24,11 +24,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { computed, defineAsyncComponent, onMounted } from 'vue';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
-import * as os from '@/os';
-import { $i } from '@/account';
-import { instance } from '@/instance';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import * as os from '@/os.js';
+import { $i } from '@/account.js';
+import { instance } from '@/instance.js';
 import MkInfo from '@/components/MkInfo.vue';
 
 const XNote = defineAsyncComponent(() => import('./search.note.vue'));
diff --git a/packages/frontend/src/pages/settings/2fa.qrdialog.vue b/packages/frontend/src/pages/settings/2fa.qrdialog.vue
index 993f756fc5..c2d1694c5b 100644
--- a/packages/frontend/src/pages/settings/2fa.qrdialog.vue
+++ b/packages/frontend/src/pages/settings/2fa.qrdialog.vue
@@ -103,11 +103,11 @@ import MkButton from '@/components/MkButton.vue';
 import MkModalWindow from '@/components/MkModalWindow.vue';
 import MkKeyValue from '@/components/MkKeyValue.vue';
 import MkInput from '@/components/MkInput.vue';
-import { i18n } from '@/i18n';
-import * as os from '@/os';
+import { i18n } from '@/i18n.js';
+import * as os from '@/os.js';
 import MkFolder from '@/components/MkFolder.vue';
 import MkInfo from '@/components/MkInfo.vue';
-import { confetti } from '@/scripts/confetti';
+import { confetti } from '@/scripts/confetti.js';
 
 defineProps<{
 	twoFactorData: {
diff --git a/packages/frontend/src/pages/settings/2fa.vue b/packages/frontend/src/pages/settings/2fa.vue
index 965fd1a500..37455ac2d0 100644
--- a/packages/frontend/src/pages/settings/2fa.vue
+++ b/packages/frontend/src/pages/settings/2fa.vue
@@ -79,9 +79,9 @@ import MkInfo from '@/components/MkInfo.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
 import FormSection from '@/components/form/section.vue';
 import MkFolder from '@/components/MkFolder.vue';
-import * as os from '@/os';
-import { $i } from '@/account';
-import { i18n } from '@/i18n';
+import * as os from '@/os.js';
+import { $i } from '@/account.js';
+import { i18n } from '@/i18n.js';
 
 // メモ: 各エンドポイントはmeUpdatedを発行するため、refreshAccountは不要
 
diff --git a/packages/frontend/src/pages/settings/accounts.vue b/packages/frontend/src/pages/settings/accounts.vue
index 30afcc26de..df8c7b440a 100644
--- a/packages/frontend/src/pages/settings/accounts.vue
+++ b/packages/frontend/src/pages/settings/accounts.vue
@@ -23,10 +23,10 @@ import { defineAsyncComponent, ref } from 'vue';
 import type * as Misskey from 'misskey-js';
 import FormSuspense from '@/components/form/suspense.vue';
 import MkButton from '@/components/MkButton.vue';
-import * as os from '@/os';
-import { getAccounts, addAccount as addAccounts, removeAccount as _removeAccount, login, $i } from '@/account';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import * as os from '@/os.js';
+import { getAccounts, addAccount as addAccounts, removeAccount as _removeAccount, login, $i } from '@/account.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 import MkUserCardMini from '@/components/MkUserCardMini.vue';
 
 const storedAccounts = ref<any>(null);
diff --git a/packages/frontend/src/pages/settings/api.vue b/packages/frontend/src/pages/settings/api.vue
index e75435dd52..e0266bccba 100644
--- a/packages/frontend/src/pages/settings/api.vue
+++ b/packages/frontend/src/pages/settings/api.vue
@@ -15,9 +15,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { defineAsyncComponent, ref } from 'vue';
 import FormLink from '@/components/form/link.vue';
 import MkButton from '@/components/MkButton.vue';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 
 const isDesktop = ref(window.innerWidth >= 1100);
 
diff --git a/packages/frontend/src/pages/settings/apps.vue b/packages/frontend/src/pages/settings/apps.vue
index e112b8b641..7fd4ed61c9 100644
--- a/packages/frontend/src/pages/settings/apps.vue
+++ b/packages/frontend/src/pages/settings/apps.vue
@@ -47,12 +47,12 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { ref } from 'vue';
 import FormPagination from '@/components/MkPagination.vue';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 import MkKeyValue from '@/components/MkKeyValue.vue';
 import MkButton from '@/components/MkButton.vue';
-import { infoImageUrl } from '@/instance';
+import { infoImageUrl } from '@/instance.js';
 
 const list = ref<any>(null);
 
diff --git a/packages/frontend/src/pages/settings/custom-css.vue b/packages/frontend/src/pages/settings/custom-css.vue
index 154c952e84..d58f959fc5 100644
--- a/packages/frontend/src/pages/settings/custom-css.vue
+++ b/packages/frontend/src/pages/settings/custom-css.vue
@@ -17,11 +17,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { ref, watch } from 'vue';
 import MkTextarea from '@/components/MkTextarea.vue';
 import FormInfo from '@/components/MkInfo.vue';
-import * as os from '@/os';
-import { unisonReload } from '@/scripts/unison-reload';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
-import { miLocalStorage } from '@/local-storage';
+import * as os from '@/os.js';
+import { unisonReload } from '@/scripts/unison-reload.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { miLocalStorage } from '@/local-storage.js';
 
 const localCustomCss = ref(miLocalStorage.getItem('customCss') ?? '');
 
diff --git a/packages/frontend/src/pages/settings/deck.vue b/packages/frontend/src/pages/settings/deck.vue
index 7e9a0d1b4c..9fecc65d6d 100644
--- a/packages/frontend/src/pages/settings/deck.vue
+++ b/packages/frontend/src/pages/settings/deck.vue
@@ -23,9 +23,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { computed } from 'vue';
 import MkSwitch from '@/components/MkSwitch.vue';
 import MkRadios from '@/components/MkRadios.vue';
-import { deckStore } from '@/ui/deck/deck-store';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import { deckStore } from '@/ui/deck/deck-store.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 
 const navWindow = computed(deckStore.makeGetterSetter('navWindow'));
 const useSimpleUiForNonRootPages = computed(deckStore.makeGetterSetter('useSimpleUiForNonRootPages'));
diff --git a/packages/frontend/src/pages/settings/drive-cleaner.vue b/packages/frontend/src/pages/settings/drive-cleaner.vue
index 171a797f79..899d824bf9 100644
--- a/packages/frontend/src/pages/settings/drive-cleaner.vue
+++ b/packages/frontend/src/pages/settings/drive-cleaner.vue
@@ -50,15 +50,15 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script setup lang="ts">
 import { computed, ref, watch } from 'vue';
 import tinycolor from 'tinycolor2';
-import * as os from '@/os';
+import * as os from '@/os.js';
 import MkPagination from '@/components/MkPagination.vue';
 import MkDriveFileThumbnail from '@/components/MkDriveFileThumbnail.vue';
-import { i18n } from '@/i18n';
-import bytes from '@/filters/bytes';
-import { dateString } from '@/filters/date';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import { i18n } from '@/i18n.js';
+import bytes from '@/filters/bytes.js';
+import { dateString } from '@/filters/date.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 import MkSelect from '@/components/MkSelect.vue';
-import { getDriveFileMenu } from '@/scripts/get-drive-file-menu';
+import { getDriveFileMenu } from '@/scripts/get-drive-file-menu.js';
 
 let sortMode = ref('+size');
 const pagination = {
diff --git a/packages/frontend/src/pages/settings/drive.vue b/packages/frontend/src/pages/settings/drive.vue
index de502e8511..01a0711682 100644
--- a/packages/frontend/src/pages/settings/drive.vue
+++ b/packages/frontend/src/pages/settings/drive.vue
@@ -64,13 +64,13 @@ import MkSwitch from '@/components/MkSwitch.vue';
 import FormSection from '@/components/form/section.vue';
 import MkKeyValue from '@/components/MkKeyValue.vue';
 import FormSplit from '@/components/form/split.vue';
-import * as os from '@/os';
-import bytes from '@/filters/bytes';
-import { defaultStore } from '@/store';
+import * as os from '@/os.js';
+import bytes from '@/filters/bytes.js';
+import { defaultStore } from '@/store.js';
 import MkChart from '@/components/MkChart.vue';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
-import { $i } from '@/account';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { $i } from '@/account.js';
 
 const fetching = ref(true);
 const usage = ref<any>(null);
diff --git a/packages/frontend/src/pages/settings/email.vue b/packages/frontend/src/pages/settings/email.vue
index ec2851108a..1a70c3dbfb 100644
--- a/packages/frontend/src/pages/settings/email.vue
+++ b/packages/frontend/src/pages/settings/email.vue
@@ -53,11 +53,11 @@ import FormSection from '@/components/form/section.vue';
 import MkInfo from '@/components/MkInfo.vue';
 import MkInput from '@/components/MkInput.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
-import * as os from '@/os';
-import { $i } from '@/account';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
-import { instance } from '@/instance';
+import * as os from '@/os.js';
+import { $i } from '@/account.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { instance } from '@/instance.js';
 
 const emailAddress = ref($i!.email);
 
diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue
index ff498e4164..ad643fc005 100644
--- a/packages/frontend/src/pages/settings/general.vue
+++ b/packages/frontend/src/pages/settings/general.vue
@@ -192,15 +192,15 @@ import MkButton from '@/components/MkButton.vue';
 import FormSection from '@/components/form/section.vue';
 import FormLink from '@/components/form/link.vue';
 import MkLink from '@/components/MkLink.vue';
-import { langs } from '@/config';
-import { defaultStore } from '@/store';
-import * as os from '@/os';
-import { unisonReload } from '@/scripts/unison-reload';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
-import { miLocalStorage } from '@/local-storage';
+import { langs } from '@/config.js';
+import { defaultStore } from '@/store.js';
+import * as os from '@/os.js';
+import { unisonReload } from '@/scripts/unison-reload.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { miLocalStorage } from '@/local-storage.js';
 import { globalEvents } from '@/events';
-import { claimAchievement } from '@/scripts/achievements';
+import { claimAchievement } from '@/scripts/achievements.js';
 
 const lang = ref(miLocalStorage.getItem('lang'));
 const fontSize = ref(miLocalStorage.getItem('fontSize'));
diff --git a/packages/frontend/src/pages/settings/import-export.vue b/packages/frontend/src/pages/settings/import-export.vue
index 2db0333351..0574a878ae 100644
--- a/packages/frontend/src/pages/settings/import-export.vue
+++ b/packages/frontend/src/pages/settings/import-export.vue
@@ -113,11 +113,11 @@ import MkButton from '@/components/MkButton.vue';
 import FormSection from '@/components/form/section.vue';
 import MkFolder from '@/components/MkFolder.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
-import * as os from '@/os';
-import { selectFile } from '@/scripts/select-file';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
-import { $i } from '@/account';
+import * as os from '@/os.js';
+import { selectFile } from '@/scripts/select-file.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { $i } from '@/account.js';
 
 const excludeMutingUsers = ref(false);
 const excludeInactiveUsers = ref(false);
diff --git a/packages/frontend/src/pages/settings/index.vue b/packages/frontend/src/pages/settings/index.vue
index 2cf6139606..f1bd501150 100644
--- a/packages/frontend/src/pages/settings/index.vue
+++ b/packages/frontend/src/pages/settings/index.vue
@@ -28,17 +28,17 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script setup lang="ts">
 import { computed, onActivated, onMounted, onUnmounted, ref, shallowRef, watch } from 'vue';
-import { i18n } from '@/i18n';
+import { i18n } from '@/i18n.js';
 import MkInfo from '@/components/MkInfo.vue';
 import MkSuperMenu from '@/components/MkSuperMenu.vue';
-import { signout, $i } from '@/account';
-import { unisonReload } from '@/scripts/unison-reload';
-import { instance } from '@/instance';
-import { useRouter } from '@/router';
-import { definePageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata';
-import * as os from '@/os';
-import { miLocalStorage } from '@/local-storage';
-import { fetchCustomEmojis } from '@/custom-emojis';
+import { signout, $i } from '@/account.js';
+import { unisonReload } from '@/scripts/unison-reload.js';
+import { instance } from '@/instance.js';
+import { useRouter } from '@/router.js';
+import { definePageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata.js';
+import * as os from '@/os.js';
+import { miLocalStorage } from '@/local-storage.js';
+import { fetchCustomEmojis } from '@/custom-emojis.js';
 
 const indexInfo = {
 	title: i18n.ts.settings,
diff --git a/packages/frontend/src/pages/settings/instance-mute.vue b/packages/frontend/src/pages/settings/instance-mute.vue
index 19deadd64d..b76fd2c906 100644
--- a/packages/frontend/src/pages/settings/instance-mute.vue
+++ b/packages/frontend/src/pages/settings/instance-mute.vue
@@ -19,10 +19,10 @@ import { ref, watch } from 'vue';
 import MkTextarea from '@/components/MkTextarea.vue';
 import MkInfo from '@/components/MkInfo.vue';
 import MkButton from '@/components/MkButton.vue';
-import * as os from '@/os';
-import { $i } from '@/account';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import * as os from '@/os.js';
+import { $i } from '@/account.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 
 const instanceMutes = ref($i!.mutedInstances.join('\n'));
 const changed = ref(false);
diff --git a/packages/frontend/src/pages/settings/migration.vue b/packages/frontend/src/pages/settings/migration.vue
index ea574689a8..15bf4691b2 100644
--- a/packages/frontend/src/pages/settings/migration.vue
+++ b/packages/frontend/src/pages/settings/migration.vue
@@ -65,11 +65,11 @@ import MkInput from '@/components/MkInput.vue';
 import MkButton from '@/components/MkButton.vue';
 import MkFolder from '@/components/MkFolder.vue';
 import MkUserInfo from '@/components/MkUserInfo.vue';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
-import { $i } from '@/account';
-import { unisonReload } from '@/scripts/unison-reload';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { $i } from '@/account.js';
+import { unisonReload } from '@/scripts/unison-reload.js';
 
 const moveToAccount = ref('');
 const movedTo = ref<Misskey.entities.UserDetailed>();
diff --git a/packages/frontend/src/pages/settings/mute-block.vue b/packages/frontend/src/pages/settings/mute-block.vue
index c35fc0e0e3..37d3d1773f 100644
--- a/packages/frontend/src/pages/settings/mute-block.vue
+++ b/packages/frontend/src/pages/settings/mute-block.vue
@@ -107,12 +107,12 @@ import MkPagination from '@/components/MkPagination.vue';
 import MkTab from '@/components/MkTab.vue';
 import FormInfo from '@/components/MkInfo.vue';
 import FormLink from '@/components/form/link.vue';
-import { userPage } from '@/filters/user';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import { userPage } from '@/filters/user.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 import MkUserCardMini from '@/components/MkUserCardMini.vue';
-import * as os from '@/os';
-import { infoImageUrl } from '@/instance';
+import * as os from '@/os.js';
+import { infoImageUrl } from '@/instance.js';
 
 let tab = $ref('renoteMute');
 
diff --git a/packages/frontend/src/pages/settings/navbar.vue b/packages/frontend/src/pages/settings/navbar.vue
index 8dcca7e91c..c9cccd7dcb 100644
--- a/packages/frontend/src/pages/settings/navbar.vue
+++ b/packages/frontend/src/pages/settings/navbar.vue
@@ -51,13 +51,13 @@ import MkRadios from '@/components/MkRadios.vue';
 import MkButton from '@/components/MkButton.vue';
 import FormSlot from '@/components/form/slot.vue';
 import MkContainer from '@/components/MkContainer.vue';
-import * as os from '@/os';
+import * as os from '@/os.js';
 import { navbarItemDef } from '@/navbar';
-import { defaultStore } from '@/store';
-import { unisonReload } from '@/scripts/unison-reload';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
-import { deepClone } from '@/scripts/clone';
+import { defaultStore } from '@/store.js';
+import { unisonReload } from '@/scripts/unison-reload.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { deepClone } from '@/scripts/clone.js';
 
 const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default));
 
diff --git a/packages/frontend/src/pages/settings/notifications.vue b/packages/frontend/src/pages/settings/notifications.vue
index 193795cdec..5b3c29e7c8 100644
--- a/packages/frontend/src/pages/settings/notifications.vue
+++ b/packages/frontend/src/pages/settings/notifications.vue
@@ -40,10 +40,10 @@ import { defineAsyncComponent } from 'vue';
 import FormLink from '@/components/form/link.vue';
 import FormSection from '@/components/form/section.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
-import * as os from '@/os';
-import { $i } from '@/account';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import * as os from '@/os.js';
+import { $i } from '@/account.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 import MkPushNotificationAllowButton from '@/components/MkPushNotificationAllowButton.vue';
 import { notificationTypes } from '@/const';
 
diff --git a/packages/frontend/src/pages/settings/other.vue b/packages/frontend/src/pages/settings/other.vue
index 44b12099c0..c3278c22f3 100644
--- a/packages/frontend/src/pages/settings/other.vue
+++ b/packages/frontend/src/pages/settings/other.vue
@@ -84,12 +84,12 @@ import MkFolder from '@/components/MkFolder.vue';
 import FormInfo from '@/components/MkInfo.vue';
 import MkKeyValue from '@/components/MkKeyValue.vue';
 import MkButton from '@/components/MkButton.vue';
-import * as os from '@/os';
-import { defaultStore } from '@/store';
-import { signout, $i } from '@/account';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
-import { unisonReload } from '@/scripts/unison-reload';
+import * as os from '@/os.js';
+import { defaultStore } from '@/store.js';
+import { signout, $i } from '@/account.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { unisonReload } from '@/scripts/unison-reload.js';
 import FormSection from '@/components/form/section.vue';
 
 const reportError = computed(defaultStore.makeGetterSetter('reportError'));
diff --git a/packages/frontend/src/pages/settings/plugin.install.vue b/packages/frontend/src/pages/settings/plugin.install.vue
index d848f8e21c..47ebe9cfd6 100644
--- a/packages/frontend/src/pages/settings/plugin.install.vue
+++ b/packages/frontend/src/pages/settings/plugin.install.vue
@@ -25,11 +25,11 @@ import { v4 as uuid } from 'uuid';
 import MkTextarea from '@/components/MkTextarea.vue';
 import MkButton from '@/components/MkButton.vue';
 import FormInfo from '@/components/MkInfo.vue';
-import * as os from '@/os';
-import { ColdDeviceStorage } from '@/store';
-import { unisonReload } from '@/scripts/unison-reload';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import * as os from '@/os.js';
+import { ColdDeviceStorage } from '@/store.js';
+import { unisonReload } from '@/scripts/unison-reload.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 
 const parser = new Parser();
 const code = ref(null);
diff --git a/packages/frontend/src/pages/settings/plugin.vue b/packages/frontend/src/pages/settings/plugin.vue
index 015d2f877d..e9bc8573b3 100644
--- a/packages/frontend/src/pages/settings/plugin.vue
+++ b/packages/frontend/src/pages/settings/plugin.vue
@@ -45,11 +45,11 @@ import MkSwitch from '@/components/MkSwitch.vue';
 import FormSection from '@/components/form/section.vue';
 import MkButton from '@/components/MkButton.vue';
 import MkKeyValue from '@/components/MkKeyValue.vue';
-import * as os from '@/os';
-import { ColdDeviceStorage } from '@/store';
-import { unisonReload } from '@/scripts/unison-reload';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import * as os from '@/os.js';
+import { ColdDeviceStorage } from '@/store.js';
+import { unisonReload } from '@/scripts/unison-reload.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 
 const plugins = ref(ColdDeviceStorage.get('plugins'));
 
diff --git a/packages/frontend/src/pages/settings/preferences-backups.vue b/packages/frontend/src/pages/settings/preferences-backups.vue
index eb33cbd3a4..3b3a6bd07d 100644
--- a/packages/frontend/src/pages/settings/preferences-backups.vue
+++ b/packages/frontend/src/pages/settings/preferences-backups.vue
@@ -42,15 +42,15 @@ import { v4 as uuid } from 'uuid';
 import FormSection from '@/components/form/section.vue';
 import MkButton from '@/components/MkButton.vue';
 import MkInfo from '@/components/MkInfo.vue';
-import * as os from '@/os';
-import { ColdDeviceStorage, defaultStore } from '@/store';
-import { unisonReload } from '@/scripts/unison-reload';
-import { useStream } from '@/stream';
-import { $i } from '@/account';
-import { i18n } from '@/i18n';
-import { version, host } from '@/config';
-import { definePageMetadata } from '@/scripts/page-metadata';
-import { miLocalStorage } from '@/local-storage';
+import * as os from '@/os.js';
+import { ColdDeviceStorage, defaultStore } from '@/store.js';
+import { unisonReload } from '@/scripts/unison-reload.js';
+import { useStream } from '@/stream.js';
+import { $i } from '@/account.js';
+import { i18n } from '@/i18n.js';
+import { version, host } from '@/config.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { miLocalStorage } from '@/local-storage.js';
 const { t, ts } = i18n;
 
 const defaultStoreSaveKeys: (keyof typeof defaultStore['state'])[] = [
diff --git a/packages/frontend/src/pages/settings/privacy.vue b/packages/frontend/src/pages/settings/privacy.vue
index a2031f60dc..d195b4142c 100644
--- a/packages/frontend/src/pages/settings/privacy.vue
+++ b/packages/frontend/src/pages/settings/privacy.vue
@@ -71,11 +71,11 @@ import MkSwitch from '@/components/MkSwitch.vue';
 import MkSelect from '@/components/MkSelect.vue';
 import FormSection from '@/components/form/section.vue';
 import MkFolder from '@/components/MkFolder.vue';
-import * as os from '@/os';
-import { defaultStore } from '@/store';
-import { i18n } from '@/i18n';
-import { $i } from '@/account';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import * as os from '@/os.js';
+import { defaultStore } from '@/store.js';
+import { i18n } from '@/i18n.js';
+import { $i } from '@/account.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 
 let isLocked = $ref($i.isLocked);
 let autoAcceptFollowed = $ref($i.autoAcceptFollowed);
diff --git a/packages/frontend/src/pages/settings/profile.vue b/packages/frontend/src/pages/settings/profile.vue
index b5c26e161e..e045009c1b 100644
--- a/packages/frontend/src/pages/settings/profile.vue
+++ b/packages/frontend/src/pages/settings/profile.vue
@@ -111,14 +111,14 @@ import MkSelect from '@/components/MkSelect.vue';
 import FormSplit from '@/components/form/split.vue';
 import MkFolder from '@/components/MkFolder.vue';
 import FormSlot from '@/components/form/slot.vue';
-import { selectFile } from '@/scripts/select-file';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
-import { $i } from '@/account';
-import { langmap } from '@/scripts/langmap';
-import { definePageMetadata } from '@/scripts/page-metadata';
-import { claimAchievement } from '@/scripts/achievements';
-import { defaultStore } from '@/store';
+import { selectFile } from '@/scripts/select-file.js';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
+import { $i } from '@/account.js';
+import { langmap } from '@/scripts/langmap.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { claimAchievement } from '@/scripts/achievements.js';
+import { defaultStore } from '@/store.js';
 
 const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default));
 
diff --git a/packages/frontend/src/pages/settings/reaction.vue b/packages/frontend/src/pages/settings/reaction.vue
index d0148ac1ab..fb0f975212 100644
--- a/packages/frontend/src/pages/settings/reaction.vue
+++ b/packages/frontend/src/pages/settings/reaction.vue
@@ -67,11 +67,11 @@ import FromSlot from '@/components/form/slot.vue';
 import MkButton from '@/components/MkButton.vue';
 import FormSection from '@/components/form/section.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
-import * as os from '@/os';
-import { defaultStore } from '@/store';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
-import { deepClone } from '@/scripts/clone';
+import * as os from '@/os.js';
+import { defaultStore } from '@/store.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { deepClone } from '@/scripts/clone.js';
 
 let reactions = $ref(deepClone(defaultStore.state.reactions));
 
diff --git a/packages/frontend/src/pages/settings/roles.vue b/packages/frontend/src/pages/settings/roles.vue
index 30d5315709..71238af72e 100644
--- a/packages/frontend/src/pages/settings/roles.vue
+++ b/packages/frontend/src/pages/settings/roles.vue
@@ -33,11 +33,11 @@ import FormSplit from '@/components/form/split.vue';
 import MkFolder from '@/components/MkFolder.vue';
 import FormSlot from '@/components/form/slot.vue';
 import FormSection from '@/components/form/section.vue';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
-import { $i } from '@/account';
-import { definePageMetadata } from '@/scripts/page-metadata';
-import { defaultStore } from '@/store';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
+import { $i } from '@/account.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { defaultStore } from '@/store.js';
 import MkRolePreview from '@/components/MkRolePreview.vue';
 
 function save() {
diff --git a/packages/frontend/src/pages/settings/security.vue b/packages/frontend/src/pages/settings/security.vue
index a42b632b15..7b04ab974b 100644
--- a/packages/frontend/src/pages/settings/security.vue
+++ b/packages/frontend/src/pages/settings/security.vue
@@ -45,9 +45,9 @@ import FormSection from '@/components/form/section.vue';
 import FormSlot from '@/components/form/slot.vue';
 import MkButton from '@/components/MkButton.vue';
 import MkPagination from '@/components/MkPagination.vue';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 
 const pagination = {
 	endpoint: 'i/signin-history' as const,
diff --git a/packages/frontend/src/pages/settings/sounds.sound.vue b/packages/frontend/src/pages/settings/sounds.sound.vue
index a5f90f3e21..08a923e104 100644
--- a/packages/frontend/src/pages/settings/sounds.sound.vue
+++ b/packages/frontend/src/pages/settings/sounds.sound.vue
@@ -25,8 +25,8 @@ import { } from 'vue';
 import MkSelect from '@/components/MkSelect.vue';
 import MkButton from '@/components/MkButton.vue';
 import MkRange from '@/components/MkRange.vue';
-import { i18n } from '@/i18n';
-import { playFile, soundsTypes } from '@/scripts/sound';
+import { i18n } from '@/i18n.js';
+import { playFile, soundsTypes } from '@/scripts/sound.js';
 
 const props = defineProps<{
 	type: string;
diff --git a/packages/frontend/src/pages/settings/sounds.vue b/packages/frontend/src/pages/settings/sounds.vue
index de909db7e4..b6ac17ed4f 100644
--- a/packages/frontend/src/pages/settings/sounds.vue
+++ b/packages/frontend/src/pages/settings/sounds.vue
@@ -32,9 +32,9 @@ import MkRange from '@/components/MkRange.vue';
 import MkButton from '@/components/MkButton.vue';
 import FormSection from '@/components/form/section.vue';
 import MkFolder from '@/components/MkFolder.vue';
-import { soundConfigStore } from '@/scripts/sound';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import { soundConfigStore } from '@/scripts/sound.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 
 const masterVolume = computed(soundConfigStore.makeGetterSetter('sound_masterVolume'));
 
diff --git a/packages/frontend/src/pages/settings/statusbar.statusbar.vue b/packages/frontend/src/pages/settings/statusbar.statusbar.vue
index 8e3beb8a73..f1b7dcc0a8 100644
--- a/packages/frontend/src/pages/settings/statusbar.statusbar.vue
+++ b/packages/frontend/src/pages/settings/statusbar.statusbar.vue
@@ -93,9 +93,9 @@ import MkSwitch from '@/components/MkSwitch.vue';
 import MkRadios from '@/components/MkRadios.vue';
 import MkButton from '@/components/MkButton.vue';
 import MkRange from '@/components/MkRange.vue';
-import { defaultStore } from '@/store';
-import { i18n } from '@/i18n';
-import { deepClone } from '@/scripts/clone';
+import { defaultStore } from '@/store.js';
+import { i18n } from '@/i18n.js';
+import { deepClone } from '@/scripts/clone.js';
 
 const props = defineProps<{
 	_id: string;
diff --git a/packages/frontend/src/pages/settings/statusbar.vue b/packages/frontend/src/pages/settings/statusbar.vue
index 748579bb2a..7103c2582a 100644
--- a/packages/frontend/src/pages/settings/statusbar.vue
+++ b/packages/frontend/src/pages/settings/statusbar.vue
@@ -20,10 +20,10 @@ import { v4 as uuid } from 'uuid';
 import XStatusbar from './statusbar.statusbar.vue';
 import MkFolder from '@/components/MkFolder.vue';
 import MkButton from '@/components/MkButton.vue';
-import * as os from '@/os';
-import { defaultStore } from '@/store';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import * as os from '@/os.js';
+import { defaultStore } from '@/store.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 
 const statusbars = defaultStore.reactiveState.statusbars;
 
diff --git a/packages/frontend/src/pages/settings/theme.install.vue b/packages/frontend/src/pages/settings/theme.install.vue
index 55367054d3..155ce9d9da 100644
--- a/packages/frontend/src/pages/settings/theme.install.vue
+++ b/packages/frontend/src/pages/settings/theme.install.vue
@@ -21,11 +21,11 @@ import { } from 'vue';
 import JSON5 from 'json5';
 import MkTextarea from '@/components/MkTextarea.vue';
 import MkButton from '@/components/MkButton.vue';
-import { applyTheme, validateTheme } from '@/scripts/theme';
-import * as os from '@/os';
+import { applyTheme, validateTheme } from '@/scripts/theme.js';
+import * as os from '@/os.js';
 import { addTheme, getThemes } from '@/theme-store';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 
 let installThemeCode = $ref(null);
 
diff --git a/packages/frontend/src/pages/settings/theme.manage.vue b/packages/frontend/src/pages/settings/theme.manage.vue
index b39b7721ff..8c90c175f0 100644
--- a/packages/frontend/src/pages/settings/theme.manage.vue
+++ b/packages/frontend/src/pages/settings/theme.manage.vue
@@ -37,12 +37,12 @@ import MkTextarea from '@/components/MkTextarea.vue';
 import MkSelect from '@/components/MkSelect.vue';
 import MkInput from '@/components/MkInput.vue';
 import MkButton from '@/components/MkButton.vue';
-import { Theme, getBuiltinThemesRef } from '@/scripts/theme';
-import copyToClipboard from '@/scripts/copy-to-clipboard';
-import * as os from '@/os';
+import { Theme, getBuiltinThemesRef } from '@/scripts/theme.js';
+import copyToClipboard from '@/scripts/copy-to-clipboard.js';
+import * as os from '@/os.js';
 import { getThemes, removeTheme } from '@/theme-store';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 
 const installedThemes = ref(getThemes());
 const builtinThemes = getBuiltinThemesRef();
diff --git a/packages/frontend/src/pages/settings/theme.vue b/packages/frontend/src/pages/settings/theme.vue
index 6517f24acb..9e55a8fd8d 100644
--- a/packages/frontend/src/pages/settings/theme.vue
+++ b/packages/frontend/src/pages/settings/theme.vue
@@ -78,16 +78,16 @@ import MkSelect from '@/components/MkSelect.vue';
 import FormSection from '@/components/form/section.vue';
 import FormLink from '@/components/form/link.vue';
 import MkButton from '@/components/MkButton.vue';
-import { getBuiltinThemesRef } from '@/scripts/theme';
-import { selectFile } from '@/scripts/select-file';
-import { isDeviceDarkmode } from '@/scripts/is-device-darkmode';
-import { ColdDeviceStorage, defaultStore } from '@/store';
-import { i18n } from '@/i18n';
-import { instance } from '@/instance';
-import { uniqueBy } from '@/scripts/array';
+import { getBuiltinThemesRef } from '@/scripts/theme.js';
+import { selectFile } from '@/scripts/select-file.js';
+import { isDeviceDarkmode } from '@/scripts/is-device-darkmode.js';
+import { ColdDeviceStorage, defaultStore } from '@/store.js';
+import { i18n } from '@/i18n.js';
+import { instance } from '@/instance.js';
+import { uniqueBy } from '@/scripts/array.js';
 import { fetchThemes, getThemes } from '@/theme-store';
-import { definePageMetadata } from '@/scripts/page-metadata';
-import { miLocalStorage } from '@/local-storage';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { miLocalStorage } from '@/local-storage.js';
 
 const installedThemes = ref(getThemes());
 const builtinThemes = getBuiltinThemesRef();
diff --git a/packages/frontend/src/pages/settings/webhook.edit.vue b/packages/frontend/src/pages/settings/webhook.edit.vue
index cfb8121130..86b8f60c99 100644
--- a/packages/frontend/src/pages/settings/webhook.edit.vue
+++ b/packages/frontend/src/pages/settings/webhook.edit.vue
@@ -47,10 +47,10 @@ import MkInput from '@/components/MkInput.vue';
 import FormSection from '@/components/form/section.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
 import MkButton from '@/components/MkButton.vue';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
-import { useRouter } from '@/router';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { useRouter } from '@/router.js';
 
 const router = useRouter();
 
diff --git a/packages/frontend/src/pages/settings/webhook.new.vue b/packages/frontend/src/pages/settings/webhook.new.vue
index 185151835b..ed56126548 100644
--- a/packages/frontend/src/pages/settings/webhook.new.vue
+++ b/packages/frontend/src/pages/settings/webhook.new.vue
@@ -44,9 +44,9 @@ import MkInput from '@/components/MkInput.vue';
 import FormSection from '@/components/form/section.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
 import MkButton from '@/components/MkButton.vue';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 
 let name = $ref('');
 let url = $ref('');
diff --git a/packages/frontend/src/pages/settings/webhook.vue b/packages/frontend/src/pages/settings/webhook.vue
index a6ce4e46cd..33841d7f8d 100644
--- a/packages/frontend/src/pages/settings/webhook.vue
+++ b/packages/frontend/src/pages/settings/webhook.vue
@@ -37,8 +37,8 @@ import { } from 'vue';
 import MkPagination from '@/components/MkPagination.vue';
 import FormSection from '@/components/form/section.vue';
 import FormLink from '@/components/form/link.vue';
-import { definePageMetadata } from '@/scripts/page-metadata';
-import { i18n } from '@/i18n';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { i18n } from '@/i18n.js';
 
 const pagination = {
 	endpoint: 'i/webhooks/list' as const,
diff --git a/packages/frontend/src/pages/settings/word-mute.vue b/packages/frontend/src/pages/settings/word-mute.vue
index e4218952bb..1fefbdc92b 100644
--- a/packages/frontend/src/pages/settings/word-mute.vue
+++ b/packages/frontend/src/pages/settings/word-mute.vue
@@ -40,12 +40,12 @@ import MkKeyValue from '@/components/MkKeyValue.vue';
 import MkButton from '@/components/MkButton.vue';
 import MkInfo from '@/components/MkInfo.vue';
 import MkTab from '@/components/MkTab.vue';
-import * as os from '@/os';
-import number from '@/filters/number';
-import { defaultStore } from '@/store';
-import { $i } from '@/account';
-import { i18n } from '@/i18n';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import * as os from '@/os.js';
+import number from '@/filters/number.js';
+import { defaultStore } from '@/store.js';
+import { $i } from '@/account.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 
 const render = (mutedWords) => mutedWords.map(x => {
 	if (Array.isArray(x)) {
diff --git a/packages/frontend/src/pages/share.vue b/packages/frontend/src/pages/share.vue
index 4845fa7979..d66457e823 100644
--- a/packages/frontend/src/pages/share.vue
+++ b/packages/frontend/src/pages/share.vue
@@ -36,10 +36,10 @@ import { } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkButton from '@/components/MkButton.vue';
 import MkPostForm from '@/components/MkPostForm.vue';
-import * as os from '@/os';
-import { mainRouter } from '@/router';
-import { definePageMetadata } from '@/scripts/page-metadata';
-import { i18n } from '@/i18n';
+import * as os from '@/os.js';
+import { mainRouter } from '@/router.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { i18n } from '@/i18n.js';
 
 const urlParams = new URLSearchParams(window.location.search);
 const localOnlyQuery = urlParams.get('localOnly');
diff --git a/packages/frontend/src/pages/signup-complete.vue b/packages/frontend/src/pages/signup-complete.vue
index 205b763791..e9c89fa3bb 100644
--- a/packages/frontend/src/pages/signup-complete.vue
+++ b/packages/frontend/src/pages/signup-complete.vue
@@ -28,9 +28,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { } from 'vue';
 import MkButton from '@/components/MkButton.vue';
 import MkAnimBg from '@/components/MkAnimBg.vue';
-import { login } from '@/account';
-import { i18n } from '@/i18n';
-import * as os from '@/os';
+import { login } from '@/account.js';
+import { i18n } from '@/i18n.js';
+import * as os from '@/os.js';
 
 let submitting = $ref(false);
 
diff --git a/packages/frontend/src/pages/tag.vue b/packages/frontend/src/pages/tag.vue
index 58722fd4de..85c571ecd6 100644
--- a/packages/frontend/src/pages/tag.vue
+++ b/packages/frontend/src/pages/tag.vue
@@ -23,11 +23,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { computed, ref } from 'vue';
 import MkNotes from '@/components/MkNotes.vue';
 import MkButton from '@/components/MkButton.vue';
-import { definePageMetadata } from '@/scripts/page-metadata';
-import { i18n } from '@/i18n';
-import { $i } from '@/account';
-import { defaultStore } from '@/store';
-import * as os from '@/os';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { i18n } from '@/i18n.js';
+import { $i } from '@/account.js';
+import { defaultStore } from '@/store.js';
+import * as os from '@/os.js';
 
 const props = defineProps<{
 	tag: string;
diff --git a/packages/frontend/src/pages/theme-editor.vue b/packages/frontend/src/pages/theme-editor.vue
index e6065b7eda..740fd5c4eb 100644
--- a/packages/frontend/src/pages/theme-editor.vue
+++ b/packages/frontend/src/pages/theme-editor.vue
@@ -83,17 +83,17 @@ import MkButton from '@/components/MkButton.vue';
 import MkTextarea from '@/components/MkTextarea.vue';
 import MkFolder from '@/components/MkFolder.vue';
 
-import { $i } from '@/account';
-import { Theme, applyTheme } from '@/scripts/theme';
+import { $i } from '@/account.js';
+import { Theme, applyTheme } from '@/scripts/theme.js';
 import lightTheme from '@/themes/_light.json5';
 import darkTheme from '@/themes/_dark.json5';
-import { host } from '@/config';
-import * as os from '@/os';
-import { ColdDeviceStorage, defaultStore } from '@/store';
+import { host } from '@/config.js';
+import * as os from '@/os.js';
+import { ColdDeviceStorage, defaultStore } from '@/store.js';
 import { addTheme } from '@/theme-store';
-import { i18n } from '@/i18n';
-import { useLeaveGuard } from '@/scripts/use-leave-guard';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import { i18n } from '@/i18n.js';
+import { useLeaveGuard } from '@/scripts/use-leave-guard.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 
 const bgColors = [
 	{ color: '#f5f5f5', kind: 'light', forPreview: '#f5f5f5' },
diff --git a/packages/frontend/src/pages/timeline.tutorial.vue b/packages/frontend/src/pages/timeline.tutorial.vue
index c642558c35..66b8e796e5 100644
--- a/packages/frontend/src/pages/timeline.tutorial.vue
+++ b/packages/frontend/src/pages/timeline.tutorial.vue
@@ -49,10 +49,10 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { computed } from 'vue';
 import MkButton from '@/components/MkButton.vue';
-import { defaultStore } from '@/store';
-import { i18n } from '@/i18n';
-import { instance } from '@/instance';
-import { host } from '@/config';
+import { defaultStore } from '@/store.js';
+import { i18n } from '@/i18n.js';
+import { instance } from '@/instance.js';
+import { host } from '@/config.js';
 
 const tutorialsNumber = 4;
 
diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue
index c0b1f03ff0..cce7360b9b 100644
--- a/packages/frontend/src/pages/timeline.vue
+++ b/packages/frontend/src/pages/timeline.vue
@@ -32,14 +32,14 @@ import { defineAsyncComponent, computed, watch, provide } from 'vue';
 import type { Tab } from '@/components/global/MkPageHeader.tabs.vue';
 import MkTimeline from '@/components/MkTimeline.vue';
 import MkPostForm from '@/components/MkPostForm.vue';
-import { scroll } from '@/scripts/scroll';
-import * as os from '@/os';
-import { defaultStore } from '@/store';
-import { i18n } from '@/i18n';
-import { instance } from '@/instance';
-import { $i } from '@/account';
-import { definePageMetadata } from '@/scripts/page-metadata';
-import { miLocalStorage } from '@/local-storage';
+import { scroll } from '@/scripts/scroll.js';
+import * as os from '@/os.js';
+import { defaultStore } from '@/store.js';
+import { i18n } from '@/i18n.js';
+import { instance } from '@/instance.js';
+import { $i } from '@/account.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { miLocalStorage } from '@/local-storage.js';
 import { antennasCache, userListsCache } from '@/cache';
 
 provide('shouldOmitHeaderTitle', true);
diff --git a/packages/frontend/src/pages/user-list-timeline.vue b/packages/frontend/src/pages/user-list-timeline.vue
index 4d28ab6b5b..83244876fe 100644
--- a/packages/frontend/src/pages/user-list-timeline.vue
+++ b/packages/frontend/src/pages/user-list-timeline.vue
@@ -26,11 +26,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { computed, watch } from 'vue';
 import MkTimeline from '@/components/MkTimeline.vue';
-import { scroll } from '@/scripts/scroll';
-import * as os from '@/os';
-import { useRouter } from '@/router';
-import { definePageMetadata } from '@/scripts/page-metadata';
-import { i18n } from '@/i18n';
+import { scroll } from '@/scripts/scroll.js';
+import * as os from '@/os.js';
+import { useRouter } from '@/router.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { i18n } from '@/i18n.js';
 
 const router = useRouter();
 
diff --git a/packages/frontend/src/pages/user-tag.vue b/packages/frontend/src/pages/user-tag.vue
index d74bd5c9e5..71f8d31924 100644
--- a/packages/frontend/src/pages/user-tag.vue
+++ b/packages/frontend/src/pages/user-tag.vue
@@ -17,9 +17,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { computed, watch } from 'vue';
-import * as os from '@/os';
+import * as os from '@/os.js';
 import MkUserList from '@/components/MkUserList.vue';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 
 const props = defineProps<{
 	tag: string;
diff --git a/packages/frontend/src/pages/user/achievements.vue b/packages/frontend/src/pages/user/achievements.vue
index 4153da6898..4e14443074 100644
--- a/packages/frontend/src/pages/user/achievements.vue
+++ b/packages/frontend/src/pages/user/achievements.vue
@@ -13,8 +13,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { onActivated, onDeactivated, onMounted, onUnmounted } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkAchievements from '@/components/MkAchievements.vue';
-import { claimAchievement } from '@/scripts/achievements';
-import { $i } from '@/account';
+import { claimAchievement } from '@/scripts/achievements.js';
+import { $i } from '@/account.js';
 
 const props = defineProps<{
 	user: Misskey.entities.User;
diff --git a/packages/frontend/src/pages/user/activity.following.vue b/packages/frontend/src/pages/user/activity.following.vue
index fa9aa99aa4..bc6bf77168 100644
--- a/packages/frontend/src/pages/user/activity.following.vue
+++ b/packages/frontend/src/pages/user/activity.following.vue
@@ -18,12 +18,12 @@ import { onMounted } from 'vue';
 import { Chart, ChartDataset } from 'chart.js';
 import * as Misskey from 'misskey-js';
 import gradient from 'chartjs-plugin-gradient';
-import * as os from '@/os';
-import { defaultStore } from '@/store';
-import { useChartTooltip } from '@/scripts/use-chart-tooltip';
-import { chartVLine } from '@/scripts/chart-vline';
-import { initChart } from '@/scripts/init-chart';
-import { chartLegend } from '@/scripts/chart-legend';
+import * as os from '@/os.js';
+import { defaultStore } from '@/store.js';
+import { useChartTooltip } from '@/scripts/use-chart-tooltip.js';
+import { chartVLine } from '@/scripts/chart-vline.js';
+import { initChart } from '@/scripts/init-chart.js';
+import { chartLegend } from '@/scripts/chart-legend.js';
 import MkChartLegend from '@/components/MkChartLegend.vue';
 
 initChart();
diff --git a/packages/frontend/src/pages/user/activity.heatmap.vue b/packages/frontend/src/pages/user/activity.heatmap.vue
index 4da738a2ef..73bec2487e 100644
--- a/packages/frontend/src/pages/user/activity.heatmap.vue
+++ b/packages/frontend/src/pages/user/activity.heatmap.vue
@@ -16,11 +16,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { onMounted, nextTick, watch } from 'vue';
 import { Chart } from 'chart.js';
 import * as Misskey from 'misskey-js';
-import * as os from '@/os';
-import { defaultStore } from '@/store';
-import { useChartTooltip } from '@/scripts/use-chart-tooltip';
-import { alpha } from '@/scripts/color';
-import { initChart } from '@/scripts/init-chart';
+import * as os from '@/os.js';
+import { defaultStore } from '@/store.js';
+import { useChartTooltip } from '@/scripts/use-chart-tooltip.js';
+import { alpha } from '@/scripts/color.js';
+import { initChart } from '@/scripts/init-chart.js';
 
 initChart();
 
diff --git a/packages/frontend/src/pages/user/activity.notes.vue b/packages/frontend/src/pages/user/activity.notes.vue
index 3d69b881f1..5ba4af7ca1 100644
--- a/packages/frontend/src/pages/user/activity.notes.vue
+++ b/packages/frontend/src/pages/user/activity.notes.vue
@@ -18,12 +18,12 @@ import { onMounted } from 'vue';
 import { Chart, ChartDataset } from 'chart.js';
 import * as Misskey from 'misskey-js';
 import gradient from 'chartjs-plugin-gradient';
-import * as os from '@/os';
-import { defaultStore } from '@/store';
-import { useChartTooltip } from '@/scripts/use-chart-tooltip';
-import { chartVLine } from '@/scripts/chart-vline';
-import { initChart } from '@/scripts/init-chart';
-import { chartLegend } from '@/scripts/chart-legend';
+import * as os from '@/os.js';
+import { defaultStore } from '@/store.js';
+import { useChartTooltip } from '@/scripts/use-chart-tooltip.js';
+import { chartVLine } from '@/scripts/chart-vline.js';
+import { initChart } from '@/scripts/init-chart.js';
+import { chartLegend } from '@/scripts/chart-legend.js';
 import MkChartLegend from '@/components/MkChartLegend.vue';
 
 initChart();
diff --git a/packages/frontend/src/pages/user/activity.pv.vue b/packages/frontend/src/pages/user/activity.pv.vue
index c5600c5c22..54d1d0c1be 100644
--- a/packages/frontend/src/pages/user/activity.pv.vue
+++ b/packages/frontend/src/pages/user/activity.pv.vue
@@ -18,12 +18,12 @@ import { onMounted } from 'vue';
 import { Chart, ChartDataset } from 'chart.js';
 import * as Misskey from 'misskey-js';
 import gradient from 'chartjs-plugin-gradient';
-import * as os from '@/os';
-import { defaultStore } from '@/store';
-import { useChartTooltip } from '@/scripts/use-chart-tooltip';
-import { chartVLine } from '@/scripts/chart-vline';
-import { initChart } from '@/scripts/init-chart';
-import { chartLegend } from '@/scripts/chart-legend';
+import * as os from '@/os.js';
+import { defaultStore } from '@/store.js';
+import { useChartTooltip } from '@/scripts/use-chart-tooltip.js';
+import { chartVLine } from '@/scripts/chart-vline.js';
+import { initChart } from '@/scripts/init-chart.js';
+import { chartLegend } from '@/scripts/chart-legend.js';
 import MkChartLegend from '@/components/MkChartLegend.vue';
 
 initChart();
diff --git a/packages/frontend/src/pages/user/followers.vue b/packages/frontend/src/pages/user/followers.vue
index d214e92639..b744f6aeec 100644
--- a/packages/frontend/src/pages/user/followers.vue
+++ b/packages/frontend/src/pages/user/followers.vue
@@ -22,9 +22,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { computed, watch } from 'vue';
 import * as Misskey from 'misskey-js';
 import XFollowList from './follow-list.vue';
-import * as os from '@/os';
-import { definePageMetadata } from '@/scripts/page-metadata';
-import { i18n } from '@/i18n';
+import * as os from '@/os.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { i18n } from '@/i18n.js';
 
 const props = withDefaults(defineProps<{
 	acct: string;
diff --git a/packages/frontend/src/pages/user/following.vue b/packages/frontend/src/pages/user/following.vue
index 7e42bc8224..52f5207119 100644
--- a/packages/frontend/src/pages/user/following.vue
+++ b/packages/frontend/src/pages/user/following.vue
@@ -22,9 +22,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { computed, watch } from 'vue';
 import * as Misskey from 'misskey-js';
 import XFollowList from './follow-list.vue';
-import * as os from '@/os';
-import { definePageMetadata } from '@/scripts/page-metadata';
-import { i18n } from '@/i18n';
+import * as os from '@/os.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { i18n } from '@/i18n.js';
 
 const props = withDefaults(defineProps<{
 	acct: string;
diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue
index 8bc6c72440..8195c40bf9 100644
--- a/packages/frontend/src/pages/user/home.vue
+++ b/packages/frontend/src/pages/user/home.vue
@@ -152,19 +152,19 @@ import MkTextarea from '@/components/MkTextarea.vue';
 import MkOmit from '@/components/MkOmit.vue';
 import MkInfo from '@/components/MkInfo.vue';
 import MkButton from '@/components/MkButton.vue';
-import { getScrollPosition } from '@/scripts/scroll';
-import { getUserMenu } from '@/scripts/get-user-menu';
-import number from '@/filters/number';
-import { userPage } from '@/filters/user';
-import * as os from '@/os';
-import { useRouter } from '@/router';
-import { i18n } from '@/i18n';
-import { $i, iAmModerator } from '@/account';
-import { dateString } from '@/filters/date';
-import { confetti } from '@/scripts/confetti';
+import { getScrollPosition } from '@/scripts/scroll.js';
+import { getUserMenu } from '@/scripts/get-user-menu.js';
+import number from '@/filters/number.js';
+import { userPage } from '@/filters/user.js';
+import * as os from '@/os.js';
+import { useRouter } from '@/router.js';
+import { i18n } from '@/i18n.js';
+import { $i, iAmModerator } from '@/account.js';
+import { dateString } from '@/filters/date.js';
+import { confetti } from '@/scripts/confetti.js';
 import MkNotes from '@/components/MkNotes.vue';
-import { api } from '@/os';
-import { isFfVisibleForMe } from '@/scripts/isFfVisibleForMe';
+import { api } from '@/os.js';
+import { isFfVisibleForMe } from '@/scripts/isFfVisibleForMe.js';
 
 function calcAge(birthdate: string): number {
 	const date = new Date(birthdate);
diff --git a/packages/frontend/src/pages/user/index.activity.vue b/packages/frontend/src/pages/user/index.activity.vue
index 38e508776a..c531bda598 100644
--- a/packages/frontend/src/pages/user/index.activity.vue
+++ b/packages/frontend/src/pages/user/index.activity.vue
@@ -24,8 +24,8 @@ import { } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkContainer from '@/components/MkContainer.vue';
 import MkChart from '@/components/MkChart.vue';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
 
 const props = withDefaults(defineProps<{
 	user: Misskey.entities.User;
diff --git a/packages/frontend/src/pages/user/index.photos.vue b/packages/frontend/src/pages/user/index.photos.vue
index 2bf1922ccd..b6cae9f131 100644
--- a/packages/frontend/src/pages/user/index.photos.vue
+++ b/packages/frontend/src/pages/user/index.photos.vue
@@ -27,13 +27,13 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { onMounted } from 'vue';
 import * as Misskey from 'misskey-js';
-import { getStaticImageUrl } from '@/scripts/media-proxy';
-import { notePage } from '@/filters/note';
-import * as os from '@/os';
+import { getStaticImageUrl } from '@/scripts/media-proxy.js';
+import { notePage } from '@/filters/note.js';
+import * as os from '@/os.js';
 import MkContainer from '@/components/MkContainer.vue';
 import ImgWithBlurhash from '@/components/MkImgWithBlurhash.vue';
-import { defaultStore } from '@/store';
-import { i18n } from '@/i18n';
+import { defaultStore } from '@/store.js';
+import { i18n } from '@/i18n.js';
 
 const props = defineProps<{
 	user: Misskey.entities.UserDetailed;
diff --git a/packages/frontend/src/pages/user/index.timeline.vue b/packages/frontend/src/pages/user/index.timeline.vue
index d4c17845ce..3a2a2ade81 100644
--- a/packages/frontend/src/pages/user/index.timeline.vue
+++ b/packages/frontend/src/pages/user/index.timeline.vue
@@ -23,7 +23,7 @@ import { ref, computed } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkNotes from '@/components/MkNotes.vue';
 import MkTab from '@/components/MkTab.vue';
-import { i18n } from '@/i18n';
+import { i18n } from '@/i18n.js';
 
 const props = defineProps<{
 	user: Misskey.entities.UserDetailed;
diff --git a/packages/frontend/src/pages/user/index.vue b/packages/frontend/src/pages/user/index.vue
index 058e20a173..7550d5bcb2 100644
--- a/packages/frontend/src/pages/user/index.vue
+++ b/packages/frontend/src/pages/user/index.vue
@@ -28,11 +28,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { defineAsyncComponent, computed, watch } from 'vue';
 import * as Misskey from 'misskey-js';
-import { acct as getAcct } from '@/filters/user';
-import * as os from '@/os';
-import { definePageMetadata } from '@/scripts/page-metadata';
-import { i18n } from '@/i18n';
-import { $i } from '@/account';
+import { acct as getAcct } from '@/filters/user.js';
+import * as os from '@/os.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { i18n } from '@/i18n.js';
+import { $i } from '@/account.js';
 
 const XHome = defineAsyncComponent(() => import('./home.vue'));
 const XTimeline = defineAsyncComponent(() => import('./index.timeline.vue'));
diff --git a/packages/frontend/src/pages/welcome.entrance.a.vue b/packages/frontend/src/pages/welcome.entrance.a.vue
index 6fc7019db8..998d942488 100644
--- a/packages/frontend/src/pages/welcome.entrance.a.vue
+++ b/packages/frontend/src/pages/welcome.entrance.a.vue
@@ -39,11 +39,11 @@ import XTimeline from './welcome.timeline.vue';
 import MarqueeText from '@/components/MkMarquee.vue';
 import MkFeaturedPhotos from '@/components/MkFeaturedPhotos.vue';
 import MkInfo from '@/components/MkInfo.vue';
-import { instanceName } from '@/config';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
-import { instance } from '@/instance';
-import number from '@/filters/number';
+import { instanceName } from '@/config.js';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
+import { instance } from '@/instance.js';
+import number from '@/filters/number.js';
 import MkNumber from '@/components/MkNumber.vue';
 import MkVisitorDashboard from '@/components/MkVisitorDashboard.vue';
 
diff --git a/packages/frontend/src/pages/welcome.setup.vue b/packages/frontend/src/pages/welcome.setup.vue
index 1fc624a55c..0e6ce61781 100644
--- a/packages/frontend/src/pages/welcome.setup.vue
+++ b/packages/frontend/src/pages/welcome.setup.vue
@@ -38,10 +38,10 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { } from 'vue';
 import MkButton from '@/components/MkButton.vue';
 import MkInput from '@/components/MkInput.vue';
-import { host, version } from '@/config';
-import * as os from '@/os';
-import { login } from '@/account';
-import { i18n } from '@/i18n';
+import { host, version } from '@/config.js';
+import * as os from '@/os.js';
+import { login } from '@/account.js';
+import { i18n } from '@/i18n.js';
 import MkAnimBg from '@/components/MkAnimBg.vue';
 
 let username = $ref('');
diff --git a/packages/frontend/src/pages/welcome.timeline.vue b/packages/frontend/src/pages/welcome.timeline.vue
index 121b5d2066..f2e151468a 100644
--- a/packages/frontend/src/pages/welcome.timeline.vue
+++ b/packages/frontend/src/pages/welcome.timeline.vue
@@ -32,9 +32,9 @@ import { onUpdated } from 'vue';
 import MkReactionsViewer from '@/components/MkReactionsViewer.vue';
 import MkMediaList from '@/components/MkMediaList.vue';
 import MkPoll from '@/components/MkPoll.vue';
-import * as os from '@/os';
-import { getScrollContainer } from '@/scripts/scroll';
-import { $i } from '@/account';
+import * as os from '@/os.js';
+import { getScrollContainer } from '@/scripts/scroll.js';
+import { $i } from '@/account.js';
 
 let notes = $ref<Misskey.entities.Note[]>([]);
 let isScrolling = $ref(false);
diff --git a/packages/frontend/src/pages/welcome.vue b/packages/frontend/src/pages/welcome.vue
index 73bd323024..2e6bb8b38f 100644
--- a/packages/frontend/src/pages/welcome.vue
+++ b/packages/frontend/src/pages/welcome.vue
@@ -14,9 +14,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { computed } from 'vue';
 import XSetup from './welcome.setup.vue';
 import XEntrance from './welcome.entrance.a.vue';
-import { instanceName } from '@/config';
-import * as os from '@/os';
-import { definePageMetadata } from '@/scripts/page-metadata';
+import { instanceName } from '@/config.js';
+import * as os from '@/os.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
 
 let meta = $ref(null);
 
diff --git a/packages/frontend/src/pizzax.ts b/packages/frontend/src/pizzax.ts
index 23b744a081..b2254a0611 100644
--- a/packages/frontend/src/pizzax.ts
+++ b/packages/frontend/src/pizzax.ts
@@ -7,12 +7,12 @@
 
 import { onUnmounted, Ref, ref, watch } from 'vue';
 import { BroadcastChannel } from 'broadcast-channel';
-import { $i } from './account';
-import { api } from './os';
-import { get, set } from './scripts/idb-proxy';
-import { defaultStore } from './store';
-import { useStream } from './stream';
-import { deepClone } from './scripts/clone';
+import { $i } from '@/account.js';
+import { api } from '@/os.js';
+import { get, set } from '@/scripts/idb-proxy.js';
+import { defaultStore } from '@/store.js';
+import { useStream } from '@/stream.js';
+import { deepClone } from '@/scripts/clone.js';
 
 type StateDef = Record<string, {
 	where: 'account' | 'device' | 'deviceAccount';
diff --git a/packages/frontend/src/plugin.ts b/packages/frontend/src/plugin.ts
index f9b57bfbee..3bc91f6ac4 100644
--- a/packages/frontend/src/plugin.ts
+++ b/packages/frontend/src/plugin.ts
@@ -4,9 +4,9 @@
  */
 
 import { Interpreter, Parser, utils, values } from '@syuilo/aiscript';
-import { createAiScriptEnv } from '@/scripts/aiscript/api';
-import { inputText } from '@/os';
-import { Plugin, noteActions, notePostInterruptors, noteViewInterruptors, postFormActions, userActions, pageViewInterruptors } from '@/store';
+import { createAiScriptEnv } from '@/scripts/aiscript/api.js';
+import { inputText } from '@/os.js';
+import { Plugin, noteActions, notePostInterruptors, noteViewInterruptors, postFormActions, userActions, pageViewInterruptors } from '@/store.js';
 
 const parser = new Parser();
 const pluginContexts = new Map<string, Interpreter>();
diff --git a/packages/frontend/src/router.ts b/packages/frontend/src/router.ts
index 2e4494814a..e658477bbc 100644
--- a/packages/frontend/src/router.ts
+++ b/packages/frontend/src/router.ts
@@ -5,7 +5,7 @@
 
 import { AsyncComponentLoader, defineAsyncComponent, inject } from 'vue';
 import { Router } from '@/nirax';
-import { $i, iAmModerator } from '@/account';
+import { $i, iAmModerator } from '@/account.js';
 import MkLoading from '@/pages/_loading_.vue';
 import MkError from '@/pages/_error_.vue';
 
diff --git a/packages/frontend/src/scripts/achievements.ts b/packages/frontend/src/scripts/achievements.ts
index 54242b330d..af7c6b060c 100644
--- a/packages/frontend/src/scripts/achievements.ts
+++ b/packages/frontend/src/scripts/achievements.ts
@@ -3,8 +3,8 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import * as os from '@/os';
-import { $i } from '@/account';
+import * as os from '@/os.js';
+import { $i } from '@/account.js';
 
 export const ACHIEVEMENT_TYPES = [
 	'notes1',
diff --git a/packages/frontend/src/scripts/aiscript/api.ts b/packages/frontend/src/scripts/aiscript/api.ts
index dcb4a9105c..0bb9185936 100644
--- a/packages/frontend/src/scripts/aiscript/api.ts
+++ b/packages/frontend/src/scripts/aiscript/api.ts
@@ -4,11 +4,11 @@
  */
 
 import { utils, values } from '@syuilo/aiscript';
-import * as os from '@/os';
-import { $i } from '@/account';
-import { miLocalStorage } from '@/local-storage';
-import { customEmojis } from '@/custom-emojis';
-import { lang } from '@/config';
+import * as os from '@/os.js';
+import { $i } from '@/account.js';
+import { miLocalStorage } from '@/local-storage.js';
+import { customEmojis } from '@/custom-emojis.js';
+import { lang } from '@/config.js';
 
 export function createAiScriptEnv(opts) {
 	return {
diff --git a/packages/frontend/src/scripts/api.ts b/packages/frontend/src/scripts/api.ts
index aac953b644..d73d60cf26 100644
--- a/packages/frontend/src/scripts/api.ts
+++ b/packages/frontend/src/scripts/api.ts
@@ -5,8 +5,8 @@
 
 import { Endpoints } from 'misskey-js/built/api.types';
 import { ref } from 'vue';
-import { apiUrl } from '@/config';
-import { $i } from '@/account';
+import { apiUrl } from '@/config.js';
+import { $i } from '@/account.js';
 export const pendingApiRequestsCount = ref(0);
 
 // Implements Misskey.api.ApiClient.request
diff --git a/packages/frontend/src/scripts/array.ts b/packages/frontend/src/scripts/array.ts
index eeadcb8794..d83b001c87 100644
--- a/packages/frontend/src/scripts/array.ts
+++ b/packages/frontend/src/scripts/array.ts
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { EndoRelation, Predicate } from './relation';
+import { EndoRelation, Predicate } from './relation.js';
 
 /**
  * Count the number of elements that satisfy the predicate
diff --git a/packages/frontend/src/scripts/autocomplete.ts b/packages/frontend/src/scripts/autocomplete.ts
index a1184fb9d4..0b4ebb4410 100644
--- a/packages/frontend/src/scripts/autocomplete.ts
+++ b/packages/frontend/src/scripts/autocomplete.ts
@@ -6,7 +6,7 @@
 import { nextTick, Ref, ref, defineAsyncComponent } from 'vue';
 import getCaretCoordinates from 'textarea-caret';
 import { toASCII } from 'punycode/';
-import { popup } from '@/os';
+import { popup } from '@/os.js';
 
 export class Autocomplete {
 	private suggestion: {
diff --git a/packages/frontend/src/scripts/clicker-game.ts b/packages/frontend/src/scripts/clicker-game.ts
index 7d8804b4af..5ad076e5ef 100644
--- a/packages/frontend/src/scripts/clicker-game.ts
+++ b/packages/frontend/src/scripts/clicker-game.ts
@@ -4,7 +4,7 @@
  */
 
 import { ref, computed } from 'vue';
-import * as os from '@/os';
+import * as os from '@/os.js';
 
 type SaveData = {
 	gameVersion: number;
diff --git a/packages/frontend/src/scripts/collapsed.ts b/packages/frontend/src/scripts/collapsed.ts
index abd436636e..c3c767bcfa 100644
--- a/packages/frontend/src/scripts/collapsed.ts
+++ b/packages/frontend/src/scripts/collapsed.ts
@@ -5,7 +5,7 @@
 
 import * as mfm from 'mfm-js';
 import * as Misskey from 'misskey-js';
-import { extractUrlFromMfm } from './extract-url-from-mfm';
+import { extractUrlFromMfm } from './extract-url-from-mfm.js';
 
 export function shouldCollapsed(note: Misskey.entities.Note): boolean {
 	const urls = note.text ? extractUrlFromMfm(mfm.parse(note.text)) : null;
diff --git a/packages/frontend/src/scripts/confetti.ts b/packages/frontend/src/scripts/confetti.ts
index efc369ff56..b394ba3e2a 100644
--- a/packages/frontend/src/scripts/confetti.ts
+++ b/packages/frontend/src/scripts/confetti.ts
@@ -4,7 +4,7 @@
  */
 
 import _confetti from 'canvas-confetti';
-import * as os from '@/os';
+import * as os from '@/os.js';
 
 export function confetti(options: { duration?: number; } = {}) {
 	const duration = options.duration ?? 1000 * 4;
diff --git a/packages/frontend/src/scripts/device-kind.ts b/packages/frontend/src/scripts/device-kind.ts
index 4a47ebf8f2..3843052a24 100644
--- a/packages/frontend/src/scripts/device-kind.ts
+++ b/packages/frontend/src/scripts/device-kind.ts
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { defaultStore } from '@/store';
+import { defaultStore } from '@/store.js';
 
 await defaultStore.ready;
 
diff --git a/packages/frontend/src/scripts/extract-url-from-mfm.ts b/packages/frontend/src/scripts/extract-url-from-mfm.ts
index e72de784f8..c1ed9338f8 100644
--- a/packages/frontend/src/scripts/extract-url-from-mfm.ts
+++ b/packages/frontend/src/scripts/extract-url-from-mfm.ts
@@ -4,7 +4,7 @@
  */
 
 import * as mfm from 'mfm-js';
-import { unique } from '@/scripts/array';
+import { unique } from '@/scripts/array.js';
 
 // unique without hash
 // [ http://a/#1, http://a/#2, http://b/#3 ] => [ http://a/#1, http://b/#3 ]
diff --git a/packages/frontend/src/scripts/gen-search-query.ts b/packages/frontend/src/scripts/gen-search-query.ts
index 1a5e520d29..54654980f2 100644
--- a/packages/frontend/src/scripts/gen-search-query.ts
+++ b/packages/frontend/src/scripts/gen-search-query.ts
@@ -4,7 +4,7 @@
  */
 
 import * as Misskey from 'misskey-js';
-import { host as localHost } from '@/config';
+import { host as localHost } from '@/config.js';
 
 export async function genSearchQuery(v: any, q: string) {
 	let host: string;
diff --git a/packages/frontend/src/scripts/get-account-from-id.ts b/packages/frontend/src/scripts/get-account-from-id.ts
index 60bd778f3e..346d283572 100644
--- a/packages/frontend/src/scripts/get-account-from-id.ts
+++ b/packages/frontend/src/scripts/get-account-from-id.ts
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { get } from '@/scripts/idb-proxy';
+import { get } from '@/scripts/idb-proxy.js';
 
 export async function getAccountFromId(id: string) {
 	const accounts = await get('accounts') as { token: string; id: string; }[];
diff --git a/packages/frontend/src/scripts/get-drive-file-menu.ts b/packages/frontend/src/scripts/get-drive-file-menu.ts
index e3a95b5d8e..0964108249 100644
--- a/packages/frontend/src/scripts/get-drive-file-menu.ts
+++ b/packages/frontend/src/scripts/get-drive-file-menu.ts
@@ -5,11 +5,11 @@
 
 import * as Misskey from 'misskey-js';
 import { defineAsyncComponent } from 'vue';
-import { i18n } from '@/i18n';
-import copyToClipboard from '@/scripts/copy-to-clipboard';
-import * as os from '@/os';
-import { MenuItem } from '@/types/menu';
-import { defaultStore } from '@/store';
+import { i18n } from '@/i18n.js';
+import copyToClipboard from '@/scripts/copy-to-clipboard.js';
+import * as os from '@/os.js';
+import { MenuItem } from '@/types/menu.js';
+import { defaultStore } from '@/store.js';
 
 function rename(file: Misskey.entities.DriveFile) {
 	os.inputText({
diff --git a/packages/frontend/src/scripts/get-note-menu.ts b/packages/frontend/src/scripts/get-note-menu.ts
index d9fae946d3..a0b8683be3 100644
--- a/packages/frontend/src/scripts/get-note-menu.ts
+++ b/packages/frontend/src/scripts/get-note-menu.ts
@@ -6,15 +6,15 @@
 import { defineAsyncComponent, Ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import { claimAchievement } from './achievements';
-import { $i } from '@/account';
-import { i18n } from '@/i18n';
-import { instance } from '@/instance';
-import * as os from '@/os';
-import copyToClipboard from '@/scripts/copy-to-clipboard';
-import { url } from '@/config';
-import { defaultStore, noteActions } from '@/store';
-import { miLocalStorage } from '@/local-storage';
-import { getUserMenu } from '@/scripts/get-user-menu';
+import { $i } from '@/account.js';
+import { i18n } from '@/i18n.js';
+import { instance } from '@/instance.js';
+import * as os from '@/os.js';
+import copyToClipboard from '@/scripts/copy-to-clipboard.js';
+import { url } from '@/config.js';
+import { defaultStore, noteActions } from '@/store.js';
+import { miLocalStorage } from '@/local-storage.js';
+import { getUserMenu } from '@/scripts/get-user-menu.js';
 import { clipsCache } from '@/cache';
 import { MenuItem } from '@/types/menu';
 
diff --git a/packages/frontend/src/scripts/get-note-summary.ts b/packages/frontend/src/scripts/get-note-summary.ts
index b277200157..1fd9f04d46 100644
--- a/packages/frontend/src/scripts/get-note-summary.ts
+++ b/packages/frontend/src/scripts/get-note-summary.ts
@@ -4,7 +4,7 @@
  */
 
 import * as Misskey from 'misskey-js';
-import { i18n } from '@/i18n';
+import { i18n } from '@/i18n.js';
 
 /**
  * 投稿を表す文字列を取得します。
diff --git a/packages/frontend/src/scripts/get-user-menu.ts b/packages/frontend/src/scripts/get-user-menu.ts
index 2ab21e6c23..1561daa6d9 100644
--- a/packages/frontend/src/scripts/get-user-menu.ts
+++ b/packages/frontend/src/scripts/get-user-menu.ts
@@ -6,15 +6,15 @@
 import { toUnicode } from 'punycode';
 import { defineAsyncComponent, ref, watch } from 'vue';
 import * as Misskey from 'misskey-js';
-import { i18n } from '@/i18n';
-import copyToClipboard from '@/scripts/copy-to-clipboard';
-import { host, url } from '@/config';
-import * as os from '@/os';
-import { defaultStore, userActions } from '@/store';
-import { $i, iAmModerator } from '@/account';
-import { mainRouter } from '@/router';
-import { Router } from '@/nirax';
-import { antennasCache, rolesCache, userListsCache } from '@/cache';
+import { i18n } from '@/i18n.js';
+import copyToClipboard from '@/scripts/copy-to-clipboard.js';
+import { host, url } from '@/config.js';
+import * as os from '@/os.js';
+import { defaultStore, userActions } from '@/store.js';
+import { $i, iAmModerator } from '@/account.js';
+import { mainRouter } from '@/router.js';
+import { Router } from '@/nirax.js';
+import { antennasCache, rolesCache, userListsCache } from '@/cache.js';
 
 export function getUserMenu(user: Misskey.entities.UserDetailed, router: Router = mainRouter) {
 	const meId = $i ? $i.id : null;
diff --git a/packages/frontend/src/scripts/hotkey.ts b/packages/frontend/src/scripts/hotkey.ts
index 09ffb405e7..48c80c066b 100644
--- a/packages/frontend/src/scripts/hotkey.ts
+++ b/packages/frontend/src/scripts/hotkey.ts
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import keyCode from './keycode';
+import keyCode from './keycode.js';
 
 type Callback = (ev: KeyboardEvent) => void;
 
diff --git a/packages/frontend/src/scripts/init-chart.ts b/packages/frontend/src/scripts/init-chart.ts
index c9d3a7ca37..ebf27667d7 100644
--- a/packages/frontend/src/scripts/init-chart.ts
+++ b/packages/frontend/src/scripts/init-chart.ts
@@ -24,7 +24,7 @@ import {
 import gradient from 'chartjs-plugin-gradient';
 import zoomPlugin from 'chartjs-plugin-zoom';
 import { MatrixController, MatrixElement } from 'chartjs-chart-matrix';
-import { defaultStore } from '@/store';
+import { defaultStore } from '@/store.js';
 import 'chartjs-adapter-date-fns';
 
 export function initChart() {
diff --git a/packages/frontend/src/scripts/initialize-sw.ts b/packages/frontend/src/scripts/initialize-sw.ts
index 538554fa64..007fc0f2f7 100644
--- a/packages/frontend/src/scripts/initialize-sw.ts
+++ b/packages/frontend/src/scripts/initialize-sw.ts
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { lang } from '@/config';
+import { lang } from '@/config.js';
 
 export async function initializeSw() {
 	if (!('serviceWorker' in navigator)) return;
diff --git a/packages/frontend/src/scripts/intl-const.ts b/packages/frontend/src/scripts/intl-const.ts
index 31aa9b99b8..8012a677ef 100644
--- a/packages/frontend/src/scripts/intl-const.ts
+++ b/packages/frontend/src/scripts/intl-const.ts
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { lang } from '@/config';
+import { lang } from '@/config.js';
 
 export const versatileLang = (lang ?? 'ja-JP').replace('ja-KS', 'ja-JP');
 export const dateTimeFormat = new Intl.DateTimeFormat(versatileLang, {
diff --git a/packages/frontend/src/scripts/isFfVisibleForMe.ts b/packages/frontend/src/scripts/isFfVisibleForMe.ts
index de1b8851e8..0567f3b34a 100644
--- a/packages/frontend/src/scripts/isFfVisibleForMe.ts
+++ b/packages/frontend/src/scripts/isFfVisibleForMe.ts
@@ -4,7 +4,7 @@
  */
 
 import * as Misskey from 'misskey-js';
-import { $i } from '@/account';
+import { $i } from '@/account.js';
 
 export function isFfVisibleForMe(user: Misskey.entities.UserDetailed): boolean {
 	if ($i && $i.id === user.id) return true;
diff --git a/packages/frontend/src/scripts/lookup-user.ts b/packages/frontend/src/scripts/lookup-user.ts
index 6ab0104f88..3dbc03f777 100644
--- a/packages/frontend/src/scripts/lookup-user.ts
+++ b/packages/frontend/src/scripts/lookup-user.ts
@@ -4,8 +4,8 @@
  */
 
 import * as Misskey from 'misskey-js';
-import { i18n } from '@/i18n';
-import * as os from '@/os';
+import { i18n } from '@/i18n.js';
+import * as os from '@/os.js';
 
 export async function lookupUser() {
 	const { canceled, result } = await os.inputText({
diff --git a/packages/frontend/src/scripts/lookup.ts b/packages/frontend/src/scripts/lookup.ts
index f3192aac11..979f40f038 100644
--- a/packages/frontend/src/scripts/lookup.ts
+++ b/packages/frontend/src/scripts/lookup.ts
@@ -3,10 +3,10 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import * as os from '@/os';
-import { i18n } from '@/i18n';
-import { mainRouter } from '@/router';
-import { Router } from '@/nirax';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
+import { mainRouter } from '@/router.js';
+import { Router } from '@/nirax.js';
 
 export async function lookup(router?: Router) {
 	const _router = router ?? mainRouter;
diff --git a/packages/frontend/src/scripts/media-proxy.ts b/packages/frontend/src/scripts/media-proxy.ts
index 81fef3fdb6..559e61211d 100644
--- a/packages/frontend/src/scripts/media-proxy.ts
+++ b/packages/frontend/src/scripts/media-proxy.ts
@@ -3,9 +3,9 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { query } from '@/scripts/url';
-import { url } from '@/config';
-import { instance } from '@/instance';
+import { query } from '@/scripts/url.js';
+import { url } from '@/config.js';
+import { instance } from '@/instance.js';
 
 export function getProxiedImageUrl(imageUrl: string, type?: 'preview' | 'emoji' | 'avatar', mustOrigin = false, noFallback = false): string {
 	const localProxy = `${url}/proxy`;
diff --git a/packages/frontend/src/scripts/please-login.ts b/packages/frontend/src/scripts/please-login.ts
index f0eebf2242..e6c08dfbc0 100644
--- a/packages/frontend/src/scripts/please-login.ts
+++ b/packages/frontend/src/scripts/please-login.ts
@@ -4,9 +4,9 @@
  */
 
 import { defineAsyncComponent } from 'vue';
-import { $i } from '@/account';
-import { i18n } from '@/i18n';
-import { popup } from '@/os';
+import { $i } from '@/account.js';
+import { i18n } from '@/i18n.js';
+import { popup } from '@/os.js';
 
 export function pleaseLogin(path?: string) {
 	if ($i) return;
diff --git a/packages/frontend/src/scripts/popout.ts b/packages/frontend/src/scripts/popout.ts
index c0974ff589..0c2ff16992 100644
--- a/packages/frontend/src/scripts/popout.ts
+++ b/packages/frontend/src/scripts/popout.ts
@@ -3,8 +3,8 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { appendQuery } from './url';
-import * as config from '@/config';
+import { appendQuery } from './url.js';
+import * as config from '@/config.js';
 
 export function popout(path: string, w?: HTMLElement) {
 	let url = path.startsWith('http://') || path.startsWith('https://') ? path : config.url + path;
diff --git a/packages/frontend/src/scripts/reaction-picker.ts b/packages/frontend/src/scripts/reaction-picker.ts
index 919c566e3f..19e1bfba2c 100644
--- a/packages/frontend/src/scripts/reaction-picker.ts
+++ b/packages/frontend/src/scripts/reaction-picker.ts
@@ -4,7 +4,7 @@
  */
 
 import { defineAsyncComponent, Ref, ref } from 'vue';
-import { popup } from '@/os';
+import { popup } from '@/os.js';
 
 class ReactionPicker {
 	private src: Ref<HTMLElement | null> = ref(null);
diff --git a/packages/frontend/src/scripts/select-file.ts b/packages/frontend/src/scripts/select-file.ts
index 5d51d9f9ed..53e2cd5b16 100644
--- a/packages/frontend/src/scripts/select-file.ts
+++ b/packages/frontend/src/scripts/select-file.ts
@@ -5,11 +5,11 @@
 
 import { ref } from 'vue';
 import * as Misskey from 'misskey-js';
-import * as os from '@/os';
-import { useStream } from '@/stream';
-import { i18n } from '@/i18n';
-import { defaultStore } from '@/store';
-import { uploadFile } from '@/scripts/upload';
+import * as os from '@/os.js';
+import { useStream } from '@/stream.js';
+import { i18n } from '@/i18n.js';
+import { defaultStore } from '@/store.js';
+import { uploadFile } from '@/scripts/upload.js';
 
 export function chooseFileFromPc(multiple: boolean, keepOriginal = false): Promise<Misskey.entities.DriveFile[]> {
 	return new Promise((res, rej) => {
diff --git a/packages/frontend/src/scripts/show-moved-dialog.ts b/packages/frontend/src/scripts/show-moved-dialog.ts
index 9e3e4ee7ff..b4defbfe7d 100644
--- a/packages/frontend/src/scripts/show-moved-dialog.ts
+++ b/packages/frontend/src/scripts/show-moved-dialog.ts
@@ -3,9 +3,9 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import * as os from '@/os';
-import { $i } from '@/account';
-import { i18n } from '@/i18n';
+import * as os from '@/os.js';
+import { $i } from '@/account.js';
+import { i18n } from '@/i18n.js';
 
 export function showMovedDialog() {
 	if (!$i) return;
diff --git a/packages/frontend/src/scripts/show-suspended-dialog.ts b/packages/frontend/src/scripts/show-suspended-dialog.ts
index 9f0d69af79..a2fd5db453 100644
--- a/packages/frontend/src/scripts/show-suspended-dialog.ts
+++ b/packages/frontend/src/scripts/show-suspended-dialog.ts
@@ -3,8 +3,8 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import * as os from '@/os';
-import { i18n } from '@/i18n';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
 
 export function showSuspendedDialog() {
 	return os.alert({
diff --git a/packages/frontend/src/scripts/sound.ts b/packages/frontend/src/scripts/sound.ts
index 7e64cafcf9..1ef41b47d3 100644
--- a/packages/frontend/src/scripts/sound.ts
+++ b/packages/frontend/src/scripts/sound.ts
@@ -4,7 +4,7 @@
  */
 
 import { markRaw } from 'vue';
-import { Storage } from '@/pizzax';
+import { Storage } from '@/pizzax.js';
 
 export const soundConfigStore = markRaw(new Storage('sound', {
 	mediaVolume: {
diff --git a/packages/frontend/src/scripts/theme-editor.ts b/packages/frontend/src/scripts/theme-editor.ts
index 69fd41255c..275f4bcdaa 100644
--- a/packages/frontend/src/scripts/theme-editor.ts
+++ b/packages/frontend/src/scripts/theme-editor.ts
@@ -5,7 +5,7 @@
 
 import { v4 as uuid } from 'uuid';
 
-import { themeProps, Theme } from './theme';
+import { themeProps, Theme } from './theme.js';
 
 export type Default = null;
 export type Color = string;
diff --git a/packages/frontend/src/scripts/theme.ts b/packages/frontend/src/scripts/theme.ts
index b8faa469b6..1c924e774f 100644
--- a/packages/frontend/src/scripts/theme.ts
+++ b/packages/frontend/src/scripts/theme.ts
@@ -19,7 +19,7 @@ export type Theme = {
 import lightTheme from '@/themes/_light.json5';
 import darkTheme from '@/themes/_dark.json5';
 import { deepClone } from './clone';
-import { miLocalStorage } from '@/local-storage';
+import { miLocalStorage } from '@/local-storage.js';
 
 export const themeProps = Object.keys(lightTheme.props).filter(key => !key.startsWith('X'));
 
diff --git a/packages/frontend/src/scripts/touch.ts b/packages/frontend/src/scripts/touch.ts
index 0695913298..05f379e4aa 100644
--- a/packages/frontend/src/scripts/touch.ts
+++ b/packages/frontend/src/scripts/touch.ts
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { deviceKind } from '@/scripts/device-kind';
+import { deviceKind } from '@/scripts/device-kind.js';
 
 const isTouchSupported = 'maxTouchPoints' in navigator && navigator.maxTouchPoints > 0;
 
diff --git a/packages/frontend/src/scripts/upload.ts b/packages/frontend/src/scripts/upload.ts
index f5ec4b60b4..b896376ec8 100644
--- a/packages/frontend/src/scripts/upload.ts
+++ b/packages/frontend/src/scripts/upload.ts
@@ -6,12 +6,12 @@
 import { reactive, ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import { readAndCompressImage } from 'browser-image-resizer';
-import { getCompressionConfig } from './upload/compress-config';
-import { defaultStore } from '@/store';
-import { apiUrl } from '@/config';
-import { $i } from '@/account';
-import { alert } from '@/os';
-import { i18n } from '@/i18n';
+import { getCompressionConfig } from './upload/compress-config.js';
+import { defaultStore } from '@/store.js';
+import { apiUrl } from '@/config.js';
+import { $i } from '@/account.js';
+import { alert } from '@/os.js';
+import { i18n } from '@/i18n.js';
 
 type Uploading = {
 	id: string;
diff --git a/packages/frontend/src/scripts/use-chart-tooltip.ts b/packages/frontend/src/scripts/use-chart-tooltip.ts
index 507fa4deca..daf915c7e3 100644
--- a/packages/frontend/src/scripts/use-chart-tooltip.ts
+++ b/packages/frontend/src/scripts/use-chart-tooltip.ts
@@ -4,7 +4,7 @@
  */
 
 import { onUnmounted, onDeactivated, ref } from 'vue';
-import * as os from '@/os';
+import * as os from '@/os.js';
 import MkChartTooltip from '@/components/MkChartTooltip.vue';
 
 export function useChartTooltip(opts: { position: 'top' | 'middle' } = { position: 'top' }) {
diff --git a/packages/frontend/src/scripts/use-note-capture.ts b/packages/frontend/src/scripts/use-note-capture.ts
index d01cf52dc8..c618532570 100644
--- a/packages/frontend/src/scripts/use-note-capture.ts
+++ b/packages/frontend/src/scripts/use-note-capture.ts
@@ -5,8 +5,8 @@
 
 import { onUnmounted, Ref } from 'vue';
 import * as Misskey from 'misskey-js';
-import { useStream } from '@/stream';
-import { $i } from '@/account';
+import { useStream } from '@/stream.js';
+import { $i } from '@/account.js';
 
 export function useNoteCapture(props: {
 	rootEl: Ref<HTMLElement>;
diff --git a/packages/frontend/src/store.ts b/packages/frontend/src/store.ts
index 4b8001bfb4..66cb8bcfbd 100644
--- a/packages/frontend/src/store.ts
+++ b/packages/frontend/src/store.ts
@@ -5,7 +5,7 @@
 
 import { markRaw, ref } from 'vue';
 import * as Misskey from 'misskey-js';
-import { Storage } from './pizzax';
+import { Storage } from '@/pizzax.js';
 
 interface PostFormAction {
 	title: string,
diff --git a/packages/frontend/src/stream.ts b/packages/frontend/src/stream.ts
index b241316648..27fce4d4b8 100644
--- a/packages/frontend/src/stream.ts
+++ b/packages/frontend/src/stream.ts
@@ -5,8 +5,8 @@
 
 import * as Misskey from 'misskey-js';
 import { markRaw } from 'vue';
-import { $i } from '@/account';
-import { url } from '@/config';
+import { $i } from '@/account.js';
+import { url } from '@/config.js';
 
 let stream: Misskey.Stream | null = null;
 
diff --git a/packages/frontend/src/theme-store.ts b/packages/frontend/src/theme-store.ts
index e71b4354f3..9cae68d5d3 100644
--- a/packages/frontend/src/theme-store.ts
+++ b/packages/frontend/src/theme-store.ts
@@ -3,10 +3,10 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { Theme } from './scripts/theme';
-import { miLocalStorage } from './local-storage';
-import { api } from '@/os';
-import { $i } from '@/account';
+import { Theme } from '@/scripts/theme.js';
+import { miLocalStorage } from '@/local-storage.js';
+import { api } from '@/os.js';
+import { $i } from '@/account.js';
 
 const lsCacheKey = $i ? `themes:${$i.id}` as const : null;
 
diff --git a/packages/frontend/src/ui/_common_/announcements.vue b/packages/frontend/src/ui/_common_/announcements.vue
index cd8b4e5275..2b1133d472 100644
--- a/packages/frontend/src/ui/_common_/announcements.vue
+++ b/packages/frontend/src/ui/_common_/announcements.vue
@@ -24,7 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 </template>
 
 <script lang="ts" setup>
-import { $i } from '@/account';
+import { $i } from '@/account.js';
 </script>
 
 <style lang="scss" module>
diff --git a/packages/frontend/src/ui/_common_/common.ts b/packages/frontend/src/ui/_common_/common.ts
index b2fdba1ece..ca4a71a67f 100644
--- a/packages/frontend/src/ui/_common_/common.ts
+++ b/packages/frontend/src/ui/_common_/common.ts
@@ -3,11 +3,11 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import * as os from '@/os';
-import { instance } from '@/instance';
-import { host } from '@/config';
-import { i18n } from '@/i18n';
-import { $i } from '@/account';
+import * as os from '@/os.js';
+import { instance } from '@/instance.js';
+import { host } from '@/config.js';
+import { i18n } from '@/i18n.js';
+import { $i } from '@/account.js';
 
 export function openInstanceMenu(ev: MouseEvent) {
 	os.popupMenu([{
diff --git a/packages/frontend/src/ui/_common_/common.vue b/packages/frontend/src/ui/_common_/common.vue
index 65c5dbb384..10972f3bc1 100644
--- a/packages/frontend/src/ui/_common_/common.vue
+++ b/packages/frontend/src/ui/_common_/common.vue
@@ -49,13 +49,13 @@ import { defineAsyncComponent, ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import { swInject } from './sw-inject';
 import XNotification from './notification.vue';
-import { popups, pendingApiRequestsCount } from '@/os';
-import { uploads } from '@/scripts/upload';
-import * as sound from '@/scripts/sound';
-import { $i } from '@/account';
-import { useStream } from '@/stream';
-import { i18n } from '@/i18n';
-import { defaultStore } from '@/store';
+import { popups, pendingApiRequestsCount } from '@/os.js';
+import { uploads } from '@/scripts/upload.js';
+import * as sound from '@/scripts/sound.js';
+import { $i } from '@/account.js';
+import { useStream } from '@/stream.js';
+import { i18n } from '@/i18n.js';
+import { defaultStore } from '@/store.js';
 import { globalEvents } from '@/events';
 
 const XStreamIndicator = defineAsyncComponent(() => import('./stream-indicator.vue'));
diff --git a/packages/frontend/src/ui/_common_/navbar-for-mobile.vue b/packages/frontend/src/ui/_common_/navbar-for-mobile.vue
index f2783ebb77..eed4cc777a 100644
--- a/packages/frontend/src/ui/_common_/navbar-for-mobile.vue
+++ b/packages/frontend/src/ui/_common_/navbar-for-mobile.vue
@@ -48,12 +48,12 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { computed, defineAsyncComponent, toRef } from 'vue';
 import { openInstanceMenu } from './common';
-import * as os from '@/os';
+import * as os from '@/os.js';
 import { navbarItemDef } from '@/navbar';
-import { $i, openAccountMenu as openAccountMenu_ } from '@/account';
-import { defaultStore } from '@/store';
-import { i18n } from '@/i18n';
-import { instance } from '@/instance';
+import { $i, openAccountMenu as openAccountMenu_ } from '@/account.js';
+import { defaultStore } from '@/store.js';
+import { i18n } from '@/i18n.js';
+import { instance } from '@/instance.js';
 
 const menu = toRef(defaultStore.state, 'menu');
 const otherMenuItemIndicated = computed(() => {
diff --git a/packages/frontend/src/ui/_common_/navbar.vue b/packages/frontend/src/ui/_common_/navbar.vue
index d0162c8330..46d92867b1 100644
--- a/packages/frontend/src/ui/_common_/navbar.vue
+++ b/packages/frontend/src/ui/_common_/navbar.vue
@@ -59,12 +59,12 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { computed, defineAsyncComponent, ref, watch } from 'vue';
 import { openInstanceMenu } from './common';
-import * as os from '@/os';
+import * as os from '@/os.js';
 import { navbarItemDef } from '@/navbar';
-import { $i, openAccountMenu as openAccountMenu_ } from '@/account';
-import { defaultStore } from '@/store';
-import { i18n } from '@/i18n';
-import { instance } from '@/instance';
+import { $i, openAccountMenu as openAccountMenu_ } from '@/account.js';
+import { defaultStore } from '@/store.js';
+import { i18n } from '@/i18n.js';
+import { instance } from '@/instance.js';
 
 const iconOnly = ref(false);
 
diff --git a/packages/frontend/src/ui/_common_/statusbar-federation.vue b/packages/frontend/src/ui/_common_/statusbar-federation.vue
index 34abb142e6..ff980e0d88 100644
--- a/packages/frontend/src/ui/_common_/statusbar-federation.vue
+++ b/packages/frontend/src/ui/_common_/statusbar-federation.vue
@@ -34,9 +34,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import MarqueeText from '@/components/MkMarquee.vue';
-import * as os from '@/os';
-import { useInterval } from '@/scripts/use-interval';
-import { getProxiedImageUrlNullable } from '@/scripts/media-proxy';
+import * as os from '@/os.js';
+import { useInterval } from '@/scripts/use-interval.js';
+import { getProxiedImageUrlNullable } from '@/scripts/media-proxy.js';
 
 const props = defineProps<{
 	display?: 'marquee' | 'oneByOne';
diff --git a/packages/frontend/src/ui/_common_/statusbar-rss.vue b/packages/frontend/src/ui/_common_/statusbar-rss.vue
index 63150e2d07..c8e5b3a8af 100644
--- a/packages/frontend/src/ui/_common_/statusbar-rss.vue
+++ b/packages/frontend/src/ui/_common_/statusbar-rss.vue
@@ -29,8 +29,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { ref } from 'vue';
 import MarqueeText from '@/components/MkMarquee.vue';
-import { useInterval } from '@/scripts/use-interval';
-import { shuffle } from '@/scripts/shuffle';
+import { useInterval } from '@/scripts/use-interval.js';
+import { shuffle } from '@/scripts/shuffle.js';
 
 const props = defineProps<{
 	url?: string;
diff --git a/packages/frontend/src/ui/_common_/statusbar-user-list.vue b/packages/frontend/src/ui/_common_/statusbar-user-list.vue
index 222b9b15b6..f1fcd315d0 100644
--- a/packages/frontend/src/ui/_common_/statusbar-user-list.vue
+++ b/packages/frontend/src/ui/_common_/statusbar-user-list.vue
@@ -34,10 +34,10 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { ref, watch } from 'vue';
 import * as Misskey from 'misskey-js';
 import MarqueeText from '@/components/MkMarquee.vue';
-import * as os from '@/os';
-import { useInterval } from '@/scripts/use-interval';
-import { getNoteSummary } from '@/scripts/get-note-summary';
-import { notePage } from '@/filters/note';
+import * as os from '@/os.js';
+import { useInterval } from '@/scripts/use-interval.js';
+import { getNoteSummary } from '@/scripts/get-note-summary.js';
+import { notePage } from '@/filters/note.js';
 
 const props = defineProps<{
 	userListId?: string;
diff --git a/packages/frontend/src/ui/_common_/statusbars.vue b/packages/frontend/src/ui/_common_/statusbars.vue
index 3d6557be11..81445df1e9 100644
--- a/packages/frontend/src/ui/_common_/statusbars.vue
+++ b/packages/frontend/src/ui/_common_/statusbars.vue
@@ -23,7 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { defineAsyncComponent } from 'vue';
-import { defaultStore } from '@/store';
+import { defaultStore } from '@/store.js';
 const XRss = defineAsyncComponent(() => import('./statusbar-rss.vue'));
 const XFederation = defineAsyncComponent(() => import('./statusbar-federation.vue'));
 const XUserList = defineAsyncComponent(() => import('./statusbar-user-list.vue'));
diff --git a/packages/frontend/src/ui/_common_/stream-indicator.vue b/packages/frontend/src/ui/_common_/stream-indicator.vue
index abeb66c326..b09221f5d2 100644
--- a/packages/frontend/src/ui/_common_/stream-indicator.vue
+++ b/packages/frontend/src/ui/_common_/stream-indicator.vue
@@ -15,11 +15,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { onUnmounted } from 'vue';
-import { useStream } from '@/stream';
-import { i18n } from '@/i18n';
+import { useStream } from '@/stream.js';
+import { i18n } from '@/i18n.js';
 import MkButton from '@/components/MkButton.vue';
-import * as os from '@/os';
-import { defaultStore } from '@/store';
+import * as os from '@/os.js';
+import { defaultStore } from '@/store.js';
 
 const zIndex = os.claimZIndex('high');
 
diff --git a/packages/frontend/src/ui/_common_/sw-inject.ts b/packages/frontend/src/ui/_common_/sw-inject.ts
index 678a2a9eea..5239b76705 100644
--- a/packages/frontend/src/ui/_common_/sw-inject.ts
+++ b/packages/frontend/src/ui/_common_/sw-inject.ts
@@ -3,11 +3,11 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { api, post } from '@/os';
-import { $i, login } from '@/account';
-import { getAccountFromId } from '@/scripts/get-account-from-id';
-import { mainRouter } from '@/router';
-import { deepClone } from '@/scripts/clone';
+import { api, post } from '@/os.js';
+import { $i, login } from '@/account.js';
+import { getAccountFromId } from '@/scripts/get-account-from-id.js';
+import { mainRouter } from '@/router.js';
+import { deepClone } from '@/scripts/clone.js';
 
 export function swInject() {
 	navigator.serviceWorker.addEventListener('message', async ev => {
diff --git a/packages/frontend/src/ui/_common_/upload.vue b/packages/frontend/src/ui/_common_/upload.vue
index 353cf36f8d..3365571a14 100644
--- a/packages/frontend/src/ui/_common_/upload.vue
+++ b/packages/frontend/src/ui/_common_/upload.vue
@@ -24,9 +24,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { } from 'vue';
-import * as os from '@/os';
-import { uploads } from '@/scripts/upload';
-import { i18n } from '@/i18n';
+import * as os from '@/os.js';
+import { uploads } from '@/scripts/upload.js';
+import { i18n } from '@/i18n.js';
 
 const zIndex = os.claimZIndex('high');
 </script>
diff --git a/packages/frontend/src/ui/classic.header.vue b/packages/frontend/src/ui/classic.header.vue
index c288308add..3b78aa93cd 100644
--- a/packages/frontend/src/ui/classic.header.vue
+++ b/packages/frontend/src/ui/classic.header.vue
@@ -49,13 +49,13 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { computed, defineAsyncComponent, onMounted } from 'vue';
 import { openInstanceMenu } from './_common_/common';
-import * as os from '@/os';
+import * as os from '@/os.js';
 import { navbarItemDef } from '@/navbar';
-import { openAccountMenu as openAccountMenu_, $i } from '@/account';
+import { openAccountMenu as openAccountMenu_, $i } from '@/account.js';
 import MkButton from '@/components/MkButton.vue';
-import { defaultStore } from '@/store';
-import { instance } from '@/instance';
-import { i18n } from '@/i18n';
+import { defaultStore } from '@/store.js';
+import { instance } from '@/instance.js';
+import { i18n } from '@/i18n.js';
 
 const WINDOW_THRESHOLD = 1400;
 
diff --git a/packages/frontend/src/ui/classic.sidebar.vue b/packages/frontend/src/ui/classic.sidebar.vue
index 86f0464ee1..9172d51893 100644
--- a/packages/frontend/src/ui/classic.sidebar.vue
+++ b/packages/frontend/src/ui/classic.sidebar.vue
@@ -47,18 +47,18 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { defineAsyncComponent, onMounted, computed, watch, nextTick } from 'vue';
-import { openInstanceMenu } from './_common_/common';
-// import { host } from '@/config';
-import * as os from '@/os';
-import { navbarItemDef } from '@/navbar';
-import { openAccountMenu as openAccountMenu_, $i } from '@/account';
+import { openInstanceMenu } from './_common_/common.js';
+// import { host } from '@/config.js';
+import * as os from '@/os.js';
+import { navbarItemDef } from '@/navbar.js';
+import { openAccountMenu as openAccountMenu_, $i } from '@/account.js';
 import MkButton from '@/components/MkButton.vue';
-// import { StickySidebar } from '@/scripts/sticky-sidebar';
-// import { mainRouter } from '@/router';
+// import { StickySidebar } from '@/scripts/sticky-sidebar.js';
+// import { mainRouter } from '@/router.js';
 //import MisskeyLogo from '@assets/client/misskey.svg';
-import { defaultStore } from '@/store';
-import { instance } from '@/instance';
-import { i18n } from '@/i18n';
+import { defaultStore } from '@/store.js';
+import { instance } from '@/instance.js';
+import { i18n } from '@/i18n.js';
 
 const WINDOW_THRESHOLD = 1400;
 
diff --git a/packages/frontend/src/ui/classic.vue b/packages/frontend/src/ui/classic.vue
index 53420aa6de..3323e813bb 100644
--- a/packages/frontend/src/ui/classic.vue
+++ b/packages/frontend/src/ui/classic.vue
@@ -49,14 +49,14 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { defineAsyncComponent, ComputedRef, onMounted, provide } from 'vue';
 import XSidebar from './classic.sidebar.vue';
 import XCommon from './_common_/common.vue';
-import { instanceName } from '@/config';
-import { StickySidebar } from '@/scripts/sticky-sidebar';
-import * as os from '@/os';
-import { mainRouter } from '@/router';
-import { PageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata';
-import { defaultStore } from '@/store';
-import { i18n } from '@/i18n';
-import { miLocalStorage } from '@/local-storage';
+import { instanceName } from '@/config.js';
+import { StickySidebar } from '@/scripts/sticky-sidebar.js';
+import * as os from '@/os.js';
+import { mainRouter } from '@/router.js';
+import { PageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata.js';
+import { defaultStore } from '@/store.js';
+import { i18n } from '@/i18n.js';
+import { miLocalStorage } from '@/local-storage.js';
 const XHeaderMenu = defineAsyncComponent(() => import('./classic.header.vue'));
 const XWidgets = defineAsyncComponent(() => import('./universal.widgets.vue'));
 
diff --git a/packages/frontend/src/ui/deck.vue b/packages/frontend/src/ui/deck.vue
index 3518891811..2510977877 100644
--- a/packages/frontend/src/ui/deck.vue
+++ b/packages/frontend/src/ui/deck.vue
@@ -90,19 +90,19 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { computed, defineAsyncComponent, ref, watch } from 'vue';
 import { v4 as uuid } from 'uuid';
 import XCommon from './_common_/common.vue';
-import { deckStore, addColumn as addColumnToStore, loadDeck, getProfiles, deleteProfile as deleteProfile_ } from './deck/deck-store';
+import { deckStore, addColumn as addColumnToStore, loadDeck, getProfiles, deleteProfile as deleteProfile_ } from './deck/deck-store.js';
 import XSidebar from '@/ui/_common_/navbar.vue';
 import XDrawerMenu from '@/ui/_common_/navbar-for-mobile.vue';
 import MkButton from '@/components/MkButton.vue';
-import { getScrollContainer } from '@/scripts/scroll';
-import * as os from '@/os';
-import { navbarItemDef } from '@/navbar';
-import { $i } from '@/account';
-import { i18n } from '@/i18n';
-import { mainRouter } from '@/router';
-import { unisonReload } from '@/scripts/unison-reload';
-import { deviceKind } from '@/scripts/device-kind';
-import { defaultStore } from '@/store';
+import { getScrollContainer } from '@/scripts/scroll.js';
+import * as os from '@/os.js';
+import { navbarItemDef } from '@/navbar.js';
+import { $i } from '@/account.js';
+import { i18n } from '@/i18n.js';
+import { mainRouter } from '@/router.js';
+import { unisonReload } from '@/scripts/unison-reload.js';
+import { deviceKind } from '@/scripts/device-kind.js';
+import { defaultStore } from '@/store.js';
 import XMainColumn from '@/ui/deck/main-column.vue';
 import XTlColumn from '@/ui/deck/tl-column.vue';
 import XAntennaColumn from '@/ui/deck/antenna-column.vue';
diff --git a/packages/frontend/src/ui/deck/antenna-column.vue b/packages/frontend/src/ui/deck/antenna-column.vue
index dfddb1bd05..a93a14648c 100644
--- a/packages/frontend/src/ui/deck/antenna-column.vue
+++ b/packages/frontend/src/ui/deck/antenna-column.vue
@@ -16,10 +16,10 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { onMounted } from 'vue';
 import XColumn from './column.vue';
-import { updateColumn, Column } from './deck-store';
+import { updateColumn, Column } from './deck-store.js';
 import MkTimeline from '@/components/MkTimeline.vue';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
 
 const props = defineProps<{
 	column: Column;
diff --git a/packages/frontend/src/ui/deck/channel-column.vue b/packages/frontend/src/ui/deck/channel-column.vue
index 2cfdfe2dcb..4b982073ad 100644
--- a/packages/frontend/src/ui/deck/channel-column.vue
+++ b/packages/frontend/src/ui/deck/channel-column.vue
@@ -21,11 +21,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import * as Misskey from 'misskey-js';
 import XColumn from './column.vue';
-import { updateColumn, Column } from './deck-store';
+import { updateColumn, Column } from './deck-store.js';
 import MkTimeline from '@/components/MkTimeline.vue';
 import MkButton from '@/components/MkButton.vue';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
 
 const props = defineProps<{
 	column: Column;
diff --git a/packages/frontend/src/ui/deck/column.vue b/packages/frontend/src/ui/deck/column.vue
index d6cc6b3e6d..269b9d57d5 100644
--- a/packages/frontend/src/ui/deck/column.vue
+++ b/packages/frontend/src/ui/deck/column.vue
@@ -44,9 +44,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { onBeforeUnmount, onMounted, provide, watch } from 'vue';
 import { updateColumn, swapLeftColumn, swapRightColumn, swapUpColumn, swapDownColumn, stackLeftColumn, popRightColumn, removeColumn, swapColumn, Column } from './deck-store';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
-import { MenuItem } from '@/types/menu';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
+import { MenuItem } from '@/types/menu.js';
 
 provide('shouldHeaderThin', true);
 provide('shouldOmitHeaderTitle', true);
diff --git a/packages/frontend/src/ui/deck/deck-store.ts b/packages/frontend/src/ui/deck/deck-store.ts
index 8aaf6c73d3..f910c5181d 100644
--- a/packages/frontend/src/ui/deck/deck-store.ts
+++ b/packages/frontend/src/ui/deck/deck-store.ts
@@ -6,9 +6,9 @@
 import { throttle } from 'throttle-debounce';
 import { markRaw } from 'vue';
 import { notificationTypes } from 'misskey-js';
-import { Storage } from '../../pizzax';
-import { api } from '@/os';
-import { deepClone } from '@/scripts/clone';
+import { Storage } from '@/pizzax.js';
+import { api } from '@/os.js';
+import { deepClone } from '@/scripts/clone.js';
 
 type ColumnWidget = {
 	name: string;
diff --git a/packages/frontend/src/ui/deck/direct-column.vue b/packages/frontend/src/ui/deck/direct-column.vue
index 228db37345..940d2d7609 100644
--- a/packages/frontend/src/ui/deck/direct-column.vue
+++ b/packages/frontend/src/ui/deck/direct-column.vue
@@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { } from 'vue';
 import XColumn from './column.vue';
-import { Column } from './deck-store';
+import { Column } from './deck-store.js';
 import MkNotes from '@/components/MkNotes.vue';
 
 defineProps<{
diff --git a/packages/frontend/src/ui/deck/list-column.vue b/packages/frontend/src/ui/deck/list-column.vue
index 225c4948b1..c43ccdfabb 100644
--- a/packages/frontend/src/ui/deck/list-column.vue
+++ b/packages/frontend/src/ui/deck/list-column.vue
@@ -18,8 +18,8 @@ import { } from 'vue';
 import XColumn from './column.vue';
 import { updateColumn, Column } from './deck-store';
 import MkTimeline from '@/components/MkTimeline.vue';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
 
 const props = defineProps<{
 	column: Column;
diff --git a/packages/frontend/src/ui/deck/main-column.vue b/packages/frontend/src/ui/deck/main-column.vue
index 08d55b8ee1..d54368c932 100644
--- a/packages/frontend/src/ui/deck/main-column.vue
+++ b/packages/frontend/src/ui/deck/main-column.vue
@@ -21,13 +21,13 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { ComputedRef, provide, shallowRef } from 'vue';
 import XColumn from './column.vue';
-import { deckStore, Column } from '@/ui/deck/deck-store';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
-import { mainRouter } from '@/router';
-import { PageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata';
+import { deckStore, Column } from '@/ui/deck/deck-store.js';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
+import { mainRouter } from '@/router.js';
+import { PageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata.js';
 import { useScrollPositionManager } from '@/nirax';
-import { getScrollContainer } from '@/scripts/scroll';
+import { getScrollContainer } from '@/scripts/scroll.js';
 
 defineProps<{
 	column: Column;
diff --git a/packages/frontend/src/ui/deck/mentions-column.vue b/packages/frontend/src/ui/deck/mentions-column.vue
index c8d0d21a02..381a9d02f2 100644
--- a/packages/frontend/src/ui/deck/mentions-column.vue
+++ b/packages/frontend/src/ui/deck/mentions-column.vue
@@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { } from 'vue';
 import XColumn from './column.vue';
-import { Column } from './deck-store';
+import { Column } from './deck-store.js';
 import MkNotes from '@/components/MkNotes.vue';
 
 defineProps<{
diff --git a/packages/frontend/src/ui/deck/notifications-column.vue b/packages/frontend/src/ui/deck/notifications-column.vue
index 3308197c5e..0455d43deb 100644
--- a/packages/frontend/src/ui/deck/notifications-column.vue
+++ b/packages/frontend/src/ui/deck/notifications-column.vue
@@ -14,10 +14,10 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { defineAsyncComponent } from 'vue';
 import XColumn from './column.vue';
-import { updateColumn, Column } from './deck-store';
+import { updateColumn, Column } from './deck-store.js';
 import XNotifications from '@/components/MkNotifications.vue';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
 
 const props = defineProps<{
 	column: Column;
diff --git a/packages/frontend/src/ui/deck/role-timeline-column.vue b/packages/frontend/src/ui/deck/role-timeline-column.vue
index 5406865f68..e986b1f7d3 100644
--- a/packages/frontend/src/ui/deck/role-timeline-column.vue
+++ b/packages/frontend/src/ui/deck/role-timeline-column.vue
@@ -16,10 +16,10 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { onMounted } from 'vue';
 import XColumn from './column.vue';
-import { updateColumn, Column } from './deck-store';
+import { updateColumn, Column } from './deck-store.js';
 import MkTimeline from '@/components/MkTimeline.vue';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
 
 const props = defineProps<{
 	column: Column;
diff --git a/packages/frontend/src/ui/deck/tl-column.vue b/packages/frontend/src/ui/deck/tl-column.vue
index 22e69d8788..813b801d21 100644
--- a/packages/frontend/src/ui/deck/tl-column.vue
+++ b/packages/frontend/src/ui/deck/tl-column.vue
@@ -27,12 +27,12 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { onMounted } from 'vue';
 import XColumn from './column.vue';
-import { removeColumn, updateColumn, Column } from './deck-store';
+import { removeColumn, updateColumn, Column } from './deck-store.js';
 import MkTimeline from '@/components/MkTimeline.vue';
-import * as os from '@/os';
-import { $i } from '@/account';
-import { i18n } from '@/i18n';
-import { instance } from '@/instance';
+import * as os from '@/os.js';
+import { $i } from '@/account.js';
+import { i18n } from '@/i18n.js';
+import { instance } from '@/instance.js';
 
 const props = defineProps<{
 	column: Column;
diff --git a/packages/frontend/src/ui/deck/widgets-column.vue b/packages/frontend/src/ui/deck/widgets-column.vue
index 5569e3d64c..5bd6d73976 100644
--- a/packages/frontend/src/ui/deck/widgets-column.vue
+++ b/packages/frontend/src/ui/deck/widgets-column.vue
@@ -17,9 +17,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { } from 'vue';
 import XColumn from './column.vue';
-import { addColumnWidget, Column, removeColumnWidget, setColumnWidgets, updateColumnWidget } from './deck-store';
+import { addColumnWidget, Column, removeColumnWidget, setColumnWidgets, updateColumnWidget } from './deck-store.js';
 import XWidgets from '@/components/MkWidgets.vue';
-import { i18n } from '@/i18n';
+import { i18n } from '@/i18n.js';
 
 const props = defineProps<{
 	column: Column;
diff --git a/packages/frontend/src/ui/minimum.vue b/packages/frontend/src/ui/minimum.vue
index 24a7a55a13..cc5433f81c 100644
--- a/packages/frontend/src/ui/minimum.vue
+++ b/packages/frontend/src/ui/minimum.vue
@@ -16,9 +16,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { provide, ComputedRef } from 'vue';
 import XCommon from './_common_/common.vue';
-import { mainRouter } from '@/router';
-import { PageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata';
-import { instanceName } from '@/config';
+import { mainRouter } from '@/router.js';
+import { PageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata.js';
+import { instanceName } from '@/config.js';
 
 let pageMetadata = $ref<null | ComputedRef<PageMetadata>>();
 
diff --git a/packages/frontend/src/ui/universal.vue b/packages/frontend/src/ui/universal.vue
index d9cb81b5ef..cc66bb47a4 100644
--- a/packages/frontend/src/ui/universal.vue
+++ b/packages/frontend/src/ui/universal.vue
@@ -93,19 +93,19 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { defineAsyncComponent, provide, onMounted, computed, ref, ComputedRef, watch, shallowRef, Ref } from 'vue';
 import XCommon from './_common_/common.vue';
 import type MkStickyContainer from '@/components/global/MkStickyContainer.vue';
-import { instanceName } from '@/config';
+import { instanceName } from '@/config.js';
 import XDrawerMenu from '@/ui/_common_/navbar-for-mobile.vue';
-import * as os from '@/os';
-import { defaultStore } from '@/store';
-import { navbarItemDef } from '@/navbar';
-import { i18n } from '@/i18n';
-import { $i } from '@/account';
-import { mainRouter } from '@/router';
-import { PageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata';
-import { deviceKind } from '@/scripts/device-kind';
-import { miLocalStorage } from '@/local-storage';
-import { CURRENT_STICKY_BOTTOM } from '@/const';
-import { useScrollPositionManager } from '@/nirax';
+import * as os from '@/os.js';
+import { defaultStore } from '@/store.js';
+import { navbarItemDef } from '@/navbar.js';
+import { i18n } from '@/i18n.js';
+import { $i } from '@/account.js';
+import { mainRouter } from '@/router.js';
+import { PageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata.js';
+import { deviceKind } from '@/scripts/device-kind.js';
+import { miLocalStorage } from '@/local-storage.js';
+import { CURRENT_STICKY_BOTTOM } from '@/const.js';
+import { useScrollPositionManager } from '@/nirax.js';
 
 const XWidgets = defineAsyncComponent(() => import('./universal.widgets.vue'));
 const XSidebar = defineAsyncComponent(() => import('@/ui/_common_/navbar.vue'));
diff --git a/packages/frontend/src/ui/universal.widgets.vue b/packages/frontend/src/ui/universal.widgets.vue
index 76ac000429..44988e6df3 100644
--- a/packages/frontend/src/ui/universal.widgets.vue
+++ b/packages/frontend/src/ui/universal.widgets.vue
@@ -18,8 +18,8 @@ let editMode = $ref(false);
 <script lang="ts" setup>
 import { } from 'vue';
 import XWidgets from '@/components/MkWidgets.vue';
-import { i18n } from '@/i18n';
-import { defaultStore } from '@/store';
+import { i18n } from '@/i18n.js';
+import { defaultStore } from '@/store.js';
 
 const props = withDefaults(defineProps<{
 	// null = 全てのウィジェットを表示
diff --git a/packages/frontend/src/ui/visitor.vue b/packages/frontend/src/ui/visitor.vue
index 010c5b210e..b7ad184da1 100644
--- a/packages/frontend/src/ui/visitor.vue
+++ b/packages/frontend/src/ui/visitor.vue
@@ -71,15 +71,15 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { ComputedRef, onMounted, provide } from 'vue';
 import XCommon from './_common_/common.vue';
-import { host, instanceName } from '@/config';
-import * as os from '@/os';
-import { instance } from '@/instance';
+import { host, instanceName } from '@/config.js';
+import * as os from '@/os.js';
+import { instance } from '@/instance.js';
 import XSigninDialog from '@/components/MkSigninDialog.vue';
 import XSignupDialog from '@/components/MkSignupDialog.vue';
-import { ColdDeviceStorage, defaultStore } from '@/store';
-import { mainRouter } from '@/router';
-import { PageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata';
-import { i18n } from '@/i18n';
+import { ColdDeviceStorage, defaultStore } from '@/store.js';
+import { mainRouter } from '@/router.js';
+import { PageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata.js';
+import { i18n } from '@/i18n.js';
 import MkVisitorDashboard from '@/components/MkVisitorDashboard.vue';
 
 const DESKTOP_THRESHOLD = 1100;
diff --git a/packages/frontend/src/ui/zen.vue b/packages/frontend/src/ui/zen.vue
index 18582d9465..4f0945eb48 100644
--- a/packages/frontend/src/ui/zen.vue
+++ b/packages/frontend/src/ui/zen.vue
@@ -24,10 +24,10 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { provide, ComputedRef } from 'vue';
 import XCommon from './_common_/common.vue';
-import { mainRouter } from '@/router';
-import { PageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata';
-import { instanceName, ui } from '@/config';
-import { i18n } from '@/i18n';
+import { mainRouter } from '@/router.js';
+import { PageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata.js';
+import { instanceName, ui } from '@/config.js';
+import { i18n } from '@/i18n.js';
 
 let pageMetadata = $ref<null | ComputedRef<PageMetadata>>();
 
diff --git a/packages/frontend/src/widgets/WidgetActivity.vue b/packages/frontend/src/widgets/WidgetActivity.vue
index 1c4a396b2e..ed969abcfc 100644
--- a/packages/frontend/src/widgets/WidgetActivity.vue
+++ b/packages/frontend/src/widgets/WidgetActivity.vue
@@ -24,11 +24,11 @@ import { ref } from 'vue';
 import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
 import XCalendar from './WidgetActivity.calendar.vue';
 import XChart from './WidgetActivity.chart.vue';
-import { GetFormResultType } from '@/scripts/form';
-import * as os from '@/os';
+import { GetFormResultType } from '@/scripts/form.js';
+import * as os from '@/os.js';
 import MkContainer from '@/components/MkContainer.vue';
-import { $i } from '@/account';
-import { i18n } from '@/i18n';
+import { $i } from '@/account.js';
+import { i18n } from '@/i18n.js';
 
 const name = 'activity';
 
diff --git a/packages/frontend/src/widgets/WidgetAichan.vue b/packages/frontend/src/widgets/WidgetAichan.vue
index dfd5164f7a..ce71413328 100644
--- a/packages/frontend/src/widgets/WidgetAichan.vue
+++ b/packages/frontend/src/widgets/WidgetAichan.vue
@@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { onMounted, onUnmounted, shallowRef } from 'vue';
 import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
-import { GetFormResultType } from '@/scripts/form';
+import { GetFormResultType } from '@/scripts/form.js';
 
 const name = 'ai';
 
diff --git a/packages/frontend/src/widgets/WidgetAiscript.vue b/packages/frontend/src/widgets/WidgetAiscript.vue
index 5bd5f90769..4b927563f8 100644
--- a/packages/frontend/src/widgets/WidgetAiscript.vue
+++ b/packages/frontend/src/widgets/WidgetAiscript.vue
@@ -22,12 +22,12 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { ref } from 'vue';
 import { Interpreter, Parser, utils } from '@syuilo/aiscript';
 import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
-import { GetFormResultType } from '@/scripts/form';
-import * as os from '@/os';
+import { GetFormResultType } from '@/scripts/form.js';
+import * as os from '@/os.js';
 import MkContainer from '@/components/MkContainer.vue';
-import { createAiScriptEnv } from '@/scripts/aiscript/api';
-import { $i } from '@/account';
-import { i18n } from '@/i18n';
+import { createAiScriptEnv } from '@/scripts/aiscript/api.js';
+import { $i } from '@/account.js';
+import { i18n } from '@/i18n.js';
 
 const name = 'aiscript';
 
diff --git a/packages/frontend/src/widgets/WidgetAiscriptApp.vue b/packages/frontend/src/widgets/WidgetAiscriptApp.vue
index df9ae95ee1..a41a8bdbd4 100644
--- a/packages/frontend/src/widgets/WidgetAiscriptApp.vue
+++ b/packages/frontend/src/widgets/WidgetAiscriptApp.vue
@@ -16,13 +16,13 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { onMounted, Ref, ref, watch } from 'vue';
 import { Interpreter, Parser } from '@syuilo/aiscript';
 import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
-import { GetFormResultType } from '@/scripts/form';
-import * as os from '@/os';
-import { createAiScriptEnv } from '@/scripts/aiscript/api';
-import { $i } from '@/account';
+import { GetFormResultType } from '@/scripts/form.js';
+import * as os from '@/os.js';
+import { createAiScriptEnv } from '@/scripts/aiscript/api.js';
+import { $i } from '@/account.js';
 import MkAsUi from '@/components/MkAsUi.vue';
 import MkContainer from '@/components/MkContainer.vue';
-import { AsUiComponent, AsUiRoot, registerAsUiLib } from '@/scripts/aiscript/ui';
+import { AsUiComponent, AsUiRoot, registerAsUiLib } from '@/scripts/aiscript/ui.js';
 
 const name = 'aiscriptApp';
 
diff --git a/packages/frontend/src/widgets/WidgetButton.vue b/packages/frontend/src/widgets/WidgetButton.vue
index d7df6bc715..b80117a9c5 100644
--- a/packages/frontend/src/widgets/WidgetButton.vue
+++ b/packages/frontend/src/widgets/WidgetButton.vue
@@ -14,10 +14,10 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { Interpreter, Parser } from '@syuilo/aiscript';
 import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
-import { GetFormResultType } from '@/scripts/form';
-import * as os from '@/os';
-import { createAiScriptEnv } from '@/scripts/aiscript/api';
-import { $i } from '@/account';
+import { GetFormResultType } from '@/scripts/form.js';
+import * as os from '@/os.js';
+import { createAiScriptEnv } from '@/scripts/aiscript/api.js';
+import { $i } from '@/account.js';
 import MkButton from '@/components/MkButton.vue';
 
 const name = 'button';
diff --git a/packages/frontend/src/widgets/WidgetCalendar.vue b/packages/frontend/src/widgets/WidgetCalendar.vue
index 18e16cd5b7..4b592203ec 100644
--- a/packages/frontend/src/widgets/WidgetCalendar.vue
+++ b/packages/frontend/src/widgets/WidgetCalendar.vue
@@ -40,9 +40,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { ref } from 'vue';
 import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
-import { GetFormResultType } from '@/scripts/form';
-import { i18n } from '@/i18n';
-import { useInterval } from '@/scripts/use-interval';
+import { GetFormResultType } from '@/scripts/form.js';
+import { i18n } from '@/i18n.js';
+import { useInterval } from '@/scripts/use-interval.js';
 
 const name = 'calendar';
 
diff --git a/packages/frontend/src/widgets/WidgetClicker.vue b/packages/frontend/src/widgets/WidgetClicker.vue
index 96600b5fab..9b19c007de 100644
--- a/packages/frontend/src/widgets/WidgetClicker.vue
+++ b/packages/frontend/src/widgets/WidgetClicker.vue
@@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
-import { GetFormResultType } from '@/scripts/form';
+import { GetFormResultType } from '@/scripts/form.js';
 import MkContainer from '@/components/MkContainer.vue';
 import MkClickerGame from '@/components/MkClickerGame.vue';
 
diff --git a/packages/frontend/src/widgets/WidgetClock.vue b/packages/frontend/src/widgets/WidgetClock.vue
index ada1fd815a..6e6e1fba96 100644
--- a/packages/frontend/src/widgets/WidgetClock.vue
+++ b/packages/frontend/src/widgets/WidgetClock.vue
@@ -31,12 +31,12 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { } from 'vue';
 import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
-import { GetFormResultType } from '@/scripts/form';
+import { GetFormResultType } from '@/scripts/form.js';
 import MkContainer from '@/components/MkContainer.vue';
 import MkAnalogClock from '@/components/MkAnalogClock.vue';
 import MkDigitalClock from '@/components/MkDigitalClock.vue';
-import { timezones } from '@/scripts/timezones';
-import { i18n } from '@/i18n';
+import { timezones } from '@/scripts/timezones.js';
+import { i18n } from '@/i18n.js';
 
 const name = 'clock';
 
diff --git a/packages/frontend/src/widgets/WidgetDigitalClock.vue b/packages/frontend/src/widgets/WidgetDigitalClock.vue
index 051ed4714b..a250d28230 100644
--- a/packages/frontend/src/widgets/WidgetDigitalClock.vue
+++ b/packages/frontend/src/widgets/WidgetDigitalClock.vue
@@ -15,8 +15,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
-import { GetFormResultType } from '@/scripts/form';
-import { timezones } from '@/scripts/timezones';
+import { GetFormResultType } from '@/scripts/form.js';
+import { timezones } from '@/scripts/timezones.js';
 import MkDigitalClock from '@/components/MkDigitalClock.vue';
 
 const name = 'digitalClock';
diff --git a/packages/frontend/src/widgets/WidgetFederation.vue b/packages/frontend/src/widgets/WidgetFederation.vue
index 8fbd1c6ff5..1d067aac69 100644
--- a/packages/frontend/src/widgets/WidgetFederation.vue
+++ b/packages/frontend/src/widgets/WidgetFederation.vue
@@ -27,14 +27,14 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { ref } from 'vue';
 import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
-import { GetFormResultType } from '@/scripts/form';
+import { GetFormResultType } from '@/scripts/form.js';
 import MkContainer from '@/components/MkContainer.vue';
 import MkMiniChart from '@/components/MkMiniChart.vue';
-import * as os from '@/os';
-import { useInterval } from '@/scripts/use-interval';
-import { i18n } from '@/i18n';
-import { getProxiedImageUrlNullable } from '@/scripts/media-proxy';
-import { defaultStore } from '@/store';
+import * as os from '@/os.js';
+import { useInterval } from '@/scripts/use-interval.js';
+import { i18n } from '@/i18n.js';
+import { getProxiedImageUrlNullable } from '@/scripts/media-proxy.js';
+import { defaultStore } from '@/store.js';
 
 const name = 'federation';
 
diff --git a/packages/frontend/src/widgets/WidgetInstanceCloud.vue b/packages/frontend/src/widgets/WidgetInstanceCloud.vue
index 72b12720da..ffcf059a42 100644
--- a/packages/frontend/src/widgets/WidgetInstanceCloud.vue
+++ b/packages/frontend/src/widgets/WidgetInstanceCloud.vue
@@ -20,12 +20,12 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { } from 'vue';
 import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
-import { GetFormResultType } from '@/scripts/form';
+import { GetFormResultType } from '@/scripts/form.js';
 import MkContainer from '@/components/MkContainer.vue';
 import MkTagCloud from '@/components/MkTagCloud.vue';
-import * as os from '@/os';
-import { useInterval } from '@/scripts/use-interval';
-import { getProxiedImageUrlNullable } from '@/scripts/media-proxy';
+import * as os from '@/os.js';
+import { useInterval } from '@/scripts/use-interval.js';
+import { getProxiedImageUrlNullable } from '@/scripts/media-proxy.js';
 
 const name = 'instanceCloud';
 
diff --git a/packages/frontend/src/widgets/WidgetInstanceInfo.vue b/packages/frontend/src/widgets/WidgetInstanceInfo.vue
index a7413d4b6a..48a1849090 100644
--- a/packages/frontend/src/widgets/WidgetInstanceInfo.vue
+++ b/packages/frontend/src/widgets/WidgetInstanceInfo.vue
@@ -21,9 +21,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
-import { GetFormResultType } from '@/scripts/form';
-import { host } from '@/config';
-import { instance } from '@/instance';
+import { GetFormResultType } from '@/scripts/form.js';
+import { host } from '@/config.js';
+import { instance } from '@/instance.js';
 
 const name = 'instanceInfo';
 
diff --git a/packages/frontend/src/widgets/WidgetJobQueue.vue b/packages/frontend/src/widgets/WidgetJobQueue.vue
index eeb9265048..e0025a8074 100644
--- a/packages/frontend/src/widgets/WidgetJobQueue.vue
+++ b/packages/frontend/src/widgets/WidgetJobQueue.vue
@@ -53,11 +53,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { onUnmounted, reactive } from 'vue';
 import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
-import { GetFormResultType } from '@/scripts/form';
-import { useStream } from '@/stream';
-import number from '@/filters/number';
-import * as sound from '@/scripts/sound';
-import { deepClone } from '@/scripts/clone';
+import { GetFormResultType } from '@/scripts/form.js';
+import { useStream } from '@/stream.js';
+import number from '@/filters/number.js';
+import * as sound from '@/scripts/sound.js';
+import { deepClone } from '@/scripts/clone.js';
 
 const name = 'jobQueue';
 
diff --git a/packages/frontend/src/widgets/WidgetMemo.vue b/packages/frontend/src/widgets/WidgetMemo.vue
index 305a48f35b..c12afd50b0 100644
--- a/packages/frontend/src/widgets/WidgetMemo.vue
+++ b/packages/frontend/src/widgets/WidgetMemo.vue
@@ -18,10 +18,10 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { ref, watch } from 'vue';
 import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
-import { GetFormResultType } from '@/scripts/form';
+import { GetFormResultType } from '@/scripts/form.js';
 import MkContainer from '@/components/MkContainer.vue';
-import { defaultStore } from '@/store';
-import { i18n } from '@/i18n';
+import { defaultStore } from '@/store.js';
+import { i18n } from '@/i18n.js';
 
 const name = 'memo';
 
diff --git a/packages/frontend/src/widgets/WidgetNotifications.vue b/packages/frontend/src/widgets/WidgetNotifications.vue
index c4ea3c530c..b9999d8011 100644
--- a/packages/frontend/src/widgets/WidgetNotifications.vue
+++ b/packages/frontend/src/widgets/WidgetNotifications.vue
@@ -18,11 +18,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { defineAsyncComponent } from 'vue';
 import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
-import { GetFormResultType } from '@/scripts/form';
+import { GetFormResultType } from '@/scripts/form.js';
 import MkContainer from '@/components/MkContainer.vue';
 import XNotifications from '@/components/MkNotifications.vue';
-import * as os from '@/os';
-import { i18n } from '@/i18n';
+import * as os from '@/os.js';
+import { i18n } from '@/i18n.js';
 
 const name = 'notifications';
 
diff --git a/packages/frontend/src/widgets/WidgetOnlineUsers.vue b/packages/frontend/src/widgets/WidgetOnlineUsers.vue
index f0447bb8b0..a77468009e 100644
--- a/packages/frontend/src/widgets/WidgetOnlineUsers.vue
+++ b/packages/frontend/src/widgets/WidgetOnlineUsers.vue
@@ -16,11 +16,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { ref } from 'vue';
 import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
-import { GetFormResultType } from '@/scripts/form';
-import * as os from '@/os';
-import { useInterval } from '@/scripts/use-interval';
-import { i18n } from '@/i18n';
-import number from '@/filters/number';
+import { GetFormResultType } from '@/scripts/form.js';
+import * as os from '@/os.js';
+import { useInterval } from '@/scripts/use-interval.js';
+import { i18n } from '@/i18n.js';
+import number from '@/filters/number.js';
 
 const name = 'onlineUsers';
 
diff --git a/packages/frontend/src/widgets/WidgetPhotos.vue b/packages/frontend/src/widgets/WidgetPhotos.vue
index c2e3140f84..8c01d3cce9 100644
--- a/packages/frontend/src/widgets/WidgetPhotos.vue
+++ b/packages/frontend/src/widgets/WidgetPhotos.vue
@@ -24,13 +24,13 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { onUnmounted, ref } from 'vue';
 import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
-import { GetFormResultType } from '@/scripts/form';
-import { useStream } from '@/stream';
-import { getStaticImageUrl } from '@/scripts/media-proxy';
-import * as os from '@/os';
+import { GetFormResultType } from '@/scripts/form.js';
+import { useStream } from '@/stream.js';
+import { getStaticImageUrl } from '@/scripts/media-proxy.js';
+import * as os from '@/os.js';
 import MkContainer from '@/components/MkContainer.vue';
-import { defaultStore } from '@/store';
-import { i18n } from '@/i18n';
+import { defaultStore } from '@/store.js';
+import { i18n } from '@/i18n.js';
 
 const name = 'photos';
 
diff --git a/packages/frontend/src/widgets/WidgetPostForm.vue b/packages/frontend/src/widgets/WidgetPostForm.vue
index 0714a34d6b..d20ea3f8f4 100644
--- a/packages/frontend/src/widgets/WidgetPostForm.vue
+++ b/packages/frontend/src/widgets/WidgetPostForm.vue
@@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { } from 'vue';
 import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
-import { GetFormResultType } from '@/scripts/form';
+import { GetFormResultType } from '@/scripts/form.js';
 import MkPostForm from '@/components/MkPostForm.vue';
 
 const name = 'postForm';
diff --git a/packages/frontend/src/widgets/WidgetProfile.vue b/packages/frontend/src/widgets/WidgetProfile.vue
index 8d20127470..8cd5ffa9e1 100644
--- a/packages/frontend/src/widgets/WidgetProfile.vue
+++ b/packages/frontend/src/widgets/WidgetProfile.vue
@@ -23,9 +23,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
-import { GetFormResultType } from '@/scripts/form';
-import { $i } from '@/account';
-import { userPage } from '@/filters/user';
+import { GetFormResultType } from '@/scripts/form.js';
+import { $i } from '@/account.js';
+import { userPage } from '@/filters/user.js';
 
 const name = 'profile';
 
diff --git a/packages/frontend/src/widgets/WidgetRss.vue b/packages/frontend/src/widgets/WidgetRss.vue
index de655c1982..22833415dd 100644
--- a/packages/frontend/src/widgets/WidgetRss.vue
+++ b/packages/frontend/src/widgets/WidgetRss.vue
@@ -25,12 +25,12 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { ref, watch, computed } from 'vue';
 import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
-import { GetFormResultType } from '@/scripts/form';
+import { GetFormResultType } from '@/scripts/form.js';
 import MkContainer from '@/components/MkContainer.vue';
-import { url as base } from '@/config';
-import { i18n } from '@/i18n';
-import { useInterval } from '@/scripts/use-interval';
-import { infoImageUrl } from '@/instance';
+import { url as base } from '@/config.js';
+import { i18n } from '@/i18n.js';
+import { useInterval } from '@/scripts/use-interval.js';
+import { infoImageUrl } from '@/instance.js';
 
 const name = 'rss';
 
diff --git a/packages/frontend/src/widgets/WidgetRssTicker.vue b/packages/frontend/src/widgets/WidgetRssTicker.vue
index ec790eef67..ce8142f1ad 100644
--- a/packages/frontend/src/widgets/WidgetRssTicker.vue
+++ b/packages/frontend/src/widgets/WidgetRssTicker.vue
@@ -30,11 +30,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { ref, watch, computed } from 'vue';
 import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
 import MarqueeText from '@/components/MkMarquee.vue';
-import { GetFormResultType } from '@/scripts/form';
+import { GetFormResultType } from '@/scripts/form.js';
 import MkContainer from '@/components/MkContainer.vue';
-import { shuffle } from '@/scripts/shuffle';
-import { url as base } from '@/config';
-import { useInterval } from '@/scripts/use-interval';
+import { shuffle } from '@/scripts/shuffle.js';
+import { url as base } from '@/config.js';
+import { useInterval } from '@/scripts/use-interval.js';
 
 const name = 'rssTicker';
 
diff --git a/packages/frontend/src/widgets/WidgetSlideshow.vue b/packages/frontend/src/widgets/WidgetSlideshow.vue
index f7b4d5b32e..fe7ac8303d 100644
--- a/packages/frontend/src/widgets/WidgetSlideshow.vue
+++ b/packages/frontend/src/widgets/WidgetSlideshow.vue
@@ -19,10 +19,10 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { onMounted, ref, shallowRef } from 'vue';
 import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
-import { GetFormResultType } from '@/scripts/form';
-import * as os from '@/os';
-import { useInterval } from '@/scripts/use-interval';
-import { i18n } from '@/i18n';
+import { GetFormResultType } from '@/scripts/form.js';
+import * as os from '@/os.js';
+import { useInterval } from '@/scripts/use-interval.js';
+import { i18n } from '@/i18n.js';
 
 const name = 'slideshow';
 
diff --git a/packages/frontend/src/widgets/WidgetTimeline.vue b/packages/frontend/src/widgets/WidgetTimeline.vue
index 56bb7f0b9e..51623023c7 100644
--- a/packages/frontend/src/widgets/WidgetTimeline.vue
+++ b/packages/frontend/src/widgets/WidgetTimeline.vue
@@ -36,13 +36,13 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { ref } from 'vue';
 import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
-import { GetFormResultType } from '@/scripts/form';
-import * as os from '@/os';
+import { GetFormResultType } from '@/scripts/form.js';
+import * as os from '@/os.js';
 import MkContainer from '@/components/MkContainer.vue';
 import MkTimeline from '@/components/MkTimeline.vue';
-import { i18n } from '@/i18n';
-import { $i } from '@/account';
-import { instance } from '@/instance';
+import { i18n } from '@/i18n.js';
+import { $i } from '@/account.js';
+import { instance } from '@/instance.js';
 
 const name = 'timeline';
 const isLocalTimelineAvailable = (($i == null && instance.policies.ltlAvailable) || ($i != null && $i.policies.ltlAvailable));
diff --git a/packages/frontend/src/widgets/WidgetTrends.vue b/packages/frontend/src/widgets/WidgetTrends.vue
index 846b02ec1f..0a57305526 100644
--- a/packages/frontend/src/widgets/WidgetTrends.vue
+++ b/packages/frontend/src/widgets/WidgetTrends.vue
@@ -26,13 +26,13 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { ref } from 'vue';
 import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
-import { GetFormResultType } from '@/scripts/form';
+import { GetFormResultType } from '@/scripts/form.js';
 import MkContainer from '@/components/MkContainer.vue';
 import MkMiniChart from '@/components/MkMiniChart.vue';
-import * as os from '@/os';
-import { useInterval } from '@/scripts/use-interval';
-import { i18n } from '@/i18n';
-import { defaultStore } from '@/store';
+import * as os from '@/os.js';
+import { useInterval } from '@/scripts/use-interval.js';
+import { i18n } from '@/i18n.js';
+import { defaultStore } from '@/store.js';
 
 const name = 'hashtags';
 
diff --git a/packages/frontend/src/widgets/WidgetUnixClock.vue b/packages/frontend/src/widgets/WidgetUnixClock.vue
index bebec705ee..69bceb7572 100644
--- a/packages/frontend/src/widgets/WidgetUnixClock.vue
+++ b/packages/frontend/src/widgets/WidgetUnixClock.vue
@@ -18,7 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { onUnmounted, ref, watch } from 'vue';
 import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
-import { GetFormResultType } from '@/scripts/form';
+import { GetFormResultType } from '@/scripts/form.js';
 
 const name = 'unixClock';
 
diff --git a/packages/frontend/src/widgets/WidgetUserList.vue b/packages/frontend/src/widgets/WidgetUserList.vue
index 7be3331414..343e9a4292 100644
--- a/packages/frontend/src/widgets/WidgetUserList.vue
+++ b/packages/frontend/src/widgets/WidgetUserList.vue
@@ -25,11 +25,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { useWidgetPropsManager, Widget, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget';
-import { GetFormResultType } from '@/scripts/form';
+import { GetFormResultType } from '@/scripts/form.js';
 import MkContainer from '@/components/MkContainer.vue';
-import * as os from '@/os';
-import { useInterval } from '@/scripts/use-interval';
-import { i18n } from '@/i18n';
+import * as os from '@/os.js';
+import { useInterval } from '@/scripts/use-interval.js';
+import { i18n } from '@/i18n.js';
 import MkButton from '@/components/MkButton.vue';
 
 const name = 'userList';
diff --git a/packages/frontend/src/widgets/server-metric/disk.vue b/packages/frontend/src/widgets/server-metric/disk.vue
index 4faa6870cf..079b326fd6 100644
--- a/packages/frontend/src/widgets/server-metric/disk.vue
+++ b/packages/frontend/src/widgets/server-metric/disk.vue
@@ -18,7 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { } from 'vue';
 import XPie from './pie.vue';
-import bytes from '@/filters/bytes';
+import bytes from '@/filters/bytes.js';
 
 const props = defineProps<{
 	meta: any; // TODO
diff --git a/packages/frontend/src/widgets/server-metric/index.vue b/packages/frontend/src/widgets/server-metric/index.vue
index d1d94ad88d..f070dbbbe1 100644
--- a/packages/frontend/src/widgets/server-metric/index.vue
+++ b/packages/frontend/src/widgets/server-metric/index.vue
@@ -28,10 +28,10 @@ import XCpu from './cpu.vue';
 import XMemory from './mem.vue';
 import XDisk from './disk.vue';
 import MkContainer from '@/components/MkContainer.vue';
-import { GetFormResultType } from '@/scripts/form';
-import * as os from '@/os';
-import { useStream } from '@/stream';
-import { i18n } from '@/i18n';
+import { GetFormResultType } from '@/scripts/form.js';
+import * as os from '@/os.js';
+import { useStream } from '@/stream.js';
+import { i18n } from '@/i18n.js';
 
 const name = 'serverMetric';
 
diff --git a/packages/frontend/src/widgets/server-metric/mem.vue b/packages/frontend/src/widgets/server-metric/mem.vue
index 96ca2d9156..47f5a70a7e 100644
--- a/packages/frontend/src/widgets/server-metric/mem.vue
+++ b/packages/frontend/src/widgets/server-metric/mem.vue
@@ -18,7 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { onMounted, onBeforeUnmount } from 'vue';
 import XPie from './pie.vue';
-import bytes from '@/filters/bytes';
+import bytes from '@/filters/bytes.js';
 
 const props = defineProps<{
 	connection: any,
diff --git a/packages/frontend/src/widgets/server-metric/net.vue b/packages/frontend/src/widgets/server-metric/net.vue
index 661ac4bfb0..5593128660 100644
--- a/packages/frontend/src/widgets/server-metric/net.vue
+++ b/packages/frontend/src/widgets/server-metric/net.vue
@@ -50,7 +50,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { onMounted, onBeforeUnmount } from 'vue';
-import bytes from '@/filters/bytes';
+import bytes from '@/filters/bytes.js';
 
 const props = defineProps<{
 	connection: any,
diff --git a/packages/frontend/src/widgets/widget.ts b/packages/frontend/src/widgets/widget.ts
index ecb5c0c92d..9c7632fc9b 100644
--- a/packages/frontend/src/widgets/widget.ts
+++ b/packages/frontend/src/widgets/widget.ts
@@ -5,9 +5,9 @@
 
 import { reactive, watch } from 'vue';
 import { throttle } from 'throttle-debounce';
-import { Form, GetFormResultType } from '@/scripts/form';
-import * as os from '@/os';
-import { deepClone } from '@/scripts/clone';
+import { Form, GetFormResultType } from '@/scripts/form.js';
+import * as os from '@/os.js';
+import { deepClone } from '@/scripts/clone.js';
 
 export type Widget<P extends Record<string, unknown>> = {
 	id: string;
diff --git a/packages/frontend/test/init.ts b/packages/frontend/test/init.ts
index 80967034f2..986fa99c17 100644
--- a/packages/frontend/test/init.ts
+++ b/packages/frontend/test/init.ts
@@ -11,7 +11,7 @@ fetchMocker.enableMocks();
 
 // Set i18n
 import locales from '../../../locales';
-import { updateI18n } from '@/i18n';
+import { updateI18n } from '@/i18n.js';
 updateI18n(locales['en-US']);
 
 // XXX: misskey-js panics if WebSocket is not defined
diff --git a/packages/frontend/test/scroll.test.ts b/packages/frontend/test/scroll.test.ts
index 3fa740b11c..2334268d43 100644
--- a/packages/frontend/test/scroll.test.ts
+++ b/packages/frontend/test/scroll.test.ts
@@ -5,7 +5,7 @@
 
 import { describe, test, assert, afterEach } from 'vitest';
 import { Window } from 'happy-dom';
-import { onScrollBottom, onScrollTop } from '@/scripts/scroll';
+import { onScrollBottom, onScrollTop } from '@/scripts/scroll.js';
 
 describe('Scroll', () => {
 	describe('onScrollTop', () => {
diff --git a/packages/frontend/tsconfig.json b/packages/frontend/tsconfig.json
index 4839ce2259..ad8fd773b0 100644
--- a/packages/frontend/tsconfig.json
+++ b/packages/frontend/tsconfig.json
@@ -5,7 +5,7 @@
 		"noImplicitAny": false,
 		"noImplicitReturns": true,
 		"noUnusedParameters": false,
-		"noUnusedLocals": true,
+		"noUnusedLocals": false,
 		"noFallthroughCasesInSwitch": true,
 		"declaration": false,
 		"sourceMap": false,
diff --git a/packages/sw/src/scripts/create-notification.ts b/packages/sw/src/scripts/create-notification.ts
index 97ff8a3101..2b7f91a741 100644
--- a/packages/sw/src/scripts/create-notification.ts
+++ b/packages/sw/src/scripts/create-notification.ts
@@ -7,11 +7,11 @@
  * Notification manager for SW
  */
 import type { BadgeNames, PushNotificationDataMap } from '@/types';
-import { char2fileName } from '@/scripts/twemoji-base';
-import { cli } from '@/scripts/operations';
-import { getAccountFromId } from '@/scripts/get-account-from-id';
-import { swLang } from '@/scripts/lang';
-import { getUserName } from '@/scripts/get-user-name';
+import { char2fileName } from '@/scripts/twemoji-base.js';
+import { cli } from '@/scripts/operations.js';
+import { getAccountFromId } from '@/scripts/get-account-from-id.js';
+import { swLang } from '@/scripts/lang.js';
+import { getUserName } from '@/scripts/get-user-name.js';
 
 const closeNotificationsByTags = async (tags: string[]): Promise<void> => {
 	for (const n of (await Promise.all(tags.map(tag => globalThis.registration.getNotifications({ tag })))).flat()) {
diff --git a/packages/sw/src/scripts/lang.ts b/packages/sw/src/scripts/lang.ts
index dbe25130b4..a2b99ff6b1 100644
--- a/packages/sw/src/scripts/lang.ts
+++ b/packages/sw/src/scripts/lang.ts
@@ -7,7 +7,7 @@
  * Language manager for SW
  */
 import { get, set } from 'idb-keyval';
-import { I18n, type Locale } from '@/scripts/i18n';
+import { I18n, type Locale } from '@/scripts/i18n.js';
 
 class SwLang {
 	public cacheName = `mk-cache-${_VERSION_}`;
diff --git a/packages/sw/src/scripts/operations.ts b/packages/sw/src/scripts/operations.ts
index a849505bc3..ca75e395c0 100644
--- a/packages/sw/src/scripts/operations.ts
+++ b/packages/sw/src/scripts/operations.ts
@@ -9,8 +9,8 @@
  */
 import * as Misskey from 'misskey-js';
 import type { SwMessage, SwMessageOrderType } from '@/types';
-import { getAccountFromId } from '@/scripts/get-account-from-id';
-import { getUrlWithLoginId } from '@/scripts/login-id';
+import { getAccountFromId } from '@/scripts/get-account-from-id.js';
+import { getUrlWithLoginId } from '@/scripts/login-id.js';
 
 export const cli = new Misskey.api.APIClient({ origin, fetch: (...args): Promise<Response> => fetch(...args) });
 
diff --git a/packages/sw/src/sw.ts b/packages/sw/src/sw.ts
index 997a14b798..080161cf14 100644
--- a/packages/sw/src/sw.ts
+++ b/packages/sw/src/sw.ts
@@ -6,9 +6,9 @@
 import { get } from 'idb-keyval';
 import * as Misskey from 'misskey-js';
 import type { PushNotificationDataMap } from '@/types';
-import { createEmptyNotification, createNotification } from '@/scripts/create-notification';
-import { swLang } from '@/scripts/lang';
-import * as swos from '@/scripts/operations';
+import { createEmptyNotification, createNotification } from '@/scripts/create-notification.js';
+import { swLang } from '@/scripts/lang.js';
+import * as swos from '@/scripts/operations.js';
 
 globalThis.addEventListener('install', () => {
 	// ev.waitUntil(globalThis.skipWaiting());