Skip to content

Instantly share code, notes, and snippets.

@mecachisenros
Created November 30, 2023 02:21
Show Gist options
  • Save mecachisenros/12e3240a5d66cffa54089066f9ffe8ff to your computer and use it in GitHub Desktop.
Save mecachisenros/12e3240a5d66cffa54089066f9ffe8ff to your computer and use it in GitHub Desktop.
Socket Supply + Vite = ♥️

Socket Supply + Vite = ♥️

Disclaimer: this guide might have been written under the influence of a few glasses of wine (what a lovely Pinotage), do NOT take the author's piss taking and opinions seriously.

TL;DR

Native apps for noobs (aka web devs, aka me)

Intro

Whether you are a [insert your framework/library of choice here] or vanilla JS lover, we can all agree (or not, you're entitled to your own opinion, me personally, don't care about it) that Vite has become a sort of de facto build/bundling tool (if you are still maintaining some dreaded webpack config that nobody knows wtf is actually doing, I feel for you). Built by a fantastic community, it's got plugins for almost anything, it's fast, it's simple, and it's awesome.

Wouldn't it be great if we could use it to take our apps beyond the browser, in a neat simple way? Well, thanks to Socket Supply and its Socket Runtime now we can (it's not like we couldn't before but for a long time all the options sucked, it wasn't simple, it was far from neat, not to point out names but remember Cordova, they have gotten better... but meh).

The Socket Runtime it's a runtime (duh 🙄) that allows you to build and run your app natively, on Desktop and Mobile. Still in development stages but it's already pretty effing sick, to make it plain without listing all the bels and whistles (I might get slapped for the next plain description), think of Electron but without the Chromium overhead (as I'm writing this, Discord is at ~800MB of RAM usage, just sitting there in the background, neat 😒) optional native extension API (i.e. build/bring your own stuff), bring your own backend, AND an actual, optional, peer to peer network, but let's not get into that or we might end up in a never ending debate, followed by throwing chairs at each other across the room.

Blah blah blah, whatever, stfu and get to the point.

The reason you are here

We're not gonna build a real app (if you are here I assume you know wtf you are doing and building an app is not the subject of this little guide) we'll build a bare Hello World demo app, using Vite as our bundler/build tool, and Socket Supply as our runtime. We'll use React (which I'm not a fan of and dread a little bit to be honest but hey, we can't all be perfect) but you can use whatever you want, it's all the same.

Prerequisites

  • Node.js: FYI, you can use the Socket Runtime without node if your app doesn't need bundling/build tools but that's not why you are reading this, I'm assuming you are a React and TypeScript lover and feel the pains of React Native (as I said, we can't all be perfect)
  • Socket Supply CLI (ssc): based on the assumption above do a npm i @socketsupply/socket -g or pnpm i @socketsupply/socket -g (I'll be using pnpm because like you, I also got opinions) here's the docs if you need them, there's a nodeless install option

Scaffold the app

Start by scaffolding a new React app using Vite's template, I'll call it socket-supplied-react because I'm a noob and naming is hard.

pnpm create vite@latest --template react-ts socket-supplied-react

cd into the project's directory and install dependencies.

cd socket-supplied-react && pnpm i

The project's file structure will look something like the below tree:

.
├── README.md
├── index.html
├── package.json
├── public
│   └── vite.svg
├── src
│   ├── App.css
│   ├── App.tsx
│   ├── assets
│   │   └── react.svg
│   ├── index.css
│   ├── main.tsx
│   └── vite-env.d.ts
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts

Initialize Socket Runtime

Initialize the Socket Runtime in the project's directory, it will create a socket.ini configuration file.

ssc init

The configuration file has quite a few options, (don't stress, but do read the great inline documentation from the lovely people at Socket Supply) we'll only need to change a couple of them:

  • first, we'll tell the Socket Runtime where our source code (aka app) will be located, by default it's src but we'll change it to dist (or whatever tickles your fancy, except build, if you want to use build make sure to also update Socket's output option to something else) which is where we'll tell Vite to spit the bundled code.
  • second, we'll tell it to run a build script before starting the runtime, we'll use pnpm build since Vite is bundling our code.
[build]

; ssc will copy everything in this directory to the build output directory.
; This is useful when you want to avoid bundling or want to use tools like
; vite, webpack, rollup, etc. to build your project and then copy output to
; the Socket bundle resources directory.
; default value: "src"
copy = "dist" 👈

...

; The build script. It runs before the `[build] copy` phase.
script = "pnpm build" 👈

; IMPORTANT NOTE: if you are using `build` as the `copy` directory, make sure to change
; the `output` directory option below to something else, copy and output CANNOT clash.
output = "build" 👈

...

Configure Vite

Tell Vite to spit the bundled code in the dist directory.

// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

export default defineConfig({
  plugins: [react()],
  build: {
    outDir: 'dist', 👈
  },
})

Build and run

We're ready to build and run our app, from the project's root directory run:

ssc build --run

or the shorthand

ssc build -r

And just like that your React app is now native, I know, mind effing blowing.

What about live reload?

Socket Runtime's got it, and it's enabled by default, just run:

ssc build --run --watch

or the shorthand

ssc build -r -w

Socket Runtime will watch for changes in your source code, run your build script and reload the app when it detects them.

What about Hot Module Replacement (HMR)?

What if your app is so awesome (and big) that you don't want to rebuild it every time you make a change? Well, Socket Runtime's got a workaround for that too

  • first, start Vite's dev server.
pnpm dev
  • second, in a second terminal start Socket Runtime providing a host and port for the dev server, host is optional, if not provided it will default to localhost.
ssc build --run --host=localhost --port=5173

or the shorthand

ssc build -r --host=localhost --port=5173

Socket Runtime will proxy Vite's dev server, you can now make changes to your source code and see them reflected in the app without rebuilding it.

What about native capabilities?

You are ready to add some native capabilities to your app, like access to the file system, or find out the host's OS platform/architecture, guess what, Socket Runtime's got it too.

Socket Runtime provides a bunch of native bindings available to your app via the 'socket:*' modules like socket:fs or socket:os, you can find a list here. Think of those as a sort of virtual/dynamic/aliased modules that only exists within the Socket Runtime, they are not part of your app's source code, they are provided by the Socket Runtime, and they are only available when building/running your app with the Socket Runtime.

Before we can use them we need to tell Vite those modules are external. Vite has a config option which allows us to specify external dependencies, we'll use a regex to match all the socket:* modules. At the time of writing, this config option is only available when building/bundling your app, not when running the dev server, but as I said Vite's got a great community and some lovely people have already took the time and effort to scratch our itch, so go and share some love to the authors of vite-plugin-externalize-dependencies

Once you've finished sharing the love, lets install the plugin.

pnpm i -D vite-plugin-externalize-dependencies

And configure Vite to externalise the socket:* modules for both build and dev server.

// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import externalize from 'vite-plugin-externalize-dependencies' 👈

export default defineConfig({
  plugins: [
    externalize({
      externals: [/^socket:.*/], 👈
    }),
    react(),
  ],
  build: {
    outDir: 'dist',
    rollupOptions: {
      external: [/^socket:.*/], 👈
    },
  },
})

Now we can import and use the socket:* modules in our app.

// src/App.tsx
import { useState } from "react";
import reactLogo from "./assets/react.svg";
import viteLogo from "/vite.svg";
import "./App.css";

import os from 'socket:os' 👈

...

      <div className="card">
        <button onClick={() => setCount((count) => count + 1)}>
          count is {count}
        </button>
        <p>
          You are running this application on <b>{os.platform()}</b> platform. 👈
        </p>
      </div>
...

That's enough rambling

I'm very excited about the Socket Runtime, if you are too, go and send some love to the lovely folks at Socket Supply.

@ansarizafar
Copy link

@mecachisenros I have exactly followed above mentioned instructions the only difference is that I am using Imba but I am still unable to import socket modules.
image

Here is my vite config

import imba from 'imba/plugin';
import { defineConfig } from 'vite';
import externalize from 'vite-plugin-externalize-dependencies'

export default defineConfig({
	plugins: [
	imba(),
	externalize({
	externals: [/^socket:.*/], 
  }),
	],
	/*
	resolve: {
		dedupe:['imba']
	  },
	  */
	build: {
		//outDir: 'dist', 
		rollupOptions: {
		 	external(id) {
			  return id.startsWith('socket:')
			}
			
		//external: [/^socket:.*/],
		  }
	  },
});

@mecachisenros
Copy link
Author

@ansarizafar here's what I've done to scaffold an imba project with vite:

  1. pnpx imba create socket-supplied-imba
  2. select the Vite - Client only application (Vite bundler) option
  3. pnpm i @socketsupply/socket
  4. pnpm i -D vite-plugin-externalize-dependencies
  5. update vite.config.js as follows:
import imba from "imba/plugin";
import { defineConfig } from "vite";
import externalize from "vite-plugin-externalize-dependencies";

export default defineConfig({
  plugins: [
    externalize({
      externals: [/^socket:.*/],
    }),
    imba(),
  ],
  build: {
    outDir: "dist",
    rollupOptions: {
      external: [/^socket:.*/],
    },
  },
});
  1. ssc init
  2. update copy and script options for ssc's build block configuration:
copy = "dist"
script = "pnpm build"
  1. update src/main.imba as follows:
global css body c:warm2 bg:warm8 ff:Arial inset:0 d:vcc

import os from 'socket:os'

tag app
	count = 0
	<self>
		<p> "Hiya, you are running the Socket Runtime on the {os.platform!} platform with {os.arch!} architecture."
		<%counter @click=count++>
			css e:250ms us:none py:3 px:5 rd:4 bg:gray9 d:hcc g:1
				bd:1px solid transparent @hover:indigo5
			<img[s:20px] src="https://imba.io/logo.svg">
			"count is {count}"

imba.mount <app>
  • do ssc build -r -w to run ssc in watch mode (live reload)
  • do pnpm dev and ssc build -r --port=5173 in two different terminals to run in proxy mode

I've put it together on StackBlitz here https://stackblitz.com/edit/vitejs-vite-pq4mx7?file=main.imba ssc won't run there but you should be able to download the template and run it locally.

Screenshot 2023-12-01 at 10 49 16

@ansarizafar
Copy link

I have exactly followed same steps. I am still not able to import socket modules in proxy mode. live reload mode is working fine. Why socket.ini file is empty on stackblitz?

@mecachisenros
Copy link
Author

I've updated socket.ini I must have forgot to save it.

I don't why is not working in your case, if you can share a repo I'll take a look.

@ansarizafar
Copy link

Socket team is working for a fix for Ubuntu https://discord.com/channels/775715380089716778/1179656402067783763/1180249339411578981 Thanks for the help

@rorymcd98
Copy link

I had a problem where the typescript compiler (tsc for anyone googling) was giving TSC 2307 Cannot find module for socket: dependcies, despite setting the module resolver to the bundler.

Adding the following to a *.d.ts file (such as vite-env.d.ts) got rid of this.
declare module "socket:*"

Weirdly I think this bug started happening out of nowhere. Maybe there was a typescript update last night.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment