feat: 完成 v0.6.0 墨呈品牌迁移

This commit is contained in:
SkyJourney
2026-08-02 10:09:14 +08:00
parent 58f87cc19f
commit 9a2aa3c341
39 changed files with 451 additions and 81 deletions
+7 -6
View File
@@ -46,8 +46,8 @@ Markdown ECharts、Core、Renderer、Application、Preview Engine 和 Web
然后才复制 `apps/web/dist`。不得绕过该链路直接调用 electron-builder。
版本化目录包、NSIS `Setup.exe` 和 ZIP 输出到
`apps/desktop/out/v0.5.1/`。该目录被 Git 忽略。公司内部分发以
`md-to-pdf-0.5.1-x86_64-Setup.exe` 为正式安装包,ZIP 作为免安装
`apps/desktop/out/v0.6.0/`。该目录被 Git 忽略。公司内部分发以
`MorphDoc-0.6.0-x86_64-Setup.exe` 为正式安装包,ZIP 作为免安装
辅助包;当前未配置代码签名,Windows 首次运行可能显示“未知发布者”
提示。
@@ -56,11 +56,12 @@ NSIS 使用标准辅助安装模式,支持选择当前用户/所有用户安
发布,不提交进 Git 历史。
桌面和开始菜单快捷方式由 `build/installer.nsh` 显式创建,安装完成后的
“运行”操作直接启动 `md-to-pdf.exe`,不依赖快捷方式文件。
“运行”操作直接启动 `MorphDoc.exe`,不依赖快捷方式文件。
产品名称、窗口标题、安装包名称和开始菜单名称保持
Markdown PDF 导出器”,实际 Windows 主程序与进程文件固定为
`md-to-pdf.exe`
界面、快捷方式与卸载项使用中文名“墨呈”,窗口标题
墨呈 · MorphDoc”,默认安装目录和 Windows 主程序分别为 `MorphDoc`
`MorphDoc.exe`。NSIS appId 保持 `com.md-to-pdf.desktop`,确保
v0.5.1 覆盖升级沿用原安装身份。
当前目录包只包含一套 Electron Chromium,不携带 Playwright Chromium。
Web 静态资源作为只读资源放在 Electron `resources/dist` 下,生产环境通过
+34 -19
View File
@@ -1,3 +1,8 @@
!define MORPHDOC_DISPLAY_NAME "墨呈"
!define MORPHDOC_EXECUTABLE "MorphDoc.exe"
!define MORPHDOC_LEGACY_DISPLAY_NAME "Markdown PDF 导出器"
!define MORPHDOC_LEGACY_EXECUTABLE "md-to-pdf.exe"
!ifdef FONT_PACK_COMPANION_FILE
!ifndef BUILD_UNINSTALLER
!include "FileFunc.nsh"
@@ -36,7 +41,7 @@
Pop $R0
StrCmp $R0 "error" 0 +2
Abort
${NSD_CreateLabel} 0 0 100% 28u "检测到经过完整性校验的可选字体包。安装后,政企公文主题可使用与 PDF 相同的原生中文字体资产。"
${NSD_CreateLabel} 0 0 100% 28u "检测到经过完整性校验的墨呈可选字体包。安装后,政企公文主题可使用与 PDF 相同的原生中文字体资产。"
Pop $R0
${NSD_CreateCheckbox} 0 38u 100% 14u "安装 ${FONT_PACK_COMPANION_NAME}"
Pop $fontPackCompanionCheckbox
@@ -70,11 +75,11 @@
WriteRegStr SHCTX \
"Software\Classes\MarkdownPdfExporter.Markdown\DefaultIcon" \
"" \
'"$INSTDIR\md-to-pdf.exe",0'
'"$INSTDIR\${MORPHDOC_EXECUTABLE}",0'
WriteRegStr SHCTX \
"Software\Classes\MarkdownPdfExporter.Markdown\shell\open\command" \
"" \
'"$INSTDIR\md-to-pdf.exe" "%1"'
'"$INSTDIR\${MORPHDOC_EXECUTABLE}" "%1"'
WriteRegStr SHCTX \
"Software\Classes\.md\OpenWithProgids" \
"MarkdownPdfExporter.Markdown" \
@@ -84,29 +89,34 @@
"MarkdownPdfExporter.Markdown" \
""
WriteRegStr SHCTX \
"Software\Classes\Applications\md-to-pdf.exe\shell\open\command" \
"Software\Classes\Applications\${MORPHDOC_EXECUTABLE}\shell\open\command" \
"" \
'"$INSTDIR\md-to-pdf.exe" "%1"'
'"$INSTDIR\${MORPHDOC_EXECUTABLE}" "%1"'
WriteRegStr SHCTX \
"Software\Classes\Applications\md-to-pdf.exe\SupportedTypes" \
"Software\Classes\Applications\${MORPHDOC_EXECUTABLE}\SupportedTypes" \
".md" \
""
WriteRegStr SHCTX \
"Software\Classes\Applications\md-to-pdf.exe\SupportedTypes" \
"Software\Classes\Applications\${MORPHDOC_EXECUTABLE}\SupportedTypes" \
".markdown" \
""
CreateDirectory "$SMPROGRAMS\Markdown PDF 导出器"
DeleteRegKey SHCTX \
"Software\Classes\Applications\${MORPHDOC_LEGACY_EXECUTABLE}"
Delete "$SMPROGRAMS\${MORPHDOC_LEGACY_DISPLAY_NAME}\${MORPHDOC_LEGACY_DISPLAY_NAME}.lnk"
RMDir "$SMPROGRAMS\${MORPHDOC_LEGACY_DISPLAY_NAME}"
Delete "$DESKTOP\${MORPHDOC_LEGACY_DISPLAY_NAME}.lnk"
CreateDirectory "$SMPROGRAMS\${MORPHDOC_DISPLAY_NAME}"
CreateShortCut \
"$SMPROGRAMS\Markdown PDF 导出器\Markdown PDF 导出器.lnk" \
"$INSTDIR\md-to-pdf.exe" \
"$SMPROGRAMS\${MORPHDOC_DISPLAY_NAME}\${MORPHDOC_DISPLAY_NAME}.lnk" \
"$INSTDIR\${MORPHDOC_EXECUTABLE}" \
"" \
"$INSTDIR\md-to-pdf.exe"
"$INSTDIR\${MORPHDOC_EXECUTABLE}"
CreateShortCut \
"$DESKTOP\Markdown PDF 导出器.lnk" \
"$INSTDIR\md-to-pdf.exe" \
"$DESKTOP\${MORPHDOC_DISPLAY_NAME}.lnk" \
"$INSTDIR\${MORPHDOC_EXECUTABLE}" \
"" \
"$INSTDIR\md-to-pdf.exe"
StrCpy $launchLink "$INSTDIR\md-to-pdf.exe"
"$INSTDIR\${MORPHDOC_EXECUTABLE}"
StrCpy $launchLink "$INSTDIR\${MORPHDOC_EXECUTABLE}"
!ifdef FONT_PACK_COMPANION_FILE
StrCmp $fontPackCompanionAvailable "1" 0 font_pack_install_done
StrCmp $fontPackCompanionSelected "1" 0 font_pack_install_done
@@ -136,11 +146,16 @@
"Software\Classes\.markdown\OpenWithProgids" \
"MarkdownPdfExporter.Markdown"
DeleteRegKey SHCTX \
"Software\Classes\Applications\md-to-pdf.exe"
"Software\Classes\Applications\${MORPHDOC_EXECUTABLE}"
DeleteRegKey SHCTX \
"Software\Classes\Applications\${MORPHDOC_LEGACY_EXECUTABLE}"
DeleteRegKey SHCTX \
"Software\Classes\MarkdownPdfExporter.Markdown"
Delete "$SMPROGRAMS\Markdown PDF 导出器\Markdown PDF 导出器.lnk"
RMDir "$SMPROGRAMS\Markdown PDF 导出器"
Delete "$DESKTOP\Markdown PDF 导出器.lnk"
Delete "$SMPROGRAMS\${MORPHDOC_DISPLAY_NAME}\${MORPHDOC_DISPLAY_NAME}.lnk"
RMDir "$SMPROGRAMS\${MORPHDOC_DISPLAY_NAME}"
Delete "$DESKTOP\${MORPHDOC_DISPLAY_NAME}.lnk"
Delete "$SMPROGRAMS\${MORPHDOC_LEGACY_DISPLAY_NAME}\${MORPHDOC_LEGACY_DISPLAY_NAME}.lnk"
RMDir "$SMPROGRAMS\${MORPHDOC_LEGACY_DISPLAY_NAME}"
Delete "$DESKTOP\${MORPHDOC_LEGACY_DISPLAY_NAME}.lnk"
System::Call 'shell32::SHChangeNotify(i 0x08000000, i 0, i 0, i 0)'
!macroend
+8 -7
View File
@@ -1,5 +1,6 @@
const path = require("node:path");
const { version } = require("./package.json");
const brand = require("../../product.json");
const windowsIcon = path.resolve(
__dirname,
@@ -9,8 +10,8 @@ const nsisInclude = process.env.MD_TO_PDF_NSIS_INCLUDE?.trim() ||
"build/installer.nsh";
module.exports = {
appId: "com.md-to-pdf.desktop",
productName: "Markdown PDF 导出器",
appId: brand.appId,
productName: brand.englishName,
copyright: "Copyright © YIXIONG Tech.ltd",
asar: true,
electronLanguages: ["zh-CN", "en-US"],
@@ -58,8 +59,8 @@ module.exports = {
}
],
icon: windowsIcon,
executableName: "md-to-pdf",
artifactName: `md-to-pdf-${version}-x86_64.\${ext}`
executableName: brand.executableName,
artifactName: `${brand.artifactPrefix}-${version}-x86_64.\${ext}`
},
nsis: {
oneClick: false,
@@ -68,12 +69,12 @@ module.exports = {
allowElevation: true,
createDesktopShortcut: false,
createStartMenuShortcut: false,
shortcutName: "Markdown PDF 导出器",
uninstallDisplayName: "Markdown PDF 导出器",
shortcutName: brand.displayName,
uninstallDisplayName: brand.displayName,
installerIcon: windowsIcon,
uninstallerIcon: windowsIcon,
installerLanguages: ["zh_CN"],
include: nsisInclude,
artifactName: `md-to-pdf-${version}-x86_64-Setup.\${ext}`
artifactName: `${brand.artifactPrefix}-${version}-x86_64-Setup.\${ext}`
}
};
+3 -3
View File
@@ -1,9 +1,9 @@
{
"name": "@md-to-pdf/desktop",
"version": "0.5.1",
"version": "0.6.0",
"private": true,
"productName": "Markdown PDF 导出器",
"description": "Markdown PDF 导出器桌面端",
"productName": "MorphDoc",
"description": "墨呈桌面端:面向结构化 Markdown 的主题化文档创作与发布工具",
"author": "YIXIONG Tech.ltd",
"type": "module",
"main": "dist/main.js",
+17 -1
View File
@@ -1,7 +1,14 @@
import { build } from "esbuild";
import { copyFile } from "node:fs/promises";
import { copyFile, readFile } from "node:fs/promises";
import { fileURLToPath } from "node:url";
const productBrand = JSON.parse(
await readFile(
fileURLToPath(new URL("../../../product.json", import.meta.url)),
"utf8"
)
);
await build({
entryPoints: ["src/main.ts"],
bundle: true,
@@ -9,6 +16,15 @@ await build({
format: "esm",
external: ["electron"],
outfile: "dist/main.js",
define: {
__APP_ID__: JSON.stringify(productBrand.appId),
__APP_ENGLISH_NAME__: JSON.stringify(productBrand.englishName),
__APP_DISPLAY_NAME__: JSON.stringify(productBrand.displayName),
__APP_WINDOW_TITLE__: JSON.stringify(productBrand.windowTitle),
__APP_LEGACY_DISPLAY_NAME__: JSON.stringify(
productBrand.legacyDisplayName
)
},
banner: {
js: [
'import { createRequire as __mdToPdfCreateRequire } from "node:module";',
+5
View File
@@ -0,0 +1,5 @@
declare const __APP_ID__: string;
declare const __APP_ENGLISH_NAME__: string;
declare const __APP_DISPLAY_NAME__: string;
declare const __APP_WINDOW_TITLE__: string;
declare const __APP_LEGACY_DISPLAY_NAME__: string;
@@ -233,7 +233,7 @@ export class DesktopApplicationController {
: undefined;
const initialBounds = this.#resolveInitialBounds(isPrimary);
const window = new BrowserWindow({
title: `Markdown PDF 导出器 v${app.getVersion()}`,
title: `${__APP_WINDOW_TITLE__} v${app.getVersion()}`,
icon: this.#iconPath,
...initialBounds,
minWidth: Math.min(
+26 -1
View File
@@ -6,6 +6,7 @@ import {
session
} from "electron";
import { access, mkdir } from "node:fs/promises";
import { existsSync } from "node:fs";
import path from "node:path";
import { fileURLToPath, pathToFileURL } from "node:url";
import {
@@ -25,6 +26,7 @@ import {
findMarkdownFileArgument,
isMarkdownFilePath
} from "./markdown-file.js";
import { migrateLegacyUserData } from "./user-data-migration.js";
const APP_SCHEME = "mdpdf";
const APP_HOST = "bundle";
@@ -37,6 +39,28 @@ let pendingExternalMarkdownPath = findMarkdownFileArgument(
process.cwd()
);
function configurePackagedUserDataDirectory() {
if (!app.isPackaged) {
return Promise.resolve();
}
const appDataDirectory = app.getPath("appData");
const targetDirectory = path.join(
appDataDirectory,
__APP_ENGLISH_NAME__
);
const targetAlreadyExists = existsSync(targetDirectory);
app.setPath("userData", targetDirectory);
if (targetAlreadyExists) {
return Promise.resolve();
}
return migrateLegacyUserData(
[path.join(appDataDirectory, __APP_LEGACY_DISPLAY_NAME__)],
targetDirectory
).then(() => undefined);
}
const userDataMigration = configurePackagedUserDataDirectory();
protocol.registerSchemesAsPrivileged([
{
scheme: APP_SCHEME,
@@ -300,9 +324,10 @@ if (!hasSingleInstanceLock) {
app
.whenReady()
.then(async () => {
await userDataMigration;
Menu.setApplicationMenu(null);
if (process.platform === "win32") {
app.setAppUserModelId("com.md-to-pdf.desktop");
app.setAppUserModelId(__APP_ID__);
}
const themeDirectory = getDesktopThemeDirectory();
await mkdir(themeDirectory, { recursive: true });
+70
View File
@@ -0,0 +1,70 @@
import { cp, lstat, mkdir } from "node:fs/promises";
import path from "node:path";
const MIGRATED_ENTRIES = [
"window-state.json",
"save-location.json",
"themes",
"Local Storage",
"Session Storage",
"IndexedDB"
] as const;
async function readOrdinaryStatus(target: string) {
try {
const status = await lstat(target);
return status.isSymbolicLink() ? undefined : status;
} catch (error) {
if ((error as NodeJS.ErrnoException).code === "ENOENT") {
return undefined;
}
throw error;
}
}
export interface LegacyUserDataMigrationResult {
migrated: boolean;
sourceDirectory?: string;
copiedEntries: string[];
}
export async function migrateLegacyUserData(
legacyDirectories: readonly string[],
targetDirectory: string
): Promise<LegacyUserDataMigrationResult> {
if (await readOrdinaryStatus(targetDirectory)) {
return { migrated: false, copiedEntries: [] };
}
for (const legacyDirectory of legacyDirectories) {
if (path.resolve(legacyDirectory) === path.resolve(targetDirectory)) {
continue;
}
const legacyStatus = await readOrdinaryStatus(legacyDirectory);
if (!legacyStatus?.isDirectory()) {
continue;
}
await mkdir(targetDirectory, { recursive: true });
const copiedEntries: string[] = [];
for (const entry of MIGRATED_ENTRIES) {
const source = path.join(legacyDirectory, entry);
if (!(await readOrdinaryStatus(source))) {
continue;
}
await cp(source, path.join(targetDirectory, entry), {
recursive: true,
errorOnExist: true,
force: false
});
copiedEntries.push(entry);
}
return {
migrated: true,
sourceDirectory: legacyDirectory,
copiedEntries
};
}
return { migrated: false, copiedEntries: [] };
}
+56 -2
View File
@@ -3,14 +3,25 @@ import { fileURLToPath } from "node:url";
import { describe, expect, it } from "vitest";
interface PackageManifest {
version?: string;
productName?: string;
scripts?: Record<string, string>;
}
interface ProductBrand {
appId: string;
englishName: string;
displayName: string;
windowTitle: string;
executableName: string;
artifactPrefix: string;
}
const desktopRoot = fileURLToPath(new URL("../", import.meta.url));
const projectRoot = fileURLToPath(new URL("../../../", import.meta.url));
function readManifest(path: string): PackageManifest {
return JSON.parse(readFileSync(path, "utf8")) as PackageManifest;
function readManifest<T = PackageManifest>(path: string): T {
return JSON.parse(readFileSync(path, "utf8")) as T;
}
describe("桌面发行构建链", () => {
@@ -58,6 +69,49 @@ describe("桌面发行构建链", () => {
expect(builderConfig).toContain('to: "samples"');
});
it("保持安装身份并从统一配置生成 MorphDoc 发行名称", () => {
const brand = readManifest<ProductBrand>(
`${projectRoot}/product.json`
);
const desktopManifest = readManifest(
`${desktopRoot}/package.json`
);
const builderConfig = readFileSync(
`${desktopRoot}/electron-builder.config.cjs`,
"utf8"
);
const installerInclude = readFileSync(
`${desktopRoot}/build/installer.nsh`,
"utf8"
);
expect(brand).toMatchObject({
appId: "com.md-to-pdf.desktop",
englishName: "MorphDoc",
displayName: "墨呈",
windowTitle: "墨呈 · MorphDoc",
executableName: "MorphDoc",
artifactPrefix: "MorphDoc"
});
expect(desktopManifest).toMatchObject({
version: "0.6.0",
productName: "MorphDoc"
});
expect(builderConfig).toContain("appId: brand.appId");
expect(builderConfig).toContain("productName: brand.englishName");
expect(builderConfig).toContain("executableName: brand.executableName");
expect(builderConfig).toContain("shortcutName: brand.displayName");
expect(installerInclude).toContain(
'!define MORPHDOC_EXECUTABLE "MorphDoc.exe"'
);
expect(installerInclude).toContain(
'!define MORPHDOC_DISPLAY_NAME "墨呈"'
);
expect(installerInclude).toContain(
"Software\\Classes\\Applications\\${MORPHDOC_LEGACY_EXECUTABLE}"
);
});
it("仅在组合构建时接入经过哈希校验的同目录字体包", () => {
const builderConfig = readFileSync(
`${desktopRoot}/electron-builder.config.cjs`,
@@ -0,0 +1,84 @@
import { mkdtemp, mkdir, readFile, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import path from "node:path";
import { afterEach, describe, expect, it } from "vitest";
import { rm } from "node:fs/promises";
import { migrateLegacyUserData } from "../src/user-data-migration.js";
const temporaryDirectories: string[] = [];
afterEach(async () => {
await Promise.all(
temporaryDirectories.splice(0).map((directory) =>
rm(directory, { recursive: true, force: true })
)
);
});
async function createRoot() {
const root = await mkdtemp(path.join(tmpdir(), "morphdoc-migration-"));
temporaryDirectories.push(root);
return root;
}
describe("品牌升级用户数据迁移", () => {
it("首次启动时复制状态、主题和浏览器本地存储但保留旧目录", async () => {
const root = await createRoot();
const legacy = path.join(root, "Markdown PDF 导出器");
const target = path.join(root, "MorphDoc");
await mkdir(path.join(legacy, "themes", "custom"), {
recursive: true
});
await mkdir(path.join(legacy, "Local Storage"), {
recursive: true
});
await writeFile(
path.join(legacy, "window-state.json"),
'{"width":1280}',
"utf8"
);
await writeFile(
path.join(legacy, "themes", "custom", "theme.css"),
"#write{}",
"utf8"
);
const result = await migrateLegacyUserData([legacy], target);
expect(result).toMatchObject({
migrated: true,
sourceDirectory: legacy,
copiedEntries: ["window-state.json", "themes", "Local Storage"]
});
expect(
await readFile(path.join(target, "window-state.json"), "utf8")
).toBe('{"width":1280}');
expect(
await readFile(
path.join(target, "themes", "custom", "theme.css"),
"utf8"
)
).toBe("#write{}");
expect(
await readFile(path.join(legacy, "window-state.json"), "utf8")
).toBe('{"width":1280}');
});
it("目标目录已经存在时不覆盖新版本数据", async () => {
const root = await createRoot();
const legacy = path.join(root, "Markdown PDF 导出器");
const target = path.join(root, "MorphDoc");
await mkdir(legacy, { recursive: true });
await mkdir(target, { recursive: true });
await writeFile(path.join(legacy, "window-state.json"), "legacy", "utf8");
await writeFile(path.join(target, "window-state.json"), "current", "utf8");
expect(await migrateLegacyUserData([legacy], target)).toEqual({
migrated: false,
copiedEntries: []
});
expect(
await readFile(path.join(target, "window-state.json"), "utf8")
).toBe("current");
});
});
+1 -1
View File
@@ -39,7 +39,7 @@ function temporaryDirectories() {
const markdown = `---
title: Server HTTP DOCX 验收
author: Markdown PDF 导出器研发组
author: 墨呈研发组
---
# HTTP 导出保持可编辑
+2 -2
View File
@@ -17,9 +17,9 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta
name="description"
content="可配置、可主题化的 Markdown PDF 导出工具"
content="面向结构化 Markdown 的主题化文档创作与发布工具"
/>
<title>Markdown PDF 导出器</title>
<title>墨呈 · MorphDoc</title>
</head>
<body>
<div id="root"></div>
+6 -2
View File
@@ -44,6 +44,10 @@ import {
type PdfExportRequest
} from "./pdf-export";
import { APP_VERSION_LABEL } from "./app-version";
import {
APP_DISPLAY_NAME,
APP_SLOGAN
} from "./app-brand";
import { getSyncedScrollTop } from "./scroll-sync";
import {
loadPreviewZoom,
@@ -1487,9 +1491,9 @@ export function App() {
<header className="topbar">
<div className="topbar-primary">
<div className="topbar-brand">
<p className="eyebrow"></p>
<p className="eyebrow">{APP_SLOGAN}</p>
<div className="topbar-brand-title">
<h1>Markdown PDF </h1>
<h1>{APP_DISPLAY_NAME}</h1>
<span className="app-version">{APP_VERSION_LABEL}</span>
</div>
</div>
+4
View File
@@ -0,0 +1,4 @@
export const APP_DISPLAY_NAME = __APP_DISPLAY_NAME__;
export const APP_WINDOW_TITLE = __APP_WINDOW_TITLE__;
export const APP_SLOGAN = __APP_SLOGAN__;
export const APP_DESCRIPTION = __APP_DESCRIPTION__;
+4
View File
@@ -1,8 +1,12 @@
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { App } from "./App";
import { APP_WINDOW_TITLE } from "./app-brand";
import { APP_VERSION_LABEL } from "./app-version";
import "./styles.css";
document.title = `${APP_WINDOW_TITLE} ${APP_VERSION_LABEL}`;
const rootElement = document.getElementById("root");
if (!rootElement) {
+4
View File
@@ -16,6 +16,10 @@ import type {
declare global {
const __APP_VERSION__: string;
const __APP_DISPLAY_NAME__: string;
const __APP_WINDOW_TITLE__: string;
const __APP_SLOGAN__: string;
const __APP_DESCRIPTION__: string;
interface DesktopPdfResult {
pdf: Uint8Array;
+34
View File
@@ -0,0 +1,34 @@
import { readFileSync } from "node:fs";
import { fileURLToPath } from "node:url";
import { describe, expect, it } from "vitest";
import {
APP_DESCRIPTION,
APP_DISPLAY_NAME,
APP_SLOGAN,
APP_WINDOW_TITLE
} from "../src/app-brand";
const webRoot = fileURLToPath(new URL("../", import.meta.url));
describe("产品品牌", () => {
it("在主界面使用中文名和产品标语", () => {
expect(APP_DISPLAY_NAME).toBe("墨呈");
expect(APP_SLOGAN).toBe("一稿多式,即写即呈");
expect(APP_WINDOW_TITLE).toBe("墨呈 · MorphDoc");
expect(APP_DESCRIPTION).toBe(
"面向结构化 Markdown 的主题化文档创作与发布工具"
);
});
it("在静态入口中移除旧工具名称", () => {
const indexHtml = readFileSync(`${webRoot}/index.html`, "utf8");
const mainSource = readFileSync(`${webRoot}/src/main.tsx`, "utf8");
expect(indexHtml).toContain("<title>墨呈 · MorphDoc</title>");
expect(mainSource).toContain(
"document.title = `${APP_WINDOW_TITLE} ${APP_VERSION_LABEL}`"
);
expect(indexHtml).not.toContain("Markdown PDF 导出器");
expect(indexHtml).not.toContain("内网文档工具");
});
});
+2 -2
View File
@@ -10,8 +10,8 @@ const webRoot = fileURLToPath(new URL("../", import.meta.url));
describe("应用版本", () => {
it("从统一构建版本生成标题徽标", () => {
expect(APP_VERSION).toBe("0.5.1");
expect(APP_VERSION_LABEL).toBe("v0.5.1");
expect(APP_VERSION).toBe("0.6.0");
expect(APP_VERSION_LABEL).toBe("v0.6.0");
});
it("允许浏览器加载同源 Web Manifest", () => {
+16 -1
View File
@@ -9,6 +9,17 @@ const rootPackage = JSON.parse(
"utf8"
)
) as { version: string };
const productBrand = JSON.parse(
readFileSync(
fileURLToPath(new URL("../../product.json", import.meta.url)),
"utf8"
)
) as {
displayName: string;
windowTitle: string;
slogan: string;
description: string;
};
export default defineConfig({
plugins: [react()],
@@ -16,7 +27,11 @@ export default defineConfig({
new URL("../../logos/web", import.meta.url)
),
define: {
__APP_VERSION__: JSON.stringify(rootPackage.version)
__APP_VERSION__: JSON.stringify(rootPackage.version),
__APP_DISPLAY_NAME__: JSON.stringify(productBrand.displayName),
__APP_WINDOW_TITLE__: JSON.stringify(productBrand.windowTitle),
__APP_SLOGAN__: JSON.stringify(productBrand.slogan),
__APP_DESCRIPTION__: JSON.stringify(productBrand.description)
},
build: {
rollupOptions: {