Sami Marreed
feat: docker-v1 with optimized frontend
3289c58
import { ConfigEnv, defineConfig, normalizePath, loadEnv } from "vite";
import webExtension, { readJsonFile } from "vite-plugin-web-extension";
import { viteStaticCopy } from "vite-plugin-static-copy";
import checker from "vite-plugin-checker";
import dateFormat from "dateformat";
import path, { resolve } from "path";
import * as fs from "node:fs/promises";
async function replaceInFile(filePath) {
try {
let data = await fs.readFile(filePath, "utf8");
const updatedData = data.replace(/_commonjsHelpers/g, "commonjsHelpers");
await fs.writeFile(filePath, updatedData, "utf8");
} catch (err) {
if (err.code === "EACCES") {
console.error(`Permission denied: ${filePath}`);
} else {
console.error(`Error processing file ${filePath}:`, err);
}
}
}
async function copyAndDeleteFile(oldFilePath) {
const dir = path.dirname(oldFilePath);
const newFilePath = path.join(dir, "commonjsHelpers.js");
try {
// Copy the file content
let data = await fs.readFile(oldFilePath, "utf8");
await fs.writeFile(newFilePath, data, "utf8");
// Delete the old file
await fs.unlink(oldFilePath);
} catch (err) {
if (err.code === "EACCES") {
console.error(`Permission denied: ${oldFilePath}`);
} else {
console.error(`Error copying and deleting file ${oldFilePath}:`, err);
}
}
}
async function traverseDirectory(dir) {
try {
const files = await fs.readdir(dir, { withFileTypes: true });
for (const file of files) {
const fullPath = path.join(dir, file.name);
if (file.isDirectory()) {
await traverseDirectory(fullPath);
} else if (file.isFile()) {
await replaceInFile(fullPath);
if (file.name === "_commonjsHelpers.js") {
await copyAndDeleteFile(fullPath);
}
}
}
} catch (err) {
console.error(`Error reading directory ${dir}:`, err);
}
}
function postBuildPlugin() {
return {
name: "vite-plugin-post-build",
apply: "build",
closeBundle() {
console.log("Starting sanitizing process");
traverseDirectory("prod");
},
};
}
const target = process.env.TARGET || "chrome";
function generateManifest(mode: string, env: { [x: string]: string }) {
const isProductionMode = mode === "production";
let manifest = readJsonFile(`src/manifest.${target}.json`);
let manifestString = JSON.stringify(manifest);
manifestString = replaceKeysInString(manifestString, env);
manifest = JSON.parse(manifestString);
const pkg = readJsonFile("package.json");
const MAX_DESCRIPTION = 132;
const name = isProductionMode
? manifest.name
: `${manifest.name} (${mode == "development" ? "Dev" : "Prod"} - ${env["NL2UI_DEPLOYMENT_NAME"] ?? "local"})`;
const description = isProductionMode
? manifest.description
: `${target}: server "${env["NL2UI_SERVER_HOST"]}" Auth: ${env["NL2UI_AUTH_TYPE"]}`.slice(0, MAX_DESCRIPTION);
const extensionKey = env["NL2UI_EXTENSION_KEY"] || undefined;
return {
...manifest,
key: extensionKey,
name: name,
//https://developer.chrome.com/docs/extensions/reference/manifest/description
description: description,
version: pkg.version,
};
}
function replaceKeysInString(stringValue, env) {
const keyIdentifier = /__(\w+)__/g;
return stringValue.replace(keyIdentifier, (match, identifier) => {
if (env.hasOwnProperty(identifier)) {
let value = env[identifier];
if (
(typeof value === "string" || value instanceof String) &&
value.startsWith('"') &&
value.endsWith('"')
) {
value = value.substring(1, value.length - 1);
}
return value;
}
// If the identifier is not found, leave it unchanged
return match;
});
}
function replaceKeysInHtml(htmlContent, keyValueMap) {
const keyIdentifier = /__(\w+)__/g;
return htmlContent.replace(keyIdentifier, (match, identifier) => {
if (keyValueMap.hasOwnProperty(match)) {
let value = keyValueMap[match];
if (
(typeof value === "string" || value instanceof String) &&
value.startsWith('"') &&
value.endsWith('"')
) {
value = value.substring(1, value.length - 1);
}
return value;
}
// If the identifier is not found, leave it unchanged
return match;
});
}
function createViteDefine(env: { [x: string]: string }, manifest) {
env["NL2UI_MANIFEST_VERSION"] = `v${manifest.manifest_version}`;
env["NL2UI_TIMESTAMP"] = dateFormat(new Date(), "yyyyMMddHHMMss");
const viteDefine = Object.entries(env).reduce((acc, [key, value]) => {
acc[`__${key}__`] = JSON.stringify(value);
return acc;
}, {});
return viteDefine;
}
// Plugin for file renaming
function renameFiles() {
return {
name: "rename-files",
generateBundle(options, bundle) {
for (const file of Object.keys(bundle)) {
if (file.startsWith("_")) {
const newName = file.replace(/^_/, "");
bundle[newName] = bundle[file];
delete bundle[file];
}
}
},
};
}
export default defineConfig((config: ConfigEnv) => {
const envFileDirectory = path.resolve(__dirname); //.env file directory
let env = loadEnv(config.mode, envFileDirectory, "NL2UI");
console.log("Environment: ");
console.log(env);
env["NL2UI_LOGOUT_URL"] = `${
env["NL2UI_APPID_OAUTH_SERVER_URL"]
}/cloud_directory/sso/logout?redirect_uri=${encodeURIComponent(`${env["NL2UI_DIALOG_SERVER"]}/logout`)}&client_id=${
env["NL2UI_APPID_CLIENT_ID"]
}`;
const manifest = generateManifest(config.mode, env);
const define = createViteDefine(env, manifest);
const outputDir = config.mode == "production" ? "prod" : "build";
if (config.mode === "production") {
return {
define,
esbuild: {
target: "es2022",
},
publicDir: false,
build: {
sourcemap: false,
outDir: "prod",
assetsInlineLimit: 0,
minify: "terser",
},
plugins: [
webExtension({
manifest: () => manifest,
additionalInputs: ["src/assets/sidepanel.html", "src/logWorker/log.worker.ts"],
browser: target,
}),
viteStaticCopy({
targets: [
{
src: normalizePath("./src/assets/*.html"),
dest: normalizePath("assets"),
transform: (content, path) => {
//replace __VAR_NAME__ within .html files.
if (path.endsWith(".html")) {
return replaceKeysInHtml(content, define);
}
return content; // Return unmodified content for other files
},
},
{
src: normalizePath("./src/assets/*.(png|jpeg|yaml)"),
dest: normalizePath("assets"),
},
{
src: normalizePath("../node_modules/@ibm/plex/IBM-Plex-Sans"),
dest: normalizePath("fonts"),
},
],
}),
// checker({
// typescript: true,
// }),
],
resolve: {
alias: {
"@uiagent/shared": path.resolve(__dirname, "../shared/dist/index.js"),
"@agentic_chat": path.resolve(__dirname, "../agentic_chat/src"),
},
},
};
}
return {
define,
logLevel: "error",
esbuild: {
target: "es2022",
},
publicDir: false,
build: {
sourcemap: true,
watch: {
include: [path.resolve("./src/**"), path.resolve("../sidepanel/src/**"), path.resolve("../runtime/**")],
},
outDir: outputDir,
assetsInlineLimit: 0,
minify: false,
},
plugins: [
webExtension({
manifest: () => manifest,
additionalInputs: ["src/assets/sidepanel.html", "src/logWorker/log.worker.ts"],
watchFilePaths: ["package.json", "manifest.chrome.json", "manifest.firefox.json"],
browser: target,
}),
viteStaticCopy({
targets: [
{
src: normalizePath("./src/assets/*.html"),
dest: normalizePath("assets"),
transform: (content, path) => {
//replace __VAR_NAME__ within .html files.
if (path.endsWith(".html")) {
return replaceKeysInHtml(content, define);
}
return content; // Return unmodified content for other files
},
},
{
src: normalizePath("./src/assets/*.(png|jpeg|yaml)"),
dest: normalizePath("assets"),
},
{
src: normalizePath("../node_modules/@ibm/plex/IBM-Plex-Sans"),
dest: normalizePath("fonts"),
},
],
}),
postBuildPlugin(),
],
resolve: {
alias: {
"@uiagent/shared": path.resolve(__dirname, "../shared/dist/index.js"),
"@agentic_chat": path.resolve(__dirname, "../agentic_chat/src"),
},
},
optimizeDeps: {
include: ["@uiagent/shared"],
},
};
});