config.mts 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  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. themeConfig: {
  101. search: {
  102. provider: "local",
  103. options: {
  104. locales: {
  105. root: {
  106. translations: {
  107. button: {
  108. buttonText: '搜索文档',
  109. buttonAriaLabel: '搜索文档'
  110. },
  111. modal: {
  112. noResultsText: '无法找到相关结果',
  113. resetButtonTitle: '清除查询条件',
  114. footer: {
  115. selectText: '选择',
  116. navigateText: '切换',
  117. closeText: '关闭',
  118. }
  119. }
  120. }
  121. },
  122. 'zh-HK': {
  123. translations: {
  124. button: {
  125. buttonText: '搜索文档hk',
  126. buttonAriaLabel: '搜索文档hk'
  127. },
  128. modal: {
  129. noResultsText: '无法找到相关结果',
  130. resetButtonTitle: '清除查询条件',
  131. footer: {
  132. selectText: '选择',
  133. navigateText: '切换',
  134. closeText: '关闭',
  135. }
  136. }
  137. }
  138. }
  139. }
  140. }
  141. },
  142. },
  143. appearance: false,
  144. vite: {
  145. publicDir: "../public",
  146. envDir: "../",
  147. ssr: {
  148. // SSG Vue-i18n workaround
  149. noExternal: [/vue-i18n/, 'element-plus'],
  150. },
  151. plugins: [
  152. VueI18nPlugin({}),
  153. Icons({
  154. compiler: "vue3",
  155. customCollections: {
  156. "ccrbi-plain": FileSystemIconLoader(
  157. "assets/icons/plain",
  158. async (svgStr) => {
  159. const svg = new SVG(svgStr);
  160. cleanupSVG(svg);
  161. parseColors(svg, {
  162. defaultColor: "currentColor",
  163. callback: (attr, colorStr, color) => {
  164. // console.log('Color:', colorStr, color);
  165. // 普通图标
  166. return "currentColor";
  167. // Change black to 'currentColor'
  168. // const blackColor = stringToColor("black");
  169. // if (color && compareColors(color, blackColor!)) {
  170. // return "currentColor";
  171. // }
  172. // switch (color?.type) {
  173. // case "none":
  174. // case "current":
  175. // return color;
  176. // }
  177. // throw new Error(
  178. // `Unexpected color "${colorStr}" in attribute ${attr}`
  179. // );
  180. },
  181. });
  182. // Optimise, but do not change shapes because they are animated
  183. runSVGO(svg, {
  184. keepShapes: true,
  185. });
  186. return svg.toMinifiedString({});
  187. }
  188. ),
  189. "ccrbi-colored": FileSystemIconLoader(
  190. "assets/icons/colored",
  191. async (svgStr) => {
  192. const svg = new SVG(svgStr);
  193. cleanupSVG(svg);
  194. // Optimise, but do not change shapes because they are animated
  195. runSVGO(svg, {
  196. keepShapes: true,
  197. });
  198. return svg.toMinifiedString({});
  199. }
  200. ),
  201. },
  202. }),
  203. ],
  204. resolve: {
  205. alias: [
  206. {
  207. find: /^.*\/VPNavBar\.vue$/,
  208. replacement: fileURLToPath(
  209. new URL("../components/CustomNavBar.vue", import.meta.url)
  210. ),
  211. },
  212. {
  213. find: "@/",
  214. replacement: fileURLToPath(new URL("../", import.meta.url)),
  215. },
  216. ],
  217. },
  218. },
  219. locales: {
  220. root: {
  221. label: "简体中文",
  222. lang: "zh-CN",
  223. themeConfig: {
  224. logo: "/logo.png",
  225. siteTitle: false,
  226. // https://vitepress.dev/reference/default-theme-config
  227. // nav: [
  228. // { text: 'Home', link: '/' },
  229. // { text: 'Examples', link: '/markdown-examples' }
  230. // ],
  231. // sidebar: [
  232. // { text: "关于CocoBlockly X", link: "/docs" },
  233. // { text: "常见问题解答", link: "/docs/常见问题解答" },
  234. // {
  235. // text: "开始使用CocoBlockly X",
  236. // link: "/docs/start-using-cocoblockly-x",
  237. // collapsed: true,
  238. // items: [],
  239. // },
  240. // {
  241. // text: "电子模块基本教学",
  242. // collapsed: true,
  243. // items: [{ text: "todo", link: "/docs" }],
  244. // },
  245. // ],
  246. sidebar: rootSideBar,
  247. // socialLinks: [
  248. // { icon: 'github', link: 'https://github.com/vuejs/vitepress' }
  249. // ]
  250. },
  251. },
  252. "zh-HK": {
  253. label: "繁体中文",
  254. lang: "zh-HK",
  255. themeConfig: {
  256. logo: "/logo.png",
  257. siteTitle: false,
  258. // https://vitepress.dev/reference/default-theme-config
  259. // nav: [
  260. // { text: 'Home', link: '/' },
  261. // { text: 'Examples', link: '/markdown-examples' }
  262. // ],
  263. // sidebar: [
  264. // { text: "什麽是CocoBlockly X", link: "/zh-HK/docs" },
  265. // { text: "常見問題解答", link: "/zh-HK/docs/faq" },
  266. // {
  267. // text: "開始使用CocoBlockly X",
  268. // link: "/zh-HK/docs/start-using-cocoblockly-x",
  269. // },
  270. // {
  271. // text: "電子模組基本教學",
  272. // collapsed: true,
  273. // items: [{ text: "todo", link: "/zh-HK/docs" }],
  274. // },
  275. // ],
  276. sidebar: zhHKSideBar,
  277. // socialLinks: [
  278. // { icon: 'github', link: 'https://github.com/vuejs/vitepress' }
  279. // ]
  280. },
  281. },
  282. "en-US": {
  283. label: "English",
  284. lang: "en-US",
  285. themeConfig: {
  286. logo: "/logo.png",
  287. siteTitle: false,
  288. // https://vitepress.dev/reference/default-theme-config
  289. // nav: [
  290. // { text: 'Home', link: '/' },
  291. // { text: 'Examples', link: '/markdown-examples' }
  292. // ],
  293. // sidebar: [
  294. // { text: "什麽是CocoBlockly X", link: "/zh-HK/docs" },
  295. // { text: "常見問題解答", link: "/zh-HK/docs/faq" },
  296. // {
  297. // text: "開始使用CocoBlockly X",
  298. // link: "/zh-HK/docs/start-using-cocoblockly-x",
  299. // },
  300. // {
  301. // text: "電子模組基本教學",
  302. // collapsed: true,
  303. // items: [{ text: "todo", link: "/zh-HK/docs" }],
  304. // },
  305. // ],
  306. sidebar: enUSSideBar,
  307. // socialLinks: [
  308. // { icon: 'github', link: 'https://github.com/vuejs/vitepress' }
  309. // ]
  310. },
  311. },
  312. },
  313. });