@linyjs/plugin-docs 0.1.0

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.
Files changed (252) hide show
  1. package/README.md +97 -0
  2. package/docs/api-reference/index.md +341 -0
  3. package/docs/best-practices/error-handling.md +462 -0
  4. package/docs/best-practices/naming-conventions.md +495 -0
  5. package/docs/best-practices/performance.md +532 -0
  6. package/docs/best-practices/security.md +620 -0
  7. package/docs/faq.md +323 -0
  8. package/docs/getting-started/first-plugin.md +358 -0
  9. package/docs/getting-started/installation.md +244 -0
  10. package/docs/getting-started/quick-start.md +174 -0
  11. package/docs/guides/client-module.md +598 -0
  12. package/docs/guides/data-persistence.md +536 -0
  13. package/docs/guides/permissions.md +440 -0
  14. package/docs/guides/server-module.md +371 -0
  15. package/docs/guides/ui-slots.md +475 -0
  16. package/docs/migration/v0-to-v1.md +354 -0
  17. package/examples/analytics-plugin/README.md +416 -0
  18. package/examples/analytics-plugin/dist/client/index.d.ts +36 -0
  19. package/examples/analytics-plugin/dist/client/index.js +173 -0
  20. package/examples/analytics-plugin/dist/index.d.ts +64 -0
  21. package/examples/analytics-plugin/dist/index.js +72 -0
  22. package/examples/analytics-plugin/dist/server/index.d.ts +119 -0
  23. package/examples/analytics-plugin/dist/server/index.js +313 -0
  24. package/examples/analytics-plugin/package.json +28 -0
  25. package/examples/analytics-plugin/src/client/index.tsx +410 -0
  26. package/examples/analytics-plugin/src/index.ts +71 -0
  27. package/examples/analytics-plugin/src/server/index.ts +348 -0
  28. package/examples/analytics-plugin/tsconfig.json +21 -0
  29. package/examples/blog-plugin/README.md +316 -0
  30. package/examples/blog-plugin/dist/client/index.d.ts +22 -0
  31. package/examples/blog-plugin/dist/client/index.js +197 -0
  32. package/examples/blog-plugin/dist/server/index.d.ts +5 -0
  33. package/examples/blog-plugin/dist/server/index.js +220 -0
  34. package/examples/blog-plugin/package.json +24 -0
  35. package/examples/blog-plugin/src/client/index.tsx +405 -0
  36. package/examples/blog-plugin/src/server/index.ts +240 -0
  37. package/examples/blog-plugin/tsconfig.json +16 -0
  38. package/examples/hello-world/README.md +101 -0
  39. package/examples/hello-world/dist/client/index.d.ts +5 -0
  40. package/examples/hello-world/dist/client/index.js +80 -0
  41. package/examples/hello-world/dist/server/index.d.ts +5 -0
  42. package/examples/hello-world/dist/server/index.js +38 -0
  43. package/examples/hello-world/node_modules/.package-lock.json +61 -0
  44. package/examples/hello-world/node_modules/@linyjs/client-module-interface/HOOKS_USAGE.md +281 -0
  45. package/examples/hello-world/node_modules/@linyjs/client-module-interface/IMPLEMENTATION_SUMMARY.md +166 -0
  46. package/examples/hello-world/node_modules/@linyjs/client-module-interface/QUICK_REFERENCE.md +130 -0
  47. package/examples/hello-world/node_modules/@linyjs/client-module-interface/README.md +214 -0
  48. package/examples/hello-world/node_modules/@linyjs/client-module-interface/package.json +26 -0
  49. package/examples/hello-world/node_modules/@linyjs/client-module-interface/src/application.ts +50 -0
  50. package/examples/hello-world/node_modules/@linyjs/client-module-interface/src/comparison-example.tsx +229 -0
  51. package/examples/hello-world/node_modules/@linyjs/client-module-interface/src/di.ts +127 -0
  52. package/examples/hello-world/node_modules/@linyjs/client-module-interface/src/domain.ts +52 -0
  53. package/examples/hello-world/node_modules/@linyjs/client-module-interface/src/hooks-example.tsx +275 -0
  54. package/examples/hello-world/node_modules/@linyjs/client-module-interface/src/hooks.ts +175 -0
  55. package/examples/hello-world/node_modules/@linyjs/client-module-interface/src/index.ts +87 -0
  56. package/examples/hello-world/node_modules/@linyjs/client-module-interface/src/interfaces.ts +117 -0
  57. package/examples/hello-world/node_modules/@linyjs/client-module-interface/src/router.ts +49 -0
  58. package/examples/hello-world/node_modules/@linyjs/client-module-interface/tsconfig.json +10 -0
  59. package/examples/hello-world/node_modules/@linyjs/server-module-interface/package.json +21 -0
  60. package/examples/hello-world/node_modules/@linyjs/server-module-interface/src/application.ts +21 -0
  61. package/examples/hello-world/node_modules/@linyjs/server-module-interface/src/di.ts +127 -0
  62. package/examples/hello-world/node_modules/@linyjs/server-module-interface/src/domain.ts +75 -0
  63. package/examples/hello-world/node_modules/@linyjs/server-module-interface/src/index.ts +55 -0
  64. package/examples/hello-world/node_modules/@linyjs/server-module-interface/src/interfaces.ts +58 -0
  65. package/examples/hello-world/node_modules/@linyjs/server-module-interface/tsconfig.json +8 -0
  66. package/examples/hello-world/node_modules/@types/react/LICENSE +21 -0
  67. package/examples/hello-world/node_modules/@types/react/README.md +15 -0
  68. package/examples/hello-world/node_modules/@types/react/canary.d.ts +129 -0
  69. package/examples/hello-world/node_modules/@types/react/compiler-runtime.d.ts +4 -0
  70. package/examples/hello-world/node_modules/@types/react/experimental.d.ts +184 -0
  71. package/examples/hello-world/node_modules/@types/react/global.d.ts +166 -0
  72. package/examples/hello-world/node_modules/@types/react/index.d.ts +4369 -0
  73. package/examples/hello-world/node_modules/@types/react/jsx-dev-runtime.d.ts +33 -0
  74. package/examples/hello-world/node_modules/@types/react/jsx-runtime.d.ts +24 -0
  75. package/examples/hello-world/node_modules/@types/react/package.json +210 -0
  76. package/examples/hello-world/node_modules/@types/react/ts5.0/canary.d.ts +129 -0
  77. package/examples/hello-world/node_modules/@types/react/ts5.0/experimental.d.ts +184 -0
  78. package/examples/hello-world/node_modules/@types/react/ts5.0/global.d.ts +166 -0
  79. package/examples/hello-world/node_modules/@types/react/ts5.0/index.d.ts +4356 -0
  80. package/examples/hello-world/node_modules/@types/react/ts5.0/jsx-dev-runtime.d.ts +33 -0
  81. package/examples/hello-world/node_modules/@types/react/ts5.0/jsx-runtime.d.ts +24 -0
  82. package/examples/hello-world/node_modules/csstype/LICENSE +19 -0
  83. package/examples/hello-world/node_modules/csstype/README.md +291 -0
  84. package/examples/hello-world/node_modules/csstype/index.d.ts +22569 -0
  85. package/examples/hello-world/node_modules/csstype/index.js.flow +6863 -0
  86. package/examples/hello-world/node_modules/csstype/package.json +70 -0
  87. package/examples/hello-world/node_modules/react/LICENSE +21 -0
  88. package/examples/hello-world/node_modules/react/README.md +37 -0
  89. package/examples/hello-world/node_modules/react/cjs/react-compiler-runtime.development.js +24 -0
  90. package/examples/hello-world/node_modules/react/cjs/react-compiler-runtime.production.js +16 -0
  91. package/examples/hello-world/node_modules/react/cjs/react-compiler-runtime.profiling.js +16 -0
  92. package/examples/hello-world/node_modules/react/cjs/react-jsx-dev-runtime.development.js +338 -0
  93. package/examples/hello-world/node_modules/react/cjs/react-jsx-dev-runtime.production.js +14 -0
  94. package/examples/hello-world/node_modules/react/cjs/react-jsx-dev-runtime.profiling.js +14 -0
  95. package/examples/hello-world/node_modules/react/cjs/react-jsx-dev-runtime.react-server.development.js +370 -0
  96. package/examples/hello-world/node_modules/react/cjs/react-jsx-dev-runtime.react-server.production.js +40 -0
  97. package/examples/hello-world/node_modules/react/cjs/react-jsx-runtime.development.js +352 -0
  98. package/examples/hello-world/node_modules/react/cjs/react-jsx-runtime.production.js +34 -0
  99. package/examples/hello-world/node_modules/react/cjs/react-jsx-runtime.profiling.js +34 -0
  100. package/examples/hello-world/node_modules/react/cjs/react-jsx-runtime.react-server.development.js +370 -0
  101. package/examples/hello-world/node_modules/react/cjs/react-jsx-runtime.react-server.production.js +40 -0
  102. package/examples/hello-world/node_modules/react/cjs/react.development.js +1284 -0
  103. package/examples/hello-world/node_modules/react/cjs/react.production.js +542 -0
  104. package/examples/hello-world/node_modules/react/cjs/react.react-server.development.js +848 -0
  105. package/examples/hello-world/node_modules/react/cjs/react.react-server.production.js +423 -0
  106. package/examples/hello-world/node_modules/react/compiler-runtime.js +14 -0
  107. package/examples/hello-world/node_modules/react/index.js +7 -0
  108. package/examples/hello-world/node_modules/react/jsx-dev-runtime.js +7 -0
  109. package/examples/hello-world/node_modules/react/jsx-dev-runtime.react-server.js +7 -0
  110. package/examples/hello-world/node_modules/react/jsx-runtime.js +7 -0
  111. package/examples/hello-world/node_modules/react/jsx-runtime.react-server.js +7 -0
  112. package/examples/hello-world/node_modules/react/package.json +51 -0
  113. package/examples/hello-world/node_modules/react/react.react-server.js +7 -0
  114. package/examples/hello-world/node_modules/typescript/LICENSE.txt +55 -0
  115. package/examples/hello-world/node_modules/typescript/README.md +50 -0
  116. package/examples/hello-world/node_modules/typescript/SECURITY.md +41 -0
  117. package/examples/hello-world/node_modules/typescript/ThirdPartyNoticeText.txt +193 -0
  118. package/examples/hello-world/node_modules/typescript/bin/tsc +2 -0
  119. package/examples/hello-world/node_modules/typescript/bin/tsserver +2 -0
  120. package/examples/hello-world/node_modules/typescript/lib/_tsc.js +133818 -0
  121. package/examples/hello-world/node_modules/typescript/lib/_tsserver.js +659 -0
  122. package/examples/hello-world/node_modules/typescript/lib/_typingsInstaller.js +222 -0
  123. package/examples/hello-world/node_modules/typescript/lib/cs/diagnosticMessages.generated.json +2122 -0
  124. package/examples/hello-world/node_modules/typescript/lib/de/diagnosticMessages.generated.json +2122 -0
  125. package/examples/hello-world/node_modules/typescript/lib/es/diagnosticMessages.generated.json +2122 -0
  126. package/examples/hello-world/node_modules/typescript/lib/fr/diagnosticMessages.generated.json +2122 -0
  127. package/examples/hello-world/node_modules/typescript/lib/it/diagnosticMessages.generated.json +2122 -0
  128. package/examples/hello-world/node_modules/typescript/lib/ja/diagnosticMessages.generated.json +2122 -0
  129. package/examples/hello-world/node_modules/typescript/lib/ko/diagnosticMessages.generated.json +2122 -0
  130. package/examples/hello-world/node_modules/typescript/lib/lib.d.ts +22 -0
  131. package/examples/hello-world/node_modules/typescript/lib/lib.decorators.d.ts +384 -0
  132. package/examples/hello-world/node_modules/typescript/lib/lib.decorators.legacy.d.ts +22 -0
  133. package/examples/hello-world/node_modules/typescript/lib/lib.dom.asynciterable.d.ts +41 -0
  134. package/examples/hello-world/node_modules/typescript/lib/lib.dom.d.ts +39429 -0
  135. package/examples/hello-world/node_modules/typescript/lib/lib.dom.iterable.d.ts +571 -0
  136. package/examples/hello-world/node_modules/typescript/lib/lib.es2015.collection.d.ts +147 -0
  137. package/examples/hello-world/node_modules/typescript/lib/lib.es2015.core.d.ts +597 -0
  138. package/examples/hello-world/node_modules/typescript/lib/lib.es2015.d.ts +28 -0
  139. package/examples/hello-world/node_modules/typescript/lib/lib.es2015.generator.d.ts +77 -0
  140. package/examples/hello-world/node_modules/typescript/lib/lib.es2015.iterable.d.ts +605 -0
  141. package/examples/hello-world/node_modules/typescript/lib/lib.es2015.promise.d.ts +81 -0
  142. package/examples/hello-world/node_modules/typescript/lib/lib.es2015.proxy.d.ts +128 -0
  143. package/examples/hello-world/node_modules/typescript/lib/lib.es2015.reflect.d.ts +144 -0
  144. package/examples/hello-world/node_modules/typescript/lib/lib.es2015.symbol.d.ts +46 -0
  145. package/examples/hello-world/node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts +326 -0
  146. package/examples/hello-world/node_modules/typescript/lib/lib.es2016.array.include.d.ts +116 -0
  147. package/examples/hello-world/node_modules/typescript/lib/lib.es2016.d.ts +21 -0
  148. package/examples/hello-world/node_modules/typescript/lib/lib.es2016.full.d.ts +23 -0
  149. package/examples/hello-world/node_modules/typescript/lib/lib.es2016.intl.d.ts +31 -0
  150. package/examples/hello-world/node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts +21 -0
  151. package/examples/hello-world/node_modules/typescript/lib/lib.es2017.d.ts +26 -0
  152. package/examples/hello-world/node_modules/typescript/lib/lib.es2017.date.d.ts +31 -0
  153. package/examples/hello-world/node_modules/typescript/lib/lib.es2017.full.d.ts +23 -0
  154. package/examples/hello-world/node_modules/typescript/lib/lib.es2017.intl.d.ts +44 -0
  155. package/examples/hello-world/node_modules/typescript/lib/lib.es2017.object.d.ts +49 -0
  156. package/examples/hello-world/node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts +135 -0
  157. package/examples/hello-world/node_modules/typescript/lib/lib.es2017.string.d.ts +45 -0
  158. package/examples/hello-world/node_modules/typescript/lib/lib.es2017.typedarrays.d.ts +53 -0
  159. package/examples/hello-world/node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts +77 -0
  160. package/examples/hello-world/node_modules/typescript/lib/lib.es2018.asynciterable.d.ts +53 -0
  161. package/examples/hello-world/node_modules/typescript/lib/lib.es2018.d.ts +24 -0
  162. package/examples/hello-world/node_modules/typescript/lib/lib.es2018.full.d.ts +24 -0
  163. package/examples/hello-world/node_modules/typescript/lib/lib.es2018.intl.d.ts +83 -0
  164. package/examples/hello-world/node_modules/typescript/lib/lib.es2018.promise.d.ts +30 -0
  165. package/examples/hello-world/node_modules/typescript/lib/lib.es2018.regexp.d.ts +37 -0
  166. package/examples/hello-world/node_modules/typescript/lib/lib.es2019.array.d.ts +79 -0
  167. package/examples/hello-world/node_modules/typescript/lib/lib.es2019.d.ts +24 -0
  168. package/examples/hello-world/node_modules/typescript/lib/lib.es2019.full.d.ts +24 -0
  169. package/examples/hello-world/node_modules/typescript/lib/lib.es2019.intl.d.ts +23 -0
  170. package/examples/hello-world/node_modules/typescript/lib/lib.es2019.object.d.ts +33 -0
  171. package/examples/hello-world/node_modules/typescript/lib/lib.es2019.string.d.ts +37 -0
  172. package/examples/hello-world/node_modules/typescript/lib/lib.es2019.symbol.d.ts +24 -0
  173. package/examples/hello-world/node_modules/typescript/lib/lib.es2020.bigint.d.ts +765 -0
  174. package/examples/hello-world/node_modules/typescript/lib/lib.es2020.d.ts +27 -0
  175. package/examples/hello-world/node_modules/typescript/lib/lib.es2020.date.d.ts +42 -0
  176. package/examples/hello-world/node_modules/typescript/lib/lib.es2020.full.d.ts +24 -0
  177. package/examples/hello-world/node_modules/typescript/lib/lib.es2020.intl.d.ts +474 -0
  178. package/examples/hello-world/node_modules/typescript/lib/lib.es2020.number.d.ts +28 -0
  179. package/examples/hello-world/node_modules/typescript/lib/lib.es2020.promise.d.ts +47 -0
  180. package/examples/hello-world/node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts +99 -0
  181. package/examples/hello-world/node_modules/typescript/lib/lib.es2020.string.d.ts +44 -0
  182. package/examples/hello-world/node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts +41 -0
  183. package/examples/hello-world/node_modules/typescript/lib/lib.es2021.d.ts +23 -0
  184. package/examples/hello-world/node_modules/typescript/lib/lib.es2021.full.d.ts +24 -0
  185. package/examples/hello-world/node_modules/typescript/lib/lib.es2021.intl.d.ts +166 -0
  186. package/examples/hello-world/node_modules/typescript/lib/lib.es2021.promise.d.ts +48 -0
  187. package/examples/hello-world/node_modules/typescript/lib/lib.es2021.string.d.ts +33 -0
  188. package/examples/hello-world/node_modules/typescript/lib/lib.es2021.weakref.d.ts +78 -0
  189. package/examples/hello-world/node_modules/typescript/lib/lib.es2022.array.d.ts +121 -0
  190. package/examples/hello-world/node_modules/typescript/lib/lib.es2022.d.ts +25 -0
  191. package/examples/hello-world/node_modules/typescript/lib/lib.es2022.error.d.ts +75 -0
  192. package/examples/hello-world/node_modules/typescript/lib/lib.es2022.full.d.ts +24 -0
  193. package/examples/hello-world/node_modules/typescript/lib/lib.es2022.intl.d.ts +145 -0
  194. package/examples/hello-world/node_modules/typescript/lib/lib.es2022.object.d.ts +26 -0
  195. package/examples/hello-world/node_modules/typescript/lib/lib.es2022.regexp.d.ts +39 -0
  196. package/examples/hello-world/node_modules/typescript/lib/lib.es2022.string.d.ts +25 -0
  197. package/examples/hello-world/node_modules/typescript/lib/lib.es2023.array.d.ts +924 -0
  198. package/examples/hello-world/node_modules/typescript/lib/lib.es2023.collection.d.ts +21 -0
  199. package/examples/hello-world/node_modules/typescript/lib/lib.es2023.d.ts +22 -0
  200. package/examples/hello-world/node_modules/typescript/lib/lib.es2023.full.d.ts +24 -0
  201. package/examples/hello-world/node_modules/typescript/lib/lib.es2023.intl.d.ts +56 -0
  202. package/examples/hello-world/node_modules/typescript/lib/lib.es2024.arraybuffer.d.ts +65 -0
  203. package/examples/hello-world/node_modules/typescript/lib/lib.es2024.collection.d.ts +29 -0
  204. package/examples/hello-world/node_modules/typescript/lib/lib.es2024.d.ts +26 -0
  205. package/examples/hello-world/node_modules/typescript/lib/lib.es2024.full.d.ts +24 -0
  206. package/examples/hello-world/node_modules/typescript/lib/lib.es2024.object.d.ts +29 -0
  207. package/examples/hello-world/node_modules/typescript/lib/lib.es2024.promise.d.ts +35 -0
  208. package/examples/hello-world/node_modules/typescript/lib/lib.es2024.regexp.d.ts +25 -0
  209. package/examples/hello-world/node_modules/typescript/lib/lib.es2024.sharedmemory.d.ts +68 -0
  210. package/examples/hello-world/node_modules/typescript/lib/lib.es2024.string.d.ts +29 -0
  211. package/examples/hello-world/node_modules/typescript/lib/lib.es5.d.ts +4601 -0
  212. package/examples/hello-world/node_modules/typescript/lib/lib.es6.d.ts +23 -0
  213. package/examples/hello-world/node_modules/typescript/lib/lib.esnext.array.d.ts +35 -0
  214. package/examples/hello-world/node_modules/typescript/lib/lib.esnext.collection.d.ts +96 -0
  215. package/examples/hello-world/node_modules/typescript/lib/lib.esnext.d.ts +29 -0
  216. package/examples/hello-world/node_modules/typescript/lib/lib.esnext.decorators.d.ts +28 -0
  217. package/examples/hello-world/node_modules/typescript/lib/lib.esnext.disposable.d.ts +193 -0
  218. package/examples/hello-world/node_modules/typescript/lib/lib.esnext.error.d.ts +24 -0
  219. package/examples/hello-world/node_modules/typescript/lib/lib.esnext.float16.d.ts +445 -0
  220. package/examples/hello-world/node_modules/typescript/lib/lib.esnext.full.d.ts +24 -0
  221. package/examples/hello-world/node_modules/typescript/lib/lib.esnext.intl.d.ts +21 -0
  222. package/examples/hello-world/node_modules/typescript/lib/lib.esnext.iterator.d.ts +148 -0
  223. package/examples/hello-world/node_modules/typescript/lib/lib.esnext.promise.d.ts +34 -0
  224. package/examples/hello-world/node_modules/typescript/lib/lib.esnext.sharedmemory.d.ts +25 -0
  225. package/examples/hello-world/node_modules/typescript/lib/lib.scripthost.d.ts +322 -0
  226. package/examples/hello-world/node_modules/typescript/lib/lib.webworker.asynciterable.d.ts +41 -0
  227. package/examples/hello-world/node_modules/typescript/lib/lib.webworker.d.ts +13150 -0
  228. package/examples/hello-world/node_modules/typescript/lib/lib.webworker.importscripts.d.ts +23 -0
  229. package/examples/hello-world/node_modules/typescript/lib/lib.webworker.iterable.d.ts +340 -0
  230. package/examples/hello-world/node_modules/typescript/lib/pl/diagnosticMessages.generated.json +2122 -0
  231. package/examples/hello-world/node_modules/typescript/lib/pt-br/diagnosticMessages.generated.json +2122 -0
  232. package/examples/hello-world/node_modules/typescript/lib/ru/diagnosticMessages.generated.json +2122 -0
  233. package/examples/hello-world/node_modules/typescript/lib/tr/diagnosticMessages.generated.json +2122 -0
  234. package/examples/hello-world/node_modules/typescript/lib/tsc.js +8 -0
  235. package/examples/hello-world/node_modules/typescript/lib/tsserver.js +8 -0
  236. package/examples/hello-world/node_modules/typescript/lib/tsserverlibrary.d.ts +17 -0
  237. package/examples/hello-world/node_modules/typescript/lib/tsserverlibrary.js +21 -0
  238. package/examples/hello-world/node_modules/typescript/lib/typesMap.json +497 -0
  239. package/examples/hello-world/node_modules/typescript/lib/typescript.d.ts +11437 -0
  240. package/examples/hello-world/node_modules/typescript/lib/typescript.js +200276 -0
  241. package/examples/hello-world/node_modules/typescript/lib/typingsInstaller.js +8 -0
  242. package/examples/hello-world/node_modules/typescript/lib/watchGuard.js +53 -0
  243. package/examples/hello-world/node_modules/typescript/lib/zh-cn/diagnosticMessages.generated.json +2122 -0
  244. package/examples/hello-world/node_modules/typescript/lib/zh-tw/diagnosticMessages.generated.json +2122 -0
  245. package/examples/hello-world/node_modules/typescript/package.json +120 -0
  246. package/examples/hello-world/package-lock.json +74 -0
  247. package/examples/hello-world/package.json +23 -0
  248. package/examples/hello-world/src/client/index.tsx +110 -0
  249. package/examples/hello-world/src/server/index.ts +42 -0
  250. package/examples/hello-world/tsconfig.json +16 -0
  251. package/package.json +42 -0
  252. package/src/cli.ts +321 -0
@@ -0,0 +1,405 @@
1
+ import React from 'react';
2
+ import type { IModule } from '@linyjs/client-module-interface';
3
+
4
+ const API_BASE = '/api/articles';
5
+
6
+ /**
7
+ * 文章列表组件
8
+ */
9
+ function ArticleList() {
10
+ const [articles, setArticles] = React.useState<any[]>([]);
11
+ const [loading, setLoading] = React.useState(true);
12
+ const [error, setError] = React.useState<string | null>(null);
13
+
14
+ React.useEffect(() => {
15
+ loadArticles();
16
+ }, []);
17
+
18
+ const loadArticles = async () => {
19
+ try {
20
+ setLoading(true);
21
+ const response = await fetch(API_BASE);
22
+ const result = await response.json();
23
+ setArticles(result.data || result);
24
+ } catch (err: any) {
25
+ setError(err.message || 'Failed to load articles');
26
+ } finally {
27
+ setLoading(false);
28
+ }
29
+ };
30
+
31
+ if (loading) return <div className="text-center py-8">加载中...</div>;
32
+ if (error) return <div className="text-red-500 text-center py-8">错误: {error}</div>;
33
+
34
+ return (
35
+ <div className="max-w-4xl mx-auto px-4 py-8">
36
+ <div className="flex justify-between items-center mb-8">
37
+ <h1 className="text-3xl font-bold text-gray-900">文章列表</h1>
38
+ <button
39
+ onClick={() => window.location.href = '/blog/new'}
40
+ className="bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700"
41
+ >
42
+ 写文章
43
+ </button>
44
+ </div>
45
+
46
+ {articles.length === 0 ? (
47
+ <div className="text-center py-12">
48
+ <p className="text-gray-500 mb-4">暂无文章</p>
49
+ <button
50
+ onClick={() => window.location.href = '/blog/new'}
51
+ className="text-blue-600 hover:underline"
52
+ >
53
+ 写第一篇文章
54
+ </button>
55
+ </div>
56
+ ) : (
57
+ <div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
58
+ {articles.map((article) => (
59
+ <div key={article.id} className="bg-white rounded-lg shadow-md p-6 hover:shadow-lg transition-shadow">
60
+ <h2 className="text-xl font-semibold mb-2 text-gray-900">
61
+ <a
62
+ href={`/blog/${article.id}`}
63
+ className="hover:text-blue-600"
64
+ >
65
+ {article.title}
66
+ </a>
67
+ </h2>
68
+ <p className="text-gray-600 mb-4 line-clamp-3">
69
+ {article.content?.substring(0, 120)}...
70
+ </p>
71
+ <div className="flex justify-between items-center text-sm text-gray-500">
72
+ <span className="font-medium">{article.author || '匿名'}</span>
73
+ <span>{new Date(article.createdAt).toLocaleDateString()}</span>
74
+ </div>
75
+ </div>
76
+ ))}
77
+ </div>
78
+ )}
79
+ </div>
80
+ );
81
+ }
82
+
83
+ /**
84
+ * 文章详情组件
85
+ */
86
+ function ArticleDetail() {
87
+ const [article, setArticle] = React.useState<any>(null);
88
+ const [loading, setLoading] = React.useState(true);
89
+ const [error, setError] = React.useState<string | null>(null);
90
+
91
+ // 获取URL参数
92
+ const urlParams = new URLSearchParams(window.location.search);
93
+ const id = urlParams.get('id');
94
+
95
+ React.useEffect(() => {
96
+ if (id) {
97
+ loadArticle(id);
98
+ }
99
+ }, [id]);
100
+
101
+ const loadArticle = async (articleId: string) => {
102
+ try {
103
+ setLoading(true);
104
+ const response = await fetch(`${API_BASE}/${articleId}`);
105
+ const result = await response.json();
106
+ setArticle(result);
107
+ } catch (err: any) {
108
+ setError(err.message || 'Failed to load article');
109
+ } finally {
110
+ setLoading(false);
111
+ }
112
+ };
113
+
114
+ if (loading) return <div className="text-center py-8">加载中...</div>;
115
+ if (error) return <div className="text-red-500 text-center py-8">错误: {error}</div>;
116
+ if (!article) return <div className="text-center py-8">文章不存在</div>;
117
+
118
+ return (
119
+ <div className="max-w-4xl mx-auto px-4 py-8">
120
+ <button
121
+ onClick={() => window.history.back()}
122
+ className="mb-4 text-blue-600 hover:underline"
123
+ >
124
+ ← 返回列表
125
+ </button>
126
+
127
+ <article className="bg-white rounded-lg shadow-md p-8">
128
+ <h1 className="text-4xl font-bold mb-4 text-gray-900">{article.title}</h1>
129
+
130
+ <div className="flex justify-between items-center mb-8 pb-4 border-b">
131
+ <div className="text-gray-600">
132
+ <span className="font-medium">作者: {article.author || '匿名'}</span>
133
+ </div>
134
+ <div className="text-gray-500">
135
+ <span>发布于 {new Date(article.createdAt).toLocaleString()}</span>
136
+ </div>
137
+ </div>
138
+
139
+ <div className="prose prose-lg max-w-none">
140
+ <div dangerouslySetInnerHTML={{ __html: formatContent(article.content) }} />
141
+ </div>
142
+
143
+ {article.updatedAt && article.updatedAt !== article.createdAt && (
144
+ <div className="mt-8 pt-4 border-t text-sm text-gray-500">
145
+ <span>最后编辑于 {new Date(article.updatedAt).toLocaleString()}</span>
146
+ </div>
147
+ )}
148
+ </article>
149
+ </div>
150
+ );
151
+ }
152
+
153
+ /**
154
+ * 文章编辑表单组件
155
+ */
156
+ function ArticleForm() {
157
+ const [title, setTitle] = React.useState('');
158
+ const [content, setContent] = React.useState('');
159
+ const [author, setAuthor] = React.useState('');
160
+ const [loading, setLoading] = React.useState(false);
161
+ const [error, setError] = React.useState<string | null>(null);
162
+
163
+ // 检查是否为编辑模式
164
+ const urlParams = new URLSearchParams(window.location.search);
165
+ const editId = urlParams.get('edit');
166
+ const isEdit = Boolean(editId);
167
+
168
+ React.useEffect(() => {
169
+ if (isEdit && editId) {
170
+ loadArticleForEdit(editId);
171
+ }
172
+ }, [isEdit, editId]);
173
+
174
+ const loadArticleForEdit = async (articleId: string) => {
175
+ try {
176
+ const response = await fetch(`${API_BASE}/${articleId}`);
177
+ const article = await response.json();
178
+ setTitle(article.title || '');
179
+ setContent(article.content || '');
180
+ setAuthor(article.author || '');
181
+ } catch (err: any) {
182
+ setError('加载文章失败: ' + err.message);
183
+ }
184
+ };
185
+
186
+ const handleSubmit = async (e: React.FormEvent) => {
187
+ e.preventDefault();
188
+
189
+ if (!title.trim() || !content.trim()) {
190
+ setError('标题和内容不能为空');
191
+ return;
192
+ }
193
+
194
+ setLoading(true);
195
+ setError(null);
196
+
197
+ try {
198
+ const method = isEdit ? 'PUT' : 'POST';
199
+ const url = isEdit ? `${API_BASE}/${editId}` : API_BASE;
200
+
201
+ const response = await fetch(url, {
202
+ method,
203
+ headers: { 'Content-Type': 'application/json' },
204
+ body: JSON.stringify({ title, content, author })
205
+ });
206
+
207
+ const result = await response.json();
208
+
209
+ if (result.success) {
210
+ // 重定向到文章列表
211
+ window.location.href = '/blog';
212
+ } else {
213
+ throw new Error(result.data?.error || '保存失败');
214
+ }
215
+ } catch (err: any) {
216
+ setError(err.message);
217
+ } finally {
218
+ setLoading(false);
219
+ }
220
+ };
221
+
222
+ return (
223
+ <div className="max-w-4xl mx-auto px-4 py-8">
224
+ <button
225
+ onClick={() => window.history.back()}
226
+ className="mb-4 text-blue-600 hover:underline"
227
+ >
228
+ ← 返回
229
+ </button>
230
+
231
+ <div className="bg-white rounded-lg shadow-md p-8">
232
+ <h1 className="text-3xl font-bold mb-8 text-gray-900">
233
+ {isEdit ? '编辑文章' : '写新文章'}
234
+ </h1>
235
+
236
+ {error && (
237
+ <div className="mb-4 p-3 bg-red-100 text-red-700 rounded">
238
+ {error}
239
+ </div>
240
+ )}
241
+
242
+ <form onSubmit={handleSubmit} className="space-y-6">
243
+ <div>
244
+ <label className="block text-sm font-medium text-gray-700 mb-2">
245
+ 标题 *
246
+ </label>
247
+ <input
248
+ type="text"
249
+ value={title}
250
+ onChange={(e) => setTitle(e.target.value)}
251
+ className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
252
+ placeholder="输入文章标题"
253
+ required
254
+ />
255
+ </div>
256
+
257
+ <div>
258
+ <label className="block text-sm font-medium text-gray-700 mb-2">
259
+ 作者
260
+ </label>
261
+ <input
262
+ type="text"
263
+ value={author}
264
+ onChange={(e) => setAuthor(e.target.value)}
265
+ className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
266
+ placeholder="作者姓名 (可选)"
267
+ />
268
+ </div>
269
+
270
+ <div>
271
+ <label className="block text-sm font-medium text-gray-700 mb-2">
272
+ 内容 *
273
+ </label>
274
+ <textarea
275
+ value={content}
276
+ onChange={(e) => setContent(e.target.value)}
277
+ rows={12}
278
+ className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
279
+ placeholder="在这里写下你的文章内容..."
280
+ required
281
+ />
282
+ </div>
283
+
284
+ <div className="flex justify-end space-x-4">
285
+ <button
286
+ type="button"
287
+ onClick={() => window.history.back()}
288
+ className="px-6 py-2 border border-gray-300 text-gray-700 rounded-md hover:bg-gray-50"
289
+ >
290
+ 取消
291
+ </button>
292
+ <button
293
+ type="submit"
294
+ disabled={loading}
295
+ className="px-6 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 disabled:opacity-50"
296
+ >
297
+ {loading ? '保存中...' : (isEdit ? '更新' : '发布')}
298
+ </button>
299
+ </div>
300
+ </form>
301
+ </div>
302
+ </div>
303
+ );
304
+ }
305
+
306
+ /**
307
+ * 博客主页组件
308
+ */
309
+ function BlogHome() {
310
+ return (
311
+ <div className="min-h-screen bg-gray-50">
312
+ <nav className="bg-white shadow-sm">
313
+ <div className="max-w-4xl mx-auto px-4 py-4">
314
+ <div className="flex justify-between items-center">
315
+ <h1 className="text-2xl font-bold text-gray-900">
316
+ <a href="/blog" className="hover:text-blue-600">我的博客</a>
317
+ </h1>
318
+ <div className="flex space-x-4">
319
+ <a href="/blog" className="text-gray-600 hover:text-blue-600">文章列表</a>
320
+ <a href="/blog/new" className="text-gray-600 hover:text-blue-600">写文章</a>
321
+ </div>
322
+ </div>
323
+ </div>
324
+ </nav>
325
+
326
+ <main>
327
+ {/* 根据URL参数显示不同组件 */}
328
+ <div id="blog-content">
329
+ {window.location.search.includes('id=') ? (
330
+ <ArticleDetail />
331
+ ) : window.location.pathname.includes('/new') || window.location.search.includes('edit=') ? (
332
+ <ArticleForm />
333
+ ) : (
334
+ <ArticleList />
335
+ )}
336
+ </div>
337
+ </main>
338
+ </div>
339
+ );
340
+ }
341
+
342
+ /**
343
+ * 简单的markdown格式化(支持基本的HTML标签)
344
+ */
345
+ function formatContent(content: string): string {
346
+ return content
347
+ .replace(/\n/g, '<br>')
348
+ .replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
349
+ .replace(/\*(.*?)\*/g, '<em>$1</em>')
350
+ .replace(/`(.*?)`/g, '<code>$1</code>');
351
+ }
352
+
353
+ /**
354
+ * 客户端模块定义
355
+ */
356
+ export const blogClientModule: IModule = {
357
+ name: 'blogClient',
358
+ serviceDefs: [
359
+ {
360
+ serviceTag: 'blogRoute',
361
+ serviceImpl: class BlogRouteService {
362
+ basePath = '/blog';
363
+
364
+ getRoutes() {
365
+ return [
366
+ {
367
+ path: '/',
368
+ component: BlogHome,
369
+ key: 'blog-home',
370
+ meta: {
371
+ title: '博客首页 - linyjs示例',
372
+ description: '一个简单的博客示例,展示linyjs插件开发'
373
+ }
374
+ }
375
+ ];
376
+ }
377
+ },
378
+ meta: { usageType: 'route' }
379
+ },
380
+
381
+ {
382
+ serviceTag: 'blogComponent',
383
+ serviceImpl: class BlogComponentService {
384
+ // 注册全局组件
385
+ getComponents() {
386
+ return {
387
+ BlogHome,
388
+ ArticleList,
389
+ ArticleDetail,
390
+ ArticleForm
391
+ };
392
+ }
393
+ },
394
+ meta: { usageType: 'component' }
395
+ }
396
+ ]
397
+ };
398
+
399
+ // 导出组件方便其他模块使用
400
+ export {
401
+ BlogHome,
402
+ ArticleList,
403
+ ArticleDetail,
404
+ ArticleForm
405
+ };
@@ -0,0 +1,240 @@
1
+ import type { IModule } from '@linyjs/server-module-interface';
2
+
3
+ /**
4
+ * 文章服务 - 处理文章的业务逻辑
5
+ */
6
+ class ArticleService {
7
+ private articles: any[] = [];
8
+ private nextId = 1;
9
+
10
+ /**
11
+ * 获取所有文章(支持分页)
12
+ */
13
+ async getArticles(page: number = 1, limit: number = 10) {
14
+ const skip = (page - 1) * limit;
15
+ const total = this.articles.length;
16
+ const items = this.articles.slice(skip, skip + limit);
17
+
18
+ return {
19
+ data: items,
20
+ total,
21
+ page,
22
+ limit,
23
+ totalPages: Math.ceil(total / limit)
24
+ };
25
+ }
26
+
27
+ /**
28
+ * 根据ID获取文章
29
+ */
30
+ async getArticleById(id: number) {
31
+ const article = this.articles.find(a => a.id === id);
32
+ if (!article) {
33
+ throw new Error('Article not found');
34
+ }
35
+ return article;
36
+ }
37
+
38
+ /**
39
+ * 创建文章
40
+ */
41
+ async createArticle(data: any) {
42
+ const article = {
43
+ id: this.nextId++,
44
+ title: data.title,
45
+ content: data.content,
46
+ author: data.author || 'Anonymous',
47
+ createdAt: new Date().toISOString(),
48
+ updatedAt: new Date().toISOString()
49
+ };
50
+
51
+ this.articles.push(article);
52
+ return article;
53
+ }
54
+
55
+ /**
56
+ * 更新文章
57
+ */
58
+ async updateArticle(id: number, data: any) {
59
+ const index = this.articles.findIndex(a => a.id === id);
60
+ if (index === -1) {
61
+ throw new Error('Article not found');
62
+ }
63
+
64
+ this.articles[index] = {
65
+ ...this.articles[index],
66
+ ...data,
67
+ updatedAt: new Date().toISOString()
68
+ };
69
+
70
+ return this.articles[index];
71
+ }
72
+
73
+ /**
74
+ * 删除文章
75
+ */
76
+ async deleteArticle(id: number) {
77
+ const index = this.articles.findIndex(a => a.id === id);
78
+ if (index === -1) {
79
+ throw new Error('Article not found');
80
+ }
81
+
82
+ this.articles.splice(index, 1);
83
+ return { success: true };
84
+ }
85
+ }
86
+
87
+ /**
88
+ * 文章控制器 - 定义API路由
89
+ */
90
+ class ArticleController {
91
+ private articleService: ArticleService;
92
+
93
+ constructor() {
94
+ this.articleService = new ArticleService();
95
+ }
96
+
97
+ getRoutes() {
98
+ return [
99
+ // GET /api/articles - 获取文章列表
100
+ {
101
+ method: 'GET' as const,
102
+ path: '/api/articles',
103
+ middlewares: [],
104
+ responseType: 'json' as const,
105
+ handler: async (params: any, ctx: any) => {
106
+ const page = parseInt(params.query?.page || '1');
107
+ const limit = parseInt(params.query?.limit || '10');
108
+
109
+ try {
110
+ const result = await this.articleService.getArticles(page, limit);
111
+ return result;
112
+ } catch (error: any) {
113
+ return {
114
+ status: 500,
115
+ data: { error: error.message }
116
+ };
117
+ }
118
+ }
119
+ },
120
+
121
+ // GET /api/articles/:id - 获取单篇文章
122
+ {
123
+ method: 'GET' as const,
124
+ path: '/api/articles/:id',
125
+ middlewares: [],
126
+ responseType: 'json' as const,
127
+ handler: async (params: any, ctx: any) => {
128
+ try {
129
+ const id = parseInt(params.id);
130
+ const article = await this.articleService.getArticleById(id);
131
+ return article;
132
+ } catch (error: any) {
133
+ return {
134
+ status: 404,
135
+ data: { error: error.message }
136
+ };
137
+ }
138
+ }
139
+ },
140
+
141
+ // POST /api/articles - 创建文章
142
+ {
143
+ method: 'POST' as const,
144
+ path: '/api/articles',
145
+ middlewares: [],
146
+ responseType: 'json' as const,
147
+ handler: async (params: any, ctx: any) => {
148
+ try {
149
+ const { title, content, author } = params.body;
150
+
151
+ if (!title || !content) {
152
+ return {
153
+ status: 400,
154
+ data: { error: 'Title and content are required' }
155
+ };
156
+ }
157
+
158
+ const article = await this.articleService.createArticle({
159
+ title,
160
+ content,
161
+ author
162
+ });
163
+
164
+ return {
165
+ success: true,
166
+ data: article
167
+ };
168
+ } catch (error: any) {
169
+ return {
170
+ status: 500,
171
+ data: { error: error.message }
172
+ };
173
+ }
174
+ }
175
+ },
176
+
177
+ // PUT /api/articles/:id - 更新文章
178
+ {
179
+ method: 'PUT' as const,
180
+ path: '/api/articles/:id',
181
+ middlewares: [],
182
+ responseType: 'json' as const,
183
+ handler: async (params: any, ctx: any) => {
184
+ try {
185
+ const id = parseInt(params.id);
186
+ const article = await this.articleService.updateArticle(id, params.body);
187
+
188
+ return {
189
+ success: true,
190
+ data: article
191
+ };
192
+ } catch (error: any) {
193
+ return {
194
+ status: 404,
195
+ data: { error: error.message }
196
+ };
197
+ }
198
+ }
199
+ },
200
+
201
+ // DELETE /api/articles/:id - 删除文章
202
+ {
203
+ method: 'DELETE' as const,
204
+ path: '/api/articles/:id',
205
+ middlewares: [],
206
+ responseType: 'json' as const,
207
+ handler: async (params: any, ctx: any) => {
208
+ try {
209
+ const id = parseInt(params.id);
210
+ await this.articleService.deleteArticle(id);
211
+
212
+ return {
213
+ success: true,
214
+ message: 'Article deleted successfully'
215
+ };
216
+ } catch (error: any) {
217
+ return {
218
+ status: 404,
219
+ data: { error: error.message }
220
+ };
221
+ }
222
+ }
223
+ }
224
+ ];
225
+ }
226
+ }
227
+
228
+ /**
229
+ * 服务端模块定义
230
+ */
231
+ export const blogServerModule: IModule = {
232
+ name: 'blogServer',
233
+ serviceDefs: [
234
+ {
235
+ serviceTag: 'articleController',
236
+ serviceImpl: ArticleController,
237
+ meta: { usageType: 'controller' }
238
+ }
239
+ ]
240
+ };
@@ -0,0 +1,16 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "ESNext",
5
+ "lib": ["ES2020", "DOM"],
6
+ "jsx": "react-jsx",
7
+ "declaration": true,
8
+ "outDir": "./dist",
9
+ "rootDir": "./src",
10
+ "strict": false,
11
+ "esModuleInterop": true,
12
+ "skipLibCheck": true,
13
+ "moduleResolution": "node"
14
+ },
15
+ "include": ["src/**/*"]
16
+ }