Skip to content

Commit

Permalink
feat(suite): add option to have vite as a dev server
Browse files Browse the repository at this point in the history
  • Loading branch information
vojtatranta committed Feb 26, 2025
1 parent 52db480 commit ff1fe99
Show file tree
Hide file tree
Showing 5 changed files with 972 additions and 275 deletions.
60 changes: 60 additions & 0 deletions packages/suite-web/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<!doctype html>
<html lang="en" translate="no">
<head>
<title>Trezor Suite</title>
<meta name="google" content="notranslate" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta charset="utf-8" />

<link media="all" rel="stylesheet" href="/static/fonts/fonts.css" />
<link rel="icon" href="/static/images/favicons/favicon.png" />
<link rel="apple-touch-icon" href="/static/images/favicons/favicon.png" />
<script type="text/javascript">
if (localStorage.getItem('translation_mode') === 'true') {
console.warn('INJECTING CROWDIN IN-CONTEXT');
var _jipt = [];
_jipt.push(['project', 'trezor-suite']);
_jipt.push([
'escape',
function () {
localStorage.removeItem('translation_mode');
window.location.reload();
},
]);
var script = document.createElement('script');
script.setAttribute('src', '//cdn.crowdin.com/jipt/jipt.js');
document.head.prepend(script);
}

// set favicon according to current system theme
function si(event) {
var isDM = event.matches; // isDarkMode
var i = document.querySelector('link[rel=icon]');
var ai = document.querySelector('link[rel=apple-touch-icon]');

if (isDM) {
i.setAttribute('href', '/static/images/favicons/favicon_dm.png');
ai.setAttribute('href', '/static/images/favicons/favicon_dm.png');
} else {
i.setAttribute('href', '/static/images/favicons/favicon.png');
ai.setAttribute('href', '/static/images/favicons/favicon.png');
}
}

if (window.matchMedia) {
var _mm = window.matchMedia('(prefers-color-scheme: dark)');

// not supported in IE
if (_mm.addEventListener) {
_mm.addEventListener('change', si);
}

si(_mm);
}
</script>
<script type="module" src="./src/vite-index.ts"></script>
</head>
<body>
<div id="app"></div>
</body>
</html>
6 changes: 6 additions & 0 deletions packages/suite-web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"type-check:watch": "yarn type-check -- --watch",
"lint:styles": "npx stylelint './src/**/*{.ts,.tsx}' --cache --config ../../.stylelintrc",
"dev": "yarn rimraf ./build && yarn workspace @trezor/suite-build run dev:web",
"dev:vite": "yarn rimraf ./build && vite",
"analyze": "ANALYZE=true yarn build",
"build": "yarn rimraf ./build && yarn workspace @trezor/suite-build run build:web"
},
Expand All @@ -29,6 +30,7 @@
"worker-loader": "^3.0.8"
},
"devDependencies": {
"@originjs/vite-plugin-commonjs": "^1.0.3",
"@suite-common/test-utils": "workspace:*",
"@suite-common/wallet-config": "workspace:*",
"@trezor/connect": "workspace:*",
Expand All @@ -45,6 +47,7 @@
"@types/react-dom": "18.2.19",
"@types/react-router": "^5.1.20",
"@types/react-router-dom": "^5.1.7",
"@vitejs/plugin-react": "^4.2.1",
"cypress": "^13.6.4",
"cypress-file-upload": "^5.0.8",
"cypress-image-snapshot": "^4.0.1",
Expand All @@ -53,6 +56,9 @@
"stylelint": "^16.14.1",
"stylelint-config-standard": "^36.0.0",
"tsx": "^4.16.3",
"vite": "^5.1.4",
"vite-plugin-node-polyfills": "^0.23.0",
"vite-plugin-wasm": "^3.4.1",
"webpack": "^5.97.1",
"ws": "^8.18.0",
"yargs": "17.7.2"
Expand Down
14 changes: 14 additions & 0 deletions packages/suite-web/src/vite-index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
* On the start of the app, the index.html file is without div with id="app" to which React app should be rendered.
* This div is added thanks to browser detection plugin in case that a user uses supported browser.
* After the div is added, MutationObserver detects the change and adds React app to the DOM.
*/

const appElement = document.getElementById('app');
if (appElement) {
import(/* webpackChunkName: "app" */ './Main')
.then(comp => comp.init(appElement))
.catch(err => console.error(err)); // Fatal error
}

export {};
180 changes: 180 additions & 0 deletions packages/suite-web/vite.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
import { viteCommonjs } from '@originjs/vite-plugin-commonjs';

Check failure on line 1 in packages/suite-web/vite.config.ts

View workflow job for this annotation

GitHub Actions / Linting and formatting

'@originjs/vite-plugin-commonjs' should be listed in the project's dependencies, not devDependencies
import react from '@vitejs/plugin-react';

Check failure on line 2 in packages/suite-web/vite.config.ts

View workflow job for this annotation

GitHub Actions / Linting and formatting

'@vitejs/plugin-react' should be listed in the project's dependencies, not devDependencies
import { execSync } from 'child_process';
import { readdirSync } from 'fs';
import { resolve } from 'path';
import { Plugin, ViteDevServer, defineConfig } from 'vite';

Check failure on line 6 in packages/suite-web/vite.config.ts

View workflow job for this annotation

GitHub Actions / Linting and formatting

'vite' should be listed in the project's dependencies, not devDependencies
import { nodePolyfills } from 'vite-plugin-node-polyfills';

Check failure on line 7 in packages/suite-web/vite.config.ts

View workflow job for this annotation

GitHub Actions / Linting and formatting

'vite-plugin-node-polyfills' should be listed in the project's dependencies, not devDependencies
import wasm from 'vite-plugin-wasm';

Check failure on line 8 in packages/suite-web/vite.config.ts

View workflow job for this annotation

GitHub Actions / Linting and formatting

'vite-plugin-wasm' should be listed in the project's dependencies, not devDependencies

import { suiteVersion } from '../suite/package.json';
import { assetPrefix, project } from '../suite-build/utils/env';

// Plugin to serve static files with /static prefix
const staticAliasPlugin = (): Plugin => ({
name: 'static-alias',
configureServer(server: ViteDevServer) {
// Middleware to handle /static requests
server.middlewares.use((req, res, next) => {
if (req.url?.startsWith('/static/')) {
// Rewrite the URL to access the file from the public directory
req.url = req.url.replace('/static/', '/');
}
next();
});
},
});

// Plugin to serve core.js in dev mode

// Plugin to handle workers similar to webpack's worker-loader
const workerPlugin = (): Plugin => ({
name: 'worker-loader',
transform(code, id) {
if (/\/workers\/[^/]+\/index\.ts$/.test(id)) {
// Return a virtual module that creates a web worker
return {
code: `
const worker = () => new Worker(new URL('${id}', import.meta.url), { type: 'module' });
export default worker;
`,
map: null,
};
}
},
});

const serveCorePlugin = () => ({
name: 'serve-core',
configureServer(server: ViteDevServer) {
server.middlewares.use(async (req, res, next) => {
if (req.url?.endsWith('/js/core.js')) {
const code = await server.transformRequest(
resolve(__dirname, '../connect/src/core/index.ts'),
{ ssr: false },
);
if (code?.code) {
res.setHeader('Content-Type', 'application/javascript');
res.end(code.code);

return;
}
}
next();
});
},
});

// This helper creates aliases for all workspace packages
const createWorkspaceAliases = () => {
const suiteCommonAliases = Object.fromEntries(
readdirSync(resolve(__dirname, '../../suite-common'), { withFileTypes: true })
.filter(dirent => dirent.isDirectory())
.map(dirent => [
`@suite-common/${dirent.name}`,
resolve(__dirname, '../../suite-common', dirent.name),
]),
);

const trezorPackagesAliases = Object.fromEntries(
readdirSync(resolve(__dirname, '../'), { withFileTypes: true })
.filter(dirent => dirent.isDirectory() && dirent.name !== 'suite-web')
.map(dirent => [`@trezor/${dirent.name}`, resolve(__dirname, '../', dirent.name)]),
);

return {
...suiteCommonAliases,
...trezorPackagesAliases,
};
};

const commitId = execSync('git rev-parse HEAD').toString().trim();

export default defineConfig({
root: '.',
base: assetPrefix,
// Use suite-data/files as the public directory
publicDir: resolve(__dirname, '../suite-data/files'),
plugins: [
nodePolyfills(),
staticAliasPlugin(),
serveCorePlugin(),
viteCommonjs(),
workerPlugin(),
wasm(),
react({
babel: {
plugins: [
[
'babel-plugin-styled-components',
{
displayName: true,
fileName: false,
},
],
],
},
}),
],
resolve: {
alias: [
{
find: /^@trezor\/connect(\/index)?$/,
replacement: '@trezor/connect-web/src/module',
},
{
find: 'src',
replacement: resolve(__dirname, '../suite/src'),
},
...Object.entries(createWorkspaceAliases()).map(([find, replacement]) => ({
find,
replacement,
})),
],
preserveSymlinks: true,
},
define: {
'process.browser': true,
'process.env.VERSION': JSON.stringify(suiteVersion),
'process.env.COMMIT_HASH': JSON.stringify(commitId),
'process.env.COMMITHASH': JSON.stringify(commitId),
'process.env.SUITE_TYPE': JSON.stringify(project),
'process.env.NODE_ENV': JSON.stringify('development'),
'process.env.ASSET_PREFIX': JSON.stringify(assetPrefix),
__DEV__: true,
ENABLE_REDUX_LOGGER: true,
},
optimizeDeps: {
include: ['@trezor/connect', '@trezor/suite', 'buffer'],
exclude: [
// Exclude WebAssembly modules
'@trezor/crypto-utils',
'@trezor/utxo-lib',
],
esbuildOptions: {
define: {
global: 'globalThis',
},
plugins: [
{
name: 'commonjs',
setup(build) {
build.onLoad({ filter: /\.js$/ }, args => {
if (args.path.includes('packages/')) {
return {
format: 'cjs',
loader: 'js',
};
}
});
},
},
],
},
},
server: {
port: 8000,
open: true,
host: true,
},
});
Loading

0 comments on commit ff1fe99

Please sign in to comment.