Skip to content

Commit cec2514

Browse files
Merge pull request Expensify#44432 from margelo/feat/release-profiler-on-web
feat: release-profiler on web
2 parents 485332d + 6f7f259 commit cec2514

26 files changed

+693
-77
lines changed

.github/workflows/platformDeploy.yml

+12
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,12 @@ jobs:
179179
env:
180180
GITHUB_TOKEN: ${{ github.token }}
181181

182+
- name: Archive desktop sourcemaps
183+
uses: actions/upload-artifact@v3
184+
with:
185+
name: desktop-sourcemap-${{ github.ref_name }}
186+
path: desktop/dist/www/merged-source-map.js.map
187+
182188
iOS:
183189
name: Build and deploy iOS
184190
needs: validateActor
@@ -348,6 +354,12 @@ jobs:
348354
env:
349355
S3_URL: s3://${{ fromJSON(env.SHOULD_DEPLOY_PRODUCTION) && '' || 'staging-' }}expensify-cash
350356

357+
- name: Archive web sourcemaps
358+
uses: actions/upload-artifact@v3
359+
with:
360+
name: web-sourcemap-${{ github.ref_name }}
361+
path: dist/merged-source-map.js.map
362+
351363
- name: Purge Cloudflare cache
352364
run: /home/runner/.local/bin/cli4 --verbose --delete hosts=["${{ fromJSON(env.SHOULD_DEPLOY_PRODUCTION) && '' || 'staging.' }}new.expensify.com"] /zones/:9ee042e6cfc7fd45e74aa7d2f78d617b/purge_cache
353365
env:

README.md

+9-7
Original file line numberDiff line numberDiff line change
@@ -230,19 +230,20 @@ Within Xcode head to the build phase - `Bundle React Native code and images`.
230230
```jsx
231231
npm i && npm run pod-install
232232
```
233-
7. Depending on the platform you are targeting, run your Android/iOS app in production mode.
234-
8. Upon completion, the generated source map can be found at:
233+
4. Depending on the platform you are targeting, run your Android/iOS app in production mode.
234+
5. Upon completion, the generated source map can be found at:
235235
Android: `android/app/build/generated/sourcemaps/react/productionRelease/index.android.bundle.map`
236236
IOS: `main.jsbundle.map`
237+
web: `dist/merged-source-map.js.map`
237238

238239
### Recording a Trace:
239240
1. Ensure you have generated the source map as outlined above.
240241
2. Launch the app in production mode.
241-
2. Navigate to the feature you wish to profile.
242-
3. Initiate the profiling session by tapping with four fingers to open the menu and selecting **`Use Profiling`**.
243-
4. Close the menu and interact with the app.
244-
5. After completing your interactions, tap with four fingers again and select to stop profiling.
245-
6. You will be presented with a **`Share`** option to export the trace, which includes a trace file (`Profile<app version>.cpuprofile`) and build info (`AppInfo<app version>.json`).
242+
3. Navigate to the feature you wish to profile.
243+
4. Initiate the profiling session by tapping with four fingers (on mobile) or `cmd+d` (on web) to open the menu and selecting **`Use Profiling`**.
244+
5. Close the menu and interact with the app.
245+
6. After completing your interactions, tap with four fingers or `cmd+d` again and select to stop profiling.
246+
7. You will be presented with a **`Share`** option to export the trace, which includes a trace file (`Profile<app version>.cpuprofile`) and build info (`AppInfo<app version>.json`).
246247

247248
Build info:
248249
```jsx
@@ -265,6 +266,7 @@ Build info:
265266
4. Use the following commands to symbolicate the trace for Android and iOS, respectively:
266267
Android: `npm run symbolicate-release:android`
267268
IOS: `npm run symbolicate-release:ios`
269+
web: `npm run symbolicate-release:web`
268270
5. A new file named `Profile_trace_for_<app version>-converted.json` will appear in your project's root folder.
269271
6. Open this file in your tool of choice:
270272
- SpeedScope ([https://www.speedscope.app](https://www.speedscope.app/))

config/webpack/webpack.dev.ts

+4
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ const getConfiguration = (environment: Environment): Promise<Configuration> =>
5353
cert: path.join(__dirname, 'certificate.pem'),
5454
},
5555
},
56+
headers: {
57+
// eslint-disable-next-line @typescript-eslint/naming-convention
58+
'Document-Policy': 'js-profiling',
59+
},
5660
},
5761
plugins: [
5862
new DefinePlugin({

desktop/electron-serve.ts

+106
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/* eslint-disable @typescript-eslint/no-misused-promises */
2+
3+
/* eslint-disable rulesdir/no-negated-variables */
4+
5+
/* eslint-disable @lwc/lwc/no-async-await */
6+
7+
/**
8+
* This file is a modified version of the electron-serve package.
9+
* We keep the same interface, but instead of file protocol we use buffer protocol (with support of JS self profiling).
10+
*/
11+
import type {BrowserWindow, Protocol} from 'electron';
12+
import {app, protocol, session} from 'electron';
13+
import fs from 'fs';
14+
import path from 'path';
15+
16+
type RegisterBufferProtocol = Protocol['registerBufferProtocol'];
17+
type HandlerType = Parameters<RegisterBufferProtocol>[1];
18+
type Optional<T> = T | null | undefined;
19+
20+
const FILE_NOT_FOUND = -6;
21+
22+
const getPath = async (filePath: string): Promise<Optional<string>> => {
23+
try {
24+
const result = await fs.promises.stat(filePath);
25+
26+
if (result.isFile()) {
27+
return filePath;
28+
}
29+
30+
if (result.isDirectory()) {
31+
// eslint-disable-next-line @typescript-eslint/return-await
32+
return getPath(path.join(filePath, 'index.html'));
33+
}
34+
} catch {
35+
return null;
36+
}
37+
};
38+
39+
type ServeOptions = {
40+
directory: string;
41+
isCorsEnabled?: boolean;
42+
scheme?: string;
43+
hostname?: string;
44+
file?: string;
45+
partition?: string;
46+
};
47+
48+
export default function electronServe(options: ServeOptions) {
49+
const mandatoryOptions = {
50+
isCorsEnabled: true,
51+
scheme: 'app',
52+
hostname: '-',
53+
file: 'index',
54+
...options,
55+
};
56+
57+
if (!mandatoryOptions.directory) {
58+
throw new Error('The `directory` option is required');
59+
}
60+
61+
mandatoryOptions.directory = path.resolve(app.getAppPath(), mandatoryOptions.directory);
62+
63+
const handler: HandlerType = async (request, callback) => {
64+
const filePath = path.join(mandatoryOptions.directory, decodeURIComponent(new URL(request.url).pathname));
65+
const resolvedPath = (await getPath(filePath)) ?? path.join(mandatoryOptions.directory, `${mandatoryOptions.file}.html`);
66+
67+
try {
68+
const data = await fs.promises.readFile(resolvedPath);
69+
callback({
70+
mimeType: 'text/html',
71+
data: Buffer.from(data),
72+
headers: {
73+
// eslint-disable-next-line @typescript-eslint/naming-convention
74+
'Document-Policy': 'js-profiling',
75+
},
76+
});
77+
} catch (error) {
78+
callback({error: FILE_NOT_FOUND});
79+
}
80+
};
81+
82+
protocol.registerSchemesAsPrivileged([
83+
{
84+
scheme: mandatoryOptions.scheme,
85+
privileges: {
86+
standard: true,
87+
secure: true,
88+
allowServiceWorkers: true,
89+
supportFetchAPI: true,
90+
corsEnabled: mandatoryOptions.isCorsEnabled,
91+
},
92+
},
93+
]);
94+
95+
app.on('ready', () => {
96+
const partitionSession = mandatoryOptions.partition ? session.fromPartition(mandatoryOptions.partition) : session.defaultSession;
97+
98+
partitionSession.protocol.registerBufferProtocol(mandatoryOptions.scheme, handler);
99+
});
100+
101+
// eslint-disable-next-line @typescript-eslint/naming-convention
102+
return async (window_: BrowserWindow, searchParameters?: URLSearchParams) => {
103+
const queryString = searchParameters ? `?${new URLSearchParams(searchParameters).toString()}` : '';
104+
await window_.loadURL(`${mandatoryOptions.scheme}://${mandatoryOptions.hostname}${queryString}`);
105+
};
106+
}

desktop/main.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import type {BrowserView, MenuItem, MenuItemConstructorOptions, WebContents, Web
33
import contextMenu from 'electron-context-menu';
44
import log from 'electron-log';
55
import type {ElectronLog} from 'electron-log';
6-
import serve from 'electron-serve';
76
import {autoUpdater} from 'electron-updater';
87
import {machineId} from 'node-machine-id';
98
import checkForUpdates from '@libs/checkForUpdates';
@@ -14,6 +13,7 @@ import type {TranslationPaths} from '@src/languages/types';
1413
import type PlatformSpecificUpdater from '@src/setup/platformSetup/types';
1514
import type {Locale} from '@src/types/onyx';
1615
import type {CreateDownloadQueueModule, DownloadItem} from './createDownloadQueue';
16+
import serve from './electron-serve';
1717
import ELECTRON_EVENTS from './ELECTRON_EVENTS';
1818

1919
const createDownloadQueue = require<CreateDownloadQueueModule>('./createDownloadQueue').default;

desktop/package-lock.json

-17
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

desktop/package.json

-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
"dependencies": {
77
"electron-context-menu": "^2.3.0",
88
"electron-log": "^4.4.8",
9-
"electron-serve": "^1.3.0",
109
"electron-updater": "^6.2.1",
1110
"node-machine-id": "^1.1.12"
1211
},

0 commit comments

Comments
 (0)