Skip to content

Commit

Permalink
Use near-bos-webcomponent (#143)
Browse files Browse the repository at this point in the history
* uses web component

* outputs html

* incremenet version

* adds deploying to web4 to readme
  • Loading branch information
elliotBraem authored Jul 19, 2024
1 parent 47cecb8 commit 508d255
Show file tree
Hide file tree
Showing 12 changed files with 675 additions and 155 deletions.
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,19 @@ It is easy to build and distribute a custom gateway using the [near-bos-webcompo

The bos-workspace dev server is specially configured with the near-bos-webcomponent to automatically set the `rpc` attribute with the [proxy-rpc](#proxy-rpc).

## Deploying to Web4

If you specify an `index` in your bos.config.json, then bos-workspace will display your widgets through the latest version of [near-bos-webcomponent](https://github.com/nearbuilders/near-bos-webcomponent), or the gateway provided via the `-g` flag.

This involves some html manipulation in order to set the web component's attributes. The html that is created can be found in the designated destination (defaults to `/build`). This html can be used to easily deploy your site with widgets to [web4](https://github.com/vgrichina/web4).

1. Be sure to have deployed a web4 smart contract, such as the [web4-min-contract](https://github.com/vgrichina/web4-min-contract)
2. Move the output index.html to your `/public` or `/dist` if not using a bundler.
3. [TEMP] Remove the rpc and config attributes from `near-social-viewer` element.
4. Run [web4 deploy](https://github.com/vgrichina/web4-deploy) with src being the directory that holds this index.html and the account you have a contract deployed to.

**This is a rough first draft of the implementation and will be improved upon.**

## Commands

You can run `bw` or `bos-workspace` to see the list of commands.
Expand Down
19 changes: 10 additions & 9 deletions lib/dev.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import { readJson, writeJson } from "fs-extra";
import { Gaze } from "gaze";
import path from "path";
import { Server as IoServer } from "socket.io";
import { buildApp } from "./build";
import { BaseConfig, loadConfig } from "./config";
import { startDevServer } from "./server";
import { startSocket } from "./socket";
import { Network } from "./types";
import { loopThroughFiles, readFile } from "./utils/fs";
import { mergeDeep, substractDeep } from "./utils/objects";
import { startFileWatcher } from "./watcher";
import { buildApp } from "@/lib/build";
import { BaseConfig, loadConfig } from "@/lib/config";
import { startDevServer } from "@/lib/server";
import { startSocket } from "@/lib/socket";
import { Network } from "@/lib/types";
import { loopThroughFiles, readFile, readJson, writeJson } from "@/lib/utils/fs";
import { mergeDeep, substractDeep } from "@/lib/utils/objects";
import { startFileWatcher } from "@/lib/watcher";

var appSrcs = [], appDists = [];
var appDevJsons = [];
Expand All @@ -25,6 +24,7 @@ export type DevOptions = {
network?: Network; // network to use
gateway?: string | boolean; // path to custom gateway dist, or false to disable
index?: string; // widget to use as index
output?: string; // output directory
};

/**
Expand Down Expand Up @@ -53,6 +53,7 @@ export async function dev(src: string, dest: string, opts: DevOptions) {
appDists = [dist];
appDevJsons = [devJson];
appDevJsonPath = devJsonPath;
opts.output = dist;
appDevOptions = opts;
const server = startDevServer(appSrcs, appDists, appDevJsonPath, appDevOptions);

Expand Down
122 changes: 88 additions & 34 deletions lib/gateway.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,8 @@

import { DevOptions } from "./dev";
import axios from "axios";

import { JSDOM } from "jsdom";

function renderAttribute(name, value) {
return value !== undefined ? `${name}="${value}"` : "";
}

function htmlStringify(json) {
return JSON.stringify(json)
.replace(/&/g, "&")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#39;");
}

export const handleReplacements = (html: string, opts: DevOptions): string => {
const envConfig = JSON.stringify({
enableHotReload: opts.hot,
Expand All @@ -42,30 +29,97 @@ function normalizeHtml(html) {
return html.replace(/\s+/g, ' ').trim();
}

const contentCache = {};

export async function fetchAndCacheContent(url) {
if (!contentCache[url]) {
const response = await axios.get(url);
contentCache[url] = response.data;
}
return contentCache[url];
}

export function modifyIndexHtml(content: string, opts: DevOptions) {
export function modifyIndexHtml(content: string, opts: DevOptions, dependencies: string[]) {
const dom = new JSDOM(content);
const document = dom.window.document;

const viewer = document.querySelector('near-social-viewer');
// Add script tags for each dependency
dependencies.forEach((dependency: string) => {
const script = document.createElement('script');
script.src = dependency;
script.defer = true;
document.head.appendChild(script);
});

const elementTag = "near-social-viewer";

// Create and configure the near-social-viewer element
const container = document.getElementById("bw-root");
const element = document.createElement(elementTag); // this could be configurable
element.setAttribute("src", opts.index);
element.setAttribute("rpc", `http://127.0.0.1:${opts.port}/api/proxy-rpc`);
element.setAttribute("network", opts.network);

if (viewer) {
viewer.setAttribute('src', opts.index);
viewer.setAttribute('rpc', `http://127.0.0.1:${opts.port}/api/proxy-rpc`);
viewer.setAttribute('network', opts.network);
if (opts.hot) {
viewer.setAttribute('enablehotreload', "");
const config = {
dev: {
hotreload: {
enabled: opts.hot
}
},
vm: {
features: {
enableComponentSrcDataKey: true
}
}
}
};

element.setAttribute('config', JSON.stringify(config));

container.appendChild(element);

// Add wallet selector

// Stylesheet
const styleLink = document.createElement('link');
styleLink.rel = 'stylesheet';
styleLink.href = 'https://cdn.jsdelivr.net/npm/@near-wallet-selector/modal-ui-js@8.7.2/styles.css';
document.head.appendChild(styleLink);

// Import wallets and setup selector
const webcomponentapp = document.createElement('script');
// We could configure wallets from bos.config.json
webcomponentapp.textContent = `
import { setupWalletSelector } from "@near-wallet-selector/core";
import { setupMyNearWallet } from "@near-wallet-selector/my-near-wallet";
import { setupHereWallet } from "@near-wallet-selector/here-wallet";
import { setupMeteorWallet } from "@near-wallet-selector/meteor-wallet";
import { setupSender } from "@near-wallet-selector/sender";
import { setupNightly } from "@near-wallet-selector/nightly";
import { setupMintbaseWallet } from "@near-wallet-selector/mintbase-wallet";
const selector = await setupWalletSelector({
network: "${opts.network}",
modules: [
setupMyNearWallet(),
setupHereWallet(),
setupMeteorWallet(),
setupSender(),
setupNightly(),
setupMintbaseWallet()
],
});
const viewer = document.querySelector("${elementTag}");
viewer.selector = selector;
`;
webcomponentapp.type = 'module';
document.body.appendChild(webcomponentapp);

return dom.serialize();
}
}

// // This should be modified to only run when necessary... only needs to be done when initializing the project
// // or when the dependencies change (which could just be managed by here... really just need to add it to the json.)
// export async function importPackages(html: string): Promise<string> {
// try {
// const { Generator } = await import('@jspm/generator');
// const generator = new Generator();
// return await generator.htmlInject(html, {
// trace: true,
// esModuleShims: true
// });
// } catch (error) {
// console.error('Error importing or using @jspm/generator:', error);
// return html; // Return original HTML if there's an error
// }
// }
2 changes: 1 addition & 1 deletion lib/repository.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { AccountID, Log } from "@/lib/types";
import { DEFAULT_CONFIG, readConfig } from "./config";
import { DEFAULT_CONFIG, readConfig } from "@/lib/config";
import fs, { existsSync, outputFile, writeFile } from "fs-extra";
import path from "path";
import { SHA256 } from "crypto-js";
Expand Down
Loading

0 comments on commit 508d255

Please sign in to comment.