for-humanity 0.13.0 → 0.14.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.
@@ -1,18 +1,18 @@
1
1
  import { d as copy_error_missing_build, n as copy_error_config } from "./copy-C1uzvEnr.js";
2
2
  import { t as logger } from "./logger-Dv1i9xSV.js";
3
- import { n as site_config_absent, t as siteConfigSchema } from "./site-config-bllORGpm.js";
4
- import { n as font_cache_control, r as asset_font_dir, t as loadSite } from "./load-site-CbpLEhwu.js";
3
+ import { n as site_config_absent, t as siteConfigSchema } from "./site-config-FmStUQ2W.js";
4
+ import { n as font_cache_control, r as asset_font_dir, t as loadSite } from "./load-site-CBJGMNfH.js";
5
5
  import { n as config_file_names, t as toErrorMessage } from "./to-error-message-pF6_7wGK.js";
6
6
  import { copyFile, mkdir, rm, writeFile } from "node:fs/promises";
7
7
  import { z } from "zod";
8
- import { existsSync } from "node:fs";
9
- import { dirname, join, relative, resolve } from "node:path";
8
+ import { existsSync, realpathSync } from "node:fs";
9
+ import { dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
10
10
  import { toSSG } from "hono/ssg";
11
11
  import { fileURLToPath, pathToFileURL } from "node:url";
12
12
  import { createJiti } from "jiti";
13
13
  //#region src/app/_function/read-site-config.ts
14
14
  /**
15
- * 문서 루트의 설정 조회, TypeScript 실행과 스키마 검증
15
+ * 프로젝트 루트의 설정 조회, TypeScript 실행과 스키마 검증
16
16
  *
17
17
  * - 중복 파일 거부, 실제 적용되는 설정의 모호성 방지
18
18
  * - Node의 기본 TypeScript 지원 여부와 무관하게 같은 설정 실행
@@ -31,6 +31,29 @@ var readSiteConfig = async (root) => {
31
31
  return parsed.data.default;
32
32
  };
33
33
  //#endregion
34
+ //#region src/app/_function/read-project.ts
35
+ /**
36
+ * 프로젝트 설정과 문서 루트 해소, 외부·시스템 폴더 symlink 거부
37
+ */
38
+ var readProject = async (directory) => {
39
+ const projectRoot = realpathSync(resolve(directory));
40
+ const site = await readSiteConfig(projectRoot);
41
+ const root = resolve(projectRoot, site.content.root);
42
+ if (existsSync(root)) {
43
+ const location = relative(projectRoot, realpathSync(root));
44
+ if (location === ".." || location.startsWith(`..${sep}`) || isAbsolute(location)) throw new Error(`content 루트는 프로젝트 내부여야 합니다: ${root}`);
45
+ if (location.split(sep).some((part) => [
46
+ "dist",
47
+ ".git",
48
+ "node_modules"
49
+ ].includes(part.toLowerCase()))) throw new Error(`content 루트는 dist, .git, node_modules 내부일 수 없습니다: ${root}`);
50
+ }
51
+ return {
52
+ root,
53
+ site
54
+ };
55
+ };
56
+ //#endregion
34
57
  //#region src/entry/preview/to-preview-config.ts
35
58
  /**
36
59
  * 빌드된 사이트 전용 Vite 설정. 소비 프로젝트의 설정과 환경 파일 자동 로드 제외
@@ -75,31 +98,31 @@ var toPreviewConfig = (params) => {
75
98
  */
76
99
  var runSiteCommand = async (params) => {
77
100
  try {
78
- if (params.args._.length > 1) throw new Error("문서 폴더는 하나만 지정하세요");
101
+ if (params.args._.length > 1) throw new Error("프로젝트 폴더는 하나만 지정하세요");
79
102
  const kitRoot = fileURLToPath(new URL("../", import.meta.url));
80
- const docsRoot = resolve(params.args.dir);
81
- const outDir = join(docsRoot, "dist");
82
- const siteConfig = await readSiteConfig(docsRoot);
103
+ const project = await readProject(params.args.dir);
104
+ const outDir = join(project.root, "dist");
83
105
  if (params.command === "preview") {
84
106
  (await (await import("vite")).preview(toPreviewConfig({
85
- root: docsRoot,
86
- port: siteConfig.port
107
+ root: project.root,
108
+ port: project.site.port
87
109
  }))).printUrls();
88
110
  return;
89
111
  }
90
112
  if (params.command === "dev") {
91
- await (await import("./start-dev-server-C4_Lu1nZ.js")).startDevServer({
92
- root: docsRoot,
113
+ await (await import("./start-dev-server-x7Ja5uwv.js")).startDevServer({
114
+ root: project.root,
93
115
  kitRoot,
94
- site: siteConfig,
95
- source: false
116
+ site: project.site,
117
+ source: false,
118
+ includePrivate: true
96
119
  });
97
120
  return;
98
121
  }
99
122
  const snapshot = await loadSite({
100
- root: docsRoot,
123
+ root: project.root,
101
124
  kitRoot,
102
- site: siteConfig
125
+ site: project.site
103
126
  });
104
127
  await rm(outDir, {
105
128
  recursive: true,
@@ -18,6 +18,14 @@ var site_port_default = 4321;
18
18
  * 포트 번호 상한. TCP
19
19
  */
20
20
  var site_port_max = 65535;
21
+ /**
22
+ * 기본 문서 루트와 공개 범위 폴더
23
+ */
24
+ var content_default = {
25
+ root: "content",
26
+ public: "public",
27
+ private: "private"
28
+ };
21
29
  //#endregion
22
30
  //#region src/constant/status.ts
23
31
  /**
@@ -39,11 +47,47 @@ var status_default_phrases = [{
39
47
  kind: status_kind.unverified
40
48
  }];
41
49
  //#endregion
50
+ //#region src/type/content-config.ts
51
+ /**
52
+ * 프로젝트 내부의 상대 경로, 출력 폴더와 시스템 폴더 제외
53
+ */
54
+ var contentPathSchema = z.string().trim().min(1).refine((value) => !/^[\\/]|[:?#%*\p{Cc}]/u.test(value) && !value.split(/[\\/]/).some((part) => [
55
+ "..",
56
+ "dist",
57
+ "node_modules",
58
+ ".git"
59
+ ].includes(part.toLowerCase())), "프로젝트 내부 상대 경로만 사용하세요. dist, node_modules, .git 경로는 사용할 수 없습니다").transform((value) => value.replaceAll("\\", "/").split("/").filter((part) => part !== "" && part !== ".").join("/"));
60
+ /**
61
+ * 공개와 내부 문서 폴더의 상대 경로 계약
62
+ */
63
+ var contentConfigSchema = z.object({
64
+ /**
65
+ * 프로젝트 루트 기준 문서 폴더, 점 하나는 프로젝트 루트
66
+ */
67
+ root: contentPathSchema.transform((value) => value === "" ? "." : value).default(content_default.root),
68
+ /**
69
+ * 문서 루트 아래 공개 문서 폴더
70
+ */
71
+ public: contentPathSchema.refine((value) => value !== "", "공개 문서는 별도 하위 폴더가 필요합니다").default(content_default.public),
72
+ /**
73
+ * 문서 루트 아래 로컬 전용 문서 폴더
74
+ */
75
+ private: contentPathSchema.refine((value) => value !== "", "내부 문서는 별도 하위 폴더가 필요합니다").default(content_default.private)
76
+ }).refine((value) => {
77
+ const published = value.public.toLowerCase();
78
+ const internal = value.private.toLowerCase();
79
+ return published !== internal && !published.startsWith(`${internal}/`) && !internal.startsWith(`${published}/`);
80
+ }, "공개 문서와 내부 문서 폴더는 같거나 중첩될 수 없습니다");
81
+ //#endregion
42
82
  //#region src/type/site-config.ts
43
83
  /**
44
84
  * 사이트 설정의 입력 검증과 기본값 적용
45
85
  */
46
86
  var siteConfigSchema = z.object({
87
+ /**
88
+ * 프로젝트 기준 문서 루트와 공개 범위
89
+ */
90
+ content: contentConfigSchema.default(content_default),
47
91
  /**
48
92
  * 사이드바 이름과 탭 제목
49
93
  */
package/dist/site.js CHANGED
@@ -1 +1 @@
1
- export { t as loadSite } from "./load-site-CbpLEhwu.js";
1
+ export { t as loadSite } from "./load-site-CBJGMNfH.js";
@@ -96,7 +96,9 @@ var toDevConfig = (params) => {
96
96
  currentSite = nextSite;
97
97
  if (generation > 0) server.hot.send({ type: "full-reload" });
98
98
  },
99
- onError: (error) => server.config.logger.error(`${copy_error_prefix}: ${toErrorMessage(error)}`)
99
+ onError: (error) => {
100
+ return server.config.logger.error(`${copy_error_prefix}: ${toErrorMessage(error)}`);
101
+ }
100
102
  });
101
103
  const reload = debounce(refresh, 150);
102
104
  /**
@@ -126,7 +128,9 @@ var toDevConfig = (params) => {
126
128
  };
127
129
  prepare = refresh;
128
130
  },
129
- closeBundle: () => dispose?.()
131
+ closeBundle: () => {
132
+ return dispose?.();
133
+ }
130
134
  }, devServer({
131
135
  exclude: params.source ? [
132
136
  ...viteClientPaths,
@@ -137,13 +141,17 @@ var toDevConfig = (params) => {
137
141
  /^\/src\/entry\/browser\//
138
142
  ] : viteClientPaths,
139
143
  ignoreWatching: dev_ignored_paths,
140
- handleHotUpdate: (context) => context.file.endsWith(".css") ? void 0 : [],
144
+ handleHotUpdate: (context) => {
145
+ return context.file.endsWith(".css") ? void 0 : [];
146
+ },
141
147
  loadModule: async () => {
142
148
  ready ??= prepare?.();
143
149
  await ready;
144
150
  if (currentSite === void 0) throw new Error("문서 준비에 실패했습니다. 위 오류를 수정하면 다시 반영합니다.");
145
151
  const app = currentSite.app;
146
- return { fetch: async (request, env) => app.fetch(request, env) };
152
+ return { fetch: async (request, env) => {
153
+ return app.fetch(request, env);
154
+ } };
147
155
  }
148
156
  })]
149
157
  };
@@ -0,0 +1,9 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * 공개와 내부 문서 폴더의 상대 경로 계약
4
+ */
5
+ export declare const contentConfigSchema: z.ZodObject<{
6
+ root: z.ZodDefault<z.ZodPipe<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>, z.ZodTransform<string, string>>>;
7
+ public: z.ZodDefault<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
8
+ private: z.ZodDefault<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
9
+ }, z.core.$strip>;
@@ -3,6 +3,11 @@ import { z } from "zod";
3
3
  * 사이트 설정의 입력 검증과 기본값 적용
4
4
  */
5
5
  export declare const siteConfigSchema: z.ZodObject<{
6
+ content: z.ZodDefault<z.ZodObject<{
7
+ root: z.ZodDefault<z.ZodPipe<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>, z.ZodTransform<string, string>>>;
8
+ public: z.ZodDefault<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
9
+ private: z.ZodDefault<z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>>;
10
+ }, z.core.$strip>>;
6
11
  title: z.ZodDefault<z.ZodString>;
7
12
  description: z.ZodOptional<z.ZodString>;
8
13
  url: z.ZodOptional<z.ZodPipe<z.ZodPipe<z.ZodURL, z.ZodTransform<URL, string>>, z.ZodTransform<string, URL>>>;
@@ -26,6 +31,10 @@ export type ResolvedSiteConfig = z.infer<typeof siteConfigSchema>;
26
31
  * 작성자가 지정하는 사이트 설정, 기본값 적용 전 입력
27
32
  */
28
33
  export interface SiteConfig {
34
+ /**
35
+ * 기본 content/public, content/private 구조의 경로 변경
36
+ */
37
+ content?: z.input<typeof siteConfigSchema>["content"];
29
38
  /**
30
39
  * 사이드바 이름과 탭 제목, 생략 시 for humanity
31
40
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "for-humanity",
3
- "version": "0.13.0",
3
+ "version": "0.14.0",
4
4
  "description": "Documents written for humans to read: a docs kit with a sidebar, parts, sections and Mermaid diagrams, built on Hono JSX",
5
5
  "keywords": [
6
6
  "docs",
@@ -27,6 +27,7 @@
27
27
  "files": [
28
28
  "dist",
29
29
  "src/asset",
30
+ "template",
30
31
  "CHANGELOG.md"
31
32
  ],
32
33
  "engines": {
@@ -37,9 +38,9 @@
37
38
  "sync:readme": "tsx util/sync-readme.ts",
38
39
  "sync:docs": "pnpm sync:readme && tsx util/sync-changelog.ts",
39
40
  "dev": "pnpm sync:docs && pnpm build:kit && tsx src/entry/dev/dev.ts",
40
- "build": "pnpm sync:docs && pnpm build:kit && node dist/cli.js build templates/document",
41
+ "build": "pnpm sync:docs && pnpm build:kit && node dist/cli.js build .",
41
42
  "build:pages": "pnpm build && tsx util/prepare-pages.ts",
42
- "preview": "node dist/cli.js preview templates/document",
43
+ "preview": "node dist/cli.js preview .",
43
44
  "test": "pnpm build:kit && vitest run",
44
45
  "test:watch": "pnpm build:kit && vitest",
45
46
  "test:browser": "pnpm build:kit && playwright test",
@@ -0,0 +1,5 @@
1
+ # Project notes
2
+
3
+ 이 프로젝트의 소개와 문서 안내.
4
+
5
+ [시작하기](public/getting-started.md)
File without changes
File without changes
@@ -0,0 +1,26 @@
1
+ ---
2
+ name: Getting started
3
+ label: 문서 작성과 공개 범위
4
+ group: Guide
5
+ order: 10
6
+ ---
7
+
8
+ # Getting started
9
+
10
+ `content/public`은 공개 문서, `content/private`는 로컬 전용 문서입니다.
11
+ 공용 이미지와 첨부는 `content/asset`, 내부 자료는 `content/private/asset`에 보관합니다.
12
+
13
+ 프로젝트 루트에서 실행합니다.
14
+
15
+ ```sh
16
+ pnpm exec for-humanity dev
17
+ pnpm exec for-humanity build
18
+ pnpm exec for-humanity preview
19
+ ```
20
+
21
+ `dev`는 전체 문서, `build`와 `preview`는 공개 결과를 제공합니다.
22
+ 배포할 폴더는 `content/dist`입니다. `content` 전체를 업로드하지 않습니다.
23
+ `private`는 사이트 배포 제외이며 Git 저장소 접근 제어가 아닙니다.
24
+
25
+ 설정은 프로젝트 루트의 `for-humanity.config.ts`, 홈은 `content/README.md`입니다.
26
+ 공개 문서의 URL에는 `public`이 붙지 않습니다. 이 문서는 `/getting-started/`에서 확인할 수 있습니다.
@@ -0,0 +1,8 @@
1
+ import type {SiteConfig} from "for-humanity";
2
+
3
+ export default {
4
+ title: "Project notes",
5
+
6
+ // 프로젝트 기준 문서 루트, 문서 루트 기준 공개와 내부 폴더
7
+ content: {root: "content", public: "public", private: "private"},
8
+ } satisfies SiteConfig;