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

Browser plugin preview #2748

Merged
merged 30 commits into from
Jul 26, 2024
Merged

Browser plugin preview #2748

merged 30 commits into from
Jul 26, 2024

Conversation

macintoshhelper
Copy link
Contributor

@macintoshhelper macintoshhelper commented May 13, 2024

Why does this PR exist?

Closes #2709

There is now an optional web preview mode for development, which starts an Express.js server locally with a WebSockets server. The plugin UI forwards messages between the controller and the browser version.

What does this pull request do?

  • Browser dev preview mode – npm run preview:browser + npm run preview:plugin
  • React Fast Refresh in web (HMR)
  • npm run build + npm run serve working
  • Small startup refactor to allow the plugin to run outside of the Figma sandbox/UI (entrypoint render). STARTUP handler is moved to useEffect in the startup component.

yarn build + yarn serve

image

yarn preview:browser

image

yarn preview:plugin

image

Testing this change

Start WebSockets server and webpack-dev-server for web preview and HMR

npm run preview:browser

Start Figma plugin dev

npm run preview:plugin

Additional Notes (if any)

May need to reload the plugin in Figma if STARTUP runs too early. Am thinking to implement a sort of dev SDK preview window in the browser, with the ability to simulate STARTUP with custom data, would need something like this to work in a Next.js preview and handle the case of having no WebSocket server or Figma plugin environment running

The preview:browser script is running an Express server with a WebSockets server, so npm run serve isn't working yet.

Plugin UI is disabled when PREVIEW_MODE/browser mode is active.

Currently Fast Refresh requires stopping the plugin preview in the command line, to prevent STARTUP from restarting the browser plugin UI.


Unrelated change, but I added dev npm packages for BundleAnalyzerPlugin and SpeedMeasurePlugin to make debugging build time/bundle size easier when needed (pass Webpack arg). These affect build times so are disabled by default.

Copy link

changeset-bot bot commented May 13, 2024

⚠️ No Changeset found

Latest commit: dce52fb

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@macintoshhelper macintoshhelper marked this pull request as ready for review May 16, 2024 12:57
Copy link
Collaborator

@six7 six7 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice overall! let's hold off merging this until 2.0 is out, too much risk (and its the basis for devex anyway)

@@ -26,6 +26,50 @@ export type AsyncMessageChannelHandlers = {
>
};

const WEBSOCKET_SERVER_URL = 'ws://localhost:9001/ws';
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could we move this to a dedicated file? as in just import those functions here - to keep things cleaner?

packages/tokens-studio-for-figma/src/app/index.tsx Outdated Show resolved Hide resolved
developer-knowledgebase/async-message-channel.md Outdated Show resolved Hide resolved
const possiblePromise = callback(event.data.pluginMessage);
if (possiblePromise === false || (possiblePromise && await possiblePromise === false)) {
window.removeEventListener('message', listener);
private startWebSocketConnection() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lots of these functions feel like we should move them to either a shared websocket file or their own files alltogether in order to improve maintainabiltiy

const possiblePromise = callback(msg);
if (possiblePromise === false || (possiblePromise && await possiblePromise === false)) {
figma.ui.off('message', listener);
switch (this.environment) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any way where we can make this function overall more readable? lots of nested ifs and switch statements 🤔

Copy link
Contributor

github-actions bot commented Jul 4, 2024

Commit SHA:409d986497c81348ca28f85565525d40d5506dc4

Test coverage results 🧪

Code coverage diff between base branch:release-139 and head branch: feat/browser-plugin-sync 
Status File % Stmts % Branch % Funcs % Lines
🔴 total 66.85 (-0.96) 57.86 (-0.86) 63.78 (-0.87) 67.21 (-1.01)
🔴 packages/tokens-studio-for-figma/src/AsyncMessageChannel.ts 98 (0.05) 94.11 (-2.76) 100 (0) 97.91 (0.04)
✨ 🆕 packages/tokens-studio-for-figma/src/AsyncMessageChannelPreview.ts 52.75 45.94 59.25 52
🔴 packages/tokens-studio-for-figma/src/app/components/Initiator.tsx 73.11 (-12.22) 65.78 (-4.8) 66.66 (-33.34) 72.52 (-12.41)
🔴 packages/tokens-studio-for-figma/src/app/store/updateSources.tsx 15.38 (-0.62) 27.27 (-2.73) 33.33 (0) 16 (-0.66)
🟢 packages/tokens-studio-for-figma/src/plugin/controller.ts 98.03 (0.03) 100 (0) 75 (0) 100 (0)
✨ 🆕 packages/tokens-studio-for-figma/src/plugin/asyncMessageHandlers/preview.ts 33.33 100 0 33.33

Copy link
Contributor

github-actions bot commented Jul 4, 2024

Commit SHA:409d986497c81348ca28f85565525d40d5506dc4
Current PR reduces the test coverage percentage by 1 for some tests

Copy link
Contributor

@cuserox cuserox left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIIICE!

General questions for my understanding, and a couple of code simplification suggestions. Let me know what you think!

I haven't played around much with it in the browser, will add any comments if there's breakages.

@@ -11,6 +11,8 @@
"build:dev": "cross-env NODE_ENV=development webpack --mode=development",
"build:cy": "cross-env LAUNCHDARKLY_FLAGS=tokenThemes,gitBranchSelector,multiFileSync,tokenFlowButton yarn build",
"start": "cross-env webpack --mode=development --watch",
"preview:plugin": "cross-env webpack --mode=development --PREVIEW_ENV=figma --watch",
"preview:browser": "cross-env WEBSOCKETS_PORT=9001 node preview-server.js & webpack-dev-server --mode=development --PREVIEW_ENV=browser",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add descriptions on the README (or sibling doc in /developer-knowledgebase) on how to run the plugin in the browser?

Similar to the PR description, stating the order of commands and possible hiccups like I had 🙈

app.use(express.static(__dirname + '/dist'))

app.get("/", (req, res) => {
// res.sendFile(path.join(__dirname, 'dist', 'index.html'));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Accidentally commented out?

});

// send immediatly a feedback to the incoming connection
// ws.send('Hi there, I am a WebSocket server');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be updated or removed?

@@ -142,3 +144,7 @@ export class AsyncMessageChannel {
return promise;
}
}

const ExportedAsyncMessageChannel = !process.env.PREVIEW_ENV ? AsyncMessageChannel : AsyncMessageChannelPreview;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const ExportedAsyncMessageChannel = !process.env.PREVIEW_ENV ? AsyncMessageChannel : AsyncMessageChannelPreview;
const ExportedAsyncMessageChannel = process.env.PREVIEW_ENV ? AsyncMessageChannelPreview : AsyncMessageChannel;

Could we turn the ternary around? Feels weird having a negation to start with.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of curiosity: what are the possible values of process.env.PREVIEW_ENV? browser | figma...?

Comment on lines 6 to 29
// credits goes to https://github.com/microsoft/TypeScript/issues/23182#issuecomment-379091887
type IsTypeOnlyObject<Obj extends Record<PropertyKey, unknown>> = [keyof Obj] extends ['type'] ? true : false;

type IncomingMessageEvent<Message = unknown> = {
data: {
pluginMessage: {
id: string
message: Message
} | {
id: string
error: unknown
}
}
};

export type AsyncMessageChannelHandlers = {
[K in AsyncMessageTypes]: (incoming: AsyncMessagesMap[K]) => Promise<
IsTypeOnlyObject<AsyncMessageResultsMap[K]> extends true
? void
: Omit<AsyncMessageResultsMap[K], 'type'>
>
};

const WEBSOCKET_SERVER_URL = 'ws://localhost:9001/ws';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are the same types as used AsyncMessageChannel. Why not create a sub-folder containing this file + its counterpart, and perhaps a shared.ts file with the shared types / constants?

Saves a bit of duplication.

import { INTERNAL_THEMES_NO_GROUP } from './constants/InternalTokenGroup';
import {
AsyncMessageTypes, GetThemeInfoMessageResult,
} from './types/AsyncMessages';

describe('Testing the mock functionality of the AsyncMessageChannel', () => {
it('should be able to communicate between UI and plugin', async () => {
const runAfter: (() => void)[] = [];
const runAfter: ((() => void) | null)[] = [];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could save () => void) | null as a type and avoid having to update it everywhere if it changes!

module.exports = (env, argv) => {
const wrapper = (callback) => {
const measureSpeed = false;
if (!measureSpeed) { // Set to true to enable SpeedMeasurePlugin (breaks Figma UI build)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this comment be more thorough?

If not here, perhaps in a doc section to go through the why's and how's 😄


```

-
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Accidental bullet point

E -->|"ReactInstance.handle(...)"| D
E -->|"sendMessageToBrowser\n(ws.send(...))"| F
F -->|"sendMessageFromBrowser\n(ws.send(...))"| E
```
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great diagram!!! 🎉

Static instances of `AsyncMessageChannel` are initialised when the class is loaded:

- `PluginInstance` - used from inside of `controller` entrypoint
- `ReactInstance` - used from inside of `ui` entrypoint
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth mentioning what inFigmaSandbox means?

Copy link
Contributor

github-actions bot commented Jul 26, 2024

⤵️ 📦 ✨ The artifact was successfully created! Want to test it? Download it here 👀 🎁

* fix AsyncMessageChannelPreview undefined error + export WS URI

* browser preview CSS file for UI fixes

* create previewUtils for browser color scheme + setFigmaBrowserTheme

* browser preview URL params + fullscreen/theme/action modes

* two bug fixes for browser/plugin websocket preview bridge

* add preview dist folder for web preview builds

* [WIP] browser preview dev knowledge docs

* feat(dev): request startup on browser preview page open

* refactor(dev): use env vars for browser preview ws src

* fix(debug): remove console.log from asyncmessagechannelpreview

* fix(css): figmaloading full height css for browser preview

* refactor(dev): use enums for websockets src in browser preview tsx

* fix(dev): remove comments

* refactor: reuse htmlClassList variable

* remove unused package

---------

Co-authored-by: macintoshhelper <[email protected]>
@six7 six7 changed the base branch from release-139 to dx July 26, 2024 17:52
@six7 six7 merged commit 90906fd into dx Jul 26, 2024
7 of 8 checks passed
@six7 six7 deleted the feat/browser-plugin-sync branch July 26, 2024 18:06
cuserox pushed a commit that referenced this pull request Aug 22, 2024
* [WIP] webpack config setup for fast refresh + websocket server

* add prod/dev hmr webpack config option

* render immediately and handle AsyncMessage in startup app hook

* forward ui AsyncMessages to browser via WebSockets

* null check sentry transaction to fix browser error

* refactor AsyncMessageChannel code for browser implementation

* webpack use swc-loader for browser version + speed/bundle size plugin options

* prettify browser dev preview UI

* enable loading screen if startup params missing (for web serve + disconnected browser dev preview)

* attempt to fix webpack build for tests

* add SpeedMeasurePlugin package

* create AsyncMessageChannel dev docs

* replace web-preview.md ASCII data flow diagram with mermaid

* use radii/spacing tokens instead of px for web preview.tsx styles

Co-authored-by: Jan Six <[email protected]>

* remove commented out startup handler (handled in startup.tsx useEffect now)

Co-authored-by: Jan Six <[email protected]>

* remove commented out code

Co-authored-by: Jan Six <[email protected]>

* replace px values with tokens

Co-authored-by: Jan Six <[email protected]>

* conditional export for AsyncMessageChannel preview env

* add browser preview WEBSOCKETS_PORT env

* fix typescript issue with PreviewAsyncMessageChannel.isWsConnected

* add test coverage for AsyncMessageChannelPreview

* Browser preview debug UI (#2803)

* fix AsyncMessageChannelPreview undefined error + export WS URI

* browser preview CSS file for UI fixes

* create previewUtils for browser color scheme + setFigmaBrowserTheme

* browser preview URL params + fullscreen/theme/action modes

* two bug fixes for browser/plugin websocket preview bridge

* add preview dist folder for web preview builds

* [WIP] browser preview dev knowledge docs

* feat(dev): request startup on browser preview page open

* refactor(dev): use env vars for browser preview ws src

* fix(debug): remove console.log from asyncmessagechannelpreview

* fix(css): figmaloading full height css for browser preview

* refactor(dev): use enums for websockets src in browser preview tsx

* fix(dev): remove comments

* refactor: reuse htmlClassList variable

* remove unused package

---------

Co-authored-by: macintoshhelper <[email protected]>

---------

Co-authored-by: macintoshhelper <[email protected]>
Co-authored-by: Jan Six <[email protected]>
macintoshhelper added a commit that referenced this pull request Oct 24, 2024
* Browser plugin preview (#2748)

* [WIP] webpack config setup for fast refresh + websocket server

* add prod/dev hmr webpack config option

* render immediately and handle AsyncMessage in startup app hook

* forward ui AsyncMessages to browser via WebSockets

* null check sentry transaction to fix browser error

* refactor AsyncMessageChannel code for browser implementation

* webpack use swc-loader for browser version + speed/bundle size plugin options

* prettify browser dev preview UI

* enable loading screen if startup params missing (for web serve + disconnected browser dev preview)

* attempt to fix webpack build for tests

* add SpeedMeasurePlugin package

* create AsyncMessageChannel dev docs

* replace web-preview.md ASCII data flow diagram with mermaid

* use radii/spacing tokens instead of px for web preview.tsx styles

Co-authored-by: Jan Six <[email protected]>

* remove commented out startup handler (handled in startup.tsx useEffect now)

Co-authored-by: Jan Six <[email protected]>

* remove commented out code

Co-authored-by: Jan Six <[email protected]>

* replace px values with tokens

Co-authored-by: Jan Six <[email protected]>

* conditional export for AsyncMessageChannel preview env

* add browser preview WEBSOCKETS_PORT env

* fix typescript issue with PreviewAsyncMessageChannel.isWsConnected

* add test coverage for AsyncMessageChannelPreview

* Browser preview debug UI (#2803)

* fix AsyncMessageChannelPreview undefined error + export WS URI

* browser preview CSS file for UI fixes

* create previewUtils for browser color scheme + setFigmaBrowserTheme

* browser preview URL params + fullscreen/theme/action modes

* two bug fixes for browser/plugin websocket preview bridge

* add preview dist folder for web preview builds

* [WIP] browser preview dev knowledge docs

* feat(dev): request startup on browser preview page open

* refactor(dev): use env vars for browser preview ws src

* fix(debug): remove console.log from asyncmessagechannelpreview

* fix(css): figmaloading full height css for browser preview

* refactor(dev): use enums for websockets src in browser preview tsx

* fix(dev): remove comments

* refactor: reuse htmlClassList variable

* remove unused package

---------

Co-authored-by: macintoshhelper <[email protected]>

---------

Co-authored-by: macintoshhelper <[email protected]>
Co-authored-by: Jan Six <[email protected]>

* Browser plugin preview (#2748)

* [WIP] webpack config setup for fast refresh + websocket server

* add prod/dev hmr webpack config option

* render immediately and handle AsyncMessage in startup app hook

* forward ui AsyncMessages to browser via WebSockets

* null check sentry transaction to fix browser error

* refactor AsyncMessageChannel code for browser implementation

* webpack use swc-loader for browser version + speed/bundle size plugin options

* prettify browser dev preview UI

* enable loading screen if startup params missing (for web serve + disconnected browser dev preview)

* attempt to fix webpack build for tests

* add SpeedMeasurePlugin package

* create AsyncMessageChannel dev docs

* replace web-preview.md ASCII data flow diagram with mermaid

* use radii/spacing tokens instead of px for web preview.tsx styles

Co-authored-by: Jan Six <[email protected]>

* remove commented out startup handler (handled in startup.tsx useEffect now)

Co-authored-by: Jan Six <[email protected]>

* remove commented out code

Co-authored-by: Jan Six <[email protected]>

* replace px values with tokens

Co-authored-by: Jan Six <[email protected]>

* conditional export for AsyncMessageChannel preview env

* add browser preview WEBSOCKETS_PORT env

* fix typescript issue with PreviewAsyncMessageChannel.isWsConnected

* add test coverage for AsyncMessageChannelPreview

* Browser preview debug UI (#2803)

* fix AsyncMessageChannelPreview undefined error + export WS URI

* browser preview CSS file for UI fixes

* create previewUtils for browser color scheme + setFigmaBrowserTheme

* browser preview URL params + fullscreen/theme/action modes

* two bug fixes for browser/plugin websocket preview bridge

* add preview dist folder for web preview builds

* [WIP] browser preview dev knowledge docs

* feat(dev): request startup on browser preview page open

* refactor(dev): use env vars for browser preview ws src

* fix(debug): remove console.log from asyncmessagechannelpreview

* fix(css): figmaloading full height css for browser preview

* refactor(dev): use enums for websockets src in browser preview tsx

* fix(dev): remove comments

* refactor: reuse htmlClassList variable

* remove unused package

---------

Co-authored-by: macintoshhelper <[email protected]>

---------

Co-authored-by: macintoshhelper <[email protected]>
Co-authored-by: Jan Six <[email protected]>

* update web preview script docs

* fix: updateStyles error from merge conflict

* lint: remove comments and cleanup

* fix: language switch on startup for new preview startup

* lint: remove more comments from preview files

* lint: fix indentation error because of prettier conflict

---------

Co-authored-by: macintoshhelper <[email protected]>
Co-authored-by: Jan Six <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants