macana

Static site generator for Obsidian Vault

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 17
  18. 18
  19. 19
  20. 20
  21. 21
  22. 22
  23. 23
  24. 24
  25. 25
  26. 26
  27. 27
  28. 28
  29. 29
  30. 30
  31. 31
  32. 32
  33. 33
  34. 34
  35. 35
  36. 36
  37. 37
  38. 38
  39. 39
  40. 40
  41. 41
  42. 42
  43. 43
  44. 44
  45. 45
  46. 46
  47. 47
  48. 48
  49. 49
  50. 50
  51. 51
  52. 52
  53. 53
  54. 54
  55. 55
  56. 56
  57. 57
  58. 58
  59. 59
  60. 60
  61. 61
  62. 62
  63. 63
  64. 64
  65. 65
  66. 66
  67. 67
  68. 68
  69. 69
  70. 70
  71. 71
  72. 72
  73. 73
  74. 74
  75. 75
  76. 76
  77. 77
  78. 78
  79. 79
  80. 80
  81. 81
  82. 82
  83. 83
  84. 84
  85. 85
  86. 86
  87. 87
  88. 88
  89. 89
  90. 90
  91. 91
  92. 92
  93. 93
  94. 94
  95. 95
  96. 96
  97. 97
  98. 98
  99. 99
  100. 100
  101. 101
  102. 102
  103. 103
  104. 104
  105. 105
  106. 106
  107. 107
  108. 108
  109. 109
  110. 110
  111. 111
  112. 112
  113. 113
  114. 114
  115. 115
// SPDX-FileCopyrightText: 2024 Shota FUJI <pockawoooh@gmail.com>
//
// SPDX-License-Identifier: Apache-2.0

import * as log from "../deps/deno.land/std/log/mod.ts";
import * as cli from "../deps/deno.land/std/cli/mod.ts";

import { DenoFsReader } from "../filesystem_reader/deno_fs.ts";
import { DenoFsWriter } from "../filesystem_writer/deno_fs.ts";
import {
	DefaultTreeBuilder,
	fileExtensions,
	ignoreDotfiles,
	langDir,
	removeExtFromMetadata,
	useFileSystemTimestamps,
} from "../tree_builder/default_tree_builder.ts";
import { ObsidianMarkdownParser } from "../content_parser/obsidian_markdown.ts";
import { JSONCanvasParser } from "../content_parser/json_canvas.ts";
import { oneof } from "../content_parser/oneof.ts";
import { DefaultThemeBuilder } from "../page_builder/default_theme/builder.tsx";

export async function build() {
	const outDir = new URL("./.dist", import.meta.url);

	await Deno.permissions.request({
		name: "write",
		path: outDir,
	});

	const srcDir = new URL(".", import.meta.url);

	await Deno.permissions.request({
		name: "read",
		path: srcDir,
	});

	await Deno.mkdir(outDir, { recursive: true });

	const fileSystemReader = new DenoFsReader(srcDir);
	const fileSystemWriter = new DenoFsWriter(outDir);
	const treeBuilder = new DefaultTreeBuilder({
		defaultLanguage: "en",
		ignore: [ignoreDotfiles],
		strategies: [
			fileExtensions([".md", ".canvas"]),
			removeExtFromMetadata(),
			useFileSystemTimestamps(),
			langDir({
				en: "English",
				ja: "日本語",
			}, true),
		],
		resolveShortestPathWhenPossible: true,
	});
	const contentParser = oneof(
		new JSONCanvasParser(),
		new ObsidianMarkdownParser({ frontmatter: true }),
	);
	const pageBuilder = new DefaultThemeBuilder({
		copyright: "© 2024 Shota FUJI. This document is licensed under CC BY 4.0",
		faviconSvg: ["Assets", "logo.svg"],
		faviconPng: ["Assets", "logo-64x64.png"],
		siteLogo: ["Assets", "logo.svg"],
	});

	const documentTree = await treeBuilder.build({
		fileSystemReader,
		contentParser,
	});
	await pageBuilder.build({
		documentTree,
		fileSystemReader,
		fileSystemWriter,
	});
}

if (import.meta.main) {
	const args = cli.parseArgs(Deno.args, {
		boolean: ["json"],
	});

	log.setup({
		handlers: {
			default: new log.ConsoleHandler("DEBUG", {
				formatter: args.json ? log.jsonFormatter : undefined,
				useColors: args.json ? false : undefined,
			}),
		},
		loggers: {
			macana: {
				level: "DEBUG",
				handlers: ["default"],
			},
		},
	});

	try {
		const start = performance.now();

		await build();

		const duration = performance.now() - start;

		log.info(`Complete docs build, elapsed ${duration}ms`, {
			duration,
		});
	} catch (error) {
		log.critical(`Build aborted due to an error: ${error}`, {
			error,
		});

		Deno.exit(1);
	}
}