Skip to content

Commit

Permalink
refactor: move webui to base url /_expo/atlas (#16)
Browse files Browse the repository at this point in the history
* refactor: move webui to base url `/_expo/atlas`

* refactor: serve atlas on base url through cli

* refactor: temporarily switch to patched `expo-router` for base url support
  • Loading branch information
byCedric authored Mar 27, 2024
1 parent 24dd5d0 commit 67c7b5e
Show file tree
Hide file tree
Showing 10 changed files with 38 additions and 30 deletions.
31 changes: 9 additions & 22 deletions src/cli/createServer.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,25 @@
import { createRequestHandler } from '@expo/server/adapter/express';
import compression from 'compression';
import express from 'express';
import morgan from 'morgan';

import { type Options } from './resolveOptions';
import { StatsFileSource } from '../data/StatsFileSource';
import { env } from '../utils/env';
import { CLIENT_BUILD_DIR, SERVER_BUILD_DIR } from '../utils/middleware';
import { createAtlasMiddleware } from '../utils/middleware';

export function createServer(options: Options) {
global.EXPO_ATLAS_SOURCE = new StatsFileSource(options.statsFile);
process.env.NODE_ENV = 'production';

const app = express();
const source = new StatsFileSource(options.statsFile);
const middleware = createAtlasMiddleware(source);
const baseUrl = '/_expo/atlas'; // Keep in sync with webui `app.json` `baseUrl`

// http://expressjs.com/en/advanced/best-practice-security.html#at-a-minimum-disable-x-powered-by-header
app.disable('x-powered-by');
const app = express();

app.disable('x-powered-by'); // http://expressjs.com/en/advanced/best-practice-security.html#at-a-minimum-disable-x-powered-by-header
app.use(compression());
app.use(baseUrl, middleware);

if (env.EXPO_ATLAS_DEBUG) {
app.use(morgan('tiny'));
}

// TODO(cedric): replace with middleware once we can
app.use(
express.static(CLIENT_BUILD_DIR, {
maxAge: '1h',
extensions: ['html'],
})
);

// TODO(cedric): replace with middleware once we can
app.all('*', createRequestHandler({ build: SERVER_BUILD_DIR }));
// Add catch-all to redirect to the webui
app.use((req, res, next) => (!req.url.startsWith(baseUrl) ? res.redirect(baseUrl) : next()));

return app;
}
5 changes: 2 additions & 3 deletions src/utils/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@ import { type StatsSource } from '../data/types';

const WEBUI_ROOT = path.resolve(__dirname, '../../../webui');

// TODO(cedric): drop these exports once we can use this as base for the standalone server
export const CLIENT_BUILD_DIR = path.join(WEBUI_ROOT, 'dist/client');
export const SERVER_BUILD_DIR = path.join(WEBUI_ROOT, 'dist/server');
const CLIENT_BUILD_DIR = path.join(WEBUI_ROOT, 'dist/client');
const SERVER_BUILD_DIR = path.join(WEBUI_ROOT, 'dist/server');

/**
* Initialize Expo Atlas to gather statistics from Metro during development.
Expand Down
1 change: 1 addition & 0 deletions webui/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"output": "server"
},
"experiments": {
"baseUrl": "/_expo/atlas",
"tsconfigPaths": true,
"typedRoutes": true
},
Expand Down
Binary file modified webui/bun.lockb
Binary file not shown.
2 changes: 1 addition & 1 deletion webui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
"echarts-for-react": "^3.0.2",
"expo": "~50.0.6",
"expo-linking": "~6.2.2",
"expo-router": "^3.4.8",
"expo-router": "0.0.1-canary-20240320-8a10e09",
"expo-status-bar": "~1.11.1",
"lucide-react": "^0.336.0",
"nativewind": "^4.0.36",
Expand Down
3 changes: 2 additions & 1 deletion webui/src/app/folders/[path].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { TreemapGraph } from '~/components/graphs/TreemapGraph';
import { useStatsEntryContext } from '~/providers/stats';
import { Skeleton } from '~/ui/Skeleton';
import { Tag } from '~/ui/Tag';
import { fetchApi } from '~/utils/api';
import { formatFileSize } from '~/utils/formatString';
import { type PartialStatsEntry } from '~core/data/types';

Expand Down Expand Up @@ -88,7 +89,7 @@ function useFolderData(entryId: string, path: string) {
queryKey: [`module`, entryId, path],
queryFn: async ({ queryKey }) => {
const [_key, entry, path] = queryKey as [string, string, string];
return fetch(`/api/stats/${entry}/folders`, {
return fetchApi(`/api/stats/${entry}/folders`, {
method: 'POST',
body: JSON.stringify({ path }),
})
Expand Down
3 changes: 2 additions & 1 deletion webui/src/app/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
} from '~/providers/modules';
import { useStatsEntryContext } from '~/providers/stats';
import { Tag } from '~/ui/Tag';
import { fetchApi } from '~/utils/api';
import { formatFileSize } from '~/utils/formatString';

export default function GraphScreen() {
Expand Down Expand Up @@ -66,7 +67,7 @@ function useBundleGraphData(entryId: string, filters?: ModuleFilters) {
? `/api/stats/${entry}/modules?${filtersToUrlParams(filters)}`
: `/api/stats/${entry}/modules`;

return fetch(url)
return fetchApi(url)
.then((res) => (res.ok ? res : Promise.reject(res)))
.then((res) => res.json());
},
Expand Down
3 changes: 2 additions & 1 deletion webui/src/app/modules/[path].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { useStatsEntryContext } from '~/providers/stats';
import { CodeBlock, CodeBlockSectionWithPrettier, guessLanguageFromPath } from '~/ui/CodeBlock';
import { Skeleton } from '~/ui/Skeleton';
import { Tag } from '~/ui/Tag';
import { fetchApi } from '~/utils/api';
import { formatFileSize } from '~/utils/formatString';
import { type PartialStatsEntry, type StatsModule } from '~core/data/types';

Expand Down Expand Up @@ -114,7 +115,7 @@ function useModuleData(entryId: string, path: string) {
queryKey: [`module`, entryId, path],
queryFn: async ({ queryKey }) => {
const [_key, entry, path] = queryKey as [string, string, string];
return fetch(`/api/stats/${entry}/modules`, {
return fetchApi(`/api/stats/${entry}/modules`, {
method: 'POST',
body: JSON.stringify({ path }),
})
Expand Down
3 changes: 2 additions & 1 deletion webui/src/providers/stats.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { useQuery } from '@tanstack/react-query';
import { type PropsWithChildren, createContext, useContext, useMemo, useState } from 'react';

import { fetchApi } from '~/utils/api';
import { type PartialStatsEntry } from '~core/data/types';

type StatsEntryContext = {
Expand Down Expand Up @@ -44,6 +45,6 @@ export function StatsEntryProvider({ children }: PropsWithChildren) {
function useStatsEntriesData() {
return useQuery<PartialStatsEntry[]>({
queryKey: ['stats-entries'],
queryFn: () => fetch('/api/stats').then((res) => res.json()),
queryFn: () => fetchApi('/api/stats').then((res) => res.json()),
});
}
17 changes: 17 additions & 0 deletions webui/src/utils/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/**
* Keep this path in sync with `app.json`'s `baseUrl`.
*
* @see https://docs.expo.dev/versions/latest/config/app/#baseurl
*/
const baseUrl = '/_expo/atlas';

/**
* Fetch data from the API routes, adding the `baseUrl` to all requests.
*/
export function fetchApi(path: string, options?: RequestInit) {
if (path.startsWith(baseUrl)) {
return fetch(path, options);
}

return fetch(path.startsWith('/') ? `${baseUrl}${path}` : `${baseUrl}/${path}`, options);
}

0 comments on commit 67c7b5e

Please sign in to comment.