perf: 프런트엔드 import 경계를 세분화
Gateway 화면을 route 단위로 지연 로딩하고 common runtime import를 실제 소유 leaf export로 분리한다.\n\npackage boundary와 route loading 회귀 테스트로 root barrel 재유입을 차단한다.
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { JosaUtil } from '@sammo-ts/common';
|
||||
import { JosaUtil } from '@sammo-ts/common/util/JosaUtil';
|
||||
|
||||
import type { CommandOption, CommandTable } from './types';
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
|
||||
import { formatServerDateTime } from '@sammo-ts/common';
|
||||
import { formatServerDateTime } from '@sammo-ts/common/time/ServerDateTime';
|
||||
|
||||
export type GeneralBattleSummaryData = {
|
||||
available?: boolean;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { formatServerDateTime } from '@sammo-ts/common';
|
||||
import { formatServerDateTime } from '@sammo-ts/common/time/ServerDateTime';
|
||||
import { computed } from 'vue';
|
||||
import { resolveTournamentStageName } from '../../utils/tournamentStatus';
|
||||
|
||||
|
||||
@@ -5,12 +5,8 @@ import {
|
||||
MESSAGE_MAILBOX_PUBLIC,
|
||||
type MessageType,
|
||||
} from '@sammo-ts/logic/messages/message.js';
|
||||
import {
|
||||
applyReadModelDelta,
|
||||
cloneReadModelJson,
|
||||
type PublicRealtimeEvent,
|
||||
type RealtimeReadModelInvalidation,
|
||||
} from '@sammo-ts/common';
|
||||
import { applyReadModelDelta, cloneReadModelJson } from '@sammo-ts/common/realtime/delta';
|
||||
import type { PublicRealtimeEvent, RealtimeReadModelInvalidation } from '@sammo-ts/common/realtime/types';
|
||||
import { trpc } from '../utils/trpc';
|
||||
import { useMapViewerStore } from './mapViewer';
|
||||
import { useSessionStore } from './session';
|
||||
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
type RealtimeReadModelChanges,
|
||||
type RealtimeReadModelInvalidation,
|
||||
type RealtimeViewerIdentity,
|
||||
} from '@sammo-ts/common';
|
||||
} from '@sammo-ts/common/realtime/types';
|
||||
|
||||
export type DashboardReadModelIdentity = RealtimeViewerIdentity;
|
||||
export type DashboardRefreshPlan = RealtimeReadModelInvalidation;
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { formatLegacyLogHtml } from '@sammo-ts/common';
|
||||
import { formatLegacyLogHtml } from '@sammo-ts/common/logging/formatLegacyLogHtml';
|
||||
|
||||
export const formatLog = (text?: string): string => formatLegacyLogHtml(text);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { trpcJsonBodyHttpClientOptions } from '@sammo-ts/common';
|
||||
import { trpcJsonBodyHttpClientOptions } from '@sammo-ts/common/http/trpcTransport';
|
||||
import { createTRPCProxyClient, httpBatchLink } from '@trpc/client';
|
||||
import type { AppRouter } from '@sammo-ts/gateway-api';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { formatServerDateTime } from '@sammo-ts/common';
|
||||
import { formatServerDateTime } from '@sammo-ts/common/time/ServerDateTime';
|
||||
|
||||
export const formatSeoulDateTime = (value: string | Date): string => formatServerDateTime(value);
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { REALTIME_ACCESS_GRANT_HEADER, trpcJsonBodyHttpClientOptions } from '@sammo-ts/common';
|
||||
import { trpcJsonBodyHttpClientOptions } from '@sammo-ts/common/http/trpcTransport';
|
||||
import { REALTIME_ACCESS_GRANT_HEADER } from '@sammo-ts/common/realtime/types';
|
||||
import { createTRPCProxyClient, httpBatchLink } from '@trpc/client';
|
||||
import type { AppRouter } from '@sammo-ts/game-api';
|
||||
import { resolveBatchRealtimeAccessGrant } from './realtimeAccessGrant';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { formatServerDateTime } from '@sammo-ts/common';
|
||||
import { formatServerDateTime } from '@sammo-ts/common/time/ServerDateTime';
|
||||
import { computed, onMounted, reactive, ref, watch } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { formatServerDateTime } from '@sammo-ts/common';
|
||||
import { formatServerDateTime } from '@sammo-ts/common/time/ServerDateTime';
|
||||
import { computed, onMounted, reactive, ref, watch } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import PanelCard from '../components/ui/PanelCard.vue';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { formatServerDateTime } from '@sammo-ts/common';
|
||||
import { formatServerDateTime } from '@sammo-ts/common/time/ServerDateTime';
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import TournamentBracket from '../components/tournament/TournamentBracket.vue';
|
||||
import TournamentPageHeader from '../components/tournament/TournamentPageHeader.vue';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { formatServerDateTime } from '@sammo-ts/common';
|
||||
import { formatServerDateTime } from '@sammo-ts/common/time/ServerDateTime';
|
||||
import { computed, nextTick, onMounted, reactive, ref, watch } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { formatServerDateTime } from '@sammo-ts/common';
|
||||
import { formatServerDateTime } from '@sammo-ts/common/time/ServerDateTime';
|
||||
import { computed, onMounted, ref, watch } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { formatServerDateTime, JosaUtil } from '@sammo-ts/common';
|
||||
import { formatServerDateTime } from '@sammo-ts/common/time/ServerDateTime';
|
||||
import { JosaUtil } from '@sammo-ts/common/util/JosaUtil';
|
||||
import { computed, onMounted, reactive, ref } from 'vue';
|
||||
import { trpc } from '../utils/trpc';
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { formatServerDateTime } from '@sammo-ts/common';
|
||||
import { formatServerDateTime } from '@sammo-ts/common/time/ServerDateTime';
|
||||
import type { RuntimeNavigationConfig } from '@sammo-ts/common/navigation/menuConfig';
|
||||
import { computed, onMounted, onUnmounted, ref, watch } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { formatServerDateTime } from '@sammo-ts/common';
|
||||
import { formatServerDateTime } from '@sammo-ts/common/time/ServerDateTime';
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { useGameFeedback } from '../composables/useGameFeedback';
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { computed, onMounted, ref, watch } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
import { JosaUtil } from '@sammo-ts/common';
|
||||
import { JosaUtil } from '@sammo-ts/common/util/JosaUtil';
|
||||
|
||||
import PersonnelSelectionDialog from '../components/personnel/PersonnelSelectionDialog.vue';
|
||||
import { useGameFeedback } from '../composables/useGameFeedback';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { formatServerDateTime } from '@sammo-ts/common';
|
||||
import { formatServerDateTime } from '@sammo-ts/common/time/ServerDateTime';
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import { trpc } from '../utils/trpc';
|
||||
type Result = Awaited<ReturnType<typeof trpc.nation.getSecretGeneralList.query>>;
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
|
||||
import { isLegacyArchiveProfile, type LegacyArchiveProfile } from '@sammo-ts/common';
|
||||
import {
|
||||
isLegacyArchiveProfile,
|
||||
type LegacyArchiveProfile,
|
||||
} from '@sammo-ts/common/legacyArchive/ArchivedGeneralSnapshot';
|
||||
|
||||
import GeneralBasicCard from '../components/main/GeneralBasicCard.vue';
|
||||
import GeneralBattleSummary, { type GeneralBattleSummaryData } from '../components/main/GeneralBattleSummary.vue';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { formatServerDateTime } from '@sammo-ts/common';
|
||||
import { formatServerDateTime } from '@sammo-ts/common/time/ServerDateTime';
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { formatServerDateTime } from '@sammo-ts/common';
|
||||
import { formatServerDateTime } from '@sammo-ts/common/time/ServerDateTime';
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import TournamentBracket from '../components/tournament/TournamentBracket.vue';
|
||||
import TournamentPageHeader from '../components/tournament/TournamentPageHeader.vue';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { formatServerDateTime } from '@sammo-ts/common';
|
||||
import { formatServerDateTime } from '@sammo-ts/common/time/ServerDateTime';
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { formatServerDateTime } from '@sammo-ts/common';
|
||||
import { formatServerDateTime } from '@sammo-ts/common/time/ServerDateTime';
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router';
|
||||
import HomeView from '../views/HomeView.vue';
|
||||
import LobbyView from '../views/LobbyView.vue';
|
||||
import AdminOverviewView from '../views/AdminOverviewView.vue';
|
||||
import AdminView from '../views/AdminView.vue';
|
||||
import ServerOperationsView from '../views/ServerOperationsView.vue';
|
||||
import AccountView from '../views/AccountView.vue';
|
||||
import OAuthCallbackView from '../views/OAuthCallbackView.vue';
|
||||
import SignupView from '../views/SignupView.vue';
|
||||
|
||||
const HomeView = () => import('../views/HomeView.vue');
|
||||
const LobbyView = () => import('../views/LobbyView.vue');
|
||||
const AdminOverviewView = () => import('../views/AdminOverviewView.vue');
|
||||
const AdminView = () => import('../views/AdminView.vue');
|
||||
const ServerOperationsView = () => import('../views/ServerOperationsView.vue');
|
||||
const AccountView = () => import('../views/AccountView.vue');
|
||||
const OAuthCallbackView = () => import('../views/OAuthCallbackView.vue');
|
||||
const SignupView = () => import('../views/SignupView.vue');
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
import { formatLegacyLogHtml } from '@sammo-ts/common';
|
||||
import { formatLegacyLogHtml } from '@sammo-ts/common/logging/formatLegacyLogHtml';
|
||||
|
||||
export const formatLog = (text: string): string => formatLegacyLogHtml(text);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { trpcJsonBodyHttpClientOptions } from '@sammo-ts/common';
|
||||
import { trpcJsonBodyHttpClientOptions } from '@sammo-ts/common/http/trpcTransport';
|
||||
import { createTRPCProxyClient, httpBatchLink } from '@trpc/client';
|
||||
import type { appRouter } from '@sammo-ts/game-api';
|
||||
|
||||
@@ -9,9 +9,7 @@ const resolveProfileUrl = (template: string, profile: string): string =>
|
||||
|
||||
export const createGameTrpc = (profile: string, port: number, gameToken?: string) => {
|
||||
const urlTemplate = import.meta.env.VITE_GAME_API_URL_TEMPLATE;
|
||||
const url = urlTemplate
|
||||
? resolveProfileUrl(urlTemplate, profile)
|
||||
: `http://localhost:${port}/api/trpc`;
|
||||
const url = urlTemplate ? resolveProfileUrl(urlTemplate, profile) : `http://localhost:${port}/api/trpc`;
|
||||
return createTRPCProxyClient<GameRouter>({
|
||||
links: [
|
||||
httpBatchLink({
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { trpcJsonBodyHttpClientOptions } from '@sammo-ts/common';
|
||||
import { trpcJsonBodyHttpClientOptions } from '@sammo-ts/common/http/trpcTransport';
|
||||
import { createTRPCProxyClient, httpBatchLink, httpLink } from '@trpc/client';
|
||||
import type { AppRouter } from '@sammo-ts/gateway-api';
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { formatServerDateTime } from '@sammo-ts/common';
|
||||
import { formatServerDateTime } from '@sammo-ts/common/time/ServerDateTime';
|
||||
import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
formatServerDateTime,
|
||||
gatewayProfileCapabilities,
|
||||
serverDateTimeInputToIso,
|
||||
toServerDateTimeInputValue,
|
||||
type GatewayProfileStatus,
|
||||
} from '@sammo-ts/common';
|
||||
} from '@sammo-ts/common/time/ServerDateTime';
|
||||
import { gatewayProfileCapabilities, type GatewayProfileStatus } from '@sammo-ts/common/gateway/profileStatus';
|
||||
import { computed, onMounted, ref, watch } from 'vue';
|
||||
import ServerProfileTabs from '../components/ServerProfileTabs.vue';
|
||||
import AdminConsoleLayout from '../layouts/AdminConsoleLayout.vue';
|
||||
|
||||
@@ -3,7 +3,7 @@ import { computed, onMounted, ref } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import type { inferRouterOutputs } from '@trpc/server';
|
||||
import type { AppRouter } from '@sammo-ts/gateway-api';
|
||||
import { gatewayProfileCapabilities } from '@sammo-ts/common';
|
||||
import { gatewayProfileCapabilities } from '@sammo-ts/common/gateway/profileStatus';
|
||||
|
||||
import MapPreview from '../components/MapPreview.vue';
|
||||
import KakaoOtpDialog from '../components/KakaoOtpDialog.vue';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { formatServerDateTime } from '@sammo-ts/common';
|
||||
import { formatServerDateTime } from '@sammo-ts/common/time/ServerDateTime';
|
||||
import { computed, ref, onMounted, onUnmounted, watch } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import type { inferRouterOutputs } from '@trpc/server';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { formatServerDateTime, serverDateTimeInputToIso } from '@sammo-ts/common';
|
||||
import { formatServerDateTime, serverDateTimeInputToIso } from '@sammo-ts/common/time/ServerDateTime';
|
||||
import { computed, nextTick, onBeforeUnmount, onMounted, reactive, ref, watch } from 'vue';
|
||||
|
||||
import CompactHelp from '../components/CompactHelp.vue';
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import path from 'node:path';
|
||||
import { describe, it } from 'node:test';
|
||||
|
||||
const routerSourcePath = path.resolve(import.meta.dirname, '../src/router/index.ts');
|
||||
|
||||
void describe('gateway route loading contract', () => {
|
||||
void it('loads view components through route-level dynamic imports', async () => {
|
||||
const source = await readFile(routerSourcePath, 'utf8');
|
||||
const staticViewImports = [...source.matchAll(/^import\s+.+\s+from\s+['"]\.\.\/views\//gm)];
|
||||
const lazyViewComponents = new Set(
|
||||
[...source.matchAll(/^const\s+([A-Z][A-Za-z0-9]+View)\s*=\s*\(\)\s*=>\s*import\(['"]\.\.\/views\//gm)].map(
|
||||
(match) => match[1]
|
||||
)
|
||||
);
|
||||
const routedViewComponents = new Set(
|
||||
[...source.matchAll(/^\s*component:\s*([A-Z][A-Za-z0-9]+View),?$/gm)].map((match) => match[1])
|
||||
);
|
||||
|
||||
assert.deepEqual(staticViewImports, []);
|
||||
assert.deepEqual(lazyViewComponents, routedViewComponents);
|
||||
assert.ok(lazyViewComponents.size >= 8);
|
||||
});
|
||||
});
|
||||
@@ -22,9 +22,41 @@
|
||||
"types": "./dist/auth/gameSessionTransfer.d.ts",
|
||||
"default": "./dist/auth/gameSessionTransfer.js"
|
||||
},
|
||||
"./gateway/profileStatus": {
|
||||
"types": "./dist/gateway/profileStatus.d.ts",
|
||||
"default": "./dist/gateway/profileStatus.js"
|
||||
},
|
||||
"./http/trpcTransport": {
|
||||
"types": "./dist/http/trpcTransport.d.ts",
|
||||
"default": "./dist/http/trpcTransport.js"
|
||||
},
|
||||
"./legacyArchive/ArchivedGeneralSnapshot": {
|
||||
"types": "./dist/legacyArchive/ArchivedGeneralSnapshot.d.ts",
|
||||
"default": "./dist/legacyArchive/ArchivedGeneralSnapshot.js"
|
||||
},
|
||||
"./logging/formatLegacyLogHtml": {
|
||||
"types": "./dist/logging/formatLegacyLogHtml.d.ts",
|
||||
"default": "./dist/logging/formatLegacyLogHtml.js"
|
||||
},
|
||||
"./navigation/menuConfig": {
|
||||
"types": "./dist/navigation/menuConfig.d.ts",
|
||||
"default": "./dist/navigation/menuConfig.js"
|
||||
},
|
||||
"./realtime/delta": {
|
||||
"types": "./dist/realtime/delta.d.ts",
|
||||
"default": "./dist/realtime/delta.js"
|
||||
},
|
||||
"./realtime/types": {
|
||||
"types": "./dist/realtime/types.d.ts",
|
||||
"default": "./dist/realtime/types.js"
|
||||
},
|
||||
"./time/ServerDateTime": {
|
||||
"types": "./dist/time/ServerDateTime.d.ts",
|
||||
"default": "./dist/time/ServerDateTime.js"
|
||||
},
|
||||
"./util/JosaUtil": {
|
||||
"types": "./dist/util/JosaUtil.d.ts",
|
||||
"default": "./dist/util/JosaUtil.js"
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
@@ -6,7 +6,15 @@ export default defineConfig({
|
||||
'auth/gameToken': 'src/auth/gameToken.ts',
|
||||
'auth/gameSessionTransfer': 'src/auth/gameSessionTransfer.ts',
|
||||
'auth/sanctions': 'src/auth/sanctions.ts',
|
||||
'gateway/profileStatus': 'src/gateway/profileStatus.ts',
|
||||
'http/trpcTransport': 'src/http/trpcTransport.ts',
|
||||
'legacyArchive/ArchivedGeneralSnapshot': 'src/legacyArchive/ArchivedGeneralSnapshot.ts',
|
||||
'logging/formatLegacyLogHtml': 'src/logging/formatLegacyLogHtml.ts',
|
||||
'navigation/menuConfig': 'src/navigation/menuConfig.ts',
|
||||
'realtime/delta': 'src/realtime/delta.ts',
|
||||
'realtime/types': 'src/realtime/types.ts',
|
||||
'time/ServerDateTime': 'src/time/ServerDateTime.ts',
|
||||
'util/JosaUtil': 'src/util/JosaUtil.ts',
|
||||
},
|
||||
format: 'es',
|
||||
outDir: 'dist',
|
||||
|
||||
@@ -67,6 +67,10 @@ const persistenceModules = new Set([
|
||||
]);
|
||||
|
||||
const backendPackages = new Set(['@sammo-ts/game-api', '@sammo-ts/gateway-api']);
|
||||
const frontendRuntimeRootBarrels = new Set(['@sammo-ts/common', '@sammo-ts/logic']);
|
||||
const frontendRuntimeRootAllowlist = new Map([
|
||||
['app/game-frontend/src/workers/battleSimulator.worker.ts', new Set(['@sammo-ts/logic'])],
|
||||
]);
|
||||
const nodeBuiltins = new Set(builtinModules.flatMap((name) => [name, `node:${name}`]));
|
||||
|
||||
const isPersistenceModule = (specifier) =>
|
||||
@@ -101,7 +105,19 @@ export const extractImports = (source) => {
|
||||
const dynamicPattern = /\bimport\(\s*['"]([^'"]+)['"]\s*\)/g;
|
||||
let match;
|
||||
while ((match = fromPattern.exec(source))) {
|
||||
imports.push({ specifier: match[3], typeOnly: Boolean(match[1]), clause: match[2] });
|
||||
const clause = match[2].trim();
|
||||
const namedElements =
|
||||
clause.startsWith('{') && clause.endsWith('}')
|
||||
? clause
|
||||
.slice(1, -1)
|
||||
.split(',')
|
||||
.map((element) => element.trim())
|
||||
.filter(Boolean)
|
||||
: [];
|
||||
const typeOnly =
|
||||
Boolean(match[1]) ||
|
||||
(namedElements.length > 0 && namedElements.every((element) => element.startsWith('type ')));
|
||||
imports.push({ specifier: match[3], typeOnly, clause: match[2] });
|
||||
}
|
||||
while ((match = sideEffectPattern.exec(source))) {
|
||||
imports.push({ specifier: match[1], typeOnly: false, clause: '' });
|
||||
@@ -130,6 +146,14 @@ export const checkSource = ({ source, relativePath, zone }) => {
|
||||
if (zone.frontend && packageName && backendPackages.has(packageName) && !imported.typeOnly) {
|
||||
violations.push(`${relativePath}: frontend may reference ${packageName} only through import type`);
|
||||
}
|
||||
if (
|
||||
zone.frontend &&
|
||||
frontendRuntimeRootBarrels.has(imported.specifier) &&
|
||||
!imported.typeOnly &&
|
||||
!frontendRuntimeRootAllowlist.get(relativePath)?.has(imported.specifier)
|
||||
) {
|
||||
violations.push(`${relativePath}: frontend runtime must import a leaf subpath from ${imported.specifier}`);
|
||||
}
|
||||
if (
|
||||
(zone.name === 'game-api' || zone.name === 'gateway-api') &&
|
||||
imported.specifier === '@sammo-ts/game-engine'
|
||||
|
||||
@@ -11,6 +11,10 @@ test('extracts multiline and type-only package imports', () => {
|
||||
extractImports("import {\n GamePrisma,\n type DatabaseClient\n} from '@sammo-ts/infra';")[0]?.specifier,
|
||||
'@sammo-ts/infra'
|
||||
);
|
||||
assert.equal(
|
||||
extractImports("import { type AppRouter, type Profile } from '@sammo-ts/gateway-api';")[0]?.typeOnly,
|
||||
true
|
||||
);
|
||||
});
|
||||
|
||||
test('rejects infrastructure access and process diagnostics in logic', () => {
|
||||
@@ -51,3 +55,35 @@ test('rejects prefixed and bare Node builtins in frontend source', () => {
|
||||
'app/game-frontend/src/example.ts: frontend production code may not import node:fs',
|
||||
]);
|
||||
});
|
||||
|
||||
test('requires frontend common and logic runtime imports to use leaf subpaths', () => {
|
||||
const zone = {
|
||||
name: 'game-frontend',
|
||||
allowed: ['@sammo-ts/common', '@sammo-ts/logic'],
|
||||
frontend: true,
|
||||
};
|
||||
const violations = checkSource({
|
||||
source: [
|
||||
"import { formatServerDateTime } from '@sammo-ts/common';",
|
||||
"import { MESSAGE_MAILBOX_PUBLIC } from '@sammo-ts/logic';",
|
||||
"import type { MessageType } from '@sammo-ts/logic';",
|
||||
].join('\n'),
|
||||
relativePath: 'app/game-frontend/src/example.ts',
|
||||
zone,
|
||||
});
|
||||
|
||||
assert.deepEqual(violations, [
|
||||
'app/game-frontend/src/example.ts: frontend runtime must import a leaf subpath from @sammo-ts/common',
|
||||
'app/game-frontend/src/example.ts: frontend runtime must import a leaf subpath from @sammo-ts/logic',
|
||||
]);
|
||||
});
|
||||
|
||||
test('allows the isolated battle simulator worker to load the logic runtime root', () => {
|
||||
const violations = checkSource({
|
||||
source: "const loadProcessor = () => import('@sammo-ts/logic');",
|
||||
relativePath: 'app/game-frontend/src/workers/battleSimulator.worker.ts',
|
||||
zone: { name: 'game-frontend', allowed: ['@sammo-ts/logic'], frontend: true },
|
||||
});
|
||||
|
||||
assert.deepEqual(violations, []);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user