mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-11 03:24:16 +00:00
split functions
This commit is contained in:
parent
645e088c1b
commit
4a2820597c
@ -1,173 +1,25 @@
|
|||||||
import { Widget } from "@models"
|
import getWidgetCode from "@utils/getWidgetCode"
|
||||||
|
|
||||||
import { transform } from "sucrase"
|
|
||||||
import UglifyJS from "uglify-js"
|
|
||||||
|
|
||||||
import path from "path"
|
|
||||||
import resolveUrl from "@utils/resolveUrl"
|
|
||||||
import replaceImportsWithRemoteURL from "@utils/replaceImportsWithRemoteURL"
|
|
||||||
|
|
||||||
async function compileWidgetCode(widgetCode, manifest, rootURL) {
|
|
||||||
if (!widgetCode) {
|
|
||||||
throw new Error("Widget code not defined.")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!manifest) {
|
|
||||||
throw new Error("Manifest not defined.")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!rootURL) {
|
|
||||||
throw new Error("Root URL not defined.")
|
|
||||||
}
|
|
||||||
|
|
||||||
let renderComponentName = null
|
|
||||||
let cssFiles = []
|
|
||||||
|
|
||||||
// inject react with cdn
|
|
||||||
widgetCode = `import React from "https://cdn.skypack.dev/react@17?dts" \n${widgetCode}`
|
|
||||||
|
|
||||||
widgetCode = await replaceImportsWithRemoteURL(widgetCode, resolveUrl(rootURL, manifest.main))
|
|
||||||
|
|
||||||
// remove css imports and append to manifest (Only its used in the entry file)
|
|
||||||
widgetCode = widgetCode.replace(/import\s+["'](.*)\.css["']/g, (match, p1) => {
|
|
||||||
cssFiles.push(resolveUrl(rootURL, `${p1}.css`))
|
|
||||||
|
|
||||||
return ""
|
|
||||||
})
|
|
||||||
|
|
||||||
// transform jsx to js
|
|
||||||
widgetCode = await transform(widgetCode, {
|
|
||||||
transforms: ["jsx"],
|
|
||||||
//jsxRuntime: "automatic",
|
|
||||||
//production: true,
|
|
||||||
}).code
|
|
||||||
|
|
||||||
// search export default and get the name of the function/const/class
|
|
||||||
const exportDefaultRegex = /export\s+default\s+(?:function|const|class)\s+([a-zA-Z0-9]+)/g
|
|
||||||
|
|
||||||
const exportDefaultMatch = exportDefaultRegex.exec(widgetCode)
|
|
||||||
|
|
||||||
if (exportDefaultMatch) {
|
|
||||||
renderComponentName = exportDefaultMatch[1]
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove export default keywords
|
|
||||||
widgetCode = widgetCode.replace("export default", "")
|
|
||||||
|
|
||||||
let manifestProcessed = {
|
|
||||||
...manifest,
|
|
||||||
}
|
|
||||||
|
|
||||||
manifestProcessed.cssFiles = cssFiles
|
|
||||||
manifestProcessed.main = resolveUrl(rootURL, manifest.main)
|
|
||||||
manifestProcessed.icon = resolveUrl(rootURL, manifest.icon)
|
|
||||||
|
|
||||||
let result = `
|
|
||||||
${widgetCode}
|
|
||||||
|
|
||||||
export default {
|
|
||||||
manifest: ${JSON.stringify(manifestProcessed)},
|
|
||||||
renderComponent: ${renderComponentName},
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
// minify code
|
|
||||||
result = UglifyJS.minify(result, {
|
|
||||||
compress: true,
|
|
||||||
mangle: true,
|
|
||||||
}).code
|
|
||||||
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
export default async (req, res) => {
|
export default async (req, res) => {
|
||||||
const widgetId = req.params.widgetId
|
const widgetId = req.params.widgetId
|
||||||
|
|
||||||
const useCache = req.query["use-cache"] ? toBoolean(req.query["use-cache"]) : true
|
const useCache = req.query["use-cache"] ? toBoolean(req.query["use-cache"]) : true
|
||||||
|
|
||||||
// try to find Widget
|
const origin = `${toBoolean(process.env.FORCE_CODE_SSL) ? "https" : req.protocol}://${req.get("host")}`
|
||||||
const widget = await Widget.findOne({
|
|
||||||
_id: widgetId,
|
let widgetCode = await getWidgetCode(widgetId, {
|
||||||
}).catch(() => {
|
useCache,
|
||||||
return null
|
origin,
|
||||||
|
}).catch((error) => {
|
||||||
|
res.status(500).json({
|
||||||
|
error: error.message,
|
||||||
|
})
|
||||||
|
|
||||||
|
return false
|
||||||
})
|
})
|
||||||
|
|
||||||
if (!widget) {
|
if (widgetCode) {
|
||||||
return res.status(404).json({
|
res.setHeader("Content-Type", "application/javascript")
|
||||||
error: "Widget not found.",
|
return res.status(200).send(widgetCode)
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!widget.manifest.main) {
|
|
||||||
return res.status(404).json({
|
|
||||||
error: "Widget entry file not defined",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
const requestedVersion = widgetId.split("@")[1] ?? widget.manifest.version
|
|
||||||
|
|
||||||
let widgetCode = null
|
|
||||||
|
|
||||||
// get origin from request url
|
|
||||||
const origin = `${toBoolean(process.env.FORCE_CODE_SSL) ? "https" : req.protocol}://${req.get("host")}/static/widgets/${widgetId}@${requestedVersion}/`
|
|
||||||
const remotePath = `/widgets/${widgetId}@${requestedVersion}/`
|
|
||||||
|
|
||||||
const remoteEntyFilePath = path.join(remotePath, widget.manifest.main)
|
|
||||||
|
|
||||||
if (useCache) {
|
|
||||||
widgetCode = await global.redis.get(`${origin}:widget:${widgetId}@${requestedVersion}}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!widgetCode) {
|
|
||||||
try {
|
|
||||||
widgetCode = await new Promise(async (resolve, reject) => {
|
|
||||||
await global.storage.getObject(process.env.S3_BUCKET, remoteEntyFilePath, (error, dataStream) => {
|
|
||||||
if (error) {
|
|
||||||
return reject(error)
|
|
||||||
}
|
|
||||||
|
|
||||||
let data = ""
|
|
||||||
|
|
||||||
dataStream.on("data", (chunk) => {
|
|
||||||
data += chunk
|
|
||||||
})
|
|
||||||
|
|
||||||
dataStream.on("end", () => {
|
|
||||||
resolve(data)
|
|
||||||
})
|
|
||||||
|
|
||||||
dataStream.on("error", (error) => {
|
|
||||||
reject(error)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
} catch (error) {
|
|
||||||
return res.status(500).json({
|
|
||||||
error: `Unable to get widget code from storage. ${error.message}`,
|
|
||||||
requestedFile: remoteEntyFilePath
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
console.log(`🔌 [widget:${widgetId}] Compiling widget code...`)
|
|
||||||
|
|
||||||
widgetCode = await compileWidgetCode(widgetCode, widget.manifest, origin)
|
|
||||||
|
|
||||||
await global.redis.set(`${origin}:widget:${widgetId}@${requestedVersion}}`, widgetCode)
|
|
||||||
} catch (error) {
|
|
||||||
return res.status(500).json({
|
|
||||||
message: "Unable to transform widget code.",
|
|
||||||
error: error.message,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!widgetCode) {
|
|
||||||
return res.status(404).json({
|
|
||||||
error: "Widget not found.",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
res.setHeader("Content-Type", "application/javascript")
|
|
||||||
return res.status(200).send(widgetCode)
|
|
||||||
}
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
import { Widget } from "@models"
|
||||||
|
import getWidgetCode from "@utils/getWidgetCode"
|
||||||
|
|
||||||
|
export default async (req, res) => {
|
||||||
|
const widget_id = req.params.widgetId
|
||||||
|
|
||||||
|
const widget = await Widget.findOne({
|
||||||
|
_id: widget_id,
|
||||||
|
}).catch(() => {
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!widget) {
|
||||||
|
return res.status(404).json({
|
||||||
|
error: "Widget not found",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const widgetCode = await getWidgetCode(widget_id, {
|
||||||
|
useCache: false,
|
||||||
|
origin: `${toBoolean(process.env.FORCE_CODE_SSL) ? "https" : req.protocol}://${req.get("host")}`,
|
||||||
|
}).catch((error) => {
|
||||||
|
res.status(500).json({
|
||||||
|
error: error.message,
|
||||||
|
})
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
|
||||||
|
// create a development web preview using react app
|
||||||
|
|
||||||
|
return res.status(200).send(`
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Widget Preview</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
|
||||||
|
<script>
|
||||||
|
window.__debugState = {
|
||||||
|
widgetCode: ${JSON.stringify(widgetCode)},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/react/umd/react.development.js"></script>
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/react-dom/umd/react-dom.development.js"></script>
|
||||||
|
<script src="http://localhost:3040/static/devScripts/main.jsx"></script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h2>Widget Preview</h2>
|
||||||
|
<div id="root"></div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`)
|
||||||
|
}
|
@ -0,0 +1,78 @@
|
|||||||
|
import { transform } from "sucrase"
|
||||||
|
import UglifyJS from "uglify-js"
|
||||||
|
|
||||||
|
import resolveUrl from "@utils/resolveUrl"
|
||||||
|
import replaceImportsWithRemoteURL from "@utils/replaceImportsWithRemoteURL"
|
||||||
|
|
||||||
|
export default async (widgetCode, manifest, rootURL) => {
|
||||||
|
if (!widgetCode) {
|
||||||
|
throw new Error("Widget code not defined.")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!manifest) {
|
||||||
|
throw new Error("Manifest not defined.")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rootURL) {
|
||||||
|
throw new Error("Root URL not defined.")
|
||||||
|
}
|
||||||
|
|
||||||
|
let renderComponentName = null
|
||||||
|
let cssFiles = []
|
||||||
|
|
||||||
|
// inject react with cdn
|
||||||
|
widgetCode = `import React from "https://cdn.skypack.dev/react@17?dts" \n${widgetCode}`
|
||||||
|
|
||||||
|
widgetCode = await replaceImportsWithRemoteURL(widgetCode, resolveUrl(rootURL, manifest.main))
|
||||||
|
|
||||||
|
// remove css imports and append to manifest (Only its used in the entry file)
|
||||||
|
widgetCode = widgetCode.replace(/import\s+["'](.*)\.css["']/g, (match, p1) => {
|
||||||
|
cssFiles.push(resolveUrl(rootURL, `${p1}.css`))
|
||||||
|
|
||||||
|
return ""
|
||||||
|
})
|
||||||
|
|
||||||
|
// transform jsx to js
|
||||||
|
widgetCode = await transform(widgetCode, {
|
||||||
|
transforms: ["jsx"],
|
||||||
|
//jsxRuntime: "automatic",
|
||||||
|
//production: true,
|
||||||
|
}).code
|
||||||
|
|
||||||
|
// search export default and get the name of the function/const/class
|
||||||
|
const exportDefaultRegex = /export\s+default\s+(?:function|const|class)\s+([a-zA-Z0-9]+)/g
|
||||||
|
|
||||||
|
const exportDefaultMatch = exportDefaultRegex.exec(widgetCode)
|
||||||
|
|
||||||
|
if (exportDefaultMatch) {
|
||||||
|
renderComponentName = exportDefaultMatch[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove export default keywords
|
||||||
|
widgetCode = widgetCode.replace("export default", "")
|
||||||
|
|
||||||
|
let manifestProcessed = {
|
||||||
|
...manifest,
|
||||||
|
}
|
||||||
|
|
||||||
|
manifestProcessed.cssFiles = cssFiles
|
||||||
|
manifestProcessed.main = resolveUrl(rootURL, manifest.main)
|
||||||
|
manifestProcessed.icon = resolveUrl(rootURL, manifest.icon)
|
||||||
|
|
||||||
|
let result = `
|
||||||
|
${widgetCode}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
manifest: ${JSON.stringify(manifestProcessed)},
|
||||||
|
renderComponent: ${renderComponentName},
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
|
// minify code
|
||||||
|
result = UglifyJS.minify(result, {
|
||||||
|
compress: true,
|
||||||
|
mangle: true,
|
||||||
|
}).code
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
86
packages/marketplace_server/src/utils/getWidgetCode/index.js
Normal file
86
packages/marketplace_server/src/utils/getWidgetCode/index.js
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
import { Widget } from "@models"
|
||||||
|
import compileWidgetCode from "@utils/compileWidgetCode"
|
||||||
|
import path from "path"
|
||||||
|
|
||||||
|
export default async (
|
||||||
|
widgetId,
|
||||||
|
{
|
||||||
|
useCache = true,
|
||||||
|
origin = null,
|
||||||
|
}
|
||||||
|
) => {
|
||||||
|
if (!widgetId) {
|
||||||
|
throw new Error("Widget ID not defined.")
|
||||||
|
}
|
||||||
|
if (!origin) {
|
||||||
|
throw new Error("Origin not defined.")
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to find Widget
|
||||||
|
const widget = await Widget.findOne({
|
||||||
|
_id: widgetId,
|
||||||
|
}).catch(() => {
|
||||||
|
return null
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!widget) {
|
||||||
|
throw new Error("Widget not found.")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!widget.manifest.main) {
|
||||||
|
throw new Error("Widget entry file not defined")
|
||||||
|
}
|
||||||
|
|
||||||
|
const requestedVersion = widgetId.split("@")[1] ?? widget.manifest.version
|
||||||
|
|
||||||
|
let widgetCode = null
|
||||||
|
|
||||||
|
const finalOrigin = `${origin}/static/widgets/${widgetId}@${requestedVersion}/`
|
||||||
|
const remotePath = `/widgets/${widgetId}@${requestedVersion}/`
|
||||||
|
|
||||||
|
const remoteEntyFilePath = path.join(remotePath, widget.manifest.main)
|
||||||
|
|
||||||
|
if (useCache) {
|
||||||
|
widgetCode = await global.redis.get(`${origin}:widget:${widgetId}@${requestedVersion}}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!widgetCode) {
|
||||||
|
try {
|
||||||
|
widgetCode = await new Promise(async (resolve, reject) => {
|
||||||
|
await global.storage.getObject(process.env.S3_BUCKET, remoteEntyFilePath, (error, dataStream) => {
|
||||||
|
if (error) {
|
||||||
|
return reject(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = ""
|
||||||
|
|
||||||
|
dataStream.on("data", (chunk) => {
|
||||||
|
data += chunk
|
||||||
|
})
|
||||||
|
|
||||||
|
dataStream.on("end", () => {
|
||||||
|
resolve(data)
|
||||||
|
})
|
||||||
|
|
||||||
|
dataStream.on("error", (error) => {
|
||||||
|
reject(error)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Unable to fetch widget code. ${error.message}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
console.log(`🔌 [widget:${widgetId}] Compiling widget code...`)
|
||||||
|
|
||||||
|
widgetCode = await compileWidgetCode(widgetCode, widget.manifest, finalOrigin)
|
||||||
|
|
||||||
|
await global.redis.set(`${origin}:widget:${widgetId}@${requestedVersion}}`, widgetCode)
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Unable to transform widget code. ${error.message}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return widgetCode
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user