@mdfriday/foundry 26.3.7 → 26.3.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -118,6 +118,7 @@ var init_project_metadata = __esm({
118
118
  contentLinks;
119
119
  staticLink;
120
120
  fileLink;
121
+ baseURLStructure;
121
122
  constructor(data) {
122
123
  this.id = data.id;
123
124
  this.name = data.name;
@@ -129,6 +130,7 @@ var init_project_metadata = __esm({
129
130
  this.contentLinks = data.contentLinks || [];
130
131
  this.staticLink = data.staticLink || null;
131
132
  this.fileLink = data.fileLink || null;
133
+ this.baseURLStructure = data.baseURLStructure || null;
132
134
  }
133
135
  static create(data) {
134
136
  if (!data.id || !data.name) {
@@ -168,6 +170,9 @@ var init_project_metadata = __esm({
168
170
  if (this.fileLink) {
169
171
  result.fileLink = this.fileLink;
170
172
  }
173
+ if (this.baseURLStructure) {
174
+ result.baseURLStructure = this.baseURLStructure;
175
+ }
171
176
  return result;
172
177
  }
173
178
  };
@@ -1337,6 +1342,80 @@ var init_project = __esm({
1337
1342
  }
1338
1343
  return linkDirs;
1339
1344
  }
1345
+ // ============================================================================
1346
+ // baseURL Structure Management
1347
+ // ============================================================================
1348
+ /**
1349
+ * 获取 baseURL(从 Hugo config.json)
1350
+ */
1351
+ async getBaseURL() {
1352
+ const config = await this.loadConfig();
1353
+ return config.baseURL || "/";
1354
+ }
1355
+ /**
1356
+ * 设置 baseURL(更新 Hugo config.json)
1357
+ */
1358
+ async setBaseURL(baseURL) {
1359
+ const config = await this.loadConfig();
1360
+ config.baseURL = baseURL;
1361
+ const configPath = import_path2.default.join(this.projectPath, "config.json");
1362
+ await import_fs.promises.writeFile(configPath, JSON.stringify(config, null, 2), "utf-8");
1363
+ this.config = config;
1364
+ }
1365
+ /**
1366
+ * 解析 baseURL 路径信息
1367
+ */
1368
+ parseBaseURLPath(baseURL) {
1369
+ let normalized = baseURL.trim();
1370
+ if (normalized.startsWith("/")) {
1371
+ normalized = normalized.slice(1);
1372
+ }
1373
+ if (normalized.endsWith("/")) {
1374
+ normalized = normalized.slice(0, -1);
1375
+ }
1376
+ if (normalized === "") {
1377
+ return {
1378
+ isRoot: true,
1379
+ pathParts: [],
1380
+ parentParts: [],
1381
+ finalDirName: "",
1382
+ relativeToPublic: "."
1383
+ };
1384
+ }
1385
+ const parts = normalized.split("/").filter((p2) => p2 !== "");
1386
+ if (parts.length === 0) {
1387
+ return {
1388
+ isRoot: true,
1389
+ pathParts: [],
1390
+ parentParts: [],
1391
+ finalDirName: "",
1392
+ relativeToPublic: "."
1393
+ };
1394
+ }
1395
+ const depth = parts.length;
1396
+ const relativeToPublic = depth === 1 ? "." : "../".repeat(depth - 1).slice(0, -1);
1397
+ return {
1398
+ isRoot: false,
1399
+ pathParts: parts,
1400
+ parentParts: parts.slice(0, -1),
1401
+ finalDirName: parts[parts.length - 1],
1402
+ relativeToPublic
1403
+ };
1404
+ }
1405
+ /**
1406
+ * 获取当前 baseURL 结构信息
1407
+ */
1408
+ getBaseURLStructure() {
1409
+ return this.metadata.baseURLStructure || null;
1410
+ }
1411
+ /**
1412
+ * 更新 baseURL 结构记录
1413
+ */
1414
+ updateBaseURLStructure(info) {
1415
+ this.metadata = this.metadata.update({
1416
+ baseURLStructure: info
1417
+ });
1418
+ }
1340
1419
  };
1341
1420
  }
1342
1421
  });
@@ -3303,7 +3382,8 @@ var init_workspace_factory = __esm({
3303
3382
  });
3304
3383
  const defaultTheme = options.theme || "https://gohugo.net/quartz-theme.zip?version=1.2";
3305
3384
  const config = {
3306
- baseURL: "/",
3385
+ baseURL: options.baseURL || "/",
3386
+ // 新增:使用传入的 baseURL
3307
3387
  title: name,
3308
3388
  contentDir,
3309
3389
  publishDir,
@@ -3792,6 +3872,154 @@ var init_workspace_factory = __esm({
3792
3872
  // ============================================================================
3793
3873
  // Private Helpers
3794
3874
  // ============================================================================
3875
+ // ============================================================================
3876
+ // baseURL Structure Management
3877
+ // ============================================================================
3878
+ /**
3879
+ * 在 public/ 目录下创建 baseURL 访问路径结构
3880
+ *
3881
+ * 例如:baseURL = "/blog/" 时,在 public/ 下创建 blog/ -> . (symlink)
3882
+ */
3883
+ async setupBaseURLStructure(project) {
3884
+ if (!this.fileSystemRepo) {
3885
+ throw new Error("FileSystemRepository is required for baseURL structure");
3886
+ }
3887
+ const baseURL = await project.getBaseURL();
3888
+ const pathInfo = project.parseBaseURLPath(baseURL);
3889
+ const projectRoot = project.getPath();
3890
+ const publicDir = import_path3.default.join(projectRoot, "public");
3891
+ if (pathInfo.isRoot) {
3892
+ log5.debug("baseURL is root, no structure needed");
3893
+ return {
3894
+ serverRoot: publicDir,
3895
+ baseURL: "/",
3896
+ symlinkCreated: false
3897
+ };
3898
+ }
3899
+ log5.info("Creating baseURL structure in public/", {
3900
+ baseURL,
3901
+ pathParts: pathInfo.pathParts
3902
+ });
3903
+ let currentDir = publicDir;
3904
+ for (const dirName of pathInfo.parentParts) {
3905
+ currentDir = import_path3.default.join(currentDir, dirName);
3906
+ if (!await this.fileSystemRepo.exists(currentDir)) {
3907
+ await this.fileSystemRepo.createDirectory(currentDir, false);
3908
+ log5.debug(`Created parent directory: ${dirName}`);
3909
+ }
3910
+ }
3911
+ const symlinkPath = import_path3.default.join(currentDir, pathInfo.finalDirName);
3912
+ if (await this.fileSystemRepo.exists(symlinkPath)) {
3913
+ await this.fileSystemRepo.remove(symlinkPath, true);
3914
+ log5.debug(`Removed existing symlink: ${symlinkPath}`);
3915
+ }
3916
+ const symlinkResult = await this.fileSystemRepo.createSymlink(
3917
+ pathInfo.relativeToPublic,
3918
+ symlinkPath
3919
+ );
3920
+ if (!symlinkResult.success) {
3921
+ log5.error("Failed to create baseURL symlink", {
3922
+ target: pathInfo.relativeToPublic,
3923
+ link: symlinkPath,
3924
+ error: symlinkResult.error
3925
+ });
3926
+ throw new Error(`Failed to create baseURL structure: ${symlinkResult.error}`);
3927
+ }
3928
+ log5.info("baseURL structure created successfully", {
3929
+ symlinkPath: pathInfo.pathParts.join("/"),
3930
+ target: pathInfo.relativeToPublic
3931
+ });
3932
+ project.updateBaseURLStructure({
3933
+ baseURL,
3934
+ createdAt: Date.now(),
3935
+ pathParts: pathInfo.pathParts
3936
+ });
3937
+ await this.projectRepo.saveProjectMetadata(
3938
+ project.getPath(),
3939
+ project.getMetadata().toJSON()
3940
+ );
3941
+ return {
3942
+ serverRoot: publicDir,
3943
+ baseURL,
3944
+ symlinkCreated: true
3945
+ };
3946
+ }
3947
+ /**
3948
+ * 清理 public/ 下的 baseURL 路径结构
3949
+ */
3950
+ async cleanBaseURLStructure(project) {
3951
+ if (!this.fileSystemRepo) {
3952
+ return;
3953
+ }
3954
+ const structureInfo = project.getBaseURLStructure();
3955
+ if (!structureInfo || structureInfo.pathParts.length === 0) {
3956
+ return;
3957
+ }
3958
+ const projectRoot = project.getPath();
3959
+ const publicDir = import_path3.default.join(projectRoot, "public");
3960
+ log5.info("Cleaning baseURL structure in public/", {
3961
+ pathParts: structureInfo.pathParts
3962
+ });
3963
+ for (let i = structureInfo.pathParts.length; i > 0; i--) {
3964
+ const targetPath = import_path3.default.join(
3965
+ publicDir,
3966
+ ...structureInfo.pathParts.slice(0, i)
3967
+ );
3968
+ if (await this.fileSystemRepo.exists(targetPath)) {
3969
+ await this.fileSystemRepo.remove(targetPath, true);
3970
+ log5.debug(`Removed: ${structureInfo.pathParts.slice(0, i).join("/")}`);
3971
+ }
3972
+ if (i > 1) {
3973
+ const parentPath = import_path3.default.join(
3974
+ publicDir,
3975
+ ...structureInfo.pathParts.slice(0, i - 1)
3976
+ );
3977
+ if (await this.fileSystemRepo.exists(parentPath)) {
3978
+ const children = await this.fileSystemRepo.readDirectory(parentPath);
3979
+ if (children.length > 0) {
3980
+ break;
3981
+ }
3982
+ }
3983
+ }
3984
+ }
3985
+ project.updateBaseURLStructure(null);
3986
+ await this.projectRepo.saveProjectMetadata(
3987
+ project.getPath(),
3988
+ project.getMetadata().toJSON()
3989
+ );
3990
+ log5.info("baseURL structure cleaned");
3991
+ }
3992
+ /**
3993
+ * 确保 baseURL 结构正确
3994
+ * 检查 baseURL 是否变化,如需要则重新创建
3995
+ */
3996
+ async ensureBaseURLStructure(project) {
3997
+ const currentBaseURL = await project.getBaseURL();
3998
+ const structureInfo = project.getBaseURLStructure();
3999
+ const needRecreate = !structureInfo || structureInfo.baseURL !== currentBaseURL;
4000
+ if (needRecreate) {
4001
+ if (structureInfo) {
4002
+ log5.info("baseURL changed, recreating structure", {
4003
+ old: structureInfo.baseURL,
4004
+ new: currentBaseURL
4005
+ });
4006
+ await this.cleanBaseURLStructure(project);
4007
+ }
4008
+ const result = await this.setupBaseURLStructure(project);
4009
+ return {
4010
+ ...result,
4011
+ recreated: true
4012
+ };
4013
+ }
4014
+ return {
4015
+ serverRoot: import_path3.default.join(project.getPath(), "public"),
4016
+ baseURL: currentBaseURL,
4017
+ recreated: false
4018
+ };
4019
+ }
4020
+ // ============================================================================
4021
+ // Private Helpers
4022
+ // ============================================================================
3795
4023
  createSampleContent() {
3796
4024
  return `---
3797
4025
  title: Welcome to MDFriday
@@ -4059,6 +4287,29 @@ Please specify a project name with --project <name>`);
4059
4287
  createIdentityStorage(workspace) {
4060
4288
  return this.workspaceFactory.createIdentityStorage(workspace);
4061
4289
  }
4290
+ // ============================================================================
4291
+ // baseURL Structure Management
4292
+ // ============================================================================
4293
+ /**
4294
+ * 设置项目的 baseURL 路径结构
4295
+ * 在 public/ 目录下创建对应的 symlink 结构
4296
+ */
4297
+ async setupProjectBaseURLStructure(project) {
4298
+ return this.workspaceFactory.setupBaseURLStructure(project);
4299
+ }
4300
+ /**
4301
+ * 清理项目的 baseURL 路径结构
4302
+ */
4303
+ async cleanProjectBaseURLStructure(project) {
4304
+ return this.workspaceFactory.cleanBaseURLStructure(project);
4305
+ }
4306
+ /**
4307
+ * 确保项目的 baseURL 路径结构正确
4308
+ * 检查 baseURL 是否变化,如需要则重新创建
4309
+ */
4310
+ async ensureProjectBaseURLStructure(project) {
4311
+ return this.workspaceFactory.ensureBaseURLStructure(project);
4312
+ }
4062
4313
  };
4063
4314
  }
4064
4315
  });
@@ -50508,16 +50759,20 @@ var ProjectNewCommand = class {
50508
50759
  createOptions.theme = options.theme;
50509
50760
  if (options.path)
50510
50761
  createOptions.projectPath = options.path;
50762
+ if (options.baseUrl)
50763
+ createOptions.baseURL = options.baseUrl;
50511
50764
  const project = await this.workspaceAppService.createProject(workspace, name, createOptions);
50512
50765
  const info = project.getInfo();
50513
50766
  const displayTheme = options.theme || "quartz (default)";
50767
+ const baseURL = options.baseUrl || "/";
50514
50768
  log19.info(`Created project: ${name}`);
50515
50769
  return {
50516
50770
  success: true,
50517
50771
  message: `\u2713 Project created: ${name}
50518
50772
  Path: ${info.path}
50519
50773
  Theme: ${displayTheme}
50520
- Language: ${options.language || defaultLanguage2}`,
50774
+ Language: ${options.language || defaultLanguage2}
50775
+ ` + (baseURL !== "/" ? ` Base URL: ${baseURL}` : ""),
50521
50776
  data: info
50522
50777
  };
50523
50778
  } catch (error) {
@@ -51410,11 +51665,19 @@ var BuildCommand = class {
51410
51665
  project.addBuildHistory(historyEntry);
51411
51666
  await this.workspaceAppService.saveProject(project);
51412
51667
  log82.info(`Build completed in ${duration}ms`);
51668
+ const structureInfo = await this.workspaceAppService.setupProjectBaseURLStructure(project);
51669
+ log82.debug("baseURL structure setup complete", structureInfo);
51413
51670
  let message = `\u2713 Built site in ${(duration / 1e3).toFixed(1)}s
51414
51671
  `;
51415
51672
  message += `\u2713 Project: ${project.getName()}
51416
51673
  `;
51417
51674
  message += `\u2713 Output: ${outputDir}`;
51675
+ if (structureInfo.symlinkCreated) {
51676
+ message += `
51677
+ \u2713 Base URL structure created: ${structureInfo.baseURL}`;
51678
+ message += `
51679
+ Run 'mdf serve' to preview at http://localhost:8080${structureInfo.baseURL}`;
51680
+ }
51418
51681
  if (options.parallel) {
51419
51682
  message += "\n\u2713 Used parallel processing";
51420
51683
  }
@@ -51437,7 +51700,8 @@ var BuildCommand = class {
51437
51700
  duration,
51438
51701
  outputDir,
51439
51702
  contentDirs,
51440
- snapshot: snapshotInfo
51703
+ snapshot: snapshotInfo,
51704
+ baseURL: structureInfo.baseURL
51441
51705
  }
51442
51706
  };
51443
51707
  } catch (error) {
@@ -52582,6 +52846,10 @@ var ServeCommand = class {
52582
52846
  const host = options.host || "localhost";
52583
52847
  const livereloadPort = options.livereloadPort || 35729;
52584
52848
  const enableLivereload = options.livereload !== false;
52849
+ const structureInfo = await this.workspaceAppService.ensureProjectBaseURLStructure(project);
52850
+ if (structureInfo.recreated) {
52851
+ log88.info("baseURL structure created/updated");
52852
+ }
52585
52853
  const projContentDirs = await project.getContentDirs();
52586
52854
  const contentDirs = project.getLinkDirs();
52587
52855
  const modulesDir = workspace.getModulesDir();
@@ -52629,11 +52897,10 @@ var ServeCommand = class {
52629
52897
  if (!this.coordinator.isInitialized()) {
52630
52898
  throw new Error("Failed to initialize build coordinator");
52631
52899
  }
52632
- const liveReloadStatus = this.coordinator.getLiveReloadStatus();
52633
- const url = liveReloadStatus.url || `http://${host}:${port}`;
52634
- log88.info(`Server running at ${url}`);
52900
+ const serverUrl = `http://${host}:${port}${structureInfo.baseURL}`;
52901
+ log88.info(`Server running at ${serverUrl}`);
52635
52902
  if (options.open) {
52636
- await this.openBrowser(url);
52903
+ await this.openBrowser(serverUrl);
52637
52904
  }
52638
52905
  let isShuttingDown = false;
52639
52906
  const cleanup = async () => {
@@ -52662,14 +52929,14 @@ var ServeCommand = class {
52662
52929
  process.on("SIGHUP", cleanup);
52663
52930
  return {
52664
52931
  success: true,
52665
- message: `\u2713 Server running at ${url}
52932
+ message: `\u2713 Server running at ${serverUrl}
52666
52933
  ` + (enableLivereload ? `\u2713 LiveReload enabled on port ${livereloadPort}
52667
52934
  ` : "") + (options.publish ? `\u2713 Auto-publish enabled (${options.publish})
52668
52935
  ` : "") + `\u2713 Watching for changes...
52669
52936
 
52670
52937
  Press Ctrl+C to stop`,
52671
52938
  data: {
52672
- url,
52939
+ url: serverUrl,
52673
52940
  port,
52674
52941
  host,
52675
52942
  livereloadPort: enableLivereload ? livereloadPort : void 0,
@@ -54380,6 +54647,7 @@ Commands:
54380
54647
  --source-file <path> Create from single file (uses note theme by default)
54381
54648
  --theme <url> Theme URL (default: quartz)
54382
54649
  --language <lang> Default language (default: en)
54650
+ --base-url <url> Base URL for the site (e.g., /blog/)
54383
54651
  project list List all projects
54384
54652
  project show [name] Show project details
54385
54653
  project delete <name> Delete a project
@@ -54488,6 +54756,8 @@ Examples:
54488
54756
 
54489
54757
  # Project
54490
54758
  mdf project new my-blog Create a new project
54759
+ mdf project new my-blog --base-url /blog/
54760
+ Create project with custom base URL
54491
54761
  mdf project new my-blog --source ~/Documents/my-blog
54492
54762
  Create project from existing folder (auto-detect content/static)
54493
54763
  mdf project new my-note --source-file ~/Documents/my-note.md
@@ -54562,7 +54832,7 @@ For more information, visit: https://help.mdfriday.com
54562
54832
  * Show version
54563
54833
  */
54564
54834
  showVersion() {
54565
- const version = "26.3.7";
54835
+ const version = "26.3.8";
54566
54836
  return {
54567
54837
  success: true,
54568
54838
  message: `MDFriday CLI v${version}`