mirror of
https://github.com/Laupetin/OpenAssetTools.git
synced 2025-10-09 16:26:44 +00:00
chore: update modman vite setup to support dev server
This commit is contained in:
@@ -37,7 +37,7 @@ function ModMan:project()
|
||||
}
|
||||
|
||||
includedirs {
|
||||
"%{prj.location}"
|
||||
"%{wks.location}/src/ModMan"
|
||||
}
|
||||
|
||||
filter { "system:linux", "action:gmake" }
|
||||
|
@@ -18,6 +18,8 @@
|
||||
|
||||
namespace
|
||||
{
|
||||
constexpr auto LOCALHOST_PREFIX = "http://localhost:";
|
||||
|
||||
std::unordered_map<std::string, UiFile> assetLookup;
|
||||
|
||||
std::string WideStringToString(const std::wstring& wideString)
|
||||
@@ -95,6 +97,13 @@ namespace
|
||||
|
||||
const auto uri = WideStringToString(wUri);
|
||||
bool fileFound = false;
|
||||
|
||||
#ifdef _DEBUG
|
||||
// Allow dev server access
|
||||
if (uri.starts_with(LOCALHOST_PREFIX))
|
||||
return S_OK;
|
||||
#endif
|
||||
|
||||
if (uri.starts_with(edge::URL_PREFIX))
|
||||
{
|
||||
const auto asset = uri.substr(std::char_traits<char>::length(edge::URL_PREFIX) - 1);
|
||||
|
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "ui/modmanui.h"
|
||||
#include "Web/ViteAssets.h"
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
@@ -2,6 +2,7 @@
|
||||
#include "webview/webview.h"
|
||||
#pragma warning(pop)
|
||||
|
||||
#include "Web/ViteAssets.h"
|
||||
#include "Web/Edge/AssetHandlerEdge.h"
|
||||
#include "Web/Gtk/AssetHandlerGtk.h"
|
||||
|
||||
@@ -58,12 +59,19 @@ int main()
|
||||
|
||||
#if defined(WEBVIEW_PLATFORM_WINDOWS) && defined(WEBVIEW_EDGE)
|
||||
edge::InstallCustomProtocolHandler(w);
|
||||
w.navigate(edge::URL_PREFIX + "index.html"s);
|
||||
constexpr auto urlPrefix = edge::URL_PREFIX;
|
||||
#elif defined(WEBVIEW_PLATFORM_LINUX) && defined(WEBVIEW_GTK)
|
||||
gtk::InstallCustomProtocolHandler(w);
|
||||
w.navigate(gtk::URL_PREFIX + "index.html"s);
|
||||
constexpr auto urlPrefix = gtk::URL_PREFIX;
|
||||
#else
|
||||
#error Unsupported platform
|
||||
#endif
|
||||
|
||||
#ifdef _DEBUG
|
||||
w.navigate(VITE_DEV_SERVER ? std::format("http://localhost:{}", VITE_DEV_SERVER_PORT) : std::format("{}index.html", urlPrefix));
|
||||
#else
|
||||
w.navigate(std::format("{}index.html", urlPrefix));
|
||||
#endif
|
||||
w.run();
|
||||
}
|
||||
catch (const webview::exception& e)
|
||||
|
@@ -1,84 +1,88 @@
|
||||
import type { Plugin, ViteDevServer } from "vite";
|
||||
import type { OutputOptions, OutputBundle, OutputAsset, OutputChunk } from "rollup";
|
||||
import type { Plugin } from "vite";
|
||||
import type { OutputAsset, OutputChunk } from "rollup";
|
||||
import path from "node:path";
|
||||
import fs from "node:fs";
|
||||
|
||||
function createTransformedTextSource(varName: string, previousSource: string) {
|
||||
const str = [...previousSource]
|
||||
.map((v) => `0x${v.charCodeAt(0).toString(16).padStart(2, "0")}`)
|
||||
.join(", ");
|
||||
return `#pragma once
|
||||
|
||||
static inline const unsigned char ${varName}[] {
|
||||
${str}
|
||||
};
|
||||
`;
|
||||
}
|
||||
type MinimalOutputAsset = Pick<OutputAsset, "type" | "fileName" | "source">;
|
||||
type MinimalOutputChunk = Pick<OutputChunk, "type" | "fileName" | "code">;
|
||||
type MinimalOutputBundle = Record<string, MinimalOutputAsset | MinimalOutputChunk>;
|
||||
|
||||
function createVarName(fileName: string) {
|
||||
return fileName.replaceAll(".", "_").toUpperCase();
|
||||
}
|
||||
|
||||
function transformAsset(asset: OutputAsset) {
|
||||
const varName = createVarName(asset.names[0]);
|
||||
function transformAsset(asset: MinimalOutputAsset) {
|
||||
const varName = createVarName(asset.fileName);
|
||||
|
||||
let bytes: string;
|
||||
if (typeof asset.source === "string") {
|
||||
asset.source = createTransformedTextSource(varName, asset.source);
|
||||
bytes = [...asset.source].map((v) => String(v.charCodeAt(0))).join(",");
|
||||
} else {
|
||||
const str = [...asset.source].map((v) => `0x${v.toString(16).padStart(2, "0")}`).join(", ");
|
||||
asset.source = `#pragma once
|
||||
|
||||
static inline const unsigned char ${varName}[] {
|
||||
${str}
|
||||
};
|
||||
`;
|
||||
bytes = [...asset.source].map((v) => String(v)).join(",");
|
||||
}
|
||||
|
||||
return varName;
|
||||
return `constexpr const unsigned char ${varName}[] {${bytes}};
|
||||
`;
|
||||
}
|
||||
|
||||
function transformChunk(chunk: OutputChunk) {
|
||||
function transformChunk(chunk: MinimalOutputChunk) {
|
||||
const varName = createVarName(chunk.fileName);
|
||||
chunk.code = createTransformedTextSource(varName, chunk.code);
|
||||
return varName;
|
||||
const bytes = [...chunk.code].map((v) => String(v.charCodeAt(0))).join(",");
|
||||
|
||||
return `constexpr const unsigned char ${varName}[] {${bytes}};
|
||||
`;
|
||||
}
|
||||
|
||||
export function headerTransformationPlugin(): Plugin {
|
||||
return {
|
||||
name: "header-transformation",
|
||||
apply: "build",
|
||||
generateBundle(options: OutputOptions, bundle: OutputBundle, isWrite: boolean) {
|
||||
const includesStr: string[] = [`#include "index.html.h"`];
|
||||
const uiFilesStr: string[] = [
|
||||
`{ "index.html", INDEX_HTML, std::extent_v<decltype(INDEX_HTML)> }`,
|
||||
];
|
||||
function writeHeader(
|
||||
bundle: MinimalOutputBundle,
|
||||
outputDir?: string,
|
||||
options?: HeaderTransformationPluginOptions,
|
||||
devServerPort?: number,
|
||||
) {
|
||||
const outputPath = options?.outputPath ?? path.join(outputDir ?? "dist", "ViteAssets.h");
|
||||
const outputPathParentDir = path.dirname(outputPath);
|
||||
|
||||
for (const curBundle of Object.values(bundle)) {
|
||||
let varName: string;
|
||||
if (curBundle.type === "asset") {
|
||||
varName = transformAsset(curBundle);
|
||||
} else {
|
||||
varName = transformChunk(curBundle);
|
||||
}
|
||||
fs.mkdirSync(outputPathParentDir, { recursive: true });
|
||||
|
||||
includesStr.push(`#include "${curBundle.fileName}.h"`);
|
||||
uiFilesStr.push(
|
||||
`{ "${curBundle.fileName}", ${varName}, std::extent_v<decltype(${varName})> }`,
|
||||
);
|
||||
const fd = fs.openSync(outputPath, "w");
|
||||
const includeFileEnumeration = options?.includeFileEnumeration ?? true;
|
||||
|
||||
curBundle.fileName = `${curBundle.fileName}.h`;
|
||||
}
|
||||
fs.writeSync(
|
||||
fd,
|
||||
`#pragma once
|
||||
|
||||
this.emitFile({
|
||||
type: "asset",
|
||||
fileName: "modmanui.h",
|
||||
source: `#pragma once
|
||||
`,
|
||||
);
|
||||
|
||||
${includesStr.join("\n")}
|
||||
|
||||
#include <cstdlib>
|
||||
if (includeFileEnumeration) {
|
||||
fs.writeSync(
|
||||
fd,
|
||||
`#include <cstdlib>
|
||||
#include <type_traits>
|
||||
|
||||
`,
|
||||
);
|
||||
}
|
||||
|
||||
fs.writeSync(
|
||||
fd,
|
||||
`constexpr auto VITE_DEV_SERVER = ${devServerPort ? "true" : "false"};
|
||||
constexpr auto VITE_DEV_SERVER_PORT = ${devServerPort ? String(devServerPort) : "-1"};
|
||||
`,
|
||||
);
|
||||
|
||||
for (const curBundle of Object.values(bundle)) {
|
||||
if (curBundle.type === "asset") {
|
||||
fs.writeSync(fd, transformAsset(curBundle));
|
||||
} else {
|
||||
fs.writeSync(fd, transformChunk(curBundle));
|
||||
}
|
||||
}
|
||||
|
||||
if (includeFileEnumeration) {
|
||||
fs.writeSync(
|
||||
fd,
|
||||
`
|
||||
struct UiFile
|
||||
{
|
||||
const char* filename;
|
||||
@@ -87,36 +91,81 @@ struct UiFile
|
||||
};
|
||||
|
||||
static inline const UiFile MOD_MAN_UI_FILES[] {
|
||||
${uiFilesStr.join(",\n")}
|
||||
`,
|
||||
);
|
||||
|
||||
let index = 0;
|
||||
for (const curBundle of Object.values(bundle)) {
|
||||
const fileName = curBundle.fileName;
|
||||
const varName = createVarName(fileName);
|
||||
|
||||
let prefix = " ";
|
||||
if (index > 0) {
|
||||
prefix = `,
|
||||
`;
|
||||
}
|
||||
|
||||
fs.writeSync(
|
||||
fd,
|
||||
`${prefix}{ "${fileName}", ${varName}, std::extent_v<decltype(${varName})> }`,
|
||||
);
|
||||
index++;
|
||||
}
|
||||
|
||||
fs.writeSync(
|
||||
fd,
|
||||
`
|
||||
};
|
||||
`,
|
||||
);
|
||||
|
||||
fs.closeSync(fd);
|
||||
}
|
||||
}
|
||||
|
||||
export interface HeaderTransformationPluginOptions {
|
||||
outputPath?: string;
|
||||
includeFileEnumeration?: boolean;
|
||||
}
|
||||
|
||||
export default function headerTransformationPlugin(
|
||||
options?: HeaderTransformationPluginOptions,
|
||||
): Plugin {
|
||||
let writeServerActive = false;
|
||||
let writeBundleActive = false;
|
||||
|
||||
return {
|
||||
name: "vite-plugin-header-transformation",
|
||||
enforce: "post",
|
||||
config(_userOptions, env) {
|
||||
if (env.command === "serve") {
|
||||
writeServerActive = true;
|
||||
} else {
|
||||
writeBundleActive = true;
|
||||
}
|
||||
},
|
||||
configureServer(server) {
|
||||
if (!writeServerActive) {
|
||||
return;
|
||||
}
|
||||
server.httpServer?.once("listening", () => {
|
||||
writeHeader(
|
||||
{
|
||||
// We need at least one array entry for MSVC
|
||||
dummyfile: { type: "chunk", fileName: "dummyfile", code: "dummy" },
|
||||
},
|
||||
server.config.build.outDir,
|
||||
options,
|
||||
server.config.server.port,
|
||||
);
|
||||
});
|
||||
},
|
||||
transformIndexHtml(
|
||||
html: string,
|
||||
ctx: {
|
||||
path: string;
|
||||
filename: string;
|
||||
server?: ViteDevServer;
|
||||
bundle?: OutputBundle;
|
||||
chunk?: OutputChunk;
|
||||
},
|
||||
) {
|
||||
html = html.replaceAll("index.js.h", "index.js");
|
||||
|
||||
html = createTransformedTextSource(createVarName("index.html"), html);
|
||||
ctx.filename = `${ctx.filename}.h`;
|
||||
|
||||
return html;
|
||||
},
|
||||
writeBundle(options, bundle) {
|
||||
for (const curBundle of Object.values(bundle)) {
|
||||
if (curBundle.fileName === "index.html" && curBundle.type === "asset") {
|
||||
const outputFilePath = path.join(options.dir!, curBundle.fileName);
|
||||
fs.renameSync(outputFilePath, outputFilePath + ".h");
|
||||
curBundle.fileName += ".h";
|
||||
}
|
||||
writeBundle(outputOptions, bundle) {
|
||||
if (!writeBundleActive) {
|
||||
return;
|
||||
}
|
||||
|
||||
writeHeader(bundle, outputOptions.dir, options);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@@ -1,7 +1,6 @@
|
||||
export interface NativeMethods{
|
||||
|
||||
greet: (name: string) => Promise<string>;
|
||||
export interface NativeMethods {
|
||||
greet: (name: string) => Promise<string>;
|
||||
}
|
||||
|
||||
// @ts-expect-error
|
||||
// @ts-expect-error Typescript expects this to be an error, it is not here though
|
||||
export const nativeMethods: NativeMethods = window as NativeMethods;
|
||||
|
@@ -3,12 +3,11 @@ import { fileURLToPath, URL } from "node:url";
|
||||
import { defineConfig } from "vite";
|
||||
import vue from "@vitejs/plugin-vue";
|
||||
import vueDevTools from "vite-plugin-vue-devtools";
|
||||
import { headerTransformationPlugin } from "./build/HeaderTransformationPlugin";
|
||||
import headerTransformationPlugin from "./build/HeaderTransformationPlugin";
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
build: {
|
||||
outDir: "../../build/src/ModMan/ui",
|
||||
copyPublicDir: false,
|
||||
emptyOutDir: true,
|
||||
rollupOptions: {
|
||||
@@ -19,14 +18,18 @@ export default defineConfig({
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [vue(), vueDevTools(), headerTransformationPlugin()],
|
||||
plugins: [
|
||||
vue(),
|
||||
vueDevTools(),
|
||||
headerTransformationPlugin({
|
||||
outputPath: fileURLToPath(
|
||||
new URL("../../build/src/ModMan/Web/ViteAssets.h", import.meta.url),
|
||||
),
|
||||
}),
|
||||
],
|
||||
resolve: {
|
||||
alias: {
|
||||
"@": fileURLToPath(new URL("./src", import.meta.url)),
|
||||
},
|
||||
},
|
||||
server: {
|
||||
port: 1420,
|
||||
strictPort: true,
|
||||
},
|
||||
});
|
||||
|
Reference in New Issue
Block a user