Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: web dashboard (compilation statistics, server logs, artifacts) #94

Merged
merged 21 commits into from
Sep 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,15 +52,12 @@ __Check the base [`webpack.config.js`](https://github.com/callstack/repack/blob/
- [x] [Code splitting](https://github.com/callstack/nativepack/discussions/45) (__experimental__):
- Dynamic `import()` support with and without `React.lazy()` (recommended).
- Manual chunks using [`entry` option](https://webpack.js.org/concepts/entry-points/) (only for advanced users).
- [x] Web Dashboard with compilation status, server logs and artifacts.

### Planned features

- [ ] `ChunksToHermesBytecodePlugin` plugin to automatically transform async chunks to bytecode format.
- [ ] `webpack-init` command
- [ ] Web dashboard / Flipper plugin with:
- Logs
- Compilations progress, errors and emitted assets
- Bundle visualizations
- [ ] [Module Federation](https://medium.com/swlh/webpack-5-module-federation-a-game-changer-to-javascript-architecture-bcdd30e02669) support

## Documentation
Expand Down
11 changes: 9 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
{
"private": true,
"workspaces": ["./packages/*"],
"workspaces": {
"packages": [
"./packages/repack",
"./packages/TesterApp",
"./packages/debugger-ui",
"./packages/dashboard"
]
},
"scripts": {
"prepare": "yarn workspaces run prepare",
"build": "yarn workspaces run build",
Expand All @@ -9,6 +16,6 @@
"test": "yarn workspaces run test",
"release": "yarn prepare && cp README.md packages/repack && yarn workspace @callstack/repack run release",
"example:start": "yarn workspace TesterApp run start",
"example:build": "yarn workspace TesterApp run build"
"example:bundle": "yarn workspace TesterApp run bundle"
}
}
2 changes: 1 addition & 1 deletion packages/TesterApp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"android": "react-native run-android",
"ios": "react-native run-ios",
"start": "react-native webpack-start",
"bundle": "react-native webpack-bundle --entry-file index.js --bundle-output ./dist/index.bundle --dev=false",
"bundle": "react-native webpack-bundle --platform ios --entry-file index.js --bundle-output ./dist/index.bundle --dev=false",
"webpack": "PLATFORM=ios webpack -c webpack.config.js"
},
"dependencies": {
Expand Down
1 change: 1 addition & 0 deletions packages/TesterApp/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ module.exports = {
*/
alias: {
'react-native': reactNativePath,
'@babel/runtime': path.join(__dirname, 'node_modules/@babel/runtime'),
},
},
/**
Expand Down
8,634 changes: 0 additions & 8,634 deletions packages/TesterApp/yarn.lock

This file was deleted.

11 changes: 11 additions & 0 deletions packages/dashboard/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module.exports = {
extends: '@callstack/eslint-config/react',
overrides: [
{
files: ['*.config.js'],
rules: {
'import/no-extraneous-dependencies': 0,
},
},
],
};
4 changes: 4 additions & 0 deletions packages/dashboard/.prettierrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
module.exports = {
"singleQuote": true,
"trailingComma": "es5"
}
17 changes: 17 additions & 0 deletions packages/dashboard/babel.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module.exports = {
presets: [
[
'@babel/preset-env',
{
targets: '> 0.25%, not dead',
modules: false,
},
],
'@babel/preset-typescript',
'@babel/preset-react',
],
plugins: [
'@babel/plugin-proposal-optional-chaining',
'@babel/plugin-proposal-nullish-coalescing-operator',
],
};
64 changes: 64 additions & 0 deletions packages/dashboard/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
{
"name": "dashboard",
"private": true,
"version": "1.0.0",
"workspaces": {
"nohoist": [
"**"
]
},
"scripts": {
"prepare": "npm run build",
"build": "NODE_ENV=production webpack -c webpack.config.js",
"start": "webpack serve",
"lint": "eslint --ext \".ts,.tsx\" src",
"typecheck": "tsc",
"test": "exit 0"
},
"dependencies": {
"byte-size": "^8.1.0",
"classnames": "^2.3.1",
"console-feed": "^3.2.2",
"core-js": "^3.18.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-router-dom": "^5.3.0",
"regenerator-runtime": "^0.13.9",
"zen-observable": "^0.8.15"
},
"devDependencies": {
"@babel/core": "^7.15.5",
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.14.5",
"@babel/plugin-proposal-optional-chaining": "^7.14.5",
"@babel/preset-env": "^7.15.6",
"@babel/preset-react": "^7.14.5",
"@babel/preset-typescript": "^7.15.0",
"@callstack/eslint-config": "^11.0.0",
"@types/byte-size": "^8.1.0",
"@types/classnames": "^2.3.1",
"@types/react": "^17.0.24",
"@types/react-dom": "^17.0.9",
"@types/react-router-dom": "^5.3.0",
"@types/zen-observable": "^0.8.3",
"autoprefixer": "^10.3.5",
"babel-loader": "^8.2.2",
"copy-webpack-plugin": "^9.0.1",
"css-loader": "^6.3.0",
"eslint": "^7.32.0",
"file-loader": "^6.2.0",
"html-webpack-plugin": "^5.3.2",
"mini-css-extract-plugin": "^2.3.0",
"node-sass": "^6.0.1",
"postcss": "^8.3.7",
"postcss-loader": "^6.1.1",
"sass-loader": "^12.1.0",
"style-loader": "^3.3.0",
"tailwindcss": "^2.2.15",
"terser-webpack-plugin": "^5.2.4",
"typescript": "^4.4.3",
"url-loader": "^4.1.1",
"webpack": "^5.53.0",
"webpack-cli": "^4.8.0",
"webpack-dev-server": "^4.2.1"
}
}
6 changes: 6 additions & 0 deletions packages/dashboard/postcss.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};
34 changes: 34 additions & 0 deletions packages/dashboard/public/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Re.Pack Dashboard</title>
<link
rel="apple-touch-icon"
sizes="180x180"
href="/dashboard/static/media/apple-touch-icon.png"
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href="/dashboard/static/media/favicon-32x32.png"
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="/dashboard/static/media/favicon-16x16.png"
/>
<link rel="manifest" href="/dashboard/static/media/site.webmanifest" />
<link rel="preconnect" href="https://fonts.gstatic.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Roboto+Mono:wght@400;500&family=Poppins:wght@400;500&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<meta name="og:title" content="Re.Pack Dashboard" />
</head>
<body>
<div id="root"></div>
</body>
</html>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
1 change: 1 addition & 0 deletions packages/dashboard/public/static/media/site.webmanifest
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"name":"Re.Pack Dashboard","short_name":"Re.Pack","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
27 changes: 27 additions & 0 deletions packages/dashboard/src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React from 'react';
import { BrowserRouter as Router, Route } from 'react-router-dom';
import { AppLayout } from './components/AppLayout';
import { DevServerProvider } from './context/DevServerContext';
import { Artifacts } from './pages/Artifacts';
import { Dash } from './pages/Dash';
import { ServerLogs } from './pages/ServerLogs';

export const App = () => {
return (
<DevServerProvider>
<Router>
<AppLayout>
<Route exact path="/dashboard">
<Dash />
</Route>
<Route path="/dashboard/logs">
<ServerLogs />
</Route>
<Route path="/dashboard/artifacts">
<Artifacts />
</Route>
</AppLayout>
</Router>
</DevServerProvider>
);
};
1 change: 1 addition & 0 deletions packages/dashboard/src/assets/repack_logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
30 changes: 30 additions & 0 deletions packages/dashboard/src/components/Admonition.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import * as React from 'react';
import cx from 'classnames';

interface Props {
type: 'info' | 'progress';
children: React.ReactNode;
className?: string;
}

export function Admonition({ type, children, className }: Props) {
return (
<mark
className={cx(
'bg-dark-400 text-gray-300 px-6 py-3 border-l-8 flex flex-row items-center',
{ 'border-blue-700': type === 'info' || type === 'progress' },
className
)}
>
<span
className={cx('material-icons mr-2', {
'text-blue-200': type === 'info',
'text-blue-200 animate-spin': type === 'progress',
})}
>
{type === 'info' ? 'info' : 'autorenew'}
</span>
{children}
</mark>
);
}
17 changes: 17 additions & 0 deletions packages/dashboard/src/components/AppLayout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import * as React from 'react';
import { NavBar } from './NavBar';

interface Props {
children?: React.ReactNode;
}

export function AppLayout({ children }: Props) {
return (
<div className="bg-dark-300 pl-80 min-h-screen">
<NavBar />
<main className="text-gray-200 py-6 px-14 min-h-screen flex flex-col">
{children}
</main>
</div>
);
}
22 changes: 22 additions & 0 deletions packages/dashboard/src/components/Button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import * as React from 'react';
import cx from 'classnames';

interface Props {
onClick?: () => void;
children: React.ReactNode;
className?: string;
}

export function Button({ children, onClick, className }: Props) {
return (
<button
className={cx(
'px-2 py-1 flex flex-row items-center rounded-sm hover:bg-gray-600 text-gray-300 hover:text-gray-200 border-2 border-gray-600 transition ease-in duration-100',
className
)}
onClick={onClick}
>
{children}
</button>
);
}
68 changes: 68 additions & 0 deletions packages/dashboard/src/components/ConnectionStatus.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import * as React from 'react';
import cx from 'classnames';
import { useDevServer } from '../hooks/useDevServer';

export function ConnectionStatus() {
const [status, setStatus] = React.useState<
'connected' | 'connecting' | 'disconnected'
>('connecting');
const [showRetry, setShowRetry] = React.useState(false);
const { getProxyConnection, tryReconnecting } = useDevServer();

const retry = React.useCallback(() => {
setShowRetry(false);
tryReconnecting();
}, [tryReconnecting]);

React.useEffect(() => {
const subscription = getProxyConnection().subscribe({
next: (event) => {
if (event.type === 'init') {
setStatus('connecting');
} else if (event.type === 'open' || event.type === 'message') {
setStatus('connected');
} else if (event.type === 'close') {
if (event.retriesLeft === 0) {
setStatus('disconnected');
setShowRetry(true);
}
}
},
complete: () => {
setStatus('disconnected');
},
error: () => {
setStatus('disconnected');
},
});

return () => subscription.unsubscribe();
}, [getProxyConnection]);

return (
<div className="px-8">
<span className="text-gray-400 text-xs">Development server:</span>
<div className="flex flex-row items-center">
<div
className={cx('w-2 h-2 rounded-sm', {
'bg-green-500': status === 'connected',
'bg-yellow-500': status === 'connecting',
'bg-red-500': status === 'disconnected',
})}
/>
<span className="ml-2 text-gray-300">
{status[0].toUpperCase()}
{status.slice(1)}
</span>
{showRetry ? (
<button
className="ml-4 material-icons text-gray-400 hover:text-gray-200 transition ease-in duration-100"
onClick={retry}
>
refresh
</button>
) : null}
</div>
</div>
);
}
Loading