From 3afeb6297d7b0fe8a834cc18ab844494e904301d Mon Sep 17 00:00:00 2001 From: Marcos Candeia Date: Wed, 6 Dec 2023 12:50:43 -0300 Subject: [PATCH] Create workspace app Signed-off-by: Marcos Candeia --- deco.ts | 1 + decohub/apps/workspace.ts | 1 + decohub/manifest.gen.ts | 66 +++++++++-------- workspace/loaders/block.ts | 3 + workspace/manifest.gen.ts | 17 +++++ workspace/mod.ts | 145 +++++++++++++++++++++++++++++++++++++ 6 files changed, 201 insertions(+), 32 deletions(-) create mode 100644 decohub/apps/workspace.ts create mode 100644 workspace/loaders/block.ts create mode 100644 workspace/manifest.gen.ts create mode 100644 workspace/mod.ts diff --git a/deco.ts b/deco.ts index 193d9077c..9c789bc7f 100644 --- a/deco.ts +++ b/deco.ts @@ -11,6 +11,7 @@ const compatibilityApps = [{ const config = { apps: [ app("ai-assistants"), + app("workspace"), app("openai"), app("brand-assistant"), app("implementation"), diff --git a/decohub/apps/workspace.ts b/decohub/apps/workspace.ts new file mode 100644 index 000000000..12fa54022 --- /dev/null +++ b/decohub/apps/workspace.ts @@ -0,0 +1 @@ +export { default } from "../../workspace/mod.ts"; \ No newline at end of file diff --git a/decohub/manifest.gen.ts b/decohub/manifest.gen.ts index e4d2771ec..076efa64d 100644 --- a/decohub/manifest.gen.ts +++ b/decohub/manifest.gen.ts @@ -5,44 +5,46 @@ import * as $$$$$$$$$$$0 from "./apps/typesense.ts"; import * as $$$$$$$$$$$1 from "./apps/wake.ts"; import * as $$$$$$$$$$$2 from "./apps/ai-assistants.ts"; -import * as $$$$$$$$$$$3 from "./apps/analytics.ts"; -import * as $$$$$$$$$$$4 from "./apps/workflows.ts"; -import * as $$$$$$$$$$$5 from "./apps/implementation.ts"; -import * as $$$$$$$$$$$6 from "./apps/vnda.ts"; -import * as $$$$$$$$$$$7 from "./apps/algolia.ts"; -import * as $$$$$$$$$$$8 from "./apps/admin.ts"; -import * as $$$$$$$$$$$9 from "./apps/nuvemshop.ts"; -import * as $$$$$$$$$$$10 from "./apps/linx.ts"; -import * as $$$$$$$$$$$11 from "./apps/vtex.ts"; -import * as $$$$$$$$$$$12 from "./apps/weather.ts"; -import * as $$$$$$$$$$$13 from "./apps/brand-assistant.ts"; -import * as $$$$$$$$$$$14 from "./apps/sourei.ts"; -import * as $$$$$$$$$$$15 from "./apps/shopify.ts"; -import * as $$$$$$$$$$$16 from "./apps/handlebars.ts"; -import * as $$$$$$$$$$$17 from "./apps/verified-reviews.ts"; -import * as $$$$$$$$$$$18 from "./apps/power-reviews.ts"; +import * as $$$$$$$$$$$3 from "./apps/workspace.ts"; +import * as $$$$$$$$$$$4 from "./apps/analytics.ts"; +import * as $$$$$$$$$$$5 from "./apps/workflows.ts"; +import * as $$$$$$$$$$$6 from "./apps/implementation.ts"; +import * as $$$$$$$$$$$7 from "./apps/vnda.ts"; +import * as $$$$$$$$$$$8 from "./apps/algolia.ts"; +import * as $$$$$$$$$$$9 from "./apps/admin.ts"; +import * as $$$$$$$$$$$10 from "./apps/nuvemshop.ts"; +import * as $$$$$$$$$$$11 from "./apps/linx.ts"; +import * as $$$$$$$$$$$12 from "./apps/vtex.ts"; +import * as $$$$$$$$$$$13 from "./apps/weather.ts"; +import * as $$$$$$$$$$$14 from "./apps/brand-assistant.ts"; +import * as $$$$$$$$$$$15 from "./apps/sourei.ts"; +import * as $$$$$$$$$$$16 from "./apps/shopify.ts"; +import * as $$$$$$$$$$$17 from "./apps/handlebars.ts"; +import * as $$$$$$$$$$$18 from "./apps/verified-reviews.ts"; +import * as $$$$$$$$$$$19 from "./apps/power-reviews.ts"; const manifest = { "apps": { - "decohub/apps/admin.ts": $$$$$$$$$$$8, + "decohub/apps/admin.ts": $$$$$$$$$$$9, "decohub/apps/ai-assistants.ts": $$$$$$$$$$$2, - "decohub/apps/algolia.ts": $$$$$$$$$$$7, - "decohub/apps/analytics.ts": $$$$$$$$$$$3, - "decohub/apps/brand-assistant.ts": $$$$$$$$$$$13, - "decohub/apps/handlebars.ts": $$$$$$$$$$$16, - "decohub/apps/implementation.ts": $$$$$$$$$$$5, - "decohub/apps/linx.ts": $$$$$$$$$$$10, - "decohub/apps/nuvemshop.ts": $$$$$$$$$$$9, - "decohub/apps/power-reviews.ts": $$$$$$$$$$$18, - "decohub/apps/shopify.ts": $$$$$$$$$$$15, - "decohub/apps/sourei.ts": $$$$$$$$$$$14, + "decohub/apps/algolia.ts": $$$$$$$$$$$8, + "decohub/apps/analytics.ts": $$$$$$$$$$$4, + "decohub/apps/brand-assistant.ts": $$$$$$$$$$$14, + "decohub/apps/handlebars.ts": $$$$$$$$$$$17, + "decohub/apps/implementation.ts": $$$$$$$$$$$6, + "decohub/apps/linx.ts": $$$$$$$$$$$11, + "decohub/apps/nuvemshop.ts": $$$$$$$$$$$10, + "decohub/apps/power-reviews.ts": $$$$$$$$$$$19, + "decohub/apps/shopify.ts": $$$$$$$$$$$16, + "decohub/apps/sourei.ts": $$$$$$$$$$$15, "decohub/apps/typesense.ts": $$$$$$$$$$$0, - "decohub/apps/verified-reviews.ts": $$$$$$$$$$$17, - "decohub/apps/vnda.ts": $$$$$$$$$$$6, - "decohub/apps/vtex.ts": $$$$$$$$$$$11, + "decohub/apps/verified-reviews.ts": $$$$$$$$$$$18, + "decohub/apps/vnda.ts": $$$$$$$$$$$7, + "decohub/apps/vtex.ts": $$$$$$$$$$$12, "decohub/apps/wake.ts": $$$$$$$$$$$1, - "decohub/apps/weather.ts": $$$$$$$$$$$12, - "decohub/apps/workflows.ts": $$$$$$$$$$$4, + "decohub/apps/weather.ts": $$$$$$$$$$$13, + "decohub/apps/workflows.ts": $$$$$$$$$$$5, + "decohub/apps/workspace.ts": $$$$$$$$$$$3, }, "name": "decohub", "baseUrl": import.meta.url, diff --git a/workspace/loaders/block.ts b/workspace/loaders/block.ts new file mode 100644 index 000000000..7f615836e --- /dev/null +++ b/workspace/loaders/block.ts @@ -0,0 +1,3 @@ +export interface Props { + name: string; +} \ No newline at end of file diff --git a/workspace/manifest.gen.ts b/workspace/manifest.gen.ts new file mode 100644 index 000000000..a3f4d6449 --- /dev/null +++ b/workspace/manifest.gen.ts @@ -0,0 +1,17 @@ +// DO NOT EDIT. This file is generated by deco. +// This file SHOULD be checked into source version control. +// This file is automatically updated during development when running `dev.ts`. + +import * as $$$0 from "./loaders/block.ts"; + +const manifest = { + "loaders": { + "workspace/loaders/block.ts": $$$0, + }, + "name": "workspace", + "baseUrl": import.meta.url, +}; + +export type Manifest = typeof manifest; + +export default manifest; diff --git a/workspace/mod.ts b/workspace/mod.ts new file mode 100644 index 000000000..1edfce182 --- /dev/null +++ b/workspace/mod.ts @@ -0,0 +1,145 @@ +import { SourceMap } from "deco/blocks/app.ts"; +import { buildSourceMap } from "deco/blocks/utils.tsx"; +import type { App, AppContext as AC, AppManifest } from "deco/mod.ts"; +import { + initialize, + transform, +} from "https://deno.land/x/esbuild@v0.19.7/wasm.js"; +import { dirname, join } from "std/path/mod.ts"; +import manifest, { Manifest } from "./manifest.gen.ts"; + +const initializePromise = initialize({ + wasmURL: "https://deno.land/x/esbuild@v0.19.7/esbuild.wasm", + worker: false, +}); + +export interface File { + /** + * @format textarea + */ + content: string; +} + +export interface Directory { + entries: FileSystemNode[]; +} + +export const isDir = (node: Directory | File): node is Directory => { + return Array.isArray((node as Directory)?.entries); +}; + +/** + * @title {{{name}}} + */ +export interface FileSystemNode { + name: string; + value: Directory | File; +} + +export interface State { + /** + * @titleBy name + */ + fileSystem: FileSystemNode[]; +} + +const currdir = dirname(import.meta.url); +const importFromString = async (modData: string) => + await transform(modData, { + loader: "tsx", + platform: "browser", + target: ["es2022"], + format: "esm", + minify: false, + jsx: "automatic", + jsxImportSource: "preact", + }).then((res) => + import(`data:application/javascript;base64,${btoa(res.code)}`) + ); + +const compile = async ( + blockType: keyof Omit, + { path, content }: TsContent, + manifest: AppManifest, + sourceMap: SourceMap, +): Promise<[AppManifest, SourceMap]> => { + await initializePromise; + const tsModule = await importFromString(content); + const blockPath = join(currdir, blockType, path); + const blockKey = `${manifest.name}/${blockType}/${path}`; + return [{ + ...manifest, + [blockType]: { + ...manifest[blockType], + [blockKey]: tsModule, + }, + }, { + ...sourceMap, + [blockKey]: { + path: blockPath, + content: content, + }, + }]; +}; + +export interface FileSystem { + [path: string]: string | FileSystem; +} + +const buildFs = (nodes: FileSystemNode[]): FileSystem => { + return nodes.reduce((acc, node) => { + if (isDir(node.value)) { + return { + ...acc, + [node.name]: buildFs(node.value.entries), + }; + } else { + return { + ...acc, + [node.name]: node.value.content, + }; + } + }, {}); +}; + +export interface TsContent { + path: string; + content: string; +} +function* walk( + fs: FileSystem | string, + root = "", +): Generator { + if (typeof fs === "string") { + if (!root.endsWith(".ts") && !root.endsWith(".tsx")) return; + return yield { path: root, content: fs }; + } + for (const [name, subdir] of Object.entries(fs)) { + yield* walk(subdir, `${root === "" ? "" : `${root}/`}${name}`); + } +} +/** + * @title My Workspace + */ +export default async function App( + { fileSystem }: State, +): Promise> { + const fs = buildFs(fileSystem); + let appManifest = manifest; + let appSourceMap: SourceMap = buildSourceMap(appManifest); + for (const [blockType, blockContentOrFs] of Object.entries(fs)) { + for (const tsContent of walk(blockContentOrFs)) { + const [newManifest, newSourceMap] = await compile( + blockType as keyof Omit, + tsContent, + appManifest, + appSourceMap, + ); + appManifest = newManifest; + appSourceMap = newSourceMap; + } + } + return { manifest: appManifest, state: fs, sourceMap: appSourceMap }; +} + +export type AppContext = AC>>;