config.mts 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. import "dotenv/config";
  2. import { fileURLToPath, URL } from "node:url";
  3. import { defineConfig } from "vitepress";
  4. import VueI18nPlugin from "@intlify/unplugin-vue-i18n/vite";
  5. import Icons from "unplugin-icons/vite";
  6. import { FileSystemIconLoader } from "unplugin-icons/loaders";
  7. import { SVG, cleanupSVG, parseColors, runSVGO } from "@iconify/tools";
  8. import {
  9. S3Client,
  10. ListObjectsCommand,
  11. GetObjectCommand,
  12. } from "@aws-sdk/client-s3";
  13. import fs from "node:fs";
  14. import path from "node:path";
  15. import { exec } from "child_process";
  16. import { buildSideBar } from "../utils/sideBar";
  17. const DOC_BASE_PATH = "pages";
  18. if (process.env.NODE_ENV === "production") {
  19. // 执行清理pages文件夹命令
  20. await new Promise((resolve, reject) =>
  21. exec(
  22. `git checkout -- ${DOC_BASE_PATH} & git clean -df ${DOC_BASE_PATH}`,
  23. (error, stdout, stderr) => {
  24. if (error) {
  25. console.error(`清理pages时出错: ${error.message}`);
  26. reject(stderr);
  27. }
  28. console.log(`清理pages结果: ${stdout}`);
  29. resolve(stdout);
  30. }
  31. )
  32. );
  33. }
  34. // 从S3构建pages里面的markdown文件
  35. const s3 = new S3Client({
  36. credentials: {
  37. accessKeyId: process.env.VITE_AWS_S3_ACCESS_KEY_ID!,
  38. secretAccessKey: process.env.VITE_AWS_S3_SECRET_ACCESS_KEY!,
  39. },
  40. region: process.env.VITE_AWS_S3_REGION!,
  41. });
  42. const command = new ListObjectsCommand({
  43. Bucket: process.env.VITE_DOCS_LIST_BUCKET,
  44. });
  45. const { Contents: contents } = await s3.send(command);
  46. await Promise.all(
  47. contents!.map((content) => {
  48. return new Promise(async (resolve, reject) => {
  49. try {
  50. const command = new GetObjectCommand({
  51. Bucket: process.env.VITE_DOCS_LIST_BUCKET,
  52. Key: content.Key,
  53. ResponseCacheControl: "no-cache",
  54. });
  55. const file = await s3.send(command);
  56. const writePath = path.join(DOC_BASE_PATH, content.Key!);
  57. const directory = path.dirname(writePath);
  58. fs.mkdirSync(directory, { recursive: true });
  59. fs.writeFileSync(writePath, await file.Body!.transformToString());
  60. resolve(content.Key);
  61. } catch (e) {
  62. reject(e);
  63. }
  64. fs.writeFile;
  65. });
  66. })
  67. );
  68. // 构建sideBar数据
  69. function readJsonFile(filePath) {
  70. try {
  71. // 同步读取文件内容,如果文件不存在会抛出异常
  72. const data = fs.readFileSync(filePath, 'utf8');
  73. return JSON.parse(data); // 解析 JSON 数据
  74. } catch (err) {
  75. if (err.code === 'ENOENT') {
  76. console.error('File not found:', filePath);
  77. // 可以返回默认值或者进行其他处理
  78. return {}; // 返回 null 或者其他默认值
  79. } else {
  80. throw err; // 抛出其他异常
  81. }
  82. }
  83. }
  84. const sideBarSortMap = {
  85. 'zh-CN': readJsonFile('pages/zh-CN::SIDEBAR_SORTED_MAP.json'),
  86. 'zh-HK': readJsonFile('pages/zh-HK::SIDEBAR_SORTED_MAP.json'),
  87. 'en-US': readJsonFile('pages/en-US::SIDEBAR_SORTED_MAP.json'),
  88. }
  89. let { rootSideBar, zhHKSideBar, enUSSideBar } = buildSideBar(contents, sideBarSortMap);
  90. import util from "util";
  91. console.log(
  92. util.inspect(sideBarSortMap, {showHidden: false, depth: null, colors: true}),
  93. util.inspect(rootSideBar, { showHidden: false, depth: null, colors: true }),
  94. );
  95. // https://vitepress.dev/reference/site-config
  96. export default defineConfig({
  97. title: "CocoBlockly帮助文档",
  98. description: "Cococlass help documents",
  99. srcDir: DOC_BASE_PATH,
  100. ignoreDeadLinks: true,
  101. lastUpdated: true,
  102. themeConfig: {
  103. search: {
  104. provider: "local",
  105. options: {
  106. locales: {
  107. root: {
  108. translations: {
  109. button: {
  110. buttonText: '搜索文档',
  111. buttonAriaLabel: '搜索文档'
  112. },
  113. modal: {
  114. noResultsText: '无法找到相关结果',
  115. resetButtonTitle: '清除查询条件',
  116. footer: {
  117. selectText: '选择',
  118. navigateText: '切换',
  119. closeText: '关闭',
  120. }
  121. }
  122. }
  123. },
  124. 'zh-HK': {
  125. translations: {
  126. button: {
  127. buttonText: '搜索文档hk',
  128. buttonAriaLabel: '搜索文档hk'
  129. },
  130. modal: {
  131. noResultsText: '无法找到相关结果',
  132. resetButtonTitle: '清除查询条件',
  133. footer: {
  134. selectText: '选择',
  135. navigateText: '切换',
  136. closeText: '关闭',
  137. }
  138. }
  139. }
  140. }
  141. }
  142. }
  143. },
  144. },
  145. appearance: false,
  146. vite: {
  147. publicDir: "../public",
  148. envDir: "../",
  149. ssr: {
  150. // SSG Vue-i18n workaround
  151. noExternal: [/vue-i18n/, 'element-plus'],
  152. },
  153. plugins: [
  154. VueI18nPlugin({}),
  155. Icons({
  156. compiler: "vue3",
  157. customCollections: {
  158. "ccrbi-plain": FileSystemIconLoader(
  159. "assets/icons/plain",
  160. async (svgStr) => {
  161. const svg = new SVG(svgStr);
  162. cleanupSVG(svg);
  163. parseColors(svg, {
  164. defaultColor: "currentColor",
  165. callback: (attr, colorStr, color) => {
  166. // console.log('Color:', colorStr, color);
  167. // 普通图标
  168. return "currentColor";
  169. // Change black to 'currentColor'
  170. // const blackColor = stringToColor("black");
  171. // if (color && compareColors(color, blackColor!)) {
  172. // return "currentColor";
  173. // }
  174. // switch (color?.type) {
  175. // case "none":
  176. // case "current":
  177. // return color;
  178. // }
  179. // throw new Error(
  180. // `Unexpected color "${colorStr}" in attribute ${attr}`
  181. // );
  182. },
  183. });
  184. // Optimise, but do not change shapes because they are animated
  185. runSVGO(svg, {
  186. keepShapes: true,
  187. });
  188. return svg.toMinifiedString({});
  189. }
  190. ),
  191. "ccrbi-colored": FileSystemIconLoader(
  192. "assets/icons/colored",
  193. async (svgStr) => {
  194. const svg = new SVG(svgStr);
  195. cleanupSVG(svg);
  196. // Optimise, but do not change shapes because they are animated
  197. runSVGO(svg, {
  198. keepShapes: true,
  199. });
  200. return svg.toMinifiedString({});
  201. }
  202. ),
  203. },
  204. }),
  205. ],
  206. resolve: {
  207. alias: [
  208. {
  209. find: /^.*\/VPNavBar\.vue$/,
  210. replacement: fileURLToPath(
  211. new URL("../components/CustomNavBar.vue", import.meta.url)
  212. ),
  213. },
  214. {
  215. find: "@/",
  216. replacement: fileURLToPath(new URL("../", import.meta.url)),
  217. },
  218. ],
  219. },
  220. },
  221. locales: {
  222. root: {
  223. label: "简体中文",
  224. lang: "zh-CN",
  225. themeConfig: {
  226. logo: "/logo.png",
  227. siteTitle: false,
  228. // https://vitepress.dev/reference/default-theme-config
  229. // nav: [
  230. // { text: 'Home', link: '/' },
  231. // { text: 'Examples', link: '/markdown-examples' }
  232. // ],
  233. // sidebar: [
  234. // { text: "关于CocoBlockly X", link: "/docs" },
  235. // { text: "常见问题解答", link: "/docs/常见问题解答" },
  236. // {
  237. // text: "开始使用CocoBlockly X",
  238. // link: "/docs/start-using-cocoblockly-x",
  239. // collapsed: true,
  240. // items: [],
  241. // },
  242. // {
  243. // text: "电子模块基本教学",
  244. // collapsed: true,
  245. // items: [{ text: "todo", link: "/docs" }],
  246. // },
  247. // ],
  248. sidebar: rootSideBar,
  249. // socialLinks: [
  250. // { icon: 'github', link: 'https://github.com/vuejs/vitepress' }
  251. // ]
  252. },
  253. },
  254. "zh-HK": {
  255. label: "繁体中文",
  256. lang: "zh-HK",
  257. themeConfig: {
  258. logo: "/logo.png",
  259. siteTitle: false,
  260. // https://vitepress.dev/reference/default-theme-config
  261. // nav: [
  262. // { text: 'Home', link: '/' },
  263. // { text: 'Examples', link: '/markdown-examples' }
  264. // ],
  265. // sidebar: [
  266. // { text: "什麽是CocoBlockly X", link: "/zh-HK/docs" },
  267. // { text: "常見問題解答", link: "/zh-HK/docs/faq" },
  268. // {
  269. // text: "開始使用CocoBlockly X",
  270. // link: "/zh-HK/docs/start-using-cocoblockly-x",
  271. // },
  272. // {
  273. // text: "電子模組基本教學",
  274. // collapsed: true,
  275. // items: [{ text: "todo", link: "/zh-HK/docs" }],
  276. // },
  277. // ],
  278. sidebar: zhHKSideBar,
  279. // socialLinks: [
  280. // { icon: 'github', link: 'https://github.com/vuejs/vitepress' }
  281. // ]
  282. },
  283. },
  284. "en-US": {
  285. label: "English",
  286. lang: "en-US",
  287. themeConfig: {
  288. logo: "/logo.png",
  289. siteTitle: false,
  290. // https://vitepress.dev/reference/default-theme-config
  291. // nav: [
  292. // { text: 'Home', link: '/' },
  293. // { text: 'Examples', link: '/markdown-examples' }
  294. // ],
  295. // sidebar: [
  296. // { text: "什麽是CocoBlockly X", link: "/zh-HK/docs" },
  297. // { text: "常見問題解答", link: "/zh-HK/docs/faq" },
  298. // {
  299. // text: "開始使用CocoBlockly X",
  300. // link: "/zh-HK/docs/start-using-cocoblockly-x",
  301. // },
  302. // {
  303. // text: "電子模組基本教學",
  304. // collapsed: true,
  305. // items: [{ text: "todo", link: "/zh-HK/docs" }],
  306. // },
  307. // ],
  308. sidebar: enUSSideBar,
  309. // socialLinks: [
  310. // { icon: 'github', link: 'https://github.com/vuejs/vitepress' }
  311. // ]
  312. },
  313. },
  314. },
  315. });