kerria 0.2.1 → 0.2.4

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.
package/LICENSE CHANGED
@@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
18
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
19
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
20
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
21
+ SOFTWARE.
package/README.md CHANGED
@@ -14,4 +14,4 @@ pnpm i kerria
14
14
 
15
15
  ## 使用方式
16
16
 
17
- 请参考[示例文件](playground/kerria.config.ts)。
17
+ 请参考[示例文件](playground/kerria.config.ts)。
package/dist/index.d.ts CHANGED
@@ -1,48 +1,55 @@
1
+ //#region src/core/useLoad.d.ts
1
2
  interface LoadInfo extends Omit<UseLoadOptions, "defaultValue" | "beforeOutput"> {
2
- name: string;
3
- value: any;
4
- output: () => void;
3
+ name: string;
4
+ value: any;
5
+ output: () => void;
5
6
  }
6
7
  interface UseLoadOptions {
7
- src?: string;
8
- out: string;
9
- defaultValue?: unknown;
10
- onUpdate?: (newVal: any, oldVal: any) => any;
11
- beforeOutput?: (val: any) => any;
8
+ src?: string;
9
+ out: string;
10
+ defaultValue?: unknown;
11
+ onUpdate?: (newVal: any, oldVal: any) => any;
12
+ beforeOutput?: (val: any) => any;
12
13
  }
13
14
  declare function useLoad(name: string, options: UseLoadOptions): LoadInfo;
14
-
15
+ //#endregion
16
+ //#region src/types.d.ts
15
17
  type MaybePromise<T> = T | Promise<T>;
16
18
 
19
+ //#endregion
20
+ //#region src/core/useSource.d.ts
17
21
  interface SourceInfo extends Omit<UseSourceOptions, "folders"> {
18
- kind: number;
19
- folders: string[];
20
- patterns: string[];
21
- filter: (path: string) => boolean;
22
- output: (path: string, data: any) => Promise<void>;
22
+ kind: number;
23
+ folders: string[];
24
+ patterns: string[];
25
+ filter: (path: string) => boolean;
26
+ output: (path: string, data: any) => Promise<void>;
23
27
  }
24
28
  interface UseSourceOptions<T = any> {
25
- base: string;
26
- dist?: string;
27
- folders?: string[];
28
- ext: string;
29
- deep?: boolean;
30
- skip?: number;
31
- parse: (path: string, info: SourceInfo) => MaybePromise<T | null | void>;
32
- unlink?: (cache: T) => void;
33
- onCacheHit?: (cache: T) => void;
29
+ base: string;
30
+ dist?: string;
31
+ folders?: string[];
32
+ ext: string;
33
+ deep?: boolean;
34
+ skip?: number;
35
+ parse: (path: string, info: SourceInfo) => MaybePromise<T | null | void>;
36
+ unlink?: (cache: T) => void;
37
+ onCacheHit?: (cache: T) => void;
34
38
  }
35
39
  declare function useSource<C extends object>(kind: number, options: UseSourceOptions<C>): SourceInfo;
36
40
 
41
+ //#endregion
42
+ //#region src/core/processor.d.ts
37
43
  interface ProcessorContext {
38
- sign: string;
39
- loadInfos: LoadInfo[];
40
- sourceInfos: SourceInfo[];
44
+ sign: string;
45
+ loadInfos: LoadInfo[];
46
+ sourceInfos: SourceInfo[];
41
47
  }
42
48
  declare function useCurrentContext(): ProcessorContext;
43
49
  declare function createProcessor(sign: string, setup: (ctx: ProcessorContext) => void): {
44
- build: () => Promise<void>;
45
- watch: () => Promise<void>;
50
+ build: () => Promise<void>;
51
+ watch: () => Promise<void>;
46
52
  };
47
53
 
48
- export { type LoadInfo, type SourceInfo, type UseLoadOptions, createProcessor, useCurrentContext, useLoad, useSource };
54
+ //#endregion
55
+ export { LoadInfo, SourceInfo, UseLoadOptions, createProcessor, useCurrentContext, useLoad, useSource };
package/dist/index.js CHANGED
@@ -1,200 +1,188 @@
1
- // src/core/processor.ts
1
+ import { createHash } from "node:crypto";
2
+ import { existsSync, mkdirSync, readFileSync, rmdirSync, writeFileSync } from "node:fs";
3
+ import { readFile, stat, writeFile } from "node:fs/promises";
2
4
  import chokidar from "chokidar";
3
5
  import consola from "consola";
4
- import CryptoES from "crypto-es";
5
- import findCacheDir from "find-cache-dir";
6
- import fs from "fs-extra";
7
- import { join } from "pathe";
6
+ import * as pkg from "empathic/package";
7
+ import { join, resolve } from "pathe";
8
8
  import { glob } from "tinyglobby";
9
+ import { dirname } from "node:path";
9
10
 
10
- // src/utils.ts
11
- var isDev = process.env.NODE_ENV === "development";
11
+ //#region src/utils.ts
12
+ const isDev = process.env.NODE_ENV === "development";
12
13
  function capitalize(str) {
13
- return str[0].toUpperCase() + str.slice(1);
14
+ return str[0].toUpperCase() + str.slice(1);
15
+ }
16
+ async function readJson(path) {
17
+ const data = await readFile(path, "utf-8");
18
+ return JSON.parse(data);
19
+ }
20
+ function readJsonSync(path) {
21
+ const data = readFileSync(path, "utf-8");
22
+ return JSON.parse(data);
23
+ }
24
+ function writeJson(path, data) {
25
+ mkdirSync(dirname(path), { recursive: true });
26
+ const json = JSON.stringify(data);
27
+ return writeFile(path, json);
28
+ }
29
+ function writeJsonSync(path, data) {
30
+ mkdirSync(dirname(path), { recursive: true });
31
+ const json = JSON.stringify(data);
32
+ return writeFileSync(path, json);
14
33
  }
15
34
 
16
- // src/core/processor.ts
17
- var currentContext = null;
35
+ //#endregion
36
+ //#region src/core/processor.ts
37
+ let currentContext = null;
18
38
  function useCurrentContext() {
19
- return currentContext;
39
+ return currentContext;
20
40
  }
21
41
  function createProcessor(sign, setup) {
22
- const ctx = {
23
- sign,
24
- loadInfos: [],
25
- sourceInfos: []
26
- };
27
- currentContext = ctx;
28
- setup(ctx);
29
- currentContext = null;
30
- ctx.sourceInfos.sort((a, b) => {
31
- return a.kind - b.kind;
32
- });
33
- const cacheDir = findCacheDir({ name: "kerria" });
34
- const cachePath = join(cacheDir, `${sign}.json`);
35
- const caches = fs.existsSync(cachePath) && fs.readJsonSync(cachePath) || {};
36
- async function build() {
37
- for (const info of ctx.sourceInfos) {
38
- const paths = await glob(info.patterns, {
39
- deep: info.deep ? Infinity : 2,
40
- absolute: true
41
- });
42
- await Promise.all(
43
- paths.map((path) => path.replaceAll("\\", "/")).filter(info.filter).sort((a, b) => a.localeCompare(b)).map((path) => parse(path, info))
44
- );
45
- }
46
- outputLoads();
47
- consola.success(`[${sign}] Build`);
48
- }
49
- async function watch() {
50
- for (const info of ctx.sourceInfos) {
51
- chokidar.watch(info.folders, {
52
- depth: info.deep ? Infinity : 0,
53
- ignoreInitial: true
54
- }).on("all", async (event, filename) => {
55
- const path = filename.replaceAll("\\", "/");
56
- if (!path.endsWith(info.ext)) {
57
- return false;
58
- } else if (!info.filter(path)) {
59
- return false;
60
- } else if (event === "change" && !await parse(path, info)) {
61
- return false;
62
- } else if (event === "add" && !await add(path, info)) {
63
- return false;
64
- } else if (event === "unlink" && !unlink(path, info)) {
65
- return false;
66
- }
67
- outputLoads();
68
- consola.success(`[${sign}] ${capitalize(event)} "${path}"`);
69
- });
70
- }
71
- for (const info of ctx.loadInfos) {
72
- if (!info.src) {
73
- continue;
74
- }
75
- chokidar.watch(info.src, {
76
- ignoreInitial: true
77
- }).on("change", async () => {
78
- const newVal = await fs.readJson(info.src);
79
- info.value = info.onUpdate?.(newVal, info.value) ?? newVal;
80
- info.output();
81
- consola.success(`[${sign}] Change "${info.src}"`);
82
- });
83
- }
84
- }
85
- async function parse(path, info) {
86
- const stats = await fs.stat(path);
87
- const hash = CryptoES.MD5(stats.size.toString()).toString();
88
- let cache = caches[path];
89
- if (isDev && cache?.hash === hash) {
90
- info.onCacheHit?.(cache);
91
- return false;
92
- }
93
- cache = { hash };
94
- const data = await info.parse(path, info);
95
- if (data !== null) {
96
- cache = {
97
- ...cache,
98
- ...data ?? {}
99
- };
100
- info.onCacheHit?.(cache);
101
- caches[path] = cache;
102
- } else {
103
- unlink(path, info);
104
- }
105
- return true;
106
- }
107
- async function add(path, info) {
108
- const cache = caches[path];
109
- if (cache) {
110
- return false;
111
- }
112
- return await parse(path, info);
113
- }
114
- function unlink(path, info) {
115
- const cache = caches[path];
116
- if (!cache) {
117
- return false;
118
- }
119
- info.unlink?.(cache);
120
- delete caches[path];
121
- return true;
122
- }
123
- function outputLoads() {
124
- if (isDev) {
125
- fs.outputJsonSync(cachePath, caches);
126
- }
127
- for (const info of ctx.loadInfos) {
128
- info.output();
129
- }
130
- }
131
- return {
132
- build,
133
- watch
134
- };
42
+ const ctx = {
43
+ sign,
44
+ loadInfos: [],
45
+ sourceInfos: []
46
+ };
47
+ currentContext = ctx;
48
+ setup(ctx);
49
+ currentContext = null;
50
+ ctx.sourceInfos.sort((a, b) => {
51
+ return a.kind - b.kind;
52
+ });
53
+ const cacheDir = pkg.cache("kerria", { create: true });
54
+ const cachePath = join(cacheDir, `${sign}.json`);
55
+ const caches = existsSync(cachePath) && readJsonSync(cachePath) || {};
56
+ async function build() {
57
+ for (const info of ctx.sourceInfos) {
58
+ const paths = await glob(info.patterns, {
59
+ deep: info.deep ? Infinity : 2,
60
+ absolute: true
61
+ });
62
+ await Promise.all(paths.map((path) => path.replaceAll("\\", "/")).filter(info.filter).sort((a, b) => a.localeCompare(b)).map((path) => parse(path, info)));
63
+ }
64
+ outputLoads();
65
+ consola.success(`[${sign}] Build`);
66
+ }
67
+ async function watch() {
68
+ for (const info of ctx.sourceInfos) chokidar.watch(info.folders, {
69
+ depth: info.deep ? Infinity : 0,
70
+ ignoreInitial: true
71
+ }).on("all", async (event, filename) => {
72
+ const path = filename.replaceAll("\\", "/");
73
+ if (!path.endsWith(info.ext)) return false;
74
+ else if (!info.filter(path)) return false;
75
+ else if (event === "change" && !await parse(path, info)) return false;
76
+ else if (event === "add" && !await add(path, info)) return false;
77
+ else if (event === "unlink" && !unlink(path, info)) return false;
78
+ outputLoads();
79
+ consola.success(`[${sign}] ${capitalize(event)} "${path}"`);
80
+ });
81
+ for (const info of ctx.loadInfos) {
82
+ if (!info.src) continue;
83
+ chokidar.watch(info.src, { ignoreInitial: true }).on("change", async () => {
84
+ const newVal = await readJson(info.src);
85
+ info.value = info.onUpdate?.(newVal, info.value) ?? newVal;
86
+ info.output();
87
+ consola.success(`[${sign}] Change "${info.src}"`);
88
+ });
89
+ }
90
+ }
91
+ async function parse(path, info) {
92
+ const stats = await stat(path);
93
+ const hash = createHash("md5").update(stats.mtimeMs.toString()).digest("hex");
94
+ let cache = caches[path];
95
+ if (isDev && cache?.hash === hash) {
96
+ info.onCacheHit?.(cache);
97
+ return false;
98
+ }
99
+ cache = { hash };
100
+ const data = await info.parse(path, info);
101
+ if (data !== null) {
102
+ cache = {
103
+ ...cache,
104
+ ...data ?? {}
105
+ };
106
+ info.onCacheHit?.(cache);
107
+ caches[path] = cache;
108
+ } else unlink(path, info);
109
+ return true;
110
+ }
111
+ async function add(path, info) {
112
+ const cache = caches[path];
113
+ if (cache) return false;
114
+ return await parse(path, info);
115
+ }
116
+ function unlink(path, info) {
117
+ const cache = caches[path];
118
+ if (!cache) return false;
119
+ info.unlink?.(cache);
120
+ delete caches[path];
121
+ return true;
122
+ }
123
+ function outputLoads() {
124
+ if (isDev) writeJsonSync(cachePath, caches);
125
+ else rmdirSync(cachePath);
126
+ for (const info of ctx.loadInfos) info.output();
127
+ }
128
+ return {
129
+ build,
130
+ watch
131
+ };
135
132
  }
136
133
 
137
- // src/core/useLoad.ts
138
- import fs2 from "fs-extra";
139
- import { resolve } from "pathe";
134
+ //#endregion
135
+ //#region src/core/useLoad.ts
140
136
  function useLoad(name, options) {
141
- const ctx = useCurrentContext();
142
- const src = options.src ? resolve(options.src) : void 0;
143
- const out = resolve(options.out);
144
- const {
145
- defaultValue = {},
146
- onUpdate,
147
- beforeOutput
148
- } = options;
149
- const info = {
150
- name,
151
- src,
152
- out,
153
- value: src ? fs2.readJsonSync(src) : defaultValue,
154
- onUpdate,
155
- output() {
156
- const data = beforeOutput?.(info.value) ?? info.value;
157
- fs2.outputJsonSync(out, data);
158
- }
159
- };
160
- ctx.loadInfos.push(info);
161
- onUpdate?.(info.value, void 0);
162
- return info;
137
+ const ctx = useCurrentContext();
138
+ const src = options.src ? resolve(options.src) : void 0;
139
+ const out = resolve(options.out);
140
+ const { defaultValue = {}, onUpdate, beforeOutput } = options;
141
+ const info = {
142
+ name,
143
+ src,
144
+ out,
145
+ value: src ? readJsonSync(src) : defaultValue,
146
+ onUpdate,
147
+ output() {
148
+ const data = beforeOutput?.(info.value) ?? info.value;
149
+ writeJsonSync(out, data);
150
+ }
151
+ };
152
+ ctx.loadInfos.push(info);
153
+ onUpdate?.(info.value, void 0);
154
+ return info;
163
155
  }
164
156
 
165
- // src/core/useSource.ts
166
- import fs3 from "fs-extra";
167
- import { resolve as resolve2 } from "pathe";
157
+ //#endregion
158
+ //#region src/core/useSource.ts
168
159
  function useSource(kind, options) {
169
- const ctx = useCurrentContext();
170
- const base = resolve2(options.base);
171
- const dist = options.dist ? resolve2(options.dist) : void 0;
172
- const folders = options.folders?.map((folder) => resolve2(base, folder)) ?? [base];
173
- const patterns = folders.map((path) => resolve2(path, `**/*${options.ext}`));
174
- const info = {
175
- ...options,
176
- kind,
177
- base,
178
- dist,
179
- folders,
180
- patterns,
181
- deep: options.deep ?? true,
182
- skip: options.skip ?? 0,
183
- filter(path) {
184
- const depth = path.split("/").length - folders[0].split("/").length;
185
- return info.skip < depth;
186
- },
187
- async output(path, data) {
188
- const outPath = path.replace(base, dist).replace(info.ext, ".json");
189
- await fs3.outputJson(outPath, data);
190
- }
191
- };
192
- ctx.sourceInfos.push(info);
193
- return info;
160
+ const ctx = useCurrentContext();
161
+ const base = resolve(options.base);
162
+ const dist = options.dist ? resolve(options.dist) : void 0;
163
+ const folders = options.folders?.map((folder) => resolve(base, folder)) ?? [base];
164
+ const patterns = folders.map((path) => resolve(path, `**/*${options.ext}`));
165
+ const info = {
166
+ ...options,
167
+ kind,
168
+ base,
169
+ dist,
170
+ folders,
171
+ patterns,
172
+ deep: options.deep ?? true,
173
+ skip: options.skip ?? 0,
174
+ filter(path) {
175
+ const depth = path.split("/").length - folders[0].split("/").length;
176
+ return info.skip < depth;
177
+ },
178
+ async output(path, data) {
179
+ const outPath = path.replace(base, dist).replace(info.ext, ".json");
180
+ await writeJson(outPath, data);
181
+ }
182
+ };
183
+ ctx.sourceInfos.push(info);
184
+ return info;
194
185
  }
195
- export {
196
- createProcessor,
197
- useCurrentContext,
198
- useLoad,
199
- useSource
200
- };
186
+
187
+ //#endregion
188
+ export { createProcessor, useCurrentContext, useLoad, useSource };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "kerria",
3
3
  "type": "module",
4
- "version": "0.2.1",
4
+ "version": "0.2.4",
5
5
  "description": "Composable source processor",
6
6
  "author": "KazariEX",
7
7
  "license": "MIT",
@@ -19,18 +19,22 @@
19
19
  "dependencies": {
20
20
  "chokidar": "^4.0.3",
21
21
  "consola": "^3.4.2",
22
- "crypto-es": "^2.1.0",
23
- "find-cache-dir": "^5.0.0",
24
- "fs-extra": "^11.3.0",
22
+ "empathic": "^1.1.0",
25
23
  "pathe": "^2.0.3",
26
- "tinyglobby": "^0.2.12"
24
+ "tinyglobby": "^0.2.13"
27
25
  },
28
26
  "devDependencies": {
29
- "@types/fs-extra": "^11.0.4"
27
+ "@antfu/eslint-config": "^4.13.0",
28
+ "@types/node": "^22.15.17",
29
+ "@zinkawaii/eslint-config": "^0.3.0",
30
+ "@zinkawaii/tsconfig": "^0.0.2",
31
+ "bumpp": "^10.1.0",
32
+ "eslint": "^9.26.0",
33
+ "tsdown": "^0.11.5"
30
34
  },
31
35
  "scripts": {
32
- "build": "tsup-node",
33
- "dev": "tsup-node --watch",
36
+ "build": "tsdown",
37
+ "dev": "tsdown -w",
34
38
  "release": "bumpp --no-push -c \"release: v%s\""
35
39
  }
36
40
  }