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 28, 2025
1 parent d442fc8 commit c536099
Show file tree
Hide file tree
Showing 10 changed files with 792 additions and 194 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"suite:build:web": "yarn workspace @trezor/suite-web build",
"_______ Start Scripts _______": "Here are standalone scripts for running individual applications for development.",
"suite:dev": "yarn workspace @trezor/suite-web dev",
"suite:dev:vite": "yarn workspace @trezor/suite-web dev:vite",
"suite:dev:desktop": "yarn workspace @trezor/suite-desktop dev",
"native:start": "yarn workspace @suite-native/app start",
"_______ Testing _______": "Testing, linting, type checking...",
Expand Down
3 changes: 3 additions & 0 deletions packages/suite-build/configs/web.webpack.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ const config: webpack.Configuration = {
output: {
path: path.join(baseDir, 'build'),
},
resolve: {
fallback: { vm: require.resolve('vm-browserify') },
},
plugins: [
new CopyWebpackPlugin({
patterns: ['browser-detection', 'fonts', 'images', 'oauth', 'videos', 'guide/assets']
Expand Down
11 changes: 10 additions & 1 deletion packages/suite-web/eslint.config.mjs
Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
import { eslint } from '@trezor/eslint';
import { eslint, globalNoExtraneousDependenciesDevDependencies } from '@trezor/eslint';

export default [
...eslint,
{
rules: {
'import/no-default-export': 'off', // Todo: fix and solve
'import/no-extraneous-dependencies': [
'error',
{
devDependencies: [
...globalNoExtraneousDependenciesDevDependencies,
'**/vite.config.ts',
],
},
],
},
},
];
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>
5 changes: 5 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 @@ -28,6 +29,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 @@ -43,10 +45,13 @@
"@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",
"rimraf": "^6.0.1",
"stylelint": "^16.14.1",
"stylelint-config-standard": "^36.0.0",
"tsx": "^4.19.3",
"vite": "^5.1.4",
"vite-plugin-wasm": "^3.4.1",
"webpack": "^5.97.1",
"ws": "^8.18.0",
"yargs": "17.7.2"
Expand Down
1 change: 0 additions & 1 deletion packages/suite-web/src/static/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
<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="<%= assetPrefix %>/static/fonts/fonts.css" />
<link rel="icon" href="<%= assetPrefix %>/static/images/favicons/favicon.png" />
<link rel="apple-touch-icon" href="<%= assetPrefix %>/static/images/favicons/favicon.png" />
Expand Down
12 changes: 12 additions & 0 deletions packages/suite-web/src/vite-index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
* This entry is only used for Vite dev server!
*/

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

export {};
221 changes: 221 additions & 0 deletions packages/suite-web/vite.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
import { viteCommonjs } from '@originjs/vite-plugin-commonjs';
import react from '@vitejs/plugin-react';
import { execSync } from 'child_process';
import { readdirSync } from 'fs';
import { resolve } from 'path';
import { Plugin, ViteDevServer, defineConfig } from 'vite';
import wasm from 'vite-plugin-wasm';

import { suiteVersion } from '../suite/package.json';

// Use require instead of import for TypeScript files
const { assetPrefix, project } = require('../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 = readdirSync(resolve(__dirname, '../../suite-common'), {
withFileTypes: true,
})
.filter(dirent => dirent.isDirectory())
.map(dirent => ({
find: `@suite-common/${dirent.name}`,
replacement: resolve(__dirname, '../../suite-common', dirent.name),
}));

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

return [...suiteCommonAliases, ...trezorPackagesAliases];
};

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

// Plugin to inject Buffer polyfill code at the beginning of every module
const injectBufferPolyfillPlugin = (): Plugin => {
const bufferPolyfillCode = `
// Ensure Buffer is available globally
import { Buffer as ImportedBuffer } from 'buffer';
// Define Buffer in all possible global scopes
if (typeof window !== 'undefined') {
window.Buffer = window.Buffer || ImportedBuffer;
};
if (typeof global !== 'undefined') {
global.Buffer = global.Buffer || ImportedBuffer;
};
if (typeof globalThis !== 'undefined') {
globalThis.Buffer = globalThis.Buffer || ImportedBuffer;
};
// Make sure global is defined
if (typeof window !== 'undefined' && typeof global === 'undefined') {
window.global = window;
};
// Make sure globalThis is defined
if (typeof window !== 'undefined' && typeof globalThis === 'undefined') {
window.globalThis = window;
};
`;

return {
name: 'inject-buffer-polyfill',
transform(code, id) {
// Skip node_modules and non-JS/TS files
if (!id.endsWith('.js') && !id.endsWith('.ts') && !id.endsWith('.tsx')) {
return null;
}

// Inject the Buffer polyfill code at the beginning of the file
return {
code: bufferPolyfillCode + code,
map: null,
};
},
};
};

export default defineConfig({
root: '.',
base: assetPrefix,
// Use suite-data/files as the public directory
publicDir: resolve(__dirname, '../suite-data/files'),
plugins: [
injectBufferPolyfillPlugin(),
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'),
},
{
find: 'crypto',
replacement: require.resolve('crypto-browserify'),
},
{
find: 'buffer',
replacement: require.resolve('buffer'),
},
{
find: 'stream',
replacement: require.resolve('stream-browserify'),
},
{
find: 'vm',
replacement: require.resolve('vm-browserify'),
},
...createWorkspaceAliases(),
],
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),
global: {},
__DEV__: true,
ENABLE_REDUX_LOGGER: true,
},
optimizeDeps: {
include: ['@trezor/connect', '@trezor/suite', 'buffer'],
exclude: [
// Exclude WebAssembly modules
'@trezor/crypto-utils',
'@trezor/utxo-lib',
],
},
server: {
port: 8000,
open: true,
host: true,
},
});
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,7 @@ svgo
sharp
@types/pdfmake
pdfmake
@originjs/vite-plugin-commonjs
vite-plugin-wasm
vite
vm-browserify
Loading

0 comments on commit c536099

Please sign in to comment.