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 +1 -1
- package/README.md +1 -1
- package/dist/index.d.ts +36 -29
- package/dist/index.js +171 -183
- package/package.json +12 -8
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
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
|
-
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
name: string;
|
|
4
|
+
value: any;
|
|
5
|
+
output: () => void;
|
|
5
6
|
}
|
|
6
7
|
interface UseLoadOptions {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
-
|
|
45
|
-
|
|
50
|
+
build: () => Promise<void>;
|
|
51
|
+
watch: () => Promise<void>;
|
|
46
52
|
};
|
|
47
53
|
|
|
48
|
-
|
|
54
|
+
//#endregion
|
|
55
|
+
export { LoadInfo, SourceInfo, UseLoadOptions, createProcessor, useCurrentContext, useLoad, useSource };
|
package/dist/index.js
CHANGED
|
@@ -1,200 +1,188 @@
|
|
|
1
|
-
|
|
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
|
|
5
|
-
import
|
|
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
|
-
|
|
11
|
-
|
|
11
|
+
//#region src/utils.ts
|
|
12
|
+
const isDev = process.env.NODE_ENV === "development";
|
|
12
13
|
function capitalize(str) {
|
|
13
|
-
|
|
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
|
-
|
|
17
|
-
|
|
35
|
+
//#endregion
|
|
36
|
+
//#region src/core/processor.ts
|
|
37
|
+
let currentContext = null;
|
|
18
38
|
function useCurrentContext() {
|
|
19
|
-
|
|
39
|
+
return currentContext;
|
|
20
40
|
}
|
|
21
41
|
function createProcessor(sign, setup) {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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
|
-
|
|
138
|
-
|
|
139
|
-
import { resolve } from "pathe";
|
|
134
|
+
//#endregion
|
|
135
|
+
//#region src/core/useLoad.ts
|
|
140
136
|
function useLoad(name, options) {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
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
|
-
|
|
166
|
-
|
|
167
|
-
import { resolve as resolve2 } from "pathe";
|
|
157
|
+
//#endregion
|
|
158
|
+
//#region src/core/useSource.ts
|
|
168
159
|
function useSource(kind, options) {
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
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
|
-
|
|
196
|
-
|
|
197
|
-
|
|
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.
|
|
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
|
-
"
|
|
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.
|
|
24
|
+
"tinyglobby": "^0.2.13"
|
|
27
25
|
},
|
|
28
26
|
"devDependencies": {
|
|
29
|
-
"@
|
|
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": "
|
|
33
|
-
"dev": "
|
|
36
|
+
"build": "tsdown",
|
|
37
|
+
"dev": "tsdown -w",
|
|
34
38
|
"release": "bumpp --no-push -c \"release: v%s\""
|
|
35
39
|
}
|
|
36
40
|
}
|