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