config.mts 9.0 KB

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